Skip to content

Commit 688c631

Browse files
committed
Adjust silkscreen text size to component bounds
1 parent b9e43e5 commit 688c631

File tree

1 file changed

+111
-23
lines changed

1 file changed

+111
-23
lines changed

src/convert-kicad-json-to-tscircuit-soup.ts

Lines changed: 111 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,43 @@ export const convertKicadJsonToTsCircuitSoup = async (
9595

9696
const circuitJson: AnyCircuitElement[] = []
9797

98+
const componentBounds = {
99+
minX: Number.POSITIVE_INFINITY,
100+
maxX: Number.NEGATIVE_INFINITY,
101+
minY: Number.POSITIVE_INFINITY,
102+
maxY: Number.NEGATIVE_INFINITY,
103+
}
104+
105+
const updateBoundsWithPoint = (x: number, y: number) => {
106+
if (!Number.isFinite(x) || !Number.isFinite(y)) return
107+
componentBounds.minX = Math.min(componentBounds.minX, x)
108+
componentBounds.maxX = Math.max(componentBounds.maxX, x)
109+
componentBounds.minY = Math.min(componentBounds.minY, y)
110+
componentBounds.maxY = Math.max(componentBounds.maxY, y)
111+
}
112+
113+
const updateBoundsWithRect = (
114+
x: number,
115+
y: number,
116+
width: number | undefined,
117+
height: number | undefined,
118+
) => {
119+
if (width === undefined || height === undefined) return
120+
const halfWidth = width / 2
121+
const halfHeight = height / 2
122+
updateBoundsWithPoint(x - halfWidth, y - halfHeight)
123+
updateBoundsWithPoint(x + halfWidth, y + halfHeight)
124+
}
125+
126+
const updateBoundsWithRoute = (
127+
route: Array<{ x: number; y: number }> | undefined,
128+
) => {
129+
if (!route) return
130+
for (const point of route) {
131+
updateBoundsWithPoint(point.x, point.y)
132+
}
133+
}
134+
98135
circuitJson.push({
99136
type: "source_component",
100137
source_component_id: "source_component_0",
@@ -143,32 +180,20 @@ export const convertKicadJsonToTsCircuitSoup = async (
143180
})
144181
}
145182

146-
let minX = Number.POSITIVE_INFINITY
147-
let maxX = Number.NEGATIVE_INFINITY
148-
let minY = Number.POSITIVE_INFINITY
149-
let maxY = Number.NEGATIVE_INFINITY
150-
for (const pad of pads) {
151-
const x = pad.at[0]
152-
const y = -pad.at[1]
153-
const w = pad.size[0]
154-
const h = pad.size[1]
155-
minX = Math.min(minX, x - w / 2)
156-
maxX = Math.max(maxX, x + w / 2)
157-
minY = Math.min(minY, y - h / 2)
158-
maxY = Math.max(maxY, y + h / 2)
159-
}
160183
const pcb_component_id = "pcb_component_0"
161184

162-
circuitJson.push({
185+
const pcbComponent = {
163186
type: "pcb_component",
164187
source_component_id: "source_component_0",
165188
pcb_component_id,
166189
layer: "top",
167190
center: { x: 0, y: 0 },
168191
rotation: 0,
169-
width: Number.isFinite(minX) ? maxX - minX : 0,
170-
height: Number.isFinite(minY) ? maxY - minY : 0,
171-
} as any)
192+
width: 0,
193+
height: 0,
194+
} as any
195+
196+
circuitJson.push(pcbComponent)
172197

173198
// Create pcb_port elements
174199
let pcbPortId = 0
@@ -187,6 +212,7 @@ export const convertKicadJsonToTsCircuitSoup = async (
187212
if (pad) {
188213
x = pad.at[0]
189214
y = -pad.at[1]
215+
updateBoundsWithRect(x, y, pad.size?.[0], pad.size?.[1])
190216
layers = pad.layers
191217
? (pad.layers
192218
.map((l) => convertKicadLayerToTscircuitLayer(l))
@@ -197,6 +223,9 @@ export const convertKicadJsonToTsCircuitSoup = async (
197223
if (hole) {
198224
x = hole.at[0]
199225
y = -hole.at[1]
226+
const holeWidth = hole.size?.width ?? hole.drill?.width
227+
const holeHeight = hole.size?.height ?? hole.drill?.height ?? holeWidth
228+
updateBoundsWithRect(x, y, holeWidth, holeHeight)
200229
layers = hole.layers
201230
? (hole.layers
202231
.map((l) => convertKicadLayerToTscircuitLayer(l))
@@ -224,6 +253,7 @@ export const convertKicadJsonToTsCircuitSoup = async (
224253
const pcb_port_id = pad.name
225254
? portNameToPcbPortId.get(pad.name)
226255
: undefined
256+
updateBoundsWithRect(pad.at[0], -pad.at[1], pad.size?.[0], pad.size?.[1])
227257
circuitJson.push({
228258
type: "pcb_smtpad",
229259
pcb_smtpad_id: `pcb_smtpad_${smtpadId++}`,
@@ -245,6 +275,7 @@ export const convertKicadJsonToTsCircuitSoup = async (
245275
const offX = pad.drill?.offset?.[0] ?? 0
246276
const offY = pad.drill?.offset?.[1] ?? 0
247277
const rotOff = rotatePoint(offX, offY, rotation)
278+
updateBoundsWithRect(pad.at[0], -pad.at[1], width, height)
248279
const pcb_port_id = pad.name
249280
? portNameToPcbPortId.get(pad.name)
250281
: undefined
@@ -271,6 +302,12 @@ export const convertKicadJsonToTsCircuitSoup = async (
271302
const pcb_port_id = pad.name
272303
? portNameToPcbPortId.get(pad.name)
273304
: undefined
305+
updateBoundsWithRect(
306+
pad.at[0],
307+
-pad.at[1],
308+
pad.size?.[0],
309+
pad.size?.[1] ?? pad.size?.[0],
310+
)
274311
circuitJson.push({
275312
type: "pcb_plated_hole",
276313
pcb_plated_hole_id: `pcb_plated_hole_${platedHoleId++}`,
@@ -288,6 +325,7 @@ export const convertKicadJsonToTsCircuitSoup = async (
288325
const pcb_port_id = pad.name
289326
? portNameToPcbPortId.get(pad.name)
290327
: undefined
328+
updateBoundsWithRect(pad.at[0], -pad.at[1], pad.size?.[0], pad.size?.[1])
291329
circuitJson.push({
292330
type: "pcb_plated_hole",
293331
pcb_plated_hole_id: `pcb_plated_hole_${platedHoleId++}`,
@@ -305,6 +343,12 @@ export const convertKicadJsonToTsCircuitSoup = async (
305343
} as any)
306344
}
307345
} else if (pad.pad_type === "np_thru_hole") {
346+
updateBoundsWithRect(
347+
pad.at[0],
348+
-pad.at[1],
349+
pad.size?.[0] ?? pad.drill?.width,
350+
pad.size?.[1] ?? pad.drill?.height ?? pad.drill?.width,
351+
)
308352
circuitJson.push({
309353
type: "pcb_hole",
310354
pcb_hole_id: `pcb_hole_${holeId++}`,
@@ -330,6 +374,13 @@ export const convertKicadJsonToTsCircuitSoup = async (
330374
const y = -(hole.at[1] + rotOff.y)
331375
const holeDiameter = hole.drill?.width ?? 0
332376
const outerDiameter = hole.size?.width ?? holeDiameter
377+
const holeWidth = isNinetyLike(rotation)
378+
? hole.size?.height ?? outerDiameter
379+
: hole.size?.width ?? outerDiameter
380+
const holeHeight = isNinetyLike(rotation)
381+
? hole.size?.width ?? outerDiameter
382+
: hole.size?.height ?? outerDiameter
383+
updateBoundsWithRect(x, y, holeWidth, holeHeight)
333384
const rr = hole.roundrect_rratio ?? 0
334385
const rectBorderRadius =
335386
rr > 0
@@ -500,11 +551,13 @@ export const convertKicadJsonToTsCircuitSoup = async (
500551
for (const polygon of closedPolygons) {
501552
const points = polygonToPoints(polygon)
502553
if (points.length >= 3) {
554+
const route = points.map((p) => ({ x: p.x, y: -p.y }))
555+
updateBoundsWithRoute(route)
503556
circuitJson.push({
504557
type: "pcb_cutout",
505558
pcb_cutout_id: `pcb_cutout_${cutoutId++}`,
506559
shape: "polygon",
507-
points: points.map((p) => ({ x: p.x, y: -p.y })),
560+
points: route,
508561
pcb_component_id,
509562
} as any)
510563
}
@@ -519,6 +572,7 @@ export const convertKicadJsonToTsCircuitSoup = async (
519572
{ x: fp_line.start[0], y: -fp_line.start[1] },
520573
{ x: fp_line.end[0], y: -fp_line.end[1] },
521574
]
575+
updateBoundsWithRoute(route)
522576
const lowerLayer = fp_line.layer.toLowerCase()
523577
if (lowerLayer === "f.cu") {
524578
circuitJson.push({
@@ -574,6 +628,7 @@ export const convertKicadJsonToTsCircuitSoup = async (
574628
if (fp_polys) {
575629
for (const fp_poly of fp_polys) {
576630
const route = fp_poly.pts.map((p) => ({ x: p[0], y: -p[1] }))
631+
updateBoundsWithRoute(route)
577632
if (fp_poly.layer.endsWith(".Cu")) {
578633
const rect = getAxisAlignedRectFromPoints(route)
579634
if (rect) {
@@ -639,13 +694,15 @@ export const convertKicadJsonToTsCircuitSoup = async (
639694
const arcLength = getArcLength(start, mid, end)
640695

641696
const arcPoints = generateArcPath(start, mid, end, Math.ceil(arcLength))
697+
const route = arcPoints.map((p) => ({ x: p.x, y: -p.y }))
698+
updateBoundsWithRoute(route)
642699

643700
if (lowerLayer.startsWith("user.")) {
644701
circuitJson.push({
645702
type: "pcb_note_path",
646703
pcb_note_path_id: `pcb_note_path_${notePathId++}`,
647704
pcb_component_id,
648-
route: arcPoints.map((p) => ({ x: p.x, y: -p.y })),
705+
route,
649706
stroke_width: fp_arc.stroke.width,
650707
} as any)
651708
continue
@@ -662,7 +719,7 @@ export const convertKicadJsonToTsCircuitSoup = async (
662719
pcb_silkscreen_path_id: `pcb_silkscreen_path_${silkPathId++}`,
663720
layer: tscircuitLayer,
664721
pcb_component_id,
665-
route: arcPoints.map((p) => ({ x: p.x, y: -p.y })),
722+
route,
666723
stroke_width: fp_arc.stroke.width,
667724
} as any)
668725
}
@@ -688,19 +745,48 @@ export const convertKicadJsonToTsCircuitSoup = async (
688745
})
689746
}
690747

748+
const route = circlePoints.map((p) => ({ x: p.x, y: -p.y }))
749+
updateBoundsWithRoute(route)
750+
691751
// Convert user-defined layers to pcb_note_path
692752
if (lowerLayer.startsWith("user.")) {
693753
circuitJson.push({
694754
type: "pcb_note_path",
695755
pcb_note_path_id: `pcb_note_path_${notePathId++}`,
696756
pcb_component_id,
697-
route: circlePoints.map((p) => ({ x: p.x, y: -p.y })),
757+
route,
698758
stroke_width: fp_circle.stroke.width,
699759
} as any)
700760
}
701761
}
702762
}
703763

764+
const componentWidth =
765+
Number.isFinite(componentBounds.minX) &&
766+
Number.isFinite(componentBounds.maxX)
767+
? componentBounds.maxX - componentBounds.minX
768+
: 0
769+
const componentHeight =
770+
Number.isFinite(componentBounds.minY) &&
771+
Number.isFinite(componentBounds.maxY)
772+
? componentBounds.maxY - componentBounds.minY
773+
: 0
774+
775+
pcbComponent.width = componentWidth
776+
pcbComponent.height = componentHeight
777+
778+
const computeDynamicFontSize = () => {
779+
const effectiveSize =
780+
componentWidth > 0 && componentHeight > 0
781+
? Math.sqrt(componentWidth * componentHeight)
782+
: Math.max(componentWidth, componentHeight)
783+
if (!effectiveSize || !Number.isFinite(effectiveSize)) {
784+
return 1.27
785+
}
786+
const scaled = effectiveSize * 0.35
787+
return Math.min(Math.max(scaled, 0.5), 2.5)
788+
}
789+
704790
for (const fp_text of fp_texts) {
705791
const layerRef = convertKicadLayerToTscircuitLayer(fp_text.layer)!
706792

@@ -742,11 +828,13 @@ export const convertKicadJsonToTsCircuitSoup = async (
742828
const propLayer = propFab!.attributes.layer?.toLowerCase()
743829
const isFabLayer = propLayer?.endsWith(".fab")
744830

831+
const font_size = computeDynamicFontSize()
832+
745833
circuitJson.push({
746834
type: isFabLayer ? "pcb_fabrication_note_text" : "pcb_silkscreen_text",
747835
layer: "top",
748836
font: "tscircuit2024",
749-
font_size: 1.27,
837+
font_size,
750838
pcb_component_id,
751839
anchor_position: { x: at[0], y: -at[1] },
752840
anchor_alignment: "center",

0 commit comments

Comments
 (0)