@tscircuit/3d-viewer 0.0.511 → 0.0.513

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 (3) hide show
  1. package/dist/index.d.ts +26 -17
  2. package/dist/index.js +719 -1043
  3. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -28,7 +28,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
28
28
  var require_flatten = __commonJS({
29
29
  "node_modules/@jscad/modeling/src/utils/flatten.js"(exports, module) {
30
30
  "use strict";
31
- var flatten = (arr) => arr.reduce((acc, val) => Array.isArray(val) ? acc.concat(flatten(val)) : acc.concat(val), []);
31
+ var flatten = (arr) => arr.flat(Infinity);
32
32
  module.exports = flatten;
33
33
  }
34
34
  });
@@ -9255,9 +9255,10 @@ var require_extrudeWalls = __commonJS({
9255
9255
  return edges;
9256
9256
  }
9257
9257
  const divisor = vec3.fromValues(multiple, multiple, multiple);
9258
+ const increment = vec3.create();
9258
9259
  const newEdges = [];
9259
9260
  edges.forEach((edge) => {
9260
- const increment = vec3.subtract(vec3.create(), edge[1], edge[0]);
9261
+ vec3.subtract(increment, edge[1], edge[0]);
9261
9262
  vec3.divide(increment, increment, divisor);
9262
9263
  let prev = edge[0];
9263
9264
  for (let i = 1; i <= multiple; ++i) {
@@ -9336,7 +9337,10 @@ var require_extrudeFromSlices = __commonJS({
9336
9337
  const edges = slice.toEdges(currentSlice);
9337
9338
  if (edges.length === 0) throw new Error("the callback function must return slices with one or more edges");
9338
9339
  if (prevSlice) {
9339
- polygons = polygons.concat(extrudeWalls(prevSlice, currentSlice));
9340
+ const walls = extrudeWalls(prevSlice, currentSlice);
9341
+ for (let i = 0; i < walls.length; i++) {
9342
+ polygons.push(walls[i]);
9343
+ }
9340
9344
  }
9341
9345
  if (s === 0) startSlice = currentSlice;
9342
9346
  if (s === numberOfSlices - 1) endSlice = currentSlice;
@@ -9345,15 +9349,22 @@ var require_extrudeFromSlices = __commonJS({
9345
9349
  }
9346
9350
  if (capEnd) {
9347
9351
  const endPolygons = slice.toPolygons(endSlice);
9348
- polygons = polygons.concat(endPolygons);
9352
+ for (let i = 0; i < endPolygons.length; i++) {
9353
+ polygons.push(endPolygons[i]);
9354
+ }
9349
9355
  }
9350
9356
  if (capStart) {
9351
9357
  const startPolygons = slice.toPolygons(startSlice).map(poly3.invert);
9352
- polygons = polygons.concat(startPolygons);
9358
+ for (let i = 0; i < startPolygons.length; i++) {
9359
+ polygons.push(startPolygons[i]);
9360
+ }
9353
9361
  }
9354
9362
  if (!capStart && !capEnd) {
9355
9363
  if (close && !slice.equals(endSlice, startSlice)) {
9356
- polygons = polygons.concat(extrudeWalls(endSlice, startSlice));
9364
+ const walls = extrudeWalls(endSlice, startSlice);
9365
+ for (let i = 0; i < walls.length; i++) {
9366
+ polygons.push(walls[i]);
9367
+ }
9357
9368
  }
9358
9369
  }
9359
9370
  return geom3.create(polygons);
@@ -9429,12 +9440,15 @@ var require_extrudeRotate = __commonJS({
9429
9440
  const baseSlice = slice.fromSides(geom2.toSides(geometry));
9430
9441
  slice.reverse(baseSlice, baseSlice);
9431
9442
  const matrix = mat4.create();
9443
+ const xRotationMatrix = mat4.fromXRotation(mat4.create(), TAU / 4);
9444
+ const zRotationMatrix = mat4.create();
9432
9445
  const createSlice = (progress, index2, base) => {
9433
9446
  let Zrotation = rotationPerSlice * index2 + startAngle;
9434
9447
  if (totalRotation === TAU && index2 === segments) {
9435
9448
  Zrotation = startAngle;
9436
9449
  }
9437
- mat4.multiply(matrix, mat4.fromZRotation(matrix, Zrotation), mat4.fromXRotation(mat4.create(), TAU / 4));
9450
+ mat4.fromZRotation(zRotationMatrix, Zrotation);
9451
+ mat4.multiply(matrix, zRotationMatrix, xRotationMatrix);
9438
9452
  return slice.transform(matrix, base);
9439
9453
  };
9440
9454
  options = {
@@ -10323,6 +10337,7 @@ var require_reTesselateCoplanarPolygons = __commonJS({
10323
10337
  const newoutpolygonrow = [];
10324
10338
  const ycoordinate = ycoordinates[yindex];
10325
10339
  const polygonindexeswithcorner = ycoordinatetopolygonindexes.get(ycoordinate);
10340
+ let removeCount = 0;
10326
10341
  for (let activepolygonindex = 0; activepolygonindex < activepolygons.length; ++activepolygonindex) {
10327
10342
  const activepolygon = activepolygons[activepolygonindex];
10328
10343
  const polygonindex = activepolygon.polygonindex;
@@ -10343,8 +10358,8 @@ var require_reTesselateCoplanarPolygons = __commonJS({
10343
10358
  newrightvertexindex = nextrightvertexindex;
10344
10359
  }
10345
10360
  if (newleftvertexindex !== activepolygon.leftvertexindex && newleftvertexindex === newrightvertexindex) {
10346
- activepolygons.splice(activepolygonindex, 1);
10347
- --activepolygonindex;
10361
+ activepolygon._remove = true;
10362
+ removeCount++;
10348
10363
  } else {
10349
10364
  activepolygon.leftvertexindex = newleftvertexindex;
10350
10365
  activepolygon.rightvertexindex = newrightvertexindex;
@@ -10359,6 +10374,9 @@ var require_reTesselateCoplanarPolygons = __commonJS({
10359
10374
  }
10360
10375
  }
10361
10376
  }
10377
+ if (removeCount > 0) {
10378
+ activepolygons = activepolygons.filter((p) => !p._remove);
10379
+ }
10362
10380
  let nextycoordinate;
10363
10381
  if (yindex >= ycoordinates.length - 1) {
10364
10382
  activepolygons = [];
@@ -10534,8 +10552,10 @@ var require_retessellate = __commonJS({
10534
10552
  const destPolygons = [];
10535
10553
  classified.forEach((group) => {
10536
10554
  if (Array.isArray(group)) {
10537
- const reTessellateCoplanarPolygons = reTesselateCoplanarPolygons(group);
10538
- destPolygons.push(...reTessellateCoplanarPolygons);
10555
+ const coplanarPolygons = reTesselateCoplanarPolygons(group);
10556
+ for (let i = 0; i < coplanarPolygons.length; i++) {
10557
+ destPolygons.push(coplanarPolygons[i]);
10558
+ }
10539
10559
  } else {
10540
10560
  destPolygons.push(group);
10541
10561
  }
@@ -10768,6 +10788,19 @@ var require_splitPolygonByPlane = __commonJS({
10768
10788
  var vec3 = require_vec3();
10769
10789
  var poly3 = require_poly3();
10770
10790
  var splitLineSegmentByPlane = require_splitLineSegmentByPlane();
10791
+ var EPS_SQUARED = EPS * EPS;
10792
+ var removeConsecutiveDuplicates = (vertices) => {
10793
+ const result = [];
10794
+ let prevvertex = vertices[vertices.length - 1];
10795
+ for (let i = 0; i < vertices.length; i++) {
10796
+ const vertex = vertices[i];
10797
+ if (vec3.squaredDistance(vertex, prevvertex) >= EPS_SQUARED) {
10798
+ result.push(vertex);
10799
+ }
10800
+ prevvertex = vertex;
10801
+ }
10802
+ return result;
10803
+ };
10771
10804
  var splitPolygonByPlane = (splane, polygon3) => {
10772
10805
  const result = {
10773
10806
  type: null,
@@ -10829,34 +10862,17 @@ var require_splitPolygonByPlane = __commonJS({
10829
10862
  }
10830
10863
  isback = nextisback;
10831
10864
  }
10832
- const EPS_SQUARED = EPS * EPS;
10833
- if (backvertices.length >= 3) {
10834
- let prevvertex = backvertices[backvertices.length - 1];
10835
- for (let vertexindex = 0; vertexindex < backvertices.length; vertexindex++) {
10836
- const vertex = backvertices[vertexindex];
10837
- if (vec3.squaredDistance(vertex, prevvertex) < EPS_SQUARED) {
10838
- backvertices.splice(vertexindex, 1);
10839
- vertexindex--;
10840
- }
10841
- prevvertex = vertex;
10842
- }
10843
- }
10844
10865
  if (frontvertices.length >= 3) {
10845
- let prevvertex = frontvertices[frontvertices.length - 1];
10846
- for (let vertexindex = 0; vertexindex < frontvertices.length; vertexindex++) {
10847
- const vertex = frontvertices[vertexindex];
10848
- if (vec3.squaredDistance(vertex, prevvertex) < EPS_SQUARED) {
10849
- frontvertices.splice(vertexindex, 1);
10850
- vertexindex--;
10851
- }
10852
- prevvertex = vertex;
10866
+ const frontFiltered = removeConsecutiveDuplicates(frontvertices);
10867
+ if (frontFiltered.length >= 3) {
10868
+ result.front = poly3.fromPointsAndPlane(frontFiltered, pplane);
10853
10869
  }
10854
10870
  }
10855
- if (frontvertices.length >= 3) {
10856
- result.front = poly3.fromPointsAndPlane(frontvertices, pplane);
10857
- }
10858
10871
  if (backvertices.length >= 3) {
10859
- result.back = poly3.fromPointsAndPlane(backvertices, pplane);
10872
+ const backFiltered = removeConsecutiveDuplicates(backvertices);
10873
+ if (backFiltered.length >= 3) {
10874
+ result.back = poly3.fromPointsAndPlane(backFiltered, pplane);
10875
+ }
10860
10876
  }
10861
10877
  }
10862
10878
  }
@@ -10900,10 +10916,6 @@ var require_PolygonTreeNode = __commonJS({
10900
10916
  if (!this.removed) {
10901
10917
  this.removed = true;
10902
10918
  this.polygon = null;
10903
- const parentschildren = this.parent.children;
10904
- const i = parentschildren.indexOf(this);
10905
- if (i < 0) throw new Error("Assertion failed");
10906
- parentschildren.splice(i, 1);
10907
10919
  this.parent.recursivelyInvalidatePolygon();
10908
10920
  }
10909
10921
  }
@@ -10923,6 +10935,13 @@ var require_PolygonTreeNode = __commonJS({
10923
10935
  return this.polygon;
10924
10936
  }
10925
10937
  getPolygons(result) {
10938
+ if (this.isRootNode() && this.children.length > 0) {
10939
+ const compacted = [];
10940
+ for (let i2 = 0; i2 < this.children.length; i2++) {
10941
+ if (!this.children[i2].removed) compacted.push(this.children[i2]);
10942
+ }
10943
+ this.children = compacted;
10944
+ }
10926
10945
  let children = [this];
10927
10946
  const queue = [children];
10928
10947
  let i, j, l, node;
@@ -14226,7 +14245,7 @@ var require_browser = __commonJS({
14226
14245
 
14227
14246
  // src/CadViewer.tsx
14228
14247
  import { useState as useState36, useCallback as useCallback21, useRef as useRef26, useEffect as useEffect44 } from "react";
14229
- import * as THREE34 from "three";
14248
+ import * as THREE36 from "three";
14230
14249
 
14231
14250
  // src/CadViewerJscad.tsx
14232
14251
  import { su as su12 } from "@tscircuit/circuit-json-util";
@@ -28338,9 +28357,9 @@ var AnyCadComponent = ({
28338
28357
  if (!cad_component.position) return void 0;
28339
28358
  let z18;
28340
28359
  if (layer === "top") {
28341
- z18 = pcbThickness / 2;
28360
+ z18 = cad_component.position.z;
28342
28361
  } else if (layer === "bottom") {
28343
- z18 = -(pcbThickness / 2);
28362
+ z18 = -(cad_component.position.z + pcbThickness);
28344
28363
  } else {
28345
28364
  z18 = cad_component.position.z;
28346
28365
  }
@@ -28464,7 +28483,7 @@ import * as THREE16 from "three";
28464
28483
  // package.json
28465
28484
  var package_default = {
28466
28485
  name: "@tscircuit/3d-viewer",
28467
- version: "0.0.510",
28486
+ version: "0.0.512",
28468
28487
  main: "./dist/index.js",
28469
28488
  module: "./dist/index.js",
28470
28489
  type: "module",
@@ -31543,529 +31562,12 @@ var ThreeErrorBoundary = class extends React11.Component {
31543
31562
  import { su as su9 } from "@tscircuit/circuit-json-util";
31544
31563
  import { useEffect as useEffect23, useMemo as useMemo19 } from "react";
31545
31564
 
31546
- // src/textures/create-copper-pour-texture-for-layer.ts
31547
- import * as THREE19 from "three";
31548
- import { CircuitToCanvasDrawer } from "circuit-to-canvas";
31549
-
31550
- // node_modules/@tscircuit/math-utils/dist/chunk-5N7UJNVK.js
31551
- var getBoundsFromPoints = (points) => {
31552
- if (points.length === 0) {
31553
- return null;
31554
- }
31555
- let minX = points[0].x;
31556
- let minY = points[0].y;
31557
- let maxX = points[0].x;
31558
- let maxY = points[0].y;
31559
- for (let i = 1; i < points.length; i++) {
31560
- const point = points[i];
31561
- if (point.x < minX) minX = point.x;
31562
- if (point.y < minY) minY = point.y;
31563
- if (point.x > maxX) maxX = point.x;
31564
- if (point.y > maxY) maxY = point.y;
31565
- }
31566
- return { minX, minY, maxX, maxY };
31567
- };
31568
-
31569
- // src/utils/outline-bounds.ts
31570
- function calculateOutlineBounds(boardData) {
31571
- if (boardData.outline && boardData.outline.length >= 3) {
31572
- const bounds2 = getBoundsFromPoints(boardData.outline);
31573
- return {
31574
- ...bounds2,
31575
- width: bounds2.maxX - bounds2.minX,
31576
- height: bounds2.maxY - bounds2.minY,
31577
- centerX: (bounds2.minX + bounds2.maxX) / 2,
31578
- centerY: (bounds2.minY + bounds2.maxY) / 2
31579
- };
31580
- }
31581
- const boardWidth = boardData.width ?? 0;
31582
- const boardHeight = boardData.height ?? 0;
31583
- const boardCenterX = boardData.center?.x ?? 0;
31584
- const boardCenterY = boardData.center?.y ?? 0;
31585
- const bounds = {
31586
- minX: boardCenterX - boardWidth / 2,
31587
- maxX: boardCenterX + boardWidth / 2,
31588
- minY: boardCenterY - boardHeight / 2,
31589
- maxY: boardCenterY + boardHeight / 2
31590
- };
31591
- return {
31592
- ...bounds,
31593
- width: boardWidth,
31594
- height: boardHeight,
31595
- centerX: boardCenterX,
31596
- centerY: boardCenterY
31597
- };
31598
- }
31599
-
31600
- // src/geoms/brep-converter.ts
31601
- var import_primitives7 = __toESM(require_primitives(), 1);
31602
- var import_booleans5 = __toESM(require_booleans(), 1);
31603
- function segmentToPoints(p1, p2, bulge, arcSegments) {
31604
- if (!bulge || Math.abs(bulge) < 1e-9) {
31605
- return [];
31606
- }
31607
- const theta = 4 * Math.atan(bulge);
31608
- const dx = p2[0] - p1[0];
31609
- const dy = p2[1] - p1[1];
31610
- const dist = Math.sqrt(dx * dx + dy * dy);
31611
- if (dist < 1e-9) return [];
31612
- const radius = Math.abs(dist / (2 * Math.sin(theta / 2)));
31613
- const m = Math.sqrt(Math.max(0, radius * radius - dist / 2 * (dist / 2)));
31614
- const midPoint = [(p1[0] + p2[0]) / 2, (p1[1] + p2[1]) / 2];
31615
- const ux = dx / dist;
31616
- const uy = dy / dist;
31617
- const nx = -uy;
31618
- const ny = ux;
31619
- const centerX = midPoint[0] + nx * m * Math.sign(bulge);
31620
- const centerY = midPoint[1] + ny * m * Math.sign(bulge);
31621
- const startAngle = Math.atan2(p1[1] - centerY, p1[0] - centerX);
31622
- const points = [];
31623
- const numSteps = Math.max(
31624
- 2,
31625
- Math.ceil(arcSegments * Math.abs(theta) / (Math.PI * 2) * 4)
31626
- );
31627
- const angleStep = theta / numSteps;
31628
- for (let i = 1; i < numSteps; i++) {
31629
- const angle = startAngle + angleStep * i;
31630
- points.push([
31631
- centerX + radius * Math.cos(angle),
31632
- centerY + radius * Math.sin(angle)
31633
- ]);
31634
- }
31635
- return points;
31636
- }
31637
- function ringToPoints(ring, arcSegments) {
31638
- const allPoints = [];
31639
- const vertices = ring.vertices;
31640
- for (let i = 0; i < vertices.length; i++) {
31641
- const p1 = vertices[i];
31642
- const p2 = vertices[(i + 1) % vertices.length];
31643
- allPoints.push([p1.x, p1.y]);
31644
- if (p1.bulge) {
31645
- const arcPoints = segmentToPoints(
31646
- [p1.x, p1.y],
31647
- [p2.x, p2.y],
31648
- p1.bulge,
31649
- arcSegments
31650
- );
31651
- allPoints.push(...arcPoints);
31652
- }
31653
- }
31654
- return allPoints;
31655
- }
31656
-
31657
- // src/textures/create-copper-pour-texture-for-layer.ts
31658
- function drawPolygon({
31659
- ctx,
31660
- points,
31661
- canvasXFromPcb,
31662
- canvasYFromPcb
31663
- }) {
31664
- if (points.length < 3) return;
31665
- ctx.beginPath();
31666
- points.forEach((point, index2) => {
31667
- const canvasX = canvasXFromPcb(point[0]);
31668
- const canvasY = canvasYFromPcb(point[1]);
31669
- if (index2 === 0) {
31670
- ctx.moveTo(canvasX, canvasY);
31671
- } else {
31672
- ctx.lineTo(canvasX, canvasY);
31673
- }
31674
- });
31675
- ctx.closePath();
31676
- ctx.fill();
31677
- }
31678
- function drawBrepShape({
31679
- ctx,
31680
- pour,
31681
- canvasXFromPcb,
31682
- canvasYFromPcb
31683
- }) {
31684
- const brepShape = pour.brep_shape;
31685
- if (!brepShape || !brepShape.outer_ring) return;
31686
- const outerRingPoints = ringToPoints(brepShape.outer_ring, 32);
31687
- if (outerRingPoints.length >= 3) {
31688
- drawPolygon({
31689
- ctx,
31690
- points: outerRingPoints,
31691
- canvasXFromPcb,
31692
- canvasYFromPcb
31693
- });
31694
- }
31695
- if (brepShape.inner_rings && brepShape.inner_rings.length > 0) {
31696
- ctx.globalCompositeOperation = "destination-out";
31697
- for (const innerRing of brepShape.inner_rings) {
31698
- const innerRingPoints = ringToPoints(innerRing, 32);
31699
- if (innerRingPoints.length >= 3) {
31700
- drawPolygon({
31701
- ctx,
31702
- points: innerRingPoints,
31703
- canvasXFromPcb,
31704
- canvasYFromPcb
31705
- });
31706
- }
31707
- }
31708
- ctx.globalCompositeOperation = "source-over";
31709
- }
31710
- }
31711
- function createCopperPourTextureForLayer({
31712
- layer,
31713
- circuitJson,
31714
- boardData,
31715
- traceTextureResolution = TRACE_TEXTURE_RESOLUTION
31716
- }) {
31717
- const copperPours = circuitJson.filter(
31718
- (e) => e.type === "pcb_copper_pour"
31719
- );
31720
- const pcbRenderLayer = layer === "top" ? "top_copper" : "bottom_copper";
31721
- const poursOnLayer = copperPours.filter((p) => p.layer === layer);
31722
- if (poursOnLayer.length === 0) return null;
31723
- const boardOutlineBounds = calculateOutlineBounds(boardData);
31724
- const canvas = document.createElement("canvas");
31725
- const canvasWidth = Math.floor(
31726
- boardOutlineBounds.width * traceTextureResolution
31727
- );
31728
- const canvasHeight = Math.floor(
31729
- boardOutlineBounds.height * traceTextureResolution
31730
- );
31731
- canvas.width = canvasWidth;
31732
- canvas.height = canvasHeight;
31733
- const ctx = canvas.getContext("2d");
31734
- if (!ctx) return null;
31735
- if (layer === "bottom") {
31736
- ctx.translate(0, canvasHeight);
31737
- ctx.scale(1, -1);
31738
- }
31739
- const canvasXFromPcb = (pcbX) => (pcbX - boardOutlineBounds.minX) * traceTextureResolution;
31740
- const canvasYFromPcb = (pcbY) => (boardOutlineBounds.maxY - pcbY) * traceTextureResolution;
31741
- const rectAndPolygonPours = poursOnLayer.filter(
31742
- (pour) => pour.shape === "rect" || pour.shape === "polygon"
31743
- );
31744
- const brepPours = poursOnLayer.filter((pour) => pour.shape === "brep");
31745
- if (rectAndPolygonPours.length > 0) {
31746
- const drawer = new CircuitToCanvasDrawer(ctx);
31747
- drawer.setCameraBounds({
31748
- minX: boardOutlineBounds.minX,
31749
- maxX: boardOutlineBounds.maxX,
31750
- minY: boardOutlineBounds.minY,
31751
- maxY: boardOutlineBounds.maxY
31752
- });
31753
- const coveredPours = rectAndPolygonPours.filter(
31754
- (p) => p.covered_with_solder_mask !== false
31755
- );
31756
- const uncoveredPours = rectAndPolygonPours.filter(
31757
- (p) => p.covered_with_solder_mask === false
31758
- );
31759
- const coveredColor = `rgb(${colors.fr4TracesWithMaskGreen.map((c) => c * 255).join(",")})`;
31760
- const uncoveredColor = `rgb(${colors.copper.map((c) => c * 255).join(",")})`;
31761
- if (coveredPours.length > 0) {
31762
- drawer.configure({
31763
- colorOverrides: {
31764
- copper: {
31765
- top: coveredColor,
31766
- bottom: coveredColor,
31767
- inner1: coveredColor,
31768
- inner2: coveredColor,
31769
- inner3: coveredColor,
31770
- inner4: coveredColor,
31771
- inner5: coveredColor,
31772
- inner6: coveredColor
31773
- }
31774
- }
31775
- });
31776
- drawer.drawElements(coveredPours, { layers: [pcbRenderLayer] });
31777
- }
31778
- if (uncoveredPours.length > 0) {
31779
- drawer.configure({
31780
- colorOverrides: {
31781
- copper: {
31782
- top: uncoveredColor,
31783
- bottom: uncoveredColor,
31784
- inner1: uncoveredColor,
31785
- inner2: uncoveredColor,
31786
- inner3: uncoveredColor,
31787
- inner4: uncoveredColor,
31788
- inner5: uncoveredColor,
31789
- inner6: uncoveredColor
31790
- }
31791
- }
31792
- });
31793
- drawer.drawElements(uncoveredPours, { layers: [pcbRenderLayer] });
31794
- }
31795
- }
31796
- for (const pour of brepPours) {
31797
- const covered = pour.covered_with_solder_mask !== false;
31798
- const colorArr = covered ? colors.fr4TracesWithMaskGreen : colors.copper;
31799
- const copperColor = `rgb(${colorArr[0] * 255}, ${colorArr[1] * 255}, ${colorArr[2] * 255})`;
31800
- ctx.fillStyle = copperColor;
31801
- drawBrepShape({ ctx, pour, canvasXFromPcb, canvasYFromPcb });
31802
- }
31803
- const texture = new THREE19.CanvasTexture(canvas);
31804
- texture.generateMipmaps = true;
31805
- texture.minFilter = THREE19.LinearMipmapLinearFilter;
31806
- texture.magFilter = THREE19.LinearFilter;
31807
- texture.anisotropy = 16;
31808
- texture.needsUpdate = true;
31809
- return texture;
31810
- }
31811
-
31812
- // src/textures/create-three-texture-meshes.ts
31813
- import * as THREE20 from "three";
31814
- function createTexturePlane(config, boardData) {
31815
- const {
31816
- texture,
31817
- yOffset,
31818
- isBottomLayer,
31819
- textureType,
31820
- usePolygonOffset = false,
31821
- renderOrder = 0,
31822
- isFaux = false
31823
- } = config;
31824
- if (!texture) return null;
31825
- const boardOutlineBounds = calculateOutlineBounds(boardData);
31826
- const planeGeom = new THREE20.PlaneGeometry(
31827
- boardOutlineBounds.width,
31828
- boardOutlineBounds.height
31829
- );
31830
- const material = new THREE20.MeshBasicMaterial({
31831
- map: texture,
31832
- transparent: true,
31833
- side: THREE20.DoubleSide,
31834
- depthWrite: textureType === "panel-outlines",
31835
- polygonOffset: usePolygonOffset,
31836
- polygonOffsetFactor: usePolygonOffset ? -4 : 0,
31837
- // Increased for better z-fighting prevention
31838
- polygonOffsetUnits: usePolygonOffset ? -4 : 0,
31839
- opacity: isFaux ? FAUX_BOARD_OPACITY : 1
31840
- });
31841
- const mesh = new THREE20.Mesh(planeGeom, material);
31842
- mesh.position.set(
31843
- boardOutlineBounds.centerX,
31844
- boardOutlineBounds.centerY,
31845
- yOffset
31846
- );
31847
- if (isBottomLayer) {
31848
- mesh.rotation.set(Math.PI, 0, 0);
31849
- }
31850
- mesh.name = `${isBottomLayer ? "bottom" : "top"}-${textureType}-texture-plane`;
31851
- mesh.renderOrder = renderOrder;
31852
- return mesh;
31853
- }
31854
- function createTextureMeshes(textures, boardData, pcbThickness, isFaux = false) {
31855
- const meshes = [];
31856
- if (!textures || !boardData || pcbThickness === null) return meshes;
31857
- const topTraceMesh = createTexturePlane(
31858
- {
31859
- texture: textures.topTrace,
31860
- yOffset: pcbThickness / 2 + BOARD_SURFACE_OFFSET.traces,
31861
- // Use consistent copper offset
31862
- isBottomLayer: false,
31863
- textureType: "trace",
31864
- usePolygonOffset: false,
31865
- renderOrder: 2,
31866
- // Render after soldermask
31867
- isFaux
31868
- },
31869
- boardData
31870
- );
31871
- if (topTraceMesh) meshes.push(topTraceMesh);
31872
- const topTraceWithMaskMesh = createTexturePlane(
31873
- {
31874
- texture: textures.topTraceWithMask,
31875
- yOffset: pcbThickness / 2 + BOARD_SURFACE_OFFSET.traces,
31876
- isBottomLayer: false,
31877
- textureType: "trace-with-mask",
31878
- usePolygonOffset: false,
31879
- renderOrder: 2,
31880
- // Render after soldermask
31881
- isFaux
31882
- },
31883
- boardData
31884
- );
31885
- if (topTraceWithMaskMesh) meshes.push(topTraceWithMaskMesh);
31886
- const topSilkscreenMesh = createTexturePlane(
31887
- {
31888
- texture: textures.topSilkscreen,
31889
- yOffset: pcbThickness / 2 + 3e-3,
31890
- // Slightly above soldermask
31891
- isBottomLayer: false,
31892
- textureType: "silkscreen",
31893
- usePolygonOffset: false,
31894
- renderOrder: 3,
31895
- // Render after traces
31896
- isFaux
31897
- },
31898
- boardData
31899
- );
31900
- if (topSilkscreenMesh) meshes.push(topSilkscreenMesh);
31901
- const bottomTraceMesh = createTexturePlane(
31902
- {
31903
- texture: textures.bottomTrace,
31904
- yOffset: -pcbThickness / 2 - BOARD_SURFACE_OFFSET.traces,
31905
- // Use consistent copper offset
31906
- isBottomLayer: true,
31907
- textureType: "trace",
31908
- usePolygonOffset: false,
31909
- renderOrder: 2,
31910
- // Render after soldermask
31911
- isFaux
31912
- },
31913
- boardData
31914
- );
31915
- if (bottomTraceMesh) meshes.push(bottomTraceMesh);
31916
- const bottomTraceWithMaskMesh = createTexturePlane(
31917
- {
31918
- texture: textures.bottomTraceWithMask,
31919
- yOffset: -pcbThickness / 2 - BOARD_SURFACE_OFFSET.traces,
31920
- isBottomLayer: true,
31921
- textureType: "trace-with-mask",
31922
- usePolygonOffset: false,
31923
- renderOrder: 2,
31924
- // Render after soldermask
31925
- isFaux
31926
- },
31927
- boardData
31928
- );
31929
- if (bottomTraceWithMaskMesh) meshes.push(bottomTraceWithMaskMesh);
31930
- const bottomSilkscreenMesh = createTexturePlane(
31931
- {
31932
- texture: textures.bottomSilkscreen,
31933
- yOffset: -pcbThickness / 2 - 3e-3,
31934
- isBottomLayer: true,
31935
- textureType: "silkscreen",
31936
- usePolygonOffset: false,
31937
- renderOrder: 3,
31938
- // Render after traces
31939
- isFaux
31940
- },
31941
- boardData
31942
- );
31943
- if (bottomSilkscreenMesh) meshes.push(bottomSilkscreenMesh);
31944
- const topSoldermaskMesh = createTexturePlane(
31945
- {
31946
- texture: textures.topSoldermask,
31947
- yOffset: pcbThickness / 2 + 1e-3,
31948
- // Just above board surface
31949
- isBottomLayer: false,
31950
- textureType: "soldermask",
31951
- usePolygonOffset: true,
31952
- // Enable polygon offset
31953
- renderOrder: 1,
31954
- // Render after board (renderOrder)
31955
- isFaux
31956
- },
31957
- boardData
31958
- );
31959
- if (topSoldermaskMesh) meshes.push(topSoldermaskMesh);
31960
- const bottomSoldermaskMesh = createTexturePlane(
31961
- {
31962
- texture: textures.bottomSoldermask,
31963
- yOffset: -pcbThickness / 2 - 1e-3,
31964
- // Just below board surface (bottom side)
31965
- isBottomLayer: true,
31966
- textureType: "soldermask",
31967
- usePolygonOffset: true,
31968
- // Enable polygon offset
31969
- renderOrder: 1,
31970
- // Render after board (renderOrder)
31971
- isFaux
31972
- },
31973
- boardData
31974
- );
31975
- if (bottomSoldermaskMesh) meshes.push(bottomSoldermaskMesh);
31976
- const topCopperTextMesh = createTexturePlane(
31977
- {
31978
- texture: textures.topCopperText,
31979
- yOffset: pcbThickness / 2 + BOARD_SURFACE_OFFSET.copper,
31980
- isBottomLayer: false,
31981
- textureType: "copper-text",
31982
- usePolygonOffset: true,
31983
- renderOrder: 2,
31984
- // Render after soldermask
31985
- isFaux
31986
- },
31987
- boardData
31988
- );
31989
- if (topCopperTextMesh) meshes.push(topCopperTextMesh);
31990
- const bottomCopperTextMesh = createTexturePlane(
31991
- {
31992
- texture: textures.bottomCopperText,
31993
- yOffset: -pcbThickness / 2 - BOARD_SURFACE_OFFSET.copper,
31994
- isBottomLayer: true,
31995
- textureType: "copper-text",
31996
- usePolygonOffset: true,
31997
- renderOrder: 2,
31998
- // Render after soldermask
31999
- isFaux
32000
- },
32001
- boardData
32002
- );
32003
- if (bottomCopperTextMesh) meshes.push(bottomCopperTextMesh);
32004
- const topCopperMesh = createTexturePlane(
32005
- {
32006
- texture: textures.topCopper,
32007
- yOffset: pcbThickness / 2 + BOARD_SURFACE_OFFSET.copper,
32008
- isBottomLayer: false,
32009
- textureType: "copper",
32010
- usePolygonOffset: true,
32011
- renderOrder: 2,
32012
- // Render after soldermask
32013
- isFaux
32014
- },
32015
- boardData
32016
- );
32017
- if (topCopperMesh) meshes.push(topCopperMesh);
32018
- const bottomCopperMesh = createTexturePlane(
32019
- {
32020
- texture: textures.bottomCopper,
32021
- yOffset: -pcbThickness / 2 - BOARD_SURFACE_OFFSET.copper,
32022
- isBottomLayer: true,
32023
- textureType: "copper",
32024
- usePolygonOffset: true,
32025
- renderOrder: 2,
32026
- // Render after soldermask
32027
- isFaux
32028
- },
32029
- boardData
32030
- );
32031
- if (bottomCopperMesh) meshes.push(bottomCopperMesh);
32032
- const topPanelOutlinesMesh = createTexturePlane(
32033
- {
32034
- texture: textures.topPanelOutlines,
32035
- yOffset: pcbThickness / 2 + 4e-3,
32036
- // Above silkscreen
32037
- isBottomLayer: false,
32038
- textureType: "panel-outlines",
32039
- usePolygonOffset: false,
32040
- renderOrder: 4,
32041
- isFaux
32042
- },
32043
- boardData
32044
- );
32045
- if (topPanelOutlinesMesh) meshes.push(topPanelOutlinesMesh);
32046
- const bottomPanelOutlinesMesh = createTexturePlane(
32047
- {
32048
- texture: textures.bottomPanelOutlines,
32049
- yOffset: -pcbThickness / 2 - 4e-3,
32050
- // Below bottom silkscreen
32051
- isBottomLayer: true,
32052
- textureType: "panel-outlines",
32053
- usePolygonOffset: false,
32054
- renderOrder: 4,
32055
- isFaux
32056
- },
32057
- boardData
32058
- );
32059
- if (bottomPanelOutlinesMesh) meshes.push(bottomPanelOutlinesMesh);
32060
- return meshes;
32061
- }
32062
-
32063
- // src/three-components/JscadBoardTextures.tsx
32064
- import * as THREE26 from "three";
31565
+ // src/textures/create-combined-board-textures.ts
31566
+ import * as THREE25 from "three";
32065
31567
 
32066
31568
  // src/utils/copper-text-texture.ts
32067
31569
  var import_text = __toESM(require_text(), 1);
32068
- import * as THREE21 from "three";
31570
+ import * as THREE19 from "three";
32069
31571
 
32070
31572
  // node_modules/transformation-matrix/src/applyToPoint.js
32071
31573
  function applyToPoint(matrix, point) {
@@ -32277,6 +31779,56 @@ peg$SyntaxError.buildMessage = function(expected, found) {
32277
31779
  return "Expected " + describeExpected(expected) + " but " + describeFound(found) + " found.";
32278
31780
  };
32279
31781
 
31782
+ // node_modules/@tscircuit/math-utils/dist/chunk-5N7UJNVK.js
31783
+ var getBoundsFromPoints = (points) => {
31784
+ if (points.length === 0) {
31785
+ return null;
31786
+ }
31787
+ let minX = points[0].x;
31788
+ let minY = points[0].y;
31789
+ let maxX = points[0].x;
31790
+ let maxY = points[0].y;
31791
+ for (let i = 1; i < points.length; i++) {
31792
+ const point = points[i];
31793
+ if (point.x < minX) minX = point.x;
31794
+ if (point.y < minY) minY = point.y;
31795
+ if (point.x > maxX) maxX = point.x;
31796
+ if (point.y > maxY) maxY = point.y;
31797
+ }
31798
+ return { minX, minY, maxX, maxY };
31799
+ };
31800
+
31801
+ // src/utils/outline-bounds.ts
31802
+ function calculateOutlineBounds(boardData) {
31803
+ if (boardData.outline && boardData.outline.length >= 3) {
31804
+ const bounds2 = getBoundsFromPoints(boardData.outline);
31805
+ return {
31806
+ ...bounds2,
31807
+ width: bounds2.maxX - bounds2.minX,
31808
+ height: bounds2.maxY - bounds2.minY,
31809
+ centerX: (bounds2.minX + bounds2.maxX) / 2,
31810
+ centerY: (bounds2.minY + bounds2.maxY) / 2
31811
+ };
31812
+ }
31813
+ const boardWidth = boardData.width ?? 0;
31814
+ const boardHeight = boardData.height ?? 0;
31815
+ const boardCenterX = boardData.center?.x ?? 0;
31816
+ const boardCenterY = boardData.center?.y ?? 0;
31817
+ const bounds = {
31818
+ minX: boardCenterX - boardWidth / 2,
31819
+ maxX: boardCenterX + boardWidth / 2,
31820
+ minY: boardCenterY - boardHeight / 2,
31821
+ maxY: boardCenterY + boardHeight / 2
31822
+ };
31823
+ return {
31824
+ ...bounds,
31825
+ width: boardWidth,
31826
+ height: boardHeight,
31827
+ centerX: boardCenterX,
31828
+ centerY: boardCenterY
31829
+ };
31830
+ }
31831
+
32280
31832
  // src/utils/copper-text-texture.ts
32281
31833
  function parseDimension2(value, defaultValue) {
32282
31834
  if (value === void 0) return defaultValue;
@@ -32516,17 +32068,17 @@ function createCopperTextTextureForLayer({
32516
32068
  );
32517
32069
  }
32518
32070
  });
32519
- const texture = new THREE21.CanvasTexture(canvas);
32071
+ const texture = new THREE19.CanvasTexture(canvas);
32520
32072
  texture.generateMipmaps = true;
32521
- texture.minFilter = THREE21.LinearMipmapLinearFilter;
32522
- texture.magFilter = THREE21.LinearFilter;
32073
+ texture.minFilter = THREE19.LinearMipmapLinearFilter;
32074
+ texture.magFilter = THREE19.LinearFilter;
32523
32075
  texture.anisotropy = 16;
32524
32076
  texture.needsUpdate = true;
32525
32077
  return texture;
32526
32078
  }
32527
32079
 
32528
32080
  // src/utils/panel-outline-texture.ts
32529
- import * as THREE22 from "three";
32081
+ import * as THREE20 from "three";
32530
32082
  import { su as su5 } from "@tscircuit/circuit-json-util";
32531
32083
  function createPanelOutlineTextureForLayer({
32532
32084
  layer,
@@ -32582,10 +32134,10 @@ function createPanelOutlineTextureForLayer({
32582
32134
  );
32583
32135
  }
32584
32136
  });
32585
- const texture = new THREE22.CanvasTexture(canvas);
32137
+ const texture = new THREE20.CanvasTexture(canvas);
32586
32138
  texture.generateMipmaps = true;
32587
- texture.minFilter = THREE22.LinearMipmapLinearFilter;
32588
- texture.magFilter = THREE22.LinearFilter;
32139
+ texture.minFilter = THREE20.LinearMipmapLinearFilter;
32140
+ texture.magFilter = THREE20.LinearFilter;
32589
32141
  texture.anisotropy = 16;
32590
32142
  texture.needsUpdate = true;
32591
32143
  return texture;
@@ -32593,7 +32145,7 @@ function createPanelOutlineTextureForLayer({
32593
32145
 
32594
32146
  // src/utils/silkscreen-texture.ts
32595
32147
  var import_text2 = __toESM(require_text(), 1);
32596
- import * as THREE23 from "three";
32148
+ import * as THREE21 from "three";
32597
32149
  import { su as su6 } from "@tscircuit/circuit-json-util";
32598
32150
 
32599
32151
  // src/utils/units.ts
@@ -33040,10 +32592,10 @@ function createSilkscreenTextureForLayer({
33040
32592
  ctx.stroke();
33041
32593
  });
33042
32594
  });
33043
- const texture = new THREE23.CanvasTexture(canvas);
32595
+ const texture = new THREE21.CanvasTexture(canvas);
33044
32596
  texture.generateMipmaps = true;
33045
- texture.minFilter = THREE23.LinearMipmapLinearFilter;
33046
- texture.magFilter = THREE23.LinearFilter;
32597
+ texture.minFilter = THREE21.LinearMipmapLinearFilter;
32598
+ texture.magFilter = THREE21.LinearFilter;
33047
32599
  texture.anisotropy = 16;
33048
32600
  texture.needsUpdate = true;
33049
32601
  return texture;
@@ -33051,7 +32603,7 @@ function createSilkscreenTextureForLayer({
33051
32603
 
33052
32604
  // src/utils/soldermask-texture.ts
33053
32605
  import { su as su7 } from "@tscircuit/circuit-json-util";
33054
- import * as THREE24 from "three";
32606
+ import * as THREE22 from "three";
33055
32607
  function createSoldermaskTextureForLayer({
33056
32608
  layer,
33057
32609
  circuitJson,
@@ -33616,17 +33168,17 @@ function createSoldermaskTextureForLayer({
33616
33168
  }
33617
33169
  });
33618
33170
  ctx.globalCompositeOperation = "source-over";
33619
- const texture = new THREE24.CanvasTexture(canvas);
33171
+ const texture = new THREE22.CanvasTexture(canvas);
33620
33172
  texture.generateMipmaps = true;
33621
- texture.minFilter = THREE24.LinearMipmapLinearFilter;
33622
- texture.magFilter = THREE24.LinearFilter;
33173
+ texture.minFilter = THREE22.LinearMipmapLinearFilter;
33174
+ texture.magFilter = THREE22.LinearFilter;
33623
33175
  texture.anisotropy = 16;
33624
33176
  texture.needsUpdate = true;
33625
33177
  return texture;
33626
33178
  }
33627
33179
 
33628
33180
  // src/utils/trace-texture.ts
33629
- import * as THREE25 from "three";
33181
+ import * as THREE23 from "three";
33630
33182
  import { su as su8 } from "@tscircuit/circuit-json-util";
33631
33183
  function isWireRoutePoint(point) {
33632
33184
  return point && point.route_type === "wire" && typeof point.layer === "string" && typeof point.width === "number";
@@ -33739,15 +33291,422 @@ function createTraceTextureForLayer({
33739
33291
  }
33740
33292
  });
33741
33293
  ctx.globalCompositeOperation = "source-over";
33742
- const texture = new THREE25.CanvasTexture(canvas);
33294
+ const texture = new THREE23.CanvasTexture(canvas);
33743
33295
  texture.generateMipmaps = true;
33744
- texture.minFilter = THREE25.LinearMipmapLinearFilter;
33745
- texture.magFilter = THREE25.LinearFilter;
33296
+ texture.minFilter = THREE23.LinearMipmapLinearFilter;
33297
+ texture.magFilter = THREE23.LinearFilter;
33746
33298
  texture.anisotropy = 16;
33747
33299
  texture.needsUpdate = true;
33748
33300
  return texture;
33749
33301
  }
33750
33302
 
33303
+ // src/textures/create-copper-pour-texture-for-layer.ts
33304
+ import * as THREE24 from "three";
33305
+ import { CircuitToCanvasDrawer } from "circuit-to-canvas";
33306
+
33307
+ // src/geoms/brep-converter.ts
33308
+ var import_primitives7 = __toESM(require_primitives(), 1);
33309
+ var import_booleans5 = __toESM(require_booleans(), 1);
33310
+ function segmentToPoints(p1, p2, bulge, arcSegments) {
33311
+ if (!bulge || Math.abs(bulge) < 1e-9) {
33312
+ return [];
33313
+ }
33314
+ const theta = 4 * Math.atan(bulge);
33315
+ const dx = p2[0] - p1[0];
33316
+ const dy = p2[1] - p1[1];
33317
+ const dist = Math.sqrt(dx * dx + dy * dy);
33318
+ if (dist < 1e-9) return [];
33319
+ const radius = Math.abs(dist / (2 * Math.sin(theta / 2)));
33320
+ const m = Math.sqrt(Math.max(0, radius * radius - dist / 2 * (dist / 2)));
33321
+ const midPoint = [(p1[0] + p2[0]) / 2, (p1[1] + p2[1]) / 2];
33322
+ const ux = dx / dist;
33323
+ const uy = dy / dist;
33324
+ const nx = -uy;
33325
+ const ny = ux;
33326
+ const centerX = midPoint[0] + nx * m * Math.sign(bulge);
33327
+ const centerY = midPoint[1] + ny * m * Math.sign(bulge);
33328
+ const startAngle = Math.atan2(p1[1] - centerY, p1[0] - centerX);
33329
+ const points = [];
33330
+ const numSteps = Math.max(
33331
+ 2,
33332
+ Math.ceil(arcSegments * Math.abs(theta) / (Math.PI * 2) * 4)
33333
+ );
33334
+ const angleStep = theta / numSteps;
33335
+ for (let i = 1; i < numSteps; i++) {
33336
+ const angle = startAngle + angleStep * i;
33337
+ points.push([
33338
+ centerX + radius * Math.cos(angle),
33339
+ centerY + radius * Math.sin(angle)
33340
+ ]);
33341
+ }
33342
+ return points;
33343
+ }
33344
+ function ringToPoints(ring, arcSegments) {
33345
+ const allPoints = [];
33346
+ const vertices = ring.vertices;
33347
+ for (let i = 0; i < vertices.length; i++) {
33348
+ const p1 = vertices[i];
33349
+ const p2 = vertices[(i + 1) % vertices.length];
33350
+ allPoints.push([p1.x, p1.y]);
33351
+ if (p1.bulge) {
33352
+ const arcPoints = segmentToPoints(
33353
+ [p1.x, p1.y],
33354
+ [p2.x, p2.y],
33355
+ p1.bulge,
33356
+ arcSegments
33357
+ );
33358
+ allPoints.push(...arcPoints);
33359
+ }
33360
+ }
33361
+ return allPoints;
33362
+ }
33363
+
33364
+ // src/textures/create-copper-pour-texture-for-layer.ts
33365
+ function drawPolygon({
33366
+ ctx,
33367
+ points,
33368
+ canvasXFromPcb,
33369
+ canvasYFromPcb
33370
+ }) {
33371
+ if (points.length < 3) return;
33372
+ ctx.beginPath();
33373
+ points.forEach((point, index2) => {
33374
+ const canvasX = canvasXFromPcb(point[0]);
33375
+ const canvasY = canvasYFromPcb(point[1]);
33376
+ if (index2 === 0) {
33377
+ ctx.moveTo(canvasX, canvasY);
33378
+ } else {
33379
+ ctx.lineTo(canvasX, canvasY);
33380
+ }
33381
+ });
33382
+ ctx.closePath();
33383
+ ctx.fill();
33384
+ }
33385
+ function drawBrepShape({
33386
+ ctx,
33387
+ pour,
33388
+ canvasXFromPcb,
33389
+ canvasYFromPcb
33390
+ }) {
33391
+ const brepShape = pour.brep_shape;
33392
+ if (!brepShape || !brepShape.outer_ring) return;
33393
+ const outerRingPoints = ringToPoints(brepShape.outer_ring, 32);
33394
+ if (outerRingPoints.length >= 3) {
33395
+ drawPolygon({
33396
+ ctx,
33397
+ points: outerRingPoints,
33398
+ canvasXFromPcb,
33399
+ canvasYFromPcb
33400
+ });
33401
+ }
33402
+ if (brepShape.inner_rings && brepShape.inner_rings.length > 0) {
33403
+ ctx.globalCompositeOperation = "destination-out";
33404
+ for (const innerRing of brepShape.inner_rings) {
33405
+ const innerRingPoints = ringToPoints(innerRing, 32);
33406
+ if (innerRingPoints.length >= 3) {
33407
+ drawPolygon({
33408
+ ctx,
33409
+ points: innerRingPoints,
33410
+ canvasXFromPcb,
33411
+ canvasYFromPcb
33412
+ });
33413
+ }
33414
+ }
33415
+ ctx.globalCompositeOperation = "source-over";
33416
+ }
33417
+ }
33418
+ function createCopperPourTextureForLayer({
33419
+ layer,
33420
+ circuitJson,
33421
+ boardData,
33422
+ traceTextureResolution = TRACE_TEXTURE_RESOLUTION
33423
+ }) {
33424
+ const copperPours = circuitJson.filter(
33425
+ (e) => e.type === "pcb_copper_pour"
33426
+ );
33427
+ const pcbRenderLayer = layer === "top" ? "top_copper" : "bottom_copper";
33428
+ const poursOnLayer = copperPours.filter((p) => p.layer === layer);
33429
+ if (poursOnLayer.length === 0) return null;
33430
+ const boardOutlineBounds = calculateOutlineBounds(boardData);
33431
+ const canvas = document.createElement("canvas");
33432
+ const canvasWidth = Math.floor(
33433
+ boardOutlineBounds.width * traceTextureResolution
33434
+ );
33435
+ const canvasHeight = Math.floor(
33436
+ boardOutlineBounds.height * traceTextureResolution
33437
+ );
33438
+ canvas.width = canvasWidth;
33439
+ canvas.height = canvasHeight;
33440
+ const ctx = canvas.getContext("2d");
33441
+ if (!ctx) return null;
33442
+ if (layer === "bottom") {
33443
+ ctx.translate(0, canvasHeight);
33444
+ ctx.scale(1, -1);
33445
+ }
33446
+ const canvasXFromPcb = (pcbX) => (pcbX - boardOutlineBounds.minX) * traceTextureResolution;
33447
+ const canvasYFromPcb = (pcbY) => (boardOutlineBounds.maxY - pcbY) * traceTextureResolution;
33448
+ const rectAndPolygonPours = poursOnLayer.filter(
33449
+ (pour) => pour.shape === "rect" || pour.shape === "polygon"
33450
+ );
33451
+ const brepPours = poursOnLayer.filter((pour) => pour.shape === "brep");
33452
+ if (rectAndPolygonPours.length > 0) {
33453
+ const drawer = new CircuitToCanvasDrawer(ctx);
33454
+ drawer.setCameraBounds({
33455
+ minX: boardOutlineBounds.minX,
33456
+ maxX: boardOutlineBounds.maxX,
33457
+ minY: boardOutlineBounds.minY,
33458
+ maxY: boardOutlineBounds.maxY
33459
+ });
33460
+ const coveredPours = rectAndPolygonPours.filter(
33461
+ (p) => p.covered_with_solder_mask !== false
33462
+ );
33463
+ const uncoveredPours = rectAndPolygonPours.filter(
33464
+ (p) => p.covered_with_solder_mask === false
33465
+ );
33466
+ const coveredColor = `rgb(${colors.fr4TracesWithMaskGreen.map((c) => c * 255).join(",")})`;
33467
+ const uncoveredColor = `rgb(${colors.copper.map((c) => c * 255).join(",")})`;
33468
+ if (coveredPours.length > 0) {
33469
+ drawer.configure({
33470
+ colorOverrides: {
33471
+ copper: {
33472
+ top: coveredColor,
33473
+ bottom: coveredColor,
33474
+ inner1: coveredColor,
33475
+ inner2: coveredColor,
33476
+ inner3: coveredColor,
33477
+ inner4: coveredColor,
33478
+ inner5: coveredColor,
33479
+ inner6: coveredColor
33480
+ }
33481
+ }
33482
+ });
33483
+ drawer.drawElements(coveredPours, { layers: [pcbRenderLayer] });
33484
+ }
33485
+ if (uncoveredPours.length > 0) {
33486
+ drawer.configure({
33487
+ colorOverrides: {
33488
+ copper: {
33489
+ top: uncoveredColor,
33490
+ bottom: uncoveredColor,
33491
+ inner1: uncoveredColor,
33492
+ inner2: uncoveredColor,
33493
+ inner3: uncoveredColor,
33494
+ inner4: uncoveredColor,
33495
+ inner5: uncoveredColor,
33496
+ inner6: uncoveredColor
33497
+ }
33498
+ }
33499
+ });
33500
+ drawer.drawElements(uncoveredPours, { layers: [pcbRenderLayer] });
33501
+ }
33502
+ }
33503
+ for (const pour of brepPours) {
33504
+ const covered = pour.covered_with_solder_mask !== false;
33505
+ const colorArr = covered ? colors.fr4TracesWithMaskGreen : colors.copper;
33506
+ const copperColor = `rgb(${colorArr[0] * 255}, ${colorArr[1] * 255}, ${colorArr[2] * 255})`;
33507
+ ctx.fillStyle = copperColor;
33508
+ drawBrepShape({ ctx, pour, canvasXFromPcb, canvasYFromPcb });
33509
+ }
33510
+ const texture = new THREE24.CanvasTexture(canvas);
33511
+ texture.generateMipmaps = true;
33512
+ texture.minFilter = THREE24.LinearMipmapLinearFilter;
33513
+ texture.magFilter = THREE24.LinearFilter;
33514
+ texture.anisotropy = 16;
33515
+ texture.needsUpdate = true;
33516
+ return texture;
33517
+ }
33518
+
33519
+ // src/textures/create-combined-board-textures.ts
33520
+ var toRgb = (colorArr) => {
33521
+ const [r = 0, g = 0, b = 0] = colorArr;
33522
+ return `rgb(${Math.round(r * 255)}, ${Math.round(g * 255)}, ${Math.round(
33523
+ b * 255
33524
+ )})`;
33525
+ };
33526
+ var createCombinedTexture = ({
33527
+ textures,
33528
+ boardData,
33529
+ traceTextureResolution
33530
+ }) => {
33531
+ const hasImage = textures.some((texture) => texture?.image);
33532
+ if (!hasImage) return null;
33533
+ const boardOutlineBounds = calculateOutlineBounds(boardData);
33534
+ const canvasWidth = Math.floor(
33535
+ boardOutlineBounds.width * traceTextureResolution
33536
+ );
33537
+ const canvasHeight = Math.floor(
33538
+ boardOutlineBounds.height * traceTextureResolution
33539
+ );
33540
+ if (canvasWidth <= 0 || canvasHeight <= 0) return null;
33541
+ const canvas = document.createElement("canvas");
33542
+ canvas.width = canvasWidth;
33543
+ canvas.height = canvasHeight;
33544
+ const ctx = canvas.getContext("2d");
33545
+ if (!ctx) return null;
33546
+ textures.forEach((texture) => {
33547
+ if (!texture?.image) return;
33548
+ const image = texture.image;
33549
+ ctx.drawImage(image, 0, 0, canvasWidth, canvasHeight);
33550
+ });
33551
+ const combinedTexture = new THREE25.CanvasTexture(canvas);
33552
+ combinedTexture.generateMipmaps = true;
33553
+ combinedTexture.minFilter = THREE25.LinearMipmapLinearFilter;
33554
+ combinedTexture.magFilter = THREE25.LinearFilter;
33555
+ combinedTexture.anisotropy = 16;
33556
+ combinedTexture.needsUpdate = true;
33557
+ return combinedTexture;
33558
+ };
33559
+ function createCombinedBoardTextures({
33560
+ circuitJson,
33561
+ boardData,
33562
+ traceTextureResolution,
33563
+ visibility
33564
+ }) {
33565
+ const soldermaskColor = toRgb(
33566
+ soldermaskColors[boardData.material] ?? colors.fr4SolderMaskGreen
33567
+ );
33568
+ const traceColorWithMask = toRgb(colors.fr4TracesWithMaskGreen);
33569
+ const traceColorWithoutMask = toRgb(colors.fr4TracesWithoutMaskTan);
33570
+ const silkscreenColor = "rgb(255,255,255)";
33571
+ const copperColor = toRgb(colors.copper);
33572
+ const showBoardBody = visibility?.boardBody ?? true;
33573
+ const buildForLayer = (layer) => {
33574
+ const showMask = (layer === "top" ? visibility?.topMask : visibility?.bottomMask) ?? true;
33575
+ const showCopper = (layer === "top" ? visibility?.topCopper : visibility?.bottomCopper) ?? true;
33576
+ const showSilkscreen = (layer === "top" ? visibility?.topSilkscreen : visibility?.bottomSilkscreen) ?? true;
33577
+ const soldermaskTexture = showMask ? createSoldermaskTextureForLayer({
33578
+ layer,
33579
+ circuitJson,
33580
+ boardData,
33581
+ soldermaskColor,
33582
+ traceTextureResolution
33583
+ }) : null;
33584
+ const traceTexture = showCopper ? createTraceTextureForLayer({
33585
+ layer,
33586
+ circuitJson,
33587
+ boardData,
33588
+ traceColor: showMask ? traceColorWithMask : traceColorWithoutMask,
33589
+ traceTextureResolution
33590
+ }) : null;
33591
+ const copperTextTexture = showCopper ? createCopperTextTextureForLayer({
33592
+ layer,
33593
+ circuitJson,
33594
+ boardData,
33595
+ copperColor,
33596
+ traceTextureResolution
33597
+ }) : null;
33598
+ const copperPourTexture = showCopper ? createCopperPourTextureForLayer({
33599
+ layer,
33600
+ circuitJson,
33601
+ boardData,
33602
+ traceTextureResolution
33603
+ }) : null;
33604
+ const silkscreenTexture = showSilkscreen ? createSilkscreenTextureForLayer({
33605
+ layer,
33606
+ circuitJson,
33607
+ boardData,
33608
+ silkscreenColor,
33609
+ traceTextureResolution
33610
+ }) : null;
33611
+ const panelOutlineTexture = showBoardBody ? createPanelOutlineTextureForLayer({
33612
+ layer,
33613
+ circuitJson,
33614
+ panelData: boardData,
33615
+ traceTextureResolution
33616
+ }) : null;
33617
+ return createCombinedTexture({
33618
+ textures: [
33619
+ soldermaskTexture,
33620
+ copperPourTexture,
33621
+ traceTexture,
33622
+ copperTextTexture,
33623
+ silkscreenTexture,
33624
+ panelOutlineTexture
33625
+ ],
33626
+ boardData,
33627
+ traceTextureResolution
33628
+ });
33629
+ };
33630
+ return {
33631
+ topBoard: buildForLayer("top"),
33632
+ bottomBoard: buildForLayer("bottom")
33633
+ };
33634
+ }
33635
+
33636
+ // src/textures/create-three-texture-meshes.ts
33637
+ import * as THREE26 from "three";
33638
+ function createTexturePlane(config, boardData) {
33639
+ const {
33640
+ texture,
33641
+ yOffset,
33642
+ isBottomLayer,
33643
+ usePolygonOffset = false,
33644
+ renderOrder = 0,
33645
+ isFaux = false
33646
+ } = config;
33647
+ if (!texture) return null;
33648
+ const boardOutlineBounds = calculateOutlineBounds(boardData);
33649
+ const planeGeom = new THREE26.PlaneGeometry(
33650
+ boardOutlineBounds.width,
33651
+ boardOutlineBounds.height
33652
+ );
33653
+ const material = new THREE26.MeshBasicMaterial({
33654
+ map: texture,
33655
+ transparent: true,
33656
+ side: THREE26.DoubleSide,
33657
+ depthWrite: false,
33658
+ polygonOffset: usePolygonOffset,
33659
+ polygonOffsetFactor: usePolygonOffset ? -4 : 0,
33660
+ // Increased for better z-fighting prevention
33661
+ polygonOffsetUnits: usePolygonOffset ? -4 : 0,
33662
+ opacity: isFaux ? FAUX_BOARD_OPACITY : 1
33663
+ });
33664
+ const mesh = new THREE26.Mesh(planeGeom, material);
33665
+ mesh.position.set(
33666
+ boardOutlineBounds.centerX,
33667
+ boardOutlineBounds.centerY,
33668
+ yOffset
33669
+ );
33670
+ if (isBottomLayer) {
33671
+ mesh.rotation.set(Math.PI, 0, 0);
33672
+ }
33673
+ mesh.name = `${isBottomLayer ? "bottom" : "top"}-board-texture-plane`;
33674
+ mesh.renderOrder = renderOrder;
33675
+ return mesh;
33676
+ }
33677
+ function createTextureMeshes(textures, boardData, pcbThickness, isFaux = false) {
33678
+ const meshes = [];
33679
+ if (!textures || !boardData || pcbThickness === null) return meshes;
33680
+ const topBoardMesh = createTexturePlane(
33681
+ {
33682
+ texture: textures.topBoard,
33683
+ yOffset: pcbThickness / 2 + 1e-3,
33684
+ isBottomLayer: false,
33685
+ usePolygonOffset: true,
33686
+ renderOrder: 1,
33687
+ isFaux
33688
+ },
33689
+ boardData
33690
+ );
33691
+ if (topBoardMesh) meshes.push(topBoardMesh);
33692
+ const bottomBoardMesh = createTexturePlane(
33693
+ {
33694
+ texture: textures.bottomBoard,
33695
+ yOffset: -pcbThickness / 2 - 1e-3,
33696
+ isBottomLayer: true,
33697
+ usePolygonOffset: true,
33698
+ renderOrder: 1,
33699
+ isFaux
33700
+ },
33701
+ boardData
33702
+ );
33703
+ if (bottomBoardMesh) meshes.push(bottomBoardMesh);
33704
+ return meshes;
33705
+ }
33706
+
33707
+ // src/three-components/JscadBoardTextures.tsx
33708
+ import * as THREE27 from "three";
33709
+
33751
33710
  // src/utils/layer-texture-resolution.ts
33752
33711
  var DEFAULT_MAX_TEXTURE_PIXELS = 4e6;
33753
33712
  var DEFAULT_MAX_TEXTURE_DIMENSION = 4096;
@@ -33815,114 +33774,57 @@ function JscadBoardTextures({
33815
33774
  }, [boardData]);
33816
33775
  const textures = useMemo19(() => {
33817
33776
  if (!boardData || !boardData.width || !boardData.height) return null;
33818
- const soldermaskColorArr = soldermaskColors[boardData.material] ?? colors.fr4SolderMaskGreen;
33819
- const soldermaskColor = `rgb(${Math.round(soldermaskColorArr[0] * 255)}, ${Math.round(soldermaskColorArr[1] * 255)}, ${Math.round(soldermaskColorArr[2] * 255)})`;
33820
- const silkscreenColor = "rgb(255,255,255)";
33821
- const traceColorWithMaskArr = colors.fr4TracesWithMaskGreen;
33822
- const traceColorWithMask = `rgb(${Math.round(traceColorWithMaskArr[0] * 255)}, ${Math.round(traceColorWithMaskArr[1] * 255)}, ${Math.round(traceColorWithMaskArr[2] * 255)})`;
33823
- return {
33824
- topSoldermask: createSoldermaskTextureForLayer({
33825
- layer: "top",
33826
- circuitJson,
33827
- boardData,
33828
- soldermaskColor,
33829
- traceTextureResolution
33830
- }),
33831
- bottomSoldermask: createSoldermaskTextureForLayer({
33832
- layer: "bottom",
33833
- circuitJson,
33834
- boardData,
33835
- soldermaskColor,
33836
- traceTextureResolution
33837
- }),
33838
- topSilkscreen: createSilkscreenTextureForLayer({
33839
- layer: "top",
33840
- circuitJson,
33841
- boardData,
33842
- silkscreenColor,
33843
- traceTextureResolution
33844
- }),
33845
- bottomSilkscreen: createSilkscreenTextureForLayer({
33846
- layer: "bottom",
33847
- circuitJson,
33848
- boardData,
33849
- silkscreenColor,
33850
- traceTextureResolution
33851
- }),
33852
- topTraceWithMask: createTraceTextureForLayer({
33853
- layer: "top",
33854
- circuitJson,
33855
- boardData,
33856
- traceColor: traceColorWithMask,
33857
- traceTextureResolution
33858
- }),
33859
- bottomTraceWithMask: createTraceTextureForLayer({
33860
- layer: "bottom",
33861
- circuitJson,
33862
- boardData,
33863
- traceColor: traceColorWithMask,
33864
- traceTextureResolution
33865
- }),
33866
- topCopperText: createCopperTextTextureForLayer({
33867
- layer: "top",
33868
- circuitJson,
33869
- boardData,
33870
- copperColor: `rgb(${Math.round(colors.copper[0] * 255)}, ${Math.round(colors.copper[1] * 255)}, ${Math.round(colors.copper[2] * 255)})`,
33871
- traceTextureResolution
33872
- }),
33873
- bottomCopperText: createCopperTextTextureForLayer({
33874
- layer: "bottom",
33875
- circuitJson,
33876
- boardData,
33877
- copperColor: `rgb(${Math.round(colors.copper[0] * 255)}, ${Math.round(colors.copper[1] * 255)}, ${Math.round(colors.copper[2] * 255)})`,
33878
- traceTextureResolution
33879
- }),
33880
- topPanelOutlines: createPanelOutlineTextureForLayer({
33881
- layer: "top",
33882
- circuitJson,
33883
- panelData: boardData,
33884
- traceTextureResolution
33885
- }),
33886
- bottomPanelOutlines: createPanelOutlineTextureForLayer({
33887
- layer: "bottom",
33888
- circuitJson,
33889
- panelData: boardData,
33890
- traceTextureResolution
33891
- }),
33892
- topCopper: createCopperPourTextureForLayer({
33893
- layer: "top",
33894
- circuitJson,
33895
- boardData,
33896
- traceTextureResolution
33897
- }),
33898
- bottomCopper: createCopperPourTextureForLayer({
33899
- layer: "bottom",
33900
- circuitJson,
33901
- boardData,
33902
- traceTextureResolution
33903
- })
33904
- };
33905
- }, [circuitJson, boardData, traceTextureResolution]);
33777
+ return createCombinedBoardTextures({
33778
+ circuitJson,
33779
+ boardData,
33780
+ traceTextureResolution,
33781
+ visibility
33782
+ });
33783
+ }, [circuitJson, boardData, traceTextureResolution, visibility]);
33906
33784
  useEffect23(() => {
33907
33785
  if (!rootObject || !boardData || !textures) return;
33908
33786
  const meshes = [];
33787
+ const disposeTextureMaterial = (material) => {
33788
+ const textureProps = [
33789
+ "map",
33790
+ "alphaMap",
33791
+ "aoMap",
33792
+ "bumpMap",
33793
+ "displacementMap",
33794
+ "emissiveMap",
33795
+ "lightMap",
33796
+ "metalnessMap",
33797
+ "normalMap",
33798
+ "roughnessMap",
33799
+ "specularMap"
33800
+ ];
33801
+ const typedMaterial = material;
33802
+ for (const prop of textureProps) {
33803
+ const texture = typedMaterial[prop];
33804
+ if (texture && texture instanceof THREE27.Texture) {
33805
+ texture.dispose();
33806
+ typedMaterial[prop] = null;
33807
+ }
33808
+ }
33809
+ material.dispose();
33810
+ };
33909
33811
  const createTexturePlane2 = (texture, zOffset, isBottomLayer, name, usePolygonOffset = false, depthWrite = false) => {
33910
33812
  if (!texture) return null;
33911
33813
  const boardOutlineBounds = calculateOutlineBounds(boardData);
33912
- const planeGeom = new THREE26.PlaneGeometry(
33814
+ const planeGeom = new THREE27.PlaneGeometry(
33913
33815
  boardOutlineBounds.width,
33914
33816
  boardOutlineBounds.height
33915
33817
  );
33916
- const material = new THREE26.MeshBasicMaterial({
33818
+ const material = new THREE27.MeshBasicMaterial({
33917
33819
  map: texture,
33918
33820
  transparent: true,
33919
- side: THREE26.DoubleSide,
33821
+ side: THREE27.DoubleSide,
33920
33822
  depthWrite,
33921
33823
  polygonOffset: usePolygonOffset,
33922
33824
  polygonOffsetUnits: usePolygonOffset ? -0.8 : 0,
33923
33825
  opacity: isFaux ? FAUX_BOARD_OPACITY : 1
33924
33826
  });
33925
- const mesh = new THREE26.Mesh(planeGeom, material);
33827
+ const mesh = new THREE27.Mesh(planeGeom, material);
33926
33828
  mesh.position.set(
33927
33829
  boardOutlineBounds.centerX,
33928
33830
  boardOutlineBounds.centerY,
@@ -33935,159 +33837,27 @@ function JscadBoardTextures({
33935
33837
  return mesh;
33936
33838
  };
33937
33839
  const SURFACE_OFFSET = 1e-3;
33938
- if (visibility.topMask) {
33939
- const topSoldermaskMesh = createTexturePlane2(
33940
- textures.topSoldermask,
33941
- pcbThickness / 2 + SURFACE_OFFSET,
33942
- false,
33943
- "jscad-top-soldermask",
33944
- true
33945
- );
33946
- if (topSoldermaskMesh) {
33947
- meshes.push(topSoldermaskMesh);
33948
- rootObject.add(topSoldermaskMesh);
33949
- }
33950
- }
33951
- if (visibility.bottomMask) {
33952
- const bottomSoldermaskMesh = createTexturePlane2(
33953
- textures.bottomSoldermask,
33954
- -pcbThickness / 2 - SURFACE_OFFSET,
33955
- true,
33956
- "jscad-bottom-soldermask",
33957
- true
33958
- );
33959
- if (bottomSoldermaskMesh) {
33960
- meshes.push(bottomSoldermaskMesh);
33961
- rootObject.add(bottomSoldermaskMesh);
33962
- }
33963
- }
33964
- if (visibility.topCopper && visibility.topMask) {
33965
- const topTraceWithMaskMesh = createTexturePlane2(
33966
- textures.topTraceWithMask,
33967
- pcbThickness / 2 + BOARD_SURFACE_OFFSET.traces + 4e-3,
33968
- false,
33969
- "jscad-top-trace-with-mask"
33970
- );
33971
- if (topTraceWithMaskMesh) {
33972
- meshes.push(topTraceWithMaskMesh);
33973
- rootObject.add(topTraceWithMaskMesh);
33974
- }
33975
- }
33976
- if (visibility.bottomCopper && visibility.bottomMask) {
33977
- const bottomTraceWithMaskMesh = createTexturePlane2(
33978
- textures.bottomTraceWithMask,
33979
- -pcbThickness / 2 - BOARD_SURFACE_OFFSET.traces - 5e-3,
33980
- true,
33981
- "jscad-bottom-trace-with-mask"
33982
- );
33983
- if (bottomTraceWithMaskMesh) {
33984
- meshes.push(bottomTraceWithMaskMesh);
33985
- rootObject.add(bottomTraceWithMaskMesh);
33986
- }
33987
- }
33988
- if (visibility.topSilkscreen) {
33989
- const topSilkscreenMesh = createTexturePlane2(
33990
- textures.topSilkscreen,
33991
- pcbThickness / 2 + SURFACE_OFFSET + 2e-3,
33992
- false,
33993
- "jscad-top-silkscreen"
33994
- );
33995
- if (topSilkscreenMesh) {
33996
- meshes.push(topSilkscreenMesh);
33997
- rootObject.add(topSilkscreenMesh);
33998
- }
33999
- }
34000
- if (visibility.bottomSilkscreen) {
34001
- const bottomSilkscreenMesh = createTexturePlane2(
34002
- textures.bottomSilkscreen,
34003
- -pcbThickness / 2 - SURFACE_OFFSET - 2e-3,
34004
- true,
34005
- "jscad-bottom-silkscreen"
34006
- );
34007
- if (bottomSilkscreenMesh) {
34008
- meshes.push(bottomSilkscreenMesh);
34009
- rootObject.add(bottomSilkscreenMesh);
34010
- }
34011
- }
34012
- if (visibility.topCopper) {
34013
- const topCopperTextMesh = createTexturePlane2(
34014
- textures.topCopperText,
34015
- pcbThickness / 2 + BOARD_SURFACE_OFFSET.copper,
34016
- false,
34017
- "jscad-top-copper-text",
34018
- true
34019
- );
34020
- if (topCopperTextMesh) {
34021
- meshes.push(topCopperTextMesh);
34022
- rootObject.add(topCopperTextMesh);
34023
- }
34024
- }
34025
- if (visibility.bottomCopper) {
34026
- const bottomCopperTextMesh = createTexturePlane2(
34027
- textures.bottomCopperText,
34028
- -pcbThickness / 2 - BOARD_SURFACE_OFFSET.copper,
34029
- true,
34030
- "jscad-bottom-copper-text",
34031
- true
34032
- );
34033
- if (bottomCopperTextMesh) {
34034
- meshes.push(bottomCopperTextMesh);
34035
- rootObject.add(bottomCopperTextMesh);
34036
- }
34037
- }
34038
- if (visibility.topCopper) {
34039
- const topCopperMesh = createTexturePlane2(
34040
- textures.topCopper,
34041
- pcbThickness / 2 + BOARD_SURFACE_OFFSET.copper,
34042
- false,
34043
- "jscad-top-copper-pour",
34044
- true
34045
- );
34046
- if (topCopperMesh) {
34047
- meshes.push(topCopperMesh);
34048
- rootObject.add(topCopperMesh);
34049
- }
34050
- }
34051
- if (visibility.bottomCopper) {
34052
- const bottomCopperMesh = createTexturePlane2(
34053
- textures.bottomCopper,
34054
- -pcbThickness / 2 - BOARD_SURFACE_OFFSET.copper,
34055
- true,
34056
- "jscad-bottom-copper-pour",
34057
- true
34058
- );
34059
- if (bottomCopperMesh) {
34060
- meshes.push(bottomCopperMesh);
34061
- rootObject.add(bottomCopperMesh);
34062
- }
34063
- }
34064
- if (visibility.boardBody) {
34065
- const topPanelOutlinesMesh = createTexturePlane2(
34066
- textures.topPanelOutlines,
34067
- pcbThickness / 2 + SURFACE_OFFSET + 3e-3,
34068
- // Above silkscreen
34069
- false,
34070
- "jscad-top-panel-outlines",
34071
- false,
34072
- true
34073
- );
34074
- if (topPanelOutlinesMesh) {
34075
- meshes.push(topPanelOutlinesMesh);
34076
- rootObject.add(topPanelOutlinesMesh);
34077
- }
34078
- const bottomPanelOutlinesMesh = createTexturePlane2(
34079
- textures.bottomPanelOutlines,
34080
- -pcbThickness / 2 - SURFACE_OFFSET - 3e-3,
34081
- // Below bottom silkscreen
34082
- true,
34083
- "jscad-bottom-panel-outlines",
34084
- false,
34085
- true
34086
- );
34087
- if (bottomPanelOutlinesMesh) {
34088
- meshes.push(bottomPanelOutlinesMesh);
34089
- rootObject.add(bottomPanelOutlinesMesh);
34090
- }
33840
+ const topBoardMesh = createTexturePlane2(
33841
+ textures.topBoard,
33842
+ pcbThickness / 2 + SURFACE_OFFSET,
33843
+ false,
33844
+ "jscad-top-board-texture",
33845
+ true
33846
+ );
33847
+ if (topBoardMesh) {
33848
+ meshes.push(topBoardMesh);
33849
+ rootObject.add(topBoardMesh);
33850
+ }
33851
+ const bottomBoardMesh = createTexturePlane2(
33852
+ textures.bottomBoard,
33853
+ -pcbThickness / 2 - SURFACE_OFFSET,
33854
+ true,
33855
+ "jscad-bottom-board-texture",
33856
+ true
33857
+ );
33858
+ if (bottomBoardMesh) {
33859
+ meshes.push(bottomBoardMesh);
33860
+ rootObject.add(bottomBoardMesh);
34091
33861
  }
34092
33862
  return () => {
34093
33863
  meshes.forEach((mesh) => {
@@ -34095,12 +33865,16 @@ function JscadBoardTextures({
34095
33865
  rootObject.remove(mesh);
34096
33866
  }
34097
33867
  mesh.geometry.dispose();
34098
- if (mesh.material instanceof THREE26.Material) {
34099
- mesh.material.dispose();
33868
+ if (Array.isArray(mesh.material)) {
33869
+ mesh.material.forEach((material) => disposeTextureMaterial(material));
33870
+ } else if (mesh.material instanceof THREE27.Material) {
33871
+ disposeTextureMaterial(mesh.material);
34100
33872
  }
34101
33873
  });
33874
+ textures.topBoard?.dispose();
33875
+ textures.bottomBoard?.dispose();
34102
33876
  };
34103
- }, [rootObject, boardData, textures, pcbThickness, visibility]);
33877
+ }, [rootObject, boardData, textures, pcbThickness]);
34104
33878
  return null;
34105
33879
  }
34106
33880
 
@@ -34326,11 +34100,12 @@ var CadViewerJscad = forwardRef3(
34326
34100
  // src/CadViewerManifold.tsx
34327
34101
  import { su as su19 } from "@tscircuit/circuit-json-util";
34328
34102
  import { useEffect as useEffect25, useMemo as useMemo22, useState as useState16 } from "react";
34103
+ import * as THREE35 from "three";
34329
34104
 
34330
34105
  // src/hooks/useManifoldBoardBuilder.ts
34331
34106
  import { useState as useState15, useEffect as useEffect24, useMemo as useMemo21, useRef as useRef9 } from "react";
34332
34107
  import { su as su18 } from "@tscircuit/circuit-json-util";
34333
- import * as THREE31 from "three";
34108
+ import * as THREE32 from "three";
34334
34109
 
34335
34110
  // src/utils/manifold/create-manifold-board.ts
34336
34111
  var arePointsClockwise2 = (points) => {
@@ -34705,17 +34480,17 @@ function processNonPlatedHolesForManifold(Manifold, CrossSection, circuitJson, p
34705
34480
 
34706
34481
  // src/utils/manifold/process-plated-holes.ts
34707
34482
  import { su as su15 } from "@tscircuit/circuit-json-util";
34708
- import * as THREE28 from "three";
34483
+ import * as THREE29 from "three";
34709
34484
 
34710
34485
  // src/utils/manifold-mesh-to-three-geometry.ts
34711
- import * as THREE27 from "three";
34486
+ import * as THREE28 from "three";
34712
34487
  function manifoldMeshToThreeGeometry(manifoldMesh) {
34713
- const geometry = new THREE27.BufferGeometry();
34488
+ const geometry = new THREE28.BufferGeometry();
34714
34489
  geometry.setAttribute(
34715
34490
  "position",
34716
- new THREE27.Float32BufferAttribute(manifoldMesh.vertProperties, 3)
34491
+ new THREE28.Float32BufferAttribute(manifoldMesh.vertProperties, 3)
34717
34492
  );
34718
- geometry.setIndex(new THREE27.Uint32BufferAttribute(manifoldMesh.triVerts, 1));
34493
+ geometry.setIndex(new THREE28.Uint32BufferAttribute(manifoldMesh.triVerts, 1));
34719
34494
  if (manifoldMesh.runIndex && manifoldMesh.runIndex.length > 1 && manifoldMesh.runOriginalID) {
34720
34495
  for (let i = 0; i < manifoldMesh.runIndex.length - 1; i++) {
34721
34496
  const start = manifoldMesh.runIndex[i];
@@ -34749,7 +34524,7 @@ var createEllipsePoints = (width10, height10, segments) => {
34749
34524
  }
34750
34525
  return points;
34751
34526
  };
34752
- var COPPER_COLOR = new THREE28.Color(...colors.copper);
34527
+ var COPPER_COLOR = new THREE29.Color(...colors.copper);
34753
34528
  var PLATED_HOLE_LIP_HEIGHT = 0.05;
34754
34529
  function processPlatedHolesForManifold(Manifold, CrossSection, circuitJson, pcbThickness, manifoldInstancesForCleanup, boardClipVolume) {
34755
34530
  const platedHoleBoardDrills = [];
@@ -35455,8 +35230,8 @@ function processPlatedHolesForManifold(Manifold, CrossSection, circuitJson, pcbT
35455
35230
 
35456
35231
  // src/utils/manifold/process-smt-pads.ts
35457
35232
  import { su as su16 } from "@tscircuit/circuit-json-util";
35458
- import * as THREE29 from "three";
35459
- var COPPER_COLOR2 = new THREE29.Color(...colors.copper);
35233
+ import * as THREE30 from "three";
35234
+ var COPPER_COLOR2 = new THREE30.Color(...colors.copper);
35460
35235
  function processSmtPadsForManifold(Manifold, circuitJson, pcbThickness, manifoldInstancesForCleanup, holeUnion, boardClipVolume) {
35461
35236
  const smtPadGeoms = [];
35462
35237
  const smtPads = su16(circuitJson).pcb_smtpad.list();
@@ -35495,7 +35270,7 @@ function processSmtPadsForManifold(Manifold, circuitJson, pcbThickness, manifold
35495
35270
 
35496
35271
  // src/utils/manifold/process-vias.ts
35497
35272
  import { su as su17 } from "@tscircuit/circuit-json-util";
35498
- import * as THREE30 from "three";
35273
+ import * as THREE31 from "three";
35499
35274
 
35500
35275
  // src/utils/via-geoms.ts
35501
35276
  function createViaCopper2({
@@ -35548,7 +35323,7 @@ function createViaCopper2({
35548
35323
  }
35549
35324
 
35550
35325
  // src/utils/manifold/process-vias.ts
35551
- var COPPER_COLOR3 = new THREE30.Color(...colors.copper);
35326
+ var COPPER_COLOR3 = new THREE31.Color(...colors.copper);
35552
35327
  function processViasForManifold(Manifold, circuitJson, pcbThickness, manifoldInstancesForCleanup, boardClipVolume) {
35553
35328
  const viaBoardDrills = [];
35554
35329
  const pcbVias = su17(circuitJson).pcb_via.list();
@@ -35600,9 +35375,8 @@ function processViasForManifold(Manifold, circuitJson, pcbThickness, manifoldIns
35600
35375
  }
35601
35376
 
35602
35377
  // src/hooks/useManifoldBoardBuilder.ts
35603
- var useManifoldBoardBuilder = (manifoldJSModule, circuitJson) => {
35378
+ var useManifoldBoardBuilder = (manifoldJSModule, circuitJson, visibility) => {
35604
35379
  const [geoms, setGeoms] = useState15(null);
35605
- const [textures, setTextures] = useState15(null);
35606
35380
  const [pcbThickness, setPcbThickness] = useState15(null);
35607
35381
  const [error, setError] = useState15(null);
35608
35382
  const [isLoading, setIsLoading] = useState15(true);
@@ -35642,14 +35416,12 @@ var useManifoldBoardBuilder = (manifoldJSModule, circuitJson) => {
35642
35416
  useEffect24(() => {
35643
35417
  if (!manifoldJSModule || !boardData) {
35644
35418
  setGeoms(null);
35645
- setTextures(null);
35646
35419
  setPcbThickness(null);
35647
35420
  setIsLoading(false);
35648
35421
  return;
35649
35422
  }
35650
35423
  if ((boardData.width === 0 || !boardData.width) && (boardData.height === 0 || !boardData.height) && (!boardData.outline || boardData.outline.length < 3)) {
35651
35424
  setGeoms({ platedHoles: [], smtPads: [], vias: [] });
35652
- setTextures({});
35653
35425
  setPcbThickness(boardData.thickness ?? 0);
35654
35426
  setIsLoading(false);
35655
35427
  return;
@@ -35672,7 +35444,6 @@ var useManifoldBoardBuilder = (manifoldJSModule, circuitJson) => {
35672
35444
  manifoldInstancesForCleanup.current = [];
35673
35445
  let boardManifold = null;
35674
35446
  const currentGeoms = {};
35675
- const layerTextureMap = {};
35676
35447
  try {
35677
35448
  const currentPcbThickness = boardData.thickness || 1.4;
35678
35449
  setPcbThickness(currentPcbThickness);
@@ -35772,7 +35543,7 @@ var useManifoldBoardBuilder = (manifoldJSModule, circuitJson) => {
35772
35543
  {
35773
35544
  key: "plated-holes-union",
35774
35545
  geometry: cutPlatedGeom,
35775
- color: new THREE31.Color(
35546
+ color: new THREE32.Color(
35776
35547
  colors.copper[0],
35777
35548
  colors.copper[1],
35778
35549
  colors.copper[2]
@@ -35802,7 +35573,7 @@ var useManifoldBoardBuilder = (manifoldJSModule, circuitJson) => {
35802
35573
  const matColorArray = boardMaterialColors[boardData.material] ?? colors.fr4Tan;
35803
35574
  currentGeoms.board = {
35804
35575
  geometry: finalBoardGeom,
35805
- color: new THREE31.Color(
35576
+ color: new THREE32.Color(
35806
35577
  matColorArray[0],
35807
35578
  matColorArray[1],
35808
35579
  matColorArray[2]
@@ -35821,117 +35592,12 @@ var useManifoldBoardBuilder = (manifoldJSModule, circuitJson) => {
35821
35592
  );
35822
35593
  currentGeoms.smtPads = smtPadGeoms;
35823
35594
  setGeoms(currentGeoms);
35824
- const traceColorWithoutMaskArr = colors.fr4TracesWithoutMaskTan;
35825
- const traceColorWithoutMask = `rgb(${Math.round(traceColorWithoutMaskArr[0] * 255)}, ${Math.round(traceColorWithoutMaskArr[1] * 255)}, ${Math.round(traceColorWithoutMaskArr[2] * 255)})`;
35826
- layerTextureMap.topTrace = createTraceTextureForLayer({
35827
- layer: "top",
35828
- circuitJson,
35829
- boardData,
35830
- traceColor: traceColorWithoutMask,
35831
- traceTextureResolution
35832
- });
35833
- layerTextureMap.bottomTrace = createTraceTextureForLayer({
35834
- layer: "bottom",
35835
- circuitJson,
35836
- boardData,
35837
- traceColor: traceColorWithoutMask,
35838
- traceTextureResolution
35839
- });
35840
- const traceColorWithMaskArr = colors.fr4TracesWithMaskGreen;
35841
- const traceColorWithMask = `rgb(${Math.round(traceColorWithMaskArr[0] * 255)}, ${Math.round(traceColorWithMaskArr[1] * 255)}, ${Math.round(traceColorWithMaskArr[2] * 255)})`;
35842
- layerTextureMap.topTraceWithMask = createTraceTextureForLayer({
35843
- layer: "top",
35844
- circuitJson,
35845
- boardData,
35846
- traceColor: traceColorWithMask,
35847
- traceTextureResolution
35848
- });
35849
- layerTextureMap.bottomTraceWithMask = createTraceTextureForLayer({
35850
- layer: "bottom",
35851
- circuitJson,
35852
- boardData,
35853
- traceColor: traceColorWithMask,
35854
- traceTextureResolution
35855
- });
35856
- const silkscreenColor = "rgb(255,255,255)";
35857
- layerTextureMap.topSilkscreen = createSilkscreenTextureForLayer({
35858
- layer: "top",
35859
- circuitJson,
35860
- boardData,
35861
- silkscreenColor,
35862
- traceTextureResolution
35863
- });
35864
- layerTextureMap.bottomSilkscreen = createSilkscreenTextureForLayer({
35865
- layer: "bottom",
35866
- circuitJson,
35867
- boardData,
35868
- silkscreenColor,
35869
- traceTextureResolution
35870
- });
35871
- const soldermaskColorArr = soldermaskColors[boardData.material] ?? colors.fr4SolderMaskGreen;
35872
- const soldermaskColor = `rgb(${Math.round(soldermaskColorArr[0] * 255)}, ${Math.round(soldermaskColorArr[1] * 255)}, ${Math.round(soldermaskColorArr[2] * 255)})`;
35873
- layerTextureMap.topSoldermask = createSoldermaskTextureForLayer({
35874
- layer: "top",
35875
- circuitJson,
35876
- boardData,
35877
- soldermaskColor,
35878
- traceTextureResolution
35879
- });
35880
- layerTextureMap.bottomSoldermask = createSoldermaskTextureForLayer({
35881
- layer: "bottom",
35882
- circuitJson,
35883
- boardData,
35884
- soldermaskColor,
35885
- traceTextureResolution
35886
- });
35887
- const copperColorArr = colors.copper;
35888
- const copperColor = `rgb(${Math.round(copperColorArr[0] * 255)}, ${Math.round(copperColorArr[1] * 255)}, ${Math.round(copperColorArr[2] * 255)})`;
35889
- layerTextureMap.topCopperText = createCopperTextTextureForLayer({
35890
- layer: "top",
35891
- circuitJson,
35892
- boardData,
35893
- copperColor,
35894
- traceTextureResolution
35895
- });
35896
- layerTextureMap.bottomCopperText = createCopperTextTextureForLayer({
35897
- layer: "bottom",
35898
- circuitJson,
35899
- boardData,
35900
- copperColor,
35901
- traceTextureResolution
35902
- });
35903
- layerTextureMap.topPanelOutlines = createPanelOutlineTextureForLayer({
35904
- layer: "top",
35905
- circuitJson,
35906
- panelData: boardData,
35907
- traceTextureResolution
35908
- });
35909
- layerTextureMap.bottomPanelOutlines = createPanelOutlineTextureForLayer({
35910
- layer: "bottom",
35911
- circuitJson,
35912
- panelData: boardData,
35913
- traceTextureResolution
35914
- });
35915
- layerTextureMap.topCopper = createCopperPourTextureForLayer({
35916
- layer: "top",
35917
- circuitJson,
35918
- boardData,
35919
- traceTextureResolution
35920
- });
35921
- layerTextureMap.bottomCopper = createCopperPourTextureForLayer({
35922
- layer: "bottom",
35923
- circuitJson,
35924
- boardData,
35925
- traceTextureResolution
35926
- });
35927
- setTextures(layerTextureMap);
35928
35595
  } catch (e) {
35929
35596
  console.error("Error processing geometry with Manifold in hook:", e);
35930
35597
  setError(
35931
35598
  e.message || "An unknown error occurred while processing geometry in hook."
35932
35599
  );
35933
35600
  setGeoms(null);
35934
- setTextures(null);
35935
35601
  } finally {
35936
35602
  setIsLoading(false);
35937
35603
  }
@@ -35939,7 +35605,16 @@ var useManifoldBoardBuilder = (manifoldJSModule, circuitJson) => {
35939
35605
  manifoldInstancesForCleanup.current.forEach(safeDelete);
35940
35606
  manifoldInstancesForCleanup.current = [];
35941
35607
  };
35942
- }, [manifoldJSModule, circuitJson, boardData, traceTextureResolution]);
35608
+ }, [manifoldJSModule, circuitJson, boardData]);
35609
+ const textures = useMemo21(() => {
35610
+ if (!boardData || !traceTextureResolution) return null;
35611
+ return createCombinedBoardTextures({
35612
+ circuitJson,
35613
+ boardData,
35614
+ traceTextureResolution,
35615
+ visibility
35616
+ });
35617
+ }, [circuitJson, boardData, traceTextureResolution, visibility]);
35943
35618
  return {
35944
35619
  geoms,
35945
35620
  textures,
@@ -35952,11 +35627,11 @@ var useManifoldBoardBuilder = (manifoldJSModule, circuitJson) => {
35952
35627
  };
35953
35628
 
35954
35629
  // src/utils/manifold/create-three-geometry-meshes.ts
35955
- import * as THREE33 from "three";
35630
+ import * as THREE34 from "three";
35956
35631
 
35957
35632
  // src/utils/create-board-material.ts
35958
- import * as THREE32 from "three";
35959
- var DEFAULT_SIDE = THREE32.DoubleSide;
35633
+ import * as THREE33 from "three";
35634
+ var DEFAULT_SIDE = THREE33.DoubleSide;
35960
35635
  var createBoardMaterial = ({
35961
35636
  material,
35962
35637
  color,
@@ -35964,7 +35639,7 @@ var createBoardMaterial = ({
35964
35639
  isFaux = false
35965
35640
  }) => {
35966
35641
  if (material === "fr4") {
35967
- return new THREE32.MeshPhysicalMaterial({
35642
+ return new THREE33.MeshPhysicalMaterial({
35968
35643
  color,
35969
35644
  side,
35970
35645
  metalness: 0,
@@ -35978,7 +35653,7 @@ var createBoardMaterial = ({
35978
35653
  flatShading: true
35979
35654
  });
35980
35655
  }
35981
- return new THREE32.MeshStandardMaterial({
35656
+ return new THREE33.MeshStandardMaterial({
35982
35657
  color,
35983
35658
  side,
35984
35659
  flatShading: true,
@@ -35994,12 +35669,12 @@ function createGeometryMeshes(geoms) {
35994
35669
  const meshes = [];
35995
35670
  if (!geoms) return meshes;
35996
35671
  if (geoms.board && geoms.board.geometry) {
35997
- const mesh = new THREE33.Mesh(
35672
+ const mesh = new THREE34.Mesh(
35998
35673
  geoms.board.geometry,
35999
35674
  createBoardMaterial({
36000
35675
  material: geoms.board.material,
36001
35676
  color: geoms.board.color,
36002
- side: THREE33.DoubleSide,
35677
+ side: THREE34.DoubleSide,
36003
35678
  isFaux: geoms.board.isFaux
36004
35679
  })
36005
35680
  );
@@ -36009,11 +35684,11 @@ function createGeometryMeshes(geoms) {
36009
35684
  const createMeshesFromArray = (geomArray) => {
36010
35685
  if (geomArray) {
36011
35686
  geomArray.forEach((comp) => {
36012
- const mesh = new THREE33.Mesh(
35687
+ const mesh = new THREE34.Mesh(
36013
35688
  comp.geometry,
36014
- new THREE33.MeshStandardMaterial({
35689
+ new THREE34.MeshStandardMaterial({
36015
35690
  color: comp.color,
36016
- side: THREE33.DoubleSide,
35691
+ side: THREE34.DoubleSide,
36017
35692
  flatShading: true,
36018
35693
  // Consistent with board
36019
35694
  polygonOffset: true,
@@ -36040,6 +35715,35 @@ var BoardMeshes = ({
36040
35715
  }) => {
36041
35716
  const { rootObject } = useThree();
36042
35717
  const { visibility } = useLayerVisibility();
35718
+ const disposeTextureMesh = (mesh) => {
35719
+ mesh.geometry.dispose();
35720
+ const materials = Array.isArray(mesh.material) ? mesh.material : [mesh.material];
35721
+ for (const material of materials) {
35722
+ if (!material) continue;
35723
+ const textureProps = [
35724
+ "map",
35725
+ "alphaMap",
35726
+ "aoMap",
35727
+ "bumpMap",
35728
+ "displacementMap",
35729
+ "emissiveMap",
35730
+ "lightMap",
35731
+ "metalnessMap",
35732
+ "normalMap",
35733
+ "roughnessMap",
35734
+ "specularMap"
35735
+ ];
35736
+ const typedMaterial = material;
35737
+ for (const prop of textureProps) {
35738
+ const texture = typedMaterial[prop];
35739
+ if (texture && texture instanceof THREE35.Texture) {
35740
+ texture.dispose();
35741
+ typedMaterial[prop] = null;
35742
+ }
35743
+ }
35744
+ material.dispose();
35745
+ }
35746
+ };
36043
35747
  useEffect25(() => {
36044
35748
  if (!rootObject) return;
36045
35749
  geometryMeshes.forEach((mesh) => {
@@ -36062,37 +35766,7 @@ var BoardMeshes = ({
36062
35766
  }
36063
35767
  });
36064
35768
  textureMeshes.forEach((mesh) => {
36065
- let shouldShow = true;
36066
- if (mesh.name.includes("top-trace-texture-plane") && !mesh.name.includes("with-mask")) {
36067
- shouldShow = visibility.topCopper && !visibility.topMask;
36068
- } else if (mesh.name.includes("top-trace-with-mask")) {
36069
- shouldShow = visibility.topCopper && visibility.topMask;
36070
- } else if (mesh.name.includes("bottom-trace-texture-plane") && !mesh.name.includes("with-mask")) {
36071
- shouldShow = visibility.bottomCopper && !visibility.bottomMask;
36072
- } else if (mesh.name.includes("bottom-trace-with-mask")) {
36073
- shouldShow = visibility.bottomCopper && visibility.bottomMask;
36074
- } else if (mesh.name.includes("top-silkscreen")) {
36075
- shouldShow = visibility.topSilkscreen;
36076
- } else if (mesh.name.includes("bottom-silkscreen")) {
36077
- shouldShow = visibility.bottomSilkscreen;
36078
- } else if (mesh.name.includes("top-soldermask")) {
36079
- shouldShow = visibility.topMask;
36080
- } else if (mesh.name.includes("bottom-soldermask")) {
36081
- shouldShow = visibility.bottomMask;
36082
- } else if (mesh.name.includes("top-copper-text")) {
36083
- shouldShow = visibility.topCopper;
36084
- } else if (mesh.name.includes("bottom-copper-text")) {
36085
- shouldShow = visibility.bottomCopper;
36086
- } else if (mesh.name.includes("top-copper")) {
36087
- shouldShow = visibility.topCopper;
36088
- } else if (mesh.name.includes("bottom-copper")) {
36089
- shouldShow = visibility.bottomCopper;
36090
- } else if (mesh.name.includes("panel-outlines")) {
36091
- shouldShow = visibility.boardBody;
36092
- }
36093
- if (shouldShow) {
36094
- rootObject.add(mesh);
36095
- }
35769
+ rootObject.add(mesh);
36096
35770
  });
36097
35771
  return () => {
36098
35772
  geometryMeshes.forEach((mesh) => {
@@ -36104,6 +35778,7 @@ var BoardMeshes = ({
36104
35778
  if (mesh.parent === rootObject) {
36105
35779
  rootObject.remove(mesh);
36106
35780
  }
35781
+ disposeTextureMesh(mesh);
36107
35782
  });
36108
35783
  };
36109
35784
  }, [rootObject, geometryMeshes, textureMeshes, visibility]);
@@ -36126,6 +35801,7 @@ var CadViewerManifold = ({
36126
35801
  }, [circuitJsonProp, childrenCircuitJson]);
36127
35802
  const [manifoldJSModule, setManifoldJSModule] = useState16(null);
36128
35803
  const [manifoldLoadingError, setManifoldLoadingError] = useState16(null);
35804
+ const { visibility } = useLayerVisibility();
36129
35805
  useEffect25(() => {
36130
35806
  if (window.ManifoldModule && typeof window.ManifoldModule === "object" && window.ManifoldModule.setup) {
36131
35807
  setManifoldJSModule(window.ManifoldModule);
@@ -36196,7 +35872,7 @@ try {
36196
35872
  isLoading: builderIsLoading,
36197
35873
  boardData,
36198
35874
  isFauxBoard
36199
- } = useManifoldBoardBuilder(manifoldJSModule, circuitJson);
35875
+ } = useManifoldBoardBuilder(manifoldJSModule, circuitJson, visibility);
36200
35876
  const geometryMeshes = useMemo22(() => createGeometryMeshes(geoms), [geoms]);
36201
35877
  const textureMeshes = useMemo22(
36202
35878
  () => createTextureMeshes(textures, boardData, pcbThickness, isFauxBoard),
@@ -42951,7 +42627,7 @@ var KeyboardShortcutsDialog = ({
42951
42627
 
42952
42628
  // src/CadViewer.tsx
42953
42629
  import { jsx as jsx38, jsxs as jsxs11 } from "react/jsx-runtime";
42954
- var DEFAULT_TARGET = new THREE34.Vector3(0, 0, 0);
42630
+ var DEFAULT_TARGET = new THREE36.Vector3(0, 0, 0);
42955
42631
  var INITIAL_CAMERA_POSITION = [5, -5, 5];
42956
42632
  var CadViewerInner = (props) => {
42957
42633
  const [engine, setEngine] = useState36("manifold");
@@ -43218,11 +42894,11 @@ var CadViewer = (props) => {
43218
42894
  // src/convert-circuit-json-to-3d-svg.ts
43219
42895
  var import_debug = __toESM(require_browser(), 1);
43220
42896
  import { su as su20 } from "@tscircuit/circuit-json-util";
43221
- import * as THREE38 from "three";
42897
+ import * as THREE40 from "three";
43222
42898
  import { SVGRenderer } from "three/examples/jsm/renderers/SVGRenderer.js";
43223
42899
 
43224
42900
  // src/utils/create-geometry-from-polygons.ts
43225
- import * as THREE35 from "three";
42901
+ import * as THREE37 from "three";
43226
42902
  import { BufferGeometry as BufferGeometry4, Float32BufferAttribute as Float32BufferAttribute3 } from "three";
43227
42903
  function createGeometryFromPolygons(polygons) {
43228
42904
  const geometry = new BufferGeometry4();
@@ -43236,12 +42912,12 @@ function createGeometryFromPolygons(polygons) {
43236
42912
  ...polygon3.vertices[i + 1]
43237
42913
  // Third vertex
43238
42914
  );
43239
- const v1 = new THREE35.Vector3(...polygon3.vertices[0]);
43240
- const v2 = new THREE35.Vector3(...polygon3.vertices[i]);
43241
- const v3 = new THREE35.Vector3(...polygon3.vertices[i + 1]);
43242
- const normal = new THREE35.Vector3().crossVectors(
43243
- new THREE35.Vector3().subVectors(v2, v1),
43244
- new THREE35.Vector3().subVectors(v3, v1)
42915
+ const v1 = new THREE37.Vector3(...polygon3.vertices[0]);
42916
+ const v2 = new THREE37.Vector3(...polygon3.vertices[i]);
42917
+ const v3 = new THREE37.Vector3(...polygon3.vertices[i + 1]);
42918
+ const normal = new THREE37.Vector3().crossVectors(
42919
+ new THREE37.Vector3().subVectors(v2, v1),
42920
+ new THREE37.Vector3().subVectors(v3, v1)
43245
42921
  ).normalize();
43246
42922
  normals.push(
43247
42923
  normal.x,
@@ -43265,10 +42941,10 @@ function createGeometryFromPolygons(polygons) {
43265
42941
  var import_modeling2 = __toESM(require_src(), 1);
43266
42942
  var import_jscad_planner2 = __toESM(require_dist(), 1);
43267
42943
  var jscadModeling2 = __toESM(require_src(), 1);
43268
- import * as THREE37 from "three";
42944
+ import * as THREE39 from "three";
43269
42945
 
43270
42946
  // src/utils/load-model.ts
43271
- import * as THREE36 from "three";
42947
+ import * as THREE38 from "three";
43272
42948
  import { GLTFLoader as GLTFLoader2 } from "three/examples/jsm/loaders/GLTFLoader.js";
43273
42949
  import { OBJLoader as OBJLoader2 } from "three/examples/jsm/loaders/OBJLoader.js";
43274
42950
  import { STLLoader as STLLoader2 } from "three/examples/jsm/loaders/STLLoader.js";
@@ -43276,12 +42952,12 @@ async function load3DModel(url) {
43276
42952
  if (url.endsWith(".stl")) {
43277
42953
  const loader = new STLLoader2();
43278
42954
  const geometry = await loader.loadAsync(url);
43279
- const material = new THREE36.MeshStandardMaterial({
42955
+ const material = new THREE38.MeshStandardMaterial({
43280
42956
  color: 8947848,
43281
42957
  metalness: 0.5,
43282
42958
  roughness: 0.5
43283
42959
  });
43284
- return new THREE36.Mesh(geometry, material);
42960
+ return new THREE38.Mesh(geometry, material);
43285
42961
  }
43286
42962
  if (url.endsWith(".obj")) {
43287
42963
  const loader = new OBJLoader2();
@@ -43314,9 +42990,9 @@ async function renderComponent(component, scene) {
43314
42990
  }
43315
42991
  if (component.rotation) {
43316
42992
  model.rotation.set(
43317
- THREE37.MathUtils.degToRad(component.rotation.x ?? 0),
43318
- THREE37.MathUtils.degToRad(component.rotation.y ?? 0),
43319
- THREE37.MathUtils.degToRad(component.rotation.z ?? 0)
42993
+ THREE39.MathUtils.degToRad(component.rotation.x ?? 0),
42994
+ THREE39.MathUtils.degToRad(component.rotation.y ?? 0),
42995
+ THREE39.MathUtils.degToRad(component.rotation.z ?? 0)
43320
42996
  );
43321
42997
  }
43322
42998
  scene.add(model);
@@ -43330,13 +43006,13 @@ async function renderComponent(component, scene) {
43330
43006
  );
43331
43007
  if (jscadObject && (jscadObject.polygons || jscadObject.sides)) {
43332
43008
  const threeGeom = convertCSGToThreeGeom(jscadObject);
43333
- const material2 = new THREE37.MeshStandardMaterial({
43009
+ const material2 = new THREE39.MeshStandardMaterial({
43334
43010
  color: 8947848,
43335
43011
  metalness: 0.5,
43336
43012
  roughness: 0.5,
43337
- side: THREE37.DoubleSide
43013
+ side: THREE39.DoubleSide
43338
43014
  });
43339
- const mesh2 = new THREE37.Mesh(threeGeom, material2);
43015
+ const mesh2 = new THREE39.Mesh(threeGeom, material2);
43340
43016
  if (component.position) {
43341
43017
  mesh2.position.set(
43342
43018
  component.position.x ?? 0,
@@ -43346,9 +43022,9 @@ async function renderComponent(component, scene) {
43346
43022
  }
43347
43023
  if (component.rotation) {
43348
43024
  mesh2.rotation.set(
43349
- THREE37.MathUtils.degToRad(component.rotation.x ?? 0),
43350
- THREE37.MathUtils.degToRad(component.rotation.y ?? 0),
43351
- THREE37.MathUtils.degToRad(component.rotation.z ?? 0)
43025
+ THREE39.MathUtils.degToRad(component.rotation.x ?? 0),
43026
+ THREE39.MathUtils.degToRad(component.rotation.y ?? 0),
43027
+ THREE39.MathUtils.degToRad(component.rotation.z ?? 0)
43352
43028
  );
43353
43029
  }
43354
43030
  scene.add(mesh2);
@@ -43365,17 +43041,17 @@ async function renderComponent(component, scene) {
43365
43041
  if (!geom || !geom.polygons && !geom.sides) {
43366
43042
  continue;
43367
43043
  }
43368
- const color = new THREE37.Color(geomInfo.color);
43044
+ const color = new THREE39.Color(geomInfo.color);
43369
43045
  color.convertLinearToSRGB();
43370
43046
  const geomWithColor = { ...geom, color: [color.r, color.g, color.b] };
43371
43047
  const threeGeom = convertCSGToThreeGeom(geomWithColor);
43372
- const material2 = new THREE37.MeshStandardMaterial({
43048
+ const material2 = new THREE39.MeshStandardMaterial({
43373
43049
  vertexColors: true,
43374
43050
  metalness: 0.2,
43375
43051
  roughness: 0.8,
43376
- side: THREE37.DoubleSide
43052
+ side: THREE39.DoubleSide
43377
43053
  });
43378
- const mesh2 = new THREE37.Mesh(threeGeom, material2);
43054
+ const mesh2 = new THREE39.Mesh(threeGeom, material2);
43379
43055
  if (component.position) {
43380
43056
  mesh2.position.set(
43381
43057
  component.position.x ?? 0,
@@ -43385,22 +43061,22 @@ async function renderComponent(component, scene) {
43385
43061
  }
43386
43062
  if (component.rotation) {
43387
43063
  mesh2.rotation.set(
43388
- THREE37.MathUtils.degToRad(component.rotation.x ?? 0),
43389
- THREE37.MathUtils.degToRad(component.rotation.y ?? 0),
43390
- THREE37.MathUtils.degToRad(component.rotation.z ?? 0)
43064
+ THREE39.MathUtils.degToRad(component.rotation.x ?? 0),
43065
+ THREE39.MathUtils.degToRad(component.rotation.y ?? 0),
43066
+ THREE39.MathUtils.degToRad(component.rotation.z ?? 0)
43391
43067
  );
43392
43068
  }
43393
43069
  scene.add(mesh2);
43394
43070
  }
43395
43071
  return;
43396
43072
  }
43397
- const geometry = new THREE37.BoxGeometry(0.5, 0.5, 0.5);
43398
- const material = new THREE37.MeshStandardMaterial({
43073
+ const geometry = new THREE39.BoxGeometry(0.5, 0.5, 0.5);
43074
+ const material = new THREE39.MeshStandardMaterial({
43399
43075
  color: 16711680,
43400
43076
  transparent: true,
43401
43077
  opacity: 0.25
43402
43078
  });
43403
- const mesh = new THREE37.Mesh(geometry, material);
43079
+ const mesh = new THREE39.Mesh(geometry, material);
43404
43080
  if (component.position) {
43405
43081
  mesh.position.set(
43406
43082
  component.position.x ?? 0,
@@ -43421,11 +43097,11 @@ async function convertCircuitJsonTo3dSvg(circuitJson, options = {}) {
43421
43097
  padding = 20,
43422
43098
  zoom = 1.5
43423
43099
  } = options;
43424
- const scene = new THREE38.Scene();
43100
+ const scene = new THREE40.Scene();
43425
43101
  const renderer = new SVGRenderer();
43426
43102
  renderer.setSize(width10, height10);
43427
- renderer.setClearColor(new THREE38.Color(backgroundColor), 1);
43428
- const camera = new THREE38.OrthographicCamera();
43103
+ renderer.setClearColor(new THREE40.Color(backgroundColor), 1);
43104
+ const camera = new THREE40.OrthographicCamera();
43429
43105
  const aspect = width10 / height10;
43430
43106
  const frustumSize = 100;
43431
43107
  const halfFrustumSize = frustumSize / 2 / zoom;
@@ -43439,11 +43115,11 @@ async function convertCircuitJsonTo3dSvg(circuitJson, options = {}) {
43439
43115
  camera.position.set(position.x, position.y, position.z);
43440
43116
  camera.up.set(0, 1, 0);
43441
43117
  const lookAt = options.camera?.lookAt ?? { x: 0, y: 0, z: 0 };
43442
- camera.lookAt(new THREE38.Vector3(lookAt.x, lookAt.y, lookAt.z));
43118
+ camera.lookAt(new THREE40.Vector3(lookAt.x, lookAt.y, lookAt.z));
43443
43119
  camera.updateProjectionMatrix();
43444
- const ambientLight = new THREE38.AmbientLight(16777215, Math.PI / 2);
43120
+ const ambientLight = new THREE40.AmbientLight(16777215, Math.PI / 2);
43445
43121
  scene.add(ambientLight);
43446
- const pointLight = new THREE38.PointLight(16777215, Math.PI / 4);
43122
+ const pointLight = new THREE40.PointLight(16777215, Math.PI / 4);
43447
43123
  pointLight.position.set(-10, -10, 10);
43448
43124
  scene.add(pointLight);
43449
43125
  const components = su20(circuitJson).cad_component.list();
@@ -43454,7 +43130,7 @@ async function convertCircuitJsonTo3dSvg(circuitJson, options = {}) {
43454
43130
  const boardGeom = createBoardGeomFromCircuitJson(circuitJson);
43455
43131
  if (boardGeom) {
43456
43132
  const solderMaskColor = colors.fr4SolderMaskGreen;
43457
- const baseColor = new THREE38.Color(
43133
+ const baseColor = new THREE40.Color(
43458
43134
  solderMaskColor[0],
43459
43135
  solderMaskColor[1],
43460
43136
  solderMaskColor[2]
@@ -43466,28 +43142,28 @@ async function convertCircuitJsonTo3dSvg(circuitJson, options = {}) {
43466
43142
  const material = createBoardMaterial({
43467
43143
  material: boardData?.material,
43468
43144
  color: baseColor,
43469
- side: THREE38.DoubleSide
43145
+ side: THREE40.DoubleSide
43470
43146
  });
43471
- const mesh = new THREE38.Mesh(geometry, material);
43147
+ const mesh = new THREE40.Mesh(geometry, material);
43472
43148
  scene.add(mesh);
43473
43149
  }
43474
43150
  }
43475
- const gridColor = new THREE38.Color(8947848);
43476
- const gridHelper = new THREE38.GridHelper(100, 100, gridColor, gridColor);
43151
+ const gridColor = new THREE40.Color(8947848);
43152
+ const gridHelper = new THREE40.GridHelper(100, 100, gridColor, gridColor);
43477
43153
  gridHelper.rotation.x = Math.PI / 2;
43478
43154
  const materials = Array.isArray(gridHelper.material) ? gridHelper.material : [gridHelper.material];
43479
43155
  for (const mat of materials) {
43480
43156
  mat.transparent = true;
43481
43157
  mat.opacity = 0.3;
43482
- if (mat instanceof THREE38.LineBasicMaterial) {
43158
+ if (mat instanceof THREE40.LineBasicMaterial) {
43483
43159
  mat.color = gridColor;
43484
43160
  mat.vertexColors = false;
43485
43161
  }
43486
43162
  }
43487
43163
  scene.add(gridHelper);
43488
- const box = new THREE38.Box3().setFromObject(scene);
43489
- const center = box.getCenter(new THREE38.Vector3());
43490
- const size4 = box.getSize(new THREE38.Vector3());
43164
+ const box = new THREE40.Box3().setFromObject(scene);
43165
+ const center = box.getCenter(new THREE40.Vector3());
43166
+ const size4 = box.getSize(new THREE40.Vector3());
43491
43167
  scene.position.sub(center);
43492
43168
  const maxDim = Math.max(size4.x, size4.y, size4.z);
43493
43169
  if (maxDim > 0) {