@things-factory/figure-ui 10.1.204 → 10.1.206
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/client/modeller/canvas.ts +91 -9
- package/client/modeller/editor-styles.ts +23 -22
- package/client/modeller/editor.ts +217 -184
- package/client/modeller/fasten-form.ts +130 -12
- package/client/modeller/figure-fasten-dialog.ts +45 -11
- package/client/modeller/figure-fasten-drawing.ts +233 -12
- package/client/modeller/figure-links.ts +2 -2
- package/client/modeller/figure-preview.ts +6 -6
- package/client/modeller/figure-section-editor.ts +6 -6
- package/client/modeller/figure-slot-layout-panel.ts +1 -1
- package/client/modeller/gizmo.ts +22 -1
- package/client/modeller/keys.ts +10 -1
- package/client/modeller/links.ts +79 -46
- package/client/modeller/palette.ts +1 -1
- package/client/modeller/product-session.ts +16 -4
- package/client/modeller/refusal-teller.ts +3 -25
- package/client/modeller/refusals.ts +19 -26
- package/client/modeller/section-drawing.ts +7 -7
- package/client/modeller/size-drag.ts +112 -0
- package/client/modeller/slot-layout-host.ts +3 -3
- package/client/modeller/stage-model.ts +6 -9
- package/client/pages/figure-modeller-page.ts +1 -4
- package/dist-client/modeller/canvas.d.ts +24 -3
- package/dist-client/modeller/canvas.js +96 -11
- package/dist-client/modeller/canvas.js.map +1 -1
- package/dist-client/modeller/editor-styles.js +23 -22
- package/dist-client/modeller/editor-styles.js.map +1 -1
- package/dist-client/modeller/editor.d.ts +35 -35
- package/dist-client/modeller/editor.js +221 -193
- package/dist-client/modeller/editor.js.map +1 -1
- package/dist-client/modeller/fasten-form.d.ts +46 -4
- package/dist-client/modeller/fasten-form.js +120 -11
- package/dist-client/modeller/fasten-form.js.map +1 -1
- package/dist-client/modeller/figure-fasten-dialog.d.ts +18 -4
- package/dist-client/modeller/figure-fasten-dialog.js +49 -12
- package/dist-client/modeller/figure-fasten-dialog.js.map +1 -1
- package/dist-client/modeller/figure-fasten-drawing.d.ts +33 -1
- package/dist-client/modeller/figure-fasten-drawing.js +235 -11
- package/dist-client/modeller/figure-fasten-drawing.js.map +1 -1
- package/dist-client/modeller/figure-links.d.ts +1 -1
- package/dist-client/modeller/figure-links.js +2 -2
- package/dist-client/modeller/figure-links.js.map +1 -1
- package/dist-client/modeller/figure-preview.js +4 -4
- package/dist-client/modeller/figure-preview.js.map +1 -1
- package/dist-client/modeller/figure-section-editor.d.ts +3 -3
- package/dist-client/modeller/figure-section-editor.js +4 -4
- package/dist-client/modeller/figure-section-editor.js.map +1 -1
- package/dist-client/modeller/figure-slot-layout-panel.js +1 -1
- package/dist-client/modeller/figure-slot-layout-panel.js.map +1 -1
- package/dist-client/modeller/gizmo.d.ts +18 -1
- package/dist-client/modeller/gizmo.js +19 -0
- package/dist-client/modeller/gizmo.js.map +1 -1
- package/dist-client/modeller/keys.d.ts +1 -1
- package/dist-client/modeller/keys.js +6 -0
- package/dist-client/modeller/keys.js.map +1 -1
- package/dist-client/modeller/links.d.ts +30 -24
- package/dist-client/modeller/links.js +67 -42
- package/dist-client/modeller/links.js.map +1 -1
- package/dist-client/modeller/palette.js +1 -1
- package/dist-client/modeller/palette.js.map +1 -1
- package/dist-client/modeller/product-session.d.ts +15 -13
- package/dist-client/modeller/product-session.js +15 -5
- package/dist-client/modeller/product-session.js.map +1 -1
- package/dist-client/modeller/refusal-teller.d.ts +1 -12
- package/dist-client/modeller/refusal-teller.js +3 -22
- package/dist-client/modeller/refusal-teller.js.map +1 -1
- package/dist-client/modeller/refusals.d.ts +6 -14
- package/dist-client/modeller/refusals.js +16 -27
- package/dist-client/modeller/refusals.js.map +1 -1
- package/dist-client/modeller/section-drawing.d.ts +3 -4
- package/dist-client/modeller/section-drawing.js +10 -9
- package/dist-client/modeller/section-drawing.js.map +1 -1
- package/dist-client/modeller/size-drag.d.ts +38 -0
- package/dist-client/modeller/size-drag.js +85 -0
- package/dist-client/modeller/size-drag.js.map +1 -0
- package/dist-client/modeller/slot-layout-host.d.ts +1 -3
- package/dist-client/modeller/slot-layout-host.js +3 -3
- package/dist-client/modeller/slot-layout-host.js.map +1 -1
- package/dist-client/modeller/stage-model.d.ts +5 -7
- package/dist-client/modeller/stage-model.js +6 -6
- package/dist-client/modeller/stage-model.js.map +1 -1
- package/dist-client/pages/figure-modeller-page.d.ts +0 -2
- package/dist-client/pages/figure-modeller-page.js +1 -6
- package/dist-client/pages/figure-modeller-page.js.map +1 -1
- package/dist-client/route.d.ts +1 -1
- package/dist-client/tsconfig.tsbuildinfo +1 -1
- package/dist-server/tsconfig.tsbuildinfo +1 -1
- package/package.json +7 -7
- package/test/align.test.ts +7 -11
- package/test/browser/acceptance-page.js +11 -1
- package/test/browser/editing.check.mjs +17 -13
- package/test/browser/links-page.js +2 -2
- package/test/browser/links.check.mjs +124 -69
- package/test/browser/modeller-page.js +30 -18
- package/test/browser/modeller-revision.check.mjs +39 -0
- package/test/browser/modeller.check.mjs +77 -26
- package/test/browser/parts.check.mjs +49 -0
- package/test/browser/screen-page.js +0 -2
- package/test/browser/screen.check.mjs +11 -13
- package/test/browser/section.check.mjs +29 -2
- package/test/fasten-form.test.ts +102 -1
- package/test/gizmo.test.ts +25 -16
- package/test/keys.test.ts +11 -0
- package/test/links.test.ts +46 -36
- package/test/refusals.test.ts +17 -21
- package/test/resize-with-placement.test.ts +14 -13
- package/test/revision.test.ts +3 -2
- package/test/section-editor.test.ts +9 -10
- package/test/size-drag.test.ts +114 -0
- package/test/slot-layout-host.test.ts +13 -8
- package/translations/en.json +32 -34
- package/translations/es.json +32 -34
- package/translations/ja.json +32 -34
- package/translations/ko.json +32 -34
- package/translations/ms.json +32 -34
- package/translations/zh.json +32 -34
|
@@ -11,6 +11,12 @@ export function keyActionOf(e, selected) {
|
|
|
11
11
|
return e.shiftKey ? 'redo' : 'undo';
|
|
12
12
|
if (mod && key === 'y')
|
|
13
13
|
return 'redo';
|
|
14
|
+
if (mod && key === 'c')
|
|
15
|
+
return selected ? 'copy' : 'hold';
|
|
16
|
+
if (mod && key === 'v')
|
|
17
|
+
return e.shiftKey ? 'hold' : 'paste';
|
|
18
|
+
if (mod && key === 'x')
|
|
19
|
+
return 'hold';
|
|
14
20
|
if (!selected)
|
|
15
21
|
return null;
|
|
16
22
|
if (mod && key === 'd')
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"keys.js","sourceRoot":"","sources":["../../client/modeller/keys.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"keys.js","sourceRoot":"","sources":["../../client/modeller/keys.ts"],"names":[],"mappings":"AAiBA,MAAM,UAAU,GAA6B,EAAE,CAAC,EAAE,WAAW,EAAE,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE,OAAO,EAAE,CAAA;AAkBxF,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,CAAC,OAAO,EAAE,QAAQ,EAAE,UAAU,CAAC,CAAC,CAAA;AAEvD,MAAM,UAAU,WAAW,CAAC,CAAU,EAAE,QAAgB;IACtD,IAAI,CAAC,CAAC,MAAM,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,WAAW,EAAE,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,iBAAiB,CAAC;QAAE,OAAO,IAAI,CAAA;IAC/G,IAAI,CAAC,CAAC,MAAM;QAAE,OAAO,IAAI,CAAA;IACzB,MAAM,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,IAAI,CAAC,CAAC,OAAO,CAAC,CAAA;IACtC,MAAM,GAAG,GAAG,CAAC,CAAC,GAAG,CAAC,WAAW,EAAE,CAAA;IAC/B,IAAI,GAAG,IAAI,GAAG,KAAK,GAAG;QAAE,OAAO,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAA;IAC3D,IAAI,GAAG,IAAI,GAAG,KAAK,GAAG;QAAE,OAAO,MAAM,CAAA;IACrC,IAAI,GAAG,IAAI,GAAG,KAAK,GAAG;QAAE,OAAO,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAA;IACzD,IAAI,GAAG,IAAI,GAAG,KAAK,GAAG;QAAE,OAAO,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAA;IAC5D,IAAI,GAAG,IAAI,GAAG,KAAK,GAAG;QAAE,OAAO,MAAM,CAAA;IACrC,IAAI,CAAC,QAAQ;QAAE,OAAO,IAAI,CAAA;IAC1B,IAAI,GAAG,IAAI,GAAG,KAAK,GAAG;QAAE,OAAO,WAAW,CAAA;IAC1C,IAAI,CAAC,GAAG,IAAI,CAAC,GAAG,KAAK,QAAQ,IAAI,GAAG,KAAK,WAAW,CAAC;QAAE,OAAO,QAAQ,CAAA;IACtE,IAAI,CAAC,GAAG,IAAI,GAAG,KAAK,QAAQ;QAAE,OAAO,UAAU,CAAA;IAC/C,IAAI,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,QAAQ,IAAI,CAAC,CAAC,CAAC,OAAO,IAAI,UAAU,CAAC,GAAG,CAAC;QAAE,OAAO,UAAU,CAAC,GAAG,CAAC,CAAA;IAChF,OAAO,IAAI,CAAA;AACb,CAAC","sourcesContent":["/*\n * The modeller's keys: undo and redo on Ctrl+Z / Ctrl+Y, and what a person reaches for next. Read apart\n * from the element so the rule is tested without a browser.\n *\n * Typing is never taken: a key pressed in a field belongs to the field -- Backspace in a name box deletes a letter,\n * not the part.\n */\n/*\n * `copy` · `paste` are the modeller's own: a part copied and pasted is the kernel's duplicate, with a name of its own.\n * Left to the scene, Ctrl+C / Ctrl+V copied the canvas object with the part's name -- two objects of one name, the copy\n * moving the original (user 2026-10-21). `hold` is a key kept from the scene that the modeller has no use for: the\n * scene's cut and bulk paste act on canvas objects alone and leave the figure out of step with them.\n */\nexport type KeyAction = 'undo' | 'redo' | 'duplicate' | 'remove' | 'deselect' | 'copy' | 'paste' | 'hold' | GizmoKey\n\n/** The gizmo's modes on W, E and R, as the scene has them. */\nexport type GizmoKey = 'translate' | 'rotate' | 'scale'\nconst GIZMO_KEYS: Record<string, GizmoKey> = { w: 'translate', e: 'rotate', r: 'scale' }\n\nexport interface KeyLike {\n key: string\n ctrlKey?: boolean\n metaKey?: boolean\n shiftKey?: boolean\n altKey?: boolean\n /** The element the key was pressed in -- the first of the event's composed path. */\n target?: { tagName?: string; isContentEditable?: boolean } | null\n /**\n * Pressed with the canvas focused. The scene takes W, E and R there itself; they are passed on only from elsewhere\n * -- a part picked in the parts list left the focus there, and the gizmo could not be had at all (2026-09-25).\n * Passing a key the scene already took would toggle the gizmo off again.\n */\n inScene?: boolean\n}\n\nconst TYPING = new Set(['INPUT', 'SELECT', 'TEXTAREA'])\n\nexport function keyActionOf(e: KeyLike, selected: string): KeyAction | null {\n if (e.target && (TYPING.has(String(e.target.tagName).toUpperCase()) || e.target.isContentEditable)) return null\n if (e.altKey) return null\n const mod = !!(e.ctrlKey || e.metaKey)\n const key = e.key.toLowerCase()\n if (mod && key === 'z') return e.shiftKey ? 'redo' : 'undo'\n if (mod && key === 'y') return 'redo'\n if (mod && key === 'c') return selected ? 'copy' : 'hold'\n if (mod && key === 'v') return e.shiftKey ? 'hold' : 'paste'\n if (mod && key === 'x') return 'hold'\n if (!selected) return null\n if (mod && key === 'd') return 'duplicate'\n if (!mod && (key === 'delete' || key === 'backspace')) return 'remove'\n if (!mod && key === 'escape') return 'deselect'\n if (!mod && !e.shiftKey && !e.inScene && GIZMO_KEYS[key]) return GIZMO_KEYS[key]\n return null\n}\n"]}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { figureJointAsFixed } from '@hatiolab/figure-model';
|
|
2
|
-
import type { FigureFollow,
|
|
3
|
-
import type { FigureAsset, FigureAuthoringAction, FigureBindingPlace, FigureFace, FigureFacing, FigureJoint } from '@hatiolab/figure-model';
|
|
2
|
+
import type { FigureFollow, FigureInstanceSizeRange } from '@hatiolab/figure-model';
|
|
3
|
+
import type { FigureAsset, FigureAuthoringAction, FigureBindingPlace, FigureFace, FigureFacing, FigureJoint, FigureSurfacePlace } from '@hatiolab/figure-model';
|
|
4
4
|
import { type FigureRevision } from './revision.js';
|
|
5
5
|
import { type JointAxisVector } from './joint-axis.js';
|
|
6
6
|
export type Axis = 'x' | 'y' | 'z';
|
|
@@ -14,6 +14,11 @@ export type Follow = FigureFollow;
|
|
|
14
14
|
*/
|
|
15
15
|
/** What one measure of a part is worked out from, read back from the graph (figure-model's reader, one copy). */
|
|
16
16
|
export declare function followOf(asset: FigureAsset, part: string, axis: Axis): Follow;
|
|
17
|
+
/**
|
|
18
|
+
* The parts whose size follows the figure's size on some axis, in the model's order: the ones that change when the
|
|
19
|
+
* figure is resized on a drawing. Read by figure-model's own follow (`figureFollowOf`), not worked out again here.
|
|
20
|
+
*/
|
|
21
|
+
export declare function sizeFollowersOf(asset: FigureAsset): string[];
|
|
17
22
|
/** Whether the asset declares an instance size on an axis — only then can a dimension follow it. */
|
|
18
23
|
export declare const hasInstanceSize: (asset: FigureAsset, axis: Axis) => boolean;
|
|
19
24
|
/** The part this one is fastened to, or `null`. The mounting plane counts as fastened (the model names no part). */
|
|
@@ -91,6 +96,11 @@ export declare function measureNow(revision: FigureRevision, part: string, axis:
|
|
|
91
96
|
* written (a portal's opening sides as well as its outer faces). A sphere has none.
|
|
92
97
|
*/
|
|
93
98
|
export declare function facesOf(asset: FigureAsset, part: string): string[];
|
|
99
|
+
/** The curved surface a part offers to be fastened to at a point: a cylinder's side or a sphere's round (ADR-0140 3.9). */
|
|
100
|
+
export declare function surfaceOf(asset: FigureAsset, part: string): {
|
|
101
|
+
form: 'around' | 'toward';
|
|
102
|
+
axis: Axis;
|
|
103
|
+
} | undefined;
|
|
94
104
|
/** The axis a face of a part faces along, from the same definitions. */
|
|
95
105
|
export declare function faceAxisOf(asset: FigureAsset, part: string, face: string): Axis | undefined;
|
|
96
106
|
/** Whether `attach` can take this part: any part whose shape has a flat face. */
|
|
@@ -105,7 +115,7 @@ export declare function rowAxesOf(asset: FigureAsset, part: string): Axis[];
|
|
|
105
115
|
* Why a part cannot be made into a row here, in the terms `rowAxesOf` refuses it: said, not hidden (ADR-0097 §5.5).
|
|
106
116
|
* Nothing when it can, or when it already is a row (that row is shown where it stands).
|
|
107
117
|
*/
|
|
108
|
-
export declare function rowBlockOf(asset: FigureAsset, part: string): 'unfastened' | 'on-plane' | 'holds-parts' | 'at-origin' | undefined;
|
|
118
|
+
export declare function rowBlockOf(asset: FigureAsset, part: string): 'unfastened' | 'on-plane' | 'holds-parts' | 'at-origin' | 'on-surface' | undefined;
|
|
109
119
|
/** The row a part is made into, as its fastening's record keeps it. */
|
|
110
120
|
export declare const rowOf: (asset: FigureAsset, part: string) => {
|
|
111
121
|
axis: import("@hatiolab/figure-model").FigureJointAxis;
|
|
@@ -187,6 +197,8 @@ export declare function fasteningOf(revision: FigureRevision, part: string): {
|
|
|
187
197
|
alignMm: Partial<Record<Axis, number>>;
|
|
188
198
|
/** Placed by a part's origin (ADR-0107 decision 4): no face to fasten by, and the form does not write it. */
|
|
189
199
|
atOrigin?: true;
|
|
200
|
+
/** On a point of the target's curved surface (ADR-0140 3.9), as the record says it. */
|
|
201
|
+
surface?: FigureSurfacePlace;
|
|
190
202
|
} | null;
|
|
191
203
|
/**
|
|
192
204
|
* The `gap` an attach command writes from the form: a worked-out gap as it was read, else the number, else none. Read as
|
|
@@ -215,7 +227,7 @@ export declare function placeKeptOf(revision: FigureRevision, part: string, targ
|
|
|
215
227
|
export declare function alignOf(asset: FigureAsset, target: {
|
|
216
228
|
part: string;
|
|
217
229
|
face: string;
|
|
218
|
-
} | 'plane', how: Partial<Record<Axis, Alignment>>, mm: Partial<Record<Axis, number>>, inputs?: Partial<Record<Axis, string>>): Partial<Record<Axis, "
|
|
230
|
+
} | 'plane', how: Partial<Record<Axis, Alignment>>, mm: Partial<Record<Axis, number>>, inputs?: Partial<Record<Axis, string>>): Partial<Record<Axis, "max" | "min" | {
|
|
219
231
|
mm: number | {
|
|
220
232
|
from: {
|
|
221
233
|
of: "instance";
|
|
@@ -346,32 +358,27 @@ export declare const holdsThings: (asset: FigureAsset) => boolean;
|
|
|
346
358
|
/** What `accepts` a person typed: names split on commas, trimmed, blanks and repeats dropped. */
|
|
347
359
|
export declare const acceptsOf: (typed: string) => string[];
|
|
348
360
|
/**
|
|
349
|
-
* The sizes a board may give this figure: the
|
|
350
|
-
*
|
|
351
|
-
*
|
|
352
|
-
* made to grow with the figure; measuring again gives the figure its whole range back. Nothing when no size is declared.
|
|
361
|
+
* The sizes a board may give this figure, per axis: the ends its author narrowed, and the rest as far as its parts
|
|
362
|
+
* allow, computed by figure-model (`figureInstanceSizeRangeOf`, ADR-0087 decision 6) -- the screen does not measure
|
|
363
|
+
* on its own. Nothing when no size is declared.
|
|
353
364
|
*/
|
|
354
|
-
export declare function instanceRangeOf(asset: FigureAsset):
|
|
355
|
-
declared: FigureSizeRange;
|
|
356
|
-
measured: FigureSizeRange;
|
|
357
|
-
} | undefined;
|
|
365
|
+
export declare function instanceRangeOf(asset: FigureAsset): Record<Axis, FigureInstanceSizeRange> | undefined;
|
|
358
366
|
/**
|
|
359
|
-
* The size
|
|
360
|
-
*
|
|
361
|
-
*
|
|
362
|
-
* to follow its size -- every figure made in the modeller was left there (user, 2026-09-25).
|
|
367
|
+
* The size to declare for a figure that has none yet: its parts' extent now, the size it is authored at. Undefined when
|
|
368
|
+
* the figure already has a size or has nothing to measure. Without a declared size a board cannot resize the figure,
|
|
369
|
+
* and no part is offered to follow its size -- every figure made in the modeller was left there (user, 2026-09-25).
|
|
363
370
|
*/
|
|
364
|
-
export declare function
|
|
365
|
-
range: FigureSizeRange;
|
|
371
|
+
export declare function instanceSizeToDeclare(asset: FigureAsset): {
|
|
366
372
|
at: Record<Axis, number>;
|
|
367
373
|
} | undefined;
|
|
368
|
-
/** Whether measuring again would change the declared range (to a micrometre). */
|
|
369
374
|
/**
|
|
370
|
-
* The
|
|
371
|
-
*
|
|
372
|
-
* value is not a number; whether the figure holds there is the command's to say.
|
|
375
|
+
* The ends the author narrowed, with one end typed or cleared: a number narrows that end, an empty field opens it
|
|
376
|
+
* again to what the parts allow. Whether the figure holds there is the command's to say.
|
|
373
377
|
*/
|
|
374
|
-
export declare function rangeWith(
|
|
378
|
+
export declare function rangeWith(range: Record<Axis, FigureInstanceSizeRange>, axis: Axis, bound: 'min' | 'max', value: number): Partial<Record<Axis, {
|
|
379
|
+
min?: number;
|
|
380
|
+
max?: number;
|
|
381
|
+
}>>;
|
|
375
382
|
/**
|
|
376
383
|
* For a figure that grows by one ratio: how many of its lengths would follow the ratio, and which it leaves with their
|
|
377
384
|
* own number (not those that follow another length -- they grow with it). Nothing for a figure of another sizing.
|
|
@@ -382,7 +389,6 @@ export declare function proportionToFollow(asset: FigureAsset): {
|
|
|
382
389
|
} | undefined;
|
|
383
390
|
/** A value of the figure as it stands, by its graph reference. */
|
|
384
391
|
export declare function valueNow(revision: FigureRevision, ref: string | undefined): number | undefined;
|
|
385
|
-
export declare function rangeDiffers(a: FigureSizeRange, b: FigureSizeRange): boolean;
|
|
386
392
|
/**
|
|
387
393
|
* Whether the figure is a mechanism -- a part turns about a joint. Such a figure should grow by one ratio (chief
|
|
388
394
|
* architect's ruling 2026-09-24); the modeller proposes it, the author declares it.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { figureShapeFaces, figurePartsExtentOf, figureFollowOf, proposeFigureProportion,
|
|
1
|
+
import { figureShapeFaces, figurePartsExtentOf, figureFollowOf, proposeFigureProportion, figureAttachmentsOf, figureBindingsOf, figureInstanceSizeRangeOf, figureFollowableAxesOf, figureShapeProviderOf, figureMimicTermsOf, figureFastenKeptPlaceOf, figureFastenAxesOf, figureJointAsFixed, figureJointWithMotion, figureSurfaceOf } from '@hatiolab/figure-model';
|
|
2
2
|
import { startingShapeKindOf } from './starting-shape.js';
|
|
3
3
|
import { boxOf } from './align.js';
|
|
4
4
|
import { graphValuesOf } from './revision.js';
|
|
@@ -14,6 +14,21 @@ const modelOf = (asset) => asset.document.model;
|
|
|
14
14
|
export function followOf(asset, part, axis) {
|
|
15
15
|
return figureFollowOf(asset, part, axis);
|
|
16
16
|
}
|
|
17
|
+
/**
|
|
18
|
+
* The parts whose size follows the figure's size on some axis, in the model's order: the ones that change when the
|
|
19
|
+
* figure is resized on a drawing. Read by figure-model's own follow (`figureFollowOf`), not worked out again here.
|
|
20
|
+
*/
|
|
21
|
+
export function sizeFollowersOf(asset) {
|
|
22
|
+
const parts = modelOf(asset).nodes.filter(n => n.op === 'place@1').map(n => n.id);
|
|
23
|
+
return parts.filter(part => AXES.some(axis => {
|
|
24
|
+
try {
|
|
25
|
+
return figureFollowOf(asset, part, axis).of === 'instance';
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
return false;
|
|
29
|
+
}
|
|
30
|
+
}));
|
|
31
|
+
}
|
|
17
32
|
/** Whether the asset declares an instance size on an axis — only then can a dimension follow it. */
|
|
18
33
|
export const hasInstanceSize = (asset, axis) => modelOf(asset).inputs.some(i => i.id === `size.${axis}` && i.role === 'design');
|
|
19
34
|
/** The part this one is fastened to, or `null`. The mounting plane counts as fastened (the model names no part). */
|
|
@@ -202,7 +217,13 @@ export function measureNow(revision, part, axis) {
|
|
|
202
217
|
*/
|
|
203
218
|
export function facesOf(asset, part) {
|
|
204
219
|
const shape = modelOf(asset).nodes.find(n => n.id === `${part}.shape`);
|
|
205
|
-
|
|
220
|
+
const faces = (shape && figureShapeFaces(shape)) ?? {};
|
|
221
|
+
/* A cylinder's curved side is no face: a part is fastened to a point on it (ADR-0140 3.9, `surfaceOf`). */
|
|
222
|
+
return inScreenOrder(Object.keys(faces).filter(f => faces[f].contact !== 'line'));
|
|
223
|
+
}
|
|
224
|
+
/** The curved surface a part offers to be fastened to at a point: a cylinder's side or a sphere's round (ADR-0140 3.9). */
|
|
225
|
+
export function surfaceOf(asset, part) {
|
|
226
|
+
return figureSurfaceOf(asset, part);
|
|
206
227
|
}
|
|
207
228
|
/** Faces in the order the screen lists them: the six outer ones left to front, then the rest as the shape gives them. */
|
|
208
229
|
const SCREEN_ORDER = ['left', 'right', 'bottom', 'top', 'back', 'front'];
|
|
@@ -217,7 +238,7 @@ export function faceAxisOf(asset, part, face) {
|
|
|
217
238
|
}
|
|
218
239
|
/** Whether `attach` can take this part: any part whose shape has a flat face. */
|
|
219
240
|
export function canFasten(asset, part) {
|
|
220
|
-
return facesOf(asset, part).length > 0;
|
|
241
|
+
return facesOf(asset, part).length > 0 || !!surfaceOf(asset, part);
|
|
221
242
|
}
|
|
222
243
|
const OUTER = { x: ['left', 'right'], y: ['bottom', 'top'], z: ['back', 'front'] };
|
|
223
244
|
/**
|
|
@@ -253,6 +274,9 @@ export function rowBlockOf(asset, part) {
|
|
|
253
274
|
return 'on-plane';
|
|
254
275
|
if (joints.some(o => 'part' in o.body0 && o.body0.part === part))
|
|
255
276
|
return 'holds-parts';
|
|
277
|
+
/* On a point of a curved surface a row has no face to run across (ADR-0140 3.9): the kernel refuses it. */
|
|
278
|
+
if ('surface' in j.body0)
|
|
279
|
+
return 'on-surface';
|
|
256
280
|
if (!('face' in j.body0) || 'at' in j.body1)
|
|
257
281
|
return 'at-origin';
|
|
258
282
|
return undefined;
|
|
@@ -328,8 +352,22 @@ export function fasteningOf(revision, part) {
|
|
|
328
352
|
return null;
|
|
329
353
|
const toPlane = 'plane' in record.body0;
|
|
330
354
|
const target = 'part' in record.body0 ? record.body0.part : '';
|
|
355
|
+
const surface = 'surface' in record.body0 ? record.body0.surface : undefined;
|
|
331
356
|
const targetFace = 'face' in record.body0 ? record.body0.face : 'top';
|
|
332
|
-
const atOrigin = !('plane' in record.body0) && (!('face' in record.body0) || 'at' in record.body1);
|
|
357
|
+
const atOrigin = !('plane' in record.body0) && ((!('face' in record.body0) && !surface) || 'at' in record.body1);
|
|
358
|
+
if (surface)
|
|
359
|
+
return {
|
|
360
|
+
toPlane,
|
|
361
|
+
target,
|
|
362
|
+
targetFace,
|
|
363
|
+
facing: record.origin?.facing ?? 'meet',
|
|
364
|
+
gap: typeof record.origin?.gap === 'number' ? record.origin.gap : 0,
|
|
365
|
+
...(record.origin?.gap !== undefined && typeof record.origin.gap !== 'number' ? { gapKept: record.origin.gap } : {}),
|
|
366
|
+
align: {},
|
|
367
|
+
alignMm: {},
|
|
368
|
+
surface,
|
|
369
|
+
...(atOrigin ? { atOrigin: true } : {})
|
|
370
|
+
};
|
|
333
371
|
/* Where across the face: as the record says it, and where it says nothing, where the part stands now. */
|
|
334
372
|
const kept = placeKeptOf(revision, part, toPlane ? 'plane' : { part: target, face: targetFace });
|
|
335
373
|
for (const [axis, how] of Object.entries(record.origin?.align ?? {})) {
|
|
@@ -591,55 +629,45 @@ export const holdsThings = (asset) => (asset.document.capabilities.names ?? []).
|
|
|
591
629
|
/** What `accepts` a person typed: names split on commas, trimmed, blanks and repeats dropped. */
|
|
592
630
|
export const acceptsOf = (typed) => [...new Set(typed.split(',').map(one => one.trim()).filter(Boolean))];
|
|
593
631
|
/**
|
|
594
|
-
* The sizes a board may give this figure: the
|
|
595
|
-
*
|
|
596
|
-
*
|
|
597
|
-
* made to grow with the figure; measuring again gives the figure its whole range back. Nothing when no size is declared.
|
|
632
|
+
* The sizes a board may give this figure, per axis: the ends its author narrowed, and the rest as far as its parts
|
|
633
|
+
* allow, computed by figure-model (`figureInstanceSizeRangeOf`, ADR-0087 decision 6) -- the screen does not measure
|
|
634
|
+
* on its own. Nothing when no size is declared.
|
|
598
635
|
*/
|
|
599
636
|
export function instanceRangeOf(asset) {
|
|
600
|
-
const
|
|
601
|
-
|
|
602
|
-
const authored = {};
|
|
603
|
-
for (const axis of AXES) {
|
|
604
|
-
const input = inputs.find(i => i.id === `size.${axis}` && i.role === 'design');
|
|
605
|
-
const at = asset.designInputs[`size.${axis}`];
|
|
606
|
-
if (!input || typeof at !== 'number')
|
|
607
|
-
return undefined;
|
|
608
|
-
declared[axis] = { min: input.min ?? 0, max: input.max ?? Infinity };
|
|
609
|
-
authored[axis] = at;
|
|
610
|
-
}
|
|
611
|
-
const start = defaultInstanceSizeRange(authored);
|
|
612
|
-
const measured = foldInstanceSizeRange(start, figureInstanceSizeLimitsOf(asset, start)).range;
|
|
613
|
-
return { declared, measured };
|
|
637
|
+
const range = figureInstanceSizeRangeOf(asset);
|
|
638
|
+
return AXES.every(axis => range[axis]) ? range : undefined;
|
|
614
639
|
}
|
|
615
640
|
/**
|
|
616
|
-
* The size
|
|
617
|
-
*
|
|
618
|
-
*
|
|
619
|
-
* to follow its size -- every figure made in the modeller was left there (user, 2026-09-25).
|
|
641
|
+
* The size to declare for a figure that has none yet: its parts' extent now, the size it is authored at. Undefined when
|
|
642
|
+
* the figure already has a size or has nothing to measure. Without a declared size a board cannot resize the figure,
|
|
643
|
+
* and no part is offered to follow its size -- every figure made in the modeller was left there (user, 2026-09-25).
|
|
620
644
|
*/
|
|
621
|
-
export function
|
|
645
|
+
export function instanceSizeToDeclare(asset) {
|
|
622
646
|
if (instanceRangeOf(asset) || !asset.appearance.length)
|
|
623
647
|
return undefined;
|
|
624
648
|
/* The parts as they rest -- not the room their joints swing them through, which declared a door 8208 mm every way. */
|
|
625
649
|
const at = figurePartsExtentOf(asset);
|
|
626
650
|
if (AXES.some(axis => !(at[axis] > 0)))
|
|
627
651
|
return undefined;
|
|
628
|
-
|
|
629
|
-
return { range: foldInstanceSizeRange(start, figureInstanceSizeLimitsOf(asset, start)).range, at };
|
|
652
|
+
return { at };
|
|
630
653
|
}
|
|
631
|
-
/** Whether measuring again would change the declared range (to a micrometre). */
|
|
632
654
|
/**
|
|
633
|
-
* The
|
|
634
|
-
*
|
|
635
|
-
* value is not a number; whether the figure holds there is the command's to say.
|
|
655
|
+
* The ends the author narrowed, with one end typed or cleared: a number narrows that end, an empty field opens it
|
|
656
|
+
* again to what the parts allow. Whether the figure holds there is the command's to say.
|
|
636
657
|
*/
|
|
637
|
-
export function rangeWith(
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
658
|
+
export function rangeWith(range, axis, bound, value) {
|
|
659
|
+
const out = {};
|
|
660
|
+
for (const a of AXES) {
|
|
661
|
+
const ends = {};
|
|
662
|
+
for (const b of ['min', 'max']) {
|
|
663
|
+
const typed = a === axis && b === bound;
|
|
664
|
+
if (typed ? Number.isFinite(value) : range[a].narrowed[b])
|
|
665
|
+
ends[b] = typed ? value : range[a][b];
|
|
666
|
+
}
|
|
667
|
+
if (Object.keys(ends).length)
|
|
668
|
+
out[a] = ends;
|
|
669
|
+
}
|
|
670
|
+
return out;
|
|
643
671
|
}
|
|
644
672
|
/**
|
|
645
673
|
* For a figure that grows by one ratio: how many of its lengths would follow the ratio, and which it leaves with their
|
|
@@ -668,9 +696,6 @@ export function valueNow(revision, ref) {
|
|
|
668
696
|
return undefined;
|
|
669
697
|
}
|
|
670
698
|
}
|
|
671
|
-
export function rangeDiffers(a, b) {
|
|
672
|
-
return AXES.some(axis => Math.abs(a[axis].min - b[axis].min) > 1e-3 || Math.abs(a[axis].max - b[axis].max) > 1e-3);
|
|
673
|
-
}
|
|
674
699
|
/**
|
|
675
700
|
* Whether the figure is a mechanism -- a part turns about a joint. Such a figure should grow by one ratio (chief
|
|
676
701
|
* architect's ruling 2026-09-24); the modeller proposes it, the author declares it.
|