First time any macro renders

This commit is contained in:
Garret Fick 2015-12-30 14:05:00 +08:00
parent ca3c682da5
commit 4a815bf25d
5 changed files with 125 additions and 0 deletions

View file

@ -16,7 +16,9 @@
# See the License for the specific language governing permissions and
# limitations under the License.
from math import pi
from .utils import validate_coordinates, inch, metric
from .primitives import Circle, Line, Rectangle
# TODO: Add support for aperture macro variables
@ -67,6 +69,12 @@ class AMPrimitive(object):
def to_metric(self):
raise NotImplementedError('Subclass must implement `to-metric`')
def to_primitive(self, units):
"""
Convert to a primitive, as defines the primitives module (for drawing)
"""
raise NotImplementedError('Subclass must implement `to-primitive`')
def __eq__(self, other):
return self.__dict__ == other.__dict__
@ -120,6 +128,12 @@ class AMCommentPrimitive(AMPrimitive):
def to_gerber(self, settings=None):
return '0 %s *' % self.comment
def to_primitive(self, units):
"""
Returns None - has not primitive representation
"""
return None
def __str__(self):
return '<Aperture Macro Comment: %s>' % self.comment
@ -189,6 +203,9 @@ class AMCirclePrimitive(AMPrimitive):
y = self.position[1])
return '{code},{exposure},{diameter},{x},{y}*'.format(**data)
def to_primitive(self, units):
return Circle((self.position), self.diameter, units=units)
class AMVectorLinePrimitive(AMPrimitive):
""" Aperture Macro Vector Line primitive. Code 2 or 20.
@ -273,6 +290,9 @@ class AMVectorLinePrimitive(AMPrimitive):
endy = self.end[1],
rotation = self.rotation)
return fmtstr.format(**data)
def to_primitive(self, units):
return Line(self.start, self.end, Rectangle(None, self.width, self.width), units=units)
class AMOutlinePrimitive(AMPrimitive):
@ -360,6 +380,9 @@ class AMOutlinePrimitive(AMPrimitive):
rotation=str(self.rotation)
)
return "{code},{exposure},{n_points},{start_point},{points},{rotation}*".format(**data)
def to_primitive(self, units):
raise NotImplementedError()
class AMPolygonPrimitive(AMPrimitive):
@ -450,6 +473,9 @@ class AMPolygonPrimitive(AMPrimitive):
)
fmt = "{code},{exposure},{vertices},{position},{diameter},{rotation}*"
return fmt.format(**data)
def to_primitive(self, units):
raise NotImplementedError()
class AMMoirePrimitive(AMPrimitive):
@ -562,6 +588,9 @@ class AMMoirePrimitive(AMPrimitive):
fmt = "{code},{position},{diameter},{ring_thickness},{gap},{max_rings},{crosshair_thickness},{crosshair_length},{rotation}*"
return fmt.format(**data)
def to_primitive(self, units):
raise NotImplementedError()
class AMThermalPrimitive(AMPrimitive):
""" Aperture Macro Thermal primitive. Code 7.
@ -646,6 +675,9 @@ class AMThermalPrimitive(AMPrimitive):
fmt = "{code},{position},{outer_diameter},{inner_diameter},{gap}*"
return fmt.format(**data)
def to_primitive(self, units):
raise NotImplementedError()
class AMCenterLinePrimitive(AMPrimitive):
""" Aperture Macro Center Line primitive. Code 21.
@ -729,6 +761,9 @@ class AMCenterLinePrimitive(AMPrimitive):
fmt = "{code},{exposure},{width},{height},{center},{rotation}*"
return fmt.format(**data)
def to_primitive(self, units):
return Rectangle(self.center, self.width, self.height, rotation=self.rotation * pi / 180.0, units=units)
class AMLowerLeftLinePrimitive(AMPrimitive):
""" Aperture Macro Lower Left Line primitive. Code 22.
@ -811,6 +846,9 @@ class AMLowerLeftLinePrimitive(AMPrimitive):
fmt = "{code},{exposure},{width},{height},{lower_left},{rotation}*"
return fmt.format(**data)
def to_primitive(self, units):
raise NotImplementedError()
class AMUnsupportPrimitive(AMPrimitive):
@classmethod
@ -829,3 +867,6 @@ class AMUnsupportPrimitive(AMPrimitive):
def to_gerber(self, settings=None):
return self.primitive
def to_primitive(self, units):
return None

