192 lines
6.4 KiB
Python
192 lines
6.4 KiB
Python
#!/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 *
|
|
from gerber.excellon import DrillSlot, DrillHit
|
|
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.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 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()
|
|
self.settings.notation = 'absolute'
|
|
self.settings.zeros = 'trailing'
|
|
with open(path, 'w') as f:
|
|
gerberex.rs274x.write_gerber_header(f, self.settings)
|
|
for statement in statements():
|
|
f.write(statement.to_gerber(self.settings) + '\n')
|
|
|
|
def _merge_gerber(self, file):
|
|
aperture_macro_map = {}
|
|
aperture_map = {}
|
|
|
|
if self.settings:
|
|
if self.settings.units == 'metric':
|
|
file.to_metric()
|
|
else:
|
|
file.to_inch()
|
|
|
|
for macro in file.aperture_macros:
|
|
statement = file.aperture_macros[macro]
|
|
name = statement.name
|
|
newname = self._register_aperture_macro(statement)
|
|
aperture_macro_map[name] = newname
|
|
|
|
for statement in file.aperture_defs:
|
|
if statement.param == 'AD':
|
|
if statement.shape in aperture_macro_map:
|
|
statement.shape = aperture_macro_map[statement.shape]
|
|
dnum = statement.d
|
|
newdnum = self._register_aperture(statement)
|
|
aperture_map[dnum] = newdnum
|
|
|
|
for statement in file.main_statements:
|
|
if statement.type == 'APERTURE':
|
|
statement.d = aperture_map[statement.d]
|
|
self.drawings.append(statement)
|
|
|
|
if not self.settings:
|
|
self.settings = file.context
|
|
|
|
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
|
|
|
|
|
|
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.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')
|
|
|
|
def dump(self, path):
|
|
def statements():
|
|
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 h.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()
|
|
|
|
self.settings.notation = 'absolute'
|
|
self.settings.zeros = 'trailing'
|
|
with open(path, 'w') as f:
|
|
gerberex.excellon.write_excellon_header(f, self.settings, self.tools)
|
|
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()
|
|
|
|
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 _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()
|
|
|
|
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):
|
|
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
|