Skip to content

Instantly share code, notes, and snippets.

@hanya
Last active August 23, 2021 02:32
Show Gist options
  • Save hanya/27931f7bb6d21957ac05 to your computer and use it in GitHub Desktop.
Save hanya/27931f7bb6d21957ac05 to your computer and use it in GitHub Desktop.
KiCAD macro - generate keepout area in circular polygon

KiCAD macro to draw keepout area in polygon of approximate circle

This macro provides way to add keepout area in polygon of approximate circle on your PCB in KiCAD pcbnew.

If you want to add circle has 3 mm radius at X, Y = 90, 100 position, call insert_keepout function as follows:

insert_keepout(90, 100, 3)

In general, you need to add keepout area around screw holes on your PCB. This can be done by insert_keepout_around_mod function. You can add keepout area at the same position of the specified modules.

Here is an exaple which adds keepout area in r=2.0 mm with 32 corners at SR1 and SR2 module position on bottom layer.

import pcbnew
insert_keepout_around_mod("SR[12]", 2.0, corners=32, layers=(pcbnew.B_Cu,))

There are some update since KiCAD 4 on area and outline code. I tried to make this code work well on recent version of KiCAD 5 dev. But this code might not work on later version of KiCAD.

def apply_modules(board, exp, func, type="reference"):
""" Call func to modules which has reference match with the regular expression.
@param board Pcbnew board
@param exp compiled regular expression or string
@param func function which takes a module as its argument
@param type reference or value
"""
import re
exp = re.compile(exp)
if type == "reference":
for module in board.GetModules():
if exp.match(module.GetReference()):
func(module)
elif type == "value":
for module in board.GetModules():
if exp.match(module.GetValue()):
func(module)
def insert_keepout_(center_x, center_y, radius, corners=16, layer=None, hatch_type=None,
no_tracks=True, no_vias=True, no_pour=True):
""" Adds keepout area in polygon of approximate circle.
Length have to be passed in internal unit.
@param center_x center X position
@param center_y center Y position
@param radius circle radius
@param corners number of vertex
@param layer layer index
@param hatch_type one of NO_HATCH, DIAGONAL_FULL, DIAGONAL_EDGE
@param no_tracks True for no tracks
@param no_vias True for no vias
@param no_pour True for no copper pour
"""
import pcbnew
Point = pcbnew.wxPoint
ver5 = hasattr(pcbnew, "SHAPE_POLY_SET")
if corners < 3 or corners > 256:
raise Exception("Wrong number of corners: %s" % corners)
if hatch_type is None:
# NO_HATCH, DIAGONAL_FULL, DIAGONAL_EDGE
hatch_type = pcbnew.CPolyLine.DIAGONAL_EDGE
if layer is None:
layer = pcbnew.F_Cu
board = pcbnew.GetBoard()
area = board.InsertArea(-1, 0xffff, layer, 0, 0, hatch_type)
area.SetIsKeepout(True)
area.SetDoNotAllowTracks(no_tracks)
area.SetDoNotAllowVias(no_vias)
area.SetDoNotAllowCopperPour(no_pour)
outline = area.Outline()
if ver5:
outline.RemoveAllContours()
#outline.RemoveVertex(0) # since CPolyLine -> SHAPE_POLY_SET
else:
outline.DeleteCorner(0) # remove first element
import math
cos, sin, floor = math.cos, math.sin, math.floor
thi = 0
dthi = 2 * math.pi / corners
# calculate corners
if ver5:
for i in range(corners):
x = int(floor(center_x + radius * cos(thi)))
y = int(floor(center_y + radius * sin(thi)))
area.AppendCorner(Point(x, y), -1)
#outline.Append(x, y) # since CPolyLine -> SHAPE_POLY_SET?
thi += dthi
else:
for i in range(corners):
x = int(floor(center_x + radius * cos(thi)))
y = int(floor(center_y + radius * sin(thi)))
outline.AppendCorner(x, y)
thi += dthi
if ver5:
if hasattr(area, "BuildFilledSolidAreasPolygons"):
# todo, do we need to call this?
# BuildFilledSolidAreasPolygons method has been gone since
# "pcbnew: made zone filling algorithm thread-safe. "
area.BuildFilledSolidAreasPolygons(board)
else:
pass
#outline.Outline(0).SetClosed(True) # no SHAPE_LINE_CHAIN implemented
else:
outline.CloseLastContour()
if hasattr(area, "Hatch"):
area.Hatch()
def insert_keepout(center_x, center_y, radius, corners=16, layer=None, hatch_type=None,
no_tracks=True, no_vias=True, no_pour=True):
""" Add keepout area in mm unit. """
import pcbnew
frommm = pcbnew.FromMM
insert_keepout_(frommm(center_x), frommm(center_y), frommm(radius), corners, layer, hatch_type)
#insert_keepout(90, 100, 3)
def insert_keepout_around_mod(exp, radius, corners=16, layers=None, hatch_type=None,
no_tracks=True, no_vias=True, no_pour=True):
""" Add keepout area around specified module.
@param exp specifies module reference in regular expression
@param radius radius in mm
@param corners number of vertex
@param layers layer or multiple layers can be specified in tuple
@param no_tracks
@param no_vias
@param no_pour
"""
import pcbnew
if not isinstance(layers, (tuple, list)):
layers = (layers,)
radius_ = pcbnew.FromMM(radius)
def func(mod):
pos = mod.GetPosition()
for layer in layers:
insert_keepout_(pos.x, pos.y, radius_, corners, layer,
hatch_type, no_tracks, no_vias, no_pour)
board = pcbnew.GetBoard()
apply_modules(board, exp, func)
#import pcbnew
#insert_keepout_around_mod("SR[5-7]", 1.8, corners=32, layers=(pcbnew.F_Cu, pcbnew.B_Cu,))
@kuldeepdhaka
Copy link

kuldeepdhaka commented Jun 26, 2016

if anyone want to increase the number look of "circle" more like circle than polygon,
change if corners < 3 or corners > 32: to if corners < 3 or corners > 1000: or what ever and
pass corners=<large-count-less-than-1000>.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment