Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

@shopware-ag/dive

Package Overview
Dependencies
Maintainers
0
Versions
80
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@shopware-ag/dive - npm Package Compare versions

Comparing version 1.2.0 to 1.3.0

src/gizmo/Gizmo.ts

70

build/dive.d.ts

@@ -1,2 +0,2 @@

import { ShadowMapType, ToneMapping, WebGLRenderer, Scene, Camera, PerspectiveCamera, Vector3Like, Mesh, ColorRepresentation, Object3D } from 'three';
import { ShadowMapType, ToneMapping, WebGLRenderer, Scene, Camera, PerspectiveCamera, Vector3Like, Mesh, ColorRepresentation, Object3D, Intersection, Vector2, Raycaster, Vector3 } from 'three';
import { OrbitControls } from 'three/examples/jsm/Addons.js';

@@ -275,6 +275,5 @@

'PAYLOAD': {
map: Map<string, COMEntity>;
ids?: string[];
ids: string[];
};
'RETURN': Map<string, COMEntity>;
'RETURN': COMEntity[];
}

@@ -397,10 +396,58 @@

interface DIVEDraggable {
isDraggable: true;
onDragStart?: (e: DraggableEvent) => void;
onDrag?: (e: DraggableEvent) => void;
onDragEnd?: (e: DraggableEvent) => void;
}
interface DIVEHoverable {
isHoverable: true;
onPointerEnter?: (i: Intersection) => void;
onPointerOver?: (i: Intersection) => void;
onPointerLeave?: () => void;
}
type DraggableEvent = {
dragStart: Vector3;
dragCurrent: Vector3;
dragEnd: Vector3;
dragDelta: Vector3;
};
declare abstract class DIVEBaseTool {
protected name: string;
protected constructor();
readonly POINTER_DRAG_THRESHOLD: number;
name: string;
protected _canvas: HTMLElement;
protected _scene: DIVEScene;
protected _controller: DIVEOrbitControls;
protected _pointer: Vector2;
protected get _pointerAnyDown(): boolean;
protected _pointerPrimaryDown: boolean;
protected _pointerMiddleDown: boolean;
protected _pointerSecondaryDown: boolean;
protected _lastPointerDown: Vector2;
protected _lastPointerUp: Vector2;
protected _raycaster: Raycaster;
protected _intersects: Intersection[];
protected _hovered: (Object3D & DIVEHoverable) | null;
protected _dragging: boolean;
protected _dragStart: Vector3;
protected _dragCurrent: Vector3;
protected _dragEnd: Vector3;
protected _dragDelta: Vector3;
protected _draggable: DIVEDraggable | null;
protected _dragRaycastOnObjects: Object3D[] | null;
protected constructor(scene: DIVEScene, controller: DIVEOrbitControls);
Activate(): void;
Deactivate(): void;
onPointerDown(e: PointerEvent): void;
onDragStart(e: PointerEvent): void;
onPointerMove(e: PointerEvent): void;
onDrag(e: PointerEvent): void;
onPointerUp(e: PointerEvent): void;
onClick(e: PointerEvent): void;
onDragEnd(e: PointerEvent): void;
onWheel(e: WheelEvent): void;
protected raycast(objects?: Object3D[]): Intersection[];
private pointerWasDragged;
}

@@ -423,2 +470,3 @@

SetGizmoMode(mode: 'translate' | 'rotate' | 'scale'): void;
onPointerMove(e: PointerEvent): void;
onPointerDown(e: PointerEvent): void;

@@ -507,2 +555,11 @@ onPointerUp(e: PointerEvent): void;

/**
* Calculate the signed angle between two vectors. Only works when the vectors are on the same plane.
* @param vecB Start Vector
* @param vecA Target Vector
* @param planeNormal The vector's plane normal
* @returns Signed angle in radians
*/
declare function signedAngleTo(vecA: Vector3, vecB: Vector3, planeNormal: Vector3): number;
declare function toFixedExp(number: number, decimals?: number): string;

@@ -518,2 +575,3 @@

truncateExp: typeof truncateExp;
signedAngleTo: typeof signedAngleTo;
};

@@ -520,0 +578,0 @@

@@ -357,7 +357,9 @@ var __defProp = Object.defineProperty;

getObjects(payload) {
if (payload.ids.length === 0) return [];
const objects = [];
this.registered.forEach((object) => {
if (payload.ids && payload.ids.length > 0 && !payload.ids.includes(object.id)) return;
payload.map.set(object.id, object);
if (!payload.ids.includes(object.id)) return;
objects.push(object);
});
return payload.map;
return objects;
}

@@ -616,3 +618,2 @@ addObject(payload) {

DeleteLight(light) {
var _a;
if (light.id === void 0) {

@@ -627,5 +628,15 @@ console.warn("LightRoot.DeleteLight: light.id is undefined");

}
if ("isMoveable" in sceneObject) {
(_a = sceneObject.gizmo) == null ? void 0 : _a.detach();
}
const findScene = (object) => {
if (object.parent !== null) {
return findScene(object.parent);
}
;
return object;
};
const scene = findScene(sceneObject);
scene.children.find((object) => {
if ("isTransformControls" in object) {
object.detach();
}
});
this.remove(sceneObject);

@@ -787,3 +798,2 @@ }

DeleteModel(object) {
var _a;
if (object.id === void 0) {

@@ -798,5 +808,15 @@ console.warn(`ModelRoot.DeleteModel: object.id is undefined`);

}
if ("isMoveable" in sceneObject) {
(_a = sceneObject.gizmo) == null ? void 0 : _a.detach();
}
const findScene = (object2) => {
if (object2.parent !== null) {
return findScene(object2.parent);
}
;
return object2;
};
const scene = findScene(sceneObject);
scene.children.find((object2) => {
if ("isTransformControls" in object2) {
object2.detach();
}
});
this.remove(sceneObject);

@@ -1114,11 +1134,69 @@ }

// src/toolbox/select/SelectTool.ts
import { Raycaster as Raycaster2, Vector2 } from "three";
import { TransformControls } from "three/examples/jsm/Addons.js";
// src/interface/Selectable.ts
function isSelectable(object) {
return "isSelectable" in object;
}
// src/toolbox/BaseTool.ts
import { Raycaster as Raycaster2, Vector2, Vector3 as Vector32 } from "three";
// src/interface/Draggable.ts
var isDraggable = (object) => {
return "isDraggable" in object;
};
var findDraggableInterface = (child) => {
if (child === void 0) return void 0;
if (child.parent === null) {
return void 0;
}
if (isDraggable(child)) {
return child;
}
return findDraggableInterface(child.parent);
};
// src/interface/Hoverable.ts
var isHoverable = (object) => {
return "isHoverable" in object;
};
var findHoverableInterface = (child) => {
if (child === void 0) return void 0;
if (child.parent === null) {
return void 0;
}
if (isHoverable(child)) {
return child;
}
return findHoverableInterface(child.parent);
};
// src/toolbox/BaseTool.ts
var DIVEBaseTool = class {
constructor() {
constructor(scene, controller) {
this.POINTER_DRAG_THRESHOLD = 1e-3;
this.name = "BaseTool";
this._canvas = controller.domElement;
this._scene = scene;
this._controller = controller;
this._pointer = new Vector2();
this._pointerPrimaryDown = false;
this._pointerMiddleDown = false;
this._pointerSecondaryDown = false;
this._lastPointerDown = new Vector2();
this._lastPointerUp = new Vector2();
this._raycaster = new Raycaster2();
this._raycaster.layers.mask = PRODUCT_LAYER_MASK | UI_LAYER_MASK;
this._intersects = [];
this._hovered = null;
this._dragging = false;
this._dragStart = new Vector32();
this._dragCurrent = new Vector32();
this._dragEnd = new Vector32();
this._dragDelta = new Vector32();
this._draggable = null;
this._dragRaycastOnObjects = null;
}
get _pointerAnyDown() {
return this._pointerPrimaryDown || this._pointerMiddleDown || this._pointerSecondaryDown;
}
Activate() {

@@ -1129,44 +1207,181 @@ }

onPointerDown(e) {
var _a;
switch (e.button) {
case 0:
this._pointerPrimaryDown = true;
break;
case 1:
this._pointerMiddleDown = true;
break;
case 2:
this._pointerSecondaryDown = true;
break;
}
this._lastPointerDown.copy(this._pointer);
this._draggable = findDraggableInterface((_a = this._intersects[0]) == null ? void 0 : _a.object) || null;
}
onDragStart(e) {
if (!this._draggable) return;
if (this._dragRaycastOnObjects !== null) {
this._intersects = this._raycaster.intersectObjects(this._dragRaycastOnObjects, true);
}
if (this._intersects.length === 0) return;
this._dragStart.copy(this._intersects[0].point.clone());
this._dragCurrent.copy(this._intersects[0].point.clone());
this._dragEnd.copy(this._dragStart.clone());
this._dragDelta.set(0, 0, 0);
if (this._draggable && this._draggable.onDragStart) {
this._draggable.onDragStart({
dragStart: this._dragStart,
dragCurrent: this._dragCurrent,
dragEnd: this._dragEnd,
dragDelta: this._dragDelta
});
this._dragging = true;
this._controller.enabled = false;
}
}
onPointerMove(e) {
var _a;
this._pointer.x = e.offsetX / this._canvas.clientWidth * 2 - 1;
this._pointer.y = -(e.offsetY / this._canvas.clientHeight) * 2 + 1;
this._raycaster.setFromCamera(this._pointer, this._controller.object);
this._intersects = this.raycast(this._scene.children);
const hoverable = findHoverableInterface((_a = this._intersects[0]) == null ? void 0 : _a.object);
if (this._intersects[0] && hoverable) {
if (!this._hovered) {
if (hoverable.onPointerEnter) hoverable.onPointerEnter(this._intersects[0]);
this._hovered = hoverable;
return;
}
if (this._hovered.uuid !== hoverable.uuid) {
if (this._hovered.onPointerLeave) this._hovered.onPointerLeave();
if (hoverable.onPointerEnter) hoverable.onPointerEnter(this._intersects[0]);
this._hovered = hoverable;
return;
}
if (hoverable.onPointerOver) hoverable.onPointerOver(this._intersects[0]);
this._hovered = hoverable;
} else {
if (this._hovered) {
if (this._hovered.onPointerLeave) this._hovered.onPointerLeave();
}
this._hovered = null;
}
if (this._pointerAnyDown) {
if (!this._dragging) {
this.onDragStart(e);
}
this.onDrag(e);
}
}
onDrag(e) {
if (this._dragRaycastOnObjects !== null) {
this._intersects = this._raycaster.intersectObjects(this._dragRaycastOnObjects, true);
}
const intersect = this._intersects[0];
if (!intersect) return;
this._dragCurrent.copy(intersect.point.clone());
this._dragEnd.copy(intersect.point.clone());
this._dragDelta.subVectors(this._dragCurrent.clone(), this._dragStart.clone());
if (this._draggable && this._draggable.onDrag) {
this._draggable.onDrag({
dragStart: this._dragStart,
dragCurrent: this._dragCurrent,
dragEnd: this._dragEnd,
dragDelta: this._dragDelta
});
}
}
onPointerUp(e) {
if (this.pointerWasDragged() || this._dragging) {
if (this._draggable) {
this.onDragEnd(e);
}
} else {
this.onClick(e);
}
switch (e.button) {
case 0:
this._pointerPrimaryDown = false;
break;
case 1:
this._pointerMiddleDown = false;
break;
case 2:
this._pointerSecondaryDown = false;
break;
}
this._lastPointerUp.copy(this._pointer);
}
onClick(e) {
}
onDragEnd(e) {
const intersect = this._intersects[0];
if (intersect) {
this._dragEnd.copy(intersect.point.clone());
this._dragCurrent.copy(intersect.point.clone());
this._dragDelta.subVectors(this._dragCurrent.clone(), this._dragStart.clone());
}
if (this._draggable && this._draggable.onDragEnd) {
this._draggable.onDragEnd({
dragStart: this._dragStart,
dragCurrent: this._dragCurrent,
dragEnd: this._dragEnd,
dragDelta: this._dragDelta
});
}
this._draggable = null;
this._dragging = false;
this._dragStart.set(0, 0, 0);
this._dragCurrent.set(0, 0, 0);
this._dragEnd.set(0, 0, 0);
this._dragDelta.set(0, 0, 0);
this._controller.enabled = true;
}
onWheel(e) {
}
raycast(objects) {
if (objects !== void 0) return this._raycaster.intersectObjects(objects, true);
return this._raycaster.intersectObjects(this._scene.children, true);
}
pointerWasDragged() {
return this._lastPointerDown.distanceTo(this._pointer) > this.POINTER_DRAG_THRESHOLD;
}
};
// src/toolbox/transform/TransformTool.ts
import { TransformControls } from "three/examples/jsm/Addons";
var DIVETransformTool = class extends DIVEBaseTool {
constructor(scene, controller) {
super(scene, controller);
this.name = "DIVETransformTool";
this._gizmo = new TransformControls(this._controller.object, this._controller.domElement);
this._gizmo.mode = "translate";
scene.add(this._gizmo);
}
Activate() {
}
SetGizmoMode(mode) {
this._gizmo.mode = mode;
}
// public onPointerDown(e: PointerEvent): void {
// super.onPointerDown(e);
// // if (this._hovered) {
// // this._dragRaycastOnObjects = this._gizmo.gizmoPlane.children;
// // }
// }
// protected raycast(): Intersection[] {
// return super.raycast(this._gizmo.gizmoNode.children);
// }
};
// src/toolbox/select/SelectTool.ts
var DIVESelectTool = class extends DIVEBaseTool {
var DIVESelectTool = class extends DIVETransformTool {
constructor(scene, controller) {
super();
super(scene, controller);
this.name = "SelectTool";
this.canvas = controller.domElement;
this.scene = scene;
this.controller = controller;
this.raycaster = new Raycaster2();
this.raycaster.layers.mask = PRODUCT_LAYER_MASK | HELPER_LAYER_MASK;
this.gizmo = new TransformControls(this.controller.object, this.canvas);
this.gizmo.layers.mask = UI_LAYER_MASK;
this.gizmo.getRaycaster().layers.mask = UI_LAYER_MASK & this.controller.object.layers.mask;
this.gizmo.traverse((child) => {
child.layers.mask = UI_LAYER_MASK;
});
this.gizmo.addEventListener("objectChange", () => {
if (!this.gizmo.object) return;
if (!("onMove" in this.gizmo.object)) return;
if (typeof this.gizmo.object.onMove !== "function") return;
this.gizmo.object.onMove();
});
this.controller.object.onSetCameraLayer = (mask) => {
this.gizmo.getRaycaster().layers.mask = UI_LAYER_MASK & mask;
};
this.gizmo.addEventListener("dragging-changed", function(event) {
controller.enabled = !event.value;
});
this.scene.add(this.gizmo);
}
Activate() {
}
SetGizmoMode(mode) {
this.gizmo.setMode(mode);
}
Select(selectable) {

@@ -1176,4 +1391,3 @@ if (selectable.onSelect) selectable.onSelect();

const movable = selectable;
movable.gizmo = this.gizmo;
this.gizmo.attach(movable);
this._gizmo.attach(movable);
}

@@ -1183,14 +1397,18 @@ }

if (selectable.onDeselect) selectable.onDeselect();
if ("isMoveable" in selectable) selectable.gizmo = null;
this.gizmo.detach();
this._gizmo.detach();
}
onPointerUp(e) {
const pointerPos = new Vector2(e.offsetX / this.canvas.clientWidth * 2 - 1, e.offsetY / this.canvas.clientHeight * -2 + 1);
this.raycaster.setFromCamera(pointerPos, this.controller.object);
const first = this.raycastFirst();
onClick(e) {
super.onClick(e);
const first = this._raycaster.intersectObjects(this._scene.Root.children, true)[0];
const selectable = this.findSelectableInterface(first == null ? void 0 : first.object);
if (!first || !selectable) {
if (this.gizmo.object) this.Deselect(this.gizmo.object);
if (this._gizmo.object) {
this.Deselect(this._gizmo.object);
}
return;
}
if (this._gizmo.object) {
if (this._gizmo.object.uuid === selectable.uuid) return;
this.Deselect(this._gizmo.object);
}
this.Select(selectable);

@@ -1203,3 +1421,3 @@ }

}
if ("isSelectable" in child) {
if (isSelectable(child)) {
return child;

@@ -1209,8 +1427,2 @@ }

}
raycastFirst() {
return this.raycastAll()[0];
}
raycastAll() {
return this.raycaster.intersectObjects(this.scene.Root.children, true);
}
};

@@ -1224,2 +1436,3 @@

this.selectTool = new DIVESelectTool(scene, controller);
controller.domElement.addEventListener("pointermove", this.onPointerMove.bind(this));
controller.domElement.addEventListener("pointerdown", this.onPointerDown.bind(this));

@@ -1229,2 +1442,3 @@ controller.domElement.addEventListener("pointerup", this.onPointerUp.bind(this));

this.removeListenersCallback = () => {
controller.domElement.removeEventListener("pointermove", this.onPointerMove.bind(this));
controller.domElement.removeEventListener("pointerdown", this.onPointerDown.bind(this));

@@ -1259,2 +1473,5 @@ controller.domElement.removeEventListener("pointerup", this.onPointerUp.bind(this));

}
onPointerMove(e) {
this.activeTool.onPointerMove(e);
}
onPointerDown(e) {

@@ -1410,2 +1627,7 @@ this.activeTool.onPointerDown(e);

// src/math/signedAngleTo/signedAngleTo.ts
function signedAngleTo(vecA, vecB, planeNormal) {
return Math.atan2(vecA.clone().cross(vecB).dot(planeNormal), vecB.clone().dot(vecA));
}
// src/math/toFixed/toFixedExp.ts

@@ -1429,3 +1651,4 @@ function toFixedExp(number, decimals = 0) {

toFixedExp,
truncateExp
truncateExp,
signedAngleTo
};

@@ -1506,2 +1729,7 @@

this.renderer.StartRenderer(this.scene, this.perspectiveCamera);
window.DIVE = {
PrintScene: () => {
console.log(this.scene);
}
};
}

@@ -1508,0 +1736,0 @@ // methods

2

package.json
{
"name": "@shopware-ag/dive",
"version": "1.2.0",
"version": "1.3.0",
"description": "Shopware Spatial Framework",

@@ -5,0 +5,0 @@ "type": "module",

@@ -163,2 +163,4 @@ import DIVE, { DIVESettings } from '../dive.ts';

expect(dive).toBeDefined();
expect((window as any).DIVE.PrintScene).toBeDefined();
expect(() => (window as any).DIVE.PrintScene()).not.toThrow();
});

