pcb-tools-extension/gerberex/am_primitive.py
Marin Mikaël 415bdbc2e4
Update am_primitive.py
Fix bug when circle doesn't have any rotation by adding a default 0 degree rotation.
2019-07-24 01:20:15 +09:00

448 lines
17 KiB
Python

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Copyright 2019 Hiroshi Murayama <opiopan@gmail.com>
from gerber.utils import *
from gerber.am_statements import *
from gerber.am_eval import OpCode
from gerberex.am_expression import eval_macro, AMConstantExpression, AMOperatorExpression
class AMPrimitiveDef(AMPrimitive):
def __init__(self, code, exposure=None, rotation=None):
super(AMPrimitiveDef, self).__init__(code, exposure)
if not rotation:
rotation = AMConstantExpression(0)
self.rotation = rotation
def rotate(self, angle, center=None):
self.rotation = AMOperatorExpression(AMOperatorExpression.ADD,
self.rotation,
AMConstantExpression(float(angle)))
self.rotation = self.rotation.optimize()
def to_inch(self):
pass
def to_metric(self):
pass
def to_gerber(self, settings=None):
pass
def to_instructions(self):
pass
class AMCommentPrimitiveDef(AMPrimitiveDef):
@classmethod
def from_modifiers(cls, code, modifiers):
return cls(code, modifiers[0])
def __init__(self, code, comment):
super(AMCommentPrimitiveDef, self).__init__(code)
self.comment = comment
def to_gerber(self, settings=None):
return '%d %s*' % (self.code, self.comment.to_gerber())
def to_instructions(self):
return [(OpCode.PUSH, self.comment), (OpCode.PRIM, self.code)]
class AMCirclePrimitiveDef(AMPrimitiveDef):
@classmethod
def from_modifiers(cls, code, modifiers):
exposure = 'on' if modifiers[0].value == 1 else 'off'
diameter = modifiers[1]
center_x = modifiers[2]
center_y = modifiers[3]
rotation = modifiers[4] if len(modifiers)>4 else AMConstantExpression(float(0))
return cls(code, exposure, diameter, center_x, center_y, rotation)
def __init__(self, code, exposure, diameter, center_x, center_y, rotation):
super(AMCirclePrimitiveDef, self).__init__(code, exposure, rotation)
self.diameter = diameter
self.center_x = center_x
self.center_y = center_y
def to_inch(self):
self.diameter = self.diameter.to_inch().optimize()
self.center_x = self.center_x.to_inch().optimize()
self.center_y = self.center_y.to_inch().optimize()
def to_metric(self):
self.diameter = self.diameter.to_metric().optimize()
self.center_x = self.center_x.to_metric().optimize()
self.center_y = self.center_y.to_metric().optimize()
def to_gerber(self, settings=None):
data = dict(code = self.code,
exposure = 1 if self.exposure == 'on' else 0,
diameter = self.diameter.to_gerber(settings),
x = self.center_x.to_gerber(settings),
y = self.center_y.to_gerber(settings),
rotation = self.rotation.to_gerber(settings))
return '{code},{exposure},{diameter},{x},{y},{rotation}*'.format(**data)
def to_instructions(self):
yield (OpCode.PUSH, 1 if self.exposure == 'on' else 0)
for modifier in [self.diameter, self.center_x, self.center_y, self.rotation]:
for i in modifier.to_instructions():
yield i
yield (OpCode.PRIM, self.code)
class AMVectorLinePrimitiveDef(AMPrimitiveDef):
@classmethod
def from_modifiers(cls, code, modifiers):
code = code
exposure = 'on' if modifiers[0].value == 1 else 'off'
width = modifiers[1]
start_x = modifiers[2]
start_y = modifiers[3]
end_x = modifiers[4]
end_y = modifiers[5]
rotation = modifiers[6]
return cls(code, exposure, width, start_x, start_y, end_x, end_y, rotation)
def __init__(self, code, exposure, width, start_x, start_y, end_x, end_y, rotation):
super(AMVectorLinePrimitiveDef, self).__init__(code, exposure, rotation)
self.width = width
self.start_x = start_x
self.start_y = start_y
self.end_x = end_x
self.end_y = end_y
def to_inch(self):
self.width = self.width.to_inch().optimize()
self.start_x = self.start_x.to_inch().optimize()
self.start_y = self.start_y.to_inch().optimize()
self.end_x = self.end_x.to_inch().optimize()
self.end_y = self.end_y.to_inch().optimize()
def to_metric(self):
self.width = self.width.to_metric().optimize()
self.start_x = self.start_x.to_metric().optimize()
self.start_y = self.start_y.to_metric().optimize()
self.end_x = self.end_x.to_metric().optimize()
self.end_y = self.end_y.to_metric().optimize()
def to_gerber(self, settings=None):
data = dict(code = self.code,
exposure = 1 if self.exposure == 'on' else 0,
width = self.width.to_gerber(settings),
start_x = self.start_x.to_gerber(settings),
start_y = self.start_y.to_gerber(settings),
end_x = self.end_x.to_gerber(settings),
end_y = self.end_y.to_gerber(settings),
rotation = self.rotation.to_gerber(settings))
return '{code},{exposure},{width},{start_x},{start_y},{end_x},{end_y},{rotation}*'.format(**data)
def to_instructions(self):
yield (OpCode.PUSH, 1 if self.exposure == 'on' else 0)
modifiers = [self.width, self.start_x, self.start_y, self.end_x, self.end_y, self.rotation]
for modifier in modifiers:
for i in modifier.to_instructions():
yield i
yield (OpCode.PRIM, self.code)
class AMCenterLinePrimitiveDef(AMPrimitiveDef):
@classmethod
def from_modifiers(cls, code, modifiers):
code = code
exposure = 'on' if modifiers[0].value == 1 else 'off'
width = modifiers[1]
height = modifiers[2]
x = modifiers[3]
y = modifiers[4]
rotation = modifiers[5]
return cls(code, exposure, width, height, x, y, rotation)
def __init__(self, code, exposure, width, height, x, y, rotation):
super(AMCenterLinePrimitiveDef, self).__init__(code, exposure, rotation)
self.width = width
self.height = height
self.x = x
self.y = y
def to_inch(self):
self.width = self.width.to_inch().optimize()
self.height = self.height.to_inch().optimize()
self.x = self.x.to_inch().optimize()
self.y = self.y.to_inch().optimize()
def to_metric(self):
self.width = self.width.to_metric().optimize()
self.height = self.height.to_metric().optimize()
self.x = self.x.to_metric().optimize()
self.y = self.y.to_metric().optimize()
def to_gerber(self, settings=None):
data = dict(code = self.code,
exposure = 1 if self.exposure == 'on' else 0,
width = self.width.to_gerber(settings),
height = self.height.to_gerber(settings),
x = self.x.to_gerber(settings),
y = self.y.to_gerber(settings),
rotation = self.rotation.to_gerber(settings))
return '{code},{exposure},{width},{height},{x},{y},{rotation}*'.format(**data)
def to_instructions(self):
yield (OpCode.PUSH, 1 if self.exposure == 'on' else 0)
modifiers = [self.width, self.height, self.x, self.y, self.rotation]
for modifier in modifiers:
for i in modifier.to_instructions():
yield i
yield (OpCode.PRIM, self.code)
class AMOutlinePrimitiveDef(AMPrimitiveDef):
@classmethod
def from_modifiers(cls, code, modifiers):
num_points = int(modifiers[1].value + 1)
code = code
exposure = 'on' if modifiers[0].value == 1 else 'off'
addrs = modifiers[2:num_points * 2 + 2]
rotation = modifiers[2 + num_points * 2]
return cls(code, exposure, addrs, rotation)
def __init__(self, code, exposure, addrs, rotation):
super(AMOutlinePrimitiveDef, self).__init__(code, exposure, rotation)
self.addrs = addrs
def to_inch(self):
self.addrs = [i.to_inch().optimize() for i in self.addrs]
def to_metric(self):
self.addrs = [i.to_metric().optimize() for i in self.addrs]
def to_gerber(self, settings=None):
def strs():
yield '%d,%d,%d' % (self.code,
1 if self.exposure == 'on' else 0,
len(self.addrs) / 2 - 1)
for i in self.addrs:
yield i.to_gerber(settings)
yield self.rotation.to_gerber(settings)
return '%s*' % ','.join(strs())
def to_instructions(self):
yield (OpCode.PUSH, 1 if self.exposure == 'on' else 0)
yield (OpCode.PUSH, int(len(self.addrs) / 2 - 1))
for modifier in self.addrs:
for i in modifier.to_instructions():
yield i
for i in self.rotation.to_instructions():
yield i
yield (OpCode.PRIM, self.code)
class AMPolygonPrimitiveDef(AMPrimitiveDef):
@classmethod
def from_modifiers(cls, code, modifiers):
code = code
exposure = 'on' if modifiers[0].value == 1 else 'off'
vertices = modifiers[1]
x = modifiers[2]
y = modifiers[3]
diameter = modifiers[4]
rotation = modifiers[5]
return cls(code, exposure, vertices, x, y, diameter, rotation)
def __init__(self, code, exposure, vertices, x, y, diameter, rotation):
super(AMPolygonPrimitiveDef, self).__init__(code, exposure, rotation)
self.vertices = vertices
self.x = x
self.y = y
self.diameter = diameter
def to_inch(self):
self.x = self.x.to_inch().optimize()
self.y = self.y.to_inch().optimize()
self.diameter = self.diameter.to_inch().optimize()
def to_metric(self):
self.x = self.x.to_metric().optimize()
self.y = self.y.to_metric().optimize()
self.diameter = self.diameter.to_metric().optimize()
def to_gerber(self, settings=None):
data = dict(code = self.code,
exposure = 1 if self.exposure == 'on' else 0,
vertices = self.vertices.to_gerber(settings),
x = self.x.to_gerber(settings),
y = self.y.to_gerber(settings),
diameter = self.diameter.to_gerber(settings),
rotation = self.rotation.to_gerber(settings))
return '{code},{exposure},{vertices},{x},{y},{diameter},{rotation}*'.format(**data)
def to_instructions(self):
yield (OpCode.PUSH, 1 if self.exposure == 'on' else 0)
modifiers = [self.vertices, self.x, self.y, self.diameter, self.rotation]
for modifier in modifiers:
for i in modifier.to_instructions():
yield i
yield (OpCode.PRIM, self.code)
class AMMoirePrimitiveDef(AMPrimitiveDef):
@classmethod
def from_modifiers(cls, code, modifiers):
code = code
exposure = 'on'
x = modifiers[0]
y = modifiers[1]
diameter = modifiers[2]
ring_thickness = modifiers[3]
gap = modifiers[4]
max_rings = modifiers[5]
crosshair_thickness = modifiers[6]
crosshair_length = modifiers[7]
rotation = modifiers[8]
return cls(code, exposure, x, y, diameter, ring_thickness, gap,
max_rings, crosshair_thickness, crosshair_length, rotation)
def __init__(self, code, exposure, x, y, diameter, ring_thickness, gap, max_rings, crosshair_thickness, crosshair_length, rotation):
super(AMMoirePrimitiveDef, self).__init__(code, exposure, rotation)
self.x = x
self.y = y
self.diameter = diameter
self.ring_thickness = ring_thickness
self.gap = gap
self.max_rings = max_rings
self.crosshair_thickness = crosshair_thickness
self.crosshair_length = crosshair_length
def to_inch(self):
self.x = self.x.to_inch().optimize()
self.y = self.y.to_inch().optimize()
self.diameter = self.diameter.to_inch().optimize()
self.ring_thickness = self.ring_thickness.to_inch().optimize()
self.gap = self.gap.to_inch().optimize()
self.crosshair_thickness = self.crosshair_thickness.to_inch().optimize()
self.crosshair_length = self.crosshair_length.to_inch().optimize()
def to_metric(self):
self.x = self.x.to_metric().optimize()
self.y = self.y.to_metric().optimize()
self.diameter = self.diameter.to_metric().optimize()
self.ring_thickness = self.ring_thickness.to_metric().optimize()
self.gap = self.gap.to_metric().optimize()
self.crosshair_thickness = self.crosshair_thickness.to_metric().optimize()
self.crosshair_length = self.crosshair_length.to_metric().optimize()
def to_gerber(self, settings=None):
data = dict(code = self.code,
x = self.x.to_gerber(settings),
y = self.y.to_gerber(settings),
diameter = self.diameter.to_gerber(settings),
ring_thickness = self.ring_thickness.to_gerber(settings),
gap = self.gap.to_gerber(settings),
max_rings = self.max_rings.to_gerber(settings),
crosshair_thickness = self.crosshair_thickness.to_gerber(settings),
crosshair_length = self.crosshair_length.to_gerber(settings),
rotation = self.rotation.to_gerber(settings))
return '{code},{x},{y},{diameter},{ring_thickness},{gap},{max_rings},'\
'{crosshair_thickness},{crosshair_length},{rotation}*'.format(**data)
def to_instructions(self):
modifiers = [self.x, self.y, self.diameter,
self.ring_thickness, self.gap, self.max_rings,
self.crosshair_thickness, self.crosshair_length,
self.rotation]
for modifier in modifiers:
for i in modifier.to_instructions():
yield i
yield (OpCode.PRIM, self.code)
class AMThermalPrimitiveDef(AMPrimitiveDef):
@classmethod
def from_modifiers(cls, code, modifiers):
code = code
exposure = 'on'
x = modifiers[0]
y = modifiers[1]
outer_diameter = modifiers[2]
inner_diameter = modifiers[3]
gap = modifiers[4]
rotation = modifiers[5]
return cls(code, exposure, x, y, outer_diameter, inner_diameter, gap, rotation)
def __init__(self, code, exposure, x, y, outer_diameter, inner_diameter, gap, rotation):
super(AMThermalPrimitiveDef, self).__init__(code, exposure, rotation)
self.x = x
self.y = y
self.outer_diameter = outer_diameter
self.inner_diameter = inner_diameter
self.gap = gap
def to_inch(self):
self.x = self.x.to_inch().optimize()
self.y = self.y.to_inch().optimize()
self.outer_diameter = self.outer_diameter.to_inch().optimize()
self.inner_diameter = self.inner_diameter.to_inch().optimize()
self.gap = self.gap.to_inch().optimize()
def to_metric(self):
self.x = self.x.to_metric().optimize()
self.y = self.y.to_metric().optimize()
self.outer_diameter = self.outer_diameter.to_metric().optimize()
self.inner_diameter = self.inner_diameter.to_metric().optimize()
self.gap = self.gap.to_metric().optimize()
def to_gerber(self, settings=None):
data = dict(code = self.code,
x = self.x.to_gerber(settings),
y = self.y.to_gerber(settings),
outer_diameter = self.outer_diameter.to_gerber(settings),
inner_diameter = self.inner_diameter.to_gerber(settings),
gap = self.gap.to_gerber(settings),
rotation = self.rotation.to_gerber(settings))
return '{code},{x},{y},{outer_diameter},{inner_diameter},'\
'{gap},{rotation}*'.format(**data)
def to_instructions(self):
modifiers = [self.x, self.y, self.outer_diameter,
self.inner_diameter, self.gap, self.rotation]
for modifier in modifiers:
for i in modifier.to_instructions():
yield i
yield (OpCode.PRIM, self.code)
class AMVariableDef(object):
def __init__(self, number, value):
self.number = number
self.value = value
def to_inch(self):
return self
def to_metric(self):
return self
def to_gerber(self, settings=None):
return '$%d=%s*' % (self.number, self.value.to_gerber(settings))
def to_instructions(self):
for i in self.value.to_instructions():
yield i
yield (OpCode.STORE, self.number)
def rotate(self, angle, center=None):
pass
def to_primitive_defs(instructions):
classes = {
0: AMCommentPrimitiveDef,
1: AMCirclePrimitiveDef,
2: AMVectorLinePrimitiveDef,
20: AMVectorLinePrimitiveDef,
21: AMCenterLinePrimitiveDef,
4: AMOutlinePrimitiveDef,
5: AMPolygonPrimitiveDef,
6: AMMoirePrimitiveDef,
7: AMThermalPrimitiveDef,
}
for code, modifiers in eval_macro(instructions):
if code < 0:
yield AMVariableDef(-code, modifiers[0])
else:
primitive = classes[code]
yield primitive.from_modifiers(code, modifiers)