diff options
Diffstat (limited to 'gerbonara/gerber/graphic_primitives.py')
-rw-r--r-- | gerbonara/gerber/graphic_primitives.py | 140 |
1 files changed, 140 insertions, 0 deletions
diff --git a/gerbonara/gerber/graphic_primitives.py b/gerbonara/gerber/graphic_primitives.py new file mode 100644 index 0000000..391a452 --- /dev/null +++ b/gerbonara/gerber/graphic_primitives.py @@ -0,0 +1,140 @@ + +import math +import itertools + +from dataclasses import dataclass, KW_ONLY, replace + +from gerber_statements import * + + +class GraphicPrimitive: + _ : KW_ONLY + polarity_dark : bool = True + + +def rotate_point(x, y, angle, cx=None, cy=None): + if cx is None: + return (x, y) + else: + return (cx + (x - cx) * math.cos(angle) - (y - cy) * math.sin(angle), + cy + (x - cx) * math.sin(angle) + (y - cy) * math.cos(angle)) + + +@dataclass +class Circle(GraphicPrimitive): + x : float + y : float + r : float # Here, we use radius as common in modern computer graphics, not diameter as gerber uses. + + def bounds(self): + return ((self.x-self.r, self.y-self.r), (self.x+self.r, self.y+self.r)) + + +@dataclass +class Obround(GraphicPrimitive): + x : float + y : float + w : float + h : float + rotation : float # radians! + + def decompose(self): + ''' decompose obround to two circles and one rectangle ''' + + cx = self.x + self.w/2 + cy = self.y + self.h/2 + + if self.w > self.h: + x = self.x + self.h/2 + yield Circle(x, cy, self.h/2) + yield Circle(x + self.w, cy, self.h/2) + yield Rectangle(x, self.y, self.w - self.h, self.h) + + elif self.h > self.w: + y = self.y + self.w/2 + yield Circle(cx, y, self.w/2) + yield Circle(cx, y + self.h, self.w/2) + yield Rectangle(self.x, y, self.w, self.h - self.w) + + else: + yield Circle(cx, cy, self.w/2) + + def bounds(self): + return ((self.x-self.w/2, self.y-self.h/2), (self.x+self.w/2, self.y+self.h/2)) + + +@dataclass +class ArcPoly(GraphicPrimitive): + """ Polygon whose sides may be either straight lines or circular arcs """ + + # list of (x : float, y : float) tuples. Describes closed outline, i.e. first and last point are considered + # connected. + outline : list(tuple(float)) + # list of radii of segments, must be either None (all segments are straight lines) or same length as outline. + # Straight line segments have None entry. + arc_centers : list(tuple(float)) + + @property + def segments(self): + return itertools.zip_longest(self.outline[:-1], self.outline[1:], self.radii or []) + + def bounds(self): + for (x1, y1), (x2, y2), radius in self.segments: + return + + +@dataclass +class Line(GraphicPrimitive): + x1 : float + y1 : float + x2 : float + y2 : float + width : float + + # FIXME bounds + +@dataclass +class Arc(GraphicPrimitive): + x : float + y : float + r : float + angle1 : float # radians! + angle2 : float # radians! + width : float + + # FIXME bounds + +@dataclass +class Rectangle(GraphicPrimitive): + # coordinates are center coordinates + x : float + y : float + w : float + h : float + rotation : float # radians, around center! + + def bounds(self): + return ((self.x, self.y), (self.x+self.w, self.y+self.h)) + + @prorperty + def center(self): + return self.x + self.w/2, self.y + self.h/2 + + +class RegularPolygon(GraphicPrimitive): + x : float + y : float + r : float + n : int + rotation : float # radians! + + def decompose(self): + ''' convert n-sided gerber polygon to normal Region defined by outline ''' + + delta = 2*math.pi / self.n + + yield Region([ + (self.x + math.cos(self.rotation + i*delta) * self.r, + self.y + math.sin(self.rotation + i*delta) * self.r) + for i in range(self.n) ]) + |