summaryrefslogtreecommitdiff
path: root/gerbonara/gerber/graphic_primitives.py
diff options
context:
space:
mode:
Diffstat (limited to 'gerbonara/gerber/graphic_primitives.py')
-rw-r--r--gerbonara/gerber/graphic_primitives.py140
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) ])
+