@@ -165,0 +167,0 @@

@@ -513,9 +513,6 @@ import DIVECommunication from '../Communication';

let map = new Map([['test0', mock0], ['test1', mock1]]);
const successWithoutIds = testCom.PerformAction('GET_OBJECTS', { ids: [] });
expect(Array.from(successWithoutIds.values())).toStrictEqual([]);
const successWithoutIds = testCom.PerformAction('GET_OBJECTS', { map });
expect(successWithoutIds).toStrictEqual(map);
map = new Map();
const successWithIds = testCom.PerformAction('GET_OBJECTS', { map, ids: ['test1'] });
const successWithIds = testCom.PerformAction('GET_OBJECTS', { ids: ['test1'] });
expect(Array.from(successWithIds.values())).toStrictEqual([{ entityType: "pov", id: "test1", position: { x: 0, y: 0, z: 0 }, target: { x: 0, y: 0, z: 0 } }]);

@@ -522,0 +519,0 @@ });

import { COMEntity } from "../../types.ts";
export default interface GET_OBJECTS {
'PAYLOAD': { map: Map<string, COMEntity>, ids?: string[] },
'RETURN': Map<string, COMEntity>,
'PAYLOAD': { ids: string[] },
'RETURN': COMEntity[],
};

@@ -213,8 +213,11 @@ import { Color, MeshStandardMaterial, MathUtils } from "three";

