fluidcad 0.0.33 → 0.0.34

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 (237) hide show
  1. package/README.md +3 -2
  2. package/bin/commands/init.js +55 -0
  3. package/bin/commands/mcp.js +33 -0
  4. package/bin/commands/serve.js +77 -0
  5. package/bin/fluidcad.js +15 -107
  6. package/lib/dist/common/scene-object.d.ts +2 -1
  7. package/lib/dist/common/scene-object.js +3 -2
  8. package/lib/dist/core/2d/tarc.d.ts +20 -2
  9. package/lib/dist/core/2d/tarc.js +24 -0
  10. package/lib/dist/core/interfaces.d.ts +31 -2
  11. package/lib/dist/core/repeat.js +62 -46
  12. package/lib/dist/features/2d/arc.d.ts +8 -2
  13. package/lib/dist/features/2d/arc.js +94 -17
  14. package/lib/dist/features/2d/back.js +3 -2
  15. package/lib/dist/features/2d/sketch.d.ts +4 -0
  16. package/lib/dist/features/2d/sketch.js +21 -0
  17. package/lib/dist/features/2d/tarc-constrained.d.ts +2 -0
  18. package/lib/dist/features/2d/tarc-constrained.js +8 -0
  19. package/lib/dist/features/2d/tarc-radius-to-object.d.ts +16 -0
  20. package/lib/dist/features/2d/tarc-radius-to-object.js +58 -0
  21. package/lib/dist/features/2d/tarc-to-object.d.ts +18 -0
  22. package/lib/dist/features/2d/tarc-to-object.js +66 -0
  23. package/lib/dist/features/2d/tarc-to-point-tangent.d.ts +2 -0
  24. package/lib/dist/features/2d/tarc-to-point-tangent.js +3 -0
  25. package/lib/dist/features/2d/tarc-to-point.d.ts +2 -0
  26. package/lib/dist/features/2d/tarc-to-point.js +3 -0
  27. package/lib/dist/features/2d/tarc-with-tangent.d.ts +2 -0
  28. package/lib/dist/features/2d/tarc-with-tangent.js +3 -0
  29. package/lib/dist/features/2d/tarc.d.ts +2 -0
  30. package/lib/dist/features/2d/tarc.js +3 -0
  31. package/lib/dist/features/extrude-base.d.ts +9 -0
  32. package/lib/dist/features/extrude-base.js +22 -0
  33. package/lib/dist/features/extrude-to-face.js +1 -5
  34. package/lib/dist/features/extrude-two-distances.js +1 -2
  35. package/lib/dist/features/extrude.js +1 -2
  36. package/lib/dist/features/mirror-feature.d.ts +3 -2
  37. package/lib/dist/features/mirror-feature.js +1 -1
  38. package/lib/dist/features/repeat-circular.d.ts +3 -3
  39. package/lib/dist/features/repeat-circular.js +8 -1
  40. package/lib/dist/features/repeat-linear.d.ts +4 -2
  41. package/lib/dist/features/repeat-linear.js +10 -1
  42. package/lib/dist/features/repeat-matrix.d.ts +3 -1
  43. package/lib/dist/features/repeat-matrix.js +7 -2
  44. package/lib/dist/features/shell.d.ts +4 -1
  45. package/lib/dist/features/shell.js +14 -3
  46. package/lib/dist/helpers/clone-transform.d.ts +2 -1
  47. package/lib/dist/index.d.ts +7 -1
  48. package/lib/dist/index.js +3 -3
  49. package/lib/dist/math/lazy-matrix.d.ts +31 -0
  50. package/lib/dist/math/lazy-matrix.js +66 -0
  51. package/lib/dist/oc/constraints/constraint-solver-adaptor.d.ts +5 -0
  52. package/lib/dist/oc/constraints/constraint-solver-adaptor.js +16 -0
  53. package/lib/dist/oc/constraints/constraint-solver.d.ts +4 -0
  54. package/lib/dist/oc/constraints/curve/curve-constraint-solver.d.ts +4 -0
  55. package/lib/dist/oc/constraints/curve/curve-constraint-solver.js +3 -0
  56. package/lib/dist/oc/constraints/geometric/geometric-constraint-solver.d.ts +6 -1
  57. package/lib/dist/oc/constraints/geometric/geometric-constraint-solver.js +4 -0
  58. package/lib/dist/oc/constraints/geometric/tangent-arc-from-point-tangent.d.ts +8 -0
  59. package/lib/dist/oc/constraints/geometric/tangent-arc-from-point-tangent.js +111 -0
  60. package/lib/dist/oc/constraints/geometric/tangent-arc-radius-to-object.d.ts +8 -0
  61. package/lib/dist/oc/constraints/geometric/tangent-arc-radius-to-object.js +161 -0
  62. package/lib/dist/oc/mesh.d.ts +9 -4
  63. package/lib/dist/oc/mesh.js +14 -13
  64. package/lib/dist/oc/shell-ops.d.ts +2 -1
  65. package/lib/dist/oc/shell-ops.js +5 -2
  66. package/lib/dist/rendering/mesh-builder.d.ts +3 -0
  67. package/lib/dist/rendering/mesh-builder.js +8 -4
  68. package/lib/dist/rendering/render-edge.d.ts +2 -1
  69. package/lib/dist/rendering/render-edge.js +2 -2
  70. package/lib/dist/rendering/render-face.d.ts +2 -1
  71. package/lib/dist/rendering/render-face.js +2 -2
  72. package/lib/dist/rendering/render-solid.d.ts +2 -1
  73. package/lib/dist/rendering/render-solid.js +2 -2
  74. package/lib/dist/rendering/render-wire.d.ts +2 -1
  75. package/lib/dist/rendering/render-wire.js +2 -2
  76. package/lib/dist/rendering/render.d.ts +3 -0
  77. package/lib/dist/rendering/render.js +7 -2
  78. package/lib/dist/scene-manager.d.ts +4 -2
  79. package/lib/dist/scene-manager.js +12 -4
  80. package/lib/dist/tests/features/2d/arc.test.js +64 -0
  81. package/lib/dist/tests/features/2d/back.test.js +17 -1
  82. package/lib/dist/tests/features/2d/tarc.test.js +157 -0
  83. package/lib/dist/tests/features/repeat-user-repro.test.d.ts +1 -0
  84. package/lib/dist/tests/features/repeat-user-repro.test.js +60 -0
  85. package/lib/dist/tests/features/shell.test.js +36 -0
  86. package/lib/dist/tests/global-setup.js +2 -1
  87. package/lib/dist/tests/helpers/extract-blocks.d.ts +9 -0
  88. package/lib/dist/tests/helpers/extract-blocks.js +56 -0
  89. package/lib/dist/tests/llm-docs-examples.test.d.ts +1 -0
  90. package/lib/dist/tests/llm-docs-examples.test.js +62 -0
  91. package/lib/dist/tests/setup.js +2 -1
  92. package/lib/dist/tsconfig.tsbuildinfo +1 -1
  93. package/llm-docs/.coverage-allowlist.txt +9 -0
  94. package/llm-docs/api/arc.md +48 -0
  95. package/llm-docs/api/axis.md +42 -0
  96. package/llm-docs/api/bezier.md +42 -0
  97. package/llm-docs/api/booleans.md +44 -0
  98. package/llm-docs/api/chamfer.md +40 -0
  99. package/llm-docs/api/circle.md +36 -0
  100. package/llm-docs/api/color.md +34 -0
  101. package/llm-docs/api/connect.md +41 -0
  102. package/llm-docs/api/constraint-qualifiers.md +48 -0
  103. package/llm-docs/api/copy.md +63 -0
  104. package/llm-docs/api/cursor-lines.md +50 -0
  105. package/llm-docs/api/cursor-move.md +61 -0
  106. package/llm-docs/api/cut.md +55 -0
  107. package/llm-docs/api/draft.md +36 -0
  108. package/llm-docs/api/edge-filter.md +57 -0
  109. package/llm-docs/api/ellipse.md +34 -0
  110. package/llm-docs/api/extrude.md +74 -0
  111. package/llm-docs/api/face-filter.md +61 -0
  112. package/llm-docs/api/fillet.md +51 -0
  113. package/llm-docs/api/index.json +139 -0
  114. package/llm-docs/api/line.md +42 -0
  115. package/llm-docs/api/load.md +37 -0
  116. package/llm-docs/api/local.md +38 -0
  117. package/llm-docs/api/loft.md +37 -0
  118. package/llm-docs/api/mirror.md +44 -0
  119. package/llm-docs/api/offset.md +36 -0
  120. package/llm-docs/api/part.md +40 -0
  121. package/llm-docs/api/plane.md +44 -0
  122. package/llm-docs/api/polygon.md +37 -0
  123. package/llm-docs/api/primitive-solids.md +39 -0
  124. package/llm-docs/api/project-intersect.md +48 -0
  125. package/llm-docs/api/rect.md +48 -0
  126. package/llm-docs/api/remove.md +32 -0
  127. package/llm-docs/api/repeat.md +79 -0
  128. package/llm-docs/api/revolve.md +38 -0
  129. package/llm-docs/api/rib.md +40 -0
  130. package/llm-docs/api/rotate.md +37 -0
  131. package/llm-docs/api/select.md +41 -0
  132. package/llm-docs/api/shell.md +41 -0
  133. package/llm-docs/api/sketch.md +76 -0
  134. package/llm-docs/api/slot.md +36 -0
  135. package/llm-docs/api/split-trim.md +42 -0
  136. package/llm-docs/api/sweep.md +43 -0
  137. package/llm-docs/api/tarc.md +45 -0
  138. package/llm-docs/api/tcircle.md +38 -0
  139. package/llm-docs/api/tline.md +42 -0
  140. package/llm-docs/api/translate.md +40 -0
  141. package/llm-docs/api/types/aline.md +35 -0
  142. package/llm-docs/api/types/arc-angles.md +29 -0
  143. package/llm-docs/api/types/arc-points.md +48 -0
  144. package/llm-docs/api/types/axis-like.md +38 -0
  145. package/llm-docs/api/types/axis.md +21 -0
  146. package/llm-docs/api/types/boolean-operation.md +50 -0
  147. package/llm-docs/api/types/circular-repeat-options.md +31 -0
  148. package/llm-docs/api/types/common.md +32 -0
  149. package/llm-docs/api/types/cut.md +125 -0
  150. package/llm-docs/api/types/draft.md +21 -0
  151. package/llm-docs/api/types/extrudable-geometry.md +23 -0
  152. package/llm-docs/api/types/extrude.md +194 -0
  153. package/llm-docs/api/types/geometry.md +51 -0
  154. package/llm-docs/api/types/hline.md +35 -0
  155. package/llm-docs/api/types/linear-repeat-options.md +31 -0
  156. package/llm-docs/api/types/loft.md +154 -0
  157. package/llm-docs/api/types/mirror.md +35 -0
  158. package/llm-docs/api/types/offset.md +31 -0
  159. package/llm-docs/api/types/plane-like.md +35 -0
  160. package/llm-docs/api/types/plane-transform-options.md +29 -0
  161. package/llm-docs/api/types/plane.md +21 -0
  162. package/llm-docs/api/types/point-like.md +22 -0
  163. package/llm-docs/api/types/point2dlike.md +26 -0
  164. package/llm-docs/api/types/polygon.md +46 -0
  165. package/llm-docs/api/types/rect.md +128 -0
  166. package/llm-docs/api/types/revolve.md +102 -0
  167. package/llm-docs/api/types/rib.md +133 -0
  168. package/llm-docs/api/types/scene-object.md +33 -0
  169. package/llm-docs/api/types/select.md +21 -0
  170. package/llm-docs/api/types/shell.md +54 -0
  171. package/llm-docs/api/types/slot.md +43 -0
  172. package/llm-docs/api/types/sweep.md +189 -0
  173. package/llm-docs/api/types/tangent-arc-two-objects.md +46 -0
  174. package/llm-docs/api/types/transformable.md +93 -0
  175. package/llm-docs/api/types/trim.md +27 -0
  176. package/llm-docs/api/types/two-objects-tangent-line.md +46 -0
  177. package/llm-docs/api/types/vertex.md +17 -0
  178. package/llm-docs/api/types/vline.md +35 -0
  179. package/llm-docs/concepts/coordinate-system.md +45 -0
  180. package/llm-docs/concepts/history-and-rollback.md +40 -0
  181. package/llm-docs/concepts/last-selection.md +49 -0
  182. package/llm-docs/concepts/scene-graph.md +37 -0
  183. package/llm-docs/index.json +1750 -0
  184. package/mcp/dist/client.d.ts +64 -0
  185. package/mcp/dist/client.js +248 -0
  186. package/mcp/dist/discovery.d.ts +11 -0
  187. package/mcp/dist/discovery.js +78 -0
  188. package/mcp/dist/docs-index.d.ts +81 -0
  189. package/mcp/dist/docs-index.js +261 -0
  190. package/mcp/dist/resources.d.ts +4 -0
  191. package/mcp/dist/resources.js +115 -0
  192. package/mcp/dist/server.d.ts +12 -0
  193. package/mcp/dist/server.js +489 -0
  194. package/mcp/dist/tools/coordination.d.ts +9 -0
  195. package/mcp/dist/tools/coordination.js +46 -0
  196. package/mcp/dist/tools/docs.d.ts +66 -0
  197. package/mcp/dist/tools/docs.js +122 -0
  198. package/mcp/dist/tools/engine.d.ts +56 -0
  199. package/mcp/dist/tools/engine.js +145 -0
  200. package/mcp/dist/tools/inspection.d.ts +75 -0
  201. package/mcp/dist/tools/inspection.js +121 -0
  202. package/mcp/dist/tools/screenshot.d.ts +63 -0
  203. package/mcp/dist/tools/screenshot.js +263 -0
  204. package/mcp/dist/tools/source.d.ts +84 -0
  205. package/mcp/dist/tools/source.js +434 -0
  206. package/mcp/dist/tools/workspaces.d.ts +13 -0
  207. package/mcp/dist/tools/workspaces.js +33 -0
  208. package/mcp/dist/types.d.ts +18 -0
  209. package/mcp/dist/types.js +11 -0
  210. package/package.json +19 -5
  211. package/server/dist/code-editor.d.ts +36 -0
  212. package/server/dist/code-editor.js +8 -0
  213. package/server/dist/fluidcad-server.d.ts +50 -0
  214. package/server/dist/fluidcad-server.js +153 -1
  215. package/server/dist/global-registry.d.ts +30 -0
  216. package/server/dist/global-registry.js +126 -0
  217. package/server/dist/index.js +171 -26
  218. package/server/dist/instance-file.d.ts +31 -0
  219. package/server/dist/instance-file.js +73 -0
  220. package/server/dist/lint-fluid-js.d.ts +15 -0
  221. package/server/dist/lint-fluid-js.js +271 -0
  222. package/server/dist/routes/editor.d.ts +24 -0
  223. package/server/dist/routes/editor.js +44 -0
  224. package/server/dist/routes/export.d.ts +1 -1
  225. package/server/dist/routes/export.js +45 -8
  226. package/server/dist/routes/health.d.ts +7 -0
  227. package/server/dist/routes/health.js +14 -0
  228. package/server/dist/routes/lint.d.ts +10 -0
  229. package/server/dist/routes/lint.js +28 -0
  230. package/server/dist/routes/render.d.ts +33 -0
  231. package/server/dist/routes/render.js +34 -0
  232. package/server/dist/routes/scene.d.ts +5 -0
  233. package/server/dist/routes/scene.js +48 -0
  234. package/server/dist/routes/screenshot.js +68 -1
  235. package/server/dist/ws-protocol.d.ts +56 -2
  236. package/ui/dist/assets/{index-CFi9p7wR.js → index-BdqrMDRu.js} +21 -21
  237. package/ui/dist/index.html +1 -1
