@tscircuit/3d-viewer 0.0.232 → 0.0.233

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -43,18 +43,36 @@ declare function useExportGltfUrl(options?: Options): [
43
43
  error: ErrorEvent | undefined
44
44
  ];
45
45
 
46
- interface UseManifoldBoardBuilderResult {
47
- boardThreeGeom: THREE.BufferGeometry | null;
48
- boardColor: THREE.Color;
49
- otherComponentGeoms: Array<{
46
+ interface ManifoldGeoms {
47
+ board?: {
48
+ geometry: THREE.BufferGeometry;
49
+ color: THREE.Color;
50
+ };
51
+ platedHoles?: Array<{
52
+ key: string;
53
+ geometry: THREE.BufferGeometry;
54
+ color: THREE.Color;
55
+ }>;
56
+ smtPads?: Array<{
50
57
  key: string;
51
58
  geometry: THREE.BufferGeometry;
52
59
  color: THREE.Color;
53
60
  }>;
54
- topTraceTexture: THREE.CanvasTexture | null;
55
- bottomTraceTexture: THREE.CanvasTexture | null;
56
- topSilkscreenTexture: THREE.CanvasTexture | null;
57
- bottomSilkscreenTexture: THREE.CanvasTexture | null;
61
+ vias?: Array<{
62
+ key: string;
63
+ geometry: THREE.BufferGeometry;
64
+ color: THREE.Color;
65
+ }>;
66
+ }
67
+ interface ManifoldTextures {
68
+ topTrace?: THREE.CanvasTexture | null;
69
+ bottomTrace?: THREE.CanvasTexture | null;
70
+ topSilkscreen?: THREE.CanvasTexture | null;
71
+ bottomSilkscreen?: THREE.CanvasTexture | null;
72
+ }
73
+ interface UseManifoldBoardBuilderResult {
74
+ geoms: ManifoldGeoms | null;
75
+ textures: ManifoldTextures | null;
58
76
  pcbThickness: number | null;
59
77
  error: string | null;
60
78
  isLoading: boolean;
@@ -64,4 +82,4 @@ declare const useManifoldBoardBuilder: (manifoldJSModule: ManifoldToplevel | nul
64
82
 
65
83
  declare function applyJsdomShim(jsdom: JSDOM): void;
66
84
 
67
- export { CadViewer, applyJsdomShim, convertCircuitJsonTo3dSvg, useExportGltfUrl, useManifoldBoardBuilder, useSaveGltfAs };
85
+ export { CadViewer, type ManifoldGeoms, type ManifoldTextures, applyJsdomShim, convertCircuitJsonTo3dSvg, useExportGltfUrl, useManifoldBoardBuilder, useSaveGltfAs };
package/dist/index.js CHANGED
@@ -16985,7 +16985,7 @@ import { Canvas, useFrame as useFrame2 } from "@react-three/fiber";
16985
16985
  // package.json
16986
16986
  var package_default = {
16987
16987
  name: "@tscircuit/3d-viewer",
16988
- version: "0.0.231",
16988
+ version: "0.0.232",
16989
16989
  main: "./dist/index.js",
16990
16990
  module: "./dist/index.js",
16991
16991
  type: "module",
@@ -17310,6 +17310,10 @@ var colors = {
17310
17310
  fr1Copper: [0.8, 0.4, 0.2],
17311
17311
  fr1CopperSolderWithMask: [0.9, 0.6, 0.2]
17312
17312
  };
17313
+ var MANIFOLD_Z_OFFSET = 1e-3;
17314
+ var SMOOTH_CIRCLE_SEGMENTS = 32;
17315
+ var DEFAULT_SMT_PAD_THICKNESS = 0.035;
17316
+ var TRACE_TEXTURE_RESOLUTION = 200;
17313
17317
  var boardMaterialColors = {
17314
17318
  fr1: colors.fr1Copper,
17315
17319
  fr4: colors.fr4Green
@@ -18976,14 +18980,13 @@ var CadViewerJscad = forwardRef2(
18976
18980
 
18977
18981
  // src/CadViewerManifold.tsx
18978
18982
  import { useEffect as useEffect6, useState as useState8, useMemo as useMemo7 } from "react";
18979
- import { su as su6 } from "@tscircuit/soup-util";
18980
- import * as THREE8 from "three";
18983
+ import { su as su12 } from "@tscircuit/soup-util";
18981
18984
  import ManifoldModule from "manifold-3d";
18982
18985
 
18983
18986
  // src/hooks/useManifoldBoardBuilder.ts
18984
18987
  import { useState as useState7, useEffect as useEffect5, useMemo as useMemo6, useRef as useRef6 } from "react";
18985
- import { su as su5 } from "@tscircuit/soup-util";
18986
- import * as THREE7 from "three";
18988
+ import { su as su11 } from "@tscircuit/soup-util";
18989
+ import * as THREE10 from "three";
18987
18990
 
18988
18991
  // src/utils/manifold-mesh-to-three-geometry.ts
18989
18992
  import * as THREE4 from "three";
@@ -19008,18 +19011,22 @@ function manifoldMeshToThreeGeometry(manifoldMesh) {
19008
19011
 
19009
19012
  // src/utils/trace-texture.ts
19010
19013
  import * as THREE5 from "three";
19014
+ import { su as su5 } from "@tscircuit/soup-util";
19011
19015
  function isWireRoutePoint(point) {
19012
19016
  return point && point.route_type === "wire" && typeof point.layer === "string" && typeof point.width === "number";
19013
19017
  }
19014
19018
  function createTraceTextureForLayer({
19015
19019
  layer,
19016
- pcbTraces,
19020
+ circuitJson,
19017
19021
  boardData,
19018
19022
  traceColor,
19019
- traceTextureResolution,
19020
- allPcbVias,
19021
- allPcbPlatedHoles
19023
+ traceTextureResolution
19022
19024
  }) {
19025
+ const pcbTraces = su5(circuitJson).pcb_trace.list();
19026
+ const allPcbVias = su5(circuitJson).pcb_via.list();
19027
+ const allPcbPlatedHoles = su5(
19028
+ circuitJson
19029
+ ).pcb_plated_hole.list();
19023
19030
  const tracesOnLayer = pcbTraces.filter(
19024
19031
  (t) => t.route.some((p) => isWireRoutePoint(p) && p.layer === layer)
19025
19032
  );
@@ -19098,14 +19105,16 @@ function createTraceTextureForLayer({
19098
19105
  // src/utils/silkscreen-texture.ts
19099
19106
  var import_text2 = __toESM(require_text(), 1);
19100
19107
  import * as THREE6 from "three";
19108
+ import { su as su6 } from "@tscircuit/soup-util";
19101
19109
  function createSilkscreenTextureForLayer({
19102
19110
  layer,
19103
- pcbSilkscreenTexts,
19104
- pcbSilkscreenPaths,
19111
+ circuitJson,
19105
19112
  boardData,
19106
19113
  silkscreenColor = "rgb(255,255,255)",
19107
19114
  traceTextureResolution
19108
19115
  }) {
19116
+ const pcbSilkscreenTexts = su6(circuitJson).pcb_silkscreen_text.list();
19117
+ const pcbSilkscreenPaths = su6(circuitJson).pcb_silkscreen_path.list();
19109
19118
  const textsOnLayer = pcbSilkscreenTexts.filter((t) => t.layer === layer);
19110
19119
  const pathsOnLayer = pcbSilkscreenPaths.filter((p) => p.layer === layer);
19111
19120
  if (textsOnLayer.length === 0 && pathsOnLayer.length === 0) return null;
@@ -19226,19 +19235,8 @@ function createSilkscreenTextureForLayer({
19226
19235
  return texture;
19227
19236
  }
19228
19237
 
19229
- // src/utils/pad-geoms.ts
19230
- function createPadManifoldOp({
19231
- Manifold,
19232
- pad,
19233
- padBaseThickness
19234
- }) {
19235
- if (pad.shape === "rect") {
19236
- return Manifold.cube([pad.width, pad.height, padBaseThickness], true);
19237
- } else if (pad.shape === "circle" && pad.radius) {
19238
- return Manifold.cylinder(padBaseThickness, pad.radius, -1, 32, true);
19239
- }
19240
- return null;
19241
- }
19238
+ // src/utils/manifold/process-non-plated-holes.ts
19239
+ import { su as su7 } from "@tscircuit/soup-util";
19242
19240
 
19243
19241
  // src/utils/hole-geoms.ts
19244
19242
  function createCircleHoleDrill({
@@ -19278,6 +19276,91 @@ function createPlatedHoleDrill({
19278
19276
  return drill.translate([x, y, 0]);
19279
19277
  }
19280
19278
 
19279
+ // src/utils/manifold/process-non-plated-holes.ts
19280
+ function isCircleHole(hole) {
19281
+ return (hole.shape === "circle" || hole.hole_shape === "circle") && typeof hole.hole_diameter === "number";
19282
+ }
19283
+ function processNonPlatedHolesForManifold(Manifold, circuitJson, pcbThickness, manifoldInstancesForCleanup) {
19284
+ const nonPlatedHoleBoardDrills = [];
19285
+ const pcbHoles = su7(circuitJson).pcb_hole.list();
19286
+ pcbHoles.forEach((hole) => {
19287
+ if (isCircleHole(hole)) {
19288
+ const translatedDrill = createCircleHoleDrill({
19289
+ Manifold,
19290
+ x: hole.x,
19291
+ y: hole.y,
19292
+ diameter: hole.hole_diameter,
19293
+ thickness: pcbThickness,
19294
+ segments: SMOOTH_CIRCLE_SEGMENTS
19295
+ });
19296
+ manifoldInstancesForCleanup.push(translatedDrill);
19297
+ nonPlatedHoleBoardDrills.push(translatedDrill);
19298
+ }
19299
+ });
19300
+ return { nonPlatedHoleBoardDrills };
19301
+ }
19302
+
19303
+ // src/utils/manifold/process-plated-holes.ts
19304
+ import { su as su8 } from "@tscircuit/soup-util";
19305
+ import * as THREE7 from "three";
19306
+ var COPPER_COLOR = new THREE7.Color(...colors.copper);
19307
+ function processPlatedHolesForManifold(Manifold, circuitJson, pcbThickness, manifoldInstancesForCleanup) {
19308
+ const platedHoleBoardDrills = [];
19309
+ const pcbPlatedHoles = su8(circuitJson).pcb_plated_hole.list();
19310
+ const platedHoleCopperGeoms = [];
19311
+ pcbPlatedHoles.forEach((ph, index) => {
19312
+ if (ph.shape === "circle") {
19313
+ const translatedDrill = createPlatedHoleDrill({
19314
+ Manifold,
19315
+ x: ph.x,
19316
+ y: ph.y,
19317
+ outerDiameter: ph.outer_diameter,
19318
+ // Drill for the board
19319
+ thickness: pcbThickness,
19320
+ zOffset: MANIFOLD_Z_OFFSET,
19321
+ segments: SMOOTH_CIRCLE_SEGMENTS
19322
+ });
19323
+ manifoldInstancesForCleanup.push(translatedDrill);
19324
+ platedHoleBoardDrills.push(translatedDrill);
19325
+ const copperPartThickness = pcbThickness + 2 * MANIFOLD_Z_OFFSET;
19326
+ let platedPart = Manifold.cylinder(
19327
+ copperPartThickness,
19328
+ ph.outer_diameter / 2,
19329
+ -1,
19330
+ SMOOTH_CIRCLE_SEGMENTS,
19331
+ true
19332
+ );
19333
+ manifoldInstancesForCleanup.push(platedPart);
19334
+ const drillForCopper = Manifold.cylinder(
19335
+ copperPartThickness * 1.05,
19336
+ // ensure it cuts through
19337
+ ph.hole_diameter / 2,
19338
+ -1,
19339
+ SMOOTH_CIRCLE_SEGMENTS,
19340
+ true
19341
+ );
19342
+ manifoldInstancesForCleanup.push(drillForCopper);
19343
+ const finalPlatedPartOp = platedPart.subtract(drillForCopper);
19344
+ manifoldInstancesForCleanup.push(finalPlatedPartOp);
19345
+ const translatedPlatedPart = finalPlatedPartOp.translate([ph.x, ph.y, 0]);
19346
+ manifoldInstancesForCleanup.push(translatedPlatedPart);
19347
+ const threeGeom = manifoldMeshToThreeGeometry(
19348
+ translatedPlatedPart.getMesh()
19349
+ );
19350
+ platedHoleCopperGeoms.push({
19351
+ key: `ph-${ph.pcb_plated_hole_id || index}`,
19352
+ geometry: threeGeom,
19353
+ color: COPPER_COLOR
19354
+ });
19355
+ }
19356
+ });
19357
+ return { platedHoleBoardDrills, platedHoleCopperGeoms };
19358
+ }
19359
+
19360
+ // src/utils/manifold/process-vias.ts
19361
+ import { su as su9 } from "@tscircuit/soup-util";
19362
+ import * as THREE8 from "three";
19363
+
19281
19364
  // src/utils/via-geoms.ts
19282
19365
  function createViaCopper({
19283
19366
  Manifold,
@@ -19308,7 +19391,98 @@ function createViaCopper({
19308
19391
  return finalViaCopperOp.translate([x, y, 0]);
19309
19392
  }
19310
19393
 
19311
- // src/hooks/useManifoldBoardBuilder.ts
19394
+ // src/utils/manifold/process-vias.ts
19395
+ var COPPER_COLOR2 = new THREE8.Color(...colors.copper);
19396
+ function processViasForManifold(Manifold, circuitJson, pcbThickness, manifoldInstancesForCleanup) {
19397
+ const viaBoardDrills = [];
19398
+ const pcbVias = su9(circuitJson).pcb_via.list();
19399
+ const viaCopperGeoms = [];
19400
+ pcbVias.forEach((via, index) => {
19401
+ if (typeof via.outer_diameter === "number") {
19402
+ const translatedDrill = createPlatedHoleDrill({
19403
+ Manifold,
19404
+ x: via.x,
19405
+ y: via.y,
19406
+ outerDiameter: via.outer_diameter,
19407
+ thickness: pcbThickness,
19408
+ zOffset: MANIFOLD_Z_OFFSET,
19409
+ segments: SMOOTH_CIRCLE_SEGMENTS
19410
+ });
19411
+ manifoldInstancesForCleanup.push(translatedDrill);
19412
+ viaBoardDrills.push(translatedDrill);
19413
+ }
19414
+ if (typeof via.outer_diameter === "number" && typeof via.hole_diameter === "number") {
19415
+ const translatedViaCopper = createViaCopper({
19416
+ Manifold,
19417
+ x: via.x,
19418
+ y: via.y,
19419
+ outerDiameter: via.outer_diameter,
19420
+ holeDiameter: via.hole_diameter,
19421
+ thickness: pcbThickness,
19422
+ zOffset: MANIFOLD_Z_OFFSET,
19423
+ segments: SMOOTH_CIRCLE_SEGMENTS
19424
+ });
19425
+ manifoldInstancesForCleanup.push(translatedViaCopper);
19426
+ const threeGeom = manifoldMeshToThreeGeometry(
19427
+ translatedViaCopper.getMesh()
19428
+ );
19429
+ viaCopperGeoms.push({
19430
+ key: `via-${via.pcb_via_id || index}`,
19431
+ geometry: threeGeom,
19432
+ color: COPPER_COLOR2
19433
+ });
19434
+ }
19435
+ });
19436
+ return { viaBoardDrills, viaCopperGeoms };
19437
+ }
19438
+
19439
+ // src/utils/manifold/process-smt-pads.ts
19440
+ import { su as su10 } from "@tscircuit/soup-util";
19441
+ import * as THREE9 from "three";
19442
+
19443
+ // src/utils/pad-geoms.ts
19444
+ function createPadManifoldOp({
19445
+ Manifold,
19446
+ pad,
19447
+ padBaseThickness
19448
+ }) {
19449
+ if (pad.shape === "rect") {
19450
+ return Manifold.cube([pad.width, pad.height, padBaseThickness], true);
19451
+ } else if (pad.shape === "circle" && pad.radius) {
19452
+ return Manifold.cylinder(padBaseThickness, pad.radius, -1, 32, true);
19453
+ }
19454
+ return null;
19455
+ }
19456
+
19457
+ // src/utils/manifold/process-smt-pads.ts
19458
+ var COPPER_COLOR3 = new THREE9.Color(...colors.copper);
19459
+ function processSmtPadsForManifold(Manifold, circuitJson, pcbThickness, manifoldInstancesForCleanup) {
19460
+ const smtPadGeoms = [];
19461
+ const smtPads = su10(circuitJson).pcb_smtpad.list();
19462
+ smtPads.forEach((pad, index) => {
19463
+ const padBaseThickness = DEFAULT_SMT_PAD_THICKNESS;
19464
+ const zPos = pad.layer === "bottom" ? -pcbThickness / 2 - padBaseThickness / 2 - MANIFOLD_Z_OFFSET : pcbThickness / 2 + padBaseThickness / 2 + MANIFOLD_Z_OFFSET;
19465
+ let padManifoldOp = createPadManifoldOp({
19466
+ Manifold,
19467
+ pad,
19468
+ padBaseThickness
19469
+ });
19470
+ if (padManifoldOp) {
19471
+ manifoldInstancesForCleanup.push(padManifoldOp);
19472
+ const translatedPad = padManifoldOp.translate([pad.x, pad.y, zPos]);
19473
+ manifoldInstancesForCleanup.push(translatedPad);
19474
+ const threeGeom = manifoldMeshToThreeGeometry(translatedPad.getMesh());
19475
+ smtPadGeoms.push({
19476
+ key: `pad-${pad.pcb_smtpad_id || index}`,
19477
+ geometry: threeGeom,
19478
+ color: COPPER_COLOR3
19479
+ });
19480
+ }
19481
+ });
19482
+ return { smtPadGeoms };
19483
+ }
19484
+
19485
+ // src/utils/manifold/create-manifold-board.ts
19312
19486
  var arePointsClockwise2 = (points) => {
19313
19487
  let area = 0;
19314
19488
  for (let i = 0; i < points.length; i++) {
@@ -19321,54 +19495,70 @@ var arePointsClockwise2 = (points) => {
19321
19495
  const signedArea = area / 2;
19322
19496
  return signedArea <= 0;
19323
19497
  };
19324
- var COPPER_COLOR = new THREE7.Color(
19325
- colors.copper[0],
19326
- colors.copper[1],
19327
- colors.copper[2]
19328
- );
19329
- var DEFAULT_SMT_PAD_THICKNESS = 0.035;
19330
- var SMOOTH_CIRCLE_SEGMENTS = 32;
19331
- var MANIFOLD_Z_OFFSET = 1e-3;
19332
- var TRACE_TEXTURE_RESOLUTION = 50;
19498
+ function createManifoldBoard(Manifold, CrossSection, boardData, pcbThickness, manifoldInstancesForCleanup) {
19499
+ let boardOp;
19500
+ if (boardData.outline && boardData.outline.length >= 3) {
19501
+ let outlineVec2 = boardData.outline.map((p) => [
19502
+ p.x,
19503
+ p.y
19504
+ ]);
19505
+ if (arePointsClockwise2(outlineVec2)) {
19506
+ outlineVec2 = outlineVec2.reverse();
19507
+ }
19508
+ const crossSection = CrossSection.ofPolygons([outlineVec2]);
19509
+ manifoldInstancesForCleanup.push(crossSection);
19510
+ boardOp = Manifold.extrude(
19511
+ crossSection,
19512
+ pcbThickness,
19513
+ void 0,
19514
+ // nDivisions
19515
+ void 0,
19516
+ // twistDegrees
19517
+ void 0,
19518
+ // scaleTop
19519
+ true
19520
+ // center (for Z-axis)
19521
+ );
19522
+ } else {
19523
+ if (boardData.outline && boardData.outline.length > 0) {
19524
+ console.warn(
19525
+ "Board outline has fewer than 3 points, falling back to rectangular board."
19526
+ );
19527
+ }
19528
+ boardOp = Manifold.cube(
19529
+ [boardData.width, boardData.height, pcbThickness],
19530
+ true
19531
+ // center (for all axes)
19532
+ );
19533
+ }
19534
+ manifoldInstancesForCleanup.push(boardOp);
19535
+ boardOp = boardOp.translate([boardData.center.x, boardData.center.y, 0]);
19536
+ manifoldInstancesForCleanup.push(boardOp);
19537
+ return boardOp;
19538
+ }
19539
+
19540
+ // src/hooks/useManifoldBoardBuilder.ts
19333
19541
  var useManifoldBoardBuilder = (manifoldJSModule, circuitJson) => {
19334
- const [boardThreeGeom, setBoardThreeGeom] = useState7(null);
19335
- const [boardColor, setBoardColor] = useState7(
19336
- new THREE7.Color(
19337
- colors.fr4Green[0],
19338
- colors.fr4Green[1],
19339
- colors.fr4Green[2]
19340
- )
19341
- );
19342
- const [otherComponentGeoms, setOtherComponentGeoms] = useState7([]);
19343
- const [topTraceTexture, setTopTraceTexture] = useState7(null);
19344
- const [bottomTraceTexture, setBottomTraceTexture] = useState7(null);
19345
- const [topSilkscreenTexture, setTopSilkscreenTexture] = useState7(null);
19346
- const [bottomSilkscreenTexture, setBottomSilkscreenTexture] = useState7(null);
19542
+ const [geoms, setGeoms] = useState7(null);
19543
+ const [textures, setTextures] = useState7(null);
19347
19544
  const [pcbThickness, setPcbThickness] = useState7(null);
19348
19545
  const [error, setError] = useState7(null);
19349
19546
  const [isLoading, setIsLoading] = useState7(true);
19350
19547
  const manifoldInstancesForCleanup = useRef6([]);
19351
19548
  const boardData = useMemo6(() => {
19352
19549
  if (!circuitJson) return null;
19353
- const boards = su5(circuitJson).pcb_board.list();
19550
+ const boards = su11(circuitJson).pcb_board.list();
19354
19551
  if (boards.length === 0) {
19355
19552
  return null;
19356
19553
  }
19357
19554
  return boards[0];
19358
19555
  }, [circuitJson]);
19359
- function isCircleHole(hole) {
19360
- return (hole.shape === "circle" || hole.hole_shape === "circle") && typeof hole.hole_diameter === "number";
19361
- }
19362
19556
  useEffect5(() => {
19363
19557
  if (!manifoldJSModule || !circuitJson || !boardData) {
19364
- setBoardThreeGeom(null);
19365
- setOtherComponentGeoms([]);
19366
- setTopTraceTexture(null);
19367
- setBottomTraceTexture(null);
19368
- setTopSilkscreenTexture(null);
19369
- setBottomSilkscreenTexture(null);
19558
+ setGeoms(null);
19559
+ setTextures(null);
19370
19560
  setPcbThickness(null);
19371
- if (circuitJson && su5(circuitJson).pcb_board.list().length === 0) {
19561
+ if (circuitJson && su11(circuitJson).pcb_board.list().length === 0) {
19372
19562
  setError("No pcb_board found in circuitJson.");
19373
19563
  }
19374
19564
  setIsLoading(false);
@@ -19381,100 +19571,44 @@ var useManifoldBoardBuilder = (manifoldJSModule, circuitJson) => {
19381
19571
  manifoldInstancesForCleanup.current.forEach((inst) => inst.delete());
19382
19572
  manifoldInstancesForCleanup.current = [];
19383
19573
  let boardManifold = null;
19384
- const newOtherGeoms = [];
19574
+ const currentGeoms = {};
19575
+ const currentTextures = {};
19385
19576
  try {
19386
19577
  const currentPcbThickness = boardData.thickness || 1.6;
19387
19578
  setPcbThickness(currentPcbThickness);
19388
- let currentBoardOp;
19389
- if (boardData.outline && boardData.outline.length >= 3) {
19390
- let outlineVec2 = boardData.outline.map(
19391
- (p) => [p.x, p.y]
19392
- );
19393
- if (arePointsClockwise2(outlineVec2)) {
19394
- outlineVec2 = outlineVec2.reverse();
19395
- }
19396
- const crossSection = CrossSection.ofPolygons([outlineVec2]);
19397
- manifoldInstancesForCleanup.current.push(crossSection);
19398
- currentBoardOp = Manifold.extrude(
19399
- crossSection,
19400
- currentPcbThickness,
19401
- void 0,
19402
- // nDivisions
19403
- void 0,
19404
- // twistDegrees
19405
- void 0,
19406
- // scaleTop
19407
- true
19408
- // center (for Z-axis)
19409
- );
19410
- } else {
19411
- if (boardData.outline && boardData.outline.length > 0) {
19412
- console.warn(
19413
- "Board outline has fewer than 3 points, falling back to rectangular board."
19414
- );
19415
- }
19416
- currentBoardOp = Manifold.cube(
19417
- [boardData.width, boardData.height, currentPcbThickness],
19418
- true
19419
- // center (for all axes)
19420
- );
19421
- }
19422
- manifoldInstancesForCleanup.current.push(currentBoardOp);
19423
- currentBoardOp = currentBoardOp.translate([
19424
- boardData.center.x,
19425
- boardData.center.y,
19426
- 0
19427
- ]);
19428
- const pcbHoles = su5(circuitJson).pcb_hole.list();
19429
- const pcbPlatedHoles = su5(circuitJson).pcb_plated_hole.list();
19430
- const pcbVias = su5(circuitJson).pcb_via.list();
19431
- const allHoleDrills = [];
19432
- pcbHoles.forEach((hole) => {
19433
- if (isCircleHole(hole)) {
19434
- const translatedDrill = createCircleHoleDrill({
19435
- Manifold,
19436
- x: hole.x,
19437
- y: hole.y,
19438
- diameter: hole.hole_diameter,
19439
- thickness: currentPcbThickness,
19440
- segments: SMOOTH_CIRCLE_SEGMENTS
19441
- });
19442
- manifoldInstancesForCleanup.current.push(translatedDrill);
19443
- allHoleDrills.push(translatedDrill);
19444
- }
19445
- });
19446
- pcbPlatedHoles.forEach((ph) => {
19447
- if (ph.shape === "circle") {
19448
- const translatedDrill = createPlatedHoleDrill({
19449
- Manifold,
19450
- x: ph.x,
19451
- y: ph.y,
19452
- outerDiameter: ph.outer_diameter,
19453
- thickness: currentPcbThickness,
19454
- zOffset: MANIFOLD_Z_OFFSET,
19455
- segments: SMOOTH_CIRCLE_SEGMENTS
19456
- });
19457
- manifoldInstancesForCleanup.current.push(translatedDrill);
19458
- allHoleDrills.push(translatedDrill);
19459
- }
19460
- });
19461
- pcbVias.forEach((via) => {
19462
- if (typeof via.outer_diameter === "number") {
19463
- const translatedDrill = createPlatedHoleDrill({
19464
- Manifold,
19465
- x: via.x,
19466
- y: via.y,
19467
- outerDiameter: via.outer_diameter,
19468
- thickness: currentPcbThickness,
19469
- zOffset: MANIFOLD_Z_OFFSET,
19470
- segments: SMOOTH_CIRCLE_SEGMENTS
19471
- });
19472
- manifoldInstancesForCleanup.current.push(translatedDrill);
19473
- allHoleDrills.push(translatedDrill);
19474
- }
19475
- });
19476
- if (allHoleDrills.length > 0) {
19477
- const unionedDrills = Manifold.union(allHoleDrills);
19579
+ let currentBoardOp = createManifoldBoard(
19580
+ Manifold,
19581
+ CrossSection,
19582
+ boardData,
19583
+ currentPcbThickness,
19584
+ manifoldInstancesForCleanup.current
19585
+ );
19586
+ const allBoardDrills = [];
19587
+ const { nonPlatedHoleBoardDrills } = processNonPlatedHolesForManifold(
19588
+ Manifold,
19589
+ circuitJson,
19590
+ currentPcbThickness,
19591
+ manifoldInstancesForCleanup.current
19592
+ );
19593
+ allBoardDrills.push(...nonPlatedHoleBoardDrills);
19594
+ const { platedHoleBoardDrills, platedHoleCopperGeoms } = processPlatedHolesForManifold(
19595
+ Manifold,
19596
+ circuitJson,
19597
+ currentPcbThickness,
19598
+ manifoldInstancesForCleanup.current
19599
+ );
19600
+ allBoardDrills.push(...platedHoleBoardDrills);
19601
+ currentGeoms.platedHoles = platedHoleCopperGeoms;
19602
+ const { viaBoardDrills, viaCopperGeoms } = processViasForManifold(
19603
+ Manifold,
19604
+ circuitJson,
19605
+ currentPcbThickness,
19606
+ manifoldInstancesForCleanup.current
19607
+ );
19608
+ allBoardDrills.push(...viaBoardDrills);
19609
+ currentGeoms.vias = viaCopperGeoms;
19610
+ if (allBoardDrills.length > 0) {
19611
+ const unionedDrills = Manifold.union(allBoardDrills);
19478
19612
  manifoldInstancesForCleanup.current.push(unionedDrills);
19479
19613
  const nextBoard = currentBoardOp.subtract(unionedDrills);
19480
19614
  manifoldInstancesForCleanup.current.push(nextBoard);
@@ -19484,165 +19618,63 @@ var useManifoldBoardBuilder = (manifoldJSModule, circuitJson) => {
19484
19618
  if (boardManifold) {
19485
19619
  const boardThreeMesh = boardManifold.getMesh();
19486
19620
  const finalBoardGeom = manifoldMeshToThreeGeometry(boardThreeMesh);
19487
- setBoardThreeGeom(finalBoardGeom);
19488
- } else {
19489
- setBoardThreeGeom(null);
19621
+ const matColorArray = boardMaterialColors[boardData.material] ?? colors.fr4Green;
19622
+ currentGeoms.board = {
19623
+ geometry: finalBoardGeom,
19624
+ color: new THREE10.Color(
19625
+ matColorArray[0],
19626
+ matColorArray[1],
19627
+ matColorArray[2]
19628
+ )
19629
+ };
19490
19630
  }
19491
- const matColorArray = boardMaterialColors[boardData.material] ?? colors.fr4Green;
19492
- setBoardColor(
19493
- new THREE7.Color(matColorArray[0], matColorArray[1], matColorArray[2])
19631
+ const { smtPadGeoms } = processSmtPadsForManifold(
19632
+ Manifold,
19633
+ circuitJson,
19634
+ currentPcbThickness,
19635
+ manifoldInstancesForCleanup.current
19494
19636
  );
19495
- pcbPlatedHoles.forEach((ph, index) => {
19496
- if (ph.shape === "circle") {
19497
- const copperPartThickness = currentPcbThickness + 2 * MANIFOLD_Z_OFFSET;
19498
- let platedPart = Manifold.cylinder(
19499
- copperPartThickness,
19500
- ph.outer_diameter / 2,
19501
- -1,
19502
- SMOOTH_CIRCLE_SEGMENTS,
19503
- true
19504
- );
19505
- manifoldInstancesForCleanup.current.push(platedPart);
19506
- const drill = Manifold.cylinder(
19507
- copperPartThickness * 1.05,
19508
- ph.hole_diameter / 2,
19509
- -1,
19510
- SMOOTH_CIRCLE_SEGMENTS,
19511
- true
19512
- );
19513
- manifoldInstancesForCleanup.current.push(drill);
19514
- const finalPlatedPartOp = platedPart.subtract(drill);
19515
- manifoldInstancesForCleanup.current.push(finalPlatedPartOp);
19516
- const translatedPlatedPart = finalPlatedPartOp.translate([
19517
- ph.x,
19518
- ph.y,
19519
- 0
19520
- ]);
19521
- manifoldInstancesForCleanup.current.push(translatedPlatedPart);
19522
- const copperMesh = translatedPlatedPart.getMesh();
19523
- const copperGeom = manifoldMeshToThreeGeometry(copperMesh);
19524
- newOtherGeoms.push({
19525
- key: `ph-${ph.pcb_plated_hole_id || index}`,
19526
- geometry: copperGeom,
19527
- color: COPPER_COLOR,
19528
- manifoldSource: translatedPlatedPart
19529
- });
19530
- }
19531
- });
19532
- const smtPads = su5(circuitJson).pcb_smtpad.list();
19533
- smtPads.forEach((pad, index) => {
19534
- const padBaseThickness = DEFAULT_SMT_PAD_THICKNESS;
19535
- const zPos = pad.layer === "bottom" ? -currentPcbThickness / 2 - padBaseThickness / 2 - MANIFOLD_Z_OFFSET : currentPcbThickness / 2 + padBaseThickness / 2 + MANIFOLD_Z_OFFSET;
19536
- let padManifoldOp = createPadManifoldOp({
19537
- Manifold,
19538
- pad,
19539
- padBaseThickness
19540
- });
19541
- if (padManifoldOp) {
19542
- manifoldInstancesForCleanup.current.push(padManifoldOp);
19543
- const translatedPad = padManifoldOp.translate([pad.x, pad.y, zPos]);
19544
- manifoldInstancesForCleanup.current.push(translatedPad);
19545
- const padMesh = translatedPad.getMesh();
19546
- const padGeom = manifoldMeshToThreeGeometry(padMesh);
19547
- newOtherGeoms.push({
19548
- key: `pad-${pad.pcb_smtpad_id || index}`,
19549
- geometry: padGeom,
19550
- color: COPPER_COLOR,
19551
- manifoldSource: translatedPad
19552
- });
19553
- }
19554
- });
19555
- pcbVias.forEach((via, index) => {
19556
- if (typeof via.outer_diameter === "number" && typeof via.hole_diameter === "number") {
19557
- const translatedViaCopper = createViaCopper({
19558
- Manifold,
19559
- x: via.x,
19560
- y: via.y,
19561
- outerDiameter: via.outer_diameter,
19562
- holeDiameter: via.hole_diameter,
19563
- thickness: currentPcbThickness,
19564
- zOffset: MANIFOLD_Z_OFFSET,
19565
- segments: SMOOTH_CIRCLE_SEGMENTS
19566
- });
19567
- manifoldInstancesForCleanup.current.push(translatedViaCopper);
19568
- const viaMesh = translatedViaCopper.getMesh();
19569
- const viaGeom = manifoldMeshToThreeGeometry(viaMesh);
19570
- newOtherGeoms.push({
19571
- key: `via-${via.pcb_via_id || index}`,
19572
- geometry: viaGeom,
19573
- color: COPPER_COLOR,
19574
- manifoldSource: translatedViaCopper
19575
- });
19576
- }
19577
- });
19578
- const pcbTraces = su5(circuitJson).pcb_trace.list();
19637
+ currentGeoms.smtPads = smtPadGeoms;
19638
+ setGeoms(currentGeoms);
19579
19639
  const traceColorArr = tracesMaterialColors[boardData.material] ?? colors.fr4GreenSolderWithMask;
19580
19640
  const traceColor = `rgb(${Math.round(traceColorArr[0] * 255)}, ${Math.round(traceColorArr[1] * 255)}, ${Math.round(traceColorArr[2] * 255)})`;
19581
- const allPcbVias = su5(circuitJson).pcb_via.list();
19582
- const allPcbPlatedHoles = su5(circuitJson).pcb_plated_hole.list();
19583
- setTopTraceTexture(
19584
- createTraceTextureForLayer({
19585
- layer: "top",
19586
- pcbTraces,
19587
- boardData,
19588
- traceColor,
19589
- traceTextureResolution: TRACE_TEXTURE_RESOLUTION,
19590
- allPcbVias,
19591
- allPcbPlatedHoles
19592
- })
19593
- );
19594
- setBottomTraceTexture(
19595
- createTraceTextureForLayer({
19596
- layer: "bottom",
19597
- pcbTraces,
19598
- boardData,
19599
- traceColor,
19600
- traceTextureResolution: TRACE_TEXTURE_RESOLUTION,
19601
- allPcbVias,
19602
- allPcbPlatedHoles
19603
- })
19604
- );
19605
- const pcbSilkscreenTexts = su5(circuitJson).pcb_silkscreen_text.list();
19606
- const pcbSilkscreenPaths = su5(circuitJson).pcb_silkscreen_path.list();
19641
+ currentTextures.topTrace = createTraceTextureForLayer({
19642
+ layer: "top",
19643
+ circuitJson,
19644
+ boardData,
19645
+ traceColor,
19646
+ traceTextureResolution: TRACE_TEXTURE_RESOLUTION
19647
+ });
19648
+ currentTextures.bottomTrace = createTraceTextureForLayer({
19649
+ layer: "bottom",
19650
+ circuitJson,
19651
+ boardData,
19652
+ traceColor,
19653
+ traceTextureResolution: TRACE_TEXTURE_RESOLUTION
19654
+ });
19607
19655
  const silkscreenColor = "rgb(255,255,255)";
19608
- setTopSilkscreenTexture(
19609
- createSilkscreenTextureForLayer({
19610
- layer: "top",
19611
- pcbSilkscreenTexts,
19612
- pcbSilkscreenPaths,
19613
- boardData,
19614
- silkscreenColor,
19615
- traceTextureResolution: TRACE_TEXTURE_RESOLUTION
19616
- })
19617
- );
19618
- setBottomSilkscreenTexture(
19619
- createSilkscreenTextureForLayer({
19620
- layer: "bottom",
19621
- pcbSilkscreenTexts,
19622
- pcbSilkscreenPaths,
19623
- boardData,
19624
- silkscreenColor,
19625
- traceTextureResolution: TRACE_TEXTURE_RESOLUTION
19626
- })
19627
- );
19628
- setOtherComponentGeoms(
19629
- newOtherGeoms.map((g) => ({
19630
- key: g.key,
19631
- geometry: g.geometry,
19632
- color: g.color
19633
- }))
19634
- );
19656
+ currentTextures.topSilkscreen = createSilkscreenTextureForLayer({
19657
+ layer: "top",
19658
+ circuitJson,
19659
+ boardData,
19660
+ silkscreenColor,
19661
+ traceTextureResolution: TRACE_TEXTURE_RESOLUTION
19662
+ });
19663
+ currentTextures.bottomSilkscreen = createSilkscreenTextureForLayer({
19664
+ layer: "bottom",
19665
+ circuitJson,
19666
+ boardData,
19667
+ silkscreenColor,
19668
+ traceTextureResolution: TRACE_TEXTURE_RESOLUTION
19669
+ });
19670
+ setTextures(currentTextures);
19635
19671
  } catch (e) {
19636
19672
  console.error("Error processing geometry with Manifold in hook:", e);
19637
19673
  setError(
19638
19674
  e.message || "An unknown error occurred while processing geometry in hook."
19639
19675
  );
19640
- setBoardThreeGeom(null);
19641
- setOtherComponentGeoms([]);
19642
- setTopTraceTexture(null);
19643
- setBottomTraceTexture(null);
19644
- setTopSilkscreenTexture(null);
19645
- setBottomSilkscreenTexture(null);
19676
+ setGeoms(null);
19677
+ setTextures(null);
19646
19678
  } finally {
19647
19679
  setIsLoading(false);
19648
19680
  }
@@ -19652,13 +19684,8 @@ var useManifoldBoardBuilder = (manifoldJSModule, circuitJson) => {
19652
19684
  };
19653
19685
  }, [manifoldJSModule, circuitJson, boardData]);
19654
19686
  return {
19655
- boardThreeGeom,
19656
- boardColor,
19657
- otherComponentGeoms,
19658
- topTraceTexture,
19659
- bottomTraceTexture,
19660
- topSilkscreenTexture,
19661
- bottomSilkscreenTexture,
19687
+ geoms,
19688
+ textures,
19662
19689
  pcbThickness,
19663
19690
  error,
19664
19691
  isLoading,
@@ -19666,6 +19693,102 @@ var useManifoldBoardBuilder = (manifoldJSModule, circuitJson) => {
19666
19693
  };
19667
19694
  };
19668
19695
 
19696
+ // src/utils/manifold/create-three-geometry-meshes.ts
19697
+ import * as THREE11 from "three";
19698
+ function createGeometryMeshes(geoms) {
19699
+ const meshes = [];
19700
+ if (!geoms) return meshes;
19701
+ if (geoms.board && geoms.board.geometry) {
19702
+ const mesh = new THREE11.Mesh(
19703
+ geoms.board.geometry,
19704
+ new THREE11.MeshStandardMaterial({
19705
+ color: geoms.board.color,
19706
+ side: THREE11.DoubleSide,
19707
+ flatShading: true
19708
+ })
19709
+ );
19710
+ mesh.name = "board-geom";
19711
+ meshes.push(mesh);
19712
+ }
19713
+ const createMeshesFromArray = (geomArray) => {
19714
+ if (geomArray) {
19715
+ geomArray.forEach((comp) => {
19716
+ const mesh = new THREE11.Mesh(
19717
+ comp.geometry,
19718
+ new THREE11.MeshStandardMaterial({
19719
+ color: comp.color,
19720
+ side: THREE11.DoubleSide,
19721
+ flatShading: true
19722
+ // Consistent with board
19723
+ })
19724
+ );
19725
+ mesh.name = comp.key;
19726
+ meshes.push(mesh);
19727
+ });
19728
+ }
19729
+ };
19730
+ createMeshesFromArray(geoms.platedHoles);
19731
+ createMeshesFromArray(geoms.smtPads);
19732
+ createMeshesFromArray(geoms.vias);
19733
+ return meshes;
19734
+ }
19735
+
19736
+ // src/utils/manifold/create-three-texture-meshes.ts
19737
+ import * as THREE12 from "three";
19738
+ function createTextureMeshes(textures, boardData, pcbThickness) {
19739
+ const meshes = [];
19740
+ if (!textures || !boardData || pcbThickness === null) return meshes;
19741
+ const createTexturePlane = (texture, yOffset, isBottomLayer, keySuffix) => {
19742
+ if (!texture) return null;
19743
+ const planeGeom = new THREE12.PlaneGeometry(boardData.width, boardData.height);
19744
+ const material = new THREE12.MeshBasicMaterial({
19745
+ map: texture,
19746
+ transparent: true,
19747
+ side: THREE12.DoubleSide,
19748
+ depthWrite: false
19749
+ // Important for layers to avoid z-fighting issues with board itself
19750
+ });
19751
+ const mesh = new THREE12.Mesh(planeGeom, material);
19752
+ mesh.position.set(boardData.center.x, boardData.center.y, yOffset);
19753
+ if (isBottomLayer) {
19754
+ mesh.rotation.set(Math.PI, 0, 0);
19755
+ }
19756
+ mesh.name = `${isBottomLayer ? "bottom" : "top"}-${keySuffix}-texture-plane`;
19757
+ return mesh;
19758
+ };
19759
+ const topTraceMesh = createTexturePlane(
19760
+ textures.topTrace,
19761
+ pcbThickness / 2 + 0.015,
19762
+ // Offset similar to CadViewerManifold
19763
+ false,
19764
+ "trace"
19765
+ );
19766
+ if (topTraceMesh) meshes.push(topTraceMesh);
19767
+ const topSilkscreenMesh = createTexturePlane(
19768
+ textures.topSilkscreen,
19769
+ pcbThickness / 2 + 0.017,
19770
+ // Slightly above trace
19771
+ false,
19772
+ "silkscreen"
19773
+ );
19774
+ if (topSilkscreenMesh) meshes.push(topSilkscreenMesh);
19775
+ const bottomTraceMesh = createTexturePlane(
19776
+ textures.bottomTrace,
19777
+ -pcbThickness / 2 - 0.015,
19778
+ true,
19779
+ "trace"
19780
+ );
19781
+ if (bottomTraceMesh) meshes.push(bottomTraceMesh);
19782
+ const bottomSilkscreenMesh = createTexturePlane(
19783
+ textures.bottomSilkscreen,
19784
+ -pcbThickness / 2 - 0.017,
19785
+ true,
19786
+ "silkscreen"
19787
+ );
19788
+ if (bottomSilkscreenMesh) meshes.push(bottomSilkscreenMesh);
19789
+ return meshes;
19790
+ }
19791
+
19669
19792
  // src/CadViewerManifold.tsx
19670
19793
  import { jsx as jsx11, jsxs as jsxs8 } from "react/jsx-runtime";
19671
19794
  var CadViewerManifold = ({
@@ -19689,20 +19812,20 @@ var CadViewerManifold = ({
19689
19812
  });
19690
19813
  }, []);
19691
19814
  const {
19692
- boardThreeGeom,
19693
- boardColor,
19694
- otherComponentGeoms,
19695
- topTraceTexture,
19696
- bottomTraceTexture,
19697
- topSilkscreenTexture,
19698
- bottomSilkscreenTexture,
19815
+ geoms,
19816
+ textures,
19699
19817
  pcbThickness,
19700
19818
  error: builderError,
19701
19819
  isLoading: builderIsLoading,
19702
19820
  boardData
19703
19821
  } = useManifoldBoardBuilder(manifoldJSModule, circuitJson);
19822
+ const geometryMeshes = useMemo7(() => createGeometryMeshes(geoms), [geoms]);
19823
+ const textureMeshes = useMemo7(
19824
+ () => createTextureMeshes(textures, boardData, pcbThickness),
19825
+ [textures, boardData, pcbThickness]
19826
+ );
19704
19827
  const cadComponents = useMemo7(
19705
- () => circuitJson ? su6(circuitJson).cad_component.list() : [],
19828
+ () => circuitJson ? su12(circuitJson).cad_component.list() : [],
19706
19829
  [circuitJson]
19707
19830
  );
19708
19831
  const initialCameraPosition = useMemo7(() => {
@@ -19750,12 +19873,9 @@ var CadViewerManifold = ({
19750
19873
  }
19751
19874
  );
19752
19875
  }
19753
- if (builderIsLoading || !boardData) {
19876
+ if (builderIsLoading || !boardData || !geoms || !textures) {
19754
19877
  return /* @__PURE__ */ jsx11("div", { style: { padding: "1em" }, children: "Processing board geometry..." });
19755
19878
  }
19756
- if (!boardThreeGeom) {
19757
- return /* @__PURE__ */ jsx11("div", { style: { padding: "1em" }, children: "Preparing board display..." });
19758
- }
19759
19879
  return /* @__PURE__ */ jsxs8(
19760
19880
  CadViewerContainer,
19761
19881
  {
@@ -19763,112 +19883,8 @@ var CadViewerManifold = ({
19763
19883
  autoRotateDisabled,
19764
19884
  clickToInteractEnabled,
19765
19885
  children: [
19766
- /* @__PURE__ */ jsx11("mesh", { geometry: boardThreeGeom, children: /* @__PURE__ */ jsx11(
19767
- "meshStandardMaterial",
19768
- {
19769
- color: boardColor,
19770
- side: THREE8.DoubleSide,
19771
- flatShading: true
19772
- }
19773
- ) }),
19774
- topTraceTexture && boardData && pcbThickness !== null && /* @__PURE__ */ jsxs8(
19775
- "mesh",
19776
- {
19777
- position: [
19778
- boardData.center.x,
19779
- boardData.center.y,
19780
- pcbThickness / 2 + 0.015
19781
- ],
19782
- children: [
19783
- /* @__PURE__ */ jsx11("planeGeometry", { args: [boardData.width, boardData.height] }),
19784
- /* @__PURE__ */ jsx11(
19785
- "meshBasicMaterial",
19786
- {
19787
- map: topTraceTexture,
19788
- transparent: true,
19789
- side: THREE8.DoubleSide,
19790
- depthWrite: false
19791
- }
19792
- )
19793
- ]
19794
- }
19795
- ),
19796
- topSilkscreenTexture && boardData && pcbThickness !== null && /* @__PURE__ */ jsxs8(
19797
- "mesh",
19798
- {
19799
- position: [
19800
- boardData.center.x,
19801
- boardData.center.y,
19802
- pcbThickness / 2 + 0.017
19803
- ],
19804
- children: [
19805
- /* @__PURE__ */ jsx11("planeGeometry", { args: [boardData.width, boardData.height] }),
19806
- /* @__PURE__ */ jsx11(
19807
- "meshBasicMaterial",
19808
- {
19809
- map: topSilkscreenTexture,
19810
- transparent: true,
19811
- side: THREE8.DoubleSide,
19812
- depthWrite: false
19813
- }
19814
- )
19815
- ]
19816
- }
19817
- ),
19818
- bottomTraceTexture && boardData && pcbThickness !== null && /* @__PURE__ */ jsxs8(
19819
- "mesh",
19820
- {
19821
- position: [
19822
- boardData.center.x,
19823
- boardData.center.y,
19824
- -pcbThickness / 2 - 0.015
19825
- ],
19826
- rotation: [Math.PI, 0, 0],
19827
- children: [
19828
- /* @__PURE__ */ jsx11("planeGeometry", { args: [boardData.width, boardData.height] }),
19829
- /* @__PURE__ */ jsx11(
19830
- "meshBasicMaterial",
19831
- {
19832
- map: bottomTraceTexture,
19833
- transparent: true,
19834
- side: THREE8.DoubleSide,
19835
- depthWrite: false
19836
- }
19837
- )
19838
- ]
19839
- }
19840
- ),
19841
- bottomSilkscreenTexture && boardData && pcbThickness !== null && /* @__PURE__ */ jsxs8(
19842
- "mesh",
19843
- {
19844
- position: [
19845
- boardData.center.x,
19846
- boardData.center.y,
19847
- -pcbThickness / 2 - 0.017
19848
- ],
19849
- rotation: [Math.PI, 0, 0],
19850
- children: [
19851
- /* @__PURE__ */ jsx11("planeGeometry", { args: [boardData.width, boardData.height] }),
19852
- /* @__PURE__ */ jsx11(
19853
- "meshBasicMaterial",
19854
- {
19855
- map: bottomSilkscreenTexture,
19856
- transparent: true,
19857
- side: THREE8.DoubleSide,
19858
- depthWrite: false
19859
- }
19860
- )
19861
- ]
19862
- }
19863
- ),
19864
- otherComponentGeoms.map((comp) => /* @__PURE__ */ jsx11("mesh", { geometry: comp.geometry, children: /* @__PURE__ */ jsx11(
19865
- "meshStandardMaterial",
19866
- {
19867
- color: comp.color,
19868
- side: THREE8.DoubleSide,
19869
- flatShading: true
19870
- }
19871
- ) }, comp.key)),
19886
+ geometryMeshes.map((mesh, index) => /* @__PURE__ */ jsx11("primitive", { object: mesh }, `${mesh.name}-${index}`)),
19887
+ textureMeshes.map((mesh, index) => /* @__PURE__ */ jsx11("primitive", { object: mesh }, `${mesh.name}-${index}`)),
19872
19888
  cadComponents.map((cad_component) => /* @__PURE__ */ jsx11(
19873
19889
  ThreeErrorBoundary,
19874
19890
  {
@@ -20031,12 +20047,12 @@ var CadViewer = (props) => {
20031
20047
 
20032
20048
  // src/convert-circuit-json-to-3d-svg.ts
20033
20049
  var import_debug = __toESM(require_browser(), 1);
20034
- import { su as su7 } from "@tscircuit/soup-util";
20035
- import * as THREE12 from "three";
20050
+ import { su as su13 } from "@tscircuit/soup-util";
20051
+ import * as THREE16 from "three";
20036
20052
  import { SVGRenderer } from "three/examples/jsm/renderers/SVGRenderer.js";
20037
20053
 
20038
20054
  // src/utils/create-geometry-from-polygons.ts
20039
- import * as THREE9 from "three";
20055
+ import * as THREE13 from "three";
20040
20056
  import { BufferGeometry as BufferGeometry4, Float32BufferAttribute as Float32BufferAttribute4 } from "three";
20041
20057
  function createGeometryFromPolygons(polygons) {
20042
20058
  const geometry = new BufferGeometry4();
@@ -20050,12 +20066,12 @@ function createGeometryFromPolygons(polygons) {
20050
20066
  ...polygon2.vertices[i + 1]
20051
20067
  // Third vertex
20052
20068
  );
20053
- const v1 = new THREE9.Vector3(...polygon2.vertices[0]);
20054
- const v2 = new THREE9.Vector3(...polygon2.vertices[i]);
20055
- const v3 = new THREE9.Vector3(...polygon2.vertices[i + 1]);
20056
- const normal = new THREE9.Vector3().crossVectors(
20057
- new THREE9.Vector3().subVectors(v2, v1),
20058
- new THREE9.Vector3().subVectors(v3, v1)
20069
+ const v1 = new THREE13.Vector3(...polygon2.vertices[0]);
20070
+ const v2 = new THREE13.Vector3(...polygon2.vertices[i]);
20071
+ const v3 = new THREE13.Vector3(...polygon2.vertices[i + 1]);
20072
+ const normal = new THREE13.Vector3().crossVectors(
20073
+ new THREE13.Vector3().subVectors(v2, v1),
20074
+ new THREE13.Vector3().subVectors(v3, v1)
20059
20075
  ).normalize();
20060
20076
  normals.push(
20061
20077
  normal.x,
@@ -20081,22 +20097,22 @@ import { Footprinter3d as Footprinter3d2 } from "jscad-electronics";
20081
20097
  import { convertCSGToThreeGeom as convertCSGToThreeGeom2 } from "jscad-fiber/three";
20082
20098
  import { createJSCADRenderer as createJSCADRenderer2 } from "jscad-fiber";
20083
20099
  import { executeJscadOperations as executeJscadOperations2, jscadPlanner as jscadPlanner2 } from "jscad-planner";
20084
- import * as THREE11 from "three";
20100
+ import * as THREE15 from "three";
20085
20101
 
20086
20102
  // src/utils/load-model.ts
20087
- import * as THREE10 from "three";
20103
+ import * as THREE14 from "three";
20088
20104
  import { OBJLoader as OBJLoader3 } from "three/examples/jsm/loaders/OBJLoader.js";
20089
20105
  import { STLLoader as STLLoader2 } from "three/examples/jsm/loaders/STLLoader.js";
20090
20106
  async function load3DModel(url) {
20091
20107
  if (url.endsWith(".stl")) {
20092
20108
  const loader = new STLLoader2();
20093
20109
  const geometry = await loader.loadAsync(url);
20094
- const material = new THREE10.MeshStandardMaterial({
20110
+ const material = new THREE14.MeshStandardMaterial({
20095
20111
  color: 8947848,
20096
20112
  metalness: 0.5,
20097
20113
  roughness: 0.5
20098
20114
  });
20099
- return new THREE10.Mesh(geometry, material);
20115
+ return new THREE14.Mesh(geometry, material);
20100
20116
  }
20101
20117
  if (url.endsWith(".obj")) {
20102
20118
  const loader = new OBJLoader3();
@@ -20123,9 +20139,9 @@ async function renderComponent(component, scene) {
20123
20139
  }
20124
20140
  if (component.rotation) {
20125
20141
  model.rotation.set(
20126
- THREE11.MathUtils.degToRad(component.rotation.x ?? 0),
20127
- THREE11.MathUtils.degToRad(component.rotation.y ?? 0),
20128
- THREE11.MathUtils.degToRad(component.rotation.z ?? 0)
20142
+ THREE15.MathUtils.degToRad(component.rotation.x ?? 0),
20143
+ THREE15.MathUtils.degToRad(component.rotation.y ?? 0),
20144
+ THREE15.MathUtils.degToRad(component.rotation.z ?? 0)
20129
20145
  );
20130
20146
  }
20131
20147
  scene.add(model);
@@ -20138,13 +20154,13 @@ async function renderComponent(component, scene) {
20138
20154
  component.model_jscad
20139
20155
  );
20140
20156
  const threeGeom = convertCSGToThreeGeom2(jscadObject);
20141
- const material2 = new THREE11.MeshStandardMaterial({
20157
+ const material2 = new THREE15.MeshStandardMaterial({
20142
20158
  color: 8947848,
20143
20159
  metalness: 0.5,
20144
20160
  roughness: 0.5,
20145
- side: THREE11.DoubleSide
20161
+ side: THREE15.DoubleSide
20146
20162
  });
20147
- const mesh2 = new THREE11.Mesh(threeGeom, material2);
20163
+ const mesh2 = new THREE15.Mesh(threeGeom, material2);
20148
20164
  if (component.position) {
20149
20165
  mesh2.position.set(
20150
20166
  component.position.x ?? 0,
@@ -20154,9 +20170,9 @@ async function renderComponent(component, scene) {
20154
20170
  }
20155
20171
  if (component.rotation) {
20156
20172
  mesh2.rotation.set(
20157
- THREE11.MathUtils.degToRad(component.rotation.x ?? 0),
20158
- THREE11.MathUtils.degToRad(component.rotation.y ?? 0),
20159
- THREE11.MathUtils.degToRad(component.rotation.z ?? 0)
20173
+ THREE15.MathUtils.degToRad(component.rotation.x ?? 0),
20174
+ THREE15.MathUtils.degToRad(component.rotation.y ?? 0),
20175
+ THREE15.MathUtils.degToRad(component.rotation.z ?? 0)
20160
20176
  );
20161
20177
  }
20162
20178
  scene.add(mesh2);
@@ -20169,13 +20185,13 @@ async function renderComponent(component, scene) {
20169
20185
  for (const operation of jscadOperations) {
20170
20186
  const jscadObject = executeJscadOperations2(import_modeling2.default, operation);
20171
20187
  const threeGeom = convertCSGToThreeGeom2(jscadObject);
20172
- const material2 = new THREE11.MeshStandardMaterial({
20188
+ const material2 = new THREE15.MeshStandardMaterial({
20173
20189
  color: 4473924,
20174
20190
  metalness: 0.2,
20175
20191
  roughness: 0.8,
20176
- side: THREE11.DoubleSide
20192
+ side: THREE15.DoubleSide
20177
20193
  });
20178
- const mesh2 = new THREE11.Mesh(threeGeom, material2);
20194
+ const mesh2 = new THREE15.Mesh(threeGeom, material2);
20179
20195
  if (component.position) {
20180
20196
  mesh2.position.set(
20181
20197
  component.position.x ?? 0,
@@ -20185,22 +20201,22 @@ async function renderComponent(component, scene) {
20185
20201
  }
20186
20202
  if (component.rotation) {
20187
20203
  mesh2.rotation.set(
20188
- THREE11.MathUtils.degToRad(component.rotation.x ?? 0),
20189
- THREE11.MathUtils.degToRad(component.rotation.y ?? 0),
20190
- THREE11.MathUtils.degToRad(component.rotation.z ?? 0)
20204
+ THREE15.MathUtils.degToRad(component.rotation.x ?? 0),
20205
+ THREE15.MathUtils.degToRad(component.rotation.y ?? 0),
20206
+ THREE15.MathUtils.degToRad(component.rotation.z ?? 0)
20191
20207
  );
20192
20208
  }
20193
20209
  scene.add(mesh2);
20194
20210
  }
20195
20211
  return;
20196
20212
  }
20197
- const geometry = new THREE11.BoxGeometry(0.5, 0.5, 0.5);
20198
- const material = new THREE11.MeshStandardMaterial({
20213
+ const geometry = new THREE15.BoxGeometry(0.5, 0.5, 0.5);
20214
+ const material = new THREE15.MeshStandardMaterial({
20199
20215
  color: 16711680,
20200
20216
  transparent: true,
20201
20217
  opacity: 0.25
20202
20218
  });
20203
- const mesh = new THREE11.Mesh(geometry, material);
20219
+ const mesh = new THREE15.Mesh(geometry, material);
20204
20220
  if (component.position) {
20205
20221
  mesh.position.set(
20206
20222
  component.position.x ?? 0,
@@ -20221,11 +20237,11 @@ async function convertCircuitJsonTo3dSvg(circuitJson, options = {}) {
20221
20237
  padding = 20,
20222
20238
  zoom = 1.5
20223
20239
  } = options;
20224
- const scene = new THREE12.Scene();
20240
+ const scene = new THREE16.Scene();
20225
20241
  const renderer = new SVGRenderer();
20226
20242
  renderer.setSize(width, height);
20227
- renderer.setClearColor(new THREE12.Color(backgroundColor), 1);
20228
- const camera = new THREE12.OrthographicCamera();
20243
+ renderer.setClearColor(new THREE16.Color(backgroundColor), 1);
20244
+ const camera = new THREE16.OrthographicCamera();
20229
20245
  const aspect = width / height;
20230
20246
  const frustumSize = 100;
20231
20247
  const halfFrustumSize = frustumSize / 2 / zoom;
@@ -20239,14 +20255,14 @@ async function convertCircuitJsonTo3dSvg(circuitJson, options = {}) {
20239
20255
  camera.position.set(position.x, position.y, position.z);
20240
20256
  camera.up.set(0, 1, 0);
20241
20257
  const lookAt = options.camera?.lookAt ?? { x: 0, y: 0, z: 0 };
20242
- camera.lookAt(new THREE12.Vector3(lookAt.x, lookAt.y, lookAt.z));
20258
+ camera.lookAt(new THREE16.Vector3(lookAt.x, lookAt.y, lookAt.z));
20243
20259
  camera.updateProjectionMatrix();
20244
- const ambientLight = new THREE12.AmbientLight(16777215, Math.PI / 2);
20260
+ const ambientLight = new THREE16.AmbientLight(16777215, Math.PI / 2);
20245
20261
  scene.add(ambientLight);
20246
- const pointLight = new THREE12.PointLight(16777215, Math.PI / 4);
20262
+ const pointLight = new THREE16.PointLight(16777215, Math.PI / 4);
20247
20263
  pointLight.position.set(-10, -10, 10);
20248
20264
  scene.add(pointLight);
20249
- const components = su7(circuitJson).cad_component.list();
20265
+ const components = su13(circuitJson).cad_component.list();
20250
20266
  for (const component of components) {
20251
20267
  await renderComponent(component, scene);
20252
20268
  }
@@ -20254,8 +20270,8 @@ async function convertCircuitJsonTo3dSvg(circuitJson, options = {}) {
20254
20270
  if (boardGeom) {
20255
20271
  for (const geom of boardGeom) {
20256
20272
  const geometry = createGeometryFromPolygons(geom.polygons);
20257
- const material = new THREE12.MeshStandardMaterial({
20258
- color: new THREE12.Color(
20273
+ const material = new THREE16.MeshStandardMaterial({
20274
+ color: new THREE16.Color(
20259
20275
  geom.color?.[0] ?? 0,
20260
20276
  geom.color?.[1] ?? 0,
20261
20277
  geom.color?.[2] ?? 0
@@ -20264,18 +20280,18 @@ async function convertCircuitJsonTo3dSvg(circuitJson, options = {}) {
20264
20280
  roughness: 0.8,
20265
20281
  opacity: 0.9,
20266
20282
  transparent: true,
20267
- side: THREE12.DoubleSide
20283
+ side: THREE16.DoubleSide
20268
20284
  });
20269
- const mesh = new THREE12.Mesh(geometry, material);
20285
+ const mesh = new THREE16.Mesh(geometry, material);
20270
20286
  scene.add(mesh);
20271
20287
  }
20272
20288
  }
20273
- const gridHelper = new THREE12.GridHelper(100, 100);
20289
+ const gridHelper = new THREE16.GridHelper(100, 100);
20274
20290
  gridHelper.rotation.x = Math.PI / 2;
20275
20291
  scene.add(gridHelper);
20276
- const box = new THREE12.Box3().setFromObject(scene);
20277
- const center = box.getCenter(new THREE12.Vector3());
20278
- const size = box.getSize(new THREE12.Vector3());
20292
+ const box = new THREE16.Box3().setFromObject(scene);
20293
+ const center = box.getCenter(new THREE16.Vector3());
20294
+ const size = box.getSize(new THREE16.Vector3());
20279
20295
  scene.position.sub(center);
20280
20296
  const maxDim = Math.max(size.x, size.y, size.z);
20281
20297
  if (maxDim > 0) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tscircuit/3d-viewer",
3
- "version": "0.0.232",
3
+ "version": "0.0.233",
4
4
  "main": "./dist/index.js",
5
5
  "module": "./dist/index.js",
6
6
  "type": "module",