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 +161 -29
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
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:
|
|
664
|
-
y:
|
|
678
|
+
x: fromOffset.x + direction.x * arrowSize,
|
|
679
|
+
y: fromOffset.y + direction.y * arrowSize
|
|
665
680
|
};
|
|
666
681
|
const toBase = {
|
|
667
|
-
x:
|
|
668
|
-
y:
|
|
682
|
+
x: toOffset.x - direction.x * arrowSize,
|
|
683
|
+
y: toOffset.y - direction.y * arrowSize
|
|
669
684
|
};
|
|
670
685
|
const fromTriangle = [
|
|
671
|
-
toScreen(
|
|
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(
|
|
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 {
|
|
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:
|
|
1069
|
-
y:
|
|
1148
|
+
x: fromOffset.x + direction.x * arrow_size,
|
|
1149
|
+
y: fromOffset.y + direction.y * arrow_size
|
|
1070
1150
|
};
|
|
1071
1151
|
const toBase = {
|
|
1072
|
-
x:
|
|
1073
|
-
y:
|
|
1152
|
+
x: toOffset.x - direction.x * arrow_size,
|
|
1153
|
+
y: toOffset.y - direction.y * arrow_size
|
|
1074
1154
|
};
|
|
1075
1155
|
const fromTriangle = [
|
|
1076
|
-
toScreen(
|
|
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(
|
|
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:
|
|
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:
|
|
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.
|
|
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.
|
|
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",
|