@@ -36,9 +36,25 @@ export class ConstraintSolverAdaptor extends ConstraintSolver {
36
36
  console.log('Using geometric solver for tangent circles');
37
37
  return this.geometricSolver.getTangentArcs(plane, shape1, shape2, radius, this.mustTouch);
38
38
  }
39
+ getTangentArcFromPointTangent(plane, startPoint, startTangent, target, flip) {
40
+ if (this.isLine(target.shape)) {
41
+ return this.geometricSolver.getTangentArcFromPointTangent(plane, startPoint, startTangent, target, flip);
42
+ }
43
+ return this.curveSolver.getTangentArcFromPointTangent(plane, startPoint, startTangent, target, flip);
44
+ }
39
45
  isCurve(shape) {
40
46
  return !(shape instanceof Vertex) && this.getShapeGeometry(shape) === 'curve';
41
47
  }
48
+ isLine(shape) {
49
+ if (shape instanceof Vertex) {
50
+ return false;
51
+ }
52
+ const oc = getOC();
53
+ const adaptor = new oc.BRepAdaptor_Curve(shape.getShape());
54
+ const type = adaptor.GetType();
55
+ adaptor.delete();
56
+ return type === oc.GeomAbs_CurveType.GeomAbs_Line;
57
+ }
42
58
  getShapeGeometry(shape) {
43
59
  const oc = getOC();
44
60
  const adaptor = new oc.BRepAdaptor_Curve(shape.getShape());
@@ -15,4 +15,8 @@ export declare abstract class ConstraintSolver implements TangentLineSolver, Tan
15
15
  edges: Edge[];
16
16
  endTangent: Point2D;
17
17
  };
