forgecad 0.9.13 → 0.9.15

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 (216) hide show
  1. package/LICENSE +6 -4
  2. package/README.md +8 -4
  3. package/dist/assets/{AdminPage-DramHHDf.js → AdminPage-CDyGUinA.js} +2 -2
  4. package/dist/assets/{BenchmarkPage-Bjgkh5m9.js → BenchmarkPage-DfPMY_-d.js} +4 -15
  5. package/dist/assets/{BlogPage-n_HGP3Qm.js → BlogPage-kF0fkdJT.js} +2 -2
  6. package/dist/assets/{DocsPage-WCIkPmzC.js → DocsPage-B954L3YN.js} +9 -3
  7. package/dist/assets/EditorApp-Beb-IZ0y.js +14014 -0
  8. package/dist/assets/{EditorApp-BAnckbsk.css → EditorApp-CuDLxKqL.css} +698 -0
  9. package/dist/assets/{EmbedViewer-DEZKqdfW.js → EmbedViewer-C77B-TrF.js} +3 -3
  10. package/dist/assets/{LandingPageProofDriven-CeRIctuj.js → LandingPageProofDriven-Cr6fXMDj.js} +35 -37
  11. package/dist/assets/LegalPage-BRlScr9A.css +91 -0
  12. package/dist/assets/LegalPage-Dzklqmmg.js +39 -0
  13. package/dist/assets/{PricingPage-BMedqFef.css → PricingPage-BPF6HKyO.css} +25 -0
  14. package/dist/assets/{PricingPage-rIRa8p4Y.js → PricingPage-zWXkvlwl.js} +19 -19
  15. package/dist/assets/{SettingsPage-BqCUvEXM.js → SettingsPage-Bz0of4KQ.js} +2 -2
  16. package/dist/assets/app-CE3sYcV7.css +3890 -0
  17. package/dist/assets/{app-BUZqJvSO.js → app-D3kDkggg.js} +2305 -960
  18. package/dist/assets/cli/{render-lhGxj50Y.js → render-DSY3mMQa.js} +423 -30
  19. package/dist/assets/{constructionHistoryWorker-ipD1jcIv.js → constructionHistoryWorker-gpDo-uH2.js} +927 -243
  20. package/dist/assets/{evalWorker-CHXSe_-u.js → evalWorker-CU0Ke6DP.js} +7799 -4163
  21. package/dist/assets/{forgecad_geometry-BVnIeXMG.js → forgecad_geometry-Dgceylq9.js} +43 -1
  22. package/dist/assets/{forgecad_geometry_bg-DufhhCBV.wasm → forgecad_geometry_bg-dD4RNQF1.wasm} +0 -0
  23. package/dist/assets/{inspectWorker-DeRnMVv1.js → inspectWorker-COyp8XXA.js} +927 -243
  24. package/dist/assets/{javascript-70-4uGcz.js → javascript-1kQXfVaz.js} +1 -1
  25. package/dist/assets/landing-proof-driven-DiGqdtWa.js +18 -0
  26. package/dist/assets/{landing-proof-driven-oFYW6mjz.css → landing-proof-driven-ORyigZ6p.css} +13 -7
  27. package/dist/assets/legalContent-ZfFGMmi4.js +251 -0
  28. package/dist/assets/{manifold-D1LZIHqn.js → manifold-BRI5prcH.js} +1 -1
  29. package/dist/assets/{manifold-C2fwoTgd.js → manifold-C-3h2M7p.js} +2 -2
  30. package/dist/assets/{manifold-BTkzxi9V.js → manifold-DNkrUWpA.js} +1 -1
  31. package/dist/assets/{reportWorker-Cq1qGmg0.js → reportWorker-CdBz5bNg.js} +7537 -10856
  32. package/dist/assets/{scalar-sampling-budget-D9Qv_UlJ.js → scalar-sampling-budget-wJF98aY9.js} +6943 -4345
  33. package/dist/assets/{scanProxyWorker-Bs2TDgLw.js → scanProxyWorker-B-9VbLIs.js} +32 -1
  34. package/dist/assets/{renderSceneState-Dr0xPq1A.js → targets-B9sGB5nB.js} +27 -1
  35. package/dist/assets/{vendor-react-Da3A2QmU.js → vendor-react-6j1Kke-Y.js} +6 -5
  36. package/dist/cli/render.html +1 -1
  37. package/dist/docs/index.html +2 -2
  38. package/dist/docs-raw/AI/ai-native-cad.md +50 -0
  39. package/dist/docs-raw/AI/usage.md +9 -17
  40. package/dist/docs-raw/CLI.md +71 -21
  41. package/dist/docs-raw/component-model.md +27 -11
  42. package/dist/docs-raw/generated/assembly.md +301 -212
  43. package/dist/docs-raw/generated/concepts.md +238 -240
  44. package/dist/docs-raw/generated/core.md +283 -6
  45. package/dist/docs-raw/generated/curves.md +274 -361
  46. package/dist/docs-raw/generated/lib.md +7 -1
  47. package/dist/docs-raw/generated/output.md +19 -4
  48. package/dist/docs-raw/generated/runtime-names.md +41 -0
  49. package/dist/docs-raw/generated/sdf.md +31 -0
  50. package/dist/docs-raw/generated/sheet-metal.md +9 -0
  51. package/dist/docs-raw/generated/sketch.md +44 -1
  52. package/dist/docs-raw/generated/viewport.md +14 -6
  53. package/dist/docs-raw/guides/coordinate-system.md +20 -16
  54. package/dist/docs-raw/guides/geometry-conventions.md +2 -2
  55. package/dist/docs-raw/guides/inspection-bundles.md +2 -1
  56. package/dist/docs-raw/guides/joint-design.md +24 -0
  57. package/dist/docs-raw/guides/positioning.md +13 -3
  58. package/dist/docs-raw/legal/privacy.md +63 -0
  59. package/dist/docs-raw/legal/software-license.md +55 -0
  60. package/dist/docs-raw/legal/terms.md +87 -0
  61. package/dist/docs-raw/skills/forgecad-3d-reconstruction.md +3 -3
  62. package/dist/docs-raw/skills/forgecad-blockout-model.md +1 -1
  63. package/dist/docs-raw/skills/forgecad-component-model.md +11 -2
  64. package/dist/docs-raw/skills/forgecad-high-level-spec.md +1 -1
  65. package/dist/docs-raw/skills/forgecad-image-replicator.md +8 -8
  66. package/dist/docs-raw/skills/forgecad-lld.md +1 -1
  67. package/dist/docs-raw/skills/forgecad-make-a-model.md +4 -4
  68. package/dist/docs-raw/skills/forgecad-model-grader.md +2 -2
  69. package/dist/docs-raw/skills/forgecad-prepare-prompt.md +2 -2
  70. package/dist/docs-raw/skills/forgecad-project.md +1 -1
  71. package/dist/docs-raw/skills/forgecad-reconstruction-benchmark.md +4 -4
  72. package/dist/docs-raw/skills/forgecad-render-inspect.md +4 -2
  73. package/dist/docs-raw/skills/forgecad-visual-spec.md +1 -1
  74. package/dist/docs-raw/skills/forgecad.md +4 -3
  75. package/dist/index.html +40 -12
  76. package/dist/llms.txt +8 -0
  77. package/dist/site.webmanifest +1 -1
  78. package/dist/sitemap.xml +49 -13
  79. package/dist-cli/{check-compiler-LOXCPEOI.js → check-compiler-SDX5QIXI.js} +1 -2
  80. package/dist-cli/{check-query-propagation-BAKNVWXR.js → check-query-propagation-EAYEFT77.js} +1 -2
  81. package/dist-cli/{chunk-RY43WF46.js → chunk-N4O47JLF.js} +13772 -9938
  82. package/dist-cli/forgecad.js +2387 -899
  83. package/dist-cli/{forgecad_geometry-GYVNKPIE.js → forgecad_geometry-QOQIIP53.js} +42 -1
  84. package/dist-cli/forgecad_geometry_bg.wasm +0 -0
  85. package/dist-cli/{solver-46FFSK2U.js → solver-OK4HECRH.js} +0 -1
  86. package/dist-skill/CONTEXT.md +1120 -724
  87. package/dist-skill/SKILL.md +3 -2
  88. package/dist-skill/docs/API/core/concepts.md +64 -1
  89. package/dist-skill/docs/CLI.md +71 -21
  90. package/dist-skill/docs/generated/assembly.md +277 -229
  91. package/dist-skill/docs/generated/core.md +283 -6
  92. package/dist-skill/docs/generated/curves.md +272 -362
  93. package/dist-skill/docs/generated/lib.md +7 -1
  94. package/dist-skill/docs/generated/output.md +19 -4
  95. package/dist-skill/docs/generated/runtime-names.md +41 -0
  96. package/dist-skill/docs/generated/sdf.md +31 -0
  97. package/dist-skill/docs/generated/sheet-metal.md +9 -0
  98. package/dist-skill/docs/generated/sketch.md +44 -2
  99. package/dist-skill/docs/generated/viewport.md +5 -90
  100. package/dist-skill/docs/guides/coordinate-system.md +20 -16
  101. package/dist-skill/docs/guides/geometry-conventions.md +2 -2
  102. package/dist-skill/docs/guides/inspection-bundles.md +2 -1
  103. package/dist-skill/docs/guides/joint-design.md +24 -0
  104. package/dist-skill/docs/guides/positioning.md +13 -3
  105. package/dist-skill/library/forgecad-3d-reconstruction/SKILL.md +2 -2
  106. package/dist-skill/library/forgecad-component-model/SKILL.md +10 -1
  107. package/dist-skill/library/forgecad-image-replicator/SKILL.md +6 -6
  108. package/dist-skill/library/forgecad-image-replicator/scripts/compare_images.py +166 -0
  109. package/dist-skill/library/forgecad-make-a-model/SKILL.md +3 -3
  110. package/dist-skill/library/forgecad-model-grader/SKILL.md +1 -1
  111. package/dist-skill/library/forgecad-prepare-prompt/SKILL.md +1 -1
  112. package/dist-skill/library/forgecad-reconstruction-benchmark/SKILL.md +3 -3
  113. package/dist-skill/library/forgecad-render-inspect/SKILL.md +3 -1
  114. package/examples/api/assembly-kinematics-foundation.forge.js +65 -0
  115. package/examples/api/assembly-kinematics-four-bar.forge.js +115 -0
  116. package/examples/api/assembly-kinematics-limb.forge.js +116 -0
  117. package/examples/api/connector-frame-rig-chain.forge.js +102 -0
  118. package/examples/api/exact-sheet-shell-assembly.forge.js +0 -2
  119. package/examples/api/exact-surface-studio.forge.js +6 -8
  120. package/examples/api/helix-basics.forge.js +6 -6
  121. package/examples/api/lean-foundations/README.md +12 -0
  122. package/examples/api/lean-foundations/curve-blend-exact.forge.js +22 -0
  123. package/examples/api/lean-foundations/curve-fit-interpolation.forge.js +18 -0
  124. package/examples/api/lean-foundations/curve-helix-canonicalization.forge.js +27 -0
  125. package/examples/api/lean-foundations/curve-route-canonicalization.forge.js +16 -0
  126. package/examples/api/lean-foundations/curve-trim-reverse.forge.js +24 -0
  127. package/examples/api/lean-foundations/exact-curve-arc.forge.js +36 -0
  128. package/examples/api/mixed-edge-finishes-proof.forge.js +8 -11
  129. package/examples/api/route3d-elbow.forge.js +68 -0
  130. package/examples/api/transition-curves.forge.js +44 -15
  131. package/examples/api/y-blend-corner-showcase.forge.js +0 -2
  132. package/examples/generative/coral-vase.forge.js +1 -1
  133. package/examples/nurbs-tube.forge.js +1 -1
  134. package/package.json +14 -18
  135. package/dist/assets/EditorApp-CP9Za6tm.js +0 -13630
  136. package/dist/assets/app-CsHnaBWt.css +0 -1789
  137. package/dist/docs-raw/API/README.md +0 -16
  138. package/dist/docs-raw/API/core/concepts.md +0 -118
  139. package/dist/docs-raw/INDEX.md +0 -138
  140. package/dist/docs-raw/RELEASING.md +0 -87
  141. package/dist/docs-raw/agent-native-api.md +0 -27
  142. package/dist/docs-raw/beta-deployment.md +0 -304
  143. package/dist/docs-raw/beta-operations.md +0 -325
  144. package/dist/docs-raw/blueprint-first.md +0 -145
  145. package/dist/docs-raw/cli-monetization.md +0 -112
  146. package/dist/docs-raw/coding-best-practices.md +0 -120
  147. package/dist/docs-raw/coding.md +0 -340
  148. package/dist/docs-raw/deployment.md +0 -374
  149. package/dist/docs-raw/guides/skill-maintenance.md +0 -161
  150. package/dist/docs-raw/guides/surface-members.md +0 -82
  151. package/dist/docs-raw/internals/backend-vocabulary.md +0 -35
  152. package/dist/docs-raw/internals/compiler.md +0 -307
  153. package/dist/docs-raw/internals/constraint-solver-quality.md +0 -161
  154. package/dist/docs-raw/internals/constraint-solver.md +0 -176
  155. package/dist/docs-raw/internals/shape-from-slices.md +0 -152
  156. package/dist/docs-raw/internals/sketch-2d-pipeline.md +0 -108
  157. package/dist/docs-raw/platform/admin.md +0 -45
  158. package/dist/docs-raw/platform/architecture.md +0 -82
  159. package/dist/docs-raw/platform/auth.md +0 -139
  160. package/dist/docs-raw/platform/email.md +0 -67
  161. package/dist/docs-raw/platform/google-oauth-setup.md +0 -88
  162. package/dist/docs-raw/platform/observability.md +0 -197
  163. package/dist/docs-raw/platform/projects.md +0 -111
  164. package/dist/docs-raw/platform/sharing.md +0 -90
  165. package/dist/docs-raw/product/README.md +0 -39
  166. package/dist/docs-raw/product/api-as-product-language.md +0 -13
  167. package/dist/docs-raw/product/business-model.md +0 -15
  168. package/dist/docs-raw/product/competitive-positioning.md +0 -17
  169. package/dist/docs-raw/product/creative-manufacturing.md +0 -15
  170. package/dist/docs-raw/product/founder-story.md +0 -11
  171. package/dist/docs-raw/product/manufacturing-workflows.md +0 -15
  172. package/dist/docs-raw/product/onboarding-first-experience.md +0 -256
  173. package/dist/docs-raw/product/product-loop.md +0 -17
  174. package/dist/docs-raw/product/strategic-decisions.md +0 -22
  175. package/dist/docs-raw/product/user-outreach-email-templates.md +0 -161
  176. package/dist/docs-raw/product/user-segments.md +0 -15
  177. package/dist/docs-raw/product/vision.md +0 -26
  178. package/dist/docs-raw/rl-environments.md +0 -508
  179. package/dist/docs-raw/runbook.md +0 -611
  180. package/dist-cli/check-compiler-LOXCPEOI.js.map +0 -1
  181. package/dist-cli/check-query-propagation-BAKNVWXR.js.map +0 -1
  182. package/dist-cli/chunk-RY43WF46.js.map +0 -1
  183. package/dist-cli/forgecad.js.map +0 -1
  184. package/dist-cli/forgecad_geometry-GYVNKPIE.js.map +0 -1
  185. package/dist-cli/solver-46FFSK2U.js.map +0 -1
  186. package/dist-skill/SKILL-dev.md +0 -145
  187. package/dist-skill/docs-dev/API/core/concepts.md +0 -118
  188. package/dist-skill/docs-dev/CLI.md +0 -647
  189. package/dist-skill/docs-dev/agent-native-api.md +0 -27
  190. package/dist-skill/docs-dev/blueprint-first.md +0 -145
  191. package/dist-skill/docs-dev/coding-best-practices.md +0 -120
  192. package/dist-skill/docs-dev/coding.md +0 -340
  193. package/dist-skill/docs-dev/component-model.md +0 -164
  194. package/dist-skill/docs-dev/generated/assembly.md +0 -794
  195. package/dist-skill/docs-dev/generated/core.md +0 -2117
  196. package/dist-skill/docs-dev/generated/curves.md +0 -2583
  197. package/dist-skill/docs-dev/generated/lib.md +0 -169
  198. package/dist-skill/docs-dev/generated/output.md +0 -247
  199. package/dist-skill/docs-dev/generated/sdf.md +0 -446
  200. package/dist-skill/docs-dev/generated/sheet-metal.md +0 -504
  201. package/dist-skill/docs-dev/generated/sketch.md +0 -1811
  202. package/dist-skill/docs-dev/generated/viewport.md +0 -585
  203. package/dist-skill/docs-dev/generated/wood.md +0 -108
  204. package/dist-skill/docs-dev/guides/coordinate-system.md +0 -46
  205. package/dist-skill/docs-dev/guides/geometry-conventions.md +0 -52
  206. package/dist-skill/docs-dev/guides/inspection-bundles.md +0 -485
  207. package/dist-skill/docs-dev/guides/joint-design.md +0 -78
  208. package/dist-skill/docs-dev/guides/modeling-recipes.md +0 -78
  209. package/dist-skill/docs-dev/guides/positioning.md +0 -161
  210. package/dist-skill/docs-dev/guides/skill-maintenance.md +0 -161
  211. package/dist-skill/docs-dev/internals/backend-vocabulary.md +0 -35
  212. package/dist-skill/docs-dev/internals/compiler.md +0 -307
  213. package/dist-skill/docs-dev/internals/constraint-solver-quality.md +0 -161
  214. package/dist-skill/docs-dev/internals/constraint-solver.md +0 -176
  215. package/dist-skill/docs-dev/internals/sketch-2d-pipeline.md +0 -108
  216. package/dist-skill/library/forgecad-image-replicator/scripts/compare_images.mjs +0 -289
