aboutsummaryrefslogtreecommitdiff
path: root/gerberex/composition.py
diff options
context:
space:
mode:
Diffstat (limited to 'gerberex/composition.py')
-rw-r--r--gerberex/composition.py201
1 files changed, 201 insertions, 0 deletions
diff --git a/gerberex/composition.py b/gerberex/composition.py
new file mode 100644
index 0000000..2613a61
--- /dev/null
+++ b/gerberex/composition.py
@@ -0,0 +1,201 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+# Copyright 2019 Hiroshi Murayama <opiopan@gmail.com>
+import os
+from functools import reduce
+from gerber.cam import FileSettings
+from gerber.gerber_statements import EofStmt
+from gerber.excellon_statements import *
+import gerberex.rs274x
+import gerberex.excellon
+import gerberex.dxf
+
+class Composition(object):
+ def __init__(self, settings = None, comments = None):
+ self.settings = settings
+ self.comments = comments if comments != None else []
+
+class GerberComposition(Composition):
+ APERTURE_ID_BIAS = 10
+
+ def __init__(self, settings=None, comments=None):
+ super(GerberComposition, self).__init__(settings, comments)
+ self.param_statements = []
+ self.aperture_macros = {}
+ self.apertures = []
+ self.drawings = []
+
+ def merge(self, file):
+ if isinstance(file, gerberex.rs274x.GerberFile):
+ self._merge_gerber(file)
+ elif isinstance(file, gerberex.dxf.DxfFile):
+ self._merge_dxf(file)
+ else:
+ raise Exception('unsupported file type')
+
+ def dump(self, path):
+ def statements():
+ for s in self.param_statements:
+ yield s
+ for k in self.aperture_macros:
+ yield self.aperture_macros[k]
+ for s in self.apertures:
+ yield s
+ for s in self.drawings:
+ yield s
+ yield EofStmt()
+ with open(path, 'w') as f:
+ for statement in statements():
+ f.write(statement.to_gerber(self.settings) + '\n')
+
+ def _merge_gerber(self, file):
+ param_statements = []
+ aperture_macro_map = {}
+ aperture_map = {}
+
+ if self.settings:
+ if self.settings.units == 'metric':
+ file.to_metric()
+ else:
+ file.to_inch()
+
+ for statement in file.statements:
+ if statement.type == 'COMMENT':
+ self.comments.append(statement.comment)
+ elif statement.type == 'PARAM':
+ if statement.param == 'AM':
+ name = statement.name
+ newname = self._register_aperture_macro(statement)
+ aperture_macro_map[name] = newname
+ elif statement.param == 'AD':
+ if not statement.shape in ['C', 'R', 'O']:
+ statement.shape = aperture_macro_map[statement.shape]
+ dnum = statement.d
+ newdnum = self._register_aperture(statement)
+ aperture_map[dnum] = newdnum
+ elif statement.param == 'LP':
+ self.drawings.append(statement)
+ else:
+ param_statements.append(statement)
+ elif statement.type in ['EOF', "DEPRECATED"]:
+ pass
+ else:
+ if statement.type == 'APERTURE':
+ statement.d = aperture_map[statement.d]
+ self.drawings.append(statement)
+
+ if not self.settings:
+ self.settings = file.settings
+ self.param_statements = param_statements
+
+ def _merge_dxf(self, file):
+ if self.settings:
+ if self.settings.units == 'metric':
+ file.to_metric()
+ else:
+ file.to_inch()
+
+ file.dcode = self._register_aperture(file.aperture)
+ self.drawings.append(file.statements)
+
+ if not self.settings:
+ self.settings = file.settings
+ self.param_statements = file.header
+
+
+ def _register_aperture_macro(self, statement):
+ name = statement.name
+ newname = name
+ offset = 0
+ while newname in self.aperture_macros:
+ offset += 1
+ newname = '%s_%d' % (name, offset)
+ statement.name = newname
+ self.aperture_macros[newname] = statement
+ return newname
+
+ def _register_aperture(self, statement):
+ statement.d = len(self.apertures) + self.APERTURE_ID_BIAS
+ self.apertures.append(statement)
+ return statement.d
+
+class DrillComposition(Composition):
+ def __init__(self, settings=None, comments=None):
+ super(DrillComposition, self).__init__(settings, comments)
+ self.header1_statements = []
+ self.header2_statements = []
+ self.tools = []
+ self.hits = []
+
+ def merge(self, file):
+ if isinstance(file, gerberex.excellon.ExcellonFileEx):
+ self._merge_excellon(file)
+ else:
+ raise Exception('unsupported file type')
+
+ def dump(self, path):
+ def statements():
+ for s in self.header1_statements:
+ yield s.to_excellon(self.settings)
+ for t in self.tools:
+ yield t.to_excellon(self.settings)
+ for s in self.header2_statements:
+ yield s.to_excellon(self.settings)
+ for t in self.tools:
+ yield ToolSelectionStmt(t.number).to_excellon(self.settings)
+ for h in self.hits:
+ if h.tool.number == t.number:
+ yield CoordinateStmt(*h.position).to_excellon(self.settings)
+ yield EndOfProgramStmt().to_excellon()
+
+ with open(path, 'w') as f:
+ for statement in statements():
+ f.write(statement + '\n')
+
+ def _merge_excellon(self, file):
+ tool_map = {}
+
+ 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:
+ in_header1 = True
+ for statement in file.statements:
+ if not isinstance(statement, ToolSelectionStmt):
+ if isinstance(statement, ExcellonTool):
+ in_header1 = False
+ else:
+ if in_header1:
+ self.header1_statements.append(statement)
+ else:
+ self.header2_statements.append(statement)
+ else:
+ break
+
+ for tool in iter(file.tools.values()):
+ num = tool.number
+ tool_map[num] = self._register_tool(tool)
+
+ for hit in file.hits:
+ hit.tool = tool_map[hit.tool.number]
+ self.hits.append(hit)
+
+ def _register_tool(self, tool):
+ for existing in self.tools:
+ if existing.equivalent(tool):
+ return existing
+ new_tool = ExcellonTool.from_tool(tool)
+ new_tool.settings = self.settings
+ def toolnums():
+ for tool in self.tools:
+ yield tool.number
+ max_num = reduce(lambda x, y: x if x > y else y, toolnums(), 0)
+ new_tool.number = max_num + 1
+ self.tools.append(new_tool)
+ return new_tool