18
+ abstract getTangentArcFromPointTangent(plane: Plane, startPoint: Point2D, startTangent: Point2D, target: QualifiedShape, flip: boolean): {
19
+ edges: Edge[];
20
+ endTangent: Point2D | null;
21
+ };
18
22
  }
@@ -10,4 +10,8 @@ export declare class CurveConstraintSolver extends ConstraintSolver {
10
10
  edges: Edge[];
11
11
  endTangent: Point2D | null;
12
12
  };
13
+ getTangentArcFromPointTangent(_plane: Plane, _startPoint: Point2D, _startTangent: Point2D, _target: QualifiedShape, _flip: boolean): {
14
+ edges: Edge[];
15
+ endTangent: Point2D | null;
16
+ };
13
17
  }
@@ -15,4 +15,7 @@ export class CurveConstraintSolver extends ConstraintSolver {
15
15
  const solver = new CurveTangentCircleSolver();
16
16
  return solver.getTangentArcs(plane, shape1, shape2, radius, mustTouch);
17
17
  }
18
+ getTangentArcFromPointTangent(_plane, _startPoint, _startTangent, _target, _flip) {
19
+ throw new Error('tArc(target): only line targets are supported');
20
+ }
18
21
  }
@@ -1,12 +1,17 @@
1
1
  import { Edge } from "../../../common/edge.js";
