├── .devcontainer └── devcontainer.json ├── .github ├── tests │ └── test pcb │ │ ├── .gitignore │ │ ├── kicad_render_pre_render.sh │ │ ├── test.kicad_pcb │ │ ├── test.kicad_pro │ │ └── test.kicad_sch └── workflows │ ├── image.yaml │ └── kicad-nightly.yaml ├── Dockerfile ├── Dockerfile.kicad-nightly ├── README.md ├── action.yaml ├── kicad_animation.sh └── render-pcb.sh /.devcontainer/devcontainer.json: -------------------------------------------------------------------------------- 1 | // For format details, see https://aka.ms/devcontainer.json. For config options, see the 2 | // README at: https://github.com/devcontainers/templates/tree/main/src/docker-existing-dockerfile 3 | { 4 | "name": "KiCad nightly", 5 | "image": "ghcr.io/linalinn/kicad:nightly-2024-04-09-13-16", 6 | 7 | // Features to add to the dev container. More info: https://containers.dev/features. 8 | "features": { 9 | "ghcr.io/devcontainers-contrib/features/zsh-plugins:0": {}, 10 | "ghcr.io/lukewiwa/features/shellcheck:0": {}, 11 | "ghcr.io/devcontainers/features/git:1": {} 12 | }, 13 | 14 | 15 | // Use 'forwardPorts' to make a list of ports inside the container available locally. 16 | // "forwardPorts": [], 17 | 18 | // Uncomment the next line to run commands after the container is created. 19 | "postCreateCommand": "apt-get install --yes openssh-client" 20 | 21 | // Configure tool-specific properties. 22 | // "customizations": {}, 23 | 24 | // Uncomment to connect as an existing user other than the container default. More info: https://aka.ms/dev-containers-non-root. 25 | // "remoteUser": "devcontainer" 26 | } 27 | -------------------------------------------------------------------------------- /.github/tests/test pcb/.gitignore: -------------------------------------------------------------------------------- 1 | # Created by https://www.toptal.com/developers/gitignore/api/kicad 2 | # Edit at https://www.toptal.com/developers/gitignore?templates=kicad 3 | 4 | ### KiCad ### 5 | # For PCBs designed using KiCad: https://www.kicad.org/ 6 | # Format documentation: https://kicad.org/help/file-formats/ 7 | 8 | # Temporary files 9 | *.000 10 | *.bak 11 | *.bck 12 | *.kicad_pcb-bak 13 | *.kicad_sch-bak 14 | *-backups 15 | *.kicad_prl 16 | *.sch-bak 17 | *~ 18 | _autosave-* 19 | *.tmp 20 | *-save.pro 21 | *-save.kicad_pcb 22 | fp-info-cache 23 | 24 | # Netlist files (exported from Eeschema) 25 | *.net 26 | 27 | # Autorouter files (exported from Pcbnew) 28 | *.dsn 29 | *.ses 30 | 31 | # Exported BOM files 32 | *.xml 33 | *.csv 34 | 35 | ### KiCad Patch ### 36 | rescue-backup/ 37 | 38 | *.tsv 39 | bom/ 40 | 41 | # Gerber export output 42 | out/ 43 | 44 | # End of https://www.toptal.com/developers/gitignore/api/kicad 45 | -------------------------------------------------------------------------------- /.github/tests/test pcb/kicad_render_pre_render.sh: -------------------------------------------------------------------------------- 1 | #!/bin/env bash 2 | echo "#" 3 | echo "# HI RUNNING PRE RENDERING SCRIPT" 4 | echo "#" 5 | 6 | echo "# INSTALLING MS FONTS" 7 | echo "#" 8 | apt-get update --yes 9 | echo ttf-mscorefonts-installer msttcorefonts/accepted-mscorefonts-eula select true | debconf-set-selections 10 | apt-get install --yes ttf-mscorefonts-installer 11 | 12 | KICAD_CLI_OPTIONAL_ARGS="$KICAD_CLI_OPTIONAL_ARGS --zoom 2" 13 | 14 | export KICAD_CLI_OPTIONAL_ARGS="$KICAD_CLI_OPTIONAL_ARGS" 15 | export INPUT_PREFIX="prefix-via pre-render" 16 | -------------------------------------------------------------------------------- /.github/tests/test pcb/test.kicad_pcb: -------------------------------------------------------------------------------- 1 | (kicad_pcb 2 | (version 20240108) 3 | (generator "pcbnew") 4 | (generator_version "8.0") 5 | (general 6 | (thickness 1.6) 7 | (legacy_teardrops no) 8 | ) 9 | (paper "A4") 10 | (layers 11 | (0 "F.Cu" signal) 12 | (31 "B.Cu" signal) 13 | (32 "B.Adhes" user "B.Adhesive") 14 | (33 "F.Adhes" user "F.Adhesive") 15 | (34 "B.Paste" user) 16 | (35 "F.Paste" user) 17 | (36 "B.SilkS" user "B.Silkscreen") 18 | (37 "F.SilkS" user "F.Silkscreen") 19 | (38 "B.Mask" user) 20 | (39 "F.Mask" user) 21 | (40 "Dwgs.User" user "User.Drawings") 22 | (41 "Cmts.User" user "User.Comments") 23 | (42 "Eco1.User" user "User.Eco1") 24 | (43 "Eco2.User" user "User.Eco2") 25 | (44 "Edge.Cuts" user) 26 | (45 "Margin" user) 27 | (46 "B.CrtYd" user "B.Courtyard") 28 | (47 "F.CrtYd" user "F.Courtyard") 29 | (48 "B.Fab" user) 30 | (49 "F.Fab" user) 31 | (50 "User.1" user) 32 | (51 "User.2" user) 33 | (52 "User.3" user) 34 | (53 "User.4" user) 35 | (54 "User.5" user) 36 | (55 "User.6" user) 37 | (56 "User.7" user) 38 | (57 "User.8" user) 39 | (58 "User.9" user) 40 | ) 41 | (setup 42 | (pad_to_mask_clearance 0) 43 | (allow_soldermask_bridges_in_footprints no) 44 | (pcbplotparams 45 | (layerselection 0x00010fc_ffffffff) 46 | (plot_on_all_layers_selection 0x0000000_00000000) 47 | (disableapertmacros no) 48 | (usegerberextensions no) 49 | (usegerberattributes yes) 50 | (usegerberadvancedattributes yes) 51 | (creategerberjobfile yes) 52 | (dashed_line_dash_ratio 12.000000) 53 | (dashed_line_gap_ratio 3.000000) 54 | (svgprecision 4) 55 | (plotframeref no) 56 | (viasonmask no) 57 | (mode 1) 58 | (useauxorigin no) 59 | (hpglpennumber 1) 60 | (hpglpenspeed 20) 61 | (hpglpendiameter 15.000000) 62 | (pdf_front_fp_property_popups yes) 63 | (pdf_back_fp_property_popups yes) 64 | (dxfpolygonmode yes) 65 | (dxfimperialunits yes) 66 | (dxfusepcbnewfont yes) 67 | (psnegative no) 68 | (psa4output no) 69 | (plotreference yes) 70 | (plotvalue yes) 71 | (plotfptext yes) 72 | (plotinvisibletext no) 73 | (sketchpadsonfab no) 74 | (subtractmaskfromsilk no) 75 | (outputformat 1) 76 | (mirror no) 77 | (drillshape 1) 78 | (scaleselection 1) 79 | (outputdirectory "") 80 | ) 81 | ) 82 | (net 0 "") 83 | (footprint "Connector_USB:USB_C_Receptacle_GCT_USB4105-xx-A_16P_TopMnt_Horizontal" 84 | (layer "F.Cu") 85 | (uuid "e518e60e-9083-4bb9-bbf8-10be4d4cdb37") 86 | (at 196.68 88.75 90) 87 | (descr "USB 2.0 Type C Receptacle, GCT, 16P, top mounted, horizontal, 5A: https://gct.co/files/drawings/usb4105.pdf") 88 | (tags "USB C Type-C Receptacle SMD USB 2.0 16P 16C USB4105-15-A USB4105-15-A-060 USB4105-15-A-120 USB4105-GF-A USB4105-GF-A-060 USB4105-GF-A-120") 89 | (property "Reference" "REF**" 90 | (at 0 -5.5 90) 91 | (unlocked yes) 92 | (layer "F.SilkS") 93 | (hide yes) 94 | (uuid "7f063368-3250-4cf4-a652-982436c948bd") 95 | (effects 96 | (font 97 | (size 1 1) 98 | (thickness 0.15) 99 | ) 100 | ) 101 | ) 102 | (property "Value" "USB_C_Receptacle_GCT_USB4105-xx-A_16P_TopMnt_Horizontal" 103 | (at 0 5 90) 104 | (unlocked yes) 105 | (layer "F.Fab") 106 | (uuid "ef701eff-bf72-43e7-b3be-46c619fed945") 107 | (effects 108 | (font 109 | (size 1 1) 110 | (thickness 0.15) 111 | ) 112 | ) 113 | ) 114 | (property "Footprint" "Connector_USB:USB_C_Receptacle_GCT_USB4105-xx-A_16P_TopMnt_Horizontal" 115 | (at 0 0 90) 116 | (unlocked yes) 117 | (layer "F.Fab") 118 | (hide yes) 119 | (uuid "7ff5132c-5aaf-48ff-9e44-9a1fcee8c209") 120 | (effects 121 | (font 122 | (size 1.27 1.27) 123 | ) 124 | ) 125 | ) 126 | (property "Datasheet" "" 127 | (at 0 0 90) 128 | (unlocked yes) 129 | (layer "F.Fab") 130 | (hide yes) 131 | (uuid "28d78651-998e-4d2d-b6ce-48836217ee36") 132 | (effects 133 | (font 134 | (size 1.27 1.27) 135 | ) 136 | ) 137 | ) 138 | (property "Description" "" 139 | (at 0 0 90) 140 | (unlocked yes) 141 | (layer "F.Fab") 142 | (hide yes) 143 | (uuid "794d115a-3722-4fc8-a602-4fa698a8dfbb") 144 | (effects 145 | (font 146 | (size 1.27 1.27) 147 | ) 148 | ) 149 | ) 150 | (attr smd) 151 | (fp_line 152 | (start 4.67 -0.1) 153 | (end 4.67 -1.8) 154 | (stroke 155 | (width 0.12) 156 | (type solid) 157 | ) 158 | (layer "F.SilkS") 159 | (uuid "4653819e-3e06-45ef-9bce-5e0bbe3c75ba") 160 | ) 161 | (fp_line 162 | (start -4.67 -0.1) 163 | (end -4.67 -1.8) 164 | (stroke 165 | (width 0.12) 166 | (type solid) 167 | ) 168 | (layer "F.SilkS") 169 | (uuid "24f418b7-cf4b-4daf-a293-5c70ce6bcba1") 170 | ) 171 | (fp_line 172 | (start 5 3.675) 173 | (end -5 3.675) 174 | (stroke 175 | (width 0.1) 176 | (type solid) 177 | ) 178 | (layer "Dwgs.User") 179 | (uuid "9dbe1824-a91d-48e1-aed3-e3ec46631ec2") 180 | ) 181 | (fp_rect 182 | (start -5.32 -4.76) 183 | (end 5.32 4.18) 184 | (stroke 185 | (width 0.05) 186 | (type solid) 187 | ) 188 | (fill none) 189 | (layer "F.CrtYd") 190 | (uuid "26df7426-6bd6-45a1-87de-32edf41f8e1c") 191 | ) 192 | (fp_rect 193 | (start -4.47 -3.675) 194 | (end 4.47 3.675) 195 | (stroke 196 | (width 0.1) 197 | (type solid) 198 | ) 199 | (fill none) 200 | (layer "F.Fab") 201 | (uuid "27a3b8c5-cd24-4039-aa5f-411803845ac4") 202 | ) 203 | (fp_text user "PCB Edge" 204 | (at 0 3.1 90) 205 | (unlocked yes) 206 | (layer "Dwgs.User") 207 | (uuid "4b33d30e-ffee-4a6c-baae-a57a7a30b4ec") 208 | (effects 209 | (font 210 | (size 0.5 0.5) 211 | (thickness 0.1) 212 | ) 213 | ) 214 | ) 215 | (fp_text user "${REFERENCE}" 216 | (at 0 0 90) 217 | (unlocked yes) 218 | (layer "F.Fab") 219 | (uuid "b6de5ac1-3026-473b-9039-9c83aff40e99") 220 | (effects 221 | (font 222 | (size 1 1) 223 | (thickness 0.15) 224 | ) 225 | ) 226 | ) 227 | (pad "" np_thru_hole circle 228 | (at -2.89 -2.605 90) 229 | (size 0.65 0.65) 230 | (drill 0.65) 231 | (layers "F&B.Cu" "*.Mask") 232 | (uuid "a043a9ab-a97c-4e20-a6d8-28e53e573111") 233 | ) 234 | (pad "" np_thru_hole circle 235 | (at 2.89 -2.605 90) 236 | (size 0.65 0.65) 237 | (drill 0.65) 238 | (layers "F&B.Cu" "*.Mask") 239 | (uuid "56bb0297-298f-4c90-bcff-80ee15812869") 240 | ) 241 | (pad "A1" smd roundrect 242 | (at -3.2 -3.68 90) 243 | (size 0.6 1.15) 244 | (layers "F.Cu" "F.Paste" "F.Mask") 245 | (roundrect_rratio 0.25) 246 | (uuid "2fca40d0-da24-44bb-bd60-68738b692849") 247 | ) 248 | (pad "A4" smd roundrect 249 | (at -2.4 -3.68 90) 250 | (size 0.6 1.15) 251 | (layers "F.Cu" "F.Paste" "F.Mask") 252 | (roundrect_rratio 0.25) 253 | (uuid "e68135ca-acfd-42ab-8167-64b9da1c2bdf") 254 | ) 255 | (pad "A5" smd roundrect 256 | (at -1.25 -3.68 90) 257 | (size 0.3 1.15) 258 | (layers "F.Cu" "F.Paste" "F.Mask") 259 | (roundrect_rratio 0.25) 260 | (uuid "9c9887c8-41fa-40b1-8fef-5cbe0907d49f") 261 | ) 262 | (pad "A6" smd roundrect 263 | (at -0.25 -3.68 90) 264 | (size 0.3 1.15) 265 | (layers "F.Cu" "F.Paste" "F.Mask") 266 | (roundrect_rratio 0.25) 267 | (uuid "b0504463-ba29-4db5-81b6-bca3282ecd03") 268 | ) 269 | (pad "A7" smd roundrect 270 | (at 0.25 -3.68 90) 271 | (size 0.3 1.15) 272 | (layers "F.Cu" "F.Paste" "F.Mask") 273 | (roundrect_rratio 0.25) 274 | (uuid "71cbc25f-0065-4b92-989f-5c58afd84c30") 275 | ) 276 | (pad "A8" smd roundrect 277 | (at 1.25 -3.68 90) 278 | (size 0.3 1.15) 279 | (layers "F.Cu" "F.Paste" "F.Mask") 280 | (roundrect_rratio 0.25) 281 | (uuid "134a9583-5cc7-4d3e-9899-59b3758d029e") 282 | ) 283 | (pad "A9" smd roundrect 284 | (at 2.4 -3.68 90) 285 | (size 0.6 1.15) 286 | (layers "F.Cu" "F.Paste" "F.Mask") 287 | (roundrect_rratio 0.25) 288 | (uuid "17180561-5eb7-4d1d-9a36-ff269589600a") 289 | ) 290 | (pad "A12" smd roundrect 291 | (at 3.2 -3.68 90) 292 | (size 0.6 1.15) 293 | (layers "F.Cu" "F.Paste" "F.Mask") 294 | (roundrect_rratio 0.25) 295 | (uuid "ec234158-c43a-446e-bbce-07dc793fa2c5") 296 | ) 297 | (pad "B1" smd roundrect 298 | (at 3.2 -3.68 90) 299 | (size 0.6 1.15) 300 | (layers "F.Cu" "F.Paste" "F.Mask") 301 | (roundrect_rratio 0.25) 302 | (uuid "2519164a-562e-4baa-a629-c58970688dff") 303 | ) 304 | (pad "B4" smd roundrect 305 | (at 2.4 -3.68 90) 306 | (size 0.6 1.15) 307 | (layers "F.Cu" "F.Paste" "F.Mask") 308 | (roundrect_rratio 0.25) 309 | (uuid "7ba31ef6-808b-416b-97d4-66370df6f589") 310 | ) 311 | (pad "B5" smd roundrect 312 | (at 1.75 -3.68 90) 313 | (size 0.3 1.15) 314 | (layers "F.Cu" "F.Paste" "F.Mask") 315 | (roundrect_rratio 0.25) 316 | (uuid "52c00742-ad08-477b-a010-33f4f1855371") 317 | ) 318 | (pad "B6" smd roundrect 319 | (at 0.75 -3.68 90) 320 | (size 0.3 1.15) 321 | (layers "F.Cu" "F.Paste" "F.Mask") 322 | (roundrect_rratio 0.25) 323 | (uuid "5ff21b74-20d5-4d88-80fa-25ae24c7c99c") 324 | ) 325 | (pad "B7" smd roundrect 326 | (at -0.75 -3.68 90) 327 | (size 0.3 1.15) 328 | (layers "F.Cu" "F.Paste" "F.Mask") 329 | (roundrect_rratio 0.25) 330 | (uuid "c3bc3713-b162-462c-a379-847d29ba4206") 331 | ) 332 | (pad "B8" smd roundrect 333 | (at -1.75 -3.68 90) 334 | (size 0.3 1.15) 335 | (layers "F.Cu" "F.Paste" "F.Mask") 336 | (roundrect_rratio 0.25) 337 | (uuid "9adb31ac-3804-4396-8f3d-c6202080a461") 338 | ) 339 | (pad "B9" smd roundrect 340 | (at -2.4 -3.68 90) 341 | (size 0.6 1.15) 342 | (layers "F.Cu" "F.Paste" "F.Mask") 343 | (roundrect_rratio 0.25) 344 | (uuid "01edcc20-650d-4e3b-bc31-3a0ab57bbe39") 345 | ) 346 | (pad "B12" smd roundrect 347 | (at -3.2 -3.68 90) 348 | (size 0.6 1.15) 349 | (layers "F.Cu" "F.Paste" "F.Mask") 350 | (roundrect_rratio 0.25) 351 | (uuid "280db069-da29-4433-890d-e3044bed7ffe") 352 | ) 353 | (pad "S1" thru_hole oval 354 | (at -4.32 -3.105 90) 355 | (size 1 2.1) 356 | (drill oval 0.6 1.7) 357 | (layers "*.Cu" "*.Mask" "F.Paste") 358 | (remove_unused_layers no) 359 | (uuid "a2da50d4-ef51-47a0-8657-8c2fdc9a1dc7") 360 | ) 361 | (pad "S1" thru_hole oval 362 | (at -4.32 1.075 90) 363 | (size 1 1.8) 364 | (drill oval 0.6 1.4) 365 | (layers "*.Cu" "*.Mask" "F.Paste") 366 | (remove_unused_layers no) 367 | (uuid "3e35f903-9002-49c0-a1d7-28b8fcbd7dcc") 368 | ) 369 | (pad "S1" thru_hole oval 370 | (at 4.32 -3.105 90) 371 | (size 1 2.1) 372 | (drill oval 0.6 1.7) 373 | (layers "*.Cu" "*.Mask" "F.Paste") 374 | (remove_unused_layers no) 375 | (uuid "e986f1d6-5dfe-4149-8a6c-872051f1fa19") 376 | ) 377 | (pad "S1" thru_hole oval 378 | (at 4.32 1.075 90) 379 | (size 1 1.8) 380 | (drill oval 0.6 1.4) 381 | (layers "*.Cu" "*.Mask" "F.Paste") 382 | (remove_unused_layers no) 383 | (uuid "aafacb1f-847c-402b-b862-3067ed09c640") 384 | ) 385 | (model "${KICAD8_3DMODEL_DIR}/Connector_USB.3dshapes/USB_C_Receptacle_GCT_USB4105-xx-A_16P_TopMnt_Horizontal.wrl" 386 | (offset 387 | (xyz 0 0 0) 388 | ) 389 | (scale 390 | (xyz 1 1 1) 391 | ) 392 | (rotate 393 | (xyz 0 0 0) 394 | ) 395 | ) 396 | ) 397 | (footprint "Connector_USB:USB_C_Receptacle_GCT_USB4105-xx-A_16P_TopMnt_Horizontal" 398 | (layer "B.Cu") 399 | (uuid "12a9916d-ecfb-478f-906b-016b05c851cc") 400 | (at 165.5 88.5 90) 401 | (descr "USB 2.0 Type C Receptacle, GCT, 16P, top mounted, horizontal, 5A: https://gct.co/files/drawings/usb4105.pdf") 402 | (tags "USB C Type-C Receptacle SMD USB 2.0 16P 16C USB4105-15-A USB4105-15-A-060 USB4105-15-A-120 USB4105-GF-A USB4105-GF-A-060 USB4105-GF-A-120") 403 | (property "Reference" "REF**" 404 | (at 0 5.5 -90) 405 | (unlocked yes) 406 | (layer "B.SilkS") 407 | (hide yes) 408 | (uuid "7f063368-3250-4cf4-a652-982436c948bd") 409 | (effects 410 | (font 411 | (size 1 1) 412 | (thickness 0.15) 413 | ) 414 | (justify mirror) 415 | ) 416 | ) 417 | (property "Value" "USB_C_Receptacle_GCT_USB4105-xx-A_16P_TopMnt_Horizontal" 418 | (at 0 -5 -90) 419 | (unlocked yes) 420 | (layer "B.Fab") 421 | (uuid "ef701eff-bf72-43e7-b3be-46c619fed945") 422 | (effects 423 | (font 424 | (size 1 1) 425 | (thickness 0.15) 426 | ) 427 | (justify mirror) 428 | ) 429 | ) 430 | (property "Footprint" "Connector_USB:USB_C_Receptacle_GCT_USB4105-xx-A_16P_TopMnt_Horizontal" 431 | (at 0 0 -90) 432 | (unlocked yes) 433 | (layer "B.Fab") 434 | (hide yes) 435 | (uuid "7ff5132c-5aaf-48ff-9e44-9a1fcee8c209") 436 | (effects 437 | (font 438 | (size 1.27 1.27) 439 | ) 440 | (justify mirror) 441 | ) 442 | ) 443 | (property "Datasheet" "" 444 | (at 0 0 -90) 445 | (unlocked yes) 446 | (layer "B.Fab") 447 | (hide yes) 448 | (uuid "28d78651-998e-4d2d-b6ce-48836217ee36") 449 | (effects 450 | (font 451 | (size 1.27 1.27) 452 | ) 453 | (justify mirror) 454 | ) 455 | ) 456 | (property "Description" "" 457 | (at 0 0 -90) 458 | (unlocked yes) 459 | (layer "B.Fab") 460 | (hide yes) 461 | (uuid "794d115a-3722-4fc8-a602-4fa698a8dfbb") 462 | (effects 463 | (font 464 | (size 1.27 1.27) 465 | ) 466 | (justify mirror) 467 | ) 468 | ) 469 | (attr smd) 470 | (fp_line 471 | (start 4.67 1.8) 472 | (end 4.67 0.1) 473 | (stroke 474 | (width 0.12) 475 | (type solid) 476 | ) 477 | (layer "B.SilkS") 478 | (uuid "4653819e-3e06-45ef-9bce-5e0bbe3c75ba") 479 | ) 480 | (fp_line 481 | (start -4.67 1.8) 482 | (end -4.67 0.1) 483 | (stroke 484 | (width 0.12) 485 | (type solid) 486 | ) 487 | (layer "B.SilkS") 488 | (uuid "24f418b7-cf4b-4daf-a293-5c70ce6bcba1") 489 | ) 490 | (fp_line 491 | (start -5 -3.675) 492 | (end 5 -3.675) 493 | (stroke 494 | (width 0.1) 495 | (type solid) 496 | ) 497 | (layer "Dwgs.User") 498 | (uuid "9dbe1824-a91d-48e1-aed3-e3ec46631ec2") 499 | ) 500 | (fp_rect 501 | (start 5.32 -4.18) 502 | (end -5.32 4.76) 503 | (stroke 504 | (width 0.05) 505 | (type solid) 506 | ) 507 | (fill none) 508 | (layer "B.CrtYd") 509 | (uuid "26df7426-6bd6-45a1-87de-32edf41f8e1c") 510 | ) 511 | (fp_rect 512 | (start 4.47 -3.675) 513 | (end -4.47 3.675) 514 | (stroke 515 | (width 0.1) 516 | (type solid) 517 | ) 518 | (fill none) 519 | (layer "B.Fab") 520 | (uuid "27a3b8c5-cd24-4039-aa5f-411803845ac4") 521 | ) 522 | (fp_text user "PCB Edge" 523 | (at 0 -3.1 -90) 524 | (unlocked yes) 525 | (layer "Dwgs.User") 526 | (uuid "4b33d30e-ffee-4a6c-baae-a57a7a30b4ec") 527 | (effects 528 | (font 529 | (size 0.5 0.5) 530 | (thickness 0.1) 531 | ) 532 | ) 533 | ) 534 | (fp_text user "${REFERENCE}" 535 | (at 0 0 -90) 536 | (unlocked yes) 537 | (layer "B.Fab") 538 | (uuid "b6de5ac1-3026-473b-9039-9c83aff40e99") 539 | (effects 540 | (font 541 | (size 1 1) 542 | (thickness 0.15) 543 | ) 544 | (justify mirror) 545 | ) 546 | ) 547 | (pad "" np_thru_hole circle 548 | (at -2.89 2.605 90) 549 | (size 0.65 0.65) 550 | (drill 0.65) 551 | (layers "F&B.Cu" "*.Mask") 552 | (uuid "a043a9ab-a97c-4e20-a6d8-28e53e573111") 553 | ) 554 | (pad "" np_thru_hole circle 555 | (at 2.89 2.605 90) 556 | (size 0.65 0.65) 557 | (drill 0.65) 558 | (layers "F&B.Cu" "*.Mask") 559 | (uuid "56bb0297-298f-4c90-bcff-80ee15812869") 560 | ) 561 | (pad "A1" smd roundrect 562 | (at -3.2 3.68 90) 563 | (size 0.6 1.15) 564 | (layers "B.Cu" "B.Paste" "B.Mask") 565 | (roundrect_rratio 0.25) 566 | (uuid "2fca40d0-da24-44bb-bd60-68738b692849") 567 | ) 568 | (pad "A4" smd roundrect 569 | (at -2.4 3.68 90) 570 | (size 0.6 1.15) 571 | (layers "B.Cu" "B.Paste" "B.Mask") 572 | (roundrect_rratio 0.25) 573 | (uuid "e68135ca-acfd-42ab-8167-64b9da1c2bdf") 574 | ) 575 | (pad "A5" smd roundrect 576 | (at -1.25 3.68 90) 577 | (size 0.3 1.15) 578 | (layers "B.Cu" "B.Paste" "B.Mask") 579 | (roundrect_rratio 0.25) 580 | (uuid "9c9887c8-41fa-40b1-8fef-5cbe0907d49f") 581 | ) 582 | (pad "A6" smd roundrect 583 | (at -0.25 3.68 90) 584 | (size 0.3 1.15) 585 | (layers "B.Cu" "B.Paste" "B.Mask") 586 | (roundrect_rratio 0.25) 587 | (uuid "b0504463-ba29-4db5-81b6-bca3282ecd03") 588 | ) 589 | (pad "A7" smd roundrect 590 | (at 0.25 3.68 90) 591 | (size 0.3 1.15) 592 | (layers "B.Cu" "B.Paste" "B.Mask") 593 | (roundrect_rratio 0.25) 594 | (uuid "71cbc25f-0065-4b92-989f-5c58afd84c30") 595 | ) 596 | (pad "A8" smd roundrect 597 | (at 1.25 3.68 90) 598 | (size 0.3 1.15) 599 | (layers "B.Cu" "B.Paste" "B.Mask") 600 | (roundrect_rratio 0.25) 601 | (uuid "134a9583-5cc7-4d3e-9899-59b3758d029e") 602 | ) 603 | (pad "A9" smd roundrect 604 | (at 2.4 3.68 90) 605 | (size 0.6 1.15) 606 | (layers "B.Cu" "B.Paste" "B.Mask") 607 | (roundrect_rratio 0.25) 608 | (uuid "17180561-5eb7-4d1d-9a36-ff269589600a") 609 | ) 610 | (pad "A12" smd roundrect 611 | (at 3.2 3.68 90) 612 | (size 0.6 1.15) 613 | (layers "B.Cu" "B.Paste" "B.Mask") 614 | (roundrect_rratio 0.25) 615 | (uuid "ec234158-c43a-446e-bbce-07dc793fa2c5") 616 | ) 617 | (pad "B1" smd roundrect 618 | (at 3.2 3.68 90) 619 | (size 0.6 1.15) 620 | (layers "B.Cu" "B.Paste" "B.Mask") 621 | (roundrect_rratio 0.25) 622 | (uuid "2519164a-562e-4baa-a629-c58970688dff") 623 | ) 624 | (pad "B4" smd roundrect 625 | (at 2.4 3.68 90) 626 | (size 0.6 1.15) 627 | (layers "B.Cu" "B.Paste" "B.Mask") 628 | (roundrect_rratio 0.25) 629 | (uuid "7ba31ef6-808b-416b-97d4-66370df6f589") 630 | ) 631 | (pad "B5" smd roundrect 632 | (at 1.75 3.68 90) 633 | (size 0.3 1.15) 634 | (layers "B.Cu" "B.Paste" "B.Mask") 635 | (roundrect_rratio 0.25) 636 | (uuid "52c00742-ad08-477b-a010-33f4f1855371") 637 | ) 638 | (pad "B6" smd roundrect 639 | (at 0.75 3.68 90) 640 | (size 0.3 1.15) 641 | (layers "B.Cu" "B.Paste" "B.Mask") 642 | (roundrect_rratio 0.25) 643 | (uuid "5ff21b74-20d5-4d88-80fa-25ae24c7c99c") 644 | ) 645 | (pad "B7" smd roundrect 646 | (at -0.75 3.68 90) 647 | (size 0.3 1.15) 648 | (layers "B.Cu" "B.Paste" "B.Mask") 649 | (roundrect_rratio 0.25) 650 | (uuid "c3bc3713-b162-462c-a379-847d29ba4206") 651 | ) 652 | (pad "B8" smd roundrect 653 | (at -1.75 3.68 90) 654 | (size 0.3 1.15) 655 | (layers "B.Cu" "B.Paste" "B.Mask") 656 | (roundrect_rratio 0.25) 657 | (uuid "9adb31ac-3804-4396-8f3d-c6202080a461") 658 | ) 659 | (pad "B9" smd roundrect 660 | (at -2.4 3.68 90) 661 | (size 0.6 1.15) 662 | (layers "B.Cu" "B.Paste" "B.Mask") 663 | (roundrect_rratio 0.25) 664 | (uuid "01edcc20-650d-4e3b-bc31-3a0ab57bbe39") 665 | ) 666 | (pad "B12" smd roundrect 667 | (at -3.2 3.68 90) 668 | (size 0.6 1.15) 669 | (layers "B.Cu" "B.Paste" "B.Mask") 670 | (roundrect_rratio 0.25) 671 | (uuid "280db069-da29-4433-890d-e3044bed7ffe") 672 | ) 673 | (pad "S1" thru_hole oval 674 | (at -4.32 -1.075 90) 675 | (size 1 1.8) 676 | (drill oval 0.6 1.4) 677 | (layers "*.Cu" "*.Mask" "B.Paste") 678 | (remove_unused_layers no) 679 | (uuid "3e35f903-9002-49c0-a1d7-28b8fcbd7dcc") 680 | ) 681 | (pad "S1" thru_hole oval 682 | (at -4.32 3.105 90) 683 | (size 1 2.1) 684 | (drill oval 0.6 1.7) 685 | (layers "*.Cu" "*.Mask" "B.Paste") 686 | (remove_unused_layers no) 687 | (uuid "a2da50d4-ef51-47a0-8657-8c2fdc9a1dc7") 688 | ) 689 | (pad "S1" thru_hole oval 690 | (at 4.32 -1.075 90) 691 | (size 1 1.8) 692 | (drill oval 0.6 1.4) 693 | (layers "*.Cu" "*.Mask" "B.Paste") 694 | (remove_unused_layers no) 695 | (uuid "aafacb1f-847c-402b-b862-3067ed09c640") 696 | ) 697 | (pad "S1" thru_hole oval 698 | (at 4.32 3.105 90) 699 | (size 1 2.1) 700 | (drill oval 0.6 1.7) 701 | (layers "*.Cu" "*.Mask" "B.Paste") 702 | (remove_unused_layers no) 703 | (uuid "e986f1d6-5dfe-4149-8a6c-872051f1fa19") 704 | ) 705 | (model "${KICAD8_3DMODEL_DIR}/Connector_USB.3dshapes/USB_C_Receptacle_GCT_USB4105-xx-A_16P_TopMnt_Horizontal.wrl" 706 | (offset 707 | (xyz 0 0 0) 708 | ) 709 | (scale 710 | (xyz 1 1 1) 711 | ) 712 | (rotate 713 | (xyz 0 0 0) 714 | ) 715 | ) 716 | ) 717 | (gr_rect 718 | (start 162.5 78) 719 | (end 198.5 114.5) 720 | (stroke 721 | (width 0.2) 722 | (type default) 723 | ) 724 | (fill none) 725 | (layer "F.Cu") 726 | (uuid "73c905c8-4229-4b8f-a692-47d4d3899763") 727 | ) 728 | (gr_text "Trans rights now!!!!\n\nback" 729 | (at 188.5 92.5 0) 730 | (layer "B.SilkS") 731 | (uuid "681b5fcc-ff7f-4a2d-9c82-636542d6bfc1") 732 | (effects 733 | (font 734 | (face "Arial Black") 735 | (size 1 1) 736 | (thickness 0.1) 737 | ) 738 | (justify left bottom mirror) 739 | ) 740 | ) 741 | (gr_text "Front ███" 742 | (at 177.5 98 0) 743 | (layer "F.SilkS") 744 | (uuid "28577b04-fc35-4189-a192-10eba91eead3") 745 | (effects 746 | (font 747 | (face "Comic Sans MS") 748 | (size 1 1) 749 | (thickness 0.1) 750 | ) 751 | (justify left bottom) 752 | ) 753 | ) 754 | (gr_text "Trans rights!!!" 755 | (at 176 83.5 0) 756 | (layer "F.SilkS") 757 | (uuid "4152af1b-be56-4431-be99-d83bdfd44541") 758 | (effects 759 | (font 760 | (size 1 1) 761 | (thickness 0.1) 762 | ) 763 | (justify left bottom) 764 | ) 765 | ) 766 | (segment 767 | (start 193 85.55) 768 | (end 190.95 85.55) 769 | (width 0.2) 770 | (layer "F.Cu") 771 | (net 0) 772 | (uuid "0f7481b4-46ba-46d6-988d-224df0878b58") 773 | ) 774 | (segment 775 | (start 190.95 85.55) 776 | (end 190.782843 85.717157) 777 | (width 0.2) 778 | (layer "F.Cu") 779 | (net 0) 780 | (uuid "101df164-21da-4002-8088-2e4a50831f5d") 781 | ) 782 | (segment 783 | (start 180 85.5) 784 | (end 190 85.5) 785 | (width 0.2) 786 | (layer "F.Cu") 787 | (net 0) 788 | (uuid "3fd898a6-40a5-47fd-979a-8f58edb14083") 789 | ) 790 | (segment 791 | (start 190 85.5) 792 | (end 190.5 86) 793 | (width 0.2) 794 | (layer "F.Cu") 795 | (net 0) 796 | (uuid "5240b6c5-630b-43fe-ac75-2f7490336d1a") 797 | ) 798 | (via 799 | (at 180 85.5) 800 | (size 0.6) 801 | (drill 0.3) 802 | (layers "F.Cu" "B.Cu") 803 | (net 0) 804 | (uuid "402ed3d0-966c-483d-9e2c-ddfac15c662d") 805 | ) 806 | (segment 807 | (start 179.8 85.3) 808 | (end 180 85.5) 809 | (width 0.2) 810 | (layer "B.Cu") 811 | (net 0) 812 | (uuid "1dd6de0a-662d-4aa1-a892-64df24bbdfcc") 813 | ) 814 | (segment 815 | (start 169.18 85.3) 816 | (end 179.8 85.3) 817 | (width 0.2) 818 | (layer "B.Cu") 819 | (net 0) 820 | (uuid "31f4a6d3-5659-4aa2-9061-3ce3b870f1fd") 821 | ) 822 | ) 823 | -------------------------------------------------------------------------------- /.github/tests/test pcb/test.kicad_pro: -------------------------------------------------------------------------------- 1 | { 2 | "board": { 3 | "3dviewports": [], 4 | "design_settings": { 5 | "defaults": { 6 | "apply_defaults_to_fp_fields": false, 7 | "apply_defaults_to_fp_shapes": false, 8 | "apply_defaults_to_fp_text": false, 9 | "board_outline_line_width": 0.05, 10 | "copper_line_width": 0.2, 11 | "copper_text_italic": false, 12 | "copper_text_size_h": 1.5, 13 | "copper_text_size_v": 1.5, 14 | "copper_text_thickness": 0.3, 15 | "copper_text_upright": false, 16 | "courtyard_line_width": 0.05, 17 | "dimension_precision": 4, 18 | "dimension_units": 3, 19 | "dimensions": { 20 | "arrow_length": 1270000, 21 | "extension_offset": 500000, 22 | "keep_text_aligned": true, 23 | "suppress_zeroes": false, 24 | "text_position": 0, 25 | "units_format": 1 26 | }, 27 | "fab_line_width": 0.1, 28 | "fab_text_italic": false, 29 | "fab_text_size_h": 1.0, 30 | "fab_text_size_v": 1.0, 31 | "fab_text_thickness": 0.15, 32 | "fab_text_upright": false, 33 | "other_line_width": 0.1, 34 | "other_text_italic": false, 35 | "other_text_size_h": 1.0, 36 | "other_text_size_v": 1.0, 37 | "other_text_thickness": 0.15, 38 | "other_text_upright": false, 39 | "pads": { 40 | "drill": 0.762, 41 | "height": 1.524, 42 | "width": 1.524 43 | }, 44 | "silk_line_width": 0.1, 45 | "silk_text_italic": false, 46 | "silk_text_size_h": 1.0, 47 | "silk_text_size_v": 1.0, 48 | "silk_text_thickness": 0.1, 49 | "silk_text_upright": false, 50 | "zones": { 51 | "min_clearance": 0.5 52 | } 53 | }, 54 | "diff_pair_dimensions": [], 55 | "drc_exclusions": [], 56 | "meta": { 57 | "version": 2 58 | }, 59 | "rule_severities": { 60 | "annular_width": "error", 61 | "clearance": "error", 62 | "connection_width": "warning", 63 | "copper_edge_clearance": "error", 64 | "copper_sliver": "warning", 65 | "courtyards_overlap": "error", 66 | "creepage": "error", 67 | "diff_pair_gap_out_of_range": "error", 68 | "diff_pair_uncoupled_length_too_long": "error", 69 | "drill_out_of_range": "error", 70 | "duplicate_footprints": "warning", 71 | "extra_footprint": "warning", 72 | "footprint": "error", 73 | "footprint_filters_mismatch": "ignore", 74 | "footprint_symbol_mismatch": "warning", 75 | "footprint_type_mismatch": "ignore", 76 | "hole_clearance": "error", 77 | "hole_near_hole": "error", 78 | "hole_to_hole": "error", 79 | "holes_co_located": "warning", 80 | "invalid_outline": "error", 81 | "isolated_copper": "warning", 82 | "item_on_disabled_layer": "error", 83 | "items_not_allowed": "error", 84 | "length_out_of_range": "error", 85 | "lib_footprint_issues": "warning", 86 | "lib_footprint_mismatch": "warning", 87 | "malformed_courtyard": "error", 88 | "microvia_drill_out_of_range": "error", 89 | "mirrored_text_on_front_layer": "warning", 90 | "missing_courtyard": "ignore", 91 | "missing_footprint": "warning", 92 | "net_conflict": "warning", 93 | "nonmirrored_text_on_back_layer": "warning", 94 | "npth_inside_courtyard": "ignore", 95 | "padstack": "warning", 96 | "pth_inside_courtyard": "ignore", 97 | "shorting_items": "error", 98 | "silk_edge_clearance": "warning", 99 | "silk_over_copper": "warning", 100 | "silk_overlap": "warning", 101 | "skew_out_of_range": "error", 102 | "solder_mask_bridge": "error", 103 | "starved_thermal": "error", 104 | "text_height": "warning", 105 | "text_thickness": "warning", 106 | "through_hole_pad_without_hole": "error", 107 | "too_many_vias": "error", 108 | "track_angle": "error", 109 | "track_dangling": "warning", 110 | "track_segment_length": "error", 111 | "track_width": "error", 112 | "tracks_crossing": "error", 113 | "unconnected_items": "error", 114 | "unresolved_variable": "error", 115 | "via_dangling": "warning", 116 | "zones_intersect": "error" 117 | }, 118 | "rules": { 119 | "max_error": 0.005, 120 | "min_clearance": 0.0, 121 | "min_connection": 0.0, 122 | "min_copper_edge_clearance": 0.5, 123 | "min_groove_width": 0.0, 124 | "min_hole_clearance": 0.25, 125 | "min_hole_to_hole": 0.25, 126 | "min_microvia_diameter": 0.2, 127 | "min_microvia_drill": 0.1, 128 | "min_resolved_spokes": 2, 129 | "min_silk_clearance": 0.0, 130 | "min_text_height": 0.8, 131 | "min_text_thickness": 0.08, 132 | "min_through_hole_diameter": 0.3, 133 | "min_track_width": 0.0, 134 | "min_via_annular_width": 0.1, 135 | "min_via_diameter": 0.5, 136 | "solder_mask_to_copper_clearance": 0.0, 137 | "use_height_for_length_calcs": true 138 | }, 139 | "teardrop_options": [ 140 | { 141 | "td_onpthpad": true, 142 | "td_onroundshapesonly": false, 143 | "td_onsmdpad": true, 144 | "td_ontrackend": false, 145 | "td_onvia": true 146 | } 147 | ], 148 | "teardrop_parameters": [ 149 | { 150 | "td_allow_use_two_tracks": true, 151 | "td_curve_segcount": 0, 152 | "td_height_ratio": 1.0, 153 | "td_length_ratio": 0.5, 154 | "td_maxheight": 2.0, 155 | "td_maxlen": 1.0, 156 | "td_on_pad_in_zone": false, 157 | "td_target_name": "td_round_shape", 158 | "td_width_to_size_filter_ratio": 0.9 159 | }, 160 | { 161 | "td_allow_use_two_tracks": true, 162 | "td_curve_segcount": 0, 163 | "td_height_ratio": 1.0, 164 | "td_length_ratio": 0.5, 165 | "td_maxheight": 2.0, 166 | "td_maxlen": 1.0, 167 | "td_on_pad_in_zone": false, 168 | "td_target_name": "td_rect_shape", 169 | "td_width_to_size_filter_ratio": 0.9 170 | }, 171 | { 172 | "td_allow_use_two_tracks": true, 173 | "td_curve_segcount": 0, 174 | "td_height_ratio": 1.0, 175 | "td_length_ratio": 0.5, 176 | "td_maxheight": 2.0, 177 | "td_maxlen": 1.0, 178 | "td_on_pad_in_zone": false, 179 | "td_target_name": "td_track_end", 180 | "td_width_to_size_filter_ratio": 0.9 181 | } 182 | ], 183 | "track_widths": [], 184 | "tuning_pattern_settings": { 185 | "diff_pair_defaults": { 186 | "corner_radius_percentage": 80, 187 | "corner_style": 1, 188 | "max_amplitude": 1.0, 189 | "min_amplitude": 0.2, 190 | "single_sided": false, 191 | "spacing": 1.0 192 | }, 193 | "diff_pair_skew_defaults": { 194 | "corner_radius_percentage": 80, 195 | "corner_style": 1, 196 | "max_amplitude": 1.0, 197 | "min_amplitude": 0.2, 198 | "single_sided": false, 199 | "spacing": 0.6 200 | }, 201 | "single_track_defaults": { 202 | "corner_radius_percentage": 80, 203 | "corner_style": 1, 204 | "max_amplitude": 1.0, 205 | "min_amplitude": 0.2, 206 | "single_sided": false, 207 | "spacing": 0.6 208 | } 209 | }, 210 | "via_dimensions": [], 211 | "zones_allow_external_fillets": false 212 | }, 213 | "ipc2581": { 214 | "dist": "", 215 | "distpn": "", 216 | "internal_id": "", 217 | "mfg": "", 218 | "mpn": "" 219 | }, 220 | "layer_pairs": [], 221 | "layer_presets": [], 222 | "viewports": [] 223 | }, 224 | "boards": [], 225 | "cvpcb": { 226 | "equivalence_files": [] 227 | }, 228 | "libraries": { 229 | "pinned_footprint_libs": [], 230 | "pinned_symbol_libs": [] 231 | }, 232 | "meta": { 233 | "filename": "test.kicad_pro", 234 | "version": 3 235 | }, 236 | "net_settings": { 237 | "classes": [ 238 | { 239 | "bus_width": 12, 240 | "clearance": 0.2, 241 | "diff_pair_gap": 0.25, 242 | "diff_pair_via_gap": 0.25, 243 | "diff_pair_width": 0.2, 244 | "line_style": 0, 245 | "microvia_diameter": 0.3, 246 | "microvia_drill": 0.1, 247 | "name": "Default", 248 | "pcb_color": "rgba(0, 0, 0, 0.000)", 249 | "priority": 2147483647, 250 | "schematic_color": "rgba(0, 0, 0, 0.000)", 251 | "track_width": 0.2, 252 | "via_diameter": 0.6, 253 | "via_drill": 0.3, 254 | "wire_width": 6 255 | } 256 | ], 257 | "meta": { 258 | "version": 4 259 | }, 260 | "net_colors": null, 261 | "netclass_assignments": null, 262 | "netclass_patterns": [] 263 | }, 264 | "pcbnew": { 265 | "last_paths": { 266 | "gencad": "", 267 | "idf": "", 268 | "netlist": "", 269 | "plot": "", 270 | "pos_files": "", 271 | "specctra_dsn": "", 272 | "step": "", 273 | "svg": "", 274 | "vrml": "" 275 | }, 276 | "page_layout_descr_file": "" 277 | }, 278 | "schematic": { 279 | "legacy_lib_dir": "", 280 | "legacy_lib_list": [] 281 | }, 282 | "sheets": [], 283 | "text_variables": {} 284 | } 285 | -------------------------------------------------------------------------------- /.github/tests/test pcb/test.kicad_sch: -------------------------------------------------------------------------------- 1 | (kicad_sch (version 20231120) (generator "eeschema") (generator_version "8.0") 2 | (paper "A4") 3 | (lib_symbols) 4 | (symbol_instances) 5 | ) 6 | -------------------------------------------------------------------------------- /.github/workflows/image.yaml: -------------------------------------------------------------------------------- 1 | name: build kicad-render image 2 | on: 3 | workflow_dispatch: 4 | push: 5 | paths: 6 | - Dockerfile 7 | - '*.sh' 8 | - .github/workflows/image.yaml 9 | - .github/workflows/tests 10 | - action.yaml 11 | 12 | jobs: 13 | 14 | push_to_registry: 15 | name: Build, Test and Push 16 | runs-on: ubuntu-latest 17 | env: 18 | docker_image: ghcr.io/linalinn/kicad-render:${{ github.ref_name }} 19 | docker_image_cache: ghcr.io/linalinn/kicad-render:cache-${{ github.ref_name }} 20 | docker_file: ./Dockerfile 21 | 22 | permissions: 23 | packages: write 24 | contents: read 25 | 26 | steps: 27 | - name: Check out the repo 28 | uses: actions/checkout@v4 29 | 30 | - name: Login to GitHub Container Registry 31 | uses: docker/login-action@v3 32 | with: 33 | registry: ghcr.io 34 | username: ${{ github.actor }} 35 | password: ${{ secrets.GITHUB_TOKEN }} 36 | 37 | - name: Set up Docker Buildx 38 | uses: docker/setup-buildx-action@v3 39 | 40 | - name: Get current date 41 | id: date 42 | run: echo "date=$(date +'%Y-%m-%d-%H-%M')" >> $GITHUB_OUTPUT 43 | 44 | - name: Build and push Docker image dev 45 | uses: docker/build-push-action@v5 46 | with: 47 | context: . 48 | file: ${{ env.docker_file }} 49 | build-args: | 50 | "VERSION=${{ github.sha }}" 51 | cache-to: "type=registry,ref=${{ env.docker_image_cache }}" 52 | cache-from: "type=registry,ref=${{ env.docker_image_cache }}" 53 | load: true 54 | tags: "${{ env.docker_image }}" 55 | 56 | - name: simple render 57 | run: docker run -v ${GITHUB_WORKSPACE}:/pwd --rm ${{ env.docker_image }} render-pcb.sh -f "/pwd/.github/tests/test pcb/test.kicad_pcb" 58 | - name: simple render - test file created 59 | run: test -s "${GITHUB_WORKSPACE}/.github/tests/test pcb/test_top.png" 60 | 61 | - name: simple render with output path 62 | run: docker run -v ${GITHUB_WORKSPACE}:/pwd --rm ${{ env.docker_image }} render-pcb.sh -f "/pwd/.github/tests/test pcb/test.kicad_pcb" -o "/pwd/images" 63 | - name: simple render with output path - test file created 64 | run: test -s "${GITHUB_WORKSPACE}/images/top.png" 65 | 66 | - name: simple render with filename prefix 67 | run: docker run -v ${GITHUB_WORKSPACE}:/pwd --rm ${{ env.docker_image }} render-pcb.sh -f "/pwd/.github/tests/test pcb/test.kicad_pcb" -p fp 68 | - name: simple render with filename prefix - test file created 69 | run: test -s "${GITHUB_WORKSPACE}/.github/tests/test pcb/fp_top.png" 70 | 71 | - name: simple render with output path and filename prefix 72 | run: docker run -v ${GITHUB_WORKSPACE}:/pwd --rm ${{ env.docker_image }} render-pcb.sh -f "/pwd/.github/tests/test pcb/test.kicad_pcb" -o "/pwd/images" -p fp 73 | - name: simple render with output path and filename prefix - test file created 74 | run: test -s "${GITHUB_WORKSPACE}/images/fp_top.png" 75 | 76 | - name: simple render with zoom 77 | run: docker run -v ${GITHUB_WORKSPACE}:/pwd --rm ${{ env.docker_image }} render-pcb.sh -f "/pwd/.github/tests/test pcb/test.kicad_pcb" -o "/pwd/images" -z "0.5" 78 | - name: simple render with zoom - test file created 79 | run: test -s "${GITHUB_WORKSPACE}/images/top.png" 80 | 81 | - name: render with animation 82 | run: docker run -v ${GITHUB_WORKSPACE}:/pwd --rm ${{ env.docker_image }} render-pcb.sh -f "/pwd/.github/tests/test pcb/test.kicad_pcb" -o "/pwd/images" -a "gif" 83 | - name: render with animation- test file created 84 | run: test -s "${GITHUB_WORKSPACE}/images/rotating.gif" 85 | 86 | - name: replace docker image with branch image 87 | run: sed -i 's#ghcr.io/linalinn/kicad-render.*$#${{ env.docker_image }}#g' ${GITHUB_WORKSPACE}/action.yaml && cat ${GITHUB_WORKSPACE}/action.yaml 88 | 89 | - name: INT-TEST render pcb image with pre_render script 90 | uses: ./ 91 | with: 92 | pcb_file: "${{ github.workspace }}/.github/tests/test pcb/test.kicad_pcb" 93 | output_path: ${{ github.workspace }}/images/action 94 | pre_render: "${{ github.workspace }}/.github/tests/test pcb/kicad_render_pre_render.sh" 95 | 96 | - name: check INT-TEST render pcb image with pre_render script 97 | run: test -s "${GITHUB_WORKSPACE}/images/action/prefix-via pre-render_top.png" 98 | 99 | - name: INT-TEST render pcb image 100 | uses: ./ 101 | with: 102 | pcb_file: "${{ github.workspace }}/.github/tests/test pcb/test.kicad_pcb" 103 | output_path: "${{ github.workspace }}/images/action" 104 | background: transparent 105 | zoom: 0.5 106 | animation: "gif" 107 | kicad_cli_options: " --perspective" 108 | 109 | - name: check INT-TEST render pcb image 110 | run: test -s "${GITHUB_WORKSPACE}/images/action/rotating.gif" 111 | 112 | - name: Archive test renders 113 | if: always() 114 | uses: actions/upload-artifact@v4 115 | with: 116 | name: test-renders 117 | path: ${{ github.workspace }}/images 118 | 119 | - name: push docker image dev 120 | if: github.ref_name != github.event.repository.default_branch 121 | run: | 122 | docker tag ${{ env.docker_image }} ghcr.io/linalinn/kicad-render:9-dev-${{ github.ref_name }} 123 | docker tag ${{ env.docker_image }} ghcr.io/linalinn/kicad-render:9-dev-${{ steps.date.outputs.date }} 124 | docker push ghcr.io/linalinn/kicad-render:9-dev-${{ github.ref_name }} 125 | docker push ghcr.io/linalinn/kicad-render:9-dev-${{ steps.date.outputs.date }} 126 | 127 | - name: push docker image 128 | if: github.ref_name == github.event.repository.default_branch 129 | run: | 130 | docker tag ${{ env.docker_image }} ghcr.io/linalinn/kicad-render:9 131 | docker tag ${{ env.docker_image }} ghcr.io/linalinn/kicad-render:9-${{ steps.date.outputs.date }} 132 | docker push ghcr.io/linalinn/kicad-render:9 133 | docker push ghcr.io/linalinn/kicad-render:9-${{ steps.date.outputs.date }} 134 | -------------------------------------------------------------------------------- /.github/workflows/kicad-nightly.yaml: -------------------------------------------------------------------------------- 1 | name: build kicad-render nightly image 2 | on: 3 | schedule: 4 | - cron: 12 12 1 * * 5 | workflow_dispatch: 6 | push: 7 | paths: 8 | - Dockerfile.kicad-nightly 9 | - .github/workflows/kicad-nightly.yaml 10 | - .github/workflows/tests 11 | - action.yaml 12 | - '*.sh' 13 | 14 | jobs: 15 | push_to_registry: 16 | name: Push Docker image to Docker Hub 17 | runs-on: ubuntu-latest 18 | env: 19 | docker_image: ghcr.io/linalinn/kicad-render:nightly-${{ github.ref_name }} 20 | docker_image_cache: ghcr.io/linalinn/kicad-render:cache-nightly-${{ github.ref_name }} 21 | docker_file: ./Dockerfile.kicad-nightly 22 | 23 | permissions: 24 | packages: write 25 | contents: read 26 | steps: 27 | - name: Check out the repo 28 | uses: actions/checkout@v4 29 | 30 | - name: Login to GitHub Container Registry 31 | uses: docker/login-action@v3 32 | with: 33 | registry: ghcr.io 34 | username: ${{ github.actor }} 35 | password: ${{ secrets.GITHUB_TOKEN }} 36 | 37 | - name: Set up Docker Buildx 38 | uses: docker/setup-buildx-action@v3 39 | 40 | - name: Get current date 41 | id: date 42 | run: echo "date=$(date +'%Y-%m-%d-%H-%M')" >> $GITHUB_OUTPUT 43 | 44 | - name: Build and push Docker image dev 45 | uses: docker/build-push-action@v5 46 | with: 47 | context: . 48 | file: ${{ env.docker_file }} 49 | build-args: | 50 | "VERSION=${{ github.sha }}" 51 | cache-to: "type=registry,ref=${{ env.docker_image_cache }}" 52 | cache-from: "type=registry,ref=${{ env.docker_image_cache }}" 53 | load: true 54 | tags: "${{ env.docker_image }}" 55 | 56 | - name: simple render 57 | run: docker run -v ${GITHUB_WORKSPACE}:/pwd --rm ${{ env.docker_image }} render-pcb.sh -f "/pwd/.github/tests/test pcb/test.kicad_pcb" 58 | - name: simple render - test file created 59 | run: test -s "${GITHUB_WORKSPACE}/.github/tests/test pcb/test_top.png" 60 | 61 | - name: simple render with output path 62 | run: docker run -v ${GITHUB_WORKSPACE}:/pwd --rm ${{ env.docker_image }} render-pcb.sh -f "/pwd/.github/tests/test pcb/test.kicad_pcb" -o "/pwd/images" 63 | - name: simple render with output path - test file created 64 | run: test -s "${GITHUB_WORKSPACE}/images/top.png" 65 | 66 | - name: simple render with filename prefix 67 | run: docker run -v ${GITHUB_WORKSPACE}:/pwd --rm ${{ env.docker_image }} render-pcb.sh -f "/pwd/.github/tests/test pcb/test.kicad_pcb" -p fp 68 | - name: simple render with filename prefix - test file created 69 | run: test -s "${GITHUB_WORKSPACE}/.github/tests/test pcb/fp_top.png" 70 | 71 | - name: simple render with output path and filename prefix 72 | run: docker run -v ${GITHUB_WORKSPACE}:/pwd --rm ${{ env.docker_image }} render-pcb.sh -f "/pwd/.github/tests/test pcb/test.kicad_pcb" -o "/pwd/images" -p fp 73 | - name: simple render with output path and filename prefix - test file created 74 | run: test -s "${GITHUB_WORKSPACE}/images/fp_top.png" 75 | 76 | - name: simple render with zoom 77 | run: docker run -v ${GITHUB_WORKSPACE}:/pwd --rm ${{ env.docker_image }} render-pcb.sh -f "/pwd/.github/tests/test pcb/test.kicad_pcb" -o "/pwd/images" -z "0.5" 78 | - name: simple render with zoom - test file created 79 | run: test -s "${GITHUB_WORKSPACE}/images/top.png" 80 | 81 | - name: render with animation 82 | run: docker run -v ${GITHUB_WORKSPACE}:/pwd --rm ${{ env.docker_image }} render-pcb.sh -f "/pwd/.github/tests/test pcb/test.kicad_pcb" -o "/pwd/images" -a "gif" 83 | - name: render with animation- test file created 84 | run: test -s "${GITHUB_WORKSPACE}/images/rotating.gif" 85 | 86 | - name: replace docker image with branch image 87 | run: sed -i 's#ghcr.io/linalinn/kicad-render.*$#${{ env.docker_image }}#g' ${GITHUB_WORKSPACE}/action.yaml && cat ${GITHUB_WORKSPACE}/action.yaml 88 | 89 | - name: INT-TEST render pcb image with pre_render script 90 | uses: ./ 91 | with: 92 | pcb_file: "${{ github.workspace }}/.github/tests/test pcb/test.kicad_pcb" 93 | output_path: ${{ github.workspace }}/images/action 94 | pre_render: "${{ github.workspace }}/.github/tests/test pcb/kicad_render_pre_render.sh" 95 | 96 | - name: check INT-TEST render pcb image with pre_render script 97 | run: test -s "${GITHUB_WORKSPACE}/images/action/prefix-via pre-render_top.png" 98 | 99 | - name: INT-TEST render pcb image 100 | uses: ./ 101 | with: 102 | pcb_file: "${{ github.workspace }}/.github/tests/test pcb/test.kicad_pcb" 103 | output_path: "${{ github.workspace }}/images/action" 104 | background: transparent 105 | zoom: 0.5 106 | animation: "gif" 107 | kicad_cli_options: " --perspective" 108 | 109 | - name: check INT-TEST render pcb image 110 | run: test -s "${GITHUB_WORKSPACE}/images/action/rotating.gif" 111 | 112 | - name: Archive test renders 113 | if: always() 114 | uses: actions/upload-artifact@v4 115 | with: 116 | name: test-renders 117 | path: ${{ github.workspace }}/images 118 | 119 | - name: push docker image dev 120 | if: github.ref_name != github.event.repository.default_branch 121 | run: | 122 | docker tag ${{ env.docker_image }} ghcr.io/linalinn/kicad-render:nightly-dev-${{ github.ref_name }} 123 | docker tag ${{ env.docker_image }} ghcr.io/linalinn/kicad-render:nightly-dev-${{ steps.date.outputs.date }} 124 | docker push ghcr.io/linalinn/kicad-render:nightly-dev-${{ github.ref_name }} 125 | docker push ghcr.io/linalinn/kicad-render:nightly-dev-${{ steps.date.outputs.date }} 126 | 127 | - name: push docker image 128 | if: github.ref_name == github.event.repository.default_branch 129 | run: | 130 | docker tag ${{ env.docker_image }} ghcr.io/linalinn/kicad-render:nightly 131 | docker tag ${{ env.docker_image }} ghcr.io/linalinn/kicad-render:nightly-${{ steps.date.outputs.date }} 132 | docker push ghcr.io/linalinn/kicad-render:nightly 133 | docker push ghcr.io/linalinn/kicad-render:nightly-${{ steps.date.outputs.date }} 134 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:24.04 as ubuntu-kicad 2 | 3 | ARG DEBIAN_FRONTEND=noninteractive 4 | 5 | ARG VERSION=no-version 6 | 7 | ARG KICAD_PPA=kicad/kicad-9.0-releases 8 | 9 | ARG KICAD_PACKAGE=kicad 10 | 11 | ENV VERSION=$VERSION 12 | 13 | RUN apt-get update -y && \ 14 | apt-get install -y software-properties-common && \ 15 | add-apt-repository -y ppa:${KICAD_PPA} && \ 16 | apt-get install $KICAD_PACKAGE ffmpeg -y && \ 17 | rm -rf /var/lib/apt/lists/* 18 | 19 | FROM ubuntu-kicad 20 | 21 | COPY *.sh /usr/bin/ 22 | 23 | RUN chmod +rx /usr/bin/render-pcb.sh && chmod +rx /usr/bin/kicad_animation.sh 24 | 25 | WORKDIR /pwd -------------------------------------------------------------------------------- /Dockerfile.kicad-nightly: -------------------------------------------------------------------------------- 1 | FROM ubuntu:24.04 as ubuntu-kicad-nightly 2 | 3 | ARG DEBIAN_FRONTEND=noninteractive 4 | 5 | ARG VERSION=no-version 6 | 7 | ENV VERSION=$VERSION 8 | 9 | RUN apt-get update -y && \ 10 | apt-get install -y software-properties-common && \ 11 | add-apt-repository -y ppa:kicad/kicad-dev-nightly && \ 12 | apt-get install kicad-nightly ffmpeg -y && \ 13 | rm -rf /var/lib/apt/lists/* 14 | 15 | FROM ubuntu-kicad-nightly 16 | 17 | COPY *.sh /usr/bin/ 18 | 19 | RUN chmod +rx /usr/bin/render-pcb.sh && chmod +rx /usr/bin/kicad_animation.sh 20 | 21 | WORKDIR /pwd -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # kicad render action 2 | [![KiCad render - for gitlab ](https://img.shields.io/badge/KiCad_render_-for_gitlab_-2ea44f?style=for-the-badge&logo=gitlab)](https://gitlab.com/linalinn/kicad-render) 3 | 4 | This action allows you to automatically render Images of your PCB and use them e.g. in a README.md 5 | 6 | ## Usage 7 | 1. Create the directory `.github/workflows` if it does not already exist. 8 | 9 | 2. Add a new yaml in that directory e.g. pcb_image.yaml 10 | 11 | 3. Adding the configuration. Set the path to your .kicad_pcb file (you may need to replace `'refs/heads/main'` with `'refs/heads/master'` on older repos) 12 | ```yaml 13 | name: pcb_image 14 | on: 15 | push: 16 | jobs: 17 | render-image: 18 | name: render-image 19 | runs-on: ubuntu-latest 20 | steps: 21 | - name: Check out the repo 22 | uses: actions/checkout@v4 23 | 24 | - name: render pcb image 25 | uses: linalinn/kicad-render@main 26 | with: 27 | pcb_file: 28 | output_path: ${{ github.workspace }}/images 29 | background: transparent # Remove for no transparency 30 | 31 | - name: Setup Pages 32 | if: github.ref == 'refs/heads/main' 33 | uses: actions/configure-pages@v5 34 | 35 | - name: Upload Artifact 36 | if: github.ref == 'refs/heads/main' 37 | uses: actions/upload-pages-artifact@v3 38 | with: 39 | path: "images" 40 | 41 | deploy-pages: 42 | if: github.ref == 'refs/heads/main' 43 | runs-on: ubuntu-latest 44 | needs: render-image 45 | 46 | permissions: 47 | pages: write 48 | id-token: write 49 | 50 | environment: 51 | name: github-pages 52 | url: ${{ steps.deployment.outputs.page_url }} 53 | 54 | steps: 55 | - name: Deploy to GitHub Pages 56 | id: deployment 57 | uses: actions/deploy-pages@v4 58 | ``` 59 | 60 | 4. Adding the images to an README.md 61 | ```Markdown 62 | # My first PCB with automatic image generation 63 | 64 | ### Images 65 | ![top](.github.io//top.png) 66 | ![bottom](.github.io//bottom.png) 67 | rendered with [kicad-render](https://github.com/linalinn/kicad-render) 68 | ``` 69 | 70 | 6. Prepare the repo 71 | - Open your repo on GitHub 72 | - Open `Setting` 73 | - (Left side) Click on `Pages` 74 | - Under `Build and deployment` select for `Source` `Github Action` from the dropdown. 75 | 76 | 5. git commit and push 77 | 78 | ### Rendering Animations 79 | This Action can also render an Animation of you pcb rotating as gif or mp4 is can be enabled by adding `animation: gif` or `animation: mp4` 80 | 81 | ```yaml 82 | - name: render pcb image 83 | uses: linalinn/kicad-render@main 84 | with: 85 | pcb_file: 86 | output_path: ${{ github.workspace }}/images 87 | animation: gif 88 | ``` 89 | 90 | To display the animation in a `README.md` add the following to you README.md. 91 | **Note:** GitHubs CDN has a file size limit this is why the animation can't be bigger then **300x300px** 92 | 93 | ```Markdown 94 | ![animation](.github.io//rotating.gif) 95 | ``` 96 | 97 | ### Example 98 | You can find a example [here in the m2sdr](https://github.com/HackModsOrg/m2sdr) and the workflow for it [here](https://github.com/HackModsOrg/m2sdr/blob/master/.github/workflows/images.yaml) 99 | 100 | ## pre render hook / installing fonts 101 | By setting `pre_render` to a path with a sourceable bash script you can run commands the are getting executed before the Images are rendered. You also can set optional inputs for the action as variables in this file by prefixing the option with `INPUT_` and the option in capslock e.g. `background` becomes `INPUT_BACKGROUND`. 102 | 103 | ### Example for ms font 104 | ```bash 105 | apt-get update --yes 106 | echo ttf-mscorefonts-installer msttcorefonts/accepted-mscorefonts-eula select true | debconf-set-selections 107 | apt-get install --yes ttf-mscorefonts-installer 108 | ``` 109 | 110 | ### Example for configureing the Action via pre render hook 111 | 112 | ```bash 113 | KICAD_CLI_OPTIONAL_ARGS=" --quality high" 114 | KICAD_CLI_OPTIONAL_ARGS="$KICAD_CLI_OPTIONAL_ARGS -w 2000" 115 | KICAD_CLI_OPTIONAL_ARGS="$KICAD_CLI_OPTIONAL_ARGS -h 2500" 116 | KICAD_CLI_OPTIONAL_ARGS="$KICAD_CLI_OPTIONAL_ARGS --zoom 2" 117 | 118 | export KICAD_CLI_OPTIONAL_ARGS="$KICAD_CLI_OPTIONAL_ARGS" 119 | export INPUT_PREFIX="prefix-via pre-render" 120 | ``` 121 | 122 | 123 | ## Animation original code 124 | The [code](https://gist.github.com/arturo182/57ab066e6a4a36ee22979063e4d5cce1) for the Animation is from [arturo182](https://github.com/arturo182) 125 | [Mastdon post with Gif and link to gist](https://mastodon.social/@arturo182/112062074668232493) 126 | 127 | 128 | ## Development 129 | In this repo, you find an `.devcontainer` folder. This is for making Development and testing easier by not having to install kicad-nightly on your system. Dev containers are supported by Visual Studio Code, JetBrains, and Github. Alternatively, you can run the following docker command in the repository root `docker run -v "$(pwd)":/pwd --workdir=/pwd --rm -it ghcr.io/linalinn/kicad:9.0 bash` and run kicad nightly from inside this container. 130 | -------------------------------------------------------------------------------- /action.yaml: -------------------------------------------------------------------------------- 1 | # action.yml 2 | name: 'kicad render pcb' 3 | description: 'Render KiCad PCBs top, bottom optionally render and gif or mp4 of the PCB rotating' 4 | inputs: 5 | pcb_file: # id of input 6 | description: 'Path to your .kicad_pcb' 7 | required: true 8 | output_path: 9 | description: 'Path to where top.png and bottom.png should be created' 10 | required: true 11 | zoom: 12 | description: 'Camera zoom, default 1' 13 | required: false 14 | background: 15 | description: 'Image background. Options: transparent, opaque. Default: opaque for JPEG, transparent for PNG' 16 | required: false 17 | default: 'opaque' 18 | animation: 19 | description: 'If an animation of the PCB rotating should be rendered (mp4 or gif) (background dose not apply)' 20 | required: false 21 | prefix: 22 | description: 'Set a prefix for the images diffrent from project name' 23 | required: false 24 | pre_render: 25 | description: 'Path to shell file which will be sourced before rendering. This can be used to e.g. install missing fonts or set configuration via enviromet variables.' 26 | required: false 27 | kicad_cli_options: 28 | description: 'Add varios cli options that are not exposed by the action' 29 | required: false 30 | runs: 31 | using: 'docker' 32 | image: docker://ghcr.io/linalinn/kicad-render:9 33 | args: 34 | - bash 35 | - -c 36 | - if [ -n "$INPUT_PRE_RENDER" ]; 37 | then source "${INPUT_PRE_RENDER}";fi; 38 | if [ -n "$INPUT_ANIMATION" ]; then 39 | KICAD_CLI_OPTIONAL_ARGS="$INPUT_KICAD_CLI_OPTIONS" filename_prefix="$INPUT_PREFIX" render-pcb.sh 40 | -f "$INPUT_PCB_FILE" 41 | -o "$INPUT_OUTPUT_PATH" 42 | -b "$INPUT_BACKGROUND" 43 | -z "$INPUT_ZOOM" 44 | -a "$INPUT_ANIMATION"; else 45 | KICAD_CLI_OPTIONAL_ARGS="$INPUT_KICAD_CLI_OPTIONS" filename_prefix="$INPUT_PREFIX" render-pcb.sh -f "$INPUT_PCB_FILE" 46 | -o "$INPUT_OUTPUT_PATH" 47 | -b "$INPUT_BACKGROUND" 48 | -z "$INPUT_ZOOM"; fi 49 | 50 | -------------------------------------------------------------------------------- /kicad_animation.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # 4 | # Original by: arturo182 5 | # gist: https://gist.github.com/arturo182/57ab066e6a4a36ee22979063e4d5cce1 6 | # 7 | 8 | extract_project_name() { 9 | echo "$1" | rev | cut -d '/' -f 1 | rev | sed -e "s/.kicad_pcb//g" 10 | } 11 | 12 | extract_output_path() { 13 | echo "$1" | sed -e "s/[^\/]*\.kicad_pcb//g" 14 | } 15 | 16 | OUTPUT_FILE="" 17 | 18 | if [[ -z "$3" ]]; then 19 | path=$(extract_output_path "$2") 20 | name=$(extract_project_name "$2") 21 | OUTPUT_FILE="${path}${name}" 22 | echo "OUTPUT_FILE: $OUTPUT_FILE" 23 | else 24 | OUTPUT_FILE="$3/rotating" 25 | fi 26 | 27 | FORMAT="$1" 28 | OUTPUT_DIR="${3:-/pwd}" 29 | FRAME_DIR="/tmp/render" 30 | INPUT_FILE="$2" 31 | ZOOM=0.7 32 | WIDTH=300 33 | HEIGHT=300 34 | ROTATE_X=0 35 | ROTATE_Z=45 36 | ROTATION=360 # Total rotation angle 37 | STEP=3 # Rotation step in degrees 38 | FRAMERATE=30 # Framerate for the final video 39 | 40 | KICAD_CLI=$(which kicad-cli || which kicad-cli-nightly) 41 | 42 | mkdir -p $OUTPUT_DIR 43 | mkdir -p $FRAME_DIR 44 | 45 | let FRAMES=ROTATION/STEP 46 | for ((i = 0; i < FRAMES; i++)); do 47 | ROTATE_Y=-$(($i * STEP)) 48 | OUTPUT_PATH="$FRAME_DIR/frame$i.png" 49 | echo "Rendering frame $i ($ROTATE_Y degrees) to $OUTPUT_PATH" 50 | $KICAD_CLI pcb render --rotate "$ROTATE_X,$ROTATE_Y,$ROTATE_Z" --zoom $ZOOM -w $WIDTH -h $HEIGHT --background opaque -o $OUTPUT_PATH "$INPUT_FILE" > /dev/null 51 | done 52 | 53 | # Combine frames into an MP4 with the specified framerate 54 | if [[ $FORMAT == "mp4" ]]; then 55 | echo "Combining frames into an MP4..." 56 | ffmpeg -y -framerate $FRAMERATE -i "$FRAME_DIR/frame%d.png" -c:v libx264 -r 30 -pix_fmt yuv420p "$OUTPUT_FILE.mp4" 57 | echo "MP4 created successfully." 58 | elif [[ $FORMAT == "gif" ]]; then 59 | echo "Combining frames into an GIF..." 60 | ffmpeg -y -framerate $FRAMERATE -i "$FRAME_DIR/frame%d.png" "$OUTPUT_FILE.gif" 61 | echo "GIF created successfully." 62 | else 63 | # TODO: this should be vaildated before rendering anything 64 | echo first argument must be mp4 or gif 65 | fi -------------------------------------------------------------------------------- /render-pcb.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | help() { 6 | echo "Convert .kicad_pcb file to front and back image and optionaly render an animation" 7 | echo 8 | echo "Syntax: render-pcb.sh [-f|o|a|h]" 9 | echo "options:" 10 | echo "f Path to .kicad_pcb file" 11 | echo "b Image background. Options: transparent, opaque. Default: opaque for JPEG, transparent for PNG" 12 | echo "o Directory where the images and optinally the animation should be written to." 13 | echo "p Set a prefix for the images diffrent from project name" 14 | echo "a Render animation and select animation output format (mp4 or gif)." 15 | echo "z Camera zoom default 1" 16 | echo "v Print protgram version" 17 | echo "h Print this Help." 18 | echo 19 | exit 20 | } 21 | 22 | extract_project_name() { 23 | echo "$1" | rev | cut -d '/' -f 1 | rev | sed -e "s/.kicad_pcb//g" 24 | } 25 | 26 | extract_output_path() { 27 | echo "$1" | sed -e 's/[^\/]*\.kicad_pcb//g' 28 | } 29 | 30 | background="opaque" 31 | 32 | while getopts :f:o:p:a:b:z:hv option 33 | do 34 | case "${option}" in 35 | f) kicad_pcb=${OPTARG};; 36 | o) output_path=${OPTARG};; 37 | p) filename_prefix=${OPTARG};; 38 | a) animation=${OPTARG};; 39 | b) background=${OPTARG};; 40 | z) zoom=${OPTARG};; 41 | h) help;; 42 | v) echo "IMAGE version: ${VERSION:-none}" && exit;; 43 | \?) 44 | echo "Error: -${OPTARG} is invalid option" 45 | exit; 46 | esac 47 | done 48 | 49 | if [[ -z "$kicad_pcb" ]]; then 50 | help 51 | fi 52 | 53 | if [[ -z "$output_path" ]]; then 54 | path=$(extract_output_path "$2") 55 | extracted_project_name=$(extract_project_name "$2") 56 | name="${filename_prefix:-$extracted_project_name}" 57 | echo "name: $name" 58 | echo "path: $path" 59 | output_path="$path" 60 | output_top="${path}${name}_top.png" 61 | output_bottom="${path}${name}_bottom.png" 62 | echo "output_top: $output_top" 63 | echo "output_bottom: $output_bottom" 64 | else 65 | if [[ -n "$filename_prefix" ]]; then 66 | output_top="$output_path/${filename_prefix}_top.png" 67 | output_bottom="$output_path/${filename_prefix}_bottom.png" 68 | output_animation="$output_path" 69 | else 70 | output_top="$output_path/top.png" 71 | output_bottom="$output_path/bottom.png" 72 | output_animation="$output_path" 73 | fi 74 | fi 75 | 76 | 77 | KICAD_CLI=$(which kicad-cli || which kicad-cli-nightly) 78 | 79 | echo "$output_path" 80 | 81 | mkdir -p "$output_path" 82 | 83 | 84 | if [[ -n "$zoom" ]]; then 85 | KICAD_CLI_OPTIONAL_ARGS="$KICAD_CLI_OPTIONAL_ARGS --zoom $zoom" 86 | fi 87 | 88 | echo "rendering top" 89 | $KICAD_CLI pcb render --side top --background $background -o "$output_top" $KICAD_CLI_OPTIONAL_ARGS "$kicad_pcb" 90 | echo "rendering bottom" 91 | $KICAD_CLI pcb render --side bottom --background $background -o "$output_bottom" $KICAD_CLI_OPTIONAL_ARGS "$kicad_pcb" 92 | 93 | if [[ -n "$animation" ]]; then 94 | echo "rendering animation" 95 | kicad_animation.sh $animation "$kicad_pcb" "$output_animation" 96 | fi 97 | 98 | ls "$output_path" 99 | --------------------------------------------------------------------------------