circuit-json-to-step 0.0.21 → 0.0.23
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 +302 -293
- package/lib/index.ts +58 -28
- package/lib/mesh-generation.ts +30 -293
- package/lib/pill-geometry.ts +46 -41
- package/lib/scene-box-to-step.ts +91 -0
- package/lib/scene-geometry.ts +58 -0
- package/lib/step-brep-utils.ts +118 -0
- package/lib/step-model-merger.ts +4 -4
- package/package.json +3 -3
- package/test/basics/basics01/__snapshots__/basics01.snap.png +0 -0
- package/test/basics/basics04/__snapshots__/basics04.snap.png +0 -0
- package/test/basics/basics05/__snapshots__/basics05.snap.png +0 -0
- package/test/basics/basics06/__snapshots__/basics06.snap.png +0 -0
- package/test/basics/basics06/basics06.json +32 -7
- package/test/repros/kicad-step/__snapshots__/kicad-step-board.snap.png +0 -0
- package/test/repros/kicad-step/kicad-step.json +102 -22
- package/test/repros/repro01/__snapshots__/repro01.snap.png +0 -0
- package/test/repros/repro02/__snapshots__/repro02.snap.png +0 -0
package/lib/index.ts
CHANGED
|
@@ -91,6 +91,7 @@ export async function circuitJsonToStep(
|
|
|
91
91
|
// Get board center position (defaults to 0, 0 if not specified)
|
|
92
92
|
const boardCenterX = pcbBoard?.center?.x ?? 0
|
|
93
93
|
const boardCenterY = pcbBoard?.center?.y ?? 0
|
|
94
|
+
const halfBoardThickness = boardThickness / 2
|
|
94
95
|
|
|
95
96
|
if (!boardWidth || !boardHeight) {
|
|
96
97
|
throw new Error(
|
|
@@ -193,7 +194,9 @@ export async function circuitJsonToStep(
|
|
|
193
194
|
repo.add(
|
|
194
195
|
new VertexPoint(
|
|
195
196
|
"",
|
|
196
|
-
repo.add(
|
|
197
|
+
repo.add(
|
|
198
|
+
new CartesianPoint("", point.x, point.y, -halfBoardThickness),
|
|
199
|
+
),
|
|
197
200
|
),
|
|
198
201
|
),
|
|
199
202
|
)
|
|
@@ -201,7 +204,9 @@ export async function circuitJsonToStep(
|
|
|
201
204
|
repo.add(
|
|
202
205
|
new VertexPoint(
|
|
203
206
|
"",
|
|
204
|
-
repo.add(
|
|
207
|
+
repo.add(
|
|
208
|
+
new CartesianPoint("", point.x, point.y, halfBoardThickness),
|
|
209
|
+
),
|
|
205
210
|
),
|
|
206
211
|
),
|
|
207
212
|
)
|
|
@@ -210,14 +215,30 @@ export async function circuitJsonToStep(
|
|
|
210
215
|
const halfWidth = boardWidth / 2
|
|
211
216
|
const halfHeight = boardHeight / 2
|
|
212
217
|
const corners = [
|
|
213
|
-
[
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
[
|
|
219
|
-
|
|
220
|
-
|
|
218
|
+
[
|
|
219
|
+
boardCenterX - halfWidth,
|
|
220
|
+
boardCenterY - halfHeight,
|
|
221
|
+
-halfBoardThickness,
|
|
222
|
+
],
|
|
223
|
+
[
|
|
224
|
+
boardCenterX + halfWidth,
|
|
225
|
+
boardCenterY - halfHeight,
|
|
226
|
+
-halfBoardThickness,
|
|
227
|
+
],
|
|
228
|
+
[
|
|
229
|
+
boardCenterX + halfWidth,
|
|
230
|
+
boardCenterY + halfHeight,
|
|
231
|
+
-halfBoardThickness,
|
|
232
|
+
],
|
|
233
|
+
[
|
|
234
|
+
boardCenterX - halfWidth,
|
|
235
|
+
boardCenterY + halfHeight,
|
|
236
|
+
-halfBoardThickness,
|
|
237
|
+
],
|
|
238
|
+
[boardCenterX - halfWidth, boardCenterY - halfHeight, halfBoardThickness],
|
|
239
|
+
[boardCenterX + halfWidth, boardCenterY - halfHeight, halfBoardThickness],
|
|
240
|
+
[boardCenterX + halfWidth, boardCenterY + halfHeight, halfBoardThickness],
|
|
241
|
+
[boardCenterX - halfWidth, boardCenterY + halfHeight, halfBoardThickness],
|
|
221
242
|
]
|
|
222
243
|
const vertices = corners.map(([x, y, z]) =>
|
|
223
244
|
repo.add(
|
|
@@ -283,11 +304,11 @@ export async function circuitJsonToStep(
|
|
|
283
304
|
verticalEdges.push(createEdge(bottomVertices[i]!, topVertices[i]!))
|
|
284
305
|
}
|
|
285
306
|
|
|
286
|
-
const origin = repo.add(new CartesianPoint("", 0, 0,
|
|
307
|
+
const origin = repo.add(new CartesianPoint("", 0, 0, -halfBoardThickness))
|
|
287
308
|
const xDir = repo.add(new Direction("", 1, 0, 0))
|
|
288
309
|
const zDir = repo.add(new Direction("", 0, 0, 1))
|
|
289
310
|
|
|
290
|
-
// Bottom face (z
|
|
311
|
+
// Bottom face (z=-boardThickness/2, normal pointing down)
|
|
291
312
|
const bottomFrame = repo.add(
|
|
292
313
|
new Axis2Placement3D(
|
|
293
314
|
"",
|
|
@@ -314,11 +335,15 @@ export async function circuitJsonToStep(
|
|
|
314
335
|
const holeY = typeof hole.y === "number" ? hole.y : (hole.y as any).value
|
|
315
336
|
const radius = hole.hole_diameter / 2
|
|
316
337
|
|
|
317
|
-
const holeCenter = repo.add(
|
|
338
|
+
const holeCenter = repo.add(
|
|
339
|
+
new CartesianPoint("", holeX, holeY, -halfBoardThickness),
|
|
340
|
+
)
|
|
318
341
|
const holeVertex = repo.add(
|
|
319
342
|
new VertexPoint(
|
|
320
343
|
"",
|
|
321
|
-
repo.add(
|
|
344
|
+
repo.add(
|
|
345
|
+
new CartesianPoint("", holeX + radius, holeY, -halfBoardThickness),
|
|
346
|
+
),
|
|
322
347
|
),
|
|
323
348
|
)
|
|
324
349
|
const holePlacement = repo.add(
|
|
@@ -338,8 +363,8 @@ export async function circuitJsonToStep(
|
|
|
338
363
|
)
|
|
339
364
|
bottomHoleLoops.push(repo.add(new FaceBound("", holeLoop, true)))
|
|
340
365
|
} else if (holeShape === "rotated_pill" || holeShape === "pill") {
|
|
341
|
-
// Create rotated pill-shaped hole boundary at z
|
|
342
|
-
const pillLoop = createPillHoleLoop(repo, hole,
|
|
366
|
+
// Create rotated pill-shaped hole boundary at z=-boardThickness/2
|
|
367
|
+
const pillLoop = createPillHoleLoop(repo, hole, -halfBoardThickness, xDir)
|
|
343
368
|
bottomHoleLoops.push(repo.add(new FaceBound("", pillLoop, true)))
|
|
344
369
|
}
|
|
345
370
|
}
|
|
@@ -356,8 +381,8 @@ export async function circuitJsonToStep(
|
|
|
356
381
|
),
|
|
357
382
|
)
|
|
358
383
|
|
|
359
|
-
// Top face (z=boardThickness, normal pointing up)
|
|
360
|
-
const topOrigin = repo.add(new CartesianPoint("", 0, 0,
|
|
384
|
+
// Top face (z=boardThickness/2, normal pointing up)
|
|
385
|
+
const topOrigin = repo.add(new CartesianPoint("", 0, 0, halfBoardThickness))
|
|
361
386
|
const topFrame = repo.add(new Axis2Placement3D("", topOrigin, zDir, xDir))
|
|
362
387
|
const topPlane = repo.add(new Plane("", topFrame))
|
|
363
388
|
const topLoop = repo.add(
|
|
@@ -378,13 +403,13 @@ export async function circuitJsonToStep(
|
|
|
378
403
|
const radius = hole.hole_diameter / 2
|
|
379
404
|
|
|
380
405
|
const holeCenter = repo.add(
|
|
381
|
-
new CartesianPoint("", holeX, holeY,
|
|
406
|
+
new CartesianPoint("", holeX, holeY, halfBoardThickness),
|
|
382
407
|
)
|
|
383
408
|
const holeVertex = repo.add(
|
|
384
409
|
new VertexPoint(
|
|
385
410
|
"",
|
|
386
411
|
repo.add(
|
|
387
|
-
new CartesianPoint("", holeX + radius, holeY,
|
|
412
|
+
new CartesianPoint("", holeX + radius, holeY, halfBoardThickness),
|
|
388
413
|
),
|
|
389
414
|
),
|
|
390
415
|
)
|
|
@@ -400,8 +425,8 @@ export async function circuitJsonToStep(
|
|
|
400
425
|
)
|
|
401
426
|
topHoleLoops.push(repo.add(new FaceBound("", holeLoop, true)))
|
|
402
427
|
} else if (holeShape === "rotated_pill" || holeShape === "pill") {
|
|
403
|
-
// Create rotated pill-shaped hole boundary at z=boardThickness
|
|
404
|
-
const pillLoop = createPillHoleLoop(repo, hole,
|
|
428
|
+
// Create rotated pill-shaped hole boundary at z=boardThickness/2
|
|
429
|
+
const pillLoop = createPillHoleLoop(repo, hole, halfBoardThickness, xDir)
|
|
405
430
|
topHoleLoops.push(repo.add(new FaceBound("", pillLoop, true)))
|
|
406
431
|
}
|
|
407
432
|
}
|
|
@@ -430,7 +455,7 @@ export async function circuitJsonToStep(
|
|
|
430
455
|
const edgeDir = {
|
|
431
456
|
x: bottomV2.x - bottomV1.x,
|
|
432
457
|
y: bottomV2.y - bottomV1.y,
|
|
433
|
-
z:
|
|
458
|
+
z: -halfBoardThickness,
|
|
434
459
|
}
|
|
435
460
|
// Normal is perpendicular (rotate 90 degrees clockwise in XY plane for outward facing)
|
|
436
461
|
const normalDir = repo.add(new Direction("", edgeDir.y, -edgeDir.x, 0))
|
|
@@ -471,11 +496,15 @@ export async function circuitJsonToStep(
|
|
|
471
496
|
const radius = hole.hole_diameter / 2
|
|
472
497
|
|
|
473
498
|
// Create circular edges at bottom and top
|
|
474
|
-
const bottomHoleCenter = repo.add(
|
|
499
|
+
const bottomHoleCenter = repo.add(
|
|
500
|
+
new CartesianPoint("", holeX, holeY, -halfBoardThickness),
|
|
501
|
+
)
|
|
475
502
|
const bottomHoleVertex = repo.add(
|
|
476
503
|
new VertexPoint(
|
|
477
504
|
"",
|
|
478
|
-
repo.add(
|
|
505
|
+
repo.add(
|
|
506
|
+
new CartesianPoint("", holeX + radius, holeY, -halfBoardThickness),
|
|
507
|
+
),
|
|
479
508
|
),
|
|
480
509
|
)
|
|
481
510
|
const bottomHolePlacement = repo.add(
|
|
@@ -500,13 +529,13 @@ export async function circuitJsonToStep(
|
|
|
500
529
|
)
|
|
501
530
|
|
|
502
531
|
const topHoleCenter = repo.add(
|
|
503
|
-
new CartesianPoint("", holeX, holeY,
|
|
532
|
+
new CartesianPoint("", holeX, holeY, halfBoardThickness),
|
|
504
533
|
)
|
|
505
534
|
const topHoleVertex = repo.add(
|
|
506
535
|
new VertexPoint(
|
|
507
536
|
"",
|
|
508
537
|
repo.add(
|
|
509
|
-
new CartesianPoint("", holeX + radius, holeY,
|
|
538
|
+
new CartesianPoint("", holeX + radius, holeY, halfBoardThickness),
|
|
510
539
|
),
|
|
511
540
|
),
|
|
512
541
|
)
|
|
@@ -547,7 +576,8 @@ export async function circuitJsonToStep(
|
|
|
547
576
|
const pillFaces = createPillCylindricalFaces(
|
|
548
577
|
repo,
|
|
549
578
|
hole,
|
|
550
|
-
|
|
579
|
+
-halfBoardThickness,
|
|
580
|
+
halfBoardThickness,
|
|
551
581
|
xDir,
|
|
552
582
|
zDir,
|
|
553
583
|
)
|
package/lib/mesh-generation.ts
CHANGED
|
@@ -1,23 +1,8 @@
|
|
|
1
1
|
import type { CircuitJson } from "circuit-json"
|
|
2
|
-
import type { Ref } from "stepts"
|
|
3
|
-
import
|
|
4
|
-
import
|
|
5
|
-
import {
|
|
6
|
-
AdvancedFace,
|
|
7
|
-
Axis2Placement3D,
|
|
8
|
-
CartesianPoint,
|
|
9
|
-
ClosedShell,
|
|
10
|
-
Direction,
|
|
11
|
-
EdgeCurve,
|
|
12
|
-
EdgeLoop,
|
|
13
|
-
FaceOuterBound,
|
|
14
|
-
Line,
|
|
15
|
-
ManifoldSolidBrep,
|
|
16
|
-
OrientedEdge,
|
|
17
|
-
Plane,
|
|
18
|
-
Vector,
|
|
19
|
-
VertexPoint,
|
|
20
|
-
} from "stepts"
|
|
2
|
+
import type { Ref, Repository } from "stepts"
|
|
3
|
+
import { ManifoldSolidBrep } from "stepts"
|
|
4
|
+
import { createSceneBoxSolid } from "./scene-box-to-step"
|
|
5
|
+
import type { SceneBox } from "./scene-geometry"
|
|
21
6
|
|
|
22
7
|
export interface MeshGenerationOptions {
|
|
23
8
|
/** Repository to add STEP entities to */
|
|
@@ -36,215 +21,6 @@ export interface MeshGenerationOptions {
|
|
|
36
21
|
pcbComponentIdsWithStepUrl?: Set<string>
|
|
37
22
|
}
|
|
38
23
|
|
|
39
|
-
/**
|
|
40
|
-
* Generates triangles for a box mesh
|
|
41
|
-
*/
|
|
42
|
-
function createBoxTriangles(box: {
|
|
43
|
-
center: { x: number; y: number; z: number }
|
|
44
|
-
size: { x: number; y: number; z: number }
|
|
45
|
-
rotation?: { x: number; y: number; z: number }
|
|
46
|
-
}): GLTFTriangle[] {
|
|
47
|
-
const { center, size } = box
|
|
48
|
-
const halfX = size.x / 2
|
|
49
|
-
const halfY = size.y / 2
|
|
50
|
-
const halfZ = size.z / 2
|
|
51
|
-
|
|
52
|
-
// Define 8 corners of the box
|
|
53
|
-
const corners = [
|
|
54
|
-
{ x: -halfX, y: -halfY, z: -halfZ },
|
|
55
|
-
{ x: halfX, y: -halfY, z: -halfZ },
|
|
56
|
-
{ x: halfX, y: halfY, z: -halfZ },
|
|
57
|
-
{ x: -halfX, y: halfY, z: -halfZ },
|
|
58
|
-
{ x: -halfX, y: -halfY, z: halfZ },
|
|
59
|
-
{ x: halfX, y: -halfY, z: halfZ },
|
|
60
|
-
{ x: halfX, y: halfY, z: halfZ },
|
|
61
|
-
{ x: -halfX, y: halfY, z: halfZ },
|
|
62
|
-
].map((p) => ({ x: p.x + center.x, y: p.y + center.y, z: p.z + center.z }))
|
|
63
|
-
|
|
64
|
-
// Define triangles for each face (2 triangles per face)
|
|
65
|
-
const triangles: GLTFTriangle[] = [
|
|
66
|
-
// Bottom face (z = -halfZ)
|
|
67
|
-
{
|
|
68
|
-
vertices: [corners[0]!, corners[1]!, corners[2]!],
|
|
69
|
-
normal: { x: 0, y: 0, z: -1 },
|
|
70
|
-
},
|
|
71
|
-
{
|
|
72
|
-
vertices: [corners[0]!, corners[2]!, corners[3]!],
|
|
73
|
-
normal: { x: 0, y: 0, z: -1 },
|
|
74
|
-
},
|
|
75
|
-
// Top face (z = halfZ)
|
|
76
|
-
{
|
|
77
|
-
vertices: [corners[4]!, corners[6]!, corners[5]!],
|
|
78
|
-
normal: { x: 0, y: 0, z: 1 },
|
|
79
|
-
},
|
|
80
|
-
{
|
|
81
|
-
vertices: [corners[4]!, corners[7]!, corners[6]!],
|
|
82
|
-
normal: { x: 0, y: 0, z: 1 },
|
|
83
|
-
},
|
|
84
|
-
// Front face (y = -halfY)
|
|
85
|
-
{
|
|
86
|
-
vertices: [corners[0]!, corners[5]!, corners[1]!],
|
|
87
|
-
normal: { x: 0, y: -1, z: 0 },
|
|
88
|
-
},
|
|
89
|
-
{
|
|
90
|
-
vertices: [corners[0]!, corners[4]!, corners[5]!],
|
|
91
|
-
normal: { x: 0, y: -1, z: 0 },
|
|
92
|
-
},
|
|
93
|
-
// Back face (y = halfY)
|
|
94
|
-
{
|
|
95
|
-
vertices: [corners[2]!, corners[6]!, corners[7]!],
|
|
96
|
-
normal: { x: 0, y: 1, z: 0 },
|
|
97
|
-
},
|
|
98
|
-
{
|
|
99
|
-
vertices: [corners[2]!, corners[7]!, corners[3]!],
|
|
100
|
-
normal: { x: 0, y: 1, z: 0 },
|
|
101
|
-
},
|
|
102
|
-
// Left face (x = -halfX)
|
|
103
|
-
{
|
|
104
|
-
vertices: [corners[0]!, corners[3]!, corners[7]!],
|
|
105
|
-
normal: { x: -1, y: 0, z: 0 },
|
|
106
|
-
},
|
|
107
|
-
{
|
|
108
|
-
vertices: [corners[0]!, corners[7]!, corners[4]!],
|
|
109
|
-
normal: { x: -1, y: 0, z: 0 },
|
|
110
|
-
},
|
|
111
|
-
// Right face (x = halfX)
|
|
112
|
-
{
|
|
113
|
-
vertices: [corners[1]!, corners[6]!, corners[2]!],
|
|
114
|
-
normal: { x: 1, y: 0, z: 0 },
|
|
115
|
-
},
|
|
116
|
-
{
|
|
117
|
-
vertices: [corners[1]!, corners[5]!, corners[6]!],
|
|
118
|
-
normal: { x: 1, y: 0, z: 0 },
|
|
119
|
-
},
|
|
120
|
-
]
|
|
121
|
-
|
|
122
|
-
return triangles
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
/**
|
|
126
|
-
* Creates STEP faces from GLTF triangles
|
|
127
|
-
*/
|
|
128
|
-
function createStepFacesFromTriangles(
|
|
129
|
-
repo: Repository,
|
|
130
|
-
triangles: GLTFTriangle[],
|
|
131
|
-
): Ref<AdvancedFace>[] {
|
|
132
|
-
const faces: Ref<AdvancedFace>[] = []
|
|
133
|
-
|
|
134
|
-
for (const triangle of triangles) {
|
|
135
|
-
// Create vertices for triangle
|
|
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
|
-
|
|
176
|
-
// Create edges between vertices
|
|
177
|
-
const p1 = v1.resolve(repo).pnt.resolve(repo)
|
|
178
|
-
const p2 = v2.resolve(repo).pnt.resolve(repo)
|
|
179
|
-
|
|
180
|
-
const createEdge = (
|
|
181
|
-
vStart: Ref<VertexPoint>,
|
|
182
|
-
vEnd: Ref<VertexPoint>,
|
|
183
|
-
): Ref<EdgeCurve> => {
|
|
184
|
-
const pStart = vStart.resolve(repo).pnt.resolve(repo)
|
|
185
|
-
const pEnd = vEnd.resolve(repo).pnt.resolve(repo)
|
|
186
|
-
const dir = repo.add(
|
|
187
|
-
new Direction(
|
|
188
|
-
"",
|
|
189
|
-
pEnd.x - pStart.x,
|
|
190
|
-
pEnd.y - pStart.y,
|
|
191
|
-
pEnd.z - pStart.z,
|
|
192
|
-
),
|
|
193
|
-
)
|
|
194
|
-
const vec = repo.add(new Vector("", dir, 1))
|
|
195
|
-
const line = repo.add(new Line("", vStart.resolve(repo).pnt, vec))
|
|
196
|
-
return repo.add(new EdgeCurve("", vStart, vEnd, line, true))
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
const edge1 = createEdge(v1, v2)
|
|
200
|
-
const edge2 = createEdge(v2, v3)
|
|
201
|
-
const edge3 = createEdge(v3, v1)
|
|
202
|
-
|
|
203
|
-
// Create edge loop for triangle
|
|
204
|
-
const edgeLoop = repo.add(
|
|
205
|
-
new EdgeLoop("", [
|
|
206
|
-
repo.add(new OrientedEdge("", edge1, true)),
|
|
207
|
-
repo.add(new OrientedEdge("", edge2, true)),
|
|
208
|
-
repo.add(new OrientedEdge("", edge3, true)),
|
|
209
|
-
]),
|
|
210
|
-
)
|
|
211
|
-
|
|
212
|
-
// Create planar surface using triangle normal
|
|
213
|
-
const normalDir = repo.add(
|
|
214
|
-
new Direction(
|
|
215
|
-
"",
|
|
216
|
-
triangle.normal.x,
|
|
217
|
-
triangle.normal.y,
|
|
218
|
-
triangle.normal.z,
|
|
219
|
-
),
|
|
220
|
-
)
|
|
221
|
-
|
|
222
|
-
// Use first vertex as origin, calculate reference direction from first edge
|
|
223
|
-
const refX = p2.x - p1.x
|
|
224
|
-
const refY = p2.y - p1.y
|
|
225
|
-
const refZ = p2.z - p1.z
|
|
226
|
-
const refDir = repo.add(new Direction("", refX, refY, refZ))
|
|
227
|
-
|
|
228
|
-
const placement = repo.add(
|
|
229
|
-
new Axis2Placement3D("", v1.resolve(repo).pnt, normalDir, refDir),
|
|
230
|
-
)
|
|
231
|
-
const plane = repo.add(new Plane("", placement))
|
|
232
|
-
|
|
233
|
-
// Create face
|
|
234
|
-
const face = repo.add(
|
|
235
|
-
new AdvancedFace(
|
|
236
|
-
"",
|
|
237
|
-
[repo.add(new FaceOuterBound("", edgeLoop, true))],
|
|
238
|
-
plane,
|
|
239
|
-
true,
|
|
240
|
-
),
|
|
241
|
-
)
|
|
242
|
-
faces.push(face)
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
return faces
|
|
246
|
-
}
|
|
247
|
-
|
|
248
24
|
/**
|
|
249
25
|
* Generates component meshes from circuit JSON and converts them to STEP solids
|
|
250
26
|
*
|
|
@@ -264,46 +40,48 @@ export async function generateComponentMeshes(
|
|
|
264
40
|
excludePcbComponentIds,
|
|
265
41
|
pcbComponentIdsWithStepUrl,
|
|
266
42
|
} = options
|
|
43
|
+
|
|
267
44
|
const solids: Ref<ManifoldSolidBrep>[] = []
|
|
45
|
+
|
|
268
46
|
try {
|
|
269
|
-
// Filter circuit JSON and optionally remove model URLs
|
|
270
47
|
const filteredCircuitJson = circuitJson
|
|
271
|
-
.filter((
|
|
272
|
-
if (
|
|
48
|
+
.filter((element) => {
|
|
49
|
+
if (element.type === "pcb_board") return false
|
|
50
|
+
|
|
273
51
|
if (
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
excludeCadComponentIds?.has(
|
|
52
|
+
element.type === "cad_component" &&
|
|
53
|
+
element.cad_component_id &&
|
|
54
|
+
excludeCadComponentIds?.has(element.cad_component_id)
|
|
277
55
|
) {
|
|
278
56
|
return false
|
|
279
57
|
}
|
|
58
|
+
|
|
280
59
|
if (
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
excludePcbComponentIds?.has(
|
|
60
|
+
element.type === "pcb_component" &&
|
|
61
|
+
element.pcb_component_id &&
|
|
62
|
+
excludePcbComponentIds?.has(element.pcb_component_id)
|
|
284
63
|
) {
|
|
285
64
|
return false
|
|
286
65
|
}
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
if (e.type === "cad_component" && e.model_step_url) {
|
|
66
|
+
|
|
67
|
+
if (element.type === "cad_component" && element.model_step_url) {
|
|
290
68
|
return false
|
|
291
69
|
}
|
|
292
|
-
|
|
70
|
+
|
|
293
71
|
if (
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
pcbComponentIdsWithStepUrl?.has(
|
|
72
|
+
element.type === "cad_component" &&
|
|
73
|
+
element.pcb_component_id &&
|
|
74
|
+
pcbComponentIdsWithStepUrl?.has(element.pcb_component_id)
|
|
297
75
|
) {
|
|
298
76
|
return false
|
|
299
77
|
}
|
|
78
|
+
|
|
300
79
|
return true
|
|
301
80
|
})
|
|
302
|
-
.map((
|
|
303
|
-
if (!includeExternalMeshes &&
|
|
304
|
-
// Remove model_*_url fields to avoid hanging on external model fetches
|
|
81
|
+
.map((element) => {
|
|
82
|
+
if (!includeExternalMeshes && element.type === "cad_component") {
|
|
305
83
|
return {
|
|
306
|
-
...
|
|
84
|
+
...element,
|
|
307
85
|
model_3mf_url: undefined,
|
|
308
86
|
model_obj_url: undefined,
|
|
309
87
|
model_stl_url: undefined,
|
|
@@ -311,66 +89,25 @@ export async function generateComponentMeshes(
|
|
|
311
89
|
model_gltf_url: undefined,
|
|
312
90
|
}
|
|
313
91
|
}
|
|
314
|
-
|
|
92
|
+
|
|
93
|
+
return element
|
|
315
94
|
})
|
|
316
95
|
|
|
317
|
-
// Dynamically import circuit-json-to-gltf to avoid bundling native dependencies
|
|
318
|
-
// Use a variable to prevent the bundler from statically analyzing the import
|
|
319
96
|
const gltfModule = "circuit-json-to-gltf"
|
|
320
97
|
const { convertCircuitJsonTo3D } = await import(
|
|
321
98
|
/* @vite-ignore */ gltfModule
|
|
322
99
|
)
|
|
323
100
|
|
|
324
|
-
// Convert circuit JSON to 3D scene
|
|
325
101
|
const scene3d = await convertCircuitJsonTo3D(filteredCircuitJson, {
|
|
326
102
|
boardThickness,
|
|
327
103
|
renderBoardTextures: false,
|
|
328
104
|
})
|
|
329
105
|
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
for (const box of scene3d.boxes) {
|
|
333
|
-
if (box.mesh && "triangles" in box.mesh) {
|
|
334
|
-
allTriangles.push(...box.mesh.triangles)
|
|
335
|
-
} else {
|
|
336
|
-
// Generate simple box mesh for this component
|
|
337
|
-
const boxTriangles = createBoxTriangles(box)
|
|
338
|
-
allTriangles.push(...boxTriangles)
|
|
339
|
-
}
|
|
340
|
-
}
|
|
341
|
-
|
|
342
|
-
// Create STEP faces from triangles if we have any
|
|
343
|
-
if (allTriangles.length > 0) {
|
|
344
|
-
// Transform triangles from GLTF XZ plane (Y=up) to STEP XY plane (Z=up)
|
|
345
|
-
const transformedTriangles = allTriangles.map((tri) => ({
|
|
346
|
-
vertices: tri.vertices.map((v) => ({
|
|
347
|
-
x: v.x,
|
|
348
|
-
y: v.z, // GLTF Z becomes STEP Y
|
|
349
|
-
z: v.y, // GLTF Y becomes STEP Z
|
|
350
|
-
})),
|
|
351
|
-
normal: {
|
|
352
|
-
x: tri.normal.x,
|
|
353
|
-
y: tri.normal.z, // GLTF Z becomes STEP Y
|
|
354
|
-
z: tri.normal.y, // GLTF Y becomes STEP Z
|
|
355
|
-
},
|
|
356
|
-
}))
|
|
357
|
-
const componentFaces = createStepFacesFromTriangles(
|
|
358
|
-
repo,
|
|
359
|
-
transformedTriangles as any,
|
|
360
|
-
)
|
|
361
|
-
|
|
362
|
-
// Create closed shell and solid for components
|
|
363
|
-
const componentShell = repo.add(
|
|
364
|
-
new ClosedShell("", componentFaces as any),
|
|
365
|
-
)
|
|
366
|
-
const componentSolid = repo.add(
|
|
367
|
-
new ManifoldSolidBrep("Components", componentShell),
|
|
368
|
-
)
|
|
369
|
-
solids.push(componentSolid)
|
|
106
|
+
for (const box of scene3d.boxes as SceneBox[]) {
|
|
107
|
+
solids.push(createSceneBoxSolid(repo, box))
|
|
370
108
|
}
|
|
371
109
|
} catch (error) {
|
|
372
110
|
console.warn("Failed to generate component mesh:", error)
|
|
373
|
-
// Continue without components if generation fails
|
|
374
111
|
}
|
|
375
112
|
|
|
376
113
|
return solids
|