This blog is about scripting in kicad/pcbnew. This post tries to showcase some of the things that you can do with python scripts in pcbnew.
The layout of this board was designed in fusion 360 and brought into kicad via scripting. Very little manual work was required in pcbnew; it amounted mostly to deleting stuff and changing some zone names. The scripts also did the routing and placed the many vias
The code I demonstrate can be found on my git hub here and here.
This board implements a ring light for photography (I use multiple boards to stay within the 10×10 limit of cheap fabricators:
I’ve created a video show all of the steps in kicad:
I won’t explain the code in this post, I’ll do that in future posts. I will, however, take a moment to recommend some useful python libraries:
The SciPy library is fantastic for graph algorithms like minimum spanning tree and delanay triangulation
The shapely library is very nice for polygon manipulation. Merging polygons, bloating and shrinking them…
The dxf_grabber library is how I parse dxf files. Together with shapely, you can do a lot of import stuff. 1
Hope it helps
sadly, I had already implemented a bunch of code merging lines and arcs into polygons.↩
I’ve written about zones and boundaries before. Since then, the data structs behind zones have changed a bit. Also, I didn’t cover zones with holes. The old post is still relevant and there’s a bunch I won’t repeat here.
In this post, I talk about zones with holes and with multiple boundaries. Multiple boundaries makes it easier to move the zones together as a unit.
But there are some details hidden there. Append actually takes a bunch more arguments. From the c++ code:
* Function Append
* adds a new vertex to the contour indexed by \p aOutline and \p aHole (defaults to the
* outline of the last polygon).
* @param x is the x coordinate of the new vertex.
* @param y is the y coordinate of the new vertex.
* @param aOutline is the index of the polygon.
* @param aHole is the index of the hole (-1 for the main outline),
* @param aAllowDuplication is a flag to indicate whether it is allowed to add this
* corner even if it is duplicated.
* @return int - the number of corners of the selected contour after the addition.
int Append( int x, int y, int aOutline = -1, int aHole = -1,
bool aAllowDuplication = false );
aOutline tells which boundary you want to add to. By default, you add to the last one -1. If aHole is -1, you’re telling it you want the bound, not a hole. You see, the boundaries/outlines are numbered 0+. Same with the holes.
so, with the single outline zone we’re messing with now, the call above is equivalent to these:
So, I wanted to convert an inkscape drawing into a zone. I have a post on my other blog talking about how to parse SVG paths. The code for the parse I wrote is here. I also have a blog post one understanding and parsing svg. The main function returns instances of a class I called SVGpath, which will give you a list of polys and holes. I have a helper function to group the bounds with their holes. The result is this code for creating my zones:
# here I load from drawing.svg in the current directory. You'll want to change that path.
paths = parse_svg_path.parse_svg_path(os.path.dirname(inspect.stack()) + '/drawing.svg')
if not paths:
raise ValueError('wasnt able to read any paths from file')
# things are a little tricky below, because the first boundary has
# its first point passed into the creation of the new area. subsequent
# bounds are not done that way.
zone_container = None
shape_poly_set = None
for path in paths:
for shape in path.group_by_bound_and_holes():
shapeid = None
if not shape_poly_set:
# the call to GetNet() gets the netcode, an integer.
zone_container = board.InsertArea(powernet.GetNet(), 0, powerlayer,
shape_poly_set = zone_container.Outline()
shapeid = 0
shapeid = shape_poly_set.NewOutline()
for pt in shape.bound[1:]:
for hole in shape.holes:
hi = shape_poly_set.NewHole()
# -1 to the third arg maintains the default behavior of
# using the last outline.
for pt in hole:
shape_poly_set.Append(int(pt*SCALE), int(pt*SCALE), -1, hi)