@shopware-ag/dive
Version:
Shopware Spatial Framework
582 lines (581 loc) • 18.6 kB
JavaScript
var S = Object.defineProperty;
var w = (n, t, e) => t in n ? S(n, t, { enumerable: !0, configurable: !0, writable: !0, value: e }) : n[t] = e;
var o = (n, t, e) => w(n, typeof t != "symbol" ? t + "" : t, e);
import { EventDispatcher as T, Vector3 as l, Raycaster as P, Vector2 as f, Layers as m } from "three/webgpu";
import "../../chunks/FileTypes-Ck6z0LqE.mjs";
import { P as _, U as d } from "../../chunks/PerspectiveCamera-35cBnxwG.mjs";
import "three/examples/jsm/loaders/HDRLoader.js";
import "three/tsl";
import { f as g, i as h } from "../../chunks/findInterface-DbJ5qzbc.mjs";
import { TransformControls as E } from "three/examples/jsm/controls/TransformControls.js";
import { A as b, a as D, b as y } from "../../chunks/AxisHelperColors-JLBHYQDi.mjs";
class z {
constructor() {
o(this, "_selected", null);
o(this, "_listeners", /* @__PURE__ */ new Set());
}
/**
* Currently selected object, or null if nothing is selected.
*/
get selected() {
return this._selected;
}
/**
* Select an object. Deselects any previously selected object.
* Calls onSelect on the new object and onDeselect on the previous.
*/
select(t) {
var e, s, i;
this._selected !== t && (this._selected && ((s = (e = this._selected).onDeselect) == null || s.call(e)), this._selected = t, (i = t.onSelect) == null || i.call(t), this.notifyListeners());
}
/**
* Deselect the currently selected object.
* Calls onDeselect on the object.
*/
deselect() {
var t, e;
this._selected && ((e = (t = this._selected).onDeselect) == null || e.call(t), this._selected = null, this.notifyListeners());
}
/**
* Register a callback to be notified when selection changes.
*/
onChange(t) {
this._listeners.add(t);
}
/**
* Unregister a previously registered callback.
*/
offChange(t) {
this._listeners.delete(t);
}
/**
* Dispose of the selection state and clear all listeners.
*/
dispose() {
this._selected = null, this._listeners.clear();
}
notifyListeners() {
for (const t of this._listeners)
t(this._selected);
}
}
class C {
constructor() {
o(this, "name", "hover");
o(this, "priority", 20);
o(this, "_hovered", null);
}
/**
* Currently hovered object, or null if nothing is hovered.
*/
get hovered() {
return this._hovered;
}
onActivate() {
this._hovered = null;
}
onDeactivate() {
var t, e;
this._hovered && ((e = (t = this._hovered).onPointerLeave) == null || e.call(t), this._hovered = null);
}
onPointerMove(t) {
var i, r, a, c, u, p, v;
const e = t.modelIntersects[0], s = g(
e == null ? void 0 : e.object,
"isHoverable"
);
if (e && s) {
if (!this._hovered) {
(i = s.onPointerEnter) == null || i.call(s, e), this._hovered = s;
return;
}
if (this._hovered.uuid !== s.uuid) {
(a = (r = this._hovered).onPointerLeave) == null || a.call(r), (c = s.onPointerEnter) == null || c.call(s, e), this._hovered = s;
return;
}
(u = s.onPointerOver) == null || u.call(s, e);
return;
}
this._hovered && ((v = (p = this._hovered).onPointerLeave) == null || v.call(p), this._hovered = null);
}
}
const B = (n) => n.name === "select";
class M {
constructor(t) {
o(this, "name", "select");
o(this, "priority", 30);
o(this, "_selectionState");
this._selectionState = t;
}
/**
* Get the currently selected object.
*/
get selected() {
return this._selectionState.selected;
}
onActivate() {
}
onDeactivate() {
}
onClick(t) {
const e = t.modelIntersects[0], s = g(
e == null ? void 0 : e.object,
"isSelectable"
);
if (!e || !s) {
this._selectionState.deselect();
return;
}
const i = this._selectionState.selected;
i && i.uuid === s.uuid || this._selectionState.select(s);
}
/**
* Programmatically select an object.
*/
select(t) {
this._selectionState.select(t);
}
/**
* Programmatically deselect the current selection.
*/
deselect() {
this._selectionState.deselect();
}
}
const Y = (n) => n.name === "transform";
class L extends T {
constructor(e, s, i) {
super();
o(this, "name", "transform");
o(this, "priority", 5);
o(this, "_scene");
o(this, "_controller");
o(this, "_selectionState");
o(this, "_gizmo");
o(this, "_gizmoHelper");
o(this, "_scaleLinked", !1);
o(this, "_gizmoVisible", !0);
o(this, "_selectionChangeHandler");
this._scene = e, this._controller = s, this._selectionState = i, this._gizmo = this.initGizmo(), this._gizmoHelper = this._gizmo.getHelper(), this._scene.add(this._gizmoHelper), this._selectionChangeHandler = this.onSelectionChange.bind(this);
}
/**
* Get the TransformControls gizmo.
*/
get gizmo() {
return this._gizmo;
}
/**
* Whether a drag operation is currently in progress.
*/
get isDragging() {
return this._gizmo.dragging;
}
onActivate() {
this._selectionState.onChange(this._selectionChangeHandler);
const e = this._selectionState.selected;
e && this.attachGizmo(e);
}
onDeactivate() {
this._selectionState.offChange(this._selectionChangeHandler), this._gizmo.detach();
}
onPointerMove(e) {
if (this._gizmo.dragging || e.uiIntersects.length > 0 && e.uiIntersects.some(
(i) => this.isGizmoChild(i.object)
))
return !0;
}
/**
* Set the gizmo transformation mode.
*/
setGizmoMode(e) {
this._gizmo.mode = e;
}
/**
* Set whether the gizmo is visible.
*/
setGizmoVisible(e) {
this._gizmoVisible = e;
const s = this._scene.children.includes(this._gizmoHelper);
e && !s ? (this._scene.add(this._gizmoHelper), this._gizmo.getRaycaster().layers.enableAll()) : !e && s && (this._scene.remove(this._gizmoHelper), this._gizmo.getRaycaster().layers.disableAll());
}
/**
* Set whether scale operations are linked (uniform scaling).
*/
setGizmoScaleLinked(e) {
this._scaleLinked = e;
}
/**
* Dispose of the tool and clean up resources.
*/
dispose() {
this._selectionState.offChange(this._selectionChangeHandler), this._gizmo.detach(), this._scene.remove(this._gizmoHelper), this._gizmo.dispose();
}
// ============ Private Methods ============
onSelectionChange(e) {
e && h(e, "isMovable") ? this.attachGizmo(e) : this._gizmo.detach();
}
attachGizmo(e) {
h(e, "isMovable") && (this._gizmo.attach(e), this.setGizmoVisible(e.visible && this._gizmoVisible));
}
isGizmoChild(e) {
let s = e;
for (; s; ) {
if (s === this._gizmoHelper) return !0;
s = s.parent;
}
return !1;
}
initGizmo() {
const e = new E(
this._controller.object,
this._controller.domElement
);
return e.mode = "translate", e.getHelper().traverse((i) => {
if (!("isMesh" in i)) return;
const r = i.material;
i.name === "X" && r.color.set(b), i.name === "Y" && r.color.set(D), i.name === "Z" && r.color.set(y), i.name === "XY" && r.color.set(y), i.name === "YZ" && r.color.set(b), i.name === "XZ" && r.color.set(D);
}), e.addEventListener("mouseDown", () => {
var i, r;
this._controller.enabled = !1, h(e.object, "isMovable") && ((r = (i = e.object).onMoveStart) == null || r.call(i));
}), e.addEventListener("objectChange", () => {
var i, r;
if (h(e.object, "isMovable")) {
if ((r = (i = e.object).onMove) == null || r.call(i), this._scaleLinked && e.object) {
const a = e.object.scale, c = (a.x + a.y + a.z) / 3;
e.object.scale.set(c, c, c);
}
switch (this.dispatchEvent({
type: "object-change",
object: e.object
}), e.mode) {
case "translate":
this.dispatchEvent({
type: "object-position-change",
object: e.object
});
break;
case "rotate":
this.dispatchEvent({
type: "object-rotation-change",
object: e.object
});
break;
case "scale":
this.dispatchEvent({
type: "object-scale-change",
object: e.object
});
break;
}
}
}), e.addEventListener("mouseUp", () => {
var i, r;
this._controller.enabled = !0, h(e.object, "isMovable") && ((r = (i = e.object).onMoveEnd) == null || r.call(i));
}), e;
}
}
const A = 1e-3;
class j {
constructor(t) {
o(this, "name", "drag");
o(this, "priority", 10);
o(this, "_controller");
o(this, "_raycaster");
// Drag state
o(this, "_dragging", !1);
o(this, "_draggable", null);
o(this, "_dragStart", new l());
o(this, "_dragCurrent", new l());
o(this, "_dragEnd", new l());
o(this, "_dragDelta", new l());
// Custom raycast targets for drag plane
o(this, "_dragRaycastTargets", null);
this._controller = t, this._raycaster = new P();
}
/**
* Whether a drag operation is currently in progress.
*/
get isDragging() {
return this._dragging;
}
/**
* The object currently being dragged, or null.
*/
get draggable() {
return this._draggable;
}
onActivate() {
this.resetDragState();
}
onDeactivate() {
this._dragging && this._draggable && this.endDrag(), this.resetDragState();
}
onPointerDown(t) {
var e;
t.pointerPrimaryDown && (this._draggable = g(
(e = t.intersects[0]) == null ? void 0 : e.object,
"isDraggable"
) || null);
}
onPointerMove(t) {
if (t.pointerPrimaryDown && this._draggable && (this._raycaster.setFromCamera(t.pointer, this._controller.object), this._dragging || t.lastPointerDown.distanceTo(t.pointer) > A && this.startDrag(t), this._dragging))
return this.updateDrag(t), !0;
}
onPointerUp(t) {
this._dragging && (this._raycaster.setFromCamera(t.pointer, this._controller.object), this.endDrag()), this._draggable = null;
}
/**
* Set custom objects to raycast against during drag.
* Useful for constraining drag to a floor plane.
*
* @param targets Objects to raycast against, or null to use scene objects
*/
setDragRaycastTargets(t) {
this._dragRaycastTargets = t;
}
// ============ Private Methods ============
startDrag(t) {
var s, i;
const e = this.getDragIntersect(t);
e && (this._dragStart.copy(e.point), this._dragCurrent.copy(e.point), this._dragEnd.copy(e.point), this._dragDelta.set(0, 0, 0), (i = (s = this._draggable) == null ? void 0 : s.onDragStart) == null || i.call(s, this.createDragEvent()), this._dragging = !0, this._controller.enabled = !1);
}
updateDrag(t) {
var s, i;
const e = this.getDragIntersect(t);
e && (this._dragCurrent.copy(e.point), this._dragEnd.copy(e.point), this._dragDelta.subVectors(this._dragCurrent, this._dragStart), (i = (s = this._draggable) == null ? void 0 : s.onDrag) == null || i.call(s, this.createDragEvent()));
}
endDrag() {
var e, s;
const t = this.createDragEvent();
(s = (e = this._draggable) == null ? void 0 : e.onDragEnd) == null || s.call(e, t), this._dragging = !1, this._controller.enabled = !0, this.resetDragState();
}
getDragIntersect(t) {
return this._dragRaycastTargets ? this._raycaster.intersectObjects(
this._dragRaycastTargets,
!0
)[0] || null : t.intersects[0] || null;
}
createDragEvent() {
return {
dragStart: this._dragStart.clone(),
dragCurrent: this._dragCurrent.clone(),
dragEnd: this._dragEnd.clone(),
dragDelta: this._dragDelta.clone()
};
}
resetDragState() {
this._dragStart.set(0, 0, 0), this._dragCurrent.set(0, 0, 0), this._dragEnd.set(0, 0, 0), this._dragDelta.set(0, 0, 0);
}
}
const k = 1e-3;
class X {
constructor(t, e) {
o(this, "_scene");
o(this, "_controller");
o(this, "_canvas");
// Tool management
o(this, "_tools");
o(this, "_activeTools", /* @__PURE__ */ new Map());
o(this, "_sortedActiveTools", []);
// Shared selection state
o(this, "_selectionState");
// Raycasting (shared, computed once per event)
o(this, "_raycaster");
o(this, "_pointer");
o(this, "_productLayerMask");
o(this, "_uiLayerMask");
// Pointer state
o(this, "_pointerPrimaryDown", !1);
o(this, "_pointerMiddleDown", !1);
o(this, "_pointerSecondaryDown", !1);
o(this, "_lastPointerDown");
// Bound event handlers (for cleanup)
o(this, "_boundPointerMove");
o(this, "_boundPointerDown");
o(this, "_boundPointerUp");
o(this, "_boundWheel");
this._scene = t, this._controller = e, this._canvas = e.domElement, this._selectionState = new z(), this._raycaster = new P(), this._pointer = new f(), this._lastPointerDown = new f(), this._productLayerMask = new m(), this._productLayerMask.set(Math.log2(_)), this._uiLayerMask = new m(), this._uiLayerMask.set(Math.log2(d)), this._tools = /* @__PURE__ */ new Map([
[
"hover",
new C()
],
[
"select",
new M(this._selectionState)
],
[
"transform",
new L(t, e, this._selectionState)
],
[
"drag",
new j(e)
]
]), this._boundPointerMove = this.onPointerMove.bind(this), this._boundPointerDown = this.onPointerDown.bind(this), this._boundPointerUp = this.onPointerUp.bind(this), this._boundWheel = this.onWheel.bind(this), this._canvas.addEventListener("pointermove", this._boundPointerMove), this._canvas.addEventListener("pointerdown", this._boundPointerDown), this._canvas.addEventListener("pointerup", this._boundPointerUp), this._canvas.addEventListener("wheel", this._boundWheel);
}
get selectionState() {
return this._selectionState;
}
/**
* Enable a tool by type.
*/
enableTool(t) {
var s;
const e = this._tools.get(t);
e && (this._activeTools.has(t) || (this._activeTools.set(t, e), this.updateSortedTools(), (s = e.onActivate) == null || s.call(e)));
}
/**
* Disable an active tool by type.
*/
disableTool(t) {
var s;
const e = this._activeTools.get(t);
e && ((s = e.onDeactivate) == null || s.call(e), this._activeTools.delete(t), this.updateSortedTools());
}
/**
* Check if a tool is currently enabled.
*/
isToolEnabled(t) {
return this._activeTools.has(t);
}
/**
* Get a tool by type.
*/
getTool(t) {
return this._tools.get(t);
}
/**
* Get all currently active tools.
*/
getActiveTools() {
return [...this._sortedActiveTools];
}
/**
* Dispose of the toolbox and clean up resources.
*/
dispose() {
var t;
for (const e of this._activeTools.values())
(t = e.onDeactivate) == null || t.call(e);
this._activeTools.clear(), this._tools.clear(), this._sortedActiveTools = [], this._canvas.removeEventListener("pointermove", this._boundPointerMove), this._canvas.removeEventListener("pointerdown", this._boundPointerDown), this._canvas.removeEventListener("pointerup", this._boundPointerUp), this._canvas.removeEventListener("wheel", this._boundWheel), this._selectionState.dispose();
}
// ============ Event Handlers ============
onPointerMove(t) {
var s;
this.updatePointer(t);
const e = this.createPointerContext(t);
for (const i of this._sortedActiveTools)
if ((s = i.onPointerMove) == null ? void 0 : s.call(i, e)) break;
}
onPointerDown(t) {
var s;
this.updatePointerState(t, !0), this.updatePointer(t), this._lastPointerDown.copy(this._pointer);
const e = this.createPointerContext(t);
for (const i of this._sortedActiveTools)
if ((s = i.onPointerDown) == null ? void 0 : s.call(i, e)) break;
}
onPointerUp(t) {
var i, r;
this.updatePointer(t);
const e = this.createPointerContext(t), s = !this.pointerWasDragged();
for (const a of this._sortedActiveTools)
if ((i = a.onPointerUp) == null ? void 0 : i.call(a, e)) break;
if (s) {
for (const a of this._sortedActiveTools)
if ((r = a.onClick) == null ? void 0 : r.call(a, e)) break;
}
this.updatePointerState(t, !1);
}
onWheel(t) {
var s;
const e = this.createWheelContext(t);
for (const i of this._sortedActiveTools)
if ((s = i.onWheel) == null ? void 0 : s.call(i, e)) break;
}
// ============ Context Creation ============
createPointerContext(t) {
const e = this.raycast();
return {
event: t,
pointer: this._pointer.clone(),
intersects: e,
modelIntersects: this.filterIntersectsByLayer(
e,
_
),
uiIntersects: this.filterIntersectsByLayer(
e,
d
),
pointerPrimaryDown: this._pointerPrimaryDown,
pointerMiddleDown: this._pointerMiddleDown,
pointerSecondaryDown: this._pointerSecondaryDown,
lastPointerDown: this._lastPointerDown.clone()
};
}
createWheelContext(t) {
const e = this.raycast();
return {
event: t,
pointer: this._pointer.clone(),
intersects: e,
modelIntersects: this.filterIntersectsByLayer(
e,
_
),
uiIntersects: this.filterIntersectsByLayer(
e,
d
)
};
}
// ============ Helper Methods ============
updatePointer(t) {
this._pointer.x = t.offsetX / this._canvas.clientWidth * 2 - 1, this._pointer.y = -(t.offsetY / this._canvas.clientHeight) * 2 + 1, this._raycaster.setFromCamera(this._pointer, this._controller.object);
}
updatePointerState(t, e) {
switch (t.button) {
case 0:
this._pointerPrimaryDown = e;
break;
case 1:
this._pointerMiddleDown = e;
break;
case 2:
this._pointerSecondaryDown = e;
break;
}
}
raycast() {
this._raycaster.layers.mask = _ | d;
const t = this._scene.children.filter(
(e) => e.visible && "isMesh" in e && e.isMesh
);
return this._raycaster.intersectObjects(t, !0);
}
filterIntersectsByLayer(t, e) {
return t.filter(
(s) => (s.object.layers.mask & e) !== 0
);
}
updateSortedTools() {
this._sortedActiveTools = [...this._activeTools.values()].sort(
(t, e) => t.priority - e.priority
);
}
pointerWasDragged() {
return this._lastPointerDown.distanceTo(this._pointer) > k;
}
}
export {
M as DIVESelectTool,
L as DIVETransformTool,
j as DragTool,
C as HoverTool,
M as SelectTool,
z as SelectionState,
X as Toolbox,
L as TransformTool,
B as isSelectTool,
Y as isTransformTool
};