Skip to content

Commit 424a027

Browse files
committed
translate-as and project on axis finished
1 parent 9accdd1 commit 424a027

File tree

12 files changed

+655
-245
lines changed

12 files changed

+655
-245
lines changed

.mocharc.json

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"node-option": ["experimental-specifier-resolution=node", "loader=ts-node/esm/transpile-only"]
3+
}

examples/handle/app.tsx

+51-119
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
import { Canvas, useFrame, useThree } from '@react-three/fiber'
22
import { createXRStore, XR } from '@react-three/xr'
3-
import { useEffect, useMemo, useRef } from 'react'
4-
import { Group, Mesh } from 'three'
5-
import { useHandle } from '@react-three/handle'
3+
import { useEffect, useRef } from 'react'
4+
import { BufferGeometry, Mesh, Vector3 } from 'three'
5+
import { Handle, HandleTarget, useHandle } from '@react-three/handle'
66
import { forwardHtmlEvents } from '@pmndrs/pointer-events'
7+
import { Environment } from '@react-three/drei'
78

89
const store = createXRStore()
910

@@ -19,15 +20,15 @@ export function App() {
1920
>
2021
<SwitchToXRPointerEvents />
2122
<XR store={store}>
22-
<ambientLight />
23+
<Environment preset="city" />
2324
<directionalLight position={[1, 1, 1]} />
2425
<Cube />
2526
<Cube />
2627
{/*<Smoke count={100} maxSize={0.3} minSize={0.1} spawnRate={10} speed={0.1} />
2728
<TeleportTarget onTeleport={setPosition}>
2829
<mesh scale={[10, 1, 10]} position={[0, -0.5, 0]}>
2930
<boxGeometry />
30-
<meshBasicMaterial color="green" />
31+
<meshStandardMaterial color="green" />
3132
</mesh>
3233
</TeleportTarget>*/}
3334
</XR>
@@ -37,123 +38,54 @@ export function App() {
3738
}
3839

3940
function Cube() {
40-
const ref = useRef<Mesh>(null)
41-
const xHandleRef = useRef<Mesh>(null)
42-
const yHandleRef = useRef<Mesh>(null)
43-
const zHandleRef = useRef<Mesh>(null)
44-
useHandle(ref, {
45-
apply: (state) => {
46-
const mesh = ref.current
47-
if (mesh == null) {
48-
return
49-
}
50-
mesh.scale.x = state.current.scale.x
51-
console.log(state.current.scale.toArray())
52-
if (xHandleRef.current != null) {
53-
xHandleRef.current.scale.x = 0.1 / mesh.scale.x
54-
}
55-
if (yHandleRef.current != null) {
56-
yHandleRef.current.scale.x = 0.1 / mesh.scale.x
57-
}
58-
if (zHandleRef.current != null) {
59-
zHandleRef.current.scale.x = 0.1 / mesh.scale.x
60-
}
61-
},
62-
handle: xHandleRef,
63-
translate: 'as-scale',
64-
scale: 'x',
65-
})
66-
useHandle(ref, {
67-
apply: (state) => {
68-
const mesh = ref.current
69-
if (mesh == null) {
70-
return
71-
}
72-
mesh.scale.y = state.current.scale.y
73-
if (xHandleRef.current != null) {
74-
xHandleRef.current.scale.y = 0.1 / mesh.scale.y
75-
}
76-
if (yHandleRef.current != null) {
77-
yHandleRef.current.scale.y = 0.1 / mesh.scale.y
78-
}
79-
if (zHandleRef.current != null) {
80-
zHandleRef.current.scale.y = 0.1 / mesh.scale.y
81-
}
82-
},
83-
handle: yHandleRef,
84-
translate: 'as-scale',
85-
scale: 'y',
86-
})
87-
useHandle(ref, {
88-
apply: (state) => {
89-
const mesh = ref.current
90-
if (mesh == null) {
91-
return
92-
}
93-
mesh.rotation.x = state.current.rotation.x
94-
},
95-
handle: zHandleRef,
96-
translate: 'as-rotate',
97-
rotate: 'x',
98-
})
99-
useHandle(ref, {
100-
apply: (state) => {
101-
ref.current?.position.copy(state.current.position)
102-
ref.current?.quaternion.copy(state.current.quaternion)
103-
if (state.current.pointerAmount > 1) {
104-
ref.current?.scale.copy(state.current.scale)
105-
if (ref.current != null) {
106-
xHandleRef.current?.scale.setScalar(0.1).divide(ref.current.scale)
107-
yHandleRef.current?.scale.setScalar(0.1).divide(ref.current.scale)
108-
zHandleRef.current?.scale.setScalar(0.1).divide(ref.current.scale)
109-
}
110-
}
111-
},
112-
translate: {
113-
z: false,
114-
},
115-
})
11641
return (
117-
<group rotation-y={Math.PI / 4} position-y={-2}>
118-
<mesh rotation-order="XZY" scale={1} pointerEventsType={{ deny: 'touch' }} ref={ref}>
119-
<boxGeometry />
120-
<meshPhongMaterial color="red" />
121-
<mesh
122-
pointerEventsOrder={1}
123-
renderOrder={1}
124-
scale={0.1}
125-
position-x={0.7}
126-
pointerEventsType={{ deny: 'touch' }}
127-
ref={xHandleRef}
128-
>
129-
<boxGeometry />
130-
<meshBasicMaterial depthTest={false} color="blue" />
131-
</mesh>
42+
<group position-y={-2}>
43+
<HandleTarget>
44+
<Handle>
45+
<mesh rotation-order="XZY" scale={1} pointerEventsType={{ deny: 'touch' }}>
46+
<boxGeometry />
47+
<meshStandardMaterial color="red" />
48+
<Handle scale="x" translate="as-scale">
49+
<mesh
50+
pointerEventsOrder={1}
51+
renderOrder={1}
52+
scale={0.1}
53+
position-x={0.7}
54+
pointerEventsType={{ deny: 'touch' }}
55+
>
56+
<boxGeometry />
57+
<meshStandardMaterial depthTest={false} color="blue" />
58+
</mesh>
59+
</Handle>
13260

133-
<mesh
134-
pointerEventsOrder={1}
135-
renderOrder={1}
136-
scale={0.1}
137-
position-y={0.7}
138-
pointerEventsType={{ deny: 'touch' }}
139-
ref={yHandleRef}
140-
>
141-
<boxGeometry />
142-
<meshBasicMaterial depthTest={false} color="yellow" />
143-
</mesh>
61+
<Handle translate="as-rotate" rotate="x">
62+
<mesh
63+
pointerEventsOrder={1}
64+
renderOrder={1}
65+
scale={0.1}
66+
position-y={0.7}
67+
pointerEventsType={{ deny: 'touch' }}
68+
>
69+
<boxGeometry />
70+
<meshStandardMaterial depthTest={false} color="yellow" />
71+
</mesh>
72+
</Handle>
14473

145-
<mesh
146-
pointerEventsOrder={1}
147-
renderOrder={1}
148-
scale={0.1}
149-
position-z={0.7}
150-
pointerEventsType={{ deny: 'touch' }}
151-
ref={zHandleRef}
152-
>
153-
<boxGeometry />
154-
<meshBasicMaterial depthTest={false} color="green" />
155-
</mesh>
156-
</mesh>
74+
<Handle translate="as-rotate-and-scale">
75+
<mesh
76+
pointerEventsOrder={1}
77+
renderOrder={1}
78+
scale={0.1}
79+
position-z={0.7}
80+
pointerEventsType={{ deny: 'touch' }}
81+
>
82+
<boxGeometry />
83+
<meshStandardMaterial depthTest={false} color="green" />
84+
</mesh>
85+
</Handle>
86+
</mesh>
87+
</Handle>
88+
</HandleTarget>
15789
</group>
15890
)
15991
}

examples/handle/package.json

+4-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
{
22
"dependencies": {
3-
"@react-three/xr": "workspace:^",
3+
"@pmndrs/pointer-events": "workspace:^",
4+
"@react-three/drei": "^9.118.0",
45
"@react-three/handle": "workspace:^",
5-
"@pmndrs/pointer-events": "workspace:^"
6+
"@pmndrs/handle": "workspace:^",
7+
"@react-three/xr": "workspace:^"
68
},
79
"scripts": {
810
"dev": "vite --host"

package.json

+8-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@
55
"devDependencies": {
66
"@react-three/eslint-plugin": "^0.1.1",
77
"@react-three/fiber": "^8.16.6",
8+
"@types/chai": "^5.0.1",
89
"@types/eslint": "^8.56.10",
10+
"@types/mocha": "^10.0.10",
911
"@types/react": "^18.3.3",
1012
"@types/react-dom": "^18.3.0",
1113
"@types/three": "^0.164.0",
@@ -14,19 +16,24 @@
1416
"@typescript-eslint/parser": "^7.12.0",
1517
"@vitejs/plugin-basic-ssl": "^1.1.0",
1618
"@vitejs/plugin-react": "^4.3.0",
19+
"chai": "^5.1.2",
1720
"eslint": "^8",
1821
"eslint-config-prettier": "^9.1.0",
1922
"eslint-plugin-import": "^2.29.1",
2023
"eslint-plugin-prettier": "^5.1.3",
2124
"eslint-plugin-react": "^7.34.2",
2225
"eslint-plugin-react-hooks": "^4.6.2",
2326
"json": "^11.0.0",
27+
"mocha": "^10.8.2",
2428
"playwright": "^1.45.1",
2529
"react": "^18.3.1",
2630
"react-dom": "^18.3.1",
2731
"three": "^0.167.1",
2832
"typescript": "^5.5.4",
2933
"vite": "^5.2.11"
3034
},
31-
"packageManager": "[email protected]+sha512.f549b8a52c9d2b8536762f99c0722205efc5af913e77835dbccc3b0b0b2ca9e7dc8022b78062c17291c48e88749c70ce88eb5a74f1fa8c4bf5e18bb46c8bd83a"
35+
"packageManager": "[email protected]+sha512.f549b8a52c9d2b8536762f99c0722205efc5af913e77835dbccc3b0b0b2ca9e7dc8022b78062c17291c48e88749c70ce88eb5a74f1fa8c4bf5e18bb46c8bd83a",
36+
"dependencies": {
37+
"ts-node": "^10.9.2"
38+
}
3239
}

packages/handle/package.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@
66
"homepage": "https://github.com/pmndrs/xr",
77
"version": "0.0.0",
88
"scripts": {
9-
"build": "tsc"
9+
"build": "tsc",
10+
"test": "mocha ./tests/*.spec.ts"
1011
},
1112
"keywords": [
1213
"r3f",

packages/handle/src/computations/translate-as.ts

+23-14
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Matrix4, Quaternion, Vector3 } from 'three'
1+
import { Euler, Matrix4, Quaternion, Vector3 } from 'three'
22
import { HandleTransformState } from '../state.js'
33
import { HandleOptions } from '../store.js'
44
import {
@@ -57,26 +57,33 @@ export function computeTranslateAsHandleTransformState(
5757
}
5858
//compute space
5959
space.length = 0
60-
if (options.translate != 'as-rotate') {
60+
if (options.translate === 'as-scale') {
6161
addSpaceFromTransformOptions(space, qHelper1, storeData.initialTargetRotation, options.scale ?? true, 'scale')
6262
}
6363
if (options.translate != 'as-scale') {
6464
addSpaceFromTransformOptions(space, qHelper1, storeData.initialTargetRotation, options.rotate ?? true, 'rotate')
6565
}
66-
//project into sapce
67-
projectOntoSpace(
68-
space,
69-
pointerData.initialPointerWorldPoint,
70-
vectorHelper1.copy(pointerData.pointerWorldPoint),
71-
pointerData.pointerWorldDirection,
72-
)
7366

7467
matrixHelper.makeTranslation(storeData.initialTargetPosition)
7568
if (storeData.initialTargetParentWorldMatrix != null) {
7669
matrixHelper.premultiply(storeData.initialTargetParentWorldMatrix)
7770
}
78-
deltaHelper1.setFromMatrixPosition(matrixHelper).negate().add(pointerData.initialPointerWorldPoint)
79-
deltaHelper2.setFromMatrixPosition(targetWorldMatrix).negate().add(vectorHelper1)
71+
72+
//compute initial delta between point and target projected on space
73+
deltaHelper1.setFromMatrixPosition(matrixHelper)
74+
projectOntoSpace(space, pointerData.initialPointerWorldPoint, deltaHelper1, undefined)
75+
deltaHelper1.negate().add(pointerData.initialPointerWorldPoint)
76+
77+
//compute current delta between point and target projected on space
78+
deltaHelper2.setFromMatrixPosition(targetWorldMatrix)
79+
projectOntoSpace(space, pointerData.initialPointerWorldPoint, deltaHelper2, undefined)
80+
projectOntoSpace(
81+
space,
82+
pointerData.initialPointerWorldPoint,
83+
vectorHelper2.copy(pointerData.pointerWorldPoint),
84+
pointerData.pointerWorldDirection,
85+
)
86+
deltaHelper2.negate().add(vectorHelper2)
8087

8188
//compute delta rotation
8289
if (options.translate === 'as-scale') {
@@ -141,11 +148,13 @@ export function computeTranslateAsHandleTransformState(
141148
}
142149
vectorHelper2.copy(deltaHelper2).applyQuaternion(quaterionHelper2.invert())
143150

144-
scaleHelper.x = vectorHelper1.x === 0 ? 1 : Math.abs(vectorHelper2.x / vectorHelper1.x)
145-
scaleHelper.y = vectorHelper1.y === 0 ? 1 : Math.abs(vectorHelper2.y / vectorHelper1.y)
146-
scaleHelper.z = vectorHelper1.z === 0 ? 1 : Math.abs(vectorHelper2.z / vectorHelper1.z)
151+
scaleHelper.x = Math.abs(vectorHelper1.x) < 0.001 ? 1 : Math.abs(vectorHelper2.x / vectorHelper1.x)
152+
scaleHelper.y = Math.abs(vectorHelper1.y) < 0.001 ? 1 : Math.abs(vectorHelper2.y / vectorHelper1.y)
153+
scaleHelper.z = Math.abs(vectorHelper1.z) < 0.001 ? 1 : Math.abs(vectorHelper2.z / vectorHelper1.z)
147154
}
148155

156+
scaleHelper.multiply(storeData.initialTargetScale)
157+
149158
matrixHelper.compose(storeData.initialTargetPosition, qHelper1, scaleHelper)
150159

151160
//we pass targetParentWorldMatrix as undefined, because we calculated matrixHelper1 in local target space

0 commit comments

Comments
 (0)