From 4ef245bbbfc04194f39600ba0d68efbaf88d75f4 Mon Sep 17 00:00:00 2001 From: Garrett Walker Date: Tue, 30 Jan 2024 22:40:50 -0600 Subject: [PATCH 01/14] new file: playground/src/components/Box.vue new file: playground/src/pages/raycaster/Propogation.vue * Started work on interactive Event Propogation playground example modified: src/components/TresCanvas.vue * Import and use `useEventStore` * defineEmits for all expected pointer events so we may emit propogated events off of the canvasa modified: src/composables/index.ts new file: src/composables/useEventStore/index.ts * Started work on an event store. I'm not sure this counts as a store just yet * Wired up majority of pointer events * Added event propogation * Does not require using userData scene props or nodeOps for registering objects to scene modified: src/composables/useRaycaster/index.ts * Added new event listeners to power newly supported pointer events. We now check whole scene/children when calling intersectObjects. * Created new EventHooks for new events * Added `forceUpdate` function that allows for pointer-move events to work without mouth movement (good for when camera is moving but mouse is not) modified: src/core/nodeOps.ts * Added supported events to array so they don't get received as props * (temporarily) unhook current pointer event solution to iterate on useEventStore modified: src/utils/index.ts * Added Camel-to-kebab case util --- playground/components.d.ts | 2 + playground/src/components/Box.vue | 50 ++++++++++ .../src/pages/raycaster/Propogation.vue | 92 ++++++++++++++++++ playground/src/router.ts | 5 + src/components/TresCanvas.vue | 23 ++++- src/composables/index.ts | 3 +- src/composables/useEventStore/index.ts | 94 +++++++++++++++++++ src/composables/useRaycaster/index.ts | 73 ++++++++++++-- src/core/nodeOps.ts | 93 +++++++++++------- src/utils/index.ts | 6 ++ 10 files changed, 397 insertions(+), 44 deletions(-) create mode 100644 playground/src/components/Box.vue create mode 100644 playground/src/pages/raycaster/Propogation.vue create mode 100644 src/composables/useEventStore/index.ts diff --git a/playground/components.d.ts b/playground/components.d.ts index 33c0bcd9c..7452099e7 100644 --- a/playground/components.d.ts +++ b/playground/components.d.ts @@ -8,12 +8,14 @@ export {} declare module 'vue' { export interface GlobalComponents { AnimatedModel: typeof import('./src/components/AnimatedModel.vue')['default'] + Box: typeof import('./src/components/Box.vue')['default'] CameraOperator: typeof import('./src/components/CameraOperator.vue')['default'] Cameras: typeof import('./src/components/Cameras.vue')['default'] copy: typeof import('./src/components/TheBasic copy.vue')['default'] DanielTest: typeof import('./src/components/DanielTest.vue')['default'] DebugUI: typeof import('./src/components/DebugUI.vue')['default'] DeleteMe: typeof import('./src/components/DeleteMe.vue')['default'] + EventsPropogation: typeof import('./src/components/EventsPropogation.vue')['default'] FBXModels: typeof import('./src/components/FBXModels.vue')['default'] Gltf: typeof import('./src/components/gltf/index.vue')['default'] LocalOrbitControls: typeof import('./src/components/LocalOrbitControls.vue')['default'] diff --git a/playground/src/components/Box.vue b/playground/src/components/Box.vue new file mode 100644 index 000000000..627647224 --- /dev/null +++ b/playground/src/components/Box.vue @@ -0,0 +1,50 @@ + + + + diff --git a/playground/src/pages/raycaster/Propogation.vue b/playground/src/pages/raycaster/Propogation.vue new file mode 100644 index 000000000..4043a252a --- /dev/null +++ b/playground/src/pages/raycaster/Propogation.vue @@ -0,0 +1,92 @@ + + + diff --git a/playground/src/router.ts b/playground/src/router.ts index e14cfa4e8..932713cfe 100644 --- a/playground/src/router.ts +++ b/playground/src/router.ts @@ -51,6 +51,11 @@ const routes = [ name: 'Raycaster', component: () => import('./pages/raycaster/TheEvents.vue'), }, + { + path: '/raycaster/propogation', + name: 'Event Propogation', + component: () => import('./pages/raycaster/Propogation.vue'), + }, { path: '/misc/text-3d', name: 'Text3D', diff --git a/src/components/TresCanvas.vue b/src/components/TresCanvas.vue index f9d0bd0e8..937f654c8 100644 --- a/src/components/TresCanvas.vue +++ b/src/components/TresCanvas.vue @@ -25,6 +25,7 @@ import pkg from '../../package.json' import { useTresContextProvider, usePointerEventHandler, + useEventStore, useRenderLoop, type TresContext, } from '../composables' @@ -69,7 +70,22 @@ const props = withDefaults(defineProps(), { renderMode: 'always', }) -const emit = defineEmits(['render']) +// Define emits for Pointer events, pass `emit` into useEventStore so we can emit events off of TresCanvas +// Not sure of this solution, but you have to have emits defined on the component to emit them in vue +const emit = defineEmits([ + 'render', + 'click', + 'double-click', + 'context-menu', + 'pointer-move', + 'pointer-up', + 'pointer-down', + 'pointer-enter', + 'pointer-leave', + 'pointer-over', + 'pointer-out', + 'wheel', +]) const canvas = ref() @@ -124,6 +140,7 @@ const disableRender = computed(() => props.disableRender) const context = shallowRef(null) defineExpose({ context, dispose: () => dispose(context.value as TresContext, true) }) + onMounted(() => { const existingCanvas = canvas as Ref @@ -136,7 +153,9 @@ onMounted(() => { emit, }) - usePointerEventHandler(context.value) + // QUESTION: Maybe I should move the useEventStore logic into usePointerEventHandler? I seem to have created a similar pattern + // usePointerEventHandler({ scene: scene.value, contextParts: context.value }) + useEventStore(scene.value, context.value, emit) const { registerCamera, camera, cameras, deregisterCamera } = context.value diff --git a/src/composables/index.ts b/src/composables/index.ts index 2f53d5cff..141d0eacd 100644 --- a/src/composables/index.ts +++ b/src/composables/index.ts @@ -7,4 +7,5 @@ export * from './useRaycaster' export * from './useLogger' export * from './useSeek' export * from './usePointerEventHandler' -export * from './useTresContextProvider' \ No newline at end of file +export * from './useTresContextProvider' +export * from './useEventStore' \ No newline at end of file diff --git a/src/composables/useEventStore/index.ts b/src/composables/useEventStore/index.ts new file mode 100644 index 000000000..7d184f264 --- /dev/null +++ b/src/composables/useEventStore/index.ts @@ -0,0 +1,94 @@ +import { computed, ref, shallowRef } from 'vue' +import type { TresContext } from '../useTresContextProvider' +import { useRaycaster } from '../useRaycaster' +import { createGlobalState } from '@vueuse/core' +import { Intersection, Event, Object3D } from 'three' +import { hyphenate } from '../../utils' + +export const useEventStore = createGlobalState( + (scene, context, emit) => { + const _scene = shallowRef() + const _context = shallowRef() + + if(scene) _scene.value = scene + if(context) _context.value = context + + const sceneChildren = computed(() => { + return _scene.value ? _scene.value.children : [] + }) + + /** + * propogateEvent + * + * Propogates an event to all intersected objects and their parents + * @param eventName - The name of the event to propogate + * @param event - The event object + * @param intersects - An array of intersections + */ + function propogateEvent(eventName: string, event, intersects) { + // console.log(`propogateEvent: ${eventName}` , event, intersects); + + // Loop through all intersected objects and call their event handler + if (intersects.length) { + for(const intersection of intersects) { + const { object } = intersection + object[eventName]?.(intersection, event) + + // Todo: Flesh out event modifiers, blocking, stopPropagation, etc here and below + if ("blocks-pointer-events" in object) { + return; + } + + // Propogate the event up the parent chain before moving on to the next intersected object + let parent = object.parent + while(parent !== null) { + parent[eventName]?.(intersection, event) + + if ("blocks-pointer-events" in parent) { + return; + } + + parent = parent.parent + } + + // Convert eventName to kebab case to emit event from TresCanvas + let kebabEventName = hyphenate(eventName.slice(2)) + emit(kebabEventName, { intersection, event }) + } + } + } + + const { onClick, onDblClick, onContextMenu, onPointerMove, onPointerDown, onPointerUp, onWheel, forceUpdate } = useRaycaster(sceneChildren, _context.value) + + onPointerUp(({event, intersects}) => propogateEvent("onPointerUp", event, intersects)) + onPointerDown(({event, intersects}) => propogateEvent("onPointerDown", event, intersects)) + onClick(({event, intersects}) => propogateEvent("onClick", event, intersects)) + onDblClick(({event, intersects}) => propogateEvent("onDoubleClick", event, intersects)) + onContextMenu(({event, intersects}) => propogateEvent("onContextMenu", event, intersects)) + onWheel(({event, intersects}) => propogateEvent("onWheel", event, intersects)) + + + let previouslyIntersectedObject: Object3D | null + + onPointerMove(({ intersects, event }) => { + const firstObject = intersects?.[0]?.object + + if (previouslyIntersectedObject && previouslyIntersectedObject !== firstObject){ + propogateEvent("onPointerLeave", event, [{object: previouslyIntersectedObject}]) + propogateEvent("onPointerOut", event, [{object: previouslyIntersectedObject}]) + } + + if (firstObject) { + if (previouslyIntersectedObject !== firstObject) { + propogateEvent("onPointerEnter", event, intersects) + propogateEvent("onPointerOver", event, intersects) + } + propogateEvent("onPointerMove", event, intersects) + } + + previouslyIntersectedObject = firstObject || null + }) + + return { forceUpdate } + } +) \ No newline at end of file diff --git a/src/composables/useRaycaster/index.ts b/src/composables/useRaycaster/index.ts index d7cc83269..1168b4328 100644 --- a/src/composables/useRaycaster/index.ts +++ b/src/composables/useRaycaster/index.ts @@ -1,7 +1,7 @@ import { Vector2 } from 'three' import type { Object3D, Intersection } from 'three' import type { Ref } from 'vue' -import { computed, onUnmounted } from 'vue' +import { computed, onUnmounted, watchEffect } from 'vue' import type { EventHook } from '@vueuse/core' import { createEventHook, useElementBounding, usePointer } from '@vueuse/core' @@ -18,6 +18,11 @@ interface PointerClickEventPayload { event: PointerEvent } +interface WheelEventPayload { + intersects: Intersects + event: WheelEvent +} + export const useRaycaster = ( objects: Ref, ctx: TresContext, @@ -43,10 +48,10 @@ export const useRaycaster = ( ctx.raycaster.value.setFromCamera(new Vector2(x, y), ctx.camera.value) - return ctx.raycaster.value.intersectObjects(objects.value, false) + return ctx.raycaster.value.intersectObjects(objects.value, true) } - const getIntersects = (event?: PointerEvent | MouseEvent) => { + const getIntersects = (event?: PointerEvent | MouseEvent | WheelEvent) => { const pointerPosition = getRelativePointerPosition({ x: event?.clientX ?? x.value, y: event?.clientY ?? y.value, @@ -59,36 +64,84 @@ export const useRaycaster = ( const intersects = computed(() => getIntersects()) const eventHookClick = createEventHook() + const eventHookDblClick = createEventHook() const eventHookPointerMove = createEventHook() + const eventHookPointerUp = createEventHook() + const eventHookPointerDown = createEventHook() + const eventHookContextMenu = createEventHook() + const eventHookWheel = createEventHook() + - const triggerEventHook = (eventHook: EventHook, event: PointerEvent | MouseEvent) => { + const triggerEventHook = (eventHook: EventHook, event: PointerEvent | MouseEvent | WheelEvent) => { eventHook.trigger({ event, intersects: getIntersects(event) }) } + let previousPointerMoveEvent: PointerEvent | undefined = undefined; const onPointerMove = (event: PointerEvent) => { triggerEventHook(eventHookPointerMove, event) + previousPointerMoveEvent = event; } - // a click event is fired whenever a pointerdown happened after pointerup on the same object + const forceUpdate = () => { + if(previousPointerMoveEvent) + triggerEventHook(eventHookPointerMove, previousPointerMoveEvent); + } + // a click event is fired whenever a pointerdown happened after pointerup on the same object let mouseDownObject: Object3D | undefined = undefined const onPointerDown = (event: PointerEvent) => { mouseDownObject = getIntersects(event)[0]?.object + triggerEventHook(eventHookPointerDown, event) } + let previousClickObject: Object3D | undefined = undefined + let doubleClickConfirmed: boolean = false; + const onPointerUp = (event: MouseEvent) => { if (!(event instanceof PointerEvent)) return // prevents triggering twice on mobile devices - if (mouseDownObject === getIntersects(event)[0]?.object) triggerEventHook(eventHookClick, event) + if (mouseDownObject === getIntersects(event)[0]?.object) { + // We clicked on the object, update the count + if(event.button === 0) { + // Left click + triggerEventHook(eventHookClick, event) + + if(previousClickObject === getIntersects(event)[0]?.object) { + // console.log("Double click confirmed"); + doubleClickConfirmed = true; + } else { + // console.log("Double click NOT confirmed"); + previousClickObject = getIntersects(event)[0]?.object; + doubleClickConfirmed = false; + } + }else if(event.button === 2) { + // Right click + triggerEventHook(eventHookContextMenu, event) + } + } + + triggerEventHook(eventHookPointerUp, event); + } + + const onDoubleClick = (event: MouseEvent) => { + if(doubleClickConfirmed) { + triggerEventHook(eventHookDblClick, event) + previousClickObject = undefined; + doubleClickConfirmed = false; + } } const onPointerLeave = (event: PointerEvent) => eventHookPointerMove.trigger({ event, intersects: [] }) + const onWheel = (event: WheelEvent) => eventHookWheel.trigger({ event, intersects: getIntersects(event) }) + canvas.value.addEventListener('pointerup', onPointerUp) canvas.value.addEventListener('pointerdown', onPointerDown) canvas.value.addEventListener('pointermove', onPointerMove) canvas.value.addEventListener('pointerleave', onPointerLeave) + canvas.value.addEventListener('dblclick', onDoubleClick); + canvas.value.addEventListener('wheel', onWheel); onUnmounted(() => { if (!canvas?.value) return @@ -96,11 +149,19 @@ export const useRaycaster = ( canvas.value.removeEventListener('pointerdown', onPointerDown) canvas.value.removeEventListener('pointermove', onPointerMove) canvas.value.removeEventListener('pointerleave', onPointerLeave) + canvas.value.removeEventListener('dblclick', onDoubleClick) + canvas.value.removeEventListener('wheel', onWheel) }) return { intersects, onClick: (fn: (value: PointerClickEventPayload) => void) => eventHookClick.on(fn).off, + onDblClick: (fn: (value: PointerClickEventPayload) => void) => eventHookDblClick.on(fn).off, + onContextMenu: (fn: (value: PointerClickEventPayload) => void) => eventHookContextMenu.on(fn).off, onPointerMove: (fn: (value: PointerMoveEventPayload) => void) => eventHookPointerMove.on(fn).off, + onPointerUp: (fn: (value: PointerMoveEventPayload) => void) => eventHookPointerUp.on(fn).off, + onPointerDown: (fn: (value: PointerMoveEventPayload) => void) => eventHookPointerDown.on(fn).off, + onWheel: (fn: (value: WheelEventPayload) => void) => eventHookWheel.on(fn).off, + forceUpdate, } } diff --git a/src/core/nodeOps.ts b/src/core/nodeOps.ts index c1def0f9a..2aa2d471b 100644 --- a/src/core/nodeOps.ts +++ b/src/core/nodeOps.ts @@ -17,9 +17,19 @@ const { logError } = useLogger() const supportedPointerEvents = [ 'onClick', + 'onContextMenu', 'onPointerMove', 'onPointerEnter', 'onPointerLeave', + 'onPointerOver', + 'onPointerOut', + 'onDoubleClick', + 'onPointerDown', + 'onPointerUp', + 'onPointerCancel', + 'onPointerMissed', + 'onLostPointerCapture', + 'onWheel', ] export function invalidateInstance(instance: TresObject) { @@ -46,7 +56,8 @@ export const nodeOps: RendererOptions = { let instance: TresObject | null if (tag === 'primitive') { - if (props?.object === undefined) logError('Tres primitives need a prop \'object\'') + if (props?.object === undefined) + logError('Tres primitives need a prop \'object\'') const object = props.object as TresObject name = object.type instance = Object.assign(object, { type: name, attach: props.attach }) @@ -54,7 +65,9 @@ export const nodeOps: RendererOptions = { else { const target = catalogue.value[name] if (!target) { - logError(`${name} is not defined on the THREE namespace. Use extend to add it to the catalog.`) + logError( + `${name} is not defined on the THREE namespace. Use extend to add it to the catalog.`, + ) } instance = new target(...props.args) } @@ -111,11 +124,11 @@ export const nodeOps: RendererOptions = { if (child?.isCamera) { registerCamera(child as unknown as Camera) } - if ( - child && supportedPointerEvents.some(eventName => child[eventName]) - ) { - registerObjectAtPointerEventHandler(child as Object3D) - } + // if ( + // child && supportedPointerEvents.some(eventName => child[eventName]) + // ) { + // registerObjectAtPointerEventHandler(child as Object3D) + // } } if (child?.isObject3D && parentObject?.isObject3D) { @@ -154,19 +167,18 @@ export const nodeOps: RendererOptions = { } } - const deregisterAtPointerEventHandlerIfRequired = (object: TresObject) => { - deregisterBlockingObjectAtPointerEventHandler(object as Object3D) - if ( - object && supportedPointerEvents.some(eventName => object[eventName]) - ) - deregisterObjectAtPointerEventHandler?.(object as Object3D) - } + // const deregisterAtPointerEventHandlerIfRequired = (object: TresObject) => { + // deregisterBlockingObjectAtPointerEventHandler(object as Object3D) + // if ( + // object && supportedPointerEvents.some(eventName => object[eventName]) + // ) + // deregisterObjectAtPointerEventHandler?.(object as Object3D) + // } const deregisterCameraIfRequired = (object: Object3D) => { const deregisterCamera = node.__tres.root.deregisterCamera - if ((object as Camera).isCamera) - deregisterCamera?.(object as Camera) + if ((object as Camera).isCamera) deregisterCamera?.(object as Camera) } node.removeFromParent?.() @@ -174,12 +186,12 @@ export const nodeOps: RendererOptions = { node.traverse((child: Object3D) => { disposeMaterialsAndGeometries(child as TresObject) deregisterCameraIfRequired(child) - deregisterAtPointerEventHandlerIfRequired?.(child as TresObject) + // deregisterAtPointerEventHandlerIfRequired?.(child as TresObject) }) disposeMaterialsAndGeometries(node) deregisterCameraIfRequired(node as Object3D) - deregisterAtPointerEventHandlerIfRequired?.(node as TresObject) + // deregisterAtPointerEventHandlerIfRequired?.(node as TresObject) } invalidateInstance(node as TresObject) @@ -191,22 +203,25 @@ export const nodeOps: RendererOptions = { let key = prop if (node.__tres.root) { - const { - registerBlockingObjectAtPointerEventHandler, - deregisterBlockingObjectAtPointerEventHandler, - } = node.__tres.root - - if (node.isObject3D && key === 'blocks-pointer-events') { - if (nextValue || nextValue === '') - registerBlockingObjectAtPointerEventHandler(node as Object3D) - else - deregisterBlockingObjectAtPointerEventHandler(node as Object3D) - - return - } + // const { + // registerBlockingObjectAtPointerEventHandler, + // deregisterBlockingObjectAtPointerEventHandler, + // } = node.__tres.root } - - let finalKey = kebabToCamel(key) + if (node?.isObject3D && key === 'blocks-pointer-events') { + // if (nextValue || nextValue === '') + // registerBlockingObjectAtPointerEventHandler(node as Object3D) + // else + // deregisterBlockingObjectAtPointerEventHandler(node as Object3D) + + if (nextValue || nextValue === '') + node[key] = nextValue + else + delete node[key] + return + } + + let finalKey = kebabToCamel(key) let target = root?.[finalKey] if (key === 'args') { @@ -215,8 +230,15 @@ export const nodeOps: RendererOptions = { const args = nextValue ?? [] const instanceName = node.__tres.type || node.type - if (instanceName && prevArgs.length && !deepArrayEqual(prevArgs, args)) { - root = Object.assign(prevNode, new catalogue.value[instanceName](...nextValue)) + if ( + instanceName + && prevArgs.length + && !deepArrayEqual(prevArgs, args) + ) { + root = Object.assign( + prevNode, + new catalogue.value[instanceName](...nextValue), + ) } return } @@ -232,6 +254,7 @@ export const nodeOps: RendererOptions = { // Traverse pierced props (e.g. foo-bar=value => foo.bar = value) if (key.includes('-') && target === undefined) { + console.log(key) const chain = key.split('-') target = chain.reduce((acc, key) => acc[kebabToCamel(key)], root) key = chain.pop() as string diff --git a/src/utils/index.ts b/src/utils/index.ts index 6f457197d..9e4d48e05 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -38,6 +38,12 @@ export function kebabToCamel(str: string) { return str.replace(/-([a-z])/g, (_, c) => c.toUpperCase()) } +//CamelCase to kebab-case +const hyphenateRE = /\B([A-Z])/g +export function hyphenate(str: string) { + return str.replace(hyphenateRE, '-$1').toLowerCase() +} + export function makeMap(str: string, expectsLowerCase?: boolean): (key: string) => boolean { const map: Record = Object.create(null) const list: Array = str.split(',') From 1a491da00c55efdebc867d829e26f3e4c48a3b6d Mon Sep 17 00:00:00 2001 From: Garrett Walker Date: Tue, 30 Jan 2024 23:45:41 -0600 Subject: [PATCH 02/14] Support multiple event listeners, add support for .stop event modifier --- .../src/pages/raycaster/Propogation.vue | 2 +- src/composables/useEventStore/index.ts | 37 +++++++++++-------- 2 files changed, 23 insertions(+), 16 deletions(-) diff --git a/playground/src/pages/raycaster/Propogation.vue b/playground/src/pages/raycaster/Propogation.vue index 4043a252a..06367c2df 100644 --- a/playground/src/pages/raycaster/Propogation.vue +++ b/playground/src/pages/raycaster/Propogation.vue @@ -41,7 +41,7 @@ const gl = { - + diff --git a/src/composables/useEventStore/index.ts b/src/composables/useEventStore/index.ts index 7d184f264..3ab5cd6e1 100644 --- a/src/composables/useEventStore/index.ts +++ b/src/composables/useEventStore/index.ts @@ -17,6 +17,20 @@ export const useEventStore = createGlobalState( return _scene.value ? _scene.value.children : [] }) + function executeEventListeners(listeners: Function | Function[], intersection, event){ + // Components with multiple event listeners will have an array of functions + if (Array.isArray(listeners)) { + for (const listener of listeners) { + listener(intersection, event); + } + } + + // Single listener will be a function + if (typeof listeners === 'function') { + listeners(intersection, event); + } + } + /** * propogateEvent * @@ -26,28 +40,21 @@ export const useEventStore = createGlobalState( * @param intersects - An array of intersections */ function propogateEvent(eventName: string, event, intersects) { - // console.log(`propogateEvent: ${eventName}` , event, intersects); - + let stopPropagating = true; + // Loop through all intersected objects and call their event handler if (intersects.length) { for(const intersection of intersects) { + if (stopPropagating) return; + intersection.stopPropagation = () => stopPropagating = true; + const { object } = intersection - object[eventName]?.(intersection, event) + executeEventListeners(object[eventName], intersection, event) - // Todo: Flesh out event modifiers, blocking, stopPropagation, etc here and below - if ("blocks-pointer-events" in object) { - return; - } - // Propogate the event up the parent chain before moving on to the next intersected object let parent = object.parent - while(parent !== null) { - parent[eventName]?.(intersection, event) - - if ("blocks-pointer-events" in parent) { - return; - } - + while(parent !== null && !stopPropagating) { + executeEventListeners(parent[eventName], intersection, event) parent = parent.parent } From 580029df4025ea3ab030798c9d5ee28fadf0fc0b Mon Sep 17 00:00:00 2001 From: Garrett Walker Date: Tue, 30 Jan 2024 23:57:52 -0600 Subject: [PATCH 03/14] Set stopProgation variable to false by default, whoops --- src/composables/useEventStore/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/composables/useEventStore/index.ts b/src/composables/useEventStore/index.ts index 3ab5cd6e1..22953ba83 100644 --- a/src/composables/useEventStore/index.ts +++ b/src/composables/useEventStore/index.ts @@ -40,7 +40,7 @@ export const useEventStore = createGlobalState( * @param intersects - An array of intersections */ function propogateEvent(eventName: string, event, intersects) { - let stopPropagating = true; + let stopPropagating = false; // Loop through all intersected objects and call their event handler if (intersects.length) { From 56776ae22b866971098d6999eff592efbe2f8852 Mon Sep 17 00:00:00 2001 From: Garrett Walker Date: Thu, 1 Feb 2024 22:57:15 -0600 Subject: [PATCH 04/14] fix typo --- .../src/pages/raycaster/{Propogation.vue => Propagation.vue} | 5 ----- playground/src/router.ts | 4 ++-- 2 files changed, 2 insertions(+), 7 deletions(-) rename playground/src/pages/raycaster/{Propogation.vue => Propagation.vue} (94%) diff --git a/playground/src/pages/raycaster/Propogation.vue b/playground/src/pages/raycaster/Propagation.vue similarity index 94% rename from playground/src/pages/raycaster/Propogation.vue rename to playground/src/pages/raycaster/Propagation.vue index 06367c2df..e41081b78 100644 --- a/playground/src/pages/raycaster/Propogation.vue +++ b/playground/src/pages/raycaster/Propagation.vue @@ -1,10 +1,6 @@ diff --git a/playground/src/pages/raycaster/Propagation.vue b/playground/src/pages/raycaster/Propagation.vue index e41081b78..97f049d64 100644 --- a/playground/src/pages/raycaster/Propagation.vue +++ b/playground/src/pages/raycaster/Propagation.vue @@ -1,85 +1,185 @@