Fix a bunch of bugs on the way to electroniceel's protoboard layout
This commit is contained in:
parent
240e5569aa
commit
5ce88e4d1b
7 changed files with 46 additions and 22 deletions
|
|
@ -65,7 +65,10 @@ class Expression:
|
|||
|
||||
class UnitExpression(Expression):
|
||||
def __init__(self, expr, unit):
|
||||
self._expr = expr
|
||||
if isinstance(expr, Expression):
|
||||
self._expr = expr
|
||||
else:
|
||||
self._expr = ConstantExpression(expr)
|
||||
self.unit = unit
|
||||
|
||||
def to_gerber(self, unit=None):
|
||||
|
|
|
|||
|
|
@ -113,7 +113,7 @@ class VectorLine(Primitive):
|
|||
start_y : UnitExpression
|
||||
end_x : UnitExpression
|
||||
end_y : UnitExpression
|
||||
rotation : Expression
|
||||
rotation : Expression = None
|
||||
|
||||
def to_graphic_primitives(self, offset, rotation, variable_binding={}, unit=None, polarity_dark=True):
|
||||
with self.Calculator(self, variable_binding, unit) as calc:
|
||||
|
|
@ -243,7 +243,7 @@ class Outline(Primitive):
|
|||
if len(args) > 5004:
|
||||
raise ValueError(f'Invalid aperture macro outline primitive, too many points ({len(args)//2-2}).')
|
||||
|
||||
self.exposure = args.pop(0)
|
||||
self.exposure = expr(args.pop(0))
|
||||
|
||||
# length arg must not contain variables (that would not make sense)
|
||||
length_arg = (args.pop(0) * ConstantExpression(1)).calculate()
|
||||
|
|
@ -252,7 +252,7 @@ class Outline(Primitive):
|
|||
raise ValueError(f'Invalid aperture macro outline primitive, given size {length_arg} does not match length of coordinate list({len(args)//2-1}).')
|
||||
|
||||
if len(args) % 2 == 1:
|
||||
self.rotation = args.pop()
|
||||
self.rotation = expr(args.pop())
|
||||
else:
|
||||
self.rotation = ConstantExpression(0.0)
|
||||
|
||||
|
|
|
|||
|
|
@ -474,7 +474,7 @@ class ApertureMacroInstance(Aperture):
|
|||
macro : object
|
||||
#: The parameters to the :py:class:`.ApertureMacro`. All elements should be floats or ints. The first item in the
|
||||
#: list is parameter ``$1``, the second is ``$2`` etc.
|
||||
parameters : list
|
||||
parameters : list = field(default_factory=list)
|
||||
#: Aperture rotation in radians. When saving, a copy of the :py:class:`.ApertureMacro` is re-written with this
|
||||
#: rotation.
|
||||
rotation : float = 0
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ from ... import graphic_primitives as gp
|
|||
from ... import graphic_objects as go
|
||||
from ... import apertures as ap
|
||||
from ...utils import MM
|
||||
from ...aperture_macros.parse import GenericMacros
|
||||
from ...aperture_macros.parse import GenericMacros, ApertureMacro
|
||||
|
||||
|
||||
@sexp_type('property')
|
||||
|
|
@ -376,7 +376,7 @@ class Pad:
|
|||
dx, dy = self.rect_delta.x, self.rect_delta.y
|
||||
|
||||
# Note: KiCad already uses MM units, so no conversion needed here.
|
||||
return ApertureMacroInstance(GenericMacros.isosceles_trapezoid,
|
||||
return ap.ApertureMacroInstance(GenericMacros.isosceles_trapezoid,
|
||||
[x+dx, y+dy,
|
||||
2*max(dx, dy),
|
||||
0, 0, # no hole
|
||||
|
|
@ -385,7 +385,7 @@ class Pad:
|
|||
elif self.shape == Atom.roundrect:
|
||||
x, y = self.size.x, self.size.y
|
||||
r = min(x, y) * self.roundrect_rratio
|
||||
return ApertureMacroInstance(GenericMacros.rounded_rect,
|
||||
return ap.ApertureMacroInstance(GenericMacros.rounded_rect,
|
||||
[x, y,
|
||||
r,
|
||||
0, 0, # no hole
|
||||
|
|
@ -398,7 +398,7 @@ class Pad:
|
|||
for gn_obj in obj.render():
|
||||
primitives += gn_obj._aperture_macro_primitives() # todo: precision params
|
||||
macro = ApertureMacro(primitives=primitives)
|
||||
return ApertureMacroInstance(macro)
|
||||
return ap.ApertureMacroInstance(macro)
|
||||
|
||||
def render_drill(self):
|
||||
if not self.drill:
|
||||
|
|
@ -548,6 +548,8 @@ class Footprint:
|
|||
for fe in obj.render():
|
||||
fe.rotate(rotation)
|
||||
fe.offset(x, y, MM)
|
||||
if isinstance(fe, go.Flash) and fe.aperture:
|
||||
fe.aperture = fe.aperture.rotated(rotation)
|
||||
layer_stack[layer_map[layer]].objects.append(fe)
|
||||
|
||||
for obj in self.pads:
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ from .primitives import *
|
|||
from ... import graphic_objects as go
|
||||
from ... import apertures as ap
|
||||
from ...newstroke import Newstroke
|
||||
from ...utils import rotate_point
|
||||
from ...utils import rotate_point, MM
|
||||
|
||||
@sexp_type('layer')
|
||||
class TextLayer:
|
||||
|
|
@ -84,7 +84,7 @@ class TextBox:
|
|||
if self.stroke.type not in (None, Atom.default, Atom.solid):
|
||||
raise ValueError('Dashed strokes are not supported on vector text')
|
||||
|
||||
yield from reg.outline_objects(aperture=CircleAperture(self.stroke.width, unit=MM))
|
||||
yield from reg.outline_objects(aperture=ap.CircleAperture(self.stroke.width, unit=MM))
|
||||
|
||||
yield reg
|
||||
|
||||
|
|
@ -137,7 +137,7 @@ class Rectangle:
|
|||
yield rect
|
||||
|
||||
if self.width:
|
||||
yield from rect.outline_objects(aperture=CircleAperture(self.width, unit=MM))
|
||||
yield from rect.outline_objects(aperture=ap.CircleAperture(self.width, unit=MM))
|
||||
|
||||
|
||||
@sexp_type('gr_circle')
|
||||
|
|
@ -177,7 +177,7 @@ class Arc:
|
|||
arc = go.Arc(x1, y1, x2, y2, cx-x1, cy-y1, unit=MM)
|
||||
|
||||
if self.width:
|
||||
arc.aperture = CircleAperture(self.width, unit=MM)
|
||||
arc.aperture = ap.CircleAperture(self.width, unit=MM)
|
||||
yield arc
|
||||
|
||||
if self.fill:
|
||||
|
|
@ -189,14 +189,14 @@ class Polygon:
|
|||
pts: PointList = field(default_factory=PointList)
|
||||
layer: Named(str) = None
|
||||
width: Named(float) = None
|
||||
fill: FillMode= False
|
||||
fill: FillMode = True
|
||||
tstamp: Timestamp = None
|
||||
|
||||
def render(self):
|
||||
reg = go.Region([(pt.x, pt.y) for pt in self.pts.xy], unit=MM)
|
||||
|
||||
if width:
|
||||
yield from reg.outline_objects(aperture=CircleAperture(self.width, unit=MM))
|
||||
if self.width and self.width >= 0.005:
|
||||
yield from reg.outline_objects(aperture=ap.CircleAperture(self.width, unit=MM))
|
||||
|
||||
if self.fill:
|
||||
yield reg
|
||||
|
|
|
|||
|
|
@ -6,10 +6,13 @@ import string
|
|||
import itertools
|
||||
from copy import copy, deepcopy
|
||||
import warnings
|
||||
import importlib.resources
|
||||
|
||||
from .primitives import *
|
||||
from ..graphic_objects import Region
|
||||
from ..apertures import RectangleAperture, CircleAperture
|
||||
from .kicad import footprints as kfp
|
||||
from . import data as package_data
|
||||
|
||||
|
||||
class ProtoBoard(Board):
|
||||
|
|
@ -468,6 +471,21 @@ class PoweredProto(ObjectGroup):
|
|||
return unit.convert_bounds_from(self.unit, ((x-p, y-p), (x+p, y+p)))
|
||||
|
||||
|
||||
class SpikyProto(ObjectGroup):
|
||||
def __init__(self, pitch=None, drill=None, clearance=None, power_pad_dia=None, via_size=None, trace_width=None, unit=MM):
|
||||
super().__init__(0, 0)
|
||||
res = importlib.resources.files(package_data)
|
||||
|
||||
self.fp_center = kfp.Footprint.load(res.joinpath('center-pad-spikes.kicad_mod').read_text(encoding='utf-8'))
|
||||
self.objects.append(kfp.FootprintInstance(1.27, 1.27, self.fp_center, unit=MM))
|
||||
|
||||
self.fp_between = kfp.Footprint.load(res.joinpath('pad-between-spiked.kicad_mod').read_text(encoding='utf-8'))
|
||||
self.objects.append(kfp.FootprintInstance(1.27, 0, self.fp_between, unit=MM))
|
||||
self.objects.append(kfp.FootprintInstance(0, 1.27, self.fp_between, rotation=math.pi/2, unit=MM))
|
||||
|
||||
self.pad = kfp.Footprint.load(res.joinpath('tht-0.8.kicad_mod').read_text(encoding='utf-8'))
|
||||
self.objects.append(kfp.FootprintInstance(0, 0, self.pad, unit=MM))
|
||||
|
||||
def convert_to_mm(value, unit):
|
||||
unitl = unit.lower()
|
||||
if unitl == 'mm':
|
||||
|
|
@ -498,7 +516,7 @@ def eval_value(value, total_length=None):
|
|||
|
||||
def _demo():
|
||||
#pattern1 = PatternProtoArea(2.54, obj=THTPad.circle(0, 0, 0.9, 1.8, paste=False))
|
||||
pattern1 = PatternProtoArea(2.54, 3.84, obj=THTPad.obround(0, 0, 0.9, 1.8, 2.5, paste=False))
|
||||
pattern1 = PatternProtoArea(2.54, 2.54, obj=SpikyProto())
|
||||
pattern2 = PatternProtoArea(1.2, 2.0, obj=SMDPad.rect(0, 0, 1.0, 1.8, paste=False))
|
||||
pattern3 = PatternProtoArea(2.54, 1.27, obj=SMDPad.rect(0, 0, 2.3, 1.0, paste=False))
|
||||
#pattern3 = EmptyProtoArea(copper_fill=True)
|
||||
|
|
@ -511,7 +529,7 @@ def _demo():
|
|||
#pattern = PatternProtoArea(2.54*1.5, obj=THTFlowerProto())
|
||||
#pattern = PatternProtoArea(2.54, obj=THTPad.circle(0, 0, 0.9, 1.8, paste=False))
|
||||
#pattern = PatternProtoArea(2.54, obj=PoweredProto())
|
||||
pb = ProtoBoard(100, 80, pattern, mounting_hole_dia=3.2, mounting_hole_offset=5)
|
||||
pb = ProtoBoard(30, 30, pattern1, mounting_hole_dia=3.2, mounting_hole_offset=5)
|
||||
print(pb.pretty_svg())
|
||||
pb.layer_stack().save_to_directory('/tmp/testdir')
|
||||
|
||||
|
|
|
|||
|
|
@ -370,7 +370,7 @@ class Region(GraphicObject):
|
|||
|
||||
if points[-1] != points[0]:
|
||||
points.append(points[0])
|
||||
yield amp.Outline(self.unit, int(self.polarity_dark), len(points)-1, *(coord for p in points for coord in p))
|
||||
yield amp.Outline(self.unit, [int(self.polarity_dark), len(points)-1, *(coord for p in points for coord in p)])
|
||||
|
||||
def to_primitives(self, unit=None):
|
||||
if unit == self.unit:
|
||||
|
|
@ -499,9 +499,10 @@ class Line(GraphicObject):
|
|||
|
||||
def _aperture_macro_primitives(self):
|
||||
obj = self.converted(MM) # Gerbonara aperture macros use MM units.
|
||||
yield amp.VectorLine(int(self.polarity_dark), obj.width, obj.x1, obj.y1, obj.x2, obj.y2)
|
||||
yield amp.Circle(int(self.polarity_dark), obj.width, obj.x1, obj.y1)
|
||||
yield amp.Circle(int(self.polarity_dark), obj.width, obj.x2, obj.y2)
|
||||
width = obj.aperture.equivalent_width(MM)
|
||||
yield amp.VectorLine(MM, [int(self.polarity_dark), width, obj.x1, obj.y1, obj.x2, obj.y2, 0])
|
||||
yield amp.Circle(MM, [int(self.polarity_dark), width, obj.x1, obj.y1])
|
||||
yield amp.Circle(MM, [int(self.polarity_dark), width, obj.x2, obj.y2])
|
||||
|
||||
def to_statements(self, gs):
|
||||
yield from gs.set_polarity(self.polarity_dark)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue