@@ -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