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

Ep roundradius handling #422

Merged
merged 22 commits into from
Jan 27, 2020
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
bf83ec7
Introduce more powerful round radius handler
poeschlr Sep 8, 2019
9241a3d
EP round radius update
poeschlr Sep 8, 2019
688d3ba
fix duplicate old style dimension
poeschlr Sep 8, 2019
08a4cf3
fix rounding of chamfered pad if handler is given
poeschlr Sep 8, 2019
343f887
update docstring
poeschlr Sep 8, 2019
24f55e8
better int/float param checking
poeschlr Sep 12, 2019
bb831c4
docstring
poeschlr Sep 15, 2019
6709c48
fix minor grammer spelling and formating mistakes
poeschlr Sep 26, 2019
7d48698
Better formating of format string
poeschlr Sep 27, 2019
baaddfa
correct exclude pin list for SOIC-4_4.55x3.7mm_P2.54mm
poeschlr Oct 9, 2019
44ff14f
Merge branch 'ep_roundradius_handling' of github.com:poeschlr/kicad-f…
poeschlr Oct 9, 2019
65dc6e5
Merge branch 'master' into ep_roundradius_handling
poeschlr Oct 10, 2019
c8e7299
VSON-10 has no place in the qfn/dfn lib
poeschlr Oct 10, 2019
13969f5
fix spelling mistakes
poeschlr Oct 10, 2019
53693b8
more options for ep rounding and default is now 0
poeschlr Nov 8, 2019
a2f9bb5
update readme with new ep rounded corner handling
poeschlr Nov 9, 2019
6dcbef1
Remove definition for footprint that required a very specialized manu…
poeschlr Jan 5, 2020
17ce20f
fix documentation links
poeschlr Jan 5, 2020
7b76c11
add page to documentation link
poeschlr Jan 13, 2020
2e50827
Merge branch 'master' into ep_roundradius_handling
poeschlr Jan 13, 2020
7ab2304
Backwardscompatibility: use default round radius 0 for chamfered and ep
poeschlr Jan 14, 2020
06d7d27
coding style
poeschlr Jan 14, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
132 changes: 114 additions & 18 deletions KicadModTree/nodes/base/Pad.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,105 @@
from KicadModTree.nodes.base.Polygon import Polygon


class RoundRadiusHandler(object):
r"""Handles round radius setting of a pad

:param \**kwargs:
See below

:Keyword Arguments:
* *radius_ratio* (``float``) --
The radius ratio of the rounded rectangle.
* *maximum_radius* (``float``) --
The maximum radius for the rounded rectangle.
If the radius produced by the radius_ratio parameter for the pad would
exceed the maximum radius, the ratio is reduced to limit the radius.
(This is useful for IPC-7351C compliance as it suggests 25% ratio with limit 0.25mm)
* *round_radius_exact* (``float``) --
Set an exact round radius for a pad.
"""
def __init__(self, **kwargs):
self.radius_ratio = getOptionalNumberTypeParam(
kwargs, 'radius_ratio', default_value=0,
low_limit=0, high_limit=0.5)

self.maximum_radius = getOptionalNumberTypeParam(kwargs, 'maximum_radius')
self.round_radius_exact = getOptionalNumberTypeParam(kwargs, 'round_radius_exact')

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

def getRadiusRatio(self, shortest_sidelength):
r"""get the resulting round radius ratio

:param shortest_sidelength: shortest sidelength of a pad
:return: the resulting round radius ratio to be used for the pad
"""
if self.kicad4_compatible:
return 0

if self.round_radius_exact:
if self.round_radius_exact > shortest_sidelength/2:
raise ValueError(
"requested round radius of {} is too large for pad size of {}"
.format(self.round_radius_exact, pad_size)
)
if self.maximum_radius:
return min(self.round_radius_exact, self.maximum_radius)/shortest_sidelength
else:
return self.round_radius_exact/shortest_sidelength
if self.maximum_radius:
if self.radius_ratio*shortest_sidelength > self.maximum_radius:
return self.maximum_radius/shortest_sidelength

return self.radius_ratio

def getRoundRadius(self, shortest_sidelength):
r"""get the resulting round radius

:param shortest_sidelength: shortest sidelength of a pad
:return: the resulting round radius to be used for the pad
"""
return self.getRadiusRatio(shortest_sidelength)*shortest_sidelength

def roundingRequested(self):
r"""Check if the pad has a rounded corner

:return: True if rounding corners is required
poeschlr marked this conversation as resolved.
Show resolved Hide resolved
"""
if self.kicad4_compatible:
return False

if self.round_radius_exact is not None:
if self.round_radius_exact > 0:
return True
else:
return False

if self.radius_ratio > 0:
return True
else:
return False

def limitMaxRadius(self, limit):
r"""Set a new maximum limit

:param limit: the new limit.
"""

if not self.roundingRequested():
return
if self.maximum_radius is not None:
self.maximum_radius = min(self.maximum_radius, limit)
else:
self.maximum_radius = limit

def __str__(self):
return "ratio {}, max {}, exact {}, v4 compatible {}".format(
self.radius_ratio, self.maximum_radius,
self.round_radius_exact, self.kicad4_compatible
)


class Pad(Node):
r"""Add a Pad to the render tree

Expand All @@ -50,13 +149,20 @@ class Pad(Node):
drill-size of the pad
* *radius_ratio* (``float``) --
The radius ratio of the rounded rectangle.
Ignored for every other shape.
Ignored for every shape except round rect.
* *maximum_radius* (``float``) --
The maximum radius for the rounded rectangle.
If the radius produced by the radius_ratio parameter for the pad would
exceed the maximum radius, the ratio is reduced to limit the ratio.
exceed the maximum radius, the ratio is reduced to limit the radius.
(This is useful for IPC-7351C compliance as it suggests 25% ratio with limit 0.25mm)
Ignored for every other shape.
Ignored for every shape except round rect.
* *round_radius_exact* (``float``) --
Set an exact round radius for a pad.
Ignored for every shape except round rect
* *round_radius_handler* (``RoundRadiusHandler``) --
An instance of the RoundRadiusHandler class
If this is given then all other round radius specifiers are Ignored
poeschlr marked this conversation as resolved.
Show resolved Hide resolved
Ignored for every shape except round rect
* *solder_paste_margin_ratio* (``float``) --
solder paste margin ratio of the pad (default: 0)
* *solder_paste_margin* (``float``) --
Expand Down Expand Up @@ -210,22 +316,12 @@ def _initLayers(self, **kwargs):
self.layers = kwargs.get('layers')

def _initRadiusRatio(self, **kwargs):
radius_ratio = kwargs.get('radius_ratio', 0)
if type(radius_ratio) not in [int, float]:
raise TypeError('radius ratio needs to be of type int or float')
if radius_ratio >= 0 and radius_ratio <= 0.5:
self.radius_ratio = radius_ratio
if('round_radius_handler' in kwargs):
self.round_radius_handler = kwargs['round_radius_handler']
else:
raise ValueError('radius ratio out of allowed range (0 <= rr <= 0.5)')

if kwargs.get('maximum_radius') is not None:
maximum_radius = kwargs.get('maximum_radius')
if type(maximum_radius) not in [int, float]:
raise TypeError('maximum radius needs to be of type int or float')
self.round_radius_handler = RoundRadiusHandler(**kwargs)

shortest_sidelength = min(self.size)
if self.radius_ratio*shortest_sidelength > maximum_radius:
self.radius_ratio = maximum_radius/shortest_sidelength
self.radius_ratio = self.round_radius_handler.getRadiusRatio(min(self.size))
poeschlr marked this conversation as resolved.
Show resolved Hide resolved

if self.radius_ratio == 0:
self.shape = Pad.SHAPE_RECT
Expand Down Expand Up @@ -275,4 +371,4 @@ def getRoundRadius(self):
if r > r_max:
r_max = r
return r_max
return self.radius_ratio*min(self.size)
return self.round_radius_handler.getRoundRadius(min(self.size))
31 changes: 19 additions & 12 deletions KicadModTree/nodes/specialized/ChamferedPad.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
from KicadModTree.util.paramUtil import *
from KicadModTree.Vector import *
from KicadModTree.nodes.base.Polygon import *
from KicadModTree.nodes.base.Pad import Pad
from KicadModTree.nodes.base.Pad import Pad, RoundRadiusHandler
from math import sqrt


Expand Down Expand Up @@ -212,11 +212,17 @@ class ChamferedPad(Node):
mirror y direction around offset "point"

* *radius_ratio* (``float``) --
The radius ratio used if the pad has no chamfer
Default: 0 means pads do not included rounded corners (normal rectangles are used)
The radius ratio of the rounded rectangle.
* *maximum_radius* (``float``) --
Only used if a radius ratio is given.
Limits the radius.
The maximum radius for the rounded rectangle.
If the radius produced by the radius_ratio parameter for the pad would
exceed the maximum radius, the ratio is reduced to limit the radius.
(This is useful for IPC-7351C compliance as it suggests 25% ratio with limit 0.25mm)
* *round_radius_exact* (``float``) --
Set an exact round radius for a pad.
* *round_radius_handler* (``RoundRadiusHandler``) --
An instance of the RoundRadiusHandler class
If this is given then all other round radius specifiers are Ignored
poeschlr marked this conversation as resolved.
Show resolved Hide resolved
"""

def __init__(self, **kwargs):
Expand All @@ -225,8 +231,12 @@ def __init__(self, **kwargs):
self._initSize(**kwargs)
self._initMirror(**kwargs)
self._initPadSettings(**kwargs)
self.radius_ratio = kwargs.get('radius_ratio', 0)
self.maximum_radius = kwargs.get('maximum_radius')

if('round_radius_handler' in kwargs):
self.round_radius_handler = kwargs['round_radius_handler']
else:
self.round_radius_handler = RoundRadiusHandler(**kwargs)

self.pad = self._generatePad()

def _initSize(self, **kwargs):
Expand Down Expand Up @@ -271,10 +281,7 @@ def _generatePad(self):
if self.corner_selection.isAnySelected() and self.chamfer_size[0] > 0 and self.chamfer_size[1] > 0:
is_chamfered = True

shortest_sidlength = min(self.size)
radius = shortest_sidlength*self.radius_ratio
if self.maximum_radius and radius > self.maximum_radius:
radius = self.maximum_radius
radius = self.round_radius_handler.getRoundRadius(min(self.size))

if is_chamfered:
outside = Vector2D(self.size.x/2, self.size.y/2)
Expand All @@ -284,7 +291,7 @@ def _generatePad(self):
]

polygon_width = 0
if self.radius_ratio > 0:
if self.round_radius_handler.roundingRequested():
if self.chamfer_size[0] != self.chamfer_size[1]:
raise NotImplementedError('rounded chamfered pads are only supported for 45 degree chamfers')
# We prefer the use of rounded rectangle over chamfered pads.
Expand Down
14 changes: 10 additions & 4 deletions KicadModTree/nodes/specialized/ChamferedPadGrid.py
Original file line number Diff line number Diff line change
Expand Up @@ -180,11 +180,17 @@ class ChamferedPadGrid(Node):
mirror y direction around offset "point"

* *radius_ratio* (``float``) --
The radius ratio used if the pad has no chamfer
Default: 0 means pads do not included rounded corners (normal rectangles are used)
The radius ratio of the rounded rectangle.
* *maximum_radius* (``float``) --
Only used if a radius ratio is given.
Limits the radius.
The maximum radius for the rounded rectangle.
If the radius produced by the radius_ratio parameter for the pad would
exceed the maximum radius, the ratio is reduced to limit the radius.
(This is useful for IPC-7351C compliance as it suggests 25% ratio with limit 0.25mm)
* *round_radius_exact* (``float``) --
Set an exact round radius for a pad.
* *round_radius_handler* (``RoundRadiusHandler``) --
An instance of the RoundRadiusHandler class
If this is given then all other round radius specifiers are Ignored
poeschlr marked this conversation as resolved.
Show resolved Hide resolved
"""

def __init__(self, **kwargs):
Expand Down
56 changes: 32 additions & 24 deletions KicadModTree/nodes/specialized/ExposedPad.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,11 +105,22 @@ class ExposedPad(Node):
0 means no rounding

* *radius_ratio* (``float``) --
The radius ratio for all pads (copper, paste and mask)
Default: 0 means pads do not included rounded corners (normal rectangles are used)
The radius ratio of the main pads.
* *maximum_radius* (``float``) --
Only used if a radius ratio is given.
Limits the radius.
The maximum radius for the main pads.
If the radius produced by the radius_ratio parameter for the pad would
exceed the maximum radius, the ratio is reduced to limit the radius.
* *round_radius_exact* (``float``) --
Set an exact round radius for the main pads.

poeschlr marked this conversation as resolved.
Show resolved Hide resolved
* *paste_radius_ratio* (``float``) --
The radius ratio of the paste pads.
* *paste_maximum_radius* (``float``) --
The maximum radius for the paste pads.
If the radius produced by the paste_radius_ratio parameter for the paste pad would
exceed the maximum radius, the ratio is reduced to limit the radius.
* *paste_round_radius_exact* (``float``) --
Set an exact round radius for the paste pads.

* *kicad4_compatible* (``bool``) --
Makes sure the resulting pad is compatible with kicad 4. default False
Expand All @@ -126,13 +137,15 @@ def __init__(self, **kwargs):
self.size_round_base = kwargs.get('size_round_base', 0.01)
self.grid_round_base = kwargs.get('grid_round_base', 0.01)

self.radius_ratio = kwargs.get('radius_ratio', 0)
self.maximum_radius = kwargs.get('maximum_radius')
self.round_radius_handler = RoundRadiusHandler(**kwargs)

self.kicad4_compatible = kwargs.get('kicad4_compatible', False)
if self.kicad4_compatible:
self.radius_ratio = 0
self.maximum_radius = None
self.paste_round_radius_handler = RoundRadiusHandler(
radius_ratio=kwargs.get('paste_radius_ratio', 0),
maximum_radius=kwargs.get('paste_maximum_radius', None),
round_radius_exact=kwargs.get('paste_round_radius_exact', None),
kicad4_compatible=self.kicad4_compatible
)

self._initNumber(**kwargs)
self._initSize(**kwargs)
Expand Down Expand Up @@ -268,7 +281,7 @@ def __createPasteIgnoreVia(self):
center=self.at, size=paste_size, layers=['F.Paste'],
chamfer_size=0, chamfer_selection=0,
pincount=self.paste_layout, grid=paste_grid,
radius_ratio=self.radius_ratio, maximum_radius=self.maximum_radius
round_radius_handler=self.paste_round_radius_handler
)]

@staticmethod
Expand Down Expand Up @@ -312,7 +325,7 @@ def __createPasteAvoidViasInside(self):
center=[0, 0], size=self.inner_size, layers=['F.Paste'],
chamfer_size=0, chamfer_selection=corner,
pincount=self.paste_between_vias, grid=self.inner_grid,
radius_ratio=self.radius_ratio, maximum_radius=self.maximum_radius
round_radius_handler=self.paste_round_radius_handler
)

if not self.kicad4_compatible:
Expand Down Expand Up @@ -342,7 +355,7 @@ def __createPasteOutsideX(self):
chamfer_size=0, chamfer_selection=corner,
pincount=[self.paste_rings_outside[0], self.paste_between_vias[1]],
grid=[self.outer_paste_grid[0], self.inner_grid[1]],
radius_ratio=self.radius_ratio, maximum_radius=self.maximum_radius
round_radius_handler=self.paste_round_radius_handler
)

if not self.kicad4_compatible:
Expand Down Expand Up @@ -388,7 +401,7 @@ def __createPasteOutsideY(self):
chamfer_size=0, chamfer_selection=corner,
pincount=[self.paste_between_vias[0], self.paste_rings_outside[1]],
grid=[self.inner_grid[0], self.outer_paste_grid[1]],
radius_ratio=self.radius_ratio, maximum_radius=self.maximum_radius
round_radius_handler=self.paste_round_radius_handler
)

if not self.kicad4_compatible:
Expand Down Expand Up @@ -436,7 +449,7 @@ def __createPasteOutsideCorners(self):
chamfer_size=0, chamfer_selection=0,
pincount=self.paste_rings_outside,
grid=self.outer_paste_grid,
radius_ratio=self.radius_ratio, maximum_radius=self.maximum_radius
round_radius_handler=self.paste_round_radius_handler
)

if not self.kicad4_compatible:
Expand Down Expand Up @@ -498,13 +511,13 @@ def __createMainPad(self):
pads.append(Pad(
number="", at=self.at, size=self.mask_size,
shape=Pad.SHAPE_ROUNDRECT, type=Pad.TYPE_SMT, layers=['F.Mask'],
radius_ratio=self.radius_ratio, maximum_radius=self.main_max_radius
round_radius_handler=self.round_radius_handler
))

pads.append(Pad(
number=self.number, at=self.at, size=self.size,
shape=Pad.SHAPE_ROUNDRECT, type=Pad.TYPE_SMT, layers=layers_main,
radius_ratio=self.radius_ratio, maximum_radius=self.main_max_radius
round_radius_handler=self.round_radius_handler
))

return pads
Expand Down Expand Up @@ -533,20 +546,15 @@ def __createVias(self):
number=self.number, at=self.at, size=self.bottom_size,
shape=Pad.SHAPE_ROUNDRECT, type=Pad.TYPE_SMT,
layers=self.bottom_pad_Layers,
radius_ratio=self.radius_ratio, maximum_radius=self.main_max_radius
round_radius_handler=self.round_radius_handler
))

return pads

def getVirtualChilds(self):
# traceback.print_stack()
if self.has_vias:
if self.maximum_radius:
self.main_max_radius = min(self.maximum_radius, self.via_size/2)
else:
self.main_max_radius = self.via_size/2
else:
self.main_max_radius = self.maximum_radius
self.round_radius_handler.limitMaxRadius(self.via_size/2)

pads = []
pads += self.__createMainPad()
Expand All @@ -556,4 +564,4 @@ def getVirtualChilds(self):
return pads

def getRoundRadius(self):
return min(self.radius_ratio*min(self.size), self.maximum_radius)
return self.round_radius_handler.getRoundRadius(min(self.size))
Loading