@hatiolab/figure-model 0.1.36 → 0.1.38

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 (61) hide show
  1. package/dist/index.d.ts +6 -3
  2. package/dist/index.d.ts.map +1 -1
  3. package/dist/index.js +6 -3
  4. package/dist/index.js.map +1 -1
  5. package/dist/v3-asset-types.d.ts +89 -16
  6. package/dist/v3-asset-types.d.ts.map +1 -1
  7. package/dist/v3-asset.d.ts.map +1 -1
  8. package/dist/v3-asset.js +34 -25
  9. package/dist/v3-asset.js.map +1 -1
  10. package/dist/v3-authoring-actions.d.ts +226 -0
  11. package/dist/v3-authoring-actions.d.ts.map +1 -0
  12. package/dist/v3-authoring-actions.js +812 -0
  13. package/dist/v3-authoring-actions.js.map +1 -0
  14. package/dist/v3-capabilities.d.ts.map +1 -1
  15. package/dist/v3-capabilities.js +4 -77
  16. package/dist/v3-capabilities.js.map +1 -1
  17. package/dist/v3-cost.d.ts +14 -0
  18. package/dist/v3-cost.d.ts.map +1 -0
  19. package/dist/v3-cost.js +69 -0
  20. package/dist/v3-cost.js.map +1 -0
  21. package/dist/v3-driver.d.ts +24 -1
  22. package/dist/v3-driver.d.ts.map +1 -1
  23. package/dist/v3-driver.js +68 -2
  24. package/dist/v3-driver.js.map +1 -1
  25. package/dist/v3-from-v2.d.ts +25 -1
  26. package/dist/v3-from-v2.d.ts.map +1 -1
  27. package/dist/v3-from-v2.js +598 -21
  28. package/dist/v3-from-v2.js.map +1 -1
  29. package/dist/v3-gate.d.ts +59 -6
  30. package/dist/v3-gate.d.ts.map +1 -1
  31. package/dist/v3-gate.js +291 -45
  32. package/dist/v3-gate.js.map +1 -1
  33. package/dist/v3-graph-types.d.ts +9 -1
  34. package/dist/v3-graph-types.d.ts.map +1 -1
  35. package/dist/v3-graph.d.ts.map +1 -1
  36. package/dist/v3-graph.js +53 -1
  37. package/dist/v3-graph.js.map +1 -1
  38. package/dist/v3-json.d.ts +0 -1
  39. package/dist/v3-json.d.ts.map +1 -1
  40. package/dist/v3-json.js +16 -1
  41. package/dist/v3-json.js.map +1 -1
  42. package/dist/v3-kernel-version.d.ts +1 -1
  43. package/dist/v3-kernel-version.js +1 -1
  44. package/dist/v3-mesh-compare.d.ts +2 -5
  45. package/dist/v3-mesh-compare.d.ts.map +1 -1
  46. package/dist/v3-mesh-compare.js +2 -145
  47. package/dist/v3-mesh-compare.js.map +1 -1
  48. package/dist/v3-shape-sampling.d.ts +14 -0
  49. package/dist/v3-shape-sampling.d.ts.map +1 -0
  50. package/dist/v3-shape-sampling.js +120 -0
  51. package/dist/v3-shape-sampling.js.map +1 -0
  52. package/dist/v3-surface.d.ts +15 -0
  53. package/dist/v3-surface.d.ts.map +1 -0
  54. package/dist/v3-surface.js +104 -0
  55. package/dist/v3-surface.js.map +1 -0
  56. package/docs/prototypes/v3-asset.schema.json +0 -8
  57. package/docs/v3-asset-persistence.md +1 -1
  58. package/docs/v3-legacy-risk-audit-2026-09-23.md +39 -0
  59. package/docs/v3-operator-contracts.md +1 -0
  60. package/docs/v3-shape-dimension-contract.md +111 -0
  61. package/package.json +1 -1
