Skip to content
This repository has been archived by the owner on Oct 7, 2020. It is now read-only.

add missing pad parameters #546

Open
wants to merge 12 commits into
base: master
Choose a base branch
from
41 changes: 38 additions & 3 deletions KicadModTree/KicadFileHandler.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,10 @@ def serialize(self, **kwargs):
if self.kicad_mod.attribute:
sexpr.append(['attr', self.kicad_mod.attribute])
sexpr.append(SexprSerializer.NEW_LINE)

if self.kicad_mod.clearance:
sexpr.append(['clearance', self.kicad_mod.clearance])
sexpr.append(SexprSerializer.NEW_LINE)

if self.kicad_mod.maskMargin:
sexpr.append(['solder_mask_margin', self.kicad_mod.maskMargin])
Expand All @@ -100,6 +104,18 @@ def serialize(self, **kwargs):
sexpr.append(['solder_paste_ratio', self.kicad_mod.pasteMarginRatio])
sexpr.append(SexprSerializer.NEW_LINE)

if not self.kicad_mod.zoneConnect is None:
sexpr.append(['zone_connect', self.kicad_mod.zoneConnect])
sexpr.append(SexprSerializer.NEW_LINE)

if self.kicad_mod.thermalWidth:
sexpr.append(['thermal_width', self.kicad_mod.thermalWidth])
sexpr.append(SexprSerializer.NEW_LINE)

if self.kicad_mod.thermalGap:
sexpr.append(['thermal_gap', self.kicad_mod.thermalGap])
sexpr.append(SexprSerializer.NEW_LINE)

sexpr.extend(self._serializeTree())

return str(SexprSerializer(sexpr))
Expand Down Expand Up @@ -244,8 +260,14 @@ def _serialize_Text(self, node):
['size', node.size.x, node.size.y],
['thickness', node.thickness]]]

jloc = (node.justify != 'center')
if node.mirror:
effects.append(['justify', 'mirror'])
if jloc:
effects.append(['justify', node.justify, 'mirror'])
else:
effects.append(['justify', 'mirror'])
elif jloc:
effects.append(['justify', node.justify])

