From fcd704e1eef9034e2000f55b2918d7df41379408 Mon Sep 17 00:00:00 2001 From: opiopan Date: Sat, 30 Mar 2019 11:16:13 +0900 Subject: add mouse bites generator function --- gerberex/composition.py | 22 ++++++ gerberex/dxf.py | 205 ++++++++++++++++++++++++++++++++++++++---------- 2 files changed, 184 insertions(+), 43 deletions(-) (limited to 'gerberex') diff --git a/gerberex/composition.py b/gerberex/composition.py index afcaf97..73f5702 100644 --- a/gerberex/composition.py +++ b/gerberex/composition.py @@ -127,10 +127,13 @@ class DrillComposition(Composition): self.header2_statements = [] self.tools = [] self.hits = [] + self.dxf_statements = [] def merge(self, file): if isinstance(file, gerberex.excellon.ExcellonFileEx): self._merge_excellon(file) + elif isinstance(file, gerberex.DxfFile): + self._merge_dxf(file) else: raise Exception('unsupported file type') @@ -147,6 +150,9 @@ class DrillComposition(Composition): for h in self.hits: if h.tool.number == t.number: yield CoordinateStmt(*h.position).to_excellon(self.settings) + for num, statement in self.dxf_statements: + if num == t.number: + yield statement.to_excellon(self.settings) yield EndOfProgramStmt().to_excellon() with open(path, 'w') as f: @@ -186,6 +192,22 @@ class DrillComposition(Composition): hit.tool = tool_map[hit.tool.number] self.hits.append(hit) + def _merge_dxf(self, file): + if not self.settings: + self.settings = file.settings + else: + if self.settings.units == 'metric': + file.to_metric() + else: + file.to_inch() + + if not self.header1_statements: + self.header1_statements = file.header + self.header2_statements = file.header2 + + tool = self._register_tool(ExcellonTool(self.settings, number=1, diameter=file.width)) + self.dxf_statements.append((tool.number, file.statements)) + def _register_tool(self, tool): for existing in self.tools: if existing.equivalent(tool): diff --git a/gerberex/dxf.py b/gerberex/dxf.py index 11072b5..a2c26d0 100644 --- a/gerberex/dxf.py +++ b/gerberex/dxf.py @@ -8,13 +8,18 @@ from math import pi, cos, sin, tan, atan, atan2, acos, asin, sqrt from gerber.cam import CamFile, FileSettings from gerber.utils import inch, metric, write_gerber_value from gerber.gerber_statements import ADParamStmt +from gerber.excellon_statements import ExcellonTool +from gerber.excellon_statements import CoordinateStmt import dxfgrabber class DxfStatement(object): def __init__(self, entity): self.entity = entity - def to_gerber(self, settings=None): + def to_gerber(self, settings=None, pitch=0, width=0): + pass + + def to_excellon(self, settings=None, pitch=0, width=0): pass def to_inch(self): @@ -27,39 +32,77 @@ class DxfLineStatement(DxfStatement): def __init__(self, entity): super(DxfLineStatement, self).__init__(entity) - def to_gerber(self, settings=FileSettings): - x0 = self.entity.start[0] - y0 = self.entity.start[1] - x1 = self.entity.end[0] - y1 = self.entity.end[1] - return 'G01*\nX{0}Y{1}D02*\nX{2}Y{3}D01*'.format( - write_gerber_value(x0, settings.format, - settings.zero_suppression), - write_gerber_value(y0, settings.format, - settings.zero_suppression), - write_gerber_value(x1, settings.format, - settings.zero_suppression), - write_gerber_value(y1, settings.format, - settings.zero_suppression) - ) + def to_gerber(self, settings=FileSettings(), pitch=0, width=0): + if pitch == 0: + x0 = self.entity.start[0] + y0 = self.entity.start[1] + x1 = self.entity.end[0] + y1 = self.entity.end[1] + return 'G01*\nX{0}Y{1}D02*\nX{2}Y{3}D01*'.format( + write_gerber_value(x0, settings.format, + settings.zero_suppression), + write_gerber_value(y0, settings.format, + settings.zero_suppression), + write_gerber_value(x1, settings.format, + settings.zero_suppression), + write_gerber_value(y1, settings.format, + settings.zero_suppression) + ) + else: + gstr = "" + for p in self._dots(pitch, width): + gstr += 'X{0}Y{1}D03*\n'.format( + write_gerber_value(p[0], settings.format, + settings.zero_suppression), + write_gerber_value(p[1], settings.format, + settings.zero_suppression)) + return gstr + + def to_excellon(self, settings=FileSettings(), pitch=0, width=0): + if not pitch: + return + gstr = "" + for p in self._dots(pitch, width): + gstr += CoordinateStmt(x=p[0], y=p[1]).to_excellon(settings) + '\n' + return gstr def to_inch(self): - self.entity.start[idx] = ( - inch(self.entity.start[idx][0]), inch(self.entity.start[idx][1])) - self.entity.end[idx] = ( - inch(self.entity.end[idx][0]), inch(self.entity.end[idx][1])) + self.entity.start = ( + inch(self.entity.start[0]), inch(self.entity.start[1])) + self.entity.end = ( + inch(self.entity.end[0]), inch(self.entity.end[1])) def to_metric(self): - self.entity.start[idx] = ( - metric(self.entity.start[idx][0]), inch(self.entity.start[idx][1])) - self.entity.end[idx] = ( - metric(self.entity.end[idx][0]), inch(self.entity.end[idx][1])) + self.entity.start = ( + metric(self.entity.start[0]), inch(self.entity.start[1])) + self.entity.end = ( + metric(self.entity.end[0]), inch(self.entity.end[1])) + + def _dots(self, pitch, width): + x0 = self.entity.start[0] + y0 = self.entity.start[1] + x1 = self.entity.end[0] + y1 = self.entity.end[1] + xp = x1 - x0 + yp = y1 - y0 + l = sqrt(xp * xp + yp * yp) + xd = xp * pitch / l + yd = yp * pitch / l + + d = 0; + while d < l + width / 2: + yield (x0, y0) + x0 += xd + y0 += yd + d += pitch class DxfCircleStatement(DxfStatement): def __init__(self, entity): super(DxfCircleStatement, self).__init__(entity) - def to_gerber(self, settings=FileSettings): + def to_gerber(self, settings=FileSettings(), pitch=0, width=0): + if pitch: + return r = self.entity.radius x0 = self.entity.center[0] y0 = self.entity.center[1] @@ -82,19 +125,21 @@ class DxfCircleStatement(DxfStatement): def to_inch(self): self.entity.radius = inch(self.entity.radius) - self.entity.center[idx] = ( - inch(self.entity.center[idx][0]), inch(self.entity.center[idx][1])) + self.entity.center = ( + inch(self.entity.center[0]), inch(self.entity.center[1])) def to_metric(self): self.entity.radius = metric(self.entity.radius) - self.entity.center[idx] = ( - metric(self.entity.center[idx][0]), metric(self.entity.center[idx][1])) + self.entity.center = ( + metric(self.entity.center[0]), metric(self.entity.center[1])) class DxfArcStatement(DxfStatement): def __init__(self, entity): super(DxfArcStatement, self).__init__(entity) - def to_gerber(self, settings=FileSettings): + def to_gerber(self, settings=FileSettings(), pitch=0, width=0): + if pitch: + return deg0 = self.entity.start_angle deg1 = self.entity.end_angle r = self.entity.radius @@ -126,21 +171,23 @@ class DxfArcStatement(DxfStatement): self.entity.start_angle = inch(self.entity.start_angle) self.entity.end_angle = inch(self.entity.end_angle) self.entity.radius = inch(self.entity.radius) - self.entity.center[idx] = ( - inch(self.entity.center[idx][0]), inch(self.entity.center[idx][1])) + self.entity.center = ( + inch(self.entity.center[0]), inch(self.entity.center[1])) def to_metric(self): self.entity.start_angle = metric(self.entity.start_angle) self.entity.end_angle = metric(self.entity.end_angle) self.entity.radius = metric(self.entity.radius) - self.entity.center[idx] = ( - metric(self.entity.center[idx][0]), metric(self.entity.center[idx][1])) + self.entity.center = ( + metric(self.entity.center[0]), metric(self.entity.center[1])) class DxfPolylineStatement(DxfStatement): def __init__(self, entity): super(DxfPolylineStatement, self).__init__(entity) - def to_gerber(self, settings=FileSettings()): + def to_gerber(self, settings=FileSettings(), pitch=0, width=0): + if pitch: + return x0 = self.entity.points[0][0] y0 = self.entity.points[0][1] b = self.entity.bulge[0] @@ -214,6 +261,8 @@ class DxfStatements(object): self._units = units self.dcode = dcode self.draw_mode = draw_mode + self.pitch = inch(1) if self._units == 'unit' else 1 + self.width = 0 self.statements = [] for entity in entities: if entity.dxftype == 'LWPOLYLINE': @@ -241,19 +290,33 @@ class DxfStatements(object): yield 'G37*' else: for statement in self.statements: - yield statement.to_gerber(settings) + yield statement.to_gerber( + settings, + pitch=self.pitch if self.draw_mode == DxfFile.DM_MOUSE_BITES else 0, + width=self.width) return '\n'.join(gerbers()) + def to_excellon(self, settings=FileSettings()): + if not self.draw_mode == DxfFile.DM_MOUSE_BITES: + return + def drills(): + for statement in self.statements: + if isinstance(statement, DxfLineStatement): + yield statement.to_excellon(settings, pitch=self.pitch, width=self.width) + return '\n'.join(drills()) + def to_inch(self): if self._units == 'metric': self._units = 'inch' + self.pitch = inch(self.pitch) for statement in self.statements: statement.to_inch() def to_metric(self): if self._units == 'inch': self._units = 'metric' + self.pitch = metric(self.pitch) for statement in self.statements: statement.to_metric() @@ -270,6 +333,32 @@ class DxfHeaderStatement(object): settings.format[0], settings.format[1], settings.format[0], settings.format[1] ) + + def to_excellon(self, settings): + return 'M48\n'\ + 'FMAT,2\n'\ + 'ICI,{0}\n'\ + '{1},{2},{3}.{4}\n'\ + '{5}'.format( + 'ON' if settings.notation == 'incremental' else 'OFF', + 'INCH' if settings.units == 'inch' else 'METRIC', + 'TZ' if settings.zero_suppression == 'leading' else 'LZ', + '0' * settings.format[0], '0' * settings.format[1], + 'M72' if settings.units == 'inch' else 'M71' + ) + + def to_inch(self): + pass + + def to_metric(self): + pass + +class DxfHeader2Statement(object): + def to_gerber(self, settings): + pass + + def to_excellon(self, settings): + return '%' def to_inch(self): pass @@ -280,8 +369,15 @@ class DxfHeaderStatement(object): class DxfFile(CamFile): DM_LINE = 0 DM_FILL = 1 + DM_MOUSE_BITES = 2 + + FT_RX274X = 0 + FT_EXCELLON = 1 + + def __init__(self, dxf, settings=None, draw_mode=None, filename=None): + if not settings: + settings = FileSettings(zero_suppression='leading') - def __init__(self, dxf, settings=FileSettings(), draw_mode=None, filename=None): if draw_mode == None: draw_mode = self.DM_LINE if dxf.header['$INSUNITS'] == 1: @@ -294,6 +390,8 @@ class DxfFile(CamFile): super(DxfFile, self).__init__(settings=settings, filename=filename) self._draw_mode = draw_mode self.header = DxfHeaderStatement() + + self.header2 = DxfHeader2Statement() self.aperture = ADParamStmt.circle(dcode=10, diameter=0.0) self.statements = DxfStatements(dxf.entities, self.units, dcode=self.aperture.d, draw_mode=self.draw_mode) @@ -313,6 +411,7 @@ class DxfFile(CamFile): @width.setter def width(self, value): self.aperture.modifiers = ([float(value),],) + self.statements.width = value @property def draw_mode(self): @@ -322,23 +421,42 @@ class DxfFile(CamFile): def draw_mode(self, value): self._draw_mode = value self.statements.draw_mode = value + + @property + def pitch(self): + return self.statements.pitch - def write(self, filename=None): + @pitch.setter + def pitch(self, value): + self.statements.pitch = value + + def write(self, filename=None, filetype=FT_RX274X): if self.settings.notation != 'absolute': raise Exception('DXF file\'s notation must be absolute ') - + filename = filename if filename is not None else self.filename with open(filename, 'w') as f: - f.write(self.header.to_gerber(self.settings) + '\n') - f.write(self.aperture.to_gerber(self.settings) + '\n') - f.write(self.statements.to_gerber(self.settings) + '\n') - f.write('M02*\n') + if filetype == self.FT_RX274X: + f.write(self.header.to_gerber(self.settings) + '\n') + f.write(self.aperture.to_gerber(self.settings) + '\n') + f.write(self.statements.to_gerber(self.settings) + '\n') + f.write('M02*\n') + else: + tool = ExcellonTool(self.settings, number=1, diameter=self.width) + f.write(self.header.to_excellon(self.settings) + '\n') + f.write(tool.to_excellon(self.settings) + '\n') + f.write(self.header2.to_excellon(self.settings) + '\n') + f.write('T01\n') + f.write(self.statements.to_excellon(self.settings) + '\n') + f.write('M30\n') + def to_inch(self): if self.units == 'metric': self.header.to_inch() self.aperture.to_inch() self.statements.to_inch() + self.pitch = inch(self.pitch) self.units = 'inch' def to_metric(self): @@ -346,6 +464,7 @@ class DxfFile(CamFile): self.header.to_metric() self.aperture.to_metric() self.statements.to_metric() + self.pitch = metric(self.pitch) self.units = 'metric' def offset(self, ofset_x, offset_y): -- cgit