circuit-json-to-gltf 0.0.72 → 0.0.74

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +348 -97
  2. package/package.json +3 -1
package/dist/index.js CHANGED
@@ -4378,6 +4378,52 @@ var require_isA3 = __commonJS({
4378
4378
  }
4379
4379
  });
4380
4380
 
4381
+ // node_modules/@jscad/modeling/src/geometries/geom3/isConvex.js
4382
+ var require_isConvex2 = __commonJS({
4383
+ "node_modules/@jscad/modeling/src/geometries/geom3/isConvex.js"(exports, module) {
4384
+ "use strict";
4385
+ var { EPS } = require_constants();
4386
+ var vec3 = require_vec3();
4387
+ var geom33 = require_isA3();
4388
+ var toPolygons3 = require_toPolygons();
4389
+ var poly3 = require_poly3();
4390
+ var isConvex = (geometry) => {
4391
+ if (!geom33(geometry)) {
4392
+ throw new Error("isConvex requires a geom3 geometry");
4393
+ }
4394
+ const polygons = toPolygons3(geometry);
4395
+ if (polygons.length === 0) {
4396
+ return true;
4397
+ }
4398
+ const vertices = [];
4399
+ const found = /* @__PURE__ */ new Set();
4400
+ for (let i = 0; i < polygons.length; i++) {
4401
+ const verts = polygons[i].vertices;
4402
+ for (let j = 0; j < verts.length; j++) {
4403
+ const v = verts[j];
4404
+ const key = `${v[0]},${v[1]},${v[2]}`;
4405
+ if (!found.has(key)) {
4406
+ found.add(key);
4407
+ vertices.push(v);
4408
+ }
4409
+ }
4410
+ }
4411
+ for (let i = 0; i < polygons.length; i++) {
4412
+ const plane = poly3.plane(polygons[i]);
4413
+ for (let j = 0; j < vertices.length; j++) {
4414
+ const v = vertices[j];
4415
+ const distance = vec3.dot(plane, v) - plane[3];
4416
+ if (distance > EPS) {
4417
+ return false;
4418
+ }
4419
+ }
4420
+ }
4421
+ return true;
4422
+ };
4423
+ module.exports = isConvex;
4424
+ }
4425
+ });
4426
+
4381
4427
  // node_modules/@jscad/modeling/src/geometries/geom3/toPoints.js
