forgecad 0.9.4 → 0.9.6

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 (90) hide show
  1. package/dist/assets/{AdminPage-jwoEgwE_.js → AdminPage-Da6hhpJx.js} +1 -1
  2. package/dist/assets/{BlogPage-Ck7g3ue2.js → BlogPage-Bl_sKeWb.js} +1 -1
  3. package/dist/assets/{DocsPage-9WaRC14b.js → DocsPage-Blz3Tp4j.js} +1 -6
  4. package/dist/assets/EditorApp-CuiPbtn5.js +12754 -0
  5. package/dist/assets/{EmbedViewer-37_PfMwv.js → EmbedViewer-BFG6-Ufm.js} +2 -2
  6. package/dist/assets/{LandingPageProofDriven-CO8WL0CY.js → LandingPageProofDriven-DB9fQd5P.js} +1 -1
  7. package/dist/assets/{PricingPage-DADKGuOa.js → PricingPage-BMxYT_F0.js} +1 -1
  8. package/dist/assets/{SettingsPage-DKKI4W49.js → SettingsPage-VVQNrCAg.js} +1 -1
  9. package/dist/assets/{app-CwI02pTA.js → app-Dl9ymBWC.js} +355 -36
  10. package/dist/assets/cli/{render-Kw5hLEcL.js → render-CFtwKCCY.js} +203 -862
  11. package/dist/assets/{sectionPlaneMath-C8N0w8o3.js → distance-BEC2RjJi.js} +4150 -801
  12. package/dist/assets/{evalWorker-D6ub3kfS.js → evalWorker-CRvbzTXm.js} +2611 -528
  13. package/dist/assets/{manifold-CwDdMKyc.js → manifold-B9QSr-qP.js} +1 -1
  14. package/dist/assets/{manifold-DTvmxSDf.js → manifold-DpBXFS2K.js} +1 -1
  15. package/dist/assets/{manifold-lru0jwVw.js → manifold-DzZ4VRPs.js} +2 -2
  16. package/dist/assets/{renderSceneState-tvtNKNRi.js → renderSceneState-BuAXF2jh.js} +1 -1
  17. package/dist/assets/{reportWorker-DeqktDGt.js → reportWorker-BNWEnRg1.js} +2606 -525
  18. package/dist/cli/render.html +1 -1
  19. package/dist/docs/index.html +2 -2
  20. package/dist/docs-raw/AI/usage.md +0 -1
  21. package/dist/docs-raw/API/core/concepts.md +11 -1
  22. package/dist/docs-raw/CLI.md +64 -13
  23. package/dist/docs-raw/beta-operations.md +4 -0
  24. package/dist/docs-raw/deployment.md +38 -23
  25. package/dist/docs-raw/generated/assembly.md +8 -3
  26. package/dist/docs-raw/generated/concepts.md +126 -46
  27. package/dist/docs-raw/generated/core.md +97 -47
  28. package/dist/docs-raw/generated/curves.md +113 -595
  29. package/dist/docs-raw/generated/lib.md +40 -3
  30. package/dist/docs-raw/generated/output.md +6 -1
  31. package/dist/docs-raw/generated/sdf.md +50 -4
  32. package/dist/docs-raw/generated/sketch.md +9 -1
  33. package/dist/docs-raw/generated/viewport.md +1 -9
  34. package/dist/docs-raw/guides/inspection-bundles.md +40 -9
  35. package/dist/docs-raw/runbook.md +3 -3
  36. package/dist/docs-raw/skills/forgecad-blockout-model.md +1 -0
  37. package/dist/docs-raw/skills/forgecad-image-replicator.md +3 -1
  38. package/dist/docs-raw/skills/forgecad-make-a-model.md +48 -4
  39. package/dist/docs-raw/skills/forgecad-render-inspect.md +3 -1
  40. package/dist/docs-raw/skills/forgecad-visual-spec.md +2 -0
  41. package/dist/docs-raw/skills/forgecad.md +2 -1
  42. package/dist/docs-raw/skills/index.md +0 -1
  43. package/dist/index.html +1 -1
  44. package/dist/sitemap.xml +6 -6
  45. package/dist-cli/blender/render.py +43 -8
  46. package/dist-cli/forgecad.js +5729 -2015
  47. package/dist-cli/forgecad.js.map +1 -1
  48. package/dist-skill/CONTEXT.md +372 -667
  49. package/dist-skill/SKILL-dev.md +2 -1
  50. package/dist-skill/SKILL.md +2 -1
  51. package/dist-skill/docs/API/core/concepts.md +11 -1
  52. package/dist-skill/docs/CLI.md +64 -13
  53. package/dist-skill/docs/generated/assembly.md +8 -3
  54. package/dist-skill/docs/generated/core.md +97 -47
  55. package/dist-skill/docs/generated/curves.md +113 -595
  56. package/dist-skill/docs/generated/lib.md +40 -3
  57. package/dist-skill/docs/generated/output.md +6 -1
  58. package/dist-skill/docs/generated/sdf.md +50 -4
  59. package/dist-skill/docs/generated/sketch.md +9 -1
  60. package/dist-skill/docs/generated/viewport.md +1 -9
  61. package/dist-skill/docs/guides/inspection-bundles.md +40 -9
  62. package/dist-skill/docs-dev/API/core/concepts.md +11 -1
  63. package/dist-skill/docs-dev/CLI.md +64 -13
  64. package/dist-skill/docs-dev/generated/assembly.md +8 -3
  65. package/dist-skill/docs-dev/generated/core.md +97 -47
  66. package/dist-skill/docs-dev/generated/curves.md +113 -595
  67. package/dist-skill/docs-dev/generated/lib.md +40 -3
  68. package/dist-skill/docs-dev/generated/output.md +6 -1
  69. package/dist-skill/docs-dev/generated/sdf.md +50 -4
  70. package/dist-skill/docs-dev/generated/sketch.md +9 -1
  71. package/dist-skill/docs-dev/generated/viewport.md +1 -9
  72. package/dist-skill/docs-dev/guides/inspection-bundles.md +40 -9
  73. package/dist-skill/library/README.md +0 -1
  74. package/dist-skill/library/forgecad-blockout-model/SKILL.md +1 -0
  75. package/dist-skill/library/forgecad-image-replicator/SKILL.md +3 -1
  76. package/dist-skill/library/forgecad-make-a-model/SKILL.md +48 -4
  77. package/dist-skill/library/forgecad-render-inspect/SKILL.md +3 -1
  78. package/dist-skill/library/forgecad-visual-spec/SKILL.md +2 -0
  79. package/examples/api/drive-wheel-regions.forge.js +43 -0
  80. package/examples/api/guided-loft-olive-oil-bottle.forge.js +135 -0
  81. package/examples/api/sdf-circular-array-knurling.forge.js +19 -0
  82. package/examples/api/sdf-pattern2d-ceramic-ripple-set.forge.js +83 -0
  83. package/examples/api/sdf-pattern2d-grip-tread.forge.js +72 -0
  84. package/examples/api/sdf-pattern2d-orbital-jewelry.forge.js +62 -0
  85. package/examples/api/sdf-surface-basket-weave.forge.js +67 -0
  86. package/examples/api/sector-gear-body.forge.js +34 -0
  87. package/package.json +20 -2
  88. package/dist/assets/EditorApp-Dja2jMmW.js +0 -12509
  89. package/dist/docs-raw/skills/forgecad-api-dogfood.md +0 -130
  90. package/dist-skill/library/forgecad-api-dogfood/SKILL.md +0 -125
@@ -0,0 +1,135 @@
1
+ /**
2
+ * Guided loft vs ProductSkin: olive oil bottle.
3
+ *
4
+ * Left: the new namespaced Loft.withGuideRails(...) primitive. Right: the
5
+ * equivalent Product.skin(...) authoring style for comparison.
6
+ */
7
+
8
+ Product.scenePreset('product');
9
+
10
+ viewConfig({
11
+ camera: { position: [190, -250, 150], target: [8, 0, 88], fov: 34 },
12
+ });
13
+
14
+ const darkOlive = {
15
+ color: '#34462f',
16
+ material: {
17
+ opacity: 0.58,
18
+ roughness: 0.14,
19
+ metalness: 0,
20
+ clearcoat: 1,
21
+ clearcoatRoughness: 0.04,
22
+ transmission: 0.28,
23
+ thickness: 4,
24
+ },
25
+ };
26
+ const labelStock = Product.materials.mattePlastic('#eee8d8');
27
+ const blackCap = Product.materials.softRubber({ color: '#171a15' });
28
+
29
+ const guidedStations = [
30
+ Loft.station(roundedRect(52, 34, 8), 0),
31
+ Loft.station(roundedRect(60, 38, 11), 78),
32
+ Loft.station(roundedRect(42, 27, 10), 128),
33
+ Loft.station(circle2d(12, 64), 166),
34
+ Loft.station(circle2d(14, 64), 184),
35
+ ];
36
+
37
+ const leftSilhouette = path()
38
+ .moveTo(-26, 0)
39
+ .bezierTo(-31, 30, -33, 55, -31, 72)
40
+ .bezierTo(-30, 90, -27, 107, -25, 118)
41
+ .bezierTo(-20, 142, -12, 152, -12, 166)
42
+ .lineTo(-14, 184);
43
+
44
+ const rightSilhouette = path()
45
+ .moveTo(26, 0)
46
+ .bezierTo(31, 30, 33, 55, 31, 72)
47
+ .bezierTo(30, 90, 27, 107, 25, 118)
48
+ .bezierTo(20, 142, 12, 152, 12, 166)
49
+ .lineTo(14, 184);
50
+
51
+ const backCrown = path()
52
+ .moveTo(-17, 0)
53
+ .bezierTo(-21, 30, -22, 56, -20, 72)
54
+ .bezierTo(-19, 92, -17, 108, -16, 118)
55
+ .bezierTo(-13, 142, -10, 154, -10, 166)
56
+ .lineTo(-12, 184);
57
+
58
+ const frontCrown = path()
59
+ .moveTo(17, 0)
60
+ .bezierTo(22, 30, 24, 56, 22, 72)
61
+ .bezierTo(21, 92, 19, 108, 18, 118)
62
+ .bezierTo(14, 142, 10, 154, 10, 166)
63
+ .lineTo(12, 184);
64
+
65
+ const leftRail = Loft.pathOnXz(leftSilhouette);
66
+ const rightRail = Loft.pathOnXz(rightSilhouette);
67
+ const backRail = Loft.pathOnYz(backCrown);
68
+ const frontRail = Loft.pathOnYz(frontCrown);
69
+
70
+ const guidedBottle = Loft.withGuideRails(
71
+ guidedStations,
72
+ [
73
+ Loft.leftRail(leftRail),
74
+ Loft.rightRail(rightRail),
75
+ Loft.backRail(backRail),
76
+ Loft.frontRail(frontRail),
77
+ ],
78
+ { samples: 19, edgeLength: 1.3 },
79
+ )
80
+ .as('guided-loft-olive-glass')
81
+ .material(darkOlive.material)
82
+ .color(darkOlive.color);
83
+
84
+ const guidedLabel = Product.applyMaterial(
85
+ roundedRect(34, 58, 4)
86
+ .extrude(0.8)
87
+ .rotateX(90)
88
+ .translate(0, 22.5, 76)
89
+ .as('guided-loft-cream-label'),
90
+ labelStock,
91
+ );
92
+
93
+ const guidedCap = Product.applyMaterial(cylinder(18, 12, 12, 64).translate(0, 0, 184).as('guided-loft-black-cap'), blackCap);
94
+
95
+ const guided = group(
96
+ { name: 'body', shape: guidedBottle },
97
+ { name: 'label', shape: guidedLabel },
98
+ { name: 'cap', shape: guidedCap },
99
+ ).translate(-52, 0, 0);
100
+
101
+ const productBottle = Product.skin('product-skin-olive-glass')
102
+ .axis('Z')
103
+ .stations([
104
+ Product.station('base').z(0).roundedRect(52, 34, 8),
105
+ Product.station('body').z(78).roundedRect(60, 38, 11),
106
+ Product.station('shoulder').z(128).roundedRect(42, 27, 10),
107
+ Product.station('neck').z(166).circle(24),
108
+ Product.station('lip').z(184).circle(28),
109
+ ])
110
+ .rails({
111
+ leftSilhouette: Product.rail.polyline(leftRail),
112
+ frontCrown: Product.rail.polyline(frontRail),
113
+ })
114
+ .material(darkOlive)
115
+ .edgeLength(1.3)
116
+ .build();
117
+
118
+ const productLabel = Product.applyMaterial(
119
+ roundedRect(34, 58, 4)
120
+ .extrude(0.8)
121
+ .rotateX(90)
122
+ .translate(0, 22.5, 76)
123
+ .as('product-skin-cream-label'),
124
+ labelStock,
125
+ );
126
+
127
+ const productCap = Product.applyMaterial(cylinder(18, 12, 12, 64).translate(0, 0, 184).as('product-skin-black-cap'), blackCap);
128
+
129
+ const productSkinComparison = group(
130
+ { name: 'body', shape: productBottle.toShape() },
131
+ { name: 'label', shape: productLabel },
132
+ { name: 'cap', shape: productCap },
133
+ ).translate(52, 0, 0);
134
+
135
+ return [guided, productSkinComparison];
@@ -0,0 +1,19 @@
1
+ // SDF circular array — fogleman-style folded-domain knurling.
2
+ // circularArray evaluates the source groove twice no matter how many copies it creates.
3
+
4
+ const body = sdf.cylinder(42, 11);
5
+
6
+ const rightHandGroove = sdf.box(2.4, 1.2, 48)
7
+ .rotateZ(45)
8
+ .circularArray(32, 11.6)
9
+ .twist(2.2);
10
+
11
+ const leftHandGroove = sdf.box(2.4, 1.2, 48)
12
+ .rotateZ(-45)
13
+ .circularArray(32, 11.6)
14
+ .twist(-2.2);
15
+
16
+ return body
17
+ .subtract(rightHandGroove, leftHandGroove)
18
+ .color('#8da2ad')
19
+ .material({ roughness: 0.38, metalness: 0.18 });
@@ -0,0 +1,83 @@
1
+ // Native Pattern2D relief on ceramic forms: flutes, bands, and woven bowl texture.
2
+
3
+ scene({
4
+ background: { top: '#fff8f1', bottom: '#d8e4ea' },
5
+ camera: { position: [90, -126, 66], target: [0, 0, 8], fov: 34 },
6
+ environment: { preset: 'studio', intensity: 1.28 },
7
+ lights: [
8
+ { type: 'ambient', color: '#ffffff', intensity: 0.28 },
9
+ { type: 'point', position: [54, -72, 90], color: '#fff1d0', intensity: 2.2, distance: 280, decay: 1.12 },
10
+ { type: 'point', position: [-76, 48, 62], color: '#84d8ff', intensity: 1.15, distance: 240, decay: 1.18 },
11
+ { type: 'directional', position: [30, -56, 150], color: '#ffffff', intensity: 0.58 },
12
+ ],
13
+ postProcessing: {
14
+ toneMappingExposure: 1.12,
15
+ bloom: { intensity: 0.08, threshold: 0.84, radius: 0.38 },
16
+ vignette: { darkness: 0.12, offset: 0.62 },
17
+ },
18
+ });
19
+
20
+ const ceramic = (shape, color) =>
21
+ shape.color(color).material({
22
+ roughness: 0.46,
23
+ metalness: 0,
24
+ clearcoat: 0.72,
25
+ clearcoatRoughness: 0.18,
26
+ });
27
+
28
+ const p = sdf.pattern2d();
29
+
30
+ const flutedPorcelain = p
31
+ .sineWave({ direction: [1, 0], wavelength: 4.6, amplitude: 0.28 })
32
+ .add(p.sineWave({ direction: [0, 1], wavelength: 18, amplitude: 0.08 }))
33
+ .clamp(-0.34, 0.34);
34
+
35
+ const carvedBands = p
36
+ .stripes({ direction: [0, 1], spacing: 7.5, width: 0.9, depth: 0.2 })
37
+ .add(p.sineWave({ direction: [1, 1], wavelength: 9, amplitude: 0.08 }))
38
+ .clamp(-0.27, 0.07);
39
+
40
+ const wovenGlaze = p
41
+ .overUnderWeave({ spacing: 2.45, threadWidth: 1.08, depth: 0.34 })
42
+ .add(p.sineWave({ direction: [1, -1], wavelength: 13, amplitude: 0.055 }))
43
+ .clamp(-0.42, 0.06);
44
+
45
+ const tallVase = ceramic(
46
+ sdf.cylinder(58, 11)
47
+ .shell(1.35)
48
+ .surfaceDisplace(flutedPorcelain, { uv: 'cylinder' })
49
+ .subtract(sdf.cylinder(66, 8.2))
50
+ .translate(-35, 0, 3),
51
+ '#f2efe2',
52
+ );
53
+
54
+ const bandedCup = ceramic(
55
+ sdf.cylinder(38, 14)
56
+ .shell(1.25)
57
+ .surfaceDisplace(carvedBands, { uv: 'cylinder' })
58
+ .subtract(sdf.cylinder(46, 10.6))
59
+ .translate(0, 0, -7),
60
+ '#8fb7c7',
61
+ );
62
+
63
+ const lowBowl = ceramic(
64
+ sdf.sphere(22)
65
+ .shell(1.4)
66
+ .surfaceDisplace(wovenGlaze)
67
+ .subtract(sdf.box(68, 68, 40).translate(0, 0, 17))
68
+ .translate(37, 0, -8),
69
+ '#c97462',
70
+ );
71
+
72
+ const footRings = ceramic(
73
+ sdf
74
+ .torus(10.5, 0.75)
75
+ .translate(-35, 0, -27)
76
+ .union(
77
+ sdf.torus(12.5, 0.72).translate(0, 0, -26),
78
+ sdf.torus(14.5, 0.72).translate(37, 0, -25),
79
+ ),
80
+ '#d8c59f',
81
+ );
82
+
83
+ return { tallVase, bandedCup, lowBowl, footRings };
@@ -0,0 +1,72 @@
1
+ // Native Pattern2D relief for a tactile grip: diamond tread, metal collars, no JS callbacks.
2
+
3
+ scene({
4
+ background: { top: '#eef4f7', bottom: '#ccd8df' },
5
+ camera: { position: [82, -118, 54], target: [0, 0, 0], fov: 32 },
6
+ environment: { preset: 'studio', intensity: 1.35 },
7
+ lights: [
8
+ { type: 'ambient', color: '#ffffff', intensity: 0.24 },
9
+ { type: 'point', position: [56, -78, 78], color: '#ffffff', intensity: 2.1, distance: 260, decay: 1.12 },
10
+ { type: 'point', position: [-62, 48, 46], color: '#73d8ff', intensity: 1.25, distance: 230, decay: 1.2 },
11
+ { type: 'directional', position: [36, -52, 130], color: '#fff0cf', intensity: 0.62 },
12
+ ],
13
+ postProcessing: {
14
+ toneMappingExposure: 1.08,
15
+ bloom: { intensity: 0.1, threshold: 0.82, radius: 0.36 },
16
+ vignette: { darkness: 0.13, offset: 0.58 },
17
+ },
18
+ });
19
+
20
+ const p = sdf.pattern2d();
21
+
22
+ const diamondTread = p
23
+ .stripes({ direction: [1, 1], spacing: 5.2, width: 1.05, depth: 0.42 })
24
+ .max(p.stripes({ direction: [1, -1], spacing: 5.2, width: 1.05, depth: 0.42 }))
25
+ .add(p.sineWave({ direction: [0, 1], wavelength: 19, amplitude: 0.06 }))
26
+ .clamp(-0.54, 0.04);
27
+
28
+ const collarGrooves = p
29
+ .stripes({ direction: [0, 1], spacing: 2.4, width: 0.45, depth: 0.09 })
30
+ .add(p.sineWave({ direction: [1, 0], wavelength: 8, amplitude: 0.018 }))
31
+ .clamp(-0.12, 0.02);
32
+
33
+ const rubber = (shape) =>
34
+ shape.color('#1f2227').material({
35
+ roughness: 0.86,
36
+ metalness: 0.02,
37
+ clearcoat: 0.08,
38
+ clearcoatRoughness: 0.72,
39
+ });
40
+
41
+ const satinMetal = (shape) =>
42
+ shape.color('#c3cbd4').material({
43
+ roughness: 0.34,
44
+ metalness: 0.78,
45
+ clearcoat: 0.42,
46
+ clearcoatRoughness: 0.18,
47
+ });
48
+
49
+ const grip = rubber(
50
+ sdf.cylinder(78, 13)
51
+ .shell(1.7)
52
+ .surfaceDisplace(diamondTread, { uv: 'cylinder' })
53
+ .subtract(sdf.cylinder(88, 9.0)),
54
+ );
55
+
56
+ const core = satinMetal(sdf.cylinder(86, 6.8));
57
+ const leftCollar = satinMetal(
58
+ sdf.torus(13.2, 1.45).surfaceDisplace(collarGrooves, { uv: 'torus' }).translate(0, 0, -39),
59
+ );
60
+ const rightCollar = satinMetal(
61
+ sdf.torus(13.2, 1.45).surfaceDisplace(collarGrooves, { uv: 'torus' }).translate(0, 0, 39),
62
+ );
63
+ const endLips = satinMetal(sdf.box(1.1, 1.1, 5.0).circularArray(44, 15.2));
64
+
65
+ return {
66
+ core,
67
+ grip,
68
+ leftCollar,
69
+ rightCollar,
70
+ leftEndKnurl: endLips.translate(0, 0, -41.5),
71
+ rightEndKnurl: endLips.translate(0, 0, 41.5),
72
+ };
@@ -0,0 +1,62 @@
1
+ // Native Pattern2D relief plus folded circular array accents for an ornamental SDF piece.
2
+
3
+ scene({
4
+ background: { top: '#101420', bottom: '#223242' },
5
+ camera: { position: [74, -104, 58], target: [0, 0, 4], fov: 31 },
6
+ environment: { preset: 'night', intensity: 1.18 },
7
+ lights: [
8
+ { type: 'ambient', color: '#172030', intensity: 0.18 },
9
+ { type: 'point', position: [48, -66, 72], color: '#ffd36f', intensity: 2.35, distance: 250, decay: 1.08 },
10
+ { type: 'point', position: [-52, 48, 58], color: '#58e5ff', intensity: 1.85, distance: 230, decay: 1.14 },
11
+ { type: 'point', position: [0, 36, 76], color: '#ff7ab8', intensity: 1.15, distance: 220, decay: 1.2 },
12
+ { type: 'directional', position: [28, -48, 138], color: '#ffffff', intensity: 0.58 },
13
+ ],
14
+ fog: { color: '#101420', near: 130, far: 330 },
15
+ postProcessing: {
16
+ toneMappingExposure: 1.32,
17
+ bloom: { intensity: 0.44, threshold: 0.56, radius: 0.56 },
18
+ vignette: { darkness: 0.42, offset: 0.44 },
19
+ },
20
+ });
21
+
22
+ const p = sdf.pattern2d();
23
+
24
+ const braidedGold = p
25
+ .overUnderWeave({ spacing: 1.6, threadWidth: 0.72, depth: 0.18 })
26
+ .add(p.sineWave({ direction: [1, 0], wavelength: 7.2, amplitude: 0.035 }))
27
+ .clamp(-0.23, 0.04);
28
+
29
+ const opalSkin = p
30
+ .sineWave({ direction: [1, 0], wavelength: 6, amplitude: 0.18 })
31
+ .add(p.sineWave({ direction: [0, 1], wavelength: 4.2, amplitude: 0.12 }))
32
+ .add(p.stripes({ direction: [1, 1], spacing: 5.5, width: 0.7, depth: 0.08 }))
33
+ .clamp(-0.2, 0.22);
34
+
35
+ const gold = (shape) =>
36
+ shape.color('#e8b95f').material({
37
+ roughness: 0.25,
38
+ metalness: 0.92,
39
+ clearcoat: 0.45,
40
+ clearcoatRoughness: 0.08,
41
+ reflectivity: 0.72,
42
+ });
43
+
44
+ const opal = (shape) =>
45
+ shape.color('#9be9dd').material({
46
+ roughness: 0.12,
47
+ metalness: 0,
48
+ clearcoat: 1,
49
+ clearcoatRoughness: 0.03,
50
+ opacity: 0.86,
51
+ transmission: 0.22,
52
+ reflectivity: 0.84,
53
+ ior: 1.42,
54
+ });
55
+
56
+ const braidedBand = gold(sdf.torus(24, 2.9).surfaceDisplace(braidedGold, { uv: 'torus' }));
57
+ const centerStone = opal(sdf.sphere(8.8).surfaceDisplace(opalSkin).translate(0, 0, 0.8));
58
+ const beadHalo = gold(sdf.sphere(1.8).circularArray(32, 31).translate(0, 0, 0.8));
59
+ const northStar = opal(sdf.sphere(3.3).surfaceDisplace(opalSkin).translate(0, 31, 1.1));
60
+ const southStar = opal(sdf.sphere(3.3).surfaceDisplace(opalSkin).translate(0, -31, 1.1));
61
+
62
+ return { braidedBand, centerStone, beadHalo, northStar, southStar };
@@ -0,0 +1,67 @@
1
+ // Native SDF surface relief — user-composed Pattern2D IR lowers to shader + CPU.
2
+ // These raw SDF leaves raymarch directly; call .toShape() only for export.
3
+
4
+ scene({
5
+ background: { top: '#f6fbff', bottom: '#dfe9f3' },
6
+ camera: { position: [92, -128, 72], target: [0, 0, 3], fov: 34 },
7
+ environment: { preset: 'studio', intensity: 1.2 },
8
+ lights: [
9
+ { type: 'ambient', color: '#ffffff', intensity: 0.22 },
10
+ { type: 'point', position: [56, -72, 86], color: '#fff0cf', intensity: 2.0, distance: 260, decay: 1.15 },
11
+ { type: 'point', position: [-70, 52, 64], color: '#79d9ff', intensity: 1.45, distance: 240, decay: 1.2 },
12
+ { type: 'directional', position: [36, -68, 150], color: '#ffffff', intensity: 0.7 },
13
+ ],
14
+ postProcessing: {
15
+ toneMappingExposure: 1.12,
16
+ bloom: { intensity: 0.18, threshold: 0.74, radius: 0.42 },
17
+ vignette: { darkness: 0.18, offset: 0.58 },
18
+ },
19
+ });
20
+
21
+ const textile = (shape, color) =>
22
+ shape.color(color).material({
23
+ roughness: 0.72,
24
+ metalness: 0,
25
+ clearcoat: 0.22,
26
+ clearcoatRoughness: 0.58,
27
+ });
28
+
29
+ const p = sdf.pattern2d();
30
+ const weave = p.overUnderWeave({ spacing: 2.8, threadWidth: 1.2, depth: 0.55 });
31
+ const diagonalRipple = p.sineWave({ direction: [1, 1], wavelength: 11, amplitude: 0.08 });
32
+ const bowlPattern = weave.add(diagonalRipple).clamp(-0.68, 0.08);
33
+
34
+ const bowl = textile(
35
+ sdf.sphere(24)
36
+ .shell(1.8)
37
+ .surfaceDisplace(bowlPattern)
38
+ .subtract(sdf.box(70, 70, 36).translate(0, 0, 20))
39
+ .translate(-46, 0, 0),
40
+ '#d9826b',
41
+ );
42
+
43
+ const sleevePattern = p
44
+ .overUnderWeave({ spacing: 2.15, threadWidth: 0.95, depth: 0.45 })
45
+ .add(p.stripes({ direction: [1, 0], spacing: 7, width: 0.7, depth: 0.18 }));
46
+
47
+ const sleeve = textile(
48
+ sdf.cylinder(38, 13)
49
+ .shell(1.4)
50
+ .surfaceDisplace(sleevePattern, { uv: 'cylinder' })
51
+ .subtract(sdf.cylinder(46, 9.8))
52
+ .translate(0, 0, 0),
53
+ '#6fa6a3',
54
+ );
55
+
56
+ const ringPattern = p
57
+ .overUnderWeave({ spacing: 1.75, threadWidth: 0.78, depth: 0.28 })
58
+ .add(p.sineWave({ direction: [0, 1], wavelength: 5.4, amplitude: 0.05 }));
59
+
60
+ const ring = textile(
61
+ sdf.torus(18, 3.4)
62
+ .surfaceDisplace(ringPattern, { uv: 'torus' })
63
+ .translate(46, 0, 0),
64
+ '#c6a15b',
65
+ );
66
+
67
+ return { bowl, sleeve, ring };
@@ -0,0 +1,34 @@
1
+ // Sector gear demo: partial tooth window composed with a visual/structural body.
2
+
3
+ const moduleSize = Param.number("Module", 1.2, { min: 0.8, max: 2.5, step: 0.05, unit: "mm" });
4
+ const teethOnFullCircle = Param.number("Pitch Teeth", 36, { min: 24, max: 72, integer: true });
5
+ const toothCount = Param.number("Active Teeth", 11, { min: 2, max: 24, integer: true });
6
+ const firstTooth = Param.number("First Tooth", 2, { min: 0, max: 35, integer: true });
7
+ const faceWidth = Param.number("Face Width", 8, { min: 4, max: 16, unit: "mm" });
8
+
9
+ const pitchRadius = (moduleSize * teethOnFullCircle) / 2;
10
+ const rootRadius = pitchRadius - moduleSize * 1.25;
11
+ const rimWidth = Math.max(2.4, moduleSize * 2);
12
+ const hubDiameter = Math.max(8, moduleSize * 8);
13
+
14
+ const body = lib.gearBodies.spoked({
15
+ outerRadius: rootRadius,
16
+ rimWidth,
17
+ hubDiameter,
18
+ boreDiameter: 5,
19
+ spokeCount: 5,
20
+ spokeWidth: Math.max(2, moduleSize * 1.8),
21
+ faceWidth,
22
+ });
23
+
24
+ const sector = lib.sectorGear({
25
+ module: moduleSize,
26
+ teethOnFullCircle,
27
+ firstTooth: Math.min(firstTooth, teethOnFullCircle - 1),
28
+ toothCount: Math.min(toothCount, teethOnFullCircle),
29
+ pressureAngleDeg: 20,
30
+ faceWidth,
31
+ body,
32
+ });
33
+
34
+ return [{ name: "Sector Gear", shape: sector.color("#d5a15f") }];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "forgecad",
3
- "version": "0.9.4",
3
+ "version": "0.9.6",
4
4
  "description": "Code-first parametric CAD for JavaScript/TypeScript, in the browser and CLI.",
5
5
  "license": "BUSL-1.1",
6
6
  "type": "module",
@@ -101,7 +101,25 @@
101
101
  "beta:shell": "bash scripts/beta.sh shell",
102
102
  "beta:db": "bash scripts/beta.sh db",
103
103
  "beta:refresh": "bash scripts/beta.sh refresh",
104
- "beta:local": "bash scripts/beta-local.sh",
104
+ "beta:local": "bash scripts/beta.sh local",
105
+ "beta:help": "bash scripts/beta.sh help",
106
+ "prod:setup": "bash scripts/prod.sh setup",
107
+ "prod:deploy": "bash scripts/prod.sh deploy",
108
+ "prod:smoke": "bash scripts/prod.sh smoke",
109
+ "prod:deploy:backend": "bash scripts/prod.sh deploy-backend",
110
+ "prod:deploy:all": "bash scripts/prod.sh deploy-all",
111
+ "prod:rollback": "bash scripts/prod.sh rollback",
112
+ "prod:rollback:backend": "bash scripts/prod.sh rollback-backend",
113
+ "prod:logs": "bash scripts/prod.sh logs",
114
+ "prod:logs:backend": "bash scripts/prod.sh logs-backend",
115
+ "prod:open": "bash scripts/prod.sh open",
116
+ "prod:shell": "bash scripts/prod.sh shell",
117
+ "prod:shell:backend": "bash scripts/prod.sh shell-backend",
118
+ "prod:db": "bash scripts/prod.sh db",
119
+ "prod:health": "bash scripts/prod/health.sh",
120
+ "prod:errors": "bash scripts/prod/errors.sh",
121
+ "prod:promote-beta": "fish scripts/promote-beta-to-prod.fish",
122
+ "prod:help": "bash scripts/prod.sh help",
105
123
  "gen:types": "node scripts/gen-forge-types.mjs",
106
124
  "gen:docs": "node scripts/gen-api-docs.mjs",
107
125
  "gen:cli-docs": "node scripts/gen-cli-docs.mjs",