@tscircuit/3d-viewer 0.0.530 → 0.0.532

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +221 -121
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -14432,7 +14432,7 @@ var require_browser = __commonJS({
14432
14432
  });
14433
14433
 
14434
14434
  // src/CadViewer.tsx
14435
- import { useState as useState36, useCallback as useCallback21, useRef as useRef26, useEffect as useEffect44 } from "react";
14435
+ import { useState as useState36, useCallback as useCallback21, useRef as useRef26, useEffect as useEffect45 } from "react";
14436
14436
  import * as THREE39 from "three";
14437
14437
 
14438
14438
  // src/CadViewerJscad.tsx
@@ -14441,7 +14441,7 @@ import { forwardRef as forwardRef3, useMemo as useMemo20 } from "react";
14441
14441
 
14442
14442
  // src/AnyCadComponent.tsx
14443
14443
  import { su as su2 } from "@tscircuit/circuit-json-util";
14444
- import { useCallback as useCallback3, useMemo as useMemo8, useState as useState7 } from "react";
14444
+ import { useCallback as useCallback3, useEffect as useEffect11, useMemo as useMemo8, useState as useState7 } from "react";
14445
14445
 
14446
14446
  // src/contexts/LayerVisibilityContext.tsx
14447
14447
  import {
@@ -28577,8 +28577,35 @@ var resolveModelUrl = (modelUrl, resolveStaticAsset) => {
28577
28577
  // src/utils/tuple.ts
28578
28578
  var tuple = (...args) => args;
28579
28579
 
28580
+ // src/three-components/ThreeErrorBoundary.tsx
28581
+ import React6 from "react";
28582
+ var ThreeErrorBoundary = class extends React6.Component {
28583
+ constructor(props) {
28584
+ super(props);
28585
+ this.state = { hasError: false, error: null };
28586
+ }
28587
+ static getDerivedStateFromError(error) {
28588
+ return { hasError: true, error };
28589
+ }
28590
+ render() {
28591
+ if (this.state.hasError && this.state.error) {
28592
+ return this.props.fallback({ error: this.state.error });
28593
+ }
28594
+ return this.props.children;
28595
+ }
28596
+ };
28597
+
28580
28598
  // src/AnyCadComponent.tsx
28581
28599
  import { Fragment as Fragment4, jsx as jsx10, jsxs as jsxs2 } from "react/jsx-runtime";
28600
+ var ModelLoadErrorReporter = ({
28601
+ error,
28602
+ onError
28603
+ }) => {
28604
+ useEffect11(() => {
28605
+ onError(error);
28606
+ }, [error, onError]);
28607
+ return null;
28608
+ };
28582
28609
  var AnyCadComponent = ({
28583
28610
  cad_component,
28584
28611
  circuitJson,
@@ -28625,6 +28652,12 @@ var AnyCadComponent = ({
28625
28652
  const stepUrl = resolveModelUrlWithStaticResolver(
28626
28653
  cad_component.model_step_url
28627
28654
  );
28655
+ const [fallbackModelIndex, setFallbackModelIndex] = useState7(0);
28656
+ const [lastModelError, setLastModelError] = useState7(null);
28657
+ useEffect11(() => {
28658
+ setFallbackModelIndex(0);
28659
+ setLastModelError(null);
28660
+ }, [cad_component.cad_component_id, url, gltfUrl, stepUrl]);
28628
28661
  const pcbComponent = circuitJson.find(
28629
28662
  (elm) => elm.type === "pcb_component" && elm.source_component_id === cad_component.source_component_id
28630
28663
  );
@@ -28651,50 +28684,104 @@ var AnyCadComponent = ({
28651
28684
  }
28652
28685
  return [cad_component.position.x, cad_component.position.y, z18];
28653
28686
  }, [cad_component.position, layer, pcbThickness]);
28687
+ const fallbackModelComponents = useMemo8(() => {
28688
+ const components = [];
28689
+ if (url) {
28690
+ components.push(
28691
+ /* @__PURE__ */ jsx10(
28692
+ MixedStlModel,
28693
+ {
28694
+ url,
28695
+ position: adjustedPosition,
28696
+ rotation: rotationOffset,
28697
+ scale: cad_component.model_unit_to_mm_scale_factor,
28698
+ onHover: handleHover,
28699
+ onUnhover: handleUnhover,
28700
+ isHovered,
28701
+ isTranslucent: cad_component.show_as_translucent_model
28702
+ },
28703
+ `${cad_component.cad_component_id}-mixed-${url}`
28704
+ )
28705
+ );
28706
+ }
28707
+ if (gltfUrl) {
28708
+ components.push(
28709
+ /* @__PURE__ */ jsx10(
28710
+ GltfModel,
28711
+ {
28712
+ gltfUrl,
28713
+ position: adjustedPosition,
28714
+ rotation: rotationOffset,
28715
+ scale: cad_component.model_unit_to_mm_scale_factor,
28716
+ onHover: handleHover,
28717
+ onUnhover: handleUnhover,
28718
+ isHovered,
28719
+ isTranslucent: cad_component.show_as_translucent_model
28720
+ },
28721
+ `${cad_component.cad_component_id}-gltf-${gltfUrl}`
28722
+ )
28723
+ );
28724
+ }
28725
+ if (stepUrl && !cad_component.model_jscad && !cad_component.footprinter_string) {
28726
+ components.push(
28727
+ /* @__PURE__ */ jsx10(
28728
+ StepModel,
28729
+ {
28730
+ stepUrl,
28731
+ position: adjustedPosition,
28732
+ rotation: rotationOffset,
28733
+ scale: cad_component.model_unit_to_mm_scale_factor,
28734
+ onHover: handleHover,
28735
+ onUnhover: handleUnhover,
28736
+ isHovered,
28737
+ isTranslucent: cad_component.show_as_translucent_model
28738
+ },
28739
+ `${cad_component.cad_component_id}-step-${stepUrl}`
28740
+ )
28741
+ );
28742
+ }
28743
+ return components;
28744
+ }, [
28745
+ adjustedPosition,
28746
+ cad_component.cad_component_id,
28747
+ cad_component.footprinter_string,
28748
+ cad_component.model_jscad,
28749
+ cad_component.model_unit_to_mm_scale_factor,
28750
+ cad_component.show_as_translucent_model,
28751
+ gltfUrl,
28752
+ handleHover,
28753
+ handleUnhover,
28754
+ isHovered,
28755
+ rotationOffset,
28756
+ stepUrl,
28757
+ url
28758
+ ]);
28654
28759
  let modelComponent = null;
28655
- if (url) {
28656
- modelComponent = /* @__PURE__ */ jsx10(
28657
- MixedStlModel,
28658
- {
28659
- url,
28660
- position: adjustedPosition,
28661
- rotation: rotationOffset,
28662
- scale: cad_component.model_unit_to_mm_scale_factor,
28663
- onHover: handleHover,
28664
- onUnhover: handleUnhover,
28665
- isHovered,
28666
- isTranslucent: cad_component.show_as_translucent_model
28667
- },
28668
- cad_component.cad_component_id
28669
- );
28670
- } else if (gltfUrl) {
28760
+ if (fallbackModelComponents.length > 0) {
28761
+ if (fallbackModelIndex >= fallbackModelComponents.length) {
28762
+ if (lastModelError) {
28763
+ throw lastModelError;
28764
+ }
28765
+ return null;
28766
+ }
28671
28767
  modelComponent = /* @__PURE__ */ jsx10(
28672
- GltfModel,
28768
+ ThreeErrorBoundary,
28673
28769
  {
28674
- gltfUrl,
28675
- position: adjustedPosition,
28676
- rotation: rotationOffset,
28677
- scale: cad_component.model_unit_to_mm_scale_factor,
28678
- onHover: handleHover,
28679
- onUnhover: handleUnhover,
28680
- isHovered,
28681
- isTranslucent: cad_component.show_as_translucent_model
28770
+ fallback: ({ error }) => /* @__PURE__ */ jsx10(
28771
+ ModelLoadErrorReporter,
28772
+ {
28773
+ error,
28774
+ onError: (nextError) => {
28775
+ setLastModelError(nextError);
28776
+ setFallbackModelIndex(
28777
+ (current) => Math.max(current, fallbackModelIndex + 1)
28778
+ );
28779
+ }
28780
+ }
28781
+ ),
28782
+ children: fallbackModelComponents[fallbackModelIndex]
28682
28783
  },
28683
- cad_component.cad_component_id
28684
- );
28685
- } else if (stepUrl && !cad_component.model_jscad && !cad_component.footprinter_string) {
28686
- modelComponent = /* @__PURE__ */ jsx10(
28687
- StepModel,
28688
- {
28689
- stepUrl,
28690
- position: adjustedPosition,
28691
- rotation: rotationOffset,
28692
- scale: cad_component.model_unit_to_mm_scale_factor,
28693
- onHover: handleHover,
28694
- onUnhover: handleUnhover,
28695
- isHovered,
28696
- isTranslucent: cad_component.show_as_translucent_model
28697
- }
28784
+ `${cad_component.cad_component_id}-fallback-${fallbackModelIndex}`
28698
28785
  );
28699
28786
  } else if (cad_component.model_jscad) {
28700
28787
  modelComponent = /* @__PURE__ */ jsx10(
@@ -28763,13 +28850,13 @@ var AnyCadComponent = ({
28763
28850
  };
28764
28851
 
28765
28852
  // src/CadViewerContainer.tsx
28766
- import { forwardRef as forwardRef2, useEffect as useEffect17, useMemo as useMemo14, useState as useState10 } from "react";
28853
+ import { forwardRef as forwardRef2, useEffect as useEffect18, useMemo as useMemo14, useState as useState10 } from "react";
28767
28854
  import * as THREE16 from "three";
28768
28855
 
28769
28856
  // package.json
28770
28857
  var package_default = {
28771
28858
  name: "@tscircuit/3d-viewer",
28772
- version: "0.0.529",
28859
+ version: "0.0.531",
28773
28860
  main: "./dist/index.js",
28774
28861
  module: "./dist/index.js",
28775
28862
  type: "module",
@@ -28847,7 +28934,7 @@ var package_default = {
28847
28934
  // src/react-three/Canvas.tsx
28848
28935
  import {
28849
28936
  useRef as useRef4,
28850
- useEffect as useEffect11,
28937
+ useEffect as useEffect12,
28851
28938
  useState as useState9,
28852
28939
  useCallback as useCallback5,
28853
28940
  forwardRef,
@@ -29114,7 +29201,7 @@ var Canvas = forwardRef(
29114
29201
  }
29115
29202
  const rootObject = useRef4(new THREE10.Object3D());
29116
29203
  useImperativeHandle(ref, () => rootObject.current);
29117
- useEffect11(() => {
29204
+ useEffect12(() => {
29118
29205
  if (!mountRef.current) return;
29119
29206
  removeExistingCanvases(mountRef.current);
29120
29207
  const renderer = new THREE10.WebGLRenderer({ antialias: true, alpha: true });
@@ -29214,7 +29301,7 @@ var Canvas = forwardRef(
29214
29301
  );
29215
29302
 
29216
29303
  // src/react-three/OrbitControls.tsx
29217
- import { useEffect as useEffect12, useMemo as useMemo11 } from "react";
29304
+ import { useEffect as useEffect13, useMemo as useMemo11 } from "react";
29218
29305
  import * as THREE11 from "three";
29219
29306
  import { OrbitControls as ThreeOrbitControls } from "three/examples/jsm/controls/OrbitControls.js";
29220
29307
  var OrbitControls = ({
@@ -29234,11 +29321,11 @@ var OrbitControls = ({
29234
29321
  if (!camera || !renderer) return null;
29235
29322
  return new ThreeOrbitControls(camera, renderer.domElement);
29236
29323
  }, [camera, renderer]);
29237
- useEffect12(() => {
29324
+ useEffect13(() => {
29238
29325
  onControlsChange?.(controls ?? null);
29239
29326
  return () => onControlsChange?.(null);
29240
29327
  }, [controls, onControlsChange]);
29241
- useEffect12(() => {
29328
+ useEffect13(() => {
29242
29329
  if (!controls) return;
29243
29330
  const handleChange = () => {
29244
29331
  onControlsChange?.(controls);
@@ -29248,7 +29335,7 @@ var OrbitControls = ({
29248
29335
  controls.removeEventListener("change", handleChange);
29249
29336
  };
29250
29337
  }, [controls, onControlsChange]);
29251
- useEffect12(() => {
29338
+ useEffect13(() => {
29252
29339
  if (!controls) return;
29253
29340
  controls.autoRotate = autoRotate || false;
29254
29341
  controls.autoRotateSpeed = autoRotateSpeed || 1;
@@ -29281,14 +29368,14 @@ var OrbitControls = ({
29281
29368
  dampingFactor,
29282
29369
  target
29283
29370
  ]);
29284
- useEffect12(() => {
29371
+ useEffect13(() => {
29285
29372
  if (!controls || !onStart) return;
29286
29373
  controls.addEventListener("start", onStart);
29287
29374
  return () => {
29288
29375
  controls.removeEventListener("start", onStart);
29289
29376
  };
29290
29377
  }, [controls, onStart]);
29291
- useEffect12(() => {
29378
+ useEffect13(() => {
29292
29379
  if (!controls) return;
29293
29380
  return () => {
29294
29381
  controls.dispose();
@@ -29301,7 +29388,7 @@ var OrbitControls = ({
29301
29388
  };
29302
29389
 
29303
29390
  // src/react-three/Grid.tsx
29304
- import { useEffect as useEffect13, useMemo as useMemo12 } from "react";
29391
+ import { useEffect as useEffect14, useMemo as useMemo12 } from "react";
29305
29392
  import * as THREE12 from "three";
29306
29393
  var vertexShader = `
29307
29394
  varying vec3 worldPosition;
@@ -29376,7 +29463,7 @@ var Grid = ({
29376
29463
  gridMesh.position.set(camera.position.x, camera.position.y, 0);
29377
29464
  }
29378
29465
  });
29379
- useEffect13(() => {
29466
+ useEffect14(() => {
29380
29467
  if (!scene || !gridMesh) return;
29381
29468
  scene.add(gridMesh);
29382
29469
  return () => {
@@ -29393,7 +29480,7 @@ var Grid = ({
29393
29480
  };
29394
29481
 
29395
29482
  // src/react-three/Lights.tsx
29396
- import { useEffect as useEffect14, useMemo as useMemo13 } from "react";
29483
+ import { useEffect as useEffect15, useMemo as useMemo13 } from "react";
29397
29484
  import * as THREE13 from "three";
29398
29485
  var Lights = () => {
29399
29486
  const { scene } = useThree();
@@ -29407,7 +29494,7 @@ var Lights = () => {
29407
29494
  light.decay = 0;
29408
29495
  return light;
29409
29496
  }, []);
29410
- useEffect14(() => {
29497
+ useEffect15(() => {
29411
29498
  if (!scene) return;
29412
29499
  scene.add(ambientLight);
29413
29500
  scene.add(pointLight);
@@ -29420,7 +29507,7 @@ var Lights = () => {
29420
29507
  };
29421
29508
 
29422
29509
  // src/hooks/cameraAnimation.ts
29423
- import { useCallback as useCallback6, useEffect as useEffect15, useRef as useRef5 } from "react";
29510
+ import { useCallback as useCallback6, useEffect as useEffect16, useRef as useRef5 } from "react";
29424
29511
  import * as THREE14 from "three";
29425
29512
  var easeInOutCubic = (t) => t < 0.5 ? 4 * t * t * t : 1 - Math.pow(-2 * t + 2, 3) / 2;
29426
29513
  var CameraAnimatorWithContext = () => {
@@ -29492,7 +29579,7 @@ var CameraAnimatorWithContext = () => {
29492
29579
  },
29493
29580
  [animateTo, getPresetConfig]
29494
29581
  );
29495
- useEffect15(() => {
29582
+ useEffect16(() => {
29496
29583
  if (!mainCameraRef.current) return;
29497
29584
  const controller = {
29498
29585
  animateTo,
@@ -29605,7 +29692,7 @@ var useCameraSession = () => {
29605
29692
  };
29606
29693
 
29607
29694
  // src/three-components/OrientationCubeCanvas.tsx
29608
- import { useEffect as useEffect16, useRef as useRef7 } from "react";
29695
+ import { useEffect as useEffect17, useRef as useRef7 } from "react";
29609
29696
  import * as THREE15 from "three";
29610
29697
  import { Text as TroikaText } from "troika-three-text";
29611
29698
  import { jsx as jsx13 } from "react/jsx-runtime";
@@ -29626,7 +29713,7 @@ var OrientationCubeCanvas = () => {
29626
29713
  const sceneRef = useRef7(null);
29627
29714
  const cameraRef = useRef7(null);
29628
29715
  const animationFrameRef = useRef7(null);
29629
- useEffect16(() => {
29716
+ useEffect17(() => {
29630
29717
  if (!containerRef.current) return;
29631
29718
  const canvas = document.createElement("canvas");
29632
29719
  canvasRef.current = canvas;
@@ -29793,7 +29880,7 @@ var CadViewerContainer = forwardRef2(
29793
29880
  handleCameraCreated,
29794
29881
  handleControlsChange: handleSessionControlsChange
29795
29882
  } = useCameraSession();
29796
- useEffect17(() => {
29883
+ useEffect18(() => {
29797
29884
  if (onCameraControllerReady) {
29798
29885
  onCameraControllerReady(controller);
29799
29886
  }
@@ -29930,12 +30017,12 @@ var useConvertChildrenToCircuitJson = (children) => {
29930
30017
  };
29931
30018
 
29932
30019
  // src/hooks/use-stls-from-geom.ts
29933
- import { useState as useState11, useEffect as useEffect18 } from "react";
30020
+ import { useState as useState11, useEffect as useEffect19 } from "react";
29934
30021
  import stlSerializer from "@jscad/stl-serializer";
29935
30022
  var useStlsFromGeom = (geom) => {
29936
30023
  const [stls, setStls] = useState11([]);
29937
30024
  const [loading, setLoading] = useState11(true);
29938
- useEffect18(() => {
30025
+ useEffect19(() => {
29939
30026
  if (!geom) return;
29940
30027
  const generateStls = async () => {
29941
30028
  setLoading(true);
@@ -29964,7 +30051,7 @@ var useStlsFromGeom = (geom) => {
29964
30051
  };
29965
30052
 
29966
30053
  // src/hooks/useBoardGeomBuilder.ts
29967
- import { useState as useState12, useEffect as useEffect19, useRef as useRef8 } from "react";
30054
+ import { useState as useState12, useEffect as useEffect20, useRef as useRef8 } from "react";
29968
30055
 
29969
30056
  // src/soup-to-3d/index.ts
29970
30057
  var import_primitives2 = __toESM(require_primitives(), 1);
@@ -31309,7 +31396,7 @@ var BoardGeomBuilder = class {
31309
31396
  var useBoardGeomBuilder = (circuitJson) => {
31310
31397
  const [boardGeom, setBoardGeom] = useState12(null);
31311
31398
  const isProcessingRef = useRef8(false);
31312
- useEffect19(() => {
31399
+ useEffect20(() => {
31313
31400
  let isCancelled = false;
31314
31401
  if (!circuitJson) {
31315
31402
  setBoardGeom(null);
@@ -31355,10 +31442,10 @@ var useBoardGeomBuilder = (circuitJson) => {
31355
31442
  };
31356
31443
 
31357
31444
  // src/three-components/Error3d.tsx
31358
- import { useState as useState13, useCallback as useCallback8, useEffect as useEffect21, useMemo as useMemo17 } from "react";
31445
+ import { useState as useState13, useCallback as useCallback8, useEffect as useEffect22, useMemo as useMemo17 } from "react";
31359
31446
 
31360
31447
  // src/react-three/Text.tsx
31361
- import { useEffect as useEffect20, useMemo as useMemo16 } from "react";
31448
+ import { useEffect as useEffect21, useMemo as useMemo16 } from "react";
31362
31449
  import { Text as TroikaText2 } from "troika-three-text";
31363
31450
  var Text = ({
31364
31451
  children,
@@ -31398,7 +31485,7 @@ var Text = ({
31398
31485
  anchorY,
31399
31486
  depthOffset
31400
31487
  ]);
31401
- useEffect20(() => {
31488
+ useEffect21(() => {
31402
31489
  const parentObject = parent || rootObject;
31403
31490
  if (!parentObject || !mesh) return;
31404
31491
  parentObject.add(mesh);
@@ -31449,7 +31536,7 @@ var Error3d = ({
31449
31536
  g.position.fromArray(position);
31450
31537
  return g;
31451
31538
  }, [position]);
31452
- useEffect21(() => {
31539
+ useEffect22(() => {
31453
31540
  if (!rootObject) return;
31454
31541
  rootObject.add(group);
31455
31542
  return () => {
@@ -31518,7 +31605,7 @@ var ErrorBox = ({ parent }) => {
31518
31605
  m.rotation.fromArray([Math.PI / 4, Math.PI / 4, 0]);
31519
31606
  return m;
31520
31607
  }, []);
31521
- useEffect21(() => {
31608
+ useEffect22(() => {
31522
31609
  parent.add(mesh);
31523
31610
  return () => {
31524
31611
  parent.remove(mesh);
@@ -31528,7 +31615,7 @@ var ErrorBox = ({ parent }) => {
31528
31615
  };
31529
31616
 
31530
31617
  // src/three-components/STLModel.tsx
31531
- import { useState as useState14, useEffect as useEffect22, useMemo as useMemo18 } from "react";
31618
+ import { useState as useState14, useEffect as useEffect23, useMemo as useMemo18 } from "react";
31532
31619
  import * as THREE18 from "three";
31533
31620
  import { STLLoader } from "three-stdlib";
31534
31621
  function STLModel({
@@ -31541,7 +31628,7 @@ function STLModel({
31541
31628
  }) {
31542
31629
  const { rootObject } = useThree();
31543
31630
  const [geom, setGeom] = useState14(null);
31544
- useEffect22(() => {
31631
+ useEffect23(() => {
31545
31632
  const loader = new STLLoader();
31546
31633
  if (stlData) {
31547
31634
  try {
@@ -31574,7 +31661,7 @@ function STLModel({
31574
31661
  createdMesh.renderOrder = isBoardLayer ? -1 : 1;
31575
31662
  return createdMesh;
31576
31663
  }, [geom, color, opacity, layerType]);
31577
- useEffect22(() => {
31664
+ useEffect23(() => {
31578
31665
  if (!rootObject || !mesh) return;
31579
31666
  rootObject.add(mesh);
31580
31667
  return () => {
@@ -31625,27 +31712,9 @@ function VisibleSTLModel({
31625
31712
  );
31626
31713
  }
31627
31714
 
31628
- // src/three-components/ThreeErrorBoundary.tsx
31629
- import React11 from "react";
31630
- var ThreeErrorBoundary = class extends React11.Component {
31631
- constructor(props) {
31632
- super(props);
31633
- this.state = { hasError: false, error: null };
31634
- }
31635
- static getDerivedStateFromError(error) {
31636
- return { hasError: true, error };
31637
- }
31638
- render() {
31639
- if (this.state.hasError && this.state.error) {
31640
- return this.props.fallback({ error: this.state.error });
31641
- }
31642
- return this.props.children;
31643
- }
31644
- };
31645
-
31646
31715
  // src/three-components/JscadBoardTextures.tsx
31647
31716
  import { su as su8 } from "@tscircuit/circuit-json-util";
31648
- import { useEffect as useEffect23, useMemo as useMemo19 } from "react";
31717
+ import { useEffect as useEffect24, useMemo as useMemo19 } from "react";
31649
31718
 
31650
31719
  // src/textures/create-combined-board-textures.ts
31651
31720
  import * as THREE28 from "three";
@@ -31916,10 +31985,43 @@ function createPanelOutlineTextureForLayer({
31916
31985
  // src/utils/trace-texture.ts
31917
31986
  import * as THREE21 from "three";
31918
31987
  import { CircuitToCanvasDrawer } from "circuit-to-canvas";
31919
- import { su as su7 } from "@tscircuit/circuit-json-util";
31988
+ import { getElementRenderLayers, su as su7 } from "@tscircuit/circuit-json-util";
31989
+
31990
+ // src/utils/trace-layer-segments.ts
31920
31991
  function isWireRoutePoint(point) {
31921
31992
  return point && point.route_type === "wire" && typeof point.layer === "string" && typeof point.width === "number";
31922
31993
  }
31994
+ function splitTraceIntoLayerSegments(trace, layer) {
31995
+ const segments = [];
31996
+ let currentRoute = [];
31997
+ const pushCurrentSegment = () => {
31998
+ if (currentRoute.length < 2) {
31999
+ currentRoute = [];
32000
+ return;
32001
+ }
32002
+ segments.push({
32003
+ ...trace,
32004
+ pcb_trace_id: `${trace.pcb_trace_id}_${layer}_${segments.length}`,
32005
+ route: currentRoute
32006
+ });
32007
+ currentRoute = [];
32008
+ };
32009
+ for (const point of trace.route) {
32010
+ if (!isWireRoutePoint(point)) {
32011
+ pushCurrentSegment();
32012
+ continue;
32013
+ }
32014
+ if (point.layer !== layer) {
32015
+ pushCurrentSegment();
32016
+ continue;
32017
+ }
32018
+ currentRoute.push(point);
32019
+ }
32020
+ pushCurrentSegment();
32021
+ return segments;
32022
+ }
32023
+
32024
+ // src/utils/trace-texture.ts
31923
32025
  function createTraceTextureForLayer({
31924
32026
  layer,
31925
32027
  circuitJson,
@@ -31931,9 +32033,7 @@ function createTraceTextureForLayer({
31931
32033
  const pcbVias = su7(circuitJson).pcb_via.list();
31932
32034
  const pcbPlatedHoles = su7(circuitJson).pcb_plated_hole.list();
31933
32035
  const pcbRenderLayer = layer === "top" ? "top_copper" : "bottom_copper";
31934
- const tracesOnLayer = pcbTraces.filter(
31935
- (t) => t.route.some((p) => isWireRoutePoint(p) && p.layer === layer)
31936
- );
32036
+ const tracesOnLayer = pcbTraces.filter((trace) => getElementRenderLayers(trace).includes(pcbRenderLayer)).flatMap((trace) => splitTraceIntoLayerSegments(trace, layer));
31937
32037
  if (tracesOnLayer.length === 0) return null;
31938
32038
  const platedHolesOnLayer = pcbPlatedHoles.filter(
31939
32039
  (hole) => hole.layers.includes(layer)
@@ -33188,7 +33288,7 @@ function JscadBoardTextures({
33188
33288
  visibility
33189
33289
  });
33190
33290
  }, [circuitJson, boardData, traceTextureResolution, visibility]);
33191
- useEffect23(() => {
33291
+ useEffect24(() => {
33192
33292
  if (!rootObject || !boardData || !textures) return;
33193
33293
  const meshes = [];
33194
33294
  const disposeTextureMaterial = (material) => {
@@ -33510,12 +33610,12 @@ var CadViewerJscad = forwardRef3(
33510
33610
 
33511
33611
  // src/CadViewerManifold.tsx
33512
33612
  import { su as su17 } from "@tscircuit/circuit-json-util";
33513
- import { useEffect as useEffect25, useMemo as useMemo22, useState as useState16 } from "react";
33613
+ import { useEffect as useEffect26, useMemo as useMemo22, useState as useState16 } from "react";
33514
33614
  import * as THREE38 from "three";
33515
33615
 
33516
33616
  // src/hooks/useManifoldBoardBuilder.ts
33517
33617
  import { su as su16 } from "@tscircuit/circuit-json-util";
33518
- import { useEffect as useEffect24, useMemo as useMemo21, useRef as useRef9, useState as useState15 } from "react";
33618
+ import { useEffect as useEffect25, useMemo as useMemo21, useRef as useRef9, useState as useState15 } from "react";
33519
33619
  import * as THREE35 from "three";
33520
33620
 
33521
33621
  // src/utils/manifold/create-manifold-board.ts
@@ -34661,7 +34761,7 @@ var useManifoldBoardBuilder = (manifoldJSModule, circuitJson, visibility) => {
34661
34761
  if (!boardData) return TRACE_TEXTURE_RESOLUTION;
34662
34762
  return getLayerTextureResolution(boardData, TRACE_TEXTURE_RESOLUTION);
34663
34763
  }, [boardData]);
34664
- useEffect24(() => {
34764
+ useEffect25(() => {
34665
34765
  if (!manifoldJSModule || !boardData) {
34666
34766
  setGeoms(null);
34667
34767
  setPcbThickness(null);
@@ -34985,7 +35085,7 @@ var BoardMeshes = ({
34985
35085
  material.dispose();
34986
35086
  }
34987
35087
  };
34988
- useEffect25(() => {
35088
+ useEffect26(() => {
34989
35089
  if (!rootObject) return;
34990
35090
  geometryMeshes.forEach((mesh) => {
34991
35091
  let shouldShow = true;
@@ -35007,7 +35107,7 @@ var BoardMeshes = ({
35007
35107
  });
35008
35108
  };
35009
35109
  }, [rootObject, geometryMeshes, visibility]);
35010
- useEffect25(() => {
35110
+ useEffect26(() => {
35011
35111
  if (!rootObject) return;
35012
35112
  textureMeshes.forEach((mesh) => {
35013
35113
  rootObject.add(mesh);
@@ -35041,7 +35141,7 @@ var CadViewerManifold = ({
35041
35141
  const [manifoldJSModule, setManifoldJSModule] = useState16(null);
35042
35142
  const [manifoldLoadingError, setManifoldLoadingError] = useState16(null);
35043
35143
  const { visibility } = useLayerVisibility();
35044
- useEffect25(() => {
35144
+ useEffect26(() => {
35045
35145
  if (window.ManifoldModule && typeof window.ManifoldModule === "object" && window.ManifoldModule.setup) {
35046
35146
  setManifoldJSModule(window.ManifoldModule);
35047
35147
  return;
@@ -35227,7 +35327,7 @@ try {
35227
35327
  var CadViewerManifold_default = CadViewerManifold;
35228
35328
 
35229
35329
  // src/hooks/useContextMenu.ts
35230
- import { useState as useState17, useCallback as useCallback9, useRef as useRef10, useEffect as useEffect26 } from "react";
35330
+ import { useState as useState17, useCallback as useCallback9, useRef as useRef10, useEffect as useEffect27 } from "react";
35231
35331
  var useContextMenu = ({ containerRef }) => {
35232
35332
  const [menuVisible, setMenuVisible] = useState17(false);
35233
35333
  const [menuPos, setMenuPos] = useState17({
@@ -35342,7 +35442,7 @@ var useContextMenu = ({ containerRef }) => {
35342
35442
  }
35343
35443
  setMenuVisible(false);
35344
35444
  }, []);
35345
- useEffect26(() => {
35445
+ useEffect27(() => {
35346
35446
  if (menuVisible) {
35347
35447
  document.addEventListener("mousedown", handleClickAway);
35348
35448
  document.addEventListener("touchstart", handleClickAway);
@@ -35445,7 +35545,7 @@ var useGlobalDownloadGltf = () => {
35445
35545
  };
35446
35546
 
35447
35547
  // src/hooks/useRegisteredHotkey.ts
35448
- import { useEffect as useEffect27, useMemo as useMemo23, useRef as useRef11, useState as useState18 } from "react";
35548
+ import { useEffect as useEffect28, useMemo as useMemo23, useRef as useRef11, useState as useState18 } from "react";
35449
35549
  var hotkeyRegistry = /* @__PURE__ */ new Map();
35450
35550
  var subscribers = /* @__PURE__ */ new Set();
35451
35551
  var isListenerAttached = false;
@@ -35576,7 +35676,7 @@ var useRegisteredHotkey = (id, handler, metadata) => {
35576
35676
  }),
35577
35677
  [metadata.shortcut, metadata.description]
35578
35678
  );
35579
- useEffect27(() => {
35679
+ useEffect28(() => {
35580
35680
  const registration = {
35581
35681
  id,
35582
35682
  ...normalizedMetadata,
@@ -35592,7 +35692,7 @@ var useHotkeyRegistry = () => {
35592
35692
  const [entries, setEntries] = useState18(
35593
35693
  () => Array.from(hotkeyRegistry.values())
35594
35694
  );
35595
- useEffect27(() => subscribeToRegistry(setEntries), []);
35695
+ useEffect28(() => subscribeToRegistry(setEntries), []);
35596
35696
  return entries;
35597
35697
  };
35598
35698
  var registerHotkeyViewer = (element) => {
@@ -41728,7 +41828,7 @@ var ContextMenu = ({
41728
41828
  };
41729
41829
 
41730
41830
  // src/components/KeyboardShortcutsDialog.tsx
41731
- import { useEffect as useEffect43, useMemo as useMemo29, useRef as useRef25, useState as useState35 } from "react";
41831
+ import { useEffect as useEffect44, useMemo as useMemo29, useRef as useRef25, useState as useState35 } from "react";
41732
41832
  import { jsx as jsx37, jsxs as jsxs10 } from "react/jsx-runtime";
41733
41833
  var KeyboardShortcutsDialog = ({
41734
41834
  open,
@@ -41737,7 +41837,7 @@ var KeyboardShortcutsDialog = ({
41737
41837
  const [query, setQuery] = useState35("");
41738
41838
  const inputRef = useRef25(null);
41739
41839
  const hotkeys = useHotkeyRegistry();
41740
- useEffect43(() => {
41840
+ useEffect44(() => {
41741
41841
  if (!open) return void 0;
41742
41842
  const handleKeyDown = (event) => {
41743
41843
  if (event.key === "Escape") {
@@ -41748,7 +41848,7 @@ var KeyboardShortcutsDialog = ({
41748
41848
  window.addEventListener("keydown", handleKeyDown);
41749
41849
  return () => window.removeEventListener("keydown", handleKeyDown);
41750
41850
  }, [open, onClose]);
41751
- useEffect43(() => {
41851
+ useEffect44(() => {
41752
41852
  if (open) {
41753
41853
  setTimeout(() => {
41754
41854
  inputRef.current?.focus();
@@ -42044,36 +42144,36 @@ var CadViewerInner = (props) => {
42044
42144
  description: "Toggle translucent components"
42045
42145
  }
42046
42146
  );
42047
- useEffect44(() => {
42147
+ useEffect45(() => {
42048
42148
  if (containerRef.current) {
42049
42149
  registerHotkeyViewer(containerRef.current);
42050
42150
  }
42051
42151
  }, []);
42052
- useEffect44(() => {
42152
+ useEffect45(() => {
42053
42153
  const stored = window.localStorage.getItem("cadViewerEngine");
42054
42154
  if (stored === "jscad" || stored === "manifold") {
42055
42155
  setEngine(stored);
42056
42156
  }
42057
42157
  }, []);
42058
- useEffect44(() => {
42158
+ useEffect45(() => {
42059
42159
  window.localStorage.setItem("cadViewerEngine", engine);
42060
42160
  }, [engine]);
42061
- useEffect44(() => {
42161
+ useEffect45(() => {
42062
42162
  window.localStorage.setItem("cadViewerAutoRotate", String(autoRotate));
42063
42163
  }, [autoRotate]);
42064
- useEffect44(() => {
42164
+ useEffect45(() => {
42065
42165
  window.localStorage.setItem(
42066
42166
  "cadViewerAutoRotateUserToggled",
42067
42167
  String(autoRotateUserToggled)
42068
42168
  );
42069
42169
  }, [autoRotateUserToggled]);
42070
- useEffect44(() => {
42170
+ useEffect45(() => {
42071
42171
  const stored = window.localStorage.getItem("cadViewerCameraType");
42072
42172
  if (stored === "orthographic" || stored === "perspective") {
42073
42173
  setCameraType(stored);
42074
42174
  }
42075
42175
  }, [setCameraType]);
42076
- useEffect44(() => {
42176
+ useEffect45(() => {
42077
42177
  window.localStorage.setItem("cadViewerCameraType", cameraType);
42078
42178
  }, [cameraType]);
42079
42179
  const viewerKey = props.circuitJson ? JSON.stringify(props.circuitJson) : void 0;
@@ -42473,7 +42573,7 @@ async function convertCircuitJsonTo3dSvg(circuitJson, options = {}) {
42473
42573
 
42474
42574
  // src/hooks/exporter/gltf.ts
42475
42575
  import { GLTFExporter as GLTFExporter3 } from "three-stdlib";
42476
- import { useEffect as useEffect45, useState as useState37, useMemo as useMemo30, useCallback as useCallback22 } from "react";
42576
+ import { useEffect as useEffect46, useState as useState37, useMemo as useMemo30, useCallback as useCallback22 } from "react";
42477
42577
  function useSaveGltfAs(options = {}) {
42478
42578
  const parse2 = useParser(options);
42479
42579
  const link = useMemo30(() => document.createElement("a"), []);
@@ -42486,7 +42586,7 @@ function useSaveGltfAs(options = {}) {
42486
42586
  link.dispatchEvent(new MouseEvent("click"));
42487
42587
  URL.revokeObjectURL(url);
42488
42588
  };
42489
- useEffect45(
42589
+ useEffect46(
42490
42590
  () => () => {
42491
42591
  link.remove();
42492
42592
  instance = null;
@@ -42507,7 +42607,7 @@ function useExportGltfUrl(options = {}) {
42507
42607
  (instance) => parse2(instance).then(setUrl).catch(setError),
42508
42608
  []
42509
42609
  );
42510
- useEffect45(() => () => URL.revokeObjectURL(url), [url]);
42610
+ useEffect46(() => () => URL.revokeObjectURL(url), [url]);
42511
42611
  return [ref, url, error];
42512
42612
  }
42513
42613
  function useParser(options = {}) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tscircuit/3d-viewer",
3
- "version": "0.0.530",
3
+ "version": "0.0.532",
4
4
  "main": "./dist/index.js",
5
5
  "module": "./dist/index.js",
6
6
  "type": "module",