Commit c1e0a8d0 authored by unknown's avatar unknown Committed by jean-pierre charras

Patch for the Python footprint wizard helpers. This adds a few more drawing...

Patch for the Python footprint wizard helpers. This adds a few more drawing functions, such as for circles, and also uses a matrix-based transform stack with greatly simplifies constructing footprints consisting of regularly spaced elements (e.g. in lines, grids, circles, or some list of points that you specify).
This fixes bug #1366299
parent 1edd8c8a
...@@ -30,7 +30,7 @@ class FPCFootprintWizard(FootprintWizardPlugin): ...@@ -30,7 +30,7 @@ class FPCFootprintWizard(FootprintWizardPlugin):
pad.SetSize(size) pad.SetSize(size)
pad.SetShape(PAD_RECT) pad.SetShape(PAD_RECT)
pad.SetAttribute(PAD_SMD) pad.SetAttribute(PAD_SMD)
pad.SetLayerSet( pad.StandardMask() ) pad.SetLayerSet( pad.SMDMask() )
pad.SetPos0(pos) pad.SetPos0(pos)
pad.SetPosition(pos) pad.SetPosition(pos)
pad.SetPadName(name) pad.SetPadName(name)
......
...@@ -14,7 +14,11 @@ ...@@ -14,7 +14,11 @@
# MA 02110-1301, USA. # MA 02110-1301, USA.
# #
from __future__ import division
import pcbnew import pcbnew
import math
class FootprintWizardDrawingAids: class FootprintWizardDrawingAids:
""" """
...@@ -24,31 +28,291 @@ class FootprintWizardDrawingAids: ...@@ -24,31 +28,291 @@ class FootprintWizardDrawingAids:
A "drawing context" is provided which can be used to set and retain A "drawing context" is provided which can be used to set and retain
settings such as line width and layer settings such as line width and layer
""" """
# directions (in degrees, compass-like)
dirN = 0
dirNE = 45
dirE = 90
dirSE = 135
dirS = 180
dirSW = 225
dirW = 270
dirNW = 315
# flip constants
flipNone = 0
flipX = 1 # flip X values, i.e. about Y
flipY = 2 # flip Y valuersabout X
flipBoth = 3
xfrmIDENTITY = [1, 0, 0, 0, 1, 0] # no transform
def __init__(self, module): def __init__(self, module):
self.module = module self.module = module
#drawing context defaults # drawing context defaults
self.dc = { self.dc = {
'layer': pcbnew.SILKSCREEN_N_FRONT, 'layer': pcbnew.F_SilkS,
'width': pcbnew.FromMM(0.2) 'width': pcbnew.FromMM(0.2),
'transforms': [],
'transform': self.xfrmIDENTITY
} }
def PushTransform(self, mat):
"""
Add a transform to the top of the stack and recompute the
overall transform
"""
self.dc['transforms'].append(mat)
self.RecomputeTransforms()
def PopTransform(self, num=1):
"""
Remove a transform from the top of the stack and recompute the
overall transform
"""
for i in range(num):
mat = self.dc['transforms'].pop()
self.RecomputeTransforms()
return mat
def ResetTransform(self):
"""
Reset the transform stack to the identity matrix
"""
self.dc['transforms'] = []
self.RecomputeTransforms()
def _ComposeMatricesWithIdentity(self, mats):
"""
Compose a sequence of matrices together by sequential
pre-mutiplciation with the identity matrix
"""
x = self.xfrmIDENTITY
for mat in mats:
#precompose with each transform in turn
x = [
x[0] * mat[0] + x[1] * mat[3],
x[0] * mat[1] + x[1] * mat[4],
x[0] * mat[2] + x[1] * mat[5] + x[2],
x[3] * mat[0] + x[4] * mat[3],
x[3] * mat[1] + x[4] * mat[4],
x[3] * mat[2] + x[4] * mat[5] + x[5]]
return x
def RecomputeTransforms(self):
"""
Re-compute the transform stack into a single transform and
store in the DC
"""
self.dc['transform'] = self._ComposeMatricesWithIdentity(
self.dc['transforms'])
def TransformTranslate(self, x, y, push=True):
"""
Set up and return a transform matrix representing a translartion
optionally pushing onto the stack
( 1 0 x )
( 0 1 y )
"""
mat = [1, 0, x, 0, 1, y]
if push:
self.PushTransform(mat)
return mat
def TransformFlipOrigin(self, flip, push=True):
"""
Set up and return a transform matrix representing a horizontal,
vertical or both flip about the origin
"""
mat = None
if flip == self.flipX:
mat = [-1, 0, 0, 0, 1, 0]
elif flip == self.flipY:
mat = [1, 0, 0, 0, -1, 0]
elif flip == self.flipBoth:
mat = [-1, 0, 0, 0, -1, 0]
elif flip == self.flipNone:
mat = self.xfrmIDENTITY
else:
raise ValueError
if push:
self.PushTransform(mat)
return mat
def TransformFlip(self, x, y, flip=flipNone, push=True):
"""
Set up and return a transform matrix representing a horizontal,
vertical or both flip about a point (x,y)
This is performed by a translate-to-origin, flip, translate-
back sequence
"""
mats = [self.TransformTranslate(x, y, push=False),
self.TransformFlipOrigin(flip, push=False),
self.TransformTranslate(-x, -y, push=False)]
#distill into a single matrix
mat = self._ComposeMatricesWithIdentity(mats)
if push:
self.PushTransform(mat)
return mat
def TransformRotationOrigin(self, rot, push=True):
"""
Set up and return a transform matrix representing a rotation
about the origin, and optionally push onto the stack
( cos(t) -sin(t) 0 )
( sin(t) cos(t) 0 )
"""
rads = rot * math.pi / 180
mat = [math.cos(rads), -math.sin(rads), 0,
math.sin(rads), math.cos(rads), 0]
if push:
self.PushTransform(mat)
return mat
def TransformRotation(self, x, y, rot, push=True):
"""
Set up and return a transform matrix representing a rotation
about the pooint (x,y), and optionally push onto the stack
This is performed by a translate-to-origin, rotate, translate-
back sequence
"""
mats = [self.TransformTranslate(x, y, push=False),
self.TransformRotationOrigin(rot, push=False),
self.TransformTranslate(-x, -y, push=False)]
#distill into a single matrix
mat = self._ComposeMatricesWithIdentity(mats)
if push:
self.PushTransform(mat)
return mat
def TransformScaleOrigin(self, sx, sy=None, push=True):
"""
Set up and return a transform matrix representing a scale about
the origin, and optionally push onto the stack
( sx 0 0 )
( 0 sy 0 )
"""
if sy is None:
sy = sx
mat = [sx, 0, 0, 0, sy, 0]
if push:
self.PushTransform(mat)
return mat
def TransformPoint(self, x, y, mat=None):
"""
Return a point (x, y) transformed by the given matrix, or if
that is not given, the drawing context transform
"""
if not mat:
mat = self.dc['transform']
return pcbnew.wxPoint(x * mat[0] + y * mat[1] + mat[2],
x * mat[3] + y * mat[4] + mat[5])
def SetWidth(self, width): def SetWidth(self, width):
"""
Set the current pen width used for subsequent drawing
operations
"""
self.dc['width'] = width self.dc['width'] = width
def GetWidth(self):
"""
Get the current drawing context width
"""
return self.dc['width']
def SetLayer(self, layer): def SetLayer(self, layer):
"""
Set the current drawing layer, used for subsequent drawing
operations
"""
self.dc['layer'] = layer self.dc['layer'] = layer
def Line(self, x1, y1, x2, y2): def Line(self, x1, y1, x2, y2):
"""
Draw a line from (x1, y1) to (x2, y2)
"""
outline = pcbnew.EDGE_MODULE(self.module) outline = pcbnew.EDGE_MODULE(self.module)
outline.SetWidth(self.dc['width']) outline.SetWidth(self.dc['width'])
outline.SetLayer(self.dc['layer']) outline.SetLayer(self.dc['layer'])
outline.SetShape(pcbnew.S_SEGMENT) outline.SetShape(pcbnew.S_SEGMENT)
start = pcbnew.wxPoint(x1, y1) start = self.TransformPoint(x1, y1)
end = pcbnew.wxPoint(x2, y2) end = self.TransformPoint(x2, y2)
outline.SetStartEnd(start, end) outline.SetStartEnd(start, end)
self.module.Add(outline) self.module.Add(outline)
def Circle(self, x, y, r, filled=False):
"""
Draw a circle at (x,y) of radius r
If filled is true, the width and radius of the line will be set
such that the circle appears filled
"""
circle = pcbnew.EDGE_MODULE(self.module)
start = self.TransformPoint(x, y)
if filled:
circle.SetWidth(r)
end = self.TransformPoint(x, y + r/2)
else:
circle.SetWidth(self.dc['width'])
end = self.TransformPoint(x, y + r)
circle.SetLayer(self.dc['layer'])
circle.SetShape(pcbnew.S_CIRCLE)
circle.SetStartEnd(start, end)
self.module.Add(circle)
def Arc(self, cx, cy, sx, sy, a):
"""
Draw an arc based on centre, start and angle
The transform matrix is applied
Note that this won't work properly if the result is not a
circular arc (eg a horzontal scale)
"""
circle = pcbnew.EDGE_MODULE(self.module)
circle.SetWidth(self.dc['width'])
center = self.TransformPoint(cx, cy)
start = self.TransformPoint(sx, sy)
circle.SetLayer(self.dc['layer'])
circle.SetShape(pcbnew.S_ARC)
# check if the angle needs to be reverse (a flip scaling)
if cmp(self.dc['transform'][0], 0) != cmp(self.dc['transform'][4], 0):
a = -a
circle.SetAngle(a)
circle.SetStartEnd(center, start)
self.module.Add(circle)
# extends from (x1,y1) right # extends from (x1,y1) right
def HLine(self, x, y, l): def HLine(self, x, y, l):
""" """
...@@ -62,13 +326,34 @@ class FootprintWizardDrawingAids: ...@@ -62,13 +326,34 @@ class FootprintWizardDrawingAids:
""" """
self.Line(x, y, x, y + l) self.Line(x, y, x, y + l)
def Polyline(self, pts): def Polyline(self, pts, mirrorX=None, mirrorY=None):
"""
Draw a polyline, optinally mirroring around the given points
"""
def _PolyLineInternal(pts):
if len(pts) < 2:
return
if len(pts) < 2: for i in range(0, len(pts) - 1):
return self.Line(pts[i][0], pts[i][1], pts[i+1][0], pts[i+1][1])
for i in range(0, len(pts) - 1): _PolyLineInternal(pts) # original
self.Line(pts[i][0], pts[i][1], pts[i+1][0], pts[i+1][1])
if mirrorX is not None:
self.TransformFlip(mirrorX, 0, self.flipX)
_PolyLineInternal(pts)
self.PopTransform()
if mirrorY is not None:
self.TransformFlipOrigin(0, mirrorY, self.flipY)
_PolyLineInternal(pts)
self.PopTransform()
if mirrorX is not None and mirrorY is not None:
self.TransformFlip(mirrorX, mirrorY, self.flipBoth) # both
_PolyLineInternal(pts)
self.PopTransform()
def Reference(self, x, y, size): def Reference(self, x, y, size):
""" """
...@@ -80,8 +365,9 @@ class FootprintWizardDrawingAids: ...@@ -80,8 +365,9 @@ class FootprintWizardDrawingAids:
text_size = pcbnew.wxSize(size, size) text_size = pcbnew.wxSize(size, size)
self.module.Reference().SetPos0(pcbnew.wxPoint(x, y)) self.module.Reference().SetPos0(self.TransformPoint(x, y))
self.module.Reference().SetTextPosition(self.module.Reference().GetPos0()) self.module.Reference().SetTextPosition(
self.module.Reference().GetPos0())
self.module.Reference().SetSize(text_size) self.module.Reference().SetSize(text_size)
def Value(self, x, y, size): def Value(self, x, y, size):
...@@ -90,7 +376,7 @@ class FootprintWizardDrawingAids: ...@@ -90,7 +376,7 @@ class FootprintWizardDrawingAids:
""" """
text_size = pcbnew.wxSize(size, size) text_size = pcbnew.wxSize(size, size)
self.module.Value().SetPos0(pcbnew.wxPoint(x, y)) self.module.Value().SetPos0(self.TransformPoint(x, y))
self.module.Value().SetTextPosition(self.module.Value().GetPos0()) self.module.Value().SetTextPosition(self.module.Value().GetPos0())
self.module.Value().SetSize(text_size) self.module.Value().SetSize(text_size)
...@@ -99,36 +385,115 @@ class FootprintWizardDrawingAids: ...@@ -99,36 +385,115 @@ class FootprintWizardDrawingAids:
Draw a rectangular box, centred at (x,y), with given width and Draw a rectangular box, centred at (x,y), with given width and
height height
""" """
self.VLine(x - w/2, y - h/2, h) # left
self.VLine(x + w/2, y - h/2, h) # right pts = [[x - w/2, y - h/2], # left
self.HLine(x - w/2, y + h/2, w) # bottom [x + w/2, y - h/2], # right
self.HLine(x - w/2, y - h/2, w) # top [x + w/2, y + h/2], # bottom
[x - w/2, y + h/2], # top
[x - w/2, y - h/2]] # close
self.Polyline(pts)
def NotchedCircle(self, x, y, r, notch_w, notch_h):
"""
Circle radus r centred at (x, y) with a raised or depressed notch
at the top
Notch height is measured from the top of the circle radius
"""
# find the angle where the notch vertical meets the circle
angle_intercept = math.asin(notch_w/(2 * r))
# and find the co-ords of this point
sx = math.sin(angle_intercept) * r
sy = -math.cos(angle_intercept) * r
# NOTE: this may be out by a factor of ten one day
arc_angle = (math.pi * 2 - angle_intercept * 2) * (1800/math.pi)
self.Arc(x,y, sx, sy, arc_angle)
pts = [[sx, sy],
[sx, -r - notch_h],
[-sx, -r - notch_h],
[-sx, sy]]
self.Polyline(pts)
def NotchedBox(self, x, y, w, h, notchW, notchH): def NotchedBox(self, x, y, w, h, notchW, notchH):
""" """
Draw a box with a notch in the top edge Draw a box with a notch in the top edge
""" """
#limit to half the overall width # limit to half the overall width
notchW = min(x + w/2, notchW) notchW = min(x + w/2, notchW)
# draw notch # draw notch
self.Polyline([ #three sides of box self.Polyline([ # three sides of box
(x - w/2, y - h/2), (x - w/2, y - h/2),
(x - w/2, y + h/2), (x - w/2, y + h/2),
(x + w/2, y + h/2), (x + w/2, y + h/2),
(x + w/2, y - h/2), (x + w/2, y - h/2),
#the notch # the notch
(notchW/2, y - h/2), (notchW/2, y - h/2),
(notchW/2, y - h/2 + notchH), (notchW/2, y - h/2 + notchH),
(-notchW/2, y - h/2 + notchH), (-notchW/2, y - h/2 + notchH),
(-notchW/2, y - h/2), (-notchW/2, y - h/2),
(x - w/2, y - h/2) (x - w/2, y - h/2)
]) ])
def BoxWithDiagonalAtCorner(self, x, y, w, h, diagSetback): def BoxWithDiagonalAtCorner(self, x, y, w, h,
setback=pcbnew.FromMM(1.27), flip=flipNone):
self.Box(x, y, w, h) """
Draw a box with a diagonal at the top left corner
#diagonal corner """
self.Line(x - w/2 + diagSetback, x - h/2, x - w/2,
x - h/2 + diagSetback) self.TransformFlip(x, y, flip, push=True)
pts = [[x - w/2 + setback, y - h/2],
[x - w/2, y - h/2 + setback],
[x - w/2, y + h/2],
[x + w/2, y + h/2],
[x + w/2, y - h/2],
[x - w/2 + setback, y - h/2]]
self.Polyline(pts)
self.PopTransform()
def BoxWithOpenCorner(self, x, y, w, h,
setback=pcbnew.FromMM(1.27), flip=flipNone):
"""
Draw a box with an opening at the top left corner
"""
self.TransformTranslate(x, y)
self.TransformFlipOrigin(flip)
pts = [[- w/2, - h/2 + setback],
[- w/2, + h/2],
[+ w/2, + h/2],
[+ w/2, - h/2],
[- w/2 + setback, - h/2]]
self.Polyline(pts)
self.PopTransform(num=2)
def MarkerArrow(self, x, y, direction=dirN, width=pcbnew.FromMM(1)):
"""
Draw a marker arrow facing in the given direction, with the
point at (x,y)
Direction of 0 is north
"""
self.TransformTranslate(x, y)
self.TransformRotationOrigin(direction)
pts = [[0, 0],
[width / 2, width / 2],
[-width / 2, width / 2],
[0, 0]]
self.Polyline(pts)
self.PopTransform(2)
...@@ -15,8 +15,10 @@ ...@@ -15,8 +15,10 @@
# #
import pcbnew import pcbnew
import math
import FootprintWizardDrawingAids import FootprintWizardDrawingAids
class FootprintWizardParameterManager: class FootprintWizardParameterManager:
""" """
Functions for helpfully managing parameters to a KiCAD Footprint Functions for helpfully managing parameters to a KiCAD Footprint
...@@ -50,8 +52,9 @@ class FootprintWizardParameterManager: ...@@ -50,8 +52,9 @@ class FootprintWizardParameterManager:
uMils = 2 uMils = 2
uNatural = 3 uNatural = 3
uBool = 4 uBool = 4
uString = 5
def AddParam(self, section, param, unit, default, hint = ''): def AddParam(self, section, param, unit, default, hint=''):
""" """
Add a parameter with some properties. Add a parameter with some properties.
...@@ -66,14 +69,16 @@ class FootprintWizardParameterManager: ...@@ -66,14 +69,16 @@ class FootprintWizardParameterManager:
val = pcbnew.FromMils(default) val = pcbnew.FromMils(default)
elif unit == self.uNatural: elif unit == self.uNatural:
val = default val = default
elif unit == self.uString:
val = str(default)
elif unit == self.uBool: elif unit == self.uBool:
val = "True" if default else "False" #ugly stringing val = "True" if default else "False" # ugly stringing
else: else:
print "Warning: Unknown unit type: %s" % unit print "Warning: Unknown unit type: %s" % unit
return return
if unit in [self.uNatural, self.uBool]: if unit in [self.uNatural, self.uBool, self.uString]:
param = "*%s" % param #star prefix for natural param = "*%s" % param # star prefix for natural
if section not in self.parameters: if section not in self.parameters:
self.parameters[section] = {} self.parameters[section] = {}
...@@ -89,7 +94,8 @@ class FootprintWizardParameterManager: ...@@ -89,7 +94,8 @@ class FootprintWizardParameterManager:
for key, value in section.iteritems(): for key, value in section.iteritems():
unit = "" unit = ""
if (type(value) is int or type(value) is float) and not "*" in key: if ((type(value) is int or type(value) is float)
and not "*" in key):
unit = "mm" unit = "mm"
if "*" in key: if "*" in key:
...@@ -101,7 +107,7 @@ class FootprintWizardParameterManager: ...@@ -101,7 +107,7 @@ class FootprintWizardParameterManager:
def _ParametersHaveErrors(self): def _ParametersHaveErrors(self):
""" """
Return true if we discovered errors suring parameter processing Return true if we discovered errors during parameter processing
""" """
for name, section in self.parameter_errors.iteritems(): for name, section in self.parameter_errors.iteritems():
...@@ -124,8 +130,8 @@ class FootprintWizardParameterManager: ...@@ -124,8 +130,8 @@ class FootprintWizardParameterManager:
if not printed_section: if not printed_section:
print " %s:" % name print " %s:" % name
print " %s: %s (have %s)" % (key, value, print " %s: %s (have %s)" % (
self.parameters[name][key]) key, value, self.parameters[name][key])
def ProcessParameters(self): def ProcessParameters(self):
""" """
...@@ -134,14 +140,15 @@ class FootprintWizardParameterManager: ...@@ -134,14 +140,15 @@ class FootprintWizardParameterManager:
""" """
self.ClearErrors() self.ClearErrors()
self.CheckParameters(); self.CheckParameters()
if self._ParametersHaveErrors(): if self._ParametersHaveErrors():
print "Cannot build footprint: Parameters have errors:" print "Cannot build footprint: Parameters have errors:"
self._PrintParameterErrors() self._PrintParameterErrors()
return False return False
print "Building new %s footprint with the following parameters:" % self.name print ("Building new %s footprint with the following parameters:"
% self.name)
self._PrintParameterTable() self._PrintParameterTable()
return True return True
...@@ -150,29 +157,37 @@ class FootprintWizardParameterManager: ...@@ -150,29 +157,37 @@ class FootprintWizardParameterManager:
# PARAMETER CHECKERS # PARAMETER CHECKERS
################################################################# #################################################################
def CheckParamPositiveInt(self, section, param, min_value = 1, def CheckParamInt(self, section, param, min_value=1,
max_value = None, is_multiple_of = 1): max_value=None, is_multiple_of=1):
""" """
Make sure a parameter can be made into an int, and enforce Make sure a parameter can be made into an int, and enforce
limits if required limits if required
""" """
try: try:
self.parameters[section][param] = int(self.parameters[section][param]) self.parameters[section][param] = (
int(self.parameters[section][param]))
except ValueError: except ValueError:
self.parameter_errors[section][param] = "Must be a valid integer" self.parameter_errors[section][param] = (
"Must be a valid integer")
return return
if min_value is not None and (self.parameters[section][param] < min_value): if min_value is not None and (
self.parameter_errors[section][param] = "Must be greater than or equal to %d" % (min_value) self.parameters[section][param] < min_value):
self.parameter_errors[section][param] = (
"Must be greater than or equal to %d" % (min_value))
return return
if max_value is not None and (self.parameters[section][param] > min_value): if max_value is not None and (
self.parameter_errors[section][param] = "Must be less than or equal to %d" % (max_value) self.parameters[section][param] > min_value):
self.parameter_errors[section][param] = (
"Must be less than or equal to %d" % (max_value))
return return
if is_multiple_of > 1 and (self.parameters[section][param] % is_multiple_of) > 0: if is_multiple_of > 1 and (
self.parameter_errors[section][param] = "Must be a multiple of %d" % is_multiple_of self.parameters[section][param] % is_multiple_of) > 0:
self.parameter_errors[section][param] = (
"Must be a multiple of %d" % is_multiple_of)
return return
return return
...@@ -182,11 +197,13 @@ class FootprintWizardParameterManager: ...@@ -182,11 +197,13 @@ class FootprintWizardParameterManager:
Make sure a parameter looks like a boolean, convert to native Make sure a parameter looks like a boolean, convert to native
boolean type if so boolean type if so
""" """
if str(self.parameters[section][param]).lower() in ["true", "t", "y", "yes", "on", "1", "1.0"]: if str(self.parameters[section][param]).lower() in [
self.parameters[section][param] = True; "true", "t", "y", "yes", "on", "1", "1.0"]:
self.parameters[section][param] = True
return return
elif str(self.parameters[section][param]).lower() in ["false", "f", "n", "no", "off", "0", "0.0"]: elif str(self.parameters[section][param]).lower() in [
self.parameters[section][param] = False; "false", "f", "n", "no", "off", "0", "0.0"]:
self.parameters[section][param] = False
return return
self.parameter_errors[section][param] = "Must be boolean (true/false)" self.parameter_errors[section][param] = "Must be boolean (true/false)"
...@@ -194,7 +211,7 @@ class FootprintWizardParameterManager: ...@@ -194,7 +211,7 @@ class FootprintWizardParameterManager:
class HelpfulFootprintWizardPlugin(pcbnew.FootprintWizardPlugin, class HelpfulFootprintWizardPlugin(pcbnew.FootprintWizardPlugin,
FootprintWizardParameterManager): FootprintWizardParameterManager):
""" """
A class to simplify many aspects of footprint creation, leaving only A class to simplify many aspects of footprint creation, leaving only
the foot-print specific routines to the wizards themselves the foot-print specific routines to the wizards themselves
...@@ -216,16 +233,46 @@ class HelpfulFootprintWizardPlugin(pcbnew.FootprintWizardPlugin, ...@@ -216,16 +233,46 @@ class HelpfulFootprintWizardPlugin(pcbnew.FootprintWizardPlugin,
self.decription = self.GetDescription() self.decription = self.GetDescription()
self.image = self.GetImage() self.image = self.GetImage()
def GetReference(self): def GetValue(self):
raise NotImplementedError raise NotImplementedError
def GetValuePrefix(self): def GetReferencePrefix(self):
return "U" # footprints needing wizards of often ICs return "U" # footprints needing wizards of often ICs
def GetImage(self): def GetImage(self):
return "" return ""
def GetTextSize(self):
"""
IPC nominal
"""
return pcbnew.FromMM(1.2)
def GetTextThickness(self):
"""
Thicker than IPC guidelines (10% of text height = 0.12mm)
as 5 wires/mm is a common silk screen limitation
"""
return pcbnew.FromMM(0.2)
def SetModule3DModel(self):
"""
Set a 3D model for the module
Default is to do nothing, you need to implement this if you have
a model to set
FIXME: This doesn't seem to be enabled yet?
"""
pass
def BuildThisFootprint(self): def BuildThisFootprint(self):
"""
Draw the footprint.
This is specific to each footprint class, you need to implment
this to draw what you want
"""
raise NotImplementedError raise NotImplementedError
def BuildFootprint(self): def BuildFootprint(self):
...@@ -234,17 +281,26 @@ class HelpfulFootprintWizardPlugin(pcbnew.FootprintWizardPlugin, ...@@ -234,17 +281,26 @@ class HelpfulFootprintWizardPlugin(pcbnew.FootprintWizardPlugin,
the implmenting class the implmenting class
""" """
self.module = pcbnew.MODULE(None) # create a new module
# do it first, so if we return early, we don't segfault KiCad
if not self.ProcessParameters(): if not self.ProcessParameters():
return return
self.module = pcbnew.MODULE(None) # create a new module self.draw = FootprintWizardDrawingAids.FootprintWizardDrawingAids(
self.module)
self.module.SetValue(self.GetValue())
self.module.SetReference("%s**" % self.GetReferencePrefix())
fpid = pcbnew.FPID(self.module.GetValue()) # the name in library
self.module.SetFPID(fpid)
self.draw = FootprintWizardDrawingAids.FootprintWizardDrawingAids(self.module) self.BuildThisFootprint() # implementer's build function
self.module.SetReference(self.GetReference()) self.SetModule3DModel() # add a 3d module if specified
self.module.SetValue("%s**" % self.GetValuePrefix())
fpid = pcbnew.FPID(self.module.GetReference()) #the name in library thick = self.GetTextThickness()
self.module.SetFPID( fpid )
self.BuildThisFootprint() # implementer's build function self.module.Reference().SetThickness(thick)
self.module.Value().SetThickness(thick)
# PadArray.py
#
# Copyright 2014 john <john@johndev>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
# MA 02110-1301, USA.
#
#
from __future__ import division
import math
import pcbnew import pcbnew
class PadMaker: class PadMaker:
...@@ -9,7 +32,7 @@ class PadMaker: ...@@ -9,7 +32,7 @@ class PadMaker:
def __init__(self, module): def __init__(self, module):
self.module = module self.module = module
def THPad(self, w, l, drill, shape = pcbnew.PAD_OVAL): def THPad(self, w, l, drill, shape=pcbnew.PAD_OVAL):
pad = pcbnew.D_PAD(self.module) pad = pcbnew.D_PAD(self.module)
pad.SetSize(pcbnew.wxSize(l, w)) pad.SetSize(pcbnew.wxSize(l, w))
...@@ -22,7 +45,23 @@ class PadMaker: ...@@ -22,7 +45,23 @@ class PadMaker:
return pad return pad
def SMDPad(self, w, l, shape = pcbnew.PAD_RECT): def THRoundPad(self, size, drill):
pad = self.THPad(size, size, drill, shape=pcbnew.PAD_CIRCLE)
return pad
def NPTHRoundPad(self, drill):
pad = pcbnew.D_PAD(self.module)
pad.SetSize(pcbnew.wxSize(drill, drill))
pad.SetShape(pcbnew.PAD_CIRCLE)
pad.SetAttribute(pcbnew.PAD_HOLE_NOT_PLATED)
pad.SetLayerSet(pad.UnplatedHoleMask())
pad.SetDrillSize(pcbnew.wxSize(drill, drill))
return pad
def SMDPad(self, w, l, shape=pcbnew.PAD_RECT):
pad = pcbnew.D_PAD(self.module) pad = pcbnew.D_PAD(self.module)
pad.SetSize(pcbnew.wxSize(l, w)) pad.SetSize(pcbnew.wxSize(l, w))
...@@ -34,23 +73,65 @@ class PadMaker: ...@@ -34,23 +73,65 @@ class PadMaker:
return pad return pad
def SMTRoundPad(self, size): def SMTRoundPad(self, size):
pad = self.SMDPad(size, size, shape = pcbnew.PAD_CIRCLE) pad = self.SMDPad(size, size, shape=pcbnew.PAD_CIRCLE)
return pad return pad
class PadArray: class PadArray:
def __init__(self): def __init__(self):
self.firstPad = 1; self.firstPadNum = 1
self.pinNames = None
self.firstPad = None
def SetPinNames(self, pinNames):
"""
Set a name for all the pins
"""
self.pinNames = pinNames
def SetFirstPadType(self, firstPad):
self.firstPad = firstPad
def SetFirstPadInArray(self, fpNum): def SetFirstPadInArray(self, fpNum):
self.firstPad = fpNum self.firstPadNum = fpNum
def AddPad(self, pad): def AddPad(self, pad):
self.pad.GetParent().Add(pad) self.pad.GetParent().Add(pad)
def GetPad(self, is_first_pad, pos):
if (self.firstPad and is_first_pad):
pad = self.firstPad
else:
pad = self.pad
# create a new pad with same characteristics
pad = pad.Duplicate()
pad.SetPos0(pos)
pad.SetPosition(pos)
return pad
def GetName(self, *args, **kwargs):
if self.pinNames is None:
return self.NamingFunction(*args, **kwargs)
return self.pinNames
def NamingFunction(self, *args, **kwargs):
"""
Implement this as needed for each array type
"""
raise NotImplementedError;
class PadGridArray(PadArray): class PadGridArray(PadArray):
def __init__(self, pad, nx, ny, px, py, pin1Pos): def __init__(self, pad, nx, ny, px, py, centre=pcbnew.wxPoint(0, 0)):
PadArray.__init__(self)
# this pad is more of a "context", we will use it as a source of # this pad is more of a "context", we will use it as a source of
# pad data, but not actually add it # pad data, but not actually add it
self.pad = pad self.pad = pad
...@@ -58,78 +139,121 @@ class PadGridArray(PadArray): ...@@ -58,78 +139,121 @@ class PadGridArray(PadArray):
self.ny = int(ny) self.ny = int(ny)
self.px = px self.px = px
self.py = py self.py = py
self.pin1Pos = pin1Pos self.centre = centre
# handy utility function 1 - A, 2 - B, 26 - AA, etc # handy utility function 1 - A, 2 - B, 26 - AA, etc
# aIndex = 0 for 0 - A # aIndex = 0 for 0 - A
# alphabet = set of allowable chars if not A-Z, eg ABCDEFGHJKLMNPRTUVWY for BGA # alphabet = set of allowable chars if not A-Z,
def AlphaNameFromNumber(self, n, aIndex = 1, alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"): # eg ABCDEFGHJKLMNPRTUVWY for BGA
def AlphaNameFromNumber(self, n, aIndex=1,
alphabet="ABCDEFGHIJKLMNOPQRSTUVWXYZ"):
div, mod = divmod(n - aIndex, len(alphabet)) div, mod = divmod(n - aIndex, len(alphabet))
alpha = alphabet[mod] alpha = alphabet[mod]
if div > 0: if div > 0:
return self.AlphaNameFromNumber(div, aIndex, alphabet) + alpha; return self.AlphaNameFromNumber(div, aIndex, alphabet) + alpha
return alpha; return alpha
# right to left, top to bottom # right to left, top to bottom
def NamingFunction(self, x, y): def NamingFunction(self, x, y):
return self.firstPad + (self.nx * y + x) return self.firstPadNum + (self.nx * y + x)
#relocate the pad and add it as many times as we need #relocate the pad and add it as many times as we need
def AddPadsToModule(self): def AddPadsToModule(self, dc):
pin1posX = self.centre.x - self.px * (self.nx - 1) / 2
pin1posY = self.centre.y - self.py * (self.ny - 1) / 2
for x in range(0, self.nx): for x in range(0, self.nx):
for y in range(self.ny):
posX = self.pin1Pos.x + (self.px * x)
posY = self.pin1Pos.y + (self.py * y)
pos = pcbnew.wxPoint(posX, posY) posX = pin1posX + (x * self.px)
# create a new pad with same characteristics for y in range(self.ny):
pad = self.pad.Duplicate() posY = pin1posY + (self.py * y)
pos = dc.TransformPoint(posX, posY)
pad.SetPos0(pos) pad = self.GetPad(x == 0 and y == 0, pos)
pad.SetPosition(pos)
pad.SetPadName(str(self.NamingFunction(x,y))) pad.SetPadName(self.GetName(x,y))
self.AddPad(pad) self.AddPad(pad)
class PadLineArray(PadGridArray): class PadLineArray(PadGridArray):
def __init__(self, pad, n, pitch, isVertical, pin1Pos): def __init__(self, pad, n, pitch, isVertical,
centre=pcbnew.wxPoint(0, 0)):
if isVertical: if isVertical:
PadGridArray.__init__(self, pad, 1, n, 0, pitch, pin1Pos) PadGridArray.__init__(self, pad, 1, n, 0, pitch, centre)
else: else:
PadGridArray.__init__(self, pad, n, 1, pitch, 0, pin1Pos) PadGridArray.__init__(self, pad, n, 1, pitch, 0, centre)
class RectPadArray(PadArray): class PadCircleArray(PadArray):
def __init__(self, nx, ny, pitch, xpitch, ypitch, pin1Pos): def __init__(self, pad, n, r, angle_offset=0, centre=pcbnew.wxPoint(0, 0),
clockwise=True):
#left row PadArray.__init__(self)
pin1Pos = pcbnew.wxPoint(-h_pitch / 2, -row_len / 2) # this pad is more of a "context", we will use it as a source of
array = PadLineArray(h_pad, pads_per_row, pad_pitch, True, pin1Pos) # pad data, but not actually add it
array.SetFirstPadInArray(1) self.pad = pad
array.AddPadsToModule() self.n = int(n)
self.r = r
#bottom row self.angle_offset = angle_offset
pin1Pos = pcbnew.wxPoint(-row_len / 2, v_pitch / 2) self.centre = centre
array = PA.PadLineArray(v_pad, pads_per_row, pad_pitch, False, pin1Pos) self.clockwise = clockwise
array.SetFirstPadInArray(pads_per_row + 1)
array.AddPadsToModule() # around the circle, CW or CCW according to the flag
def NamingFunction(self, n):
#right row return str(self.firstPadNum + n)
pin1Pos = pcbnew.wxPoint(h_pitch / 2, row_len / 2)
array = PadLineArray(h_pad, pads_per_row, -pad_pitch, True, pin1Pos) #relocate the pad and add it as many times as we need
array.SetFirstPadInArray(2*pads_per_row + 1) def AddPadsToModule(self, dc):
array.AddPadsToModule()
for pin in range(0, self.n):
#top row
pin1Pos = pcbnew.wxPoint(row_len / 2, -v_pitch / 2) angle = self.angle_offset + (360 / self.n) * pin
array = PadLineArray(v_pad, pads_per_row, -pad_pitch, False, pin1Pos)
array.SetFirstPadInArray(3*pads_per_row + 1) if not self.clockwise:
array.AddPadsToModule() angle = -angle
pos_x = math.sin(angle * math.pi / 180) * self.r
pos_y = -math.cos(angle * math.pi / 180) * self.r
pos = dc.TransformPoint(pos_x, pos_y)
pad = self.GetPad(pin == 0, pos)
pad.SetPadName(self.GetName(pin))
self.AddPad(pad)
class PadCustomArray(PadArray):
"""
Layout pads according to a custom array of [x,y] data
"""
def __init__(self, pad, array):
PadArray.__init__(self)
self.pad = pad
self.array = array
def NamingFunction(self, n):
return str(self.firstPadNum + n)
#relocate the pad and add it as many times as we need
def AddPadsToModule(self, dc):
for i in range(len(self.array)):
pos = dc.TransformPoint(self.array[i][0], self.array[i][1])
pad = self.GetPad(i == 0, pos)
pad.SetPadName(self.GetName(i))
self.AddPad(pad)
...@@ -23,8 +23,10 @@ import PadArray as PA ...@@ -23,8 +23,10 @@ import PadArray as PA
class BGAPadGridArray(PA.PadGridArray): class BGAPadGridArray(PA.PadGridArray):
def NamingFunction(self, x, y): def NamingFunction(self, n_x, n_y):
return "%s%d" % (self.AlphaNameFromNumber(y + 1, alphabet="ABCDEFGHJKLMNPRTUVWY"), x + 1) return "%s%d" % (
self.AlphaNameFromNumber(n_y + 1, alphabet="ABCDEFGHJKLMNPRTUVWY"),
n_x + 1)
class BGAWizard(HFPW.HelpfulFootprintWizardPlugin): class BGAWizard(HFPW.HelpfulFootprintWizardPlugin):
...@@ -46,21 +48,19 @@ class BGAWizard(HFPW.HelpfulFootprintWizardPlugin): ...@@ -46,21 +48,19 @@ class BGAWizard(HFPW.HelpfulFootprintWizardPlugin):
def CheckParameters(self): def CheckParameters(self):
self.CheckParamPositiveInt("Pads", "*row count") self.CheckParamInt("Pads", "*row count")
self.CheckParamPositiveInt("Pads", "*column count") self.CheckParamInt("Pads", "*column count")
def GetValue(self):
def GetReference(self): pins = (self.parameters["Pads"]["*row count"]
* self.parameters["Pads"]["*column count"])
pins = self.parameters["Pads"]["*row count"] * self.parameters["Pads"]["*column count"]
return "BGA %d" % pins return "BGA %d" % pins
def GetReferencePrefix(self):
def GetValuePrefix(self):
return "U" return "U"
def BuildThisFootprint(self): def BuildThisFootprint(self):
pads = self.parameters["Pads"] pads = self.parameters["Pads"]
...@@ -76,23 +76,24 @@ class BGAWizard(HFPW.HelpfulFootprintWizardPlugin): ...@@ -76,23 +76,24 @@ class BGAWizard(HFPW.HelpfulFootprintWizardPlugin):
# add in the pads # add in the pads
pad = PA.PadMaker(self.module).SMTRoundPad(pads["pad size"]) pad = PA.PadMaker(self.module).SMTRoundPad(pads["pad size"])
pin1Pos = pcbnew.wxPoint(-((cols - 1) * pad_pitch) / 2, pin1_pos = pcbnew.wxPoint(-((cols - 1) * pad_pitch) / 2,
-((rows - 1) * pad_pitch) / 2) -((rows - 1) * pad_pitch) / 2)
array = BGAPadGridArray(pad, cols, rows, pad_pitch, pad_pitch, pin1Pos) array = BGAPadGridArray(pad, cols, rows, pad_pitch, pad_pitch)
array.AddPadsToModule() array.AddPadsToModule(self.draw)
#box #box
ssX = -pin1Pos.x + pads["outline x margin"] ssx = -pin1_pos.x + pads["outline x margin"]
ssY = -pin1Pos.y + pads["outline y margin"] ssy = -pin1_pos.y + pads["outline y margin"]
self.draw.BoxWithDiagonalAtCorner(0, 0, ssX*2, ssY*2, pads["outline x margin"]) self.draw.BoxWithDiagonalAtCorner(0, 0, ssx*2, ssy*2,
pads["outline x margin"])
#reference and value #reference and value
textSize = pcbnew.FromMM(0.8) text_size = pcbnew.FromMM(1.2) # IPC nominal
self.draw.Value(0, - ssY - textSize, textSize) self.draw.Value(0, - ssy - text_size, text_size)
self.draw.Reference(0, ssY + textSize, textSize) self.draw.Reference(0, ssy + text_size, text_size)
BGAWizard().register() BGAWizard().register()
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
# MA 02110-1301, USA.
#
from __future__ import division
import math
import pcbnew
import HelpfulFootprintWizardPlugin as HFPW
import PadArray as PA
class circular_array_wizard(HFPW.HelpfulFootprintWizardPlugin):
def GetName(self):
return "Circular Array"
def GetDescription(self):
return "Circular array of pads"
def GenerateParameterList(self):
self.AddParam("Pads", "n", self.uNatural, 6)
self.AddParam("Pads", "pad width", self.uMM, 1.5)
self.AddParam("Pads", "drill", self.uMM, 1)
self.AddParam("Pads", "circle diameter", self.uMM, 5)
self.AddParam("Pads", "first pad angle", self.uNatural, 0)
self.AddParam("Pads", "number clockwise", self.uBool, True)
self.AddParam("Pads", "first pad number", self.uNatural, 1)
def CheckParameters(self):
self.CheckParamInt("Pads", "*n")
self.CheckParamInt("Pads", "*first pad number")
self.CheckParamBool("Pads", "*number clockwise")
def GetValue(self):
return "A"
def GetReference(self):
return ""
def BuildThisFootprint(self):
prm = self.parameters['Pads']
pad_size = prm['pad width']
pad = PA.PadMaker(self.module).THPad(
prm['pad width'],
prm['pad width'],
prm['drill'])
array = PA.PadCircleArray(
pad, prm['*n'], prm['circle diameter'] / 2,
angle_offset=prm["*first pad angle"],
centre=pcbnew.wxPoint(0, 0),
clockwise=prm["*number clockwise"])
array.SetFirstPadInArray(prm["*first pad number"])
array.AddPadsToModule(self.draw)
circular_array_wizard().register()
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
# MA 02110-1301, USA.
#
from __future__ import division from __future__ import division
import pcbnew import pcbnew
import HelpfulFootprintWizardPlugin import HelpfulFootprintWizardPlugin
import PadArray as PA import PadArray as PA
class QFPWizard(HelpfulFootprintWizardPlugin.HelpfulFootprintWizardPlugin): class QFPWizard(HelpfulFootprintWizardPlugin.HelpfulFootprintWizardPlugin):
def GetName(self): def GetName(self):
...@@ -26,10 +43,10 @@ class QFPWizard(HelpfulFootprintWizardPlugin.HelpfulFootprintWizardPlugin): ...@@ -26,10 +43,10 @@ class QFPWizard(HelpfulFootprintWizardPlugin.HelpfulFootprintWizardPlugin):
def CheckParameters(self): def CheckParameters(self):
self.CheckParamPositiveInt("Pads", "*n", is_multiple_of = 4) self.CheckParamInt("Pads", "*n", is_multiple_of=4)
self.CheckParamBool("Pads", "*oval") self.CheckParamBool("Pads", "*oval")
def GetReference(self): def GetValue(self):
return "QFP %d" % self.parameters["Pads"]["*n"] return "QFP %d" % self.parameters["Pads"]["*n"]
def BuildThisFootprint(self): def BuildThisFootprint(self):
...@@ -49,44 +66,58 @@ class QFPWizard(HelpfulFootprintWizardPlugin.HelpfulFootprintWizardPlugin): ...@@ -49,44 +66,58 @@ class QFPWizard(HelpfulFootprintWizardPlugin.HelpfulFootprintWizardPlugin):
pad_shape = pcbnew.PAD_OVAL if pads["*oval"] else pcbnew.PAD_RECT pad_shape = pcbnew.PAD_OVAL if pads["*oval"] else pcbnew.PAD_RECT
h_pad = PA.PadMaker(self.module).SMDPad(pad_width, pad_length, shape = pad_shape) h_pad = PA.PadMaker(self.module).SMDPad(
v_pad = PA.PadMaker(self.module).SMDPad(pad_length, pad_width, shape = pad_shape) pad_width, pad_length, shape=pad_shape)
v_pad = PA.PadMaker(self.module).SMDPad(
pad_length, pad_width, shape=pad_shape)
#left row #left row
pin1Pos = pcbnew.wxPoint(-h_pitch / 2, -row_len / 2) pin1Pos = pcbnew.wxPoint(-h_pitch / 2, 0)
array = PA.PadLineArray(h_pad, pads_per_row, pad_pitch, True, pin1Pos) array = PA.PadLineArray(h_pad, pads_per_row, pad_pitch, True,
pin1Pos)
array.SetFirstPadInArray(1) array.SetFirstPadInArray(1)
array.AddPadsToModule() array.AddPadsToModule(self.draw)
#bottom row #bottom row
pin1Pos = pcbnew.wxPoint(-row_len / 2, v_pitch / 2) pin1Pos = pcbnew.wxPoint(0, v_pitch / 2)
array = PA.PadLineArray(v_pad, pads_per_row, pad_pitch, False, pin1Pos) array = PA.PadLineArray(v_pad, pads_per_row, pad_pitch, False,
pin1Pos)
array.SetFirstPadInArray(pads_per_row + 1) array.SetFirstPadInArray(pads_per_row + 1)
array.AddPadsToModule() array.AddPadsToModule(self.draw)
#right row #right row
pin1Pos = pcbnew.wxPoint(h_pitch / 2, row_len / 2) pin1Pos = pcbnew.wxPoint(h_pitch / 2, 0)
array = PA.PadLineArray(h_pad, pads_per_row, -pad_pitch, True, pin1Pos) array = PA.PadLineArray(h_pad, pads_per_row, -pad_pitch, True,
pin1Pos)
array.SetFirstPadInArray(2*pads_per_row + 1) array.SetFirstPadInArray(2*pads_per_row + 1)
array.AddPadsToModule() array.AddPadsToModule(self.draw)
#top row #top row
pin1Pos = pcbnew.wxPoint(row_len / 2, -v_pitch / 2) pin1Pos = pcbnew.wxPoint(0, -v_pitch / 2)
array = PA.PadLineArray(v_pad, pads_per_row, -pad_pitch, False, pin1Pos) array = PA.PadLineArray(v_pad, pads_per_row, -pad_pitch, False,
pin1Pos)
array.SetFirstPadInArray(3*pads_per_row + 1) array.SetFirstPadInArray(3*pads_per_row + 1)
array.AddPadsToModule() array.AddPadsToModule(self.draw)
limX = pads["package width"] / 2 lim_x = pads["package width"] / 2
limY = pads["package height"] / 2 lim_y = pads["package height"] / 2
inner = (row_len / 2) + pad_pitch inner = (row_len / 2) + pad_pitch
#top left - diagonal #top left - diagonal
self.draw.Line(-limX, -inner, -inner, -limY) self.draw.Line(-lim_x, -inner, -inner, -lim_y)
# top right # top right
self.draw.Polyline([(inner, -limY), (limX, -limY), (limX, -inner)]) self.draw.Polyline([(inner, -lim_y), (lim_x, -lim_y), (lim_x, -inner)])
# bottom left # bottom left
self.draw.Polyline([(-inner, limY), (-limX, limY), (-limX, inner)]) self.draw.Polyline([(-inner, lim_y), (-lim_x, lim_y), (-lim_x, inner)])
# bottom right # bottom right
self.draw.Polyline([(inner, limY), (limX, limY), (limX, inner)]) self.draw.Polyline([(inner, lim_y), (lim_x, lim_y), (lim_x, inner)])
#reference and value
text_size = pcbnew.FromMM(1.2) # IPC nominal
text_offset = v_pitch / 2 + text_size + pad_length / 2
self.draw.Value(0, -text_offset, text_size)
self.draw.Reference(0, text_offset, text_size)
QFPWizard().register() QFPWizard().register()
...@@ -24,10 +24,11 @@ import PadArray as PA ...@@ -24,10 +24,11 @@ import PadArray as PA
class RowedGridArray(PA.PadGridArray): class RowedGridArray(PA.PadGridArray):
def NamingFunction(self, x, y): def NamingFunction(self, x, y):
if (x % 2) == 0: # even row, count up if (x % 2) == 0: # even row, count up
return (x * self.ny) + y + 1; return (x * self.ny) + y + 1
else: # odd row, count down else: # odd row, count down
return (self.ny * (x + 1)) - y; return (self.ny * (x + 1)) - y
class RowedFootprint(HFPW.HelpfulFootprintWizardPlugin): class RowedFootprint(HFPW.HelpfulFootprintWizardPlugin):
...@@ -39,9 +40,13 @@ class RowedFootprint(HFPW.HelpfulFootprintWizardPlugin): ...@@ -39,9 +40,13 @@ class RowedFootprint(HFPW.HelpfulFootprintWizardPlugin):
self.AddParam("Pads", "row count", self.uNatural, 2) self.AddParam("Pads", "row count", self.uNatural, 2)
def CheckParameters(self): def CheckParameters(self):
self.CheckParamPositiveInt("Pads", "*row count") self.CheckParamInt("Pads", "*row count")
self.CheckParamPositiveInt("Pads", "*n", is_multiple_of = self.parameters["Pads"]["*row count"]) self.CheckParamInt(
self.CheckParamBool("Pads", "*silk screen inside") #can do this internally to parameter manager? "Pads", "*n",
is_multiple_of=self.parameters["Pads"]["*row count"])
# can do this internally to parameter manager?
self.CheckParamBool("Pads", "*silk screen inside")
def BuildThisFootprint(self): def BuildThisFootprint(self):
...@@ -57,39 +62,40 @@ class RowedFootprint(HFPW.HelpfulFootprintWizardPlugin): ...@@ -57,39 +62,40 @@ class RowedFootprint(HFPW.HelpfulFootprintWizardPlugin):
pads_per_row = num_pads // num_rows pads_per_row = num_pads // num_rows
row_length = pad_pitch * (pads_per_row - 1) #fenceposts row_length = pad_pitch * (pads_per_row - 1) # fenceposts
# add in the pads # add in the pads
pad = self.GetPad() pad = self.GetPad()
pin1Pos = pcbnew.wxPoint(-((num_rows - 1) * row_pitch) / 2, -row_length / 2) pin1_pos = pcbnew.wxPoint(
-((num_rows - 1) * row_pitch) / 2,
-row_length / 2)
array = RowedGridArray(pad, num_rows, pads_per_row, row_pitch, pad_pitch, pin1Pos) array = RowedGridArray(pad, num_rows, pads_per_row, row_pitch,
array.AddPadsToModule() pad_pitch)
array.AddPadsToModule(self.draw)
# draw the Silk Screen # draw the Silk Screen
pad_length = pads["pad length"] pad_length = pads["pad length"]
pad_width = pads["pad width"] pad_width = pads["pad width"]
ssXOffset = -pad_length / 2 - pads["outline x margin"] ssx_offset = -pad_length / 2 - pads["outline x margin"]
ssYOffset = -pad_width / 2 - pads["outline y margin"] ssy_offset = -pad_width / 2 - pads["outline y margin"]
if pads["*silk screen inside"]: if pads["*silk screen inside"]:
ssXOffset *= -1 ssx_offset *= -1
ssX = -pin1Pos.x - ssXOffset
ssY = -pin1Pos.y - ssYOffset
ssx = -pin1_pos.x - ssx_offset
ssy = -pin1_pos.y - ssy_offset
self.DrawBox(ssX, ssY) self.DrawBox(ssx, ssy)
#reference and value #reference and value
textSize = pcbnew.FromMM(0.8) text_size = pcbnew.FromMM(1.2) # IPC nominal
self.draw.Value(0, - ssY - textSize, textSize) self.draw.Value(0, - ssy - text_size, text_size)
self.draw.Reference(0, ssY + textSize, textSize) self.draw.Reference(0, ssy + text_size, text_size)
class SDIPWizard(RowedFootprint): class SDIPWizard(RowedFootprint):
...@@ -111,15 +117,15 @@ class SDIPWizard(RowedFootprint): ...@@ -111,15 +117,15 @@ class SDIPWizard(RowedFootprint):
self.AddParam("Pads", "outline x margin", self.uMM, 0.5) self.AddParam("Pads", "outline x margin", self.uMM, 0.5)
self.AddParam("Pads", "outline y margin", self.uMM, 1) self.AddParam("Pads", "outline y margin", self.uMM, 1)
def GetReference(self): def GetValue(self):
rows = self.parameters["Pads"]["*row count"] rows = self.parameters["Pads"]["*row count"]
if rows == 1: if rows == 1:
name = "SIP" name = "SIP"
elif rows == 2: elif rows == 2:
name = "DIP" name = "DIP"
else: # triple and up aren't really a thing, but call it something! else: # triple and up aren't really a thing, but call it something!
name = "xIP" name = "xIP"
return "%s %d" % (name, self.parameters["Pads"]["*n"]) return "%s %d" % (name, self.parameters["Pads"]["*n"])
...@@ -128,9 +134,10 @@ class SDIPWizard(RowedFootprint): ...@@ -128,9 +134,10 @@ class SDIPWizard(RowedFootprint):
pad_length = self.parameters["Pads"]["pad length"] pad_length = self.parameters["Pads"]["pad length"]
pad_width = self.parameters["Pads"]["pad width"] pad_width = self.parameters["Pads"]["pad width"]
drill = self.parameters["Pads"]["drill size"] drill = self.parameters["Pads"]["drill size"]
return PA.PadMaker(self.module).THPad(pad_width, pad_length, drill, shape = pcbnew.PAD_OVAL) return PA.PadMaker(self.module).THPad(
pad_width, pad_length, drill, shape=pcbnew.PAD_OVAL)
def DrawBox(self, ssX, ssY): def DrawBox(self, ssx, ssy):
if self.parameters["Pads"]["*row count"] == 2: if self.parameters["Pads"]["*row count"] == 2:
...@@ -144,18 +151,19 @@ class SDIPWizard(RowedFootprint): ...@@ -144,18 +151,19 @@ class SDIPWizard(RowedFootprint):
notchWidth = pcbnew.FromMM(3) notchWidth = pcbnew.FromMM(3)
notchHeight = pcbnew.FromMM(1) notchHeight = pcbnew.FromMM(1)
self.draw.NotchedBox(0, 0, ssX*2, ssY*2, notchWidth, notchHeight) self.draw.NotchedBox(0, 0, ssx*2, ssy*2, notchWidth, notchHeight)
else: else:
# ----------------- # -----------------
# |1|2 3 4 5 6 7 8| # |1|2 3 4 5 6 7 8|
# ----------------- # -----------------
self.draw.Box(ssX*2, ssY*2) self.draw.Box(0, 0, ssx*2, ssy*2)
#line between pin1 and pin2 #line between pin1 and pin2
pad_pitch = self.parameters["Pads"]["pad pitch"]; pad_pitch = self.parameters["Pads"]["pad pitch"]
self.draw.HLine(-ssX, pin1Pos.y + pad_pitch/2, ssX * 2) line_y = - (self.parameters["Pads"]["*n"] - 2) * pad_pitch / 2
self.draw.HLine(-ssx, line_y, ssx * 2)
return ssX, ssY return ssx, ssy
SDIPWizard().register() SDIPWizard().register()
...@@ -168,7 +176,7 @@ class SOICWizard(RowedFootprint): ...@@ -168,7 +176,7 @@ class SOICWizard(RowedFootprint):
def GetDescription(self): def GetDescription(self):
return "SOIC, MSOP, SSOP, TSSOP, etc, footprint wizard" return "SOIC, MSOP, SSOP, TSSOP, etc, footprint wizard"
def GetReference(self): def GetValue(self):
return "%s %d" % ("SOIC", self.parameters["Pads"]["*n"]) return "%s %d" % ("SOIC", self.parameters["Pads"]["*n"])
def GenerateParameterList(self): def GenerateParameterList(self):
...@@ -186,15 +194,16 @@ class SOICWizard(RowedFootprint): ...@@ -186,15 +194,16 @@ class SOICWizard(RowedFootprint):
def GetPad(self): def GetPad(self):
pad_length = self.parameters["Pads"]["pad length"] pad_length = self.parameters["Pads"]["pad length"]
pad_width = self.parameters["Pads"]["pad width"] pad_width = self.parameters["Pads"]["pad width"]
return PA.PadMaker(self.module).SMDPad(pad_width, pad_length, shape = pcbnew.PAD_RECT) return PA.PadMaker(self.module).SMDPad(
pad_width, pad_length, shape=pcbnew.PAD_RECT)
def DrawBox(self, ssX, ssY): def DrawBox(self, ssx, ssy):
# ---------- # ----------
# |8 7 6 5 | # |8 7 6 5 |
# |1 2 3 4 | # |1 2 3 4 |
# \--------- # \---------
self.draw.BoxWithDiagonalAtCorner(0, 0, ssX*2, ssY*2, pcbnew.FromMM(1)) self.draw.BoxWithDiagonalAtCorner(0, 0, ssx*2, ssy*2, pcbnew.FromMM(1))
SOICWizard().register() SOICWizard().register()
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment