diff options
Diffstat (limited to 'gerbonara/cad')
-rw-r--r-- | gerbonara/cad/kicad/base_types.py | 17 | ||||
-rw-r--r-- | gerbonara/cad/kicad/footprints.py | 29 | ||||
-rw-r--r-- | gerbonara/cad/kicad/graphical_primitives.py | 22 | ||||
-rw-r--r-- | gerbonara/cad/kicad/primitives.py | 25 | ||||
-rw-r--r-- | gerbonara/cad/kicad/symbols.py | 27 |
5 files changed, 78 insertions, 42 deletions
diff --git a/gerbonara/cad/kicad/base_types.py b/gerbonara/cad/kicad/base_types.py index 32717fb..1161996 100644 --- a/gerbonara/cad/kicad/base_types.py +++ b/gerbonara/cad/kicad/base_types.py @@ -97,7 +97,8 @@ class Stroke: class Dasher: def __init__(self, obj): if obj.stroke: - w, t = obj.stroke.width or 0.254, obj.stroke.type + w = obj.stroke.width if obj.stroke.width is not None else 0.254 + t = obj.stroke.type else: w = obj.width or 0 t = Atom.solid @@ -210,6 +211,20 @@ class XYCoord: else: self.x, self.y = x, y + def __iter__(self): + return iter((self.x, self.y)) + + def __getitem__(self, index): + return (self.x, self.y)[index] + + def __setitem__(self, index, value): + if index == 0: + self.x = value + elif index == 1: + self.y = value + else: + raise IndexError(f'Invalid 2D point coordinate index {index}') + def within_distance(self, x, y, dist): return math.dist((x, y), (self.x, self.y)) < dist diff --git a/gerbonara/cad/kicad/footprints.py b/gerbonara/cad/kicad/footprints.py index b24e004..9debaa9 100644 --- a/gerbonara/cad/kicad/footprints.py +++ b/gerbonara/cad/kicad/footprints.py @@ -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=True, aperture=aperture, unit=MM) if dasher.solid: yield arc @@ -249,13 +249,14 @@ 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) + if dasher.width > 0: + 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) if self.fill == Atom.solid: yield go.Region([(pt.x, -pt.y) for pt in self.pts.xy], unit=MM) @@ -466,10 +467,10 @@ class Pad: 0, 0, # no hole rotation), unit=MM) else: - return ap.RectangleAperture(self.size.x+2*margin, self.size.y+2*margin, unit=MM).rotated(rotation) + return ap.RectangleAperture(self.size.x+2*margin, self.size.y+2*margin, unit=MM).rotated(-rotation) elif self.shape == Atom.oval: - return ap.ObroundAperture(self.size.x+2*margin, self.size.y+2*margin, unit=MM).rotated(rotation) + return ap.ObroundAperture(self.size.x+2*margin, self.size.y+2*margin, unit=MM).rotated(-rotation) elif self.shape == Atom.trapezoid: # KiCad's trapezoid aperture "rect_delta" param is just weird to the point that I think it's probably @@ -495,14 +496,14 @@ class Pad: (x+dy+2*margin*math.cos(alpha), y+2*margin, 2*dy, 0, 0, # no hole - rotation), unit=MM) + -rotation + math.pi), unit=MM) else: return ap.ApertureMacroInstance(GenericMacros.rounded_isosceles_trapezoid, (x+dy, y, 2*dy, margin, 0, 0, # no hole - rotation), unit=MM) + -rotation + math.pi), unit=MM) elif self.shape == Atom.roundrect: x, y = self.size.x, self.size.y @@ -514,7 +515,7 @@ class Pad: 0, 0, # no hole rotation), unit=MM) else: - return ap.RectangleAperture(x+margin, y+margin, unit=MM).rotated(rotation) + return ap.RectangleAperture(x+margin, y+margin, unit=MM).rotated(-rotation) elif self.shape == Atom.custom: primitives = [] @@ -556,7 +557,7 @@ class Pad: elif self.options.anchor == Atom.circle and self.size.x > 0: primitives.append(amp.Circle(MM, 1, self.size.x+2*margin, 0, 0, 0)) - macro = ApertureMacro(primitives=tuple(primitives)).rotated(rotation) + macro = ApertureMacro(primitives=tuple(primitives)).rotated(-rotation) return ap.ApertureMacroInstance(macro, unit=MM) def render_drill(self): @@ -881,7 +882,7 @@ class Footprint: for text in self.texts: text.at.rotation = (text.at.rotation + delta) % 360 - def objects(self, text=False, pads=True, groups=True): + def objects(self, text=False, pads=True, groups=True, zones=True): return chain( (self.texts if text else []), (self.text_boxes if text else []), @@ -893,7 +894,7 @@ class Footprint: self.curves, (self.dimensions if text else []), (self.pads if pads else []), - self.zones, + (self.zones if zones else []), self.groups if groups else []) def render(self, layer_stack, layer_map, x=0, y=0, rotation=0, text=False, flip=False, variables={}, cache=None): @@ -901,7 +902,7 @@ class Footprint: y += self.at.y rotation += math.radians(self.at.rotation) - for obj in self.objects(pads=False, text=text): + for obj in self.objects(pads=False, text=text, zones=False): if not (layer := layer_map.get(obj.layer)): continue diff --git a/gerbonara/cad/kicad/graphical_primitives.py b/gerbonara/cad/kicad/graphical_primitives.py index a2393d2..94a61a4 100644 --- a/gerbonara/cad/kicad/graphical_primitives.py +++ b/gerbonara/cad/kicad/graphical_primitives.py @@ -203,10 +203,10 @@ class Arc: return aperture = ap.CircleAperture(self.width, unit=MM) - 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) + (cx, cy), _r = kicad_mid_to_center_arc(self.mid, self.start, self.end) + yield go.Arc(x1, -y1, x2, -y2, cx-x1, -(cy-y1), aperture=aperture, clockwise=False, unit=MM) def offset(self, x=0, y=0): self.start = self.start.with_offset(x, y) @@ -224,7 +224,23 @@ 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) + points = [] + centers = [] + for point_or_arc in self.pts: + if points: + centers.append((None, (None, None))) + + if isinstance(point_or_arc, XYCoord): + points.append((point_or_arc.x, -point_or_arc.y)) + + else: # base_types.Arc + points.append((point_or_arc.start.x, -point_or_arc.start.y)) + points.append((point_or_arc.end.x, -point_or_arc.end.y)) + (cx, cy), _r = kicad_mid_to_center_arc(point_or_arc.mid, point_or_arc.start, point_or_arc.end) + centers.append((False, (cx, -cy))) + + reg = go.Region(points, centers, unit=MM) + reg.close() # FIXME stroke support if self.width and self.width >= 0.005 or self.stroke.width and self.stroke.width > 0.005: diff --git a/gerbonara/cad/kicad/primitives.py b/gerbonara/cad/kicad/primitives.py index 74ce4e4..fa55568 100644 --- a/gerbonara/cad/kicad/primitives.py +++ b/gerbonara/cad/kicad/primitives.py @@ -59,6 +59,31 @@ def center_arc_to_kicad_mid(center, start, end): return XYCoord(mx, my) +def kicad_mid_to_center_arc(mid, start, end): + """ Convert kicad's slightly insane midpoint notation to standrad center/p1/p2 notation. + + Returns the center and radius of the circle passing the given 3 points. + In case the 3 points form a line, raises a ValueError. + """ + # https://stackoverflow.com/questions/28910718/give-3-points-and-a-plot-circle + p1, p2, p3 = start, mid, end + + temp = p2[0] * p2[0] + p2[1] * p2[1] + bc = (p1[0] * p1[0] + p1[1] * p1[1] - temp) / 2 + cd = (temp - p3[0] * p3[0] - p3[1] * p3[1]) / 2 + det = (p1[0] - p2[0]) * (p2[1] - p3[1]) - (p2[0] - p3[0]) * (p1[1] - p2[1]) + + if abs(det) < 1.0e-6: + raise ValueError() + + # Center of circle + cx = (bc*(p2[1] - p3[1]) - cd*(p1[1] - p2[1])) / det + cy = ((p1[0] - p2[0]) * cd - (p2[0] - p3[0]) * bc) / det + + radius = math.sqrt((cx - p1[0])**2 + (cy - p1[1])**2) + return ((cx, cy), radius) + + @sexp_type('hatch') class Hatch: style: AtomChoice(Atom.none, Atom.edge, Atom.full) = Atom.edge diff --git a/gerbonara/cad/kicad/symbols.py b/gerbonara/cad/kicad/symbols.py index baa77bb..ed93f7b 100644 --- a/gerbonara/cad/kicad/symbols.py +++ b/gerbonara/cad/kicad/symbols.py @@ -20,6 +20,7 @@ from .base_types import * from ...utils import rotate_point, Tag, arc_bounds from ...newstroke import Newstroke from .schematic_colors import * +from .primitives import center_arc_to_kicad_mid PIN_ETYPE = AtomChoice(Atom.input, Atom.output, Atom.bidirectional, Atom.tri_state, Atom.passive, Atom.free, @@ -249,28 +250,6 @@ class Circle: **self.stroke.svg_attrs(colorscheme.lines)) -# https://stackoverflow.com/questions/28910718/give-3-points-and-a-plot-circle -def define_circle(p1, p2, p3): - """ - Returns the center and radius of the circle passing the given 3 points. - In case the 3 points form a line, raises a ValueError. - """ - temp = p2[0] * p2[0] + p2[1] * p2[1] - bc = (p1[0] * p1[0] + p1[1] * p1[1] - temp) / 2 - cd = (temp - p3[0] * p3[0] - p3[1] * p3[1]) / 2 - det = (p1[0] - p2[0]) * (p2[1] - p3[1]) - (p2[0] - p3[0]) * (p1[1] - p2[1]) - - if abs(det) < 1.0e-6: - raise ValueError() - - # Center of circle - cx = (bc*(p2[1] - p3[1]) - cd*(p1[1] - p2[1])) / det - cy = ((p1[0] - p2[0]) * cd - (p2[0] - p3[0]) * bc) / det - - radius = math.sqrt((cx - p1[0])**2 + (cy - p1[1])**2) - return ((cx, cy), radius) - - @sexp_type('arc') class Arc: start: Rename(XYCoord) = field(default_factory=XYCoord) @@ -280,7 +259,7 @@ class Arc: fill: Fill = field(default_factory=Fill) def bounding_box(self, default=None): - (cx, cy), r = define_circle((self.start.x, self.start.y), (self.mid.x, self.mid.y), (self.end.x, self.end.y)) + (cx, cy), r = center_arc_to_kicad_mid(self.mid, self.start, self.end) x1, y1 = self.start.x, self.start.y x2, y2 = self.mid.x-x1, self.mid.y-x2 x3, y3 = (self.end.x - x1)/2, (self.end.y - y1)/2 @@ -289,7 +268,7 @@ class Arc: def to_svg(self, colorscheme=Colorscheme.KiCad): - (cx, cy), r = define_circle((self.start.x, self.start.y), (self.mid.x, self.mid.y), (self.end.x, self.end.y)) + (cx, cy), r = center_arc_to_kicad_mid(self.mid, self.start, self.end) x1r = self.start.x - cx y1r = self.start.y - cy |