circuit-to-svg 0.0.282 → 0.0.284

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.js CHANGED
@@ -3202,6 +3202,9 @@ function createSvgObjectsFromSmtPad(pad, ctx) {
3202
3202
  const height = pad.height * Math.abs(transform.d);
3203
3203
  const radius = pad.radius * Math.abs(transform.a);
3204
3204
  const [x, y] = applyToPoint20(transform, [pad.x, pad.y]);
3205
+ const rotationTransformAttributes = isRotated ? {
3206
+ transform: `translate(${x} ${y}) rotate(${-(pad.ccw_rotation ?? 0)})`
3207
+ } : void 0;
3205
3208
  const baseAttributes = {
3206
3209
  class: "pcb-pad",
3207
3210
  fill: layerNameToColor(pad.layer, colorMap2),
@@ -3213,9 +3216,7 @@ function createSvgObjectsFromSmtPad(pad, ctx) {
3213
3216
  ry: radius.toString(),
3214
3217
  "data-type": "pcb_smtpad",
3215
3218
  "data-pcb-layer": pad.layer,
3216
- ...isRotated ? {
3217
- transform: `translate(${x} ${y}) rotate(${-(pad.ccw_rotation ?? 0)})`
3218
- } : {}
3219
+ ...rotationTransformAttributes ?? {}
3219
3220
  };
3220
3221
  const padElement = {
3221
3222
  name: "rect",
@@ -3239,14 +3240,15 @@ function createSvgObjectsFromSmtPad(pad, ctx) {
3239
3240
  attributes: {
3240
3241
  class: "pcb-pad-covered",
3241
3242
  fill: soldermaskWithCopperUnderneathColor,
3242
- x: (x - width / 2).toString(),
3243
- y: (y - height / 2).toString(),
3243
+ x: isRotated ? (-width / 2).toString() : (x - width / 2).toString(),
3244
+ y: isRotated ? (-height / 2).toString() : (y - height / 2).toString(),
3244
3245
  width: width.toString(),
3245
3246
  height: height.toString(),
3246
3247
  rx: radius.toString(),
3247
3248
  ry: radius.toString(),
3248
3249
  "data-type": "pcb_smtpad",
3249
- "data-pcb-layer": pad.layer
3250
+ "data-pcb-layer": pad.layer,
3251
+ ...rotationTransformAttributes ?? {}
3250
3252
  }
3251
3253
  };
3252
3254
  const exposedAttributes = {
@@ -3260,7 +3262,7 @@ function createSvgObjectsFromSmtPad(pad, ctx) {
3260
3262
  ry: maskRadius.toString(),
3261
3263
  "data-type": "pcb_soldermask",
3262
3264
  "data-pcb-layer": pad.layer,
3263
- ...isRotated && pad.ccw_rotation ? { transform: `translate(${x} ${y}) rotate(${-pad.ccw_rotation})` } : {}
3265
+ ...rotationTransformAttributes ?? {}
3264
3266
  };
3265
3267
  const exposedOpeningElement = {
3266
3268
  name: "rect",
@@ -3280,14 +3282,15 @@ function createSvgObjectsFromSmtPad(pad, ctx) {
3280
3282
  attributes: {
3281
3283
  class: "pcb-pad-covered",
3282
3284
  fill: soldermaskWithCopperUnderneathColor,
3283
- x: (x - width / 2).toString(),
3284
- y: (y - height / 2).toString(),
3285
+ x: isRotated ? (-width / 2).toString() : (x - width / 2).toString(),
3286
+ y: isRotated ? (-height / 2).toString() : (y - height / 2).toString(),
3285
3287
  width: width.toString(),
3286
3288
  height: height.toString(),
3287
3289
  rx: radius.toString(),
3288
3290
  ry: radius.toString(),
3289
3291
  "data-type": "pcb_smtpad",
3290
- "data-pcb-layer": pad.layer
3292
+ "data-pcb-layer": pad.layer,
3293
+ ...rotationTransformAttributes ?? {}
3291
3294
  }
3292
3295
  };
3293
3296
  return [coveredPadElement];
@@ -3303,7 +3306,7 @@ function createSvgObjectsFromSmtPad(pad, ctx) {
3303
3306
  ry: maskRadius.toString(),
3304
3307
  "data-type": "pcb_soldermask_opening",
3305
3308
  "data-pcb-layer": pad.layer,
3306
- ...isRotated && pad.ccw_rotation ? { transform: `translate(${x} ${y}) rotate(${-pad.ccw_rotation})` } : {}
3309
+ ...rotationTransformAttributes ?? {}
3307
3310
  };
3308
3311
  const substrateElement = {
3309
3312
  name: "rect",
@@ -3505,180 +3508,503 @@ function createSvgObjectsFromSmtPad(pad, ctx) {
3505
3508
  }
3506
3509
 
3507
3510
  // lib/pcb/svg-object-fns/create-svg-objects-from-pcb-board.ts
3511
+ import { applyToPoint as applyToPoint22 } from "transformation-matrix";
3512
+
3513
+ // lib/utils/create-pcb-component-anchor-offset-indicators.ts
3508
3514
  import { applyToPoint as applyToPoint21 } from "transformation-matrix";
3509
- function createSvgObjectsFromPcbBoard(pcbBoard, ctx) {
3510
- const { transform, colorMap: colorMap2, showSolderMask } = ctx;
3511
- const { width, height, center, outline } = pcbBoard;
3512
- let path;
3513
- if (outline && Array.isArray(outline) && outline.length >= 3) {
3514
- path = outline.map((point, index) => {
3515
- const [x, y] = applyToPoint21(transform, [point.x, point.y]);
3516
- return index === 0 ? `M ${x} ${y}` : `L ${x} ${y}`;
3517
- }).join(" ");
3518
- } else {
3519
- const halfWidth = width / 2;
3520
- const halfHeight = height / 2;
3521
- const topLeft = applyToPoint21(transform, [
3522
- center.x - halfWidth,
3523
- center.y - halfHeight
3524
- ]);
3525
- const topRight = applyToPoint21(transform, [
3526
- center.x + halfWidth,
3527
- center.y - halfHeight
3528
- ]);
3529
- const bottomRight = applyToPoint21(transform, [
3530
- center.x + halfWidth,
3531
- center.y + halfHeight
3532
- ]);
3533
- const bottomLeft = applyToPoint21(transform, [
3534
- center.x - halfWidth,
3535
- center.y + halfHeight
3536
- ]);
3537
- path = `M ${topLeft[0]} ${topLeft[1]} L ${topRight[0]} ${topRight[1]} L ${bottomRight[0]} ${bottomRight[1]} L ${bottomLeft[0]} ${bottomLeft[1]}`;
3538
- }
3539
- path += " Z";
3540
- const svgObjects = [];
3541
- if (showSolderMask) {
3542
- const layer = ctx.layer ?? "top";
3543
- const maskLayer = layer === "bottom" ? "soldermask-bottom" : "soldermask-top";
3544
- svgObjects.push({
3545
- name: "path",
3546
- type: "element",
3547
- value: "",
3548
- children: [],
3549
- attributes: {
3550
- class: "pcb-board-soldermask",
3551
- d: path,
3552
- fill: colorMap2.soldermask.top,
3553
- "fill-opacity": "0.8",
3554
- stroke: "none",
3555
- "data-type": "pcb_soldermask",
3556
- "data-pcb-layer": maskLayer
3557
- }
3558
- });
3559
- }
3560
- svgObjects.push({
3561
- name: "path",
3515
+ var OFFSET_THRESHOLD_MM = 0.01;
3516
+ var TICK_SIZE_PX = 4;
3517
+ var LABEL_GAP_PX = 8;
3518
+ var LABEL_FONT_SIZE_PX = 11;
3519
+ var STROKE_WIDTH_PX = 1;
3520
+ var ANCHOR_MARKER_SIZE_PX = 5;
3521
+ var ANCHOR_MARKER_STROKE_WIDTH_PX = 1.5;
3522
+ var COMPONENT_GAP_PX = 15;
3523
+ var COMPONENT_SIDE_GAP_PX = 10;
3524
+ var DISTANCE_MULTIPLIER = 0.2;
3525
+ var MAX_OFFSET_PX = 50;
3526
+ function createAnchorOffsetIndicators(params) {
3527
+ const {
3528
+ groupAnchorPosition,
3529
+ componentPosition,
3530
+ transform,
3531
+ componentWidth = 0,
3532
+ componentHeight = 0
3533
+ } = params;
3534
+ const objects = [];
3535
+ const [screenGroupAnchorX, screenGroupAnchorY] = applyToPoint21(transform, [
3536
+ groupAnchorPosition.x,
3537
+ groupAnchorPosition.y
3538
+ ]);
3539
+ const [screenComponentX, screenComponentY] = applyToPoint21(transform, [
3540
+ componentPosition.x,
3541
+ componentPosition.y
3542
+ ]);
3543
+ const offsetX = componentPosition.x - groupAnchorPosition.x;
3544
+ const offsetY = componentPosition.y - groupAnchorPosition.y;
3545
+ const scale9 = Math.abs(transform.a);
3546
+ const screenComponentWidth = componentWidth * scale9;
3547
+ const screenComponentHeight = componentHeight * scale9;
3548
+ objects.push(createAnchorMarker(screenGroupAnchorX, screenGroupAnchorY));
3549
+ objects.push({
3550
+ name: "line",
3562
3551
  type: "element",
3563
- value: "",
3552
+ attributes: {
3553
+ x1: screenGroupAnchorX.toString(),
3554
+ y1: screenGroupAnchorY.toString(),
3555
+ x2: screenComponentX.toString(),
3556
+ y2: screenComponentY.toString(),
3557
+ stroke: "#ffffff",
3558
+ "stroke-width": "0.5",
3559
+ "stroke-dasharray": "3,3",
3560
+ opacity: "0.5",
3561
+ class: "anchor-offset-connector"
3562
+ },
3564
3563
  children: [],
3564
+ value: ""
3565
+ });
3566
+ objects.push({
3567
+ name: "circle",
3568
+ type: "element",
3565
3569
  attributes: {
3566
- class: "pcb-board",
3567
- d: path,
3568
- fill: "none",
3569
- stroke: colorMap2.boardOutline,
3570
- "stroke-width": (0.1 * Math.abs(transform.a)).toString(),
3571
- "data-type": "pcb_board",
3572
- "data-pcb-layer": "board"
3573
- }
3570
+ cx: screenComponentX.toString(),
3571
+ cy: screenComponentY.toString(),
3572
+ r: "2",
3573
+ fill: "#ffffff",
3574
+ opacity: "0.7",
3575
+ class: "anchor-offset-component-marker"
3576
+ },
3577
+ children: [],
3578
+ value: ""
3574
3579
  });
3575
- return svgObjects;
3576
- }
3577
-
3578
- // lib/pcb/svg-object-fns/create-svg-objects-from-pcb-panel.ts
3579
- import { applyToPoint as applyToPoint22 } from "transformation-matrix";
3580
- function createSvgObjectsFromPcbPanel(pcbPanel, ctx) {
3581
- const { transform, colorMap: colorMap2, showSolderMask } = ctx;
3582
- const width = Number(pcbPanel.width);
3583
- const height = Number(pcbPanel.height);
3584
- const center = pcbPanel.center ?? { x: width / 2, y: height / 2 };
3585
- const halfWidth = width / 2;
3586
- const halfHeight = height / 2;
3587
- const topLeft = applyToPoint22(transform, [
3588
- center.x - halfWidth,
3589
- center.y - halfHeight
3590
- ]);
3591
- const topRight = applyToPoint22(transform, [
3592
- center.x + halfWidth,
3593
- center.y - halfHeight
3594
- ]);
3595
- const bottomRight = applyToPoint22(transform, [
3596
- center.x + halfWidth,
3597
- center.y + halfHeight
3598
- ]);
3599
- const bottomLeft = applyToPoint22(transform, [
3600
- center.x - halfWidth,
3601
- center.y + halfHeight
3602
- ]);
3603
- const path = `M ${topLeft[0]} ${topLeft[1]} L ${topRight[0]} ${topRight[1]} L ${bottomRight[0]} ${bottomRight[1]} L ${bottomLeft[0]} ${bottomLeft[1]} Z`;
3604
- const isCoveredWithSolderMask = pcbPanel.covered_with_solder_mask !== false;
3605
- const shouldShowSolderMask = Boolean(
3606
- showSolderMask && isCoveredWithSolderMask
3580
+ const yDistance = Math.abs(screenComponentY - screenGroupAnchorY);
3581
+ const xDistance = Math.abs(screenComponentX - screenGroupAnchorX);
3582
+ const totalDistance = Math.sqrt(xDistance * xDistance + yDistance * yDistance);
3583
+ const componentHeightOffset = screenComponentHeight / 2 + COMPONENT_GAP_PX;
3584
+ const dynamicOffset = Math.max(
3585
+ componentHeightOffset,
3586
+ Math.min(MAX_OFFSET_PX, totalDistance * DISTANCE_MULTIPLIER)
3607
3587
  );
3608
- return [
3609
- {
3610
- name: "path",
3611
- type: "element",
3612
- value: "",
3613
- children: [],
3614
- attributes: {
3615
- class: "pcb-panel",
3616
- d: path,
3617
- fill: "none",
3618
- stroke: colorMap2.boardOutline,
3619
- "stroke-width": (0.1 * Math.abs(transform.a)).toString(),
3620
- "data-type": "pcb_panel",
3621
- "data-pcb-layer": "board"
3622
- }
3623
- }
3624
- ];
3588
+ const horizontalLineY = offsetY > 0 ? screenComponentY - dynamicOffset : screenComponentY + dynamicOffset;
3589
+ const componentWidthOffset = screenComponentWidth / 2 + COMPONENT_SIDE_GAP_PX;
3590
+ const verticalLineX = offsetX > 0 ? screenComponentX + componentWidthOffset : screenComponentX - componentWidthOffset;
3591
+ if (Math.abs(offsetX) > OFFSET_THRESHOLD_MM) {
3592
+ objects.push(
3593
+ ...createHorizontalDimension({
3594
+ startX: screenGroupAnchorX,
3595
+ endX: screenComponentX,
3596
+ y: horizontalLineY,
3597
+ offsetMm: offsetX,
3598
+ offsetY
3599
+ })
3600
+ );
3601
+ }
3602
+ if (Math.abs(offsetY) > OFFSET_THRESHOLD_MM) {
3603
+ objects.push(
3604
+ ...createVerticalDimension({
3605
+ x: verticalLineX,
3606
+ startY: screenGroupAnchorY,
3607
+ endY: screenComponentY,
3608
+ offsetMm: offsetY,
3609
+ offsetX
3610
+ })
3611
+ );
3612
+ }
3613
+ return objects;
3625
3614
  }
3626
-
3627
- // lib/pcb/svg-object-fns/create-svg-objects-from-pcb-via.ts
3628
- import { applyToPoint as applyToPoint23 } from "transformation-matrix";
3629
- function createSvgObjectsFromPcbVia(hole, ctx) {
3630
- const { transform, colorMap: colorMap2 } = ctx;
3631
- const [x, y] = applyToPoint23(transform, [hole.x, hole.y]);
3632
- const scaledOuterWidth = hole.outer_diameter * Math.abs(transform.a);
3633
- const scaledOuterHeight = hole.outer_diameter * Math.abs(transform.a);
3634
- const scaledHoleWidth = hole.hole_diameter * Math.abs(transform.a);
3635
- const scaledHoleHeight = hole.hole_diameter * Math.abs(transform.a);
3636
- const outerRadius = Math.min(scaledOuterWidth, scaledOuterHeight) / 2;
3637
- const innerRadius = Math.min(scaledHoleWidth, scaledHoleHeight) / 2;
3615
+ function createAnchorMarker(x, y) {
3638
3616
  return {
3639
3617
  name: "g",
3640
3618
  type: "element",
3641
3619
  attributes: {
3642
- "data-type": "pcb_via",
3643
- "data-pcb-layer": "through"
3620
+ class: "anchor-offset-marker",
3621
+ "data-type": "anchor_offset_marker"
3644
3622
  },
3645
3623
  children: [
3646
3624
  {
3647
- name: "circle",
3625
+ name: "line",
3648
3626
  type: "element",
3649
3627
  attributes: {
3650
- class: "pcb-hole-outer",
3651
- fill: colorMap2.copper.top,
3652
- cx: x.toString(),
3653
- cy: y.toString(),
3654
- r: outerRadius.toString(),
3655
- "data-type": "pcb_via",
3656
- "data-pcb-layer": "top"
3657
- }
3628
+ x1: x.toString(),
3629
+ y1: (y - ANCHOR_MARKER_SIZE_PX).toString(),
3630
+ x2: x.toString(),
3631
+ y2: (y + ANCHOR_MARKER_SIZE_PX).toString(),
3632
+ stroke: "#ffffff",
3633
+ "stroke-width": ANCHOR_MARKER_STROKE_WIDTH_PX.toString(),
3634
+ "stroke-linecap": "round"
3635
+ },
3636
+ children: [],
3637
+ value: ""
3658
3638
  },
3659
3639
  {
3660
- name: "circle",
3640
+ name: "line",
3661
3641
  type: "element",
3662
3642
  attributes: {
3663
- class: "pcb-hole-inner",
3664
- fill: colorMap2.drill,
3665
- cx: x.toString(),
3666
- cy: y.toString(),
3667
- r: innerRadius.toString(),
3668
- "data-type": "pcb_via",
3669
- "data-pcb-layer": "drill"
3670
- }
3671
- }
3672
- ]
3673
- };
3674
- }
3675
-
3676
- // lib/pcb/svg-object-fns/create-svg-objects-from-pcb-hole.ts
3643
+ x1: (x - ANCHOR_MARKER_SIZE_PX).toString(),
3644
+ y1: y.toString(),
3645
+ x2: (x + ANCHOR_MARKER_SIZE_PX).toString(),
3646
+ y2: y.toString(),
3647
+ stroke: "#ffffff",
3648
+ "stroke-width": ANCHOR_MARKER_STROKE_WIDTH_PX.toString(),
3649
+ "stroke-linecap": "round"
3650
+ },
3651
+ children: [],
3652
+ value: ""
3653
+ }
3654
+ ],
3655
+ value: ""
3656
+ };
3657
+ }
3658
+ function createHorizontalDimension({
3659
+ startX,
3660
+ endX,
3661
+ y,
3662
+ offsetMm,
3663
+ offsetY
3664
+ }) {
3665
+ const objects = [];
3666
+ objects.push({
3667
+ name: "line",
3668
+ type: "element",
3669
+ attributes: {
3670
+ x1: startX.toString(),
3671
+ y1: y.toString(),
3672
+ x2: endX.toString(),
3673
+ y2: y.toString(),
3674
+ stroke: "#ffffff",
3675
+ "stroke-width": STROKE_WIDTH_PX.toString(),
3676
+ class: "anchor-offset-dimension-x"
3677
+ },
3678
+ children: [],
3679
+ value: ""
3680
+ });
3681
+ objects.push({
3682
+ name: "line",
3683
+ type: "element",
3684
+ attributes: {
3685
+ x1: startX.toString(),
3686
+ y1: (y - TICK_SIZE_PX).toString(),
3687
+ x2: startX.toString(),
3688
+ y2: (y + TICK_SIZE_PX).toString(),
3689
+ stroke: "#ffffff",
3690
+ "stroke-width": STROKE_WIDTH_PX.toString()
3691
+ },
3692
+ children: [],
3693
+ value: ""
3694
+ });
3695
+ objects.push({
3696
+ name: "line",
3697
+ type: "element",
3698
+ attributes: {
3699
+ x1: endX.toString(),
3700
+ y1: (y - TICK_SIZE_PX).toString(),
3701
+ x2: endX.toString(),
3702
+ y2: (y + TICK_SIZE_PX).toString(),
3703
+ stroke: "#ffffff",
3704
+ "stroke-width": STROKE_WIDTH_PX.toString()
3705
+ },
3706
+ children: [],
3707
+ value: ""
3708
+ });
3709
+ const midX = (startX + endX) / 2;
3710
+ const labelY = offsetY > 0 ? y - TICK_SIZE_PX - LABEL_GAP_PX : y + TICK_SIZE_PX + LABEL_GAP_PX;
3711
+ objects.push({
3712
+ name: "text",
3713
+ type: "element",
3714
+ attributes: {
3715
+ x: midX.toString(),
3716
+ y: labelY.toString(),
3717
+ fill: "#ffffff",
3718
+ "font-size": LABEL_FONT_SIZE_PX.toString(),
3719
+ "font-family": "Arial, sans-serif",
3720
+ "text-anchor": "middle",
3721
+ "dominant-baseline": offsetY > 0 ? "baseline" : "hanging",
3722
+ class: "anchor-offset-label"
3723
+ },
3724
+ children: [
3725
+ {
3726
+ type: "text",
3727
+ value: `X: ${offsetMm.toFixed(2)}mm`,
3728
+ name: "",
3729
+ attributes: {},
3730
+ children: []
3731
+ }
3732
+ ],
3733
+ value: ""
3734
+ });
3735
+ return objects;
3736
+ }
3737
+ function createVerticalDimension({
3738
+ x,
3739
+ startY,
3740
+ endY,
3741
+ offsetMm,
3742
+ offsetX
3743
+ }) {
3744
+ const objects = [];
3745
+ objects.push({
3746
+ name: "line",
3747
+ type: "element",
3748
+ attributes: {
3749
+ x1: x.toString(),
3750
+ y1: startY.toString(),
3751
+ x2: x.toString(),
3752
+ y2: endY.toString(),
3753
+ stroke: "#ffffff",
3754
+ "stroke-width": STROKE_WIDTH_PX.toString(),
3755
+ class: "anchor-offset-dimension-y"
3756
+ },
3757
+ children: [],
3758
+ value: ""
3759
+ });
3760
+ objects.push({
3761
+ name: "line",
3762
+ type: "element",
3763
+ attributes: {
3764
+ x1: (x - TICK_SIZE_PX).toString(),
3765
+ y1: startY.toString(),
3766
+ x2: (x + TICK_SIZE_PX).toString(),
3767
+ y2: startY.toString(),
3768
+ stroke: "#ffffff",
3769
+ "stroke-width": STROKE_WIDTH_PX.toString()
3770
+ },
3771
+ children: [],
3772
+ value: ""
3773
+ });
3774
+ objects.push({
3775
+ name: "line",
3776
+ type: "element",
3777
+ attributes: {
3778
+ x1: (x - TICK_SIZE_PX).toString(),
3779
+ y1: endY.toString(),
3780
+ x2: (x + TICK_SIZE_PX).toString(),
3781
+ y2: endY.toString(),
3782
+ stroke: "#ffffff",
3783
+ "stroke-width": STROKE_WIDTH_PX.toString()
3784
+ },
3785
+ children: [],
3786
+ value: ""
3787
+ });
3788
+ const midY = (startY + endY) / 2;
3789
+ const labelX = offsetX < 0 ? x - TICK_SIZE_PX - 4 : x + TICK_SIZE_PX + 4;
3790
+ objects.push({
3791
+ name: "text",
3792
+ type: "element",
3793
+ attributes: {
3794
+ x: labelX.toString(),
3795
+ y: midY.toString(),
3796
+ fill: "#ffffff",
3797
+ "font-size": LABEL_FONT_SIZE_PX.toString(),
3798
+ "font-family": "Arial, sans-serif",
3799
+ "text-anchor": offsetX < 0 ? "end" : "start",
3800
+ "dominant-baseline": "middle",
3801
+ class: "anchor-offset-label"
3802
+ },
3803
+ children: [
3804
+ {
3805
+ type: "text",
3806
+ value: `Y: ${offsetMm.toFixed(2)}mm`,
3807
+ name: "",
3808
+ attributes: {},
3809
+ children: []
3810
+ }
3811
+ ],
3812
+ value: ""
3813
+ });
3814
+ return objects;
3815
+ }
3816
+
3817
+ // lib/pcb/svg-object-fns/create-svg-objects-from-pcb-board.ts
3818
+ function createSvgObjectsFromPcbBoard(pcbBoard, ctx) {
3819
+ const { transform, colorMap: colorMap2, showSolderMask, circuitJson } = ctx;
3820
+ const { width, height, center, outline } = pcbBoard;
3821
+ let path;
3822
+ if (outline && Array.isArray(outline) && outline.length >= 3) {
3823
+ path = outline.map((point, index) => {
3824
+ const [x, y] = applyToPoint22(transform, [point.x, point.y]);
3825
+ return index === 0 ? `M ${x} ${y}` : `L ${x} ${y}`;
3826
+ }).join(" ");
3827
+ } else {
3828
+ const halfWidth = width / 2;
3829
+ const halfHeight = height / 2;
3830
+ const topLeft = applyToPoint22(transform, [
3831
+ center.x - halfWidth,
3832
+ center.y - halfHeight
3833
+ ]);
3834
+ const topRight = applyToPoint22(transform, [
3835
+ center.x + halfWidth,
3836
+ center.y - halfHeight
3837
+ ]);
3838
+ const bottomRight = applyToPoint22(transform, [
3839
+ center.x + halfWidth,
3840
+ center.y + halfHeight
3841
+ ]);
3842
+ const bottomLeft = applyToPoint22(transform, [
3843
+ center.x - halfWidth,
3844
+ center.y + halfHeight
3845
+ ]);
3846
+ path = `M ${topLeft[0]} ${topLeft[1]} L ${topRight[0]} ${topRight[1]} L ${bottomRight[0]} ${bottomRight[1]} L ${bottomLeft[0]} ${bottomLeft[1]}`;
3847
+ }
3848
+ path += " Z";
3849
+ const svgObjects = [];
3850
+ if (showSolderMask) {
3851
+ const layer = ctx.layer ?? "top";
3852
+ const maskLayer = layer === "bottom" ? "soldermask-bottom" : "soldermask-top";
3853
+ svgObjects.push({
3854
+ name: "path",
3855
+ type: "element",
3856
+ value: "",
3857
+ children: [],
3858
+ attributes: {
3859
+ class: "pcb-board-soldermask",
3860
+ d: path,
3861
+ fill: colorMap2.soldermask.top,
3862
+ "fill-opacity": "0.8",
3863
+ stroke: "none",
3864
+ "data-type": "pcb_soldermask",
3865
+ "data-pcb-layer": maskLayer
3866
+ }
3867
+ });
3868
+ }
3869
+ svgObjects.push({
3870
+ name: "path",
3871
+ type: "element",
3872
+ value: "",
3873
+ children: [],
3874
+ attributes: {
3875
+ class: "pcb-board",
3876
+ d: path,
3877
+ fill: "none",
3878
+ stroke: colorMap2.boardOutline,
3879
+ "stroke-width": (0.1 * Math.abs(transform.a)).toString(),
3880
+ "data-type": "pcb_board",
3881
+ "data-pcb-layer": "board"
3882
+ }
3883
+ });
3884
+ if (ctx.showAnchorOffsets && circuitJson) {
3885
+ const panel = circuitJson.find(
3886
+ (elm) => elm.type === "pcb_panel"
3887
+ );
3888
+ if (panel) {
3889
+ const panelCenter = panel.center ?? { x: 0, y: 0 };
3890
+ svgObjects.push(
3891
+ ...createAnchorOffsetIndicators({
3892
+ groupAnchorPosition: panelCenter,
3893
+ componentPosition: center,
3894
+ transform,
3895
+ componentWidth: width,
3896
+ componentHeight: height
3897
+ })
3898
+ );
3899
+ }
3900
+ }
3901
+ return svgObjects;
3902
+ }
3903
+
3904
+ // lib/pcb/svg-object-fns/create-svg-objects-from-pcb-panel.ts
3905
+ import { applyToPoint as applyToPoint23 } from "transformation-matrix";
3906
+ function createSvgObjectsFromPcbPanel(pcbPanel, ctx) {
3907
+ const { transform, colorMap: colorMap2, showSolderMask } = ctx;
3908
+ const width = Number(pcbPanel.width);
3909
+ const height = Number(pcbPanel.height);
3910
+ const center = pcbPanel.center ?? { x: width / 2, y: height / 2 };
3911
+ const halfWidth = width / 2;
3912
+ const halfHeight = height / 2;
3913
+ const topLeft = applyToPoint23(transform, [
3914
+ center.x - halfWidth,
3915
+ center.y - halfHeight
3916
+ ]);
3917
+ const topRight = applyToPoint23(transform, [
3918
+ center.x + halfWidth,
3919
+ center.y - halfHeight
3920
+ ]);
3921
+ const bottomRight = applyToPoint23(transform, [
3922
+ center.x + halfWidth,
3923
+ center.y + halfHeight
3924
+ ]);
3925
+ const bottomLeft = applyToPoint23(transform, [
3926
+ center.x - halfWidth,
3927
+ center.y + halfHeight
3928
+ ]);
3929
+ const path = `M ${topLeft[0]} ${topLeft[1]} L ${topRight[0]} ${topRight[1]} L ${bottomRight[0]} ${bottomRight[1]} L ${bottomLeft[0]} ${bottomLeft[1]} Z`;
3930
+ const isCoveredWithSolderMask = pcbPanel.covered_with_solder_mask !== false;
3931
+ const shouldShowSolderMask = Boolean(
3932
+ showSolderMask && isCoveredWithSolderMask
3933
+ );
3934
+ return [
3935
+ {
3936
+ name: "path",
3937
+ type: "element",
3938
+ value: "",
3939
+ children: [],
3940
+ attributes: {
3941
+ class: "pcb-panel",
3942
+ d: path,
3943
+ fill: "none",
3944
+ stroke: colorMap2.boardOutline,
3945
+ "stroke-width": (0.1 * Math.abs(transform.a)).toString(),
3946
+ "data-type": "pcb_panel",
3947
+ "data-pcb-layer": "board"
3948
+ }
3949
+ }
3950
+ ];
3951
+ }
3952
+
3953
+ // lib/pcb/svg-object-fns/create-svg-objects-from-pcb-via.ts
3677
3954
  import { applyToPoint as applyToPoint24 } from "transformation-matrix";
3955
+ function createSvgObjectsFromPcbVia(hole, ctx) {
3956
+ const { transform, colorMap: colorMap2 } = ctx;
3957
+ const [x, y] = applyToPoint24(transform, [hole.x, hole.y]);
3958
+ const scaledOuterWidth = hole.outer_diameter * Math.abs(transform.a);
3959
+ const scaledOuterHeight = hole.outer_diameter * Math.abs(transform.a);
3960
+ const scaledHoleWidth = hole.hole_diameter * Math.abs(transform.a);
3961
+ const scaledHoleHeight = hole.hole_diameter * Math.abs(transform.a);
3962
+ const outerRadius = Math.min(scaledOuterWidth, scaledOuterHeight) / 2;
3963
+ const innerRadius = Math.min(scaledHoleWidth, scaledHoleHeight) / 2;
3964
+ return {
3965
+ name: "g",
3966
+ type: "element",
3967
+ attributes: {
3968
+ "data-type": "pcb_via",
3969
+ "data-pcb-layer": "through"
3970
+ },
3971
+ children: [
3972
+ {
3973
+ name: "circle",
3974
+ type: "element",
3975
+ attributes: {
3976
+ class: "pcb-hole-outer",
3977
+ fill: colorMap2.copper.top,
3978
+ cx: x.toString(),
3979
+ cy: y.toString(),
3980
+ r: outerRadius.toString(),
3981
+ "data-type": "pcb_via",
3982
+ "data-pcb-layer": "top"
3983
+ }
3984
+ },
3985
+ {
3986
+ name: "circle",
3987
+ type: "element",
3988
+ attributes: {
3989
+ class: "pcb-hole-inner",
3990
+ fill: colorMap2.drill,
3991
+ cx: x.toString(),
3992
+ cy: y.toString(),
3993
+ r: innerRadius.toString(),
3994
+ "data-type": "pcb_via",
3995
+ "data-pcb-layer": "drill"
3996
+ }
3997
+ }
3998
+ ]
3999
+ };
4000
+ }
4001
+
4002
+ // lib/pcb/svg-object-fns/create-svg-objects-from-pcb-hole.ts
4003
+ import { applyToPoint as applyToPoint25 } from "transformation-matrix";
3678
4004
  function createSvgObjectsFromPcbHole(hole, ctx) {
3679
4005
  const { transform, colorMap: colorMap2, showSolderMask } = ctx;
3680
4006
  const layer = ctx.layer ?? "top";
3681
- const [x, y] = applyToPoint24(transform, [hole.x, hole.y]);
4007
+ const [x, y] = applyToPoint25(transform, [hole.x, hole.y]);
3682
4008
  const isCoveredWithSolderMask = Boolean(hole.is_covered_with_solder_mask);
3683
4009
  const soldermaskMargin = (hole.soldermask_margin ?? 0) * Math.abs(transform.a);
3684
4010
  const shouldShowSolderMask = showSolderMask && isCoveredWithSolderMask && soldermaskMargin !== 0;
@@ -4175,7 +4501,7 @@ import {
4175
4501
  getFullConnectivityMapFromCircuitJson
4176
4502
  } from "circuit-json-to-connectivity-map";
4177
4503
  import "svgson";
4178
- import { applyToPoint as applyToPoint25 } from "transformation-matrix";
4504
+ import { applyToPoint as applyToPoint26 } from "transformation-matrix";
4179
4505
 
4180
4506
  // lib/pcb/create-svg-objects-from-pcb-rats-nest/get-element-position.ts
4181
4507
  import { su } from "@tscircuit/circuit-json-util";
@@ -4255,11 +4581,11 @@ function createSvgObjectsForRatsNest(circuitJson, ctx) {
4255
4581
  });
4256
4582
  const svgObjects = [];
4257
4583
  for (const line of ratsNestLines) {
4258
- const transformedStart = applyToPoint25(transform, [
4584
+ const transformedStart = applyToPoint26(transform, [
4259
4585
  line.startPoint.x,
4260
4586
  line.startPoint.y
4261
4587
  ]);
4262
- const transformedEnd = applyToPoint25(transform, [
4588
+ const transformedEnd = applyToPoint26(transform, [
4263
4589
  line.endPoint.x,
4264
4590
  line.endPoint.y
4265
4591
  ]);
@@ -4287,7 +4613,7 @@ function createSvgObjectsForRatsNest(circuitJson, ctx) {
4287
4613
 
4288
4614
  // lib/pcb/svg-object-fns/create-svg-objects-from-pcb-cutout.ts
4289
4615
  import {
4290
- applyToPoint as applyToPoint26,
4616
+ applyToPoint as applyToPoint27,
4291
4617
  compose as compose3,
4292
4618
  rotate as rotate3,
4293
4619
  translate as translate3,
@@ -4297,7 +4623,7 @@ function createSvgObjectsFromPcbCutout(cutout, ctx) {
4297
4623
  const { transform, colorMap: colorMap2 } = ctx;
4298
4624
  if (cutout.shape === "rect") {
4299
4625
  const rectCutout = cutout;
4300
- const [cx, cy] = applyToPoint26(transform, [
4626
+ const [cx, cy] = applyToPoint27(transform, [
4301
4627
  rectCutout.center.x,
4302
4628
  rectCutout.center.y
4303
4629
  ]);
@@ -4339,7 +4665,7 @@ function createSvgObjectsFromPcbCutout(cutout, ctx) {
4339
4665
  }
4340
4666
  if (cutout.shape === "circle") {
4341
4667
  const circleCutout = cutout;
4342
- const [cx, cy] = applyToPoint26(transform, [
4668
+ const [cx, cy] = applyToPoint27(transform, [
4343
4669
  circleCutout.center.x,
4344
4670
  circleCutout.center.y
4345
4671
  ]);
@@ -4366,7 +4692,7 @@ function createSvgObjectsFromPcbCutout(cutout, ctx) {
4366
4692
  const polygonCutout = cutout;
4367
4693
  if (!polygonCutout.points || polygonCutout.points.length === 0) return [];
4368
4694
  const transformedPoints = polygonCutout.points.map(
4369
- (p) => applyToPoint26(transform, [p.x, p.y])
4695
+ (p) => applyToPoint27(transform, [p.x, p.y])
4370
4696
  );
4371
4697
  const pointsString = transformedPoints.map((p) => `${p[0]},${p[1]}`).join(" ");
4372
4698
  return [
@@ -4390,7 +4716,7 @@ function createSvgObjectsFromPcbCutout(cutout, ctx) {
4390
4716
 
4391
4717
  // lib/pcb/svg-object-fns/create-svg-objects-from-pcb-copper-pour.ts
4392
4718
  import {
4393
- applyToPoint as applyToPoint28,
4719
+ applyToPoint as applyToPoint29,
4394
4720
  compose as compose4,
4395
4721
  rotate as rotate4,
4396
4722
  toString as matrixToString7,
@@ -4398,11 +4724,11 @@ import {
4398
4724
  } from "transformation-matrix";
4399
4725
 
4400
4726
  // lib/utils/ring-to-path-d.ts
4401
- import { applyToPoint as applyToPoint27 } from "transformation-matrix";
4727
+ import { applyToPoint as applyToPoint28 } from "transformation-matrix";
4402
4728
  function ringToPathD(vertices, transform) {
4403
4729
  if (vertices.length === 0) return "";
4404
4730
  const transformedVertices = vertices.map((v) => {
4405
- const [x, y] = applyToPoint27(transform, [v.x, v.y]);
4731
+ const [x, y] = applyToPoint28(transform, [v.x, v.y]);
4406
4732
  return { ...v, x, y };
4407
4733
  });
4408
4734
  let d = `M ${transformedVertices[0].x} ${transformedVertices[0].y}`;
@@ -4491,7 +4817,7 @@ function createSvgObjectsFromPcbCopperPour(pour, ctx) {
4491
4817
  const maskOverlayColor = layer === "bottom" ? colorMap2.soldermaskOverCopper.bottom : colorMap2.soldermaskOverCopper.top;
4492
4818
  const maskOverlayOpacity = "0.9";
4493
4819
  if (pour.shape === "rect") {
4494
- const [cx, cy] = applyToPoint28(transform, [pour.center.x, pour.center.y]);
4820
+ const [cx, cy] = applyToPoint29(transform, [pour.center.x, pour.center.y]);
4495
4821
  const scaledWidth = pour.width * Math.abs(transform.a);
4496
4822
  const scaledHeight = pour.height * Math.abs(transform.d);
4497
4823
  const svgRotation = -(pour.rotation ?? 0);
@@ -4543,7 +4869,7 @@ function createSvgObjectsFromPcbCopperPour(pour, ctx) {
4543
4869
  if (pour.shape === "polygon") {
4544
4870
  if (!pour.points || pour.points.length === 0) return [];
4545
4871
  const transformedPoints = pour.points.map(
4546
- (p) => applyToPoint28(transform, [p.x, p.y])
4872
+ (p) => applyToPoint29(transform, [p.x, p.y])
4547
4873
  );
4548
4874
  const pointsString = transformedPoints.map((p) => `${p[0]},${p[1]}`).join(" ");
4549
4875
  const copperPolygon = {
@@ -4681,398 +5007,92 @@ function createSvgObjectsForPcbGrid({
4681
5007
  stroke: gridLineColor,
4682
5008
  "stroke-width": "1",
4683
5009
  "shape-rendering": "crispEdges"
4684
- },
4685
- children: []
4686
- }
4687
- ];
4688
- const defs = {
4689
- name: "defs",
4690
- type: "element",
4691
- value: "",
4692
- attributes: {},
4693
- children: [
4694
- {
4695
- name: "pattern",
4696
- type: "element",
4697
- value: "",
4698
- attributes: {
4699
- id: GRID_PATTERN_ID,
4700
- width: hasMajorGrid ? majorCellSize.toString() : gridCellSize.toString(),
4701
- height: hasMajorGrid ? majorCellSize.toString() : gridCellSize.toString(),
4702
- patternUnits: "userSpaceOnUse"
4703
- },
4704
- children: patternChildren
4705
- }
4706
- ]
4707
- };
4708
- const rect = {
4709
- name: "rect",
4710
- type: "element",
4711
- value: "",
4712
- attributes: {
4713
- x: "0",
4714
- y: "0",
4715
- width: svgWidth.toString(),
4716
- height: svgHeight.toString(),
4717
- fill: `url(#${GRID_PATTERN_ID})`,
4718
- "pointer-events": "none",
4719
- "data-type": "pcb_grid",
4720
- "data-pcb-layer": "global"
4721
- },
4722
- children: []
4723
- };
4724
- return { defs, rect };
4725
- }
4726
- function createMajorGridPatternChildren(cellSize, majorCellSize, lineColor, majorLineColor) {
4727
- const children = [];
4728
- const steps = Math.round(majorCellSize / cellSize);
4729
- for (let step = 0; step < steps; step += 1) {
4730
- const offset = Number((step * cellSize).toFixed(6));
4731
- const offsetString = offset.toString();
4732
- const color = step === 0 ? majorLineColor : lineColor;
4733
- const majorSizeString = majorCellSize.toString();
4734
- children.push({
4735
- name: "line",
4736
- type: "element",
4737
- value: "",
4738
- attributes: {
4739
- x1: offsetString,
4740
- y1: "0",
4741
- x2: offsetString,
4742
- y2: majorSizeString,
4743
- stroke: color,
4744
- "stroke-width": "1",
4745
- "shape-rendering": "crispEdges"
4746
- },
4747
- children: []
4748
- });
4749
- children.push({
4750
- name: "line",
4751
- type: "element",
4752
- value: "",
4753
- attributes: {
4754
- x1: "0",
4755
- y1: offsetString,
4756
- x2: majorSizeString,
4757
- y2: offsetString,
4758
- stroke: color,
4759
- "stroke-width": "1",
4760
- "shape-rendering": "crispEdges"
4761
- },
4762
- children: []
4763
- });
4764
- }
4765
- return children;
4766
- }
4767
-
4768
- // lib/pcb/svg-object-fns/create-svg-objects-from-pcb-component.ts
4769
- import { applyToPoint as applyToPoint30 } from "transformation-matrix";
4770
-
4771
- // lib/utils/create-pcb-component-anchor-offset-indicators.ts
4772
- import { applyToPoint as applyToPoint29 } from "transformation-matrix";
4773
- var OFFSET_THRESHOLD_MM = 0.01;
4774
- var TICK_SIZE_PX = 4;
4775
- var LABEL_GAP_PX = 8;
4776
- var LABEL_FONT_SIZE_PX = 11;
4777
- var STROKE_WIDTH_PX = 1;
4778
- var ANCHOR_MARKER_SIZE_PX = 5;
4779
- var ANCHOR_MARKER_STROKE_WIDTH_PX = 1.5;
4780
- var COMPONENT_GAP_PX = 15;
4781
- var COMPONENT_SIDE_GAP_PX = 10;
4782
- var DISTANCE_MULTIPLIER = 0.2;
4783
- var MAX_OFFSET_PX = 50;
4784
- function createAnchorOffsetIndicators(params) {
4785
- const {
4786
- groupAnchorPosition,
4787
- componentPosition,
4788
- transform,
4789
- componentWidth = 0,
4790
- componentHeight = 0
4791
- } = params;
4792
- const objects = [];
4793
- const [screenGroupAnchorX, screenGroupAnchorY] = applyToPoint29(transform, [
4794
- groupAnchorPosition.x,
4795
- groupAnchorPosition.y
4796
- ]);
4797
- const [screenComponentX, screenComponentY] = applyToPoint29(transform, [
4798
- componentPosition.x,
4799
- componentPosition.y
4800
- ]);
4801
- const offsetX = componentPosition.x - groupAnchorPosition.x;
4802
- const offsetY = componentPosition.y - groupAnchorPosition.y;
4803
- const scale9 = Math.abs(transform.a);
4804
- const screenComponentWidth = componentWidth * scale9;
4805
- const screenComponentHeight = componentHeight * scale9;
4806
- objects.push(createAnchorMarker(screenGroupAnchorX, screenGroupAnchorY));
4807
- objects.push({
4808
- name: "line",
4809
- type: "element",
4810
- attributes: {
4811
- x1: screenGroupAnchorX.toString(),
4812
- y1: screenGroupAnchorY.toString(),
4813
- x2: screenComponentX.toString(),
4814
- y2: screenComponentY.toString(),
4815
- stroke: "#ffffff",
4816
- "stroke-width": "0.5",
4817
- "stroke-dasharray": "3,3",
4818
- opacity: "0.5",
4819
- class: "anchor-offset-connector"
4820
- },
4821
- children: [],
4822
- value: ""
4823
- });
4824
- objects.push({
4825
- name: "circle",
4826
- type: "element",
4827
- attributes: {
4828
- cx: screenComponentX.toString(),
4829
- cy: screenComponentY.toString(),
4830
- r: "2",
4831
- fill: "#ffffff",
4832
- opacity: "0.7",
4833
- class: "anchor-offset-component-marker"
4834
- },
4835
- children: [],
4836
- value: ""
4837
- });
4838
- const yDistance = Math.abs(screenComponentY - screenGroupAnchorY);
4839
- const xDistance = Math.abs(screenComponentX - screenGroupAnchorX);
4840
- const totalDistance = Math.sqrt(xDistance * xDistance + yDistance * yDistance);
4841
- const componentHeightOffset = screenComponentHeight / 2 + COMPONENT_GAP_PX;
4842
- const dynamicOffset = Math.max(
4843
- componentHeightOffset,
4844
- Math.min(MAX_OFFSET_PX, totalDistance * DISTANCE_MULTIPLIER)
4845
- );
4846
- const horizontalLineY = offsetY > 0 ? screenComponentY - dynamicOffset : screenComponentY + dynamicOffset;
4847
- const componentWidthOffset = screenComponentWidth / 2 + COMPONENT_SIDE_GAP_PX;
4848
- const verticalLineX = offsetX > 0 ? screenComponentX + componentWidthOffset : screenComponentX - componentWidthOffset;
4849
- if (Math.abs(offsetX) > OFFSET_THRESHOLD_MM) {
4850
- objects.push(
4851
- ...createHorizontalDimension({
4852
- startX: screenGroupAnchorX,
4853
- endX: screenComponentX,
4854
- y: horizontalLineY,
4855
- offsetMm: offsetX,
4856
- offsetY
4857
- })
4858
- );
4859
- }
4860
- if (Math.abs(offsetY) > OFFSET_THRESHOLD_MM) {
4861
- objects.push(
4862
- ...createVerticalDimension({
4863
- x: verticalLineX,
4864
- startY: screenGroupAnchorY,
4865
- endY: screenComponentY,
4866
- offsetMm: -offsetY,
4867
- offsetX
4868
- })
4869
- );
4870
- }
4871
- return objects;
4872
- }
4873
- function createAnchorMarker(x, y) {
4874
- return {
4875
- name: "g",
5010
+ },
5011
+ children: []
5012
+ }
5013
+ ];
5014
+ const defs = {
5015
+ name: "defs",
4876
5016
  type: "element",
4877
- attributes: {
4878
- class: "anchor-offset-marker",
4879
- "data-type": "anchor_offset_marker"
4880
- },
5017
+ value: "",
5018
+ attributes: {},
4881
5019
  children: [
4882
5020
  {
4883
- name: "line",
4884
- type: "element",
4885
- attributes: {
4886
- x1: x.toString(),
4887
- y1: (y - ANCHOR_MARKER_SIZE_PX).toString(),
4888
- x2: x.toString(),
4889
- y2: (y + ANCHOR_MARKER_SIZE_PX).toString(),
4890
- stroke: "#ffffff",
4891
- "stroke-width": ANCHOR_MARKER_STROKE_WIDTH_PX.toString(),
4892
- "stroke-linecap": "round"
4893
- },
4894
- children: [],
4895
- value: ""
4896
- },
4897
- {
4898
- name: "line",
5021
+ name: "pattern",
4899
5022
  type: "element",
5023
+ value: "",
4900
5024
  attributes: {
4901
- x1: (x - ANCHOR_MARKER_SIZE_PX).toString(),
4902
- y1: y.toString(),
4903
- x2: (x + ANCHOR_MARKER_SIZE_PX).toString(),
4904
- y2: y.toString(),
4905
- stroke: "#ffffff",
4906
- "stroke-width": ANCHOR_MARKER_STROKE_WIDTH_PX.toString(),
4907
- "stroke-linecap": "round"
5025
+ id: GRID_PATTERN_ID,
5026
+ width: hasMajorGrid ? majorCellSize.toString() : gridCellSize.toString(),
5027
+ height: hasMajorGrid ? majorCellSize.toString() : gridCellSize.toString(),
5028
+ patternUnits: "userSpaceOnUse"
4908
5029
  },
4909
- children: [],
4910
- value: ""
5030
+ children: patternChildren
4911
5031
  }
4912
- ],
4913
- value: ""
5032
+ ]
4914
5033
  };
4915
- }
4916
- function createHorizontalDimension({
4917
- startX,
4918
- endX,
4919
- y,
4920
- offsetMm,
4921
- offsetY
4922
- }) {
4923
- const objects = [];
4924
- objects.push({
4925
- name: "line",
4926
- type: "element",
4927
- attributes: {
4928
- x1: startX.toString(),
4929
- y1: y.toString(),
4930
- x2: endX.toString(),
4931
- y2: y.toString(),
4932
- stroke: "#ffffff",
4933
- "stroke-width": STROKE_WIDTH_PX.toString(),
4934
- class: "anchor-offset-dimension-x"
4935
- },
4936
- children: [],
4937
- value: ""
4938
- });
4939
- objects.push({
4940
- name: "line",
4941
- type: "element",
4942
- attributes: {
4943
- x1: startX.toString(),
4944
- y1: (y - TICK_SIZE_PX).toString(),
4945
- x2: startX.toString(),
4946
- y2: (y + TICK_SIZE_PX).toString(),
4947
- stroke: "#ffffff",
4948
- "stroke-width": STROKE_WIDTH_PX.toString()
4949
- },
4950
- children: [],
4951
- value: ""
4952
- });
4953
- objects.push({
4954
- name: "line",
4955
- type: "element",
4956
- attributes: {
4957
- x1: endX.toString(),
4958
- y1: (y - TICK_SIZE_PX).toString(),
4959
- x2: endX.toString(),
4960
- y2: (y + TICK_SIZE_PX).toString(),
4961
- stroke: "#ffffff",
4962
- "stroke-width": STROKE_WIDTH_PX.toString()
4963
- },
4964
- children: [],
4965
- value: ""
4966
- });
4967
- const midX = (startX + endX) / 2;
4968
- const labelY = offsetY > 0 ? y - TICK_SIZE_PX - LABEL_GAP_PX : y + TICK_SIZE_PX + LABEL_GAP_PX;
4969
- objects.push({
4970
- name: "text",
5034
+ const rect = {
5035
+ name: "rect",
4971
5036
  type: "element",
5037
+ value: "",
4972
5038
  attributes: {
4973
- x: midX.toString(),
4974
- y: labelY.toString(),
4975
- fill: "#ffffff",
4976
- "font-size": LABEL_FONT_SIZE_PX.toString(),
4977
- "font-family": "Arial, sans-serif",
4978
- "text-anchor": "middle",
4979
- "dominant-baseline": offsetY > 0 ? "baseline" : "hanging",
4980
- class: "anchor-offset-label"
5039
+ x: "0",
5040
+ y: "0",
5041
+ width: svgWidth.toString(),
5042
+ height: svgHeight.toString(),
5043
+ fill: `url(#${GRID_PATTERN_ID})`,
5044
+ "pointer-events": "none",
5045
+ "data-type": "pcb_grid",
5046
+ "data-pcb-layer": "global"
4981
5047
  },
4982
- children: [
4983
- {
4984
- type: "text",
4985
- value: `X: ${offsetMm.toFixed(2)}mm`,
4986
- name: "",
4987
- attributes: {},
4988
- children: []
4989
- }
4990
- ],
4991
- value: ""
4992
- });
4993
- return objects;
5048
+ children: []
5049
+ };
5050
+ return { defs, rect };
4994
5051
  }
4995
- function createVerticalDimension({
4996
- x,
4997
- startY,
4998
- endY,
4999
- offsetMm,
5000
- offsetX
5001
- }) {
5002
- const objects = [];
5003
- objects.push({
5004
- name: "line",
5005
- type: "element",
5006
- attributes: {
5007
- x1: x.toString(),
5008
- y1: startY.toString(),
5009
- x2: x.toString(),
5010
- y2: endY.toString(),
5011
- stroke: "#ffffff",
5012
- "stroke-width": STROKE_WIDTH_PX.toString(),
5013
- class: "anchor-offset-dimension-y"
5014
- },
5015
- children: [],
5016
- value: ""
5017
- });
5018
- objects.push({
5019
- name: "line",
5020
- type: "element",
5021
- attributes: {
5022
- x1: (x - TICK_SIZE_PX).toString(),
5023
- y1: startY.toString(),
5024
- x2: (x + TICK_SIZE_PX).toString(),
5025
- y2: startY.toString(),
5026
- stroke: "#ffffff",
5027
- "stroke-width": STROKE_WIDTH_PX.toString()
5028
- },
5029
- children: [],
5030
- value: ""
5031
- });
5032
- objects.push({
5033
- name: "line",
5034
- type: "element",
5035
- attributes: {
5036
- x1: (x - TICK_SIZE_PX).toString(),
5037
- y1: endY.toString(),
5038
- x2: (x + TICK_SIZE_PX).toString(),
5039
- y2: endY.toString(),
5040
- stroke: "#ffffff",
5041
- "stroke-width": STROKE_WIDTH_PX.toString()
5042
- },
5043
- children: [],
5044
- value: ""
5045
- });
5046
- const midY = (startY + endY) / 2;
5047
- const labelX = offsetX < 0 ? x - TICK_SIZE_PX - 4 : x + TICK_SIZE_PX + 4;
5048
- objects.push({
5049
- name: "text",
5050
- type: "element",
5051
- attributes: {
5052
- x: labelX.toString(),
5053
- y: midY.toString(),
5054
- fill: "#ffffff",
5055
- "font-size": LABEL_FONT_SIZE_PX.toString(),
5056
- "font-family": "Arial, sans-serif",
5057
- "text-anchor": offsetX < 0 ? "end" : "start",
5058
- "dominant-baseline": "middle",
5059
- class: "anchor-offset-label"
5060
- },
5061
- children: [
5062
- {
5063
- type: "text",
5064
- value: `Y: ${offsetMm.toFixed(2)}mm`,
5065
- name: "",
5066
- attributes: {},
5067
- children: []
5068
- }
5069
- ],
5070
- value: ""
5071
- });
5072
- return objects;
5052
+ function createMajorGridPatternChildren(cellSize, majorCellSize, lineColor, majorLineColor) {
5053
+ const children = [];
5054
+ const steps = Math.round(majorCellSize / cellSize);
5055
+ for (let step = 0; step < steps; step += 1) {
5056
+ const offset = Number((step * cellSize).toFixed(6));
5057
+ const offsetString = offset.toString();
5058
+ const color = step === 0 ? majorLineColor : lineColor;
5059
+ const majorSizeString = majorCellSize.toString();
5060
+ children.push({
5061
+ name: "line",
5062
+ type: "element",
5063
+ value: "",
5064
+ attributes: {
5065
+ x1: offsetString,
5066
+ y1: "0",
5067
+ x2: offsetString,
5068
+ y2: majorSizeString,
5069
+ stroke: color,
5070
+ "stroke-width": "1",
5071
+ "shape-rendering": "crispEdges"
5072
+ },
5073
+ children: []
5074
+ });
5075
+ children.push({
5076
+ name: "line",
5077
+ type: "element",
5078
+ value: "",
5079
+ attributes: {
5080
+ x1: "0",
5081
+ y1: offsetString,
5082
+ x2: majorSizeString,
5083
+ y2: offsetString,
5084
+ stroke: color,
5085
+ "stroke-width": "1",
5086
+ "shape-rendering": "crispEdges"
5087
+ },
5088
+ children: []
5089
+ });
5090
+ }
5091
+ return children;
5073
5092
  }
5074
5093
 
5075
5094
  // lib/pcb/svg-object-fns/create-svg-objects-from-pcb-component.ts
5095
+ import { applyToPoint as applyToPoint30 } from "transformation-matrix";
5076
5096
  function createSvgObjectsFromPcbComponent(component, ctx) {
5077
5097
  const { transform, circuitJson } = ctx;
5078
5098
  const { center, width, height, rotation = 0 } = component;
@@ -5220,7 +5240,7 @@ function getSoftwareUsedString(circuitJson) {
5220
5240
  var package_default = {
5221
5241
  name: "circuit-to-svg",
5222
5242
  type: "module",
5223
- version: "0.0.281",
5243
+ version: "0.0.283",
5224
5244
  description: "Convert Circuit JSON to SVG",
5225
5245
  main: "dist/index.js",
5226
5246
  files: [
@@ -5244,12 +5264,12 @@ var package_default = {
5244
5264
  "bun-match-svg": "^0.0.12",
5245
5265
  esbuild: "^0.20.2",
5246
5266
  "performance-now": "^2.1.0",
5247
- "circuit-json": "^0.0.319",
5267
+ "circuit-json": "^0.0.327",
5248
5268
  react: "19.1.0",
5249
5269
  "react-cosmos": "7.0.0",
5250
5270
  "react-cosmos-plugin-vite": "7.0.0",
5251
5271
  "react-dom": "19.1.0",
5252
- tscircuit: "^0.0.937",
5272
+ tscircuit: "^0.0.1018",
5253
5273
  tsup: "^8.0.2",
5254
5274
  typescript: "^5.4.5",
5255
5275
  "vite-tsconfig-paths": "^5.0.1"