Excellon update
This commit is contained in:
parent
ca2143380f
commit
41a7b90dff
4 changed files with 242 additions and 93 deletions
|
|
@ -100,12 +100,12 @@ class DrillHit(object):
|
|||
self.position = position
|
||||
|
||||
def to_inch(self):
|
||||
if self.tool.units == 'metric':
|
||||
if self.tool.settings.units == 'metric':
|
||||
self.tool.to_inch()
|
||||
self.position = tuple(map(inch, self.position))
|
||||
|
||||
def to_metric(self):
|
||||
if self.tool.units == 'inch':
|
||||
if self.tool.settings.units == 'inch':
|
||||
self.tool.to_metric()
|
||||
self.position = tuple(map(metric, self.position))
|
||||
|
||||
|
|
@ -120,7 +120,7 @@ class DrillHit(object):
|
|||
max_y = position[1] + radius
|
||||
return ((min_x, max_x), (min_y, max_y))
|
||||
|
||||
def offset(self, x_offset, y_offset):
|
||||
def offset(self, x_offset=0, y_offset=0):
|
||||
self.position = tuple(map(operator.add, self.position, (x_offset, y_offset)))
|
||||
|
||||
def __str__(self):
|
||||
|
|
@ -141,13 +141,13 @@ class DrillSlot(object):
|
|||
self.slot_type = slot_type
|
||||
|
||||
def to_inch(self):
|
||||
if self.tool.units == 'metric':
|
||||
if self.tool.settings.units == 'metric':
|
||||
self.tool.to_inch()
|
||||
self.start = tuple(map(inch, self.start))
|
||||
self.end = tuple(map(inch, self.end))
|
||||
|
||||
def to_metric(self):
|
||||
if self.tool.units == 'inch':
|
||||
if self.tool.settings.units == 'inch':
|
||||
self.tool.to_metric()
|
||||
self.start = tuple(map(metric, self.start))
|
||||
self.end = tuple(map(metric, self.end))
|
||||
|
|
@ -163,7 +163,7 @@ class DrillSlot(object):
|
|||
max_y = max(start[1], end[1]) + radius
|
||||
return ((min_x, max_x), (min_y, max_y))
|
||||
|
||||
def offset(self, x_offset, y_offset):
|
||||
def offset(self, x_offset=0, y_offset=0):
|
||||
self.start = tuple(map(operator.add, self.start, (x_offset, y_offset)))
|
||||
self.end = tuple(map(operator.add, self.end, (x_offset, y_offset)))
|
||||
|
||||
|
|
@ -183,6 +183,7 @@ class ExcellonFile(CamFile):
|
|||
|
||||
hits : list of tuples
|
||||
list of drill hits as (<Tool>, (x, y))
|
||||
|
||||
settings : dict
|
||||
Dictionary of gerber file settings
|
||||
|
||||
|
|
@ -211,16 +212,17 @@ class ExcellonFile(CamFile):
|
|||
primitives = []
|
||||
for hit in self.hits:
|
||||
if isinstance(hit, DrillHit):
|
||||
primitives.append(Drill(hit.position, hit.tool.diameter, hit, units=self.settings.units))
|
||||
primitives.append(Drill(hit.position, hit.tool.diameter,
|
||||
units=self.settings.units))
|
||||
elif isinstance(hit, DrillSlot):
|
||||
primitives.append(Slot(hit.start, hit.end, hit.tool.diameter, hit, units=self.settings.units))
|
||||
primitives.append(Slot(hit.start, hit.end, hit.tool.diameter,
|
||||
units=self.settings.units))
|
||||
else:
|
||||
raise ValueError('Unknown hit type')
|
||||
|
||||
return primitives
|
||||
|
||||
@property
|
||||
def bounds(self):
|
||||
def bounding_box(self):
|
||||
xmin = ymin = 100000000000
|
||||
xmax = ymax = -100000000000
|
||||
for hit in self.hits:
|
||||
|
|
@ -282,29 +284,31 @@ class ExcellonFile(CamFile):
|
|||
Convert units to inches
|
||||
"""
|
||||
if self.units != 'inch':
|
||||
self.units = 'inch'
|
||||
for statement in self.statements:
|
||||
statement.to_inch()
|
||||
for tool in iter(self.tools.values()):
|
||||
tool.to_inch()
|
||||
for primitive in self.primitives:
|
||||
primitive.to_inch()
|
||||
for hit in self.hits:
|
||||
hit.to_inch()
|
||||
#for primitive in self.primitives:
|
||||
# primitive.to_inch()
|
||||
#for hit in self.hits:
|
||||
# hit.to_inch()
|
||||
self.units = 'inch'
|
||||
|
||||
def to_metric(self):
|
||||
""" Convert units to metric
|
||||
"""
|
||||
if self.units != 'metric':
|
||||
self.units = 'metric'
|
||||
for statement in self.statements:
|
||||
statement.to_metric()
|
||||
for tool in iter(self.tools.values()):
|
||||
tool.to_metric()
|
||||
for primitive in self.primitives:
|
||||
primitive.to_metric()
|
||||
#for primitive in self.primitives:
|
||||
# print("Converting to metric: {}".format(primitive))
|
||||
# primitive.to_metric()
|
||||
# print(primitive)
|
||||
for hit in self.hits:
|
||||
hit.to_metric()
|
||||
self.units = 'metric'
|
||||
|
||||
def offset(self, x_offset=0, y_offset=0):
|
||||
for statement in self.statements:
|
||||
|
|
@ -663,7 +667,8 @@ class ExcellonParser(object):
|
|||
if 'G85' in line:
|
||||
stmt = SlotStmt.from_excellon(line, self._settings())
|
||||
|
||||
# I don't know if this is actually correct, but it makes sense that this is where the tool would end
|
||||
# I don't know if this is actually correct, but it makes sense
|
||||
# that this is where the tool would end
|
||||
x = stmt.x_end
|
||||
y = stmt.y_end
|
||||
|
||||
|
|
@ -835,7 +840,7 @@ def detect_excellon_format(data=None, filename=None):
|
|||
try:
|
||||
p = ExcellonParser(settings)
|
||||
ef = p.parse_raw(data)
|
||||
size = tuple([t[0] - t[1] for t in ef.bounds])
|
||||
size = tuple([t[0] - t[1] for t in ef.bounding_box])
|
||||
hole_area = 0.0
|
||||
for hit in p.hits:
|
||||
tool = hit.tool
|
||||
|
|
|
|||
|
|
@ -113,16 +113,16 @@ class ExcellonTool(ExcellonStatement):
|
|||
hit_count : integer
|
||||
Number of tool hits in excellon file.
|
||||
"""
|
||||
|
||||
|
||||
PLATED_UNKNOWN = None
|
||||
PLATED_YES = 'plated'
|
||||
PLATED_NO = 'nonplated'
|
||||
PLATED_OPTIONAL = 'optional'
|
||||
|
||||
|
||||
@classmethod
|
||||
def from_tool(cls, tool):
|
||||
args = {}
|
||||
|
||||
|
||||
args['depth_offset'] = tool.depth_offset
|
||||
args['diameter'] = tool.diameter
|
||||
args['feed_rate'] = tool.feed_rate
|
||||
|
|
@ -131,7 +131,7 @@ class ExcellonTool(ExcellonStatement):
|
|||
args['plated'] = tool.plated
|
||||
args['retract_rate'] = tool.retract_rate
|
||||
args['rpm'] = tool.rpm
|
||||
|
||||
|
||||
return cls(None, **args)
|
||||
|
||||
@classmethod
|
||||
|
|
@ -172,9 +172,9 @@ class ExcellonTool(ExcellonStatement):
|
|||
args['number'] = int(val)
|
||||
elif cmd == 'Z':
|
||||
args['depth_offset'] = parse_gerber_value(val, nformat, zero_suppression)
|
||||
|
||||
|
||||
if plated != ExcellonTool.PLATED_UNKNOWN:
|
||||
# Sometimees we can can parse the
|
||||
# Sometimees we can can parse the plating status
|
||||
args['plated'] = plated
|
||||
return cls(settings, **args)
|
||||
|
||||
|
|
@ -209,7 +209,7 @@ class ExcellonTool(ExcellonStatement):
|
|||
self.max_hit_count = kwargs.get('max_hit_count')
|
||||
self.depth_offset = kwargs.get('depth_offset')
|
||||
self.plated = kwargs.get('plated')
|
||||
|
||||
|
||||
self.hit_count = 0
|
||||
|
||||
def to_excellon(self, settings=None):
|
||||
|
|
@ -249,15 +249,15 @@ class ExcellonTool(ExcellonStatement):
|
|||
|
||||
def _hit(self):
|
||||
self.hit_count += 1
|
||||
|
||||
|
||||
def equivalent(self, other):
|
||||
"""
|
||||
Is the other tool equal to this, ignoring the tool number, and other file specified properties
|
||||
"""
|
||||
|
||||
|
||||
if type(self) != type(other):
|
||||
return False
|
||||
|
||||
|
||||
return (self.diameter == other.diameter
|
||||
and self.feed_rate == other.feed_rate
|
||||
and self.retract_rate == other.retract_rate
|
||||
|
|
@ -314,12 +314,12 @@ class ToolSelectionStmt(ExcellonStatement):
|
|||
if self.compensation_index is not None:
|
||||
stmt += '%02d' % self.compensation_index
|
||||
return stmt
|
||||
|
||||
|
||||
class NextToolSelectionStmt(ExcellonStatement):
|
||||
|
||||
|
||||
# TODO the statement exists outside of the context of the file,
|
||||
# so it is imposible to know that it is really the next tool
|
||||
|
||||
|
||||
def __init__(self, cur_tool, next_tool, **kwargs):
|
||||
"""
|
||||
Select the next tool in the wheel.
|
||||
|
|
@ -329,10 +329,10 @@ class NextToolSelectionStmt(ExcellonStatement):
|
|||
next_tool : the that that is now selected
|
||||
"""
|
||||
super(NextToolSelectionStmt, self).__init__(**kwargs)
|
||||
|
||||
|
||||
self.cur_tool = cur_tool
|
||||
self.next_tool = next_tool
|
||||
|
||||
|
||||
def to_excellon(self, settings=None):
|
||||
stmt = 'M00'
|
||||
return stmt
|
||||
|
|
@ -651,11 +651,11 @@ class EndOfProgramStmt(ExcellonStatement):
|
|||
|
||||
|
||||
class UnitStmt(ExcellonStatement):
|
||||
|
||||
|
||||
@classmethod
|
||||
def from_settings(cls, settings):
|
||||
"""Create the unit statement from the FileSettings"""
|
||||
|
||||
|
||||
return cls(settings.units, settings.zeros)
|
||||
|
||||
@classmethod
|
||||
|
|
@ -742,7 +742,7 @@ class FormatStmt(ExcellonStatement):
|
|||
|
||||
def to_excellon(self, settings=None):
|
||||
return 'FMAT,%d' % self.format
|
||||
|
||||
|
||||
@property
|
||||
def format_tuple(self):
|
||||
return (self.format, 6 - self.format)
|
||||
|
|
@ -844,38 +844,38 @@ class UnknownStmt(ExcellonStatement):
|
|||
class SlotStmt(ExcellonStatement):
|
||||
"""
|
||||
G85 statement. Defines a slot created by multiple drills between two specified points.
|
||||
|
||||
|
||||
Format is two coordinates, split by G85in the middle, for example, XnY0nG85XnYn
|
||||
"""
|
||||
|
||||
|
||||
@classmethod
|
||||
def from_points(cls, start, end):
|
||||
|
||||
|
||||
return cls(start[0], start[1], end[0], end[1])
|
||||
|
||||
|
||||
@classmethod
|
||||
def from_excellon(cls, line, settings, **kwargs):
|
||||
# Split the line based on the G85 separator
|
||||
sub_coords = line.split('G85')
|
||||
(x_start_coord, y_start_coord) = SlotStmt.parse_sub_coords(sub_coords[0], settings)
|
||||
(x_end_coord, y_end_coord) = SlotStmt.parse_sub_coords(sub_coords[1], settings)
|
||||
|
||||
|
||||
# Some files seem to specify only one of the coordinates
|
||||
if x_end_coord == None:
|
||||
x_end_coord = x_start_coord
|
||||
if y_end_coord == None:
|
||||
y_end_coord = y_start_coord
|
||||
|
||||
|
||||
c = cls(x_start_coord, y_start_coord, x_end_coord, y_end_coord, **kwargs)
|
||||
c.units = settings.units
|
||||
return c
|
||||
|
||||
return c
|
||||
|
||||
@staticmethod
|
||||
def parse_sub_coords(line, settings):
|
||||
|
||||
|
||||
x_coord = None
|
||||
y_coord = None
|
||||
|
||||
|
||||
if line[0] == 'X':
|
||||
splitline = line.strip('X').split('Y')
|
||||
x_coord = parse_gerber_value(splitline[0], settings.format,
|
||||
|
|
@ -886,7 +886,7 @@ class SlotStmt(ExcellonStatement):
|
|||
else:
|
||||
y_coord = parse_gerber_value(line.strip(' Y'), settings.format,
|
||||
settings.zero_suppression)
|
||||
|
||||
|
||||
return (x_coord, y_coord)
|
||||
|
||||
|
||||
|
|
@ -907,16 +907,16 @@ class SlotStmt(ExcellonStatement):
|
|||
if self.y_start is not None:
|
||||
stmt += 'Y%s' % write_gerber_value(self.y_start, settings.format,
|
||||
settings.zero_suppression)
|
||||
|
||||
|
||||
stmt += 'G85'
|
||||
|
||||
|
||||
if self.x_end is not None:
|
||||
stmt += 'X%s' % write_gerber_value(self.x_end, settings.format,
|
||||
settings.zero_suppression)
|
||||
if self.y_end is not None:
|
||||
stmt += 'Y%s' % write_gerber_value(self.y_end, settings.format,
|
||||
settings.zero_suppression)
|
||||
|
||||
|
||||
return stmt
|
||||
|
||||
def to_inch(self):
|
||||
|
|
@ -959,7 +959,7 @@ class SlotStmt(ExcellonStatement):
|
|||
start_str += 'X: %g ' % self.x_start
|
||||
if self.y_start is not None:
|
||||
start_str += 'Y: %g ' % self.y_start
|
||||
|
||||
|
||||
end_str = ''
|
||||
if self.x_end is not None:
|
||||
end_str += 'X: %g ' % self.x_end
|
||||
|
|
|
|||
|
|
@ -28,9 +28,9 @@ try:
|
|||
from cStringIO import StringIO
|
||||
except(ImportError):
|
||||
from io import StringIO
|
||||
|
||||
|
||||
from .excellon_statements import ExcellonTool
|
||||
|
||||
|
||||
def loads(data, settings=None):
|
||||
""" Read tool file information and return a map of tools
|
||||
Parameters
|
||||
|
|
@ -52,13 +52,13 @@ class ExcellonToolDefinitionParser(object):
|
|||
----------
|
||||
None
|
||||
"""
|
||||
|
||||
|
||||
allegro_tool = re.compile(r'(?P<size>[0-9/.]+)\s+(?P<plated>P|N)\s+T(?P<toolid>[0-9]{2})\s+(?P<xtol>[0-9/.]+)\s+(?P<ytol>[0-9/.]+)')
|
||||
allegro_comment_mils = re.compile('Holesize (?P<toolid>[0-9]{1,2})\. = (?P<size>[0-9/.]+) Tolerance = \+(?P<xtol>[0-9/.]+)/-(?P<ytol>[0-9/.]+) (?P<plated>(PLATED)|(NON_PLATED)|(OPTIONAL)) MILS Quantity = [0-9]+')
|
||||
allegro2_comment_mils = re.compile('T(?P<toolid>[0-9]{1,2}) Holesize (?P<toolid2>[0-9]{1,2})\. = (?P<size>[0-9/.]+) Tolerance = \+(?P<xtol>[0-9/.]+)/-(?P<ytol>[0-9/.]+) (?P<plated>(PLATED)|(NON_PLATED)|(OPTIONAL)) MILS Quantity = [0-9]+')
|
||||
allegro_comment_mm = re.compile('Holesize (?P<toolid>[0-9]{1,2})\. = (?P<size>[0-9/.]+) Tolerance = \+(?P<xtol>[0-9/.]+)/-(?P<ytol>[0-9/.]+) (?P<plated>(PLATED)|(NON_PLATED)|(OPTIONAL)) MM Quantity = [0-9]+')
|
||||
allegro2_comment_mm = re.compile('T(?P<toolid>[0-9]{1,2}) Holesize (?P<toolid2>[0-9]{1,2})\. = (?P<size>[0-9/.]+) Tolerance = \+(?P<xtol>[0-9/.]+)/-(?P<ytol>[0-9/.]+) (?P<plated>(PLATED)|(NON_PLATED)|(OPTIONAL)) MM Quantity = [0-9]+')
|
||||
|
||||
|
||||
matchers = [
|
||||
(allegro_tool, 'mils'),
|
||||
(allegro_comment_mils, 'mils'),
|
||||
|
|
@ -66,34 +66,34 @@ class ExcellonToolDefinitionParser(object):
|
|||
(allegro_comment_mm, 'mm'),
|
||||
(allegro2_comment_mm, 'mm'),
|
||||
]
|
||||
|
||||
|
||||
def __init__(self, settings=None):
|
||||
self.tools = {}
|
||||
self.settings = settings
|
||||
|
||||
|
||||
def parse_raw(self, data):
|
||||
for line in StringIO(data):
|
||||
self._parse(line.strip())
|
||||
|
||||
|
||||
return self.tools
|
||||
|
||||
|
||||
def _parse(self, line):
|
||||
|
||||
|
||||
for matcher in ExcellonToolDefinitionParser.matchers:
|
||||
m = matcher[0].match(line)
|
||||
if m:
|
||||
unit = matcher[1]
|
||||
|
||||
|
||||
size = float(m.group('size'))
|
||||
platedstr = m.group('plated')
|
||||
toolid = int(m.group('toolid'))
|
||||
xtol = float(m.group('xtol'))
|
||||
ytol = float(m.group('ytol'))
|
||||
|
||||
|
||||
size = self._convert_length(size, unit)
|
||||
xtol = self._convert_length(xtol, unit)
|
||||
ytol = self._convert_length(ytol, unit)
|
||||
|
||||
|
||||
if platedstr == 'PLATED':
|
||||
plated = ExcellonTool.PLATED_YES
|
||||
elif platedstr == 'NON_PLATED':
|
||||
|
|
@ -102,19 +102,20 @@ class ExcellonToolDefinitionParser(object):
|
|||
plated = ExcellonTool.PLATED_OPTIONAL
|
||||
else:
|
||||
plated = ExcellonTool.PLATED_UNKNOWN
|
||||
|
||||
tool = ExcellonTool(None, number=toolid, diameter=size, plated=plated)
|
||||
|
||||
|
||||
tool = ExcellonTool(None, number=toolid, diameter=size,
|
||||
plated=plated)
|
||||
|
||||
self.tools[tool.number] = tool
|
||||
|
||||
|
||||
break
|
||||
|
||||
|
||||
def _convert_length(self, value, unit):
|
||||
|
||||
|
||||
# Convert the value to mm
|
||||
if unit == 'mils':
|
||||
value /= 39.3700787402
|
||||
|
||||
|
||||
# Now convert to the settings unit
|
||||
if self.settings.units == 'inch':
|
||||
return value / 25.4
|
||||
|
|
@ -137,34 +138,35 @@ def loads_rep(data, settings=None):
|
|||
return ExcellonReportParser(settings).parse_raw(data)
|
||||
|
||||
class ExcellonReportParser(object):
|
||||
|
||||
|
||||
# We sometimes get files with different encoding, so we can't actually
|
||||
# match the text - the best we can do it detect the table header
|
||||
header = re.compile(r'====\s+====\s+====\s+====\s+=====\s+===')
|
||||
|
||||
|
||||
def __init__(self, settings=None):
|
||||
self.tools = {}
|
||||
self.settings = settings
|
||||
|
||||
|
||||
self.found_header = False
|
||||
|
||||
|
||||
def parse_raw(self, data):
|
||||
for line in StringIO(data):
|
||||
self._parse(line.strip())
|
||||
|
||||
|
||||
return self.tools
|
||||
|
||||
|
||||
def _parse(self, line):
|
||||
|
||||
|
||||
# skip empty lines and "comments"
|
||||
if not line.strip():
|
||||
return
|
||||
|
||||
|
||||
if not self.found_header:
|
||||
# Try to find the heaader, since we need that to be sure we understand the contents correctly.
|
||||
# Try to find the heaader, since we need that to be sure we
|
||||
# understand the contents correctly.
|
||||
if ExcellonReportParser.header.match(line):
|
||||
self.found_header = True
|
||||
|
||||
|
||||
elif line[0] != '=':
|
||||
# Already found the header, so we know to to map the contents
|
||||
parts = line.split()
|
||||
|
|
@ -180,7 +182,9 @@ class ExcellonReportParser(object):
|
|||
feedrate = int(parts[3])
|
||||
speed = int(parts[4])
|
||||
qty = int(parts[5])
|
||||
|
||||
tool = ExcellonTool(None, number=toolid, diameter=size, plated=plated, feed_rate=feedrate, rpm=speed)
|
||||
|
||||
self.tools[tool.number] = tool
|
||||
|
||||
tool = ExcellonTool(None, number=toolid, diameter=size,
|
||||
plated=plated, feed_rate=feedrate,
|
||||
rpm=speed)
|
||||
|
||||
self.tools[tool.number] = tool
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import os
|
|||
|
||||
from ..cam import FileSettings
|
||||
from ..excellon import read, detect_excellon_format, ExcellonFile, ExcellonParser
|
||||
from ..excellon import DrillHit, DrillSlot
|
||||
from ..excellon_statements import ExcellonTool
|
||||
from .tests import *
|
||||
|
||||
|
|
@ -50,29 +51,28 @@ def test_read_settings():
|
|||
assert_equal(ncdrill.settings['zeros'], 'trailing')
|
||||
|
||||
|
||||
def test_bounds():
|
||||
def test_bounding_box():
|
||||
ncdrill = read(NCDRILL_FILE)
|
||||
xbound, ybound = ncdrill.bounds
|
||||
xbound, ybound = ncdrill.bounding_box
|
||||
assert_array_almost_equal(xbound, (0.1300, 2.1430))
|
||||
assert_array_almost_equal(ybound, (0.3946, 1.7164))
|
||||
|
||||
|
||||
def test_report():
|
||||
ncdrill = read(NCDRILL_FILE)
|
||||
|
||||
rprt = ncdrill.report()
|
||||
|
||||
def test_conversion():
|
||||
import copy
|
||||
ncdrill = read(NCDRILL_FILE)
|
||||
assert_equal(ncdrill.settings.units, 'inch')
|
||||
ncdrill_inch = copy.deepcopy(ncdrill)
|
||||
|
||||
ncdrill.to_metric()
|
||||
assert_equal(ncdrill.settings.units, 'metric')
|
||||
inch_primitives = ncdrill_inch.primitives
|
||||
for tool in iter(ncdrill_inch.tools.values()):
|
||||
tool.to_metric()
|
||||
for primitive in inch_primitives:
|
||||
primitive.to_metric()
|
||||
|
||||
for statement in ncdrill_inch.statements:
|
||||
statement.to_metric()
|
||||
|
||||
|
|
@ -80,7 +80,8 @@ def test_conversion():
|
|||
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, ncdrill_inch.primitives):
|
||||
|
||||
assert_equal(m.position, i.position, '%s not equal to %s' % (m, i))
|
||||
assert_equal(m.diameter, i.diameter, '%s not equal to %s' % (m, i))
|
||||
|
||||
|
|
@ -197,3 +198,142 @@ def test_parse_unknown():
|
|||
p = ExcellonParser(FileSettings())
|
||||
p._parse_line('Not A Valid Statement')
|
||||
assert_equal(p.statements[0].stmt, 'Not A Valid Statement')
|
||||
|
||||
def test_drill_hit_units_conversion():
|
||||
""" Test unit conversion for drill hits
|
||||
"""
|
||||
# Inch hit
|
||||
settings = FileSettings(units='inch')
|
||||
tool = ExcellonTool(settings, diameter=1.0)
|
||||
hit = DrillHit(tool, (1.0, 1.0))
|
||||
|
||||
assert_equal(hit.tool.settings.units, 'inch')
|
||||
assert_equal(hit.tool.diameter, 1.0)
|
||||
assert_equal(hit.position, (1.0, 1.0))
|
||||
|
||||
# No Effect
|
||||
hit.to_inch()
|
||||
|
||||
assert_equal(hit.tool.settings.units, 'inch')
|
||||
assert_equal(hit.tool.diameter, 1.0)
|
||||
assert_equal(hit.position, (1.0, 1.0))
|
||||
|
||||
# Should convert
|
||||
hit.to_metric()
|
||||
|
||||
assert_equal(hit.tool.settings.units, 'metric')
|
||||
assert_equal(hit.tool.diameter, 25.4)
|
||||
assert_equal(hit.position, (25.4, 25.4))
|
||||
|
||||
# No Effect
|
||||
hit.to_metric()
|
||||
|
||||
assert_equal(hit.tool.settings.units, 'metric')
|
||||
assert_equal(hit.tool.diameter, 25.4)
|
||||
assert_equal(hit.position, (25.4, 25.4))
|
||||
|
||||
# Convert back to inch
|
||||
hit.to_inch()
|
||||
|
||||
assert_equal(hit.tool.settings.units, 'inch')
|
||||
assert_equal(hit.tool.diameter, 1.0)
|
||||
assert_equal(hit.position, (1.0, 1.0))
|
||||
|
||||
def test_drill_hit_offset():
|
||||
TEST_VECTORS = [
|
||||
((0.0 ,0.0), (0.0, 1.0), (0.0, 1.0)),
|
||||
((0.0, 0.0), (1.0, 1.0), (1.0, 1.0)),
|
||||
((1.0, 1.0), (0.0, -1.0), (1.0, 0.0)),
|
||||
((1.0, 1.0), (-1.0, -1.0), (0.0, 0.0)),
|
||||
|
||||
]
|
||||
for position, offset, expected in TEST_VECTORS:
|
||||
settings = FileSettings(units='inch')
|
||||
tool = ExcellonTool(settings, diameter=1.0)
|
||||
hit = DrillHit(tool, position)
|
||||
|
||||
assert_equal(hit.position, position)
|
||||
|
||||
hit.offset(offset[0], offset[1])
|
||||
|
||||
assert_equal(hit.position, expected)
|
||||
|
||||
|
||||
def test_drill_slot_units_conversion():
|
||||
""" Test unit conversion for drill hits
|
||||
"""
|
||||
# Inch hit
|
||||
settings = FileSettings(units='inch')
|
||||
tool = ExcellonTool(settings, diameter=1.0)
|
||||
hit = DrillSlot(tool, (1.0, 1.0), (10.0, 10.0), DrillSlot.TYPE_ROUT)
|
||||
|
||||
assert_equal(hit.tool.settings.units, 'inch')
|
||||
assert_equal(hit.tool.diameter, 1.0)
|
||||
assert_equal(hit.start, (1.0, 1.0))
|
||||
assert_equal(hit.end, (10.0, 10.0))
|
||||
|
||||
# No Effect
|
||||
hit.to_inch()
|
||||
|
||||
assert_equal(hit.tool.settings.units, 'inch')
|
||||
assert_equal(hit.tool.diameter, 1.0)
|
||||
assert_equal(hit.start, (1.0, 1.0))
|
||||
assert_equal(hit.end, (10.0, 10.0))
|
||||
|
||||
# Should convert
|
||||
hit.to_metric()
|
||||
|
||||
assert_equal(hit.tool.settings.units, 'metric')
|
||||
assert_equal(hit.tool.diameter, 25.4)
|
||||
assert_equal(hit.start, (25.4, 25.4))
|
||||
assert_equal(hit.end, (254.0, 254.0))
|
||||
|
||||
# No Effect
|
||||
hit.to_metric()
|
||||
|
||||
assert_equal(hit.tool.settings.units, 'metric')
|
||||
assert_equal(hit.tool.diameter, 25.4)
|
||||
assert_equal(hit.start, (25.4, 25.4))
|
||||
assert_equal(hit.end, (254.0, 254.0))
|
||||
|
||||
# Convert back to inch
|
||||
hit.to_inch()
|
||||
|
||||
assert_equal(hit.tool.settings.units, 'inch')
|
||||
assert_equal(hit.tool.diameter, 1.0)
|
||||
assert_equal(hit.start, (1.0, 1.0))
|
||||
assert_equal(hit.end, (10.0, 10.0))
|
||||
|
||||
def test_drill_slot_offset():
|
||||
TEST_VECTORS = [
|
||||
((0.0 ,0.0), (1.0, 1.0), (0.0, 0.0), (0.0, 0.0), (1.0, 1.0)),
|
||||
((0.0, 0.0), (1.0, 1.0), (1.0, 0.0), (1.0, 0.0), (2.0, 1.0)),
|
||||
((0.0, 0.0), (1.0, 1.0), (1.0, 1.0), (1.0, 1.0), (2.0, 2.0)),
|
||||
((0.0, 0.0), (1.0, 1.0), (-1.0, 1.0), (-1.0, 1.0), (0.0, 2.0)),
|
||||
]
|
||||
for start, end, offset, expected_start, expected_end in TEST_VECTORS:
|
||||
settings = FileSettings(units='inch')
|
||||
tool = ExcellonTool(settings, diameter=1.0)
|
||||
slot = DrillSlot(tool, start, end, DrillSlot.TYPE_ROUT)
|
||||
|
||||
assert_equal(slot.start, start)
|
||||
assert_equal(slot.end, end)
|
||||
|
||||
slot.offset(offset[0], offset[1])
|
||||
|
||||
assert_equal(slot.start, expected_start)
|
||||
assert_equal(slot.end, expected_end)
|
||||
|
||||
def test_drill_slot_bounds():
|
||||
TEST_VECTORS = [
|
||||
((0.0, 0.0), (1.0, 1.0), 1.0, ((-0.5, 1.5), (-0.5, 1.5))),
|
||||
((0.0, 0.0), (1.0, 1.0), 0.5, ((-0.25, 1.25), (-0.25, 1.25))),
|
||||
]
|
||||
for start, end, diameter, expected, in TEST_VECTORS:
|
||||
settings = FileSettings(units='inch')
|
||||
tool = ExcellonTool(settings, diameter=diameter)
|
||||
slot = DrillSlot(tool, start, end, DrillSlot.TYPE_ROUT)
|
||||
|
||||
assert_equal(slot.bounding_box, expected)
|
||||
|
||||
#def test_exce
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue