circuit-to-svg 0.0.132 → 0.0.134

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/README.md CHANGED
@@ -50,6 +50,12 @@ Converts a schematic circuit description to an SVG string.
50
50
 
51
51
  Converts a PCB layout description to an SVG string.
52
52
 
53
+ #### Options
54
+
55
+ - `width` and `height` – dimensions of the output SVG. Defaults to `800x600`.
56
+ - `matchBoardAspectRatio` – if `true`, adjust the SVG dimensions so the
57
+ resulting aspect ratio matches the `pcb_board` found in the circuit JSON.
58
+
53
59
  ## Contributing
54
60
 
55
61
  Contributions are welcome! Please feel free to submit a Pull Request.
package/dist/index.d.ts CHANGED
@@ -1,12 +1,20 @@
1
1
  import { AnyCircuitElement } from 'circuit-json';
2
+ import { Matrix } from 'transformation-matrix';
2
3
 
3
4
  interface Options$3 {
4
5
  width?: number;
5
6
  height?: number;
6
7
  shouldDrawErrors?: boolean;
7
8
  shouldDrawRatsNest?: boolean;
9
+ layer?: "top" | "bottom";
10
+ matchBoardAspectRatio?: boolean;
8
11
  }
9
- declare function convertCircuitJsonToPcbSvg(soup: AnyCircuitElement[], options?: Options$3): string;
12
+ interface PcbContext {
13
+ transform: Matrix;
14
+ layer?: "top" | "bottom";
15
+ shouldDrawErrors?: boolean;
16
+ }
17
+ declare function convertCircuitJsonToPcbSvg(circuitJson: AnyCircuitElement[], options?: Options$3): string;
10
18
  /**
11
19
  * @deprecated use `convertCircuitJsonToPcbSvg` instead
12
20
  */
@@ -206,6 +214,6 @@ interface Options {
206
214
  width?: number;
207
215
  height?: number;
208
216
  }
209
- declare function convertCircuitJsonToSolderPasteMask(soup: AnyCircuitElement[], options: Options): string;
217
+ declare function convertCircuitJsonToSolderPasteMask(circuitJson: AnyCircuitElement[], options: Options): string;
210
218
 
