@things-factory/figure-ui 10.1.198 → 10.1.200
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/client/modeller/joint-drag.ts +6 -8
- package/dist-client/modeller/joint-drag.js +6 -8
- package/dist-client/modeller/joint-drag.js.map +1 -1
- package/dist-client/tsconfig.tsbuildinfo +1 -1
- package/dist-server/tsconfig.tsbuildinfo +1 -1
- package/package.json +6 -6
- package/test/joint-frames.test.ts +33 -26
|
@@ -35,14 +35,12 @@ const eulerMatrix = (deg: number[]) => new THREE.Matrix4().makeRotationFromEuler
|
|
|
35
35
|
const unit = (axis: unknown) => new THREE.Vector3(...(jointAxisUnit(axis) ?? [0, 0, 0]))
|
|
36
36
|
const round = (v: number, step: number) => Number((Math.round(v / step) * step).toFixed(Math.max(0, -Math.floor(Math.log10(step)))))
|
|
37
37
|
|
|
38
|
-
/**
|
|
39
|
-
const
|
|
40
|
-
/** `origin.rotate`, URDF's rpy: Rz·Ry·Rx about the fixed axes. */
|
|
41
|
-
const rotateMatrix = (j: FigureJoint) => eulerMatrixOrder(AXES.map(a => framesOf(j).rotate?.[a] ?? 0), 'ZYX')
|
|
38
|
+
/** `origin.parentRotate` (ADR-0107, named for its end by ADR-0140 3.6), URDF's rpy: Rz·Ry·Rx about the fixed axes. */
|
|
39
|
+
const rotateMatrix = (j: FigureJoint) => eulerMatrixOrder(AXES.map(a => j.origin?.parentRotate?.[a] ?? 0), 'ZYX')
|
|
42
40
|
const eulerMatrixOrder = (deg: number[], order: THREE.EulerOrder) => new THREE.Matrix4().makeRotationFromEuler(new THREE.Euler(...(deg.map(THREE.MathUtils.degToRad) as [number, number, number]), order))
|
|
43
41
|
|
|
44
|
-
/** The part's own quarter turn in the joint frame, as the record says it. */
|
|
45
|
-
const turnMatrix = (j: FigureJoint) => eulerMatrix(AXES.map(a => j.origin?.
|
|
42
|
+
/** The part's own quarter turn in the joint frame, as the record says it (`origin.childRotate`). */
|
|
43
|
+
const turnMatrix = (j: FigureJoint) => eulerMatrix(AXES.map(a => j.origin?.childRotate?.[a] ?? 0))
|
|
46
44
|
|
|
47
45
|
/** The joint frame's rotation in the figure: the part's rotation now, less its own turn and less the joint's. */
|
|
48
46
|
function jointFrameRotation(j: FigureJoint, part: FigureRenderPart, angle: number): THREE.Matrix4 {
|
|
@@ -100,12 +98,12 @@ export function jointDragOf(asset: FigureAsset, id: string, part: FigureRenderPa
|
|
|
100
98
|
}
|
|
101
99
|
}
|
|
102
100
|
|
|
103
|
-
/* Moving the joint's origin: the change in the frame it is placed in, before `origin.
|
|
101
|
+
/* Moving the joint's origin: the change in the frame it is placed in, before `origin.parentRotate` turns the joint frame. */
|
|
104
102
|
const local = moved.clone().applyMatrix4(frame.clone().multiply(rotateMatrix(j).invert()).invert())
|
|
105
103
|
const origin: NonNullable<FigureJoint['origin']> = { ...(j.origin ?? {}) }
|
|
106
104
|
if (!('plane' in j.body0) && !('face' in j.body0)) {
|
|
107
105
|
/* At the parent's origin there is no face: the frame moves by its offset, along the parent's axes. */
|
|
108
|
-
const offset = { ...(
|
|
106
|
+
const offset = { ...(j.origin?.offset ?? {}) }
|
|
109
107
|
for (const axis of AXES) {
|
|
110
108
|
const d = local[axis]
|
|
111
109
|
if (Math.abs(d) < 0.5) continue
|
|
@@ -9,13 +9,11 @@ const matrixOf = (r) => new THREE.Matrix4().set(r[0][0], r[0][1], r[0][2], 0, r[
|
|
|
9
9
|
const eulerMatrix = (deg) => new THREE.Matrix4().makeRotationFromEuler(new THREE.Euler(...deg.map(THREE.MathUtils.degToRad), 'XYZ'));
|
|
10
10
|
const unit = (axis) => new THREE.Vector3(...(jointAxisUnit(axis) ?? [0, 0, 0]));
|
|
11
11
|
const round = (v, step) => Number((Math.round(v / step) * step).toFixed(Math.max(0, -Math.floor(Math.log10(step)))));
|
|
12
|
-
/**
|
|
13
|
-
const
|
|
14
|
-
/** `origin.rotate`, URDF's rpy: Rz·Ry·Rx about the fixed axes. */
|
|
15
|
-
const rotateMatrix = (j) => eulerMatrixOrder(AXES.map(a => framesOf(j).rotate?.[a] ?? 0), 'ZYX');
|
|
12
|
+
/** `origin.parentRotate` (ADR-0107, named for its end by ADR-0140 3.6), URDF's rpy: Rz·Ry·Rx about the fixed axes. */
|
|
13
|
+
const rotateMatrix = (j) => eulerMatrixOrder(AXES.map(a => j.origin?.parentRotate?.[a] ?? 0), 'ZYX');
|
|
16
14
|
const eulerMatrixOrder = (deg, order) => new THREE.Matrix4().makeRotationFromEuler(new THREE.Euler(...deg.map(THREE.MathUtils.degToRad), order));
|
|
17
|
-
/** The part's own quarter turn in the joint frame, as the record says it. */
|
|
18
|
-
const turnMatrix = (j) => eulerMatrix(AXES.map(a => j.origin?.
|
|
15
|
+
/** The part's own quarter turn in the joint frame, as the record says it (`origin.childRotate`). */
|
|
16
|
+
const turnMatrix = (j) => eulerMatrix(AXES.map(a => j.origin?.childRotate?.[a] ?? 0));
|
|
19
17
|
/** The joint frame's rotation in the figure: the part's rotation now, less its own turn and less the joint's. */
|
|
20
18
|
function jointFrameRotation(j, part, angle) {
|
|
21
19
|
const now = matrixOf(part.pose.r);
|
|
@@ -75,12 +73,12 @@ export function jointDragOf(asset, id, part, result, values) {
|
|
|
75
73
|
return { kind: 'value', input: stateId, unit: j.travel ? 'ratio' : 'mm', value: round(value, j.travel ? 0.001 : 1) };
|
|
76
74
|
}
|
|
77
75
|
}
|
|
78
|
-
/* Moving the joint's origin: the change in the frame it is placed in, before `origin.
|
|
76
|
+
/* Moving the joint's origin: the change in the frame it is placed in, before `origin.parentRotate` turns the joint frame. */
|
|
79
77
|
const local = moved.clone().applyMatrix4(frame.clone().multiply(rotateMatrix(j).invert()).invert());
|
|
80
78
|
const origin = { ...(j.origin ?? {}) };
|
|
81
79
|
if (!('plane' in j.body0) && !('face' in j.body0)) {
|
|
82
80
|
/* At the parent's origin there is no face: the frame moves by its offset, along the parent's axes. */
|
|
83
|
-
const offset = { ...(
|
|
81
|
+
const offset = { ...(j.origin?.offset ?? {}) };
|
|
84
82
|
for (const axis of AXES) {
|
|
85
83
|
const d = local[axis];
|
|
86
84
|
if (Math.abs(d) < 0.5)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"joint-drag.js","sourceRoot":"","sources":["../../client/modeller/joint-drag.ts"],"names":[],"mappings":"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","sourcesContent":["import * as THREE from 'three'\nimport { compileFigureAsset, figureMimicTermsOf } from '@hatiolab/figure-model'\nimport type { FigureAsset, FigureJoint } from '@hatiolab/figure-model'\n\nimport type { GizmoResult } from './gizmo.js'\nimport { jointAxisUnit, jointAxisWord } from './joint-axis.js'\nimport type { FigureRenderPart } from './renderer.js'\n\n/*\n * A gizmo drag on a part that hangs from a joint edits the joint, not the part (ADR-0093; r3 §3: the modeller changes\n * the inputs of the relation, and never breaks it because a computed place was moved):\n *\n * - turning a part on a revolute or continuous joint sets the joint's value -- the turn about the joint's axis;\n * - sliding a part on a prismatic joint along its axis sets its value;\n * - moving a part any other way moves the joint's origin: the gap along the face it sits on, the offset across it;\n * at a parent's origin (ADR-0107 decision 4), its `origin.offset` in the parent's frame.\n *\n * What would break a relation is refused and said: an origin that follows another part's size, a joint that mimics\n * another, a turn on a joint that does not turn.\n */\n\nexport type JointDrag =\n | { kind: 'value'; input: string; unit: 'deg' | 'mm' | 'ratio'; value: number }\n | { kind: 'joint'; joint: FigureJoint }\n | { kind: 'refused'; reason: 'mimic' | 'fixed-turn' | 'origin-follows' | 'turned-slide' | 'off-axis'; joint: string; leader?: string; axis?: string }\n\ntype Axis = 'x' | 'y' | 'z'\nconst AXES: Axis[] = ['x', 'y', 'z']\n/** The axis each face faces along, and which way (as figure-model's `FIGURE_FACES`). */\nconst FACE_AXIS: Record<string, Axis> = { left: 'x', right: 'x', bottom: 'y', top: 'y', back: 'z', front: 'z' }\nconst FACE_SIGN: Record<string, number> = { left: -1, right: 1, bottom: -1, top: 1, back: -1, front: 1 }\n\nconst matrixOf = (r: number[][]) => new THREE.Matrix4().set(r[0]![0]!, r[0]![1]!, r[0]![2]!, 0, r[1]![0]!, r[1]![1]!, r[1]![2]!, 0, r[2]![0]!, r[2]![1]!, r[2]![2]!, 0, 0, 0, 0, 1)\nconst eulerMatrix = (deg: number[]) => new THREE.Matrix4().makeRotationFromEuler(new THREE.Euler(...(deg.map(THREE.MathUtils.degToRad) as [number, number, number]), 'XYZ'))\nconst unit = (axis: unknown) => new THREE.Vector3(...(jointAxisUnit(axis) ?? [0, 0, 0]))\nconst round = (v: number, step: number) => Number((Math.round(v / step) * step).toFixed(Math.max(0, -Math.floor(Math.log10(step)))))\n\n/** The joint frame's words of ADR-0107, read without the kernel's types (an older kernel has none). */\nconst framesOf = (j: FigureJoint) => (j.origin ?? {}) as { offset?: Partial<Record<Axis, unknown>>; rotate?: Partial<Record<Axis, number>> }\n/** `origin.rotate`, URDF's rpy: Rz·Ry·Rx about the fixed axes. */\nconst rotateMatrix = (j: FigureJoint) => eulerMatrixOrder(AXES.map(a => framesOf(j).rotate?.[a] ?? 0), 'ZYX')\nconst eulerMatrixOrder = (deg: number[], order: THREE.EulerOrder) => new THREE.Matrix4().makeRotationFromEuler(new THREE.Euler(...(deg.map(THREE.MathUtils.degToRad) as [number, number, number]), order))\n\n/** The part's own quarter turn in the joint frame, as the record says it. */\nconst turnMatrix = (j: FigureJoint) => eulerMatrix(AXES.map(a => j.origin?.turn?.[a] ?? 0))\n\n/** The joint frame's rotation in the figure: the part's rotation now, less its own turn and less the joint's. */\nfunction jointFrameRotation(j: FigureJoint, part: FigureRenderPart, angle: number): THREE.Matrix4 {\n const now = matrixOf(part.pose.r as number[][])\n const own = turnMatrix(j)\n const turn = j.type === 'revolute' ? new THREE.Matrix4().makeRotationAxis(unit(j.axis!), THREE.MathUtils.degToRad(angle)) : new THREE.Matrix4()\n return now.multiply(own.invert()).multiply(turn.invert())\n}\n\nexport function jointDragOf(asset: FigureAsset, id: string, part: FigureRenderPart, result: GizmoResult, values: Record<string, number>): JointDrag | null {\n const j = asset.joints?.find(joint => joint.body1.part === id)\n /* A size is the part's own, not the joint's: a scale drag goes to the part's measures (the editor drops the shift). */\n if (!j || result.scale || (!result.rotation && !result.position)) return null\n const stateId = j.state?.id ?? j.id\n const now = values[stateId] ?? asset.stateDefaults[stateId] ?? 0\n const turns = j.type === 'revolute'\n /* A revolute joint without limits turns without end (USD). */\n const endless = turns && j.lowerLimit === undefined && j.upperLimit === undefined\n\n if (result.rotation) {\n if (!turns) return { kind: 'refused', reason: j.type === 'prismatic' ? 'turned-slide' : 'fixed-turn', joint: j.id }\n if (j.mimic) return { kind: 'refused', reason: 'mimic', joint: j.id, leader: figureMimicTermsOf(j.mimic)!.sum.map(t => t.joint).join(', ') }\n /* The turn about the joint's axis (the twist of the change): the only turn the joint can make. */\n const before = new THREE.Quaternion().setFromRotationMatrix(matrixOf(part.pose.r as number[][]))\n const after = new THREE.Quaternion().setFromRotationMatrix(eulerMatrix(result.rotation))\n const change = after.multiply(before.invert())\n const axis = unit(j.axis!).applyMatrix4(jointFrameRotation(j, part, now)).normalize()\n const twist = 2 * Math.atan2(change.x * axis.x + change.y * axis.y + change.z * axis.z, change.w)\n /* Turned about another axis: the joint cannot make that turn, and nothing is written for it. */\n const whole = 2 * Math.acos(Math.min(1, Math.abs(change.w)))\n if (Math.abs(twist) < whole * 0.5) return { kind: 'refused', reason: 'off-axis', joint: j.id, axis: jointAxisWord(j.axis) }\n let value = now + THREE.MathUtils.radToDeg(twist)\n if (endless) value = ((((value + 180) % 360) + 360) % 360) - 180\n else value = Math.min(Math.max(value, j.lowerLimit!), j.upperLimit!)\n return { kind: 'value', input: stateId, unit: 'deg', value: round(value, 0.1) }\n }\n\n const moved = new THREE.Vector3(...result.position!).sub(new THREE.Vector3(...(part.pose.t as [number, number, number])))\n if (moved.length() < 0.5) return null\n const frame = jointFrameRotation(j, part, turns ? now : 0)\n\n if (j.type === 'prismatic') {\n if (j.mimic) return { kind: 'refused', reason: 'mimic', joint: j.id, leader: figureMimicTermsOf(j.mimic)!.sum.map(t => t.joint).join(', ') }\n /* How far one unit of the value moves the part along its axis, measured on the figure itself (a ratio travels a length). */\n const along = unit(j.axis!).applyMatrix4(frame).normalize()\n const at = (v: number) => {\n const g = compileFigureAsset(asset).evaluate({ ...values, [stateId]: v }).geometry.find(g => g.id === id && 'pose' in g) as { pose: { t: number[] } } | undefined\n return g ? new THREE.Vector3(...(g.pose.t as [number, number, number])).dot(along) : 0\n }\n const step = j.travel ? 0.01 : 1\n const rate = (at(now + step) - at(now)) / step\n if (Math.abs(rate) > 1e-9 && Math.abs(moved.dot(along)) >= moved.length() * 0.7) {\n const value = Math.min(Math.max(now + moved.dot(along) / rate, j.lowerLimit!), j.upperLimit!)\n return { kind: 'value', input: stateId, unit: j.travel ? 'ratio' : 'mm', value: round(value, j.travel ? 0.001 : 1) }\n }\n }\n\n /* Moving the joint's origin: the change in the frame it is placed in, before `origin.rotate` turns the joint frame. */\n const local = moved.clone().applyMatrix4(frame.clone().multiply(rotateMatrix(j).invert()).invert())\n const origin: NonNullable<FigureJoint['origin']> = { ...(j.origin ?? {}) }\n if (!('plane' in j.body0) && !('face' in j.body0)) {\n /* At the parent's origin there is no face: the frame moves by its offset, along the parent's axes. */\n const offset = { ...(framesOf(j).offset ?? {}) }\n for (const axis of AXES) {\n const d = local[axis]\n if (Math.abs(d) < 0.5) continue\n const was = offset[axis] ?? 0\n if (typeof was !== 'number') return { kind: 'refused', reason: 'origin-follows', joint: j.id }\n offset[axis] = round(was + d, 1)\n }\n return { kind: 'joint', joint: { ...j, origin: { ...origin, offset } } as FigureJoint }\n }\n const normal: Axis = 'plane' in j.body0 ? 'y' : FACE_AXIS[j.body0.face]!\n /* Which way a larger gap moves the part: out of the face it meets (on the plane: up when it stands, down when it hangs). */\n const outward = 'plane' in j.body0 ? ('face' in j.body1 && j.body1.face === 'bottom' ? 1 : -1) : ((j.origin?.facing ?? 'meet') === 'meet' ? 1 : -1) * FACE_SIGN[j.body0.face]!\n const along = local[normal]\n if (Math.abs(along) >= 0.5) {\n const gap = origin.gap ?? 0\n if (typeof gap !== 'number') return { kind: 'refused', reason: 'origin-follows', joint: j.id }\n origin.gap = round(gap + outward * along, 1)\n }\n for (const axis of AXES.filter(a => a !== normal)) {\n const d = local[axis]\n if (Math.abs(d) < 0.5) continue\n const how = origin.align?.[axis] ?? 'centre'\n const was = how === 'centre' ? 0 : typeof how === 'object' && typeof how.mm === 'number' ? how.mm : null\n if (was === null) return { kind: 'refused', reason: 'origin-follows', joint: j.id }\n origin.align = { ...(origin.align ?? {}), [axis]: { mm: round(was + d, 1) } }\n }\n return { kind: 'joint', joint: { ...j, origin } }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"joint-drag.js","sourceRoot":"","sources":["../../client/modeller/joint-drag.ts"],"names":[],"mappings":"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","sourcesContent":["import * as THREE from 'three'\nimport { compileFigureAsset, figureMimicTermsOf } from '@hatiolab/figure-model'\nimport type { FigureAsset, FigureJoint } from '@hatiolab/figure-model'\n\nimport type { GizmoResult } from './gizmo.js'\nimport { jointAxisUnit, jointAxisWord } from './joint-axis.js'\nimport type { FigureRenderPart } from './renderer.js'\n\n/*\n * A gizmo drag on a part that hangs from a joint edits the joint, not the part (ADR-0093; r3 §3: the modeller changes\n * the inputs of the relation, and never breaks it because a computed place was moved):\n *\n * - turning a part on a revolute or continuous joint sets the joint's value -- the turn about the joint's axis;\n * - sliding a part on a prismatic joint along its axis sets its value;\n * - moving a part any other way moves the joint's origin: the gap along the face it sits on, the offset across it;\n * at a parent's origin (ADR-0107 decision 4), its `origin.offset` in the parent's frame.\n *\n * What would break a relation is refused and said: an origin that follows another part's size, a joint that mimics\n * another, a turn on a joint that does not turn.\n */\n\nexport type JointDrag =\n | { kind: 'value'; input: string; unit: 'deg' | 'mm' | 'ratio'; value: number }\n | { kind: 'joint'; joint: FigureJoint }\n | { kind: 'refused'; reason: 'mimic' | 'fixed-turn' | 'origin-follows' | 'turned-slide' | 'off-axis'; joint: string; leader?: string; axis?: string }\n\ntype Axis = 'x' | 'y' | 'z'\nconst AXES: Axis[] = ['x', 'y', 'z']\n/** The axis each face faces along, and which way (as figure-model's `FIGURE_FACES`). */\nconst FACE_AXIS: Record<string, Axis> = { left: 'x', right: 'x', bottom: 'y', top: 'y', back: 'z', front: 'z' }\nconst FACE_SIGN: Record<string, number> = { left: -1, right: 1, bottom: -1, top: 1, back: -1, front: 1 }\n\nconst matrixOf = (r: number[][]) => new THREE.Matrix4().set(r[0]![0]!, r[0]![1]!, r[0]![2]!, 0, r[1]![0]!, r[1]![1]!, r[1]![2]!, 0, r[2]![0]!, r[2]![1]!, r[2]![2]!, 0, 0, 0, 0, 1)\nconst eulerMatrix = (deg: number[]) => new THREE.Matrix4().makeRotationFromEuler(new THREE.Euler(...(deg.map(THREE.MathUtils.degToRad) as [number, number, number]), 'XYZ'))\nconst unit = (axis: unknown) => new THREE.Vector3(...(jointAxisUnit(axis) ?? [0, 0, 0]))\nconst round = (v: number, step: number) => Number((Math.round(v / step) * step).toFixed(Math.max(0, -Math.floor(Math.log10(step)))))\n\n/** `origin.parentRotate` (ADR-0107, named for its end by ADR-0140 3.6), URDF's rpy: Rz·Ry·Rx about the fixed axes. */\nconst rotateMatrix = (j: FigureJoint) => eulerMatrixOrder(AXES.map(a => j.origin?.parentRotate?.[a] ?? 0), 'ZYX')\nconst eulerMatrixOrder = (deg: number[], order: THREE.EulerOrder) => new THREE.Matrix4().makeRotationFromEuler(new THREE.Euler(...(deg.map(THREE.MathUtils.degToRad) as [number, number, number]), order))\n\n/** The part's own quarter turn in the joint frame, as the record says it (`origin.childRotate`). */\nconst turnMatrix = (j: FigureJoint) => eulerMatrix(AXES.map(a => j.origin?.childRotate?.[a] ?? 0))\n\n/** The joint frame's rotation in the figure: the part's rotation now, less its own turn and less the joint's. */\nfunction jointFrameRotation(j: FigureJoint, part: FigureRenderPart, angle: number): THREE.Matrix4 {\n const now = matrixOf(part.pose.r as number[][])\n const own = turnMatrix(j)\n const turn = j.type === 'revolute' ? new THREE.Matrix4().makeRotationAxis(unit(j.axis!), THREE.MathUtils.degToRad(angle)) : new THREE.Matrix4()\n return now.multiply(own.invert()).multiply(turn.invert())\n}\n\nexport function jointDragOf(asset: FigureAsset, id: string, part: FigureRenderPart, result: GizmoResult, values: Record<string, number>): JointDrag | null {\n const j = asset.joints?.find(joint => joint.body1.part === id)\n /* A size is the part's own, not the joint's: a scale drag goes to the part's measures (the editor drops the shift). */\n if (!j || result.scale || (!result.rotation && !result.position)) return null\n const stateId = j.state?.id ?? j.id\n const now = values[stateId] ?? asset.stateDefaults[stateId] ?? 0\n const turns = j.type === 'revolute'\n /* A revolute joint without limits turns without end (USD). */\n const endless = turns && j.lowerLimit === undefined && j.upperLimit === undefined\n\n if (result.rotation) {\n if (!turns) return { kind: 'refused', reason: j.type === 'prismatic' ? 'turned-slide' : 'fixed-turn', joint: j.id }\n if (j.mimic) return { kind: 'refused', reason: 'mimic', joint: j.id, leader: figureMimicTermsOf(j.mimic)!.sum.map(t => t.joint).join(', ') }\n /* The turn about the joint's axis (the twist of the change): the only turn the joint can make. */\n const before = new THREE.Quaternion().setFromRotationMatrix(matrixOf(part.pose.r as number[][]))\n const after = new THREE.Quaternion().setFromRotationMatrix(eulerMatrix(result.rotation))\n const change = after.multiply(before.invert())\n const axis = unit(j.axis!).applyMatrix4(jointFrameRotation(j, part, now)).normalize()\n const twist = 2 * Math.atan2(change.x * axis.x + change.y * axis.y + change.z * axis.z, change.w)\n /* Turned about another axis: the joint cannot make that turn, and nothing is written for it. */\n const whole = 2 * Math.acos(Math.min(1, Math.abs(change.w)))\n if (Math.abs(twist) < whole * 0.5) return { kind: 'refused', reason: 'off-axis', joint: j.id, axis: jointAxisWord(j.axis) }\n let value = now + THREE.MathUtils.radToDeg(twist)\n if (endless) value = ((((value + 180) % 360) + 360) % 360) - 180\n else value = Math.min(Math.max(value, j.lowerLimit!), j.upperLimit!)\n return { kind: 'value', input: stateId, unit: 'deg', value: round(value, 0.1) }\n }\n\n const moved = new THREE.Vector3(...result.position!).sub(new THREE.Vector3(...(part.pose.t as [number, number, number])))\n if (moved.length() < 0.5) return null\n const frame = jointFrameRotation(j, part, turns ? now : 0)\n\n if (j.type === 'prismatic') {\n if (j.mimic) return { kind: 'refused', reason: 'mimic', joint: j.id, leader: figureMimicTermsOf(j.mimic)!.sum.map(t => t.joint).join(', ') }\n /* How far one unit of the value moves the part along its axis, measured on the figure itself (a ratio travels a length). */\n const along = unit(j.axis!).applyMatrix4(frame).normalize()\n const at = (v: number) => {\n const g = compileFigureAsset(asset).evaluate({ ...values, [stateId]: v }).geometry.find(g => g.id === id && 'pose' in g) as { pose: { t: number[] } } | undefined\n return g ? new THREE.Vector3(...(g.pose.t as [number, number, number])).dot(along) : 0\n }\n const step = j.travel ? 0.01 : 1\n const rate = (at(now + step) - at(now)) / step\n if (Math.abs(rate) > 1e-9 && Math.abs(moved.dot(along)) >= moved.length() * 0.7) {\n const value = Math.min(Math.max(now + moved.dot(along) / rate, j.lowerLimit!), j.upperLimit!)\n return { kind: 'value', input: stateId, unit: j.travel ? 'ratio' : 'mm', value: round(value, j.travel ? 0.001 : 1) }\n }\n }\n\n /* Moving the joint's origin: the change in the frame it is placed in, before `origin.parentRotate` turns the joint frame. */\n const local = moved.clone().applyMatrix4(frame.clone().multiply(rotateMatrix(j).invert()).invert())\n const origin: NonNullable<FigureJoint['origin']> = { ...(j.origin ?? {}) }\n if (!('plane' in j.body0) && !('face' in j.body0)) {\n /* At the parent's origin there is no face: the frame moves by its offset, along the parent's axes. */\n const offset = { ...(j.origin?.offset ?? {}) }\n for (const axis of AXES) {\n const d = local[axis]\n if (Math.abs(d) < 0.5) continue\n const was = offset[axis] ?? 0\n if (typeof was !== 'number') return { kind: 'refused', reason: 'origin-follows', joint: j.id }\n offset[axis] = round(was + d, 1)\n }\n return { kind: 'joint', joint: { ...j, origin: { ...origin, offset } } as FigureJoint }\n }\n const normal: Axis = 'plane' in j.body0 ? 'y' : FACE_AXIS[j.body0.face]!\n /* Which way a larger gap moves the part: out of the face it meets (on the plane: up when it stands, down when it hangs). */\n const outward = 'plane' in j.body0 ? ('face' in j.body1 && j.body1.face === 'bottom' ? 1 : -1) : ((j.origin?.facing ?? 'meet') === 'meet' ? 1 : -1) * FACE_SIGN[j.body0.face]!\n const along = local[normal]\n if (Math.abs(along) >= 0.5) {\n const gap = origin.gap ?? 0\n if (typeof gap !== 'number') return { kind: 'refused', reason: 'origin-follows', joint: j.id }\n origin.gap = round(gap + outward * along, 1)\n }\n for (const axis of AXES.filter(a => a !== normal)) {\n const d = local[axis]\n if (Math.abs(d) < 0.5) continue\n const how = origin.align?.[axis] ?? 'centre'\n const was = how === 'centre' ? 0 : typeof how === 'object' && typeof how.mm === 'number' ? how.mm : null\n if (was === null) return { kind: 'refused', reason: 'origin-follows', joint: j.id }\n origin.align = { ...(origin.align ?? {}), [axis]: { mm: round(was + d, 1) } }\n }\n return { kind: 'joint', joint: { ...j, origin } }\n}\n"]}
|