circuit-to-svg 0.0.253 → 0.0.255

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
@@ -627,7 +627,9 @@ function createSvgObjectsFromPcbFabricationNoteDimension(dimension, ctx) {
627
627
  arrow_size,
628
628
  layer,
629
629
  pcb_component_id,
630
- pcb_fabrication_note_dimension_id
630
+ pcb_fabrication_note_dimension_id,
631
+ offset_distance,
632
+ offset_direction
631
633
  } = dimension;
632
634
  if (layerFilter && layer && layer !== layerFilter) return [];
633
635
  if (!from || !to || typeof from !== "object" || typeof to !== "object") {
@@ -658,17 +660,30 @@ function createSvgObjectsFromPcbFabricationNoteDimension(dimension, ctx) {
658
660
  return [];
659
661
  }
660
662
  const perpendicular = { x: -direction.y, y: direction.x };
663
+ const hasOffsetDirection = offset_direction && typeof offset_direction.x === "number" && typeof offset_direction.y === "number";
664
+ const normalizedOffsetDirection = hasOffsetDirection ? normalize({ x: offset_direction.x, y: offset_direction.y }) : { x: 0, y: 0 };
665
+ const offsetMagnitude = typeof offset_distance === "number" ? offset_distance : 0;
666
+ const offsetVector = {
667
+ x: normalizedOffsetDirection.x * offsetMagnitude,
668
+ y: normalizedOffsetDirection.y * offsetMagnitude
669
+ };
670
+ const applyOffset = (point) => ({
671
+ x: point.x + offsetVector.x,
672
+ y: point.y + offsetVector.y
673
+ });
674
+ const fromOffset = applyOffset(from);
675
+ const toOffset = applyOffset(to);
661
676
  const arrowHalfWidth = arrowSize / 2;
662
677
  const fromBase = {
663
- x: from.x + direction.x * arrowSize,
664
- y: from.y + direction.y * arrowSize
678
+ x: fromOffset.x + direction.x * arrowSize,
679
+ y: fromOffset.y + direction.y * arrowSize
665
680
  };
666
681
  const toBase = {
667
- x: to.x - direction.x * arrowSize,
668
- y: to.y - direction.y * arrowSize
682
+ x: toOffset.x - direction.x * arrowSize,
683
+ y: toOffset.y - direction.y * arrowSize
669
684
  };
670
685
  const fromTriangle = [
671
- toScreen(from),
686
+ toScreen(fromOffset),
672
687
  toScreen({
673
688
  x: fromBase.x + perpendicular.x * arrowHalfWidth,
674
689
  y: fromBase.y + perpendicular.y * arrowHalfWidth
@@ -679,7 +694,7 @@ function createSvgObjectsFromPcbFabricationNoteDimension(dimension, ctx) {
679
694
  })
680
695
  ];
681
696
  const toTriangle = [
682
- toScreen(to),
697
+ toScreen(toOffset),
683
698
  toScreen({
684
699
  x: toBase.x + perpendicular.x * arrowHalfWidth,
685
700
  y: toBase.y + perpendicular.y * arrowHalfWidth
@@ -696,9 +711,34 @@ function createSvgObjectsFromPcbFabricationNoteDimension(dimension, ctx) {
696
711
  const [lineEndX, lineEndY] = applyToPoint6(transform, [toBase.x, toBase.y]);
697
712
  const strokeWidth = arrowSize / 5 * Math.abs(transform.a);
698
713
  const lineColor = color || "rgba(255,255,255,0.5)";
714
+ const extensionDirection = hasOffsetDirection && (Math.abs(normalizedOffsetDirection.x) > Number.EPSILON || Math.abs(normalizedOffsetDirection.y) > Number.EPSILON) ? normalizedOffsetDirection : perpendicular;
715
+ const extensionLength = offsetMagnitude + arrowSize;
716
+ const createExtensionLine = (anchor) => {
717
+ const endPoint = {
718
+ x: anchor.x + extensionDirection.x * extensionLength,
719
+ y: anchor.y + extensionDirection.y * extensionLength
720
+ };
721
+ const [startX, startY] = applyToPoint6(transform, [anchor.x, anchor.y]);
722
+ const [endX, endY] = applyToPoint6(transform, [endPoint.x, endPoint.y]);
723
+ return {
724
+ name: "path",
725
+ type: "element",
726
+ value: "",
727
+ attributes: {
728
+ d: `M ${startX} ${startY} L ${endX} ${endY}`,
729
+ stroke: lineColor,
730
+ fill: "none",
731
+ "stroke-width": strokeWidth.toString(),
732
+ "stroke-linecap": "round",
733
+ class: "pcb-fabrication-note-dimension-extension"
734
+ },
735
+ children: []
736
+ };
737
+ };
738
+ const extensionSegments = [createExtensionLine(from), createExtensionLine(to)];
699
739
  const midPoint = {
700
- x: (from.x + to.x) / 2,
701
- y: (from.y + to.y) / 2
740
+ x: (from.x + to.x) / 2 + offsetVector.x,
741
+ y: (from.y + to.y) / 2 + offsetVector.y
702
742
  };
703
743
  const textOffset = arrowSize * 1.5;
704
744
  const textPoint = {
@@ -706,8 +746,25 @@ function createSvgObjectsFromPcbFabricationNoteDimension(dimension, ctx) {
706
746
  y: midPoint.y + perpendicular.y * textOffset
707
747
  };
708
748
  const [textX, textY] = applyToPoint6(transform, [textPoint.x, textPoint.y]);
749
+ const [screenFromX, screenFromY] = applyToPoint6(transform, [
750
+ fromOffset.x,
751
+ fromOffset.y
752
+ ]);
753
+ const [screenToX, screenToY] = applyToPoint6(transform, [
754
+ toOffset.x,
755
+ toOffset.y
756
+ ]);
757
+ const screenDirection = normalize({
758
+ x: screenToX - screenFromX,
759
+ y: screenToY - screenFromY
760
+ });
761
+ let textAngle = Math.atan2(screenDirection.y, screenDirection.x) * 180 / Math.PI;
762
+ if (textAngle > 90 || textAngle < -90) {
763
+ textAngle += 180;
764
+ }
709
765
  const transformedFontSize = font_size * Math.abs(transform.a);
710
766
  const children = [
767
+ ...extensionSegments,
711
768
  {
712
769
  name: "path",
713
770
  type: "element",
@@ -758,7 +815,8 @@ function createSvgObjectsFromPcbFabricationNoteDimension(dimension, ctx) {
758
815
  "font-family": "Arial, sans-serif",
759
816
  "text-anchor": "middle",
760
817
  "dominant-baseline": "central",
761
- class: "pcb-fabrication-note-dimension-text"
818
+ class: "pcb-fabrication-note-dimension-text",
819
+ transform: `rotate(${textAngle} ${textX} ${textY})`
762
820
  },
763
821
  children: [
764
822
  {
@@ -1049,7 +1107,16 @@ function toPath2(points) {
1049
1107
  }
1050
1108
  function createSvgObjectsFromPcbNoteDimension(dimension, ctx) {
1051
1109
  const { transform } = ctx;
1052
- const { from, to, text, font_size = 1, color, arrow_size } = dimension;
1110
+ const {
1111
+ from,
1112
+ to,
1113
+ text,
1114
+ font_size = 1,
1115
+ color,
1116
+ arrow_size,
1117
+ offset_distance,
1118
+ offset_direction
1119
+ } = dimension;
1053
1120
  if (!from || !to) {
1054
1121
  console.error("Invalid pcb_note_dimension endpoints", { from, to });
1055
1122
  return [];
@@ -1063,17 +1130,30 @@ function createSvgObjectsFromPcbNoteDimension(dimension, ctx) {
1063
1130
  return [];
1064
1131
  }
1065
1132
  const perpendicular = { x: -direction.y, y: direction.x };
1133
+ const hasOffsetDirection = offset_direction && typeof offset_direction.x === "number" && typeof offset_direction.y === "number";
1134
+ const normalizedOffsetDirection = hasOffsetDirection ? normalize2({ x: offset_direction.x, y: offset_direction.y }) : { x: 0, y: 0 };
1135
+ const offsetMagnitude = typeof offset_distance === "number" ? offset_distance : 0;
1136
+ const offsetVector = {
1137
+ x: normalizedOffsetDirection.x * offsetMagnitude,
1138
+ y: normalizedOffsetDirection.y * offsetMagnitude
1139
+ };
1140
+ const applyOffset = (point) => ({
1141
+ x: point.x + offsetVector.x,
1142
+ y: point.y + offsetVector.y
1143
+ });
1144
+ const fromOffset = applyOffset(from);
1145
+ const toOffset = applyOffset(to);
1066
1146
  const arrowHalfWidth = arrow_size / 2;
1067
1147
  const fromBase = {
1068
- x: from.x + direction.x * arrow_size,
1069
- y: from.y + direction.y * arrow_size
1148
+ x: fromOffset.x + direction.x * arrow_size,
1149
+ y: fromOffset.y + direction.y * arrow_size
1070
1150
  };
1071
1151
  const toBase = {
1072
- x: to.x - direction.x * arrow_size,
1073
- y: to.y - direction.y * arrow_size
1152
+ x: toOffset.x - direction.x * arrow_size,
1153
+ y: toOffset.y - direction.y * arrow_size
1074
1154
  };
1075
1155
  const fromTriangle = [
1076
- toScreen(from),
1156
+ toScreen(fromOffset),
1077
1157
  toScreen({
1078
1158
  x: fromBase.x + perpendicular.x * arrowHalfWidth,
1079
1159
  y: fromBase.y + perpendicular.y * arrowHalfWidth
@@ -1084,7 +1164,7 @@ function createSvgObjectsFromPcbNoteDimension(dimension, ctx) {
1084
1164
  })
1085
1165
  ];
1086
1166
  const toTriangle = [
1087
- toScreen(to),
1167
+ toScreen(toOffset),
1088
1168
  toScreen({
1089
1169
  x: toBase.x + perpendicular.x * arrowHalfWidth,
1090
1170
  y: toBase.y + perpendicular.y * arrowHalfWidth
@@ -1101,9 +1181,34 @@ function createSvgObjectsFromPcbNoteDimension(dimension, ctx) {
1101
1181
  const [lineEndX, lineEndY] = applyToPoint7(transform, [toBase.x, toBase.y]);
1102
1182
  const strokeWidth = arrow_size / 5 * Math.abs(transform.a);
1103
1183
  const lineColor = color || colorMap.board.user_2;
1184
+ const extensionDirection = hasOffsetDirection && (Math.abs(normalizedOffsetDirection.x) > Number.EPSILON || Math.abs(normalizedOffsetDirection.y) > Number.EPSILON) ? normalizedOffsetDirection : perpendicular;
1185
+ const extensionLength = offsetMagnitude + arrow_size;
1186
+ const createExtensionLine = (anchor) => {
1187
+ const endPoint = {
1188
+ x: anchor.x + extensionDirection.x * extensionLength,
1189
+ y: anchor.y + extensionDirection.y * extensionLength
1190
+ };
1191
+ const [startX, startY] = applyToPoint7(transform, [anchor.x, anchor.y]);
1192
+ const [endX, endY] = applyToPoint7(transform, [endPoint.x, endPoint.y]);
1193
+ return {
1194
+ name: "path",
1195
+ type: "element",
1196
+ value: "",
1197
+ attributes: {
1198
+ d: `M ${startX} ${startY} L ${endX} ${endY}`,
1199
+ stroke: lineColor,
1200
+ fill: "none",
1201
+ "stroke-width": strokeWidth.toString(),
1202
+ "stroke-linecap": "round",
1203
+ class: "pcb-note-dimension-extension"
1204
+ },
1205
+ children: []
1206
+ };
1207
+ };
1208
+ const extensionSegments = [createExtensionLine(from), createExtensionLine(to)];
1104
1209
  const midPoint = {
1105
- x: (from.x + to.x) / 2,
1106
- y: (from.y + to.y) / 2
1210
+ x: (from.x + to.x) / 2 + offsetVector.x,
1211
+ y: (from.y + to.y) / 2 + offsetVector.y
1107
1212
  };
1108
1213
  const textOffset = arrow_size * 1.5;
1109
1214
  const textPoint = {
@@ -1111,8 +1216,25 @@ function createSvgObjectsFromPcbNoteDimension(dimension, ctx) {
1111
1216
  y: midPoint.y + perpendicular.y * textOffset
1112
1217
  };
1113
1218
  const [textX, textY] = applyToPoint7(transform, [textPoint.x, textPoint.y]);
1219
+ const [screenFromX, screenFromY] = applyToPoint7(transform, [
1220
+ fromOffset.x,
1221
+ fromOffset.y
1222
+ ]);
1223
+ const [screenToX, screenToY] = applyToPoint7(transform, [
1224
+ toOffset.x,
1225
+ toOffset.y
1226
+ ]);
1227
+ const screenDirection = normalize2({
1228
+ x: screenToX - screenFromX,
1229
+ y: screenToY - screenFromY
1230
+ });
1231
+ let textAngle = Math.atan2(screenDirection.y, screenDirection.x) * 180 / Math.PI;
1232
+ if (textAngle > 90 || textAngle < -90) {
1233
+ textAngle += 180;
1234
+ }
1114
1235
  const transformedFontSize = font_size * Math.abs(transform.a);
1115
1236
  const children = [
1237
+ ...extensionSegments,
1116
1238
  {
1117
1239
  name: "path",
1118
1240
  type: "element",
@@ -1163,7 +1285,8 @@ function createSvgObjectsFromPcbNoteDimension(dimension, ctx) {
1163
1285
  "font-family": "Arial, sans-serif",
1164
1286
  "text-anchor": "middle",
1165
1287
  "dominant-baseline": "central",
1166
- class: "pcb-note-dimension-text"
1288
+ class: "pcb-note-dimension-text",
1289
+ transform: `rotate(${textAngle} ${textX} ${textY})`
1167
1290
  },
1168
1291
  children: [
1169
1292
  {
@@ -1444,14 +1567,23 @@ function createSvgObjectsFromPcbPlatedHole(hole, ctx) {
1444
1567
  const scaledOuterHeight = hole.outer_height * Math.abs(transform.a);
1445
1568
  const scaledHoleWidth = hole.hole_width * Math.abs(transform.a);
1446
1569
  const scaledHoleHeight = hole.hole_height * Math.abs(transform.a);
1447
- const outerRadiusX = scaledOuterWidth / 2;
1448
- const outerRadiusY = scaledOuterHeight / 2;
1449
- const innerRadiusX = scaledHoleWidth / 2;
1450
- const innerRadiusY = scaledHoleHeight / 2;
1451
- const straightLength = scaledOuterHeight - scaledOuterWidth;
1452
1570
  const rotation = hole.ccw_rotation || 0;
1453
1571
  const outerTransform = rotation ? `translate(${x} ${y}) rotate(${-rotation})` : `translate(${x} ${y})`;
1454
1572
  const innerTransform = rotation ? `translate(${x} ${y}) rotate(${-rotation})` : `translate(${x} ${y})`;
1573
+ const createPillPath = (width, height) => {
1574
+ if (width > height) {
1575
+ const radius = height / 2;
1576
+ const straightLength = width - 2 * radius;
1577
+ return `M${-width / 2 + radius},${-radius} h${straightLength} a${radius},${radius} 0 0 1 0,${height} h${-straightLength} a${radius},${radius} 0 0 1 0,${-height} z`;
1578
+ } else if (height > width) {
1579
+ const radius = width / 2;
1580
+ const straightLength = height - 2 * radius;
1581
+ return `M${radius},${-height / 2 + radius} v${straightLength} a${radius},${radius} 0 0 1 ${-width},0 v${-straightLength} a${radius},${radius} 0 0 1 ${width},0 z`;
1582
+ } else {
1583
+ const radius = width / 2;
1584
+ return `M${-radius},0 a${radius},${radius} 0 0 1 ${width},0 a${radius},${radius} 0 0 1 ${-width},0 z`;
1585
+ }
1586
+ };
1455
1587
  return [
1456
1588
  {
1457
1589
  name: "g",
@@ -1468,7 +1600,7 @@ function createSvgObjectsFromPcbPlatedHole(hole, ctx) {
1468
1600
  attributes: {
1469
1601
  class: "pcb-hole-outer",
1470
1602
  fill: colorMap2.copper.top,
1471
- d: `M${-outerRadiusX},${-straightLength / 2} v${straightLength} a${outerRadiusX},${outerRadiusX} 0 0 0 ${scaledOuterWidth},0 v-${straightLength} a${outerRadiusX},${outerRadiusX} 0 0 0 -${scaledOuterWidth},0 z`,
1603
+ d: createPillPath(scaledOuterWidth, scaledOuterHeight),
1472
1604
  transform: outerTransform,
1473
1605
  "data-type": "pcb_plated_hole",
1474
1606
  "data-pcb-layer": copperLayer
@@ -1483,7 +1615,7 @@ function createSvgObjectsFromPcbPlatedHole(hole, ctx) {
1483
1615
  attributes: {
1484
1616
  class: "pcb-hole-inner",
1485
1617
  fill: colorMap2.drill,
1486
- d: `M${-innerRadiusX},${-(scaledHoleHeight - scaledHoleWidth) / 2} v${scaledHoleHeight - scaledHoleWidth} a${innerRadiusX},${innerRadiusX} 0 0 0 ${scaledHoleWidth},0 v-${scaledHoleHeight - scaledHoleWidth} a${innerRadiusX},${innerRadiusX} 0 0 0 -${scaledHoleWidth},0 z`,
1618
+ d: createPillPath(scaledHoleWidth, scaledHoleHeight),
1487
1619
  transform: innerTransform,
1488
1620
  "data-type": "pcb_plated_hole_drill",
1489
1621
  "data-pcb-layer": "drill"
@@ -3339,7 +3471,7 @@ function getSoftwareUsedString(circuitJson) {
3339
3471
  var package_default = {
3340
3472
  name: "circuit-to-svg",
3341
3473
  type: "module",
3342
- version: "0.0.252",
3474
+ version: "0.0.254",
3343
3475
  description: "Convert Circuit JSON to SVG",
3344
3476
  main: "dist/index.js",
3345
3477
  files: [
@@ -3363,7 +3495,7 @@ var package_default = {
3363
3495
  "bun-match-svg": "^0.0.12",
3364
3496
  esbuild: "^0.20.2",
3365
3497
  "performance-now": "^2.1.0",
3366
- "circuit-json": "^0.0.288",
3498
+ "circuit-json": "^0.0.292",
3367
3499
  react: "19.1.0",
3368
3500
  "react-cosmos": "7.0.0",
3369
3501
  "react-cosmos-plugin-vite": "7.0.0",