circuit-json-to-step 0.0.1
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/.claude/settings.local.json +16 -0
- package/CLAUDE.md +10 -0
- package/LICENSE +21 -0
- package/README.md +6 -0
- package/biome.json +100 -0
- package/bunfig.toml +5 -0
- package/dist/index.d.ts +22 -0
- package/dist/index.js +705 -0
- package/lib/index.ts +545 -0
- package/lib/mesh-generation.ts +333 -0
- package/package.json +29 -0
- package/test/basics/basics01/basics01.json +40 -0
- package/test/basics/basics01/basics01.test.ts +54 -0
- package/test/basics/basics02/basics02.json +19 -0
- package/test/basics/basics02/basics02.test.ts +38 -0
- package/test/basics/basics03/basics03.json +49 -0
- package/test/basics/basics03/basics03.test.ts +38 -0
- package/test/basics/basics04/basics04.json +52 -0
- package/test/basics/basics04/basics04.test.ts +49 -0
- package/test/repros/repro01/repro01.json +2917 -0
- package/test/repros/repro01/repro01.test.ts +48 -0
- package/test/utils/occt/importer.ts +119 -0
- package/tsconfig.json +30 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,705 @@
|
|
|
1
|
+
// lib/index.ts
|
|
2
|
+
import {
|
|
3
|
+
Repository,
|
|
4
|
+
ApplicationContext,
|
|
5
|
+
ApplicationProtocolDefinition,
|
|
6
|
+
ProductContext,
|
|
7
|
+
Product,
|
|
8
|
+
ProductDefinitionContext,
|
|
9
|
+
ProductDefinitionFormation,
|
|
10
|
+
ProductDefinition,
|
|
11
|
+
ProductDefinitionShape,
|
|
12
|
+
Unknown,
|
|
13
|
+
CartesianPoint as CartesianPoint2,
|
|
14
|
+
Direction as Direction2,
|
|
15
|
+
Axis2Placement3D as Axis2Placement3D2,
|
|
16
|
+
Plane as Plane2,
|
|
17
|
+
CylindricalSurface,
|
|
18
|
+
VertexPoint as VertexPoint2,
|
|
19
|
+
EdgeCurve as EdgeCurve2,
|
|
20
|
+
Line as Line2,
|
|
21
|
+
Vector as Vector2,
|
|
22
|
+
EdgeLoop as EdgeLoop2,
|
|
23
|
+
OrientedEdge as OrientedEdge2,
|
|
24
|
+
FaceOuterBound as FaceOuterBound2,
|
|
25
|
+
FaceBound,
|
|
26
|
+
AdvancedFace as AdvancedFace2,
|
|
27
|
+
Circle,
|
|
28
|
+
ClosedShell as ClosedShell2,
|
|
29
|
+
ManifoldSolidBrep as ManifoldSolidBrep2,
|
|
30
|
+
ColourRgb,
|
|
31
|
+
FillAreaStyleColour,
|
|
32
|
+
FillAreaStyle,
|
|
33
|
+
SurfaceStyleFillArea,
|
|
34
|
+
SurfaceSideStyle,
|
|
35
|
+
SurfaceStyleUsage,
|
|
36
|
+
PresentationStyleAssignment,
|
|
37
|
+
StyledItem,
|
|
38
|
+
MechanicalDesignGeometricPresentationRepresentation,
|
|
39
|
+
AdvancedBrepShapeRepresentation,
|
|
40
|
+
ShapeDefinitionRepresentation
|
|
41
|
+
} from "stepts";
|
|
42
|
+
|
|
43
|
+
// lib/mesh-generation.ts
|
|
44
|
+
import { convertCircuitJsonTo3D } from "circuit-json-to-gltf";
|
|
45
|
+
import {
|
|
46
|
+
AdvancedFace,
|
|
47
|
+
Axis2Placement3D,
|
|
48
|
+
CartesianPoint,
|
|
49
|
+
ClosedShell,
|
|
50
|
+
Direction,
|
|
51
|
+
EdgeCurve,
|
|
52
|
+
EdgeLoop,
|
|
53
|
+
FaceOuterBound,
|
|
54
|
+
Line,
|
|
55
|
+
ManifoldSolidBrep,
|
|
56
|
+
OrientedEdge,
|
|
57
|
+
Plane,
|
|
58
|
+
Vector,
|
|
59
|
+
VertexPoint
|
|
60
|
+
} from "stepts";
|
|
61
|
+
function createBoxTriangles(box) {
|
|
62
|
+
const { center, size } = box;
|
|
63
|
+
const halfX = size.x / 2;
|
|
64
|
+
const halfY = size.y / 2;
|
|
65
|
+
const halfZ = size.z / 2;
|
|
66
|
+
const corners = [
|
|
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
|
+
{ x: -halfX, y: halfY, z: halfZ }
|
|
75
|
+
].map((p) => ({ x: p.x + center.x, y: p.y + center.y, z: p.z + center.z }));
|
|
76
|
+
const triangles = [
|
|
77
|
+
// Bottom face (z = -halfZ)
|
|
78
|
+
{
|
|
79
|
+
vertices: [corners[0], corners[1], corners[2]],
|
|
80
|
+
normal: { x: 0, y: 0, z: -1 }
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
vertices: [corners[0], corners[2], corners[3]],
|
|
84
|
+
normal: { x: 0, y: 0, z: -1 }
|
|
85
|
+
},
|
|
86
|
+
// Top face (z = halfZ)
|
|
87
|
+
{
|
|
88
|
+
vertices: [corners[4], corners[6], corners[5]],
|
|
89
|
+
normal: { x: 0, y: 0, z: 1 }
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
vertices: [corners[4], corners[7], corners[6]],
|
|
93
|
+
normal: { x: 0, y: 0, z: 1 }
|
|
94
|
+
},
|
|
95
|
+
// Front face (y = -halfY)
|
|
96
|
+
{
|
|
97
|
+
vertices: [corners[0], corners[5], corners[1]],
|
|
98
|
+
normal: { x: 0, y: -1, z: 0 }
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
vertices: [corners[0], corners[4], corners[5]],
|
|
102
|
+
normal: { x: 0, y: -1, z: 0 }
|
|
103
|
+
},
|
|
104
|
+
// Back face (y = halfY)
|
|
105
|
+
{
|
|
106
|
+
vertices: [corners[2], corners[6], corners[7]],
|
|
107
|
+
normal: { x: 0, y: 1, z: 0 }
|
|
108
|
+
},
|
|
109
|
+
{
|
|
110
|
+
vertices: [corners[2], corners[7], corners[3]],
|
|
111
|
+
normal: { x: 0, y: 1, z: 0 }
|
|
112
|
+
},
|
|
113
|
+
// Left face (x = -halfX)
|
|
114
|
+
{
|
|
115
|
+
vertices: [corners[0], corners[3], corners[7]],
|
|
116
|
+
normal: { x: -1, y: 0, z: 0 }
|
|
117
|
+
},
|
|
118
|
+
{
|
|
119
|
+
vertices: [corners[0], corners[7], corners[4]],
|
|
120
|
+
normal: { x: -1, y: 0, z: 0 }
|
|
121
|
+
},
|
|
122
|
+
// Right face (x = halfX)
|
|
123
|
+
{
|
|
124
|
+
vertices: [corners[1], corners[6], corners[2]],
|
|
125
|
+
normal: { x: 1, y: 0, z: 0 }
|
|
126
|
+
},
|
|
127
|
+
{
|
|
128
|
+
vertices: [corners[1], corners[5], corners[6]],
|
|
129
|
+
normal: { x: 1, y: 0, z: 0 }
|
|
130
|
+
}
|
|
131
|
+
];
|
|
132
|
+
return triangles;
|
|
133
|
+
}
|
|
134
|
+
function createStepFacesFromTriangles(repo, triangles) {
|
|
135
|
+
const faces = [];
|
|
136
|
+
for (const triangle of triangles) {
|
|
137
|
+
const v1 = repo.add(
|
|
138
|
+
new VertexPoint(
|
|
139
|
+
"",
|
|
140
|
+
repo.add(
|
|
141
|
+
new CartesianPoint(
|
|
142
|
+
"",
|
|
143
|
+
triangle.vertices[0].x,
|
|
144
|
+
triangle.vertices[0].y,
|
|
145
|
+
triangle.vertices[0].z
|
|
146
|
+
)
|
|
147
|
+
)
|
|
148
|
+
)
|
|
149
|
+
);
|
|
150
|
+
const v2 = repo.add(
|
|
151
|
+
new VertexPoint(
|
|
152
|
+
"",
|
|
153
|
+
repo.add(
|
|
154
|
+
new CartesianPoint(
|
|
155
|
+
"",
|
|
156
|
+
triangle.vertices[1].x,
|
|
157
|
+
triangle.vertices[1].y,
|
|
158
|
+
triangle.vertices[1].z
|
|
159
|
+
)
|
|
160
|
+
)
|
|
161
|
+
)
|
|
162
|
+
);
|
|
163
|
+
const v3 = repo.add(
|
|
164
|
+
new VertexPoint(
|
|
165
|
+
"",
|
|
166
|
+
repo.add(
|
|
167
|
+
new CartesianPoint(
|
|
168
|
+
"",
|
|
169
|
+
triangle.vertices[2].x,
|
|
170
|
+
triangle.vertices[2].y,
|
|
171
|
+
triangle.vertices[2].z
|
|
172
|
+
)
|
|
173
|
+
)
|
|
174
|
+
)
|
|
175
|
+
);
|
|
176
|
+
const p1 = v1.resolve(repo).pnt.resolve(repo);
|
|
177
|
+
const p2 = v2.resolve(repo).pnt.resolve(repo);
|
|
178
|
+
const createEdge = (vStart, vEnd) => {
|
|
179
|
+
const pStart = vStart.resolve(repo).pnt.resolve(repo);
|
|
180
|
+
const pEnd = vEnd.resolve(repo).pnt.resolve(repo);
|
|
181
|
+
const dir = repo.add(
|
|
182
|
+
new Direction(
|
|
183
|
+
"",
|
|
184
|
+
pEnd.x - pStart.x,
|
|
185
|
+
pEnd.y - pStart.y,
|
|
186
|
+
pEnd.z - pStart.z
|
|
187
|
+
)
|
|
188
|
+
);
|
|
189
|
+
const vec = repo.add(new Vector("", dir, 1));
|
|
190
|
+
const line = repo.add(new Line("", vStart.resolve(repo).pnt, vec));
|
|
191
|
+
return repo.add(new EdgeCurve("", vStart, vEnd, line, true));
|
|
192
|
+
};
|
|
193
|
+
const edge1 = createEdge(v1, v2);
|
|
194
|
+
const edge2 = createEdge(v2, v3);
|
|
195
|
+
const edge3 = createEdge(v3, v1);
|
|
196
|
+
const edgeLoop = repo.add(
|
|
197
|
+
new EdgeLoop("", [
|
|
198
|
+
repo.add(new OrientedEdge("", edge1, true)),
|
|
199
|
+
repo.add(new OrientedEdge("", edge2, true)),
|
|
200
|
+
repo.add(new OrientedEdge("", edge3, true))
|
|
201
|
+
])
|
|
202
|
+
);
|
|
203
|
+
const normalDir = repo.add(
|
|
204
|
+
new Direction(
|
|
205
|
+
"",
|
|
206
|
+
triangle.normal.x,
|
|
207
|
+
triangle.normal.y,
|
|
208
|
+
triangle.normal.z
|
|
209
|
+
)
|
|
210
|
+
);
|
|
211
|
+
const refX = p2.x - p1.x;
|
|
212
|
+
const refY = p2.y - p1.y;
|
|
213
|
+
const refZ = p2.z - p1.z;
|
|
214
|
+
const refDir = repo.add(new Direction("", refX, refY, refZ));
|
|
215
|
+
const placement = repo.add(
|
|
216
|
+
new Axis2Placement3D("", v1.resolve(repo).pnt, normalDir, refDir)
|
|
217
|
+
);
|
|
218
|
+
const plane = repo.add(new Plane("", placement));
|
|
219
|
+
const face = repo.add(
|
|
220
|
+
new AdvancedFace(
|
|
221
|
+
"",
|
|
222
|
+
[repo.add(new FaceOuterBound("", edgeLoop, true))],
|
|
223
|
+
plane,
|
|
224
|
+
true
|
|
225
|
+
)
|
|
226
|
+
);
|
|
227
|
+
faces.push(face);
|
|
228
|
+
}
|
|
229
|
+
return faces;
|
|
230
|
+
}
|
|
231
|
+
async function generateComponentMeshes(options) {
|
|
232
|
+
const {
|
|
233
|
+
repo,
|
|
234
|
+
circuitJson,
|
|
235
|
+
boardThickness,
|
|
236
|
+
includeExternalMeshes = false
|
|
237
|
+
} = options;
|
|
238
|
+
const solids = [];
|
|
239
|
+
try {
|
|
240
|
+
const filteredCircuitJson = circuitJson.filter((e) => e.type !== "pcb_board").map((e) => {
|
|
241
|
+
if (!includeExternalMeshes && e.type === "cad_component") {
|
|
242
|
+
return {
|
|
243
|
+
...e,
|
|
244
|
+
model_3mf_url: void 0,
|
|
245
|
+
model_obj_url: void 0,
|
|
246
|
+
model_stl_url: void 0,
|
|
247
|
+
model_glb_url: void 0,
|
|
248
|
+
model_gltf_url: void 0
|
|
249
|
+
};
|
|
250
|
+
}
|
|
251
|
+
return e;
|
|
252
|
+
});
|
|
253
|
+
const scene3d = await convertCircuitJsonTo3D(filteredCircuitJson, {
|
|
254
|
+
boardThickness,
|
|
255
|
+
renderBoardTextures: false
|
|
256
|
+
});
|
|
257
|
+
const allTriangles = [];
|
|
258
|
+
for (const box of scene3d.boxes) {
|
|
259
|
+
if (box.mesh && "triangles" in box.mesh) {
|
|
260
|
+
allTriangles.push(...box.mesh.triangles);
|
|
261
|
+
} else {
|
|
262
|
+
const boxTriangles = createBoxTriangles(box);
|
|
263
|
+
allTriangles.push(...boxTriangles);
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
if (allTriangles.length > 0) {
|
|
267
|
+
const transformedTriangles = allTriangles.map((tri) => ({
|
|
268
|
+
vertices: tri.vertices.map((v) => ({
|
|
269
|
+
x: v.x,
|
|
270
|
+
y: v.z,
|
|
271
|
+
// GLTF Z becomes STEP Y
|
|
272
|
+
z: v.y
|
|
273
|
+
// GLTF Y becomes STEP Z
|
|
274
|
+
})),
|
|
275
|
+
normal: {
|
|
276
|
+
x: tri.normal.x,
|
|
277
|
+
y: tri.normal.z,
|
|
278
|
+
// GLTF Z becomes STEP Y
|
|
279
|
+
z: tri.normal.y
|
|
280
|
+
// GLTF Y becomes STEP Z
|
|
281
|
+
}
|
|
282
|
+
}));
|
|
283
|
+
const componentFaces = createStepFacesFromTriangles(
|
|
284
|
+
repo,
|
|
285
|
+
transformedTriangles
|
|
286
|
+
);
|
|
287
|
+
const componentShell = repo.add(
|
|
288
|
+
new ClosedShell("", componentFaces)
|
|
289
|
+
);
|
|
290
|
+
const componentSolid = repo.add(
|
|
291
|
+
new ManifoldSolidBrep("Components", componentShell)
|
|
292
|
+
);
|
|
293
|
+
solids.push(componentSolid);
|
|
294
|
+
}
|
|
295
|
+
} catch (error) {
|
|
296
|
+
console.warn("Failed to generate component mesh:", error);
|
|
297
|
+
}
|
|
298
|
+
return solids;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
// lib/index.ts
|
|
302
|
+
async function circuitJsonToStep(circuitJson, options = {}) {
|
|
303
|
+
const repo = new Repository();
|
|
304
|
+
const pcbBoard = circuitJson.find((item) => item.type === "pcb_board");
|
|
305
|
+
const holes = circuitJson.filter(
|
|
306
|
+
(item) => item.type === "pcb_hole" || item.type === "pcb_plated_hole"
|
|
307
|
+
);
|
|
308
|
+
const boardWidth = options.boardWidth ?? pcbBoard?.width;
|
|
309
|
+
const boardHeight = options.boardHeight ?? pcbBoard?.height;
|
|
310
|
+
const boardThickness = options.boardThickness ?? pcbBoard?.thickness ?? 1.6;
|
|
311
|
+
const productName = options.productName ?? "PCB";
|
|
312
|
+
const boardCenterX = pcbBoard?.center?.x ?? 0;
|
|
313
|
+
const boardCenterY = pcbBoard?.center?.y ?? 0;
|
|
314
|
+
if (!boardWidth || !boardHeight) {
|
|
315
|
+
throw new Error(
|
|
316
|
+
"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."
|
|
317
|
+
);
|
|
318
|
+
}
|
|
319
|
+
const appContext = repo.add(
|
|
320
|
+
new ApplicationContext(
|
|
321
|
+
"core data for automotive mechanical design processes"
|
|
322
|
+
)
|
|
323
|
+
);
|
|
324
|
+
repo.add(
|
|
325
|
+
new ApplicationProtocolDefinition(
|
|
326
|
+
"international standard",
|
|
327
|
+
"automotive_design",
|
|
328
|
+
2010,
|
|
329
|
+
appContext
|
|
330
|
+
)
|
|
331
|
+
);
|
|
332
|
+
const productContext = repo.add(
|
|
333
|
+
new ProductContext("", appContext, "mechanical")
|
|
334
|
+
);
|
|
335
|
+
const product = repo.add(
|
|
336
|
+
new Product(productName, productName, "", [productContext])
|
|
337
|
+
);
|
|
338
|
+
const productDefContext = repo.add(
|
|
339
|
+
new ProductDefinitionContext("part definition", appContext, "design")
|
|
340
|
+
);
|
|
341
|
+
const productDefFormation = repo.add(
|
|
342
|
+
new ProductDefinitionFormation("", "", product)
|
|
343
|
+
);
|
|
344
|
+
const productDef = repo.add(
|
|
345
|
+
new ProductDefinition("", "", productDefFormation, productDefContext)
|
|
346
|
+
);
|
|
347
|
+
const productDefShape = repo.add(
|
|
348
|
+
new ProductDefinitionShape("", "", productDef)
|
|
349
|
+
);
|
|
350
|
+
const lengthUnit = repo.add(
|
|
351
|
+
new Unknown("", [
|
|
352
|
+
"( LENGTH_UNIT() NAMED_UNIT(*) SI_UNIT(.MILLI.,.METRE.) )"
|
|
353
|
+
])
|
|
354
|
+
);
|
|
355
|
+
const angleUnit = repo.add(
|
|
356
|
+
new Unknown("", [
|
|
357
|
+
"( NAMED_UNIT(*) PLANE_ANGLE_UNIT() SI_UNIT($,.RADIAN.) )"
|
|
358
|
+
])
|
|
359
|
+
);
|
|
360
|
+
const solidAngleUnit = repo.add(
|
|
361
|
+
new Unknown("", [
|
|
362
|
+
"( NAMED_UNIT(*) SI_UNIT($,.STERADIAN.) SOLID_ANGLE_UNIT() )"
|
|
363
|
+
])
|
|
364
|
+
);
|
|
365
|
+
const uncertainty = repo.add(
|
|
366
|
+
new Unknown("UNCERTAINTY_MEASURE_WITH_UNIT", [
|
|
367
|
+
`LENGTH_MEASURE(1.E-07)`,
|
|
368
|
+
`${lengthUnit}`,
|
|
369
|
+
`'distance_accuracy_value'`,
|
|
370
|
+
`'Maximum Tolerance'`
|
|
371
|
+
])
|
|
372
|
+
);
|
|
373
|
+
const geomContext = repo.add(
|
|
374
|
+
new Unknown("", [
|
|
375
|
+
`( GEOMETRIC_REPRESENTATION_CONTEXT(3) GLOBAL_UNCERTAINTY_ASSIGNED_CONTEXT((${uncertainty})) GLOBAL_UNIT_ASSIGNED_CONTEXT((${lengthUnit},${angleUnit},${solidAngleUnit})) REPRESENTATION_CONTEXT('${productName}','3D') )`
|
|
376
|
+
])
|
|
377
|
+
);
|
|
378
|
+
const outline = pcbBoard?.outline;
|
|
379
|
+
let bottomVertices;
|
|
380
|
+
let topVertices;
|
|
381
|
+
if (outline && Array.isArray(outline) && outline.length >= 3) {
|
|
382
|
+
bottomVertices = outline.map(
|
|
383
|
+
(point) => repo.add(
|
|
384
|
+
new VertexPoint2(
|
|
385
|
+
"",
|
|
386
|
+
repo.add(new CartesianPoint2("", point.x, point.y, 0))
|
|
387
|
+
)
|
|
388
|
+
)
|
|
389
|
+
);
|
|
390
|
+
topVertices = outline.map(
|
|
391
|
+
(point) => repo.add(
|
|
392
|
+
new VertexPoint2(
|
|
393
|
+
"",
|
|
394
|
+
repo.add(new CartesianPoint2("", point.x, point.y, boardThickness))
|
|
395
|
+
)
|
|
396
|
+
)
|
|
397
|
+
);
|
|
398
|
+
} else {
|
|
399
|
+
const halfWidth = boardWidth / 2;
|
|
400
|
+
const halfHeight = boardHeight / 2;
|
|
401
|
+
const corners = [
|
|
402
|
+
[boardCenterX - halfWidth, boardCenterY - halfHeight, 0],
|
|
403
|
+
[boardCenterX + halfWidth, boardCenterY - halfHeight, 0],
|
|
404
|
+
[boardCenterX + halfWidth, boardCenterY + halfHeight, 0],
|
|
405
|
+
[boardCenterX - halfWidth, boardCenterY + halfHeight, 0],
|
|
406
|
+
[boardCenterX - halfWidth, boardCenterY - halfHeight, boardThickness],
|
|
407
|
+
[boardCenterX + halfWidth, boardCenterY - halfHeight, boardThickness],
|
|
408
|
+
[boardCenterX + halfWidth, boardCenterY + halfHeight, boardThickness],
|
|
409
|
+
[boardCenterX - halfWidth, boardCenterY + halfHeight, boardThickness]
|
|
410
|
+
];
|
|
411
|
+
const vertices = corners.map(
|
|
412
|
+
([x, y, z]) => repo.add(
|
|
413
|
+
new VertexPoint2("", repo.add(new CartesianPoint2("", x, y, z)))
|
|
414
|
+
)
|
|
415
|
+
);
|
|
416
|
+
bottomVertices = [vertices[0], vertices[1], vertices[2], vertices[3]];
|
|
417
|
+
topVertices = [vertices[4], vertices[5], vertices[6], vertices[7]];
|
|
418
|
+
}
|
|
419
|
+
function createEdge(v1, v2) {
|
|
420
|
+
const p1 = v1.resolve(repo).pnt.resolve(repo);
|
|
421
|
+
const p2 = v2.resolve(repo).pnt.resolve(repo);
|
|
422
|
+
const dir = repo.add(
|
|
423
|
+
new Direction2("", p2.x - p1.x, p2.y - p1.y, p2.z - p1.z)
|
|
424
|
+
);
|
|
425
|
+
const vec = repo.add(new Vector2("", dir, 1));
|
|
426
|
+
const line = repo.add(new Line2("", v1.resolve(repo).pnt, vec));
|
|
427
|
+
return repo.add(new EdgeCurve2("", v1, v2, line, true));
|
|
428
|
+
}
|
|
429
|
+
const bottomEdges = [];
|
|
430
|
+
const topEdges = [];
|
|
431
|
+
const verticalEdges = [];
|
|
432
|
+
for (let i = 0; i < bottomVertices.length; i++) {
|
|
433
|
+
const v1 = bottomVertices[i];
|
|
434
|
+
const v2 = bottomVertices[(i + 1) % bottomVertices.length];
|
|
435
|
+
bottomEdges.push(createEdge(v1, v2));
|
|
436
|
+
}
|
|
437
|
+
for (let i = 0; i < topVertices.length; i++) {
|
|
438
|
+
const v1 = topVertices[i];
|
|
439
|
+
const v2 = topVertices[(i + 1) % topVertices.length];
|
|
440
|
+
topEdges.push(createEdge(v1, v2));
|
|
441
|
+
}
|
|
442
|
+
for (let i = 0; i < bottomVertices.length; i++) {
|
|
443
|
+
verticalEdges.push(createEdge(bottomVertices[i], topVertices[i]));
|
|
444
|
+
}
|
|
445
|
+
const origin = repo.add(new CartesianPoint2("", 0, 0, 0));
|
|
446
|
+
const xDir = repo.add(new Direction2("", 1, 0, 0));
|
|
447
|
+
const zDir = repo.add(new Direction2("", 0, 0, 1));
|
|
448
|
+
const bottomFrame = repo.add(
|
|
449
|
+
new Axis2Placement3D2(
|
|
450
|
+
"",
|
|
451
|
+
origin,
|
|
452
|
+
repo.add(new Direction2("", 0, 0, -1)),
|
|
453
|
+
xDir
|
|
454
|
+
)
|
|
455
|
+
);
|
|
456
|
+
const bottomPlane = repo.add(new Plane2("", bottomFrame));
|
|
457
|
+
const bottomLoop = repo.add(
|
|
458
|
+
new EdgeLoop2(
|
|
459
|
+
"",
|
|
460
|
+
bottomEdges.map((edge) => repo.add(new OrientedEdge2("", edge, true)))
|
|
461
|
+
)
|
|
462
|
+
);
|
|
463
|
+
const bottomHoleLoops = [];
|
|
464
|
+
for (const hole of holes) {
|
|
465
|
+
const holeShape = hole.hole_shape || hole.shape;
|
|
466
|
+
if (holeShape === "circle") {
|
|
467
|
+
const holeX = typeof hole.x === "number" ? hole.x : hole.x.value;
|
|
468
|
+
const holeY = typeof hole.y === "number" ? hole.y : hole.y.value;
|
|
469
|
+
const radius = hole.hole_diameter / 2;
|
|
470
|
+
const holeCenter = repo.add(new CartesianPoint2("", holeX, holeY, 0));
|
|
471
|
+
const holeVertex = repo.add(
|
|
472
|
+
new VertexPoint2(
|
|
473
|
+
"",
|
|
474
|
+
repo.add(new CartesianPoint2("", holeX + radius, holeY, 0))
|
|
475
|
+
)
|
|
476
|
+
);
|
|
477
|
+
const holePlacement = repo.add(
|
|
478
|
+
new Axis2Placement3D2(
|
|
479
|
+
"",
|
|
480
|
+
holeCenter,
|
|
481
|
+
repo.add(new Direction2("", 0, 0, -1)),
|
|
482
|
+
xDir
|
|
483
|
+
)
|
|
484
|
+
);
|
|
485
|
+
const holeCircle = repo.add(new Circle("", holePlacement, radius));
|
|
486
|
+
const holeEdge = repo.add(
|
|
487
|
+
new EdgeCurve2("", holeVertex, holeVertex, holeCircle, true)
|
|
488
|
+
);
|
|
489
|
+
const holeLoop = repo.add(
|
|
490
|
+
new EdgeLoop2("", [repo.add(new OrientedEdge2("", holeEdge, false))])
|
|
491
|
+
);
|
|
492
|
+
bottomHoleLoops.push(repo.add(new FaceBound("", holeLoop, true)));
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
const bottomFace = repo.add(
|
|
496
|
+
new AdvancedFace2(
|
|
497
|
+
"",
|
|
498
|
+
[
|
|
499
|
+
repo.add(new FaceOuterBound2("", bottomLoop, true)),
|
|
500
|
+
...bottomHoleLoops
|
|
501
|
+
],
|
|
502
|
+
bottomPlane,
|
|
503
|
+
true
|
|
504
|
+
)
|
|
505
|
+
);
|
|
506
|
+
const topOrigin = repo.add(new CartesianPoint2("", 0, 0, boardThickness));
|
|
507
|
+
const topFrame = repo.add(new Axis2Placement3D2("", topOrigin, zDir, xDir));
|
|
508
|
+
const topPlane = repo.add(new Plane2("", topFrame));
|
|
509
|
+
const topLoop = repo.add(
|
|
510
|
+
new EdgeLoop2(
|
|
511
|
+
"",
|
|
512
|
+
topEdges.map((edge) => repo.add(new OrientedEdge2("", edge, false)))
|
|
513
|
+
)
|
|
514
|
+
);
|
|
515
|
+
const topHoleLoops = [];
|
|
516
|
+
for (const hole of holes) {
|
|
517
|
+
const holeShape = hole.hole_shape || hole.shape;
|
|
518
|
+
if (holeShape === "circle") {
|
|
519
|
+
const holeX = typeof hole.x === "number" ? hole.x : hole.x.value;
|
|
520
|
+
const holeY = typeof hole.y === "number" ? hole.y : hole.y.value;
|
|
521
|
+
const radius = hole.hole_diameter / 2;
|
|
522
|
+
const holeCenter = repo.add(
|
|
523
|
+
new CartesianPoint2("", holeX, holeY, boardThickness)
|
|
524
|
+
);
|
|
525
|
+
const holeVertex = repo.add(
|
|
526
|
+
new VertexPoint2(
|
|
527
|
+
"",
|
|
528
|
+
repo.add(
|
|
529
|
+
new CartesianPoint2("", holeX + radius, holeY, boardThickness)
|
|
530
|
+
)
|
|
531
|
+
)
|
|
532
|
+
);
|
|
533
|
+
const holePlacement = repo.add(
|
|
534
|
+
new Axis2Placement3D2("", holeCenter, zDir, xDir)
|
|
535
|
+
);
|
|
536
|
+
const holeCircle = repo.add(new Circle("", holePlacement, radius));
|
|
537
|
+
const holeEdge = repo.add(
|
|
538
|
+
new EdgeCurve2("", holeVertex, holeVertex, holeCircle, true)
|
|
539
|
+
);
|
|
540
|
+
const holeLoop = repo.add(
|
|
541
|
+
new EdgeLoop2("", [repo.add(new OrientedEdge2("", holeEdge, true))])
|
|
542
|
+
);
|
|
543
|
+
topHoleLoops.push(repo.add(new FaceBound("", holeLoop, true)));
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
const topFace = repo.add(
|
|
547
|
+
new AdvancedFace2(
|
|
548
|
+
"",
|
|
549
|
+
[repo.add(new FaceOuterBound2("", topLoop, true)), ...topHoleLoops],
|
|
550
|
+
topPlane,
|
|
551
|
+
true
|
|
552
|
+
)
|
|
553
|
+
);
|
|
554
|
+
const sideFaces = [];
|
|
555
|
+
for (let i = 0; i < bottomEdges.length; i++) {
|
|
556
|
+
const nextI = (i + 1) % bottomEdges.length;
|
|
557
|
+
const bottomV1Pnt = bottomVertices[i].resolve(repo).pnt;
|
|
558
|
+
const bottomV2Pnt = bottomVertices[nextI].resolve(repo).pnt;
|
|
559
|
+
const bottomV1 = bottomV1Pnt.resolve(repo);
|
|
560
|
+
const bottomV2 = bottomV2Pnt.resolve(repo);
|
|
561
|
+
const edgeDir = {
|
|
562
|
+
x: bottomV2.x - bottomV1.x,
|
|
563
|
+
y: bottomV2.y - bottomV1.y,
|
|
564
|
+
z: 0
|
|
565
|
+
};
|
|
566
|
+
const normalDir = repo.add(new Direction2("", edgeDir.y, -edgeDir.x, 0));
|
|
567
|
+
const refDir = repo.add(new Direction2("", edgeDir.x, edgeDir.y, 0));
|
|
568
|
+
const sideFrame = repo.add(
|
|
569
|
+
new Axis2Placement3D2("", bottomV1Pnt, normalDir, refDir)
|
|
570
|
+
);
|
|
571
|
+
const sidePlane = repo.add(new Plane2("", sideFrame));
|
|
572
|
+
const sideLoop = repo.add(
|
|
573
|
+
new EdgeLoop2("", [
|
|
574
|
+
repo.add(new OrientedEdge2("", bottomEdges[i], true)),
|
|
575
|
+
repo.add(new OrientedEdge2("", verticalEdges[nextI], true)),
|
|
576
|
+
repo.add(new OrientedEdge2("", topEdges[i], false)),
|
|
577
|
+
repo.add(new OrientedEdge2("", verticalEdges[i], false))
|
|
578
|
+
])
|
|
579
|
+
);
|
|
580
|
+
const sideFace = repo.add(
|
|
581
|
+
new AdvancedFace2(
|
|
582
|
+
"",
|
|
583
|
+
[repo.add(new FaceOuterBound2("", sideLoop, true))],
|
|
584
|
+
sidePlane,
|
|
585
|
+
true
|
|
586
|
+
)
|
|
587
|
+
);
|
|
588
|
+
sideFaces.push(sideFace);
|
|
589
|
+
}
|
|
590
|
+
const holeCylindricalFaces = [];
|
|
591
|
+
for (const hole of holes) {
|
|
592
|
+
const holeShape = hole.hole_shape || hole.shape;
|
|
593
|
+
if (holeShape === "circle") {
|
|
594
|
+
const holeX = typeof hole.x === "number" ? hole.x : hole.x.value;
|
|
595
|
+
const holeY = typeof hole.y === "number" ? hole.y : hole.y.value;
|
|
596
|
+
const radius = hole.hole_diameter / 2;
|
|
597
|
+
const bottomHoleCenter = repo.add(new CartesianPoint2("", holeX, holeY, 0));
|
|
598
|
+
const bottomHoleVertex = repo.add(
|
|
599
|
+
new VertexPoint2(
|
|
600
|
+
"",
|
|
601
|
+
repo.add(new CartesianPoint2("", holeX + radius, holeY, 0))
|
|
602
|
+
)
|
|
603
|
+
);
|
|
604
|
+
const bottomHolePlacement = repo.add(
|
|
605
|
+
new Axis2Placement3D2(
|
|
606
|
+
"",
|
|
607
|
+
bottomHoleCenter,
|
|
608
|
+
repo.add(new Direction2("", 0, 0, -1)),
|
|
609
|
+
xDir
|
|
610
|
+
)
|
|
611
|
+
);
|
|
612
|
+
const bottomHoleCircle = repo.add(
|
|
613
|
+
new Circle("", bottomHolePlacement, radius)
|
|
614
|
+
);
|
|
615
|
+
const bottomHoleEdge = repo.add(
|
|
616
|
+
new EdgeCurve2(
|
|
617
|
+
"",
|
|
618
|
+
bottomHoleVertex,
|
|
619
|
+
bottomHoleVertex,
|
|
620
|
+
bottomHoleCircle,
|
|
621
|
+
true
|
|
622
|
+
)
|
|
623
|
+
);
|
|
624
|
+
const topHoleCenter = repo.add(
|
|
625
|
+
new CartesianPoint2("", holeX, holeY, boardThickness)
|
|
626
|
+
);
|
|
627
|
+
const topHoleVertex = repo.add(
|
|
628
|
+
new VertexPoint2(
|
|
629
|
+
"",
|
|
630
|
+
repo.add(
|
|
631
|
+
new CartesianPoint2("", holeX + radius, holeY, boardThickness)
|
|
632
|
+
)
|
|
633
|
+
)
|
|
634
|
+
);
|
|
635
|
+
const topHolePlacement = repo.add(
|
|
636
|
+
new Axis2Placement3D2("", topHoleCenter, zDir, xDir)
|
|
637
|
+
);
|
|
638
|
+
const topHoleCircle = repo.add(new Circle("", topHolePlacement, radius));
|
|
639
|
+
const topHoleEdge = repo.add(
|
|
640
|
+
new EdgeCurve2("", topHoleVertex, topHoleVertex, topHoleCircle, true)
|
|
641
|
+
);
|
|
642
|
+
const holeCylinderLoop = repo.add(
|
|
643
|
+
new EdgeLoop2("", [
|
|
644
|
+
repo.add(new OrientedEdge2("", bottomHoleEdge, true)),
|
|
645
|
+
repo.add(new OrientedEdge2("", topHoleEdge, false))
|
|
646
|
+
])
|
|
647
|
+
);
|
|
648
|
+
const holeCylinderPlacement = repo.add(
|
|
649
|
+
new Axis2Placement3D2("", bottomHoleCenter, zDir, xDir)
|
|
650
|
+
);
|
|
651
|
+
const holeCylinderSurface = repo.add(
|
|
652
|
+
new CylindricalSurface("", holeCylinderPlacement, radius)
|
|
653
|
+
);
|
|
654
|
+
const holeCylinderFace = repo.add(
|
|
655
|
+
new AdvancedFace2(
|
|
656
|
+
"",
|
|
657
|
+
[repo.add(new FaceOuterBound2("", holeCylinderLoop, true))],
|
|
658
|
+
holeCylinderSurface,
|
|
659
|
+
false
|
|
660
|
+
)
|
|
661
|
+
);
|
|
662
|
+
holeCylindricalFaces.push(holeCylinderFace);
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
const allFaces = [bottomFace, topFace, ...sideFaces, ...holeCylindricalFaces];
|
|
666
|
+
const shell = repo.add(new ClosedShell2("", allFaces));
|
|
667
|
+
const solid = repo.add(new ManifoldSolidBrep2(productName, shell));
|
|
668
|
+
const allSolids = [solid];
|
|
669
|
+
if (options.includeComponents) {
|
|
670
|
+
const componentSolids = await generateComponentMeshes({
|
|
671
|
+
repo,
|
|
672
|
+
circuitJson,
|
|
673
|
+
boardThickness,
|
|
674
|
+
includeExternalMeshes: options.includeExternalMeshes
|
|
675
|
+
});
|
|
676
|
+
allSolids.push(...componentSolids);
|
|
677
|
+
}
|
|
678
|
+
const styledItems = [];
|
|
679
|
+
for (const solidRef of allSolids) {
|
|
680
|
+
const color = repo.add(new ColourRgb("", 0.2, 0.6, 0.2));
|
|
681
|
+
const fillColor = repo.add(new FillAreaStyleColour("", color));
|
|
682
|
+
const fillStyle = repo.add(new FillAreaStyle("", [fillColor]));
|
|
683
|
+
const surfaceFill = repo.add(new SurfaceStyleFillArea(fillStyle));
|
|
684
|
+
const surfaceSide = repo.add(new SurfaceSideStyle("", [surfaceFill]));
|
|
685
|
+
const surfaceUsage = repo.add(new SurfaceStyleUsage(".BOTH.", surfaceSide));
|
|
686
|
+
const presStyle = repo.add(new PresentationStyleAssignment([surfaceUsage]));
|
|
687
|
+
const styledItem = repo.add(new StyledItem("", [presStyle], solidRef));
|
|
688
|
+
styledItems.push(styledItem);
|
|
689
|
+
}
|
|
690
|
+
repo.add(
|
|
691
|
+
new MechanicalDesignGeometricPresentationRepresentation(
|
|
692
|
+
"",
|
|
693
|
+
styledItems,
|
|
694
|
+
geomContext
|
|
695
|
+
)
|
|
696
|
+
);
|
|
697
|
+
const shapeRep = repo.add(
|
|
698
|
+
new AdvancedBrepShapeRepresentation(productName, allSolids, geomContext)
|
|
699
|
+
);
|
|
700
|
+
repo.add(new ShapeDefinitionRepresentation(productDefShape, shapeRep));
|
|
701
|
+
return repo.toPartFile({ name: productName });
|
|
702
|
+
}
|
|
703
|
+
export {
|
|
704
|
+
circuitJsonToStep
|
|
705
|
+
};
|