View file

@ -26,6 +26,7 @@ from .utils import (parse_gerber_value, write_gerber_value, decimal_string,
from .am_statements import *
from .am_read import read_macro
from .am_eval import eval_macro
from .primitives import AMGroup
class Statement(object):
@ -388,6 +389,8 @@ class AMParamStmt(ParamStmt):
self.primitives.append(AMThermalPrimitive.from_gerber(primitive))
else:
self.primitives.append(AMUnsupportPrimitive.from_gerber(primitive))
return AMGroup(self.primitives, units=self.units)
def to_inch(self):
if self.units == 'metric':

View file

@ -18,6 +18,7 @@ import math
from operator import add, sub
from .utils import validate_coordinates, inch, metric
from jsonpickle.util import PRIMITIVES
class Primitive(object):
@ -425,6 +426,10 @@ 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, **kwargs):
super(Rectangle, self).__init__(**kwargs)
@ -702,6 +707,57 @@ class Polygon(Primitive):
def offset(self, x_offset=0, y_offset=0):
self.position = tuple(map(add, self.position, (x_offset, y_offset)))
class AMGroup(Primitive):
"""
"""
def __init__(self, amprimitives, **kwargs):
super(AMGroup, self).__init__(**kwargs)
self.primitives = []
for amprim in amprimitives:
prim = amprim.to_primitive(self.units)
if prim:
self.primitives.append(prim)
self._position = None
self._to_convert = ['arimitives']
@property
def flashed(self):
return True
@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))
@property
def position(self):
return self._position
@position.setter
def position(self, new_pos):
'''
Sets the position of the AMGroup.
This offset all of the objects by the specified distance.
'''
if self._position:
dx = new_pos[0] - self._position[0]
dy = new_pos[0] - self._position[0]
else:
dx = new_pos[0]
dy = new_pos[1]
for primitive in self.primitives:
primitive.offset(dx, dy)
self._position = new_pos
class Region(Primitive):
"""

View file

@ -122,11 +122,27 @@ class GerberCairoContext(GerberContext):
def _render_rectangle(self, rectangle, color):
ll = map(mul, rectangle.lower_left, self.scale)
width, height = tuple(map(mul, (rectangle.width, rectangle.height), map(abs, self.scale)))
if rectangle.rotation != 0:
self.ctx.save()
center = map(mul, rectangle.position, self.scale)
matrix = cairo.Matrix()
matrix.translate(center[0], center[1])
# For drawing, we already handles the translation
ll[0] = ll[0] - center[0]
ll[1] = ll[1] - center[1]
matrix.rotate(rectangle.rotation)
self.ctx.transform(matrix)
self.ctx.set_source_rgba(color[0], color[1], color[2], self.alpha)
self.ctx.set_operator(cairo.OPERATOR_OVER if (rectangle.level_polarity == "dark" and not self.invert) else cairo.OPERATOR_CLEAR)
self.ctx.set_line_width(0)
self.ctx.rectangle(ll[0], ll[1], width, height)
self.ctx.fill()
if rectangle.rotation != 0:
self.ctx.restore()
def _render_obround(self, obround, color):
self._render_circle(obround.subshapes['circle1'], color)
@ -135,6 +151,10 @@ class GerberCairoContext(GerberContext):
def _render_drill(self, circle, color):
self._render_circle(circle, color)
def _render_amgroup(self, amgroup, color):
for primitive in amgroup.primitives:
self.render(primitive)
def _render_test_record(self, primitive, color):
self.ctx.select_font_face('monospace', cairo.FONT_SLANT_NORMAL, cairo.FONT_WEIGHT_NORMAL)

View file

@ -150,6 +150,8 @@ class GerberContext(object):
self._render_polygon(primitive, color)
elif isinstance(primitive, Drill):
self._render_drill(primitive, self.drill_color)
elif isinstance(primitive, AMGroup):
self._render_amgroup(primitive, color)
elif isinstance(primitive, TestRecord):
self._render_test_record(primitive, color)
else:
@ -178,6 +180,9 @@ class GerberContext(object):
def _render_drill(self, primitive, color):
pass
def _render_amgroup(self, primitive, color):
pass
def _render_test_record(self, primitive, color):
pass