diff options
Diffstat (limited to 'gerberex/composition.py')
-rw-r--r-- | gerberex/composition.py | 201 |
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 |