forgecad 0.10.2 → 0.10.4

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 (132) hide show
  1. package/README.md +7 -6
  2. package/dist/assets/{AdminPage-CHY6ZN-p.js → AdminPage-B3L3W1Uo.js} +1 -1
  3. package/dist/assets/{BenchmarkPage-BcRT5iGN.js → BenchmarkPage-DXKVXMrJ.js} +2 -2
  4. package/dist/assets/{BlogPage-BssBbnb-.js → BlogPage-B7BWxOCg.js} +1 -1
  5. package/dist/assets/{DocsPage-DsvdiRNK.js → DocsPage-BPGGwht1.js} +28 -48
  6. package/dist/assets/{EditorApp-Bfd3jbtC.js → EditorApp-BWUGCdD5.js} +183 -21
  7. package/dist/assets/{EditorApp-BpjZgzk0.css → EditorApp-C5f24ZN9.css} +8 -0
  8. package/dist/assets/{EmbedViewer-D5t8WamV.js → EmbedViewer-DygByZS2.js} +2 -2
  9. package/dist/assets/{LandingPageProofDriven-DbN7o-Be.js → LandingPageProofDriven-BoVE7JGY.js} +54 -36
  10. package/dist/assets/{LegalPage-DNGrrY0p.js → LegalPage-Din8wv8d.js} +2 -2
  11. package/dist/assets/{PricingPage-Nczr3pRz.js → PricingPage-C2PMzmDc.js} +2 -2
  12. package/dist/assets/{SettingsPage-DZlyu4d4.js → SettingsPage-BlJDCRe8.js} +1 -1
  13. package/dist/assets/{app-C9ct2hRD.js → app-BsRYSfxY.js} +2264 -6259
  14. package/dist/assets/{backendInit-ymjonyQp.js → backendInit-6C0DLgH0.js} +8290 -2136
  15. package/dist/assets/cli/{render-B_0lQwKU.js → render-XXol_ET7.js} +822 -105
  16. package/dist/assets/{constructionHistoryWorker-CZ42Dksy.js → constructionHistoryWorker-cTHWRJEi.js} +699 -284
  17. package/dist/assets/{evalWorker-C2pm8LHP.js → evalWorker-BssDYW9u.js} +2559 -1330
  18. package/dist/assets/{forgecad_geometry-BlMtqluF.js → forgecad_geometry-CZ_IfuvA.js} +1 -9
  19. package/dist/assets/{forgecad_geometry_bg-BllP_WiL.wasm → forgecad_geometry_bg-C3rQHfwg.wasm} +0 -0
  20. package/dist/assets/{inspectWorker-D5T5VbfK.js → inspectWorker-ymhBV4Ll.js} +6254 -671
  21. package/dist/assets/{jointPose-4r8ed8_5.js → jointPose-B0blBj9A.js} +1 -1
  22. package/dist/assets/{landing-proof-driven-ORyigZ6p.css → landing-proof-driven-Cpf-MIbI.css} +73 -13
  23. package/dist/assets/{manifold-5PP1eGLN.js → manifold-B_7QXpGB.js} +1 -1
  24. package/dist/assets/{manifold-DjBkyIc8.js → manifold-CNShmpEJ.js} +1 -1
  25. package/dist/assets/{manifold-C4r6B-XY.js → manifold-CYlIm-M6.js} +2 -2
  26. package/dist/assets/{reportWorker-CwenM7wB.js → reportWorker-Cb5eyM7D.js} +2485 -1275
  27. package/dist/cli/render.html +1 -1
  28. package/dist/docs/index.html +2 -2
  29. package/dist/docs-raw/AI/usage.md +17 -17
  30. package/dist/docs-raw/CLI.md +9 -7
  31. package/dist/docs-raw/README.md +1 -1
  32. package/dist/docs-raw/component-model.md +2 -2
  33. package/dist/docs-raw/generated/assembly.md +1 -1
  34. package/dist/docs-raw/generated/concepts.md +10 -4
  35. package/dist/docs-raw/generated/core.md +96 -1
  36. package/dist/docs-raw/generated/curves.md +8 -1
  37. package/dist/docs-raw/generated/output.md +0 -64
  38. package/dist/docs-raw/generated/runtime-names.md +6 -6
  39. package/dist/docs-raw/generated/viewport.md +3 -12
  40. package/dist/docs-raw/guides/inspection-bundles.md +1 -1
  41. package/dist/docs-raw/simulation-workflow.md +58 -0
  42. package/{dist-skill/website/skills/forgecad-make-a-model.md → dist/docs-raw/skills/forgecad-build-model.md} +18 -8
  43. package/dist/docs-raw/skills/forgecad-design-spec.md +145 -0
  44. package/dist/docs-raw/skills/{forgecad-model-grader.md → forgecad-grade-model.md} +8 -6
  45. package/{dist-skill/website/skills/forgecad-visual-spec.md → dist/docs-raw/skills/forgecad-image-prompt.md} +7 -7
  46. package/dist/docs-raw/skills/{forgecad-render-inspect.md → forgecad-inspect-model.md} +6 -6
  47. package/{dist-skill/website/skills/forgecad-project.md → dist/docs-raw/skills/forgecad-project-sync.md} +5 -5
  48. package/{dist-skill/website/skills/forgecad-3d-reconstruction.md → dist/docs-raw/skills/forgecad-reconstruct-cad-file.md} +7 -7
  49. package/dist/docs-raw/skills/{forgecad-image-replicator.md → forgecad-reconstruct-from-images.md} +12 -12
  50. package/dist/docs-raw/skills/forgecad-verify-mujoco.md +78 -0
  51. package/dist/docs-raw/skills/forgecad.md +24 -24
  52. package/dist/docs-raw/skills/index.md +9 -13
  53. package/dist/index.html +9 -9
  54. package/dist/llms.txt +7 -7
  55. package/dist/sitemap.xml +16 -16
  56. package/dist-cli/{check-compiler-SP7FAL7R.js → check-compiler-4RPB6SB5.js} +1 -1
  57. package/dist-cli/{check-query-propagation-BRLSHP22.js → check-query-propagation-KN3DFQTX.js} +1 -1
  58. package/dist-cli/{chunk-RQQ42YCP.js → chunk-UHBRMYA6.js} +30770 -29253
  59. package/dist-cli/forgecad.js +3277 -237
  60. package/dist-cli/{forgecad_geometry-7TVSNVUB.js → forgecad_geometry-2IMYCUWW.js} +0 -8
  61. package/dist-cli/forgecad_geometry_bg.wasm +0 -0
  62. package/dist-skill/CONTEXT.md +111 -73
  63. package/dist-skill/SKILL.md +1 -1
  64. package/dist-skill/docs/CLI.md +9 -7
  65. package/dist-skill/docs/generated/assembly.md +1 -1
  66. package/dist-skill/docs/generated/core.md +96 -1
  67. package/dist-skill/docs/generated/curves.md +8 -1
  68. package/dist-skill/docs/generated/output.md +0 -64
  69. package/dist-skill/docs/generated/runtime-names.md +6 -6
  70. package/dist-skill/docs/generated/viewport.md +3 -12
  71. package/dist-skill/docs/guides/inspection-bundles.md +1 -1
  72. package/dist-skill/library/README.md +9 -13
  73. package/dist-skill/library/{forgecad-make-a-model → forgecad-build-model}/SKILL.md +16 -6
  74. package/dist-skill/library/forgecad-design-spec/SKILL.md +132 -0
  75. package/dist-skill/library/{forgecad-prepare-prompt → forgecad-design-spec}/references/master-prompt.md +1 -1
  76. package/dist-skill/library/{forgecad-model-grader → forgecad-grade-model}/SKILL.md +6 -4
  77. package/dist-skill/library/forgecad-grade-model/agents/openai.yaml +4 -0
  78. package/dist-skill/library/{forgecad-visual-spec → forgecad-image-prompt}/SKILL.md +5 -5
  79. package/dist-skill/library/forgecad-image-prompt/agents/openai.yaml +4 -0
  80. package/dist-skill/library/{forgecad-render-inspect → forgecad-inspect-model}/SKILL.md +4 -4
  81. package/dist-skill/library/{forgecad-project → forgecad-project-sync}/SKILL.md +3 -3
  82. package/dist-skill/library/{forgecad-3d-reconstruction → forgecad-reconstruct-cad-file}/SKILL.md +5 -5
  83. package/dist-skill/library/forgecad-reconstruct-cad-file/agents/openai.yaml +4 -0
  84. package/dist-skill/library/{forgecad-image-replicator → forgecad-reconstruct-from-images}/SKILL.md +10 -10
  85. package/dist-skill/library/forgecad-reconstruct-from-images/agents/openai.yaml +4 -0
  86. package/dist-skill/library/forgecad-verify-mujoco/SKILL.md +66 -0
  87. package/dist-skill/library/forgecad-verify-mujoco/scripts/mujoco_verify.py +385 -0
  88. package/{dist/docs-raw/skills/forgecad-make-a-model.md → dist-skill/website/skills/forgecad-build-model.md} +18 -8
  89. package/dist-skill/website/skills/forgecad-design-spec.md +145 -0
  90. package/dist-skill/website/skills/{forgecad-model-grader.md → forgecad-grade-model.md} +8 -6
  91. package/{dist/docs-raw/skills/forgecad-visual-spec.md → dist-skill/website/skills/forgecad-image-prompt.md} +7 -7
  92. package/dist-skill/website/skills/{forgecad-render-inspect.md → forgecad-inspect-model.md} +6 -6
  93. package/{dist/docs-raw/skills/forgecad-project.md → dist-skill/website/skills/forgecad-project-sync.md} +5 -5
  94. package/{dist/docs-raw/skills/forgecad-3d-reconstruction.md → dist-skill/website/skills/forgecad-reconstruct-cad-file.md} +7 -7
  95. package/dist-skill/website/skills/{forgecad-image-replicator.md → forgecad-reconstruct-from-images.md} +12 -12
  96. package/dist-skill/website/skills/forgecad-verify-mujoco.md +78 -0
  97. package/dist-skill/website/skills/forgecad.md +24 -24
  98. package/dist-skill/website/skills/index.md +9 -13
  99. package/examples/analysis/clearance-fit.forge.js +31 -0
  100. package/examples/analysis/lever-arm-actuator.forge.js +43 -0
  101. package/examples/analysis/tipping-tripod.forge.js +35 -0
  102. package/examples/api/texture-projection.forge.js +75 -0
  103. package/examples/assets/uv-grid.png +0 -0
  104. package/examples/products/sportscar.forge.js +77 -0
  105. package/package.json +1 -3
  106. package/dist/docs-raw/skills/forgecad-blockout-model.md +0 -49
  107. package/dist/docs-raw/skills/forgecad-component-model.md +0 -53
  108. package/dist/docs-raw/skills/forgecad-high-level-spec.md +0 -101
  109. package/dist/docs-raw/skills/forgecad-lld.md +0 -41
  110. package/dist/docs-raw/skills/forgecad-prepare-prompt.md +0 -63
  111. package/dist/docs-raw/skills/forgecad-reconstruction-benchmark.md +0 -60
  112. package/dist-skill/library/forgecad-3d-reconstruction/agents/openai.yaml +0 -4
  113. package/dist-skill/library/forgecad-blockout-model/SKILL.md +0 -42
  114. package/dist-skill/library/forgecad-component-model/SKILL.md +0 -46
  115. package/dist-skill/library/forgecad-high-level-spec/SKILL.md +0 -94
  116. package/dist-skill/library/forgecad-image-replicator/agents/openai.yaml +0 -4
  117. package/dist-skill/library/forgecad-lld/SKILL.md +0 -34
  118. package/dist-skill/library/forgecad-model-grader/agents/openai.yaml +0 -4
  119. package/dist-skill/library/forgecad-prepare-prompt/SKILL.md +0 -50
  120. package/dist-skill/library/forgecad-reconstruction-benchmark/SKILL.md +0 -48
  121. package/dist-skill/library/forgecad-reconstruction-benchmark/agents/openai.yaml +0 -4
  122. package/dist-skill/library/forgecad-visual-spec/agents/openai.yaml +0 -4
  123. package/dist-skill/website/skills/forgecad-blockout-model.md +0 -49
  124. package/dist-skill/website/skills/forgecad-component-model.md +0 -53
  125. package/dist-skill/website/skills/forgecad-high-level-spec.md +0 -101
  126. package/dist-skill/website/skills/forgecad-lld.md +0 -41
  127. package/dist-skill/website/skills/forgecad-prepare-prompt.md +0 -63
  128. package/dist-skill/website/skills/forgecad-reconstruction-benchmark.md +0 -60
  129. /package/dist/assets/{landing-proof-driven-DiGqdtWa.js → landing-proof-driven-BxZZh5r5.js} +0 -0
  130. /package/dist-skill/library/{forgecad-prepare-prompt → forgecad-design-spec}/references/default-profiles.md +0 -0
  131. /package/dist-skill/library/{forgecad-render-inspect → forgecad-inspect-model}/summarize_manifest.py +0 -0
  132. /package/dist-skill/library/{forgecad-image-replicator → forgecad-reconstruct-from-images}/scripts/compare_images.py +0 -0