sexpr.append(effects)
sexpr.append(SexprSerializer.NEW_LINE)
Expand All @@ -255,7 +277,7 @@ def _serialize_Text(self, node):
def _serialize_Model(self, node):
sexpr = ['model', node.filename,
SexprSerializer.NEW_LINE,
['at', ['xyz', node.at.x, node.at.y, node.at.z]],
['at', ['xyz', node.at.x, node.at.y, node.at.z]] if node.offset == [0,0,0] else ['offset', ['xyz', node.offset.x, node.offset.y, node.offset.z]],
SexprSerializer.NEW_LINE,
['scale', ['xyz', node.scale.x, node.scale.y, node.scale.z]],
SexprSerializer.NEW_LINE,
Expand Down Expand Up @@ -323,6 +345,8 @@ def _serialize_Pad(self, node):
sexpr.append(['drill', node.drill.x])
else:
sexpr.append(['drill', 'oval', node.drill.x, node.drill.y])
if node.offset != [0,0]:
sexpr[-1] += ['(offset',node.offset.x, node.offset.y, ')']

sexpr.append(['layers'] + node.layers)
if node.shape == Pad.SHAPE_ROUNDRECT:
Expand All @@ -339,15 +363,26 @@ def _serialize_Pad(self, node):
sexpr_primitives = self._serialize_CustomPadPrimitives(node)
sexpr.append(['primitives', SexprSerializer.NEW_LINE] + sexpr_primitives)

if node.solder_paste_margin_ratio != 0 or node.solder_mask_margin != 0 or node.solder_paste_margin != 0:
if node.solder_paste_margin_ratio != 0 or node.solder_mask_margin != 0 or node.solder_paste_margin != 0 or node.clearance != 0:
sexpr.append(SexprSerializer.NEW_LINE)
if node.clearance != 0:
sexpr.append(['clearance', node.clearance])
if node.solder_mask_margin != 0:
sexpr.append(['solder_mask_margin', node.solder_mask_margin])
if node.solder_paste_margin_ratio != 0:
sexpr.append(['solder_paste_margin_ratio', node.solder_paste_margin_ratio])
if node.solder_paste_margin != 0:
sexpr.append(['solder_paste_margin', node.solder_paste_margin])

if not node.zone_connect is None or node.thermal_width != 0 or node.thermal_gap!=0:
sexpr.append(SexprSerializer.NEW_LINE)
if not node.zone_connect is None:
sexpr.append(['zone_connect', node.zone_connect])
if node.thermal_width != 0:
sexpr.append(['thermal_width', node.thermal_width])
if node.thermal_gap != 0:
sexpr.append(['thermal_gap', node.thermal_gap])

return sexpr

def _serialize_PolygonPoints(self, node, newline_after_pts=False):
Expand Down
16 changes: 16 additions & 0 deletions KicadModTree/nodes/Footprint.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,13 @@ def __init__(self, name):
self.description = None
self.tags = None
self.attribute = None
self.clearance = None
self.maskMargin = None
self.pasteMargin = None
self.pasteMarginRatio = None
self.zoneConnect = None
self.thermalWidth = None
self.thermalGap = None

def setName(self, name):
self.name = name
Expand All @@ -63,6 +67,9 @@ def setTags(self, tags):
def setAttribute(self, value):
self.attribute = value

def setClearance(self, value):
self.clearance = value

def setMaskMargin(self, value):
self.maskMargin = value

Expand All @@ -74,3 +81,12 @@ def setPasteMarginRatio(self, value):
assert abs(value) <= 1, "Solder paste margin must be between -1 and 1. {} is too large.".format(value)

self.pasteMarginRatio = value

def setZoneConnect(self, value):
self.zoneConnect = value

def setThermalWidth(self, value):
self.thermalWidth = value

def setThermalGap(self, value):
self.thermalGap = value
8 changes: 6 additions & 2 deletions KicadModTree/nodes/base/Model.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,13 @@ class Model(Node):
* *filename* (``str``) --
name of the 3d-model file
* *at* (``Vector3D``) --
position of the model (default: [0, 0, 0])
position of the model in inches (default: [0, 0, 0])
* *offset* (``Vector3D``) --
position of the model in mm (default: [0, 0, 0])
* *scale* (``Vector3D``) --
scale of the model (default: [1, 1, 1])
* *rotate* (``Vector3D``) --
rotation of the model (default: [0, 0, 0])
rotation of the model in degrees (default: [0, 0, 0])

:Example:

Expand All @@ -44,6 +46,7 @@ def __init__(self, **kwargs):
Node.__init__(self)
self.filename = kwargs['filename']
self.at = Vector3D(kwargs.get('at', [0, 0, 0]))
self.offset = Vector3D(kwargs.get('offset', [0, 0, 0]))
self.scale = Vector3D(kwargs.get('scale', [1, 1, 1]))
self.rotate = Vector3D(kwargs.get('rotate', [0, 0, 0]))

Expand All @@ -52,6 +55,7 @@ def _getRenderTreeText(self):

render_string = ['filename: {filename}'.format(filename=self.filename),
'at: {at}'.format(at=self.at.render('(xyz {x} {y} {z})')),
'offset: {offset}'.format(at=self.offset.render('(xyz {x} {y} {z})')),
'scale: {scale}'.format(scale=self.scale.render('(xyz {x} {y} {z})')),
'rotate: {rotate}'.format(rotate=self.rotate.render('(xyz {x} {y} {z})'))]

Expand Down
40 changes: 38 additions & 2 deletions KicadModTree/nodes/base/Pad.py
Original file line number Diff line number Diff line change
Expand Up @@ -174,13 +174,24 @@ class Pad(Node):
If this is given then all other round radius specifiers are ignored
Ignored for every shape except round rect

* *clearance* (``float``) --
clearance margin of the pad (default: 0)
* *solder_paste_margin_ratio* (``float``) --
solder paste margin ratio of the pad (default: 0)
* *solder_paste_margin* (``float``) --
solder paste margin of the pad (default: 0)
* *solder_mask_margin* (``float``) --
solder mask margin of the pad (default: 0)


* *zone_connect* (``int``) --
zone connection type of the pad (default: None)
None maps to CONNECT_PARENT, which means connection type is governed
by parent footprint.
* *thermal_width* (``float``) --
thermal relief spoke width of the pad (default: 0)
* *thermal_gap* (``float``) --
thermal relief gap of the pad (default: 0)

* *x_mirror* (``[int, float](mirror offset)``) --
mirror x direction around offset "point"
* *y_mirror* (``[int, float](mirror offset)``) --
Expand Down Expand Up @@ -211,6 +222,13 @@ class Pad(Node):
LAYERS_THT = ['*.Cu', '*.Mask']
LAYERS_NPTH = ['*.Cu', '*.Mask']

CONNECT_PARENT = None
CONNECT_NONE = 0
CONNECT_THERMAL = 1
CONNECT_SOLID = 2
_CONNECTS = [CONNECT_PARENT, CONNECT_NONE, CONNECT_THERMAL, CONNECT_SOLID]


ANCHOR_CIRCLE = 'circle'
ANCHOR_RECT = 'rect'
_ANCHOR_SHAPE = [ANCHOR_CIRCLE, ANCHOR_RECT]
Expand All @@ -230,9 +248,13 @@ def __init__(self, **kwargs):
self._initSize(**kwargs)
self._initOffset(**kwargs)
self._initDrill(**kwargs) # requires pad type and offset
self._initClearance(**kwargs)
self._initSolderPasteMargin(**kwargs)
self._initSolderPasteMarginRatio(**kwargs)
self._initSolderMaskMargin(**kwargs)
self._initZoneConnect(**kwargs)
self._initThermalWidth(**kwargs)
self._initThermalGap(**kwargs)
self._initLayers(**kwargs)
self._initMirror(**kwargs)

Expand Down Expand Up @@ -310,7 +332,10 @@ def _initDrill(self, **kwargs):
self.drill = None
if kwargs.get('drill'):
pass # TODO: throw warning because drill is not supported


def _initClearance(self, **kwargs):
self.clearance = kwargs.get('clearance', 0)

def _initSolderPasteMarginRatio(self, **kwargs):
self.solder_paste_margin_ratio = kwargs.get('solder_paste_margin_ratio', 0)

Expand All @@ -319,6 +344,17 @@ def _initSolderPasteMargin(self, **kwargs):

def _initSolderMaskMargin(self, **kwargs):
self.solder_mask_margin = kwargs.get('solder_mask_margin', 0)

def _initZoneConnect(self, **kwargs):
self.zone_connect = kwargs.get('zone_connect', Pad.CONNECT_PARENT)
if self.zone_connect not in Pad._CONNECTS:
raise ValueError('{zone_connect} is an invalid zone connection for pads'.format(type=self.zone_connect))

def _initThermalWidth(self, **kwargs):
self.thermal_width = kwargs.get('thermal_width', 0)

def _initThermalGap(self, **kwargs):
self.thermal_gap = kwargs.get('thermal_gap', 0)

def _initLayers(self, **kwargs):
if not kwargs.get('layers'):
Expand Down
14 changes: 14 additions & 0 deletions KicadModTree/nodes/base/Text.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ class Text(Node):
thickness of the text (default: 0.15)
* *hide* (``bool``) --
hide text (default: False)
* *justify* (``str``) --
justify text (default: 'center')

:Example:

Expand All @@ -56,6 +58,11 @@ class Text(Node):
TYPE_USER = 'user'
_TYPES = [TYPE_REFERENCE, TYPE_VALUE, TYPE_USER]

HJUSTIFY_LEFT = 'left'
HJUSTIFY_CENTER = 'center'
HJUSTIFY_RIGHT = 'right'
_HJUSTIFYS = [HJUSTIFY_LEFT, HJUSTIFY_CENTER, HJUSTIFY_RIGHT]

def __init__(self, **kwargs):
Node.__init__(self)
self._initType(**kwargs)
Expand All @@ -71,10 +78,17 @@ def __init__(self, **kwargs):

self.hide = kwargs.get('hide', False)

self._initJustify(**kwargs)

def _initType(self, **kwargs):
self.type = kwargs['type']
if self.type not in Text._TYPES:
raise ValueError('Illegal type selected for text field.')

def _initJustify(self, **kwargs):
self.justify = kwargs.get('justify',Text.HJUSTIFY_CENTER)
if self.justify not in Text._HJUSTIFYS:
raise ValueError('Illegal justify selected for text field.')

def rotate(self, angle, origin=(0, 0), use_degrees=True):
r""" Rotate text around given origin
Expand Down
29 changes: 20 additions & 9 deletions KicadModTree/nodes/specialized/RingPad.py
Original file line number Diff line number Diff line change
Expand Up @@ -283,12 +283,18 @@ class RingPad(Node):
solder mask margin of the pad (default: 0)
* *minimum_overlap* (``float``) --
minimum arc overlap. default 0.1
* *skip_paste* (``bool``) --
remove the paste from this pad. default False
* *skip_mask* (``bool``) --
remove the mask from this pad. default False
"""

def __init__(self, **kwargs):
Node.__init__(self)
self.solder_mask_margin = kwargs.get('solder_mask_margin', 0)
self.minimum_overlap = kwargs.get('minimum_overlap', 0.1)
self.skip_mask = kwargs.get('skip_mask',False)
self.skip_paste = kwargs.get('skip_paste',False)
self._initPosition(**kwargs)
self._initSize(**kwargs)
self._initNumber(**kwargs)
Expand All @@ -304,7 +310,7 @@ def _initSize(self, **kwargs):
if type(_id) not in [int, float] or type(_od) not in [int, float]:
raise ValueError('ring pad size and inner_diameter only support int or float')
if _id >= _od:
raise ValueError('inner diameter must be larger than size')
raise ValueError('inner diameter must be smaller than size')

self.radius = (_id+_od)/4
self.width = (_od-_id)/2
Expand Down Expand Up @@ -356,11 +362,16 @@ def _initPasteSettings(self, **kwargs):

def _generatePads(self):
self.pads = []
layers = ['F.Cu']
if self.num_paste_zones > 1:
layers = ['F.Cu', 'F.Mask']
if not self.skip_mask:
layers.append('F.Mask')
self._generatePastePads()
else:
layers = ['F.Cu', 'F.Mask', 'F.Paste']
if not self.skip_mask:
layers.append('F.Mask')
if not self.skip_paste:
layers.append('F.Paste')

if not self.is_circle:
self._generateCopperPads()
Expand Down Expand Up @@ -409,7 +420,7 @@ def _generateMaskPads(self):
RingPadPrimitive(
number="",
at=self.at,
width=self.width+2*self.solder_mask_margin,
width=w,
layers=['F.Mask'],
radius=self.radius
))
Expand All @@ -418,9 +429,9 @@ def _generateCopperPads(self):
# kicad_mod.append(c)
layers = ['F.Cu']
if self.num_paste_zones == 1:
if self.solder_paste_margin == 0:
if self.solder_paste_margin == 0 and not self.skip_paste:
layers.append('F.Paste')
else:
elif not self.skip_paste:
self.pads.append(
RingPadPrimitive(
number="",
Expand All @@ -429,11 +440,11 @@ def _generateCopperPads(self):
layers=['F.Paste'],
radius=self.radius
))

if self.solder_mask_margin == 0:
if self.solder_mask_margin == 0 and not self.skip_mask:
# bug in kicad so any clearance other than 0 needs a workaround
layers.append('F.Mask')
else:
elif not self.skip_mask:
self._generateMaskPads()
self.pads.append(
RingPadPrimitive(
Expand Down
6 changes: 3 additions & 3 deletions KicadModTree/util/kicad_util.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,12 @@ def formatFloat(val):

def lispString(string):
'''
add quotation marks to string, when it include a white space or is empty
add quotation marks to string, when it include a white space, a newline or is empty
'''
if type(string) is not str:
string = str(string)

if len(string) == 0 or re.match(".*\s.*", string):
if len(string) == 0 or re.match(".*\s.*", string) or '\\n' in string:
return '"{}"'.format(string.replace('"', '\\"')) # escape text

return string
Expand Down Expand Up @@ -168,7 +168,7 @@ def get_separator():
for attr in sexpr:
if isinstance(attr, (tuple, list)):
return_string = self.sexpr_to_string(attr, prefix + " ")

if loop_ctrl['indentation']:
return_string = return_string.replace('\n', '\n ')
serial_string += get_separator()
Expand Down