@metamesh/modeling 0.0.0-bootstrap.0 → 0.6.0

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 (129) hide show
  1. package/CHANGELOG.md +105 -0
  2. package/README.md +313 -6
  3. package/THIRD_PARTY_NOTICES.md +153 -0
  4. package/dist/api/engine.d.ts +54 -0
  5. package/dist/api/geometry.d.ts +2912 -0
  6. package/dist/api/index.d.ts +1648 -0
  7. package/dist/api/text.d.ts +77 -0
  8. package/dist/engine/construction-operations.js +189 -0
  9. package/dist/engine/execution.js +227 -0
  10. package/dist/engine/export-3mf.js +100 -0
  11. package/dist/engine/extrusion-finishing.js +82 -0
  12. package/dist/engine/finalization.js +214 -0
  13. package/dist/engine/index.js +72 -0
  14. package/dist/engine/inspection-operations.js +52 -0
  15. package/dist/engine/kernel/LICENSE +201 -0
  16. package/dist/engine/kernel/manifold.js +2 -0
  17. package/dist/engine/kernel/manifold.wasm +0 -0
  18. package/dist/engine/loft-operations.js +156 -0
  19. package/dist/engine/mechanical-operations.js +51 -0
  20. package/dist/engine/modeling-context.js +38 -0
  21. package/dist/engine/native/adapter.js +20 -0
  22. package/dist/engine/native/context.js +209 -0
  23. package/dist/engine/native/copied-views.js +46 -0
  24. package/dist/engine/native/index.js +13 -0
  25. package/dist/engine/native/operations.js +115 -0
  26. package/dist/engine/native/ownership.js +98 -0
  27. package/dist/engine/native/regions.js +138 -0
  28. package/dist/engine/native/scoped-primitives.js +127 -0
  29. package/dist/engine/native/scoped-threads.js +34 -0
  30. package/dist/engine/native/threads.js +52 -0
  31. package/dist/engine/native/transforms.js +111 -0
  32. package/dist/engine/operation-environment.js +20 -0
  33. package/dist/engine/pattern-operations.js +36 -0
  34. package/dist/engine/pattern-placements.js +176 -0
  35. package/dist/engine/primitive-anchors.js +70 -0
  36. package/dist/engine/primitive-correspondence.js +74 -0
  37. package/dist/engine/primitive-operations.js +151 -0
  38. package/dist/engine/primitive-profile-types.js +1 -0
  39. package/dist/engine/primitive-profiles.js +250 -0
  40. package/dist/engine/profile-mesh.js +279 -0
  41. package/dist/engine/section-operations.js +93 -0
  42. package/dist/engine/shape-anchors.js +88 -0
  43. package/dist/engine/transform-operations.js +225 -0
  44. package/dist/engine/types.js +1 -0
  45. package/dist/geometry/anchors.js +226 -0
  46. package/dist/geometry/curves/evaluation.js +162 -0
  47. package/dist/geometry/curves/sampling.js +265 -0
  48. package/dist/geometry/curves/types.js +1 -0
  49. package/dist/geometry/cylinder.js +117 -0
  50. package/dist/geometry/edge-finishing.js +160 -0
  51. package/dist/geometry/errors.js +76 -0
  52. package/dist/geometry/extrusion.js +150 -0
  53. package/dist/geometry/heightfield.js +107 -0
  54. package/dist/geometry/index.js +42 -0
  55. package/dist/geometry/loft-correspondence.js +204 -0
  56. package/dist/geometry/loft-geometry.js +184 -0
  57. package/dist/geometry/loft-options.js +56 -0
  58. package/dist/geometry/loft-simple-mesh.js +92 -0
  59. package/dist/geometry/loft-triangulation.js +64 -0
  60. package/dist/geometry/loft.js +80 -0
  61. package/dist/geometry/math/vector3.js +41 -0
  62. package/dist/geometry/mechanical/gear-motion.js +94 -0
  63. package/dist/geometry/mechanical/gears.js +484 -0
  64. package/dist/geometry/mechanical/index.js +13 -0
  65. package/dist/geometry/mechanical/standards.js +67 -0
  66. package/dist/geometry/mechanical/thread-helices.js +44 -0
  67. package/dist/geometry/mechanical/thread-mesh-plan.js +358 -0
  68. package/dist/geometry/mechanical/thread-mesh.js +142 -0
  69. package/dist/geometry/mechanical/thread-profiles.js +368 -0
  70. package/dist/geometry/mechanical/thread-solids.js +230 -0
  71. package/dist/geometry/mechanical/thread-surface.js +433 -0
  72. package/dist/geometry/mechanical/threads.js +138 -0
  73. package/dist/geometry/mesh-planar.js +408 -0
  74. package/dist/geometry/mesh-quality.js +396 -0
  75. package/dist/geometry/mesh-signature.js +137 -0
  76. package/dist/geometry/mesh.js +300 -0
  77. package/dist/geometry/model-limits.js +50 -0
  78. package/dist/geometry/numeric.js +11 -0
  79. package/dist/geometry/paths/frames.js +118 -0
  80. package/dist/geometry/paths/helix.js +49 -0
  81. package/dist/geometry/paths/path.js +30 -0
  82. package/dist/geometry/paths/planar.js +135 -0
  83. package/dist/geometry/paths/resampling.js +124 -0
  84. package/dist/geometry/polygon-clipping.js +52 -0
  85. package/dist/geometry/primitive-options.js +1 -0
  86. package/dist/geometry/primitive-resolution.js +17 -0
  87. package/dist/geometry/primitive-resolvers.js +235 -0
  88. package/dist/geometry/profiles/matched.js +184 -0
  89. package/dist/geometry/profiles/open-profile.js +137 -0
  90. package/dist/geometry/profiles/profiles.js +70 -0
  91. package/dist/geometry/profiles/rectangle.js +235 -0
  92. package/dist/geometry/profiles/shapes.js +34 -0
  93. package/dist/geometry/profiles/span-sampling.js +28 -0
  94. package/dist/geometry/profiles/types.js +1 -0
  95. package/dist/geometry/profiles/validation.js +20 -0
  96. package/dist/geometry/regions.js +371 -0
  97. package/dist/geometry/resolution.js +236 -0
  98. package/dist/geometry/revolve/revolve-mesh.js +123 -0
  99. package/dist/geometry/sampling-limits.js +29 -0
  100. package/dist/geometry/stroke-path.js +181 -0
  101. package/dist/geometry/svg/errors.js +23 -0
  102. package/dist/geometry/svg/fill.js +172 -0
  103. package/dist/geometry/svg/index.js +1 -0
  104. package/dist/geometry/svg/matrix.js +130 -0
  105. package/dist/geometry/svg/parser.js +536 -0
  106. package/dist/geometry/svg/path.js +349 -0
  107. package/dist/geometry/svg/types.js +1 -0
  108. package/dist/geometry/svg-import.js +662 -0
  109. package/dist/geometry/sweeps/options.js +28 -0
  110. package/dist/geometry/sweeps/orientation.js +106 -0
  111. package/dist/geometry/sweeps/stations.js +161 -0
  112. package/dist/geometry/sweeps/sweep-closed.js +96 -0
  113. package/dist/geometry/sweeps/sweep-mesh.js +20 -0
  114. package/dist/geometry/sweeps/sweep-open.js +76 -0
  115. package/dist/geometry/sweeps/validation.js +116 -0
  116. package/dist/geometry/text-contours.js +32 -0
  117. package/dist/geometry/text-layout.js +1 -0
  118. package/dist/geometry/tolerance.js +53 -0
  119. package/dist/geometry/transform-geometry.js +41 -0
  120. package/dist/geometry/transform3.js +340 -0
  121. package/dist/geometry/types.js +1 -0
  122. package/dist/index.js +1 -0
  123. package/dist/model/execution-binding.js +30 -0
  124. package/dist/model/operations.js +152 -0
  125. package/dist/model/options.js +1 -0
  126. package/dist/model/types.js +1 -0
  127. package/dist/text/index.js +48 -0
  128. package/dist/text/vector-text-layout.js +279 -0
  129. package/package.json +89 -8
