@pmndrs/pointer-events
Advanced tools
Comparing version 6.4.12 to 6.5.0
@@ -114,3 +114,3 @@ import { intersectPointerEventTargets } from './intersections/utils.js'; | ||
//intersect scene using the non captured pointers | ||
intersectPointerEventTargets(scene, this.nonCapturedPointers); | ||
intersectPointerEventTargets('pointer', scene, this.nonCapturedPointers); | ||
//finalize the intersection for the non captured pointers | ||
@@ -117,0 +117,0 @@ const nonCapturedPointerLength = this.nonCapturedPointers.length; |
@@ -45,3 +45,3 @@ import { BaseEvent, Face, Object3D, Quaternion, Ray, Vector2, Vector3 } from 'three'; | ||
get face(): Face | null | undefined; | ||
get faceIndex(): number | undefined; | ||
get faceIndex(): number | null | undefined; | ||
get uv(): Vector2 | undefined; | ||
@@ -48,0 +48,0 @@ get uv1(): Vector2 | undefined; |
@@ -101,8 +101,13 @@ import { Ray, Vector2, Vector3 } from 'three'; | ||
get ray() { | ||
if (this._ray == null) { | ||
this._ray = new Ray(); | ||
this._ray.origin.setFromMatrixPosition(this.camera.matrixWorld); | ||
this._ray.lookAt(this.point); | ||
if (this._ray != null) { | ||
return this._ray; | ||
} | ||
return this._ray; | ||
switch (this.intersection.details.type) { | ||
case 'screen-ray': | ||
case 'ray': | ||
case 'sphere': | ||
return (this._ray = new Ray(this.intersection.pointerPosition, new Vector3(0, 0, -1).applyQuaternion(this.intersection.pointerQuaternion))); | ||
case 'lines': | ||
return (this._ray = new Ray(this.intersection.details.line.start, this.intersection.details.line.end.clone().sub(this.intersection.details.line.start).normalize())); | ||
} | ||
} | ||
@@ -109,0 +114,0 @@ _intersections = []; |
@@ -10,4 +10,4 @@ import { Pointer } from './pointer.js'; | ||
const { width, height, top, left } = element.getBoundingClientRect(); | ||
const x = e.pageX - left; | ||
const y = e.pageY - top; | ||
const x = e.clientX - left; | ||
const y = e.clientY - top; | ||
return target.set((x / width) * 2 - 1, -(y / height) * 2 + 1); | ||
@@ -57,3 +57,3 @@ } | ||
//this allows enter, down, ... events to be forwarded to the scene even when they dont come with a move event | ||
innerPointer.setIntersection(innerPointer.computeIntersection(scene, event)); | ||
innerPointer.setIntersection(innerPointer.computeIntersection('pointer', scene, event)); | ||
innerPointer.commit(event, false); | ||
@@ -60,0 +60,0 @@ } |
@@ -1,2 +0,2 @@ | ||
import { Intersection as ThreeIntersection, Quaternion, Vector3, Vector2 } from 'three'; | ||
import { Intersection as ThreeIntersection, Quaternion, Vector3, Line3, Vector2 } from 'three'; | ||
export type Intersection = ThreeIntersection & { | ||
@@ -15,2 +15,3 @@ pointerPosition: Vector3; | ||
lineIndex: number; | ||
line: Line3; | ||
} | { | ||
@@ -22,2 +23,3 @@ type: 'screen-ray'; | ||
distanceViewPlane: number; | ||
direction: Vector3; | ||
/** | ||
@@ -24,0 +26,0 @@ * point on the screen for x and y from -1 to 1 |
@@ -6,2 +6,3 @@ import { Line3, Matrix4, Plane, Quaternion, Ray, Raycaster, Vector3, Mesh, Vector2, } from 'three'; | ||
const lineHelper = new Line3(); | ||
const scaleHelper = new Vector3(); | ||
const planeHelper = new Plane(); | ||
@@ -53,2 +54,5 @@ const rayHelper = new Ray(); | ||
const pointOnFace = rayHelper.intersectPlane(planeHelper, new Vector3()) ?? point; | ||
const pointerPosition = new Vector3(); | ||
const pointerQuaternion = new Quaternion(); | ||
this.fromMatrixWorld.decompose(pointerPosition, pointerQuaternion, scaleHelper); | ||
let uv = intersection.uv; | ||
@@ -64,4 +68,4 @@ if (intersection.object instanceof Mesh && getClosestUV(point2Helper, point, intersection.object)) { | ||
point, | ||
pointerPosition: new Vector3().setFromMatrixPosition(this.fromMatrixWorld), | ||
pointerQuaternion: new Quaternion().setFromRotationMatrix(this.fromMatrixWorld), | ||
pointerPosition, | ||
pointerQuaternion, | ||
}; | ||
@@ -118,3 +122,4 @@ } | ||
const lastRaycaster = this.raycasters[lastRaycasterIndex]; | ||
return voidObjectIntersectionFromRay(scene, lastRaycaster.ray, (distanceOnLine) => ({ | ||
return voidObjectIntersectionFromRay(scene, lastRaycaster.ray, (point, distanceOnLine) => ({ | ||
line: new Line3(lastRaycaster.ray.origin.clone(), point), | ||
lineIndex: this.raycasters.length - 1, | ||
@@ -131,2 +136,3 @@ distanceOnLine, | ||
//TODO: consider maxLength | ||
const raycaster = this.raycasters[raycasterIndex]; | ||
return Object.assign(intersection, { | ||
@@ -137,2 +143,3 @@ details: { | ||
type: 'lines', | ||
line: new Line3(raycaster.ray.origin.clone(), raycaster.ray.direction.clone().multiplyScalar(raycaster.far).add(raycaster.ray.origin)), | ||
}, | ||
@@ -139,0 +146,0 @@ distance, |
@@ -31,6 +31,7 @@ import { Vector3, Object3D, Camera, Vector2 } from 'three'; | ||
private readonly raycaster; | ||
private readonly cameraQuaternion; | ||
private readonly fromPosition; | ||
private readonly fromQuaternion; | ||
private readonly coords; | ||
private viewPlane; | ||
private readonly viewPlane; | ||
private readonly intersects; | ||
@@ -37,0 +38,0 @@ private readonly pointerEventsOrders; |
@@ -7,3 +7,2 @@ import { Matrix4, Plane, Quaternion, Raycaster, Vector3, Vector2, Mesh, } from 'three'; | ||
const NegZAxis = new Vector3(0, 0, -1); | ||
const directionHelper = new Vector3(); | ||
const planeHelper = new Plane(); | ||
@@ -107,2 +106,3 @@ const point2Helper = new Vector2(); | ||
} | ||
const directionHelper = new Vector3(); | ||
export class ScreenRayIntersector { | ||
@@ -112,2 +112,3 @@ prepareTransformation; | ||
raycaster = new Raycaster(); | ||
cameraQuaternion = new Quaternion(); | ||
fromPosition = new Vector3(); | ||
@@ -150,2 +151,3 @@ fromQuaternion = new Quaternion(); | ||
...details, | ||
direction: this.raycaster.ray.direction.clone(), | ||
screenPoint: this.coords.clone(), | ||
@@ -157,4 +159,4 @@ }, | ||
pointOnFace: point, | ||
pointerPosition: this.fromPosition.clone(), | ||
pointerQuaternion: this.fromQuaternion.clone(), | ||
pointerPosition: this.raycaster.ray.origin.clone(), | ||
pointerQuaternion: this.cameraQuaternion.clone(), | ||
}; | ||
@@ -180,3 +182,4 @@ } | ||
const pointerPosition = this.fromPosition.clone(); | ||
const pointerQuaternion = this.fromQuaternion.clone(); | ||
const pointerQuaternion = this.cameraQuaternion.clone(); | ||
const pointerDirection = this.raycaster.ray.direction.clone(); | ||
const index = getDominantIntersectionIndex(this.intersects, this.pointerEventsOrders, this.options); | ||
@@ -187,3 +190,8 @@ const intersection = index == null ? undefined : this.intersects[index]; | ||
if (intersection == null) { | ||
return voidObjectIntersectionFromRay(scene, this.raycaster.ray, (distance) => ({ type: 'screen-ray', distanceViewPlane: distance, screenPoint: this.coords.clone() }), pointerPosition, pointerQuaternion); | ||
return voidObjectIntersectionFromRay(scene, this.raycaster.ray, (_point, distance) => ({ | ||
type: 'screen-ray', | ||
distanceViewPlane: distance, | ||
screenPoint: this.coords.clone(), | ||
direction: pointerDirection, | ||
}), pointerPosition, pointerQuaternion); | ||
} | ||
@@ -197,2 +205,3 @@ intersection.object.updateWorldMatrix(true, false); | ||
screenPoint: this.coords.clone(), | ||
direction: pointerDirection, | ||
}, | ||
@@ -199,0 +208,0 @@ pointOnFace: intersection.point, |
@@ -1,2 +0,2 @@ | ||
import { Object3D } from 'three'; | ||
import { Object3D, Sphere, Intersection as ThreeIntersection } from 'three'; | ||
import type { PointerCapture } from '../pointer.js'; | ||
@@ -14,3 +14,2 @@ import { Intersector } from './intersector.js'; | ||
private readonly intersects; | ||
private readonly pointerEventsOrders; | ||
constructor(space: { | ||
@@ -23,4 +22,9 @@ current?: Object3D | null; | ||
startIntersection(): void; | ||
executeIntersection(object: Object3D, objectPointerEventsOrder: number | undefined): void; | ||
executeIntersection(object: Object3D): void; | ||
finalizeIntersection(scene: Object3D): Intersection; | ||
} | ||
declare module 'three' { | ||
interface Object3D { | ||
spherecast?(sphere: Sphere, intersects: Array<ThreeIntersection>): void; | ||
} | ||
} |
import { InstancedMesh, Matrix4, Mesh, Vector3, Sphere, Quaternion, Plane, Vector2, } from 'three'; | ||
import { computeIntersectionWorldPlane, getDominantIntersectionIndex, pushTimes } from './utils.js'; | ||
import { computeIntersectionWorldPlane, getDominantIntersectionIndex } from './utils.js'; | ||
import { getVoidObject } from '../index.js'; | ||
@@ -16,3 +16,2 @@ import { getClosestUV, updateAndCheckWorldTransformation } from '../utils.js'; | ||
intersects = []; | ||
pointerEventsOrders = []; | ||
constructor(space, getSphereRadius, options) { | ||
@@ -63,3 +62,3 @@ this.space = space; | ||
uv, | ||
distance: intersection.distance, | ||
distance: point.distanceTo(pointOnFace), | ||
pointerPosition: this.fromPosition.clone(), | ||
@@ -81,9 +80,7 @@ pointerQuaternion: this.fromQuaternion.clone(), | ||
} | ||
executeIntersection(object, objectPointerEventsOrder) { | ||
executeIntersection(object) { | ||
if (!this.isReady()) { | ||
return; | ||
} | ||
const start = this.intersects.length; | ||
intersectSphereWithObject(this.collisionSphere, object, this.intersects); | ||
pushTimes(this.pointerEventsOrders, objectPointerEventsOrder, this.intersects.length - start); | ||
} | ||
@@ -93,6 +90,5 @@ finalizeIntersection(scene) { | ||
const pointerQuaternion = this.fromQuaternion.clone(); | ||
const index = getDominantIntersectionIndex(this.intersects, this.pointerEventsOrders, this.options); | ||
const index = getDominantIntersectionIndex(this.intersects, undefined, this.options); | ||
const intersection = index == null ? undefined : this.intersects[index]; | ||
this.intersects.length = 0; | ||
this.pointerEventsOrders.length = 0; | ||
if (intersection == null) { | ||
@@ -127,8 +123,5 @@ return { | ||
const matrixHelper = new Matrix4(); | ||
function isSpherecastable(obj) { | ||
return 'spherecast' in obj; | ||
} | ||
function intersectSphereWithObject(pointerSphere, object, target) { | ||
object.updateWorldMatrix(true, false); | ||
if (isSpherecastable(object)) { | ||
if (object.spherecast != null) { | ||
object.spherecast(pointerSphere, target); | ||
@@ -206,2 +199,6 @@ return; | ||
const point = vectorHelper.clone(); | ||
let uv; | ||
if (getClosestUV(point2Helper, point, mesh)) { | ||
uv = point2Helper.clone(); | ||
} | ||
return { | ||
@@ -216,2 +213,3 @@ distance: Math.sqrt(distanceToSphereCenterSquared), | ||
}, | ||
uv, | ||
normal, | ||
@@ -218,0 +216,0 @@ point, |
@@ -5,3 +5,3 @@ import { Plane, Intersection as ThreeIntersection, Object3D, Vector3, Ray, Quaternion, Matrix4 } from 'three'; | ||
export declare function computeIntersectionWorldPlane(target: Plane, intersection: Intersection, objectMatrixWorld: Matrix4): boolean; | ||
export declare function intersectPointerEventTargets(object: Object3D, pointers: Array<Pointer>, parentHasListener?: boolean, parentPointerEvents?: AllowedPointerEvents, parentPointerEventsType?: AllowedPointerEventsType, parentPointerEventsOrder?: number): void; | ||
export declare function intersectPointerEventTargets(type: 'wheel' | 'pointer', object: Object3D, pointers: Array<Pointer>, parentHasListener?: boolean, parentPointerEvents?: AllowedPointerEvents, parentPointerEventsType?: AllowedPointerEventsType, parentPointerEventsOrder?: number): void; | ||
/** | ||
@@ -11,4 +11,4 @@ * @returns undefined if `i1` is the dominant intersection | ||
*/ | ||
export declare function getDominantIntersectionIndex<T extends ThreeIntersection>(intersections: Array<T>, pointerEventsOrders: Array<number | undefined>, { customSort: compare }?: IntersectionOptions, filter?: (intersection: ThreeIntersection) => boolean): number | undefined; | ||
export declare function voidObjectIntersectionFromRay(scene: Object3D, ray: Ray, getDetails: (distanceOnRay: number) => Intersection['details'], pointerPosition: Vector3, pointerQuaternion: Quaternion, addToDistance?: number): Intersection; | ||
export declare function getDominantIntersectionIndex<T extends ThreeIntersection>(intersections: Array<T>, pointerEventsOrders: Array<number | undefined> | undefined, { customSort: compare }?: IntersectionOptions, filter?: (intersection: ThreeIntersection) => boolean): number | undefined; | ||
export declare function voidObjectIntersectionFromRay(scene: Object3D, ray: Ray, getDetails: (pointer: Vector3, distanceOnRay: number) => Intersection['details'], pointerPosition: Vector3, pointerQuaternion: Quaternion, addToDistance?: number): Intersection; | ||
export declare function pushTimes<T>(target: Array<T>, value: T, times: number): void; |
@@ -43,4 +43,4 @@ import { getVoidObject } from './intersector.js'; | ||
} | ||
export function intersectPointerEventTargets(object, pointers, parentHasListener = false, parentPointerEvents, parentPointerEventsType, parentPointerEventsOrder) { | ||
const hasListener = parentHasListener || hasObjectListeners(object); | ||
export function intersectPointerEventTargets(type, object, pointers, parentHasListener = false, parentPointerEvents, parentPointerEventsType, parentPointerEventsOrder) { | ||
const hasListener = parentHasListener || hasObjectListeners(type, object); | ||
const pointerEvents = object.pointerEvents ?? parentPointerEvents; | ||
@@ -77,15 +77,30 @@ const pointerEventsOrDefault = pointerEvents ?? object.defaultPointerEvents ?? 'listener'; | ||
for (let i = 0; i < descendantsLength; i++) { | ||
intersectPointerEventTargets(descendants[i], pointers, hasListener, pointerEvents, pointerEventsType, pointerEventsOrder); | ||
intersectPointerEventTargets(type, descendants[i], pointers, hasListener, pointerEvents, pointerEventsType, pointerEventsOrder); | ||
} | ||
} | ||
function hasObjectListeners(object) { | ||
function hasObjectListeners(type, object) { | ||
if (object.ancestorsHaveListeners) { | ||
return true; | ||
} | ||
if (object.__r3f != null && object.__r3f?.eventCount > 0) { | ||
if (type === 'pointer' && object.ancestorsHavePointerListeners) { | ||
return true; | ||
} | ||
if (type === 'wheel' && object.ancestorsHaveWheelListeners) { | ||
return true; | ||
} | ||
if (object.__r3f != null && object.__r3f?.eventCount > 0) { | ||
if (type === 'wheel' && object.__r3f['handlers']['onWheel'] != null) { | ||
return true; | ||
} | ||
if (type === 'pointer' && Object.keys(object.__r3f['handlers']).some((key) => key != 'onWheel')) { | ||
return true; | ||
} | ||
} | ||
if (object._listeners == null) { | ||
return false; | ||
} | ||
if (type === 'wheel') { | ||
const wheelListeners = object._listeners.wheel; | ||
return wheelListeners != null && wheelListeners.length > 0; | ||
} | ||
const entries = Object.entries(object._listeners); | ||
@@ -95,2 +110,5 @@ const length = entries.length; | ||
const entry = entries[i]; | ||
if (entry[0] === 'wheel') { | ||
continue; | ||
} | ||
if (!listenerNames.includes(entry[0])) { | ||
@@ -125,3 +143,3 @@ continue; | ||
} | ||
const newPointerEventsOrder = pointerEventsOrders[i]; | ||
const newPointerEventsOrder = pointerEventsOrders?.[i]; | ||
if (intersection == null || compare(newIntersection, newPointerEventsOrder, intersection, pointerEventsOrder) < 0) { | ||
@@ -155,3 +173,3 @@ index = i; | ||
normal: ray.origin.clone().sub(point).normalize(), | ||
details: getDetails(distanceOnRay), | ||
details: getDetails(point, distanceOnRay), | ||
pointerPosition, | ||
@@ -158,0 +176,0 @@ pointerQuaternion, |
@@ -50,3 +50,8 @@ import { Object3D, OrthographicCamera, PerspectiveCamera } from 'three'; | ||
interactableDescendants?: Array<Object3D>; | ||
/** | ||
* @deprecated | ||
*/ | ||
ancestorsHaveListeners?: boolean; | ||
ancestorsHavePointerListeners?: boolean; | ||
ancestorsHaveWheelListeners?: boolean; | ||
} | ||
@@ -92,3 +97,3 @@ } | ||
setEnabled(enabled: boolean, nativeEvent: NativeEvent, commit?: boolean): void; | ||
computeIntersection(scene: Object3D, nativeEvent: NativeEvent): Intersection; | ||
computeIntersection(type: 'wheel' | 'pointer', scene: Object3D, nativeEvent: NativeEvent): Intersection; | ||
setIntersection(intersection: Intersection): void; | ||
@@ -95,0 +100,0 @@ commit(nativeEvent: NativeEvent, emitMove: boolean): void; |
@@ -107,3 +107,3 @@ import { Object3D } from 'three'; | ||
} | ||
computeIntersection(scene, nativeEvent) { | ||
computeIntersection(type, scene, nativeEvent) { | ||
if (this.pointerCapture != null) { | ||
@@ -113,3 +113,3 @@ return this.intersector.intersectPointerCapture(this.pointerCapture, nativeEvent); | ||
this.intersector.startIntersection(nativeEvent); | ||
intersectPointerEventTargets(scene, [this]); | ||
intersectPointerEventTargets(type, scene, [this]); | ||
return this.intersector.finalizeIntersection(scene); | ||
@@ -168,3 +168,3 @@ } | ||
move(scene, nativeEvent) { | ||
this.intersection = this.computeIntersection(scene, nativeEvent); | ||
this.intersection = this.computeIntersection('pointer', scene, nativeEvent); | ||
this.commit(nativeEvent, true); | ||
@@ -263,3 +263,3 @@ } | ||
if (!useMoveIntersection) { | ||
this.wheelIntersection = this.computeIntersection(scene, nativeEvent); | ||
this.wheelIntersection = this.computeIntersection('wheel', scene, nativeEvent); | ||
} | ||
@@ -266,0 +266,0 @@ const intersection = useMoveIntersection ? this.intersection : this.wheelIntersection; |
@@ -5,3 +5,3 @@ { | ||
"license": "SEE LICENSE IN LICENSE", | ||
"version": "6.4.12", | ||
"version": "6.5.0", | ||
"homepage": "https://github.com/pmndrs/xr", | ||
@@ -29,5 +29,3 @@ "author": "Bela Bohlender", | ||
"@types/node": "^20.12.11", | ||
"@types/three": "^0.164.0", | ||
"@react-three/fiber": "^8.17.9", | ||
"three": "^0.164.1", | ||
"vite": "^5.2.11", | ||
@@ -42,4 +40,8 @@ "vitest": "^2.0.5" | ||
"example": "vite example --host", | ||
"example:build": "vite build example" | ||
"example:build": "vite build example", | ||
"check:prettier": "prettier --check src", | ||
"check:eslint": "eslint 'src/**/*.ts'", | ||
"fix:prettier": "prettier --write src", | ||
"fix:eslint": "eslint 'src/**/*.ts' --fix" | ||
} | ||
} |
106247
4
2544