@things-factory/figure-ui 10.1.270 → 10.1.272

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.
Files changed (113) hide show
  1. package/client/graphql/index.ts +2 -0
  2. package/client/modeller/canvas.ts +4 -2
  3. package/client/modeller/clips.ts +74 -4
  4. package/client/modeller/data-interface.ts +14 -0
  5. package/client/modeller/editor-styles.ts +11 -0
  6. package/client/modeller/editor.ts +39 -11
  7. package/client/modeller/fasten-form.ts +73 -3
  8. package/client/modeller/fasten-geometry.ts +11 -1
  9. package/client/modeller/figure-cavity-drawing.ts +1 -3
  10. package/client/modeller/figure-clips.ts +101 -8
  11. package/client/modeller/figure-fasten-dialog.ts +41 -30
  12. package/client/modeller/figure-fasten-drawing.ts +5 -4
  13. package/client/modeller/figure-first-save.ts +4 -3
  14. package/client/modeller/figure-links.ts +85 -28
  15. package/client/modeller/figure-preview.ts +50 -4
  16. package/client/modeller/figure-size-editor.ts +7 -2
  17. package/client/modeller/first-save.ts +8 -0
  18. package/client/modeller/focus-on-render.ts +4 -0
  19. package/client/modeller/palette.ts +27 -0
  20. package/client/modeller/pivot.ts +76 -0
  21. package/client/modeller/product-session.ts +5 -1
  22. package/client/modeller/size-response.ts +14 -0
  23. package/client/modeller/stage-model.ts +50 -6
  24. package/client/types.ts +2 -0
  25. package/dist-client/graphql/index.js +2 -0
  26. package/dist-client/graphql/index.js.map +1 -1
  27. package/dist-client/modeller/canvas.d.ts +1 -0
  28. package/dist-client/modeller/canvas.js +3 -2
  29. package/dist-client/modeller/canvas.js.map +1 -1
  30. package/dist-client/modeller/clips.d.ts +23 -6
  31. package/dist-client/modeller/clips.js +69 -4
  32. package/dist-client/modeller/clips.js.map +1 -1
  33. package/dist-client/modeller/data-interface.d.ts +5 -0
  34. package/dist-client/modeller/data-interface.js +15 -0
  35. package/dist-client/modeller/data-interface.js.map +1 -1
  36. package/dist-client/modeller/editor-styles.js +11 -0
  37. package/dist-client/modeller/editor-styles.js.map +1 -1
  38. package/dist-client/modeller/editor.d.ts +6 -0
  39. package/dist-client/modeller/editor.js +35 -11
  40. package/dist-client/modeller/editor.js.map +1 -1
  41. package/dist-client/modeller/fasten-form.d.ts +8 -0
  42. package/dist-client/modeller/fasten-form.js +68 -5
  43. package/dist-client/modeller/fasten-form.js.map +1 -1
  44. package/dist-client/modeller/fasten-geometry.d.ts +6 -0
  45. package/dist-client/modeller/fasten-geometry.js +10 -1
  46. package/dist-client/modeller/fasten-geometry.js.map +1 -1
  47. package/dist-client/modeller/figure-cavity-drawing.js +1 -3
  48. package/dist-client/modeller/figure-cavity-drawing.js.map +1 -1
  49. package/dist-client/modeller/figure-clips.d.ts +9 -0
  50. package/dist-client/modeller/figure-clips.js +104 -8
  51. package/dist-client/modeller/figure-clips.js.map +1 -1
  52. package/dist-client/modeller/figure-fasten-dialog.js +39 -24
  53. package/dist-client/modeller/figure-fasten-dialog.js.map +1 -1
  54. package/dist-client/modeller/figure-fasten-drawing.js +5 -4
  55. package/dist-client/modeller/figure-fasten-drawing.js.map +1 -1
  56. package/dist-client/modeller/figure-first-save.js +4 -3
  57. package/dist-client/modeller/figure-first-save.js.map +1 -1
  58. package/dist-client/modeller/figure-links.d.ts +9 -0
  59. package/dist-client/modeller/figure-links.js +89 -35
  60. package/dist-client/modeller/figure-links.js.map +1 -1
  61. package/dist-client/modeller/figure-preview.js +48 -4
  62. package/dist-client/modeller/figure-preview.js.map +1 -1
  63. package/dist-client/modeller/figure-size-editor.js +7 -3
  64. package/dist-client/modeller/figure-size-editor.js.map +1 -1
  65. package/dist-client/modeller/first-save.d.ts +5 -0
  66. package/dist-client/modeller/first-save.js +7 -0
  67. package/dist-client/modeller/first-save.js.map +1 -1
  68. package/dist-client/modeller/focus-on-render.d.ts +2 -0
  69. package/dist-client/modeller/focus-on-render.js +4 -0
  70. package/dist-client/modeller/focus-on-render.js.map +1 -0
  71. package/dist-client/modeller/links.d.ts +1 -1
  72. package/dist-client/modeller/palette.d.ts +14 -0
  73. package/dist-client/modeller/palette.js +23 -0
  74. package/dist-client/modeller/palette.js.map +1 -1
  75. package/dist-client/modeller/pivot.d.ts +12 -0
  76. package/dist-client/modeller/pivot.js +59 -0
  77. package/dist-client/modeller/pivot.js.map +1 -0
  78. package/dist-client/modeller/product-session.js +6 -2
  79. package/dist-client/modeller/product-session.js.map +1 -1
  80. package/dist-client/modeller/size-response.d.ts +6 -0
  81. package/dist-client/modeller/size-response.js +14 -0
  82. package/dist-client/modeller/size-response.js.map +1 -1
  83. package/dist-client/modeller/stage-model.d.ts +17 -3
  84. package/dist-client/modeller/stage-model.js +44 -6
  85. package/dist-client/modeller/stage-model.js.map +1 -1
  86. package/dist-client/route.d.ts +1 -1
  87. package/dist-client/tsconfig.tsbuildinfo +1 -1
  88. package/dist-client/types.d.ts +2 -0
  89. package/dist-client/types.js.map +1 -1
  90. package/package.json +5 -5
  91. package/test/browser/editing.check.mjs +6 -5
  92. package/test/browser/section.check.mjs +21 -0
  93. package/test/clips.test.ts +49 -4
  94. package/test/data-motion.test.ts +8 -0
  95. package/test/fasten-form.test.ts +57 -0
  96. package/test/fasten-geometry.test.ts +25 -1
  97. package/test/first-part-on-plane.test.ts +52 -0
  98. package/test/first-save.test.ts +11 -1
  99. package/test/pivot.test.ts +57 -0
  100. package/test/save-landed.test.ts +20 -1
  101. package/test/size-response.test.ts +10 -2
  102. package/test/stage-model.test.ts +23 -1
  103. package/translations/en.json +9 -0
  104. package/translations/es.json +9 -0
  105. package/translations/ja.json +9 -0
  106. package/translations/ko.json +9 -0
  107. package/translations/ms.json +9 -0
  108. package/translations/zh.json +9 -0
  109. package/tutorial/humanoid/README.md +22 -0
  110. package/tutorial/humanoid/chapters.mjs +247 -0
  111. package/tutorial/humanoid/encode.mjs +26 -0
  112. package/tutorial/humanoid/lib.mjs +197 -0
  113. package/tutorial/humanoid/record.mjs +65 -0
@@ -146,20 +146,58 @@ export function boardFor(parts, at = { width: 0, height: 0 }) {
146
146
  return {
147
147
  width: Math.max(at.width, Math.ceil(reach('x') * 1.2), 200),
148
148
  height: Math.max(at.height, Math.ceil(reach('z') * 1.2), 200),
149
- /* Only grows, as the plan does: a raised arm made the board taller than it was, and a new board lost the camera. */
150
- depth: Math.max(at.depth ?? 0, Math.ceil(size.y * 1.1), 200)
149
+ /*
150
+ Only grows, as the plan does: a raised arm made the board taller than it was, and a new board lost the camera. The
151
+ height is reached from the floor, as width and depth are from the middle: a pelvis set 880 mm up stood as tall as
152
+ its own 180 mm, and the view never followed it up (review 2026-11-11).
153
+ */
154
+ depth: Math.max(at.depth ?? 0, Math.ceil(Math.max(size.y, box.isEmpty() ? 0 : Math.max(Math.abs(box.max.y), Math.abs(box.min.y))) * 1.1), 200)
151
155
  };
152
156
  }
157
+ export function drawnSpanOf(parts) {
158
+ const box = new THREE.Box3();
159
+ for (const part of parts) {
160
+ const frame = frameOf(part);
161
+ if (!frame)
162
+ continue;
163
+ const half = frame.extent.clone().multiplyScalar(0.5);
164
+ for (const x of [-1, 1])
165
+ for (const y of [-1, 1])
166
+ for (const z of [-1, 1])
167
+ box.expandByPoint(new THREE.Vector3(x * half.x, y * half.y, z * half.z).add(frame.centre).applyMatrix4(frame.rotation).add(frame.origin));
168
+ }
169
+ return box.isEmpty() ? undefined : { min: box.min.toArray(), max: box.max.toArray() };
170
+ }
171
+ /**
172
+ * Whether the figure now reaches past the box the view was fitted round, on any side, by more than a tenth of that box
173
+ * (review 2026-11-11: a thigh hung under a pelvis ran off the bottom of the view; the board's size alone, measured from
174
+ * the middle and the floor, had not changed enough to say so).
175
+ */
176
+ export function outgrowsFit(fitted, now) {
177
+ if (!fitted || !now)
178
+ return false;
179
+ return [0, 1, 2].some(i => {
180
+ const room = Math.max((fitted.max[i] - fitted.min[i]) * 0.1, 20);
181
+ return now.max[i] > fitted.max[i] + room || now.min[i] < fitted.min[i] - room;
182
+ });
183
+ }
184
+ /** How far the figure may grow past its last fit before the view follows: inside the fit's own margin. */
185
+ export const REFIT_GROWN = 1.1;
153
186
  /**
154
187
  * Whether the figure's reach has changed so far since the view was last fitted that the camera should fit it again:
155
- * more than twice, or less than half, on any axis (first 15 minutes, 2026-10-16: a 100 mm box typed to 3000 × 800 × 600
156
- * put the camera inside it, and the author saw grey until pressing 「화면에 맞추기」). A drag that nudges a part does not
157
- * move the camera.
188
+ * grown past `REFIT_GROWN`, or shrunk below half, on any axis (first 15 minutes, 2026-10-16: a 100 mm box typed to
189
+ * 3000 × 800 × 600 put the camera inside it, and the author saw grey until pressing 「화면에 맞추기」). A drag that nudges
190
+ * a part within the board's rounding does not move the camera.
158
191
  */
159
192
  export function needsRefit(fitted, now) {
160
193
  if (!fitted)
161
194
  return false;
162
- return ['width', 'height', 'depth'].some(key => now[key] > fitted[key] * 2 || now[key] < fitted[key] / 2);
195
+ /*
196
+ Grown past the room the fit leaves round it (the fit's margin, 1.15) the figure runs off the view: a head added on a
197
+ body went out at the top, and the author pressed the fit after every part (review 2026-11-11). Shrunk, it is only
198
+ small in the view: fitted again at half.
199
+ */
200
+ return ['width', 'height', 'depth'].some(key => now[key] > fitted[key] * REFIT_GROWN || now[key] < fitted[key] / 2);
163
201
  }
164
202
  /**
165
203
  * A body's placement as its fields would read it: its origin in mm and its turn in degrees (rigid@1's X·Y·Z order, as
@@ -1 +1 @@
1
- {"version":3,"file":"stage-model.js","sourceRoot":"","sources":["../../client/modeller/stage-model.ts"],"names":[],"mappings":"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","sourcesContent":["import * as THREE from 'three'\nimport { buildFigureGeometry } from '@hatiolab/things-scene'\n\nimport type { GizmoResult } from './gizmo.js'\nimport type { FigureRenderPart } from './renderer.js'\n\n/*\n * bodies <-> scene components, in one place.\n *\n * The modeller stands on a things-scene board in editing mode: each body figure-model evaluates is a\n * `figure-part` component, and what the scene's gizmo writes back comes here to become a change of the part's\n * inputs. The two vocabularies:\n *\n * (asset frame, mm, y up) scene component\n * ──────────────────────────────── ─────────────────────────────────────────────\n * body box extent x · y · z width · depth · height\n * body box centre x left + width/2 - board width/2\n * y zPos + depth/2\n * z top + height/2 - board height/2\n * body rotation (rigid@1, Rx·Ry·Rz) rotationX · -rotation · rotationY (radians; three 'XYZ')\n *\n * The box is the body's own bounding box in its local frame, so a turned body keeps its size and the gizmo's handles\n * sit on its faces. Its centre is not always the body's origin (a portal rises from its bottom), so a turn about the\n * centre moves the origin: the result gives the origin where the turned body puts it.\n */\n\nexport interface Board {\n width: number\n height: number\n}\n\nexport interface BodyFrame {\n /** The body's box: its centre in the body's own frame, and its extent. */\n centre: THREE.Vector3\n extent: THREE.Vector3\n rotation: THREE.Matrix4\n origin: THREE.Vector3\n}\n\nconst round = (n: number) => Math.round(n * 1e6) / 1e6\n\nexport function frameOf(part: FigureRenderPart): BodyFrame | null {\n /* The box things-scene's `figure-part` centres the body on: the same builder, measured the same way. */\n let built: THREE.BufferGeometry | undefined\n try {\n built = buildFigureGeometry(part.shape as never, part.segments) as THREE.BufferGeometry | undefined\n } catch {\n return null\n }\n if (!built?.getAttribute('position')) return null\n built.computeBoundingBox()\n const box = built.boundingBox!\n const unit = { centre: box.getCenter(new THREE.Vector3()), extent: box.getSize(new THREE.Vector3()) }\n built.dispose()\n const r = part.pose.r\n const rotation = 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)\n return { centre: unit.centre, extent: unit.extent, rotation, origin: new THREE.Vector3(...(part.pose.t as [number, number, number])) }\n}\n\n/** The colours a body is drawn in, from the palette the modeller resolved (the asset's, over which the theme wins). */\nfunction colours(part: FigureRenderPart, palette: Record<string, string>) {\n const m = part.material\n const glow = m.emissive?.on ? { color: palette[m.emissive.token] ?? palette[m.token] ?? '#9a9a9a', intensity: m.emissive.intensity } : undefined\n return { color: palette[m.token] ?? '#9a9a9a', preset: m.preset, transparent: !!m.transparent, ...(glow ? { glow } : {}) }\n}\n\n/**\n * The component state that draws a body where figure-model put it. A part the author edits is `owned`: while its size\n * is dragged the scene does not move it and tells the handle held (`scaleGrab`), and the modeller draws where\n * figure-model's size function puts it, every frame (ADR-0140 4.4). Body x · y · z are the component's width · depth ·\n * height, as the scene's axes are.\n */\nexport function componentOf(part: FigureRenderPart, palette: Record<string, string>, board: Board, owned = false): Record<string, unknown> | null {\n const frame = frameOf(part)\n if (!frame) return null\n const { centre, extent, rotation, origin } = frame\n const world = centre.clone().applyMatrix4(rotation).add(origin)\n const euler = new THREE.Euler().setFromRotationMatrix(rotation, 'XYZ')\n const width = extent.x || 1,\n depth = extent.y || 1,\n height = extent.z || 1\n return {\n type: 'figure-part',\n name: part.id,\n shape: part.shape,\n ...(part.segments !== undefined ? { segments: part.segments } : {}),\n ...colours(part, palette),\n width: round(width),\n depth: round(depth),\n height: round(height),\n left: round(world.x + board.width / 2 - width / 2),\n top: round(world.z + board.height / 2 - height / 2),\n zPos: round(world.y - depth / 2),\n rotationX: round(euler.x),\n rotation: round(-euler.y),\n rotationY: round(euler.z),\n ...(owned ? { scalePlacement: 'owner' } : {})\n }\n}\n\nconst num = (v: unknown, fallback = 0) => (typeof v === 'number' && Number.isFinite(v) ? v : fallback)\n\n/**\n * What the gizmo did, in the inputs' terms: where the body's origin is now, how it is turned (degrees, rigid@1's\n * order) and how much each of its own axes grew. Only what changed is given; a component the gizmo did not move gives\n * nothing.\n */\nexport function gizmoResultOf(part: FigureRenderPart, after: Record<string, unknown>, board: Board): GizmoResult {\n const frame = frameOf(part)\n if (!frame) return {}\n const { centre, extent, rotation, origin } = frame\n const width = num(after.width, extent.x),\n depth = num(after.depth, extent.y),\n height = num(after.height, extent.z)\n const scale = new THREE.Vector3(width / (extent.x || 1), depth / (extent.y || 1), height / (extent.z || 1))\n const world = new THREE.Vector3(num(after.left) + width / 2 - board.width / 2, num(after.zPos) + depth / 2, num(after.top) + height / 2 - board.height / 2)\n const euler = new THREE.Euler(num(after.rotationX), -num(after.rotation), num(after.rotationY), 'XYZ')\n const turnedTo = new THREE.Matrix4().makeRotationFromEuler(euler)\n\n const before = new THREE.Quaternion().setFromRotationMatrix(rotation)\n const now = new THREE.Quaternion().setFromEuler(euler)\n const turned = before.angleTo(now) > 1e-4\n const sized = [scale.x, scale.y, scale.z].some(s => Math.abs(s - 1) > 1e-4)\n /* The origin that puts the (scaled, turned) box centre where the scene has it. */\n const newOrigin = world.clone().sub(centre.clone().multiply(scale).applyMatrix4(turnedTo))\n const moved = newOrigin.distanceTo(origin) > 0.5\n\n const result: GizmoResult = {}\n if (moved || turned) result.position = newOrigin.toArray() as [number, number, number]\n if (turned) result.rotation = [euler.x, euler.y, euler.z].map(THREE.MathUtils.radToDeg) as [number, number, number]\n if (sized) result.scale = scale.toArray() as [number, number, number]\n return result\n}\n\n/**\n * A drag as amounts, for a part whose place is worked out (`nudge-part`): how far its origin moved and how far each of\n * its angles turned (rigid@1's order, degrees). Nothing on an axis that did not change.\n */\nexport function nudgeOf(part: FigureRenderPart, result: GizmoResult): { by: Partial<Record<'x' | 'y' | 'z', number>>; turn: Partial<Record<'x' | 'y' | 'z', number>> } {\n const axes = ['x', 'y', 'z'] as const\n const by: Partial<Record<'x' | 'y' | 'z', number>> = {}\n const turn: Partial<Record<'x' | 'y' | 'z', number>> = {}\n if (result.position) axes.forEach((axis, i) => {\n const d = round(result.position![i]! - (part.pose.t[i] as number))\n if (Math.abs(d) > 1e-6) by[axis] = d\n })\n if (result.rotation) {\n const r = part.pose.r\n const now = new THREE.Euler().setFromRotationMatrix(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), 'XYZ')\n const was: number[] = [now.x, now.y, now.z].map(radians => (radians * 180) / Math.PI)\n axes.forEach((axis, i) => {\n const d = round(result.rotation![i]! - was[i]!)\n if (Math.abs(d) > 1e-6) turn[axis] = d\n })\n }\n return { by, turn }\n}\n\n/** The keys of `next` whose values differ from `current` -- only those are set, so the scene raises no echo. */\nexport function changedKeys(current: Record<string, unknown>, next: Record<string, unknown>): Record<string, unknown> | null {\n const diff: Record<string, unknown> = {}\n for (const [key, value] of Object.entries(next)) {\n if (key === 'type') continue\n const was = current[key]\n const same = typeof value === 'object' && value !== null ? JSON.stringify(was) === JSON.stringify(value) : was === value\n if (!same) diff[key] = value\n }\n return Object.keys(diff).length ? diff : null\n}\n\n/** A board wide and deep enough for the figure, with room around it; its size only grows while a figure is edited. */\nexport function boardFor(parts: readonly FigureRenderPart[], at: Board & { depth?: number } = { width: 0, height: 0 }): Board & { depth: number } {\n const box = new THREE.Box3()\n for (const part of parts) {\n const frame = frameOf(part)\n if (!frame) continue\n const half = frame.extent.clone().multiplyScalar(0.5)\n const corners = [-1, 1].flatMap(x => [-1, 1].flatMap(y => [-1, 1].map(z => new THREE.Vector3(x * half.x, y * half.y, z * half.z))))\n for (const corner of corners) box.expandByPoint(corner.add(frame.centre).applyMatrix4(frame.rotation).add(frame.origin))\n }\n const size = box.isEmpty() ? new THREE.Vector3(1000, 1000, 1000) : box.getSize(new THREE.Vector3())\n const reach = (axis: 'x' | 'z') => 2 * Math.max(Math.abs(box.isEmpty() ? 500 : box.min[axis]), Math.abs(box.isEmpty() ? 500 : box.max[axis]))\n return {\n width: Math.max(at.width, Math.ceil(reach('x') * 1.2), 200),\n height: Math.max(at.height, Math.ceil(reach('z') * 1.2), 200),\n /* Only grows, as the plan does: a raised arm made the board taller than it was, and a new board lost the camera. */\n depth: Math.max(at.depth ?? 0, Math.ceil(size.y * 1.1), 200)\n }\n}\n\n/**\n * Whether the figure's reach has changed so far since the view was last fitted that the camera should fit it again:\n * more than twice, or less than half, on any axis (first 15 minutes, 2026-10-16: a 100 mm box typed to 3000 × 800 × 600\n * put the camera inside it, and the author saw grey until pressing 「화면에 맞추기」). A drag that nudges a part does not\n * move the camera.\n */\nexport function needsRefit(fitted: (Board & { depth: number }) | undefined, now: Board & { depth: number }): boolean {\n if (!fitted) return false\n return (['width', 'height', 'depth'] as const).some(key => now[key] > fitted[key] * 2 || now[key] < fitted[key] / 2)\n}\n\n/**\n * A body's placement as its fields would read it: its origin in mm and its turn in degrees (rigid@1's X·Y·Z order, as\n * `componentOf` reads it). For a field a rule sets -- a fastened part's height, a turn its joint fixes -- the screen shows\n * this number read-only rather than a blank (designer 2026-09-27).\n */\nexport function placementOf(part: FigureRenderPart): { position: [number, number, number]; rotation: [number, number, number] } {\n const r = part.pose.r\n const m = 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)\n const e = new THREE.Euler().setFromRotationMatrix(m, 'XYZ')\n const deg = (v: number) => Math.round(THREE.MathUtils.radToDeg(v) * 10) / 10 || 0\n return { position: part.pose.t.map(round) as [number, number, number], rotation: [deg(e.x), deg(e.y), deg(e.z)] }\n}\n"]}
1
+ {"version":3,"file":"stage-model.js","sourceRoot":"","sources":["../../client/modeller/stage-model.ts"],"names":[],"mappings":"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","sourcesContent":["import * as THREE from 'three'\nimport { buildFigureGeometry } from '@hatiolab/things-scene'\n\nimport type { GizmoResult } from './gizmo.js'\nimport type { FigureRenderPart } from './renderer.js'\n\n/*\n * bodies <-> scene components, in one place.\n *\n * The modeller stands on a things-scene board in editing mode: each body figure-model evaluates is a\n * `figure-part` component, and what the scene's gizmo writes back comes here to become a change of the part's\n * inputs. The two vocabularies:\n *\n * (asset frame, mm, y up) scene component\n * ──────────────────────────────── ─────────────────────────────────────────────\n * body box extent x · y · z width · depth · height\n * body box centre x left + width/2 - board width/2\n * y zPos + depth/2\n * z top + height/2 - board height/2\n * body rotation (rigid@1, Rx·Ry·Rz) rotationX · -rotation · rotationY (radians; three 'XYZ')\n *\n * The box is the body's own bounding box in its local frame, so a turned body keeps its size and the gizmo's handles\n * sit on its faces. Its centre is not always the body's origin (a portal rises from its bottom), so a turn about the\n * centre moves the origin: the result gives the origin where the turned body puts it.\n */\n\nexport interface Board {\n width: number\n height: number\n}\n\nexport interface BodyFrame {\n /** The body's box: its centre in the body's own frame, and its extent. */\n centre: THREE.Vector3\n extent: THREE.Vector3\n rotation: THREE.Matrix4\n origin: THREE.Vector3\n}\n\nconst round = (n: number) => Math.round(n * 1e6) / 1e6\n\nexport function frameOf(part: FigureRenderPart): BodyFrame | null {\n /* The box things-scene's `figure-part` centres the body on: the same builder, measured the same way. */\n let built: THREE.BufferGeometry | undefined\n try {\n built = buildFigureGeometry(part.shape as never, part.segments) as THREE.BufferGeometry | undefined\n } catch {\n return null\n }\n if (!built?.getAttribute('position')) return null\n built.computeBoundingBox()\n const box = built.boundingBox!\n const unit = { centre: box.getCenter(new THREE.Vector3()), extent: box.getSize(new THREE.Vector3()) }\n built.dispose()\n const r = part.pose.r\n const rotation = 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)\n return { centre: unit.centre, extent: unit.extent, rotation, origin: new THREE.Vector3(...(part.pose.t as [number, number, number])) }\n}\n\n/** The colours a body is drawn in, from the palette the modeller resolved (the asset's, over which the theme wins). */\nfunction colours(part: FigureRenderPart, palette: Record<string, string>) {\n const m = part.material\n const glow = m.emissive?.on ? { color: palette[m.emissive.token] ?? palette[m.token] ?? '#9a9a9a', intensity: m.emissive.intensity } : undefined\n return { color: palette[m.token] ?? '#9a9a9a', preset: m.preset, transparent: !!m.transparent, ...(glow ? { glow } : {}) }\n}\n\n/**\n * The component state that draws a body where figure-model put it. A part the author edits is `owned`: while its size\n * is dragged the scene does not move it and tells the handle held (`scaleGrab`), and the modeller draws where\n * figure-model's size function puts it, every frame (ADR-0140 4.4). Body x · y · z are the component's width · depth ·\n * height, as the scene's axes are.\n */\nexport function componentOf(part: FigureRenderPart, palette: Record<string, string>, board: Board, owned = false): Record<string, unknown> | null {\n const frame = frameOf(part)\n if (!frame) return null\n const { centre, extent, rotation, origin } = frame\n const world = centre.clone().applyMatrix4(rotation).add(origin)\n const euler = new THREE.Euler().setFromRotationMatrix(rotation, 'XYZ')\n const width = extent.x || 1,\n depth = extent.y || 1,\n height = extent.z || 1\n return {\n type: 'figure-part',\n name: part.id,\n shape: part.shape,\n ...(part.segments !== undefined ? { segments: part.segments } : {}),\n ...colours(part, palette),\n width: round(width),\n depth: round(depth),\n height: round(height),\n left: round(world.x + board.width / 2 - width / 2),\n top: round(world.z + board.height / 2 - height / 2),\n zPos: round(world.y - depth / 2),\n rotationX: round(euler.x),\n rotation: round(-euler.y),\n rotationY: round(euler.z),\n ...(owned ? { scalePlacement: 'owner' } : {})\n }\n}\n\nconst num = (v: unknown, fallback = 0) => (typeof v === 'number' && Number.isFinite(v) ? v : fallback)\n\n/**\n * What the gizmo did, in the inputs' terms: where the body's origin is now, how it is turned (degrees, rigid@1's\n * order) and how much each of its own axes grew. Only what changed is given; a component the gizmo did not move gives\n * nothing.\n */\nexport function gizmoResultOf(part: FigureRenderPart, after: Record<string, unknown>, board: Board): GizmoResult {\n const frame = frameOf(part)\n if (!frame) return {}\n const { centre, extent, rotation, origin } = frame\n const width = num(after.width, extent.x),\n depth = num(after.depth, extent.y),\n height = num(after.height, extent.z)\n const scale = new THREE.Vector3(width / (extent.x || 1), depth / (extent.y || 1), height / (extent.z || 1))\n const world = new THREE.Vector3(num(after.left) + width / 2 - board.width / 2, num(after.zPos) + depth / 2, num(after.top) + height / 2 - board.height / 2)\n const euler = new THREE.Euler(num(after.rotationX), -num(after.rotation), num(after.rotationY), 'XYZ')\n const turnedTo = new THREE.Matrix4().makeRotationFromEuler(euler)\n\n const before = new THREE.Quaternion().setFromRotationMatrix(rotation)\n const now = new THREE.Quaternion().setFromEuler(euler)\n const turned = before.angleTo(now) > 1e-4\n const sized = [scale.x, scale.y, scale.z].some(s => Math.abs(s - 1) > 1e-4)\n /* The origin that puts the (scaled, turned) box centre where the scene has it. */\n const newOrigin = world.clone().sub(centre.clone().multiply(scale).applyMatrix4(turnedTo))\n const moved = newOrigin.distanceTo(origin) > 0.5\n\n const result: GizmoResult = {}\n if (moved || turned) result.position = newOrigin.toArray() as [number, number, number]\n if (turned) result.rotation = [euler.x, euler.y, euler.z].map(THREE.MathUtils.radToDeg) as [number, number, number]\n if (sized) result.scale = scale.toArray() as [number, number, number]\n return result\n}\n\n/**\n * A drag as amounts, for a part whose place is worked out (`nudge-part`): how far its origin moved and how far each of\n * its angles turned (rigid@1's order, degrees). Nothing on an axis that did not change.\n */\nexport function nudgeOf(part: FigureRenderPart, result: GizmoResult): { by: Partial<Record<'x' | 'y' | 'z', number>>; turn: Partial<Record<'x' | 'y' | 'z', number>> } {\n const axes = ['x', 'y', 'z'] as const\n const by: Partial<Record<'x' | 'y' | 'z', number>> = {}\n const turn: Partial<Record<'x' | 'y' | 'z', number>> = {}\n if (result.position) axes.forEach((axis, i) => {\n const d = round(result.position![i]! - (part.pose.t[i] as number))\n if (Math.abs(d) > 1e-6) by[axis] = d\n })\n if (result.rotation) {\n const r = part.pose.r\n const now = new THREE.Euler().setFromRotationMatrix(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), 'XYZ')\n const was: number[] = [now.x, now.y, now.z].map(radians => (radians * 180) / Math.PI)\n axes.forEach((axis, i) => {\n const d = round(result.rotation![i]! - was[i]!)\n if (Math.abs(d) > 1e-6) turn[axis] = d\n })\n }\n return { by, turn }\n}\n\n/** The keys of `next` whose values differ from `current` -- only those are set, so the scene raises no echo. */\nexport function changedKeys(current: Record<string, unknown>, next: Record<string, unknown>): Record<string, unknown> | null {\n const diff: Record<string, unknown> = {}\n for (const [key, value] of Object.entries(next)) {\n if (key === 'type') continue\n const was = current[key]\n const same = typeof value === 'object' && value !== null ? JSON.stringify(was) === JSON.stringify(value) : was === value\n if (!same) diff[key] = value\n }\n return Object.keys(diff).length ? diff : null\n}\n\n/** A board wide and deep enough for the figure, with room around it; its size only grows while a figure is edited. */\nexport function boardFor(parts: readonly FigureRenderPart[], at: Board & { depth?: number } = { width: 0, height: 0 }): Board & { depth: number } {\n const box = new THREE.Box3()\n for (const part of parts) {\n const frame = frameOf(part)\n if (!frame) continue\n const half = frame.extent.clone().multiplyScalar(0.5)\n const corners = [-1, 1].flatMap(x => [-1, 1].flatMap(y => [-1, 1].map(z => new THREE.Vector3(x * half.x, y * half.y, z * half.z))))\n for (const corner of corners) box.expandByPoint(corner.add(frame.centre).applyMatrix4(frame.rotation).add(frame.origin))\n }\n const size = box.isEmpty() ? new THREE.Vector3(1000, 1000, 1000) : box.getSize(new THREE.Vector3())\n const reach = (axis: 'x' | 'z') => 2 * Math.max(Math.abs(box.isEmpty() ? 500 : box.min[axis]), Math.abs(box.isEmpty() ? 500 : box.max[axis]))\n return {\n width: Math.max(at.width, Math.ceil(reach('x') * 1.2), 200),\n height: Math.max(at.height, Math.ceil(reach('z') * 1.2), 200),\n /*\n Only grows, as the plan does: a raised arm made the board taller than it was, and a new board lost the camera. The\n height is reached from the floor, as width and depth are from the middle: a pelvis set 880 mm up stood as tall as\n its own 180 mm, and the view never followed it up (review 2026-11-11).\n */\n depth: Math.max(at.depth ?? 0, Math.ceil(Math.max(size.y, box.isEmpty() ? 0 : Math.max(Math.abs(box.max.y), Math.abs(box.min.y))) * 1.1), 200)\n }\n}\n\n/** Where the figure's drawn parts reach, in mm: the box the view was last fitted round, or the one it has now. */\nexport interface DrawnSpan {\n min: [number, number, number]\n max: [number, number, number]\n}\n\nexport function drawnSpanOf(parts: readonly FigureRenderPart[]): DrawnSpan | undefined {\n const box = new THREE.Box3()\n for (const part of parts) {\n const frame = frameOf(part)\n if (!frame) continue\n const half = frame.extent.clone().multiplyScalar(0.5)\n for (const x of [-1, 1]) for (const y of [-1, 1]) for (const z of [-1, 1])\n box.expandByPoint(new THREE.Vector3(x * half.x, y * half.y, z * half.z).add(frame.centre).applyMatrix4(frame.rotation).add(frame.origin))\n }\n return box.isEmpty() ? undefined : { min: box.min.toArray() as [number, number, number], max: box.max.toArray() as [number, number, number] }\n}\n\n/**\n * Whether the figure now reaches past the box the view was fitted round, on any side, by more than a tenth of that box\n * (review 2026-11-11: a thigh hung under a pelvis ran off the bottom of the view; the board's size alone, measured from\n * the middle and the floor, had not changed enough to say so).\n */\nexport function outgrowsFit(fitted: DrawnSpan | undefined, now: DrawnSpan | undefined): boolean {\n if (!fitted || !now) return false\n return [0, 1, 2].some(i => {\n const room = Math.max((fitted.max[i]! - fitted.min[i]!) * 0.1, 20)\n return now.max[i]! > fitted.max[i]! + room || now.min[i]! < fitted.min[i]! - room\n })\n}\n\n/** How far the figure may grow past its last fit before the view follows: inside the fit's own margin. */\nexport const REFIT_GROWN = 1.1\n\n/**\n * Whether the figure's reach has changed so far since the view was last fitted that the camera should fit it again:\n * grown past `REFIT_GROWN`, or shrunk below half, on any axis (first 15 minutes, 2026-10-16: a 100 mm box typed to\n * 3000 × 800 × 600 put the camera inside it, and the author saw grey until pressing 「화면에 맞추기」). A drag that nudges\n * a part within the board's rounding does not move the camera.\n */\n\nexport function needsRefit(fitted: (Board & { depth: number }) | undefined, now: Board & { depth: number }): boolean {\n if (!fitted) return false\n /*\n Grown past the room the fit leaves round it (the fit's margin, 1.15) the figure runs off the view: a head added on a\n body went out at the top, and the author pressed the fit after every part (review 2026-11-11). Shrunk, it is only\n small in the view: fitted again at half.\n */\n return (['width', 'height', 'depth'] as const).some(key => now[key] > fitted[key] * REFIT_GROWN || now[key] < fitted[key] / 2)\n}\n\n/**\n * A body's placement as its fields would read it: its origin in mm and its turn in degrees (rigid@1's X·Y·Z order, as\n * `componentOf` reads it). For a field a rule sets -- a fastened part's height, a turn its joint fixes -- the screen shows\n * this number read-only rather than a blank (designer 2026-09-27).\n */\nexport function placementOf(part: FigureRenderPart): { position: [number, number, number]; rotation: [number, number, number] } {\n const r = part.pose.r\n const m = 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)\n const e = new THREE.Euler().setFromRotationMatrix(m, 'XYZ')\n const deg = (v: number) => Math.round(THREE.MathUtils.radToDeg(v) * 10) / 10 || 0\n return { position: part.pose.t.map(round) as [number, number, number], rotation: [deg(e.x), deg(e.y), deg(e.z)] }\n}\n"]}
@@ -1 +1 @@
1
- export default function route(page: string): "figure-modeller" | "figure-list" | "figure-public-list" | undefined;
1
+ export default function route(page: string): "figure-list" | "figure-modeller" | "figure-public-list" | undefined;