circuit-json-to-step 0.0.22 → 0.0.24

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.
package/dist/index.js CHANGED
@@ -26,207 +26,283 @@ import {
26
26
  AdvancedFace as AdvancedFace3,
27
27
  Circle as Circle2,
28
28
  ClosedShell as ClosedShell2,
29
- ManifoldSolidBrep as ManifoldSolidBrep3,
30
- ColourRgb,
31
- FillAreaStyleColour,
32
- FillAreaStyle,
33
- SurfaceStyleFillArea,
34
- SurfaceSideStyle,
35
- SurfaceStyleUsage,
36
- PresentationStyleAssignment,
37
- StyledItem,
29
+ ManifoldSolidBrep as ManifoldSolidBrep4,
38
30
  MechanicalDesignGeometricPresentationRepresentation,
39
31
  AdvancedBrepShapeRepresentation,
40
32
  ShapeDefinitionRepresentation
41
33
  } from "stepts";
42
34
 
43
35
  // lib/mesh-generation.ts
36
+ import "stepts";
37
+
38
+ // lib/scene-box-to-step.ts
39
+ import { ClosedShell, ManifoldSolidBrep } from "stepts";
40
+
41
+ // lib/step-style-utils.ts
42
+ import {
43
+ ColourRgb,
44
+ FillAreaStyle,
45
+ FillAreaStyleColour,
46
+ PresentationStyleAssignment,
47
+ StyledItem,
48
+ SurfaceSideStyle,
49
+ SurfaceStyleFillArea,
50
+ SurfaceStyleUsage
51
+ } from "stepts";
52
+ function createStyleCache() {
53
+ return /* @__PURE__ */ new Map();
54
+ }
55
+ function createStyledItem(repo, options) {
56
+ const { itemRef, rgb, styleCache, name = "color" } = options;
57
+ const key = rgb.map((value) => value.toFixed(6)).join(",");
58
+ let presStyle = styleCache.get(key);
59
+ if (!presStyle) {
60
+ const color = repo.add(new ColourRgb("", rgb[0], rgb[1], rgb[2]));
61
+ const fillColor = repo.add(new FillAreaStyleColour("", color));
62
+ const fillStyle = repo.add(new FillAreaStyle("", [fillColor]));
63
+ const surfaceFill = repo.add(new SurfaceStyleFillArea(fillStyle));
64
+ const surfaceSide = repo.add(new SurfaceSideStyle("", [surfaceFill]));
65
+ const surfaceUsage = repo.add(new SurfaceStyleUsage(".BOTH.", surfaceSide));
66
+ presStyle = repo.add(new PresentationStyleAssignment([surfaceUsage]));
67
+ styleCache.set(key, presStyle);
68
+ }
69
+ return repo.add(new StyledItem(name, [presStyle], itemRef));
70
+ }
71
+ function createStyledItems(repo, options) {
72
+ const { itemRefs, rgb, styleCache, name } = options;
73
+ return itemRefs.map(
74
+ (itemRef) => createStyledItem(repo, { itemRef, rgb, styleCache, name })
75
+ );
76
+ }
77
+
78
+ // lib/scene-geometry.ts
79
+ function rotatePoint3(point, rotation) {
80
+ if (!rotation) return point;
81
+ let { x, y, z } = point;
82
+ if (rotation.x) {
83
+ const cos = Math.cos(rotation.x);
84
+ const sin = Math.sin(rotation.x);
85
+ const nextY = y * cos - z * sin;
86
+ const nextZ = y * sin + z * cos;
87
+ y = nextY;
88
+ z = nextZ;
89
+ }
90
+ if (rotation.y) {
91
+ const cos = Math.cos(rotation.y);
92
+ const sin = Math.sin(rotation.y);
93
+ const nextX = x * cos + z * sin;
94
+ const nextZ = -x * sin + z * cos;
95
+ x = nextX;
96
+ z = nextZ;
97
+ }
98
+ if (rotation.z) {
99
+ const cos = Math.cos(rotation.z);
100
+ const sin = Math.sin(rotation.z);
101
+ const nextX = x * cos - y * sin;
102
+ const nextY = x * sin + y * cos;
103
+ x = nextX;
104
+ y = nextY;
105
+ }
106
+ return { x, y, z };
107
+ }
108
+
109
+ // lib/step-brep-utils.ts
44
110
  import {
45
111
  AdvancedFace,
46
112
  Axis2Placement3D,
47
113
  CartesianPoint,
48
- ClosedShell,
49
114
  Direction,
50
115
  EdgeCurve,
51
116
  EdgeLoop,
52
117
  FaceOuterBound,
53
118
  Line,
54
- ManifoldSolidBrep,
55
119
  OrientedEdge,
56
120
  Plane,
57
121
  Vector,
58
122
  VertexPoint
59
123
  } from "stepts";
60
- function createBoxTriangles(box) {
61
- const { center, size } = box;
62
- const halfX = size.x / 2;
63
- const halfY = size.y / 2;
64
- const halfZ = size.z / 2;
65
- const corners = [
66
- { x: -halfX, y: -halfY, z: -halfZ },
67
- { x: halfX, y: -halfY, z: -halfZ },
68
- { x: halfX, y: halfY, z: -halfZ },
69
- { x: -halfX, y: halfY, z: -halfZ },
70
- { x: -halfX, y: -halfY, z: halfZ },
71
- { x: halfX, y: -halfY, z: halfZ },
72
- { x: halfX, y: halfY, z: halfZ },
73
- { x: -halfX, y: halfY, z: halfZ }
74
- ].map((p) => ({ x: p.x + center.x, y: p.y + center.y, z: p.z + center.z }));
75
- const triangles = [
76
- // Bottom face (z = -halfZ)
77
- {
78
- vertices: [corners[0], corners[1], corners[2]],
79
- normal: { x: 0, y: 0, z: -1 }
80
- },
81
- {
82
- vertices: [corners[0], corners[2], corners[3]],
83
- normal: { x: 0, y: 0, z: -1 }
84
- },
85
- // Top face (z = halfZ)
86
- {
87
- vertices: [corners[4], corners[6], corners[5]],
88
- normal: { x: 0, y: 0, z: 1 }
89
- },
90
- {
91
- vertices: [corners[4], corners[7], corners[6]],
92
- normal: { x: 0, y: 0, z: 1 }
93
- },
94
- // Front face (y = -halfY)
95
- {
96
- vertices: [corners[0], corners[5], corners[1]],
97
- normal: { x: 0, y: -1, z: 0 }
98
- },
99
- {
100
- vertices: [corners[0], corners[4], corners[5]],
101
- normal: { x: 0, y: -1, z: 0 }
102
- },
103
- // Back face (y = halfY)
104
- {
105
- vertices: [corners[2], corners[6], corners[7]],
106
- normal: { x: 0, y: 1, z: 0 }
107
- },
108
- {
109
- vertices: [corners[2], corners[7], corners[3]],
110
- normal: { x: 0, y: 1, z: 0 }
111
- },
112
- // Left face (x = -halfX)
113
- {
114
- vertices: [corners[0], corners[3], corners[7]],
115
- normal: { x: -1, y: 0, z: 0 }
116
- },
117
- {
118
- vertices: [corners[0], corners[7], corners[4]],
119
- normal: { x: -1, y: 0, z: 0 }
120
- },
121
- // Right face (x = halfX)
122
- {
123
- vertices: [corners[1], corners[6], corners[2]],
124
- normal: { x: 1, y: 0, z: 0 }
124
+ function createVertex(repo, point) {
125
+ return repo.add(
126
+ new VertexPoint(
127
+ "",
128
+ repo.add(new CartesianPoint("", point.x, point.y, point.z))
129
+ )
130
+ );
131
+ }
132
+ function createEdge(repo, vStart, vEnd) {
133
+ const pStart = vStart.resolve(repo).pnt.resolve(repo);
134
+ const pEnd = vEnd.resolve(repo).pnt.resolve(repo);
135
+ const dx = pEnd.x - pStart.x;
136
+ const dy = pEnd.y - pStart.y;
137
+ const dz = pEnd.z - pStart.z;
138
+ const length = Math.sqrt(dx * dx + dy * dy + dz * dz);
139
+ if (length < 1e-10) {
140
+ const dir2 = repo.add(new Direction("", 1, 0, 0));
141
+ const vec2 = repo.add(new Vector("", dir2, 1e-10));
142
+ const line2 = repo.add(new Line("", vStart.resolve(repo).pnt, vec2));
143
+ return repo.add(new EdgeCurve("", vStart, vEnd, line2, true));
144
+ }
145
+ const dir = repo.add(new Direction("", dx / length, dy / length, dz / length));
146
+ const vec = repo.add(new Vector("", dir, length));
147
+ const line = repo.add(new Line("", vStart.resolve(repo).pnt, vec));
148
+ return repo.add(new EdgeCurve("", vStart, vEnd, line, true));
149
+ }
150
+ function createFaceFromVertices(repo, vertices) {
151
+ const edges = vertices.map(
152
+ (vertex, index) => createEdge(repo, vertex, vertices[(index + 1) % vertices.length])
153
+ );
154
+ const edgeLoop = repo.add(
155
+ new EdgeLoop(
156
+ "",
157
+ edges.map((edge) => repo.add(new OrientedEdge("", edge, true)))
158
+ )
159
+ );
160
+ const p1 = vertices[0].resolve(repo).pnt.resolve(repo);
161
+ const p2 = vertices[1].resolve(repo).pnt.resolve(repo);
162
+ const p3 = vertices[2].resolve(repo).pnt.resolve(repo);
163
+ const ux = p2.x - p1.x;
164
+ const uy = p2.y - p1.y;
165
+ const uz = p2.z - p1.z;
166
+ const vx = p3.x - p1.x;
167
+ const vy = p3.y - p1.y;
168
+ const vz = p3.z - p1.z;
169
+ const nx = uy * vz - uz * vy;
170
+ const ny = uz * vx - ux * vz;
171
+ const nz = ux * vy - uy * vx;
172
+ const normalLength = Math.sqrt(nx * nx + ny * ny + nz * nz);
173
+ const normal = normalLength < 1e-10 ? repo.add(new Direction("", 0, 0, 1)) : repo.add(
174
+ new Direction(
175
+ "",
176
+ nx / normalLength,
177
+ ny / normalLength,
178
+ nz / normalLength
179
+ )
180
+ );
181
+ const refLength = Math.sqrt(ux * ux + uy * uy + uz * uz);
182
+ const refDir = refLength < 1e-10 ? repo.add(new Direction("", 1, 0, 0)) : repo.add(
183
+ new Direction("", ux / refLength, uy / refLength, uz / refLength)
184
+ );
185
+ const placement = repo.add(
186
+ new Axis2Placement3D("", vertices[0].resolve(repo).pnt, normal, refDir)
187
+ );
188
+ const plane = repo.add(new Plane("", placement));
189
+ return repo.add(
190
+ new AdvancedFace(
191
+ "",
192
+ [repo.add(new FaceOuterBound("", edgeLoop, true))],
193
+ plane,
194
+ true
195
+ )
196
+ );
197
+ }
198
+
199
+ // lib/scene-box-to-step.ts
200
+ function createSceneBoxSolid(repo, box) {
201
+ if (box.mesh?.triangles?.length) {
202
+ return createSceneMeshSolid(repo, box);
203
+ }
204
+ const localBounds = box.mesh?.boundingBox ? {
205
+ min: box.mesh.boundingBox.min,
206
+ max: box.mesh.boundingBox.max
207
+ } : {
208
+ min: {
209
+ x: -box.size.x / 2,
210
+ y: -box.size.y / 2,
211
+ z: -box.size.z / 2
125
212
  },
126
- {
127
- vertices: [corners[1], corners[5], corners[6]],
128
- normal: { x: 1, y: 0, z: 0 }
213
+ max: {
214
+ x: box.size.x / 2,
215
+ y: box.size.y / 2,
216
+ z: box.size.z / 2
129
217
  }
130
- ];
131
- return triangles;
218
+ };
219
+ const corners = [
220
+ { x: localBounds.min.x, y: localBounds.min.y, z: localBounds.min.z },
221
+ { x: localBounds.max.x, y: localBounds.min.y, z: localBounds.min.z },
222
+ { x: localBounds.max.x, y: localBounds.max.y, z: localBounds.min.z },
223
+ { x: localBounds.min.x, y: localBounds.max.y, z: localBounds.min.z },
224
+ { x: localBounds.min.x, y: localBounds.min.y, z: localBounds.max.z },
225
+ { x: localBounds.max.x, y: localBounds.min.y, z: localBounds.max.z },
226
+ { x: localBounds.max.x, y: localBounds.max.y, z: localBounds.max.z },
227
+ { x: localBounds.min.x, y: localBounds.max.y, z: localBounds.max.z }
228
+ ].map((corner) => {
229
+ const rotated = rotatePoint3(corner, box.rotation);
230
+ return {
231
+ x: rotated.x + box.center.x,
232
+ y: rotated.y + box.center.y,
233
+ z: rotated.z + box.center.z
234
+ };
235
+ });
236
+ const stepCorners = corners.map((corner) => ({
237
+ x: corner.x,
238
+ y: corner.z,
239
+ z: corner.y
240
+ }));
241
+ const vertices = stepCorners.map((corner) => createVertex(repo, corner));
242
+ const faces = [
243
+ [vertices[0], vertices[1], vertices[2], vertices[3]],
244
+ [vertices[4], vertices[7], vertices[6], vertices[5]],
245
+ [vertices[0], vertices[4], vertices[5], vertices[1]],
246
+ [vertices[1], vertices[5], vertices[6], vertices[2]],
247
+ [vertices[2], vertices[6], vertices[7], vertices[3]],
248
+ [vertices[3], vertices[7], vertices[4], vertices[0]]
249
+ ].map((faceVertices) => createFaceFromVertices(repo, faceVertices));
250
+ const shell = repo.add(new ClosedShell("", faces));
251
+ return {
252
+ solid: repo.add(new ManifoldSolidBrep(box.label ?? "Component", shell)),
253
+ styledItems: [],
254
+ usesIntrinsicFaceStyles: false,
255
+ styleTargets: faces
256
+ };
132
257
  }
133
- function createStepFacesFromTriangles(repo, triangles) {
134
- const faces = [];
135
- for (const triangle of triangles) {
136
- const v1 = repo.add(
137
- new VertexPoint(
138
- "",
139
- repo.add(
140
- new CartesianPoint(
141
- "",
142
- triangle.vertices[0].x,
143
- triangle.vertices[0].y,
144
- triangle.vertices[0].z
145
- )
146
- )
147
- )
148
- );
149
- const v2 = repo.add(
150
- new VertexPoint(
151
- "",
152
- repo.add(
153
- new CartesianPoint(
154
- "",
155
- triangle.vertices[1].x,
156
- triangle.vertices[1].y,
157
- triangle.vertices[1].z
158
- )
159
- )
160
- )
161
- );
162
- const v3 = repo.add(
163
- new VertexPoint(
164
- "",
165
- repo.add(
166
- new CartesianPoint(
167
- "",
168
- triangle.vertices[2].x,
169
- triangle.vertices[2].y,
170
- triangle.vertices[2].z
171
- )
172
- )
173
- )
174
- );
175
- const p1 = v1.resolve(repo).pnt.resolve(repo);
176
- const p2 = v2.resolve(repo).pnt.resolve(repo);
177
- const createEdge = (vStart, vEnd) => {
178
- const pStart = vStart.resolve(repo).pnt.resolve(repo);
179
- const pEnd = vEnd.resolve(repo).pnt.resolve(repo);
180
- const dir = repo.add(
181
- new Direction(
182
- "",
183
- pEnd.x - pStart.x,
184
- pEnd.y - pStart.y,
185
- pEnd.z - pStart.z
186
- )
258
+ function createSceneMeshSolid(repo, box) {
259
+ const styleCache = createStyleCache();
260
+ const styledItems = [];
261
+ const faces = box.mesh.triangles.map((triangle) => {
262
+ const vertices = triangle.vertices.map((vertex) => {
263
+ const rotated = rotatePoint3(vertex, box.rotation);
264
+ const translated = {
265
+ x: rotated.x + box.center.x,
266
+ y: rotated.z + box.center.z,
267
+ z: rotated.y + box.center.y
268
+ };
269
+ return createVertex(repo, translated);
270
+ });
271
+ const face = createFaceFromVertices(repo, vertices);
272
+ const faceColor = normalizeTriangleColor(triangle.color);
273
+ if (faceColor) {
274
+ styledItems.push(
275
+ createStyledItem(repo, {
276
+ itemRef: face,
277
+ rgb: faceColor,
278
+ styleCache
279
+ })
187
280
  );
188
- const vec = repo.add(new Vector("", dir, 1));
189
- const line = repo.add(new Line("", vStart.resolve(repo).pnt, vec));
190
- return repo.add(new EdgeCurve("", vStart, vEnd, line, true));
191
- };
192
- const edge1 = createEdge(v1, v2);
193
- const edge2 = createEdge(v2, v3);
194
- const edge3 = createEdge(v3, v1);
195
- const edgeLoop = repo.add(
196
- new EdgeLoop("", [
197
- repo.add(new OrientedEdge("", edge1, true)),
198
- repo.add(new OrientedEdge("", edge2, true)),
199
- repo.add(new OrientedEdge("", edge3, true))
200
- ])
201
- );
202
- const normalDir = repo.add(
203
- new Direction(
204
- "",
205
- triangle.normal.x,
206
- triangle.normal.y,
207
- triangle.normal.z
208
- )
209
- );
210
- const refX = p2.x - p1.x;
211
- const refY = p2.y - p1.y;
212
- const refZ = p2.z - p1.z;
213
- const refDir = repo.add(new Direction("", refX, refY, refZ));
214
- const placement = repo.add(
215
- new Axis2Placement3D("", v1.resolve(repo).pnt, normalDir, refDir)
216
- );
217
- const plane = repo.add(new Plane("", placement));
218
- const face = repo.add(
219
- new AdvancedFace(
220
- "",
221
- [repo.add(new FaceOuterBound("", edgeLoop, true))],
222
- plane,
223
- true
224
- )
225
- );
226
- faces.push(face);
227
- }
228
- return faces;
281
+ }
282
+ return face;
283
+ });
284
+ const shell = repo.add(new ClosedShell("", faces));
285
+ return {
286
+ solid: repo.add(new ManifoldSolidBrep(box.label ?? "Component", shell)),
287
+ styledItems,
288
+ usesIntrinsicFaceStyles: faces.length > 0 && styledItems.length === faces.length,
289
+ styleTargets: []
290
+ };
291
+ }
292
+ function normalizeTriangleColor(color) {
293
+ if (!Array.isArray(color) || color.length < 3) return null;
294
+ const scale = color.some((value) => value > 1) ? 255 : 1;
295
+ return [
296
+ clampColorChannel(color[0] / scale),
297
+ clampColorChannel(color[1] / scale),
298
+ clampColorChannel(color[2] / scale)
299
+ ];
300
+ }
301
+ function clampColorChannel(value) {
302
+ return Math.max(0, Math.min(1, value));
229
303
  }
304
+
305
+ // lib/mesh-generation.ts
230
306
  async function generateComponentMeshes(options) {
231
307
  const {
232
308
  repo,
@@ -239,25 +315,25 @@ async function generateComponentMeshes(options) {
239
315
  } = options;
240
316
  const solids = [];
241
317
  try {
242
- const filteredCircuitJson = circuitJson.filter((e) => {
243
- if (e.type === "pcb_board") return false;
244
- if (e.type === "cad_component" && e.cad_component_id && excludeCadComponentIds?.has(e.cad_component_id)) {
318
+ const filteredCircuitJson = circuitJson.filter((element) => {
319
+ if (element.type === "pcb_board") return false;
320
+ if (element.type === "cad_component" && element.cad_component_id && excludeCadComponentIds?.has(element.cad_component_id)) {
245
321
  return false;
246
322
  }
247
- if (e.type === "pcb_component" && e.pcb_component_id && excludePcbComponentIds?.has(e.pcb_component_id)) {
323
+ if (element.type === "pcb_component" && element.pcb_component_id && excludePcbComponentIds?.has(element.pcb_component_id)) {
248
324
  return false;
249
325
  }
250
- if (e.type === "cad_component" && e.model_step_url) {
326
+ if (element.type === "cad_component" && element.model_step_url) {
251
327
  return false;
252
328
  }
253
- if (e.type === "cad_component" && e.pcb_component_id && pcbComponentIdsWithStepUrl?.has(e.pcb_component_id)) {
329
+ if (element.type === "cad_component" && element.pcb_component_id && pcbComponentIdsWithStepUrl?.has(element.pcb_component_id)) {
254
330
  return false;
255
331
  }
256
332
  return true;
257
- }).map((e) => {
258
- if (!includeExternalMeshes && e.type === "cad_component") {
333
+ }).map((element) => {
334
+ if (!includeExternalMeshes && element.type === "cad_component") {
259
335
  return {
260
- ...e,
336
+ ...element,
261
337
  model_3mf_url: void 0,
262
338
  model_obj_url: void 0,
263
339
  model_stl_url: void 0,
@@ -265,7 +341,7 @@ async function generateComponentMeshes(options) {
265
341
  model_gltf_url: void 0
266
342
  };
267
343
  }
268
- return e;
344
+ return element;
269
345
  });
270
346
  const gltfModule = "circuit-json-to-gltf";
271
347
  const { convertCircuitJsonTo3D } = await import(
@@ -276,43 +352,8 @@ async function generateComponentMeshes(options) {
276
352
  boardThickness,
277
353
  renderBoardTextures: false
278
354
  });
279
- const allTriangles = [];
280
355
  for (const box of scene3d.boxes) {
281
- if (box.mesh && "triangles" in box.mesh) {
282
- allTriangles.push(...box.mesh.triangles);
283
- } else {
284
- const boxTriangles = createBoxTriangles(box);
285
- allTriangles.push(...boxTriangles);
286
- }
287
- }
288
- if (allTriangles.length > 0) {
289
- const transformedTriangles = allTriangles.map((tri) => ({
290
- vertices: tri.vertices.map((v) => ({
291
- x: v.x,
292
- y: v.z,
293
- // GLTF Z becomes STEP Y
294
- z: v.y
295
- // GLTF Y becomes STEP Z
296
- })),
297
- normal: {
298
- x: tri.normal.x,
299
- y: tri.normal.z,
300
- // GLTF Z becomes STEP Y
301
- z: tri.normal.y
302
- // GLTF Y becomes STEP Z
303
- }
304
- }));
305
- const componentFaces = createStepFacesFromTriangles(
306
- repo,
307
- transformedTriangles
308
- );
309
- const componentShell = repo.add(
310
- new ClosedShell("", componentFaces)
311
- );
312
- const componentSolid = repo.add(
313
- new ManifoldSolidBrep("Components", componentShell)
314
- );
315
- solids.push(componentSolid);
356
+ solids.push(createSceneBoxSolid(repo, box));
316
357
  }
317
358
  } catch (error) {
318
359
  console.warn("Failed to generate component mesh:", error);
@@ -324,7 +365,7 @@ async function generateComponentMeshes(options) {
324
365
  import {
325
366
  CartesianPoint as CartesianPoint2,
326
367
  Direction as Direction2,
327
- ManifoldSolidBrep as ManifoldSolidBrep2,
368
+ ManifoldSolidBrep as ManifoldSolidBrep3,
328
369
  Ref,
329
370
  Unknown,
330
371
  parseRepository
@@ -343,17 +384,6 @@ var EXCLUDED_ENTITY_TYPES = /* @__PURE__ */ new Set([
343
384
  "PRODUCT_DEFINITION_SHAPE",
344
385
  "SHAPE_DEFINITION_REPRESENTATION",
345
386
  "ADVANCED_BREP_SHAPE_REPRESENTATION",
346
- "MECHANICAL_DESIGN_GEOMETRIC_PRESENTATION_REPRESENTATION",
347
- "PRESENTATION_STYLE_ASSIGNMENT",
348
- "SURFACE_STYLE_USAGE",
349
- "SURFACE_SIDE_STYLE",
350
- "SURFACE_STYLE_FILL_AREA",
351
- "FILL_AREA_STYLE",
352
- "FILL_AREA_STYLE_COLOUR",
353
- "COLOUR_RGB",
354
- "STYLED_ITEM",
355
- "CURVE_STYLE",
356
- "DRAUGHTING_PRE_DEFINED_CURVE_FONT",
357
387
  "PRODUCT_RELATED_PRODUCT_CATEGORY",
358
388
  "NEXT_ASSEMBLY_USAGE_OCCURRENCE",
359
389
  "CONTEXT_DEPENDENT_SHAPE_REPRESENTATION",
@@ -498,7 +528,7 @@ function mergeSingleStepModel(targetRepo, stepText, transform, placement) {
498
528
  }
499
529
  const solids = [];
500
530
  for (const [oldId, entity] of entries) {
501
- if (entity instanceof ManifoldSolidBrep2) {
531
+ if (entity instanceof ManifoldSolidBrep3) {
502
532
  const mappedId = idMapping.get(oldId);
503
533
  if (mappedId !== void 0) {
504
534
  solids.push(new Ref(eid(mappedId)));
@@ -548,16 +578,16 @@ function adjustTransformForPlacement(entries, transform, placement) {
548
578
  transform.translation.x = targetX - center.x;
549
579
  transform.translation.y = targetY - center.y;
550
580
  if (isThroughHoleComponent) {
551
- transform.translation.z = boardThickness;
581
+ transform.translation.z = boardThickness / 2;
552
582
  }
553
583
  if (!isThroughHoleComponent && boardThickness > 0) {
554
584
  const halfThickness = boardThickness / 2;
555
- const offsetZ = targetZ - halfThickness;
585
+ const offsetZ = targetZ;
556
586
  if (normalizedLayer === "bottom") {
557
587
  transform.translation.z = -maxZ + offsetZ;
558
588
  transform.rotation.x = normalizeDegrees(transform.rotation.x + 180);
559
589
  } else {
560
- transform.translation.z = boardThickness - minZ + offsetZ;
590
+ transform.translation.z = halfThickness - minZ + offsetZ;
561
591
  }
562
592
  } else if (!isThroughHoleComponent) {
563
593
  transform.translation.z = targetZ - center.z;
@@ -722,7 +752,7 @@ function normalizeStepNumericExponents(stepText) {
722
752
  var package_default = {
723
753
  name: "circuit-json-to-step",
724
754
  main: "dist/index.js",
725
- version: "0.0.21",
755
+ version: "0.0.23",
726
756
  type: "module",
727
757
  scripts: {
728
758
  "pull-reference": `git clone https://github.com/tscircuit/circuit-json.git && find circuit-json/tests -name '*.test.ts' -exec bash -c 'mv "$0" "\${0%.test.ts}.ts"' {} \\; && git clone https://github.com/tscircuit/stepts.git && find stepts/tests -name '*.test.ts' -exec bash -c 'mv "$0" "\${0%.test.ts}.ts"' {} \\;`,
@@ -740,7 +770,7 @@ var package_default = {
740
770
  "@resvg/resvg-wasm": "^2.6.2",
741
771
  "@tscircuit/circuit-json-util": "^0.0.75",
742
772
  "@types/bun": "latest",
743
- "circuit-json": "^0.0.286",
773
+ "circuit-json": "^0.0.406",
744
774
  "looks-same": "^10.0.1",
745
775
  "occt-import-js": "^0.0.23",
746
776
  poppygl: "^0.0.17",
@@ -752,7 +782,7 @@ var package_default = {
752
782
  },
753
783
  dependencies: {
754
784
  "circuit-json-to-connectivity-map": "^0.0.22",
755
- "circuit-json-to-gltf": "^0.0.91",
785
+ "circuit-json-to-gltf": "^0.0.93",
756
786
  "circuit-to-svg": "^0.0.327",
757
787
  "schematic-symbols": "^0.0.202",
758
788
  stepts: "^0.0.4"
@@ -1049,7 +1079,7 @@ function createPillHoleLoop(repo, hole, z, xDir) {
1049
1079
  );
1050
1080
  return repo.add(new EdgeLoop2("", orientedEdges));
1051
1081
  }
1052
- function createPillCylindricalFaces(repo, hole, boardThickness, xDir, zDir) {
1082
+ function createPillCylindricalFaces(repo, hole, zMin, zMax, xDir, zDir) {
1053
1083
  const geom = getPillGeometry(hole);
1054
1084
  const {
1055
1085
  centerX,
@@ -1073,7 +1103,8 @@ function createPillCylindricalFaces(repo, hole, boardThickness, xDir, zDir) {
1073
1103
  rotation,
1074
1104
  centerX,
1075
1105
  centerY,
1076
- boardThickness,
1106
+ zMin,
1107
+ zMax,
1077
1108
  zDir,
1078
1109
  xDir
1079
1110
  )
@@ -1088,7 +1119,8 @@ function createPillCylindricalFaces(repo, hole, boardThickness, xDir, zDir) {
1088
1119
  rotation,
1089
1120
  centerX,
1090
1121
  centerY,
1091
- boardThickness,
1122
+ zMin,
1123
+ zMax,
1092
1124
  zDir
1093
1125
  )
1094
1126
  );
@@ -1103,7 +1135,8 @@ function createPillCylindricalFaces(repo, hole, boardThickness, xDir, zDir) {
1103
1135
  rotation,
1104
1136
  centerX,
1105
1137
  centerY,
1106
- boardThickness,
1138
+ zMin,
1139
+ zMax,
1107
1140
  zDir,
1108
1141
  xDir
1109
1142
  )
@@ -1118,7 +1151,8 @@ function createPillCylindricalFaces(repo, hole, boardThickness, xDir, zDir) {
1118
1151
  rotation,
1119
1152
  centerX,
1120
1153
  centerY,
1121
- boardThickness,
1154
+ zMin,
1155
+ zMax,
1122
1156
  zDir
1123
1157
  )
1124
1158
  );
@@ -1135,7 +1169,8 @@ function createPillCylindricalFaces(repo, hole, boardThickness, xDir, zDir) {
1135
1169
  rotation,
1136
1170
  centerX,
1137
1171
  centerY,
1138
- boardThickness,
1172
+ zMin,
1173
+ zMax,
1139
1174
  zDir,
1140
1175
  xDir
1141
1176
  )
@@ -1150,7 +1185,8 @@ function createPillCylindricalFaces(repo, hole, boardThickness, xDir, zDir) {
1150
1185
  rotation,
1151
1186
  centerX,
1152
1187
  centerY,
1153
- boardThickness,
1188
+ zMin,
1189
+ zMax,
1154
1190
  zDir
1155
1191
  )
1156
1192
  );
@@ -1165,7 +1201,8 @@ function createPillCylindricalFaces(repo, hole, boardThickness, xDir, zDir) {
1165
1201
  rotation,
1166
1202
  centerX,
1167
1203
  centerY,
1168
- boardThickness,
1204
+ zMin,
1205
+ zMax,
1169
1206
  zDir,
1170
1207
  xDir
1171
1208
  )
@@ -1180,14 +1217,15 @@ function createPillCylindricalFaces(repo, hole, boardThickness, xDir, zDir) {
1180
1217
  rotation,
1181
1218
  centerX,
1182
1219
  centerY,
1183
- boardThickness,
1220
+ zMin,
1221
+ zMax,
1184
1222
  zDir
1185
1223
  )
1186
1224
  );
1187
1225
  }
1188
1226
  return faces;
1189
1227
  }
1190
- function createCylindricalWall(repo, centerX, centerY, radius, startAngle, endAngle, rotation, centerX0, centerY0, boardThickness, zDir, xDir) {
1228
+ function createCylindricalWall(repo, centerX, centerY, radius, startAngle, endAngle, rotation, centerX0, centerY0, zMin, zMax, zDir, xDir) {
1191
1229
  const bottomStartX = centerX + radius * Math.cos(startAngle);
1192
1230
  const bottomStartY = centerY + radius * Math.sin(startAngle);
1193
1231
  const bottomEndX = centerX + radius * Math.cos(endAngle);
@@ -1209,29 +1247,25 @@ function createCylindricalWall(repo, centerX, centerY, radius, startAngle, endAn
1209
1247
  const bottomStartVertex = repo.add(
1210
1248
  new VertexPoint2(
1211
1249
  "",
1212
- repo.add(new CartesianPoint3("", bottomStart.x, bottomStart.y, 0))
1250
+ repo.add(new CartesianPoint3("", bottomStart.x, bottomStart.y, zMin))
1213
1251
  )
1214
1252
  );
1215
1253
  const bottomEndVertex = repo.add(
1216
1254
  new VertexPoint2(
1217
1255
  "",
1218
- repo.add(new CartesianPoint3("", bottomEnd.x, bottomEnd.y, 0))
1256
+ repo.add(new CartesianPoint3("", bottomEnd.x, bottomEnd.y, zMin))
1219
1257
  )
1220
1258
  );
1221
1259
  const topStart = repo.add(
1222
1260
  new VertexPoint2(
1223
1261
  "",
1224
- repo.add(
1225
- new CartesianPoint3("", bottomStart.x, bottomStart.y, boardThickness)
1226
- )
1262
+ repo.add(new CartesianPoint3("", bottomStart.x, bottomStart.y, zMax))
1227
1263
  )
1228
1264
  );
1229
1265
  const topEnd = repo.add(
1230
1266
  new VertexPoint2(
1231
1267
  "",
1232
- repo.add(
1233
- new CartesianPoint3("", bottomEnd.x, bottomEnd.y, boardThickness)
1234
- )
1268
+ repo.add(new CartesianPoint3("", bottomEnd.x, bottomEnd.y, zMax))
1235
1269
  )
1236
1270
  );
1237
1271
  const centerRotated = rotatePoint(
@@ -1242,7 +1276,7 @@ function createCylindricalWall(repo, centerX, centerY, radius, startAngle, endAn
1242
1276
  rotation
1243
1277
  );
1244
1278
  const bottomCenter = repo.add(
1245
- new CartesianPoint3("", centerRotated.x, centerRotated.y, 0)
1279
+ new CartesianPoint3("", centerRotated.x, centerRotated.y, zMin)
1246
1280
  );
1247
1281
  const bottomPlacement = repo.add(
1248
1282
  new Axis2Placement3D2(
@@ -1257,7 +1291,7 @@ function createCylindricalWall(repo, centerX, centerY, radius, startAngle, endAn
1257
1291
  new EdgeCurve2("", bottomStartVertex, bottomEndVertex, bottomCircle, false)
1258
1292
  );
1259
1293
  const topCenter = repo.add(
1260
- new CartesianPoint3("", centerRotated.x, centerRotated.y, boardThickness)
1294
+ new CartesianPoint3("", centerRotated.x, centerRotated.y, zMax)
1261
1295
  );
1262
1296
  const topPlacement = repo.add(new Axis2Placement3D2("", topCenter, zDir, xDir));
1263
1297
  const topCircle = repo.add(new Circle("", topPlacement, radius));
@@ -1265,37 +1299,34 @@ function createCylindricalWall(repo, centerX, centerY, radius, startAngle, endAn
1265
1299
  const v1 = repo.add(
1266
1300
  new VertexPoint2(
1267
1301
  "",
1268
- repo.add(new CartesianPoint3("", bottomStart.x, bottomStart.y, 0))
1302
+ repo.add(new CartesianPoint3("", bottomStart.x, bottomStart.y, zMin))
1269
1303
  )
1270
1304
  );
1271
1305
  const v2 = repo.add(
1272
1306
  new VertexPoint2(
1273
1307
  "",
1274
- repo.add(
1275
- new CartesianPoint3("", bottomStart.x, bottomStart.y, boardThickness)
1276
- )
1308
+ repo.add(new CartesianPoint3("", bottomStart.x, bottomStart.y, zMax))
1277
1309
  )
1278
1310
  );
1279
1311
  const v3 = repo.add(
1280
1312
  new VertexPoint2(
1281
1313
  "",
1282
- repo.add(new CartesianPoint3("", bottomEnd.x, bottomEnd.y, 0))
1314
+ repo.add(new CartesianPoint3("", bottomEnd.x, bottomEnd.y, zMin))
1283
1315
  )
1284
1316
  );
1285
1317
  const v4 = repo.add(
1286
1318
  new VertexPoint2(
1287
1319
  "",
1288
- repo.add(
1289
- new CartesianPoint3("", bottomEnd.x, bottomEnd.y, boardThickness)
1290
- )
1320
+ repo.add(new CartesianPoint3("", bottomEnd.x, bottomEnd.y, zMax))
1291
1321
  )
1292
1322
  );
1293
1323
  const dir1 = repo.add(new Direction3("", 0, 0, 1));
1294
- const vec1 = repo.add(new Vector2("", dir1, boardThickness));
1324
+ const height = zMax - zMin;
1325
+ const vec1 = repo.add(new Vector2("", dir1, height));
1295
1326
  const line1 = repo.add(new Line2("", v1.resolve(repo).pnt, vec1));
1296
1327
  const edge1 = repo.add(new EdgeCurve2("", v1, v2, line1, true));
1297
1328
  const dir2 = repo.add(new Direction3("", 0, 0, 1));
1298
- const vec2 = repo.add(new Vector2("", dir2, boardThickness));
1329
+ const vec2 = repo.add(new Vector2("", dir2, height));
1299
1330
  const line2 = repo.add(new Line2("", v3.resolve(repo).pnt, vec2));
1300
1331
  const edge2 = repo.add(new EdgeCurve2("", v3, v4, line2, true));
1301
1332
  const loop = repo.add(
@@ -1321,25 +1352,25 @@ function createCylindricalWall(repo, centerX, centerY, radius, startAngle, endAn
1321
1352
  )
1322
1353
  );
1323
1354
  }
1324
- function createPlanarWall(repo, startX, startY, endX, endY, rotation, centerX0, centerY0, boardThickness, zDir) {
1355
+ function createPlanarWall(repo, startX, startY, endX, endY, rotation, centerX0, centerY0, zMin, zMax, zDir) {
1325
1356
  const start = rotatePoint(startX, startY, centerX0, centerY0, rotation);
1326
1357
  const end = rotatePoint(endX, endY, centerX0, centerY0, rotation);
1327
1358
  const v1 = repo.add(
1328
- new VertexPoint2("", repo.add(new CartesianPoint3("", start.x, start.y, 0)))
1359
+ new VertexPoint2(
1360
+ "",
1361
+ repo.add(new CartesianPoint3("", start.x, start.y, zMin))
1362
+ )
1329
1363
  );
1330
1364
  const v2 = repo.add(
1331
- new VertexPoint2("", repo.add(new CartesianPoint3("", end.x, end.y, 0)))
1365
+ new VertexPoint2("", repo.add(new CartesianPoint3("", end.x, end.y, zMin)))
1332
1366
  );
1333
1367
  const v3 = repo.add(
1334
- new VertexPoint2(
1335
- "",
1336
- repo.add(new CartesianPoint3("", end.x, end.y, boardThickness))
1337
- )
1368
+ new VertexPoint2("", repo.add(new CartesianPoint3("", end.x, end.y, zMax)))
1338
1369
  );
1339
1370
  const v4 = repo.add(
1340
1371
  new VertexPoint2(
1341
1372
  "",
1342
- repo.add(new CartesianPoint3("", start.x, start.y, boardThickness))
1373
+ repo.add(new CartesianPoint3("", start.x, start.y, zMax))
1343
1374
  )
1344
1375
  );
1345
1376
  const dx = end.x - start.x;
@@ -1358,10 +1389,11 @@ function createPlanarWall(repo, startX, startY, endX, endY, rotation, centerX0,
1358
1389
  const topLine = repo.add(new Line2("", v4.resolve(repo).pnt, topVec));
1359
1390
  const topEdge = repo.add(new EdgeCurve2("", v4, v3, topLine, true));
1360
1391
  const vertDir = repo.add(new Direction3("", 0, 0, 1));
1361
- const vertVec1 = repo.add(new Vector2("", vertDir, boardThickness));
1392
+ const height = zMax - zMin;
1393
+ const vertVec1 = repo.add(new Vector2("", vertDir, height));
1362
1394
  const vertLine1 = repo.add(new Line2("", v2.resolve(repo).pnt, vertVec1));
1363
1395
  const vertEdge1 = repo.add(new EdgeCurve2("", v2, v3, vertLine1, true));
1364
- const vertVec2 = repo.add(new Vector2("", vertDir, boardThickness));
1396
+ const vertVec2 = repo.add(new Vector2("", vertDir, height));
1365
1397
  const vertLine2 = repo.add(new Line2("", v1.resolve(repo).pnt, vertVec2));
1366
1398
  const vertEdge2 = repo.add(new EdgeCurve2("", v1, v4, vertLine2, true));
1367
1399
  const loop = repo.add(
@@ -1378,7 +1410,7 @@ function createPlanarWall(repo, startX, startY, endX, endY, rotation, centerX0,
1378
1410
  const refDir = repo.add(
1379
1411
  new Direction3("", dx / edgeLength, dy / edgeLength, 0)
1380
1412
  );
1381
- const planeOrigin = repo.add(new CartesianPoint3("", start.x, start.y, 0));
1413
+ const planeOrigin = repo.add(new CartesianPoint3("", start.x, start.y, zMin));
1382
1414
  const placement = repo.add(
1383
1415
  new Axis2Placement3D2("", planeOrigin, normalDir, refDir)
1384
1416
  );
@@ -1406,6 +1438,7 @@ async function circuitJsonToStep(circuitJson, options = {}) {
1406
1438
  const productName = options.productName ?? "PCB";
1407
1439
  const boardCenterX = pcbBoard?.center?.x ?? 0;
1408
1440
  const boardCenterY = pcbBoard?.center?.y ?? 0;
1441
+ const halfBoardThickness = boardThickness / 2;
1409
1442
  if (!boardWidth || !boardHeight) {
1410
1443
  throw new Error(
1411
1444
  "Board dimensions not found. Either provide boardWidth and boardHeight in options, or include a pcb_board in the circuit JSON with width and height properties."
@@ -1495,7 +1528,9 @@ async function circuitJsonToStep(circuitJson, options = {}) {
1495
1528
  (point) => repo.add(
1496
1529
  new VertexPoint3(
1497
1530
  "",
1498
- repo.add(new CartesianPoint4("", point.x, point.y, 0))
1531
+ repo.add(
1532
+ new CartesianPoint4("", point.x, point.y, -halfBoardThickness)
1533
+ )
1499
1534
  )
1500
1535
  )
1501
1536
  );
@@ -1503,7 +1538,9 @@ async function circuitJsonToStep(circuitJson, options = {}) {
1503
1538
  (point) => repo.add(
1504
1539
  new VertexPoint3(
1505
1540
  "",
1506
- repo.add(new CartesianPoint4("", point.x, point.y, boardThickness))
1541
+ repo.add(
1542
+ new CartesianPoint4("", point.x, point.y, halfBoardThickness)
1543
+ )
1507
1544
  )
1508
1545
  )
1509
1546
  );
@@ -1511,14 +1548,30 @@ async function circuitJsonToStep(circuitJson, options = {}) {
1511
1548
  const halfWidth = boardWidth / 2;
1512
1549
  const halfHeight = boardHeight / 2;
1513
1550
  const corners = [
1514
- [boardCenterX - halfWidth, boardCenterY - halfHeight, 0],
1515
- [boardCenterX + halfWidth, boardCenterY - halfHeight, 0],
1516
- [boardCenterX + halfWidth, boardCenterY + halfHeight, 0],
1517
- [boardCenterX - halfWidth, boardCenterY + halfHeight, 0],
1518
- [boardCenterX - halfWidth, boardCenterY - halfHeight, boardThickness],
1519
- [boardCenterX + halfWidth, boardCenterY - halfHeight, boardThickness],
1520
- [boardCenterX + halfWidth, boardCenterY + halfHeight, boardThickness],
1521
- [boardCenterX - halfWidth, boardCenterY + halfHeight, boardThickness]
1551
+ [
1552
+ boardCenterX - halfWidth,
1553
+ boardCenterY - halfHeight,
1554
+ -halfBoardThickness
1555
+ ],
1556
+ [
1557
+ boardCenterX + halfWidth,
1558
+ boardCenterY - halfHeight,
1559
+ -halfBoardThickness
1560
+ ],
1561
+ [
1562
+ boardCenterX + halfWidth,
1563
+ boardCenterY + halfHeight,
1564
+ -halfBoardThickness
1565
+ ],
1566
+ [
1567
+ boardCenterX - halfWidth,
1568
+ boardCenterY + halfHeight,
1569
+ -halfBoardThickness
1570
+ ],
1571
+ [boardCenterX - halfWidth, boardCenterY - halfHeight, halfBoardThickness],
1572
+ [boardCenterX + halfWidth, boardCenterY - halfHeight, halfBoardThickness],
1573
+ [boardCenterX + halfWidth, boardCenterY + halfHeight, halfBoardThickness],
1574
+ [boardCenterX - halfWidth, boardCenterY + halfHeight, halfBoardThickness]
1522
1575
  ];
1523
1576
  const vertices = corners.map(
1524
1577
  ([x, y, z]) => repo.add(
@@ -1528,7 +1581,7 @@ async function circuitJsonToStep(circuitJson, options = {}) {
1528
1581
  bottomVertices = [vertices[0], vertices[1], vertices[2], vertices[3]];
1529
1582
  topVertices = [vertices[4], vertices[5], vertices[6], vertices[7]];
1530
1583
  }
1531
- function createEdge(v1, v2) {
1584
+ function createEdge2(v1, v2) {
1532
1585
  const p1 = v1.resolve(repo).pnt.resolve(repo);
1533
1586
  const p2 = v2.resolve(repo).pnt.resolve(repo);
1534
1587
  const dx = p2.x - p1.x;
@@ -1554,17 +1607,17 @@ async function circuitJsonToStep(circuitJson, options = {}) {
1554
1607
  for (let i = 0; i < bottomVertices.length; i++) {
1555
1608
  const v1 = bottomVertices[i];
1556
1609
  const v2 = bottomVertices[(i + 1) % bottomVertices.length];
1557
- bottomEdges.push(createEdge(v1, v2));
1610
+ bottomEdges.push(createEdge2(v1, v2));
1558
1611
  }
1559
1612
  for (let i = 0; i < topVertices.length; i++) {
1560
1613
  const v1 = topVertices[i];
1561
1614
  const v2 = topVertices[(i + 1) % topVertices.length];
1562
- topEdges.push(createEdge(v1, v2));
1615
+ topEdges.push(createEdge2(v1, v2));
1563
1616
  }
1564
1617
  for (let i = 0; i < bottomVertices.length; i++) {
1565
- verticalEdges.push(createEdge(bottomVertices[i], topVertices[i]));
1618
+ verticalEdges.push(createEdge2(bottomVertices[i], topVertices[i]));
1566
1619
  }
1567
- const origin = repo.add(new CartesianPoint4("", 0, 0, 0));
1620
+ const origin = repo.add(new CartesianPoint4("", 0, 0, -halfBoardThickness));
1568
1621
  const xDir = repo.add(new Direction4("", 1, 0, 0));
1569
1622
  const zDir = repo.add(new Direction4("", 0, 0, 1));
1570
1623
  const bottomFrame = repo.add(
@@ -1584,16 +1637,20 @@ async function circuitJsonToStep(circuitJson, options = {}) {
1584
1637
  );
1585
1638
  const bottomHoleLoops = [];
1586
1639
  for (const hole of holes) {
1587
- const holeShape = hole.hole_shape || hole.shape;
1640
+ const holeShape = hole.hole_shape ?? hole.shape;
1588
1641
  if (holeShape === "circle") {
1589
- const holeX = typeof hole.x === "number" ? hole.x : hole.x.value;
1590
- const holeY = typeof hole.y === "number" ? hole.y : hole.y.value;
1591
- const radius = hole.hole_diameter / 2;
1592
- const holeCenter = repo.add(new CartesianPoint4("", holeX, holeY, 0));
1642
+ const holeX = typeof hole.x === "number" ? hole.x : hole.x ?? 0;
1643
+ const holeY = typeof hole.y === "number" ? hole.y : hole.y ?? 0;
1644
+ const radius = (hole.hole_diameter ?? 0) / 2;
1645
+ const holeCenter = repo.add(
1646
+ new CartesianPoint4("", holeX, holeY, -halfBoardThickness)
1647
+ );
1593
1648
  const holeVertex = repo.add(
1594
1649
  new VertexPoint3(
1595
1650
  "",
1596
- repo.add(new CartesianPoint4("", holeX + radius, holeY, 0))
1651
+ repo.add(
1652
+ new CartesianPoint4("", holeX + radius, holeY, -halfBoardThickness)
1653
+ )
1597
1654
  )
1598
1655
  );
1599
1656
  const holePlacement = repo.add(
@@ -1613,22 +1670,19 @@ async function circuitJsonToStep(circuitJson, options = {}) {
1613
1670
  );
1614
1671
  bottomHoleLoops.push(repo.add(new FaceBound("", holeLoop, true)));
1615
1672
  } else if (holeShape === "rotated_pill" || holeShape === "pill") {
1616
- const pillLoop = createPillHoleLoop(repo, hole, 0, xDir);
1673
+ const pillLoop = createPillHoleLoop(repo, hole, -halfBoardThickness, xDir);
1617
1674
  bottomHoleLoops.push(repo.add(new FaceBound("", pillLoop, true)));
1618
1675
  }
1619
1676
  }
1620
1677
  const bottomFace = repo.add(
1621
1678
  new AdvancedFace3(
1622
1679
  "",
1623
- [
1624
- repo.add(new FaceOuterBound3("", bottomLoop, true)),
1625
- ...bottomHoleLoops
1626
- ],
1680
+ [repo.add(new FaceOuterBound3("", bottomLoop, true)), ...bottomHoleLoops],
1627
1681
  bottomPlane,
1628
1682
  true
1629
1683
  )
1630
1684
  );
1631
- const topOrigin = repo.add(new CartesianPoint4("", 0, 0, boardThickness));
1685
+ const topOrigin = repo.add(new CartesianPoint4("", 0, 0, halfBoardThickness));
1632
1686
  const topFrame = repo.add(new Axis2Placement3D3("", topOrigin, zDir, xDir));
1633
1687
  const topPlane = repo.add(new Plane3("", topFrame));
1634
1688
  const topLoop = repo.add(
@@ -1639,19 +1693,19 @@ async function circuitJsonToStep(circuitJson, options = {}) {
1639
1693
  );
1640
1694
  const topHoleLoops = [];
1641
1695
  for (const hole of holes) {
1642
- const holeShape = hole.hole_shape || hole.shape;
1696
+ const holeShape = hole.hole_shape ?? hole.shape;
1643
1697
  if (holeShape === "circle") {
1644
- const holeX = typeof hole.x === "number" ? hole.x : hole.x.value;
1645
- const holeY = typeof hole.y === "number" ? hole.y : hole.y.value;
1646
- const radius = hole.hole_diameter / 2;
1698
+ const holeX = typeof hole.x === "number" ? hole.x : hole.x ?? 0;
1699
+ const holeY = typeof hole.y === "number" ? hole.y : hole.y ?? 0;
1700
+ const radius = (hole.hole_diameter ?? 0) / 2;
1647
1701
  const holeCenter = repo.add(
1648
- new CartesianPoint4("", holeX, holeY, boardThickness)
1702
+ new CartesianPoint4("", holeX, holeY, halfBoardThickness)
1649
1703
  );
1650
1704
  const holeVertex = repo.add(
1651
1705
  new VertexPoint3(
1652
1706
  "",
1653
1707
  repo.add(
1654
- new CartesianPoint4("", holeX + radius, holeY, boardThickness)
1708
+ new CartesianPoint4("", holeX + radius, holeY, halfBoardThickness)
1655
1709
  )
1656
1710
  )
1657
1711
  );
@@ -1667,7 +1721,7 @@ async function circuitJsonToStep(circuitJson, options = {}) {
1667
1721
  );
1668
1722
  topHoleLoops.push(repo.add(new FaceBound("", holeLoop, true)));
1669
1723
  } else if (holeShape === "rotated_pill" || holeShape === "pill") {
1670
- const pillLoop = createPillHoleLoop(repo, hole, boardThickness, xDir);
1724
+ const pillLoop = createPillHoleLoop(repo, hole, halfBoardThickness, xDir);
1671
1725
  topHoleLoops.push(repo.add(new FaceBound("", pillLoop, true)));
1672
1726
  }
1673
1727
  }
@@ -1689,7 +1743,7 @@ async function circuitJsonToStep(circuitJson, options = {}) {
1689
1743
  const edgeDir = {
1690
1744
  x: bottomV2.x - bottomV1.x,
1691
1745
  y: bottomV2.y - bottomV1.y,
1692
- z: 0
1746
+ z: -halfBoardThickness
1693
1747
  };
1694
1748
  const normalDir = repo.add(new Direction4("", edgeDir.y, -edgeDir.x, 0));
1695
1749
  const refDir = repo.add(new Direction4("", edgeDir.x, edgeDir.y, 0));
@@ -1717,16 +1771,20 @@ async function circuitJsonToStep(circuitJson, options = {}) {
1717
1771
  }
1718
1772
  const holeCylindricalFaces = [];
1719
1773
  for (const hole of holes) {
1720
- const holeShape = hole.hole_shape || hole.shape;
1774
+ const holeShape = hole.hole_shape ?? hole.shape;
1721
1775
  if (holeShape === "circle") {
1722
- const holeX = typeof hole.x === "number" ? hole.x : hole.x.value;
1723
- const holeY = typeof hole.y === "number" ? hole.y : hole.y.value;
1724
- const radius = hole.hole_diameter / 2;
1725
- const bottomHoleCenter = repo.add(new CartesianPoint4("", holeX, holeY, 0));
1776
+ const holeX = typeof hole.x === "number" ? hole.x : hole.x ?? 0;
1777
+ const holeY = typeof hole.y === "number" ? hole.y : hole.y ?? 0;
1778
+ const radius = (hole.hole_diameter ?? 0) / 2;
1779
+ const bottomHoleCenter = repo.add(
1780
+ new CartesianPoint4("", holeX, holeY, -halfBoardThickness)
1781
+ );
1726
1782
  const bottomHoleVertex = repo.add(
1727
1783
  new VertexPoint3(
1728
1784
  "",
1729
- repo.add(new CartesianPoint4("", holeX + radius, holeY, 0))
1785
+ repo.add(
1786
+ new CartesianPoint4("", holeX + radius, holeY, -halfBoardThickness)
1787
+ )
1730
1788
  )
1731
1789
  );
1732
1790
  const bottomHolePlacement = repo.add(
@@ -1750,13 +1808,13 @@ async function circuitJsonToStep(circuitJson, options = {}) {
1750
1808
  )
1751
1809
  );
1752
1810
  const topHoleCenter = repo.add(
1753
- new CartesianPoint4("", holeX, holeY, boardThickness)
1811
+ new CartesianPoint4("", holeX, holeY, halfBoardThickness)
1754
1812
  );
1755
1813
  const topHoleVertex = repo.add(
1756
1814
  new VertexPoint3(
1757
1815
  "",
1758
1816
  repo.add(
1759
- new CartesianPoint4("", holeX + radius, holeY, boardThickness)
1817
+ new CartesianPoint4("", holeX + radius, holeY, halfBoardThickness)
1760
1818
  )
1761
1819
  )
1762
1820
  );
@@ -1792,7 +1850,8 @@ async function circuitJsonToStep(circuitJson, options = {}) {
1792
1850
  const pillFaces = createPillCylindricalFaces(
1793
1851
  repo,
1794
1852
  hole,
1795
- boardThickness,
1853
+ -halfBoardThickness,
1854
+ halfBoardThickness,
1796
1855
  xDir,
1797
1856
  zDir
1798
1857
  );
@@ -1800,9 +1859,17 @@ async function circuitJsonToStep(circuitJson, options = {}) {
1800
1859
  }
1801
1860
  }
1802
1861
  const allFaces = [bottomFace, topFace, ...sideFaces, ...holeCylindricalFaces];
1862
+ const styleCache = createStyleCache();
1863
+ const boardStyledItems = createStyledItems(repo, {
1864
+ itemRefs: allFaces,
1865
+ rgb: [0.2, 0.6, 0.2],
1866
+ styleCache
1867
+ });
1803
1868
  const shell = repo.add(new ClosedShell2("", allFaces));
1804
- const solid = repo.add(new ManifoldSolidBrep3(productName, shell));
1869
+ const solid = repo.add(new ManifoldSolidBrep4(productName, shell));
1805
1870
  const allSolids = [solid];
1871
+ const componentStyledItems = [];
1872
+ const solidsWithIntrinsicFaceStyles = /* @__PURE__ */ new Set();
1806
1873
  let handledComponentIds = /* @__PURE__ */ new Set();
1807
1874
  let handledPcbComponentIds = /* @__PURE__ */ new Set();
1808
1875
  if (options.includeComponents && options.includeExternalMeshes) {
@@ -1815,6 +1882,9 @@ async function circuitJsonToStep(circuitJson, options = {}) {
1815
1882
  handledComponentIds = mergeResult.handledComponentIds;
1816
1883
  handledPcbComponentIds = mergeResult.handledPcbComponentIds;
1817
1884
  allSolids.push(...mergeResult.solids);
1885
+ mergeResult.solids.forEach((solidRef) => {
1886
+ solidsWithIntrinsicFaceStyles.add(String(solidRef.id));
1887
+ });
1818
1888
  }
1819
1889
  if (options.includeComponents) {
1820
1890
  const pcbComponentIdsWithStepUrl = /* @__PURE__ */ new Set();
@@ -1854,21 +1924,39 @@ async function circuitJsonToStep(circuitJson, options = {}) {
1854
1924
  excludePcbComponentIds: handledPcbComponentIds,
1855
1925
  pcbComponentIdsWithStepUrl
1856
1926
  });
1857
- allSolids.push(...componentSolids);
1927
+ for (const componentSolid of componentSolids) {
1928
+ allSolids.push(componentSolid.solid);
1929
+ componentStyledItems.push(...componentSolid.styledItems);
1930
+ if (componentSolid.usesIntrinsicFaceStyles) {
1931
+ solidsWithIntrinsicFaceStyles.add(String(componentSolid.solid.id));
1932
+ } else if (componentSolid.styleTargets.length > 0) {
1933
+ componentStyledItems.push(
1934
+ ...createStyledItems(repo, {
1935
+ itemRefs: componentSolid.styleTargets,
1936
+ rgb: [0.75, 0.75, 0.75],
1937
+ styleCache
1938
+ })
1939
+ );
1940
+ solidsWithIntrinsicFaceStyles.add(String(componentSolid.solid.id));
1941
+ }
1942
+ }
1858
1943
  }
1859
1944
  }
1860
- const styledItems = [];
1861
- allSolids.forEach((solidRef, index) => {
1945
+ const styledItems = [
1946
+ ...boardStyledItems,
1947
+ ...componentStyledItems
1948
+ ];
1949
+ allSolids.forEach((itemRef, index) => {
1862
1950
  const isBoard = index === 0;
1863
- const [r, g, b] = isBoard ? [0.2, 0.6, 0.2] : [0.75, 0.75, 0.75];
1864
- const color = repo.add(new ColourRgb("", r, g, b));
1865
- const fillColor = repo.add(new FillAreaStyleColour("", color));
1866
- const fillStyle = repo.add(new FillAreaStyle("", [fillColor]));
1867
- const surfaceFill = repo.add(new SurfaceStyleFillArea(fillStyle));
1868
- const surfaceSide = repo.add(new SurfaceSideStyle("", [surfaceFill]));
1869
- const surfaceUsage = repo.add(new SurfaceStyleUsage(".BOTH.", surfaceSide));
1870
- const presStyle = repo.add(new PresentationStyleAssignment([surfaceUsage]));
1871
- const styledItem = repo.add(new StyledItem("", [presStyle], solidRef));
1951
+ if (isBoard || solidsWithIntrinsicFaceStyles.has(String(itemRef.id))) {
1952
+ return;
1953
+ }
1954
+ const styledItem = createStyledItem(repo, {
1955
+ itemRef,
1956
+ rgb: [0.75, 0.75, 0.75],
1957
+ styleCache,
1958
+ name: ""
1959
+ });
1872
1960
  styledItems.push(styledItem);
1873
1961
  });
1874
1962
  repo.add(