4382
4428
  var require_toPoints3 = __commonJS({
4383
4429
  "node_modules/@jscad/modeling/src/geometries/geom3/toPoints.js"(exports, module) {
@@ -4535,6 +4581,7 @@ var require_geom3 = __commonJS({
4535
4581
  fromCompactBinary: require_fromCompactBinary2(),
4536
4582
  invert: require_invert3(),
4537
4583
  isA: require_isA3(),
4584
+ isConvex: require_isConvex2(),
4538
4585
  toPoints: require_toPoints3(),
4539
4586
  toPolygons: require_toPolygons(),
4540
4587
  toString: require_toString7(),
@@ -4731,7 +4778,7 @@ var require_appendArc = __commonJS({
4731
4778
  } else if (sweepFlag && deltatheta < 0) {
4732
4779
  deltatheta += TAU;
4733
4780
  }
4734
- let numsteps = Math.ceil(Math.abs(deltatheta) / TAU * segments) + 1;
4781
+ let numsteps = Math.floor(segments * (Math.abs(deltatheta) / TAU));
4735
4782
  if (numsteps < 1) numsteps = 1;
4736
4783
  for (let step = 1; step < numsteps; step++) {
4737
4784
  const theta = theta1 + step / numsteps * deltatheta;
@@ -7200,7 +7247,7 @@ var require_arc = __commonJS({
7200
7247
  vec2.add(point, point, centerv);
7201
7248
  pointArray.push(point);
7202
7249
  } else {
7203
- const numsteps = Math.max(1, Math.floor(segments * (rotation / TAU))) + 1;
7250
+ const numsteps = Math.floor(segments * (Math.abs(rotation) / TAU));
7204
7251
  let edgestepsize = numsteps * 0.5 / rotation;
7205
7252
  if (edgestepsize > 0.25) edgestepsize = 0.25;
7206
7253
  const totalsteps = makeTangent ? numsteps + 2 : numsteps;
@@ -11233,6 +11280,205 @@ var require_intersect2 = __commonJS({
11233
11280
  }
11234
11281
  });
11235
11282
 
11283
+ // node_modules/@jscad/modeling/src/operations/hulls/hullPoints3.js
11284
+ var require_hullPoints3 = __commonJS({
11285
+ "node_modules/@jscad/modeling/src/operations/hulls/hullPoints3.js"(exports, module) {
11286
+ "use strict";
11287
+ var poly3 = require_poly3();
11288
+ var quickhull = require_quickhull();
11289
+ var hullPoints3 = (uniquePoints) => {
11290
+ const faces = quickhull(uniquePoints, { skipTriangulation: true });
11291
+ const polygons = faces.map((face) => {
11292
+ const vertices = face.map((index) => uniquePoints[index]);
11293
+ return poly3.create(vertices);
11294
+ });
11295
+ return polygons;
11296
+ };
11297
+ module.exports = hullPoints3;
11298
+ }
11299
+ });
11300
+
11301
+ // node_modules/@jscad/modeling/src/operations/booleans/unionGeom3Sub.js
11302
+ var require_unionGeom3Sub = __commonJS({
11303
+ "node_modules/@jscad/modeling/src/operations/booleans/unionGeom3Sub.js"(exports, module) {
11304
+ "use strict";
11305
+ var geom33 = require_geom3();
11306
+ var mayOverlap = require_mayOverlap();
11307
+ var { Tree } = require_trees();
11308
+ var unionSub = (geometry1, geometry2) => {
11309
+ if (!mayOverlap(geometry1, geometry2)) {
11310
+ return unionForNonIntersecting(geometry1, geometry2);
11311
+ }
11312
+ const a = new Tree(geom33.toPolygons(geometry1));
11313
+ const b = new Tree(geom33.toPolygons(geometry2));
11314
+ a.clipTo(b, false);
11315
+ b.clipTo(a);
11316
+ b.invert();
11317
+ b.clipTo(a);
11318
+ b.invert();
11319
+ const newpolygons = a.allPolygons().concat(b.allPolygons());
11320
+ const result = geom33.create(newpolygons);
11321
+ return result;
11322
+ };
11323
+ var unionForNonIntersecting = (geometry1, geometry2) => {
11324
+ let newpolygons = geom33.toPolygons(geometry1);
11325
+ newpolygons = newpolygons.concat(geom33.toPolygons(geometry2));
11326
+ return geom33.create(newpolygons);
11327
+ };
11328
+ module.exports = unionSub;
11329
+ }
11330
+ });
11331
+
11332
+ // node_modules/@jscad/modeling/src/operations/booleans/unionGeom3.js
11333
+ var require_unionGeom3 = __commonJS({
11334
+ "node_modules/@jscad/modeling/src/operations/booleans/unionGeom3.js"(exports, module) {
11335
+ "use strict";
11336
+ var flatten = require_flatten();
11337
+ var retessellate = require_retessellate();
11338
+ var unionSub = require_unionGeom3Sub();
11339
+ var union3 = (...geometries) => {
11340
+ geometries = flatten(geometries);
11341
+ let i;
11342
+ for (i = 1; i < geometries.length; i += 2) {
11343
+ geometries.push(unionSub(geometries[i - 1], geometries[i]));
11344
+ }
11345
+ let newgeometry = geometries[i - 1];
11346
+ newgeometry = retessellate(newgeometry);
11347
+ return newgeometry;
11348
+ };
11349
+ module.exports = union3;
11350
+ }
11351
+ });
11352
+
11353
+ // node_modules/@jscad/modeling/src/operations/minkowski/minkowskiSum.js
11354
+ var require_minkowskiSum = __commonJS({
11355
+ "node_modules/@jscad/modeling/src/operations/minkowski/minkowskiSum.js"(exports, module) {
11356
+ "use strict";
11357
+ var flatten = require_flatten();
11358
+ var geom33 = require_geom3();
11359
+ var poly3 = require_poly3();
11360
+ var hullPoints3 = require_hullPoints3();
11361
+ var unionGeom3 = require_unionGeom3();
11362
+ var minkowskiSum = (...geometries) => {
11363
+ geometries = flatten(geometries);
11364
+ if (geometries.length !== 2) {
11365
+ throw new Error("minkowskiSum requires exactly two geometries");
11366
+ }
11367
+ const [geomA, geomB] = geometries;
11368
+ if (!geom33.isA(geomA) || !geom33.isA(geomB)) {
11369
+ throw new Error("minkowskiSum requires geom3 geometries");
11370
+ }
11371
+ const aConvex = geom33.isConvex(geomA);
11372
+ const bConvex = geom33.isConvex(geomB);
11373
+ if (aConvex && bConvex) {
11374
+ return minkowskiSumConvex(geomA, geomB);
11375
+ }
11376
+ if (!aConvex && bConvex) {
11377
+ return minkowskiSumNonConvexConvex(geomA, geomB);
11378
+ }
11379
+ if (aConvex && !bConvex) {
11380
+ return minkowskiSumNonConvexConvex(geomB, geomA);
11381
+ }
11382
+ throw new Error("minkowskiSum of two non-convex geometries is not yet supported");
11383
+ };
11384
+ var minkowskiSumNonConvexConvex = (geomA, geomB) => {
11385
+ const tetrahedra = decomposeIntoTetrahedra(geomA);
11386
+ if (tetrahedra.length === 0) {
11387
+ return geom33.create();
11388
+ }
11389
+ const parts = tetrahedra.map((tet) => minkowskiSumConvex(tet, geomB));
11390
+ if (parts.length === 1) {
11391
+ return parts[0];
11392
+ }
11393
+ return unionGeom3(parts);
11394
+ };
11395
+ var decomposeIntoTetrahedra = (geometry) => {
11396
+ const polygons = geom33.toPolygons(geometry);
11397
+ if (polygons.length === 0) {
11398
+ return [];
11399
+ }
11400
+ const tetrahedra = [];
11401
+ for (let i = 0; i < polygons.length; i++) {
11402
+ const polygon3 = polygons[i];
11403
+ const vertices = polygon3.vertices;
11404
+ let cx = 0, cy = 0, cz = 0;
11405
+ for (let k = 0; k < vertices.length; k++) {
11406
+ cx += vertices[k][0];
11407
+ cy += vertices[k][1];
11408
+ cz += vertices[k][2];
11409
+ }
11410
+ cx /= vertices.length;
11411
+ cy /= vertices.length;
11412
+ cz /= vertices.length;
11413
+ const plane = poly3.plane(polygon3);
11414
+ const nx = plane[0], ny = plane[1], nz = plane[2];
11415
+ const offset = 0.1;
11416
+ const apex = [
11417
+ // Vertex used as apex in tetrahedron polygons below
11418
+ cx - nx * offset,
11419
+ cy - ny * offset,
11420
+ cz - nz * offset
11421
+ ];
11422
+ for (let j = 1; j < vertices.length - 1; j++) {
11423
+ const v0 = vertices[0];
11424
+ const v1 = vertices[j];
11425
+ const v2 = vertices[j + 1];
11426
+ const tetPolygons = createTetrahedronPolygons(apex, v0, v1, v2);
11427
+ tetrahedra.push(geom33.create(tetPolygons));
11428
+ }
11429
+ }
11430
+ return tetrahedra;
11431
+ };
11432
+ var createTetrahedronPolygons = (p0, p1, p2, p3) => {
11433
+ return [
11434
+ poly3.create([p0, p2, p1]),
11435
+ // base seen from p3
11436
+ poly3.create([p0, p1, p3]),
11437
+ // face opposite p2
11438
+ poly3.create([p1, p2, p3]),
11439
+ // face opposite p0
11440
+ poly3.create([p2, p0, p3])
11441
+ // face opposite p1
11442
+ ];
11443
+ };
11444
+ var minkowskiSumConvex = (geomA, geomB) => {
11445
+ const pointsA = extractUniqueVertices(geomA);
11446
+ const pointsB = extractUniqueVertices(geomB);
11447
+ if (pointsA.length === 0 || pointsB.length === 0) {
11448
+ return geom33.create();
11449
+ }
11450
+ const summedPoints = [];
11451
+ for (let i = 0; i < pointsA.length; i++) {
11452
+ const a = pointsA[i];
11453
+ for (let j = 0; j < pointsB.length; j++) {
11454
+ const b = pointsB[j];
11455
+ summedPoints.push([a[0] + b[0], a[1] + b[1], a[2] + b[2]]);
11456
+ }
11457
+ }
11458
+ const hullPolygons = hullPoints3(summedPoints);
11459
+ return geom33.create(hullPolygons);
11460
+ };
11461
+ var extractUniqueVertices = (geometry) => {
11462
+ const found = /* @__PURE__ */ new Set();
11463
+ const unique = [];
11464
+ const polygons = geom33.toPolygons(geometry);
11465
+ for (let i = 0; i < polygons.length; i++) {
11466
+ const vertices = polygons[i].vertices;
11467
+ for (let j = 0; j < vertices.length; j++) {
11468
+ const v = vertices[j];
11469
+ const key = `${v[0]},${v[1]},${v[2]}`;
11470
+ if (!found.has(key)) {
11471
+ found.add(key);
11472
+ unique.push(v);
11473
+ }
11474
+ }
11475
+ }
11476
+ return unique;
11477
+ };
11478
+ module.exports = minkowskiSum;
11479
+ }
11480
+ });
11481
+
11236
11482
  // node_modules/@jscad/modeling/src/operations/booleans/scissionGeom3.js
11237
11483
  var require_scissionGeom3 = __commonJS({
11238
11484
  "node_modules/@jscad/modeling/src/operations/booleans/scissionGeom3.js"(exports, module) {
@@ -11421,58 +11667,6 @@ var require_subtract4 = __commonJS({
11421
11667
  }
11422
11668
  });
11423
11669
 
11424
- // node_modules/@jscad/modeling/src/operations/booleans/unionGeom3Sub.js
11425
- var require_unionGeom3Sub = __commonJS({
11426
- "node_modules/@jscad/modeling/src/operations/booleans/unionGeom3Sub.js"(exports, module) {
11427
- "use strict";
11428
- var geom33 = require_geom3();
11429
- var mayOverlap = require_mayOverlap();
11430
- var { Tree } = require_trees();
11431
- var unionSub = (geometry1, geometry2) => {
11432
- if (!mayOverlap(geometry1, geometry2)) {
11433
- return unionForNonIntersecting(geometry1, geometry2);
11434
- }
11435
- const a = new Tree(geom33.toPolygons(geometry1));
11436
- const b = new Tree(geom33.toPolygons(geometry2));
11437
- a.clipTo(b, false);
11438
- b.clipTo(a);
11439
- b.invert();
11440
- b.clipTo(a);
11441
- b.invert();
11442
- const newpolygons = a.allPolygons().concat(b.allPolygons());
11443
- const result = geom33.create(newpolygons);
11444
- return result;
11445
- };
11446
- var unionForNonIntersecting = (geometry1, geometry2) => {
11447
- let newpolygons = geom33.toPolygons(geometry1);
11448
- newpolygons = newpolygons.concat(geom33.toPolygons(geometry2));
11449
- return geom33.create(newpolygons);
11450
- };
11451
- module.exports = unionSub;
11452
- }
11453
- });
11454
-
11455
- // node_modules/@jscad/modeling/src/operations/booleans/unionGeom3.js
11456
- var require_unionGeom3 = __commonJS({
11457
- "node_modules/@jscad/modeling/src/operations/booleans/unionGeom3.js"(exports, module) {
11458
- "use strict";
11459
- var flatten = require_flatten();
11460
- var retessellate = require_retessellate();
11461
- var unionSub = require_unionGeom3Sub();
11462
- var union = (...geometries) => {
11463
- geometries = flatten(geometries);
11464
- let i;
11465
- for (i = 1; i < geometries.length; i += 2) {
11466
- geometries.push(unionSub(geometries[i - 1], geometries[i]));
11467
- }
11468
- let newgeometry = geometries[i - 1];
11469
- newgeometry = retessellate(newgeometry);
11470
- return newgeometry;
11471
- };
11472
- module.exports = union;
11473
- }
11474
- });
11475
-
11476
11670
  // node_modules/@jscad/modeling/src/operations/booleans/unionGeom2.js
11477
11671
  var require_unionGeom2 = __commonJS({
11478
11672
  "node_modules/@jscad/modeling/src/operations/booleans/unionGeom2.js"(exports, module) {
@@ -11483,14 +11677,14 @@ var require_unionGeom2 = __commonJS({
11483
11677
  var fromFakePolygons = require_fromFakePolygons();
11484
11678
  var to3DWalls = require_to3DWalls();
11485
11679
  var unionGeom3 = require_unionGeom3();
11486
- var union = (...geometries) => {
11680
+ var union3 = (...geometries) => {
11487
11681
  geometries = flatten(geometries);
11488
11682
  const newgeometries = geometries.map((geometry) => to3DWalls({ z0: -1, z1: 1 }, geometry));
11489
11683
  const newgeom3 = unionGeom3(newgeometries);
11490
11684
  const epsilon = measureEpsilon(newgeom3);
11491
11685
  return fromFakePolygons(epsilon, geom33.toPolygons(newgeom3));
11492
11686
  };
11493
- module.exports = union;
11687
+ module.exports = union3;
11494
11688
  }
11495
11689
  });
11496
11690
 
@@ -11504,7 +11698,7 @@ var require_union = __commonJS({
11504
11698
  var geom33 = require_geom3();
11505
11699
  var unionGeom2 = require_unionGeom2();
11506
11700
  var unionGeom3 = require_unionGeom3();
11507
- var union = (...geometries) => {
11701
+ var union3 = (...geometries) => {
11508
11702
  geometries = flatten(geometries);
11509
11703
  if (geometries.length === 0) throw new Error("wrong number of arguments");
11510
11704
  if (!areAllShapesTheSameType(geometries)) {
@@ -11515,7 +11709,7 @@ var require_union = __commonJS({
11515
11709
  if (geom33.isA(geometry)) return unionGeom3(geometries);
11516
11710
  return geometry;
11517
11711
  };
11518
- module.exports = union;
11712
+ module.exports = union3;
11519
11713
  }
11520
11714
  });
11521
11715
 
@@ -11525,6 +11719,7 @@ var require_booleans = __commonJS({
11525
11719
  "use strict";
11526
11720
  module.exports = {
11527
11721
  intersect: require_intersect2(),
11722
+ minkowski: require_minkowskiSum(),
11528
11723
  scission: require_scission(),
11529
11724
  subtract: require_subtract4(),
11530
11725
  union: require_union()
@@ -11905,7 +12100,7 @@ var require_expandGeom3 = __commonJS({
11905
12100
  "node_modules/@jscad/modeling/src/operations/expansions/expandGeom3.js"(exports, module) {
11906
12101
  "use strict";
11907
12102
  var geom33 = require_geom3();
11908
- var union = require_union();
12103
+ var union3 = require_union();
11909
12104
  var expandShell = require_expandShell();
11910
12105
  var expandGeom3 = (options, geometry) => {
11911
12106
  const defaults = {
@@ -11921,7 +12116,7 @@ var require_expandGeom3 = __commonJS({
11921
12116
  if (polygons.length === 0) throw new Error("the given geometry cannot be empty");
11922
12117
  options = { delta, corners, segments };
11923
12118
  const expanded = expandShell(options, geometry);
11924
- return union(geometry, expanded);
12119
+ return union3(geometry, expanded);
11925
12120
  };
11926
12121
  module.exports = expandGeom3;
11927
12122
  }
@@ -12564,24 +12759,6 @@ var require_hullGeom2 = __commonJS({
12564
12759
  }
12565
12760
  });
12566
12761
 
12567
- // node_modules/@jscad/modeling/src/operations/hulls/hullPoints3.js
12568
- var require_hullPoints3 = __commonJS({
12569
- "node_modules/@jscad/modeling/src/operations/hulls/hullPoints3.js"(exports, module) {
12570
- "use strict";
12571
- var poly3 = require_poly3();
12572
- var quickhull = require_quickhull();
12573
- var hullPoints3 = (uniquePoints) => {
12574
- const faces = quickhull(uniquePoints, { skipTriangulation: true });
12575
- const polygons = faces.map((face) => {
12576
- const vertices = face.map((index) => uniquePoints[index]);
12577
- return poly3.create(vertices);
12578
- });
12579
- return polygons;
12580
- };
12581
- module.exports = hullPoints3;
12582
- }
12583
- });
12584
-
12585
12762
  // node_modules/@jscad/modeling/src/operations/hulls/hullGeom3.js
12586
12763
  var require_hullGeom3 = __commonJS({
12587
12764
  "node_modules/@jscad/modeling/src/operations/hulls/hullGeom3.js"(exports, module) {
@@ -12633,7 +12810,7 @@ var require_hullChain = __commonJS({
12633
12810
  "node_modules/@jscad/modeling/src/operations/hulls/hullChain.js"(exports, module) {
12634
12811
  "use strict";
12635
12812
  var flatten = require_flatten();
12636
- var union = require_union();
12813
+ var union3 = require_union();
12637
12814
  var hull = require_hull();
12638
12815
  var hullChain = (...geometries) => {
12639
12816
  geometries = flatten(geometries);
@@ -12642,7 +12819,7 @@ var require_hullChain = __commonJS({
12642
12819
  for (let i = 1; i < geometries.length; i++) {
12643
12820
  hulls.push(hull(geometries[i - 1], geometries[i]));
12644
12821
  }
12645
- return union(hulls);
12822
+ return union3(hulls);
12646
12823
  };
12647
12824
  module.exports = hullChain;
12648
12825
  }
@@ -12661,6 +12838,16 @@ var require_hulls = __commonJS({
12661
12838
  }
12662
12839
  });
12663
12840
 
12841
+ // node_modules/@jscad/modeling/src/operations/minkowski/index.js
12842
+ var require_minkowski = __commonJS({
12843
+ "node_modules/@jscad/modeling/src/operations/minkowski/index.js"(exports, module) {
12844
+ "use strict";
12845
+ module.exports = {
12846
+ minkowskiSum: require_minkowskiSum()
12847
+ };
12848
+ }
12849
+ });
12850
+
12664
12851
  // node_modules/@jscad/modeling/src/operations/modifiers/snapPolygons.js
12665
12852
  var require_snapPolygons = __commonJS({
12666
12853
  "node_modules/@jscad/modeling/src/operations/modifiers/snapPolygons.js"(exports, module) {
@@ -13502,6 +13689,7 @@ var require_src = __commonJS({
13502
13689
  expansions: require_expansions(),
13503
13690
  extrusions: require_extrusions(),
13504
13691
  hulls: require_hulls(),
13692
+ minkowski: require_minkowski(),
13505
13693
  modifiers: require_modifiers(),
13506
13694
  transforms: require_transforms()
13507
13695
  };
@@ -14613,7 +14801,20 @@ var occtModulePromise = null;
14613
14801
  async function getOcctModule() {
14614
14802
  if (!occtModulePromise) {
14615
14803
  const occtimportjs = (await import("occt-import-js")).default;
14616
- occtModulePromise = occtimportjs();
14804
+ const isBrowser = typeof window !== "undefined" || typeof self !== "undefined";
14805
+ if (isBrowser) {
14806
+ let wasmUrl;
14807
+ try {
14808
+ wasmUrl = (await import("occt-import-js/dist/occt-import-js.wasm?url")).default;
14809
+ } catch {
14810
+ wasmUrl = void 0;
14811
+ }
14812
+ occtModulePromise = occtimportjs({
14813
+ locateFile: (path) => path.endsWith(".wasm") && wasmUrl ? wasmUrl : path
14814
+ });
14815
+ } else {
14816
+ occtModulePromise = occtimportjs();
14817
+ }
14617
14818
  }
14618
14819
  return occtModulePromise;
14619
14820
  }
@@ -15001,6 +15202,8 @@ var import_extrusions = __toESM(require_extrusions(), 1);
15001
15202
  var import_primitives = __toESM(require_primitives(), 1);
15002
15203
  var import_transforms = __toESM(require_transforms(), 1);
15003
15204
  var DEFAULT_SEGMENTS = 64;
15205
+ var REDUCED_SEGMENTS = 16;
15206
+ var HOLE_COUNT_THRESHOLD = 50;
15004
15207
  var toBoardSpaceVec2 = (point, center) => [point.x - center.x, -(point.y - center.y)];
15005
15208
  var isFiniteNumber = (value) => typeof value === "number" && Number.isFinite(value);
15006
15209
  var arePointsClockwise = (points) => {
@@ -15013,11 +15216,11 @@ var arePointsClockwise = (points) => {
15013
15216
  const signedArea = area / 2;
15014
15217
  return signedArea <= 0;
15015
15218
  };
15016
- var createCircularHole = (x, y, radius, thickness) => (0, import_primitives.cylinder)({
15219
+ var createCircularHole = (x, y, radius, thickness, segments = DEFAULT_SEGMENTS) => (0, import_primitives.cylinder)({
15017
15220
  center: [x, y, 0],
15018
15221
  height: thickness + 1,
15019
15222
  radius,
15020
- segments: DEFAULT_SEGMENTS
15223
+ segments
15021
15224
  });
15022
15225
  var createCutoutGeoms = (boardCenter, thickness, cutouts = []) => {
15023
15226
  const geoms = [];
@@ -15129,9 +15332,36 @@ var filterCutoutsForBoard = (cutouts, board) => {
15129
15332
  var import_extrusions2 = __toESM(require_extrusions(), 1);
15130
15333
  var import_primitives2 = __toESM(require_primitives(), 1);
15131
15334
  var import_transforms2 = __toESM(require_transforms(), 1);
15132
- var import_booleans = __toESM(require_booleans(), 1);
15335
+ var import_booleans2 = __toESM(require_booleans(), 1);
15133
15336
  var geom3 = __toESM(require_geom3(), 1);
15134
15337
  var import_measureBoundingBox = __toESM(require_measureBoundingBox2(), 1);
15338
+
15339
+ // lib/utils/batched-union.ts
15340
+ var import_booleans = __toESM(require_booleans(), 1);
15341
+ var batchedUnion = (geoms, batchSize = 50) => {
15342
+ if (geoms.length === 0) {
15343
+ throw new Error("Cannot union empty array");
15344
+ }
15345
+ if (geoms.length === 1) {
15346
+ return geoms[0];
15347
+ }
15348
+ let results = [...geoms];
15349
+ while (results.length > 1) {
15350
+ const newResults = [];
15351
+ for (let i = 0; i < results.length; i += batchSize) {
15352
+ const batch = results.slice(i, i + batchSize);
15353
+ if (batch.length === 1) {
15354
+ newResults.push(batch[0]);
15355
+ } else {
15356
+ newResults.push((0, import_booleans.union)(...batch));
15357
+ }
15358
+ }
15359
+ results = newResults;
15360
+ }
15361
+ return results[0];
15362
+ };
15363
+
15364
+ // lib/utils/pcb-board-geometry.ts
15135
15365
  var RADIUS_EPSILON = 1e-4;
15136
15366
  var getNumberProperty = (obj, key) => {
15137
15367
  const value = obj[key];
@@ -15157,14 +15387,14 @@ var createBoardOutlineGeom = (board, center, thickness) => {
15157
15387
  geom = (0, import_transforms2.translate)([0, 0, -thickness / 2], geom);
15158
15388
  return geom;
15159
15389
  };
15160
- var createPillHole = (x, y, width, height, thickness, rotate) => {
15390
+ var createPillHoleWithSegments = (x, y, width, height, thickness, rotate, segments = DEFAULT_SEGMENTS) => {
15161
15391
  const minDimension = Math.min(width, height);
15162
15392
  const maxAllowedRadius = Math.max(0, minDimension / 2 - RADIUS_EPSILON);
15163
15393
  const roundRadius = maxAllowedRadius <= 0 ? 0 : Math.min(height / 2, maxAllowedRadius);
15164
15394
  const hole2d = (0, import_primitives2.roundedRectangle)({
15165
15395
  size: [width, height],
15166
15396
  roundRadius,
15167
- segments: DEFAULT_SEGMENTS
15397
+ segments
15168
15398
  });
15169
15399
  let hole3d = (0, import_extrusions2.extrudeLinear)({ height: thickness + 1 }, hole2d);
15170
15400
  hole3d = (0, import_transforms2.translate)([0, 0, -(thickness + 1) / 2], hole3d);
@@ -15173,7 +15403,7 @@ var createPillHole = (x, y, width, height, thickness, rotate) => {
15173
15403
  }
15174
15404
  return (0, import_transforms2.translate)([x, y, 0], hole3d);
15175
15405
  };
15176
- var createHoleGeoms = (boardCenter, thickness, holes = [], platedHoles = []) => {
15406
+ var createHoleGeoms = (boardCenter, thickness, holes = [], platedHoles = [], segments = DEFAULT_SEGMENTS) => {
15177
15407
  const holeGeoms = [];
15178
15408
  for (const hole of holes) {
15179
15409
  const holeRecord = hole;
@@ -15187,13 +15417,14 @@ var createHoleGeoms = (boardCenter, thickness, holes = [], platedHoles = []) =>
15187
15417
  const rotate = holeHeight > holeWidth;
15188
15418
  const width = rotate ? holeHeight : holeWidth;
15189
15419
  const height = rotate ? holeWidth : holeHeight;
15190
- const pillHole = createPillHole(
15420
+ const pillHole = createPillHoleWithSegments(
15191
15421
  relX,
15192
15422
  relY,
15193
15423
  width,
15194
15424
  height,
15195
15425
  thickness,
15196
- rotate
15426
+ rotate,
15427
+ segments
15197
15428
  );
15198
15429
  holeGeoms.push(pillHole);
15199
15430
  continue;
@@ -15210,7 +15441,7 @@ var createHoleGeoms = (boardCenter, thickness, holes = [], platedHoles = []) =>
15210
15441
  const hole2d = (0, import_primitives2.roundedRectangle)({
15211
15442
  size: [holeWidth, holeHeight],
15212
15443
  roundRadius,
15213
- segments: DEFAULT_SEGMENTS
15444
+ segments
15214
15445
  });
15215
15446
  let hole3d = (0, import_extrusions2.extrudeLinear)({ height: thickness + 1 }, hole2d);
15216
15447
  hole3d = (0, import_transforms2.translate)([0, 0, -(thickness + 1) / 2], hole3d);
@@ -15224,7 +15455,7 @@ var createHoleGeoms = (boardCenter, thickness, holes = [], platedHoles = []) =>
15224
15455
  const diameter = getNumberProperty(holeRecord, "hole_diameter") ?? getNumberProperty(holeRecord, "diameter");
15225
15456
  if (!diameter) continue;
15226
15457
  const radius = diameter / 2;
15227
- holeGeoms.push(createCircularHole(relX, relY, radius, thickness));
15458
+ holeGeoms.push(createCircularHole(relX, relY, radius, thickness, segments));
15228
15459
  }
15229
15460
  for (const plated of platedHoles) {
15230
15461
  const platedRecord = plated;
@@ -15240,13 +15471,23 @@ var createHoleGeoms = (boardCenter, thickness, holes = [], platedHoles = []) =>
15240
15471
  const width = rotate ? holeHeight : holeWidth;
15241
15472
  const height = rotate ? holeWidth : holeHeight;
15242
15473
  holeGeoms.push(
15243
- createPillHole(relX, relY, width, height, thickness, rotate)
15474
+ createPillHoleWithSegments(
15475
+ relX,
15476
+ relY,
15477
+ width,
15478
+ height,
15479
+ thickness,
15480
+ rotate,
15481
+ segments
15482
+ )
15244
15483
  );
15245
15484
  continue;
15246
15485
  }
15247
15486
  const diameter = getNumberProperty(platedRecord, "hole_diameter") ?? getNumberProperty(platedRecord, "outer_diameter");
15248
15487
  if (!diameter) continue;
15249
- holeGeoms.push(createCircularHole(relX, relY, diameter / 2, thickness));
15488
+ holeGeoms.push(
15489
+ createCircularHole(relX, relY, diameter / 2, thickness, segments)
15490
+ );
15250
15491
  }
15251
15492
  return holeGeoms;
15252
15493
  };
@@ -15302,11 +15543,21 @@ var createBoardMesh = (board, options) => {
15302
15543
  const { thickness, holes = [], platedHoles = [], cutouts = [] } = options;
15303
15544
  const center = board.center ?? { x: 0, y: 0 };
15304
15545
  let boardGeom = createBoardOutlineGeom(board, center, thickness);
15305
- const holeGeoms = createHoleGeoms(center, thickness, holes, platedHoles);
15546
+ const totalHoleCount = holes.length + platedHoles.length;
15547
+ const useReducedSegments = totalHoleCount > HOLE_COUNT_THRESHOLD;
15548
+ const segments = useReducedSegments ? REDUCED_SEGMENTS : DEFAULT_SEGMENTS;
15549
+ const holeGeoms = createHoleGeoms(
15550
+ center,
15551
+ thickness,
15552
+ holes,
15553
+ platedHoles,
15554
+ segments
15555
+ );
15306
15556
  const cutoutGeoms = createCutoutGeoms(center, thickness, cutouts);
15307
15557
  const subtractGeoms = [...holeGeoms, ...cutoutGeoms];
15308
15558
  if (subtractGeoms.length > 0) {
15309
- boardGeom = (0, import_booleans.subtract)(boardGeom, ...subtractGeoms);
15559
+ const unifiedHoles = batchedUnion(subtractGeoms);
15560
+ boardGeom = (0, import_booleans2.subtract)(boardGeom, unifiedHoles);
15310
15561
  }
15311
15562
  boardGeom = (0, import_transforms2.rotateX)(-Math.PI / 2, boardGeom);
15312
15563
  const polygons = geom3.toPolygons(boardGeom);
@@ -15321,7 +15572,7 @@ var createBoardMesh = (board, options) => {
15321
15572
 
15322
15573
  // lib/utils/pcb-panel-geometry.ts
15323
15574
  var import_transforms3 = __toESM(require_transforms(), 1);
15324
- var import_booleans2 = __toESM(require_booleans(), 1);
15575
+ var import_booleans3 = __toESM(require_booleans(), 1);
15325
15576
  var geom32 = __toESM(require_geom3(), 1);
15326
15577
  var import_measureBoundingBox2 = __toESM(require_measureBoundingBox2(), 1);
15327
15578
  var createPanelMesh = (panel, options) => {
@@ -15332,7 +15583,7 @@ var createPanelMesh = (panel, options) => {
15332
15583
  const cutoutGeoms = createCutoutGeoms(center, thickness, cutouts);
15333
15584
  const subtractGeoms = [...holeGeoms, ...cutoutGeoms];
15334
15585
  if (subtractGeoms.length > 0) {
15335
- panelGeom = (0, import_booleans2.subtract)(panelGeom, ...subtractGeoms);
15586
+ panelGeom = (0, import_booleans3.subtract)(panelGeom, ...subtractGeoms);
15336
15587
  }
15337
15588
  panelGeom = (0, import_transforms3.rotateX)(-Math.PI / 2, panelGeom);
15338
15589
  const polygons = geom32.toPolygons(panelGeom);
package/package.json CHANGED
@@ -2,9 +2,10 @@
2
2
  "name": "circuit-json-to-gltf",
3
3
  "main": "dist/index.js",
4
4
  "type": "module",
5
- "version": "0.0.72",
5
+ "version": "0.0.74",
6
6
  "scripts": {
7
7
  "test": "bun test tests/",
8
+ "test:playwright": "playwright test",
8
9
  "format": "biome format --write .",
9
10
  "format:check": "biome format .",
10
11
  "build:site": "cosmos-export",
@@ -23,6 +24,7 @@
23
24
  },
24
25
  "devDependencies": {
25
26
  "@biomejs/biome": "^2.1.4",
27
+ "@playwright/test": "^1.58.2",
26
28
  "@google/model-viewer": "^4.1.0",
27
29
  "@resvg/resvg-js": "^2.6.2",
28
30
  "@resvg/resvg-wasm": "^2.6.2",