@things-factory/figure-ui 10.1.82 → 10.1.84

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 (83) hide show
  1. package/client/modeller/command-preview.ts +4 -2
  2. package/client/modeller/drawn-shapes.ts +29 -0
  3. package/client/modeller/editor.ts +11 -29
  4. package/client/modeller/figure-links.ts +47 -101
  5. package/client/modeller/figure-material-panel.ts +2 -3
  6. package/client/modeller/figure-parts.ts +9 -11
  7. package/client/modeller/figure-section-panel.ts +175 -0
  8. package/client/modeller/gizmo.ts +26 -16
  9. package/client/modeller/joint-fan.ts +4 -3
  10. package/client/modeller/links.ts +24 -24
  11. package/client/modeller/maturity.ts +12 -6
  12. package/client/modeller/motion-ends.ts +2 -1
  13. package/client/modeller/refusals.ts +20 -0
  14. package/client/modeller/renderer.ts +3 -2
  15. package/client/modeller/section-controls.ts +34 -0
  16. package/client/modeller/starting-shape.ts +34 -0
  17. package/dist-client/modeller/clips.d.ts +1 -1
  18. package/dist-client/modeller/command-preview.js +4 -2
  19. package/dist-client/modeller/command-preview.js.map +1 -1
  20. package/dist-client/modeller/drawn-shapes.d.ts +44 -0
  21. package/dist-client/modeller/drawn-shapes.js +12 -0
  22. package/dist-client/modeller/drawn-shapes.js.map +1 -0
  23. package/dist-client/modeller/editor.d.ts +1 -6
  24. package/dist-client/modeller/editor.js +10 -29
  25. package/dist-client/modeller/editor.js.map +1 -1
  26. package/dist-client/modeller/figure-links.d.ts +2 -2
  27. package/dist-client/modeller/figure-links.js +45 -96
  28. package/dist-client/modeller/figure-links.js.map +1 -1
  29. package/dist-client/modeller/figure-material-panel.js +2 -3
  30. package/dist-client/modeller/figure-material-panel.js.map +1 -1
  31. package/dist-client/modeller/figure-parts.d.ts +3 -2
  32. package/dist-client/modeller/figure-parts.js +6 -9
  33. package/dist-client/modeller/figure-parts.js.map +1 -1
  34. package/dist-client/modeller/figure-section-panel.d.ts +28 -0
  35. package/dist-client/modeller/figure-section-panel.js +172 -0
  36. package/dist-client/modeller/figure-section-panel.js.map +1 -0
  37. package/dist-client/modeller/gizmo.d.ts +3 -3
  38. package/dist-client/modeller/gizmo.js +24 -11
  39. package/dist-client/modeller/gizmo.js.map +1 -1
  40. package/dist-client/modeller/joint-fan.js +4 -3
  41. package/dist-client/modeller/joint-fan.js.map +1 -1
  42. package/dist-client/modeller/links.d.ts +4 -1
  43. package/dist-client/modeller/links.js +23 -25
  44. package/dist-client/modeller/links.js.map +1 -1
  45. package/dist-client/modeller/maturity.d.ts +3 -2
  46. package/dist-client/modeller/maturity.js +9 -6
  47. package/dist-client/modeller/maturity.js.map +1 -1
  48. package/dist-client/modeller/motion-ends.js +2 -1
  49. package/dist-client/modeller/motion-ends.js.map +1 -1
  50. package/dist-client/modeller/refusals.d.ts +0 -5
  51. package/dist-client/modeller/refusals.js +25 -0
  52. package/dist-client/modeller/refusals.js.map +1 -1
  53. package/dist-client/modeller/renderer.d.ts +3 -4
  54. package/dist-client/modeller/renderer.js +3 -2
  55. package/dist-client/modeller/renderer.js.map +1 -1
  56. package/dist-client/modeller/section-controls.d.ts +11 -0
  57. package/dist-client/modeller/section-controls.js +27 -0
  58. package/dist-client/modeller/section-controls.js.map +1 -0
  59. package/dist-client/modeller/starting-shape.d.ts +32 -0
  60. package/dist-client/modeller/starting-shape.js +37 -0
  61. package/dist-client/modeller/starting-shape.js.map +1 -0
  62. package/dist-client/route.d.ts +1 -1
  63. package/dist-client/tsconfig.tsbuildinfo +1 -1
  64. package/dist-server/tsconfig.tsbuildinfo +1 -1
  65. package/package.json +5 -5
  66. package/test/browser/acceptance-page.js +8 -0
  67. package/test/command-preview.test.ts +3 -2
  68. package/test/fixtures/joint-column.json +110 -15
  69. package/test/fixtures/joint-roller.json +66 -9
  70. package/test/fixtures/robot-arm-6dof.json +203 -27
  71. package/test/joint-fan.test.ts +10 -7
  72. package/test/links.test.ts +6 -5
  73. package/test/material.test.ts +7 -2
  74. package/test/renderer.test.ts +2 -1
  75. package/test/resize-with-placement.test.ts +15 -14
  76. package/test/scene-canvas.test.ts +2 -1
  77. package/test/section-panel.test.ts +93 -0
  78. package/translations/en.json +21 -2
  79. package/translations/ja.json +20 -1
  80. package/translations/ko.json +22 -3
  81. package/translations/ms.json +20 -1
  82. package/translations/zh.json +20 -1
  83. package/dist-client/tsconfig.localkernel.tsbuildinfo +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"motion-ends.js","sourceRoot":"","sources":["../../client/modeller/motion-ends.ts"],"names":[],"mappings":"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","sourcesContent":["import { compileFigureAsset } from '@hatiolab/figure-model'\nimport type { FigureAsset } from '@hatiolab/figure-model'\n\nimport { PREVIEW, TARGET_FACE } from './command-preview.js'\nimport type { FigureRenderPart } from './renderer.js'\n\n/*\n * Where a part's motion takes it (ADR-0097 §5.5, designer 2026-09-27): the bodies its control moves, drawn translucent at\n * the control's lowest and highest value -- evaluated there by figure-model, not guessed -- and, for a slide, the line it\n * travels. Only the two ends are shown; nothing here says what lies between them is clear.\n */\n\ntype Pose = { r: number[][]; t: number[] }\ntype Body = FigureRenderPart & { pose: Pose }\n\n/** The line a slide travels along, thin enough not to hide the part. */\nconst THICKNESS = 6\n\nexport interface MotionEnds {\n control: string\n min: number\n max: number\n unit: string\n /** The moving bodies at the lowest value, then at the highest. */\n ghost: FigureRenderPart[]\n}\n\nconst samePose = (a: Pose, b: Pose) => [0, 1, 2].every(i => Math.abs(a.t[i]! - b.t[i]!) < 1e-6 && [0, 1, 2].every(k => Math.abs(a.r[i]![k]! - b.r[i]![k]!) < 1e-9))\n\n/** The ends of the selected part's own motion, or nothing: a part that does not move, follows another, or turns without end. */\nexport function motionEndsOf(asset: FigureAsset, part: string, values: Record<string, number> = {}): MotionEnds | null {\n const joint = asset.joints?.find(j => j.body1.part === part)\n /* A turn without end is a revolute joint whose control has no range: the range check below leaves it out. */\n if (!joint || joint.type === 'fixed' || joint.mimic) return null\n const control = joint.state?.id ?? joint.id\n const input = asset.document.model.inputs.find(i => i.id === control && i.role === 'state')\n if (!input || typeof input.min !== 'number' || typeof input.max !== 'number' || input.min === input.max) return null\n /* The values in hand, each where it belongs: design values into the figure, controls into the evaluation. */\n const pick = (of: Record<string, number>) => Object.fromEntries(Object.entries(values).filter(([id]) => id in of))\n const shown = { ...asset, designInputs: { ...asset.designInputs, ...pick(asset.designInputs) } }\n const state = pick(asset.stateDefaults)\n const at = (value: number) => {\n try {\n return compileFigureAsset(shown).evaluate({ ...state, [control]: value }).geometry as unknown as Body[]\n } catch {\n return null\n }\n }\n const low = at(input.min)\n const high = at(input.max)\n if (!low || !high) return null\n const byId = new Map(high.filter(b => b.pose).map(b => [b.id, b]))\n const moving = low.filter(b => b.pose && 'shape' in b && byId.has(b.id) && !samePose(b.pose, byId.get(b.id)!.pose))\n if (!moving.length) return null\n const ghost: FigureRenderPart[] = [\n ...moving.map(b => ({ ...b, id: `${PREVIEW}:min:${b.id}`, material: { token: PREVIEW, transparent: true } })),\n ...moving.map(b => ({ ...byId.get(b.id)!, id: `${PREVIEW}:max:${b.id}`, material: { token: PREVIEW, transparent: true } }))\n ]\n /* A slide: the line its own body travels, from the lowest value to the highest. */\n const own = moving.find(b => b.id === part)\n if (joint.type === 'prismatic' && own) {\n const from = own.pose.t\n const to = byId.get(part)!.pose.t\n const v = [0, 1, 2].map(i => to[i]! - from[i]!)\n const length = Math.hypot(...v)\n if (length > 1e-6) {\n const x = v.map(c => c / length)\n /* Any two directions square to it and to each other complete its frame. */\n const helper = Math.abs(x[1]!) < 0.9 ? [0, 1, 0] : [1, 0, 0]\n const zRaw = [x[1]! * helper[2]! - x[2]! * helper[1]!, x[2]! * helper[0]! - x[0]! * helper[2]!, x[0]! * helper[1]! - x[1]! * helper[0]!]\n const zl = Math.hypot(...zRaw)\n const z = zRaw.map(c => c / zl)\n const y = [z[1]! * x[2]! - z[2]! * x[1]!, z[2]! * x[0]! - z[0]! * x[2]!, z[0]! * x[1]! - z[1]! * x[0]!]\n ghost.push({\n id: `${TARGET_FACE}:slide`,\n shape: { provider: 'box', dimensions: { width: length, height: THICKNESS, depth: THICKNESS } },\n pose: { r: [0, 1, 2].map(i => [x[i]!, y[i]!, z[i]!]), t: [0, 1, 2].map(i => (from[i]! + to[i]!) / 2) },\n material: { token: TARGET_FACE }\n })\n }\n }\n return { control, min: input.min, max: input.max, unit: input.unit, ghost }\n}\n"]}