private getObjects(payload: Actions['GET_OBJECTS']['PAYLOAD']): Actions['GET_OBJECTS']['RETURN'] {
if (payload.ids.length === 0) return [];
const objects: COMEntity[] = [];
this.registered.forEach((object) => {
if (payload.ids && payload.ids.length > 0 && !payload.ids.includes(object.id)) return;
payload.map.set(object.id, object);
if (!payload.ids.includes(object.id)) return;
objects.push(object);
});
return payload.map;
return objects;
}

@@ -221,0 +224,0 @@

@@ -166,2 +166,9 @@ import { Vector4 } from "three";

this.renderer.StartRenderer(this.scene, this.perspectiveCamera);
// eslint-disable-next-line @typescript-eslint/no-explicit-any
(window as any).DIVE = {
PrintScene: () => {
console.log(this.scene);
},
}
}

@@ -168,0 +175,0 @@

@@ -0,1 +1,2 @@

import { type Object3D } from 'three';
import * as Moveable_DEF from '../Moveable';

@@ -5,2 +6,4 @@ import * as Rotatable_DEF from '../Rotatable';

import * as Selectable_DEF from '../Selectable';
import * as Draggable_DEF from '../Draggable';
import * as Hoverable_DEF from '../Hoverable';

@@ -13,3 +16,56 @@ describe('interfaces', () => {

expect(Selectable_DEF).toBeDefined();
expect(Draggable_DEF).toBeDefined();
expect(Hoverable_DEF).toBeDefined();
});
it('should identify Selectable', () => {
const Selectable = { isSelectable: true };
expect(Selectable_DEF.isSelectable(Selectable as unknown as Object3D)).toBe(true);
});
it('should identify Draggable', () => {
const Draggable = { isDraggable: true };
expect(Draggable_DEF.isDraggable(Draggable as unknown as Object3D)).toBe(true);
});
it('should find Draggable', () => {
let Draggable = {
isDraggable: true,
} as unknown as Object3D & Draggable_DEF.DIVEDraggable;
expect(Draggable_DEF.findDraggableInterface(Draggable as unknown as Object3D)).toBe(Draggable);
let parent = {
isDraggable: true,
}
Draggable = {
parent: parent,
} as unknown as Object3D & Draggable_DEF.DIVEDraggable;
expect(Draggable_DEF.findDraggableInterface(Draggable as unknown as Object3D)).toBe(parent);
Draggable = { isDraggable: true, parent: null } as unknown as Object3D & Draggable_DEF.DIVEDraggable;
expect(Draggable_DEF.findDraggableInterface(Draggable as unknown as Object3D)).toBe(undefined);
});
it('should identify Hoverable', () => {
const Hoverable = { isHoverable: true };
expect(Hoverable_DEF.isHoverable(Hoverable as unknown as Object3D)).toBe(true);
});
it('should find Hoverable', () => {
let Hoverable = {
isHoverable: true,
} as unknown as Object3D & Hoverable_DEF.DIVEHoverable;
expect(Hoverable_DEF.findHoverableInterface(Hoverable as unknown as Object3D)).toBe(Hoverable);
let parent = {
isHoverable: true,
}
Hoverable = {
parent: parent,
} as unknown as Object3D & Hoverable_DEF.DIVEHoverable;
expect(Hoverable_DEF.findHoverableInterface(Hoverable as unknown as Object3D)).toBe(parent);
Hoverable = { isHoverable: true, parent: null } as unknown as Object3D & Hoverable_DEF.DIVEHoverable;
expect(Hoverable_DEF.findHoverableInterface(Hoverable as unknown as Object3D)).toBe(undefined);
});
});

@@ -1,2 +0,1 @@

import type { TransformControls } from "three/examples/jsm/Addons.js";

@@ -11,4 +10,3 @@ /**

isMoveable: true;
gizmo: TransformControls | null;
onMove?: () => void;
}

@@ -7,2 +7,4 @@ /**

import { Object3D } from "three";
export interface DIVESelectable {

@@ -12,2 +14,6 @@ isSelectable: true;

onDeselect?: () => void;
}
export function isSelectable(object: Object3D): object is Object3D & DIVESelectable {
return 'isSelectable' in object;
}
import ceilExp from "./ceil/ceilExp.ts";
import floorExp from "./floor/floorExp.ts";
import roundExp from "./round/roundExp.ts";
import signedAngleTo from "./signedAngleTo/signedAngleTo.ts";
import toFixedExp from "./toFixed/toFixedExp.ts";

@@ -13,2 +14,3 @@ import truncateExp from "./truncate/truncateExp.ts";

truncateExp: typeof truncateExp;
signedAngleTo: typeof signedAngleTo;
} = {

@@ -20,2 +22,3 @@ ceilExp,

truncateExp,
signedAngleTo,
}

@@ -5,2 +5,3 @@ import { TransformControls } from 'three/examples/jsm/Addons';

import DIVELightRoot from '../LightRoot';
import type DIVEScene from '../../../Scene';

@@ -119,2 +120,13 @@ const mock_SetPosition = jest.fn();

const lightRoot = new DIVELightRoot();
const sceneParent = {
parent: null,
children: [
{
isTransformControls: true,
detach: jest.fn(),
}
],
}
lightRoot.parent = sceneParent as unknown as DIVEScene;
expect(() => lightRoot.DeleteLight({ id: undefined })).not.toThrow();

@@ -133,5 +145,2 @@

(lightRoot.children[0] as unknown as DIVEMoveable).isMoveable = true;
(lightRoot.children[0] as unknown as DIVEMoveable).gizmo = {
detach: jest.fn(),
} as unknown as TransformControls;
expect(() => lightRoot.DeleteLight({ id: 'test_id' })).not.toThrow();

@@ -138,0 +147,0 @@ expect(lightRoot.children).toHaveLength(0);

@@ -5,4 +5,5 @@ import { Color, Object3D } from "three";

import DIVEPointLight from "../../../light/PointLight.ts";
import type { DIVEMoveable } from "../../../interface/Moveable.ts";
import DIVESceneLight from "../../../light/SceneLight.ts";
import { type TransformControls } from "three/examples/jsm/Addons";
import type DIVEScene from "../../Scene.ts";

@@ -80,8 +81,21 @@ /**

if ('isMoveable' in sceneObject) {
(sceneObject as unknown as DIVEMoveable).gizmo?.detach();
// _______________________________________________________
// this is only neccessary due to using the old TransformControls instead of the new DIVEGizmo
const findScene = (object: Object3D): DIVEScene => {
if (object.parent !== null) {
return findScene(object.parent);
};
return object as DIVEScene;
}
const scene = findScene(sceneObject);
scene.children.find((object) => {
if ('isTransformControls' in object) {
(object as TransformControls).detach();
}
});
// _______________________________________________________
this.remove(sceneObject);
}
}
import DIVEModelRoot from '../ModelRoot';
import DIVECommunication from '../../../../com/Communication';
import { TransformControls } from 'three/examples/jsm/Addons';
import { DIVEMoveable } from '../../../../interface/Moveable';
import type DIVEScene from '../../../Scene';

@@ -157,2 +157,14 @@ const mock_LoadGLTF = jest.fn().mockResolvedValue({});

const modelRoot = new DIVEModelRoot();
const sceneParent = {
parent: null,
children: [
{
isTransformControls: true,
detach: jest.fn(),
}
],
}
modelRoot.parent = sceneParent as unknown as DIVEScene;
expect(() => modelRoot.DeleteModel({ id: undefined })).not.toThrow();

@@ -172,17 +184,4 @@ expect(consoleWarnSpy).toHaveBeenCalled();

expect(() => modelRoot.DeleteModel({ id: 'test_id' })).not.toThrow();
await expect(() => modelRoot.UpdateModel({
id: 'test_id',
uri: 'not a real uri',
position: { x: 1, y: 2, z: 3 },
rotation: { x: 1, y: 2, z: 3 },
scale: { x: 1, y: 2, z: 3 },
})).not.toThrow();
(modelRoot.children[0] as unknown as DIVEMoveable).isMoveable = true;
(modelRoot.children[0] as unknown as DIVEMoveable).gizmo = {
detach: jest.fn(),
} as unknown as TransformControls;
expect(() => modelRoot.DeleteModel({ id: 'test_id' })).not.toThrow();
expect(modelRoot.children).toHaveLength(0);
});
});

@@ -6,3 +6,4 @@ import { Object3D } from "three";

import DIVECommunication from "../../../com/Communication.ts";
import type { DIVEMoveable } from "../../../interface/Moveable.ts";
import { type TransformControls } from "three/examples/jsm/Addons";
import type DIVEScene from "../../Scene.ts";

@@ -69,6 +70,19 @@ /**

if ('isMoveable' in sceneObject) {
(sceneObject as unknown as DIVEMoveable).gizmo?.detach();
// _______________________________________________________
// this is only neccessary due to using the old TransformControls instead of the new DIVEGizmo
const findScene = (object: Object3D): DIVEScene => {
if (object.parent !== null) {
return findScene(object.parent);
};
return object as DIVEScene;
}
const scene = findScene(sceneObject);
scene.children.find((object) => {
if ('isTransformControls' in object) {
(object as TransformControls).detach();
}
});
// _______________________________________________________
this.remove(sceneObject);

@@ -75,0 +89,0 @@ }

@@ -27,2 +27,3 @@ import DIVEOrbitControls from '../../controls/OrbitControls';

const mock_onPointerDown = jest.fn();
const mock_onPointerMove = jest.fn();
const mock_onPointerUp = jest.fn();

@@ -37,2 +38,3 @@ const mock_onWheel = jest.fn();

this.onPointerDown = mock_onPointerDown;
this.onPointerMove = mock_onPointerMove;
this.onPointerUp = mock_onPointerUp;

@@ -89,2 +91,8 @@ this.onWheel = mock_onWheel;

it('should execute pointer move event on tool', () => {
const toolBox = new DIVEToolbox({} as DIVEScene, mockController);
toolBox.onPointerMove({ type: 'pointermove' } as PointerEvent);
expect(mock_onPointerMove).toHaveBeenCalledTimes(1);
});
it('should execute pointer up event on tool', () => {

@@ -91,0 +99,0 @@ const toolBox = new DIVEToolbox({} as DIVEScene, mockController);

@@ -0,7 +1,84 @@

import { Intersection, Object3D, Raycaster, Vector2, Vector3 } from "three";
import { PRODUCT_LAYER_MASK, UI_LAYER_MASK } from "../constant/VisibilityLayerMask";
import DIVEScene from "../scene/Scene";
import DIVEOrbitControls from "../controls/OrbitControls";
import { DIVEDraggable, findDraggableInterface } from "../interface/Draggable";
import { DIVEHoverable, findHoverableInterface } from "../interface/Hoverable";
export type DraggableEvent = {
dragStart: Vector3;
dragCurrent: Vector3;
dragEnd: Vector3;
dragDelta: Vector3;
}
/* eslint-disable @typescript-eslint/no-unused-vars */
export default abstract class DIVEBaseTool {
protected name: string;
readonly POINTER_DRAG_THRESHOLD: number = 0.001;
protected constructor() {
public name: string;
protected _canvas: HTMLElement;
protected _scene: DIVEScene;
protected _controller: DIVEOrbitControls;
// general pointer members
protected _pointer: Vector2;
protected get _pointerAnyDown(): boolean {
return this._pointerPrimaryDown
|| this._pointerMiddleDown
|| this._pointerSecondaryDown;
};
protected _pointerPrimaryDown: boolean;
protected _pointerMiddleDown: boolean;
protected _pointerSecondaryDown: boolean;
protected _lastPointerDown: Vector2;
protected _lastPointerUp: Vector2;
// raycast members
protected _raycaster: Raycaster;
protected _intersects: Intersection[];
// hovering members
protected _hovered: (Object3D & DIVEHoverable) | null;
// dragging members
protected _dragging: boolean;
protected _dragStart: Vector3;
protected _dragCurrent: Vector3;
protected _dragEnd: Vector3;
protected _dragDelta: Vector3;
protected _draggable: DIVEDraggable | null;
protected _dragRaycastOnObjects: Object3D[] | null;
protected constructor(scene: DIVEScene, controller: DIVEOrbitControls) {
this.name = "BaseTool";
this._canvas = controller.domElement;
this._scene = scene;
this._controller = controller;
this._pointer = new Vector2();
this._pointerPrimaryDown = false;
this._pointerMiddleDown = false;
this._pointerSecondaryDown = false;
this._lastPointerDown = new Vector2();
this._lastPointerUp = new Vector2();
this._raycaster = new Raycaster();
this._raycaster.layers.mask = PRODUCT_LAYER_MASK | UI_LAYER_MASK;
this._intersects = [];
this._hovered = null;
this._dragging = false;
this._dragStart = new Vector3();
this._dragCurrent = new Vector3();
this._dragEnd = new Vector3();
this._dragDelta = new Vector3();
this._draggable = null;
this._dragRaycastOnObjects = null;
}

@@ -13,7 +90,180 @@

public onPointerDown(e: PointerEvent): void { }
public onPointerDown(e: PointerEvent): void {
switch (e.button) {
case 0:
this._pointerPrimaryDown = true;
break;
case 1:
this._pointerMiddleDown = true;
break;
case 2:
this._pointerSecondaryDown = true;
break;
}
public onPointerUp(e: PointerEvent): void { }
this._lastPointerDown.copy(this._pointer);
this._draggable = findDraggableInterface(this._intersects[0]?.object) || null;
}
public onDragStart(e: PointerEvent): void {
if (!this._draggable) return;
if (this._dragRaycastOnObjects !== null) {
this._intersects = this._raycaster.intersectObjects(this._dragRaycastOnObjects, true);
}
if (this._intersects.length === 0) return;
this._dragStart.copy(this._intersects[0].point.clone());
this._dragCurrent.copy(this._intersects[0].point.clone());
this._dragEnd.copy(this._dragStart.clone());
this._dragDelta.set(0, 0, 0);
if (this._draggable && this._draggable.onDragStart) {
this._draggable.onDragStart({
dragStart: this._dragStart,
dragCurrent: this._dragCurrent,
dragEnd: this._dragEnd,
dragDelta: this._dragDelta,
});
this._dragging = true;
this._controller.enabled = false;
}
}
public onPointerMove(e: PointerEvent): void {
// update pointer
this._pointer.x = (e.offsetX / this._canvas.clientWidth) * 2 - 1;
this._pointer.y = -(e.offsetY / this._canvas.clientHeight) * 2 + 1;
// set raycaster
this._raycaster.setFromCamera(this._pointer, this._controller.object);
// refresh intersects
this._intersects = this.raycast(this._scene.children);
// handle hover
const hoverable = findHoverableInterface(this._intersects[0]?.object);
if (this._intersects[0] && hoverable) {
if (!this._hovered) {
if (hoverable.onPointerEnter) hoverable.onPointerEnter(this._intersects[0]);
this._hovered = hoverable;
return;
}
if (this._hovered.uuid !== hoverable.uuid) {
if (this._hovered.onPointerLeave) this._hovered.onPointerLeave();
if (hoverable.onPointerEnter) hoverable.onPointerEnter(this._intersects[0]);
this._hovered = hoverable;
return;
}
if (hoverable.onPointerOver) hoverable.onPointerOver(this._intersects[0]);
this._hovered = hoverable;
} else {
if (this._hovered) {
if (this._hovered.onPointerLeave) this._hovered.onPointerLeave();
}
this._hovered = null;
}
// handle drag
if (this._pointerAnyDown) {
if (!this._dragging) {
this.onDragStart(e);
}
this.onDrag(e);
}
}
public onDrag(e: PointerEvent): void {
if (this._dragRaycastOnObjects !== null) {
this._intersects = this._raycaster.intersectObjects(this._dragRaycastOnObjects, true);
}
const intersect = this._intersects[0];
if (!intersect) return;
this._dragCurrent.copy(intersect.point.clone());
this._dragEnd.copy(intersect.point.clone());
this._dragDelta.subVectors(this._dragCurrent.clone(), this._dragStart.clone());
if (this._draggable && this._draggable.onDrag) {
this._draggable.onDrag({
dragStart: this._dragStart,
dragCurrent: this._dragCurrent,
dragEnd: this._dragEnd,
dragDelta: this._dragDelta,
});
}
}
public onPointerUp(e: PointerEvent): void {
if (this.pointerWasDragged() || this._dragging) {
if (this._draggable) {
this.onDragEnd(e);
}
} else {
this.onClick(e);
}
switch (e.button) {
case 0:
this._pointerPrimaryDown = false;
break;
case 1:
this._pointerMiddleDown = false;
break;
case 2:
this._pointerSecondaryDown = false;
break;
}
this._lastPointerUp.copy(this._pointer);
}
public onClick(e: PointerEvent): void { }
public onDragEnd(e: PointerEvent): void {
const intersect = this._intersects[0];
if (intersect) {
this._dragEnd.copy(intersect.point.clone());
this._dragCurrent.copy(intersect.point.clone());
this._dragDelta.subVectors(this._dragCurrent.clone(), this._dragStart.clone());
}
if (this._draggable && this._draggable.onDragEnd) {
this._draggable.onDragEnd({
dragStart: this._dragStart,
dragCurrent: this._dragCurrent,
dragEnd: this._dragEnd,
dragDelta: this._dragDelta,
});
}
this._draggable = null;
this._dragging = false;
this._dragStart.set(0, 0, 0);
this._dragCurrent.set(0, 0, 0);
this._dragEnd.set(0, 0, 0);
this._dragDelta.set(0, 0, 0);
this._controller.enabled = true;
}
public onWheel(e: WheelEvent): void { }
protected raycast(objects?: Object3D[]): Intersection[] {
if (objects !== undefined) return this._raycaster.intersectObjects(objects, true);
return this._raycaster.intersectObjects(this._scene.children, true);
}
private pointerWasDragged(): boolean {
return this._lastPointerDown.distanceTo(this._pointer) > this.POINTER_DRAG_THRESHOLD;
}
}

@@ -6,3 +6,4 @@ import DIVESelectTool from '../SelectTool';

import DIVERenderer, { DIVERendererDefaultSettings } from '../../../renderer/Renderer';
import { DIVESelectable } from '../../../interface/Selectable';
import { DIVESelectable, isSelectable } from '../../../interface/Selectable';
import { type Object3D } from 'three';

@@ -15,10 +16,2 @@ jest.mock('../../../renderer/Renderer', () => {

jest.mock('../../../toolbox/BaseTool', () => {
return jest.fn(function (cam) {
this.cam = cam;
this.add = jest.fn();
return this;
});
});
jest.mock('../../../camera/PerspectiveCamera', () => {

@@ -67,2 +60,5 @@ return jest.fn(function () {

}),
Vector3: jest.fn(function () {
return this;
}),
Raycaster: jest.fn(function () {

@@ -126,4 +122,2 @@ this.setFromCamera = jest.fn();

expect(selectTool).toBeDefined();
expect(mockController.object.onSetCameraLayer).toBeDefined();
expect(() => mockController.object.onSetCameraLayer(0)).not.toThrow();
});

@@ -136,17 +130,19 @@

it('should execute onPointerUp without hit', () => {
it('should execute onClick without hit', () => {
const selectTool = new DIVESelectTool(mockScene, mockController);
expect(() => selectTool.onPointerUp({ offsetX: 0, offsetY: 0 } as PointerEvent)).not.toThrow();
selectTool['_gizmo'].object = {} as unknown as Object3D & DIVESelectable;
expect(() => selectTool.onClick({ offsetX: 0, offsetY: 0 } as PointerEvent)).not.toThrow();
});
it('should execute onPointerUp with hit', () => {
mock_intersectObjects.mockReturnValueOnce([{ object: { parent: { name: 'this is the test scene root!!!', parent: null } } }]);
it('should execute onClick with hit', () => {
mock_intersectObjects.mockReturnValueOnce([{ object: { uuid: 'test', parent: { name: 'this is the test scene root!!!', parent: null } } }]);
const selectTool = new DIVESelectTool(mockScene, mockController);
expect(() => selectTool.onPointerUp({ offsetX: 0, offsetY: 0 } as PointerEvent)).not.toThrow();
expect(() => selectTool.onClick({ offsetX: 0, offsetY: 0 } as PointerEvent)).not.toThrow();
});
it('should execute onPointerUp with ISelectable hit', () => {
it('should execute onClick with same ISelectable hit', () => {
const mock_onSelect = jest.fn();
mock_intersectObjects.mockReturnValueOnce([{
object: {

@@ -159,15 +155,42 @@ isSelectable: true,

},
uuid: 'test0',
},
}]);
const selectTool = new DIVESelectTool(mockScene, mockController);
expect(() => selectTool.onPointerUp({ offsetX: 0, offsetY: 0 } as PointerEvent)).not.toThrow();
expect(mock_onSelect).toHaveBeenCalledTimes(1);
selectTool['_gizmo'].object = {
isSelectable: true,
uuid: 'test0',
} as unknown as Object3D & DIVESelectable;
expect(() => selectTool.onClick({ offsetX: 0, offsetY: 0 } as PointerEvent)).not.toThrow();
});
it('should execute onPointerUp with IMovable hit', () => {
it('should execute onClick with ISelectable hit', () => {
const mock_onSelect = jest.fn();
mock_intersectObjects.mockReturnValueOnce([{
object: {
isSelectable: true,
onSelect: mock_onSelect,
parent: {
name: 'this is the test scene root!!!',
parent: null,
},
uuid: 'test0',
},
}]);
const selectTool = new DIVESelectTool(mockScene, mockController);
selectTool['_gizmo'].object = {
isSelectable: true,
uuid: 'test1',
} as unknown as Object3D & DIVESelectable;
expect(() => selectTool.onClick({ offsetX: 0, offsetY: 0 } as PointerEvent)).not.toThrow();
});
it('should execute onClick with IMovable hit', () => {
const mock_onSelect = jest.fn();
mock_intersectObjects.mockReturnValueOnce([{
object: {
isSelectable: true,
isMoveable: true,

@@ -182,11 +205,20 @@ onSelect: mock_onSelect,

const selectTool = new DIVESelectTool(mockScene, mockController);
expect(() => selectTool.onPointerUp({ offsetX: 0, offsetY: 0 } as PointerEvent)).not.toThrow();
expect(mock_attach).toHaveBeenCalledTimes(1);
expect(() => selectTool.onClick({ offsetX: 0, offsetY: 0 } as PointerEvent)).not.toThrow();
});
it('should deselect', () => {
it('should Select', () => {
const selectTool = new DIVESelectTool(mockScene, mockController);
const mock_onSelect = jest.fn();
expect(() => selectTool.Select({ isSelectable: true })).not.toThrow();
expect(() => selectTool.Select({ isMoveable: true, onSelect: mock_onSelect } as unknown as DIVESelectable)).not.toThrow();
expect(mock_onSelect).toHaveBeenCalledTimes(1);
});
it('should Deselect', () => {
const selectTool = new DIVESelectTool(mockScene, mockController);
const mock_onDeselect = jest.fn();
expect(() => selectTool.Deselect({ isSelectable: true })).not.toThrow();
expect(() => selectTool.Deselect({ isMoveable: true, onDeselect: jest.fn() } as unknown as DIVESelectable)).not.toThrow();
})
expect(() => selectTool.Deselect({ isMoveable: true, onDeselect: mock_onDeselect } as unknown as DIVESelectable)).not.toThrow();
expect(mock_onDeselect).toHaveBeenCalledTimes(1);
});

@@ -193,0 +225,0 @@ it('should set gizmo mode', () => {

@@ -1,9 +0,7 @@

import { Intersection, Object3D, Raycaster, Vector2 } from "three";
import { TransformControls } from "three/examples/jsm/Addons.js";
import DIVEBaseTool from "../BaseTool.ts";
import { DIVESelectable } from "../../interface/Selectable.ts";
import { Object3D } from "three";
import { DIVESelectable, isSelectable } from "../../interface/Selectable.ts";
import DIVEScene from "../../scene/Scene.ts";
import { HELPER_LAYER_MASK, PRODUCT_LAYER_MASK, UI_LAYER_MASK } from "../../constant/VisibilityLayerMask.ts";
import { DIVEMoveable } from "../../interface/Moveable.ts";
import DIVEOrbitControls from "../../controls/OrbitControls.ts";
import DIVETransformTool from "../transform/TransformTool.ts";

@@ -22,43 +20,7 @@ export interface DIVEObjectEventMap {

export default class DIVESelectTool extends DIVEBaseTool {
private canvas: HTMLElement;
private scene: DIVEScene;
private controller: DIVEOrbitControls;
private raycaster: Raycaster;
private gizmo: TransformControls;
export default class DIVESelectTool extends DIVETransformTool {
constructor(scene: DIVEScene, controller: DIVEOrbitControls) {
super();
super(scene, controller);
this.name = "SelectTool";
this.canvas = controller.domElement;
this.scene = scene;
this.controller = controller;
this.raycaster = new Raycaster();
this.raycaster.layers.mask = PRODUCT_LAYER_MASK | HELPER_LAYER_MASK;
this.gizmo = new TransformControls(this.controller.object, this.canvas);
this.gizmo.layers.mask = UI_LAYER_MASK;
this.gizmo.getRaycaster().layers.mask = UI_LAYER_MASK & this.controller.object.layers.mask;
this.gizmo.traverse((child) => {
child.layers.mask = UI_LAYER_MASK;
});
this.gizmo.addEventListener('objectChange', () => {
if (!this.gizmo.object) return;
if (!('onMove' in this.gizmo.object)) return;
if (typeof this.gizmo.object.onMove !== 'function') return;
this.gizmo.object.onMove();
});
this.controller.object.onSetCameraLayer = (mask: number) => {
this.gizmo.getRaycaster().layers.mask = UI_LAYER_MASK & mask;
};
this.gizmo.addEventListener('dragging-changed', function (event) {
controller.enabled = !event.value;
});
this.scene.add(this.gizmo);
}

@@ -68,5 +30,2 @@

public SetGizmoMode(mode: 'translate' | 'rotate' | 'scale'): void {
this.gizmo.setMode(mode);
}

@@ -78,4 +37,3 @@ public Select(selectable: DIVESelectable): void {

const movable = selectable as (Object3D & DIVESelectable & DIVEMoveable);
movable.gizmo = this.gizmo;
this.gizmo.attach(movable);
this._gizmo.attach(movable);
}

@@ -86,17 +44,29 @@ }

if (selectable.onDeselect) selectable.onDeselect();
if (('isMoveable' in selectable)) (selectable as unknown as DIVEMoveable).gizmo = null;
this.gizmo.detach();
this._gizmo.detach();
}
public onPointerUp(e: PointerEvent): void {
const pointerPos: Vector2 = new Vector2(e.offsetX / this.canvas.clientWidth * 2 - 1, e.offsetY / this.canvas.clientHeight * -2 + 1);
this.raycaster.setFromCamera(pointerPos, this.controller.object);
public onClick(e: PointerEvent): void {
super.onClick(e);
const first = this.raycastFirst();
const first = this._raycaster.intersectObjects(this._scene.Root.children, true)[0];
const selectable = this.findSelectableInterface(first?.object);
// if nothing is hit
if (!first || !selectable) {
if (this.gizmo.object) this.Deselect(this.gizmo.object as (Object3D & DIVESelectable));
if (this._gizmo.object) {
this.Deselect(this._gizmo.object as Object3D & DIVESelectable);
}
return;
}
if (this._gizmo.object) {
// do not reselect if the same object was clicked
if (this._gizmo.object.uuid === selectable.uuid) return;
// deselect previous object
this.Deselect(this._gizmo.object as (Object3D & DIVESelectable));
}
// select clicked object
this.Select(selectable);

@@ -113,16 +83,10 @@ }

if ('isSelectable' in child) {
return child as (Object3D & DIVESelectable);
if (isSelectable(child)) {
// in this case it is the Selectable
return child;
}
// search recursively in parent
return this.findSelectableInterface(child.parent);
}
private raycastFirst(): Intersection {
return this.raycastAll()[0];
}
private raycastAll(): Intersection[] {
return this.raycaster.intersectObjects(this.scene.Root.children, true);
}
}

@@ -24,2 +24,3 @@ import DIVEOrbitControls from "../controls/OrbitControls.ts";

controller.domElement.addEventListener('pointermove', this.onPointerMove.bind(this));
controller.domElement.addEventListener('pointerdown', this.onPointerDown.bind(this));

@@ -30,2 +31,3 @@ controller.domElement.addEventListener('pointerup', this.onPointerUp.bind(this));

this.removeListenersCallback = () => {
controller.domElement.removeEventListener('pointermove', this.onPointerMove.bind(this));
controller.domElement.removeEventListener('pointerdown', this.onPointerDown.bind(this));

@@ -67,2 +69,6 @@ controller.domElement.removeEventListener('pointerup', this.onPointerUp.bind(this));

public onPointerMove(e: PointerEvent): void {
this.activeTool.onPointerMove(e);
}
public onPointerDown(e: PointerEvent): void {

@@ -69,0 +75,0 @@ this.activeTool.onPointerDown(e);

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc