@tscircuit/3d-viewer 0.0.489 → 0.0.491

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 +143 -101
  2. package/package.json +3 -3
package/dist/index.js CHANGED
@@ -7502,7 +7502,7 @@ var require_ellipse = __commonJS({
7502
7502
  var geom2 = require_geom2();
7503
7503
  var { sin: sin2, cos: cos2 } = require_trigonometry();
7504
7504
  var { isGTE, isNumberArray } = require_commonChecks();
7505
- var ellipse2 = (options) => {
7505
+ var ellipse3 = (options) => {
7506
7506
  const defaults = {
7507
7507
  center: [0, 0],
7508
7508
  radius: [1, 1],
@@ -7544,7 +7544,7 @@ var require_ellipse = __commonJS({
7544
7544
  if (rotation2 < TAU) points.push(centerv);
7545
7545
  return geom2.fromPoints(points);
7546
7546
  };
7547
- module.exports = ellipse2;
7547
+ module.exports = ellipse3;
7548
7548
  }
7549
7549
  });
7550
7550
 
@@ -7553,7 +7553,7 @@ var require_circle = __commonJS({
7553
7553
  "node_modules/@jscad/modeling/src/primitives/circle.js"(exports, module) {
7554
7554
  "use strict";
7555
7555
  var { TAU } = require_constants();
7556
- var ellipse2 = require_ellipse();
7556
+ var ellipse3 = require_ellipse();
7557
7557
  var { isGTE } = require_commonChecks();
7558
7558
  var circle = (options) => {
7559
7559
  const defaults = {
@@ -7566,7 +7566,7 @@ var require_circle = __commonJS({
7566
7566
  let { center, radius, startAngle, endAngle, segments } = Object.assign({}, defaults, options);
7567
7567
  if (!isGTE(radius, 0)) throw new Error("radius must be positive");
7568
7568
  radius = [radius, radius];
7569
- return ellipse2({ center, radius, startAngle, endAngle, segments });
7569
+ return ellipse3({ center, radius, startAngle, endAngle, segments });
7570
7570
  };
7571
7571
  module.exports = circle;
7572
7572
  }
@@ -12269,7 +12269,7 @@ var require_expand = __commonJS({
12269
12269
  var expandGeom2 = require_expandGeom2();
12270
12270
  var expandGeom3 = require_expandGeom3();
12271
12271
  var expandPath2 = require_expandPath2();
12272
- var expand3 = (options, ...objects) => {
12272
+ var expand2 = (options, ...objects) => {
12273
12273
  objects = flatten(objects);
12274
12274
  if (objects.length === 0) throw new Error("wrong number of arguments");
12275
12275
  const results = objects.map((object) => {
@@ -12280,7 +12280,7 @@ var require_expand = __commonJS({
12280
12280
  });
12281
12281
  return results.length === 1 ? results[0] : results;
12282
12282
  };
12283
- module.exports = expand3;
12283
+ module.exports = expand2;
12284
12284
  }
12285
12285
  });
12286
12286
 
@@ -12478,7 +12478,7 @@ var require_extrudeRectangularPath2 = __commonJS({
12478
12478
  "node_modules/@jscad/modeling/src/operations/extrusions/extrudeRectangularPath2.js"(exports, module) {
12479
12479
  "use strict";
12480
12480
  var path2 = require_path2();
12481
- var expand3 = require_expand();
12481
+ var expand2 = require_expand();
12482
12482
  var extrudeLinearGeom2 = require_extrudeLinearGeom2();
12483
12483
  var extrudeRectangularPath2 = (options, geometry) => {
12484
12484
  const defaults = {
@@ -12490,7 +12490,7 @@ var require_extrudeRectangularPath2 = __commonJS({
12490
12490
  options.offset = [0, 0, height10];
12491
12491
  const points = path2.toPoints(geometry);
12492
12492
  if (points.length === 0) throw new Error("the given geometry cannot be empty");
12493
- const newgeometry = expand3(options, geometry);
12493
+ const newgeometry = expand2(options, geometry);
12494
12494
  return extrudeLinearGeom2(options, newgeometry);
12495
12495
  };
12496
12496
  module.exports = extrudeRectangularPath2;
@@ -12504,7 +12504,7 @@ var require_extrudeRectangularGeom2 = __commonJS({
12504
12504
  var { area } = require_utils();
12505
12505
  var geom2 = require_geom2();
12506
12506
  var path2 = require_path2();
12507
- var expand3 = require_expand();
12507
+ var expand2 = require_expand();
12508
12508
  var extrudeLinearGeom2 = require_extrudeLinearGeom2();
12509
12509
  var extrudeRectangularGeom2 = (options, geometry) => {
12510
12510
  const defaults = {
@@ -12518,7 +12518,7 @@ var require_extrudeRectangularGeom2 = __commonJS({
12518
12518
  if (outlines.length === 0) throw new Error("the given geometry cannot be empty");
12519
12519
  const newparts = outlines.map((outline) => {
12520
12520
  if (area(outline) < 0) outline.reverse();
12521
- return expand3(options, path2.fromPoints({ closed: true }, outline));
12521
+ return expand2(options, path2.fromPoints({ closed: true }, outline));
12522
12522
  });
12523
12523
  const allsides = newparts.reduce((sides2, part) => sides2.concat(geom2.toSides(part)), []);
12524
12524
  const newgeometry = geom2.create(allsides);
@@ -30825,7 +30825,7 @@ import * as THREE16 from "three";
30825
30825
  // package.json
30826
30826
  var package_default = {
30827
30827
  name: "@tscircuit/3d-viewer",
30828
- version: "0.0.488",
30828
+ version: "0.0.490",
30829
30829
  main: "./dist/index.js",
30830
30830
  module: "./dist/index.js",
30831
30831
  type: "module",
@@ -30854,7 +30854,7 @@ var package_default = {
30854
30854
  dependencies: {
30855
30855
  "@jscad/regl-renderer": "^2.6.12",
30856
30856
  "@jscad/stl-serializer": "^2.1.20",
30857
- "circuit-to-canvas": "^0.0.26",
30857
+ "circuit-to-canvas": "^0.0.49",
30858
30858
  "react-hot-toast": "^2.6.0",
30859
30859
  three: "^0.165.0",
30860
30860
  "three-stdlib": "^2.36.0",
@@ -30891,7 +30891,7 @@ var package_default = {
30891
30891
  "react-use-gesture": "^9.1.3",
30892
30892
  semver: "^7.7.0",
30893
30893
  "strip-ansi": "^7.1.0",
30894
- tscircuit: "^0.0.1123",
30894
+ tscircuit: "^0.0.1141",
30895
30895
  tsup: "^8.3.6",
30896
30896
  typescript: "^5.7.3",
30897
30897
  vite: "^7.1.5",
@@ -32705,7 +32705,6 @@ var platedHole = (plated_hole, ctx, options = {}) => {
32705
32705
 
32706
32706
  // src/BoardGeomBuilder.ts
32707
32707
  var import_extrusions4 = __toESM(require_extrusions(), 1);
32708
- var import_expansions2 = __toESM(require_expansions(), 1);
32709
32708
 
32710
32709
  // src/geoms/brep-converter.ts
32711
32710
  var import_primitives5 = __toESM(require_primitives(), 1);
@@ -33353,60 +33352,68 @@ var BoardGeomBuilder = class {
33353
33352
  this.platedHoleGeoms = this.platedHoleGeoms.map(
33354
33353
  (phg) => (0, import_colors3.colorize)(colors.copper, (0, import_booleans5.subtract)(phg, copperCut))
33355
33354
  );
33356
- } else if (hole.hole_shape === "pill" || hole.hole_shape === "rotated_pill") {
33357
- const holeWidth = hole.hole_width;
33358
- const holeHeight = hole.hole_height;
33359
- const holeRadius = Math.min(holeWidth, holeHeight) / 2;
33360
- const rectLength = Math.abs(holeWidth - holeHeight);
33361
- const isRotated = hole.hole_shape === "rotated_pill";
33362
- let pillHole;
33363
- if (holeWidth > holeHeight) {
33364
- pillHole = (0, import_booleans5.union)(
33365
- (0, import_primitives7.cuboid)({
33366
- center: [hole.x, hole.y, 0],
33367
- size: [rectLength, holeHeight, holeDepth]
33368
- }),
33369
- (0, import_primitives7.cylinder)({
33370
- center: [hole.x - rectLength / 2, hole.y, 0],
33371
- radius: holeRadius,
33372
- height: holeDepth
33373
- }),
33374
- (0, import_primitives7.cylinder)({
33375
- center: [hole.x + rectLength / 2, hole.y, 0],
33376
- radius: holeRadius,
33377
- height: holeDepth
33378
- })
33355
+ } else if (hole.hole_shape === "pill" || hole.hole_shape === "rotated_pill" || hole.hole_shape === "oval") {
33356
+ const holeWidth = hole.hole_width ?? hole.hole_diameter;
33357
+ const holeHeight = hole.hole_height ?? hole.hole_diameter;
33358
+ const rotation2 = hole.ccw_rotation ?? hole.rotation ?? 0;
33359
+ const copperInset2 = 0.02;
33360
+ const createHoleGeom = (w, h2, depth, isOval) => {
33361
+ if (w <= 0 || h2 <= 0) return null;
33362
+ if (isOval) {
33363
+ return (0, import_transforms6.translate)(
33364
+ [0, 0, -depth / 2],
33365
+ (0, import_extrusions4.extrudeLinear)(
33366
+ { height: depth },
33367
+ (0, import_primitives7.ellipse)({ radius: [w / 2, h2 / 2] })
33368
+ )
33369
+ );
33370
+ }
33371
+ const radius = Math.min(w, h2) / 2;
33372
+ const length2 = Math.abs(w - h2);
33373
+ if (w > h2) {
33374
+ return (0, import_booleans5.union)(
33375
+ (0, import_primitives7.cuboid)({ center: [0, 0, 0], size: [length2, h2, depth] }),
33376
+ (0, import_primitives7.cylinder)({ center: [-length2 / 2, 0, 0], radius, height: depth }),
33377
+ (0, import_primitives7.cylinder)({ center: [length2 / 2, 0, 0], radius, height: depth })
33378
+ );
33379
+ }
33380
+ return (0, import_booleans5.union)(
33381
+ (0, import_primitives7.cuboid)({ center: [0, 0, 0], size: [w, length2, depth] }),
33382
+ (0, import_primitives7.cylinder)({ center: [0, -length2 / 2, 0], radius, height: depth }),
33383
+ (0, import_primitives7.cylinder)({ center: [0, length2 / 2, 0], radius, height: depth })
33379
33384
  );
33380
- } else {
33381
- pillHole = (0, import_booleans5.union)(
33382
- (0, import_primitives7.cuboid)({
33383
- center: [hole.x, hole.y, 0],
33384
- size: [holeWidth, rectLength, holeDepth]
33385
- }),
33386
- (0, import_primitives7.cylinder)({
33387
- center: [hole.x, hole.y - rectLength / 2, 0],
33388
- radius: holeRadius,
33389
- height: holeDepth
33390
- }),
33391
- (0, import_primitives7.cylinder)({
33392
- center: [hole.x, hole.y + rectLength / 2, 0],
33393
- radius: holeRadius,
33394
- height: holeDepth
33395
- })
33385
+ };
33386
+ let boardHole = createHoleGeom(
33387
+ holeWidth,
33388
+ holeHeight,
33389
+ holeDepth,
33390
+ hole.hole_shape === "oval"
33391
+ );
33392
+ let copperCut = createHoleGeom(
33393
+ holeWidth - 2 * copperInset2,
33394
+ holeHeight - 2 * copperInset2,
33395
+ holeDepth,
33396
+ hole.hole_shape === "oval"
33397
+ );
33398
+ if (boardHole && rotation2 !== 0) {
33399
+ boardHole = (0, import_transforms6.rotateZ)(rotation2 * Math.PI / 180, boardHole);
33400
+ }
33401
+ if (copperCut && rotation2 !== 0) {
33402
+ copperCut = (0, import_transforms6.rotateZ)(rotation2 * Math.PI / 180, copperCut);
33403
+ }
33404
+ if (boardHole) {
33405
+ const positionedBoardHole = (0, import_transforms6.translate)([hole.x, hole.y, 0], boardHole);
33406
+ this.boardGeom = (0, import_booleans5.subtract)(this.boardGeom, positionedBoardHole);
33407
+ this.padGeoms = this.padGeoms.map(
33408
+ (pg) => (0, import_colors3.colorize)(colors.copper, (0, import_booleans5.subtract)(pg, positionedBoardHole))
33396
33409
  );
33397
33410
  }
33398
- if (isRotated) {
33399
- const rotationRadians = hole.ccw_rotation * Math.PI / 180;
33400
- pillHole = (0, import_transforms6.rotateZ)(rotationRadians, pillHole);
33411
+ if (copperCut) {
33412
+ const positionedCopperCut = (0, import_transforms6.translate)([hole.x, hole.y, 0], copperCut);
33413
+ this.platedHoleGeoms = this.platedHoleGeoms.map(
33414
+ (phg) => (0, import_colors3.colorize)(colors.copper, (0, import_booleans5.subtract)(phg, positionedCopperCut))
33415
+ );
33401
33416
  }
33402
- this.boardGeom = (0, import_booleans5.subtract)(this.boardGeom, pillHole);
33403
- this.padGeoms = this.padGeoms.map(
33404
- (pg) => (0, import_colors3.colorize)(colors.copper, (0, import_booleans5.subtract)(pg, pillHole))
33405
- );
33406
- const copperPill = (0, import_expansions2.expand)({ delta: -copperInset }, pillHole);
33407
- this.platedHoleGeoms = this.platedHoleGeoms.map(
33408
- (phg) => (0, import_colors3.colorize)(colors.copper, (0, import_booleans5.subtract)(phg, copperPill))
33409
- );
33410
33417
  }
33411
33418
  }
33412
33419
  processPad(pad2) {
@@ -34288,6 +34295,14 @@ function createSoldermaskTextureForLayer({
34288
34295
  );
34289
34296
  ctx.fill();
34290
34297
  }
34298
+ } else if (holeShape === "oval" && typeof hole.hole_width === "number" && typeof hole.hole_height === "number") {
34299
+ const width10 = hole.hole_width * traceTextureResolution;
34300
+ const height10 = hole.hole_height * traceTextureResolution;
34301
+ const radiusX = width10 / 2;
34302
+ const radiusY = height10 / 2;
34303
+ ctx.beginPath();
34304
+ ctx.ellipse(canvasX, canvasY, radiusX, radiusY, 0, 0, 2 * Math.PI);
34305
+ ctx.fill();
34291
34306
  }
34292
34307
  });
34293
34308
  const pcbCopperPours = su5(circuitJson).pcb_copper_pour.list();
@@ -36301,16 +36316,7 @@ function createPlatedHoleDrill({
36301
36316
  }
36302
36317
 
36303
36318
  // src/utils/manifold/process-non-plated-holes.ts
36304
- function isCircleHole(hole) {
36305
- return hole.hole_shape === "circle" && typeof hole.hole_diameter === "number";
36306
- }
36307
- function isPillHole(hole) {
36308
- return hole.hole_shape === "pill" && typeof hole.hole_width === "number" && typeof hole.hole_height === "number";
36309
- }
36310
- function isRotatedPillHole(hole) {
36311
- return hole.hole_shape === "rotated_pill" && typeof hole.hole_width === "number" && typeof hole.hole_height === "number" && typeof hole.ccw_rotation === "number";
36312
- }
36313
- function processNonPlatedHolesForManifold(Manifold, circuitJson, pcbThickness, manifoldInstancesForCleanup) {
36319
+ function processNonPlatedHolesForManifold(Manifold, CrossSection, circuitJson, pcbThickness, manifoldInstancesForCleanup) {
36314
36320
  const nonPlatedHoleBoardDrills = [];
36315
36321
  const pcbHoles = su14(circuitJson).pcb_hole.list();
36316
36322
  const createPillOp = (width10, height10, depth) => {
@@ -36324,37 +36330,71 @@ function processNonPlatedHolesForManifold(Manifold, circuitJson, pcbThickness, m
36324
36330
  manifoldInstancesForCleanup.push(pillOp);
36325
36331
  return pillOp;
36326
36332
  };
36333
+ const createEllipsePoints2 = (w, h2, segments) => {
36334
+ const points = [];
36335
+ for (let i = 0; i < segments; i++) {
36336
+ const theta = 2 * Math.PI * i / segments;
36337
+ points.push([w / 2 * Math.cos(theta), h2 / 2 * Math.sin(theta)]);
36338
+ }
36339
+ return points;
36340
+ };
36327
36341
  pcbHoles.forEach((hole) => {
36328
- if (isCircleHole(hole)) {
36329
- const translatedDrill = createCircleHoleDrill({
36342
+ const holeShape = hole.hole_shape;
36343
+ const holeX = hole.x;
36344
+ const holeY = hole.y;
36345
+ const drillDepth = pcbThickness * 1.2;
36346
+ const rotation2 = hole.ccw_rotation ?? hole.rotation ?? 0;
36347
+ const holeW = hole.hole_width ?? hole.hole_diameter;
36348
+ const holeH = hole.hole_height ?? hole.hole_diameter;
36349
+ let holeOp = null;
36350
+ if (holeShape === "circle") {
36351
+ holeOp = createCircleHoleDrill({
36330
36352
  Manifold,
36331
- x: hole.x,
36332
- y: hole.y,
36353
+ x: holeX,
36354
+ y: holeY,
36333
36355
  diameter: hole.hole_diameter,
36334
36356
  thickness: pcbThickness,
36335
36357
  segments: SMOOTH_CIRCLE_SEGMENTS
36336
36358
  });
36337
- manifoldInstancesForCleanup.push(translatedDrill);
36338
- nonPlatedHoleBoardDrills.push(translatedDrill);
36339
- } else if (isPillHole(hole)) {
36340
- const holeW = hole.hole_width;
36341
- const holeH = hole.hole_height;
36342
- const drillDepth = pcbThickness * 1.2;
36343
- const pillDrillOp = createPillOp(holeW, holeH, drillDepth);
36344
- const translatedPillDrill = pillDrillOp.translate([hole.x, hole.y, 0]);
36345
- manifoldInstancesForCleanup.push(translatedPillDrill);
36346
- nonPlatedHoleBoardDrills.push(translatedPillDrill);
36347
- } else if (isRotatedPillHole(hole)) {
36348
- const holeW = hole.hole_width;
36349
- const holeH = hole.hole_height;
36350
- const drillDepth = pcbThickness * 1.2;
36351
- let pillDrillOp = createPillOp(holeW, holeH, drillDepth);
36352
- const rotatedOp = pillDrillOp.rotate([0, 0, hole.ccw_rotation]);
36353
- manifoldInstancesForCleanup.push(rotatedOp);
36354
- pillDrillOp = rotatedOp;
36355
- const translatedPillDrill = pillDrillOp.translate([hole.x, hole.y, 0]);
36356
- manifoldInstancesForCleanup.push(translatedPillDrill);
36357
- nonPlatedHoleBoardDrills.push(translatedPillDrill);
36359
+ nonPlatedHoleBoardDrills.push(holeOp);
36360
+ manifoldInstancesForCleanup.push(holeOp);
36361
+ return;
36362
+ }
36363
+ if (holeShape === "pill" || holeShape === "rotated_pill") {
36364
+ holeOp = createPillOp(holeW, holeH, drillDepth);
36365
+ } else if (holeShape === "oval") {
36366
+ let points = createEllipsePoints2(holeW, holeH, SMOOTH_CIRCLE_SEGMENTS);
36367
+ let area = 0;
36368
+ for (let i = 0; i < points.length; i++) {
36369
+ const j = (i + 1) % points.length;
36370
+ area += points[i][0] * points[j][1];
36371
+ area -= points[j][0] * points[i][1];
36372
+ }
36373
+ if (area <= 0) {
36374
+ points = points.reverse();
36375
+ }
36376
+ const crossSection = CrossSection.ofPolygons([points]);
36377
+ manifoldInstancesForCleanup.push(crossSection);
36378
+ holeOp = Manifold.extrude(
36379
+ crossSection,
36380
+ drillDepth,
36381
+ 0,
36382
+ 0,
36383
+ [1, 1],
36384
+ true
36385
+ // center
36386
+ );
36387
+ manifoldInstancesForCleanup.push(holeOp);
36388
+ }
36389
+ if (holeOp) {
36390
+ if (rotation2 !== 0) {
36391
+ const rotatedOp = holeOp.rotate([0, 0, rotation2]);
36392
+ manifoldInstancesForCleanup.push(rotatedOp);
36393
+ holeOp = rotatedOp;
36394
+ }
36395
+ const translatedHole = holeOp.translate([holeX, holeY, 0]);
36396
+ manifoldInstancesForCleanup.push(translatedHole);
36397
+ nonPlatedHoleBoardDrills.push(translatedHole);
36358
36398
  }
36359
36399
  });
36360
36400
  return { nonPlatedHoleBoardDrills };
@@ -37189,6 +37229,7 @@ function createCopperPourTextureForLayer({
37189
37229
  const copperPours = circuitJson.filter(
37190
37230
  (e) => e.type === "pcb_copper_pour"
37191
37231
  );
37232
+ const pcbRenderLayer2 = layer === "top" ? "top_copper" : "bottom_copper";
37192
37233
  const poursOnLayer = copperPours.filter((p) => p.layer === layer);
37193
37234
  if (poursOnLayer.length === 0) return null;
37194
37235
  const boardOutlineBounds = calculateOutlineBounds(boardData);
@@ -37244,7 +37285,7 @@ function createCopperPourTextureForLayer({
37244
37285
  }
37245
37286
  }
37246
37287
  });
37247
- drawer.drawElements(coveredPours, { layers: [layer] });
37288
+ drawer.drawElements(coveredPours, { layers: [pcbRenderLayer2] });
37248
37289
  }
37249
37290
  if (uncoveredPours.length > 0) {
37250
37291
  drawer.configure({
@@ -37261,7 +37302,7 @@ function createCopperPourTextureForLayer({
37261
37302
  }
37262
37303
  }
37263
37304
  });
37264
- drawer.drawElements(uncoveredPours, { layers: [layer] });
37305
+ drawer.drawElements(uncoveredPours, { layers: [pcbRenderLayer2] });
37265
37306
  }
37266
37307
  }
37267
37308
  for (const pour of brepPours) {
@@ -37653,6 +37694,7 @@ var useManifoldBoardBuilder = (manifoldJSModule, circuitJson) => {
37653
37694
  let holeUnion = null;
37654
37695
  const { nonPlatedHoleBoardDrills } = processNonPlatedHolesForManifold(
37655
37696
  Manifold,
37697
+ CrossSection,
37656
37698
  circuitJson,
37657
37699
  currentPcbThickness,
37658
37700
  manifoldInstancesForCleanup.current
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tscircuit/3d-viewer",
3
- "version": "0.0.489",
3
+ "version": "0.0.491",
4
4
  "main": "./dist/index.js",
5
5
  "module": "./dist/index.js",
6
6
  "type": "module",
@@ -29,7 +29,7 @@
29
29
  "dependencies": {
30
30
  "@jscad/regl-renderer": "^2.6.12",
31
31
  "@jscad/stl-serializer": "^2.1.20",
32
- "circuit-to-canvas": "^0.0.26",
32
+ "circuit-to-canvas": "^0.0.49",
33
33
  "react-hot-toast": "^2.6.0",
34
34
  "three": "^0.165.0",
35
35
  "three-stdlib": "^2.36.0",
@@ -66,7 +66,7 @@
66
66
  "react-use-gesture": "^9.1.3",
67
67
  "semver": "^7.7.0",
68
68
  "strip-ansi": "^7.1.0",
69
- "tscircuit": "^0.0.1123",
69
+ "tscircuit": "^0.0.1141",
70
70
  "tsup": "^8.3.6",
71
71
  "typescript": "^5.7.3",
72
72
  "vite": "^7.1.5",