kopia lustrzana https://gitlab.com/gerbolyze/gerbonara
Fix a bunch of rendering bugs.
- 'clear' polarity primitives no longer erase background - Added aperture macro support for polygons - Added aperture macro rendring support - Renderer now creates a new surface for each layer and merges them instead of working directly on a single surface - Updated examples accordinglyrefactor
rodzic
7a53251463
commit
5476da8aa3
Plik binarny nie jest wyświetlany.
Przed Szerokość: | Wysokość: | Rozmiar: 102 KiB Po Szerokość: | Wysokość: | Rozmiar: 102 KiB |
|
@ -24,6 +24,7 @@ import os
|
|||
from gerber import PCB
|
||||
from gerber.render import GerberCairoContext, theme
|
||||
|
||||
|
||||
GERBER_FOLDER = os.path.abspath(os.path.join(os.path.dirname(__file__), 'gerbers'))
|
||||
|
||||
|
||||
|
@ -33,7 +34,7 @@ ctx = GerberCairoContext()
|
|||
# Create a new PCB
|
||||
pcb = PCB.from_directory(GERBER_FOLDER)
|
||||
|
||||
pcb.theme = theme.THEMES['OSH Park']
|
||||
ctx.render_layers(pcb.top_layers, os.path.join(os.path.dirname(__file__), 'pcb_top.png'))
|
||||
ctx.render_layers(pcb.bottom_layers, os.path.join(os.path.dirname(__file__), 'pcb_bottom.png'))
|
||||
# Render PCB
|
||||
ctx.render_layers(pcb.top_layers, os.path.join(os.path.dirname(__file__), 'pcb_top.png',), theme.THEMES['OSH Park'])
|
||||
ctx.render_layers(pcb.bottom_layers, os.path.join(os.path.dirname(__file__), 'pcb_bottom.png'), theme.THEMES['OSH Park'])
|
||||
|
||||
|
|
Plik binarny nie jest wyświetlany.
Przed Szerokość: | Wysokość: | Rozmiar: 97 KiB Po Szerokość: | Wysokość: | Rozmiar: 96 KiB |
|
@ -18,15 +18,16 @@
|
|||
""" This module provides RS-274-X AM macro evaluation.
|
||||
"""
|
||||
|
||||
|
||||
class OpCode:
|
||||
PUSH = 1
|
||||
LOAD = 2
|
||||
PUSH = 1
|
||||
LOAD = 2
|
||||
STORE = 3
|
||||
ADD = 4
|
||||
SUB = 5
|
||||
MUL = 6
|
||||
DIV = 7
|
||||
PRIM = 8
|
||||
ADD = 4
|
||||
SUB = 5
|
||||
MUL = 6
|
||||
DIV = 7
|
||||
PRIM = 8
|
||||
|
||||
@staticmethod
|
||||
def str(opcode):
|
||||
|
@ -49,16 +50,18 @@ class OpCode:
|
|||
else:
|
||||
return "UNKNOWN"
|
||||
|
||||
|
||||
def eval_macro(instructions, parameters={}):
|
||||
|
||||
if not isinstance(parameters, type({})):
|
||||
p = {}
|
||||
for i, val in enumerate(parameters):
|
||||
p[i+1] = val
|
||||
p[i + 1] = val
|
||||
|
||||
parameters = p
|
||||
|
||||
stack = []
|
||||
|
||||
def pop():
|
||||
return stack.pop()
|
||||
|
||||
|
|
|
@ -26,7 +26,8 @@ import string
|
|||
class Token:
|
||||
ADD = "+"
|
||||
SUB = "-"
|
||||
MULT = ("x", "X") # compatibility as many gerber writes do use non compliant X
|
||||
# compatibility as many gerber writes do use non compliant X
|
||||
MULT = ("x", "X")
|
||||
DIV = "/"
|
||||
OPERATORS = (ADD, SUB, MULT[0], MULT[1], DIV)
|
||||
LEFT_PARENS = "("
|
||||
|
@ -62,6 +63,7 @@ def is_op(token):
|
|||
|
||||
|
||||
class Scanner:
|
||||
|
||||
def __init__(self, s):
|
||||
self.buff = s
|
||||
self.n = 0
|
||||
|
@ -111,7 +113,8 @@ class Scanner:
|
|||
|
||||
def print_instructions(instructions):
|
||||
for opcode, argument in instructions:
|
||||
print("%s %s" % (OpCode.str(opcode), str(argument) if argument is not None else ""))
|
||||
print("%s %s" % (OpCode.str(opcode),
|
||||
str(argument) if argument is not None else ""))
|
||||
|
||||
|
||||
def read_macro(macro):
|
||||
|
|
|
@ -17,9 +17,7 @@
|
|||
# limitations under the License.
|
||||
|
||||
from .utils import validate_coordinates, inch, metric
|
||||
|
||||
|
||||
# TODO: Add support for aperture macro variables
|
||||
from .primitives import *
|
||||
|
||||
__all__ = ['AMPrimitive', 'AMCommentPrimitive', 'AMCirclePrimitive',
|
||||
'AMVectorLinePrimitive', 'AMOutlinePrimitive', 'AMPolygonPrimitive',
|
||||
|
@ -51,12 +49,14 @@ class AMPrimitive(object):
|
|||
------
|
||||
TypeError, ValueError
|
||||
"""
|
||||
|
||||
def __init__(self, code, exposure=None):
|
||||
VALID_CODES = (0, 1, 2, 4, 5, 6, 7, 20, 21, 22, 9999)
|
||||
if not isinstance(code, int):
|
||||
raise TypeError('Aperture Macro Primitive code must be an integer')
|
||||
elif code not in VALID_CODES:
|
||||
raise ValueError('Invalid Code. Valid codes are %s.' % ', '.join(map(str, VALID_CODES)))
|
||||
raise ValueError('Invalid Code. Valid codes are %s.' %
|
||||
', '.join(map(str, VALID_CODES)))
|
||||
if exposure is not None and exposure.lower() not in ('on', 'off'):
|
||||
raise ValueError('Exposure must be either on or off')
|
||||
self.code = code
|
||||
|
@ -68,9 +68,15 @@ class AMPrimitive(object):
|
|||
def to_metric(self):
|
||||
raise NotImplementedError('Subclass must implement `to-metric`')
|
||||
|
||||
def to_primitive(self, position, level_polarity, units):
|
||||
""" Return a Primitive instance based on the specified macro params.
|
||||
"""
|
||||
print('Rendering {}s is not supported yet.'.format(str(self.__class__)))
|
||||
|
||||
def __eq__(self, other):
|
||||
return self.__dict__ == other.__dict__
|
||||
|
||||
|
||||
class AMCommentPrimitive(AMPrimitive):
|
||||
""" Aperture Macro Comment primitive. Code 0
|
||||
|
||||
|
@ -181,12 +187,19 @@ class AMCirclePrimitive(AMPrimitive):
|
|||
self.diameter = metric(self.diameter)
|
||||
self.position = tuple([metric(x) for x in self.position])
|
||||
|
||||
def to_primitive(self, position, level_polarity, units):
|
||||
# Offset the primitive from macro position
|
||||
position = tuple([a + b for a , b in zip (position, self.position)])
|
||||
# Return a renderable primitive
|
||||
return Circle(position, self.diameter, level_polarity=level_polarity,
|
||||
units=units)
|
||||
|
||||
def to_gerber(self, settings=None):
|
||||
data = dict(code = self.code,
|
||||
exposure = '1' if self.exposure == 'on' else 0,
|
||||
diameter = self.diameter,
|
||||
x = self.position[0],
|
||||
y = self.position[1])
|
||||
data = dict(code=self.code,
|
||||
exposure='1' if self.exposure == 'on' else 0,
|
||||
diameter=self.diameter,
|
||||
x=self.position[0],
|
||||
y=self.position[1])
|
||||
return '{code},{exposure},{diameter},{x},{y}*'.format(**data)
|
||||
|
||||
|
||||
|
@ -261,17 +274,24 @@ class AMVectorLinePrimitive(AMPrimitive):
|
|||
self.start = tuple([metric(x) for x in self.start])
|
||||
self.end = tuple([metric(x) for x in self.end])
|
||||
|
||||
def to_primitive(self, position, level_polarity, units):
|
||||
# Offset the primitive from macro position
|
||||
start = tuple([a + b for a , b in zip (position, self.start)])
|
||||
end = tuple([a + b for a , b in zip (position, self.end)])
|
||||
# Return a renderable primitive
|
||||
ap = Rectangle((0, 0), self.width, self.width)
|
||||
return Line(start, end, ap, level_polarity=level_polarity, units=units)
|
||||
|
||||
def to_gerber(self, settings=None):
|
||||
fmtstr = '{code},{exp},{width},{startx},{starty},{endx},{endy},{rotation}*'
|
||||
data = dict(code = self.code,
|
||||
exp = 1 if self.exposure == 'on' else 0,
|
||||
width = self.width,
|
||||
startx = self.start[0],
|
||||
starty = self.start[1],
|
||||
endx = self.end[0],
|
||||
endy = self.end[1],
|
||||
rotation = self.rotation)
|
||||
data = dict(code=self.code,
|
||||
exp=1 if self.exposure == 'on' else 0,
|
||||
width=self.width,
|
||||
startx=self.start[0],
|
||||
starty=self.start[1],
|
||||
endx=self.end[0],
|
||||
endy=self.end[1],
|
||||
rotation=self.rotation)
|
||||
return fmtstr.format(**data)
|
||||
|
||||
|
||||
|
@ -323,7 +343,8 @@ class AMOutlinePrimitive(AMPrimitive):
|
|||
start_point = (float(modifiers[3]), float(modifiers[4]))
|
||||
points = []
|
||||
for i in range(n):
|
||||
points.append((float(modifiers[5 + i*2]), float(modifiers[5 + i*2 + 1])))
|
||||
points.append((float(modifiers[5 + i * 2]),
|
||||
float(modifiers[5 + i * 2 + 1])))
|
||||
rotation = float(modifiers[-1])
|
||||
return cls(code, exposure, start_point, points, rotation)
|
||||
|
||||
|
@ -416,7 +437,6 @@ class AMPolygonPrimitive(AMPrimitive):
|
|||
rotation = float(modifiers[6])
|
||||
return cls(code, exposure, vertices, position, diameter, rotation)
|
||||
|
||||
|
||||
def __init__(self, code, exposure, vertices, position, diameter, rotation):
|
||||
""" Initialize AMPolygonPrimitive
|
||||
"""
|
||||
|
@ -439,13 +459,21 @@ class AMPolygonPrimitive(AMPrimitive):
|
|||
self.position = tuple([metric(x) for x in self.position])
|
||||
self.diameter = metric(self.diameter)
|
||||
|
||||
def to_primitive(self, position, level_polarity, units):
|
||||
# Offset the primitive from macro position
|
||||
position = tuple([a + b for a , b in zip (position, self.position)])
|
||||
# Return a renderable primitive
|
||||
return Polygon(position, vertices, self.diameter/2.,
|
||||
rotation=self.rotation, level_polarity=level_polarity,
|
||||
units=units)
|
||||
|
||||
def to_gerber(self, settings=None):
|
||||
data = dict(
|
||||
code=self.code,
|
||||
exposure="1" if self.exposure == "on" else "0",
|
||||
vertices=self.vertices,
|
||||
position="%.4g,%.4g" % self.position,
|
||||
diameter = '%.4g' % self.diameter,
|
||||
diameter='%.4g' % self.diameter,
|
||||
rotation=str(self.rotation)
|
||||
)
|
||||
fmt = "{code},{exposure},{vertices},{position},{diameter},{rotation}*"
|
||||
|
@ -546,17 +574,16 @@ class AMMoirePrimitive(AMPrimitive):
|
|||
self.crosshair_thickness = metric(self.crosshair_thickness)
|
||||
self.crosshair_length = metric(self.crosshair_length)
|
||||
|
||||
|
||||
def to_gerber(self, settings=None):
|
||||
data = dict(
|
||||
code=self.code,
|
||||
position="%.4g,%.4g" % self.position,
|
||||
diameter = self.diameter,
|
||||
ring_thickness = self.ring_thickness,
|
||||
gap = self.gap,
|
||||
max_rings = self.max_rings,
|
||||
crosshair_thickness = self.crosshair_thickness,
|
||||
crosshair_length = self.crosshair_length,
|
||||
diameter=self.diameter,
|
||||
ring_thickness=self.ring_thickness,
|
||||
gap=self.gap,
|
||||
max_rings=self.max_rings,
|
||||
crosshair_thickness=self.crosshair_thickness,
|
||||
crosshair_length=self.crosshair_length,
|
||||
rotation=self.rotation
|
||||
)
|
||||
fmt = "{code},{position},{diameter},{ring_thickness},{gap},{max_rings},{crosshair_thickness},{crosshair_length},{rotation}*"
|
||||
|
@ -608,7 +635,7 @@ class AMThermalPrimitive(AMPrimitive):
|
|||
code = int(modifiers[0])
|
||||
position = (float(modifiers[1]), float(modifiers[2]))
|
||||
outer_diameter = float(modifiers[3])
|
||||
inner_diameter= float(modifiers[4])
|
||||
inner_diameter = float(modifiers[4])
|
||||
gap = float(modifiers[5])
|
||||
return cls(code, position, outer_diameter, inner_diameter, gap)
|
||||
|
||||
|
@ -628,7 +655,6 @@ class AMThermalPrimitive(AMPrimitive):
|
|||
self.inner_diameter = inch(self.inner_diameter)
|
||||
self.gap = inch(self.gap)
|
||||
|
||||
|
||||
def to_metric(self):
|
||||
self.position = tuple([metric(x) for x in self.position])
|
||||
self.outer_diameter = metric(self.outer_diameter)
|
||||
|
@ -639,9 +665,9 @@ class AMThermalPrimitive(AMPrimitive):
|
|||
data = dict(
|
||||
code=self.code,
|
||||
position="%.4g,%.4g" % self.position,
|
||||
outer_diameter = self.outer_diameter,
|
||||
inner_diameter = self.inner_diameter,
|
||||
gap = self.gap,
|
||||
outer_diameter=self.outer_diameter,
|
||||
inner_diameter=self.inner_diameter,
|
||||
gap=self.gap,
|
||||
)
|
||||
fmt = "{code},{position},{outer_diameter},{inner_diameter},{gap}*"
|
||||
return fmt.format(**data)
|
||||
|
@ -693,14 +719,14 @@ class AMCenterLinePrimitive(AMPrimitive):
|
|||
exposure = 'on' if float(modifiers[1]) == 1 else 'off'
|
||||
width = float(modifiers[2])
|
||||
height = float(modifiers[3])
|
||||
center= (float(modifiers[4]), float(modifiers[5]))
|
||||
center = (float(modifiers[4]), float(modifiers[5]))
|
||||
rotation = float(modifiers[6])
|
||||
return cls(code, exposure, width, height, center, rotation)
|
||||
|
||||
def __init__(self, code, exposure, width, height, center, rotation):
|
||||
if code != 21:
|
||||
raise ValueError('CenterLinePrimitive code is 21')
|
||||
super (AMCenterLinePrimitive, self).__init__(code, exposure)
|
||||
super(AMCenterLinePrimitive, self).__init__(code, exposure)
|
||||
self.width = width
|
||||
self.height = height
|
||||
validate_coordinates(center)
|
||||
|
@ -717,12 +743,19 @@ class AMCenterLinePrimitive(AMPrimitive):
|
|||
self.width = metric(self.width)
|
||||
self.height = metric(self.height)
|
||||
|
||||
def to_primitive(self, position, level_polarity, units):
|
||||
# Offset the primitive from macro position
|
||||
position = tuple([a + b for a , b in zip (position, self.center)])
|
||||
# Return a renderable primitive
|
||||
return Rectangle(position, self.width, self.height,
|
||||
level_polarity=level_polarity, units=units)
|
||||
|
||||
def to_gerber(self, settings=None):
|
||||
data = dict(
|
||||
code=self.code,
|
||||
exposure = '1' if self.exposure == 'on' else '0',
|
||||
width = self.width,
|
||||
height = self.height,
|
||||
exposure='1' if self.exposure == 'on' else '0',
|
||||
width=self.width,
|
||||
height=self.height,
|
||||
center="%.4g,%.4g" % self.center,
|
||||
rotation=self.rotation
|
||||
)
|
||||
|
@ -782,7 +815,7 @@ class AMLowerLeftLinePrimitive(AMPrimitive):
|
|||
def __init__(self, code, exposure, width, height, lower_left, rotation):
|
||||
if code != 22:
|
||||
raise ValueError('LowerLeftLinePrimitive code is 22')
|
||||
super (AMLowerLeftLinePrimitive, self).__init__(code, exposure)
|
||||
super(AMLowerLeftLinePrimitive, self).__init__(code, exposure)
|
||||
self.width = width
|
||||
self.height = height
|
||||
validate_coordinates(lower_left)
|
||||
|
@ -799,12 +832,21 @@ class AMLowerLeftLinePrimitive(AMPrimitive):
|
|||
self.width = metric(self.width)
|
||||
self.height = metric(self.height)
|
||||
|
||||
def to_primitive(self, position, level_polarity, units):
|
||||
# Offset the primitive from macro position
|
||||
position = tuple([a + b for a , b in zip (position, self.lower_left)])
|
||||
position = tuple([pos + offset for pos, offset in
|
||||
zip(position, (self.width/2, self.height/2))])
|
||||
# Return a renderable primitive
|
||||
return Rectangle(position, self.width, self.height,
|
||||
level_polarity=level_polarity, units=units)
|
||||
|
||||
def to_gerber(self, settings=None):
|
||||
data = dict(
|
||||
code=self.code,
|
||||
exposure = '1' if self.exposure == 'on' else '0',
|
||||
width = self.width,
|
||||
height = self.height,
|
||||
exposure='1' if self.exposure == 'on' else '0',
|
||||
width=self.width,
|
||||
height=self.height,
|
||||
lower_left="%.4g,%.4g" % self.lower_left,
|
||||
rotation=self.rotation
|
||||
)
|
||||
|
@ -813,6 +855,7 @@ class AMLowerLeftLinePrimitive(AMPrimitive):
|
|||
|
||||
|
||||
class AMUnsupportPrimitive(AMPrimitive):
|
||||
|
||||
@classmethod
|
||||
def from_gerber(cls, primitive):
|
||||
return cls(primitive)
|
||||
|
|
|
@ -22,6 +22,7 @@ CAM File
|
|||
This module provides common base classes for Excellon/Gerber CNC files
|
||||
"""
|
||||
|
||||
|
||||
class FileSettings(object):
|
||||
""" CAM File Settings
|
||||
|
||||
|
@ -52,6 +53,7 @@ class FileSettings(object):
|
|||
specify both. `zero_suppression` will take on the opposite value of `zeros`
|
||||
and vice versa
|
||||
"""
|
||||
|
||||
def __init__(self, notation='absolute', units='inch',
|
||||
zero_suppression=None, format=(2, 5), zeros=None,
|
||||
angle_units='degrees'):
|
||||
|
@ -243,6 +245,12 @@ class CamFile(object):
|
|||
"""
|
||||
pass
|
||||
|
||||
def to_inch(self):
|
||||
pass
|
||||
|
||||
def to_metric(self):
|
||||
pass
|
||||
|
||||
def render(self, ctx, invert=False, filename=None):
|
||||
""" Generate image of layer.
|
||||
|
||||
|
@ -256,15 +264,11 @@ class CamFile(object):
|
|||
"""
|
||||
ctx.set_bounds(self.bounds)
|
||||
ctx._paint_background()
|
||||
|
||||
if invert:
|
||||
ctx.invert = True
|
||||
ctx._clear_mask()
|
||||
ctx.invert = invert
|
||||
ctx._new_render_layer()
|
||||
for p in self.primitives:
|
||||
ctx.render(p)
|
||||
if invert:
|
||||
ctx.invert = False
|
||||
ctx._render_mask()
|
||||
ctx._flatten()
|
||||
|
||||
if filename is not None:
|
||||
ctx.dump(filename)
|
||||
|
|
|
@ -22,7 +22,6 @@ from .exceptions import ParseError
|
|||
from .utils import detect_file_format
|
||||
|
||||
|
||||
|
||||
def read(filename):
|
||||
""" Read a gerber or excellon file and return a representative object.
|
||||
|
||||
|
@ -73,5 +72,3 @@ def loads(data):
|
|||
return excellon.loads(data)
|
||||
else:
|
||||
raise TypeError('Unable to detect file format')
|
||||
|
||||
|
||||
|
|
|
@ -76,6 +76,7 @@ def loads(data):
|
|||
|
||||
|
||||
class DrillHit(object):
|
||||
|
||||
def __init__(self, tool, position):
|
||||
self.tool = tool
|
||||
self.position = position
|
||||
|
@ -118,6 +119,7 @@ class ExcellonFile(CamFile):
|
|||
either 'inch' or 'metric'.
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, statements, tools, hits, settings, filename=None):
|
||||
super(ExcellonFile, self).__init__(statements=statements,
|
||||
settings=settings,
|
||||
|
@ -127,8 +129,7 @@ class ExcellonFile(CamFile):
|
|||
|
||||
@property
|
||||
def primitives(self):
|
||||
return [Drill(hit.position, hit.tool.diameter,units=self.settings.units) for hit in self.hits]
|
||||
|
||||
return [Drill(hit.position, hit.tool.diameter, units=self.settings.units) for hit in self.hits]
|
||||
|
||||
@property
|
||||
def bounds(self):
|
||||
|
@ -162,7 +163,8 @@ class ExcellonFile(CamFile):
|
|||
rprt += ' Code Size Hits Path Length\n'
|
||||
rprt += ' --------------------------------------\n'
|
||||
for tool in iter(self.tools.values()):
|
||||
rprt += toolfmt.format(tool.number, tool.diameter, tool.hit_count, self.path_length(tool.number))
|
||||
rprt += toolfmt.format(tool.number, tool.diameter,
|
||||
tool.hit_count, self.path_length(tool.number))
|
||||
if filename is not None:
|
||||
with open(filename, 'w') as f:
|
||||
f.write(rprt)
|
||||
|
@ -184,7 +186,8 @@ class ExcellonFile(CamFile):
|
|||
f.write(ToolSelectionStmt(tool.number).to_excellon(self.settings) + '\n')
|
||||
for hit in self.hits:
|
||||
if hit.tool.number == tool.number:
|
||||
f.write(CoordinateStmt(*hit.position).to_excellon(self.settings) + '\n')
|
||||
f.write(CoordinateStmt(
|
||||
*hit.position).to_excellon(self.settings) + '\n')
|
||||
f.write(EndOfProgramStmt().to_excellon() + '\n')
|
||||
|
||||
def to_inch(self):
|
||||
|
@ -200,8 +203,7 @@ class ExcellonFile(CamFile):
|
|||
for primitive in self.primitives:
|
||||
primitive.to_inch()
|
||||
for hit in self.hits:
|
||||
hit.position = tuple(map(inch, hit,position))
|
||||
|
||||
hit.position = tuple(map(inch, hit, position))
|
||||
|
||||
def to_metric(self):
|
||||
""" Convert units to metric
|
||||
|
@ -223,7 +225,8 @@ class ExcellonFile(CamFile):
|
|||
for primitive in self.primitives:
|
||||
primitive.offset(x_offset, y_offset)
|
||||
for hit in self. hits:
|
||||
hit.position = tuple(map(operator.add, hit.position, (x_offset, y_offset)))
|
||||
hit.position = tuple(map(operator.add, hit.position,
|
||||
(x_offset, y_offset)))
|
||||
|
||||
def path_length(self, tool_number=None):
|
||||
""" Return the path length for a given tool
|
||||
|
@ -233,9 +236,11 @@ class ExcellonFile(CamFile):
|
|||
for hit in self.hits:
|
||||
tool = hit.tool
|
||||
num = tool.number
|
||||
positions[num] = (0, 0) if positions.get(num) is None else positions[num]
|
||||
positions[num] = (0, 0) if positions.get(
|
||||
num) is None else positions[num]
|
||||
lengths[num] = 0.0 if lengths.get(num) is None else lengths[num]
|
||||
lengths[num] = lengths[num] + math.hypot(*tuple(map(operator.sub, positions[num], hit.position)))
|
||||
lengths[num] = lengths[
|
||||
num] + math.hypot(*tuple(map(operator.sub, positions[num], hit.position)))
|
||||
positions[num] = hit.position
|
||||
|
||||
if tool_number is None:
|
||||
|
@ -244,13 +249,13 @@ class ExcellonFile(CamFile):
|
|||
return lengths.get(tool_number)
|
||||
|
||||
def hit_count(self, tool_number=None):
|
||||
counts = {}
|
||||
for tool in iter(self.tools.values()):
|
||||
counts[tool.number] = tool.hit_count
|
||||
if tool_number is None:
|
||||
return counts
|
||||
else:
|
||||
return counts.get(tool_number)
|
||||
counts = {}
|
||||
for tool in iter(self.tools.values()):
|
||||
counts[tool.number] = tool.hit_count
|
||||
if tool_number is None:
|
||||
return counts
|
||||
else:
|
||||
return counts.get(tool_number)
|
||||
|
||||
def update_tool(self, tool_number, **kwargs):
|
||||
""" Change parameters of a tool
|
||||
|
@ -274,7 +279,6 @@ class ExcellonFile(CamFile):
|
|||
hit.tool = newtool
|
||||
|
||||
|
||||
|
||||
class ExcellonParser(object):
|
||||
""" Excellon File Parser
|
||||
|
||||
|
@ -283,6 +287,7 @@ class ExcellonParser(object):
|
|||
settings : FileSettings or dict-like
|
||||
Excellon file settings to use when interpreting the excellon file.
|
||||
"""
|
||||
|
||||
def __init__(self, settings=None):
|
||||
self.notation = 'absolute'
|
||||
self.units = 'inch'
|
||||
|
@ -300,7 +305,6 @@ class ExcellonParser(object):
|
|||
self.notation = settings.notation
|
||||
self.format = settings.format
|
||||
|
||||
|
||||
@property
|
||||
def coordinates(self):
|
||||
return [(stmt.x, stmt.y) for stmt in self.statements if isinstance(stmt, CoordinateStmt)]
|
||||
|
@ -350,7 +354,8 @@ class ExcellonParser(object):
|
|||
|
||||
# get format from altium comment
|
||||
if "FILE_FORMAT" in comment_stmt.comment:
|
||||
detected_format = tuple([int(x) for x in comment_stmt.comment.split('=')[1].split(":")])
|
||||
detected_format = tuple(
|
||||
[int(x) for x in comment_stmt.comment.split('=')[1].split(":")])
|
||||
if detected_format:
|
||||
self.format = detected_format
|
||||
|
||||
|
@ -435,7 +440,7 @@ class ExcellonParser(object):
|
|||
self.zeros = stmt.zeros
|
||||
self.statements.append(stmt)
|
||||
|
||||
elif line[:3] == 'M71' or line [:3] == 'M72':
|
||||
elif line[:3] == 'M71' or line[:3] == 'M72':
|
||||
stmt = MeasuringModeStmt.from_excellon(line)
|
||||
self.units = stmt.units
|
||||
self.statements.append(stmt)
|
||||
|
@ -481,17 +486,20 @@ class ExcellonParser(object):
|
|||
|
||||
# T0 is used as END marker, just ignore
|
||||
if stmt.tool != 0:
|
||||
# FIXME: for weird files with no tools defined, original calc from gerbv
|
||||
# FIXME: for weird files with no tools defined, original calc
|
||||
# from gerbv
|
||||
if stmt.tool not in self.tools:
|
||||
if self._settings().units == "inch":
|
||||
diameter = (16 + 8 * stmt.tool) / 1000.0;
|
||||
diameter = (16 + 8 * stmt.tool) / 1000.0
|
||||
else:
|
||||
diameter = metric((16 + 8 * stmt.tool) / 1000.0);
|
||||
diameter = metric((16 + 8 * stmt.tool) / 1000.0)
|
||||
|
||||
tool = ExcellonTool(self._settings(), number=stmt.tool, diameter=diameter)
|
||||
tool = ExcellonTool(
|
||||
self._settings(), number=stmt.tool, diameter=diameter)
|
||||
self.tools[tool.number] = tool
|
||||
|
||||
# FIXME: need to add this tool definition inside header to make sure it is properly written
|
||||
# FIXME: need to add this tool definition inside header to
|
||||
# make sure it is properly written
|
||||
for i, s in enumerate(self.statements):
|
||||
if isinstance(s, ToolSelectionStmt) or isinstance(s, ExcellonTool):
|
||||
self.statements.insert(i, tool)
|
||||
|
@ -575,7 +583,7 @@ def detect_excellon_format(data=None, filename=None):
|
|||
and 'FILE_FORMAT' in stmt.comment]
|
||||
|
||||
detected_format = (tuple([int(val) for val in
|
||||
format_comment[0].split('=')[1].split(':')])
|
||||
format_comment[0].split('=')[1].split(':')])
|
||||
if len(format_comment) == 1 else None)
|
||||
detected_zeros = zero_statements[0] if len(zero_statements) == 1 else None
|
||||
|
||||
|
@ -637,5 +645,5 @@ def _layer_size_score(size, hole_count, hole_area):
|
|||
board_area = size[0] * size[1]
|
||||
hole_percentage = hole_area / board_area
|
||||
hole_score = (hole_percentage - 0.25) ** 2
|
||||
size_score = (board_area - 8) **2
|
||||
size_score = (board_area - 8) ** 2
|
||||
return hole_score * size_score
|
||||
|
|
|
@ -55,6 +55,7 @@ class ExcellonStatement(object):
|
|||
def to_excellon(self, settings=None):
|
||||
raise NotImplementedError('to_excellon must be implemented in a '
|
||||
'subclass')
|
||||
|
||||
def to_inch(self):
|
||||
self.units = 'inch'
|
||||
|
||||
|
@ -67,6 +68,7 @@ class ExcellonStatement(object):
|
|||
def __eq__(self, other):
|
||||
return self.__dict__ == other.__dict__
|
||||
|
||||
|
||||
class ExcellonTool(ExcellonStatement):
|
||||
""" Excellon Tool class
|
||||
|
||||
|
@ -210,7 +212,6 @@ class ExcellonTool(ExcellonStatement):
|
|||
if self.diameter is not None:
|
||||
self.diameter = inch(self.diameter)
|
||||
|
||||
|
||||
def to_metric(self):
|
||||
if self.settings.units != 'metric':
|
||||
self.settings.units = 'metric'
|
||||
|
@ -573,6 +574,7 @@ class EndOfProgramStmt(ExcellonStatement):
|
|||
if self.y is not None:
|
||||
self.y += y_offset
|
||||
|
||||
|
||||
class UnitStmt(ExcellonStatement):
|
||||
|
||||
@classmethod
|
||||
|
@ -598,6 +600,7 @@ class UnitStmt(ExcellonStatement):
|
|||
def to_metric(self):
|
||||
self.units = 'metric'
|
||||
|
||||
|
||||
class IncrementalModeStmt(ExcellonStatement):
|
||||
|
||||
@classmethod
|
||||
|
@ -689,6 +692,7 @@ class MeasuringModeStmt(ExcellonStatement):
|
|||
def to_metric(self):
|
||||
self.units = 'metric'
|
||||
|
||||
|
||||
class RouteModeStmt(ExcellonStatement):
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
|
|
|
@ -43,6 +43,7 @@ class Statement(object):
|
|||
type : string
|
||||
String identifying the statement type.
|
||||
"""
|
||||
|
||||
def __init__(self, stype, units='inch'):
|
||||
self.type = stype
|
||||
self.units = units
|
||||
|
@ -84,6 +85,7 @@ class ParamStmt(Statement):
|
|||
param : string
|
||||
Parameter type code
|
||||
"""
|
||||
|
||||
def __init__(self, param):
|
||||
Statement.__init__(self, "PARAM")
|
||||
self.param = param
|
||||
|
@ -157,8 +159,6 @@ class FSParamStmt(ParamStmt):
|
|||
|
||||
return '%FS{0}{1}X{2}Y{3}*%'.format(zero_suppression, notation, fmt, fmt)
|
||||
|
||||
|
||||
|
||||
def __str__(self):
|
||||
return ('<Format Spec: %d:%d %s zero suppression %s notation>' %
|
||||
(self.format[0], self.format[1], self.zero_suppression, self.notation))
|
||||
|
@ -293,19 +293,22 @@ class ADParamStmt(ParamStmt):
|
|||
self.d = d
|
||||
self.shape = shape
|
||||
if modifiers:
|
||||
self.modifiers = [tuple([float(x) for x in m.split("X") if len(x)]) for m in modifiers.split(",") if len(m)]
|
||||
self.modifiers = [tuple([float(x) for x in m.split("X") if len(x)])
|
||||
for m in modifiers.split(",") if len(m)]
|
||||
else:
|
||||
self.modifiers = [tuple()]
|
||||
|
||||
def to_inch(self):
|
||||
if self.units == 'metric':
|
||||
self.units = 'inch'
|
||||
self.modifiers = [tuple([inch(x) for x in modifier]) for modifier in self.modifiers]
|
||||
self.units = 'inch'
|
||||
self.modifiers = [tuple([inch(x) for x in modifier])
|
||||
for modifier in self.modifiers]
|
||||
|
||||
def to_metric(self):
|
||||
if self.units == 'inch':
|
||||
self.units = 'metric'
|
||||
self.modifiers = [tuple([metric(x) for x in modifier]) for modifier in self.modifiers]
|
||||
self.units = 'metric'
|
||||
self.modifiers = [tuple([metric(x) for x in modifier])
|
||||
for modifier in self.modifiers]
|
||||
|
||||
def to_gerber(self, settings=None):
|
||||
if any(self.modifiers):
|
||||
|
@ -382,12 +385,15 @@ class AMParamStmt(ParamStmt):
|
|||
self.primitives.append(AMOutlinePrimitive.from_gerber(primitive))
|
||||
elif primitive[0] == '5':
|
||||
self.primitives.append(AMPolygonPrimitive.from_gerber(primitive))
|
||||
elif primitive[0] =='6':
|
||||
elif primitive[0] == '6':
|
||||
self.primitives.append(AMMoirePrimitive.from_gerber(primitive))
|
||||
elif primitive[0] == '7':
|
||||
self.primitives.append(AMThermalPrimitive.from_gerber(primitive))
|
||||
self.primitives.append(
|
||||
AMThermalPrimitive.from_gerber(primitive))
|
||||
else:
|
||||
self.primitives.append(AMUnsupportPrimitive.from_gerber(primitive))
|
||||
self.primitives.append(
|
||||
AMUnsupportPrimitive.from_gerber(primitive))
|
||||
return self
|
||||
|
||||
def to_inch(self):
|
||||
if self.units == 'metric':
|
||||
|
@ -824,13 +830,17 @@ class CoordStmt(Statement):
|
|||
op = stmt_dict.get('op')
|
||||
|
||||
if x is not None:
|
||||
x = parse_gerber_value(stmt_dict.get('x'), settings.format, settings.zero_suppression)
|
||||
x = parse_gerber_value(stmt_dict.get('x'), settings.format,
|
||||
settings.zero_suppression)
|
||||
if y is not None:
|
||||
y = parse_gerber_value(stmt_dict.get('y'), settings.format, settings.zero_suppression)
|
||||
y = parse_gerber_value(stmt_dict.get('y'), settings.format,
|
||||
settings.zero_suppression)
|
||||
if i is not None:
|
||||
i = parse_gerber_value(stmt_dict.get('i'), settings.format, settings.zero_suppression)
|
||||
i = parse_gerber_value(stmt_dict.get('i'), settings.format,
|
||||
settings.zero_suppression)
|
||||
if j is not None:
|
||||
j = parse_gerber_value(stmt_dict.get('j'), settings.format, settings.zero_suppression)
|
||||
j = parse_gerber_value(stmt_dict.get('j'), settings.format,
|
||||
settings.zero_suppression)
|
||||
return cls(function, x, y, i, j, op, settings)
|
||||
|
||||
def __init__(self, function, x, y, i, j, op, settings):
|
||||
|
@ -878,13 +888,17 @@ class CoordStmt(Statement):
|
|||
if self.function:
|
||||
ret += self.function
|
||||
if self.x is not None:
|
||||
ret += 'X{0}'.format(write_gerber_value(self.x, settings.format, settings.zero_suppression))
|
||||
ret += 'X{0}'.format(write_gerber_value(self.x, settings.format,
|
||||
settings.zero_suppression))
|
||||
if self.y is not None:
|
||||
ret += 'Y{0}'.format(write_gerber_value(self.y, settings.format, settings.zero_suppression))
|
||||
ret += 'Y{0}'.format(write_gerber_value(self.y, settings.format,
|
||||
settings.zero_suppression))
|
||||
if self.i is not None:
|
||||
ret += 'I{0}'.format(write_gerber_value(self.i, settings.format, settings.zero_suppression))
|
||||
ret += 'I{0}'.format(write_gerber_value(self.i, settings.format,
|
||||
settings.zero_suppression))
|
||||
if self.j is not None:
|
||||
ret += 'J{0}'.format(write_gerber_value(self.j, settings.format, settings.zero_suppression))
|
||||
ret += 'J{0}'.format(write_gerber_value(self.j, settings.format,
|
||||
settings.zero_suppression))
|
||||
if self.op:
|
||||
ret += self.op
|
||||
return ret + '*'
|
||||
|
@ -956,6 +970,7 @@ class CoordStmt(Statement):
|
|||
class ApertureStmt(Statement):
|
||||
""" Aperture Statement
|
||||
"""
|
||||
|
||||
def __init__(self, d, deprecated=None):
|
||||
Statement.__init__(self, "APERTURE")
|
||||
self.d = int(d)
|
||||
|
@ -989,6 +1004,7 @@ class CommentStmt(Statement):
|
|||
class EofStmt(Statement):
|
||||
""" EOF Statement
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
Statement.__init__(self, "EOF")
|
||||
|
||||
|
@ -1043,6 +1059,7 @@ class RegionModeStmt(Statement):
|
|||
class UnknownStmt(Statement):
|
||||
""" Unknown Statement
|
||||
"""
|
||||
|
||||
def __init__(self, line):
|
||||
Statement.__init__(self, "UNKNOWN")
|
||||
self.line = line
|
||||
|
@ -1052,4 +1069,3 @@ class UnknownStmt(Statement):
|
|||
|
||||
def __str__(self):
|
||||
return '<Unknown Statement: \'%s\'>' % self.line
|
||||
|
||||
|
|
|
@ -95,7 +95,8 @@ def sort_layers(layers):
|
|||
'bottompaste', 'drill', ]
|
||||
output = []
|
||||
drill_layers = [layer for layer in layers if layer.layer_class == 'drill']
|
||||
internal_layers = list(sorted([layer for layer in layers if layer.layer_class == 'internal']))
|
||||
internal_layers = list(sorted([layer for layer in layers
|
||||
if layer.layer_class == 'internal']))
|
||||
|
||||
for layer_class in layer_order:
|
||||
if layer_class == 'internal':
|
||||
|
@ -151,6 +152,8 @@ class PCBLayer(object):
|
|||
else:
|
||||
return None
|
||||
|
||||
def __repr__(self):
|
||||
return '<PCBLayer: {}>'.format(self.layer_class)
|
||||
|
||||
class DrillLayer(PCBLayer):
|
||||
@classmethod
|
||||
|
@ -163,6 +166,7 @@ class DrillLayer(PCBLayer):
|
|||
|
||||
|
||||
class InternalLayer(PCBLayer):
|
||||
|
||||
@classmethod
|
||||
def from_gerber(cls, camfile):
|
||||
filename = camfile.filename
|
||||
|
@ -208,6 +212,7 @@ class InternalLayer(PCBLayer):
|
|||
|
||||
|
||||
class LayerSet(object):
|
||||
|
||||
def __init__(self, name, layers, **kwargs):
|
||||
super(LayerSet, self).__init__(**kwargs)
|
||||
self.name = name
|
||||
|
|
|
@ -22,6 +22,7 @@ CAM File Operations
|
|||
"""
|
||||
import copy
|
||||
|
||||
|
||||
def to_inch(cam_file):
|
||||
""" Convert Gerber or Excellon file units to imperial
|
||||
|
||||
|
@ -39,6 +40,7 @@ def to_inch(cam_file):
|
|||
cam_file.to_inch()
|
||||
return cam_file
|
||||
|
||||
|
||||
def to_metric(cam_file):
|
||||
""" Convert Gerber or Excellon file units to metric
|
||||
|
||||
|
@ -56,6 +58,7 @@ def to_metric(cam_file):
|
|||
cam_file.to_metric()
|
||||
return cam_file
|
||||
|
||||
|
||||
def offset(cam_file, x_offset, y_offset):
|
||||
""" Offset a Cam file by a specified amount in the X and Y directions.
|
||||
|
||||
|
@ -79,6 +82,7 @@ def offset(cam_file, x_offset, y_offset):
|
|||
cam_file.offset(x_offset, y_offset)
|
||||
return cam_file
|
||||
|
||||
|
||||
def scale(cam_file, x_scale, y_scale):
|
||||
""" Scale a Cam file by a specified amount in the X and Y directions.
|
||||
|
||||
|
@ -101,6 +105,7 @@ def scale(cam_file, x_scale, y_scale):
|
|||
# TODO
|
||||
pass
|
||||
|
||||
|
||||
def rotate(cam_file, angle):
|
||||
""" Rotate a Cam file a specified amount about the origin.
|
||||
|
||||
|
|
|
@ -63,13 +63,15 @@ class PCB(object):
|
|||
|
||||
@property
|
||||
def top_layers(self):
|
||||
board_layers = [l for l in reversed(self.layers) if l.layer_class in ('topsilk', 'topmask', 'top')]
|
||||
board_layers = [l for l in reversed(self.layers) if l.layer_class in
|
||||
('topsilk', 'topmask', 'top')]
|
||||
drill_layers = [l for l in self.drill_layers if 'top' in l.layers]
|
||||
return board_layers + drill_layers
|
||||
|
||||
@property
|
||||
def bottom_layers(self):
|
||||
board_layers = [l for l in self.layers if l.layer_class in ('bottomsilk', 'bottommask', 'bottom')]
|
||||
board_layers = [l for l in self.layers if l.layer_class in
|
||||
('bottomsilk', 'bottommask', 'bottom')]
|
||||
drill_layers = [l for l in self.drill_layers if 'bottom' in l.layers]
|
||||
return board_layers + drill_layers
|
||||
|
||||
|
@ -77,11 +79,17 @@ class PCB(object):
|
|||
def drill_layers(self):
|
||||
return [l for l in self.layers if l.layer_class == 'drill']
|
||||
|
||||
@property
|
||||
def copper_layers(self):
|
||||
return [layer for layer in self.layers if layer.layer_class in
|
||||
('top', 'bottom', 'internal')]
|
||||
|
||||
@property
|
||||
def layer_count(self):
|
||||
""" Number of *COPPER* layers
|
||||
"""
|
||||
return len([l for l in self.layers if l.layer_class in ('top', 'bottom', 'internal')])
|
||||
return len([l for l in self.layers if l.layer_class in
|
||||
('top', 'bottom', 'internal')])
|
||||
|
||||
@property
|
||||
def board_bounds(self):
|
||||
|
@ -91,4 +99,3 @@ class PCB(object):
|
|||
for layer in self.layers:
|
||||
if layer.layer_class == 'top':
|
||||
return layer.bounds
|
||||
|
||||
|
|
1091
gerber/primitives.py
1091
gerber/primitives.py
Plik diff jest za duży
Load Diff
|
@ -17,8 +17,6 @@
|
|||
|
||||
|
||||
import cairocffi as cairo
|
||||
from operator import mul
|
||||
import math
|
||||
import tempfile
|
||||
|
||||
from .render import GerberContext, RenderSettings
|
||||
|
@ -32,11 +30,14 @@ except(ImportError):
|
|||
|
||||
|
||||
class GerberCairoContext(GerberContext):
|
||||
|
||||
def __init__(self, scale=300):
|
||||
GerberContext.__init__(self)
|
||||
super(GerberCairoContext, self).__init__()
|
||||
self.scale = (scale, scale)
|
||||
self.surface = None
|
||||
self.ctx = None
|
||||
self.active_layer = None
|
||||
self.output_ctx = None
|
||||
self.bg = False
|
||||
self.mask = None
|
||||
self.mask_ctx = None
|
||||
|
@ -46,37 +47,40 @@ class GerberCairoContext(GerberContext):
|
|||
|
||||
@property
|
||||
def origin_in_pixels(self):
|
||||
return tuple(map(mul, self.origin_in_inch, self.scale)) if self.origin_in_inch is not None else (0.0, 0.0)
|
||||
return (self.scale_point(self.origin_in_inch)
|
||||
if self.origin_in_inch is not None else (0.0, 0.0))
|
||||
|
||||
@property
|
||||
def size_in_pixels(self):
|
||||
return tuple(map(mul, self.size_in_inch, self.scale)) if self.size_in_inch is not None else (0.0, 0.0)
|
||||
return (self.scale_point(self.size_in_inch)
|
||||
if self.size_in_inch is not None else (0.0, 0.0))
|
||||
|
||||
def set_bounds(self, bounds, new_surface=False):
|
||||
origin_in_inch = (bounds[0][0], bounds[1][0])
|
||||
size_in_inch = (abs(bounds[0][1] - bounds[0][0]), abs(bounds[1][1] - bounds[1][0]))
|
||||
size_in_pixels = tuple(map(mul, size_in_inch, self.scale))
|
||||
size_in_inch = (abs(bounds[0][1] - bounds[0][0]),
|
||||
abs(bounds[1][1] - bounds[1][0]))
|
||||
size_in_pixels = self.scale_point(size_in_inch)
|
||||
self.origin_in_inch = origin_in_inch if self.origin_in_inch is None else self.origin_in_inch
|
||||
self.size_in_inch = size_in_inch if self.size_in_inch is None else self.size_in_inch
|
||||
if (self.surface is None) or new_surface:
|
||||
self.surface_buffer = tempfile.NamedTemporaryFile()
|
||||
self.surface = cairo.SVGSurface(self.surface_buffer, size_in_pixels[0], size_in_pixels[1])
|
||||
self.ctx = cairo.Context(self.surface)
|
||||
self.ctx.set_fill_rule(cairo.FILL_RULE_EVEN_ODD)
|
||||
self.ctx.scale(1, -1)
|
||||
self.ctx.translate(-(origin_in_inch[0] * self.scale[0]), (-origin_in_inch[1]*self.scale[0]) - size_in_pixels[1])
|
||||
self.mask = cairo.SVGSurface(None, size_in_pixels[0], size_in_pixels[1])
|
||||
self.mask_ctx = cairo.Context(self.mask)
|
||||
self.mask_ctx.set_fill_rule(cairo.FILL_RULE_EVEN_ODD)
|
||||
self.mask_ctx.scale(1, -1)
|
||||
self.mask_ctx.translate(-(origin_in_inch[0] * self.scale[0]), (-origin_in_inch[1]*self.scale[0]) - size_in_pixels[1])
|
||||
self._xform_matrix = cairo.Matrix(xx=1.0, yy=-1.0, x0=-self.origin_in_pixels[0], y0=self.size_in_pixels[1] + self.origin_in_pixels[1])
|
||||
self.surface = cairo.SVGSurface(
|
||||
self.surface_buffer, size_in_pixels[0], size_in_pixels[1])
|
||||
self.output_ctx = cairo.Context(self.surface)
|
||||
self.output_ctx.set_fill_rule(cairo.FILL_RULE_EVEN_ODD)
|
||||
self.output_ctx.scale(1, -1)
|
||||
self.output_ctx.translate(-(origin_in_inch[0] * self.scale[0]),
|
||||
(-origin_in_inch[1] * self.scale[0]) - size_in_pixels[1])
|
||||
self._xform_matrix = cairo.Matrix(xx=1.0, yy=-1.0,
|
||||
x0=-self.origin_in_pixels[0],
|
||||
y0=self.size_in_pixels[1] + self.origin_in_pixels[1])
|
||||
|
||||
def render_layers(self, layers, filename, theme=THEMES['default']):
|
||||
""" Render a set of layers
|
||||
"""
|
||||
self.set_bounds(layers[0].bounds, True)
|
||||
self._paint_background(True)
|
||||
|
||||
for layer in layers:
|
||||
self._render_layer(layer, theme)
|
||||
self.dump(filename)
|
||||
|
@ -114,158 +118,181 @@ class GerberCairoContext(GerberContext):
|
|||
self.color = settings.color
|
||||
self.alpha = settings.alpha
|
||||
self.invert = settings.invert
|
||||
|
||||
# Get a new clean layer to render on
|
||||
self._new_render_layer()
|
||||
if settings.mirror:
|
||||
raise Warning('mirrored layers aren\'t supported yet...')
|
||||
if self.invert:
|
||||
self._clear_mask()
|
||||
for prim in layer.primitives:
|
||||
self.render(prim)
|
||||
if self.invert:
|
||||
self._render_mask()
|
||||
# Add layer to image
|
||||
self._flatten()
|
||||
|
||||
def _render_line(self, line, color):
|
||||
start = map(mul, line.start, self.scale)
|
||||
end = map(mul, line.end, self.scale)
|
||||
start = [pos * scale for pos, scale in zip(line.start, self.scale)]
|
||||
end = [pos * scale for pos, scale in zip(line.end, self.scale)]
|
||||
if not self.invert:
|
||||
ctx = self.ctx
|
||||
ctx.set_source_rgba(*color, alpha=self.alpha)
|
||||
ctx.set_operator(cairo.OPERATOR_OVER if line.level_polarity == "dark" else cairo.OPERATOR_CLEAR)
|
||||
self.ctx.set_source_rgba(*color, alpha=self.alpha)
|
||||
self.ctx.set_operator(cairo.OPERATOR_OVER
|
||||
if line.level_polarity == 'dark'
|
||||
else cairo.OPERATOR_CLEAR)
|
||||
else:
|
||||
ctx = self.mask_ctx
|
||||
ctx.set_source_rgba(0.0, 0.0, 0.0, 1.0)
|
||||
ctx.set_operator(cairo.OPERATOR_CLEAR)
|
||||
self.ctx.set_source_rgba(0.0, 0.0, 0.0, 1.0)
|
||||
self.ctx.set_operator(cairo.OPERATOR_CLEAR)
|
||||
if isinstance(line.aperture, Circle):
|
||||
width = line.aperture.diameter
|
||||
ctx.set_line_width(width * self.scale[0])
|
||||
ctx.set_line_cap(cairo.LINE_CAP_ROUND)
|
||||
ctx.move_to(*start)
|
||||
ctx.line_to(*end)
|
||||
ctx.stroke()
|
||||
self.ctx.set_line_width(width * self.scale[0])
|
||||
self.ctx.set_line_cap(cairo.LINE_CAP_ROUND)
|
||||
self.ctx.move_to(*start)
|
||||
self.ctx.line_to(*end)
|
||||
self.ctx.stroke()
|
||||
elif isinstance(line.aperture, Rectangle):
|
||||
points = [tuple(map(mul, x, self.scale)) for x in line.vertices]
|
||||
ctx.set_line_width(0)
|
||||
ctx.move_to(*points[0])
|
||||
points = [self.scale_point(x) for x in line.vertices]
|
||||
self.ctx.set_line_width(0)
|
||||
self.ctx.move_to(*points[0])
|
||||
for point in points[1:]:
|
||||
ctx.line_to(*point)
|
||||
ctx.fill()
|
||||
self.ctx.line_to(*point)
|
||||
self.ctx.fill()
|
||||
|
||||
def _render_arc(self, arc, color):
|
||||
center = map(mul, arc.center, self.scale)
|
||||
start = map(mul, arc.start, self.scale)
|
||||
end = map(mul, arc.end, self.scale)
|
||||
center = self.scale_point(arc.center)
|
||||
start = self.scale_point(arc.start)
|
||||
end = self.scale_point(arc.end)
|
||||
radius = self.scale[0] * arc.radius
|
||||
angle1 = arc.start_angle
|
||||
angle2 = arc.end_angle
|
||||
width = arc.aperture.diameter if arc.aperture.diameter != 0 else 0.001
|
||||
if not self.invert:
|
||||
ctx = self.ctx
|
||||
ctx.set_source_rgba(*color, alpha=self.alpha)
|
||||
ctx.set_operator(cairo.OPERATOR_OVER if arc.level_polarity == "dark" else cairo.OPERATOR_CLEAR)
|
||||
self.ctx.set_source_rgba(*color, alpha=self.alpha)
|
||||
self.ctx.set_operator(cairo.OPERATOR_OVER
|
||||
if arc.level_polarity == 'dark'
|
||||
else cairo.OPERATOR_CLEAR)
|
||||
else:
|
||||
ctx = self.mask_ctx
|
||||
ctx.set_source_rgba(0.0, 0.0, 0.0, 1.0)
|
||||
ctx.set_operator(cairo.OPERATOR_CLEAR)
|
||||
ctx.set_line_width(width * self.scale[0])
|
||||
ctx.set_line_cap(cairo.LINE_CAP_ROUND)
|
||||
ctx.move_to(*start) # You actually have to do this...
|
||||
self.ctx.set_source_rgba(0.0, 0.0, 0.0, 1.0)
|
||||
self.ctx.set_operator(cairo.OPERATOR_CLEAR)
|
||||
self.ctx.set_line_width(width * self.scale[0])
|
||||
self.ctx.set_line_cap(cairo.LINE_CAP_ROUND)
|
||||
self.ctx.move_to(*start) # You actually have to do this...
|
||||
if arc.direction == 'counterclockwise':
|
||||
ctx.arc(*center, radius=radius, angle1=angle1, angle2=angle2)
|
||||
self.ctx.arc(*center, radius=radius, angle1=angle1, angle2=angle2)
|
||||
else:
|
||||
ctx.arc_negative(*center, radius=radius, angle1=angle1, angle2=angle2)
|
||||
ctx.move_to(*end) # ...lame
|
||||
self.ctx.arc_negative(*center, radius=radius,
|
||||
angle1=angle1, angle2=angle2)
|
||||
self.ctx.move_to(*end) # ...lame
|
||||
|
||||
def _render_region(self, region, color):
|
||||
if not self.invert:
|
||||
ctx = self.ctx
|
||||
ctx.set_source_rgba(*color, alpha=self.alpha)
|
||||
ctx.set_operator(cairo.OPERATOR_OVER if region.level_polarity == "dark" else cairo.OPERATOR_CLEAR)
|
||||
self.ctx.set_source_rgba(*color, alpha=self.alpha)
|
||||
self.ctx.set_operator(cairo.OPERATOR_OVER
|
||||
if region.level_polarity == 'dark'
|
||||
else cairo.OPERATOR_CLEAR)
|
||||
else:
|
||||
ctx = self.mask_ctx
|
||||
ctx.set_source_rgba(0.0, 0.0, 0.0, 1.0)
|
||||
ctx.set_operator(cairo.OPERATOR_CLEAR)
|
||||
ctx.set_line_width(0)
|
||||
ctx.set_line_cap(cairo.LINE_CAP_ROUND)
|
||||
ctx.move_to(*tuple(map(mul, region.primitives[0].start, self.scale)))
|
||||
for p in region.primitives:
|
||||
if isinstance(p, Line):
|
||||
ctx.line_to(*tuple(map(mul, p.end, self.scale)))
|
||||
self.ctx.set_source_rgba(0.0, 0.0, 0.0, 1.0)
|
||||
self.ctx.set_operator(cairo.OPERATOR_CLEAR)
|
||||
self.ctx.set_line_width(0)
|
||||
self.ctx.set_line_cap(cairo.LINE_CAP_ROUND)
|
||||
self.ctx.move_to(*self.scale_point(region.primitives[0].start))
|
||||
for prim in region.primitives:
|
||||
if isinstance(prim, Line):
|
||||
self.ctx.line_to(*self.scale_point(prim.end))
|
||||
else:
|
||||
center = map(mul, p.center, self.scale)
|
||||
start = map(mul, p.start, self.scale)
|
||||
end = map(mul, p.end, self.scale)
|
||||
radius = self.scale[0] * p.radius
|
||||
angle1 = p.start_angle
|
||||
angle2 = p.end_angle
|
||||
if p.direction == 'counterclockwise':
|
||||
ctx.arc(*center, radius=radius, angle1=angle1, angle2=angle2)
|
||||
center = self.scale_point(prim.center)
|
||||
radius = self.scale[0] * prim.radius
|
||||
angle1 = prim.start_angle
|
||||
angle2 = prim.end_angle
|
||||
if prim.direction == 'counterclockwise':
|
||||
self.ctx.arc(*center, radius=radius,
|
||||
angle1=angle1, angle2=angle2)
|
||||
else:
|
||||
ctx.arc_negative(*center, radius=radius, angle1=angle1, angle2=angle2)
|
||||
ctx.fill()
|
||||
self.ctx.arc_negative(*center, radius=radius,
|
||||
angle1=angle1, angle2=angle2)
|
||||
self.ctx.fill()
|
||||
|
||||
def _render_circle(self, circle, color):
|
||||
center = tuple(map(mul, circle.position, self.scale))
|
||||
center = self.scale_point(circle.position)
|
||||
if not self.invert:
|
||||
ctx = self.ctx
|
||||
ctx.set_source_rgba(*color, alpha=self.alpha)
|
||||
ctx.set_operator(cairo.OPERATOR_OVER if circle.level_polarity == "dark" else cairo.OPERATOR_CLEAR)
|
||||
self.ctx.set_source_rgba(*color, alpha=self.alpha)
|
||||
self.ctx.set_operator(
|
||||
cairo.OPERATOR_OVER if circle.level_polarity == 'dark' else cairo.OPERATOR_CLEAR)
|
||||
else:
|
||||
ctx = self.mask_ctx
|
||||
ctx.set_source_rgba(0.0, 0.0, 0.0, 1.0)
|
||||
ctx.set_operator(cairo.OPERATOR_CLEAR)
|
||||
ctx.set_line_width(0)
|
||||
ctx.arc(*center, radius=circle.radius * self.scale[0], angle1=0, angle2=2 * math.pi)
|
||||
ctx.fill()
|
||||
self.ctx.set_source_rgba(0.0, 0.0, 0.0, 1.0)
|
||||
self.ctx.set_operator(cairo.OPERATOR_CLEAR)
|
||||
self.ctx.set_line_width(0)
|
||||
self.ctx.arc(*center, radius=circle.radius *
|
||||
self.scale[0], angle1=0, angle2=2 * math.pi)
|
||||
self.ctx.fill()
|
||||
|
||||
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)))
|
||||
lower_left = self.scale_point(rectangle.lower_left)
|
||||
width, height = tuple([abs(coord) for coord in self.scale_point((rectangle.width, rectangle.height))])
|
||||
|
||||
if not self.invert:
|
||||
ctx = self.ctx
|
||||
ctx.set_source_rgba(*color, alpha=self.alpha)
|
||||
ctx.set_operator(cairo.OPERATOR_OVER if rectangle.level_polarity == "dark" else cairo.OPERATOR_CLEAR)
|
||||
self.ctx.set_source_rgba(*color, alpha=self.alpha)
|
||||
self.ctx.set_operator(
|
||||
cairo.OPERATOR_OVER if rectangle.level_polarity == 'dark' else cairo.OPERATOR_CLEAR)
|
||||
else:
|
||||
ctx = self.mask_ctx
|
||||
ctx.set_source_rgba(0.0, 0.0, 0.0, 1.0)
|
||||
ctx.set_operator(cairo.OPERATOR_CLEAR)
|
||||
ctx.set_line_width(0)
|
||||
ctx.rectangle(*ll, width=width, height=height)
|
||||
ctx.fill()
|
||||
self.ctx.set_source_rgba(0.0, 0.0, 0.0, 1.0)
|
||||
self.ctx.set_operator(cairo.OPERATOR_CLEAR)
|
||||
self.ctx.set_line_width(0)
|
||||
self.ctx.rectangle(*lower_left, width=width, height=height)
|
||||
self.ctx.fill()
|
||||
|
||||
def _render_obround(self, obround, color):
|
||||
self._render_circle(obround.subshapes['circle1'], color)
|
||||
self._render_circle(obround.subshapes['circle2'], color)
|
||||
self._render_rectangle(obround.subshapes['rectangle'], color)
|
||||
|
||||
def _render_drill(self, circle, color):
|
||||
def _render_drill(self, circle, color=None):
|
||||
color = color if color is not None else self.drill_color
|
||||
self._render_circle(circle, color)
|
||||
|
||||
def _render_test_record(self, primitive, color):
|
||||
position = tuple(map(add, primitive.position, self.origin_in_inch))
|
||||
position = [pos + origin for pos, origin in zip(primitive.position, self.origin_in_inch)]
|
||||
self.ctx.set_operator(cairo.OPERATOR_OVER)
|
||||
self.ctx.select_font_face('monospace', cairo.FONT_SLANT_NORMAL, cairo.FONT_WEIGHT_BOLD)
|
||||
self.ctx.select_font_face(
|
||||
'monospace', cairo.FONT_SLANT_NORMAL, cairo.FONT_WEIGHT_BOLD)
|
||||
self.ctx.set_font_size(13)
|
||||
self._render_circle(Circle(position, 0.015), color)
|
||||
self.ctx.set_source_rgba(*color, alpha=self.alpha)
|
||||
self.ctx.set_operator(cairo.OPERATOR_OVER if primitive.level_polarity == "dark" else cairo.OPERATOR_CLEAR)
|
||||
self.ctx.move_to(*[self.scale[0] * (coord + 0.015) for coord in position])
|
||||
self.ctx.set_operator(
|
||||
cairo.OPERATOR_OVER if primitive.level_polarity == 'dark' else cairo.OPERATOR_CLEAR)
|
||||
self.ctx.move_to(*[self.scale[0] * (coord + 0.015)
|
||||
for coord in position])
|
||||
self.ctx.scale(1, -1)
|
||||
self.ctx.show_text(primitive.net_name)
|
||||
self.ctx.scale(1, -1)
|
||||
|
||||
def _clear_mask(self):
|
||||
self.mask_ctx.set_operator(cairo.OPERATOR_OVER)
|
||||
self.mask_ctx.set_source_rgba(*self.color, alpha=self.alpha)
|
||||
self.mask_ctx.paint()
|
||||
def _new_render_layer(self, color=None):
|
||||
size_in_pixels = self.scale_point(self.size_in_inch)
|
||||
layer = cairo.SVGSurface(None, size_in_pixels[0], size_in_pixels[1])
|
||||
ctx = cairo.Context(layer)
|
||||
ctx.set_fill_rule(cairo.FILL_RULE_EVEN_ODD)
|
||||
ctx.scale(1, -1)
|
||||
ctx.translate(-(self.origin_in_inch[0] * self.scale[0]),
|
||||
(-self.origin_in_inch[1] * self.scale[0])
|
||||
- size_in_pixels[1])
|
||||
if self.invert:
|
||||
ctx.set_operator(cairo.OPERATOR_OVER)
|
||||
ctx.set_source_rgba(*self.color, alpha=self.alpha)
|
||||
ctx.paint()
|
||||
self.ctx = ctx
|
||||
self.active_layer = layer
|
||||
|
||||
def _render_mask(self):
|
||||
self.ctx.set_operator(cairo.OPERATOR_OVER)
|
||||
ptn = cairo.SurfacePattern(self.mask)
|
||||
def _flatten(self):
|
||||
self.output_ctx.set_operator(cairo.OPERATOR_OVER)
|
||||
ptn = cairo.SurfacePattern(self.active_layer)
|
||||
ptn.set_matrix(self._xform_matrix)
|
||||
self.ctx.set_source(ptn)
|
||||
self.ctx.paint()
|
||||
self.output_ctx.set_source(ptn)
|
||||
self.output_ctx.paint()
|
||||
self.ctx = None
|
||||
self.active_layer = None
|
||||
|
||||
def _paint_background(self, force=False):
|
||||
if (not self.bg) or force:
|
||||
self.bg = True
|
||||
self.ctx.set_source_rgba(*self.background_color, alpha=1.0)
|
||||
self.ctx.paint()
|
||||
self.output_ctx.set_operator(cairo.OPERATOR_OVER)
|
||||
self.output_ctx.set_source_rgba(*self.background_color, alpha=1.0)
|
||||
self.output_ctx.paint()
|
||||
|
||||
def scale_point(self, point):
|
||||
return tuple([coord * scale for coord, scale in zip(point, self.scale)])
|
||||
|
|
|
@ -57,12 +57,14 @@ class GerberContext(object):
|
|||
alpha : float
|
||||
Rendering opacity. Between 0.0 (transparent) and 1.0 (opaque.)
|
||||
"""
|
||||
|
||||
def __init__(self, units='inch'):
|
||||
self._units = units
|
||||
self._color = (0.7215, 0.451, 0.200)
|
||||
self._background_color = (0.0, 0.0, 0.0)
|
||||
self._alpha = 1.0
|
||||
self._invert = False
|
||||
self.ctx = None
|
||||
|
||||
@property
|
||||
def units(self):
|
||||
|
@ -132,8 +134,7 @@ class GerberContext(object):
|
|||
self._invert = invert
|
||||
|
||||
def render(self, primitive):
|
||||
color = (self.color if primitive.level_polarity == 'dark'
|
||||
else self.background_color)
|
||||
color = self.color
|
||||
if isinstance(primitive, Line):
|
||||
self._render_line(primitive, color)
|
||||
elif isinstance(primitive, Arc):
|
||||
|
@ -155,6 +156,7 @@ class GerberContext(object):
|
|||
else:
|
||||
return
|
||||
|
||||
|
||||
def _render_line(self, primitive, color):
|
||||
pass
|
||||
|
||||
|
@ -184,9 +186,9 @@ class GerberContext(object):
|
|||
|
||||
|
||||
class RenderSettings(object):
|
||||
|
||||
def __init__(self, color=(0.0, 0.0, 0.0), alpha=1.0, invert=False, mirror=False):
|
||||
self.color = color
|
||||
self.alpha = alpha
|
||||
self.invert = invert
|
||||
self.mirror = mirror
|
||||
|
||||
|
|
|
@ -23,7 +23,7 @@ COLORS = {
|
|||
'white': (1.0, 1.0, 1.0),
|
||||
'red': (1.0, 0.0, 0.0),
|
||||
'green': (0.0, 1.0, 0.0),
|
||||
'blue' : (0.0, 0.0, 1.0),
|
||||
'blue': (0.0, 0.0, 1.0),
|
||||
'fr-4': (0.290, 0.345, 0.0),
|
||||
'green soldermask': (0.0, 0.612, 0.396),
|
||||
'blue soldermask': (0.059, 0.478, 0.651),
|
||||
|
@ -36,6 +36,7 @@ COLORS = {
|
|||
|
||||
|
||||
class Theme(object):
|
||||
|
||||
def __init__(self, name=None, **kwargs):
|
||||
self.name = 'Default' if name is None else name
|
||||
self.background = kwargs.get('background', RenderSettings(COLORS['black'], alpha=0.0))
|
||||
|
@ -67,4 +68,3 @@ THEMES = {
|
|||
topmask=RenderSettings(COLORS['blue soldermask'], alpha=0.8, invert=True),
|
||||
bottommask=RenderSettings(COLORS['blue soldermask'], alpha=0.8, invert=True)),
|
||||
}
|
||||
|
||||
|
|
|
@ -93,6 +93,7 @@ class GerberFile(CamFile):
|
|||
`bounds` is stored as ((min x, max x), (min y, max y))
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, statements, settings, primitives, filename=None):
|
||||
super(GerberFile, self).__init__(statements, settings, primitives, filename)
|
||||
|
||||
|
@ -181,7 +182,8 @@ class GerberParser(object):
|
|||
DEPRECATED_FORMAT = re.compile(r'(?P<format>G9[01])\*')
|
||||
# end deprecated
|
||||
|
||||
PARAMS = (FS, MO, LP, AD_CIRCLE, AD_RECT, AD_OBROUND, AD_POLY, AD_MACRO, AM, AS, IN, IP, IR, MI, OF, SF, LN)
|
||||
PARAMS = (FS, MO, LP, AD_CIRCLE, AD_RECT, AD_OBROUND, AD_POLY,
|
||||
AD_MACRO, AM, AS, IN, IP, IR, MI, OF, SF, LN)
|
||||
|
||||
PARAM_STMT = [re.compile(r"%?{0}\*%?".format(p)) for p in PARAMS]
|
||||
|
||||
|
@ -362,7 +364,8 @@ class GerberParser(object):
|
|||
# deprecated codes
|
||||
(deprecated_unit, r) = _match_one(self.DEPRECATED_UNIT, line)
|
||||
if deprecated_unit:
|
||||
stmt = MOParamStmt(param="MO", mo="inch" if "G70" in deprecated_unit["mode"] else "metric")
|
||||
stmt = MOParamStmt(param="MO", mo="inch" if "G70" in
|
||||
deprecated_unit["mode"] else "metric")
|
||||
self.settings.units = stmt.mode
|
||||
yield stmt
|
||||
line = r
|
||||
|
@ -436,8 +439,9 @@ class GerberParser(object):
|
|||
height = modifiers[0][1]
|
||||
aperture = Obround(position=None, width=width, height=height)
|
||||
elif shape == 'P':
|
||||
# FIXME: not supported yet?
|
||||
pass
|
||||
diameter = modifiers[0][0]
|
||||
sides = modifiers[0][1]
|
||||
aperture = Polygon(position=None, radius=diameter/2.0, sides=sides)
|
||||
else:
|
||||
aperture = self.macros[shape].build(modifiers)
|
||||
|
||||
|
@ -446,7 +450,8 @@ class GerberParser(object):
|
|||
def _evaluate_mode(self, stmt):
|
||||
if stmt.type == 'RegionMode':
|
||||
if self.region_mode == 'on' and stmt.mode == 'off':
|
||||
self.primitives.append(Region(self.current_region, level_polarity=self.level_polarity))
|
||||
self.primitives.append(Region(self.current_region,
|
||||
level_polarity=self.level_polarity))
|
||||
self.current_region = None
|
||||
self.region_mode = stmt.mode
|
||||
elif stmt.type == 'QuadrantMode':
|
||||
|
@ -476,7 +481,8 @@ class GerberParser(object):
|
|||
self.interpolation = 'linear'
|
||||
elif stmt.function in ('G02', 'G2', 'G03', 'G3'):
|
||||
self.interpolation = 'arc'
|
||||
self.direction = ('clockwise' if stmt.function in ('G02', 'G2') else 'counterclockwise')
|
||||
self.direction = ('clockwise' if stmt.function in
|
||||
('G02', 'G2') else 'counterclockwise')
|
||||
|
||||
if stmt.op:
|
||||
self.op = stmt.op
|
||||
|
@ -490,43 +496,71 @@ class GerberParser(object):
|
|||
|
||||
if self.interpolation == 'linear':
|
||||
if self.region_mode == 'off':
|
||||
self.primitives.append(Line(start, end, self.apertures[self.aperture], level_polarity=self.level_polarity, units=self.settings.units))
|
||||
self.primitives.append(Line(start, end,
|
||||
self.apertures[self.aperture],
|
||||
level_polarity=self.level_polarity,
|
||||
units=self.settings.units))
|
||||
else:
|
||||
# from gerber spec revision J3, Section 4.5, page 55:
|
||||
# The segments are not graphics objects in themselves; segments are part of region which is the graphics object. The segments have no thickness.
|
||||
# The current aperture is associated with the region. This has no graphical effect, but allows all its attributes to be applied to the region.
|
||||
# The current aperture is associated with the region. This
|
||||
# has no graphical effect, but allows all its attributes to
|
||||
# be applied to the region.
|
||||
if self.current_region is None:
|
||||
self.current_region = [Line(start, end, self.apertures.get(self.aperture, Circle((0,0), 0)), level_polarity=self.level_polarity, units=self.settings.units),]
|
||||
else:
|
||||
self.current_region.append(Line(start, end, self.apertures.get(self.aperture, Circle((0,0), 0)), level_polarity=self.level_polarity, units=self.settings.units))
|
||||
self.current_region = [Line(start, end,
|
||||
self.apertures.get(self.aperture,
|
||||
Circle((0, 0), 0)),
|
||||
level_polarity=self.level_polarity,
|
||||
units=self.settings.units), ]
|
||||
else:
|
||||
self.current_region.append(Line(start, end,
|
||||
self.apertures.get(self.aperture,
|
||||
Circle((0, 0), 0)),
|
||||
level_polarity=self.level_polarity,
|
||||
units=self.settings.units))
|
||||
else:
|
||||
i = 0 if stmt.i is None else stmt.i
|
||||
j = 0 if stmt.j is None else stmt.j
|
||||
center = (start[0] + i, start[1] + j)
|
||||
if self.region_mode == 'off':
|
||||
self.primitives.append(Arc(start, end, center, self.direction, self.apertures[self.aperture], level_polarity=self.level_polarity, units=self.settings.units))
|
||||
self.primitives.append(Arc(start, end, center, self.direction,
|
||||
self.apertures[self.aperture],
|
||||
level_polarity=self.level_polarity,
|
||||
units=self.settings.units))
|
||||
else:
|
||||
if self.current_region is None:
|
||||
self.current_region = [Arc(start, end, center, self.direction, self.apertures[self.aperture], level_polarity=self.level_polarity, units=self.settings.units),]
|
||||
self.current_region = [Arc(start, end, center, self.direction,
|
||||
self.apertures[self.aperture],
|
||||
level_polarity=self.level_polarity,
|
||||
units=self.settings.units), ]
|
||||
else:
|
||||
self.current_region.append(Arc(start, end, center, self.direction, self.apertures[self.aperture], level_polarity=self.level_polarity, units=self.settings.units))
|
||||
self.current_region.append(Arc(start, end, center, self.direction,
|
||||
self.apertures[self.aperture],
|
||||
level_polarity=self.level_polarity,
|
||||
units=self.settings.units))
|
||||
|
||||
elif self.op == "D02":
|
||||
pass
|
||||
|
||||
elif self.op == "D03":
|
||||
primitive = copy.deepcopy(self.apertures[self.aperture])
|
||||
# XXX: temporary fix because there are no primitives for Macros and Polygon
|
||||
|
||||
|
||||
if primitive is not None:
|
||||
# XXX: just to make it easy to spot
|
||||
if isinstance(primitive, type([])):
|
||||
print(primitive[0].to_gerber())
|
||||
else:
|
||||
|
||||
if not isinstance(primitive, AMParamStmt):
|
||||
primitive.position = (x, y)
|
||||
primitive.level_polarity = self.level_polarity
|
||||
primitive.units = self.settings.units
|
||||
self.primitives.append(primitive)
|
||||
|
||||
else:
|
||||
# Aperture Macro
|
||||
for am_prim in primitive.primitives:
|
||||
renderable = am_prim.to_primitive((x, y),
|
||||
self.level_polarity,
|
||||
self.settings.units)
|
||||
if renderable is not None:
|
||||
self.primitives.append(renderable)
|
||||
self.x, self.y = x, y
|
||||
|
||||
def _evaluate_aperture(self, stmt):
|
||||
|
|
|
@ -7,6 +7,7 @@ from .tests import *
|
|||
from ..am_statements import *
|
||||
from ..am_statements import inch, metric
|
||||
|
||||
|
||||
def test_AMPrimitive_ctor():
|
||||
for exposure in ('on', 'off', 'ON', 'OFF'):
|
||||
for code in (0, 1, 2, 4, 5, 6, 7, 20, 21, 22):
|
||||
|
@ -20,13 +21,13 @@ def test_AMPrimitive_validation():
|
|||
assert_raises(ValueError, AMPrimitive, 0, 'exposed')
|
||||
assert_raises(ValueError, AMPrimitive, 3, 'off')
|
||||
|
||||
|
||||
def test_AMPrimitive_conversion():
|
||||
p = AMPrimitive(4, 'on')
|
||||
assert_raises(NotImplementedError, p.to_inch)
|
||||
assert_raises(NotImplementedError, p.to_metric)
|
||||
|
||||
|
||||
|
||||
def test_AMCommentPrimitive_ctor():
|
||||
c = AMCommentPrimitive(0, ' This is a comment *')
|
||||
assert_equal(c.code, 0)
|
||||
|
@ -47,6 +48,7 @@ def test_AMCommentPrimitive_dump():
|
|||
c = AMCommentPrimitive(0, 'Rectangle with rounded corners.')
|
||||
assert_equal(c.to_gerber(), '0 Rectangle with rounded corners. *')
|
||||
|
||||
|
||||
def test_AMCommentPrimitive_conversion():
|
||||
c = AMCommentPrimitive(0, 'Rectangle with rounded corners.')
|
||||
ci = c
|
||||
|
@ -56,6 +58,7 @@ def test_AMCommentPrimitive_conversion():
|
|||
assert_equal(c, ci)
|
||||
assert_equal(c, cm)
|
||||
|
||||
|
||||
def test_AMCommentPrimitive_string():
|
||||
c = AMCommentPrimitive(0, 'Test Comment')
|
||||
assert_equal(str(c), '<Aperture Macro Comment: Test Comment>')
|
||||
|
@ -83,7 +86,7 @@ def test_AMCirclePrimitive_factory():
|
|||
assert_equal(c.code, 1)
|
||||
assert_equal(c.exposure, 'off')
|
||||
assert_equal(c.diameter, 5)
|
||||
assert_equal(c.position, (0,0))
|
||||
assert_equal(c.position, (0, 0))
|
||||
|
||||
|
||||
def test_AMCirclePrimitive_dump():
|
||||
|
@ -92,6 +95,7 @@ def test_AMCirclePrimitive_dump():
|
|||
c = AMCirclePrimitive(1, 'on', 5, (0, 0))
|
||||
assert_equal(c.to_gerber(), '1,1,5,0,0*')
|
||||
|
||||
|
||||
def test_AMCirclePrimitive_conversion():
|
||||
c = AMCirclePrimitive(1, 'off', 25.4, (25.4, 0))
|
||||
c.to_inch()
|
||||
|
@ -103,8 +107,11 @@ def test_AMCirclePrimitive_conversion():
|
|||
assert_equal(c.diameter, 25.4)
|
||||
assert_equal(c.position, (25.4, 0))
|
||||
|
||||
|
||||
def test_AMVectorLinePrimitive_validation():
|
||||
assert_raises(ValueError, AMVectorLinePrimitive, 3, 'on', 0.1, (0,0), (3.3, 5.4), 0)
|
||||
assert_raises(ValueError, AMVectorLinePrimitive,
|
||||
3, 'on', 0.1, (0, 0), (3.3, 5.4), 0)
|
||||
|
||||
|
||||
def test_AMVectorLinePrimitive_factory():
|
||||
l = AMVectorLinePrimitive.from_gerber('20,1,0.9,0,0.45,12,0.45,0*')
|
||||
|
@ -115,26 +122,32 @@ def test_AMVectorLinePrimitive_factory():
|
|||
assert_equal(l.end, (12, 0.45))
|
||||
assert_equal(l.rotation, 0)
|
||||
|
||||
|
||||
def test_AMVectorLinePrimitive_dump():
|
||||
l = AMVectorLinePrimitive.from_gerber('20,1,0.9,0,0.45,12,0.45,0*')
|
||||
assert_equal(l.to_gerber(), '20,1,0.9,0.0,0.45,12.0,0.45,0.0*')
|
||||
|
||||
|
||||
def test_AMVectorLinePrimtive_conversion():
|
||||
l = AMVectorLinePrimitive(20, 'on', 25.4, (0,0), (25.4, 25.4), 0)
|
||||
l = AMVectorLinePrimitive(20, 'on', 25.4, (0, 0), (25.4, 25.4), 0)
|
||||
l.to_inch()
|
||||
assert_equal(l.width, 1)
|
||||
assert_equal(l.start, (0, 0))
|
||||
assert_equal(l.end, (1, 1))
|
||||
|
||||
l = AMVectorLinePrimitive(20, 'on', 1, (0,0), (1, 1), 0)
|
||||
l = AMVectorLinePrimitive(20, 'on', 1, (0, 0), (1, 1), 0)
|
||||
l.to_metric()
|
||||
assert_equal(l.width, 25.4)
|
||||
assert_equal(l.start, (0, 0))
|
||||
assert_equal(l.end, (25.4, 25.4))
|
||||
|
||||
|
||||
def test_AMOutlinePrimitive_validation():
|
||||
assert_raises(ValueError, AMOutlinePrimitive, 7, 'on', (0,0), [(3.3, 5.4), (4.0, 5.4), (0, 0)], 0)
|
||||
assert_raises(ValueError, AMOutlinePrimitive, 4, 'on', (0,0), [(3.3, 5.4), (4.0, 5.4), (0, 1)], 0)
|
||||
assert_raises(ValueError, AMOutlinePrimitive, 7, 'on',
|
||||
(0, 0), [(3.3, 5.4), (4.0, 5.4), (0, 0)], 0)
|
||||
assert_raises(ValueError, AMOutlinePrimitive, 4, 'on',
|
||||
(0, 0), [(3.3, 5.4), (4.0, 5.4), (0, 1)], 0)
|
||||
|
||||
|
||||
def test_AMOutlinePrimitive_factory():
|
||||
o = AMOutlinePrimitive.from_gerber('4,1,3,0,0,3,3,3,0,0,0,0*')
|
||||
|
@ -144,12 +157,15 @@ def test_AMOutlinePrimitive_factory():
|
|||
assert_equal(o.points, [(3, 3), (3, 0), (0, 0)])
|
||||
assert_equal(o.rotation, 0)
|
||||
|
||||
|
||||
def test_AMOUtlinePrimitive_dump():
|
||||
o = AMOutlinePrimitive(4, 'on', (0, 0), [(3, 3), (3, 0), (0, 0)], 0)
|
||||
assert_equal(o.to_gerber(), '4,1,3,0,0,3,3,3,0,0,0,0*')
|
||||
|
||||
|
||||
def test_AMOutlinePrimitive_conversion():
|
||||
o = AMOutlinePrimitive(4, 'on', (0, 0), [(25.4, 25.4), (25.4, 0), (0, 0)], 0)
|
||||
o = AMOutlinePrimitive(
|
||||
4, 'on', (0, 0), [(25.4, 25.4), (25.4, 0), (0, 0)], 0)
|
||||
o.to_inch()
|
||||
assert_equal(o.start_point, (0, 0))
|
||||
assert_equal(o.points, ((1., 1.), (1., 0.), (0., 0.)))
|
||||
|
@ -165,6 +181,7 @@ def test_AMPolygonPrimitive_validation():
|
|||
assert_raises(ValueError, AMPolygonPrimitive, 5, 'on', 2, (3.3, 5.4), 3, 0)
|
||||
assert_raises(ValueError, AMPolygonPrimitive, 5, 'on', 13, (3.3, 5.4), 3, 0)
|
||||
|
||||
|
||||
def test_AMPolygonPrimitive_factory():
|
||||
p = AMPolygonPrimitive.from_gerber('5,1,3,3.3,5.4,3,0')
|
||||
assert_equal(p.code, 5)
|
||||
|
@ -174,10 +191,12 @@ def test_AMPolygonPrimitive_factory():
|
|||
assert_equal(p.diameter, 3)
|
||||
assert_equal(p.rotation, 0)
|
||||
|
||||
|
||||
def test_AMPolygonPrimitive_dump():
|
||||
p = AMPolygonPrimitive(5, 'on', 3, (3.3, 5.4), 3, 0)
|
||||
assert_equal(p.to_gerber(), '5,1,3,3.3,5.4,3,0*')
|
||||
|
||||
|
||||
def test_AMPolygonPrimitive_conversion():
|
||||
p = AMPolygonPrimitive(5, 'off', 3, (25.4, 0), 25.4, 0)
|
||||
p.to_inch()
|
||||
|
@ -191,7 +210,9 @@ def test_AMPolygonPrimitive_conversion():
|
|||
|
||||
|
||||
def test_AMMoirePrimitive_validation():
|
||||
assert_raises(ValueError, AMMoirePrimitive, 7, (0, 0), 5.1, 0.2, 0.4, 6, 0.1, 6.1, 0)
|
||||
assert_raises(ValueError, AMMoirePrimitive, 7,
|
||||
(0, 0), 5.1, 0.2, 0.4, 6, 0.1, 6.1, 0)
|
||||
|
||||
|
||||
def test_AMMoirePrimitive_factory():
|
||||
m = AMMoirePrimitive.from_gerber('6,0,0,5,0.5,0.5,2,0.1,6,0*')
|
||||
|
@ -205,10 +226,12 @@ def test_AMMoirePrimitive_factory():
|
|||
assert_equal(m.crosshair_length, 6)
|
||||
assert_equal(m.rotation, 0)
|
||||
|
||||
|
||||
def test_AMMoirePrimitive_dump():
|
||||
m = AMMoirePrimitive.from_gerber('6,0,0,5,0.5,0.5,2,0.1,6,0*')
|
||||
assert_equal(m.to_gerber(), '6,0,0,5.0,0.5,0.5,2,0.1,6.0,0.0*')
|
||||
|
||||
|
||||
def test_AMMoirePrimitive_conversion():
|
||||
m = AMMoirePrimitive(6, (25.4, 25.4), 25.4, 25.4, 25.4, 6, 25.4, 25.4, 0)
|
||||
m.to_inch()
|
||||
|
@ -228,10 +251,12 @@ def test_AMMoirePrimitive_conversion():
|
|||
assert_equal(m.crosshair_thickness, 25.4)
|
||||
assert_equal(m.crosshair_length, 25.4)
|
||||
|
||||
|
||||
def test_AMThermalPrimitive_validation():
|
||||
assert_raises(ValueError, AMThermalPrimitive, 8, (0.0, 0.0), 7, 5, 0.2)
|
||||
assert_raises(TypeError, AMThermalPrimitive, 7, (0.0, '0'), 7, 5, 0.2)
|
||||
|
||||
|
||||
def test_AMThermalPrimitive_factory():
|
||||
t = AMThermalPrimitive.from_gerber('7,0,0,7,6,0.2*')
|
||||
assert_equal(t.code, 7)
|
||||
|
@ -240,10 +265,12 @@ def test_AMThermalPrimitive_factory():
|
|||
assert_equal(t.inner_diameter, 6)
|
||||
assert_equal(t.gap, 0.2)
|
||||
|
||||
|
||||
def test_AMThermalPrimitive_dump():
|
||||
t = AMThermalPrimitive.from_gerber('7,0,0,7,6,0.2*')
|
||||
assert_equal(t.to_gerber(), '7,0,0,7.0,6.0,0.2*')
|
||||
|
||||
|
||||
def test_AMThermalPrimitive_conversion():
|
||||
t = AMThermalPrimitive(7, (25.4, 25.4), 25.4, 25.4, 25.4)
|
||||
t.to_inch()
|
||||
|
@ -261,7 +288,9 @@ def test_AMThermalPrimitive_conversion():
|
|||
|
||||
|
||||
def test_AMCenterLinePrimitive_validation():
|
||||
assert_raises(ValueError, AMCenterLinePrimitive, 22, 1, 0.2, 0.5, (0, 0), 0)
|
||||
assert_raises(ValueError, AMCenterLinePrimitive,
|
||||
22, 1, 0.2, 0.5, (0, 0), 0)
|
||||
|
||||
|
||||
def test_AMCenterLinePrimtive_factory():
|
||||
l = AMCenterLinePrimitive.from_gerber('21,1,6.8,1.2,3.4,0.6,0*')
|
||||
|
@ -272,10 +301,12 @@ def test_AMCenterLinePrimtive_factory():
|
|||
assert_equal(l.center, (3.4, 0.6))
|
||||
assert_equal(l.rotation, 0)
|
||||
|
||||
|
||||
def test_AMCenterLinePrimitive_dump():
|
||||
l = AMCenterLinePrimitive.from_gerber('21,1,6.8,1.2,3.4,0.6,0*')
|
||||
assert_equal(l.to_gerber(), '21,1,6.8,1.2,3.4,0.6,0.0*')
|
||||
|
||||
|
||||
def test_AMCenterLinePrimitive_conversion():
|
||||
l = AMCenterLinePrimitive(21, 'on', 25.4, 25.4, (25.4, 25.4), 0)
|
||||
l.to_inch()
|
||||
|
@ -289,8 +320,11 @@ def test_AMCenterLinePrimitive_conversion():
|
|||
assert_equal(l.height, 25.4)
|
||||
assert_equal(l.center, (25.4, 25.4))
|
||||
|
||||
|
||||
def test_AMLowerLeftLinePrimitive_validation():
|
||||
assert_raises(ValueError, AMLowerLeftLinePrimitive, 23, 1, 0.2, 0.5, (0, 0), 0)
|
||||
assert_raises(ValueError, AMLowerLeftLinePrimitive,
|
||||
23, 1, 0.2, 0.5, (0, 0), 0)
|
||||
|
||||
|
||||
def test_AMLowerLeftLinePrimtive_factory():
|
||||
l = AMLowerLeftLinePrimitive.from_gerber('22,1,6.8,1.2,3.4,0.6,0*')
|
||||
|
@ -301,10 +335,12 @@ def test_AMLowerLeftLinePrimtive_factory():
|
|||
assert_equal(l.lower_left, (3.4, 0.6))
|
||||
assert_equal(l.rotation, 0)
|
||||
|
||||
|
||||
def test_AMLowerLeftLinePrimitive_dump():
|
||||
l = AMLowerLeftLinePrimitive.from_gerber('22,1,6.8,1.2,3.4,0.6,0*')
|
||||
assert_equal(l.to_gerber(), '22,1,6.8,1.2,3.4,0.6,0.0*')
|
||||
|
||||
|
||||
def test_AMLowerLeftLinePrimitive_conversion():
|
||||
l = AMLowerLeftLinePrimitive(22, 'on', 25.4, 25.4, (25.4, 25.4), 0)
|
||||
l.to_inch()
|
||||
|
@ -318,24 +354,23 @@ def test_AMLowerLeftLinePrimitive_conversion():
|
|||
assert_equal(l.height, 25.4)
|
||||
assert_equal(l.lower_left, (25.4, 25.4))
|
||||
|
||||
|
||||
def test_AMUnsupportPrimitive():
|
||||
u = AMUnsupportPrimitive.from_gerber('Test')
|
||||
assert_equal(u.primitive, 'Test')
|
||||
u = AMUnsupportPrimitive('Test')
|
||||
assert_equal(u.to_gerber(), 'Test')
|
||||
|
||||
|
||||
def test_AMUnsupportPrimitive_smoketest():
|
||||
u = AMUnsupportPrimitive.from_gerber('Test')
|
||||
u.to_inch()
|
||||
u.to_metric()
|
||||
|
||||
|
||||
|
||||
def test_inch():
|
||||
assert_equal(inch(25.4), 1)
|
||||
|
||||
|
||||
def test_metric():
|
||||
assert_equal(metric(1), 25.4)
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -54,17 +54,20 @@ def test_filesettings_dict_assign():
|
|||
assert_equal(fs.zero_suppression, 'leading')
|
||||
assert_equal(fs.format, (1, 2))
|
||||
|
||||
|
||||
def test_camfile_init():
|
||||
""" Smoke test CamFile test
|
||||
"""
|
||||
cf = CamFile()
|
||||
|
||||
|
||||
def test_camfile_settings():
|
||||
""" Test CamFile Default Settings
|
||||
"""
|
||||
cf = CamFile()
|
||||
assert_equal(cf.settings, FileSettings())
|
||||
|
||||
|
||||
def test_bounds_override_smoketest():
|
||||
cf = CamFile()
|
||||
cf.bounds
|
||||
|
@ -89,7 +92,7 @@ def test_zeros():
|
|||
assert_equal(fs.zeros, 'trailing')
|
||||
assert_equal(fs.zero_suppression, 'leading')
|
||||
|
||||
fs.zeros= 'leading'
|
||||
fs.zeros = 'leading'
|
||||
assert_equal(fs.zeros, 'leading')
|
||||
assert_equal(fs.zero_suppression, 'trailing')
|
||||
|
||||
|
@ -113,12 +116,19 @@ def test_zeros():
|
|||
def test_filesettings_validation():
|
||||
""" Test FileSettings constructor argument validation
|
||||
"""
|
||||
assert_raises(ValueError, FileSettings, 'absolute-ish', 'inch', None, (2, 5), None)
|
||||
assert_raises(ValueError, FileSettings, 'absolute', 'degrees kelvin', None, (2, 5), None)
|
||||
assert_raises(ValueError, FileSettings, 'absolute', 'inch', 'leading', (2, 5), 'leading')
|
||||
assert_raises(ValueError, FileSettings, 'absolute', 'inch', 'following', (2, 5), None)
|
||||
assert_raises(ValueError, FileSettings, 'absolute', 'inch', None, (2, 5), 'following')
|
||||
assert_raises(ValueError, FileSettings, 'absolute', 'inch', None, (2, 5, 6), None)
|
||||
assert_raises(ValueError, FileSettings, 'absolute-ish',
|
||||
'inch', None, (2, 5), None)
|
||||
assert_raises(ValueError, FileSettings, 'absolute',
|
||||
'degrees kelvin', None, (2, 5), None)
|
||||
assert_raises(ValueError, FileSettings, 'absolute',
|
||||
'inch', 'leading', (2, 5), 'leading')
|
||||
assert_raises(ValueError, FileSettings, 'absolute',
|
||||
'inch', 'following', (2, 5), None)
|
||||
assert_raises(ValueError, FileSettings, 'absolute',
|
||||
'inch', None, (2, 5), 'following')
|
||||
assert_raises(ValueError, FileSettings, 'absolute',
|
||||
'inch', None, (2, 5, 6), None)
|
||||
|
||||
|
||||
def test_key_validation():
|
||||
fs = FileSettings()
|
||||
|
@ -129,5 +139,3 @@ def test_key_validation():
|
|||
assert_raises(ValueError, fs.__setitem__, 'zero_suppression', 'following')
|
||||
assert_raises(ValueError, fs.__setitem__, 'zeros', 'following')
|
||||
assert_raises(ValueError, fs.__setitem__, 'format', (2, 5, 6))
|
||||
|
||||
|
||||
|
|
|
@ -12,9 +12,10 @@ import os
|
|||
|
||||
|
||||
NCDRILL_FILE = os.path.join(os.path.dirname(__file__),
|
||||
'resources/ncdrill.DRD')
|
||||
'resources/ncdrill.DRD')
|
||||
TOP_COPPER_FILE = os.path.join(os.path.dirname(__file__),
|
||||
'resources/top_copper.GTL')
|
||||
'resources/top_copper.GTL')
|
||||
|
||||
|
||||
def test_file_type_detection():
|
||||
""" Test file type detection
|
||||
|
@ -38,6 +39,3 @@ def test_file_type_validation():
|
|||
""" Test file format validation
|
||||
"""
|
||||
assert_raises(ParseError, read, 'LICENSE')
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -13,6 +13,7 @@ from .tests import *
|
|||
NCDRILL_FILE = os.path.join(os.path.dirname(__file__),
|
||||
'resources/ncdrill.DRD')
|
||||
|
||||
|
||||
def test_format_detection():
|
||||
""" Test file type detection
|
||||
"""
|
||||
|
@ -75,10 +76,11 @@ def test_conversion():
|
|||
for statement in ncdrill_inch.statements:
|
||||
statement.to_metric()
|
||||
|
||||
for m_tool, i_tool in zip(iter(ncdrill.tools.values()), iter(ncdrill_inch.tools.values())):
|
||||
for m_tool, i_tool in zip(iter(ncdrill.tools.values()),
|
||||
iter(ncdrill_inch.tools.values())):
|
||||
assert_equal(i_tool, m_tool)
|
||||
|
||||
for m, i in zip(ncdrill.primitives,inch_primitives):
|
||||
for m, i in zip(ncdrill.primitives, inch_primitives):
|
||||
assert_equal(m, i)
|
||||
|
||||
|
||||
|
@ -187,12 +189,10 @@ def test_parse_incremental_position():
|
|||
p = ExcellonParser(FileSettings(notation='incremental'))
|
||||
p._parse_line('X01Y01')
|
||||
p._parse_line('X01Y01')
|
||||
assert_equal(p.pos, [2.,2.])
|
||||
assert_equal(p.pos, [2., 2.])
|
||||
|
||||
|
||||
def test_parse_unknown():
|
||||
p = ExcellonParser(FileSettings())
|
||||
p._parse_line('Not A Valid Statement')
|
||||
assert_equal(p.statements[0].stmt, 'Not A Valid Statement')
|
||||
|
||||
|
||||
|
|
|
@ -7,11 +7,13 @@ from .tests import assert_equal, assert_not_equal, assert_raises
|
|||
from ..excellon_statements import *
|
||||
from ..cam import FileSettings
|
||||
|
||||
|
||||
def test_excellon_statement_implementation():
|
||||
stmt = ExcellonStatement()
|
||||
assert_raises(NotImplementedError, stmt.from_excellon, None)
|
||||
assert_raises(NotImplementedError, stmt.to_excellon)
|
||||
|
||||
|
||||
def test_excellontstmt():
|
||||
""" Smoke test ExcellonStatement
|
||||
"""
|
||||
|
@ -20,17 +22,18 @@ def test_excellontstmt():
|
|||
stmt.to_metric()
|
||||
stmt.offset()
|
||||
|
||||
|
||||
def test_excellontool_factory():
|
||||
""" Test ExcellonTool factory methods
|
||||
"""
|
||||
exc_line = 'T8F01B02S00003H04Z05C0.12500'
|
||||
settings = FileSettings(format=(2, 5), zero_suppression='trailing',
|
||||
units='inch', notation='absolute')
|
||||
units='inch', notation='absolute')
|
||||
tool = ExcellonTool.from_excellon(exc_line, settings)
|
||||
assert_equal(tool.number, 8)
|
||||
assert_equal(tool.diameter, 0.125)
|
||||
assert_equal(tool.feed_rate, 1)
|
||||
assert_equal(tool.retract_rate,2)
|
||||
assert_equal(tool.retract_rate, 2)
|
||||
assert_equal(tool.rpm, 3)
|
||||
assert_equal(tool.max_hit_count, 4)
|
||||
assert_equal(tool.depth_offset, 5)
|
||||
|
@ -41,7 +44,7 @@ def test_excellontool_factory():
|
|||
assert_equal(tool.number, 8)
|
||||
assert_equal(tool.diameter, 0.125)
|
||||
assert_equal(tool.feed_rate, 1)
|
||||
assert_equal(tool.retract_rate,2)
|
||||
assert_equal(tool.retract_rate, 2)
|
||||
assert_equal(tool.rpm, 3)
|
||||
assert_equal(tool.max_hit_count, 4)
|
||||
assert_equal(tool.depth_offset, 5)
|
||||
|
@ -55,7 +58,7 @@ def test_excellontool_dump():
|
|||
'T07F0S0C0.04300', 'T08F0S0C0.12500', 'T09F0S0C0.13000',
|
||||
'T08B01F02H03S00003C0.12500Z04', 'T01F0S300.999C0.01200']
|
||||
settings = FileSettings(format=(2, 5), zero_suppression='trailing',
|
||||
units='inch', notation='absolute')
|
||||
units='inch', notation='absolute')
|
||||
for line in exc_lines:
|
||||
tool = ExcellonTool.from_excellon(line, settings)
|
||||
assert_equal(tool.to_excellon(), line)
|
||||
|
@ -63,7 +66,7 @@ def test_excellontool_dump():
|
|||
|
||||
def test_excellontool_order():
|
||||
settings = FileSettings(format=(2, 5), zero_suppression='trailing',
|
||||
units='inch', notation='absolute')
|
||||
units='inch', notation='absolute')
|
||||
line = 'T8F00S00C0.12500'
|
||||
tool1 = ExcellonTool.from_excellon(line, settings)
|
||||
line = 'T8C0.12500F00S00'
|
||||
|
@ -72,36 +75,48 @@ def test_excellontool_order():
|
|||
assert_equal(tool1.feed_rate, tool2.feed_rate)
|
||||
assert_equal(tool1.rpm, tool2.rpm)
|
||||
|
||||
|
||||
def test_excellontool_conversion():
|
||||
tool = ExcellonTool.from_dict(FileSettings(units='metric'), {'number': 8, 'diameter': 25.4})
|
||||
tool = ExcellonTool.from_dict(FileSettings(units='metric'),
|
||||
{'number': 8, 'diameter': 25.4})
|
||||
tool.to_inch()
|
||||
assert_equal(tool.diameter, 1.)
|
||||
tool = ExcellonTool.from_dict(FileSettings(units='inch'), {'number': 8, 'diameter': 1.})
|
||||
tool = ExcellonTool.from_dict(FileSettings(units='inch'),
|
||||
{'number': 8, 'diameter': 1.})
|
||||
tool.to_metric()
|
||||
assert_equal(tool.diameter, 25.4)
|
||||
|
||||
# Shouldn't change units if we're already using target units
|
||||
tool = ExcellonTool.from_dict(FileSettings(units='inch'), {'number': 8, 'diameter': 25.4})
|
||||
tool = ExcellonTool.from_dict(FileSettings(units='inch'),
|
||||
{'number': 8, 'diameter': 25.4})
|
||||
tool.to_inch()
|
||||
assert_equal(tool.diameter, 25.4)
|
||||
tool = ExcellonTool.from_dict(FileSettings(units='metric'), {'number': 8, 'diameter': 1.})
|
||||
tool = ExcellonTool.from_dict(FileSettings(units='metric'),
|
||||
{'number': 8, 'diameter': 1.})
|
||||
tool.to_metric()
|
||||
assert_equal(tool.diameter, 1.)
|
||||
|
||||
|
||||
def test_excellontool_repr():
|
||||
tool = ExcellonTool.from_dict(FileSettings(), {'number': 8, 'diameter': 0.125})
|
||||
tool = ExcellonTool.from_dict(FileSettings(),
|
||||
{'number': 8, 'diameter': 0.125})
|
||||
assert_equal(str(tool), '<ExcellonTool 08: 0.125in. dia.>')
|
||||
tool = ExcellonTool.from_dict(FileSettings(units='metric'), {'number': 8, 'diameter': 0.125})
|
||||
tool = ExcellonTool.from_dict(FileSettings(units='metric'),
|
||||
{'number': 8, 'diameter': 0.125})
|
||||
assert_equal(str(tool), '<ExcellonTool 08: 0.125mm dia.>')
|
||||
|
||||
|
||||
def test_excellontool_equality():
|
||||
t = ExcellonTool.from_dict(FileSettings(), {'number': 8, 'diameter': 0.125})
|
||||
t1 = ExcellonTool.from_dict(FileSettings(), {'number': 8, 'diameter': 0.125})
|
||||
t = ExcellonTool.from_dict(
|
||||
FileSettings(), {'number': 8, 'diameter': 0.125})
|
||||
t1 = ExcellonTool.from_dict(
|
||||
FileSettings(), {'number': 8, 'diameter': 0.125})
|
||||
assert_equal(t, t1)
|
||||
t1 = ExcellonTool.from_dict(FileSettings(units='metric'), {'number': 8, 'diameter': 0.125})
|
||||
t1 = ExcellonTool.from_dict(FileSettings(units='metric'),
|
||||
{'number': 8, 'diameter': 0.125})
|
||||
assert_not_equal(t, t1)
|
||||
|
||||
|
||||
def test_toolselection_factory():
|
||||
""" Test ToolSelectionStmt factory method
|
||||
"""
|
||||
|
@ -115,6 +130,7 @@ def test_toolselection_factory():
|
|||
assert_equal(stmt.tool, 42)
|
||||
assert_equal(stmt.compensation_index, None)
|
||||
|
||||
|
||||
def test_toolselection_dump():
|
||||
""" Test ToolSelectionStmt to_excellon()
|
||||
"""
|
||||
|
@ -123,6 +139,7 @@ def test_toolselection_dump():
|
|||
stmt = ToolSelectionStmt.from_excellon(line)
|
||||
assert_equal(stmt.to_excellon(), line)
|
||||
|
||||
|
||||
def test_z_axis_infeed_rate_factory():
|
||||
""" Test ZAxisInfeedRateStmt factory method
|
||||
"""
|
||||
|
@ -133,6 +150,7 @@ def test_z_axis_infeed_rate_factory():
|
|||
stmt = ZAxisInfeedRateStmt.from_excellon('F03')
|
||||
assert_equal(stmt.rate, 3)
|
||||
|
||||
|
||||
def test_z_axis_infeed_rate_dump():
|
||||
""" Test ZAxisInfeedRateStmt to_excellon()
|
||||
"""
|
||||
|
@ -145,11 +163,12 @@ def test_z_axis_infeed_rate_dump():
|
|||
stmt = ZAxisInfeedRateStmt.from_excellon(input_rate)
|
||||
assert_equal(stmt.to_excellon(), expected_output)
|
||||
|
||||
|
||||
def test_coordinatestmt_factory():
|
||||
""" Test CoordinateStmt factory method
|
||||
"""
|
||||
settings = FileSettings(format=(2, 5), zero_suppression='trailing',
|
||||
units='inch', notation='absolute')
|
||||
units='inch', notation='absolute')
|
||||
|
||||
line = 'X0278207Y0065293'
|
||||
stmt = CoordinateStmt.from_excellon(line, settings)
|
||||
|
@ -165,7 +184,7 @@ def test_coordinatestmt_factory():
|
|||
# assert_equal(stmt.y, 0.575)
|
||||
|
||||
settings = FileSettings(format=(2, 4), zero_suppression='leading',
|
||||
units='inch', notation='absolute')
|
||||
units='inch', notation='absolute')
|
||||
|
||||
line = 'X9660Y4639'
|
||||
stmt = CoordinateStmt.from_excellon(line, settings)
|
||||
|
@ -173,12 +192,12 @@ def test_coordinatestmt_factory():
|
|||
assert_equal(stmt.y, 0.4639)
|
||||
assert_equal(stmt.to_excellon(settings), "X9660Y4639")
|
||||
assert_equal(stmt.units, 'inch')
|
||||
|
||||
|
||||
settings.units = 'metric'
|
||||
stmt = CoordinateStmt.from_excellon(line, settings)
|
||||
assert_equal(stmt.units, 'metric')
|
||||
|
||||
|
||||
|
||||
|
||||
def test_coordinatestmt_dump():
|
||||
""" Test CoordinateStmt to_excellon()
|
||||
"""
|
||||
|
@ -186,102 +205,110 @@ def test_coordinatestmt_dump():
|
|||
'X251295Y81528', 'X2525Y78', 'X255Y575', 'Y52',
|
||||
'X2675', 'Y575', 'X2425', 'Y52', 'X23', ]
|
||||
settings = FileSettings(format=(2, 4), zero_suppression='leading',
|
||||
units='inch', notation='absolute')
|
||||
units='inch', notation='absolute')
|
||||
for line in lines:
|
||||
stmt = CoordinateStmt.from_excellon(line, settings)
|
||||
assert_equal(stmt.to_excellon(settings), line)
|
||||
|
||||
|
||||
def test_coordinatestmt_conversion():
|
||||
|
||||
|
||||
settings = FileSettings()
|
||||
settings.units = 'metric'
|
||||
stmt = CoordinateStmt.from_excellon('X254Y254', settings)
|
||||
|
||||
#No effect
|
||||
|
||||
# No effect
|
||||
stmt.to_metric()
|
||||
assert_equal(stmt.x, 25.4)
|
||||
assert_equal(stmt.y, 25.4)
|
||||
|
||||
|
||||
stmt.to_inch()
|
||||
assert_equal(stmt.units, 'inch')
|
||||
assert_equal(stmt.x, 1.)
|
||||
assert_equal(stmt.y, 1.)
|
||||
|
||||
#No effect
|
||||
|
||||
# No effect
|
||||
stmt.to_inch()
|
||||
assert_equal(stmt.x, 1.)
|
||||
assert_equal(stmt.y, 1.)
|
||||
|
||||
|
||||
settings.units = 'inch'
|
||||
stmt = CoordinateStmt.from_excellon('X01Y01', settings)
|
||||
|
||||
#No effect
|
||||
|
||||
# No effect
|
||||
stmt.to_inch()
|
||||
assert_equal(stmt.x, 1.)
|
||||
assert_equal(stmt.y, 1.)
|
||||
|
||||
|
||||
stmt.to_metric()
|
||||
assert_equal(stmt.units, 'metric')
|
||||
assert_equal(stmt.x, 25.4)
|
||||
assert_equal(stmt.y, 25.4)
|
||||
|
||||
#No effect
|
||||
|
||||
# No effect
|
||||
stmt.to_metric()
|
||||
assert_equal(stmt.x, 25.4)
|
||||
assert_equal(stmt.y, 25.4)
|
||||
|
||||
|
||||
def test_coordinatestmt_offset():
|
||||
stmt = CoordinateStmt.from_excellon('X01Y01', FileSettings())
|
||||
stmt.offset()
|
||||
assert_equal(stmt.x, 1)
|
||||
assert_equal(stmt.y, 1)
|
||||
stmt.offset(1,0)
|
||||
stmt.offset(1, 0)
|
||||
assert_equal(stmt.x, 2.)
|
||||
assert_equal(stmt.y, 1.)
|
||||
stmt.offset(0,1)
|
||||
stmt.offset(0, 1)
|
||||
assert_equal(stmt.x, 2.)
|
||||
assert_equal(stmt.y, 2.)
|
||||
|
||||
|
||||
def test_coordinatestmt_string():
|
||||
settings = FileSettings(format=(2, 4), zero_suppression='leading',
|
||||
units='inch', notation='absolute')
|
||||
units='inch', notation='absolute')
|
||||
stmt = CoordinateStmt.from_excellon('X9660Y4639', settings)
|
||||
assert_equal(str(stmt), '<Coordinate Statement: X: 0.966 Y: 0.4639 >')
|
||||
|
||||
|
||||
def test_repeathole_stmt_factory():
|
||||
stmt = RepeatHoleStmt.from_excellon('R0004X015Y32', FileSettings(zeros='leading', units='inch'))
|
||||
stmt = RepeatHoleStmt.from_excellon('R0004X015Y32',
|
||||
FileSettings(zeros='leading',
|
||||
units='inch'))
|
||||
assert_equal(stmt.count, 4)
|
||||
assert_equal(stmt.xdelta, 1.5)
|
||||
assert_equal(stmt.ydelta, 32)
|
||||
assert_equal(stmt.units, 'inch')
|
||||
|
||||
stmt = RepeatHoleStmt.from_excellon('R0004X015Y32', FileSettings(zeros='leading', units='metric'))
|
||||
|
||||
stmt = RepeatHoleStmt.from_excellon('R0004X015Y32',
|
||||
FileSettings(zeros='leading',
|
||||
units='metric'))
|
||||
assert_equal(stmt.units, 'metric')
|
||||
|
||||
|
||||
def test_repeatholestmt_dump():
|
||||
line = 'R4X015Y32'
|
||||
stmt = RepeatHoleStmt.from_excellon(line, FileSettings())
|
||||
assert_equal(stmt.to_excellon(FileSettings()), line)
|
||||
|
||||
|
||||
def test_repeatholestmt_conversion():
|
||||
line = 'R4X0254Y254'
|
||||
settings = FileSettings()
|
||||
settings.units = 'metric'
|
||||
stmt = RepeatHoleStmt.from_excellon(line, settings)
|
||||
|
||||
#No effect
|
||||
|
||||
# No effect
|
||||
stmt.to_metric()
|
||||
assert_equal(stmt.xdelta, 2.54)
|
||||
assert_equal(stmt.ydelta, 25.4)
|
||||
|
||||
|
||||
stmt.to_inch()
|
||||
assert_equal(stmt.units, 'inch')
|
||||
assert_equal(stmt.xdelta, 0.1)
|
||||
assert_equal(stmt.ydelta, 1.)
|
||||
|
||||
#no effect
|
||||
|
||||
# no effect
|
||||
stmt.to_inch()
|
||||
assert_equal(stmt.xdelta, 0.1)
|
||||
assert_equal(stmt.ydelta, 1.)
|
||||
|
@ -289,26 +316,28 @@ def test_repeatholestmt_conversion():
|
|||
line = 'R4X01Y1'
|
||||
settings.units = 'inch'
|
||||
stmt = RepeatHoleStmt.from_excellon(line, settings)
|
||||
|
||||
#no effect
|
||||
|
||||
# no effect
|
||||
stmt.to_inch()
|
||||
assert_equal(stmt.xdelta, 1.)
|
||||
assert_equal(stmt.ydelta, 10.)
|
||||
|
||||
|
||||
stmt.to_metric()
|
||||
assert_equal(stmt.units, 'metric')
|
||||
assert_equal(stmt.xdelta, 25.4)
|
||||
assert_equal(stmt.ydelta, 254.)
|
||||
|
||||
#No effect
|
||||
|
||||
# No effect
|
||||
stmt.to_metric()
|
||||
assert_equal(stmt.xdelta, 25.4)
|
||||
assert_equal(stmt.ydelta, 254.)
|
||||
|
||||
|
||||
def test_repeathole_str():
|
||||
stmt = RepeatHoleStmt.from_excellon('R4X015Y32', FileSettings())
|
||||
assert_equal(str(stmt), '<Repeat Hole: 4 times, offset X: 1.5 Y: 32>')
|
||||
|
||||
|
||||
def test_commentstmt_factory():
|
||||
""" Test CommentStmt factory method
|
||||
"""
|
||||
|
@ -333,42 +362,52 @@ def test_commentstmt_dump():
|
|||
stmt = CommentStmt.from_excellon(line)
|
||||
assert_equal(stmt.to_excellon(), line)
|
||||
|
||||
|
||||
def test_header_begin_stmt():
|
||||
stmt = HeaderBeginStmt()
|
||||
assert_equal(stmt.to_excellon(None), 'M48')
|
||||
|
||||
|
||||
def test_header_end_stmt():
|
||||
stmt = HeaderEndStmt()
|
||||
assert_equal(stmt.to_excellon(None), 'M95')
|
||||
|
||||
|
||||
def test_rewindstop_stmt():
|
||||
stmt = RewindStopStmt()
|
||||
assert_equal(stmt.to_excellon(None), '%')
|
||||
|
||||
|
||||
def test_z_axis_rout_position_stmt():
|
||||
stmt = ZAxisRoutPositionStmt()
|
||||
assert_equal(stmt.to_excellon(None), 'M15')
|
||||
|
||||
|
||||
def test_retract_with_clamping_stmt():
|
||||
stmt = RetractWithClampingStmt()
|
||||
assert_equal(stmt.to_excellon(None), 'M16')
|
||||
|
||||
|
||||
def test_retract_without_clamping_stmt():
|
||||
stmt = RetractWithoutClampingStmt()
|
||||
assert_equal(stmt.to_excellon(None), 'M17')
|
||||
|
||||
|
||||
def test_cutter_compensation_off_stmt():
|
||||
stmt = CutterCompensationOffStmt()
|
||||
assert_equal(stmt.to_excellon(None), 'G40')
|
||||
|
||||
|
||||
def test_cutter_compensation_left_stmt():
|
||||
stmt = CutterCompensationLeftStmt()
|
||||
assert_equal(stmt.to_excellon(None), 'G41')
|
||||
|
||||
|
||||
def test_cutter_compensation_right_stmt():
|
||||
stmt = CutterCompensationRightStmt()
|
||||
assert_equal(stmt.to_excellon(None), 'G42')
|
||||
|
||||
|
||||
def test_endofprogramstmt_factory():
|
||||
settings = FileSettings(units='inch')
|
||||
stmt = EndOfProgramStmt.from_excellon('M30X01Y02', settings)
|
||||
|
@ -384,61 +423,65 @@ def test_endofprogramstmt_factory():
|
|||
assert_equal(stmt.x, None)
|
||||
assert_equal(stmt.y, 2.)
|
||||
|
||||
|
||||
def test_endofprogramStmt_dump():
|
||||
lines = ['M30X01Y02',]
|
||||
lines = ['M30X01Y02', ]
|
||||
for line in lines:
|
||||
stmt = EndOfProgramStmt.from_excellon(line, FileSettings())
|
||||
assert_equal(stmt.to_excellon(FileSettings()), line)
|
||||
|
||||
|
||||
def test_endofprogramstmt_conversion():
|
||||
settings = FileSettings()
|
||||
settings.units = 'metric'
|
||||
stmt = EndOfProgramStmt.from_excellon('M30X0254Y254', settings)
|
||||
#No effect
|
||||
# No effect
|
||||
stmt.to_metric()
|
||||
assert_equal(stmt.x, 2.54)
|
||||
assert_equal(stmt.y, 25.4)
|
||||
|
||||
|
||||
stmt.to_inch()
|
||||
assert_equal(stmt.units, 'inch')
|
||||
assert_equal(stmt.x, 0.1)
|
||||
assert_equal(stmt.y, 1.0)
|
||||
|
||||
#No effect
|
||||
|
||||
# No effect
|
||||
stmt.to_inch()
|
||||
assert_equal(stmt.x, 0.1)
|
||||
assert_equal(stmt.y, 1.0)
|
||||
|
||||
settings.units = 'inch'
|
||||
stmt = EndOfProgramStmt.from_excellon('M30X01Y1', settings)
|
||||
|
||||
#No effect
|
||||
|
||||
# No effect
|
||||
stmt.to_inch()
|
||||
assert_equal(stmt.x, 1.)
|
||||
assert_equal(stmt.y, 10.0)
|
||||
|
||||
|
||||
stmt.to_metric()
|
||||
assert_equal(stmt.units, 'metric')
|
||||
assert_equal(stmt.x, 25.4)
|
||||
assert_equal(stmt.y, 254.)
|
||||
|
||||
#No effect
|
||||
|
||||
# No effect
|
||||
stmt.to_metric()
|
||||
assert_equal(stmt.x, 25.4)
|
||||
assert_equal(stmt.y, 254.)
|
||||
|
||||
|
||||
def test_endofprogramstmt_offset():
|
||||
stmt = EndOfProgramStmt(1, 1)
|
||||
stmt.offset()
|
||||
assert_equal(stmt.x, 1)
|
||||
assert_equal(stmt.y, 1)
|
||||
stmt.offset(1,0)
|
||||
stmt.offset(1, 0)
|
||||
assert_equal(stmt.x, 2.)
|
||||
assert_equal(stmt.y, 1.)
|
||||
stmt.offset(0,1)
|
||||
stmt.offset(0, 1)
|
||||
assert_equal(stmt.x, 2.)
|
||||
assert_equal(stmt.y, 2.)
|
||||
|
||||
|
||||
def test_unitstmt_factory():
|
||||
""" Test UnitStmt factory method
|
||||
"""
|
||||
|
@ -471,6 +514,7 @@ def test_unitstmt_dump():
|
|||
stmt = UnitStmt.from_excellon(line)
|
||||
assert_equal(stmt.to_excellon(), line)
|
||||
|
||||
|
||||
def test_unitstmt_conversion():
|
||||
stmt = UnitStmt.from_excellon('METRIC,TZ')
|
||||
stmt.to_inch()
|
||||
|
@ -480,6 +524,7 @@ def test_unitstmt_conversion():
|
|||
stmt.to_metric()
|
||||
assert_equal(stmt.units, 'metric')
|
||||
|
||||
|
||||
def test_incrementalmode_factory():
|
||||
""" Test IncrementalModeStmt factory method
|
||||
"""
|
||||
|
@ -527,6 +572,7 @@ def test_versionstmt_dump():
|
|||
stmt = VersionStmt.from_excellon(line)
|
||||
assert_equal(stmt.to_excellon(), line)
|
||||
|
||||
|
||||
def test_versionstmt_validation():
|
||||
""" Test VersionStmt input validation
|
||||
"""
|
||||
|
@ -608,6 +654,7 @@ def test_measmodestmt_validation():
|
|||
assert_raises(ValueError, MeasuringModeStmt.from_excellon, 'M70')
|
||||
assert_raises(ValueError, MeasuringModeStmt, 'millimeters')
|
||||
|
||||
|
||||
def test_measmodestmt_conversion():
|
||||
line = 'M72'
|
||||
stmt = MeasuringModeStmt.from_excellon(line)
|
||||
|
@ -621,27 +668,33 @@ def test_measmodestmt_conversion():
|
|||
stmt.to_inch()
|
||||
assert_equal(stmt.units, 'inch')
|
||||
|
||||
|
||||
def test_routemode_stmt():
|
||||
stmt = RouteModeStmt()
|
||||
assert_equal(stmt.to_excellon(FileSettings()), 'G00')
|
||||
|
||||
|
||||
def test_linearmode_stmt():
|
||||
stmt = LinearModeStmt()
|
||||
assert_equal(stmt.to_excellon(FileSettings()), 'G01')
|
||||
|
||||
|
||||
def test_drillmode_stmt():
|
||||
stmt = DrillModeStmt()
|
||||
assert_equal(stmt.to_excellon(FileSettings()), 'G05')
|
||||
|
||||
|
||||
def test_absolutemode_stmt():
|
||||
stmt = AbsoluteModeStmt()
|
||||
assert_equal(stmt.to_excellon(FileSettings()), 'G90')
|
||||
|
||||
|
||||
def test_unknownstmt():
|
||||
stmt = UnknownStmt('TEST')
|
||||
assert_equal(stmt.stmt, 'TEST')
|
||||
assert_equal(str(stmt), '<Unknown Statement: TEST>')
|
||||
|
||||
|
||||
def test_unknownstmt_dump():
|
||||
stmt = UnknownStmt('TEST')
|
||||
assert_equal(stmt.to_excellon(FileSettings()), 'TEST')
|
||||
|
|
|
@ -7,6 +7,7 @@ from .tests import *
|
|||
from ..gerber_statements import *
|
||||
from ..cam import FileSettings
|
||||
|
||||
|
||||
def test_Statement_smoketest():
|
||||
stmt = Statement('Test')
|
||||
assert_equal(stmt.type, 'Test')
|
||||
|
@ -16,7 +17,8 @@ def test_Statement_smoketest():
|
|||
assert_in('units=inch', str(stmt))
|
||||
stmt.to_metric()
|
||||
stmt.offset(1, 1)
|
||||
assert_in('type=Test',str(stmt))
|
||||
assert_in('type=Test', str(stmt))
|
||||
|
||||
|
||||
def test_FSParamStmt_factory():
|
||||
""" Test FSParamStruct factory
|
||||
|
@ -35,6 +37,7 @@ def test_FSParamStmt_factory():
|
|||
assert_equal(fs.notation, 'incremental')
|
||||
assert_equal(fs.format, (2, 7))
|
||||
|
||||
|
||||
def test_FSParamStmt():
|
||||
""" Test FSParamStmt initialization
|
||||
"""
|
||||
|
@ -48,6 +51,7 @@ def test_FSParamStmt():
|
|||
assert_equal(stmt.notation, notation)
|
||||
assert_equal(stmt.format, fmt)
|
||||
|
||||
|
||||
def test_FSParamStmt_dump():
|
||||
""" Test FSParamStmt to_gerber()
|
||||
"""
|
||||
|
@ -62,16 +66,20 @@ def test_FSParamStmt_dump():
|
|||
settings = FileSettings(zero_suppression='leading', notation='absolute')
|
||||
assert_equal(fs.to_gerber(settings), '%FSLAX25Y25*%')
|
||||
|
||||
|
||||
def test_FSParamStmt_string():
|
||||
""" Test FSParamStmt.__str__()
|
||||
"""
|
||||
stmt = {'param': 'FS', 'zero': 'L', 'notation': 'A', 'x': '27'}
|
||||
fs = FSParamStmt.from_dict(stmt)
|
||||
assert_equal(str(fs), '<Format Spec: 2:7 leading zero suppression absolute notation>')
|
||||
assert_equal(str(fs),
|
||||
'<Format Spec: 2:7 leading zero suppression absolute notation>')
|
||||
|
||||
stmt = {'param': 'FS', 'zero': 'T', 'notation': 'I', 'x': '25'}
|
||||
fs = FSParamStmt.from_dict(stmt)
|
||||
assert_equal(str(fs), '<Format Spec: 2:5 trailing zero suppression incremental notation>')
|
||||
assert_equal(str(fs),
|
||||
'<Format Spec: 2:5 trailing zero suppression incremental notation>')
|
||||
|
||||
|
||||
def test_MOParamStmt_factory():
|
||||
""" Test MOParamStruct factory
|
||||
|
@ -94,6 +102,7 @@ def test_MOParamStmt_factory():
|
|||
stmt = {'param': 'MO', 'mo': 'degrees kelvin'}
|
||||
assert_raises(ValueError, MOParamStmt.from_dict, stmt)
|
||||
|
||||
|
||||
def test_MOParamStmt():
|
||||
""" Test MOParamStmt initialization
|
||||
"""
|
||||
|
@ -106,6 +115,7 @@ def test_MOParamStmt():
|
|||
stmt = MOParamStmt(param, mode)
|
||||
assert_equal(stmt.mode, mode)
|
||||
|
||||
|
||||
def test_MOParamStmt_dump():
|
||||
""" Test MOParamStmt to_gerber()
|
||||
"""
|
||||
|
@ -117,6 +127,7 @@ def test_MOParamStmt_dump():
|
|||
mo = MOParamStmt.from_dict(stmt)
|
||||
assert_equal(mo.to_gerber(), '%MOMM*%')
|
||||
|
||||
|
||||
def test_MOParamStmt_conversion():
|
||||
stmt = {'param': 'MO', 'mo': 'MM'}
|
||||
mo = MOParamStmt.from_dict(stmt)
|
||||
|
@ -128,6 +139,7 @@ def test_MOParamStmt_conversion():
|
|||
mo.to_metric()
|
||||
assert_equal(mo.mode, 'metric')
|
||||
|
||||
|
||||
def test_MOParamStmt_string():
|
||||
""" Test MOParamStmt.__str__()
|
||||
"""
|
||||
|
@ -139,6 +151,7 @@ def test_MOParamStmt_string():
|
|||
mo = MOParamStmt.from_dict(stmt)
|
||||
assert_equal(str(mo), '<Mode: millimeters>')
|
||||
|
||||
|
||||
def test_IPParamStmt_factory():
|
||||
""" Test IPParamStruct factory
|
||||
"""
|
||||
|
@ -150,6 +163,7 @@ def test_IPParamStmt_factory():
|
|||
ip = IPParamStmt.from_dict(stmt)
|
||||
assert_equal(ip.ip, 'negative')
|
||||
|
||||
|
||||
def test_IPParamStmt():
|
||||
""" Test IPParamStmt initialization
|
||||
"""
|
||||
|
@ -159,6 +173,7 @@ def test_IPParamStmt():
|
|||
assert_equal(stmt.param, param)
|
||||
assert_equal(stmt.ip, ip)
|
||||
|
||||
|
||||
def test_IPParamStmt_dump():
|
||||
""" Test IPParamStmt to_gerber()
|
||||
"""
|
||||
|
@ -170,6 +185,7 @@ def test_IPParamStmt_dump():
|
|||
ip = IPParamStmt.from_dict(stmt)
|
||||
assert_equal(ip.to_gerber(), '%IPNEG*%')
|
||||
|
||||
|
||||
def test_IPParamStmt_string():
|
||||
stmt = {'param': 'IP', 'ip': 'POS'}
|
||||
ip = IPParamStmt.from_dict(stmt)
|
||||
|
@ -179,22 +195,26 @@ def test_IPParamStmt_string():
|
|||
ip = IPParamStmt.from_dict(stmt)
|
||||
assert_equal(str(ip), '<Image Polarity: negative>')
|
||||
|
||||
|
||||
def test_IRParamStmt_factory():
|
||||
stmt = {'param': 'IR', 'angle': '45'}
|
||||
ir = IRParamStmt.from_dict(stmt)
|
||||
assert_equal(ir.param, 'IR')
|
||||
assert_equal(ir.angle, 45)
|
||||
|
||||
|
||||
def test_IRParamStmt_dump():
|
||||
stmt = {'param': 'IR', 'angle': '45'}
|
||||
ir = IRParamStmt.from_dict(stmt)
|
||||
assert_equal(ir.to_gerber(), '%IR45*%')
|
||||
|
||||
|
||||
def test_IRParamStmt_string():
|
||||
stmt = {'param': 'IR', 'angle': '45'}
|
||||
ir = IRParamStmt.from_dict(stmt)
|
||||
assert_equal(str(ir), '<Image Angle: 45>')
|
||||
|
||||
|
||||
def test_OFParamStmt_factory():
|
||||
""" Test OFParamStmt factory
|
||||
"""
|
||||
|
@ -203,6 +223,7 @@ def test_OFParamStmt_factory():
|
|||
assert_equal(of.a, 0.1234567)
|
||||
assert_equal(of.b, 0.1234567)
|
||||
|
||||
|
||||
def test_OFParamStmt():
|
||||
""" Test IPParamStmt initialization
|
||||
"""
|
||||
|
@ -213,6 +234,7 @@ def test_OFParamStmt():
|
|||
assert_equal(stmt.a, val)
|
||||
assert_equal(stmt.b, val)
|
||||
|
||||
|
||||
def test_OFParamStmt_dump():
|
||||
""" Test OFParamStmt to_gerber()
|
||||
"""
|
||||
|
@ -220,10 +242,11 @@ def test_OFParamStmt_dump():
|
|||
of = OFParamStmt.from_dict(stmt)
|
||||
assert_equal(of.to_gerber(), '%OFA0.12345B0.12345*%')
|
||||
|
||||
|
||||
def test_OFParamStmt_conversion():
|
||||
stmt = {'param': 'OF', 'a': '2.54', 'b': '25.4'}
|
||||
of = OFParamStmt.from_dict(stmt)
|
||||
of.units='metric'
|
||||
of.units = 'metric'
|
||||
|
||||
# No effect
|
||||
of.to_metric()
|
||||
|
@ -235,7 +258,7 @@ def test_OFParamStmt_conversion():
|
|||
assert_equal(of.a, 0.1)
|
||||
assert_equal(of.b, 1.0)
|
||||
|
||||
#No effect
|
||||
# No effect
|
||||
of.to_inch()
|
||||
assert_equal(of.a, 0.1)
|
||||
assert_equal(of.b, 1.0)
|
||||
|
@ -244,7 +267,7 @@ def test_OFParamStmt_conversion():
|
|||
of = OFParamStmt.from_dict(stmt)
|
||||
of.units = 'inch'
|
||||
|
||||
#No effect
|
||||
# No effect
|
||||
of.to_inch()
|
||||
assert_equal(of.a, 0.1)
|
||||
assert_equal(of.b, 1.0)
|
||||
|
@ -254,11 +277,12 @@ def test_OFParamStmt_conversion():
|
|||
assert_equal(of.a, 2.54)
|
||||
assert_equal(of.b, 25.4)
|
||||
|
||||
#No effect
|
||||
# No effect
|
||||
of.to_metric()
|
||||
assert_equal(of.a, 2.54)
|
||||
assert_equal(of.b, 25.4)
|
||||
|
||||
|
||||
def test_OFParamStmt_offset():
|
||||
s = OFParamStmt('OF', 0, 0)
|
||||
s.offset(1, 0)
|
||||
|
@ -268,6 +292,7 @@ def test_OFParamStmt_offset():
|
|||
assert_equal(s.a, 1.)
|
||||
assert_equal(s.b, 1.)
|
||||
|
||||
|
||||
def test_OFParamStmt_string():
|
||||
""" Test OFParamStmt __str__
|
||||
"""
|
||||
|
@ -275,6 +300,7 @@ def test_OFParamStmt_string():
|
|||
of = OFParamStmt.from_dict(stmt)
|
||||
assert_equal(str(of), '<Offset: X: 0.123456 Y: 0.123456 >')
|
||||
|
||||
|
||||
def test_SFParamStmt_factory():
|
||||
stmt = {'param': 'SF', 'a': '1.4', 'b': '0.9'}
|
||||
sf = SFParamStmt.from_dict(stmt)
|
||||
|
@ -282,18 +308,20 @@ def test_SFParamStmt_factory():
|
|||
assert_equal(sf.a, 1.4)
|
||||
assert_equal(sf.b, 0.9)
|
||||
|
||||
|
||||
def test_SFParamStmt_dump():
|
||||
stmt = {'param': 'SF', 'a': '1.4', 'b': '0.9'}
|
||||
sf = SFParamStmt.from_dict(stmt)
|
||||
assert_equal(sf.to_gerber(), '%SFA1.4B0.9*%')
|
||||
|
||||
|
||||
def test_SFParamStmt_conversion():
|
||||
stmt = {'param': 'OF', 'a': '2.54', 'b': '25.4'}
|
||||
of = SFParamStmt.from_dict(stmt)
|
||||
of.units = 'metric'
|
||||
of.to_metric()
|
||||
|
||||
#No effect
|
||||
# No effect
|
||||
assert_equal(of.a, 2.54)
|
||||
assert_equal(of.b, 25.4)
|
||||
|
||||
|
@ -302,7 +330,7 @@ def test_SFParamStmt_conversion():
|
|||
assert_equal(of.a, 0.1)
|
||||
assert_equal(of.b, 1.0)
|
||||
|
||||
#No effect
|
||||
# No effect
|
||||
of.to_inch()
|
||||
assert_equal(of.a, 0.1)
|
||||
assert_equal(of.b, 1.0)
|
||||
|
@ -311,7 +339,7 @@ def test_SFParamStmt_conversion():
|
|||
of = SFParamStmt.from_dict(stmt)
|
||||
of.units = 'inch'
|
||||
|
||||
#No effect
|
||||
# No effect
|
||||
of.to_inch()
|
||||
assert_equal(of.a, 0.1)
|
||||
assert_equal(of.b, 1.0)
|
||||
|
@ -321,11 +349,12 @@ def test_SFParamStmt_conversion():
|
|||
assert_equal(of.a, 2.54)
|
||||
assert_equal(of.b, 25.4)
|
||||
|
||||
#No effect
|
||||
# No effect
|
||||
of.to_metric()
|
||||
assert_equal(of.a, 2.54)
|
||||
assert_equal(of.b, 25.4)
|
||||
|
||||
|
||||
def test_SFParamStmt_offset():
|
||||
s = SFParamStmt('OF', 0, 0)
|
||||
s.offset(1, 0)
|
||||
|
@ -335,11 +364,13 @@ def test_SFParamStmt_offset():
|
|||
assert_equal(s.a, 1.)
|
||||
assert_equal(s.b, 1.)
|
||||
|
||||
|
||||
def test_SFParamStmt_string():
|
||||
stmt = {'param': 'SF', 'a': '1.4', 'b': '0.9'}
|
||||
sf = SFParamStmt.from_dict(stmt)
|
||||
assert_equal(str(sf), '<Scale Factor: X: 1.4 Y: 0.9>')
|
||||
|
||||
|
||||
def test_LPParamStmt_factory():
|
||||
""" Test LPParamStmt factory
|
||||
"""
|
||||
|
@ -351,6 +382,7 @@ def test_LPParamStmt_factory():
|
|||
lp = LPParamStmt.from_dict(stmt)
|
||||
assert_equal(lp.lp, 'dark')
|
||||
|
||||
|
||||
def test_LPParamStmt_dump():
|
||||
""" Test LPParamStmt to_gerber()
|
||||
"""
|
||||
|
@ -362,6 +394,7 @@ def test_LPParamStmt_dump():
|
|||
lp = LPParamStmt.from_dict(stmt)
|
||||
assert_equal(lp.to_gerber(), '%LPD*%')
|
||||
|
||||
|
||||
def test_LPParamStmt_string():
|
||||
""" Test LPParamStmt.__str__()
|
||||
"""
|
||||
|
@ -373,6 +406,7 @@ def test_LPParamStmt_string():
|
|||
lp = LPParamStmt.from_dict(stmt)
|
||||
assert_equal(str(lp), '<Level Polarity: clear>')
|
||||
|
||||
|
||||
def test_AMParamStmt_factory():
|
||||
name = 'DONUTVAR'
|
||||
macro = (
|
||||
|
@ -387,7 +421,7 @@ def test_AMParamStmt_factory():
|
|||
7,0,0,7,6,0.2,0*
|
||||
8,THIS IS AN UNSUPPORTED PRIMITIVE*
|
||||
''')
|
||||
s = AMParamStmt.from_dict({'param': 'AM', 'name': name, 'macro': macro })
|
||||
s = AMParamStmt.from_dict({'param': 'AM', 'name': name, 'macro': macro})
|
||||
s.build()
|
||||
assert_equal(len(s.primitives), 10)
|
||||
assert_true(isinstance(s.primitives[0], AMCommentPrimitive))
|
||||
|
@ -401,15 +435,16 @@ def test_AMParamStmt_factory():
|
|||
assert_true(isinstance(s.primitives[8], AMThermalPrimitive))
|
||||
assert_true(isinstance(s.primitives[9], AMUnsupportPrimitive))
|
||||
|
||||
|
||||
def testAMParamStmt_conversion():
|
||||
name = 'POLYGON'
|
||||
macro = '5,1,8,25.4,25.4,25.4,0*'
|
||||
s = AMParamStmt.from_dict({'param': 'AM', 'name': name, 'macro': macro })
|
||||
s = AMParamStmt.from_dict({'param': 'AM', 'name': name, 'macro': macro})
|
||||
|
||||
s.build()
|
||||
s.units = 'metric'
|
||||
|
||||
#No effect
|
||||
# No effect
|
||||
s.to_metric()
|
||||
assert_equal(s.primitives[0].position, (25.4, 25.4))
|
||||
assert_equal(s.primitives[0].diameter, 25.4)
|
||||
|
@ -419,17 +454,17 @@ def testAMParamStmt_conversion():
|
|||
assert_equal(s.primitives[0].position, (1., 1.))
|
||||
assert_equal(s.primitives[0].diameter, 1.)
|
||||
|
||||
#No effect
|
||||
# No effect
|
||||
s.to_inch()
|
||||
assert_equal(s.primitives[0].position, (1., 1.))
|
||||
assert_equal(s.primitives[0].diameter, 1.)
|
||||
|
||||
macro = '5,1,8,1,1,1,0*'
|
||||
s = AMParamStmt.from_dict({'param': 'AM', 'name': name, 'macro': macro })
|
||||
s = AMParamStmt.from_dict({'param': 'AM', 'name': name, 'macro': macro})
|
||||
s.build()
|
||||
s.units = 'inch'
|
||||
|
||||
#No effect
|
||||
# No effect
|
||||
s.to_inch()
|
||||
assert_equal(s.primitives[0].position, (1., 1.))
|
||||
assert_equal(s.primitives[0].diameter, 1.)
|
||||
|
@ -439,42 +474,48 @@ def testAMParamStmt_conversion():
|
|||
assert_equal(s.primitives[0].position, (25.4, 25.4))
|
||||
assert_equal(s.primitives[0].diameter, 25.4)
|
||||
|
||||
#No effect
|
||||
# No effect
|
||||
s.to_metric()
|
||||
assert_equal(s.primitives[0].position, (25.4, 25.4))
|
||||
assert_equal(s.primitives[0].diameter, 25.4)
|
||||
|
||||
|
||||
def test_AMParamStmt_dump():
|
||||
name = 'POLYGON'
|
||||
macro = '5,1,8,25.4,25.4,25.4,0.0'
|
||||
s = AMParamStmt.from_dict({'param': 'AM', 'name': name, 'macro': macro })
|
||||
s = AMParamStmt.from_dict({'param': 'AM', 'name': name, 'macro': macro})
|
||||
s.build()
|
||||
|
||||
assert_equal(s.to_gerber(), '%AMPOLYGON*5,1,8,25.4,25.4,25.4,0.0*%')
|
||||
|
||||
|
||||
def test_AMParamStmt_string():
|
||||
name = 'POLYGON'
|
||||
macro = '5,1,8,25.4,25.4,25.4,0*'
|
||||
s = AMParamStmt.from_dict({'param': 'AM', 'name': name, 'macro': macro })
|
||||
s = AMParamStmt.from_dict({'param': 'AM', 'name': name, 'macro': macro})
|
||||
s.build()
|
||||
assert_equal(str(s), '<Aperture Macro POLYGON: 5,1,8,25.4,25.4,25.4,0*>')
|
||||
|
||||
|
||||
def test_ASParamStmt_factory():
|
||||
stmt = {'param': 'AS', 'mode': 'AXBY'}
|
||||
s = ASParamStmt.from_dict(stmt)
|
||||
assert_equal(s.param, 'AS')
|
||||
assert_equal(s.mode, 'AXBY')
|
||||
|
||||
|
||||
def test_ASParamStmt_dump():
|
||||
stmt = {'param': 'AS', 'mode': 'AXBY'}
|
||||
s = ASParamStmt.from_dict(stmt)
|
||||
assert_equal(s.to_gerber(), '%ASAXBY*%')
|
||||
|
||||
|
||||
def test_ASParamStmt_string():
|
||||
stmt = {'param': 'AS', 'mode': 'AXBY'}
|
||||
s = ASParamStmt.from_dict(stmt)
|
||||
assert_equal(str(s), '<Axis Select: AXBY>')
|
||||
|
||||
|
||||
def test_INParamStmt_factory():
|
||||
""" Test INParamStmt factory
|
||||
"""
|
||||
|
@ -482,6 +523,7 @@ def test_INParamStmt_factory():
|
|||
inp = INParamStmt.from_dict(stmt)
|
||||
assert_equal(inp.name, 'test')
|
||||
|
||||
|
||||
def test_INParamStmt_dump():
|
||||
""" Test INParamStmt to_gerber()
|
||||
"""
|
||||
|
@ -489,11 +531,13 @@ def test_INParamStmt_dump():
|
|||
inp = INParamStmt.from_dict(stmt)
|
||||
assert_equal(inp.to_gerber(), '%INtest*%')
|
||||
|
||||
|
||||
def test_INParamStmt_string():
|
||||
stmt = {'param': 'IN', 'name': 'test'}
|
||||
inp = INParamStmt.from_dict(stmt)
|
||||
assert_equal(str(inp), '<Image Name: test>')
|
||||
|
||||
|
||||
def test_LNParamStmt_factory():
|
||||
""" Test LNParamStmt factory
|
||||
"""
|
||||
|
@ -501,6 +545,7 @@ def test_LNParamStmt_factory():
|
|||
lnp = LNParamStmt.from_dict(stmt)
|
||||
assert_equal(lnp.name, 'test')
|
||||
|
||||
|
||||
def test_LNParamStmt_dump():
|
||||
""" Test LNParamStmt to_gerber()
|
||||
"""
|
||||
|
@ -508,11 +553,13 @@ def test_LNParamStmt_dump():
|
|||
lnp = LNParamStmt.from_dict(stmt)
|
||||
assert_equal(lnp.to_gerber(), '%LNtest*%')
|
||||
|
||||
|
||||
def test_LNParamStmt_string():
|
||||
stmt = {'param': 'LN', 'name': 'test'}
|
||||
lnp = LNParamStmt.from_dict(stmt)
|
||||
assert_equal(str(lnp), '<Level Name: test>')
|
||||
|
||||
|
||||
def test_comment_stmt():
|
||||
""" Test comment statement
|
||||
"""
|
||||
|
@ -520,31 +567,37 @@ def test_comment_stmt():
|
|||
assert_equal(stmt.type, 'COMMENT')
|
||||
assert_equal(stmt.comment, 'A comment')
|
||||
|
||||
|
||||
def test_comment_stmt_dump():
|
||||
""" Test CommentStmt to_gerber()
|
||||
"""
|
||||
stmt = CommentStmt('A comment')
|
||||
assert_equal(stmt.to_gerber(), 'G04A comment*')
|
||||
|
||||
|
||||
def test_comment_stmt_string():
|
||||
stmt = CommentStmt('A comment')
|
||||
assert_equal(str(stmt), '<Comment: A comment>')
|
||||
|
||||
|
||||
def test_eofstmt():
|
||||
""" Test EofStmt
|
||||
"""
|
||||
stmt = EofStmt()
|
||||
assert_equal(stmt.type, 'EOF')
|
||||
|
||||
|
||||
def test_eofstmt_dump():
|
||||
""" Test EofStmt to_gerber()
|
||||
"""
|
||||
stmt = EofStmt()
|
||||
assert_equal(stmt.to_gerber(), 'M02*')
|
||||
|
||||
|
||||
def test_eofstmt_string():
|
||||
assert_equal(str(EofStmt()), '<EOF Statement>')
|
||||
|
||||
|
||||
def test_quadmodestmt_factory():
|
||||
""" Test QuadrantModeStmt.from_gerber()
|
||||
"""
|
||||
|
@ -557,6 +610,7 @@ def test_quadmodestmt_factory():
|
|||
stmt = QuadrantModeStmt.from_gerber(line)
|
||||
assert_equal(stmt.mode, 'multi-quadrant')
|
||||
|
||||
|
||||
def test_quadmodestmt_validation():
|
||||
""" Test QuadrantModeStmt input validation
|
||||
"""
|
||||
|
@ -564,6 +618,7 @@ def test_quadmodestmt_validation():
|
|||
assert_raises(ValueError, QuadrantModeStmt.from_gerber, line)
|
||||
assert_raises(ValueError, QuadrantModeStmt, 'quadrant-ful')
|
||||
|
||||
|
||||
def test_quadmodestmt_dump():
|
||||
""" Test QuadrantModeStmt.to_gerber()
|
||||
"""
|
||||
|
@ -571,6 +626,7 @@ def test_quadmodestmt_dump():
|
|||
stmt = QuadrantModeStmt.from_gerber(line)
|
||||
assert_equal(stmt.to_gerber(), line)
|
||||
|
||||
|
||||
def test_regionmodestmt_factory():
|
||||
""" Test RegionModeStmt.from_gerber()
|
||||
"""
|
||||
|
@ -583,6 +639,7 @@ def test_regionmodestmt_factory():
|
|||
stmt = RegionModeStmt.from_gerber(line)
|
||||
assert_equal(stmt.mode, 'off')
|
||||
|
||||
|
||||
def test_regionmodestmt_validation():
|
||||
""" Test RegionModeStmt input validation
|
||||
"""
|
||||
|
@ -590,6 +647,7 @@ def test_regionmodestmt_validation():
|
|||
assert_raises(ValueError, RegionModeStmt.from_gerber, line)
|
||||
assert_raises(ValueError, RegionModeStmt, 'off-ish')
|
||||
|
||||
|
||||
def test_regionmodestmt_dump():
|
||||
""" Test RegionModeStmt.to_gerber()
|
||||
"""
|
||||
|
@ -597,6 +655,7 @@ def test_regionmodestmt_dump():
|
|||
stmt = RegionModeStmt.from_gerber(line)
|
||||
assert_equal(stmt.to_gerber(), line)
|
||||
|
||||
|
||||
def test_unknownstmt():
|
||||
""" Test UnknownStmt
|
||||
"""
|
||||
|
@ -605,6 +664,7 @@ def test_unknownstmt():
|
|||
assert_equal(stmt.type, 'UNKNOWN')
|
||||
assert_equal(stmt.line, line)
|
||||
|
||||
|
||||
def test_unknownstmt_dump():
|
||||
""" Test UnknownStmt.to_gerber()
|
||||
"""
|
||||
|
@ -613,15 +673,17 @@ def test_unknownstmt_dump():
|
|||
stmt = UnknownStmt(line)
|
||||
assert_equal(stmt.to_gerber(), line)
|
||||
|
||||
|
||||
def test_statement_string():
|
||||
""" Test Statement.__str__()
|
||||
"""
|
||||
stmt = Statement('PARAM')
|
||||
assert_in('type=PARAM', str(stmt))
|
||||
stmt.test='PASS'
|
||||
stmt.test = 'PASS'
|
||||
assert_in('test=PASS', str(stmt))
|
||||
assert_in('type=PARAM', str(stmt))
|
||||
|
||||
|
||||
def test_ADParamStmt_factory():
|
||||
""" Test ADParamStmt factory
|
||||
"""
|
||||
|
@ -653,12 +715,14 @@ def test_ADParamStmt_factory():
|
|||
assert_equal(ad.shape, 'R')
|
||||
assert_equal(ad.modifiers, [(1.42, 1.24)])
|
||||
|
||||
|
||||
def test_ADParamStmt_conversion():
|
||||
stmt = {'param': 'AD', 'd': 0, 'shape': 'C', 'modifiers': '25.4X25.4,25.4X25.4'}
|
||||
stmt = {'param': 'AD', 'd': 0, 'shape': 'C',
|
||||
'modifiers': '25.4X25.4,25.4X25.4'}
|
||||
ad = ADParamStmt.from_dict(stmt)
|
||||
ad.units = 'metric'
|
||||
|
||||
#No effect
|
||||
# No effect
|
||||
ad.to_metric()
|
||||
assert_equal(ad.modifiers[0], (25.4, 25.4))
|
||||
assert_equal(ad.modifiers[1], (25.4, 25.4))
|
||||
|
@ -668,7 +732,7 @@ def test_ADParamStmt_conversion():
|
|||
assert_equal(ad.modifiers[0], (1., 1.))
|
||||
assert_equal(ad.modifiers[1], (1., 1.))
|
||||
|
||||
#No effect
|
||||
# No effect
|
||||
ad.to_inch()
|
||||
assert_equal(ad.modifiers[0], (1., 1.))
|
||||
assert_equal(ad.modifiers[1], (1., 1.))
|
||||
|
@ -677,7 +741,7 @@ def test_ADParamStmt_conversion():
|
|||
ad = ADParamStmt.from_dict(stmt)
|
||||
ad.units = 'inch'
|
||||
|
||||
#No effect
|
||||
# No effect
|
||||
ad.to_inch()
|
||||
assert_equal(ad.modifiers[0], (1., 1.))
|
||||
assert_equal(ad.modifiers[1], (1., 1.))
|
||||
|
@ -686,11 +750,12 @@ def test_ADParamStmt_conversion():
|
|||
assert_equal(ad.modifiers[0], (25.4, 25.4))
|
||||
assert_equal(ad.modifiers[1], (25.4, 25.4))
|
||||
|
||||
#No effect
|
||||
# No effect
|
||||
ad.to_metric()
|
||||
assert_equal(ad.modifiers[0], (25.4, 25.4))
|
||||
assert_equal(ad.modifiers[1], (25.4, 25.4))
|
||||
|
||||
|
||||
def test_ADParamStmt_dump():
|
||||
stmt = {'param': 'AD', 'd': 0, 'shape': 'C'}
|
||||
ad = ADParamStmt.from_dict(stmt)
|
||||
|
@ -699,6 +764,7 @@ def test_ADParamStmt_dump():
|
|||
ad = ADParamStmt.from_dict(stmt)
|
||||
assert_equal(ad.to_gerber(), '%ADD0C,1X1,1X1*%')
|
||||
|
||||
|
||||
def test_ADPamramStmt_string():
|
||||
stmt = {'param': 'AD', 'd': 0, 'shape': 'C'}
|
||||
ad = ADParamStmt.from_dict(stmt)
|
||||
|
@ -716,12 +782,14 @@ def test_ADPamramStmt_string():
|
|||
ad = ADParamStmt.from_dict(stmt)
|
||||
assert_equal(str(ad), '<Aperture Definition: 0: test>')
|
||||
|
||||
|
||||
def test_MIParamStmt_factory():
|
||||
stmt = {'param': 'MI', 'a': 1, 'b': 1}
|
||||
mi = MIParamStmt.from_dict(stmt)
|
||||
assert_equal(mi.a, 1)
|
||||
assert_equal(mi.b, 1)
|
||||
|
||||
|
||||
def test_MIParamStmt_dump():
|
||||
stmt = {'param': 'MI', 'a': 1, 'b': 1}
|
||||
mi = MIParamStmt.from_dict(stmt)
|
||||
|
@ -733,6 +801,7 @@ def test_MIParamStmt_dump():
|
|||
mi = MIParamStmt.from_dict(stmt)
|
||||
assert_equal(mi.to_gerber(), '%MIA0B1*%')
|
||||
|
||||
|
||||
def test_MIParamStmt_string():
|
||||
stmt = {'param': 'MI', 'a': 1, 'b': 1}
|
||||
mi = MIParamStmt.from_dict(stmt)
|
||||
|
@ -746,6 +815,7 @@ def test_MIParamStmt_string():
|
|||
mi = MIParamStmt.from_dict(stmt)
|
||||
assert_equal(str(mi), '<Image Mirror: A=1 B=0>')
|
||||
|
||||
|
||||
def test_coordstmt_ctor():
|
||||
cs = CoordStmt('G04', 0.0, 0.1, 0.2, 0.3, 'D01', FileSettings())
|
||||
assert_equal(cs.function, 'G04')
|
||||
|
@ -755,8 +825,10 @@ def test_coordstmt_ctor():
|
|||
assert_equal(cs.j, 0.3)
|
||||
assert_equal(cs.op, 'D01')
|
||||
|
||||
|
||||
def test_coordstmt_factory():
|
||||
stmt = {'function': 'G04', 'x': '0', 'y': '001', 'i': '002', 'j': '003', 'op': 'D01'}
|
||||
stmt = {'function': 'G04', 'x': '0', 'y': '001',
|
||||
'i': '002', 'j': '003', 'op': 'D01'}
|
||||
cs = CoordStmt.from_dict(stmt, FileSettings())
|
||||
assert_equal(cs.function, 'G04')
|
||||
assert_equal(cs.x, 0.0)
|
||||
|
@ -765,15 +837,17 @@ def test_coordstmt_factory():
|
|||
assert_equal(cs.j, 0.3)
|
||||
assert_equal(cs.op, 'D01')
|
||||
|
||||
|
||||
def test_coordstmt_dump():
|
||||
cs = CoordStmt('G04', 0.0, 0.1, 0.2, 0.3, 'D01', FileSettings())
|
||||
assert_equal(cs.to_gerber(FileSettings()), 'G04X0Y001I002J003D01*')
|
||||
|
||||
|
||||
def test_coordstmt_conversion():
|
||||
cs = CoordStmt('G71', 25.4, 25.4, 25.4, 25.4, 'D01', FileSettings())
|
||||
cs.units = 'metric'
|
||||
|
||||
#No effect
|
||||
# No effect
|
||||
cs.to_metric()
|
||||
assert_equal(cs.x, 25.4)
|
||||
assert_equal(cs.y, 25.4)
|
||||
|
@ -789,7 +863,7 @@ def test_coordstmt_conversion():
|
|||
assert_equal(cs.j, 1.)
|
||||
assert_equal(cs.function, 'G70')
|
||||
|
||||
#No effect
|
||||
# No effect
|
||||
cs.to_inch()
|
||||
assert_equal(cs.x, 1.)
|
||||
assert_equal(cs.y, 1.)
|
||||
|
@ -800,7 +874,7 @@ def test_coordstmt_conversion():
|
|||
cs = CoordStmt('G70', 1., 1., 1., 1., 'D01', FileSettings())
|
||||
cs.units = 'inch'
|
||||
|
||||
#No effect
|
||||
# No effect
|
||||
cs.to_inch()
|
||||
assert_equal(cs.x, 1.)
|
||||
assert_equal(cs.y, 1.)
|
||||
|
@ -815,7 +889,7 @@ def test_coordstmt_conversion():
|
|||
assert_equal(cs.j, 25.4)
|
||||
assert_equal(cs.function, 'G71')
|
||||
|
||||
#No effect
|
||||
# No effect
|
||||
cs.to_metric()
|
||||
assert_equal(cs.x, 25.4)
|
||||
assert_equal(cs.y, 25.4)
|
||||
|
@ -823,6 +897,7 @@ def test_coordstmt_conversion():
|
|||
assert_equal(cs.j, 25.4)
|
||||
assert_equal(cs.function, 'G71')
|
||||
|
||||
|
||||
def test_coordstmt_offset():
|
||||
c = CoordStmt('G71', 0, 0, 0, 0, 'D01', FileSettings())
|
||||
c.offset(1, 0)
|
||||
|
@ -836,9 +911,11 @@ def test_coordstmt_offset():
|
|||
assert_equal(c.i, 1.)
|
||||
assert_equal(c.j, 1.)
|
||||
|
||||
|
||||
def test_coordstmt_string():
|
||||
cs = CoordStmt('G04', 0, 1, 2, 3, 'D01', FileSettings())
|
||||
assert_equal(str(cs), '<Coordinate Statement: Fn: G04 X: 0 Y: 1 I: 2 J: 3 Op: Lights On>')
|
||||
assert_equal(str(cs),
|
||||
'<Coordinate Statement: Fn: G04 X: 0 Y: 1 I: 2 J: 3 Op: Lights On>')
|
||||
cs = CoordStmt('G04', None, None, None, None, 'D02', FileSettings())
|
||||
assert_equal(str(cs), '<Coordinate Statement: Fn: G04 Op: Lights Off>')
|
||||
cs = CoordStmt('G04', None, None, None, None, 'D03', FileSettings())
|
||||
|
@ -846,6 +923,7 @@ def test_coordstmt_string():
|
|||
cs = CoordStmt('G04', None, None, None, None, 'TEST', FileSettings())
|
||||
assert_equal(str(cs), '<Coordinate Statement: Fn: G04 Op: TEST>')
|
||||
|
||||
|
||||
def test_aperturestmt_ctor():
|
||||
ast = ApertureStmt(3, False)
|
||||
assert_equal(ast.d, 3)
|
||||
|
@ -860,11 +938,10 @@ def test_aperturestmt_ctor():
|
|||
assert_equal(ast.d, 3)
|
||||
assert_equal(ast.deprecated, False)
|
||||
|
||||
|
||||
def test_aperturestmt_dump():
|
||||
ast = ApertureStmt(3, False)
|
||||
assert_equal(ast.to_gerber(), 'D3*')
|
||||
ast = ApertureStmt(3, True)
|
||||
assert_equal(ast.to_gerber(), 'G54D3*')
|
||||
assert_equal(str(ast), '<Aperture: 3>')
|
||||
|
||||
|
||||
|
|
|
@ -2,18 +2,21 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Author: Hamilton Kibbe <ham@hamiltonkib.be>
|
||||
from ..ipc356 import *
|
||||
from ..ipc356 import *
|
||||
from ..cam import FileSettings
|
||||
from .tests import *
|
||||
|
||||
import os
|
||||
|
||||
IPC_D_356_FILE = os.path.join(os.path.dirname(__file__),
|
||||
'resources/ipc-d-356.ipc')
|
||||
'resources/ipc-d-356.ipc')
|
||||
|
||||
|
||||
def test_read():
|
||||
ipcfile = read(IPC_D_356_FILE)
|
||||
assert(isinstance(ipcfile, IPC_D_356))
|
||||
|
||||
|
||||
def test_parser():
|
||||
ipcfile = read(IPC_D_356_FILE)
|
||||
assert_equal(ipcfile.settings.units, 'inch')
|
||||
|
@ -28,6 +31,7 @@ def test_parser():
|
|||
assert_equal(set(ipcfile.outlines[0].points),
|
||||
{(0., 0.), (2.25, 0.), (2.25, 1.5), (0., 1.5), (0.13, 0.024)})
|
||||
|
||||
|
||||
def test_comment():
|
||||
c = IPC356_Comment('Layer Stackup:')
|
||||
assert_equal(c.comment, 'Layer Stackup:')
|
||||
|
@ -36,6 +40,7 @@ def test_comment():
|
|||
assert_raises(ValueError, IPC356_Comment.from_line, 'P JOB')
|
||||
assert_equal(str(c), '<IPC-D-356 Comment: Layer Stackup:>')
|
||||
|
||||
|
||||
def test_parameter():
|
||||
p = IPC356_Parameter('VER', 'IPC-D-356A')
|
||||
assert_equal(p.parameter, 'VER')
|
||||
|
@ -43,27 +48,32 @@ def test_parameter():
|
|||
p = IPC356_Parameter.from_line('P VER IPC-D-356A ')
|
||||
assert_equal(p.parameter, 'VER')
|
||||
assert_equal(p.value, 'IPC-D-356A')
|
||||
assert_raises(ValueError, IPC356_Parameter.from_line, 'C Layer Stackup: ')
|
||||
assert_raises(ValueError, IPC356_Parameter.from_line,
|
||||
'C Layer Stackup: ')
|
||||
assert_equal(str(p), '<IPC-D-356 Parameter: VER=IPC-D-356A>')
|
||||
|
||||
|
||||
def test_eof():
|
||||
e = IPC356_EndOfFile()
|
||||
assert_equal(e.to_netlist(), '999')
|
||||
assert_equal(str(e), '<IPC-D-356 EOF>')
|
||||
|
||||
|
||||
def test_outline():
|
||||
type = 'BOARD_EDGE'
|
||||
points = [(0.01, 0.01), (2., 2.), (4., 2.), (4., 6.)]
|
||||
b = IPC356_Outline(type, points)
|
||||
assert_equal(b.type, type)
|
||||
assert_equal(b.points, points)
|
||||
b = IPC356_Outline.from_line('389BOARD_EDGE X100Y100 X20000Y20000'
|
||||
' X40000 Y60000', FileSettings(units='inch'))
|
||||
b = IPC356_Outline.from_line('389BOARD_EDGE X100Y100 X20000Y20000 X40000 Y60000',
|
||||
FileSettings(units='inch'))
|
||||
assert_equal(b.type, 'BOARD_EDGE')
|
||||
assert_equal(b.points, points)
|
||||
|
||||
|
||||
def test_test_record():
|
||||
assert_raises(ValueError, IPC356_TestRecord.from_line, 'P JOB', FileSettings())
|
||||
assert_raises(ValueError, IPC356_TestRecord.from_line,
|
||||
'P JOB', FileSettings())
|
||||
record_string = '317+5VDC VIA - D0150PA00X 006647Y 012900X0000 S3'
|
||||
r = IPC356_TestRecord.from_line(record_string, FileSettings(units='inch'))
|
||||
assert_equal(r.feature_type, 'through-hole')
|
||||
|
@ -81,8 +91,7 @@ def test_test_record():
|
|||
assert_almost_equal(r.x_coord, 6.647)
|
||||
assert_almost_equal(r.y_coord, 12.9)
|
||||
assert_equal(r.rect_x, 0.)
|
||||
assert_equal(str(r),
|
||||
'<IPC-D-356 +5VDC Test Record: through-hole>')
|
||||
assert_equal(str(r), '<IPC-D-356 +5VDC Test Record: through-hole>')
|
||||
|
||||
record_string = '327+3.3VDC R40 -1 PA01X 032100Y 007124X0236Y0315R180 S0'
|
||||
r = IPC356_TestRecord.from_line(record_string, FileSettings(units='inch'))
|
||||
|
@ -98,13 +107,13 @@ def test_test_record():
|
|||
assert_almost_equal(r.rect_y, 0.0315)
|
||||
assert_equal(r.rect_rotation, 180)
|
||||
assert_equal(r.soldermask_info, 'none')
|
||||
r = IPC356_TestRecord.from_line(record_string, FileSettings(units='metric'))
|
||||
r = IPC356_TestRecord.from_line(
|
||||
record_string, FileSettings(units='metric'))
|
||||
assert_almost_equal(r.x_coord, 32.1)
|
||||
assert_almost_equal(r.y_coord, 7.124)
|
||||
assert_almost_equal(r.rect_x, 0.236)
|
||||
assert_almost_equal(r.rect_y, 0.315)
|
||||
|
||||
|
||||
record_string = '317 J4 -M2 D0330PA00X 012447Y 008030X0000 S1'
|
||||
r = IPC356_TestRecord.from_line(record_string, FileSettings(units='inch'))
|
||||
assert_equal(r.feature_type, 'through-hole')
|
||||
|
|
|
@ -15,7 +15,7 @@ def test_guess_layer_class():
|
|||
test_vectors = [(None, 'unknown'), ('NCDRILL.TXT', 'unknown'),
|
||||
('example_board.gtl', 'top'),
|
||||
('exampmle_board.sst', 'topsilk'),
|
||||
('ipc-d-356.ipc', 'ipc_netlist'),]
|
||||
('ipc-d-356.ipc', 'ipc_netlist'), ]
|
||||
|
||||
for hint in hints:
|
||||
for ext in hint.ext:
|
||||
|
|
Plik diff jest za duży
Load Diff
|
@ -9,31 +9,35 @@ from .tests import *
|
|||
|
||||
|
||||
TOP_COPPER_FILE = os.path.join(os.path.dirname(__file__),
|
||||
'resources/top_copper.GTL')
|
||||
'resources/top_copper.GTL')
|
||||
|
||||
MULTILINE_READ_FILE = os.path.join(os.path.dirname(__file__),
|
||||
'resources/multiline_read.ger')
|
||||
'resources/multiline_read.ger')
|
||||
|
||||
|
||||
def test_read():
|
||||
top_copper = read(TOP_COPPER_FILE)
|
||||
assert(isinstance(top_copper, GerberFile))
|
||||
|
||||
|
||||
def test_multiline_read():
|
||||
multiline = read(MULTILINE_READ_FILE)
|
||||
assert(isinstance(multiline, GerberFile))
|
||||
assert_equal(10, len(multiline.statements))
|
||||
|
||||
|
||||
def test_comments_parameter():
|
||||
top_copper = read(TOP_COPPER_FILE)
|
||||
assert_equal(top_copper.comments[0], 'This is a comment,:')
|
||||
|
||||
|
||||
def test_size_parameter():
|
||||
top_copper = read(TOP_COPPER_FILE)
|
||||
size = top_copper.size
|
||||
assert_almost_equal(size[0], 2.256900, 6)
|
||||
assert_almost_equal(size[1], 1.500000, 6)
|
||||
|
||||
|
||||
def test_conversion():
|
||||
import copy
|
||||
top_copper = read(TOP_COPPER_FILE)
|
||||
|
@ -50,4 +54,3 @@ def test_conversion():
|
|||
|
||||
for i, m in zip(top_copper.primitives, top_copper_inch.primitives):
|
||||
assert_equal(i, m)
|
||||
|
||||
|
|
|
@ -52,7 +52,7 @@ def test_format():
|
|||
((2, 6), '-1', -0.000001), ((2, 5), '-1', -0.00001),
|
||||
((2, 4), '-1', -0.0001), ((2, 3), '-1', -0.001),
|
||||
((2, 2), '-1', -0.01), ((2, 1), '-1', -0.1),
|
||||
((2, 6), '0', 0) ]
|
||||
((2, 6), '0', 0)]
|
||||
for fmt, string, value in test_cases:
|
||||
assert_equal(value, parse_gerber_value(string, fmt, zero_suppression))
|
||||
assert_equal(string, write_gerber_value(value, fmt, zero_suppression))
|
||||
|
@ -76,7 +76,7 @@ def test_decimal_truncation():
|
|||
value = 1.123456789
|
||||
for x in range(10):
|
||||
result = decimal_string(value, precision=x)
|
||||
calculated = '1.' + ''.join(str(y) for y in range(1,x+1))
|
||||
calculated = '1.' + ''.join(str(y) for y in range(1, x + 1))
|
||||
assert_equal(result, calculated)
|
||||
|
||||
|
||||
|
@ -96,25 +96,34 @@ def test_parse_format_validation():
|
|||
"""
|
||||
assert_raises(ValueError, parse_gerber_value, '00001111', (7, 5))
|
||||
assert_raises(ValueError, parse_gerber_value, '00001111', (5, 8))
|
||||
assert_raises(ValueError, parse_gerber_value, '00001111', (13,1))
|
||||
|
||||
assert_raises(ValueError, parse_gerber_value, '00001111', (13, 1))
|
||||
|
||||
|
||||
def test_write_format_validation():
|
||||
""" Test write_gerber_value() format validation
|
||||
"""
|
||||
assert_raises(ValueError, write_gerber_value, 69.0, (7, 5))
|
||||
assert_raises(ValueError, write_gerber_value, 69.0, (5, 8))
|
||||
assert_raises(ValueError, write_gerber_value, 69.0, (13,1))
|
||||
assert_raises(ValueError, write_gerber_value, 69.0, (13, 1))
|
||||
|
||||
|
||||
def test_detect_format_with_short_file():
|
||||
""" Verify file format detection works with short files
|
||||
"""
|
||||
assert_equal('unknown', detect_file_format('gerber/tests/__init__.py'))
|
||||
|
||||
|
||||
|
||||
def test_validate_coordinates():
|
||||
assert_raises(TypeError, validate_coordinates, 3)
|
||||
assert_raises(TypeError, validate_coordinates, 3.1)
|
||||
assert_raises(TypeError, validate_coordinates, '14')
|
||||
assert_raises(TypeError, validate_coordinates, (0,))
|
||||
assert_raises(TypeError, validate_coordinates, (0,1,2))
|
||||
assert_raises(TypeError, validate_coordinates, (0,'string'))
|
||||
assert_raises(TypeError, validate_coordinates, (0, 1, 2))
|
||||
assert_raises(TypeError, validate_coordinates, (0, 'string'))
|
||||
|
||||
|
||||
def test_convex_hull():
|
||||
points = [(0, 0), (1, 0), (1, 1), (0.5, 0.5), (0, 1), (0, 0)]
|
||||
expected = [(0, 0), (1, 0), (1, 1), (0, 1), (0, 0)]
|
||||
assert_equal(set(convex_hull(points)), set(expected))
|
||||
|
|
@ -16,7 +16,8 @@ from nose import with_setup
|
|||
|
||||
__all__ = ['assert_in', 'assert_not_in', 'assert_equal', 'assert_not_equal',
|
||||
'assert_almost_equal', 'assert_array_almost_equal', 'assert_true',
|
||||
'assert_false', 'assert_raises', 'raises', 'with_setup' ]
|
||||
'assert_false', 'assert_raises', 'raises', 'with_setup']
|
||||
|
||||
|
||||
def assert_array_almost_equal(arr1, arr2, decimal=6):
|
||||
assert_equal(len(arr1), len(arr2))
|
||||
|
|
|
@ -23,15 +23,15 @@ This module provides utility functions for working with Gerber and Excellon
|
|||
files.
|
||||
"""
|
||||
|
||||
# Author: Hamilton Kibbe <ham@hamiltonkib.be>
|
||||
# License:
|
||||
|
||||
import os
|
||||
from math import radians, sin, cos
|
||||
from operator import sub
|
||||
from copy import deepcopy
|
||||
from pyhull.convex_hull import ConvexHull
|
||||
|
||||
MILLIMETERS_PER_INCH = 25.4
|
||||
|
||||
|
||||
def parse_gerber_value(value, format=(2, 5), zero_suppression='trailing'):
|
||||
""" Convert gerber/excellon formatted string to floating-point number
|
||||
|
||||
|
@ -92,7 +92,8 @@ def parse_gerber_value(value, format=(2, 5), zero_suppression='trailing'):
|
|||
else:
|
||||
digits = list(value)
|
||||
|
||||
result = float(''.join(digits[:integer_digits] + ['.'] + digits[integer_digits:]))
|
||||
result = float(
|
||||
''.join(digits[:integer_digits] + ['.'] + digits[integer_digits:]))
|
||||
return -result if negative else result
|
||||
|
||||
|
||||
|
@ -132,7 +133,8 @@ def write_gerber_value(value, format=(2, 5), zero_suppression='trailing'):
|
|||
if MAX_DIGITS > 13 or integer_digits > 6 or decimal_digits > 7:
|
||||
raise ValueError('Parser only supports precision up to 6:7 format')
|
||||
|
||||
# Edge case... (per Gerber spec we should return 0 in all cases, see page 77)
|
||||
# Edge case... (per Gerber spec we should return 0 in all cases, see page
|
||||
# 77)
|
||||
if value == 0:
|
||||
return '0'
|
||||
|
||||
|
@ -222,7 +224,7 @@ def detect_file_format(data):
|
|||
elif '%FS' in line:
|
||||
return 'rs274x'
|
||||
elif ((len(line.split()) >= 2) and
|
||||
(line.split()[0] == 'P') and (line.split()[1] == 'JOB')):
|
||||
(line.split()[0] == 'P') and (line.split()[1] == 'JOB')):
|
||||
return 'ipc_d_356'
|
||||
return 'unknown'
|
||||
|
||||
|
@ -252,6 +254,7 @@ def metric(value):
|
|||
"""
|
||||
return value * MILLIMETERS_PER_INCH
|
||||
|
||||
|
||||
def inch(value):
|
||||
""" Convert millimeter value to inches
|
||||
|
||||
|
@ -295,6 +298,26 @@ def rotate_point(point, angle, center=(0.0, 0.0)):
|
|||
|
||||
|
||||
def listdir(directory, ignore_hidden=True, ignore_os=True):
|
||||
""" List files in given directory.
|
||||
Differs from os.listdir() in that hidden and OS-generated files are ignored
|
||||
by default.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
directory : str
|
||||
path to the directory for which to list files.
|
||||
|
||||
ignore_hidden : bool
|
||||
If True, ignore files beginning with a leading '.'
|
||||
|
||||
ignore_os : bool
|
||||
If True, ignore OS-generated files, e.g. Thumbs.db
|
||||
|
||||
Returns
|
||||
-------
|
||||
files : list
|
||||
list of files in specified directory
|
||||
"""
|
||||
os_files = ('.DS_Store', 'Thumbs.db', 'ethumbs.db')
|
||||
files = os.listdir(directory)
|
||||
if ignore_hidden:
|
||||
|
@ -302,3 +325,9 @@ def listdir(directory, ignore_hidden=True, ignore_os=True):
|
|||
if ignore_os:
|
||||
files = [f for f in files if not f in os_files]
|
||||
return files
|
||||
|
||||
|
||||
def convex_hull(points):
|
||||
vertices = ConvexHull(points).vertices
|
||||
return [points[idx] for idx in
|
||||
set([point for pair in vertices for point in pair])]
|
||||
|
|
|
@ -1,2 +1,3 @@
|
|||
## The following requirements were added by pip --freeze:
|
||||
cairocffi==0.6
|
||||
pyhull==1.5.6
|
||||
|
|
Ładowanie…
Reference in New Issue