@@ -31,13 +31,6 @@ function geometry_chamfer_edges(handle, spec_json) {
31
31
  }
32
32
  return ret[0] >>> 0;
33
33
  }
34
- function geometry_clone(handle) {
35
- const ret = wasm.geometry_clone(handle);
36
- if (ret[2]) {
37
- throw takeFromExternrefTable0(ret[1]);
38
- }
39
- return ret[0] >>> 0;
40
- }
41
34
  function geometry_create_box(width, depth, height) {
42
35
  const ret = wasm.geometry_create_box(width, depth, height);
43
36
  return ret >>> 0;
@@ -889,7 +882,6 @@ export {
889
882
  geometry_boolean,
890
883
  geometry_capabilities,
891
884
  geometry_chamfer_edges,
892
- geometry_clone,
893
885
  geometry_create_box,
894
886
  geometry_create_cylinder,
895
887
  geometry_create_empty,
Binary file
@@ -141,12 +141,12 @@ lib, Line2D, linearPattern, linearPattern2d, loadFont, loft, Loft, mirrorCopy
141
141
  mock, ngon, NurbsCurve3D, NurbsSurface, offsetSolid, param, Param, path
142
142
  Point2D, Points, polygon, polygonVertices, port, Product, ProductPanelBuilder, ProductRibbonBuilder
143
143
  ProductSkin, ProductSkinBuilder, ProductStationBuilder, ProductSurfaceBuilder, ProductSurfaceRef, projectToPlane, queueMicrotask, rect
144
- Rectangle2D, robotExport, roundedRect, Route3D, scene, Sculpt, sdf, SdfShape
145
- selectEdge, selectEdges, self, setActiveBackend, setImmediate, setInterval, setTimeout, Shape
146
- ShapeGroup, sheetMetal, SheetMetalPart, sheetStock, Sim, Sketch, sketchToDxf, sketchToSvg
147
- slot, SolvedAssembly, spec, sphere, spline2d, stroke, Surface, SurfaceBody
148
- SurfaceMembers, sweep, text2d, textWidth, torus, toShape, Transform, union
149
- union2d, variableSweep, verify, Viewport, window, Wood
144
+ Rectangle2D, roundedRect, Route3D, scene, Sculpt, sdf, SdfShape, selectEdge
145
+ selectEdges, self, setActiveBackend, setImmediate, setInterval, setTimeout, Shape, ShapeGroup
146
+ sheetMetal, SheetMetalPart, sheetStock, Sim, Sketch, sketchToDxf, sketchToSvg, slot
147
+ SolvedAssembly, spec, sphere, spline2d, stroke, Surface, SurfaceBody, SurfaceMembers
148
+ sweep, text2d, textWidth, torus, toShape, Transform, union, union2d
149
+ variableSweep, verify, Viewport, window, Wood, Wrap
150
150
  ```
151
151
 
152
152
  `showLabels` is also a runtime global, but it is not part of the top-level collision check. Avoid reusing it unless you intentionally want a local value with that name.
@@ -176,12 +176,17 @@ union2d, variableSweep, verify, Viewport, window, Wood
176
176
  - [SurfacePattern](#surfacepattern)
177
177
  - [Pattern2D](#pattern2d)
178
178
  - [Pattern2DBuilder](#pattern2dbuilder)
179
+ - [Sheet](#sheet)
180
+ - [CurveNetBuilder](#curvenetbuilder)
181
+ - [MatchEdgeBuilder](#matchedgebuilder)
182
+ - [BridgeBuilder](#bridgebuilder)
179
183
  - [ShapeRef](#shaperef)
180
184
  - [ANCHOR3D_NAMES](#anchor3d-names)
181
185
  - [verify](#verify)
182
186
  - [Points](#points)
183
187
  - [connector](#connector)
184
188
  - [Import](#import)
189
+ - [Wrap](#wrap)
185
190
 
186
191
  ## Functions
187
192
 
@@ -860,7 +865,7 @@ Supports transforms (translate, rotate, scale, mirror, transform, rotateAround,
860
865
 
861
866
  Returns a new Shape with the specified material properties merged on top of any previously set properties. All properties are optional — omitted keys retain their current value. Material properties survive transforms and boolean operations.
862
867
 
863
- Use `.color()` to set the base diffuse color; `.material()` controls how that color behaves under light (metalness, roughness, clearcoat) and can add emissive glow independent of lighting. Emissive glow pairs naturally with the `postProcessing.bloom` effect in [`scene()`](/docs/viewport#scene).
868
+ Use `.color()` to set the base diffuse color; `.material()` controls how that color behaves under light (metalness, roughness, clearcoat) and can add emissive glow independent of lighting.
864
869
 
865
870
  ```js
866
871
  box(50, 50, 50).material({ metalness: 0.9, roughness: 0.1 }); // polished metal
@@ -889,6 +894,7 @@ box(100, 100, 10).color('#gold').material({ metalness: 0.95, roughness: 0.05 }).
889
894
  | `specularIntensity?` | `number` | Specular highlight intensity (0–1). |
890
895
  | `specularColor?` | `string` | Specular highlight tint. |
891
896
  | `reflectivity?` | `number` | Reflection strength for supported renderers (0–1). |
897
+ | `texture?` | `{ image: string; projection: UvProjectionSpec; ...` | Projected bitmap texture set by `Shape.wrapTexture`. `image` is a self-contained `data:` URI; `projection` maps each vertex's final world position to (u,v) in the shader, so the texture survives transforms and boolean cuts. `imageWidth`/`imageHeight` are the intrinsic pixel dimensions. |
892
898
 
893
899
  **Face Topology**
894
900
 
@@ -1237,6 +1243,22 @@ Overloads:
1237
1243
 
1238
1244
  **Other**
1239
1245
 
1246
+ #### `wrapTexture(image: ImageHandle, projection: UvProjectionSpec): Shape` — Wrap an imported bitmap image around this shape using a projection.
1247
+
1248
+ The `image` comes from `Import.image('path.png')`; the `projection` is one of the `Wrap.*` helpers — `Wrap.flat({ onto: 'top' })` lays it flat on a face, `Wrap.aroundCylinder({ axis: 'z' })` wraps it like a can label, `Wrap.onSphere()` maps it like a globe, and `Wrap.box()` cube-maps it onto the six sides.
1249
+
1250
+ By default the image **auto-fits** the shape — one copy across the relevant extent, so no `width`/`height`/`size` is needed (pass them only to override). The (u,v) is derived from each vertex's final world position, so the image stays glued to the surface through transforms and boolean cuts with no UV layout to maintain — apply `wrapTexture` *after* positioning the shape. Returns a new Shape; the original is unchanged.
1251
+
1252
+ ```js
1253
+ const logo = Import.image('./logo.png');
1254
+ box(80, 80, 10).wrapTexture(logo, Wrap.flat({ onto: 'top' })); // auto-fits the face
1255
+
1256
+ const label = Import.image('./label.jpg');
1257
+ cylinder(60, 20).wrapTexture(label, Wrap.aroundCylinder({ axis: 'z' })); // wraps the side
1258
+ ```
1259
+
1260
+ `ImageHandle`: `{ __forgeImage: true, dataUri: string, width: number, height: number, mimeType: string, byteLength: number }`
1261
+
1240
1262
  #### `clone(): Shape` — Return a new Shape wrapper for explicit duplication in scripts.
1241
1263
 
1242
1264
  #### `geometryInfo(): GeometryInfo` — Inspect which backend/representation produced this solid.
@@ -1485,6 +1507,71 @@ const bracket = group(
1485
1507
  | `depth?` | `number` | Thread groove depth in millimeters. Default: 0.8. |
1486
1508
  | `underScale?` | `number` | Relative height of the under-crossing thread. Default: 0.15. |
1487
1509
 
1510
+ ### `Sheet`
1511
+
1512
+ A parametric open surface value (control grid + knots + analytic differential geometry).
1513
+
1514
+ **Properties:**
1515
+
1516
+ | Property | Type | Description |
1517
+ |----------|------|-------------|
1518
+ | `surface` | `BSplineSurface` | — |
1519
+
1520
+ **Methods:**
1521
+
1522
+ #### `get frontEdge(): SheetEdge` — Edge naming follows parameter direction (documented): front=v0, rear=v1, left=u0, right=u1.
1523
+
1524
+ #### `thicken(wall: number, options?: { resolution?: number; }): Shape` — Offset the sheet along its analytic normals into a watertight solid shell of the given wall thickness. Throws if the wall would self-intersect on a concave region (no silent degenerate solid).
1525
+
1526
+ #### `matchEdge(edge: SheetEdge): MatchEdgeBuilder` — Per-edge continuity match against a neighbor (returns a NEW Sheet).
1527
+
1528
+ **`SheetEdge`**
1529
+ - `fixed: "u" | "v"` — Which parameter is held fixed along this edge.
1530
+ - `value: 0 | 1` — The fixed value (0 or 1).
1531
+ - Also: `sheet: Sheet`.
1532
+
1533
+ - `get rearEdge(): SheetEdge`
1534
+ - `get leftEdge(): SheetEdge`
1535
+ - `get rightEdge(): SheetEdge`
1536
+ - `pointAt(u: number, v: number): Vec3`
1537
+ - `normalAt(u: number, v: number): Vec3`
1538
+ - `curvatureAt(u: number, v: number): SurfaceCurvature`
1539
+
1540
+ ### `CurveNetBuilder`
1541
+
1542
+ #### `toSheet(): Sheet` — Build (once) and return the Sheet.
1543
+
1544
+ - `lengthwise(...curves: CurveInput[]): this`
1545
+ - `crosswise(...curves: CurveInput[]): this`
1546
+ - `alongRails(railA: CurveInput, railB: CurveInput): this`
1547
+ - `sections(...curves: CurveInput[]): this`
1548
+ - `cage(grid: Vec3[][]): this`
1549
+ - `degree(u: number, v: number): this`
1550
+ - `get frontEdge(): SheetEdge`
1551
+ - `get rearEdge(): SheetEdge`
1552
+ - `get leftEdge(): SheetEdge`
1553
+ - `get rightEdge(): SheetEdge`
1554
+ - `get surface(): BSplineSurface`
1555
+ - `pointAt(u: number, v: number): Vec3`
1556
+ - `normalAt(u: number, v: number): Vec3`
1557
+ - `curvatureAt(u: number, v: number): SurfaceCurvature`
1558
+ - `thicken(wall: number, options?: { resolution?: number; }): Shape`
1559
+ - `matchEdge(edge: SheetEdge): MatchEdgeBuilder`
1560
+
1561
+ ### `MatchEdgeBuilder`
1562
+
1563
+ - `toG0(neighbor: SheetEdge): Sheet`
1564
+ - `toG1(neighbor: SheetEdge): Sheet`
1565
+ - `toG2(neighbor: SheetEdge): Sheet`
1566
+
1567
+ ### `BridgeBuilder`
1568
+
1569
+ #### `bulge(a: number, b: number): this` — Tune the influence of each side (Rhino-style bulge).
1570
+
1571
+ - `g0(): Sheet`
1572
+ - `g1(): Sheet`
1573
+ - `g2(): Sheet`
1574
+
1488
1575
  ### `ShapeRef`
1489
1576
 
1490
1577
  A first-class reference path over a shape's semantic faces and face relationships.
@@ -1577,6 +1664,14 @@ Namespaced file-format import helpers — the single vocabulary for bringing ext
1577
1664
  const yUpPart = Import.mesh("./part.obj", { sourceFrame: { up: "+Y" } });
1578
1665
  ```
1579
1666
  - `step(fileName: string, options?: StepImportOptions): Shape` — Import a STEP file (.step, .stp) as an exact OCCT-backed Shape. Preserves NURBS curves, B-spline surfaces, and exact topology. Requires running with the OCCT backend. Use `sourceFrame: { up: "+Y" }` to rotate Y-up source files into ForgeCAD's Z-up world.
1667
+ - `image(fileName: string): ImageHandle` — Import a bitmap image (PNG, JPEG, or WebP) as an ImageHandle for projected texturing. Reads the pixel dimensions from the file header and embeds the bytes as a data URI. Pass the result to `Shape.wrapTexture(image, projection)` with a `Wrap.*` projection.
1668
+
1669
+ ### `Wrap`
1670
+
1671
+ - `flat(opts: FlatWrapOptions): UvProjectionSpec` — Project the image flat onto an axis-aligned face — `onto` is one of top/bottom/front/back/left/right. Auto-fits the face (no width/height needed).
1672
+ - `aroundCylinder(opts: CylinderWrapOptions): UvProjectionSpec` — Wrap the image around a cylinder like a can label — `axis` is 'x' | 'y' | 'z'. Auto-fits one wrap around and the full height.
1673
+ - `onSphere(opts?: SphereWrapOptions): UvProjectionSpec` — Map the image over a sphere like a globe (longitude/latitude). Auto-centers on the sphere.
1674
+ - `box(opts?: BoxWrapOptions): UvProjectionSpec` — Cube-map the image onto a box — one copy per face. Auto-fits the box (no size needed).
1580
1675
 
1581
1676
  ---
1582
1677
 
@@ -3025,7 +3120,11 @@ const outlet = route.port("outlet");
3025
3120
 
3026
3121
  #### `pointAt(u: number, v: number): Vec3` — Evaluate the surface at parameters (u, v) ∈ [0, 1]². Uses tensor product evaluation: evaluate basis functions in U and V independently.
3027
3122
 
3028
- #### `normalAt(u: number, v: number): Vec3` — Evaluate the surface normal at (u, v) via cross product of partial derivatives.
3123
+ #### `normalAt(u: number, v: number): Vec3` — Evaluate the surface unit normal at (u, v) from analytic first derivatives.
3124
+
3125
+ Uses Algorithm A2.3 basis-function derivatives with the rational quotient rule, so the normal is exact (no finite-difference epsilon, no error near the boundary). Constant chain-rule factors from the parameter remap scale Su and Sv positively and cancel under normalization, so they are omitted.
3126
+
3127
+ #### `derivativesAt(u: number, v: number): { S: Vec3; Su: Vec3; Sv: Vec3; }` — Analytic first partial derivatives S_u, S_v (rational quotient rule).
3029
3128
 
3030
3129
  #### `tessellate(resU?: number, resV?: number): { positions: Vec3[]; normals: Vec3[]; indices: number[]; }` — Tessellate the surface into a triangle mesh. Returns positions, normals, and triangle indices.
3031
3130
 
@@ -3764,16 +3863,19 @@ Members (full entries under [Curves & Surfacing](#curves-surfacing)): `Curve.Ble
3764
3863
  - `Trim(shape: Shape, tool: Shape | SurfacePlaneOp): Shape`
3765
3864
  - `Split(shape: Shape, tool: Shape | SurfacePlaneOp): [ Shape, Shape ]`
3766
3865
  - `Match(shape: Shape, options: { edge: "u0" | "u1" | "v0" | "v1"; target: EdgeRef; continuity?: SurfaceContinuity; }): Shape`
3866
+ - `Net(): CurveNet` — Begin a curve-network (Gordon) surface — the class-A keystone. Chain `.lengthwise(...)/.crosswise(...)` (or `.alongRails(a,b).sections(...)`, or `.cage(grid)`), then `.thicken(wall)` to get a solid Shape. Returns a fluent [`Sheet`](/docs/core#sheet) builder with analytic point/normal/curvature queries and named edges.
3767
3867
 
3768
3868
  ### `Blend`
3769
3869
 
3770
3870
  - `Edge(options: BlendEdgeOptions): Shape`
3771
3871
  - `Surface(options: BlendSurfaceOptions): Shape`
3872
+ - `Bridge(edgeA: SheetEdge, edgeB: SheetEdge): BridgeBuilder` — Build a transition strip between two `Surface.Net` sheet edges. Chain `.bulge(a, b)` then `.g0()/.g1()/.g2()` for the continuity order. Returns a [`Sheet`](/docs/core#sheet); verify the seam with `Analysis.EdgeMatch`.
3772
3873
 
3773
3874
  ### `Analysis`
3774
3875
 
3775
3876
  - `EdgeContinuity(shape: Shape, options?: EdgeContinuityThresholds): EdgeContinuityReport`
3776
3877
  - `SurfaceContinuity(shape: Shape, options?: EdgeContinuityThresholds): EdgeContinuityReport`
3878
+ - `EdgeMatch(edgeA: SheetEdge, edgeB: SheetEdge, options?: { samples?: number; }): ContinuityReport` — Measure G0/G1/G2 agreement between two `Surface.Net` sheet edges: worst position gap, cross-boundary tangent angle (0 = G1), and normal-curvature mismatch (0 = G2). The reflection/fairness check for matched panel seams.
3777
3879
  - `CurvatureComb(input: NurbsCurve3D | EdgeRef, options?: { samples?: number; }): CurvatureSample[]`
3778
3880
  - `SurfaceHealth(shape: Shape, options?: { tinyEdgeThreshold?: number; sliverThreshold?: number; }): SurfaceHealthReport`
3779
3881
  - `BRepValidity(shape: Shape, options?: BRepValidityOptions): BRepValidityReport` — Validate B-rep/shell/solid structure and return closedness, manifoldness, orientation, and issue diagnostics.
@@ -4226,7 +4328,7 @@ return mech.solve({ theta: 45 });
4226
4328
 
4227
4329
  #### `withSimulation(options: SimAssemblySimulationOptions): Assembly` — Attach the root simulation contract for this assembly.
4228
4330
 
4229
- Use this after adding physical parts and joints. Robot-body profiles require `rootPart`; asset profiles can describe one-part or multi-part physical assets. URDF/SDF exporters and `forgecad check simready` read this contract directly, so model files no longer need a separate `robotExport(...)` side effect.
4331
+ Use this after adding physical parts and joints. Robot-body profiles require `rootPart`; asset profiles can describe one-part or multi-part physical assets. URDF/SDF/MJCF/USD exporters and `forgecad check simready` read this contract directly from the returned assembly.
4230
4332
 
4231
4333
  `SimAssemblySimulationOptions`: `{ profile: SimProfileDef, rootPart?: string, controllers?: SimControllerDef[] }`
4232
4334
 
@@ -4498,70 +4600,6 @@ bom(tubeLen, "rectangular steel tube", {
4498
4600
  | `notes?` | `string` | Free-form notes |
4499
4601
  | `grain?` | `string` | Wood grain direction, e.g. "long", "cross" |
4500
4602
 
4501
- #### `robotExport(options: RobotExportOptions): CollectedRobotExport` — Compatibility shim for SDF/URDF robot package metadata.
4502
-
4503
- Prefer returning `assembly(...).withSimulation(...)` with `Sim.body(...)`, `Sim.drive.*(...)`, and `Sim.controller.*(...)` metadata. The CLI commands `forgecad export sdf` and `forgecad export urdf` now read that assembly simulation contract directly.
4504
-
4505
- `robotExport()` remains available for one compatibility window. It converts the legacy descriptor into the same internal simulation model used by source-authored `Sim` metadata and produces a robot package with:
4506
-
4507
- - Mesh-based inertia tensors (full 6-component, not bounding-box approximations)
4508
- - Separate collision meshes
4509
- - Joint limits, effort/velocity/damping/friction metadata from assembly joints
4510
-
4511
- **Collision mesh modes** (set per-link via `links["PartName"].collision`):
4512
-
4513
- | Mode | Description | Default |
4514
- |------|-------------|---------|
4515
- | `'convex'` | Convex hull (separate `_collision.stl`) | Yes |
4516
- | `'box'` | AABB primitive — fastest physics | |
4517
- | `'visual'` | Same mesh as visual — exact but slow | |
4518
- | `'none'` | No collision geometry | |
4519
-
4520
- **Unit conventions:**
4521
-
4522
- - Revolute `velocity` is in degrees/second in Forge; exporters convert to rad/s.
4523
- - Prismatic distances are in mm in Forge; exported in meters.
4524
- - `massKg` is preferred; `densityKgM3` is used when mass is unknown.
4525
- - Compatibility coupling metadata, when present, maps only the primary term (largest ratio) to `<mimic>` — SDF/URDF support single-leader mimic only. Dropped terms emit a warning.
4526
-
4527
- **Legacy example**
4528
-
4529
- ```ts
4530
- robotExport({
4531
- assembly: rover, // assembly() with parts + revolute wheel joints
4532
- modelName: "Scout",
4533
- links: { Chassis: { massKg: 10 }, "Left Wheel": { massKg: 0.8 } },
4534
- plugins: {
4535
- diffDrive: {
4536
- leftJoints: ["leftWheel"], rightJoints: ["rightWheel"],
4537
- wheelSeparationMm: 280, wheelRadiusMm: 60,
4538
- },
4539
- },
4540
- world: { generateDemoWorld: true },
4541
- });
4542
- ```
4543
-
4544
- **Preferred CLI usage**
4545
-
4546
- ```bash
4547
- forgecad export sdf model.forge.js # SDF package (Gazebo/Ignition)
4548
- forgecad export urdf model.forge.js # URDF package (ROS/PyBullet/MuJoCo)
4549
- ```
4550
-
4551
- **`RobotExportOptions`**: `assembly: Assembly`, `modelName?: string`, `state?: JointState`, `static?: boolean`, `selfCollide?: boolean`, `allowAutoDisable?: boolean`, `links?: Record<string, RobotLinkExportOptions>`, `joints?: Record<string, RobotJointExportOptions>`, `plugins?: { diffDrive?: RobotDiffDrivePluginOptions; jointStatePublisher?: RobotJointStatePublisherOptions; }`, `world?: RobotWorldOptions`
4552
-
4553
- `RobotLinkExportOptions`: `{ massKg?: number, densityKgM3?: number, collision?: "visual" | "convex" | "box" | "none" }`
4554
-
4555
- `RobotJointExportOptions`: `{ effort?: number, velocity?: number, damping?: number, friction?: number }`
4556
-
4557
- **`RobotDiffDrivePluginOptions`**: `leftJoints: string[]`, `rightJoints: string[]`, `wheelSeparationMm: number`, `wheelRadiusMm: number`, `topic?: string`, `odomTopic?: string`, `tfTopic?: string`, `frameId?: string`, `odomFrameId?: string`, `maxLinearVelocity?: number`, `maxAngularVelocity?: number`, `linearAcceleration?: number`, `angularAcceleration?: number`
4558
-
4559
- `RobotJointStatePublisherOptions`: `{ enabled?: boolean, joints?: string[], topic?: string, updateRate?: number }`
4560
-
4561
- `RobotWorldOptions`: `{ name?: string, generateDemoWorld?: boolean, spawnPose?: RobotPose6, keyboardTeleop?: RobotWorldKeyboardTeleopOptions }`
4562
-
4563
- `RobotWorldKeyboardTeleopOptions`: `{ enabled?: boolean, linearStep?: number, angularStep?: number }`
4564
-
4565
4603
  #### `dim()` — Add a dimension annotation between two points, or along an entity.
4566
4604
 
4567
4605
  Overloads:
@@ -65,7 +65,7 @@ Smooth curves, Hermite splines, lofted and swept solids. For straps, inlays, gua
65
65
 
66
66
  ### 5. Assemblies and Mechanisms (for joints or kinematics)
67
67
 
68
- Assembly graph, joint types, couplings, validation, robot export.
68
+ Assembly graph, joint types, couplings, validation, and simulation export.
69
69
 
70
70
  - `{{SKILL_DIR}}/docs/generated/assembly.md`
71
71
 
@@ -265,7 +265,7 @@ forgecad render section model.forge.js --output out/section.svg --plane XZ --off
265
265
 
266
266
  ### Cross-cutting flags
267
267
 
268
- These flags work across run / render / capture / inspect commands:
268
+ These flags work across script-backed run, render, export, capture, inspect, and check commands that evaluate `.forge.js` files. Use `--param Key=Value` for parameter overrides; repeat it for multiple parameters.
269
269
 
270
270
  | Option | Description |
271
271
  |--------|-------------|
@@ -306,39 +306,41 @@ Export to every format you need.
306
306
 
307
307
  ```bash
308
308
  # Sheet material
309
- forgecad cut-list shelf.forge.js
309
+ forgecad cut-list shelf.forge.js --param Material=plywood
310
310
  forgecad export cutting-layout shelf.forge.js --sheet-width 420 --sheet-height 594 --kerf 3
311
311
  forgecad export cutting-layout shelf.forge.js --output out/layout.dxf
312
312
 
313
313
  # 3D printing
314
314
  forgecad check print bracket.forge.js
315
- forgecad export stl bracket.forge.js
315
+ forgecad export stl bracket.forge.js --param Width=42
316
316
  forgecad export 3mf bracket.forge.js --quality high
317
317
 
318
318
  # CAD interchange
319
- forgecad export step bracket.forge.js
319
+ forgecad export step bracket.forge.js --param Width=42
320
320
 
321
321
  # Technical drawings
322
322
  forgecad export report bracket.forge.js --output out/report.pdf
323
323
 
324
324
  # Robot simulation
325
325
  forgecad export sdf rover.forge.js --output out/forge_scout
326
- forgecad export mjcf rover.forge.js --output out/forge_scout_mjcf
326
+ forgecad export mjcf rover.forge.js --param Wheelbase=180 --output out/forge_scout_mjcf
327
327
  forgecad export usd rover.forge.js --output out/forge_scout_usd
328
328
  ```
329
329
 
330
+ Script-backed exports accept repeatable `--param Key=Value` overrides before the model is evaluated. The MuJoCo/MJX package export command is `export mjcf`.
331
+
330
332
  <details>
331
333
  <summary>Export flags</summary>
332
334
 
333
335
  | Option | Description |
334
336
  |--------|-------------|
337
+ | `--param <Key=Value>` | Override a parameter value (Key=Value). Repeatable. |
338
+ | `-p <Key=Value>` | Shorthand for --param |
335
339
  | `--joint <JointName=Value>` | Override a Motion tab joint value (JointName=Value). Repeatable. |
336
340
  | `--output <path>` | Output SVG path for a single input |
337
341
  | `--backend <occt\|truck>` | Exact BREP exporter: occt (default) or truck (native analytic kernel) |
338
342
  | `--quality <default\|live\|high>` | Forge quality preset |
339
343
  | `-o <path>` | Shorthand for --output |
340
- | `--param <Key=Value>` | Override a parameter value (Key=Value). Repeatable. |
341
- | `-p <Key=Value>` | Shorthand for --param |
342
344
  | `--format <json\|webgpu-brick>` | Implicit artifact format |
343
345
  | `-q <live\|default\|high>` | Shorthand for --quality |
344
346
  | `--workgroup-size <x>x<y>x<z>` | WebGPU compute workgroup size |
@@ -356,7 +356,7 @@ return mech.solve({ theta: 45 });
356
356
 
357
357
  #### `withSimulation(options: SimAssemblySimulationOptions): Assembly` — Attach the root simulation contract for this assembly.
358
358
 
359
- Use this after adding physical parts and joints. Robot-body profiles require `rootPart`; asset profiles can describe one-part or multi-part physical assets. URDF/SDF exporters and `forgecad check simready` read this contract directly, so model files no longer need a separate `robotExport(...)` side effect.
359
+ Use this after adding physical parts and joints. Robot-body profiles require `rootPart`; asset profiles can describe one-part or multi-part physical assets. URDF/SDF/MJCF/USD exporters and `forgecad check simready` read this contract directly from the returned assembly.
360
360
 
361
361
  `SimAssemblySimulationOptions`: `{ profile: SimProfileDef, rootPart?: string, controllers?: SimControllerDef[] }`
362
362
 
@@ -24,12 +24,17 @@ skill-order: 100
24
24
  - [SurfacePattern](#surfacepattern)
25
25
  - [Pattern2D](#pattern2d)
26
26
  - [Pattern2DBuilder](#pattern2dbuilder)
27
+ - [Sheet](#sheet)
28
+ - [CurveNetBuilder](#curvenetbuilder)
29
+ - [MatchEdgeBuilder](#matchedgebuilder)
30
+ - [BridgeBuilder](#bridgebuilder)
27
31
  - [ShapeRef](#shaperef)
28
32
  - [ANCHOR3D_NAMES](#anchor3d-names)
29
33
  - [verify](#verify)
30
34
  - [Points](#points)
31
35
  - [connector](#connector)
32
36
  - [Import](#import)
37
+ - [Wrap](#wrap)
33
38
 
34
39
  ## Functions
35
40
 
@@ -708,7 +713,7 @@ Supports transforms (translate, rotate, scale, mirror, transform, rotateAround,
708
713
 
709
714
  Returns a new Shape with the specified material properties merged on top of any previously set properties. All properties are optional — omitted keys retain their current value. Material properties survive transforms and boolean operations.
710
715
 
711
- Use `.color()` to set the base diffuse color; `.material()` controls how that color behaves under light (metalness, roughness, clearcoat) and can add emissive glow independent of lighting. Emissive glow pairs naturally with the `postProcessing.bloom` effect in [`scene()`](/docs/viewport#scene).
716
+ Use `.color()` to set the base diffuse color; `.material()` controls how that color behaves under light (metalness, roughness, clearcoat) and can add emissive glow independent of lighting.
712
717
 
713
718
  ```js
714
719
  box(50, 50, 50).material({ metalness: 0.9, roughness: 0.1 }); // polished metal
@@ -737,6 +742,7 @@ box(100, 100, 10).color('#gold').material({ metalness: 0.95, roughness: 0.05 }).
737
742
  | `specularIntensity?` | `number` | Specular highlight intensity (0–1). |
738
743
  | `specularColor?` | `string` | Specular highlight tint. |
739
744
  | `reflectivity?` | `number` | Reflection strength for supported renderers (0–1). |
745
+ | `texture?` | `{ image: string; projection: UvProjectionSpec; ...` | Projected bitmap texture set by `Shape.wrapTexture`. `image` is a self-contained `data:` URI; `projection` maps each vertex's final world position to (u,v) in the shader, so the texture survives transforms and boolean cuts. `imageWidth`/`imageHeight` are the intrinsic pixel dimensions. |
740
746
 
741
747
  **Face Topology**
742
748
 
@@ -1085,6 +1091,22 @@ Overloads:
1085
1091
 
1086
1092
  **Other**
1087
1093
 
1094
+ #### `wrapTexture(image: ImageHandle, projection: UvProjectionSpec): Shape` — Wrap an imported bitmap image around this shape using a projection.
1095
+
1096
+ The `image` comes from `Import.image('path.png')`; the `projection` is one of the `Wrap.*` helpers — `Wrap.flat({ onto: 'top' })` lays it flat on a face, `Wrap.aroundCylinder({ axis: 'z' })` wraps it like a can label, `Wrap.onSphere()` maps it like a globe, and `Wrap.box()` cube-maps it onto the six sides.
1097
+
1098
+ By default the image **auto-fits** the shape — one copy across the relevant extent, so no `width`/`height`/`size` is needed (pass them only to override). The (u,v) is derived from each vertex's final world position, so the image stays glued to the surface through transforms and boolean cuts with no UV layout to maintain — apply `wrapTexture` *after* positioning the shape. Returns a new Shape; the original is unchanged.
1099
+
1100
+ ```js
1101
+ const logo = Import.image('./logo.png');
1102
+ box(80, 80, 10).wrapTexture(logo, Wrap.flat({ onto: 'top' })); // auto-fits the face
1103
+
1104
+ const label = Import.image('./label.jpg');
1105
+ cylinder(60, 20).wrapTexture(label, Wrap.aroundCylinder({ axis: 'z' })); // wraps the side
1106
+ ```
1107
+
1108
+ `ImageHandle`: `{ __forgeImage: true, dataUri: string, width: number, height: number, mimeType: string, byteLength: number }`
1109
+
1088
1110
  #### `clone(): Shape` — Return a new Shape wrapper for explicit duplication in scripts.
1089
1111
 
1090
1112
  #### `geometryInfo(): GeometryInfo` — Inspect which backend/representation produced this solid.
@@ -1333,6 +1355,71 @@ const bracket = group(
1333
1355
  | `depth?` | `number` | Thread groove depth in millimeters. Default: 0.8. |
1334
1356
  | `underScale?` | `number` | Relative height of the under-crossing thread. Default: 0.15. |
1335
1357
 
1358
+ ### `Sheet`
1359
+
1360
+ A parametric open surface value (control grid + knots + analytic differential geometry).
1361
+
1362
+ **Properties:**
1363
+
1364
+ | Property | Type | Description |
1365
+ |----------|------|-------------|
1366
+ | `surface` | `BSplineSurface` | — |
1367
+
1368
+ **Methods:**
1369
+
1370
+ #### `get frontEdge(): SheetEdge` — Edge naming follows parameter direction (documented): front=v0, rear=v1, left=u0, right=u1.
1371
+
1372
+ #### `thicken(wall: number, options?: { resolution?: number; }): Shape` — Offset the sheet along its analytic normals into a watertight solid shell of the given wall thickness. Throws if the wall would self-intersect on a concave region (no silent degenerate solid).
1373
+
1374
+ #### `matchEdge(edge: SheetEdge): MatchEdgeBuilder` — Per-edge continuity match against a neighbor (returns a NEW Sheet).
1375
+
1376
+ **`SheetEdge`**
1377
+ - `fixed: "u" | "v"` — Which parameter is held fixed along this edge.
1378
+ - `value: 0 | 1` — The fixed value (0 or 1).
1379
+ - Also: `sheet: Sheet`.
1380
+
1381
+ - `get rearEdge(): SheetEdge`
1382
+ - `get leftEdge(): SheetEdge`
1383
+ - `get rightEdge(): SheetEdge`
1384
+ - `pointAt(u: number, v: number): Vec3`
1385
+ - `normalAt(u: number, v: number): Vec3`
1386
+ - `curvatureAt(u: number, v: number): SurfaceCurvature`
1387
+
1388
+ ### `CurveNetBuilder`
1389
+
1390
+ #### `toSheet(): Sheet` — Build (once) and return the Sheet.
1391
+
1392
+ - `lengthwise(...curves: CurveInput[]): this`
1393
+ - `crosswise(...curves: CurveInput[]): this`
1394
+ - `alongRails(railA: CurveInput, railB: CurveInput): this`
1395
+ - `sections(...curves: CurveInput[]): this`
1396
+ - `cage(grid: Vec3[][]): this`
1397
+ - `degree(u: number, v: number): this`
1398
+ - `get frontEdge(): SheetEdge`
1399
+ - `get rearEdge(): SheetEdge`
1400
+ - `get leftEdge(): SheetEdge`
1401
+ - `get rightEdge(): SheetEdge`
1402
+ - `get surface(): BSplineSurface`
1403
+ - `pointAt(u: number, v: number): Vec3`
1404
+ - `normalAt(u: number, v: number): Vec3`
1405
+ - `curvatureAt(u: number, v: number): SurfaceCurvature`
1406
+ - `thicken(wall: number, options?: { resolution?: number; }): Shape`
1407
+ - `matchEdge(edge: SheetEdge): MatchEdgeBuilder`
1408
+
1409
+ ### `MatchEdgeBuilder`
1410
+
1411
+ - `toG0(neighbor: SheetEdge): Sheet`
1412
+ - `toG1(neighbor: SheetEdge): Sheet`
1413
+ - `toG2(neighbor: SheetEdge): Sheet`
1414
+
1415
+ ### `BridgeBuilder`
1416
+
1417
+ #### `bulge(a: number, b: number): this` — Tune the influence of each side (Rhino-style bulge).
1418
+
1419
+ - `g0(): Sheet`
1420
+ - `g1(): Sheet`
1421
+ - `g2(): Sheet`
1422
+
1336
1423
  ### `ShapeRef`
1337
1424
 
1338
1425
  A first-class reference path over a shape's semantic faces and face relationships.
@@ -1425,3 +1512,11 @@ Namespaced file-format import helpers — the single vocabulary for bringing ext
1425
1512
  const yUpPart = Import.mesh("./part.obj", { sourceFrame: { up: "+Y" } });
1426
1513
  ```
1427
1514
  - `step(fileName: string, options?: StepImportOptions): Shape` — Import a STEP file (.step, .stp) as an exact OCCT-backed Shape. Preserves NURBS curves, B-spline surfaces, and exact topology. Requires running with the OCCT backend. Use `sourceFrame: { up: "+Y" }` to rotate Y-up source files into ForgeCAD's Z-up world.
1515
+ - `image(fileName: string): ImageHandle` — Import a bitmap image (PNG, JPEG, or WebP) as an ImageHandle for projected texturing. Reads the pixel dimensions from the file header and embeds the bytes as a data URI. Pass the result to `Shape.wrapTexture(image, projection)` with a `Wrap.*` projection.
1516
+
1517
+ ### `Wrap`
1518
+
1519
+ - `flat(opts: FlatWrapOptions): UvProjectionSpec` — Project the image flat onto an axis-aligned face — `onto` is one of top/bottom/front/back/left/right. Auto-fits the face (no width/height needed).
1520
+ - `aroundCylinder(opts: CylinderWrapOptions): UvProjectionSpec` — Wrap the image around a cylinder like a can label — `axis` is 'x' | 'y' | 'z'. Auto-fits one wrap around and the full height.
1521
+ - `onSphere(opts?: SphereWrapOptions): UvProjectionSpec` — Map the image over a sphere like a globe (longitude/latitude). Auto-centers on the sphere.
1522
+ - `box(opts?: BoxWrapOptions): UvProjectionSpec` — Cube-map the image onto a box — one copy per face. Auto-fits the box (no size needed).
@@ -426,7 +426,11 @@ const outlet = route.port("outlet");
426
426
 
427
427
  #### `pointAt(u: number, v: number): Vec3` — Evaluate the surface at parameters (u, v) ∈ [0, 1]². Uses tensor product evaluation: evaluate basis functions in U and V independently.
428
428
 
429
- #### `normalAt(u: number, v: number): Vec3` — Evaluate the surface normal at (u, v) via cross product of partial derivatives.
429
+ #### `normalAt(u: number, v: number): Vec3` — Evaluate the surface unit normal at (u, v) from analytic first derivatives.
430
+
431
+ Uses Algorithm A2.3 basis-function derivatives with the rational quotient rule, so the normal is exact (no finite-difference epsilon, no error near the boundary). Constant chain-rule factors from the parameter remap scale Su and Sv positively and cancel under normalization, so they are omitted.
432
+
433
+ #### `derivativesAt(u: number, v: number): { S: Vec3; Su: Vec3; Sv: Vec3; }` — Analytic first partial derivatives S_u, S_v (rational quotient rule).
430
434
 
431
435
  #### `tessellate(resU?: number, resV?: number): { positions: Vec3[]; normals: Vec3[]; indices: number[]; }` — Tessellate the surface into a triangle mesh. Returns positions, normals, and triangle indices.
432
436
 
@@ -1165,16 +1169,19 @@ Members (full entries under [Curves & Surfacing](#curves-surfacing)): `Curve.Ble
1165
1169
  - `Trim(shape: Shape, tool: Shape | SurfacePlaneOp): Shape`
1166
1170
  - `Split(shape: Shape, tool: Shape | SurfacePlaneOp): [ Shape, Shape ]`
1167
1171
  - `Match(shape: Shape, options: { edge: "u0" | "u1" | "v0" | "v1"; target: EdgeRef; continuity?: SurfaceContinuity; }): Shape`
1172
+ - `Net(): CurveNet` — Begin a curve-network (Gordon) surface — the class-A keystone. Chain `.lengthwise(...)/.crosswise(...)` (or `.alongRails(a,b).sections(...)`, or `.cage(grid)`), then `.thicken(wall)` to get a solid Shape. Returns a fluent [`Sheet`](/docs/core#sheet) builder with analytic point/normal/curvature queries and named edges.
1168
1173
 
1169
1174
  ### `Blend`
1170
1175
 
1171
1176
  - `Edge(options: BlendEdgeOptions): Shape`
1172
1177
  - `Surface(options: BlendSurfaceOptions): Shape`
1178
+ - `Bridge(edgeA: SheetEdge, edgeB: SheetEdge): BridgeBuilder` — Build a transition strip between two `Surface.Net` sheet edges. Chain `.bulge(a, b)` then `.g0()/.g1()/.g2()` for the continuity order. Returns a [`Sheet`](/docs/core#sheet); verify the seam with `Analysis.EdgeMatch`.
1173
1179
 
1174
1180
  ### `Analysis`
1175
1181
 
1176
1182
  - `EdgeContinuity(shape: Shape, options?: EdgeContinuityThresholds): EdgeContinuityReport`
1177
1183
  - `SurfaceContinuity(shape: Shape, options?: EdgeContinuityThresholds): EdgeContinuityReport`
1184
+ - `EdgeMatch(edgeA: SheetEdge, edgeB: SheetEdge, options?: { samples?: number; }): ContinuityReport` — Measure G0/G1/G2 agreement between two `Surface.Net` sheet edges: worst position gap, cross-boundary tangent angle (0 = G1), and normal-curvature mismatch (0 = G2). The reflection/fairness check for matched panel seams.
1178
1185
  - `CurvatureComb(input: NurbsCurve3D | EdgeRef, options?: { samples?: number; }): CurvatureSample[]`
1179
1186
  - `SurfaceHealth(shape: Shape, options?: { tinyEdgeThreshold?: number; sliverThreshold?: number; }): SurfaceHealthReport`
1180
1187
  - `BRepValidity(shape: Shape, options?: BRepValidityOptions): BRepValidityReport` — Validate B-rep/shell/solid structure and return closedness, manifoldness, orientation, and issue diagnostics.
@@ -58,70 +58,6 @@ bom(tubeLen, "rectangular steel tube", {
58
58
  | `notes?` | `string` | Free-form notes |
59
59
  | `grain?` | `string` | Wood grain direction, e.g. "long", "cross" |
60
60
 
61
- #### `robotExport(options: RobotExportOptions): CollectedRobotExport` — Compatibility shim for SDF/URDF robot package metadata.
62
-
63
- Prefer returning `assembly(...).withSimulation(...)` with `Sim.body(...)`, `Sim.drive.*(...)`, and `Sim.controller.*(...)` metadata. The CLI commands `forgecad export sdf` and `forgecad export urdf` now read that assembly simulation contract directly.
64
-
65
- `robotExport()` remains available for one compatibility window. It converts the legacy descriptor into the same internal simulation model used by source-authored `Sim` metadata and produces a robot package with:
66
-
67
- - Mesh-based inertia tensors (full 6-component, not bounding-box approximations)
68
- - Separate collision meshes
69
- - Joint limits, effort/velocity/damping/friction metadata from assembly joints
70
-
71
- **Collision mesh modes** (set per-link via `links["PartName"].collision`):
72
-
73
- | Mode | Description | Default |
74
- |------|-------------|---------|
75
- | `'convex'` | Convex hull (separate `_collision.stl`) | Yes |
76
- | `'box'` | AABB primitive — fastest physics | |
77
- | `'visual'` | Same mesh as visual — exact but slow | |
78
- | `'none'` | No collision geometry | |
79
-
80
- **Unit conventions:**
81
-
82
- - Revolute `velocity` is in degrees/second in Forge; exporters convert to rad/s.
83
- - Prismatic distances are in mm in Forge; exported in meters.
84
- - `massKg` is preferred; `densityKgM3` is used when mass is unknown.
85
- - Compatibility coupling metadata, when present, maps only the primary term (largest ratio) to `<mimic>` — SDF/URDF support single-leader mimic only. Dropped terms emit a warning.
86
-
87
- **Legacy example**
88
-
89
- ```ts
90
- robotExport({
91
- assembly: rover, // assembly() with parts + revolute wheel joints
92
- modelName: "Scout",
93
- links: { Chassis: { massKg: 10 }, "Left Wheel": { massKg: 0.8 } },
94
- plugins: {
95
- diffDrive: {
96
- leftJoints: ["leftWheel"], rightJoints: ["rightWheel"],
97
- wheelSeparationMm: 280, wheelRadiusMm: 60,
98
- },
99
- },
100
- world: { generateDemoWorld: true },
101
- });
102
- ```
103
-
104
- **Preferred CLI usage**
105
-
106
- ```bash
107
- forgecad export sdf model.forge.js # SDF package (Gazebo/Ignition)
108
- forgecad export urdf model.forge.js # URDF package (ROS/PyBullet/MuJoCo)
109
- ```
110
-
111
- **`RobotExportOptions`**: `assembly: Assembly`, `modelName?: string`, `state?: JointState`, `static?: boolean`, `selfCollide?: boolean`, `allowAutoDisable?: boolean`, `links?: Record<string, RobotLinkExportOptions>`, `joints?: Record<string, RobotJointExportOptions>`, `plugins?: { diffDrive?: RobotDiffDrivePluginOptions; jointStatePublisher?: RobotJointStatePublisherOptions; }`, `world?: RobotWorldOptions`
112
-
113
- `RobotLinkExportOptions`: `{ massKg?: number, densityKgM3?: number, collision?: "visual" | "convex" | "box" | "none" }`
114
-
115
- `RobotJointExportOptions`: `{ effort?: number, velocity?: number, damping?: number, friction?: number }`
116
-
117
- **`RobotDiffDrivePluginOptions`**: `leftJoints: string[]`, `rightJoints: string[]`, `wheelSeparationMm: number`, `wheelRadiusMm: number`, `topic?: string`, `odomTopic?: string`, `tfTopic?: string`, `frameId?: string`, `odomFrameId?: string`, `maxLinearVelocity?: number`, `maxAngularVelocity?: number`, `linearAcceleration?: number`, `angularAcceleration?: number`
118
-
119
- `RobotJointStatePublisherOptions`: `{ enabled?: boolean, joints?: string[], topic?: string, updateRate?: number }`
120
-
121
- `RobotWorldOptions`: `{ name?: string, generateDemoWorld?: boolean, spawnPose?: RobotPose6, keyboardTeleop?: RobotWorldKeyboardTeleopOptions }`
122
-
123
- `RobotWorldKeyboardTeleopOptions`: `{ enabled?: boolean, linearStep?: number, angularStep?: number }`
124
-
125
61
  #### `dim()` — Add a dimension annotation between two points, or along an entity.
126
62
 
127
63
  Overloads:
@@ -24,12 +24,12 @@ lib, Line2D, linearPattern, linearPattern2d, loadFont, loft, Loft, mirrorCopy
24
24
  mock, ngon, NurbsCurve3D, NurbsSurface, offsetSolid, param, Param, path
25
25
  Point2D, Points, polygon, polygonVertices, port, Product, ProductPanelBuilder, ProductRibbonBuilder
26
26
  ProductSkin, ProductSkinBuilder, ProductStationBuilder, ProductSurfaceBuilder, ProductSurfaceRef, projectToPlane, queueMicrotask, rect
27
- Rectangle2D, robotExport, roundedRect, Route3D, scene, Sculpt, sdf, SdfShape
28
- selectEdge, selectEdges, self, setActiveBackend, setImmediate, setInterval, setTimeout, Shape
29
- ShapeGroup, sheetMetal, SheetMetalPart, sheetStock, Sim, Sketch, sketchToDxf, sketchToSvg
30
- slot, SolvedAssembly, spec, sphere, spline2d, stroke, Surface, SurfaceBody
31
- SurfaceMembers, sweep, text2d, textWidth, torus, toShape, Transform, union
32
- union2d, variableSweep, verify, Viewport, window, Wood
27
+ Rectangle2D, roundedRect, Route3D, scene, Sculpt, sdf, SdfShape, selectEdge
28
+ selectEdges, self, setActiveBackend, setImmediate, setInterval, setTimeout, Shape, ShapeGroup
29
+ sheetMetal, SheetMetalPart, sheetStock, Sim, Sketch, sketchToDxf, sketchToSvg, slot
30
+ SolvedAssembly, spec, sphere, spline2d, stroke, Surface, SurfaceBody, SurfaceMembers
31
+ sweep, text2d, textWidth, torus, toShape, Transform, union, union2d
32
+ variableSweep, verify, Viewport, window, Wood, Wrap
33
33
  ```
34
34
 
35
35
  `showLabels` is also a runtime global, but it is not part of the top-level collision check. Avoid reusing it unless you intentionally want a local value with that name.