kopia lustrzana https://gitlab.com/gerbolyze/gerbonara
Fix all failing tests that don't involve kicad-cli
rodzic
31af2b260c
commit
a35125b123
|
@ -196,8 +196,10 @@ class XYCoord:
|
|||
x: float = 0
|
||||
y: float = 0
|
||||
|
||||
def __init__(self, x=0, y=0):
|
||||
if isinstance(x, XYCoord):
|
||||
def __init__(self, x=None, y=None):
|
||||
if x is None:
|
||||
self.x, self.y = None, None
|
||||
elif isinstance(x, XYCoord):
|
||||
self.x, self.y = x.x, x.y
|
||||
elif isinstance(x, (tuple, list)):
|
||||
self.x, self.y = x
|
||||
|
@ -227,6 +229,25 @@ class PointList:
|
|||
xy : List(XYCoord) = field(default_factory=list)
|
||||
|
||||
|
||||
@sexp_type('arc')
|
||||
class Arc:
|
||||
start: Rename(XYCoord) = None
|
||||
mid: Rename(XYCoord) = None
|
||||
end: Rename(XYCoord) = None
|
||||
|
||||
|
||||
@sexp_type('pts')
|
||||
class ArcPointList:
|
||||
@classmethod
|
||||
def __map__(kls, obj, parent=None):
|
||||
_tag, *values = obj
|
||||
return [map_sexp((XYCoord if elem[0] == 'xy' else Arc), elem, parent=parent) for elem in values]
|
||||
|
||||
@classmethod
|
||||
def __sexp__(kls, value):
|
||||
yield [kls.name_atom, *(e for elem in value for e in elem.__sexp__(elem))]
|
||||
|
||||
|
||||
@sexp_type('xyz')
|
||||
class XYZCoord:
|
||||
x: float = 0
|
||||
|
@ -322,7 +343,7 @@ class TextMixin:
|
|||
x2 = max(max(l.x1, l.x2) for l in lines)
|
||||
y2 = max(max(l.y1, l.y2) for l in lines)
|
||||
r = self.effects.font.thickness/2
|
||||
return (x1-r, y1-r), (x2+r, y2+r)
|
||||
return (x1-r, -(y1-r)), (x2+r, -(y2+r))
|
||||
|
||||
def svg_path_data(self):
|
||||
for line in self.render():
|
||||
|
@ -409,7 +430,7 @@ class TextMixin:
|
|||
x, y = x+offx, y+offy
|
||||
x, y = rotate_point(x, y, math.radians(-rot or 0))
|
||||
x, y = x+self.at.x, y+self.at.y
|
||||
points.append((x, y))
|
||||
points.append((x, -y))
|
||||
|
||||
for p1, p2 in zip(points[:-1], points[1:]):
|
||||
yield go.Line(*p1, *p2, aperture=aperture, unit=MM)
|
||||
|
|
|
@ -105,7 +105,7 @@ class Line:
|
|||
dasher.line(self.end.x, self.end.y)
|
||||
|
||||
for x1, y1, x2, y2 in dasher:
|
||||
yield go.Line(x1, y1, x2, y2, aperture=ap.CircleAperture(dasher.width, unit=MM), unit=MM)
|
||||
yield go.Line(x1, -y1, x2, -y2, aperture=ap.CircleAperture(dasher.width, unit=MM), unit=MM)
|
||||
|
||||
|
||||
@sexp_type('fp_rect')
|
||||
|
@ -127,7 +127,7 @@ class Rectangle:
|
|||
w, h = x2-x1, y2-y1
|
||||
|
||||
if self.fill == Atom.solid:
|
||||
yield go.Region.from_rectangle(x1, y1, w, h, unit=MM)
|
||||
yield go.Region.from_rectangle(x1, -y1, w, h, unit=MM)
|
||||
|
||||
dasher = Dasher(self)
|
||||
dasher.move(x1, y1)
|
||||
|
@ -138,7 +138,7 @@ class Rectangle:
|
|||
|
||||
aperture = ap.CircleAperture(dasher.width, unit=MM)
|
||||
for x1, y1, x2, y2 in dasher:
|
||||
yield go.Line(x1, y1, x2, y2, aperture=aperture, unit=MM)
|
||||
yield go.Line(x1, -y1, x2, -y2, aperture=aperture, unit=MM)
|
||||
|
||||
|
||||
@sexp_type('fp_circle')
|
||||
|
@ -159,7 +159,7 @@ class Circle:
|
|||
dasher = Dasher(self)
|
||||
aperture = ap.CircleAperture(dasher.width or 0, unit=MM)
|
||||
|
||||
circle = go.Arc.from_circle(x, y, r, aperture=aperture, unit=MM)
|
||||
circle = go.Arc.from_circle(x, -y, r, aperture=aperture, unit=MM)
|
||||
|
||||
if self.fill == Atom.solid:
|
||||
yield circle.to_region()
|
||||
|
@ -173,7 +173,7 @@ class Circle:
|
|||
|
||||
aperture = ap.CircleAperture(dasher.width, unit=MM)
|
||||
for x1, y1, x2, y2 in dasher:
|
||||
yield go.Line(x1, y1, x2, y2, aperture=aperture, unit=MM)
|
||||
yield go.Line(x1, -y1, x2, -y2, aperture=aperture, unit=MM)
|
||||
|
||||
|
||||
@sexp_type('fp_arc')
|
||||
|
@ -201,7 +201,7 @@ class Arc:
|
|||
if math.isclose(x1, x2, abs_tol=1e-6) and math.isclose(y1, y2, abs_tol=1e-6):
|
||||
cx = (x1 + mx) / 2
|
||||
cy = (y1 + my) / 2
|
||||
arc = go.Arc(x1, y1, x2, y2, cx-x1, cy-y1, clockwise=True, aperture=aperture, unit=MM)
|
||||
arc = go.Arc(x1, -y1, x2, -y2, cx-x1, -(cy-y1), clockwise=True, aperture=aperture, unit=MM)
|
||||
if dasher.solid:
|
||||
yield arc
|
||||
|
||||
|
@ -211,7 +211,7 @@ class Arc:
|
|||
dasher.segments.append((line.x1, line.y1, line.x2, line.y2))
|
||||
|
||||
for line in dasher:
|
||||
yield go.Line(x1, y1, x2, y2, aperture=ap.CircleAperture(dasher.width, unit=MM), unit=MM)
|
||||
yield go.Line(x1, -y1, x2, -y2, aperture=ap.CircleAperture(dasher.width, unit=MM), unit=MM)
|
||||
|
||||
else:
|
||||
# https://stackoverflow.com/questions/56224824/how-do-i-find-the-circumcenter-of-the-triangle-using-python-without-external-lib
|
||||
|
@ -220,7 +220,7 @@ class Arc:
|
|||
cy = ((x1 * x1 + y1 * y1) * (mx - x2) + (x2 * x2 + y2 * y2) * (x1 - mx) + (mx * mx + my * my) * (x2 - x1)) / d
|
||||
|
||||
# KiCad only has clockwise arcs.
|
||||
arc = go.Arc(x1, y1, x2, y2, cx-x1, cy-y1, clockwise=False, aperture=aperture, unit=MM)
|
||||
arc = go.Arc(x1, -y1, x2, -y2, cx-x1, -(cy-y1), clockwise=False, aperture=aperture, unit=MM)
|
||||
if dasher.solid:
|
||||
yield arc
|
||||
|
||||
|
@ -230,7 +230,7 @@ class Arc:
|
|||
dasher.segments.append((line.x1, line.y1, line.x2, line.y2))
|
||||
|
||||
for line in dasher:
|
||||
yield go.Line(x1, y1, x2, y2, aperture=ap.CircleAperture(dasher.width, unit=MM), unit=MM)
|
||||
yield go.Line(x1, -y1, x2, -y2, aperture=ap.CircleAperture(dasher.width, unit=MM), unit=MM)
|
||||
|
||||
|
||||
@sexp_type('fp_poly')
|
||||
|
@ -249,16 +249,16 @@ class Polygon:
|
|||
|
||||
dasher = Dasher(self)
|
||||
start = self.pts.xy[0]
|
||||
dasher.move(start.x, start.y)
|
||||
dasher.move(start.x, -start.y)
|
||||
for point in self.pts.xy[1:]:
|
||||
dasher.line(point.x, point.y)
|
||||
|
||||
aperture = ap.CircleAperture(dasher.width, unit=MM)
|
||||
for x1, y1, x2, y2 in dasher:
|
||||
yield go.Line(x1, y1, x2, y2, aperture=aperture, unit=MM)
|
||||
yield go.Line(x1, -y1, x2, -y2, aperture=aperture, unit=MM)
|
||||
|
||||
if self.fill == Atom.solid:
|
||||
yield go.Region([(pt.x, pt.y) for pt in self.pts.xy], unit=MM)
|
||||
yield go.Region([(pt.x, -pt.y) for pt in self.pts.xy], unit=MM)
|
||||
|
||||
|
||||
@sexp_type('fp_curve')
|
||||
|
@ -449,7 +449,7 @@ class Pad:
|
|||
else:
|
||||
aperture = self.aperture(margin)
|
||||
|
||||
yield go.Flash(self.at.x+ox, self.at.y+oy, aperture, unit=MM)
|
||||
yield go.Flash(self.at.x+ox, -(self.at.y+oy), aperture, unit=MM)
|
||||
|
||||
def aperture(self, margin=None):
|
||||
rotation = math.radians(self.at.rotation)
|
||||
|
@ -581,14 +581,14 @@ class Pad:
|
|||
dy = 0
|
||||
|
||||
aperture = ap.ExcellonTool(min(dia, w), plated=plated, unit=MM)
|
||||
l = go.Line(ox-dx, oy-dy, ox+dx, oy+dy, aperture=aperture, unit=MM)
|
||||
l = go.Line(ox-dx, -(oy-dy), ox+dx, -(oy+dy), aperture=aperture, unit=MM)
|
||||
l.rotate(math.radians(self.at.rotation))
|
||||
l.offset(self.at.x, self.at.y)
|
||||
l.offset(self.at.x, -self.at.y)
|
||||
yield l
|
||||
|
||||
else:
|
||||
aperture = ap.ExcellonTool(self.drill.diameter, plated=plated, unit=MM)
|
||||
yield go.Flash(self.at.x, self.at.y, aperture=aperture, unit=MM)
|
||||
yield go.Flash(self.at.x, -self.at.y, aperture=aperture, unit=MM)
|
||||
|
||||
|
||||
@sexp_type('model')
|
||||
|
@ -907,7 +907,7 @@ class Footprint:
|
|||
|
||||
for fe in obj.render(variables=variables):
|
||||
fe.rotate(rotation)
|
||||
fe.offset(x, y, MM)
|
||||
fe.offset(x, -y, MM)
|
||||
layer_stack[layer].objects.append(fe)
|
||||
|
||||
for obj in self.pads:
|
||||
|
@ -939,7 +939,7 @@ class Footprint:
|
|||
|
||||
for fe in obj.render(margin=margin, cache=cache):
|
||||
fe.rotate(rotation)
|
||||
fe.offset(x, y, MM)
|
||||
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)
|
||||
|
@ -947,7 +947,7 @@ class Footprint:
|
|||
for obj in self.pads:
|
||||
for fe in obj.render_drill():
|
||||
fe.rotate(rotation)
|
||||
fe.offset(x, y, MM)
|
||||
fe.offset(x, -y, MM)
|
||||
|
||||
if obj.type == Atom.np_thru_hole:
|
||||
layer_stack.drill_npth.append(fe)
|
||||
|
|
|
@ -53,7 +53,7 @@ class TextBox:
|
|||
raise ValueError('Vector font text with empty render cache')
|
||||
|
||||
for poly in render_cache.polygons:
|
||||
reg = go.Region([(p.x, p.y) for p in poly.pts.xy], unit=MM)
|
||||
reg = go.Region([(p.x, -p.y) for p in poly.pts.xy], unit=MM)
|
||||
|
||||
if self.stroke:
|
||||
if self.stroke.type not in (None, Atom.default, Atom.solid):
|
||||
|
@ -91,7 +91,7 @@ class Line:
|
|||
dasher.line(self.end.x, self.end.y)
|
||||
|
||||
for x1, y1, x2, y2 in dasher:
|
||||
yield go.Line(x1, y1, x2, y2, aperture=ap.CircleAperture(dasher.width, unit=MM), unit=MM)
|
||||
yield go.Line(x1, -y1, x2, -y2, aperture=ap.CircleAperture(dasher.width, unit=MM), unit=MM)
|
||||
# FIXME render all primitives using dasher, maybe share code w/ fp_ prefix primitives
|
||||
|
||||
def offset(self, x=0, y=0):
|
||||
|
@ -105,11 +105,11 @@ class FillMode:
|
|||
fill: AtomChoice(Atom.solid, Atom.yes, Atom.no, Atom.none) = False
|
||||
|
||||
@classmethod
|
||||
def __map__(self, obj, parent=None):
|
||||
def __map__(kls, obj, parent=None):
|
||||
return obj[1] in (Atom.solid, Atom.yes)
|
||||
|
||||
@classmethod
|
||||
def __sexp__(self, value):
|
||||
def __sexp__(kls, value):
|
||||
yield [Atom.fill, Atom.solid if value else Atom.none]
|
||||
|
||||
@sexp_type('gr_rect')
|
||||
|
@ -123,8 +123,8 @@ class Rectangle:
|
|||
tstamp: Timestamp = None
|
||||
|
||||
def render(self, variables=None):
|
||||
rect = go.Region.from_rectangle(self.start.x, self.start.y,
|
||||
self.end.x-self.start.x, self.end.y-self.start.y,
|
||||
rect = go.Region.from_rectangle(self.start.x, -self.start.y,
|
||||
self.end.x-self.start.x, -(self.end.y-self.start.y),
|
||||
unit=MM)
|
||||
|
||||
if self.fill:
|
||||
|
@ -155,9 +155,9 @@ class Circle:
|
|||
tstamp: Timestamp = None
|
||||
|
||||
def render(self, variables=None):
|
||||
r = math.dist((self.center.x, self.center.y), (self.end.x, self.end.y))
|
||||
r = math.dist((self.center.x, -self.center.y), (self.end.x, -self.end.y))
|
||||
aperture = ap.CircleAperture(self.width or 0, unit=MM)
|
||||
arc = go.Arc.from_circle(self.center.x, self.center.y, r, aperture=aperture, unit=MM)
|
||||
arc = go.Arc.from_circle(self.center.x, -self.center.y, r, aperture=aperture, unit=MM)
|
||||
|
||||
if self.width:
|
||||
# FIXME stroke support
|
||||
|
@ -186,8 +186,11 @@ class Arc:
|
|||
def __post_init__(self):
|
||||
self.start = XYCoord(self.start)
|
||||
self.end = XYCoord(self.end)
|
||||
self.mid = XYCoord(self.mid) if self.mid else center_arc_to_kicad_mid(XYCoord(self.center), self.start, self.end)
|
||||
self.center = None
|
||||
if self.mid or self.center is None:
|
||||
self.mid = XYCoord(self.mid)
|
||||
elif self.center:
|
||||
self.mid = center_arc_to_kicad_mid(XYCoord(self.center), self.start, self.end)
|
||||
self.center = None
|
||||
|
||||
def rotate(self, angle, cx=None, cy=None):
|
||||
self.start.x, self.start.y = rotate_point(self.start.x, self.start.y, angle, cx, cy)
|
||||
|
@ -203,7 +206,7 @@ class Arc:
|
|||
cx, cy = self.mid.x, self.mid.y
|
||||
x1, y1 = self.start.x, self.start.y
|
||||
x2, y2 = self.end.x, self.end.y
|
||||
yield go.Arc(x1, y1, x2, y2, cx-x1, cy-y1, aperture=aperture, clockwise=True, unit=MM)
|
||||
yield go.Arc(x1, -y1, x2, -y2, cx-x1, -(cy-y1), aperture=aperture, clockwise=True, unit=MM)
|
||||
|
||||
def offset(self, x=0, y=0):
|
||||
self.start = self.start.with_offset(x, y)
|
||||
|
@ -213,7 +216,7 @@ class Arc:
|
|||
|
||||
@sexp_type('gr_poly')
|
||||
class Polygon:
|
||||
pts: PointList = field(default_factory=PointList)
|
||||
pts: ArcPointList = field(default_factory=list)
|
||||
layer: Named(str) = None
|
||||
width: Named(float) = None
|
||||
stroke: Stroke = field(default_factory=Stroke)
|
||||
|
@ -221,7 +224,7 @@ class Polygon:
|
|||
tstamp: Timestamp = None
|
||||
|
||||
def render(self, variables=None):
|
||||
reg = go.Region([(pt.x, pt.y) for pt in self.pts.xy], unit=MM)
|
||||
reg = go.Region([(pt.x, -pt.y) for pt in self.pts.xy], unit=MM)
|
||||
|
||||
# FIXME stroke support
|
||||
if self.width and self.width >= 0.005 or self.stroke.width and self.stroke.width > 0.005:
|
||||
|
|
|
@ -180,7 +180,7 @@ class TrackSegment:
|
|||
return
|
||||
|
||||
aperture = ap.CircleAperture(self.width, unit=MM)
|
||||
yield go.Line(self.start.x, self.start.y, self.end.x, self.end.y, aperture=aperture, unit=MM)
|
||||
yield go.Line(self.start.x, -self.start.y, self.end.x, -self.end.y, aperture=aperture, unit=MM)
|
||||
|
||||
def rotate(self, angle, cx=None, cy=None):
|
||||
if cx is None or cy is None:
|
||||
|
@ -225,7 +225,7 @@ class TrackArc:
|
|||
cx, cy = self.mid.x, self.mid.y
|
||||
x1, y1 = self.start.x, self.start.y
|
||||
x2, y2 = self.end.x, self.end.y
|
||||
yield go.Arc(x1, y1, x2, y2, cx-x1, cy-y1, aperture=aperture, clockwise=True, unit=MM)
|
||||
yield go.Arc(x1, -y1, x2, -y2, cx-x1, -(cy-y1), aperture=aperture, clockwise=True, unit=MM)
|
||||
|
||||
def rotate(self, angle, cx=None, cy=None):
|
||||
self.start.x, self.start.y = rotate_point(self.start.x, self.start.y, angle, cx, cy)
|
||||
|
@ -287,11 +287,11 @@ class Via:
|
|||
|
||||
def render_drill(self):
|
||||
aperture = ap.ExcellonTool(self.drill, plated=True, unit=MM)
|
||||
yield go.Flash(self.at.x, self.at.y, aperture=aperture, unit=MM)
|
||||
yield go.Flash(self.at.x, -self.at.y, aperture=aperture, unit=MM)
|
||||
|
||||
def render(self, variables=None, cache=None):
|
||||
aperture = ap.CircleAperture(self.size, unit=MM)
|
||||
yield go.Flash(self.at.x, self.at.y, aperture, unit=MM)
|
||||
yield go.Flash(self.at.x, -self.at.y, aperture, unit=MM)
|
||||
|
||||
def rotate(self, angle, cx=None, cy=None):
|
||||
if cx is None or cy is None:
|
||||
|
@ -763,7 +763,7 @@ class Board:
|
|||
|
||||
for fe in obj.render(variables=variables):
|
||||
fe.rotate(rotation)
|
||||
fe.offset(x, y, MM)
|
||||
fe.offset(x, -y, MM)
|
||||
layer_stack[layer].objects.append(fe)
|
||||
|
||||
for obj in self.vias:
|
||||
|
@ -771,13 +771,13 @@ class Board:
|
|||
for layer in fnmatch.filter(layer_map, glob):
|
||||
for fe in obj.render(cache=cache):
|
||||
fe.rotate(rotation)
|
||||
fe.offset(x, y, MM)
|
||||
fe.offset(x, -y, MM)
|
||||
fe.aperture = fe.aperture.rotated(rotation)
|
||||
layer_stack[layer_map[layer]].objects.append(fe)
|
||||
|
||||
for fe in obj.render_drill():
|
||||
fe.rotate(rotation)
|
||||
fe.offset(x, y, MM)
|
||||
fe.offset(x, -y, MM)
|
||||
layer_stack.drill_pth.append(fe)
|
||||
|
||||
def bounding_box(self, unit=MM):
|
||||
|
|
|
@ -159,6 +159,8 @@ class Rename(WrapperType):
|
|||
def __bind_field__(self, field):
|
||||
if self.name_atom is None:
|
||||
self.name_atom = Atom(field.name)
|
||||
if hasattr(self.next_type, '__bind_field__'):
|
||||
self.next_type.__bind_field__(field)
|
||||
|
||||
def __map__(self, obj, parent=None):
|
||||
return map_sexp(self.next_type, obj, parent=parent)
|
||||
|
@ -229,106 +231,6 @@ class Untagged(WrapperType):
|
|||
_tag, *rest = inner
|
||||
yield rest
|
||||
|
||||
|
||||
class List(WrapperType):
|
||||
def __bind_field__(self, field):
|
||||
self.attr = field.name
|
||||
|
||||
def __map__(self, value, parent):
|
||||
l = getattr(parent, self.attr, [])
|
||||
mapped = map_sexp(self.next_type, value, parent=parent)
|
||||
l.append(mapped)
|
||||
setattr(parent, self.attr, l)
|
||||
|
||||
def __sexp__(self, value):
|
||||
for elem in value:
|
||||
yield from sexp(self.next_type, elem)
|
||||
|
||||
|
||||
class _SexpTemplate:
|
||||
@staticmethod
|
||||
def __atoms__(kls):
|
||||
return [kls.name_atom]
|
||||
|
||||
@staticmethod
|
||||
def __map__(kls, value, *args, parent=None, **kwargs):
|
||||
positional = iter(kls.positional)
|
||||
inst = kls(*args, **kwargs)
|
||||
|
||||
for v in value[1:]: # skip key
|
||||
if isinstance(v, Atom) and v in kls.keys:
|
||||
name, etype = kls.keys[v]
|
||||
mapped = map_sexp(etype, [v], parent=inst)
|
||||
if mapped is not None:
|
||||
setattr(inst, name, mapped)
|
||||
|
||||
elif isinstance(v, list):
|
||||
name, etype = kls.keys[v[0]]
|
||||
mapped = map_sexp(etype, v, parent=inst)
|
||||
if mapped is not None:
|
||||
setattr(inst, name, mapped)
|
||||
|
||||
else:
|
||||
try:
|
||||
pos_key = next(positional)
|
||||
setattr(inst, pos_key.name, v)
|
||||
except StopIteration:
|
||||
raise TypeError(f'Unhandled positional argument {v!r} while parsing {kls}')
|
||||
|
||||
getattr(inst, '__after_parse__', lambda x: None)(parent)
|
||||
return inst
|
||||
|
||||
@staticmethod
|
||||
def __sexp__(kls, value):
|
||||
getattr(value, '__before_sexp__', lambda: None)()
|
||||
|
||||
out = [kls.name_atom]
|
||||
for f in fields(kls):
|
||||
if f.type is SEXP_END:
|
||||
break
|
||||
out += sexp(f.type, getattr(value, f.name))
|
||||
yield out
|
||||
|
||||
@staticmethod
|
||||
def parse(kls, data, *args, **kwargs):
|
||||
return kls.__map__(parse_sexp(data), *args, **kwargs)
|
||||
|
||||
@staticmethod
|
||||
def sexp(self):
|
||||
return next(self.__sexp__(self))
|
||||
|
||||
|
||||
def sexp_type(name=None):
|
||||
def register(cls):
|
||||
cls = dataclass(cls)
|
||||
cls.name_atom = Atom(name) if name is not None else None
|
||||
for key in '__sexp__', '__map__', '__atoms__', 'parse':
|
||||
if not hasattr(cls, key):
|
||||
setattr(cls, key, classmethod(getattr(_SexpTemplate, key)))
|
||||
|
||||
if not hasattr(cls, 'sexp'):
|
||||
setattr(cls, 'sexp', getattr(_SexpTemplate, 'sexp'))
|
||||
cls.positional = []
|
||||
cls.keys = {}
|
||||
for f in fields(cls):
|
||||
f_type = f.type
|
||||
if f_type is SEXP_END:
|
||||
break
|
||||
|
||||
if hasattr(f_type, '__bind_field__'):
|
||||
f_type.__bind_field__(f)
|
||||
|
||||
atoms = getattr(f_type, '__atoms__', lambda: [])
|
||||
atoms = list(atoms())
|
||||
for atom in atoms:
|
||||
cls.keys[atom] = (f.name, f_type)
|
||||
if not atoms:
|
||||
cls.positional.append(f)
|
||||
|
||||
return cls
|
||||
return register
|
||||
|
||||
|
||||
class List(WrapperType):
|
||||
def __bind_field__(self, field):
|
||||
self.attr = field.name
|
||||
|
@ -406,7 +308,6 @@ class _SexpTemplate:
|
|||
# those from being called more than once on the same object.
|
||||
return replace(self, **{f.name: copy.copy(getattr(self, f.name)) for f in fields(self) if not f.kw_only and hasattr(f.type, '__before_sexp__')})
|
||||
|
||||
|
||||
def sexp_type(name=None):
|
||||
def register(cls):
|
||||
cls = dataclass(cls)
|
||||
|
|
|
@ -501,7 +501,7 @@ class Symbol:
|
|||
power: Wrap(Flag()) = False
|
||||
pin_numbers: OmitDefault(PinNumberSpec) = field(default_factory=PinNumberSpec)
|
||||
pin_names: OmitDefault(PinNameSpec) = field(default_factory=PinNameSpec)
|
||||
exclude_from_sim: Named(YesNoAtom()) = False
|
||||
exclude_from_sim: OmitDefault(Named(YesNoAtom())) = False
|
||||
in_bom: Named(YesNoAtom()) = True
|
||||
on_board: Named(YesNoAtom()) = True
|
||||
properties: List(Property) = field(default_factory=list)
|
||||
|
|
|
@ -165,11 +165,10 @@ class ArcPoly(GraphicPrimitive):
|
|||
|
||||
yield f'M {float(self.outline[0][0]):.6} {float(self.outline[0][1]):.6}'
|
||||
|
||||
for old, new, arc in self.segments:
|
||||
if not arc:
|
||||
for old, new, (clockwise, center) in self.segments:
|
||||
if clockwise is None:
|
||||
yield f'L {float(new[0]):.6} {float(new[1]):.6}'
|
||||
else:
|
||||
clockwise, center = arc
|
||||
yield svg_arc(old, new, center, clockwise)
|
||||
|
||||
def to_svg(self, fg='black', bg='white', tag=Tag):
|
||||
|
|
|
@ -736,7 +736,7 @@ class LayerStack:
|
|||
tags = []
|
||||
layer_transform = f'translate(0 {bounds[0][1] + bounds[1][1]}) scale(1 -1)'
|
||||
for (side, use), layer in reversed(self.graphic_layers.items()):
|
||||
if re.match(side_re, side) and (fg := colors.get(f'{side} {use}')):
|
||||
if re.fullmatch(side_re, side) and (fg := colors.get(f'{side} {use}')):
|
||||
tags.append(tag('g', list(layer.svg_objects(svg_unit=svg_unit, fg=fg, bg="white", tag=Tag)),
|
||||
**stroke_attrs, id=f'l-{side}-{use}', transform=layer_transform))
|
||||
|
||||
|
|
|
@ -194,9 +194,9 @@ def test_render(kicad_mod_file, tmpfile, print_on_error):
|
|||
stack.add_layer('mechanical drawings')
|
||||
stack.add_layer('mechanical comments')
|
||||
fp.render(stack)
|
||||
color_map = {gn_id: KICAD_LAYER_COLORS[kicad_id] for gn_id, kicad_id in LAYER_MAP_G2K.items()}
|
||||
color_map[('drill', 'pth')] = (255, 255, 255, 1)
|
||||
color_map[('drill', 'npth')] = (255, 255, 255, 1)
|
||||
color_map = {f'{side} {use}': KICAD_LAYER_COLORS[kicad_id] for (side, use), kicad_id in LAYER_MAP_G2K.items()}
|
||||
color_map['drill pth'] = (255, 255, 255, 1)
|
||||
color_map['drill npth'] = (255, 255, 255, 1)
|
||||
# Remove alpha since overlaid shapes won't work correctly with non-1 alpha without complicated svg filter hacks
|
||||
color_map = {key: (f'#{r:02x}{g:02x}{b:02x}', '1') for key, (r, g, b, _a) in color_map.items()}
|
||||
|
||||
|
@ -223,7 +223,7 @@ def test_render(kicad_mod_file, tmpfile, print_on_error):
|
|||
print_on_error('Gerbonara bounds:', bounds, f'w={w:.6f}', f'h={h:.6f}')
|
||||
|
||||
out_svg = tmpfile('Output', '.svg')
|
||||
out_svg.write_text(str(stack.to_svg(color_map=color_map, force_bounds=bounds, margin=margin)))
|
||||
out_svg.write_text(str(stack.to_svg(colors=color_map, force_bounds=bounds, margin=margin)))
|
||||
|
||||
print_on_error('Input footprint:', kicad_mod_file)
|
||||
ref_svg = tmpfile('Reference render', '.svg')
|
||||
|
|
|
@ -530,7 +530,7 @@ def svg_arc(old, new, center, clockwise):
|
|||
f'A {r:.6} {r:.6} 0 1 {sweep_flag} {float(new[0]):.6} {float(new[1]):.6}'
|
||||
|
||||
else: # normal case
|
||||
d = point_line_distance(old, new, center[0], center[1])
|
||||
d = point_line_distance(old, new, (center[0], center[1]))
|
||||
large_arc = int((d < 0) == clockwise)
|
||||
return f'A {r:.6} {r:.6} 0 {large_arc} {sweep_flag} {float(new[0]):.6} {float(new[1]):.6}'
|
||||
|
||||
|
|
Ładowanie…
Reference in New Issue