diff --git a/src/fn/pinrow.ts b/src/fn/pinrow.ts
index 4c2cc389..bd2dee41 100644
--- a/src/fn/pinrow.ts
+++ b/src/fn/pinrow.ts
@@ -152,7 +152,9 @@ export const pinrow = (
break
}
} else {
- const offset = od * 0.75
+ // Keep through-hole pin labels beyond the outer edge of the plating.
+ // A fixed clearance remains effective for every supported pad diameter.
+ const offset = od / 2 + 0.6
switch (anchorSide) {
case "right":
dx = offset
diff --git a/src/helpers/silkscreenPin.ts b/src/helpers/silkscreenPin.ts
index d09fa5d3..e83cc9b4 100644
--- a/src/helpers/silkscreenPin.ts
+++ b/src/helpers/silkscreenPin.ts
@@ -11,6 +11,7 @@ export const silkscreenPin = ({
anchor_x,
anchor_y,
textalign = "center",
+ anchorplacement,
orthogonal = false,
verticallyinverted = false,
layer = "top",
@@ -37,13 +38,41 @@ export const silkscreenPin = ({
}
let anchor_alignment: PcbSilkscreenText["anchor_alignment"] = "center"
- if (textalign === "left") {
- if (verticallyinverted) anchor_alignment = "center_right"
- else anchor_alignment = "center_left"
- }
- if (textalign === "right") {
- if (verticallyinverted) anchor_alignment = "center_left"
- else anchor_alignment = "center_right"
+ if (textalign !== "center") {
+ const fallbackAlignment =
+ textalign === "left"
+ ? verticallyinverted
+ ? "center_right"
+ : "center_left"
+ : verticallyinverted
+ ? "center_left"
+ : "center_right"
+
+ anchor_alignment = fallbackAlignment
+
+ if (anchorplacement) {
+ const placementDirection = {
+ top: { x: 0, y: 1 },
+ bottom: { x: 0, y: -1 },
+ left: { x: -1, y: 0 },
+ right: { x: 1, y: 0 },
+ }[anchorplacement]
+ const rotationRadians = (ccw_rotation * Math.PI) / 180
+ const textDirection = {
+ x: Math.cos(rotationRadians),
+ y: Math.sin(rotationRadians),
+ }
+ const directionDotProduct =
+ placementDirection.x * textDirection.x +
+ placementDirection.y * textDirection.y
+
+ // Anchor the near edge of the label at the clearance point so rotated
+ // text extends away from the corresponding pad.
+ if (Math.abs(directionDotProduct) > 0.5) {
+ anchor_alignment =
+ directionDotProduct > 0 ? "center_left" : "center_right"
+ }
+ }
}
if (layer === "bottom") {
diff --git a/tests/__snapshots__/pinrow2_id1.016_od1.88_nosquareplating_pinlabeltextalignright_pinlabelorthogonal_doublesidedpinlabel_pinlabelverticallyinverted.snap.svg b/tests/__snapshots__/pinrow2_id1.016_od1.88_nosquareplating_pinlabeltextalignright_pinlabelorthogonal_doublesidedpinlabel_pinlabelverticallyinverted.snap.svg
index 167dc0bf..d3379e2c 100644
--- a/tests/__snapshots__/pinrow2_id1.016_od1.88_nosquareplating_pinlabeltextalignright_pinlabelorthogonal_doublesidedpinlabel_pinlabelverticallyinverted.snap.svg
+++ b/tests/__snapshots__/pinrow2_id1.016_od1.88_nosquareplating_pinlabeltextalignright_pinlabelorthogonal_doublesidedpinlabel_pinlabelverticallyinverted.snap.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/tests/__snapshots__/pinrow4_rows2_1.snap.svg b/tests/__snapshots__/pinrow4_rows2_1.snap.svg
index bd575113..5aba9745 100644
--- a/tests/__snapshots__/pinrow4_rows2_1.snap.svg
+++ b/tests/__snapshots__/pinrow4_rows2_1.snap.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/tests/__snapshots__/pinrow4_vertical_column_labels_left.snap.svg b/tests/__snapshots__/pinrow4_vertical_column_labels_left.snap.svg
new file mode 100644
index 00000000..c00e81dc
--- /dev/null
+++ b/tests/__snapshots__/pinrow4_vertical_column_labels_left.snap.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/tests/__snapshots__/pinrow4_vertical_column_labels_right.snap.svg b/tests/__snapshots__/pinrow4_vertical_column_labels_right.snap.svg
new file mode 100644
index 00000000..aa6f486d
--- /dev/null
+++ b/tests/__snapshots__/pinrow4_vertical_column_labels_right.snap.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/tests/__snapshots__/pinrow5_1.snap.svg b/tests/__snapshots__/pinrow5_1.snap.svg
index c7227793..64a9c711 100644
--- a/tests/__snapshots__/pinrow5_1.snap.svg
+++ b/tests/__snapshots__/pinrow5_1.snap.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/tests/__snapshots__/pinrow5_bottomsidepinlabel.snap.svg b/tests/__snapshots__/pinrow5_bottomsidepinlabel.snap.svg
index 5c0a8478..27887965 100644
--- a/tests/__snapshots__/pinrow5_bottomsidepinlabel.snap.svg
+++ b/tests/__snapshots__/pinrow5_bottomsidepinlabel.snap.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/tests/__snapshots__/pinrow5_doublesidedpinlabel.snap.svg b/tests/__snapshots__/pinrow5_doublesidedpinlabel.snap.svg
index 8a2d99e0..21ee6050 100644
--- a/tests/__snapshots__/pinrow5_doublesidedpinlabel.snap.svg
+++ b/tests/__snapshots__/pinrow5_doublesidedpinlabel.snap.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/tests/__snapshots__/pinrow5_textaligncenter.snap.svg b/tests/__snapshots__/pinrow5_textaligncenter.snap.svg
index c7227793..64a9c711 100644
--- a/tests/__snapshots__/pinrow5_textaligncenter.snap.svg
+++ b/tests/__snapshots__/pinrow5_textaligncenter.snap.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/tests/__snapshots__/pinrow5_textaligncenter_orthogonal.snap.svg b/tests/__snapshots__/pinrow5_textaligncenter_orthogonal.snap.svg
index 154ed2ef..63d5477b 100644
--- a/tests/__snapshots__/pinrow5_textaligncenter_orthogonal.snap.svg
+++ b/tests/__snapshots__/pinrow5_textaligncenter_orthogonal.snap.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/tests/__snapshots__/pinrow5_textaligncenter_orthogonal_verticallyinverted.snap.svg b/tests/__snapshots__/pinrow5_textaligncenter_orthogonal_verticallyinverted.snap.svg
index 3a52b7d3..10c7c7a6 100644
--- a/tests/__snapshots__/pinrow5_textaligncenter_orthogonal_verticallyinverted.snap.svg
+++ b/tests/__snapshots__/pinrow5_textaligncenter_orthogonal_verticallyinverted.snap.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/tests/__snapshots__/pinrow5_textaligncenter_verticallyinverted.snap.svg b/tests/__snapshots__/pinrow5_textaligncenter_verticallyinverted.snap.svg
index 5dffeb28..1a0857fe 100644
--- a/tests/__snapshots__/pinrow5_textaligncenter_verticallyinverted.snap.svg
+++ b/tests/__snapshots__/pinrow5_textaligncenter_verticallyinverted.snap.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/tests/__snapshots__/pinrow5_textalignleft.snap.svg b/tests/__snapshots__/pinrow5_textalignleft.snap.svg
index ac2d34e9..44ed958f 100644
--- a/tests/__snapshots__/pinrow5_textalignleft.snap.svg
+++ b/tests/__snapshots__/pinrow5_textalignleft.snap.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/tests/__snapshots__/pinrow5_textalignleft_orthogonal.snap.svg b/tests/__snapshots__/pinrow5_textalignleft_orthogonal.snap.svg
index 6e3a7b40..ff3c0874 100644
--- a/tests/__snapshots__/pinrow5_textalignleft_orthogonal.snap.svg
+++ b/tests/__snapshots__/pinrow5_textalignleft_orthogonal.snap.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/tests/__snapshots__/pinrow5_textalignleft_orthogonal_verticallyinverted.snap.svg b/tests/__snapshots__/pinrow5_textalignleft_orthogonal_verticallyinverted.snap.svg
index a33624bf..f1c17151 100644
--- a/tests/__snapshots__/pinrow5_textalignleft_orthogonal_verticallyinverted.snap.svg
+++ b/tests/__snapshots__/pinrow5_textalignleft_orthogonal_verticallyinverted.snap.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/tests/__snapshots__/pinrow5_textalignleft_verticallyinverted.snap.svg b/tests/__snapshots__/pinrow5_textalignleft_verticallyinverted.snap.svg
index 2d81ec0f..3846e711 100644
--- a/tests/__snapshots__/pinrow5_textalignleft_verticallyinverted.snap.svg
+++ b/tests/__snapshots__/pinrow5_textalignleft_verticallyinverted.snap.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/tests/__snapshots__/pinrow5_textalignright.snap.svg b/tests/__snapshots__/pinrow5_textalignright.snap.svg
index 51d141f8..34f09669 100644
--- a/tests/__snapshots__/pinrow5_textalignright.snap.svg
+++ b/tests/__snapshots__/pinrow5_textalignright.snap.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/tests/__snapshots__/pinrow5_textalignright_orthogonal.snap.svg b/tests/__snapshots__/pinrow5_textalignright_orthogonal.snap.svg
index b7cf8ead..442f6e10 100644
--- a/tests/__snapshots__/pinrow5_textalignright_orthogonal.snap.svg
+++ b/tests/__snapshots__/pinrow5_textalignright_orthogonal.snap.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/tests/__snapshots__/pinrow5_textalignright_orthogonal_verticallyinverted.snap.svg b/tests/__snapshots__/pinrow5_textalignright_orthogonal_verticallyinverted.snap.svg
index b8c26b7b..a75991b8 100644
--- a/tests/__snapshots__/pinrow5_textalignright_orthogonal_verticallyinverted.snap.svg
+++ b/tests/__snapshots__/pinrow5_textalignright_orthogonal_verticallyinverted.snap.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/tests/__snapshots__/pinrow5_textalignright_verticallyinverted.snap.svg b/tests/__snapshots__/pinrow5_textalignright_verticallyinverted.snap.svg
index c15d6fe4..2ff54559 100644
--- a/tests/__snapshots__/pinrow5_textalignright_verticallyinverted.snap.svg
+++ b/tests/__snapshots__/pinrow5_textalignright_verticallyinverted.snap.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/tests/__snapshots__/pinrow6_female_rows2_1.snap.svg b/tests/__snapshots__/pinrow6_female_rows2_1.snap.svg
index 75701f10..7b535873 100644
--- a/tests/__snapshots__/pinrow6_female_rows2_1.snap.svg
+++ b/tests/__snapshots__/pinrow6_female_rows2_1.snap.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/tests/__snapshots__/pinrow6_nosquareplating_1.snap.svg b/tests/__snapshots__/pinrow6_nosquareplating_1.snap.svg
index df38d0bd..d472c839 100644
--- a/tests/__snapshots__/pinrow6_nosquareplating_1.snap.svg
+++ b/tests/__snapshots__/pinrow6_nosquareplating_1.snap.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/tests/__snapshots__/pinrow8_rows4_1.snap.svg b/tests/__snapshots__/pinrow8_rows4_1.snap.svg
index c5ab6094..e572462d 100644
--- a/tests/__snapshots__/pinrow8_rows4_1.snap.svg
+++ b/tests/__snapshots__/pinrow8_rows4_1.snap.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/tests/__snapshots__/pinrow9_male_rows3_1.snap.svg b/tests/__snapshots__/pinrow9_male_rows3_1.snap.svg
index 822bd4e8..14fb88c0 100644
--- a/tests/__snapshots__/pinrow9_male_rows3_1.snap.svg
+++ b/tests/__snapshots__/pinrow9_male_rows3_1.snap.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/tests/kicad-parity/__snapshots__/pinrow.snap.svg b/tests/kicad-parity/__snapshots__/pinrow.snap.svg
index 0675e011..87808557 100644
--- a/tests/kicad-parity/__snapshots__/pinrow.snap.svg
+++ b/tests/kicad-parity/__snapshots__/pinrow.snap.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/tests/pinrow.test.ts b/tests/pinrow.test.ts
index 21b1d1fb..149d7782 100644
--- a/tests/pinrow.test.ts
+++ b/tests/pinrow.test.ts
@@ -58,6 +58,57 @@ test("pinrow4_rows2 geometry is centered on origin", () => {
expect(refText?.anchor_position).toEqual({ x: 0, y: 3.81 })
})
+const verticalColumnLabelCases = [
+ {
+ side: "left",
+ definition: "pinrow4_rows4_nosquareplating_pinlabeltextalignright",
+ expectedX: -1.35,
+ anchorAlignment: "center_right",
+ },
+ {
+ side: "right",
+ definition: "pinrow4_rows4_nosquareplating_pinlabeltextalignleft",
+ expectedX: 1.35,
+ anchorAlignment: "center_left",
+ },
+] as const
+
+for (const {
+ side,
+ definition,
+ expectedX,
+ anchorAlignment,
+} of verticalColumnLabelCases) {
+ test(`vertical pin column keeps labels on the ${side} of plated holes`, () => {
+ const circuitJson = fp.string(definition).circuitJson()
+ const svgContent = convertCircuitJsonToPcbSvg(circuitJson)
+ const platedHoles = circuitJson.filter(
+ (element) => element.type === "pcb_plated_hole",
+ )
+ const pinLabels = circuitJson.filter(
+ (element) =>
+ element.type === "pcb_silkscreen_text" &&
+ element.text.startsWith("{PIN"),
+ )
+ const pinYs = [3.81, 1.27, -1.27, -3.81]
+
+ expect(platedHoles.map(({ x, y }) => ({ x, y }))).toEqual(
+ pinYs.map((y) => ({ x: 0, y })),
+ )
+ expect(pinLabels).toMatchObject(
+ pinYs.map((y, index) => ({
+ text: `{PIN${index + 1}}`,
+ anchor_position: { x: expectedX, y },
+ anchor_alignment: anchorAlignment,
+ })),
+ )
+ expect(svgContent).toMatchSvgSnapshot(
+ import.meta.path,
+ `pinrow4_vertical_column_labels_${side}`,
+ )
+ })
+}
+
test("pinrow8_rows4", () => {
const circuitJson = fp.string("pinrow8_rows4").circuitJson()
const svgContent = convertCircuitJsonToPcbSvg(circuitJson)
@@ -152,7 +203,6 @@ test("pinrow6_nosquareplating", () => {
"pinrow6_nosquareplating_1",
)
})
-
const textAlignments = ["left", "center", "right"] as const
const orthogonalStates = [
{ name: "", value: false },
diff --git a/tests/pinrow2_id1.016_od1.88_nosquareplating_pinlabeltextalignright_pinlabelorthogonal_doublesidedpinlabel_pinlabelverticallyinverted.test.ts b/tests/pinrow2_id1.016_od1.88_nosquareplating_pinlabeltextalignright_pinlabelorthogonal_doublesidedpinlabel_pinlabelverticallyinverted.test.ts
index 55456e19..34d0881a 100644
--- a/tests/pinrow2_id1.016_od1.88_nosquareplating_pinlabeltextalignright_pinlabelorthogonal_doublesidedpinlabel_pinlabelverticallyinverted.test.ts
+++ b/tests/pinrow2_id1.016_od1.88_nosquareplating_pinlabeltextalignright_pinlabelorthogonal_doublesidedpinlabel_pinlabelverticallyinverted.test.ts
@@ -53,29 +53,29 @@ test(def, () => {
{
text: "{PIN1}",
layer: "top",
- anchor_position: { x: -1.27, y: 1.41 },
- anchor_alignment: "center_left",
+ anchor_position: { x: -1.27, y: 1.54 },
+ anchor_alignment: "center_right",
ccw_rotation: 270,
},
{
text: "{PIN1}",
layer: "bottom",
- anchor_position: { x: -1.27, y: 1.41 },
- anchor_alignment: "center_right",
+ anchor_position: { x: -1.27, y: 1.54 },
+ anchor_alignment: "center_left",
ccw_rotation: 270,
},
{
text: "{PIN2}",
layer: "top",
- anchor_position: { x: 1.27, y: 1.41 },
- anchor_alignment: "center_left",
+ anchor_position: { x: 1.27, y: 1.54 },
+ anchor_alignment: "center_right",
ccw_rotation: 270,
},
{
text: "{PIN2}",
layer: "bottom",
- anchor_position: { x: 1.27, y: 1.41 },
- anchor_alignment: "center_right",
+ anchor_position: { x: 1.27, y: 1.54 },
+ anchor_alignment: "center_left",
ccw_rotation: 270,
},
])