@@ -0,0 +1,226 @@
1
+ import type { Axis } from './types.ts';
2
+ import type { V3Asset } from './v3-asset-types.ts';
3
+ /** The six faces of a box-shaped part, and the axis each one faces along. */
4
+ export declare const V3_FACES: {
5
+ readonly left: "x";
6
+ readonly right: "x";
7
+ readonly bottom: "y";
8
+ readonly top: "y";
9
+ readonly back: "z";
10
+ readonly front: "z";
11
+ };
12
+ export type V3Face = keyof typeof V3_FACES;
13
+ /**
14
+ * What a dimension follows. Named rather than implied, because "as wide as the gate" and "as wide as the
15
+ * opening" and "as wide as the rail" are three different intentions that happen to agree at one size.
16
+ */
17
+ export type V3DimensionSource =
18
+ /** The instance's own size on an axis, as a board scales it. */
19
+ {
20
+ of: 'instance';
21
+ axis: Axis;
22
+ }
23
+ /** A design input the author declared, such as an opening width that is not the whole figure's width. */
24
+ | {
25
+ of: 'input';
26
+ input: string;
27
+ }
28
+ /** Another part's dimension on an axis. */
29
+ | {
30
+ of: 'part';
31
+ part: string;
32
+ axis: Axis;
33
+ };
34
+ /**
35
+ * A length a command takes: a fixed number of millimetres, or something the figure already knows, so the value
36
+ * keeps up when the instance is resized. `{ from: ..., times, plus }` reads the same sources a dimension link
37
+ * does, which is why an opening can be "the instance's width" in a gap as well as in a dimension.
38
+ */
39
+ export type V3Measure = number | {
40
+ from: V3DimensionSource;
41
+ times?: number;
42
+ plus?: number;
43
+ };
44
+ /** Where a part sits inside the face it is fastened to, on one of that face's two axes. */
45
+ export type V3FaceAlignment = 'centre' | 'min' | 'max' | {
46
+ mm: V3Measure;
47
+ };
48
+ export type V3AuthoringAction =
49
+ /**
50
+ * "This side of this part follows that." The value becomes `source × times + plus`, as graph nodes, so it
51
+ * keeps following after the instance is resized. An axis that already follows something is refused unless
52
+ * `replace` says so: a link is never overwritten in passing.
53
+ */
54
+ {
55
+ kind: 'link-dimension';
56
+ part: string;
57
+ axis: Axis;
58
+ source: V3DimensionSource;
59
+ times?: number;
60
+ plus?: number;
61
+ replace?: boolean;
62
+ }
63
+ /** "Stop following; this side is this long." The explicit way back to a fixed dimension. */
64
+ | {
65
+ kind: 'unlink-dimension';
66
+ part: string;
67
+ axis: Axis;
68
+ mm: number;
69
+ }
70
+ /**
71
+ * "Fasten this face of this part to that face of that one."
72
+ *
73
+ * Two faces do not place a part by themselves, so the rest is named too, with defaults:
74
+ * - `facing`: `meet` (the default) puts the two faces against each other, the part outside its target past
75
+ * that face — a leaf under a rail. `flush` lays them in the same plane pointing the same way, the part
76
+ * inside its target — a panel set level with the front of a frame.
77
+ * - `gap`: millimetres between the faces along the target face's normal. 0 by default.
78
+ * - `align`: where the part sits within the face, on each of the face's own two axes. `centre` by default;
79
+ * `min` and `max` bring the part's own edges level with the target's, and stay level as either is
80
+ * resized, because all of it is written as graph nodes.
81
+ *
82
+ * Both parts must be box-shaped and unturned: a face of a turned body is not an axis-aligned plane, and this
83
+ * command says nothing about rotation. A fastening that would make a part its own ancestor is refused.
84
+ */
85
+ | {
86
+ kind: 'attach';
87
+ part: string;
88
+ face: V3Face;
89
+ /** The face of another part, or the plane the figure is mounted on, which is a thing a part can stand on. */
90
+ to: {
91
+ part: string;
92
+ face: V3Face;
93
+ } | {
94
+ plane: 'mounting-plane';
95
+ };
96
+ facing?: 'meet' | 'flush';
97
+ gap?: V3Measure;
98
+ align?: Partial<Record<Axis, V3FaceAlignment>>;
99
+ replace?: boolean;
100
+ }
101
+ /** "This part is not fastened to anything any more." It keeps where it is now, in the asset frame. */
102
+ | {
103
+ kind: 'detach';
104
+ part: string;
105
+ }
106
+ /** "This part moves like this" — see `V3MotionRequest`. */
107
+ | V3MotionRequest
108
+ /** "This part does not move any more." The control it created and the clip that drove it go with it. */
109
+ | {
110
+ kind: 'remove-motion';
111
+ part: string;
112
+ }
113
+ /** "This is the room it takes up" — a proposal the author has looked at, confirmed as it stands. */
114
+ | {
115
+ kind: 'declare-occupancy';
116
+ proposal: V3OccupancyProposal;
117
+ placement: 'floor' | 'ceiling' | 'center';
118
+ }
119
+ /** "This face is what it is mounted on." A separate choice from the room it takes up. */
120
+ | {
121
+ kind: 'set-mounting-face';
122
+ part: string;
123
+ face: V3Face;
124
+ }
125
+ /** "It is not mounted on anything in particular." */
126
+ | {
127
+ kind: 'clear-mounting-face';
128
+ };
129
+ /** Apply one authoring command. Throws on any refusal, leaving the asset it was given untouched. */
130
+ export declare function applyV3Authoring(source: V3Asset, action: V3AuthoringAction): V3Asset;
131
+ /** What each part is fastened to, for a reader that wants the structure rather than the graph. */
132
+ export declare function v3AttachmentsOf(asset: V3Asset): Record<string, string | null>;
133
+ /**
134
+ * "This part moves like this."
135
+ *
136
+ * Added on top of whatever the part is fastened to, never instead of it: the motion becomes its own node between
137
+ * the part's pose and the frame it is fastened into, so the fastening is still there and still holds when the
138
+ * parent is resized (V3 designer's ruling 2026-09-23 — attachment and motion may not overwrite one pose).
139
+ *
140
+ * Named rather than assumed:
141
+ * - `frame`: which frame the axis is measured in. `attachment`, the default, is the frame the part is fastened
142
+ * into — the parent's, or the asset's for a part fastened to nothing. `asset` is only accepted for a part
143
+ * fastened to nothing, where the two are the same frame; for a fastened part the axis would have to be
144
+ * re-expressed, and this command does not do that quietly.
145
+ * - `state`: the control a person moves — its id, unit, range and starting value, and optionally the name it
146
+ * shows under and how long it takes to travel its range.
147
+ * - `travel`: for a `ratio` control on a slide, how far the part goes at 1. It can follow the instance or a
148
+ * part, so a gate opens by its own width whatever the gate's width is. A `mm` control needs none: it is the
149
+ * distance.
150
+ * - `clip`: optional, and named. A clip is declared, never inferred from an id.
151
+ */
152
+ export interface V3MotionRequest {
153
+ kind: 'add-motion';
154
+ part: string;
155
+ motion: {
156
+ kind: 'slide' | 'turn';
157
+ axis: Axis;
158
+ frame?: 'attachment' | 'asset';
159
+ };
160
+ state: {
161
+ id: string;
162
+ unit: 'mm' | 'deg' | 'ratio';
163
+ min: number;
164
+ max: number;
165
+ start?: number;
166
+ label?: string;
167
+ sweep?: number;
168
+ };
169
+ travel?: {
170
+ source?: V3DimensionSource;
171
+ times?: number;
172
+ plus?: number;
173
+ };
174
+ clip?: {
175
+ name: string;
176
+ duration: number;
177
+ keys: Array<{
178
+ at: number;
179
+ value: number;
180
+ }>;
181
+ loop?: 'wrap' | 'hold';
182
+ accumulates?: boolean;
183
+ };
184
+ replace?: boolean;
185
+ }
186
+ /**
187
+ * The room a figure takes up, worked out from its parts.
188
+ *
189
+ * Proposing and declaring are two steps on purpose (V3 designer's ruling 2026-09-23). What the parts come to is
190
+ * a candidate; the author confirms it. Nothing here ever widens a declaration that is already in the asset
191
+ * because the model changed — a figure that has outgrown its declared volume is something the gate reports, not
192
+ * something an authoring command papers over. Re-propose and confirm again.
193
+ *
194
+ * `over` says which question is being answered, because they have different answers:
195
+ * - `rest`: the room the figure takes up as it stands, every control at its starting value.
196
+ * - `range`: the room it takes up anywhere in its travel, every control swept over its whole range.
197
+ *
198
+ * Naming the face the figure is mounted on is a separate command. It is a different claim from how much room
199
+ * the figure takes up, and an author may want one without the other.
200
+ */
201
+ export interface V3OccupancyProposal {
202
+ over: 'rest' | 'range';
203
+ /** What the bounds come to at the asset's current design inputs, so a person sees what they are confirming. */
204
+ at: Record<Axis, {
205
+ min: number;
206
+ max: number;
207
+ }>;
208
+ /** The parts the bounds were read from. */
209
+ parts: string[];
210
+ /** Parts left out, and why. A proposal never pretends to cover what it could not read. */
211
+ skipped: Array<{
212
+ part: string;
213
+ reason: string;
214
+ }>;
215
+ /**
216
+ * The figure this was read from, as one value. Confirming a proposal against a different figure is refused:
217
+ * comparing the list of parts was not enough, because widening a part keeps the list and changes the answer
218
+ * (V3 designer's counterexample 2026-09-23 — a 100 mm part grown to 300 mm slipped an unseen volume through).
219
+ */
220
+ basis: string;
221
+ }
222
+ /** What the parts come to, and what a person is being asked to confirm. Reads the asset; changes nothing. */
223
+ export declare function proposeV3Occupancy(asset: V3Asset, options?: {
224
+ over?: 'rest' | 'range';
225
+ }): V3OccupancyProposal;
226
+ //# sourceMappingURL=v3-authoring-actions.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"v3-authoring-actions.d.ts","sourceRoot":"","sources":["../src/v3-authoring-actions.ts"],"names":[],"mappings":"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"}