@@ -1,307 +0,0 @@
1
- ---
2
- skill-group: dev-compiler
3
- skill-order: 1
4
- ---
5
-
6
- # Forge Compiler Architecture
7
-
8
- This is the durable architecture record for ForgeCAD's multi-backend modeling system.
9
-
10
- If you add or refactor geometry features, read this first.
11
-
12
- Canonical naming for this area lives in [backend-vocabulary.md](backend-vocabulary.md).
13
-
14
- ## Core Position
15
-
16
- Forge should not invent a new CAD language or a new geometry kernel.
17
-
18
- Forge should:
19
-
20
- - keep JS/TS as the host language
21
- - keep Forge's semantic feature model as the source of truth
22
- - lower that model into different geometry backends intentionally
23
- - make capability gaps explicit in diagnostics and tests
24
-
25
- The unique thing to protect is not backend code. It is Forge's code-first modeling layer:
26
-
27
- - browser-first iteration
28
- - parametric code as the authoring format
29
- - composable multi-file projects
30
- - AI-writable design code
31
- - explicit provenance and backend visibility
32
-
33
- ## What To Steal
34
-
35
- We should steal proven ideas aggressively.
36
-
37
- Steal from Fusion 360 / Onshape / FeatureScript:
38
-
39
- - semantic feature graphs
40
- - workplanes and sketch planes as first-class modeling context
41
- - query/reference systems for faces, edges, bodies, and feature results
42
- - feature definitions that lower into kernels instead of directly calling them from UI/API code
43
-
44
- Steal from CadQuery / build123d / replicad:
45
-
46
- - OCCT execution patterns
47
- - exact solid feature expectations
48
- - practical scripting ergonomics around sketches, planes, and feature builders
49
-
50
- Steal from OCAF-style document systems:
51
-
52
- - durable reference identity
53
- - explicit ownership of topology/reference propagation
54
-
55
- Do not steal:
56
-
57
- - a separate Forge DSL
58
- - backend object models as the user-facing API
59
- - exporter-only side systems that bypass the compiler
60
-
61
- ## The Make-Or-Break Layer
62
-
63
- This is where Forge will likely succeed or fail:
64
-
65
- - the semantic feature graph
66
- - the compiled-scene routing layer
67
- - the reference / workplane / query system
68
-
69
- Why:
70
-
71
- - primitives, booleans, extrudes, and revolves are not the hard part
72
- - real mechanical design depends on downstream face- and edge-driven features
73
- - those features only stay usable if references survive feature chains predictably
74
-
75
- If this layer is weak, the repo will accumulate:
76
-
77
- - backend-specific feature code at callsites
78
- - export-only implementations
79
- - brittle face-name assumptions
80
- - features that work in demos but collapse in real parts
81
-
82
- If this layer is strong, future features stay additive.
83
-
84
- ## Success Criteria
85
-
86
- We are succeeding only if all of these are true:
87
-
88
- 1. Forge compile intent is the source of truth for mainstream part-design features.
89
- 2. Scene-level routing is centralized, inspectable, and reused by export/debug/test flows.
90
- 3. New features do not need ad hoc backend decisions at callsites.
91
- 4. Workplane/reference/query semantics are stable enough for ordinary downstream feature chains.
92
- 5. Most normal Design-workspace features can be added by extending one semantic node family and two lowerers.
93
- 6. Tests catch plan drift, routing drift, and backend-output drift before release.
94
-
95
- We are failing if any of these become normal:
96
-
97
- - "this feature only works in export"
98
- - "this feature only works in Manifold"
99
- - "this feature only works if you know the right face name"
100
- - "just add one more backend escape hatch"
101
- - "the snapshot changed, probably fine"
102
-
103
- ## Contributor Contract
104
-
105
- When adding a geometry feature:
106
-
107
- 1. Define the Forge semantic intent first.
108
- 2. Decide which domain owns it.
109
- 3. Add or extend the compile node.
110
- 4. Lower it through Manifold.
111
- 5. Lower it through the maintained export backend, or add explicit unsupported diagnostics.
112
- 6. Route it through the scene compiler.
113
- 7. Add invariants and snapshot coverage.
114
- 8. Update permanent docs and the living mission tracker.
115
-
116
- Do not:
117
-
118
- - call a concrete backend directly from user-facing feature APIs unless that code is the backend lowerer itself
119
- - add exporter-only feature behavior that is invisible to the compiler
120
- - silently widen or narrow exact coverage
121
-
122
- ## Implementation Path
123
-
124
- ### Phase 1: Compiler Ownership
125
-
126
- Goal:
127
-
128
- - every supported feature records Forge intent
129
- - lowerers consume that intent
130
- - scene routing is centralized
131
-
132
- Deliverables:
133
-
134
- - backend-neutral compile nodes
135
- - compiled-scene routing and diagnostics
136
- - snapshot and invariant coverage
137
-
138
- ### Phase 2: Reference / Workplane / Query Model
139
-
140
- Goal:
141
-
142
- - face-, edge-, and plane-driven features stop depending on brittle synthetic naming
143
-
144
- Deliverables:
145
-
146
- - first-class workplane representation
147
- - query/reference API for "what this feature means"
148
- - propagation rules for downstream feature ownership
149
-
150
- This is the highest-leverage next layer. Without it, `shell`, fillet/chamfer, holes, projection, and sheet-metal-style features will all stay fragile.
151
-
152
- Initial slice:
153
-
154
- - sketches placed with `onFace()` should carry semantic workplane/query metadata, not just a resolved 4x4 matrix
155
- - downstream feature code should be able to ask "which workplane/query produced this?" without reverse-engineering transforms
156
-
157
- Current progress:
158
-
159
- - `onFace()` placements now record semantic workplane models on sketches
160
- - `extrude()` and `revolve()` now preserve that intent in the shape compile graph as a first-class workplane-placement transform instead of collapsing it to an anonymous rigid transform
161
- - downstream compiler-aware code can now inspect that preserved workplane placement directly
162
- - that compiler-visible workplane placement now propagates through later shape transforms, so provenance inspection stays aligned with the current transformed feature state
163
- - both lowerers now execute that preserved workplane-placement intent, and export regression tests exercise the maintained export-backend path end-to-end
164
- - compile-covered feature results now carry compiler-owned query-owner lineage, so parent-body ownership is no longer implicit runtime state
165
- - `src/forge/queryModel.ts` now defines the shared query/reference contract for face provenance instead of letting workplane and topology code drift separately
166
- - workplane sources and `FaceRef` values now share that face-query contract when compiler-owned provenance exists
167
- - `src/forge/queryModel.ts` now also defines the shared edge-query contract for tracked edges and direct edge refs, including selector semantics for whole-edge vs. `start` / `end` / `midpoint` references
168
- - tracked-topology flows now preserve that edge-query metadata through clone/translate/workplane-placement transforms, and placement invariants assert that tracked edge selectors stay aligned with the actual transformed edge geometry
169
- - booleans, shell, split/trim flows, and downstream workplane-driven features now preserve that owner lineage through the compile graph instead of erasing it at each feature boundary
170
- - mirrored results plus `linearPattern()` / `circularPattern()` helper copies now get fresh compiler-owned repeated-result owners when their source shapes stay compile-covered
171
- - placement and exact-export invariants now check that owner lineage survives ordinary shell-plus-cut-plus-boolean style part workflows
172
- - topology-changing compile nodes now also carry an explicit backend-neutral `queryPropagation` contract for preserved/created query meaning instead of leaving post-rewrite semantics implicit
173
- - that shared propagation contract now models propagated face/edge queries, feature-created query slots, and explicit ambiguity/unsupported diagnostics on rewrite-producing results
174
- - trim/split-by-plane, shell, hole, cut, boolean, and edge-finish rewrites now also declare descendant contracts on top of that propagation kernel
175
- - the shared descendant-resolution layer turns those lineage inputs into defended singles, face regions/sets, edge chains, or explicit unsupported results instead of making each caller reverse-engineer rewrite heuristics
176
- - trim/split-by-plane now expose their plane-cap face plus descendant-region contracts for preserved source surfaces, while hole/cut host-face splits and supported boolean difference/intersection descendants stay reviewable as defended regions instead of disappearing behind one generic ambiguity bucket
177
- - boolean rewrites now consume that same contract: supported unions still preserve operand label/query lineage when the source owners stay distinct, and coplanar/multi-member descendants can now surface as defended face sets instead of only masked name conflicts
178
- - shell, hole, and cut now layer defended created-face slots plus defended descendant regions on top of that propagation kernel instead of leaving all post-rewrite face meaning as placeholders
179
- - compile-covered `Shape.face(name)` resolution now reads from the compile graph plus the descendant layer, so supported shell inner walls, blind-hole floors, cut-created walls, split host faces, and defended boolean face sets can produce real `FaceRef` values after topology rewrites
180
- - label-based `onFace(shape, 'inner-side-right', ...)` placement now routes through the compiler-owned face resolver, while direct `FaceRef` placements preserve created-face/propagated-face provenance instead of collapsing everything back to anonymous refs
181
- - `src/forge/kernel.ts` and the compiler inspection surface now expose collected topology-rewrite propagation plus descendant contracts directly, and the placement/compiler invariants assert that those contracts stay inspectable and deterministic through later transforms
182
- - `forgecad check query-propagation` now snapshots both the propagation surface and the descendant contracts directly, so defended plane-cap faces, split-face regions, face sets, merged-edge chains, and explicit unsupported rewrite boundaries stay reviewable without wading through the full compiler-scene baseline
183
-
184
- Still missing:
185
-
186
- - universal durable face/edge identity across topology-changing operations
187
- - mesh/SDF rewrite families still need their own explicit descendant contracts instead of borrowed BREP-style assumptions
188
- - shared edge-query selectors exist for tracked topology, but topology-changing features still do not produce new single-edge ownership for most rewritten/created edges beyond today's defended preserved-edge and edge-chain subset
189
- - stable downstream face/edge references beyond today's defended single/region/set/chain subset still need the follow-on work in tasks 180, 190, and 195
190
-
191
- ### Phase 3: Mainstream Feature Families
192
-
193
- Goal:
194
-
195
- - dual-lower the ordinary part-design stack
196
-
197
- Priority order:
198
-
199
- - `shell`
200
- - fillet / chamfer
201
- - holes / patterned cuts
202
- - projection and sketch-on-face refinement
203
- - mirror / pattern workflows driven by semantic references
204
-
205
- Current progress:
206
-
207
- - `shell()` is now compiler-owned as the first mainstream exact feature-family slice instead of being left for exporter-only logic
208
- - both lowerers consume the same semantic `shell` node and rewrite supported cases into backend-native boolean/extrude/cylinder plans
209
- - `shape.hole()` and `shape.cutout()` now form a compiler-owned hole/cut workflow slice anchored to the shared face-query/workplane model
210
- - through holes, blind holes, counterbores, countersinks, and planar `upToFace` hole/cut extents now lower through both Manifold and the maintained export backend from that shared semantic node family
211
- - shell, hole, and cut now expose defended named created-face subsets on top of the topology-rewrite kernel, so downstream features can target inner shell walls, blind-hole floors, and supported cut walls/floors without falling back to anonymous runtime placement
212
- - richer hole variants now also expose defended `counterbore-floor`, `counterbore-wall`, and `countersink-wall` created-face slots where Forge can model them directly
213
- - `filletEdge()` and `chamferEdge()` now form a first compiler-owned tracked-edge finishing slice for supported vertical edges on compile-covered `box()` and `rectangle(...).extrude(...)` bodies
214
- - the post-rewrite edge-query layer now also defends untouched sibling vertical edges after those supported edge-finish rewrites, plus later supported boolean-union descendants when one preserved propagated-edge lineage stays explicit
215
- - regression coverage now includes compiler snapshots plus exact-export invariants for `shell()`
216
- - regression coverage now also includes exact/runtime/export checks for the supported hole/cut workflow subset
217
- - regression coverage now also includes API, placement-owner, query-propagation, compiler-snapshot, exact-plan, corpus, and end-to-end export checks for the broadened tracked-edge fillet/chamfer subset
218
- - the regression suite now also includes a file-backed ordinary-parts corpus under `examples/compiler-corpus/`, so shell, richer hole/cut workflows, projection replay, trim-created faces, repeated descendants, and finishing flows are exercised together instead of only as isolated unit slices
219
- - the MLP closeout review surface now lives in that corpus plus `forgecad check compiler`, `forgecad check query-propagation`, and `forgecad check brep`, so the defended subset is reviewable from the repo instead of from tribal knowledge
220
- - mirrored downstream features and helper-driven linear/circular repetition now preserve repeated-result ownership on top of the shared face-query backbone
221
- - supported boolean unions now also preserve owner-scoped label queries from repeated descendants, and compiler regressions cover both explicit duplicate-owner merge ambiguity and later boolean chains that inherit those propagated queries
222
- - exact export regression coverage now includes a repeated-feature part where a mirrored descendant drives a downstream workplane feature inside a boolean chain
223
- - `projectToPlane()` sketches now keep an explicit projection node in the compiler graph instead of collapsing immediately to anonymous runtime geometry
224
- - compile-covered `Sketch.onFace(shape, name)` resolution now prefers the defended face-query table on `Shape` targets, so supported boolean-preserved names and explicit repeated-descendant `FaceRef`s stay visible to later features instead of falling back to anonymous heuristics
225
- - the supported exact subset can now replay projection-driven follow-on features when the source reduces to one defended planar projection basis: placed straight extrusions, compatible shell/hole/cut descendants, and boolean unions of compatible projected operands all stay aligned on matching parallel target planes
226
-
227
- Current limits:
228
-
229
- - `shell()` v1 only covers compile-covered `box()`, `cylinder()`, and straight `extrude()` bases with optional `top` / `bottom` openings, while defended named created faces currently cover the exact profile families Forge can model directly (`rect`, `roundedRect`, `circle`)
230
- - `shape.hole()` still only covers circular holes, and `shape.cutout()` still only covers sketches already placed with `onFace(...)`
231
- - `filletEdge()` / `chamferEdge()` v1 cover tracked vertical edges on compile-covered `box()` and `rectangle(...).extrude(...)` bodies plus preserved propagated sibling edges through supported edge-finish and boolean-union chains; the selected rewritten edge now resolves as an explicit descendant edge-chain for inspection/debug, but not yet as a new single finishable edge target
232
- - two-sided extents, tapered cutouts, and thread metadata are now supported; combined counterbore+countersink heads, modeled helical threads, and broader durable identity beyond today's defended shell/hole/cut created-face subset are still missing
233
- - `upToFace` currently requires a planar termination face parallel to the feature direction, but defended split termination faces can now stay queryable as descendant regions where Forge still owns the source plane
234
- - boolean/pattern propagation currently defends owner-scoped face label lineage through supported unions, and the descendant layer can now expose some post-merge/post-difference results as face sets/regions, but broader durable per-face ownership after arbitrary merged topology changes is still incomplete
235
- - repeated-feature ownership currently tracks repeated bodies and mirrored descendants, not durable per-face identity after merged pattern topology changes
236
- - shell, hole/cut, tracked-edge finishing, and repeated-feature workflows now preserve parent-body ownership lineage, but stable downstream face/edge ownership after topology-changing edits is still not solved, which is why richer boolean-difference/intersection targets and broader fillet/chamfer workflows remain harder next layers
237
- - projection replay still rejects boolean difference/intersection sources, trim/fillet/chamfer silhouette changes, and non-parallel projection bases with explicit compiler diagnostics instead of silently pretending those paths are exact-safe
238
-
239
- ### Phase 4: Higher-Order Workflows
240
-
241
- Goal:
242
-
243
- - features like sheet metal, advanced library helpers, and richer manufacturing flows build on the same semantic core
244
-
245
- Current progress:
246
-
247
- - `sheetMetal()` now exists as the first dedicated higher-order semantic family on top of the descendant-resolution layer
248
- - one sheet-metal model now lowers to both a folded solid and a flat pattern instead of splitting folded/export logic across backend-specific codepaths
249
- - named `panel`, `flange-*`, and `bend-*` descendants now flow through the shared face-descendant machinery, so downstream cutouts can still expose honest region/set semantics after topology rewrites
250
- - the maintained `folded-service-panel-cover` proof part now lives in the API examples, compiler corpus, query-propagation snapshots, API invariants, and exact BREP checks
251
-
252
- These higher-order families should keep arriving only after the shared reference/workplane/query layer is credible enough to defend them honestly.
253
-
254
- ## Current Architectural Boundary
255
-
256
- Today, the intended compiler boundary is:
257
-
258
- ```text
259
- Forge scripts
260
- -> Forge semantic feature graph
261
- -> compiled scene + capability routing
262
- -> Manifold lowerer / export-backend lowerer / faceted fallback
263
- ```
264
-
265
- Future feature work should strengthen this boundary, not route around it.
266
-
267
- ## Next Achievable CAD Families
268
-
269
- Once the descendant-resolution layer is in place, the next credible expansion lanes are:
270
-
271
- - richer hole and cut variants
272
- - broader shell workflows
273
- - broader fillet/chamfer workflows
274
- - stronger projection and sketch-on-face flows
275
- - richer sheet-metal corner and detail workflows on top of the new semantic family
276
- - manufacturing outputs such as flat patterns and DXF/SVG profile export
277
- - toolbox and library feature families
278
- - stronger assembly metadata and exact/faceted route visibility
279
-
280
- These are all good fits for the compiler architecture because they can be expressed as Forge-owned semantic intent and lowered intentionally into both backends.
281
-
282
- ## Bigger Leap Areas
283
-
284
- Some CAD-adjacent areas are still important, but they are a bigger leap than the current compiler program:
285
-
286
- - arbitrary direct editing over imported or heavily rewritten BReps
287
- - advanced surfacing or subD/T-spline workflows
288
- - CAM
289
- - simulation / FEA
290
- - full enterprise document/PDM systems
291
- - full large-assembly and mate-solver parity with major desktop CAD tools
292
-
293
- Keep these visible, but do not plan them as if they were just another feature module on the current part-design stack.
294
-
295
- ## Legacy Cleanup Rule
296
-
297
- Yes, the old architecture needs cleanup.
298
-
299
- No, that should not happen as a single flag-day rewrite.
300
-
301
- The rule is:
302
-
303
- 1. add compiler-owned replacements first
304
- 2. fence old bypass paths so new work cannot extend them
305
- 3. retire legacy shims once supported features and examples no longer need them
306
-
307
- That keeps the repo honest without destabilizing active work.
@@ -1,161 +0,0 @@
1
- ---
2
- skill-group: dev-solver
3
- skill-order: 2
4
- ---
5
-
6
- # Constraint Solver Quality — What's Tunable vs What's Architecture
7
-
8
- > For engineers working on the constraint solver. Answers the question: "is the math
9
- > settled, or do we have magic numbers we need to get right?"
10
-
11
- ---
12
-
13
- ## The short answer
14
-
15
- **The math is settled. The craft is not.**
16
-
17
- The core algorithms — LM, CG, the normal equations — are 50-year-old numerical analysis
18
- with proofs. You cannot implement them wrongly in any meaningful sense. What IS hard:
19
-
20
- - Choosing the right initial guess (determines which branch you land on)
21
- - Handling the cases where the math breaks down (degenerate geometry, redundant constraints)
22
- - Making solutions persistent across save/reload/sharing
23
-
24
- These are not numerical parameters. They are architectural decisions.
25
-
26
- ---
27
-
28
- ## What you can tune (and what actually happens when you do)
29
-
30
- | Parameter | Location | Current value | Effect of changing |
31
- |---|---|---|---|
32
- | `tolerance` | `registry.ts` | `1e-3` | Looser → faster but visibly imprecise. Tighter → more iterations, may miss interactive frame budget. |
33
- | `lambda0` | `registry.ts` | `1e-3` | Starting trust region. Too high → slow. Too low → diverges on ill-conditioned sketches. Standard value. |
34
- | `nu` (growth factor) | `registry.ts` | `2` | Standard. Changing this rarely helps. |
35
- | GS escape rounds | `registry.ts` | `3` | More rounds → more expensive. Fewer → LM gets worse warm start. |
36
- | `restarts` | `registry.ts` | `1` | One retry from scratch. More rarely helps unless null-space perturbation is implemented (task 410). |
37
-
38
- **The only parameter with major quality impact is `tolerance`.** The others are in the
39
- "reasonable defaults from the literature" category. `tolerance = 1e-3` is too loose for
40
- mm-scale CAD — production solvers use `1e-6` to `1e-10`.
41
-
42
- ---
43
-
44
- ## What matters more than parameters
45
-
46
- ### 1. Initial guess quality
47
-
48
- LM is a local optimizer. It finds the nearest solution to the starting point. Period.
49
- If your starting geometry is far from the intended configuration, you get the wrong solution
50
- even with `tolerance = 1e-10`.
51
-
52
- This is why GS warm-up exists: to propagate positions forward from anchor constraints before
53
- LM runs. It converts "all points at (0,1)" into something LM can work with.
54
-
55
- **The limit**: GS warm-up only propagates along constraint chains. Circular sub-graphs
56
- (closed loops) can't be bootstrapped this way. For these, the starting positions are
57
- whatever was stored in the file or left over from the previous solve.
58
-
59
- ### 2. Branch selection
60
-
61
- When a constraint system has multiple valid solutions (two circle intersections, mirror
62
- image triangles, open vs. crossed linkages), the solver returns whichever is nearest to
63
- the starting point. This is not a solver quality problem — it is a *correct* behavior.
64
- The issue is that "nearest to starting point" is not the same as "what the user intended."
65
-
66
- Production CAD systems (SolidWorks, Siemens NX) solve this via:
67
- - **Explicit branch hints** stored with the sketch (e.g., signed area of each sub-triangle)
68
- - **Solution continuity**: track the branch from the previous solve and bias the initial
69
- guess toward it
70
-
71
- ForgeCAD does solution continuity implicitly through warm-start. It breaks on file reload
72
- because nothing in the serialization format encodes branch intent. See task 440.
73
-
74
- ### 3. Degenerate case handling
75
-
76
- Several constraints return `residual = 0` (satisfied) when the input entities are degenerate
77
- (zero-length lines, zero-radius circles, zero-sweep arcs). This is caused by the `len || 1`
78
- pattern in `helpers.ts`. The result: invalid geometry that passes the constraint check.
79
-
80
- This is not a numerical issue. It is a correctness issue: the solver is being asked to
81
- evaluate a constraint that is mathematically undefined, and it returns a misleading answer
82
- instead of an error.
83
-
84
- ### 4. Redundant constraint detection
85
-
86
- Overconstrained sketches (more independent constraints than DOF) don't fail — LM minimizes
87
- the sum of squares and returns a "best fit" that satisfies most constraints but silently
88
- violates others. Without rank analysis (SVD of J at convergence), the user gets wrong
89
- geometry with no indication of which constraint caused it.
90
-
91
- The rank analysis infrastructure exists in `rigidity.ts` but is not wired into the solve path.
92
-
93
- ---
94
-
95
- ## The architectural hierarchy of constraint solver quality
96
-
97
- ```
98
- 1. Mathematical correctness of residuals and Jacobians
99
- → If these are wrong, nothing else matters
100
- → Status: Largely correct in TS; verify in Rust solver (task 430)
101
-
102
- 2. Degenerate case handling
103
- → Return errors, not silent wrong answers
104
- → Status: Known gap (len || 1 pattern). See task 442.
105
-
106
- 3. Tolerance
107
- → 1e-3 is too loose. Should be 1e-6.
108
- → Status: Quick win. See task 442.
109
-
110
- 4. Solution branch architecture
111
- → Need to encode branch intent in the file format
112
- → Status: Not done. Hard problem. See tasks 440, 441.
113
-
114
- 5. Initial guess quality (constructive first)
115
- → Eliminate cases where LM starts from a degenerate position
116
- → Status: In progress. See task 420.
117
-
118
- 6. Redundant constraint detection
119
- → Wire rigidity.ts into the solve path to report over/under-constrained status
120
- → Status: Infrastructure exists, not wired. See task 420 requirements section.
121
-
122
- 7. LM hardening (central diff, Nielsen update, null-space restarts)
123
- → Incremental improvements for pathological cases
124
- → Status: Task 410.
125
- ```
126
-
127
- The most impactful work is at levels 2–4. Levels 5–7 are important but affect edge cases.
128
- If you are chasing a specific user-visible quality complaint, start by identifying which
129
- level it belongs to.
130
-
131
- ---
132
-
133
- ## What the constraint solver *cannot* do well
134
-
135
- - **Solve globally overdetermined systems with conflicting constraints gracefully**: LM will
136
- find a local minimum of ||r||², which is not necessarily a meaningful answer. The result
137
- depends entirely on starting position and constraint ordering. Only solution: detect
138
- over-constraint *before* running LM and report it to the user.
139
-
140
- - **Guarantee the "correct" assembly branch**: there is no mathematical notion of the correct
141
- branch. It is a user intent problem, not a solver problem. The solver can only minimize
142
- distance from the initial guess.
143
-
144
- - **Handle discontinuous constraint landscapes robustly**: when constraints have discontinuous
145
- residual gradients (e.g., a "snap to nearest multiple of 45°" constraint), LM cannot
146
- navigate them — GS projectors handle these better. This is why the hybrid LM+GS
147
- architecture exists rather than pure LM.
148
-
149
- ---
150
-
151
- ## Connection to the constraint-solver-from-scratch course
152
-
153
- | Quality concern | Chapter that explains the math |
154
- |---|---|
155
- | Why tolerance matters | 01 (residuals) — what "solved" means |
156
- | Why initial guess matters | 03 (Newton-Raphson) — local convergence basin |
157
- | Branch selection | No chapter yet — potential Chapter 16 |
158
- | Redundant constraint detection | 11 (SVD/null space) — rank and DOF |
159
- | Degenerate Jacobians | 11 (SVD) — condition number and near-singularity |
160
- | GS vs LM trade-off | 05 (Gauss-Seidel) + 06 (LM) |
161
- | Constructive solving | 12 (graph-based solving) |
@@ -1,176 +0,0 @@
1
- ---
2
- skill-group: dev-solver
3
- skill-order: 1
4
- ---
5
-
6
- # Constraint Solver Internals
7
-
8
- > For a new team member. This covers the solver architecture, the key design
9
- > decisions behind it, and the pitfalls we've already hit and solved.
10
-
11
- ## Architecture overview
12
-
13
- The constraint solver lives in `src/forge/sketch/constraints/`. Each constraint
14
- type is a file in `defs/` that calls `registerConstraint()` at module load time.
15
- The central registry (`registry.ts`) drives the solver.
16
-
17
- ### Solver pipeline
18
-
19
- When the user calls `sk.solve()`, the pipeline is:
20
-
21
- ```
22
- 1. Clone the definition (preserve originals for undo/compare)
23
- 2. Build Maps for fast lookup (points, lines, circles, arcs, shapes)
24
- 3. PRESOLVE — call constraint.presolve() once per constraint
25
- 4. GS WARM-UP — 5 cheap Gauss-Seidel iterations to propagate positions
26
- 5. NEWTON-RAPHSON — up to N iterations (if every constraint has a residual)
27
- 6. GS FALLBACK — if NR wasn't available or didn't converge, run full GS loop
28
- 7. Arc enforcement — scale arc endpoints to consistent radius
29
- 8. Status — compute DOF, detect over/under constraint, find redundancies
30
- ```
31
-
32
- ### Constraint definition anatomy
33
-
34
- Every constraint def file exports a `ConstraintDef` with these methods:
35
-
36
- | Method | Called by | Purpose |
37
- |---|---|---|
38
- | `presolve` | Once, before iterations | One-shot initialisation (pin fixed points, snap angles, enforce winding) |
39
- | `solve` | Each GS iteration | Move points to satisfy this constraint (returns error magnitude) |
40
- | `residual` | Each NR iteration | Return equation residual(s) for Gauss-Newton step |
41
- | `computeDof` | Status computation | Declare which points this constraint "touches" for DOF counting |
42
- | `displayPosition` | UI rendering | Where to place the constraint label in the viewport |
43
-
44
- ## The three solver phases, and why we need all of them
45
-
46
- ### Presolve
47
-
48
- Each constraint can optionally run a one-shot setup before any iterations.
49
- The `fixed` constraint uses this to pin points (`pt.fixed = true`). Angle
50
- constraints use it to snap endpoints into the correct half-plane so NR doesn't
51
- start near a degenerate configuration.
52
-
53
- ### GS warm-up (Gauss-Seidel)
54
-
55
- **Why this exists:** Fresh points often start at degenerate positions (all at
56
- `(0, 1)`, creating zero-length lines). Newton-Raphson needs a reasonable
57
- starting point — if the Jacobian is near-singular at the starting position, NR
58
- can't compute a useful step.
59
-
60
- The warm-up runs 5 cheap GS iterations: for each constraint in definition
61
- order, call `solve()` which directly pushes points toward the target. For a
62
- chain of angle-constrained lines, this propagates positions forward:
63
-
64
- ```
65
- GS iteration 1:
66
- absoluteAngle(line1, -90°) → anchors line1.a, moves line1.b downward
67
- absoluteAngle(line2, 0°) → anchors line2.a = line1.b, moves line2.b right
68
- absoluteAngle(line3, 90°) → anchors line3.a = line2.b, moves line3.b up
69
- ...
70
- ```
71
-
72
- After a few passes, all points are near their final positions. NR takes over
73
- for fine convergence.
74
-
75
- ### Newton-Raphson (NR)
76
-
77
- The main solver for precision. Uses numerical Jacobian (forward finite
78
- differences, step = 1e-6) and Gauss-Newton least-squares with Armijo
79
- backtracking line search. All free point coordinates are variables; all
80
- constraint residuals form the equation system.
81
-
82
- NR only runs if **every** constraint defines a `residual()` function.
83
- Constraints with `equations: 0` (like `fixed` and `ccw`) return `[]` from
84
- `residual()` — this is fine; they just don't contribute equations.
85
-
86
- ### GS fallback
87
-
88
- If NR wasn't available (some constraint missing residual) or didn't converge
89
- below tolerance, a full Gauss-Seidel loop runs for up to N iterations.
90
-
91
- ## Key design pitfalls (and how we handle them)
92
-
93
- ### The "modulo-180°" ambiguity
94
-
95
- **Problem:** A line from `a` to `b` has angle `θ` from the positive X-axis.
96
- The same line traversed from `b` to `a` has angle `θ + 180°`. A naïve residual
97
- like `sin(angle − target) = 0` is satisfied at **both** `θ = target` and
98
- `θ = target + 180°`, so the solver can converge to the wrong orientation.
99
-
100
- **Solution:** Use `normalizeAngle(angle − target)` as the residual. This has:
101
- - A unique zero at `angle = target`
102
- - Gradient ≈ 1 everywhere (unlike `1 − cos` which has gradient 0 at the target)
103
- - A discontinuity at `±π`, which `presolve` avoids by snapping points into the
104
- correct half-plane before NR starts.
105
-
106
- We tried two other formulations before landing on this:
107
-
108
- | Residual | Unique zero? | Gradient at target |
109
- |---|---|---|
110
- | `sin(angle − t)` | No (also zero at t+π) | 1 (good) |
111
- | `1 − cos(angle − t)` | Yes | **0** (NR stalls!) |
112
- | `normalizeAngle(angle − t)` | Yes | 1 (good) |
113
-
114
- ### The "reference-count heuristic" for which point to move
115
-
116
- **Problem:** When `absoluteAngle.solve()` runs, it needs to decide which
117
- endpoint to anchor and which to move. If neither point is explicitly `fixed`,
118
- the wrong choice corrupts shared points. Example: the last line in a chain
119
- closes back to a triangle vertex — moving the triangle vertex breaks the
120
- triangle.
121
-
122
- **Solution:** Count how many lines each point appears in (`pointLineRefs`).
123
- The point referenced by more lines is more constrained by the rest of the
124
- system — anchor it, move the other one.
125
-
126
- ```
127
- Line closing back to triangle:
128
- a = chain endpoint (2 line refs) → LESS constrained → MOVE this one
129
- b = triangle vertex (3 line refs) → MORE constrained → ANCHOR this
130
- ```
131
-
132
- ### The "unconditional presolve snap"
133
-
134
- **Problem:** Fresh points often start at `(0, 1)`, creating zero-length lines.
135
- `angleOfLine(a, b)` returns 0 for a zero-length line (atan2(0,0) = 0). If this
136
- bogus angle happens to equal the target, presolve skips the line. Then NR starts
137
- with a degenerate Jacobian (zero-length lines have undefined angular gradients)
138
- and can't converge.
139
-
140
- **Solution:** `absoluteAngle.presolve()` unconditionally snaps the free
141
- endpoint to `(anchor + cos/sin(target) * max(len, 1))`. This ensures every
142
- line starts with a meaningful direction and nonzero length.
143
-
144
- ### The "discrete orientation ambiguity" (CCW constraint)
145
-
146
- **Problem:** An equilateral triangle with a fixed vertex and `absoluteAngle` on
147
- one side has its shape fully determined — except the third vertex can be on
148
- either side of the first line (mirror image). Both solutions satisfy all
149
- continuous constraints. Nothing in the equation-based solver prefers one over
150
- the other.
151
-
152
- **Solution:** The `ccw` constraint (0 equations, presolve + solve only). It
153
- computes the signed polygon area and, if negative (clockwise), reflects the
154
- last non-fixed vertex across the line formed by the first two vertices. Since it
155
- runs in presolve (before NR) and in solve (during GS), the triangle is always
156
- pushed to CCW. NR respects this because its steps are small — it won't swing a
157
- vertex through the opposite side in a single step.
158
-
159
- Usage:
160
- ```js
161
- const t = eqilateralTriangle(origin, sk.point(1, 1), sk.point(0, 5));
162
- sk.ccw(origin, sk.point(1, 1), sk.point(0, 5)); // lock CCW winding
163
- ```
164
-
165
- ## Builder rejection protocol
166
-
167
- When a user calls `sk.absoluteAngle(line, 90)`, the builder doesn't just
168
- record the constraint — it does a **test solve** (30 iterations) to check
169
- feasibility. If `maxError > tolerance × 5`, the constraint is rejected and
170
- stored in `rejectedConstraints[]`. This is why solver convergence from cold
171
- start matters: a constraint that is mathematically satisfiable can still get
172
- rejected if the solver can't reach the solution in 30 iterations from the
173
- initial point positions.
174
-
175
- The GS warm-up phase was added specifically to address this: it gets points
176
- close enough that NR can finish the job within the iteration budget.