brepjs 18.63.0 → 18.65.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 (77) hide show
  1. package/dist/2d.cjs +6 -6
  2. package/dist/2d.js +6 -6
  3. package/dist/{blueprint-DaRszQg-.js → blueprint-1wDhBDmB.js} +5 -5
  4. package/dist/{blueprint-XEJiK6ON.cjs → blueprint-smNMCQA1.cjs} +5 -5
  5. package/dist/{blueprintFns-C0ULkqGT.js → blueprintFns-DeCDdsDz.js} +2 -2
  6. package/dist/{blueprintFns-CTJGTMYw.cjs → blueprintFns-DkHfzzv0.cjs} +2 -2
  7. package/dist/{blueprintSketcher-DHWmjOmJ.js → blueprintSketcher-BL85kdgo.js} +3 -3
  8. package/dist/{blueprintSketcher-r0uUFBZZ.cjs → blueprintSketcher-Bpd7nMHt.cjs} +3 -3
  9. package/dist/{boolean2D-BT3RGDXW.cjs → boolean2D-CHu_3RIB.cjs} +4 -4
  10. package/dist/{boolean2D-BfavHsXe.js → boolean2D-CWgORqab.js} +4 -4
  11. package/dist/{booleanFns-B3vIRhjz.cjs → booleanFns-C9olM-r4.cjs} +4 -4
  12. package/dist/{booleanFns-DNG6xxu8.js → booleanFns-ogc1TMXG.js} +4 -4
  13. package/dist/brepjs.cjs +24 -24
  14. package/dist/brepjs.js +24 -24
  15. package/dist/{cameraFns-CQkS1TSY.js → cameraFns-9SLctf9O.js} +2 -2
  16. package/dist/{cameraFns-CqplcvtL.cjs → cameraFns-Za6Rsnmg.cjs} +2 -2
  17. package/dist/core.cjs +1 -1
  18. package/dist/core.js +1 -1
  19. package/dist/{cornerFinder-ByEss8KB.cjs → cornerFinder-CUhZgX6X.cjs} +1 -1
  20. package/dist/{cornerFinder-CE1b1IMu.js → cornerFinder-Cchldpsv.js} +1 -1
  21. package/dist/{curveFns-vZbW7CVb.js → curveFns-DH5NpaKO.js} +1 -1
  22. package/dist/{curveFns-Dx9ha0n_.cjs → curveFns-DR68aqxR.cjs} +1 -1
  23. package/dist/{drawFns-DiJFFH16.js → drawFns-Cq6bAjFC.js} +12 -12
  24. package/dist/{drawFns-ZPhrUGWl.cjs → drawFns-DPE6EU_v.cjs} +12 -12
  25. package/dist/{extrudeFns-BamV4cSU.js → extrudeFns-DZEN4PPg.js} +1 -1
  26. package/dist/{extrudeFns-DsvQYfqd.cjs → extrudeFns-yfUN1m7e.cjs} +1 -1
  27. package/dist/{faceFns-LJ_lU5IE.js → faceFns-Qrz3lIDv.js} +2 -2
  28. package/dist/{faceFns-DGmQI57P.cjs → faceFns-_m0px2ug.cjs} +2 -2
  29. package/dist/{helpers-B6abm0X7.cjs → helpers-BXiYZ2Dl.cjs} +6 -6
  30. package/dist/{helpers-CxZqc5vt.js → helpers-Bk538__I.js} +6 -6
  31. package/dist/{historyFns-a2plj33G.cjs → historyFns-B-R48Dze.cjs} +4 -4
  32. package/dist/{historyFns-DpHCn1yw.js → historyFns-BJKkvynn.js} +4 -4
  33. package/dist/{importFns-CEz6N0mP.js → importFns-Cid57qQ1.js} +2 -2
  34. package/dist/{importFns-iJZHFevB.cjs → importFns-D8EPeKcC.cjs} +2 -2
  35. package/dist/io.cjs +2 -2
  36. package/dist/io.js +2 -2
  37. package/dist/kernel/manifold/curveDesc.d.ts +54 -0
  38. package/dist/kernel/manifold/profileOps.d.ts +2 -1
  39. package/dist/{measureFns-1vEKylTt.js → measureFns-95m36tbx.js} +3 -3
  40. package/dist/{measureFns-CvAOj_Io.cjs → measureFns-CqSFeI4R.cjs} +3 -3
  41. package/dist/measurement.cjs +1 -1
  42. package/dist/measurement.js +1 -1
  43. package/dist/{meshFns-CoPoc7dh.js → meshFns-BCggXbKk.js} +3 -3
  44. package/dist/{meshFns-D2rlqrTt.cjs → meshFns-CR8dRmoo.cjs} +3 -3
  45. package/dist/operations.cjs +2 -2
  46. package/dist/operations.js +2 -2
  47. package/dist/{primitiveFns-Bi22B25-.cjs → primitiveFns-DFsImECE.cjs} +7 -7
  48. package/dist/{primitiveFns-OuEHerl6.js → primitiveFns-Fy3scr-a.js} +7 -7
  49. package/dist/projection.cjs +1 -1
  50. package/dist/projection.js +1 -1
  51. package/dist/query.cjs +2 -2
  52. package/dist/query.js +2 -2
  53. package/dist/{shapeFns-C3wVDhtn.js → shapeFns-Ct1lekCK.js} +2 -2
  54. package/dist/{shapeFns-n4A9y80v.cjs → shapeFns-DNqkQ2yJ.cjs} +2 -2
  55. package/dist/shapeRef.cjs +1 -1
  56. package/dist/shapeRef.js +1 -1
  57. package/dist/{shapeRefFns-eqCfRF0P.cjs → shapeRefFns-BB9iWqZb.cjs} +4 -4
  58. package/dist/{shapeRefFns-CUt56Cul.js → shapeRefFns-CZqzvMgX.js} +4 -4
  59. package/dist/{shapeTypes-9yfC5Z8_.js → shapeTypes-C-32Klnb.js} +369 -31
  60. package/dist/{shapeTypes-C_liO43e.cjs → shapeTypes-DAhE_41P.cjs} +369 -31
  61. package/dist/sketching.cjs +3 -3
  62. package/dist/sketching.js +3 -3
  63. package/dist/{solidBuilders-DSAmLmHd.js → solidBuilders-B-gb9ZsJ.js} +2 -2
  64. package/dist/{solidBuilders-BtfspzpT.cjs → solidBuilders-iapbn6CO.cjs} +2 -2
  65. package/dist/{surfaceBuilders-6BfGn0MN.cjs → surfaceBuilders-B9kzwe0E.cjs} +2 -2
  66. package/dist/{surfaceBuilders-ChWLM9Io.js → surfaceBuilders-Cwecm13F.js} +2 -2
  67. package/dist/text.cjs +2 -2
  68. package/dist/text.js +2 -2
  69. package/dist/{textBlueprints-DTaz774E.cjs → textBlueprints-7KWxP1zT.cjs} +7 -7
  70. package/dist/{textBlueprints-B_RYPM3J.js → textBlueprints-CyIv54Ac.js} +7 -7
  71. package/dist/{textMetrics-CEBrAzpE.cjs → textMetrics-BXoJfZKn.cjs} +1 -1
  72. package/dist/{textMetrics-RRDn50ym.js → textMetrics-D740OLnV.js} +1 -1
  73. package/dist/topology.cjs +7 -7
  74. package/dist/topology.js +7 -7
  75. package/dist/{topologyQueryFns-nbhh_XJF.js → topologyQueryFns-C0yUIRDE.js} +1 -1
  76. package/dist/{topologyQueryFns-B6BTVGLX.cjs → topologyQueryFns-CBVVRWvE.cjs} +1 -1
  77. package/package.json +1 -1
@@ -10062,8 +10062,8 @@ function translationMatrix(x, y, z) {
10062
10062
  m[14] = z;
10063
10063
  return m;
10064
10064
  }
10065
- function rotationMatrix(angleDeg, axis, center) {
10066
- const rad = angleDeg * Math.PI / 180;
10065
+ function rotationMatrix(angleRad, axis, center) {
10066
+ const rad = angleRad;
10067
10067
  const [ax, ay, az] = normalizeAxis(axis);
10068
10068
  const c = Math.cos(rad);
10069
10069
  const s = Math.sin(rad);
@@ -10293,6 +10293,171 @@ function multiplyMatrix(a, b) {
10293
10293
  return out;
10294
10294
  }
10295
10295
  //#endregion
10296
+ //#region src/kernel/manifold/curveDesc.ts
10297
+ var TAU = 2 * Math.PI;
10298
+ var hypot3 = (a) => Math.hypot(a[0], a[1], a[2]);
10299
+ var sub3 = (a, b) => [
10300
+ a[0] - b[0],
10301
+ a[1] - b[1],
10302
+ a[2] - b[2]
10303
+ ];
10304
+ function lineLen(d) {
10305
+ return hypot3(sub3(d.p2, d.p1));
10306
+ }
10307
+ function bezierAt$1(points, t) {
10308
+ const tmp = points.map((p) => [
10309
+ p[0],
10310
+ p[1],
10311
+ p[2]
10312
+ ]);
10313
+ for (let k = 1; k < tmp.length; k++) for (let i = 0; i < tmp.length - k; i++) {
10314
+ const a = tmp[i] ?? [
10315
+ 0,
10316
+ 0,
10317
+ 0
10318
+ ];
10319
+ const b = tmp[i + 1] ?? [
10320
+ 0,
10321
+ 0,
10322
+ 0
10323
+ ];
10324
+ tmp[i] = [
10325
+ a[0] * (1 - t) + b[0] * t,
10326
+ a[1] * (1 - t) + b[1] * t,
10327
+ a[2] * (1 - t) + b[2] * t
10328
+ ];
10329
+ }
10330
+ return tmp[0] ?? [
10331
+ 0,
10332
+ 0,
10333
+ 0
10334
+ ];
10335
+ }
10336
+ function descType(d) {
10337
+ if (d.k === "line") return "LINE";
10338
+ if (d.k === "bezier") return "BEZIER";
10339
+ if (d.k === "helix") return "BSPLINE";
10340
+ return Math.abs(d.rx - d.ry) < 1e-9 * Math.max(1, d.rx) ? "CIRCLE" : "ELLIPSE";
10341
+ }
10342
+ /** Parameter bounds: line → [0, length]; conic → [a0, a1]; helix → [0, 2π·turns]; bezier → [0, 1]. */
10343
+ function descBounds(d) {
10344
+ if (d.k === "line") return {
10345
+ first: 0,
10346
+ last: lineLen(d)
10347
+ };
10348
+ if (d.k === "conic") return {
10349
+ first: d.a0,
10350
+ last: d.a1
10351
+ };
10352
+ if (d.k === "helix") return {
10353
+ first: 0,
10354
+ last: TAU * d.turns
10355
+ };
10356
+ return {
10357
+ first: 0,
10358
+ last: 1
10359
+ };
10360
+ }
10361
+ function descPointAt(d, param) {
10362
+ if (d.k === "line") {
10363
+ const t = param / (lineLen(d) || 1);
10364
+ return [
10365
+ d.p1[0] + (d.p2[0] - d.p1[0]) * t,
10366
+ d.p1[1] + (d.p2[1] - d.p1[1]) * t,
10367
+ d.p1[2] + (d.p2[2] - d.p1[2]) * t
10368
+ ];
10369
+ }
10370
+ if (d.k === "bezier") return bezierAt$1(d.points, param);
10371
+ if (d.k === "helix") {
10372
+ const ct = Math.cos(param);
10373
+ const st = Math.sin(param);
10374
+ const z = d.pitch * param / TAU;
10375
+ return [
10376
+ d.center[0] + d.radius * (ct * d.x[0] + st * d.y[0]) + z * d.axis[0],
10377
+ d.center[1] + d.radius * (ct * d.x[1] + st * d.y[1]) + z * d.axis[1],
10378
+ d.center[2] + d.radius * (ct * d.x[2] + st * d.y[2]) + z * d.axis[2]
10379
+ ];
10380
+ }
10381
+ const ct = Math.cos(param);
10382
+ const st = Math.sin(param);
10383
+ return [
10384
+ d.center[0] + d.rx * ct * d.x[0] + d.ry * st * d.y[0],
10385
+ d.center[1] + d.rx * ct * d.x[1] + d.ry * st * d.y[1],
10386
+ d.center[2] + d.rx * ct * d.x[2] + d.ry * st * d.y[2]
10387
+ ];
10388
+ }
10389
+ function descTangent(d, param) {
10390
+ let t;
10391
+ if (d.k === "line") t = sub3(d.p2, d.p1);
10392
+ else if (d.k === "bezier") {
10393
+ const a = bezierAt$1(d.points, Math.max(0, param - 1e-4));
10394
+ t = sub3(bezierAt$1(d.points, Math.min(1, param + 1e-4)), a);
10395
+ } else if (d.k === "helix") {
10396
+ const ct = Math.cos(param);
10397
+ const st = Math.sin(param);
10398
+ const k = d.pitch / TAU;
10399
+ t = [
10400
+ d.radius * (-st * d.x[0] + ct * d.y[0]) + k * d.axis[0],
10401
+ d.radius * (-st * d.x[1] + ct * d.y[1]) + k * d.axis[1],
10402
+ d.radius * (-st * d.x[2] + ct * d.y[2]) + k * d.axis[2]
10403
+ ];
10404
+ } else {
10405
+ const ct = Math.cos(param);
10406
+ const st = Math.sin(param);
10407
+ t = [
10408
+ -d.rx * st * d.x[0] + d.ry * ct * d.y[0],
10409
+ -d.rx * st * d.x[1] + d.ry * ct * d.y[1],
10410
+ -d.rx * st * d.x[2] + d.ry * ct * d.y[2]
10411
+ ];
10412
+ }
10413
+ const l = hypot3(t) || 1;
10414
+ return [
10415
+ t[0] / l,
10416
+ t[1] / l,
10417
+ t[2] / l
10418
+ ];
10419
+ }
10420
+ /** Exact length where closed-form exists; Ramanujan for full ellipses; numeric otherwise. */
10421
+ function descLength(d) {
10422
+ if (d.k === "line") return lineLen(d);
10423
+ if (d.k === "helix") {
10424
+ const c = TAU * d.radius;
10425
+ return d.turns * Math.sqrt(c * c + d.pitch * d.pitch);
10426
+ }
10427
+ if (d.k === "conic") {
10428
+ const span = Math.abs(d.a1 - d.a0);
10429
+ if (Math.abs(d.rx - d.ry) < 1e-9 * Math.max(1, d.rx)) return d.rx * span;
10430
+ if (Math.abs(span - TAU) < 1e-9) {
10431
+ const a = d.rx;
10432
+ const b = d.ry;
10433
+ const h = (a - b) * (a - b) / ((a + b) * (a + b));
10434
+ return Math.PI * (a + b) * (1 + 3 * h / (10 + Math.sqrt(4 - 3 * h)));
10435
+ }
10436
+ return numericLength((t) => descPointAt(d, t), d.a0, d.a1, 256);
10437
+ }
10438
+ return numericLength((t) => descPointAt(d, t), 0, 1, 128);
10439
+ }
10440
+ function descIsClosed(d) {
10441
+ if (d.k === "conic") return Math.abs(Math.abs(d.a1 - d.a0) - TAU) < 1e-9;
10442
+ return false;
10443
+ }
10444
+ function descIsPeriodic(d) {
10445
+ return descIsClosed(d);
10446
+ }
10447
+ function descPeriod(d) {
10448
+ return d.k === "conic" ? TAU : 0;
10449
+ }
10450
+ function numericLength(at, t0, t1, n) {
10451
+ let len = 0;
10452
+ let prev = at(t0);
10453
+ for (let i = 1; i <= n; i++) {
10454
+ const p = at(t0 + (t1 - t0) * i / n);
10455
+ len += hypot3(sub3(p, prev));
10456
+ prev = p;
10457
+ }
10458
+ return len;
10459
+ }
10460
+ //#endregion
10296
10461
  //#region src/kernel/manifold/approximations.ts
10297
10462
  function asShape$2(shape) {
10298
10463
  return shape;
@@ -10694,10 +10859,22 @@ function rotationMinimizingFrames(path, seed) {
10694
10859
  }
10695
10860
  //#endregion
10696
10861
  //#region src/kernel/manifold/profileOps.ts
10697
- /** Segments used to approximate a full circle; arcs scale by angle span. */
10862
+ /** Fallback full-circle segment count when the kernel exposes no quality fn. */
10698
10863
  var FULL_CIRCLE_SEGMENTS = 24;
10699
10864
  /** Bezier sampling segments per edge. */
10700
10865
  var BEZIER_SEGMENTS = 24;
10866
+ /**
10867
+ * Full-circle segment count, following the Manifold global quality setting
10868
+ * (`getCircularSegments`) when available so profile-curve fidelity scales with
10869
+ * the kernel's tessellation quality — fine for accuracy-sensitive callers,
10870
+ * coarse for fast preview — instead of a hardcoded constant. Set by
10871
+ * {@link makeProfileBuilders}.
10872
+ */
10873
+ var circularSegmentsFor = null;
10874
+ function fullCircleSegments(radius) {
10875
+ const n = circularSegmentsFor ? circularSegmentsFor(Math.max(Math.abs(radius), 1e-6)) : FULL_CIRCLE_SEGMENTS;
10876
+ return Math.max(FULL_CIRCLE_SEGMENTS, n);
10877
+ }
10701
10878
  var ZERO3 = [
10702
10879
  0,
10703
10880
  0,
@@ -10711,8 +10888,8 @@ var PLACEHOLDER = {
10711
10888
  function at3(pts, i) {
10712
10889
  return pts[i] ?? ZERO3;
10713
10890
  }
10714
- function arcSegments(angleSpan) {
10715
- return Math.max(2, Math.ceil(Math.abs(angleSpan) / (2 * Math.PI) * FULL_CIRCLE_SEGMENTS));
10891
+ function arcSegments(angleSpan, radius = 1) {
10892
+ return Math.max(2, Math.ceil(Math.abs(angleSpan) / (2 * Math.PI) * fullCircleSegments(radius)));
10716
10893
  }
10717
10894
  function pickPerp(n) {
10718
10895
  return normalize3(cross(n, Math.abs(n[0]) < .9 ? [
@@ -10731,7 +10908,7 @@ function sampleArc(center, normal, radius, startAngle, endAngle, xDir) {
10731
10908
  const x = xDir ? normalize3(xDir) : pickPerp(n);
10732
10909
  const y = normalize3(cross(n, x));
10733
10910
  const span = endAngle - startAngle;
10734
- const segs = arcSegments(span);
10911
+ const segs = arcSegments(span, radius);
10735
10912
  const pts = [];
10736
10913
  for (let i = 0; i <= segs; i++) {
10737
10914
  const a = startAngle + span * i / segs;
@@ -10867,9 +11044,54 @@ function sampleOcctEdge(occt, edge) {
10867
11044
  function discretizeOcctWire(occt, wire) {
10868
11045
  return chainEdges(occt.iterShapes(wire, "edge").map((e) => sampleOcctEdge(occt, e)));
10869
11046
  }
10870
- function makeProfileBuilders(_module) {
10871
- function edge(pts) {
10872
- return wrap(PLACEHOLDER, makeNode("profileEdge", { pts }, []));
11047
+ function makeProfileBuilders(module) {
11048
+ const getSegs = module.getCircularSegments;
11049
+ if (typeof getSegs === "function") circularSegmentsFor = (r) => getSegs(r);
11050
+ function edge(pts, curve) {
11051
+ return wrap(PLACEHOLDER, makeNode("profileEdge", curve ? {
11052
+ pts,
11053
+ curve
11054
+ } : { pts }, []));
11055
+ }
11056
+ /** In-plane orthonormal frame (x, y) for a conic on a plane with the given normal. */
11057
+ function conicFrame(normal, xDir) {
11058
+ const n = normalize3(normal);
11059
+ const x = xDir ? normalize3(xDir) : pickPerp(n);
11060
+ return {
11061
+ x,
11062
+ y: normalize3(cross(n, x))
11063
+ };
11064
+ }
11065
+ /** Analytic conic descriptor for the circle through three points (or null if collinear). */
11066
+ function conicDescFrom3(p1, p2, p3) {
11067
+ const v1 = sub(p2, p1);
11068
+ const v2 = sub(p3, p1);
11069
+ const n = cross(v1, v2);
11070
+ if (length3(n) < 1e-12) return void 0;
11071
+ const nn = normalize3(n);
11072
+ const b = dot(v1, v1);
11073
+ const c = dot(v2, v2);
11074
+ const dd = dot(v1, v2);
11075
+ const denom = 2 * (b * c - dd * dd);
11076
+ if (Math.abs(denom) < 1e-18) return void 0;
11077
+ const s = c * (b - dd) / denom;
11078
+ const t = b * (c - dd) / denom;
11079
+ const center = add(p1, add(scaleVec(v1, s), scaleVec(v2, t)));
11080
+ const radius = length3(sub(p1, center));
11081
+ const x = normalize3(sub(p1, center));
11082
+ const y = normalize3(cross(nn, x));
11083
+ let a1 = Math.atan2(dot(sub(p3, center), y), dot(sub(p3, center), x));
11084
+ if (a1 < 0) a1 += 2 * Math.PI;
11085
+ return {
11086
+ k: "conic",
11087
+ center,
11088
+ x,
11089
+ y,
11090
+ rx: radius,
11091
+ ry: radius,
11092
+ a0: 0,
11093
+ a1
11094
+ };
10873
11095
  }
10874
11096
  function ringOrPts(shape) {
10875
11097
  const ms = asManifoldShape(shape);
@@ -10957,8 +11179,9 @@ function makeProfileBuilders(_module) {
10957
11179
  const x = xDir ? normalize3(xDir) : pickPerp(n);
10958
11180
  const y = normalize3(cross(n, x));
10959
11181
  const pts = [];
10960
- for (let i = 0; i <= FULL_CIRCLE_SEGMENTS; i++) {
10961
- const a = 2 * Math.PI * i / FULL_CIRCLE_SEGMENTS;
11182
+ const segs = fullCircleSegments(Math.max(majorRadius, minorRadius));
11183
+ for (let i = 0; i <= segs; i++) {
11184
+ const a = 2 * Math.PI * i / segs;
10962
11185
  pts.push(add(center, add(scaleVec(x, majorRadius * Math.cos(a)), scaleVec(y, minorRadius * Math.sin(a)))));
10963
11186
  }
10964
11187
  return pts;
@@ -10969,13 +11192,89 @@ function makeProfileBuilders(_module) {
10969
11192
  y,
10970
11193
  z
10971
11194
  ]]),
10972
- makeLineEdge: (p1, p2) => edge([p1, p2]),
10973
- makeCircleEdge: (center, normal, radius) => edge(sampleArc(center, normal, radius, 0, 2 * Math.PI)),
10974
- makeCircleArc: (center, normal, radius, startAngle, endAngle) => edge(sampleArc(center, normal, radius, startAngle, endAngle)),
10975
- makeArcEdge: (p1, p2, p3) => edge(circleFrom3(p1, p2, p3)),
10976
- makeEllipseEdge: (center, normal, majorRadius, minorRadius, xDir) => edge(ellipsePts(center, normal, majorRadius, minorRadius, xDir)),
10977
- makeBezierEdge: (points) => edge(sampleBezier(points)),
10978
- makeTangentArc: (startPoint, _startTangent, endPoint) => edge([startPoint, endPoint]),
11195
+ makeLineEdge: (p1, p2) => edge([p1, p2], {
11196
+ k: "line",
11197
+ p1,
11198
+ p2
11199
+ }),
11200
+ makeCircleEdge: (center, normal, radius) => {
11201
+ const { x, y } = conicFrame(normal);
11202
+ return edge(sampleArc(center, normal, radius, 0, 2 * Math.PI), {
11203
+ k: "conic",
11204
+ center,
11205
+ x,
11206
+ y,
11207
+ rx: radius,
11208
+ ry: radius,
11209
+ a0: 0,
11210
+ a1: 2 * Math.PI
11211
+ });
11212
+ },
11213
+ makeCircleArc: (center, normal, radius, startAngle, endAngle) => {
11214
+ const { x, y } = conicFrame(normal);
11215
+ return edge(sampleArc(center, normal, radius, startAngle, endAngle), {
11216
+ k: "conic",
11217
+ center,
11218
+ x,
11219
+ y,
11220
+ rx: radius,
11221
+ ry: radius,
11222
+ a0: startAngle,
11223
+ a1: endAngle
11224
+ });
11225
+ },
11226
+ makeArcEdge: (p1, p2, p3) => edge(circleFrom3(p1, p2, p3), conicDescFrom3(p1, p2, p3)),
11227
+ makeEllipseEdge: (center, normal, majorRadius, minorRadius, xDir) => {
11228
+ const { x, y } = conicFrame(normal, xDir);
11229
+ return edge(ellipsePts(center, normal, majorRadius, minorRadius, xDir), {
11230
+ k: "conic",
11231
+ center,
11232
+ x,
11233
+ y,
11234
+ rx: majorRadius,
11235
+ ry: minorRadius,
11236
+ a0: 0,
11237
+ a1: 2 * Math.PI
11238
+ });
11239
+ },
11240
+ makeBezierEdge: (points) => edge(sampleBezier(points), {
11241
+ k: "bezier",
11242
+ points: points.map((p) => [...p])
11243
+ }),
11244
+ makeTangentArc: (startPoint, _startTangent, endPoint) => edge([startPoint, endPoint], {
11245
+ k: "line",
11246
+ p1: startPoint,
11247
+ p2: endPoint
11248
+ }),
11249
+ makeHelixWire: (pitch, height, radius, center = [
11250
+ 0,
11251
+ 0,
11252
+ 0
11253
+ ], direction = [
11254
+ 0,
11255
+ 0,
11256
+ 1
11257
+ ], leftHanded = false) => {
11258
+ const axis = normalize3(direction);
11259
+ const x = pickPerp(axis);
11260
+ const y0 = normalize3(cross(axis, x));
11261
+ const y = leftHanded ? scaleVec(y0, -1) : y0;
11262
+ const turns = pitch !== 0 ? height / pitch : 0;
11263
+ const desc = {
11264
+ k: "helix",
11265
+ center,
11266
+ axis,
11267
+ x,
11268
+ y,
11269
+ radius,
11270
+ pitch,
11271
+ turns
11272
+ };
11273
+ const segs = Math.max(8, Math.ceil(turns * FULL_CIRCLE_SEGMENTS));
11274
+ const pts = [];
11275
+ for (let i = 0; i <= segs; i++) pts.push(descPointAt(desc, 2 * Math.PI * turns * i / segs));
11276
+ return edge(pts, desc);
11277
+ },
10979
11278
  makeWire: (edges) => wireFrom(edges),
10980
11279
  makeWireFromMixed: (items) => wireFrom(items),
10981
11280
  makeFace,
@@ -11021,7 +11320,7 @@ function makeBuilderOps(module) {
11021
11320
  makeEllipseArc: () => notImplemented("makeEllipseArc"),
11022
11321
  makeBezierEdge: (points) => profile.makeBezierEdge(points),
11023
11322
  makeTangentArc: (startPoint, startTangent, endPoint) => profile.makeTangentArc(startPoint, startTangent, endPoint),
11024
- makeHelixWire: () => notImplemented("makeHelixWire"),
11323
+ makeHelixWire: (pitch, height, radius, center, direction, leftHanded) => profile.makeHelixWire(pitch, height, radius, center, direction, leftHanded),
11025
11324
  makeWireFromMixed: (items) => profile.makeWireFromMixed(items),
11026
11325
  makeCompound: () => notImplemented("makeCompound"),
11027
11326
  solidFromShell: () => notImplemented("solidFromShell"),
@@ -12613,6 +12912,8 @@ function makeMeasureOps(_module) {
12613
12912
  length: (shape) => {
12614
12913
  const e = shape;
12615
12914
  if (e && e.__nativeEdge && typeof e.length === "number") return e.length;
12915
+ const node = asManifoldShape(shape)?.node;
12916
+ if (node?.op === "profileEdge" && node.params?.curve) return descLength(node.params.curve);
12616
12917
  return notImplemented("length");
12617
12918
  },
12618
12919
  centerOfMass: (shape) => centerOfMass(shape),
@@ -13119,8 +13420,7 @@ function hashCode(shape, upperBound) {
13119
13420
  function isNull(shape) {
13120
13421
  const s = asManifoldShape(shape);
13121
13422
  if (!s) return true;
13122
- const solid = unwrap(s);
13123
- return !solid || typeof solid.isEmpty === "function" && solid.isEmpty();
13423
+ return !unwrap(s);
13124
13424
  }
13125
13425
  function iterShapes(shape, type) {
13126
13426
  const s = asManifoldShape(shape);
@@ -13777,18 +14077,56 @@ function viaOcct(shape, query) {
13777
14077
  }
13778
14078
  return query(occtShape, occt);
13779
14079
  }
14080
+ /** The analytic curve descriptor of a standalone profile edge, if it has one. */
14081
+ function descOf(shape) {
14082
+ const ms = asManifoldShape(shape);
14083
+ if (!ms) return void 0;
14084
+ const node = ms.node;
14085
+ return node.op === "profileEdge" ? node.params?.curve : void 0;
14086
+ }
13780
14087
  function makeGeometryOps(_module) {
13781
14088
  return {
13782
- curveType: (shape) => isNativeEdge(shape) ? shape.curveType : viaOcct(shape, (s, occt) => occt.curveType(s)),
13783
- curveParameters: (shape) => isNativeEdge(shape) ? [0, shape.length] : viaOcct(shape, (s, occt) => occt.curveParameters(s)),
13784
- curvePointAtParam: (shape, param) => isNativeEdge(shape) ? edgePointAt(shape, param) : viaOcct(shape, (s, occt) => occt.curvePointAtParam(s, param)),
13785
- curveTangent: (shape, param) => isNativeEdge(shape) ? {
13786
- point: edgePointAt(shape, param),
13787
- tangent: edgeTangentAt(shape, param)
13788
- } : viaOcct(shape, (s, occt) => occt.curveTangent(s, param)),
13789
- curveIsClosed: (shape) => viaOcct(shape, (s, occt) => occt.curveIsClosed(s)),
13790
- curveIsPeriodic: (shape) => viaOcct(shape, (s, occt) => occt.curveIsPeriodic(s)),
13791
- curvePeriod: (shape) => viaOcct(shape, (s, occt) => occt.curvePeriod(s)),
14089
+ curveType: (shape) => {
14090
+ const d = descOf(shape);
14091
+ if (d) return descType(d);
14092
+ return isNativeEdge(shape) ? shape.curveType : viaOcct(shape, (s, occt) => occt.curveType(s));
14093
+ },
14094
+ curveParameters: (shape) => {
14095
+ const d = descOf(shape);
14096
+ if (d) {
14097
+ const b = descBounds(d);
14098
+ return [b.first, b.last];
14099
+ }
14100
+ return isNativeEdge(shape) ? [0, shape.length] : viaOcct(shape, (s, occt) => occt.curveParameters(s));
14101
+ },
14102
+ curvePointAtParam: (shape, param) => {
14103
+ const d = descOf(shape);
14104
+ if (d) return descPointAt(d, param);
14105
+ return isNativeEdge(shape) ? edgePointAt(shape, param) : viaOcct(shape, (s, occt) => occt.curvePointAtParam(s, param));
14106
+ },
14107
+ curveTangent: (shape, param) => {
14108
+ const d = descOf(shape);
14109
+ if (d) return {
14110
+ point: descPointAt(d, param),
14111
+ tangent: descTangent(d, param)
14112
+ };
14113
+ return isNativeEdge(shape) ? {
14114
+ point: edgePointAt(shape, param),
14115
+ tangent: edgeTangentAt(shape, param)
14116
+ } : viaOcct(shape, (s, occt) => occt.curveTangent(s, param));
14117
+ },
14118
+ curveIsClosed: (shape) => {
14119
+ const d = descOf(shape);
14120
+ return d ? descIsClosed(d) : viaOcct(shape, (s, occt) => occt.curveIsClosed(s));
14121
+ },
14122
+ curveIsPeriodic: (shape) => {
14123
+ const d = descOf(shape);
14124
+ return d ? descIsPeriodic(d) : viaOcct(shape, (s, occt) => occt.curveIsPeriodic(s));
14125
+ },
14126
+ curvePeriod: (shape) => {
14127
+ const d = descOf(shape);
14128
+ return d ? descPeriod(d) : viaOcct(shape, (s, occt) => occt.curvePeriod(s));
14129
+ },
13792
14130
  interpolatePoints: (points, options) => occtOrThrow("interpolatePoints").interpolatePoints(points, options),
13793
14131
  approximatePoints: (points, options) => occtOrThrow("approximatePoints").approximatePoints(points, options),
13794
14132
  curveDegreeElevate: (edge, elevateBy) => viaOcct(edge, (s, occt) => occt.curveDegreeElevate(s, elevateBy)),