diff options
author | Hamilton Kibbe <hamilton.kibbe@gmail.com> | 2016-01-21 03:57:44 -0500 |
---|---|---|
committer | Garret Fick <garret@ficksworkshop.com> | 2016-09-24 02:28:36 +0800 |
commit | 5af19af190c1fb0f0c5be029d46d63e657dde4d9 (patch) | |
tree | dd60a0b21e1d1ca7258b9f978ce973354d96062c /gerber/primitives.py | |
parent | 8d5e782ccf220d77f0aad5a4e5605dc5cbe0f410 (diff) | |
download | gerbonara-5af19af190c1fb0f0c5be029d46d63e657dde4d9.tar.gz gerbonara-5af19af190c1fb0f0c5be029d46d63e657dde4d9.tar.bz2 gerbonara-5af19af190c1fb0f0c5be029d46d63e657dde4d9.zip |
Commit partial merge so I can work on the plane
Diffstat (limited to 'gerber/primitives.py')
-rw-r--r-- | gerber/primitives.py | 172 |
1 files changed, 94 insertions, 78 deletions
diff --git a/gerber/primitives.py b/gerber/primitives.py index d78c6d9..a291c26 100644 --- a/gerber/primitives.py +++ b/gerber/primitives.py @@ -16,11 +16,11 @@ # limitations under the License.
-from itertools import combinations
+ import math
from operator import add
-
-from .utils import validate_coordinates, inch, metric, convex_hull, rotate_point, nearly_equal
+from itertools import combinations
+from .utils import validate_coordinates, inch, metric, convex_hull, rotate_point, nearly_equal @@ -50,9 +50,9 @@ class Primitive(object): def __init__(self, level_polarity='dark', rotation=0, units=None, net_name=None):
self.level_polarity = level_polarity
- self.net_name = net_name
+ self.net_name = net_name self._to_convert = list() - self.id = id
+ self.id = id self._memoized = list()
self._units = units
self._rotation = rotation
@@ -60,18 +60,21 @@ class Primitive(object): self._sin_theta = math.sin(math.radians(rotation))
self._bounding_box = None
self._vertices = None
- self._segments = None
+ self._segments = None @property
def flashed(self):
'''Is this a flashed primitive'''
raise NotImplementedError('Is flashed must be '
- 'implemented in subclass')
+ 'implemented in subclass') + def __eq__(self, other):
+ return self.__dict__ == other.__dict__
+
@property
def units(self):
- return self._units
+ return self._units @units.setter
def units(self, value):
@@ -81,7 +84,7 @@ class Primitive(object): @property
def rotation(self):
return self._rotation
-
+ @rotation.setter
def rotation(self, value):
self._changed()
@@ -172,8 +175,8 @@ class Primitive(object): except:
if value is not None:
setattr(self, attr, metric(value))
-
- def offset(self, x_offset=0, y_offset=0): + + def offset(self, x_offset=0, y_offset=0):
""" Move the primitive by the specified x and y offset amount.
values are specified in the primitive's native units
@@ -183,10 +186,7 @@ class Primitive(object): self.position = tuple([coord + offset for coord, offset
in zip(self.position,
(x_offset, y_offset))])
-
- def __eq__(self, other):
- return self.__dict__ == other.__dict__
-
+
def to_statement(self):
pass
@@ -201,9 +201,8 @@ class Primitive(object): self._bounding_box = None
self._vertices = None
self._segments = None
- for attr in self._memoized:
- setattr(self, attr, None) -
+ for attr in self._memoized: + setattr(self, attr, None)
class Line(Primitive):
"""
@@ -238,7 +237,6 @@ class Line(Primitive): self._changed()
self._end = value
-
@property
def angle(self):
delta_x, delta_y = tuple(
@@ -246,7 +244,7 @@ class Line(Primitive): angle = math.atan2(delta_y, delta_x)
return angle
- @property
+ @property def bounding_box(self): if self._bounding_box is None:
if isinstance(self.aperture, Circle):
@@ -261,7 +259,7 @@ class Line(Primitive): max_y = max(self.start[1], self.end[1]) + height_2
self._bounding_box = ((min_x, max_x), (min_y, max_y))
return self._bounding_box
-
+
@property
def bounding_box_no_aperture(self):
'''Gets the bounding box without the aperture'''
@@ -293,13 +291,13 @@ class Line(Primitive): # The line is defined by the convex hull of the points
self._vertices = convex_hull((start_ll, start_lr, start_ul, start_ur, end_ll, end_lr, end_ul, end_ur))
return self._vertices
-
- def offset(self, x_offset=0, y_offset=0): + + def offset(self, x_offset=0, y_offset=0):
+ self._changed() self.start = tuple([coord + offset for coord, offset
in zip(self.start, (x_offset, y_offset))])
self.end = tuple([coord + offset for coord, offset
in zip(self.end, (x_offset, y_offset))])
- self._changed()
def equivalent(self, other, offset):
@@ -308,12 +306,14 @@ class Line(Primitive): equiv_start = tuple(map(add, other.start, offset))
equiv_end = tuple(map(add, other.end, offset)) + return nearly_equal(self.start, equiv_start) and nearly_equal(self.end, equiv_end)
class Arc(Primitive):
"""
- """ + """
+ def __init__(self, start, end, center, direction, aperture, quadrant_mode, **kwargs): super(Arc, self).__init__(**kwargs)
self._start = start
@@ -436,7 +436,7 @@ class Arc(Primitive): min_y = min(y) - self.aperture.radius
max_y = max(y) + self.aperture.radius
self._bounding_box = ((min_x, max_x), (min_y, max_y))
- return self._bounding_box
+ return self._bounding_box @property
def bounding_box_no_aperture(self):
@@ -488,12 +488,13 @@ class Arc(Primitive): class Circle(Primitive):
"""
- """ - def __init__(self, position, diameter, hole_diameter = None, **kwargs):
+ """
+ + def __init__(self, position, diameter, hole_diameter = None, **kwargs): super(Circle, self).__init__(**kwargs)
validate_coordinates(position)
self._position = position
- self._diameter = diameter
+ self._diameter = diameter self.hole_diameter = hole_diameter
self._to_convert = ['position', 'diameter', 'hole_diameter'] @@ -537,7 +538,7 @@ class Circle(Primitive): min_y = self.position[1] - self.radius
max_y = self.position[1] + self.radius
self._bounding_box = ((min_x, max_x), (min_y, max_y))
- return self._bounding_box
+ return self._bounding_box def offset(self, x_offset=0, y_offset=0):
self.position = tuple(map(add, self.position, (x_offset, y_offset)))
@@ -553,7 +554,7 @@ class Circle(Primitive): equiv_position = tuple(map(add, other.position, offset))
- return nearly_equal(self.position, equiv_position)
+ return nearly_equal(self.position, equiv_position) class Ellipse(Primitive):
@@ -575,7 +576,7 @@ class Ellipse(Primitive): @property
def position(self):
return self._position
-
+ @position.setter
def position(self, value):
self._changed()
@@ -625,18 +626,19 @@ class Ellipse(Primitive): class Rectangle(Primitive):
- """
+ """ When rotated, the rotation is about the center point.
Only aperture macro generated Rectangle objects can be rotated. If you aren't in a AMGroup,
then you don't need to worry about rotation
- """ - def __init__(self, position, width, height, hole_diameter=0, **kwargs):
+ """
+ + def __init__(self, position, width, height, hole_diameter=0, **kwargs): super(Rectangle, self).__init__(**kwargs)
validate_coordinates(position)
self._position = position
self._width = width
- self._height = height
+ self._height = height self.hole_diameter = hole_diameter
self._to_convert = ['position', 'width', 'height', 'hole_diameter']
# TODO These are probably wrong when rotated
@@ -656,14 +658,14 @@ class Rectangle(Primitive): self._changed()
self._position = value
- @property
+ @property def width(self):
return self._width
@width.setter
def width(self, value):
self._changed()
- self._width = value
+ self._width = value @property
def height(self):
@@ -685,7 +687,7 @@ class Rectangle(Primitive): def upper_right(self):
return (self.position[0] + (self._abs_width / 2.),
self.position[1] + (self._abs_height / 2.))
-
+
@property def lower_left(self):
return (self.position[0] - (self.axis_aligned_width / 2.),
@@ -765,7 +767,7 @@ class Diamond(Primitive): @position.setter
def position(self, value):
self._changed()
- self._position = value
+ self._position = value @property
def width(self):
@@ -776,7 +778,7 @@ class Diamond(Primitive): self._changed()
self._width = value
- @property
+ @property def height(self):
return self._height
@@ -950,7 +952,7 @@ class RoundRectangle(Primitive): @height.setter
def height(self, value):
self._changed()
- self._height = value
+ self._height = value @property
def radius(self):
@@ -985,21 +987,22 @@ class RoundRectangle(Primitive): return (self._cos_theta * self.width +
self._sin_theta * self.height)
- @property
+ @property def axis_aligned_height(self):
return (self._cos_theta * self.height +
self._sin_theta * self.width)
class Obround(Primitive):
- """
""" - def __init__(self, position, width, height, hole_diameter=0, **kwargs):
+ """
+ + def __init__(self, position, width, height, hole_diameter=0, **kwargs): super(Obround, self).__init__(**kwargs)
validate_coordinates(position)
self._position = position
self._width = width
- self._height = height
+ self._height = height self.hole_diameter = hole_diameter
self._to_convert = ['position', 'width', 'height', 'hole_diameter']
@@ -1014,7 +1017,7 @@ class Obround(Primitive): @position.setter
def position(self, value):
self._changed()
- self._position = value
+ self._position = value @property
def width(self):
@@ -1030,7 +1033,7 @@ class Obround(Primitive): return (self.position[0] + (self._abs_width / 2.),
self.position[1] + (self._abs_height / 2.))
- @property + @property
def height(self):
return self._height
@@ -1093,7 +1096,7 @@ class Obround(Primitive): class Polygon(Primitive):
- """
+ """ Polygon flash defined by a set number of sides.
""" def __init__(self, position, sides, radius, hole_diameter, **kwargs): @@ -1126,7 +1129,7 @@ class Polygon(Primitive): @position.setter
def position(self, value):
self._changed()
- self._position = value
+ self._position = value @property
def radius(self):
@@ -1162,6 +1165,18 @@ class Polygon(Primitive): return points
+ @property
+ def vertices(self):
+ if self._vertices is None:
+ theta = math.radians(360/self.sides)
+ vertices = [(self.position[0] + (math.cos(theta * side) * self.radius),
+ self.position[1] + (math.sin(theta * side) * self.radius))
+ for side in range(self.sides)]
+ self._vertices = [(((x * self._cos_theta) - (y * self._sin_theta)),
+ ((x * self._sin_theta) + (y * self._cos_theta)))
+ for x, y in vertices]
+ return self._vertices +
def equivalent(self, other, offset):
"""
Is this the outline the same as the other, ignoring the position offset?
@@ -1170,7 +1185,7 @@ class Polygon(Primitive): # Quick check if it even makes sense to compare them
if type(self) != type(other) or self.sides != other.sides or self.radius != other.radius:
return False
-
+
equiv_pos = tuple(map(add, other.position, offset))
return nearly_equal(self.position, equiv_pos)
@@ -1178,7 +1193,7 @@ class Polygon(Primitive): class AMGroup(Primitive):
"""
- """
+ """ def __init__(self, amprimitives, stmt = None, **kwargs):
"""
@@ -1281,6 +1296,7 @@ class Outline(Primitive): Outlines only exist as the rendering for a apeture macro outline.
They don't exist outside of AMGroup objects
"""
+ def __init__(self, primitives, **kwargs):
super(Outline, self).__init__(**kwargs)
self.primitives = primitives
@@ -1295,16 +1311,19 @@ class Outline(Primitive): @property
def bounding_box(self):
- xlims, ylims = zip(*[p.bounding_box for p in self.primitives])
- minx, maxx = zip(*xlims)
- miny, maxy = zip(*ylims)
- min_x = min(minx)
- max_x = max(maxx)
- min_y = min(miny)
- max_y = max(maxy)
- return ((min_x, max_x), (min_y, max_y))
+ if self._bounding_box is None:
+ xlims, ylims = zip(*[p.bounding_box for p in self.primitives])
+ minx, maxx = zip(*xlims)
+ miny, maxy = zip(*ylims)
+ min_x = min(minx)
+ max_x = max(maxx)
+ min_y = min(miny)
+ max_y = max(maxy)
+ self._bounding_box = ((min_x, max_x), (min_y, max_y))
+ return self._bounding_box
def offset(self, x_offset=0, y_offset=0):
+ self._changed()
for p in self.primitives:
p.offset(x_offset, y_offset)
@@ -1416,11 +1435,11 @@ class SquareButterfly(Primitive): self._to_convert = ['position', 'side']
# TODO This does not reset bounding box correctly
-
+ @property
def flashed(self):
return True -
+ @property
def bounding_box(self):
if self._bounding_box is None:
@@ -1456,9 +1475,10 @@ class Donut(Primitive): else:
# Hexagon
self.width = 0.5 * math.sqrt(3.) * outer_diameter
- self.height = outer_diameter
+ self.height = outer_diameter - self._to_convert = ['position', 'width', 'height', 'inner_diameter', 'outer_diameter']
+ self._to_convert = ['position', 'width',
+ 'height', 'inner_diameter', 'outer_diameter']
# TODO This does not reset bounding box correctly
@@ -1474,7 +1494,7 @@ class Donut(Primitive): @property
def upper_right(self):
return (self.position[0] + (self.width / 2.),
- self.position[1] + (self.height / 2.)) + self.position[1] + (self.height / 2.) @property
def bounding_box(self):
@@ -1526,11 +1546,13 @@ class Drill(Primitive): self.hit = hit
self._to_convert = ['position', 'diameter', 'hit']
+ # TODO Ths won't handle the hit updates correctly
+
@property
def flashed(self):
return False - @property
+ @property def position(self):
return self._position
@@ -1588,19 +1610,13 @@ class Slot(Primitive): @property
def flashed(self):
return False
-
- @property
- def radius(self):
- return self.diameter / 2.
-
- @property
+ def bounding_box(self):
- radius = self.radius
- min_x = min(self.start[0], self.end[0]) - radius
- max_x = max(self.start[0], self.end[0]) + radius
- min_y = min(self.start[1], self.end[1]) - radius
- max_y = max(self.start[1], self.end[1]) + radius
- return ((min_x, max_x), (min_y, max_y))
+ if self._bounding_box is None:
+ ll = tuple([c - self.outer_diameter / 2. for c in self.position])
+ ur = tuple([c + self.outer_diameter / 2. for c in self.position])
+ self._bounding_box = ((ll[0], ur[0]), (ll[1], ur[1]))
+ return self._bounding_box
def offset(self, x_offset=0, y_offset=0):
self.start = tuple(map(add, self.start, (x_offset, y_offset)))
|