diff --git a/package-lock.json b/package-lock.json index 25c5359..88763df 100644 --- a/package-lock.json +++ b/package-lock.json @@ -27,7 +27,7 @@ "ngx-bootstrap": "^8.0.0", "rxjs": "~7.4.0", "three": "^0.138.0", - "three.interactive": "^1.2.1", + "three.interactive": "^1.3.0", "tslib": "^2.3.0", "zone.js": "~0.11.4" }, @@ -10829,10 +10829,10 @@ "integrity": "sha512-xM/WwUd53ClkbHFrftW29aIzMVvyhj4rSZmIVgn6hZ/kYB7aMjDD8FFy4VJamygXSrldJuOgEJuaI+E+8mt/KA==" }, "node_modules/three.interactive": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/three.interactive/-/three.interactive-1.2.1.tgz", - "integrity": "sha512-p+gLMZFHiq96CzemNADyTU4L+Qvsji7oxp6U0x05WsU5QciYzqQXdPOIoRyBHdoi8wXXVwUUOgIe6o4gd6MvVA==", - "dependencies": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/three.interactive/-/three.interactive-1.3.0.tgz", + "integrity": "sha512-gWsrEqYpGAbziduSTJhSqttt1v9Zcl/wQKYMXfsC3Wsnnsnj9anYpUNcr8Rw2Y72pylv2pEfakWw/P+DI2bhIA==", + "peerDependencies": { "three": "^0.138.0" } }, @@ -19523,12 +19523,10 @@ "integrity": "sha512-xM/WwUd53ClkbHFrftW29aIzMVvyhj4rSZmIVgn6hZ/kYB7aMjDD8FFy4VJamygXSrldJuOgEJuaI+E+8mt/KA==" }, "three.interactive": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/three.interactive/-/three.interactive-1.2.1.tgz", - "integrity": "sha512-p+gLMZFHiq96CzemNADyTU4L+Qvsji7oxp6U0x05WsU5QciYzqQXdPOIoRyBHdoi8wXXVwUUOgIe6o4gd6MvVA==", - "requires": { - "three": "^0.138.0" - } + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/three.interactive/-/three.interactive-1.3.0.tgz", + "integrity": "sha512-gWsrEqYpGAbziduSTJhSqttt1v9Zcl/wQKYMXfsC3Wsnnsnj9anYpUNcr8Rw2Y72pylv2pEfakWw/P+DI2bhIA==", + "requires": {} }, "through": { "version": "2.3.8", diff --git a/package.json b/package.json index f956a07..ca03fef 100644 --- a/package.json +++ b/package.json @@ -31,6 +31,7 @@ "ngx-bootstrap": "^8.0.0", "rxjs": "~7.4.0", "three": "^0.138.0", + "three.interactive": "^1.3.0", "tslib": "^2.3.0", "zone.js": "~0.11.4" }, diff --git a/src/app/shared/three-interactive.spec.ts b/src/app/shared/three-interactive.spec.ts deleted file mode 100644 index 56baf05..0000000 --- a/src/app/shared/three-interactive.spec.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { ThreeInteractive } from './three-interactive'; - -describe('ThreeInteractive', () => { - it('should create an instance', () => { - expect(new ThreeInteractive()).toBeTruthy(); - }); -}); diff --git a/src/app/shared/three-interactive.ts b/src/app/shared/three-interactive.ts deleted file mode 100644 index e37b066..0000000 --- a/src/app/shared/three-interactive.ts +++ /dev/null @@ -1,376 +0,0 @@ -/**! - * Taken from: https://github.com/markuslerner/THREE.Interactive/blob/master/src/index.ts - */ - -/**! - * MIT License - * - * Copyright (c) 2020 Markus Lerner - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -import { Raycaster, Vector2 } from 'three'; - -export class InteractiveObject { - target: THREE.Object3D; - name: string; - intersected: boolean; - wasIntersected: boolean = false; - distance: number; - constructor(target: THREE.Object3D, name: string) { - this.target = target; - this.name = name; - this.intersected = false; - this.distance = 0; - } -} - -export class InteractiveEvent { - type: string; - cancelBubble: boolean; - originalEvent: Event | null; - - // Dummy default values - coords: Vector2 = new Vector2(0, 0); - distance: number = 0; - intersected: boolean = false; - - constructor(type: string, originalEvent: Event | null = null) { - this.cancelBubble = false; - this.type = type; - this.originalEvent = originalEvent; - } - stopPropagation() { - this.cancelBubble = true; - } -} - -export class InteractionManager { - renderer: THREE.Renderer; - camera: THREE.Camera; - domElement: HTMLElement; - bindEventsOnBodyElement: boolean; - mouse: Vector2; - supportsPointerEvents: boolean; - interactiveObjects: InteractiveObject[]; - closestObject: InteractiveObject | null; - raycaster: THREE.Raycaster; - - treatTouchEventsAsMouseEvents: boolean; - - constructor( - renderer: THREE.Renderer, - camera: THREE.Camera, - domElement: HTMLElement, - dontBindEventsOnBody: boolean | undefined - ) { - this.renderer = renderer; - this.camera = camera; - this.domElement = domElement; - this.bindEventsOnBodyElement = true; - if (typeof dontBindEventsOnBody !== 'undefined' && dontBindEventsOnBody) { - this.bindEventsOnBodyElement = false; - } - - this.mouse = new Vector2(-1, 1); // top left default position - - this.supportsPointerEvents = !!window.PointerEvent; - - this.interactiveObjects = []; - this.closestObject = null; - - this.raycaster = new Raycaster(); - - domElement.addEventListener('click', this.onMouseClick); - - if (this.supportsPointerEvents) { - if (this.bindEventsOnBodyElement) { - domElement.ownerDocument.addEventListener( - 'pointermove', - this.onDocumentMouseMove - ); - } else { - domElement.addEventListener('pointermove', this.onDocumentMouseMove); - } - domElement.addEventListener('pointerdown', this.onMouseDown); - domElement.addEventListener('pointerup', this.onMouseUp); - } else { - if (this.bindEventsOnBodyElement) { - domElement.ownerDocument.addEventListener( - 'mousemove', - this.onDocumentMouseMove - ); - } else { - domElement.addEventListener('mousemove', this.onDocumentMouseMove); - } - domElement.addEventListener('mousedown', this.onMouseDown); - domElement.addEventListener('mouseup', this.onMouseUp); - domElement.addEventListener('touchstart', this.onTouchStart, { - passive: true, - }); - domElement.addEventListener('touchmove', this.onTouchMove, { - passive: true, - }); - domElement.addEventListener('touchend', this.onTouchEnd, { - passive: true, - }); - } - - this.treatTouchEventsAsMouseEvents = true; - } - - dispose = () => { - this.domElement.removeEventListener('click', this.onMouseClick); - - if (this.supportsPointerEvents) { - this.domElement.ownerDocument.removeEventListener( - 'pointermove', - this.onDocumentMouseMove - ); - this.domElement.removeEventListener('pointerdown', this.onMouseDown); - this.domElement.removeEventListener('pointerup', this.onMouseUp); - } else { - this.domElement.ownerDocument.removeEventListener( - 'mousemove', - this.onDocumentMouseMove - ); - this.domElement.removeEventListener('mousedown', this.onMouseDown); - this.domElement.removeEventListener('mouseup', this.onMouseUp); - this.domElement.removeEventListener('touchstart', this.onTouchStart); - this.domElement.removeEventListener('touchmove', this.onTouchMove); - this.domElement.removeEventListener('touchend', this.onTouchEnd); - } - }; - - add = (object: THREE.Object3D, childNames: string[] = []) => { - if (object) { - if (childNames.length > 0) { - childNames.forEach((name) => { - const o = object.getObjectByName(name); - if (o) { - const interactiveObject = new InteractiveObject(o, name); - this.interactiveObjects.push(interactiveObject); - } - }); - } else { - const interactiveObject = new InteractiveObject(object, object.name); - this.interactiveObjects.push(interactiveObject); - } - } - }; - - remove = (object: THREE.Object3D, childNames: string[] = []) => { - if (!object) return; - const filterSet = new Set( - childNames.length > 0 ? childNames : [object.name] - ); - - this.interactiveObjects = this.interactiveObjects.filter( - (o) => !filterSet.has(o.name) - ); - }; - - update = () => { - this.raycaster.setFromCamera(this.mouse, this.camera); - - // console.log( scene.children ); - - this.interactiveObjects.forEach((object) => { - if (object.target) this.checkIntersection(object); - }); - - this.interactiveObjects.sort(function (a, b) { - return a.distance - b.distance; - }); - - const newClosestObject = - this.interactiveObjects.find((object) => object.intersected) ?? null; - if (newClosestObject != this.closestObject) { - if (this.closestObject) { - const eventOutClosest = new InteractiveEvent('mouseout'); - this.dispatch(this.closestObject, eventOutClosest); - } - if (newClosestObject) { - const eventOverClosest = new InteractiveEvent('mouseover'); - this.dispatch(newClosestObject, eventOverClosest); - } - this.closestObject = newClosestObject; - } - - let eventLeave: InteractiveEvent; - this.interactiveObjects.forEach((object) => { - if (!object.intersected && object.wasIntersected) { - if (!eventLeave) { - eventLeave = new InteractiveEvent('mouseleave'); - } - this.dispatch(object, eventLeave); - } - }); - let eventEnter: InteractiveEvent; - this.interactiveObjects.forEach((object) => { - if (object.intersected && !object.wasIntersected) { - if (!eventEnter) { - eventEnter = new InteractiveEvent('mouseenter'); - } - this.dispatch(object, eventEnter); - } - }); - }; - - checkIntersection = (object: InteractiveObject) => { - const intersects = this.raycaster.intersectObjects([object.target], true); - - object.wasIntersected = object.intersected; - - if (intersects.length > 0) { - let distance = intersects[0].distance; - intersects.forEach((i) => { - if (i.distance < distance) { - distance = i.distance; - } - }); - object.intersected = true; - object.distance = distance; - } else { - object.intersected = false; - } - }; - - onDocumentMouseMove = (mouseEvent: MouseEvent | PointerEvent) => { - // event.preventDefault(); - - this.mapPositionToPoint(this.mouse, mouseEvent.clientX, mouseEvent.clientY); - - const event = new InteractiveEvent('mousemove', mouseEvent); - - this.interactiveObjects.forEach((object) => { - this.dispatch(object, event); - }); - }; - - onTouchMove = (touchEvent: TouchEvent) => { - // event.preventDefault(); - - this.mapPositionToPoint( - this.mouse, - touchEvent.touches[0].clientX, - touchEvent.touches[0].clientY - ); - - const event = new InteractiveEvent( - this.treatTouchEventsAsMouseEvents ? 'mousemove' : 'touchmove', - touchEvent - ); - - this.interactiveObjects.forEach((object) => { - this.dispatch(object, event); - }); - }; - - onMouseClick = (mouseEvent: MouseEvent) => { - this.update(); - - const event = new InteractiveEvent('click', mouseEvent); - - this.interactiveObjects.forEach((object) => { - if (object.intersected) { - this.dispatch(object, event); - } - }); - }; - - onMouseDown = (mouseEvent: MouseEvent | PointerEvent) => { - this.mapPositionToPoint(this.mouse, mouseEvent.clientX, mouseEvent.clientY); - - this.update(); - - const event = new InteractiveEvent('mousedown', mouseEvent); - - this.interactiveObjects.forEach((object) => { - if (object.intersected) { - this.dispatch(object, event); - } - }); - }; - - onTouchStart = (touchEvent: TouchEvent) => { - this.mapPositionToPoint( - this.mouse, - touchEvent.touches[0].clientX, - touchEvent.touches[0].clientY - ); - - this.update(); - - const event = new InteractiveEvent( - this.treatTouchEventsAsMouseEvents ? 'mousedown' : 'touchstart', - touchEvent - ); - - this.interactiveObjects.forEach((object) => { - if (object.intersected) { - this.dispatch(object, event); - } - }); - }; - - onMouseUp = (mouseEvent: MouseEvent | PointerEvent) => { - const event = new InteractiveEvent('mouseup', mouseEvent); - - this.interactiveObjects.forEach((object) => { - this.dispatch(object, event); - }); - }; - - onTouchEnd = (touchEvent: TouchEvent) => { - this.mapPositionToPoint( - this.mouse, - touchEvent.touches[0].clientX, - touchEvent.touches[0].clientY - ); - - this.update(); - - const event = new InteractiveEvent( - this.treatTouchEventsAsMouseEvents ? 'mouseup' : 'touchend', - touchEvent - ); - - this.interactiveObjects.forEach((object) => { - this.dispatch(object, event); - }); - }; - - dispatch = (object: InteractiveObject, event: InteractiveEvent) => { - if (object.target && !event.cancelBubble) { - event.coords = this.mouse; - event.distance = object.distance; - event.intersected = object.intersected; - object.target.dispatchEvent(event); - } - }; - - mapPositionToPoint = (point: Vector2, x: number, y: number) => { - const rect = this.renderer.domElement.getBoundingClientRect(); - - point.x = ((x - rect.left) / rect.width) * 2 - 1; - point.y = -((y - rect.top) / rect.height) * 2 + 1; - }; -} diff --git a/src/app/three-dview/three-dview.component.ts b/src/app/three-dview/three-dview.component.ts index 7f9f167..832e79b 100644 --- a/src/app/three-dview/three-dview.component.ts +++ b/src/app/three-dview/three-dview.component.ts @@ -15,7 +15,7 @@ import * as THREE from 'three'; import { PuzzleDirection } from '../shared/puzzle-direction'; import { PuzzleInfo } from '../shared/puzzle-info'; import { PuzzleType } from '../shared/puzzle-type'; -import { InteractionManager } from '../shared/three-interactive'; +import { InteractionManager } from 'three.interactive'; @Component({ selector: 'app-three-dview',