2
2
  import { QualifiedShape } from "../../../features/2d/constraints/qualified-geometry.js";
3
3
  import { Plane } from "../../../math/plane.js";
4
+ import { Point2D } from "../../../math/point.js";
4
5
  import { ConstraintSolver } from "../constraint-solver.js";
5
6
  export declare class GeometricConstraintSolver extends ConstraintSolver {
6
7
  getTangentLines(plane: Plane, shape1: QualifiedShape, shape2: QualifiedShape, mustTouch: boolean): Edge[];
7
8
  getTangentCircles(plane: Plane, shape1: QualifiedShape, shape2: QualifiedShape, radius: number, mustTouch: boolean): Edge[];
8
9
  getTangentArcs(plane: Plane, shape1: QualifiedShape, shape2: QualifiedShape, radius: number, mustTouch: boolean): {
9
10
  edges: Edge[];
10
- endTangent: import("../../../math/point.js").Point2D | null;
11
+ endTangent: Point2D | null;
12
+ };
13
+ getTangentArcFromPointTangent(plane: Plane, startPoint: Point2D, startTangent: Point2D, target: QualifiedShape, flip: boolean): {
14
+ edges: Edge[];
15
+ endTangent: Point2D | null;
11
16
  };
12
17
  }
@@ -1,6 +1,7 @@
1
1
  import { ConstraintSolver } from "../constraint-solver.js";
2
2
  import { GeometricTangentCircleSolver } from "./tangent-circle-solver.js";
3
3
  import { GeometricTangentLineSolver } from "./tangent-line-solver.js";
4
+ import { solveTangentArcFromPointTangent } from "./tangent-arc-from-point-tangent.js";
4
5
  export class GeometricConstraintSolver extends ConstraintSolver {
5
6
  getTangentLines(plane, shape1, shape2, mustTouch) {
6
7
  const tangentLineSolver = new GeometricTangentLineSolver();
@@ -14,4 +15,7 @@ export class GeometricConstraintSolver extends ConstraintSolver {
14
15
  const solver = new GeometricTangentCircleSolver();
15
16
  return solver.getTangentArcs(plane, shape1, shape2, radius, mustTouch);
16
17
  }
18
+ getTangentArcFromPointTangent(plane, startPoint, startTangent, target, flip) {
19
+ return solveTangentArcFromPointTangent(plane, startPoint, startTangent, target, flip);
20
+ }
17
21
  }
@@ -0,0 +1,8 @@
1
+ import { Edge } from "../../../common/edge.js";
2
+ import { QualifiedShape } from "../../../features/2d/constraints/qualified-geometry.js";
3
+ import { Plane } from "../../../math/plane.js";
4
+ import { Point2D } from "../../../math/point.js";
5
+ export declare function solveTangentArcFromPointTangent(plane: Plane, startPoint: Point2D, startTangent: Point2D, target: QualifiedShape, flip: boolean): {
6
+ edges: Edge[];
7
+ endTangent: Point2D | null;
8
+ };
@@ -0,0 +1,111 @@
1
+ import { Edge } from "../../../common/edge.js";
2
+ import { Point2D } from "../../../math/point.js";
3
+ import { Geometry } from "../../geometry.js";
4
+ import { getOC } from "../../init.js";
5
+ const EPS = 1e-10;
6
+ export function solveTangentArcFromPointTangent(plane, startPoint, startTangent, target, flip) {
7
+ const lineTarget = extractLineTarget(plane, target);
8
+ const tlen = startTangent.length();
9
+ if (tlen < EPS) {
10
+ throw new Error('tArc(target): start tangent has zero magnitude');
11
+ }
12
+ const Tx = startTangent.x / tlen;
13
+ const Ty = startTangent.y / tlen;
14
+ const Nx = -Ty;
15
+ const Ny = Tx;
16
+ const Qx = lineTarget.pointOnLine.x;
17
+ const Qy = lineTarget.pointOnLine.y;
18
+ const dx = lineTarget.direction.x;
19
+ const dy = lineTarget.direction.y;
20
+ const nx = -dy;
21
+ const ny = dx;
22
+ const dPL = (startPoint.x - Qx) * nx + (startPoint.y - Qy) * ny;
23
+ const k = Nx * nx + Ny * ny;
24
+ // Two candidate signed radii from the line tangency equation:
25
+ // d_PL + r·k = s·r ⇒ r = d_PL / (s − k) for s ∈ {+1, −1}
26
+ // Generically one yields r > 0 (center on the +N̂ side = "primary/left")
27
+ // and the other yields r < 0 ("flipped/right"). Pick by sign so the
28
+ // user-facing default is deterministic regardless of input geometry.
29
+ const candidates = [];
30
+ for (const s of [+1, -1]) {
31
+ const denom = s - k;
32
+ if (Math.abs(denom) < EPS) {
33
+ continue;
34
+ }
35
+ const r = dPL / denom;
36
+ if (Math.abs(r) < EPS) {
37
+ continue;
38
+ }
39
+ candidates.push(buildCandidate(startPoint, Nx, Ny, r, lineTarget));
40
+ }
41
+ if (candidates.length === 0) {
42
+ return { edges: [], endTangent: null };
43
+ }
44
+ const primary = candidates.find(c => c.ccw) ?? null;
45
+ const flipped = candidates.find(c => !c.ccw) ?? null;
46
+ let chosen;
47
+ if (flip) {
48
+ chosen = flipped ?? primary;
49
+ }
50
+ else {
51
+ chosen = primary ?? flipped;
52
+ }
53
+ const edge = buildArcEdge(plane, chosen);
54
+ return { edges: [edge], endTangent: chosen.endTangent };
55
+ }
56
+ function extractLineTarget(plane, target) {
57
+ const oc = getOC();
58
+ const shape = target.shape;
59
+ if (!(shape instanceof Edge)) {
60
+ throw new Error('tArc(target): target must be a line edge');
61
+ }
62
+ const adaptor = new oc.BRepAdaptor_Curve(shape.getShape());
63
+ const type = adaptor.GetType();
64
+ if (type !== oc.GeomAbs_CurveType.GeomAbs_Line) {
65
+ adaptor.delete();
66
+ throw new Error('tArc(target): only line targets are supported');
67
+ }
68
+ adaptor.delete();
69
+ const start = plane.worldToLocal(shape.getFirstVertex().toPoint());
70
+ const end = plane.worldToLocal(shape.getLastVertex().toPoint());
71
+ const diff = end.subtract(start);
72
+ const len = diff.length();
73
+ if (len < EPS) {
74
+ throw new Error('tArc(target): target line has zero length');
75
+ }
76
+ return {
77
+ pointOnLine: start,
78
+ direction: diff.multiplyScalar(1 / len)
79
+ };
80
+ }
81
+ function buildCandidate(P, Nx, Ny, r, target) {
82
+ const Ox = P.x + r * Nx;
83
+ const Oy = P.y + r * Ny;
84
+ const dx = target.direction.x;
85
+ const dy = target.direction.y;
86
+ const t = (Ox - target.pointOnLine.x) * dx + (Oy - target.pointOnLine.y) * dy;
87
+ const Qx = target.pointOnLine.x + t * dx;
88
+ const Qy = target.pointOnLine.y + t * dy;
89
+ const radius = Math.abs(r);
90
+ const ccw = r > 0;
91
+ const endAngle = Math.atan2(Qy - Oy, Qx - Ox);
92
+ const sign = ccw ? 1 : -1;
93
+ const endTangent = new Point2D(sign * (-Math.sin(endAngle)), sign * Math.cos(endAngle));
94
+ return {
95
+ center: new Point2D(Ox, Oy),
96
+ radius,
97
+ ccw,
98
+ startPoint: P,
99
+ endPoint: new Point2D(Qx, Qy),
100
+ endAngle,
101
+ endTangent
102
+ };
103
+ }
104
+ function buildArcEdge(plane, c) {
105
+ const normal = c.ccw ? plane.normal : plane.normal.negate();
106
+ const center = plane.localToWorld(c.center);
107
+ const start = plane.localToWorld(c.startPoint);
108
+ const end = plane.localToWorld(c.endPoint);
109
+ const arc = Geometry.makeArc(center, c.radius, normal, start, end);
110
+ return Geometry.makeEdgeFromCurve(arc);
111
+ }
@@ -0,0 +1,8 @@
1
+ import { Edge } from "../../../common/edge.js";
2
+ import { QualifiedShape } from "../../../features/2d/constraints/qualified-geometry.js";
3
+ import { Plane } from "../../../math/plane.js";
4
+ import { Point2D } from "../../../math/point.js";
5
+ export declare function solveTangentArcRadiusToObject(plane: Plane, startPoint: Point2D, startTangent: Point2D, signedRadius: number, target: QualifiedShape): {
6
+ edges: Edge[];
7
+ endTangent: Point2D | null;
8
+ };
@@ -0,0 +1,161 @@
1
+ import { Edge } from "../../../common/edge.js";
2
+ import { Point2D } from "../../../math/point.js";
3
+ import { Convert } from "../../convert.js";
4
+ import { Geometry } from "../../geometry.js";
5
+ import { getOC } from "../../init.js";
6
+ const EPS = 1e-10;
7
+ export function solveTangentArcRadiusToObject(plane, startPoint, startTangent, signedRadius, target) {
8
+ if (Math.abs(signedRadius) < EPS) {
9
+ throw new Error('tArc(radius, target): radius must be non-zero');
10
+ }
11
+ const tlen = startTangent.length();
12
+ if (tlen < EPS) {
13
+ throw new Error('tArc(radius, target): start tangent has zero magnitude');
14
+ }
15
+ const Tx = startTangent.x / tlen;
16
+ const Ty = startTangent.y / tlen;
17
+ // N̂: 90° CCW from T̂. Signed radius > 0 puts center on +N̂ (left) → CCW arc.
18
+ const Nx = -Ty;
19
+ const Ny = Tx;
20
+ const Ox = startPoint.x + signedRadius * Nx;
21
+ const Oy = startPoint.y + signedRadius * Ny;
22
+ const center = new Point2D(Ox, Oy);
23
+ const radius = Math.abs(signedRadius);
24
+ const ccw = signedRadius > 0;
25
+ const targetGeom = extractTargetGeometry(plane, target);
26
+ const intersections = findIntersections(center, radius, targetGeom);
27
+ if (intersections.length === 0) {
28
+ throw new Error('tArc(radius, target): arc circle does not intersect target');
29
+ }
30
+ const startAngle = Math.atan2(startPoint.y - Oy, startPoint.x - Ox);
31
+ let best = null;
32
+ for (const Q of intersections) {
33
+ const endAngle = Math.atan2(Q.y - Oy, Q.x - Ox);
34
+ let sweep = endAngle - startAngle;
35
+ if (ccw) {
36
+ if (sweep < 0) {
37
+ sweep += 2 * Math.PI;
38
+ }
39
+ }
40
+ else {
41
+ if (sweep > 0) {
42
+ sweep -= 2 * Math.PI;
43
+ }
44
+ }
45
+ // Skip degenerate "no-sweep" intersections (start point itself on target).
46
+ if (Math.abs(sweep) < EPS) {
47
+ continue;
48
+ }
49
+ if (!best || Math.abs(sweep) < Math.abs(best.sweep)) {
50
+ best = { endPoint: Q, sweep, endAngle };
51
+ }
52
+ }
53
+ if (!best) {
54
+ throw new Error('tArc(radius, target): no intersection reachable in the arc direction');
55
+ }
56
+ const normal = ccw ? plane.normal : plane.normal.negate();
57
+ const worldCenter = plane.localToWorld(center);
58
+ const worldStart = plane.localToWorld(startPoint);
59
+ const worldEnd = plane.localToWorld(best.endPoint);
60
+ const arc = Geometry.makeArc(worldCenter, radius, normal, worldStart, worldEnd);
61
+ const edge = Geometry.makeEdgeFromCurve(arc);
62
+ const sign = ccw ? 1 : -1;
63
+ const endTangent = new Point2D(sign * (-Math.sin(best.endAngle)), sign * Math.cos(best.endAngle));
64
+ return { edges: [edge], endTangent };
65
+ }
66
+ function extractTargetGeometry(plane, target) {
67
+ const oc = getOC();
68
+ const shape = target.shape;
69
+ if (!(shape instanceof Edge)) {
70
+ throw new Error('tArc(radius, target): target must be a line, circle, or arc edge');
71
+ }
72
+ const adaptor = new oc.BRepAdaptor_Curve(shape.getShape());
73
+ const type = adaptor.GetType();
74
+ if (type === oc.GeomAbs_CurveType.GeomAbs_Line) {
75
+ adaptor.delete();
76
+ const start = plane.worldToLocal(shape.getFirstVertex().toPoint());
77
+ const end = plane.worldToLocal(shape.getLastVertex().toPoint());
78
+ const diff = end.subtract(start);
79
+ const len = diff.length();
80
+ if (len < EPS) {
81
+ throw new Error('tArc(radius, target): target line has zero length');
82
+ }
83
+ return {
84
+ type: 'line',
85
+ pointOnLine: start,
86
+ direction: diff.multiplyScalar(1 / len)
87
+ };
88
+ }
89
+ if (type === oc.GeomAbs_CurveType.GeomAbs_Circle) {
90
+ const circle = adaptor.Circle();
91
+ const r = circle.Radius();
92
+ const cWorld = Convert.toPoint(circle.Location());
93
+ adaptor.delete();
94
+ return {
95
+ type: 'circle',
96
+ center: plane.worldToLocal(cWorld),
97
+ radius: r
98
+ };
99
+ }
100
+ adaptor.delete();
101
+ throw new Error('tArc(radius, target): target must be a line, circle, or arc');
102
+ }
103
+ function findIntersections(center, radius, target) {
104
+ if (target.type === 'line') {
105
+ return lineCircleIntersections(center, radius, target.pointOnLine, target.direction);
106
+ }
107
+ return circleCircleIntersections(center, radius, target.center, target.radius);
108
+ }
109
+ function lineCircleIntersections(O, radius, Q_L, d_L) {
110
+ // Line unit normal (rotate direction +90° CCW).
111
+ const nx = -d_L.y;
112
+ const ny = d_L.x;
113
+ const d_OL = (O.x - Q_L.x) * nx + (O.y - Q_L.y) * ny;
114
+ const absD = Math.abs(d_OL);
115
+ if (absD > radius + EPS) {
116
+ return [];
117
+ }
118
+ // Foot of perpendicular from O onto the line.
119
+ const Fx = O.x - d_OL * nx;
120
+ const Fy = O.y - d_OL * ny;
121
+ const inside = Math.max(radius * radius - d_OL * d_OL, 0);
122
+ const h = Math.sqrt(inside);
123
+ if (h < EPS) {
124
+ return [new Point2D(Fx, Fy)];
125
+ }
126
+ return [
127
+ new Point2D(Fx + h * d_L.x, Fy + h * d_L.y),
128
+ new Point2D(Fx - h * d_L.x, Fy - h * d_L.y)
129
+ ];
130
+ }
131
+ function circleCircleIntersections(O, r1, Cc, r2) {
132
+ const dx = Cc.x - O.x;
133
+ const dy = Cc.y - O.y;
134
+ const d2 = dx * dx + dy * dy;
135
+ const d = Math.sqrt(d2);
136
+ if (d < EPS) {
137
+ return [];
138
+ }
139
+ if (d > r1 + r2 + EPS) {
140
+ return [];
141
+ }
142
+ if (d < Math.abs(r1 - r2) - EPS) {
143
+ return [];
144
+ }
145
+ const a = (d2 + r1 * r1 - r2 * r2) / (2 * d);
146
+ const inside = Math.max(r1 * r1 - a * a, 0);
147
+ const h = Math.sqrt(inside);
148
+ // Midpoint M = O + (a/d) * (Cc - O).
149
+ const Mx = O.x + (a / d) * dx;
150
+ const My = O.y + (a / d) * dy;
151
+ if (h < EPS) {
152
+ return [new Point2D(Mx, My)];
153
+ }
154
+ // Perpendicular (unit) to (Cc - O).
155
+ const px = -dy / d;
156
+ const py = dx / d;
157
+ return [
158
+ new Point2D(Mx + h * px, My + h * py),
159
+ new Point2D(Mx - h * px, My - h * py)
160
+ ];
161
+ }
@@ -7,6 +7,11 @@ export interface MeshData {
7
7
  indices: number[];
8
8
  count?: number;
9
9
  }
10
+ export interface MeshConfig {
11
+ linDefl: number;
12
+ angDefl: number;
13
+ }
14
+ export declare const DEFAULT_MESH_CONFIG: MeshConfig;
10
15
  export interface EnsureTriangulatedOptions {
11
16
  linDefl?: number;
12
17
  angDefl?: number;
@@ -15,15 +20,15 @@ export interface EnsureTriangulatedOptions {
15
20
  checkFreeEdges?: boolean;
16
21
  }
17
22
  export declare class Mesh {
18
- static triangulateFace(face: Face, vertexOffset?: number): MeshData | null;
19
- static discretizeEdge(edge: Shape): MeshData;
23
+ static triangulateFace(face: Face, vertexOffset?: number, opts?: EnsureTriangulatedOptions): MeshData | null;
24
+ static discretizeEdge(edge: Shape, opts?: EnsureTriangulatedOptions): MeshData;
20
25
  /**
21
26
  * Triangulates `shape` only if it doesn't already have an up-to-date
22
27
  * triangulation at the requested deflection. Returns true when a fresh
23
28
  * mesh was built, false when the stored one was reused.
24
29
  */
25
30
  static ensureTriangulated(shape: TopoDS_Shape, opts?: EnsureTriangulatedOptions): boolean;
26
- static triangulateFaceRaw(face: TopoDS_Face, vertexOffset?: number): MeshData | null;
31
+ static triangulateFaceRaw(face: TopoDS_Face, vertexOffset?: number, opts?: EnsureTriangulatedOptions): MeshData | null;
27
32
  static extractFaceTriangulationRaw(face: TopoDS_Face, vertexOffset?: number): MeshData | null;
28
33
  /**
29
34
  * Reads the polyline stored for `edge` as a polygon-on-triangulation of
@@ -36,5 +41,5 @@ export declare class Mesh {
36
41
  * meshed face). Caller must have already run `ensureTriangulated` on the
37
42
  * edge or its parent wire.
38
43
  */
39
- static discretizeEdgeRaw(edge: TopoDS_Shape): MeshData;
44
+ static discretizeEdgeRaw(edge: TopoDS_Shape, opts?: EnsureTriangulatedOptions): MeshData;
40
45
  }
@@ -1,15 +1,16 @@
1
1
  import { getOC } from "./init.js";
2
2
  import { Explorer } from "./explorer.js";
3
- // Flip to false to benchmark single-threaded meshing.
4
- const DEFAULT_LIN_DEFLECTION = 0.1;
5
- const DEFAULT_ANG_DEFLECTION = 0.5;
3
+ export const DEFAULT_MESH_CONFIG = {
4
+ linDefl: 0.1,
5
+ angDefl: 0.5,
6
+ };
6
7
  export class Mesh {
7
8
  // Wrapper methods (public API for external callers)
8
- static triangulateFace(face, vertexOffset = 0) {
9
- return Mesh.triangulateFaceRaw(face.getShape(), vertexOffset);
9
+ static triangulateFace(face, vertexOffset = 0, opts) {
10
+ return Mesh.triangulateFaceRaw(face.getShape(), vertexOffset, opts);
10
11
  }
11
- static discretizeEdge(edge) {
12
- return Mesh.discretizeEdgeRaw(edge.getShape());
12
+ static discretizeEdge(edge, opts) {
13
+ return Mesh.discretizeEdgeRaw(edge.getShape(), opts);
13
14
  }
14
15
  /**
15
16
  * Triangulates `shape` only if it doesn't already have an up-to-date
@@ -18,8 +19,8 @@ export class Mesh {
18
19
  */
19
20
  static ensureTriangulated(shape, opts = {}) {
20
21
  const oc = getOC();
21
- const linDefl = opts.linDefl ?? DEFAULT_LIN_DEFLECTION;
22
- const angDefl = opts.angDefl ?? DEFAULT_ANG_DEFLECTION;
22
+ const linDefl = opts.linDefl ?? DEFAULT_MESH_CONFIG.linDefl;
23
+ const angDefl = opts.angDefl ?? DEFAULT_MESH_CONFIG.angDefl;
23
24
  const relative = opts.relative ?? false;
24
25
  const checkFreeEdges = opts.checkFreeEdges ?? true;
25
26
  if (oc.BRepTools.Triangulation(shape, linDefl, checkFreeEdges)) {
@@ -31,9 +32,9 @@ export class Mesh {
31
32
  return true;
32
33
  }
33
34
  // Raw methods (for oc-internal use)
34
- static triangulateFaceRaw(face, vertexOffset = 0) {
35
+ static triangulateFaceRaw(face, vertexOffset = 0, opts) {
35
36
  try {
36
- Mesh.ensureTriangulated(face);
37
+ Mesh.ensureTriangulated(face, opts);
37
38
  }
38
39
  catch (e) {
39
40
  console.error("Face mesh failed", e);
@@ -151,14 +152,14 @@ export class Mesh {
151
152
  * meshed face). Caller must have already run `ensureTriangulated` on the
152
153
  * edge or its parent wire.
153
154
  */
154
- static discretizeEdgeRaw(edge) {
155
+ static discretizeEdgeRaw(edge, opts) {
155
156
  const oc = getOC();
156
157
  const ocEdge = oc.TopoDS.Edge(edge);
157
158
  if (oc.BRep_Tool.Degenerated(ocEdge)) {
158
159
  ocEdge.delete();
159
160
  return { vertices: [], normals: [], indices: [] };
160
161
  }
161
- Mesh.ensureTriangulated(edge);
162
+ Mesh.ensureTriangulated(edge, opts);
162
163
  const loc = new oc.TopLoc_Location();
163
164
  const polyHandle = oc.BRep_Tool.Polygon3D(ocEdge, loc);
164
165
  if (polyHandle.IsNull()) {
@@ -1,5 +1,6 @@
1
1
  import { Shape } from "../common/shape.js";
2
2
  import { Face } from "../common/face.js";
3
+ import { ShellJoinType } from "../core/interfaces.js";
3
4
  export declare class ShellOps {
4
- static makeThickSolid(solid: Shape, faces: Face[], thickness: number): Shape;
5
+ static makeThickSolid(solid: Shape, faces: Face[], thickness: number, joinType?: ShellJoinType): Shape;
5
6
  }
@@ -3,15 +3,18 @@ import { ShapeOps } from "./shape-ops.js";
3
3
  import { ShapeFactory } from "../common/shape-factory.js";
4
4
  import { ColorTransfer } from "./color-transfer.js";
5
5
  export class ShellOps {
6
- static makeThickSolid(solid, faces, thickness) {
6
+ static makeThickSolid(solid, faces, thickness, joinType = 'arc') {
7
7
  const oc = getOC();
8
8
  const listOfFaces = new oc.TopTools_ListOfShape();
9
9
  for (const f of faces) {
10
10
  listOfFaces.Append(f.getShape());
11
11
  }
12
+ const ocJoinType = joinType === 'intersection' ? oc.GeomAbs_JoinType.GeomAbs_Intersection
13
+ : joinType === 'tangent' ? oc.GeomAbs_JoinType.GeomAbs_Tangent
14
+ : oc.GeomAbs_JoinType.GeomAbs_Arc;
12
15
  const maker = new oc.BRepOffsetAPI_MakeThickSolid();
13
16
  const progress = new oc.Message_ProgressRange();
14
- maker.MakeThickSolidByJoin(oc.TopoDS.Solid(solid.getShape()), listOfFaces, thickness, oc.Precision.Confusion(), oc.BRepOffset_Mode.BRepOffset_Skin, false, false, oc.GeomAbs_JoinType.GeomAbs_Arc, false, progress);
17
+ maker.MakeThickSolidByJoin(oc.TopoDS.Solid(solid.getShape()), listOfFaces, thickness, oc.Precision.Confusion(), oc.BRepOffset_Mode.BRepOffset_Skin, false, false, ocJoinType, false, progress);
15
18
  progress.delete();
16
19
  if (!maker.IsDone()) {
17
20
  maker.delete();
@@ -1,5 +1,8 @@
1
1
  import { Shape } from "../common/shape.js";
2
2
  import { SceneObjectMesh } from "./scene.js";
3
+ import type { MeshConfig } from "../oc/mesh.js";
3
4
  export declare class MeshBuilder {
5
+ private readonly meshConfig;
6
+ constructor(meshConfig: MeshConfig);
4
7
  build(shapeObj: Shape): SceneObjectMesh[];
5
8
  }
@@ -4,20 +4,24 @@ import { renderFace } from "./render-face.js";
4
4
  import { renderWire } from "./render-wire.js";
5
5
  import { renderEdge } from "./render-edge.js";
6
6
  export class MeshBuilder {
7
+ meshConfig;
8
+ constructor(meshConfig) {
9
+ this.meshConfig = meshConfig;
10
+ }
7
11
  build(shapeObj) {
8
12
  const shape = shapeObj.getShape();
9
13
  let result = null;
10
14
  if (Explorer.isSolid(shape)) {
11
- result = renderSolid(shapeObj);
15
+ result = renderSolid(shapeObj, this.meshConfig);
12
16
  }
13
17
  else if (Explorer.isFace(shape)) {
14
- result = renderFace(shapeObj);
18
+ result = renderFace(shapeObj, 0, this.meshConfig);
15
19
  }
16
20
  else if (Explorer.isWire(shape)) {
17
- result = renderWire(shapeObj);
21
+ result = renderWire(shapeObj, this.meshConfig);
18
22
  }
19
23
  else if (Explorer.isEdge(shape)) {
20
- result = renderEdge(shapeObj);
24
+ result = renderEdge(shapeObj, this.meshConfig);
21
25
  }
22
26
  else if (Explorer.isCompound(shape)) {
23
27
  console.warn("Compound shapes are not supported yet.");
@@ -1,3 +1,4 @@
1
1
  import { Shape } from "../common/shape.js";
2
+ import type { MeshConfig } from "../oc/mesh.js";
2
3
  import type { SceneObjectMesh } from "./scene.js";
3
- export declare function renderEdge(edgeObj: Shape): SceneObjectMesh;
4
+ export declare function renderEdge(edgeObj: Shape, meshConfig?: MeshConfig): SceneObjectMesh;
@@ -1,4 +1,4 @@
1
1
  import { Mesh } from "../oc/mesh.js";
2
- export function renderEdge(edgeObj) {
3
- return Mesh.discretizeEdge(edgeObj);
2
+ export function renderEdge(edgeObj, meshConfig) {
3
+ return Mesh.discretizeEdge(edgeObj, meshConfig);
4
4
  }
@@ -1,2 +1,3 @@
1
1
  import { Shape } from "../common/shape.js";
2
- export declare function renderFace(faceObj: Shape, vertexOffset?: number): import("../oc/mesh.js").MeshData;
2
+ import type { MeshConfig } from "../oc/mesh.js";
3
+ export declare function renderFace(faceObj: Shape, vertexOffset?: number, meshConfig?: MeshConfig): import("../oc/mesh.js").MeshData;
@@ -1,5 +1,5 @@
1
1
  import { Mesh } from "../oc/mesh.js";
2
- export function renderFace(faceObj, vertexOffset = 0) {
2
+ export function renderFace(faceObj, vertexOffset = 0, meshConfig) {
3
3
  const face = faceObj.getShape();
4
- return Mesh.triangulateFaceRaw(face, vertexOffset);
4
+ return Mesh.triangulateFaceRaw(face, vertexOffset, meshConfig);
5
5
  }
@@ -1,3 +1,4 @@
1
1
  import { Shape } from "../common/shape.js";
2
2
  import { SceneObjectMesh } from "./scene.js";
3
- export declare function renderSolid(shapeObj: Shape): SceneObjectMesh[];
3
+ import type { MeshConfig } from "../oc/mesh.js";
4
+ export declare function renderSolid(shapeObj: Shape, meshConfig?: MeshConfig): SceneObjectMesh[];
@@ -1,8 +1,8 @@
1
1
  import { Explorer } from "../oc/explorer.js";
2
2
  import { Mesh } from "../oc/mesh.js";
3
3
  import { getOC } from "../oc/init.js";
4
- export function renderSolid(shapeObj) {
5
- Mesh.ensureTriangulated(shapeObj.getShape());
4
+ export function renderSolid(shapeObj, meshConfig) {
5
+ Mesh.ensureTriangulated(shapeObj.getShape(), meshConfig);
6
6
  const facesMeshes = getFacesMesh(shapeObj);
7
7
  const edgesMesh = getEdgesMesh(shapeObj);
8
8
  return [...facesMeshes, ...edgesMesh];
@@ -1,5 +1,6 @@
1
1
  import { Shape } from "../common/shape.js";
2
- export declare function renderWire(shapeObj: Shape): {
2
+ import type { MeshConfig } from "../oc/mesh.js";
3
+ export declare function renderWire(shapeObj: Shape, meshConfig?: MeshConfig): {
3
4
  vertices: number[];
4
5
  normals: number[];
5
6
  indices: number[];
@@ -2,9 +2,9 @@ import { renderEdge } from "./render-edge.js";
2
2
  import { Edge } from "../common/edge.js";
3
3
  import { Explorer } from "../oc/explorer.js";
4
4
  import { Mesh } from "../oc/mesh.js";
5
- export function renderWire(shapeObj) {
5
+ export function renderWire(shapeObj, meshConfig) {
6
6
  const shape = shapeObj.getShape();
7
- Mesh.ensureTriangulated(shape);
7
+ Mesh.ensureTriangulated(shape, meshConfig);
8
8
  const edges = Explorer.findShapes(shape, Explorer.getOcShapeType("edge"));
9
9
  const allVertices = [];
10
10
  const allNormals = [];
@@ -1,6 +1,9 @@
1
1
  import { Scene } from "./scene.js";
2
+ import type { MeshConfig } from "../oc/mesh.js";
2
3
  export declare class SceneRenderer {
4
+ private readonly meshConfig;
3
5
  private readonly meshBuilder;
6
+ constructor(meshConfig: MeshConfig);
4
7
  render(scene: Scene): Scene;
5
8
  renderRollback(scene: Scene, rollbackIndex: number): Scene;
6
9
  private batchTriangulate;