211
- export { type ColorMap, type ColorOverrides, circuitJsonToPcbSvg, circuitJsonToSchematicSvg, convertCircuitJsonToAssemblySvg, convertCircuitJsonToPcbSvg, convertCircuitJsonToSchematicSvg, convertCircuitJsonToSolderPasteMask };
219
+ export { type ColorMap, type ColorOverrides, type PcbContext, circuitJsonToPcbSvg, circuitJsonToSchematicSvg, convertCircuitJsonToAssemblySvg, convertCircuitJsonToPcbSvg, convertCircuitJsonToSchematicSvg, convertCircuitJsonToSolderPasteMask };
package/dist/index.js CHANGED
@@ -9,7 +9,8 @@ import {
9
9
 
10
10
  // lib/pcb/svg-object-fns/create-svg-objects-from-pcb-trace-error.ts
11
11
  import { applyToPoint } from "transformation-matrix";
12
- function createSvgObjectsFromPcbTraceError(pcbTraceError, transform, circuitJson, shouldDrawErrors) {
12
+ function createSvgObjectsFromPcbTraceError(pcbTraceError, circuitJson, ctx) {
13
+ const { transform, shouldDrawErrors } = ctx;
13
14
  if (!shouldDrawErrors) return [];
14
15
  const { pcb_port_ids } = pcbTraceError;
15
16
  const port1 = circuitJson.find(
@@ -27,7 +28,7 @@ function createSvgObjectsFromPcbTraceError(pcbTraceError, transform, circuitJson
27
28
  (el) => el.type === "pcb_via" && el.pcb_via_id === viaId
28
29
  );
29
30
  if (via && via.type === "pcb_via") {
30
- return createSvgObjectsForViaTraceError(pcbTraceError, via, transform);
31
+ return createSvgObjectsForViaTraceError(pcbTraceError, via, ctx);
31
32
  }
32
33
  if (pcbTraceError.center) {
33
34
  const screenCenter = applyToPoint(transform, {
@@ -159,7 +160,8 @@ function createSvgObjectsFromPcbTraceError(pcbTraceError, transform, circuitJson
159
160
  ];
160
161
  return svgObjects;
161
162
  }
162
- function createSvgObjectsForViaTraceError(pcbTraceError, via, transform) {
163
+ function createSvgObjectsForViaTraceError(pcbTraceError, via, ctx) {
164
+ const { transform } = ctx;
163
165
  if (pcbTraceError.center && via) {
164
166
  const screenCenter = applyToPoint(transform, {
165
167
  x: pcbTraceError.center.x,
@@ -242,7 +244,8 @@ function createSvgObjectsForViaTraceError(pcbTraceError, via, transform) {
242
244
 
243
245
  // lib/pcb/svg-object-fns/create-svg-objects-from-pcb-fabrication-note-path.ts
244
246
  import { applyToPoint as applyToPoint2 } from "transformation-matrix";
245
- function createSvgObjectsFromPcbFabricationNotePath(fabNotePath, transform) {
247
+ function createSvgObjectsFromPcbFabricationNotePath(fabNotePath, ctx) {
248
+ const { transform, layer: layerFilter } = ctx;
246
249
  if (!fabNotePath.route || !Array.isArray(fabNotePath.route)) return [];
247
250
  const firstPoint = fabNotePath.route[0];
248
251
  const lastPoint = fabNotePath.route[fabNotePath.route.length - 1];
@@ -272,13 +275,9 @@ function createSvgObjectsFromPcbFabricationNotePath(fabNotePath, transform) {
272
275
 
273
276
  // lib/pcb/svg-object-fns/create-svg-objects-from-pcb-fabrication-note-text.ts
274
277
  import { toString as matrixToString } from "transformation-matrix";
275
- import {
276
- applyToPoint as applyToPoint3,
277
- compose,
278
- rotate,
279
- translate
280
- } from "transformation-matrix";
281
- function createSvgObjectsFromPcbFabricationNoteText(pcbFabNoteText, transform) {
278
+ import { applyToPoint as applyToPoint3, compose, rotate, translate } from "transformation-matrix";
279
+ function createSvgObjectsFromPcbFabricationNoteText(pcbFabNoteText, ctx) {
280
+ const { transform, layer: layerFilter } = ctx;
282
281
  const {
283
282
  anchor_position,
284
283
  anchor_alignment,
@@ -287,6 +286,7 @@ function createSvgObjectsFromPcbFabricationNoteText(pcbFabNoteText, transform) {
287
286
  layer = "top",
288
287
  color
289
288
  } = pcbFabNoteText;
289
+ if (layerFilter && layer !== layerFilter) return [];
290
290
  if (!anchor_position || typeof anchor_position.x !== "number" || typeof anchor_position.y !== "number") {
291
291
  console.error("Invalid anchor_position:", anchor_position);
292
292
  return [];
@@ -332,7 +332,8 @@ function createSvgObjectsFromPcbFabricationNoteText(pcbFabNoteText, transform) {
332
332
 
333
333
  // lib/pcb/svg-object-fns/create-svg-objects-from-pcb-plated-hole.ts
334
334
  import { applyToPoint as applyToPoint4 } from "transformation-matrix";
335
- function createSvgObjectsFromPcbPlatedHole(hole, transform) {
335
+ function createSvgObjectsFromPcbPlatedHole(hole, ctx) {
336
+ const { transform } = ctx;
336
337
  const [x, y] = applyToPoint4(transform, [hole.x, hole.y]);
337
338
  if (hole.shape === "pill") {
338
339
  const scaledOuterWidth = hole.outer_width * Math.abs(transform.a);
@@ -529,7 +530,8 @@ var SILKSCREEN_TOP_COLOR = "#f2eda1";
529
530
  var SILKSCREEN_BOTTOM_COLOR = "#5da9e9";
530
531
 
531
532
  // lib/pcb/svg-object-fns/create-svg-objects-from-pcb-silkscreen-path.ts
532
- function createSvgObjectsFromPcbSilkscreenPath(silkscreenPath, transform) {
533
+ function createSvgObjectsFromPcbSilkscreenPath(silkscreenPath, ctx) {
534
+ const { transform, layer: layerFilter } = ctx;
533
535
  if (!silkscreenPath.route || !Array.isArray(silkscreenPath.route)) return [];
534
536
  let path = silkscreenPath.route.map((point, index) => {
535
537
  const [x, y] = applyToPoint5(transform, [point.x, point.y]);
@@ -541,6 +543,7 @@ function createSvgObjectsFromPcbSilkscreenPath(silkscreenPath, transform) {
541
543
  path += " Z";
542
544
  }
543
545
  const layer = silkscreenPath.layer || "top";
546
+ if (layerFilter && layer !== layerFilter) return [];
544
547
  const color = layer === "bottom" ? SILKSCREEN_BOTTOM_COLOR : SILKSCREEN_TOP_COLOR;
545
548
  return [
546
549
  {
@@ -572,7 +575,8 @@ import {
572
575
  scale,
573
576
  toString as matrixToString2
574
577
  } from "transformation-matrix";
575
- function createSvgObjectsFromPcbSilkscreenText(pcbSilkscreenText, transform) {
578
+ function createSvgObjectsFromPcbSilkscreenText(pcbSilkscreenText, ctx) {
579
+ const { transform, layer: layerFilter } = ctx;
576
580
  const {
577
581
  anchor_position,
578
582
  text,
@@ -581,6 +585,7 @@ function createSvgObjectsFromPcbSilkscreenText(pcbSilkscreenText, transform) {
581
585
  ccw_rotation = 0,
582
586
  anchor_alignment = "center"
583
587
  } = pcbSilkscreenText;
588
+ if (layerFilter && layer !== layerFilter) return [];
584
589
  if (!anchor_position || typeof anchor_position.x !== "number" || typeof anchor_position.y !== "number") {
585
590
  console.error("Invalid anchor_position:", anchor_position);
586
591
  return [];
@@ -672,10 +677,9 @@ function createSvgObjectsFromPcbSilkscreenText(pcbSilkscreenText, transform) {
672
677
  }
673
678
 
674
679
  // lib/pcb/svg-object-fns/create-svg-objects-from-pcb-silkscreen-rect.ts
675
- import {
676
- applyToPoint as applyToPoint7
677
- } from "transformation-matrix";
678
- function createSvgObjectsFromPcbSilkscreenRect(pcbSilkscreenRect, transform) {
680
+ import { applyToPoint as applyToPoint7 } from "transformation-matrix";
681
+ function createSvgObjectsFromPcbSilkscreenRect(pcbSilkscreenRect, ctx) {
682
+ const { transform, layer: layerFilter } = ctx;
679
683
  const {
680
684
  center,
681
685
  width,
@@ -684,6 +688,7 @@ function createSvgObjectsFromPcbSilkscreenRect(pcbSilkscreenRect, transform) {
684
688
  pcb_silkscreen_rect_id,
685
689
  stroke_width = 1
686
690
  } = pcbSilkscreenRect;
691
+ if (layerFilter && layer !== layerFilter) return [];
687
692
  if (!center || typeof center.x !== "number" || typeof center.y !== "number" || typeof width !== "number" || typeof height !== "number") {
688
693
  console.error("Invalid rectangle data:", { center, width, height });
689
694
  return [];
@@ -717,10 +722,9 @@ function createSvgObjectsFromPcbSilkscreenRect(pcbSilkscreenRect, transform) {
717
722
  }
718
723
 
719
724
  // lib/pcb/svg-object-fns/create-svg-objects-from-pcb-silkscreen-circle.ts
720
- import {
721
- applyToPoint as applyToPoint8
722
- } from "transformation-matrix";
723
- function createSvgObjectsFromPcbSilkscreenCircle(pcbSilkscreenCircle, transform) {
725
+ import { applyToPoint as applyToPoint8 } from "transformation-matrix";
726
+ function createSvgObjectsFromPcbSilkscreenCircle(pcbSilkscreenCircle, ctx) {
727
+ const { transform, layer: layerFilter } = ctx;
724
728
  const {
725
729
  center,
726
730
  radius,
@@ -728,6 +732,7 @@ function createSvgObjectsFromPcbSilkscreenCircle(pcbSilkscreenCircle, transform)
728
732
  pcb_silkscreen_circle_id,
729
733
  stroke_width = 1
730
734
  } = pcbSilkscreenCircle;
735
+ if (layerFilter && layer !== layerFilter) return [];
731
736
  if (!center || typeof center.x !== "number" || typeof center.y !== "number" || typeof radius !== "number") {
732
737
  console.error("Invalid PCB Silkscreen Circle data:", { center, radius });
733
738
  return [];
@@ -758,10 +763,9 @@ function createSvgObjectsFromPcbSilkscreenCircle(pcbSilkscreenCircle, transform)
758
763
  }
759
764
 
760
765
  // lib/pcb/svg-object-fns/create-svg-objects-from-pcb-silkscreen-line.ts
761
- import {
762
- applyToPoint as applyToPoint9
763
- } from "transformation-matrix";
764
- function createSvgObjectsFromPcbSilkscreenLine(pcbSilkscreenLine, transform) {
766
+ import { applyToPoint as applyToPoint9 } from "transformation-matrix";
767
+ function createSvgObjectsFromPcbSilkscreenLine(pcbSilkscreenLine, ctx) {
768
+ const { transform, layer: layerFilter } = ctx;
765
769
  const {
766
770
  x1,
767
771
  y1,
@@ -771,6 +775,7 @@ function createSvgObjectsFromPcbSilkscreenLine(pcbSilkscreenLine, transform) {
771
775
  layer = "top",
772
776
  pcb_silkscreen_line_id
773
777
  } = pcbSilkscreenLine;
778
+ if (layerFilter && layer !== layerFilter) return [];
774
779
  if (typeof x1 !== "number" || typeof y1 !== "number" || typeof x2 !== "number" || typeof y2 !== "number") {
775
780
  console.error("Invalid coordinates:", { x1, y1, x2, y2 });
776
781
  return [];
@@ -828,7 +833,8 @@ function solderPasteLayerNameToColor(layerName) {
828
833
  }
829
834
 
830
835
  // lib/pcb/svg-object-fns/create-svg-objects-from-pcb-trace.ts
831
- function createSvgObjectsFromPcbTrace(trace, transform) {
836
+ function createSvgObjectsFromPcbTrace(trace, ctx) {
837
+ const { transform, layer: layerFilter } = ctx;
832
838
  if (!trace.route || !Array.isArray(trace.route) || trace.route.length < 2)
833
839
  return [];
834
840
  const segments = pairs(trace.route);
@@ -838,6 +844,7 @@ function createSvgObjectsFromPcbTrace(trace, transform) {
838
844
  const endPoint = applyToPoint10(transform, [end.x, end.y]);
839
845
  const layer = "layer" in start ? start.layer : "layer" in end ? end.layer : null;
840
846
  if (!layer) continue;
847
+ if (layerFilter && layer !== layerFilter) continue;
841
848
  const layerColor = LAYER_NAME_TO_COLOR[layer] ?? "white";
842
849
  const traceWidth = "width" in start ? start.width : "width" in end ? end.width : null;
843
850
  const svgObject = {
@@ -875,8 +882,10 @@ function createSvgObjectsFromPcbTrace(trace, transform) {
875
882
 
876
883
  // lib/pcb/svg-object-fns/create-svg-objects-from-smt-pads.ts
877
884
  import { applyToPoint as applyToPoint11 } from "transformation-matrix";
878
- function createSvgObjectsFromSmtPad(pad, transform) {
885
+ function createSvgObjectsFromSmtPad(pad, ctx) {
886
+ const { transform, layer: layerFilter } = ctx;
879
887
  const [x, y] = applyToPoint11(transform, [pad.x, pad.y]);
888
+ if (layerFilter && pad.layer !== layerFilter) return [];
880
889
  if (pad.shape === "rect" || pad.shape === "rotated_rect") {
881
890
  const width = pad.width * Math.abs(transform.a);
882
891
  const height = pad.height * Math.abs(transform.d);
@@ -938,7 +947,8 @@ function createSvgObjectsFromSmtPad(pad, transform) {
938
947
 
939
948
  // lib/pcb/svg-object-fns/create-svg-objects-from-pcb-board.ts
940
949
  import { applyToPoint as applyToPoint12 } from "transformation-matrix";
941
- function createSvgObjectsFromPcbBoard(pcbBoard, transform) {
950
+ function createSvgObjectsFromPcbBoard(pcbBoard, ctx) {
951
+ const { transform } = ctx;
942
952
  const { width, height, center, outline } = pcbBoard;
943
953
  let path;
944
954
  if (outline && Array.isArray(outline) && outline.length >= 3) {
@@ -987,7 +997,8 @@ function createSvgObjectsFromPcbBoard(pcbBoard, transform) {
987
997
 
988
998
  // lib/pcb/svg-object-fns/create-svg-objects-from-pcb-via.ts
989
999
  import { applyToPoint as applyToPoint13 } from "transformation-matrix";
990
- function createSvgObjectsFromPcbVia(hole, transform) {
1000
+ function createSvgObjectsFromPcbVia(hole, ctx) {
1001
+ const { transform } = ctx;
991
1002
  const [x, y] = applyToPoint13(transform, [hole.x, hole.y]);
992
1003
  const scaledOuterWidth = hole.outer_diameter * Math.abs(transform.a);
993
1004
  const scaledOuterHeight = hole.outer_diameter * Math.abs(transform.a);
@@ -1027,7 +1038,8 @@ function createSvgObjectsFromPcbVia(hole, transform) {
1027
1038
 
1028
1039
  // lib/pcb/svg-object-fns/create-svg-objects-from-pcb-hole.ts
1029
1040
  import { applyToPoint as applyToPoint14 } from "transformation-matrix";
1030
- function createSvgObjectsFromPcbHole(hole, transform) {
1041
+ function createSvgObjectsFromPcbHole(hole, ctx) {
1042
+ const { transform } = ctx;
1031
1043
  const [x, y] = applyToPoint14(transform, [hole.x, hole.y]);
1032
1044
  if (hole.hole_shape === "circle" || hole.hole_shape === "square") {
1033
1045
  const scaledDiameter = hole.hole_diameter * Math.abs(transform.a);
@@ -1135,7 +1147,8 @@ var findNearestPointInNet = (sourcePoint, netId, connectivity, circuitJson) => {
1135
1147
 
1136
1148
  // lib/pcb/svg-object-fns/create-svg-objects-from-pcb-rats-nests.ts
1137
1149
  import { su as su2 } from "@tscircuit/circuit-json-util";
1138
- function createSvgObjectsForRatsNest(circuitJson, transform) {
1150
+ function createSvgObjectsForRatsNest(circuitJson, ctx) {
1151
+ const { transform } = ctx;
1139
1152
  const connectivity = getFullConnectivityMapFromCircuitJson(circuitJson);
1140
1153
  const pcbPorts = circuitJson.filter((elm) => elm.type === "pcb_port");
1141
1154
  const sourceTraces = circuitJson.filter((elm) => elm.type === "source_trace");
@@ -1211,7 +1224,8 @@ import {
1211
1224
  translate as translate3,
1212
1225
  toString as matrixToString6
1213
1226
  } from "transformation-matrix";
1214
- function createSvgObjectsFromPcbCutout(cutout, transform) {
1227
+ function createSvgObjectsFromPcbCutout(cutout, ctx) {
1228
+ const { transform } = ctx;
1215
1229
  if (cutout.shape === "rect") {
1216
1230
  const rectCutout = cutout;
1217
1231
  const [cx, cy] = applyToPoint16(transform, [
@@ -1303,34 +1317,64 @@ var OBJECT_ORDER = [
1303
1317
  "pcb_component",
1304
1318
  "pcb_board"
1305
1319
  ];
1306
- function convertCircuitJsonToPcbSvg(soup, options) {
1320
+ function convertCircuitJsonToPcbSvg(circuitJson, options) {
1321
+ const layer = options?.layer;
1307
1322
  let minX = Number.POSITIVE_INFINITY;
1308
1323
  let minY = Number.POSITIVE_INFINITY;
1309
1324
  let maxX = Number.NEGATIVE_INFINITY;
1310
1325
  let maxY = Number.NEGATIVE_INFINITY;
1311
- for (const item of soup) {
1312
- if (item.type === "pcb_board") {
1313
- if (item.outline && Array.isArray(item.outline) && item.outline.length >= 3)
1314
- updateBoundsToIncludeOutline(item.outline);
1315
- else if ("center" in item && "width" in item && "height" in item)
1316
- updateBounds(item.center, item.width, item.height);
1317
- } else if ("x" in item && "y" in item) {
1318
- updateBounds({ x: item.x, y: item.y }, 0, 0);
1319
- } else if ("route" in item) {
1320
- updateTraceBounds(item.route);
1321
- } else if (item.type === "pcb_silkscreen_text" || item.type === "pcb_silkscreen_rect" || item.type === "pcb_silkscreen_circle" || item.type === "pcb_silkscreen_line") {
1322
- updateSilkscreenBounds(item);
1326
+ let boardMinX = Number.POSITIVE_INFINITY;
1327
+ let boardMinY = Number.POSITIVE_INFINITY;
1328
+ let boardMaxX = Number.NEGATIVE_INFINITY;
1329
+ let boardMaxY = Number.NEGATIVE_INFINITY;
1330
+ for (const circuitJsonElm of circuitJson) {
1331
+ if (circuitJsonElm.type === "pcb_board") {
1332
+ if (circuitJsonElm.outline && Array.isArray(circuitJsonElm.outline) && circuitJsonElm.outline.length >= 3) {
1333
+ updateBoundsToIncludeOutline(circuitJsonElm.outline);
1334
+ updateBoardBoundsToIncludeOutline(circuitJsonElm.outline);
1335
+ } else if ("center" in circuitJsonElm && "width" in circuitJsonElm && "height" in circuitJsonElm) {
1336
+ updateBounds(
1337
+ circuitJsonElm.center,
1338
+ circuitJsonElm.width,
1339
+ circuitJsonElm.height
1340
+ );
1341
+ updateBoardBounds(
1342
+ circuitJsonElm.center,
1343
+ circuitJsonElm.width,
1344
+ circuitJsonElm.height
1345
+ );
1346
+ }
1347
+ } else if ("x" in circuitJsonElm && "y" in circuitJsonElm) {
1348
+ updateBounds({ x: circuitJsonElm.x, y: circuitJsonElm.y }, 0, 0);
1349
+ } else if ("route" in circuitJsonElm) {
1350
+ updateTraceBounds(circuitJsonElm.route);
1351
+ } else if (circuitJsonElm.type === "pcb_silkscreen_text" || circuitJsonElm.type === "pcb_silkscreen_rect" || circuitJsonElm.type === "pcb_silkscreen_circle" || circuitJsonElm.type === "pcb_silkscreen_line") {
1352
+ updateSilkscreenBounds(circuitJsonElm);
1323
1353
  }
1324
1354
  }
1325
1355
  const padding = 1;
1326
1356
  const circuitWidth = maxX - minX + 2 * padding;
1327
1357
  const circuitHeight = maxY - minY + 2 * padding;
1328
- const svgWidth = options?.width ?? 800;
1329
- const svgHeight = options?.height ?? 600;
1358
+ let svgWidth = options?.width ?? 800;
1359
+ let svgHeight = options?.height ?? 600;
1360
+ if (options?.matchBoardAspectRatio) {
1361
+ const boardWidth = boardMaxX - boardMinX;
1362
+ const boardHeight = boardMaxY - boardMinY;
1363
+ if (boardWidth > 0 && boardHeight > 0) {
1364
+ const aspect = boardWidth / boardHeight;
1365
+ if (options?.width && !options?.height) {
1366
+ svgHeight = options.width / aspect;
1367
+ } else if (options?.height && !options?.width) {
1368
+ svgWidth = options.height * aspect;
1369
+ } else {
1370
+ svgHeight = svgWidth / aspect;
1371
+ }
1372
+ }
1373
+ }
1330
1374
  const paths = [];
1331
- for (const item of soup) {
1332
- if ("route" in item && item.route !== void 0) {
1333
- paths.push(item.route);
1375
+ for (const circuitJsonElm of circuitJson) {
1376
+ if ("route" in circuitJsonElm && circuitJsonElm.route !== void 0) {
1377
+ paths.push(circuitJsonElm.route);
1334
1378
  }
1335
1379
  }
1336
1380
  const scaleX = svgWidth / circuitWidth;
@@ -1346,20 +1390,23 @@ function convertCircuitJsonToPcbSvg(soup, options) {
1346
1390
  scale2(scaleFactor, -scaleFactor)
1347
1391
  // Flip in y-direction
1348
1392
  );
1349
- let svgObjects = soup.sort(
1393
+ const ctx = {
1394
+ transform,
1395
+ layer,
1396
+ shouldDrawErrors: options?.shouldDrawErrors
1397
+ };
1398
+ let svgObjects = circuitJson.sort(
1350
1399
  (a, b) => (OBJECT_ORDER.indexOf(b.type) ?? 9999) - (OBJECT_ORDER.indexOf(a.type) ?? 9999)
1351
- ).flatMap(
1352
- (item) => createSvgObjects(item, transform, soup, options?.shouldDrawErrors)
1353
- );
1400
+ ).flatMap((elm) => createSvgObjects({ elm, circuitJson, ctx }));
1354
1401
  let strokeWidth = String(0.05 * scaleFactor);
1355
- for (const element of soup) {
1402
+ for (const element of circuitJson) {
1356
1403
  if ("stroke_width" in element) {
1357
1404
  strokeWidth = String(scaleFactor * element.stroke_width);
1358
1405
  break;
1359
1406
  }
1360
1407
  }
1361
1408
  if (options?.shouldDrawRatsNest) {
1362
- const ratsNestObjects = createSvgObjectsForRatsNest(soup, transform);
1409
+ const ratsNestObjects = createSvgObjectsForRatsNest(circuitJson, ctx);
1363
1410
  svgObjects = svgObjects.concat(ratsNestObjects);
1364
1411
  }
1365
1412
  const svgObject = {
@@ -1412,6 +1459,14 @@ function convertCircuitJsonToPcbSvg(soup, options) {
1412
1459
  maxX = Math.max(maxX, center.x + halfWidth);
1413
1460
  maxY = Math.max(maxY, center.y + halfHeight);
1414
1461
  }
1462
+ function updateBoardBounds(center, width, height) {
1463
+ const halfWidth = width / 2;
1464
+ const halfHeight = height / 2;
1465
+ boardMinX = Math.min(boardMinX, center.x - halfWidth);
1466
+ boardMinY = Math.min(boardMinY, center.y - halfHeight);
1467
+ boardMaxX = Math.max(boardMaxX, center.x + halfWidth);
1468
+ boardMaxY = Math.max(boardMaxY, center.y + halfHeight);
1469
+ }
1415
1470
  function updateBoundsToIncludeOutline(outline) {
1416
1471
  for (const point of outline) {
1417
1472
  minX = Math.min(minX, point.x);
@@ -1420,6 +1475,14 @@ function convertCircuitJsonToPcbSvg(soup, options) {
1420
1475
  maxY = Math.max(maxY, point.y);
1421
1476
  }
1422
1477
  }
1478
+ function updateBoardBoundsToIncludeOutline(outline) {
1479
+ for (const point of outline) {
1480
+ boardMinX = Math.min(boardMinX, point.x);
1481
+ boardMinY = Math.min(boardMinY, point.y);
1482
+ boardMaxX = Math.max(boardMaxX, point.x);
1483
+ boardMaxY = Math.max(boardMaxY, point.y);
1484
+ }
1485
+ }
1423
1486
  function updateTraceBounds(route) {
1424
1487
  for (const point of route) {
1425
1488
  minX = Math.min(minX, point.x);
@@ -1452,50 +1515,53 @@ function convertCircuitJsonToPcbSvg(soup, options) {
1452
1515
  }
1453
1516
  }
1454
1517
  }
1455
- function createSvgObjects(elm, transform, soup, shouldDrawErrors) {
1518
+ function createSvgObjects({
1519
+ elm,
1520
+ circuitJson,
1521
+ ctx
1522
+ }) {
1523
+ const { transform, layer: layerFilter, shouldDrawErrors } = ctx;
1456
1524
  switch (elm.type) {
1457
1525
  case "pcb_trace_error":
1458
- return createSvgObjectsFromPcbTraceError(
1459
- elm,
1460
- transform,
1461
- soup,
1462
- shouldDrawErrors
1463
- ).filter(Boolean);
1526
+ return createSvgObjectsFromPcbTraceError(elm, circuitJson, ctx).filter(
1527
+ Boolean
1528
+ );
1464
1529
  case "pcb_component":
1465
- return [createSvgObjectsFromPcbComponent(elm, transform)].filter(Boolean);
1530
+ return [createSvgObjectsFromPcbComponent(elm, ctx)].filter(Boolean);
1466
1531
  case "pcb_trace":
1467
- return createSvgObjectsFromPcbTrace(elm, transform);
1532
+ return createSvgObjectsFromPcbTrace(elm, ctx);
1468
1533
  case "pcb_plated_hole":
1469
- return createSvgObjectsFromPcbPlatedHole(elm, transform).filter(Boolean);
1534
+ return createSvgObjectsFromPcbPlatedHole(elm, ctx).filter(Boolean);
1470
1535
  case "pcb_hole":
1471
- return createSvgObjectsFromPcbHole(elm, transform);
1536
+ return createSvgObjectsFromPcbHole(elm, ctx);
1472
1537
  case "pcb_smtpad":
1473
- return createSvgObjectsFromSmtPad(elm, transform);
1538
+ return createSvgObjectsFromSmtPad(elm, ctx);
1474
1539
  case "pcb_silkscreen_text":
1475
- return createSvgObjectsFromPcbSilkscreenText(elm, transform);
1540
+ return createSvgObjectsFromPcbSilkscreenText(elm, ctx);
1476
1541
  case "pcb_silkscreen_rect":
1477
- return createSvgObjectsFromPcbSilkscreenRect(elm, transform);
1542
+ return createSvgObjectsFromPcbSilkscreenRect(elm, ctx);
1478
1543
  case "pcb_silkscreen_circle":
1479
- return createSvgObjectsFromPcbSilkscreenCircle(elm, transform);
1544
+ return createSvgObjectsFromPcbSilkscreenCircle(elm, ctx);
1480
1545
  case "pcb_silkscreen_line":
1481
- return createSvgObjectsFromPcbSilkscreenLine(elm, transform);
1546
+ return createSvgObjectsFromPcbSilkscreenLine(elm, ctx);
1482
1547
  case "pcb_fabrication_note_path":
1483
- return createSvgObjectsFromPcbFabricationNotePath(elm, transform);
1548
+ return createSvgObjectsFromPcbFabricationNotePath(elm, ctx);
1484
1549
  case "pcb_fabrication_note_text":
1485
- return createSvgObjectsFromPcbFabricationNoteText(elm, transform);
1550
+ return createSvgObjectsFromPcbFabricationNoteText(elm, ctx);
1486
1551
  case "pcb_silkscreen_path":
1487
- return createSvgObjectsFromPcbSilkscreenPath(elm, transform);
1552
+ return createSvgObjectsFromPcbSilkscreenPath(elm, ctx);
1488
1553
  case "pcb_board":
1489
- return createSvgObjectsFromPcbBoard(elm, transform);
1554
+ return createSvgObjectsFromPcbBoard(elm, ctx);
1490
1555
  case "pcb_via":
1491
- return createSvgObjectsFromPcbVia(elm, transform);
1556
+ return createSvgObjectsFromPcbVia(elm, ctx);
1492
1557
  case "pcb_cutout":
1493
- return createSvgObjectsFromPcbCutout(elm, transform);
1558
+ return createSvgObjectsFromPcbCutout(elm, ctx);
1494
1559
  default:
1495
1560
  return [];
1496
1561
  }
1497
1562
  }
1498
- function createSvgObjectsFromPcbComponent(component, transform) {
1563
+ function createSvgObjectsFromPcbComponent(component, ctx) {
1564
+ const { transform } = ctx;
1499
1565
  const { center, width, height, rotation = 0 } = component;
1500
1566
  const [x, y] = applyToPoint17(transform, [center.x, center.y]);
1501
1567
  const scaledWidth = width * Math.abs(transform.a);
@@ -4842,7 +4908,9 @@ import {
4842
4908
 
4843
4909
  // lib/pcb/svg-object-fns/convert-circuit-json-to-solder-paste-mask.ts
4844
4910
  import { applyToPoint as applyToPoint38 } from "transformation-matrix";
4845
- function createSvgObjectsFromSolderPaste(solderPaste, transform) {
4911
+ function createSvgObjectsFromSolderPaste(solderPaste, ctx) {
4912
+ const { transform, layer: layerFilter } = ctx;
4913
+ if (layerFilter && solderPaste.layer !== layerFilter) return [];
4846
4914
  const [x, y] = applyToPoint38(transform, [solderPaste.x, solderPaste.y]);
4847
4915
  if (solderPaste.shape === "rect" || solderPaste.shape === "rotated_rect") {
4848
4916
  const width = solderPaste.width * Math.abs(transform.a);
@@ -4922,15 +4990,15 @@ var OBJECT_ORDER3 = [
4922
4990
  "pcb_board",
4923
4991
  "pcb_solder_paste"
4924
4992
  ];
4925
- function convertCircuitJsonToSolderPasteMask(soup, options) {
4993
+ function convertCircuitJsonToSolderPasteMask(circuitJson, options) {
4926
4994
  let minX = Number.POSITIVE_INFINITY;
4927
4995
  let minY = Number.POSITIVE_INFINITY;
4928
4996
  let maxX = Number.NEGATIVE_INFINITY;
4929
4997
  let maxY = Number.NEGATIVE_INFINITY;
4930
- const filteredSoup = soup.filter(
4998
+ const filteredCircuitJson = circuitJson.filter(
4931
4999
  (elm) => elm.type === "pcb_board" || elm.type === "pcb_solder_paste" && elm.layer === options.layer
4932
5000
  );
4933
- for (const item of filteredSoup) {
5001
+ for (const item of filteredCircuitJson) {
4934
5002
  if (item.type === "pcb_board") {
4935
5003
  if (item.outline && Array.isArray(item.outline) && item.outline.length >= 3) {
4936
5004
  updateBoundsToIncludeOutline(item.outline);
@@ -4959,9 +5027,13 @@ function convertCircuitJsonToSolderPasteMask(soup, options) {
4959
5027
  scale8(scaleFactor, -scaleFactor)
4960
5028
  // Flip in y-direction
4961
5029
  );
4962
- const svgObjects = filteredSoup.sort(
5030
+ const ctx = {
5031
+ transform,
5032
+ layer: options.layer
5033
+ };
5034
+ const svgObjects = filteredCircuitJson.sort(
4963
5035
  (a, b) => (OBJECT_ORDER3.indexOf(b.type) ?? 9999) - (OBJECT_ORDER3.indexOf(a.type) ?? 9999)
4964
- ).flatMap((item) => createSvgObjects3(item, transform));
5036
+ ).flatMap((item) => createSvgObjects3({ elm: item, ctx }));
4965
5037
  const svgObject = {
4966
5038
  name: "svg",
4967
5039
  type: "element",
@@ -5021,12 +5093,13 @@ function convertCircuitJsonToSolderPasteMask(soup, options) {
5021
5093
  }
5022
5094
  }
5023
5095
  }
5024
- function createSvgObjects3(elm, transform) {
5096
+ function createSvgObjects3({ elm, ctx }) {
5097
+ const { transform } = ctx;
5025
5098
  switch (elm.type) {
5026
5099
  case "pcb_board":
5027
- return createSvgObjectsFromPcbBoard(elm, transform);
5100
+ return createSvgObjectsFromPcbBoard(elm, ctx);
5028
5101
  case "pcb_solder_paste":
5029
- return createSvgObjectsFromSolderPaste(elm, transform);
5102
+ return createSvgObjectsFromSolderPaste(elm, ctx);
5030
5103
  default:
5031
5104
  return [];
5032
5105
  }