mirror of
https://github.com/xibyte/jsketcher
synced 2025-12-07 00:45:08 +01:00
216 lines
5 KiB
JavaScript
216 lines
5 KiB
JavaScript
import {printRaycastDebugInfo, RayCastDebugInfo} from "./rayCastDebug";
|
|
import {LOG_FLAGS} from "cad/logFlags";
|
|
import {stream} from "lstream";
|
|
|
|
export const BundleName = "@MouseEventSystem";
|
|
|
|
const MouseStates = {
|
|
IDLE: 'IDLE',
|
|
DOWN: 'DOWN'
|
|
}
|
|
|
|
export function activate(ctx) {
|
|
const {services, streams} = ctx;
|
|
const domElement = services.viewer.sceneSetup.domElement();
|
|
const event = {
|
|
viewer: services.viewer,
|
|
mouseState: MouseStates.IDLE
|
|
};
|
|
|
|
domElement.addEventListener('mousedown', mousedown, false);
|
|
domElement.addEventListener('mouseup', mouseup, false);
|
|
domElement.addEventListener('mousemove', mousemove, false);
|
|
domElement.addEventListener('dblclick', dblclick, false);
|
|
|
|
const onMoveLogicRequest$ = stream();
|
|
|
|
onMoveLogicRequest$.throttle(100).attach(() => {
|
|
const hits = performRaycast(event.mouseEvent);
|
|
dispatchMousemove(event.mouseEvent, hits)
|
|
});
|
|
|
|
const performRaycast = e => {
|
|
const hits = services.viewer.raycast(e, services.cadScene.workGroup.children, RayCastDebugInfo);
|
|
hits.sort((a, b) => {
|
|
if (Math.abs(a.distance - b.distance) < 0.01 && (a.object.raycastPriority || b.object.raycastPriority)) {
|
|
return b.object.raycastPriority||0 - a.object.raycastPriority||0;
|
|
}
|
|
return a.distance - b.distance;
|
|
})
|
|
return hits;
|
|
}
|
|
|
|
let toDrag = null;
|
|
const pressed = new Set();
|
|
|
|
event.startDrag = objectToDrag => {
|
|
if (toDrag) {
|
|
stopDrag();
|
|
}
|
|
toDrag = objectToDrag;
|
|
services.viewer.sceneSetup.trackballControls.enabled = false;
|
|
};
|
|
|
|
function stopDrag() {
|
|
toDrag.dragDrop(event);
|
|
toDrag = null;
|
|
services.viewer.sceneSetup.trackballControls.enabled = true;
|
|
}
|
|
|
|
function mousedown(e) {
|
|
event.mouseState = MouseStates.DOWN;
|
|
const hits = performRaycast(e);
|
|
dispatchMousedown(e, hits);
|
|
}
|
|
|
|
function dispatchMousedown(e, hits) {
|
|
event.mouseEvent = e;
|
|
event.hits = hits;
|
|
pressed.clear();
|
|
|
|
for (const hit of hits) {
|
|
if (LOG_FLAGS.PICK) {
|
|
printRaycastDebugInfo('mouseDown', hit);
|
|
}
|
|
|
|
const obj = hit.object;
|
|
if (obj && obj.onMouseDown) {
|
|
safeCall(() => obj.onMouseDown(event));
|
|
}
|
|
pressed.add(obj);
|
|
if (!hit.object.passMouseEvent || !hit.object.passMouseEvent(event)) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
function mouseup(e) {
|
|
event.mouseState = MouseStates.IDLE;
|
|
event.mouseEvent = e;
|
|
if (toDrag) {
|
|
stopDrag(e);
|
|
mousemove(e);
|
|
} else {
|
|
const hits = performRaycast(e);
|
|
dispatchMouseup(e, hits);
|
|
}
|
|
}
|
|
|
|
function dispatchMouseup(e, hits) {
|
|
|
|
event.mouseEvent = e;
|
|
event.hits = hits;
|
|
|
|
for (const hit of hits) {
|
|
if (LOG_FLAGS.PICK) {
|
|
printRaycastDebugInfo('mouseUp', hit);
|
|
}
|
|
const obj = hit.object;
|
|
if (obj && obj.onMouseUp) {
|
|
safeCall(() => obj.onMouseUp(event));
|
|
}
|
|
if (pressed.has(obj) && obj.onMouseClick) {
|
|
safeCall(() => obj.onMouseClick(event));
|
|
}
|
|
if (!hit.object.passMouseEvent || !hit.object.passMouseEvent(event)) {
|
|
break;
|
|
}
|
|
}
|
|
pressed.clear();
|
|
}
|
|
|
|
let entered = new Set();
|
|
let valid = new Set();
|
|
|
|
function mousemove(e) {
|
|
event.mouseEvent = e;
|
|
|
|
if (toDrag) {
|
|
toDrag.dragMove(event);
|
|
} else {
|
|
if (event.mouseState === MouseStates.IDLE) {
|
|
onMoveLogicRequest$.next();
|
|
}
|
|
}
|
|
}
|
|
|
|
function dispatchMousemove(e, hits) {
|
|
event.mouseEvent = e;
|
|
event.hits = hits;
|
|
|
|
valid.clear();
|
|
for (const hit of hits) {
|
|
valid.add(hit.object);
|
|
if (!hit.object.passMouseEvent || !hit.object.passMouseEvent(event)) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
entered.forEach(el => {
|
|
//need to check parent in case of object removed
|
|
if (!valid.has(el) && el.onMouseLeave && !isGone(el)) {
|
|
safeCall(() => el.onMouseLeave(event));
|
|
}
|
|
});
|
|
|
|
valid.forEach(el => {
|
|
if (!entered.has(el) && el.onMouseEnter) {
|
|
safeCall(() => el.onMouseEnter(event));
|
|
}
|
|
if (el.onMouseMove) {
|
|
safeCall(() => el.onMouseMove(event));
|
|
}
|
|
});
|
|
|
|
const t = valid;
|
|
valid = entered;
|
|
entered = t;
|
|
valid.clear();
|
|
}
|
|
|
|
function dblclick(e) {
|
|
const hits = performRaycast(e);
|
|
dispatchDblclick(e, hits);
|
|
}
|
|
|
|
function dispatchDblclick(e, hits) {
|
|
event.mouseEvent = e;
|
|
event.hits = hits;
|
|
for (const hit of hits) {
|
|
if (LOG_FLAGS.PICK) {
|
|
printRaycastDebugInfo('dblclick', hit);
|
|
}
|
|
const obj = hit.object;
|
|
if (obj && obj.onDblclick) {
|
|
safeCall(() => obj.onDblclick(event));
|
|
}
|
|
if (!hit.object.passMouseEvent || !hit.object.passMouseEvent(event)) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
ctx.services.modelMouseEventSystem = {
|
|
dispatchMousedown, dispatchMouseup, dispatchMousemove, dispatchDblclick
|
|
}
|
|
}
|
|
|
|
export function hasObject(hits, object) {
|
|
return hits.find(hit => hit.object === object);
|
|
}
|
|
|
|
function safeCall(fn) {
|
|
try {
|
|
fn();
|
|
} catch (e) {
|
|
console.error(e);
|
|
}
|
|
}
|
|
function isGone(el) {
|
|
while (el.parent != null) {
|
|
el = el.parent;
|
|
}
|
|
return !el.isScene;
|
|
}
|
|
|