@@ -0,0 +1,53 @@
1
+ import { GeometryError } from "./errors.js";
2
+ /** Default absolute, relative, and angular tolerances for millimetre-scale models. */
3
+ export const DEFAULT_GEOMETRY_TOLERANCE = Object.freeze({
4
+ /** Default absolute distance floor in model units. */
5
+ absolute: 1e-9,
6
+ /** Default angular comparison threshold in radians. */
7
+ angular: 1e-10,
8
+ /** Default dimensionless model-scale multiplier. */
9
+ relative: 1e-12,
10
+ });
11
+ const positiveFinite = (value, label) => {
12
+ if (!Number.isFinite(value) || !(value > 0)) {
13
+ throw new GeometryError("INVALID_TOLERANCE", `${label} must be finite and greater than zero.`);
14
+ }
15
+ return value;
16
+ };
17
+ /**
18
+ * Resolves comparison tolerances for a characteristic model scale.
19
+ *
20
+ * The effective linear tolerance is `max(absolute, relative * abs(scale))`.
21
+ * Supplying a scale of zero therefore retains the absolute floor. The function
22
+ * returns a new frozen value with finite distance and area thresholds and never mutates `options`.
23
+ *
24
+ * @param scale - Finite characteristic size in model units.
25
+ * @param options - Optional policy overrides.
26
+ * @returns A complete tolerance policy including the scale-resolved distance.
27
+ * @throws {@link GeometryError} When a scale or override is invalid.
28
+ */
29
+ export function resolveGeometryTolerance(scale = 1, options = {}) {
30
+ if (!Number.isFinite(scale)) {
31
+ throw new GeometryError("INVALID_TOLERANCE", "Geometry tolerance scale must be finite.");
32
+ }
33
+ const absolute = positiveFinite(options.absolute ?? DEFAULT_GEOMETRY_TOLERANCE.absolute, "Absolute geometry tolerance");
34
+ const angular = positiveFinite(options.angular ?? DEFAULT_GEOMETRY_TOLERANCE.angular, "Angular geometry tolerance");
35
+ const relative = options.relative ?? DEFAULT_GEOMETRY_TOLERANCE.relative;
36
+ if (!Number.isFinite(relative) || relative < 0) {
37
+ throw new GeometryError("INVALID_TOLERANCE", "Relative geometry tolerance must be finite and non-negative.");
38
+ }
39
+ const linear = Math.max(absolute, relative * Math.abs(scale));
40
+ const area = linear * linear;
41
+ if (!Number.isFinite(linear) || !Number.isFinite(area)) {
42
+ throw new GeometryError("INVALID_TOLERANCE", "Resolved geometry tolerances must be finite.", {
43
+ operation: "resolveGeometryTolerance",
44
+ });
45
+ }
46
+ return Object.freeze({
47
+ absolute,
48
+ angular,
49
+ area,
50
+ linear,
51
+ relative,
52
+ });
53
+ }
@@ -0,0 +1,41 @@
1
+ import { GeometryError } from "./errors.js";
2
+ import { normalizeLoop2, normalizeRegion2, signedAreaLoop2 } from "./regions.js";
3
+ import { resolveGeometryTolerance } from "./tolerance.js";
4
+ import { applyTransformPoint3, validateTransform3 } from "./transform3.js";
5
+ /** Applies an affine transform to a 3D path without mutating its points. */
6
+ export function transformPath3(path, transform) {
7
+ validateTransform3(transform);
8
+ return Object.freeze(path.map((point) => Object.freeze(applyTransformPoint3(transform, point))));
9
+ }
10
+ const transformPoint2 = (point, transform, tolerance) => {
11
+ const transformed = applyTransformPoint3(transform, [point[0], point[1], 0]);
12
+ const resolved = resolveGeometryTolerance(Math.max(1, Math.abs(transformed[0]), Math.abs(transformed[1])), tolerance);
13
+ if (Math.abs(transformed[2]) > resolved.linear) {
14
+ throw new GeometryError("INVALID_ARGUMENT", "A 2D transform must preserve the XY plane.", {
15
+ operation: "transformPath2",
16
+ });
17
+ }
18
+ return Object.freeze([transformed[0], transformed[1]]);
19
+ };
20
+ /** Applies an XY-plane-preserving affine transform to a 2D path. */
21
+ export function transformPath2(path, transform, tolerance = {}) {
22
+ validateTransform3(transform, tolerance);
23
+ return Object.freeze(path.map((point) => transformPoint2(point, transform, tolerance)));
24
+ }
25
+ /**
26
+ * Transforms a closed loop and preserves its original winding convention.
27
+ *
28
+ * Reflections reverse the point order automatically through normalization.
29
+ */
30
+ export function transformLoop2(loop, transform, tolerance = {}) {
31
+ const counterClockwise = signedAreaLoop2(loop) > 0;
32
+ const normalized = normalizeLoop2(loop, counterClockwise, tolerance);
33
+ return normalizeLoop2(transformPath2(normalized, transform, tolerance), counterClockwise, tolerance);
34
+ }
35
+ /** Transforms an explicit outer/hole region with reflection-safe winding correction. */
36
+ export function transformRegion2(region, transform, tolerance = {}) {
37
+ return normalizeRegion2(region.map((polygon) => ({
38
+ holes: polygon.holes.map((hole) => transformLoop2(hole, transform, tolerance)),
39
+ outer: transformLoop2(polygon.outer, transform, tolerance),
40
+ })), tolerance);
41
+ }
@@ -0,0 +1,340 @@
1
+ import { GeometryError } from "./errors.js";
2
+ import { resolveGeometryTolerance } from "./tolerance.js";
3
+ const finiteVector = (vector, label) => {
4
+ if (!vector.every(Number.isFinite)) {
5
+ throw new GeometryError("NON_FINITE_VALUE", `${label} coordinates must be finite.`);
6
+ }
7
+ };
8
+ const dot = (a, b) => a[0] * b[0] + a[1] * b[1] + a[2] * b[2];
9
+ const cross = (a, b) => [
10
+ a[1] * b[2] - a[2] * b[1],
11
+ a[2] * b[0] - a[0] * b[2],
12
+ a[0] * b[1] - a[1] * b[0],
13
+ ];
14
+ const length = (vector) => Math.hypot(vector[0], vector[1], vector[2]);
15
+ const normalized = (vector, label, tolerance) => {
16
+ finiteVector(vector, label);
17
+ const magnitude = length(vector);
18
+ if (magnitude <= tolerance) {
19
+ throw new GeometryError("DEGENERATE_GEOMETRY", `${label} must have non-zero length.`);
20
+ }
21
+ return [vector[0] / magnitude, vector[1] / magnitude, vector[2] / magnitude];
22
+ };
23
+ const frozenTransform = (values) => Object.freeze([...values]);
24
+ /** Identity transform, safe to share because it is frozen. */
25
+ export const IDENTITY_TRANSFORM3 = frozenTransform([
26
+ 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1,
27
+ ]);
28
+ /**
29
+ * Validates that a transform is finite, affine, and non-singular.
30
+ *
31
+ * @param transform - Row-major matrix to validate.
32
+ * @param tolerance - Optional comparison overrides.
33
+ * @returns The original transform after successful validation.
34
+ * @throws {@link GeometryError} For non-finite, non-affine, or singular matrices.
35
+ */
36
+ export function validateTransform3(transform, tolerance = {}) {
37
+ if (transform.length !== 16 || !transform.every(Number.isFinite)) {
38
+ throw new GeometryError("NON_FINITE_VALUE", "Transforms require sixteen finite values.");
39
+ }
40
+ const scale = Math.max(Math.abs(transform[0]), Math.abs(transform[1]), Math.abs(transform[2]), Math.abs(transform[4]), Math.abs(transform[5]), Math.abs(transform[6]), Math.abs(transform[8]), Math.abs(transform[9]), Math.abs(transform[10]));
41
+ const resolved = resolveGeometryTolerance(scale, tolerance);
42
+ if (Math.abs(transform[12]) > resolved.angular ||
43
+ Math.abs(transform[13]) > resolved.angular ||
44
+ Math.abs(transform[14]) > resolved.angular ||
45
+ Math.abs(transform[15] - 1) > resolved.angular) {
46
+ throw new GeometryError("INVALID_ARGUMENT", "Transform final row must be [0, 0, 0, 1].");
47
+ }
48
+ const determinant = transform[0] * (transform[5] * transform[10] - transform[6] * transform[9]) -
49
+ transform[1] * (transform[4] * transform[10] - transform[6] * transform[8]) +
50
+ transform[2] * (transform[4] * transform[9] - transform[5] * transform[8]);
51
+ const determinantScale = Math.hypot(transform[0], transform[1], transform[2]) *
52
+ Math.hypot(transform[4], transform[5], transform[6]) *
53
+ Math.hypot(transform[8], transform[9], transform[10]);
54
+ if (!Number.isFinite(determinant) || !Number.isFinite(determinantScale)) {
55
+ throw new GeometryError("NON_FINITE_VALUE", "Transform magnitude exceeds stable floating-point evaluation.");
56
+ }
57
+ if (Math.abs(determinant) <= determinantScale * resolved.angular) {
58
+ throw new GeometryError("SINGULAR_TRANSFORM", "Transform linear component is singular.");
59
+ }
60
+ return transform;
61
+ }
62
+ /**
63
+ * Creates an immutable translation transform.
64
+ *
65
+ * @param offset - Finite XYZ translation in model units.
66
+ * @returns A frozen row-major transform.
67
+ */
68
+ export function translationTransform3(offset) {
69
+ finiteVector(offset, "Translation");
70
+ return frozenTransform([1, 0, 0, offset[0], 0, 1, 0, offset[1], 0, 0, 1, offset[2], 0, 0, 0, 1]);
71
+ }
72
+ /**
73
+ * Creates an immutable axis-aligned scale transform.
74
+ *
75
+ * Negative components are reflections. Zero components are rejected because
76
+ * they collapse volume and cannot be inverted.
77
+ *
78
+ * @param factors - Finite, non-zero XYZ scale factors.
79
+ * @returns A frozen row-major transform.
80
+ */
81
+ export function scaleTransform3(factors) {
82
+ finiteVector(factors, "Scale");
83
+ if (factors.some((factor) => factor === 0)) {
84
+ throw new GeometryError("SINGULAR_TRANSFORM", "Scale factors must be non-zero.");
85
+ }
86
+ return frozenTransform([
87
+ factors[0],
88
+ 0,
89
+ 0,
90
+ 0,
91
+ 0,
92
+ factors[1],
93
+ 0,
94
+ 0,
95
+ 0,
96
+ 0,
97
+ factors[2],
98
+ 0,
99
+ 0,
100
+ 0,
101
+ 0,
102
+ 1,
103
+ ]);
104
+ }
105
+ /**
106
+ * Creates a right-handed axis-angle rotation transform.
107
+ *
108
+ * @param axis - Rotation axis; normalization is performed internally.
109
+ * @param angle - Finite right-handed angle with exactly one explicit unit.
110
+ * @param tolerance - Optional zero-length comparison overrides.
111
+ * @returns A frozen row-major transform.
112
+ */
113
+ export function rotationTransform3(axis, angle, tolerance = {}) {
114
+ if (typeof angle !== "object" || angle === null || "degrees" in angle === "radians" in angle) {
115
+ throw new GeometryError("INVALID_ARGUMENT", "Rotation requires exactly one of degrees or radians.", {
116
+ operation: "rotationTransform3",
117
+ });
118
+ }
119
+ const radians = "degrees" in angle ? (angle.degrees / 180) * Math.PI : angle.radians;
120
+ if (!Number.isFinite(radians)) {
121
+ throw new GeometryError("NON_FINITE_VALUE", "Rotation angle must be finite.");
122
+ }
123
+ const resolved = resolveGeometryTolerance(length(axis), tolerance);
124
+ const [x, y, z] = normalized(axis, "Rotation axis", resolved.linear);
125
+ const cosine = Math.cos(radians);
126
+ const sine = Math.sin(radians);
127
+ const inverse = 1 - cosine;
128
+ return frozenTransform([
129
+ cosine + x * x * inverse,
130
+ x * y * inverse - z * sine,
131
+ x * z * inverse + y * sine,
132
+ 0,
133
+ y * x * inverse + z * sine,
134
+ cosine + y * y * inverse,
135
+ y * z * inverse - x * sine,
136
+ 0,
137
+ z * x * inverse - y * sine,
138
+ z * y * inverse + x * sine,
139
+ cosine + z * z * inverse,
140
+ 0,
141
+ 0,
142
+ 0,
143
+ 0,
144
+ 1,
145
+ ]);
146
+ }
147
+ /**
148
+ * Creates an immutable right-handed XYZ Euler rotation transform.
149
+ *
150
+ * Rotations are applied about global X first, then Y, then Z. This matches the
151
+ * degree-based Euler convention used by the Manifold adapters.
152
+ *
153
+ * @param rotationDegrees - Finite `[x, y, z]` rotation angles in degrees.
154
+ * @returns A frozen row-major transform.
155
+ * @throws {@link GeometryError} When an angle is non-finite.
156
+ */
157
+ export function eulerTransform3(rotationDegrees) {
158
+ finiteVector(rotationDegrees, "Euler rotation");
159
+ const x = (rotationDegrees[0] * Math.PI) / 180;
160
+ const y = (rotationDegrees[1] * Math.PI) / 180;
161
+ const z = (rotationDegrees[2] * Math.PI) / 180;
162
+ return composeTransform3(rotationTransform3([0, 0, 1], { radians: z }), composeTransform3(rotationTransform3([0, 1, 0], { radians: y }), rotationTransform3([1, 0, 0], { radians: x })));
163
+ }
164
+ /**
165
+ * Composes two affine transforms without mutating either matrix.
166
+ *
167
+ * `composeTransform3(after, before)` applies `before` first and `after` second.
168
+ *
169
+ * @param after - Transform applied second.
170
+ * @param before - Transform applied first.
171
+ * @returns A new frozen transform equal to `after * before`.
172
+ */
173
+ export function composeTransform3(after, before) {
174
+ validateTransform3(after);
175
+ validateTransform3(before);
176
+ const values = new Array(16).fill(0);
177
+ for (let row = 0; row < 4; row += 1) {
178
+ for (let column = 0; column < 4; column += 1) {
179
+ let value = 0;
180
+ for (let inner = 0; inner < 4; inner += 1) {
181
+ value += after[row * 4 + inner] * before[inner * 4 + column];
182
+ }
183
+ values[row * 4 + column] = value;
184
+ }
185
+ }
186
+ const composed = frozenTransform(values);
187
+ validateTransform3(composed);
188
+ return composed;
189
+ }
190
+ /**
191
+ * Applies an affine transform to a point, including translation.
192
+ *
193
+ * @param transform - Valid row-major affine transform.
194
+ * @param point - Finite XYZ point.
195
+ * @returns A fresh transformed point.
196
+ */
197
+ export function applyTransformPoint3(transform, point) {
198
+ validateTransform3(transform);
199
+ finiteVector(point, "Point");
200
+ return [
201
+ transform[0] * point[0] + transform[1] * point[1] + transform[2] * point[2] + transform[3],
202
+ transform[4] * point[0] + transform[5] * point[1] + transform[6] * point[2] + transform[7],
203
+ transform[8] * point[0] + transform[9] * point[1] + transform[10] * point[2] + transform[11],
204
+ ];
205
+ }
206
+ /**
207
+ * Applies only the linear component of an affine transform to a direction.
208
+ *
209
+ * The result is not normalized, preserving scale information.
210
+ *
211
+ * @param transform - Valid row-major affine transform.
212
+ * @param direction - Finite XYZ direction.
213
+ * @returns A fresh transformed direction.
214
+ */
215
+ export function applyTransformDirection3(transform, direction) {
216
+ validateTransform3(transform);
217
+ finiteVector(direction, "Direction");
218
+ return [
219
+ transform[0] * direction[0] + transform[1] * direction[1] + transform[2] * direction[2],
220
+ transform[4] * direction[0] + transform[5] * direction[1] + transform[6] * direction[2],
221
+ transform[8] * direction[0] + transform[9] * direction[1] + transform[10] * direction[2],
222
+ ];
223
+ }
224
+ /**
225
+ * Returns the determinant of a transform's linear 3x3 component.
226
+ *
227
+ * A negative result identifies a reflection; zero is rejected as singular.
228
+ *
229
+ * @param transform - Valid affine transform.
230
+ * @returns Signed linear determinant.
231
+ */
232
+ export function transformDeterminant3(transform) {
233
+ validateTransform3(transform);
234
+ return (transform[0] * (transform[5] * transform[10] - transform[6] * transform[9]) -
235
+ transform[1] * (transform[4] * transform[10] - transform[6] * transform[8]) +
236
+ transform[2] * (transform[4] * transform[9] - transform[5] * transform[8]));
237
+ }
238
+ /**
239
+ * Inverts a finite, non-singular affine transform.
240
+ *
241
+ * @param transform - Transform to invert.
242
+ * @returns A new frozen transform whose composition with the input is identity.
243
+ */
244
+ export function inverseTransform3(transform) {
245
+ validateTransform3(transform);
246
+ const a = transform[0];
247
+ const b = transform[1];
248
+ const c = transform[2];
249
+ const d = transform[4];
250
+ const e = transform[5];
251
+ const f = transform[6];
252
+ const g = transform[8];
253
+ const h = transform[9];
254
+ const i = transform[10];
255
+ const determinant = transformDeterminant3(transform);
256
+ const inverseLinear = [
257
+ (e * i - f * h) / determinant,
258
+ (c * h - b * i) / determinant,
259
+ (b * f - c * e) / determinant,
260
+ (f * g - d * i) / determinant,
261
+ (a * i - c * g) / determinant,
262
+ (c * d - a * f) / determinant,
263
+ (d * h - e * g) / determinant,
264
+ (b * g - a * h) / determinant,
265
+ (a * e - b * d) / determinant,
266
+ ];
267
+ const translation = [transform[3], transform[7], transform[11]];
268
+ const inverseTranslation = [
269
+ -dot([inverseLinear[0], inverseLinear[1], inverseLinear[2]], translation),
270
+ -dot([inverseLinear[3], inverseLinear[4], inverseLinear[5]], translation),
271
+ -dot([inverseLinear[6], inverseLinear[7], inverseLinear[8]], translation),
272
+ ];
273
+ const inverse = frozenTransform([
274
+ inverseLinear[0],
275
+ inverseLinear[1],
276
+ inverseLinear[2],
277
+ inverseTranslation[0],
278
+ inverseLinear[3],
279
+ inverseLinear[4],
280
+ inverseLinear[5],
281
+ inverseTranslation[1],
282
+ inverseLinear[6],
283
+ inverseLinear[7],
284
+ inverseLinear[8],
285
+ inverseTranslation[2],
286
+ 0,
287
+ 0,
288
+ 0,
289
+ 1,
290
+ ]);
291
+ validateTransform3(inverse);
292
+ return inverse;
293
+ }
294
+ /**
295
+ * Converts a right-handed orthonormal frame into a local-to-world transform.
296
+ *
297
+ * @param frame - Frame whose axes become the transform columns.
298
+ * @param tolerance - Optional orthonormality comparison overrides.
299
+ * @returns A frozen rigid transform.
300
+ * @throws {@link GeometryError} When axes are non-unit, non-orthogonal, or left-handed.
301
+ */
302
+ export function frameTransform3(frame, tolerance = {}) {
303
+ finiteVector(frame.origin, "Frame origin");
304
+ const resolved = resolveGeometryTolerance(1, tolerance);
305
+ const xLength = length(frame.xAxis);
306
+ const yLength = length(frame.yAxis);
307
+ const zLength = length(frame.zAxis);
308
+ if (Math.abs(xLength - 1) > resolved.angular ||
309
+ Math.abs(yLength - 1) > resolved.angular ||
310
+ Math.abs(zLength - 1) > resolved.angular) {
311
+ throw new GeometryError("INVALID_ARGUMENT", "Frame axes must have unit length.");
312
+ }
313
+ const x = normalized(frame.xAxis, "Frame X axis", resolved.linear);
314
+ const y = normalized(frame.yAxis, "Frame Y axis", resolved.linear);
315
+ const z = normalized(frame.zAxis, "Frame Z axis", resolved.linear);
316
+ if (Math.abs(dot(x, y)) > resolved.angular ||
317
+ Math.abs(dot(x, z)) > resolved.angular ||
318
+ Math.abs(dot(y, z)) > resolved.angular ||
319
+ dot(cross(x, y), z) < 1 - resolved.angular) {
320
+ throw new GeometryError("INVALID_ARGUMENT", "Frame axes must be right-handed and mutually orthogonal.");
321
+ }
322
+ return frozenTransform([
323
+ x[0],
324
+ y[0],
325
+ z[0],
326
+ frame.origin[0],
327
+ x[1],
328
+ y[1],
329
+ z[1],
330
+ frame.origin[1],
331
+ x[2],
332
+ y[2],
333
+ z[2],
334
+ frame.origin[2],
335
+ 0,
336
+ 0,
337
+ 0,
338
+ 1,
339
+ ]);
340
+ }
@@ -0,0 +1 @@
1
+ export {};
package/dist/index.js ADDED
@@ -0,0 +1 @@
1
+ export * from "./model/operations.js";
@@ -0,0 +1,30 @@
1
+ import { GeometryError } from "../geometry/errors.js";
2
+ // One protocol key lets locally bundled operation wrappers resolve the host's execution
3
+ // without depending on package instances, class identity, or initializing a native runtime.
4
+ const executionKey = Symbol.for("@metamesh/modeling/execution/2");
5
+ const bindings = globalThis;
6
+ /** Internal host binding. Executable callbacks are synchronous and may not nest executions. */
7
+ export function bindModelContext(context, build) {
8
+ if (bindings[executionKey] !== undefined) {
9
+ throw new GeometryError("INVALID_ARGUMENT", "Model builds cannot nest.", {
10
+ operation: "build",
11
+ });
12
+ }
13
+ bindings[executionKey] = { version: 2, context };
14
+ try {
15
+ return build();
16
+ }
17
+ finally {
18
+ delete bindings[executionKey];
19
+ }
20
+ }
21
+ /** Resolves only the current live build; imported functions carry no native state themselves. */
22
+ export function currentModelContext(operation) {
23
+ const binding = bindings[executionKey];
24
+ if (binding?.version !== 2) {
25
+ throw new GeometryError("INVALID_ARGUMENT", "Modeling functions can only run inside build().", {
26
+ operation,
27
+ });
28
+ }
29
+ return binding.context;
30
+ }
@@ -0,0 +1,152 @@
1
+ import { currentModelContext } from "./execution-binding.js";
2
+ function operation(name) {
3
+ const invoke = (...args) => {
4
+ const context = currentModelContext(name);
5
+ const method = context[name];
6
+ if (typeof method !== "function")
7
+ throw new Error(`Unknown modeling operation "${name}".`);
8
+ return Reflect.apply(method, undefined, args);
9
+ };
10
+ return invoke;
11
+ }
12
+ /** Matches an explicit profile family using the current build's physical resolution. */
13
+ export const matchProfiles =
14
+ /* @__PURE__ */ operation("matchProfiles");
15
+ /** Copies intersections along a finite directed ray segment during the active build. */
16
+ export const rayCast = /* @__PURE__ */ operation("rayCast");
17
+ /** Returns bound local operations, optionally with derived resolution, without changing imported operations. */
18
+ export function modelingFunctions(resolution) {
19
+ const context = currentModelContext("modelingFunctions");
20
+ return resolution === undefined ? context : context.withResolution(resolution);
21
+ }
22
+ /** Host-bound box operation. Valid only during a synchronous build. */
23
+ export const box = /* @__PURE__ */ operation("box");
24
+ /** Constructs a closed regularly sampled heightfield during the active synchronous build. */
25
+ export const heightfield = /* @__PURE__ */ operation("heightfield");
26
+ /** Returns an immutable copy with the explicit native millimetre tolerance. */
27
+ export const setTolerance =
28
+ /* @__PURE__ */ operation("setTolerance");
29
+ /** Host-bound cylinder operation. Valid only during a synchronous build. */
30
+ export const cylinder = /* @__PURE__ */ operation("cylinder");
31
+ /** Host-bound tube operation. Valid only during a synchronous build. */
32
+ export const tube = /* @__PURE__ */ operation("tube");
33
+ /** Host-bound sphere operation. Valid only during a synchronous build. */
34
+ export const sphere = /* @__PURE__ */ operation("sphere");
35
+ /** Host-bound torus operation. Valid only during a synchronous build. */
36
+ export const torus = /* @__PURE__ */ operation("torus");
37
+ /** Host-bound union operation. Valid only during a synchronous build. */
38
+ export const union = /* @__PURE__ */ operation("union");
39
+ /** Host-bound subtract operation. Valid only during a synchronous build. */
40
+ export const subtract = /* @__PURE__ */ operation("subtract");
41
+ /** Host-bound intersect operation. Valid only during a synchronous build. */
42
+ export const intersect = /* @__PURE__ */ operation("intersect");
43
+ /** Host-bound compose operation. Valid only during a synchronous build. */
44
+ export const compose = /* @__PURE__ */ operation("compose");
45
+ /** Host-bound hull operation. Valid only during a synchronous build. */
46
+ export const hull = /* @__PURE__ */ operation("hull");
47
+ /** Host-bound minkowskiSum operation. Valid only during a synchronous build. */
48
+ export const minkowskiSum =
49
+ /* @__PURE__ */ operation("minkowskiSum");
50
+ /** Host-bound solidFromMesh operation. Valid only during a synchronous build. */
51
+ export const solidFromMesh =
52
+ /* @__PURE__ */ operation("solidFromMesh");
53
+ /** Host-bound toTriangleMesh operation. Valid only during a synchronous build. */
54
+ export const toTriangleMesh =
55
+ /* @__PURE__ */ operation("toTriangleMesh");
56
+ /** Host-bound translate operation. Valid only during a synchronous build. */
57
+ export const translate = /* @__PURE__ */ operation("translate");
58
+ /** Host-bound rotate operation. Valid only during a synchronous build. */
59
+ export const rotate = /* @__PURE__ */ operation("rotate");
60
+ /** Host-bound scale operation. Valid only during a synchronous build. */
61
+ export const scale = /* @__PURE__ */ operation("scale");
62
+ /** Host-bound mirror operation. Valid only during a synchronous build. */
63
+ export const mirror = /* @__PURE__ */ operation("mirror");
64
+ /** Host-bound transform operation. Valid only during a synchronous build. */
65
+ export const transform = /* @__PURE__ */ operation("transform");
66
+ /** Host-bound fromRegion operation. Valid only during a synchronous build. */
67
+ export const fromRegion = /* @__PURE__ */ operation("fromRegion");
68
+ /** Host-bound toRegion operation. Valid only during a synchronous build. */
69
+ export const toRegion = /* @__PURE__ */ operation("toRegion");
70
+ /** Host-bound rectangle operation. Valid only during a synchronous build. */
71
+ export const rectangle = /* @__PURE__ */ operation("rectangle");
72
+ /** Host-bound circle operation. Valid only during a synchronous build. */
73
+ export const circle = /* @__PURE__ */ operation("circle");
74
+ /** Host-bound regularPolygon operation. Valid only during a synchronous build. */
75
+ export const regularPolygon =
76
+ /* @__PURE__ */ operation("regularPolygon");
77
+ /** Host-bound star operation. Valid only during a synchronous build. */
78
+ export const star = /* @__PURE__ */ operation("star");
79
+ /** Host-bound extrude operation. Valid only during a synchronous build. */
80
+ export const extrude = /* @__PURE__ */ operation("extrude");
81
+ /** Host-bound revolve operation. Valid only during a synchronous build. */
82
+ export const revolve = /* @__PURE__ */ operation("revolve");
83
+ /** Host-bound projectSection operation. Valid only during a synchronous build. */
84
+ export const projectSection =
85
+ /* @__PURE__ */ operation("projectSection");
86
+ /** Host-bound sliceSection operation. Valid only during a synchronous build. */
87
+ export const sliceSection =
88
+ /* @__PURE__ */ operation("sliceSection");
89
+ /** Host-bound loft operation. Valid only during a synchronous build. */
90
+ export const loft = /* @__PURE__ */ operation("loft");
91
+ /** Host-bound sweep operation. Valid only during a synchronous build. */
92
+ export const sweep = /* @__PURE__ */ operation("sweep");
93
+ /** Host-bound inspectSection operation. Valid only during a synchronous build. */
94
+ export const inspectSection =
95
+ /* @__PURE__ */ operation("inspectSection");
96
+ /** Host-bound inspectSolid operation. Valid only during a synchronous build. */
97
+ export const inspectSolid =
98
+ /* @__PURE__ */ operation("inspectSolid");
99
+ /** Host-bound components operation. Valid only during a synchronous build. */
100
+ export const components = /* @__PURE__ */ operation("components");
101
+ /** Host-bound simplify operation. Valid only during a synchronous build. */
102
+ export const simplify = /* @__PURE__ */ operation("simplify");
103
+ /** Host-bound implicit operation. Valid only during a synchronous build. */
104
+ export const implicit = /* @__PURE__ */ operation("implicit");
105
+ /** Host-bound linearPattern operation. Valid only during a synchronous build. */
106
+ export const linearPattern =
107
+ /* @__PURE__ */ operation("linearPattern");
108
+ /** Host-bound radialPattern operation. Valid only during a synchronous build. */
109
+ export const radialPattern =
110
+ /* @__PURE__ */ operation("radialPattern");
111
+ /** Host-bound gridPattern operation. Valid only during a synchronous build. */
112
+ export const gridPattern = /* @__PURE__ */ operation("gridPattern");
113
+ /** Host-bound pathPattern operation. Valid only during a synchronous build. */
114
+ export const pathPattern = /* @__PURE__ */ operation("pathPattern");
115
+ /** Host-bound threadRidge operation. Valid only during a synchronous build. */
116
+ export const threadRidge = /* @__PURE__ */ operation("threadRidge");
117
+ /** Host-bound threadedRod operation. Valid only during a synchronous build. */
118
+ export const threadedRod = /* @__PURE__ */ operation("threadedRod");
119
+ /** Host-bound internalThreadCutter operation. Valid only during a synchronous build. */
120
+ export const internalThreadCutter =
121
+ /* @__PURE__ */ operation("internalThreadCutter");
122
+ /** Host-bound threadedNut operation. Valid only during a synchronous build. */
123
+ export const threadedNut = /* @__PURE__ */ operation("threadedNut");
124
+ /** Host-bound preflightThreadedRod operation. Valid only during a synchronous build. */
125
+ export const preflightThreadedRod =
126
+ /* @__PURE__ */ operation("preflightThreadedRod");
127
+ /** Host-bound preflightThreadedNut operation. Valid only during a synchronous build. */
128
+ export const preflightThreadedNut =
129
+ /* @__PURE__ */ operation("preflightThreadedNut");
130
+ /** Host-bound textSection operation. Valid only during a synchronous build. */
131
+ export const textSection = /* @__PURE__ */ operation("textSection");
132
+ /** Host-bound svgSection operation. Valid only during a synchronous build. */
133
+ export const svgSection = /* @__PURE__ */ operation("svgSection");
134
+ /** Host-bound svgPathSection operation. Valid only during a synchronous build. */
135
+ export const svgPathSection =
136
+ /* @__PURE__ */ operation("svgPathSection");
137
+ /** Host-bound attach operation. Valid only during a synchronous build. */
138
+ export const attach = /* @__PURE__ */ operation("attach");
139
+ /** Reads one immutable named anchor from a shape in the active build. */
140
+ export const anchor = /* @__PURE__ */ operation("anchor");
141
+ /** Returns a new same-build shape handle with explicit copied named anchors. */
142
+ export const withAnchors = /* @__PURE__ */ operation("withAnchors");
143
+ /** Host-bound sectionFromContours operation. Valid only during a synchronous build. */
144
+ export const sectionFromContours =
145
+ /* @__PURE__ */ operation("sectionFromContours");
146
+ /** Host-bound ellipse operation. Valid only during a synchronous build. */
147
+ export const ellipse = /* @__PURE__ */ operation("ellipse");
148
+ /** Host-bound offset operation. Valid only during a synchronous build. */
149
+ export const offset = /* @__PURE__ */ operation("offset");
150
+ /** Host-bound preflightInternalThreadCutter operation. Valid only during a synchronous build. */
151
+ export const preflightInternalThreadCutter =
152
+ /* @__PURE__ */ operation("preflightInternalThreadCutter");
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};