1
+ {"version":3,"file":"motion-ends.js","sourceRoot":"","sources":["../../client/modeller/motion-ends.ts"],"names":[],"mappings":"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","sourcesContent":["import { compileFigureAsset } from '@hatiolab/figure-model'\nimport type { FigureAsset } from '@hatiolab/figure-model'\n\nimport { PREVIEW, TARGET_FACE } from './command-preview.js'\nimport type { FigureRenderPart } from './renderer.js'\nimport { boxShape } from './drawn-shapes.js'\n\n/*\n * Where a part's motion takes it (ADR-0097 §5.5, designer 2026-09-27): the bodies its control moves, drawn translucent at\n * the control's lowest and highest value -- evaluated there by figure-model, not guessed -- and, for a slide, the line it\n * travels. Only the two ends are shown; nothing here says what lies between them is clear.\n */\n\ntype Pose = { r: number[][]; t: number[] }\ntype Body = FigureRenderPart & { pose: Pose }\n\n/** The line a slide travels along, thin enough not to hide the part. */\nconst THICKNESS = 6\n\nexport interface MotionEnds {\n control: string\n min: number\n max: number\n unit: string\n /** The moving bodies at the lowest value, then at the highest. */\n ghost: FigureRenderPart[]\n}\n\nconst samePose = (a: Pose, b: Pose) => [0, 1, 2].every(i => Math.abs(a.t[i]! - b.t[i]!) < 1e-6 && [0, 1, 2].every(k => Math.abs(a.r[i]![k]! - b.r[i]![k]!) < 1e-9))\n\n/** The ends of the selected part's own motion, or nothing: a part that does not move, follows another, or turns without end. */\nexport function motionEndsOf(asset: FigureAsset, part: string, values: Record<string, number> = {}): MotionEnds | null {\n const joint = asset.joints?.find(j => j.body1.part === part)\n /* A turn without end is a revolute joint whose control has no range: the range check below leaves it out. */\n if (!joint || joint.type === 'fixed' || joint.mimic) return null\n const control = joint.state?.id ?? joint.id\n const input = asset.document.model.inputs.find(i => i.id === control && i.role === 'state')\n if (!input || typeof input.min !== 'number' || typeof input.max !== 'number' || input.min === input.max) return null\n /* The values in hand, each where it belongs: design values into the figure, controls into the evaluation. */\n const pick = (of: Record<string, number>) => Object.fromEntries(Object.entries(values).filter(([id]) => id in of))\n const shown = { ...asset, designInputs: { ...asset.designInputs, ...pick(asset.designInputs) } }\n const state = pick(asset.stateDefaults)\n const at = (value: number) => {\n try {\n return compileFigureAsset(shown).evaluate({ ...state, [control]: value }).geometry as unknown as Body[]\n } catch {\n return null\n }\n }\n const low = at(input.min)\n const high = at(input.max)\n if (!low || !high) return null\n const byId = new Map(high.filter(b => b.pose).map(b => [b.id, b]))\n const moving = low.filter(b => b.pose && 'shape' in b && byId.has(b.id) && !samePose(b.pose, byId.get(b.id)!.pose))\n if (!moving.length) return null\n const ghost: FigureRenderPart[] = [\n ...moving.map(b => ({ ...b, id: `${PREVIEW}:min:${b.id}`, material: { token: PREVIEW, transparent: true } })),\n ...moving.map(b => ({ ...byId.get(b.id)!, id: `${PREVIEW}:max:${b.id}`, material: { token: PREVIEW, transparent: true } }))\n ]\n /* A slide: the line its own body travels, from the lowest value to the highest. */\n const own = moving.find(b => b.id === part)\n if (joint.type === 'prismatic' && own) {\n const from = own.pose.t\n const to = byId.get(part)!.pose.t\n const v = [0, 1, 2].map(i => to[i]! - from[i]!)\n const length = Math.hypot(...v)\n if (length > 1e-6) {\n const x = v.map(c => c / length)\n /* Any two directions square to it and to each other complete its frame. */\n const helper = Math.abs(x[1]!) < 0.9 ? [0, 1, 0] : [1, 0, 0]\n const zRaw = [x[1]! * helper[2]! - x[2]! * helper[1]!, x[2]! * helper[0]! - x[0]! * helper[2]!, x[0]! * helper[1]! - x[1]! * helper[0]!]\n const zl = Math.hypot(...zRaw)\n const z = zRaw.map(c => c / zl)\n const y = [z[1]! * x[2]! - z[2]! * x[1]!, z[2]! * x[0]! - z[0]! * x[2]!, z[0]! * x[1]! - z[1]! * x[0]!]\n ghost.push({\n id: `${TARGET_FACE}:slide`,\n shape: boxShape(length, THICKNESS, THICKNESS),\n pose: { r: [0, 1, 2].map(i => [x[i]!, y[i]!, z[i]!]), t: [0, 1, 2].map(i => (from[i]! + to[i]!) / 2) },\n material: { token: TARGET_FACE }\n })\n }\n }\n return { control, min: input.min, max: input.max, unit: input.unit, ghost }\n}\n"]}
@@ -53,11 +53,6 @@ export declare function removalNoticeOf(notice: {
53
53
  * range, a value's range, a placement), otherwise the kernel's code. The kernel says 「steps[2]: attach: …」.
54
54
  */
55
55
  export declare function recipeRefusalOf(error: unknown, say: Say): string | null;
56
- /**
57
- * A refusal no sentence above knows, said as a refusal with its code -- never the kernel's English on the screen (the
58
- * duplicate of a row's part showed 「EDIT_TARGET: … not a repeated template」, 2026-09-27). The figure's node limit
59
- * is said with its number.
60
- */
61
56
  export declare function editRefusalOf(error: unknown, say: Say): string;
62
57
  /**
63
58
  * A value outside the range its input declares, with the range and the value given -- the kernel says
@@ -114,11 +114,36 @@ export function recipeRefusalOf(error, say) {
114
114
  * duplicate of a row's part showed 「EDIT_TARGET: … not a repeated template」, 2026-09-27). The figure's node limit
115
115
  * is said with its number.
116
116
  */
117
+ /**
118
+ * A corner or an inside the part cannot take (ADR-0100 stage 1): what was too large or too thick, or what holds on
119
+ * to what the edit would take away. The kernel names the problem in its message; the screen names it in words.
120
+ */
121
+ function sectionRefusalOf(code, message, say) {
122
+ if (code === 'FEATURE_MAPPING_REQUIRED') {
123
+ /* 「takes away inside-bottom (joint j1); top (feature f2)」: the faces, each in the author's words. */
124
+ const faces = [...message.matchAll(/([\w-]+) \([^)]*\)/g)].map(([, face]) => say('figure.label.face-' + face, {}));
125
+ return say('figure.text.section-face-in-use', { faces: faces.join(', ') });
126
+ }
127
+ if (code === 'DIMENSION_LINKED')
128
+ return say('figure.text.section-dimension-linked', {});
129
+ if (code !== 'GEOMETRY_DOMAIN')
130
+ return null;
131
+ if (/CORNER_TOO_LARGE/.test(message))
132
+ return say('figure.text.section-corner-too-large', {});
133
+ if (/NO_ROOM_INSIDE/.test(message))
134
+ return say('figure.text.section-wall-too-thick', {});
135
+ if (/CAVITY_NO_LENGTH/.test(message))
136
+ return say('figure.text.section-ends-too-thick', {});
137
+ return null;
138
+ }
117
139
  export function editRefusalOf(error, say) {
118
140
  const code = error?.code;
119
141
  const message = error instanceof Error ? error.message : String(error);
120
142
  if (code === 'DOCUMENT_LIMIT')
121
143
  return say('figure.text.edit-refused-node-limit', { max: /maximum (\d+) nodes/.exec(message)?.[1] ?? '' });
144
+ const section = sectionRefusalOf(code, message, say);
145
+ if (section)
146
+ return section;
122
147
  return say('figure.text.edit-refused', { code: code ?? '' });
123
148
  }
124
149
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"refusals.js","sourceRoot":"","sources":["../../client/modeller/refusals.ts"],"names":[],"mappings":"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","sourcesContent":["/*\n * A command refused because the figure could not be drawn at an end of its declared size range, in the author's words\n * with the numbers and where to put it right -- not the kernel's English (user, 2026-09-24: 「what is this warning\n * message means?」). Kept apart from the element so it is tested without a browser.\n */\n\ntype Say = (key: string, params: Record<string, string>) => string\n\n/** The refusal in words, or nothing for any other refusal. */\nexport function sizeRangeRefusalOf(error: unknown, say: Say): string | null {\n if ((error as { code?: string })?.code !== 'INSTANCE_SIZE_RANGE') return null\n const message = error instanceof Error ? error.message : String(error)\n const found = /size\\.([xyz]):\\s*(below|above) ([\\d.]+) mm [^(;]*(?:\\(([^)]+)\\))?;/.exec(message)\n if (!found) return null\n const [, axis, side, mm, part] = found\n return say(side === 'below' ? 'figure.text.size-range-too-small' : 'figure.text.size-range-too-large', {\n axis: axis!.toUpperCase(),\n mm: Number(mm).toLocaleString(undefined, { maximumFractionDigits: 1 }),\n part: part ?? ''\n })\n}\n\n/**\n * A link refused because the size range it would leave cannot hold: measuring again cannot help (the link is not there\n * to measure), so the author is offered the same link that narrows the range to what the figure then allows.\n */\nexport function narrowingRetryOf<A extends { kind: string }>(action: A, error: unknown, say: Say): { message: string; label: string; retry: A & { narrowRange: true } } | null {\n if (action.kind !== 'link-dimension' || (error as { code?: string })?.code !== 'INSTANCE_SIZE_RANGE') return null\n const message = error instanceof Error ? error.message : String(error)\n const found = /size\\.([xyz]):\\s*(below|above) ([\\d.]+) mm [^(;]*(?:\\(([^)]+)\\))?;/.exec(message)\n if (!found) return null\n const [, axis, side, raw, part] = found\n const mm = Number(raw).toLocaleString(undefined, { maximumFractionDigits: 1 })\n const words = { axis: axis!.toUpperCase(), mm, part: part ?? '' }\n return {\n message: say(side === 'below' ? 'figure.text.link-needs-smaller-range' : 'figure.text.link-needs-larger-range', words),\n label: say(side === 'below' ? 'figure.button.narrow-range-from' : 'figure.button.narrow-range-to', words),\n retry: { ...action, narrowRange: true }\n }\n}\n\n/**\n * A placement refused because a part is fastened to the mounting plane by a face the new plane does not take (a bottom\n * on the floor, when the figure is to hang on a wall), said in the author's words with the parts it names.\n */\nexport function placementRefusalOf(error: unknown, say: Say): string | null {\n if ((error as { code?: string })?.code !== 'PLACEMENT_PLANE') return null\n const message = error instanceof Error ? error.message : String(error)\n const found = /: (.+?) (?:is|are) fastened to the mounting plane by a face an? (\\w+) does not take \\(([^)]+)\\)/.exec(message)\n if (!found) return null\n return say('figure.text.placement-plane-refused', { parts: found[1]!, placement: say('figure.label.placement-' + found[2]!, {}), faces: found[3]! })\n}\n\n/** A part refused because the declared room reads it -- the one refusal deleting it with the room refitted answers. */\nexport function inDeclaredRoom(error: unknown): boolean {\n return (error as { code?: string })?.code === 'PART_REFERENCED' && /is in the space the figure declares/.test(error instanceof Error ? error.message : String(error))\n}\n\n/**\n * Why a part cannot be deleted, in the author's words. Since the chief architect's ruling of 2026-09-27 a part that\n * holds others, leads a motion or is followed goes, and what depended on it is let go of (`removalNoticeOf`); what is\n * left is the declared space (with a way on: fit it again) and a row -- one laid along the part, or hanging in its row.\n */\nexport function partRefusalOf(error: unknown, part: string, say: Say): string | null {\n const code = (error as { code?: string })?.code\n if (code !== 'PART_REFERENCED' && code !== 'PART_ROW') return null\n /* figure-model's sentence after its code and path: 「PART_ROW: bed: roller is a row laid along bed; …」. */\n const message = (error instanceof Error ? error.message : String(error)).replace(/^[A-Z_]+: [^:]+: /, '')\n if (/is in the space the figure declares/.test(message)) return say('figure.text.part-in-declared-space', { part })\n const along = /^(.+?) (?:is a row|are rows) laid along /.exec(message)\n if (along) return say('figure.text.part-row-along', { part, rows: along[1]! })\n const carried = /^(.+?) hangs? in the row /.exec(message)\n if (carried) return say('figure.text.part-row-carries', { part, others: carried[1]! })\n return say('figure.text.part-is-referenced', { part })\n}\n\n/** What removing a part let go of (figure-model's `RemovalNotice`), said in one line, or nothing when it let go of nothing. */\nexport function removalNoticeOf(\n notice:\n | {\n released: Array<{ part: string; control?: string }>\n fixed: Array<{ part: string }>\n controls: Array<{ part: string; control: string; min: number; max: number }>\n mountingFace?: boolean\n bindings?: Array<{ id: string; label?: string }>\n }\n | undefined,\n say: Say\n): string | null {\n if (!notice) return null\n const number = (v: number) => v.toLocaleString(undefined, { maximumFractionDigits: 3 })\n const lines: string[] = []\n const still = notice.released.filter(r => !r.control).map(r => r.part)\n if (still.length) lines.push(say('figure.text.removal-released', { parts: still.join(', ') }))\n for (const r of notice.released.filter(r => r.control)) lines.push(say('figure.text.removal-released-motion', { part: r.part, control: r.control! }))\n for (const c of notice.controls) lines.push(say('figure.text.removal-own-control', { part: c.part, control: c.control, min: number(c.min), max: number(c.max) }))\n const fixed = [...new Set(notice.fixed.map(f => f.part))]\n if (fixed.length) lines.push(say('figure.text.removal-fixed', { parts: fixed.join(', ') }))\n if (notice.bindings?.length) lines.push(say('figure.text.removal-bindings', { places: notice.bindings.map(b => b.label ?? b.id).join(', ') }))\n if (notice.mountingFace) lines.push(say('figure.text.removal-mounting-face', {}))\n return lines.length ? lines.join(' ') : null\n}\n\n/**\n * A recipe refused at one of its steps: which step, and why -- the particular sentence when there is one (a size\n * range, a value's range, a placement), otherwise the kernel's code. The kernel says 「steps[2]: attach: …」.\n */\nexport function recipeRefusalOf(error: unknown, say: Say): string | null {\n const step = /^steps\\[(\\d+)\\]$/.exec((error as { path?: string })?.path ?? '')\n if (!step) return null\n const why = sizeRangeRefusalOf(error, say) ?? inputRangeRefusalOf(error, say) ?? placementRefusalOf(error, say)\n return say('figure.text.recipe-step-refused', { step: String(Number(step[1]) + 1), why: why ?? '', code: (error as { code?: string })?.code ?? '' })\n}\n\n/**\n * A refusal no sentence above knows, said as a refusal with its code -- never the kernel's English on the screen (the\n * duplicate of a row's part showed 「EDIT_TARGET: … not a repeated template」, 2026-09-27). The figure's node limit\n * is said with its number.\n */\nexport function editRefusalOf(error: unknown, say: Say): string {\n const code = (error as { code?: string })?.code\n const message = error instanceof Error ? error.message : String(error)\n if (code === 'DOCUMENT_LIMIT') return say('figure.text.edit-refused-node-limit', { max: /maximum (\\d+) nodes/.exec(message)?.[1] ?? '' })\n return say('figure.text.edit-refused', { code: code ?? '' })\n}\n\n/**\n * A value outside the range its input declares, with the range and the value given -- the kernel says\n * 「size.x: 6021100 outside [300,1200]」. The figure's own size is named by its axis; any other input by its range only.\n * The repeat pitch's refusal shares the code and has its own words (`figure.text.repeat-pitch-too-small`).\n */\nexport function inputRangeRefusalOf(error: unknown, say: Say): string | null {\n if ((error as { code?: string })?.code !== 'INPUT_RANGE') return null\n const message = error instanceof Error ? error.message : String(error)\n const found = /([\\w.-]+): (-?[\\d.e+]+) outside \\[(-?[\\d.e+]+),\\s*(-?[\\d.e+]+)\\]/.exec(message)\n if (!found) return null\n const [, input, value, min, max] = found\n const number = (text: string) => Number(text).toLocaleString(undefined, { maximumFractionDigits: 3 })\n const axis = /^size\\.([xyz])$/.exec(input!)?.[1]\n const params = { value: number(value!), min: number(min!), max: number(max!) }\n return axis ? say('figure.text.input-range-size', { ...params, axis: axis.toUpperCase() }) : say('figure.text.input-range', params)\n}\n\n/**\n * Why \"follow the size and keep where the figure is placed\" was not applied, in the author's words (ADR-0097): the plan\n * says what it would have had to overwrite, and for a part through the plane how far. A code the screen has no words\n * for is said as a refusal with its code, never in the kernel's English.\n */\nexport function resizeRefusalOf(plan: { mode: string; refusal?: { code: string; axis?: string; mm?: number } }, part: string, say: Say): string {\n const refusal = plan.refusal\n if (!refusal) return ''\n const place = say(`figure.label.resize-place-${plan.mode}`, {})\n const params = { part, place, axis: (refusal.axis ?? '').toUpperCase(), mm: refusal.mm === undefined ? '' : String(Math.round(refusal.mm)) }\n const known = ['HELD', 'TURNED', 'UNIFORM_POLICY', 'SHAPE_EDIT', 'DIMENSION_RULE', 'POSITION_RULE', 'GAP_CHOICE', 'NO_FACE']\n if (known.includes(refusal.code)) return say(`figure.text.resize-refused-${refusal.code.toLowerCase().replace(/_/g, '-')}`, params)\n return say('figure.text.edit-refused', { code: refusal.code })\n}\n"]}
1
+ {"version":3,"file":"refusals.js","sourceRoot":"","sources":["../../client/modeller/refusals.ts"],"names":[],"mappings":"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","sourcesContent":["/*\n * A command refused because the figure could not be drawn at an end of its declared size range, in the author's words\n * with the numbers and where to put it right -- not the kernel's English (user, 2026-09-24: 「what is this warning\n * message means?」). Kept apart from the element so it is tested without a browser.\n */\n\ntype Say = (key: string, params: Record<string, string>) => string\n\n/** The refusal in words, or nothing for any other refusal. */\nexport function sizeRangeRefusalOf(error: unknown, say: Say): string | null {\n if ((error as { code?: string })?.code !== 'INSTANCE_SIZE_RANGE') return null\n const message = error instanceof Error ? error.message : String(error)\n const found = /size\\.([xyz]):\\s*(below|above) ([\\d.]+) mm [^(;]*(?:\\(([^)]+)\\))?;/.exec(message)\n if (!found) return null\n const [, axis, side, mm, part] = found\n return say(side === 'below' ? 'figure.text.size-range-too-small' : 'figure.text.size-range-too-large', {\n axis: axis!.toUpperCase(),\n mm: Number(mm).toLocaleString(undefined, { maximumFractionDigits: 1 }),\n part: part ?? ''\n })\n}\n\n/**\n * A link refused because the size range it would leave cannot hold: measuring again cannot help (the link is not there\n * to measure), so the author is offered the same link that narrows the range to what the figure then allows.\n */\nexport function narrowingRetryOf<A extends { kind: string }>(action: A, error: unknown, say: Say): { message: string; label: string; retry: A & { narrowRange: true } } | null {\n if (action.kind !== 'link-dimension' || (error as { code?: string })?.code !== 'INSTANCE_SIZE_RANGE') return null\n const message = error instanceof Error ? error.message : String(error)\n const found = /size\\.([xyz]):\\s*(below|above) ([\\d.]+) mm [^(;]*(?:\\(([^)]+)\\))?;/.exec(message)\n if (!found) return null\n const [, axis, side, raw, part] = found\n const mm = Number(raw).toLocaleString(undefined, { maximumFractionDigits: 1 })\n const words = { axis: axis!.toUpperCase(), mm, part: part ?? '' }\n return {\n message: say(side === 'below' ? 'figure.text.link-needs-smaller-range' : 'figure.text.link-needs-larger-range', words),\n label: say(side === 'below' ? 'figure.button.narrow-range-from' : 'figure.button.narrow-range-to', words),\n retry: { ...action, narrowRange: true }\n }\n}\n\n/**\n * A placement refused because a part is fastened to the mounting plane by a face the new plane does not take (a bottom\n * on the floor, when the figure is to hang on a wall), said in the author's words with the parts it names.\n */\nexport function placementRefusalOf(error: unknown, say: Say): string | null {\n if ((error as { code?: string })?.code !== 'PLACEMENT_PLANE') return null\n const message = error instanceof Error ? error.message : String(error)\n const found = /: (.+?) (?:is|are) fastened to the mounting plane by a face an? (\\w+) does not take \\(([^)]+)\\)/.exec(message)\n if (!found) return null\n return say('figure.text.placement-plane-refused', { parts: found[1]!, placement: say('figure.label.placement-' + found[2]!, {}), faces: found[3]! })\n}\n\n/** A part refused because the declared room reads it -- the one refusal deleting it with the room refitted answers. */\nexport function inDeclaredRoom(error: unknown): boolean {\n return (error as { code?: string })?.code === 'PART_REFERENCED' && /is in the space the figure declares/.test(error instanceof Error ? error.message : String(error))\n}\n\n/**\n * Why a part cannot be deleted, in the author's words. Since the chief architect's ruling of 2026-09-27 a part that\n * holds others, leads a motion or is followed goes, and what depended on it is let go of (`removalNoticeOf`); what is\n * left is the declared space (with a way on: fit it again) and a row -- one laid along the part, or hanging in its row.\n */\nexport function partRefusalOf(error: unknown, part: string, say: Say): string | null {\n const code = (error as { code?: string })?.code\n if (code !== 'PART_REFERENCED' && code !== 'PART_ROW') return null\n /* figure-model's sentence after its code and path: 「PART_ROW: bed: roller is a row laid along bed; …」. */\n const message = (error instanceof Error ? error.message : String(error)).replace(/^[A-Z_]+: [^:]+: /, '')\n if (/is in the space the figure declares/.test(message)) return say('figure.text.part-in-declared-space', { part })\n const along = /^(.+?) (?:is a row|are rows) laid along /.exec(message)\n if (along) return say('figure.text.part-row-along', { part, rows: along[1]! })\n const carried = /^(.+?) hangs? in the row /.exec(message)\n if (carried) return say('figure.text.part-row-carries', { part, others: carried[1]! })\n return say('figure.text.part-is-referenced', { part })\n}\n\n/** What removing a part let go of (figure-model's `RemovalNotice`), said in one line, or nothing when it let go of nothing. */\nexport function removalNoticeOf(\n notice:\n | {\n released: Array<{ part: string; control?: string }>\n fixed: Array<{ part: string }>\n controls: Array<{ part: string; control: string; min: number; max: number }>\n mountingFace?: boolean\n bindings?: Array<{ id: string; label?: string }>\n }\n | undefined,\n say: Say\n): string | null {\n if (!notice) return null\n const number = (v: number) => v.toLocaleString(undefined, { maximumFractionDigits: 3 })\n const lines: string[] = []\n const still = notice.released.filter(r => !r.control).map(r => r.part)\n if (still.length) lines.push(say('figure.text.removal-released', { parts: still.join(', ') }))\n for (const r of notice.released.filter(r => r.control)) lines.push(say('figure.text.removal-released-motion', { part: r.part, control: r.control! }))\n for (const c of notice.controls) lines.push(say('figure.text.removal-own-control', { part: c.part, control: c.control, min: number(c.min), max: number(c.max) }))\n const fixed = [...new Set(notice.fixed.map(f => f.part))]\n if (fixed.length) lines.push(say('figure.text.removal-fixed', { parts: fixed.join(', ') }))\n if (notice.bindings?.length) lines.push(say('figure.text.removal-bindings', { places: notice.bindings.map(b => b.label ?? b.id).join(', ') }))\n if (notice.mountingFace) lines.push(say('figure.text.removal-mounting-face', {}))\n return lines.length ? lines.join(' ') : null\n}\n\n/**\n * A recipe refused at one of its steps: which step, and why -- the particular sentence when there is one (a size\n * range, a value's range, a placement), otherwise the kernel's code. The kernel says 「steps[2]: attach: …」.\n */\nexport function recipeRefusalOf(error: unknown, say: Say): string | null {\n const step = /^steps\\[(\\d+)\\]$/.exec((error as { path?: string })?.path ?? '')\n if (!step) return null\n const why = sizeRangeRefusalOf(error, say) ?? inputRangeRefusalOf(error, say) ?? placementRefusalOf(error, say)\n return say('figure.text.recipe-step-refused', { step: String(Number(step[1]) + 1), why: why ?? '', code: (error as { code?: string })?.code ?? '' })\n}\n\n/**\n * A refusal no sentence above knows, said as a refusal with its code -- never the kernel's English on the screen (the\n * duplicate of a row's part showed 「EDIT_TARGET: … not a repeated template」, 2026-09-27). The figure's node limit\n * is said with its number.\n */\n/**\n * A corner or an inside the part cannot take (ADR-0100 stage 1): what was too large or too thick, or what holds on\n * to what the edit would take away. The kernel names the problem in its message; the screen names it in words.\n */\nfunction sectionRefusalOf(code: string | undefined, message: string, say: Say): string | null {\n if (code === 'FEATURE_MAPPING_REQUIRED') {\n /* 「takes away inside-bottom (joint j1); top (feature f2)」: the faces, each in the author's words. */\n const faces = [...message.matchAll(/([\\w-]+) \\([^)]*\\)/g)].map(([, face]) => say('figure.label.face-' + face, {}))\n return say('figure.text.section-face-in-use', { faces: faces.join(', ') })\n }\n if (code === 'DIMENSION_LINKED') return say('figure.text.section-dimension-linked', {})\n if (code !== 'GEOMETRY_DOMAIN') return null\n if (/CORNER_TOO_LARGE/.test(message)) return say('figure.text.section-corner-too-large', {})\n if (/NO_ROOM_INSIDE/.test(message)) return say('figure.text.section-wall-too-thick', {})\n if (/CAVITY_NO_LENGTH/.test(message)) return say('figure.text.section-ends-too-thick', {})\n return null\n}\n\nexport function editRefusalOf(error: unknown, say: Say): string {\n const code = (error as { code?: string })?.code\n const message = error instanceof Error ? error.message : String(error)\n if (code === 'DOCUMENT_LIMIT') return say('figure.text.edit-refused-node-limit', { max: /maximum (\\d+) nodes/.exec(message)?.[1] ?? '' })\n const section = sectionRefusalOf(code, message, say)\n if (section) return section\n return say('figure.text.edit-refused', { code: code ?? '' })\n}\n\n/**\n * A value outside the range its input declares, with the range and the value given -- the kernel says\n * 「size.x: 6021100 outside [300,1200]」. The figure's own size is named by its axis; any other input by its range only.\n * The repeat pitch's refusal shares the code and has its own words (`figure.text.repeat-pitch-too-small`).\n */\nexport function inputRangeRefusalOf(error: unknown, say: Say): string | null {\n if ((error as { code?: string })?.code !== 'INPUT_RANGE') return null\n const message = error instanceof Error ? error.message : String(error)\n const found = /([\\w.-]+): (-?[\\d.e+]+) outside \\[(-?[\\d.e+]+),\\s*(-?[\\d.e+]+)\\]/.exec(message)\n if (!found) return null\n const [, input, value, min, max] = found\n const number = (text: string) => Number(text).toLocaleString(undefined, { maximumFractionDigits: 3 })\n const axis = /^size\\.([xyz])$/.exec(input!)?.[1]\n const params = { value: number(value!), min: number(min!), max: number(max!) }\n return axis ? say('figure.text.input-range-size', { ...params, axis: axis.toUpperCase() }) : say('figure.text.input-range', params)\n}\n\n/**\n * Why \"follow the size and keep where the figure is placed\" was not applied, in the author's words (ADR-0097): the plan\n * says what it would have had to overwrite, and for a part through the plane how far. A code the screen has no words\n * for is said as a refusal with its code, never in the kernel's English.\n */\nexport function resizeRefusalOf(plan: { mode: string; refusal?: { code: string; axis?: string; mm?: number } }, part: string, say: Say): string {\n const refusal = plan.refusal\n if (!refusal) return ''\n const place = say(`figure.label.resize-place-${plan.mode}`, {})\n const params = { part, place, axis: (refusal.axis ?? '').toUpperCase(), mm: refusal.mm === undefined ? '' : String(Math.round(refusal.mm)) }\n const known = ['HELD', 'TURNED', 'UNIFORM_POLICY', 'SHAPE_EDIT', 'DIMENSION_RULE', 'POSITION_RULE', 'GAP_CHOICE', 'NO_FACE']\n if (known.includes(refusal.code)) return say(`figure.text.resize-refused-${refusal.code.toLowerCase().replace(/_/g, '-')}`, params)\n return say('figure.text.edit-refused', { code: refusal.code })\n}\n"]}
@@ -5,10 +5,9 @@ export interface FigureRenderPart {
5
5
  shape: {
6
6
  provider: string;
7
7
  dimensions: Record<string, number>;
8
- profile?: Array<{
9
- x: number;
10
- y: number;
11
- }>;
8
+ extrusion?: unknown;
9
+ cavity?: unknown;
10
+ revolution?: unknown;
12
11
  };
13
12
  pose: {
14
13
  r: number[][];
@@ -20,8 +20,9 @@ export function drawableFigureParts(geometry) {
20
20
  */
21
21
  function geometry(part) {
22
22
  const built = buildFigureGeometry(part.shape, part.segments ?? 12);
23
- if (!built)
24
- throw Error('Unsupported provider: ' + part.shape.provider);
23
+ /* A shape the builder does not know comes back empty: said, not drawn as nothing. */
24
+ if (!built?.getAttribute('position'))
25
+ throw Error('Unsupported shape: ' + part.shape.provider);
25
26
  return built;
26
27
  }
27
28
  export function disposeFigureMeshes(group) {
@@ -1 +1 @@
1
- {"version":3,"file":"renderer.js","sourceRoot":"","sources":["../../client/modeller/renderer.ts"],"names":[],"mappings":"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","sourcesContent":["import * as THREE from 'three'\nimport { buildFigureGeometry } from '@hatiolab/things-scene'\n\n/** Evaluated boundary: graph values are resolved before reaching the renderer. */\nexport interface FigureRenderPart {\n id: string\n shape: { provider: string; dimensions: Record<string, number>; profile?: Array<{x:number;y:number}> }\n pose: {r:number[][];t:number[]}\n material: {token:string;preset?:string;transparent?:boolean;emissive?:{token:string;intensity:number;on:boolean}}\n segments?:number\n}\n\n/**\n * The bodies a canvas draws, out of everything the asset evaluated.\n *\n * Two are left out. A scalar body carries no shape or pose — it is a number the graph produced, not something\n * on screen. A body the asset hides is evaluated, placed and gated exactly as a shown one, and excluded from\n * rendering, from shadows and from pointer picking (designer's ruling 2026-09-22); drawing it would make\n * \"hidden\" mean nothing in the modeller while it means something on a board.\n */\nexport function drawableFigureParts(geometry: readonly unknown[]): FigureRenderPart[] {\n return geometry.filter(\n (body): body is FigureRenderPart =>\n !!body && typeof body === 'object' && 'shape' in body && 'pose' in body && (body as { visible?: boolean }).visible !== false\n )\n}\n\n/**\n * The mesh for one body — things-scene's builder, not a copy of it.\n *\n * The modeller kept its own provider switch and it knew four providers; figures use eleven. A body the\n * board drew was refused here with \"Unsupported provider: roundedBoxXZ\" (2026-09-24, the first canvas check of\n * ADR-0087 step 4). One builder, and the canvas an author looks at shows what a board will show.\n */\nfunction geometry(part: FigureRenderPart): THREE.BufferGeometry {\n const built = buildFigureGeometry(part.shape as never, part.segments ?? 12) as THREE.BufferGeometry | undefined\n if (!built) throw Error('Unsupported provider: ' + part.shape.provider)\n return built\n}\n\nexport function disposeFigureMeshes(group:THREE.Group):void {\n for(const child of [...group.children]){\n if(child instanceof THREE.Mesh){child.geometry.dispose();const materials=Array.isArray(child.material)?child.material:[child.material];materials.forEach(m=>m.dispose())}\n group.remove(child)\n }\n}\n\n/** Build first, then swap; failed rendering must not erase the previous scene. No old meshes survive a successful swap. */\nexport function replaceFigureMeshes(group:THREE.Group,parts:readonly FigureRenderPart[],palette:Record<string,string>):void {\n const staged=new THREE.Group(),ids=new Set<string>()\n try{\n for(const part of parts){\n if(ids.has(part.id))throw Error('Duplicate geometry ID: '+part.id);ids.add(part.id)\n const color=palette[part.material.token]\n if(!color||!/^#[0-9a-f]{6}$/i.test(color))throw Error('Unresolved material token: '+part.material.token)\n const {r,t}=part.pose\n if(r.length!==3||r.some(row=>row.length!==3||row.some(x=>!Number.isFinite(x)))||t.length!==3||t.some(x=>!Number.isFinite(x)))throw Error('Invalid pose: '+part.id)\n const emissive=part.material.emissive\n if(emissive?.on&&!/^#[0-9a-f]{6}$/i.test(palette[emissive.token]??''))throw Error('Unresolved emissive token')\n const g=geometry(part)\n const material=new THREE.MeshStandardMaterial({color,metalness:part.material.preset==='metal'?0.8:0,roughness:part.material.preset==='metal'?0.3:0.65,transparent:!!part.material.transparent,opacity:part.material.transparent?0.35:1,emissive:emissive?.on?palette[emissive.token]:0,emissiveIntensity:emissive?.on?emissive.intensity:0})\n const mesh=new THREE.Mesh(g,material);mesh.name=part.id;mesh.userData.figurePartId=part.id\n mesh.matrixAutoUpdate=false;mesh.matrix.set(r[0][0],r[0][1],r[0][2],t[0],r[1][0],r[1][1],r[1][2],t[1],r[2][0],r[2][1],r[2][2],t[2],0,0,0,1)\n staged.add(mesh)\n }\n }catch(error){disposeFigureMeshes(staged);throw error}\n disposeFigureMeshes(group)\n for(const mesh of [...staged.children])group.add(mesh)\n group.updateMatrixWorld(true)\n}\n"]}
1
+ {"version":3,"file":"renderer.js","sourceRoot":"","sources":["../../client/modeller/renderer.ts"],"names":[],"mappings":"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","sourcesContent":["import * as THREE from 'three'\nimport { buildFigureGeometry } from '@hatiolab/things-scene'\n\n/** Evaluated boundary: graph values are resolved before reaching the renderer. */\nexport interface FigureRenderPart {\n id: string\n shape: { provider: string; dimensions: Record<string, number>; extrusion?: unknown; cavity?: unknown; revolution?: unknown }\n pose: {r:number[][];t:number[]}\n material: {token:string;preset?:string;transparent?:boolean;emissive?:{token:string;intensity:number;on:boolean}}\n segments?:number\n}\n\n/**\n * The bodies a canvas draws, out of everything the asset evaluated.\n *\n * Two are left out. A scalar body carries no shape or pose — it is a number the graph produced, not something\n * on screen. A body the asset hides is evaluated, placed and gated exactly as a shown one, and excluded from\n * rendering, from shadows and from pointer picking (designer's ruling 2026-09-22); drawing it would make\n * \"hidden\" mean nothing in the modeller while it means something on a board.\n */\nexport function drawableFigureParts(geometry: readonly unknown[]): FigureRenderPart[] {\n return geometry.filter(\n (body): body is FigureRenderPart =>\n !!body && typeof body === 'object' && 'shape' in body && 'pose' in body && (body as { visible?: boolean }).visible !== false\n )\n}\n\n/**\n * The mesh for one body — things-scene's builder, not a copy of it.\n *\n * The modeller kept its own provider switch and it knew four providers; figures use eleven. A body the\n * board drew was refused here with \"Unsupported provider: roundedBoxXZ\" (2026-09-24, the first canvas check of\n * ADR-0087 step 4). One builder, and the canvas an author looks at shows what a board will show.\n */\nfunction geometry(part: FigureRenderPart): THREE.BufferGeometry {\n const built = buildFigureGeometry(part.shape as never, part.segments ?? 12) as THREE.BufferGeometry | undefined\n /* A shape the builder does not know comes back empty: said, not drawn as nothing. */\n if (!built?.getAttribute('position')) throw Error('Unsupported shape: ' + part.shape.provider)\n return built\n}\n\nexport function disposeFigureMeshes(group:THREE.Group):void {\n for(const child of [...group.children]){\n if(child instanceof THREE.Mesh){child.geometry.dispose();const materials=Array.isArray(child.material)?child.material:[child.material];materials.forEach(m=>m.dispose())}\n group.remove(child)\n }\n}\n\n/** Build first, then swap; failed rendering must not erase the previous scene. No old meshes survive a successful swap. */\nexport function replaceFigureMeshes(group:THREE.Group,parts:readonly FigureRenderPart[],palette:Record<string,string>):void {\n const staged=new THREE.Group(),ids=new Set<string>()\n try{\n for(const part of parts){\n if(ids.has(part.id))throw Error('Duplicate geometry ID: '+part.id);ids.add(part.id)\n const color=palette[part.material.token]\n if(!color||!/^#[0-9a-f]{6}$/i.test(color))throw Error('Unresolved material token: '+part.material.token)\n const {r,t}=part.pose\n if(r.length!==3||r.some(row=>row.length!==3||row.some(x=>!Number.isFinite(x)))||t.length!==3||t.some(x=>!Number.isFinite(x)))throw Error('Invalid pose: '+part.id)\n const emissive=part.material.emissive\n if(emissive?.on&&!/^#[0-9a-f]{6}$/i.test(palette[emissive.token]??''))throw Error('Unresolved emissive token')\n const g=geometry(part)\n const material=new THREE.MeshStandardMaterial({color,metalness:part.material.preset==='metal'?0.8:0,roughness:part.material.preset==='metal'?0.3:0.65,transparent:!!part.material.transparent,opacity:part.material.transparent?0.35:1,emissive:emissive?.on?palette[emissive.token]:0,emissiveIntensity:emissive?.on?emissive.intensity:0})\n const mesh=new THREE.Mesh(g,material);mesh.name=part.id;mesh.userData.figurePartId=part.id\n mesh.matrixAutoUpdate=false;mesh.matrix.set(r[0][0],r[0][1],r[0][2],t[0],r[1][0],r[1][1],r[1][2],t[1],r[2][0],r[2][1],r[2][2],t[2],0,0,0,1)\n staged.add(mesh)\n }\n }catch(error){disposeFigureMeshes(staged);throw error}\n disposeFigureMeshes(group)\n for(const mesh of [...staged.children])group.add(mesh)\n group.updateMatrixWorld(true)\n}\n"]}
@@ -0,0 +1,11 @@
1
+ import type { FigureCavityRequest, FigureEvaluatedShape } from '@hatiolab/figure-model';
2
+ /** The one value every entry holds, or undefined when they differ (or there are none). */
3
+ export declare const sameOf: (values: readonly (number | undefined)[]) => number | undefined;
4
+ export declare const mm: (value: number) => number;
5
+ /** The wall a new inside starts with: a tenth of the section's narrower side, at least 1 mm. */
6
+ export declare function defaultWallOf(shape: FigureEvaluatedShape): number;
7
+ /**
8
+ * The inside as the panel asks for it: the part's own, with one thing changed. A part with none yet gets a default
9
+ * wall all round, a closed start as thick as the wall and an open end -- a tray standing on its floor.
10
+ */
11
+ export declare function cavityRequestOf(shape: FigureEvaluatedShape, patch?: Partial<FigureCavityRequest>): FigureCavityRequest;
@@ -0,0 +1,27 @@
1
+ /** The one value every entry holds, or undefined when they differ (or there are none). */
2
+ export const sameOf = (values) => values.length && values.every(v => v !== undefined && Math.abs(v - values[0]) < 1e-6) ? values[0] : undefined;
3
+ export const mm = (value) => Math.round(value * 1000) / 1000;
4
+ /** The wall a new inside starts with: a tenth of the section's narrower side, at least 1 mm. */
5
+ export function defaultWallOf(shape) {
6
+ const section = shape.extrusion.section;
7
+ const across = section.kind === 'rectangle'
8
+ ? Object.values(section.size)
9
+ : section.kind === 'ellipse'
10
+ ? Object.values(section.radius).map(r => 2 * r)
11
+ : [];
12
+ return Math.max(1, Math.round(Math.min(...across) / 10));
13
+ }
14
+ /**
15
+ * The inside as the panel asks for it: the part's own, with one thing changed. A part with none yet gets a default
16
+ * wall all round, a closed start as thick as the wall and an open end -- a tray standing on its floor.
17
+ */
18
+ export function cavityRequestOf(shape, patch = {}) {
19
+ const cavity = shape.cavity;
20
+ return {
21
+ wall: cavity ? (sameOf(Object.values(cavity.wall)) ?? { ...cavity.wall }) : defaultWallOf(shape),
22
+ start: cavity?.start ?? { closed: defaultWallOf(shape) },
23
+ end: cavity?.end ?? 'open',
24
+ ...patch
25
+ };
26
+ }
27
+ //# sourceMappingURL=section-controls.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"section-controls.js","sourceRoot":"","sources":["../../client/modeller/section-controls.ts"],"names":[],"mappings":"AAEA,0FAA0F;AAC1F,MAAM,CAAC,MAAM,MAAM,GAAG,CAAC,MAAuC,EAAE,EAAE,CAChE,MAAM,CAAC,MAAM,IAAI,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,KAAK,SAAS,IAAI,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC,CAAE,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAA;AAEhH,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,KAAa,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,GAAG,IAAI,CAAC,GAAG,IAAI,CAAA;AAEpE,gGAAgG;AAChG,MAAM,UAAU,aAAa,CAAC,KAA2B;IACvD,MAAM,OAAO,GAAG,KAAK,CAAC,SAAU,CAAC,OAAO,CAAA;IACxC,MAAM,MAAM,GACV,OAAO,CAAC,IAAI,KAAK,WAAW;QAC1B,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC;QAC7B,CAAC,CAAC,OAAO,CAAC,IAAI,KAAK,SAAS;YAC1B,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,CAAE,CAAC;YAChD,CAAC,CAAC,EAAE,CAAA;IACV,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,GAAI,MAAmB,CAAC,GAAG,EAAE,CAAC,CAAC,CAAA;AACxE,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,eAAe,CAAC,KAA2B,EAAE,QAAsC,EAAE;IACnG,MAAM,MAAM,GAAG,KAAK,CAAC,MAAM,CAAA;IAC3B,OAAO;QACL,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,GAAG,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,aAAa,CAAC,KAAK,CAAC;QAChG,KAAK,EAAE,MAAM,EAAE,KAAK,IAAI,EAAE,MAAM,EAAE,aAAa,CAAC,KAAK,CAAC,EAAE;QACxD,GAAG,EAAE,MAAM,EAAE,GAAG,IAAI,MAAM;QAC1B,GAAG,KAAK;KACT,CAAA;AACH,CAAC","sourcesContent":["import type { FigureCavityRequest, FigureEvaluatedShape } from '@hatiolab/figure-model'\n\n/** The one value every entry holds, or undefined when they differ (or there are none). */\nexport const sameOf = (values: readonly (number | undefined)[]) =>\n values.length && values.every(v => v !== undefined && Math.abs(v - values[0]!) < 1e-6) ? values[0] : undefined\n\nexport const mm = (value: number) => Math.round(value * 1000) / 1000\n\n/** The wall a new inside starts with: a tenth of the section's narrower side, at least 1 mm. */\nexport function defaultWallOf(shape: FigureEvaluatedShape): number {\n const section = shape.extrusion!.section\n const across =\n section.kind === 'rectangle'\n ? Object.values(section.size)\n : section.kind === 'ellipse'\n ? Object.values(section.radius).map(r => 2 * r!)\n : []\n return Math.max(1, Math.round(Math.min(...(across as number[])) / 10))\n}\n\n/**\n * The inside as the panel asks for it: the part's own, with one thing changed. A part with none yet gets a default\n * wall all round, a closed start as thick as the wall and an open end -- a tray standing on its floor.\n */\nexport function cavityRequestOf(shape: FigureEvaluatedShape, patch: Partial<FigureCavityRequest> = {}): FigureCavityRequest {\n const cavity = shape.cavity\n return {\n wall: cavity ? (sameOf(Object.values(cavity.wall)) ?? { ...cavity.wall }) : defaultWallOf(shape),\n start: cavity?.start ?? { closed: defaultWallOf(shape) },\n end: cavity?.end ?? 'open',\n ...patch\n }\n}\n\n"]}
@@ -0,0 +1,32 @@
1
+ /**
2
+ * What a person calls a part's shape (ADR-0100 §2.1): the starting shape it reads as -- 상자, 둥근 상자, 위가 열린 상자,
3
+ * 원기둥, 문틀, 구, 원뿔, 그린 단면. A word for the screen, read from the shape node's own params; it is not a kind of
4
+ * stored shape, and nothing is decided by it.
5
+ */
6
+ export type StartingShapeKind = 'cube' | 'rounded-box' | 'hollow-box' | 'cylinder' | 'portal' | 'sphere' | 'frustum' | 'polygon';
7
+ export declare function startingShapeKindOf(shape: {
8
+ op: string;
9
+ params?: Record<string, unknown>;
10
+ } | undefined): StartingShapeKind | undefined;
11
+ /**
12
+ * The same word for an evaluated body, when only the shape the kernel evaluated is in hand (a row of the parts list):
13
+ * a door frame is the section with jambs, a drawn section the rest of the drawn ones.
14
+ */
15
+ export declare function startingShapeKindOfBody(shape: {
16
+ provider?: string;
17
+ dimensions?: Record<string, number>;
18
+ extrusion?: {
19
+ section: {
20
+ kind: string;
21
+ corner?: unknown;
22
+ };
23
+ };
24
+ cavity?: unknown;
25
+ revolution?: {
26
+ profile: {
27
+ kind: string;
28
+ };
29
+ };
30
+ } | undefined): StartingShapeKind | undefined;
31
+ /** Whether a body is round across -- a circle or an ellipse pushed, anything turned: it takes a segment count. */
32
+ export declare const isRoundBody: (shape: Parameters<typeof startingShapeKindOfBody>[0]) => boolean;
@@ -0,0 +1,37 @@
1
+ export function startingShapeKindOf(shape) {
2
+ const p = shape?.params ?? {};
3
+ if (shape?.op === 'revolve@1')
4
+ return p.profile === 'taper' ? 'frustum' : 'sphere';
5
+ if (shape?.op !== 'extrude@1')
6
+ return undefined;
7
+ if (p.section === 'ellipse')
8
+ return 'cylinder';
9
+ if (p.section === 'polygon')
10
+ return 'polygon';
11
+ if (p.section === 'portal')
12
+ return 'portal';
13
+ if (p.cavity)
14
+ return 'hollow-box';
15
+ return p.corner === 'round' ? 'rounded-box' : 'cube';
16
+ }
17
+ /**
18
+ * The same word for an evaluated body, when only the shape the kernel evaluated is in hand (a row of the parts list):
19
+ * a door frame is the section with jambs, a drawn section the rest of the drawn ones.
20
+ */
21
+ export function startingShapeKindOfBody(shape) {
22
+ if (shape?.revolution)
23
+ return shape.revolution.profile.kind === 'taper' ? 'frustum' : 'sphere';
24
+ const s = shape?.extrusion?.section;
25
+ if (!s)
26
+ return undefined;
27
+ if (s.kind === 'ellipse')
28
+ return 'cylinder';
29
+ if (s.kind === 'polygon')
30
+ return shape.dimensions?.jamb !== undefined ? 'portal' : 'polygon';
31
+ if (shape.cavity)
32
+ return 'hollow-box';
33
+ return s.corner ? 'rounded-box' : 'cube';
34
+ }
35
+ /** Whether a body is round across -- a circle or an ellipse pushed, anything turned: it takes a segment count. */
36
+ export const isRoundBody = (shape) => ['cylinder', 'sphere', 'frustum'].includes(startingShapeKindOfBody(shape) ?? '');
37
+ //# sourceMappingURL=starting-shape.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"starting-shape.js","sourceRoot":"","sources":["../../client/modeller/starting-shape.ts"],"names":[],"mappings":"AAOA,MAAM,UAAU,mBAAmB,CAAC,KAAmE;IACrG,MAAM,CAAC,GAAG,KAAK,EAAE,MAAM,IAAI,EAAE,CAAA;IAC7B,IAAI,KAAK,EAAE,EAAE,KAAK,WAAW;QAAE,OAAO,CAAC,CAAC,OAAO,KAAK,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,QAAQ,CAAA;IAClF,IAAI,KAAK,EAAE,EAAE,KAAK,WAAW;QAAE,OAAO,SAAS,CAAA;IAC/C,IAAI,CAAC,CAAC,OAAO,KAAK,SAAS;QAAE,OAAO,UAAU,CAAA;IAC9C,IAAI,CAAC,CAAC,OAAO,KAAK,SAAS;QAAE,OAAO,SAAS,CAAA;IAC7C,IAAI,CAAC,CAAC,OAAO,KAAK,QAAQ;QAAE,OAAO,QAAQ,CAAA;IAC3C,IAAI,CAAC,CAAC,MAAM;QAAE,OAAO,YAAY,CAAA;IACjC,OAAO,CAAC,CAAC,MAAM,KAAK,OAAO,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,MAAM,CAAA;AACtD,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,uBAAuB,CAAC,KAAwM;IAC9O,IAAI,KAAK,EAAE,UAAU;QAAE,OAAO,KAAK,CAAC,UAAU,CAAC,OAAO,CAAC,IAAI,KAAK,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,QAAQ,CAAA;IAC9F,MAAM,CAAC,GAAG,KAAK,EAAE,SAAS,EAAE,OAAO,CAAA;IACnC,IAAI,CAAC,CAAC;QAAE,OAAO,SAAS,CAAA;IACxB,IAAI,CAAC,CAAC,IAAI,KAAK,SAAS;QAAE,OAAO,UAAU,CAAA;IAC3C,IAAI,CAAC,CAAC,IAAI,KAAK,SAAS;QAAE,OAAO,KAAM,CAAC,UAAU,EAAE,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAA;IAC7F,IAAI,KAAM,CAAC,MAAM;QAAE,OAAO,YAAY,CAAA;IACtC,OAAO,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,MAAM,CAAA;AAC1C,CAAC;AAED,kHAAkH;AAClH,MAAM,CAAC,MAAM,WAAW,GAAG,CAAC,KAAoD,EAAE,EAAE,CAAC,CAAC,UAAU,EAAE,QAAQ,EAAE,SAAS,CAAC,CAAC,QAAQ,CAAC,uBAAuB,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAA","sourcesContent":["/**\n * What a person calls a part's shape (ADR-0100 §2.1): the starting shape it reads as -- 상자, 둥근 상자, 위가 열린 상자,\n * 원기둥, 문틀, 구, 원뿔, 그린 단면. A word for the screen, read from the shape node's own params; it is not a kind of\n * stored shape, and nothing is decided by it.\n */\nexport type StartingShapeKind = 'cube' | 'rounded-box' | 'hollow-box' | 'cylinder' | 'portal' | 'sphere' | 'frustum' | 'polygon'\n\nexport function startingShapeKindOf(shape: { op: string; params?: Record<string, unknown> } | undefined): StartingShapeKind | undefined {\n const p = shape?.params ?? {}\n if (shape?.op === 'revolve@1') return p.profile === 'taper' ? 'frustum' : 'sphere'\n if (shape?.op !== 'extrude@1') return undefined\n if (p.section === 'ellipse') return 'cylinder'\n if (p.section === 'polygon') return 'polygon'\n if (p.section === 'portal') return 'portal'\n if (p.cavity) return 'hollow-box'\n return p.corner === 'round' ? 'rounded-box' : 'cube'\n}\n\n/**\n * The same word for an evaluated body, when only the shape the kernel evaluated is in hand (a row of the parts list):\n * a door frame is the section with jambs, a drawn section the rest of the drawn ones.\n */\nexport function startingShapeKindOfBody(shape: { provider?: string; dimensions?: Record<string, number>; extrusion?: { section: { kind: string; corner?: unknown } }; cavity?: unknown; revolution?: { profile: { kind: string } } } | undefined): StartingShapeKind | undefined {\n if (shape?.revolution) return shape.revolution.profile.kind === 'taper' ? 'frustum' : 'sphere'\n const s = shape?.extrusion?.section\n if (!s) return undefined\n if (s.kind === 'ellipse') return 'cylinder'\n if (s.kind === 'polygon') return shape!.dimensions?.jamb !== undefined ? 'portal' : 'polygon'\n if (shape!.cavity) return 'hollow-box'\n return s.corner ? 'rounded-box' : 'cube'\n}\n\n/** Whether a body is round across -- a circle or an ellipse pushed, anything turned: it takes a segment count. */\nexport const isRoundBody = (shape: Parameters<typeof startingShapeKindOfBody>[0]) => ['cylinder', 'sphere', 'frustum'].includes(startingShapeKindOfBody(shape) ?? '')\n"]}
@@ -1 +1 @@
1
- export default function route(page: string): "figure-list" | "figure-modeller" | undefined;
1
+ export default function route(page: string): "figure-modeller" | "figure-list" | undefined;