summaryrefslogtreecommitdiff
path: root/gerbonara/gerber
diff options
context:
space:
mode:
Diffstat (limited to 'gerbonara/gerber')
-rw-r--r--gerbonara/gerber/aperture_macros/parse.py4
-rw-r--r--gerbonara/gerber/rs274x.py52
2 files changed, 54 insertions, 2 deletions
diff --git a/gerbonara/gerber/aperture_macros/parse.py b/gerbonara/gerber/aperture_macros/parse.py
index 47f45d1..2f578ee 100644
--- a/gerbonara/gerber/aperture_macros/parse.py
+++ b/gerbonara/gerber/aperture_macros/parse.py
@@ -85,6 +85,10 @@ class ApertureMacro:
else:
return f'gn_{hash(self)}'
+ @name.setter
+ def name(self, name):
+ self._name = name
+
def __str__(self):
return f'<Aperture macro, variables {str(self.variables)}, primitives {self.primitives}>'
diff --git a/gerbonara/gerber/rs274x.py b/gerbonara/gerber/rs274x.py
index f2473b9..1b62cc4 100644
--- a/gerbonara/gerber/rs274x.py
+++ b/gerbonara/gerber/rs274x.py
@@ -51,6 +51,49 @@ class GerberFile(CamFile):
self.comments = []
self.objects = []
+ def merge(self, other):
+ """ Merge other GerberFile into this one """
+ # FIXME unit handling
+
+ self.comments += other.comments
+
+ # dedup apertures
+ new_apertures = {}
+ replace_apertures = {}
+ for ap in self.apertures + other.apertures:
+ gbr = ap.to_gerber()
+ if gbr not in new_apertures:
+ new_apertures[gbr] = ap
+ else:
+ replace_apertures[id(ap)] = new_apertures[gbr]
+ self.apertures = new_apertures
+
+ self.objects += other.objects
+ for obj in self.objects:
+ if (ap := replace_apertures.get(id(getattr(obj, 'aperture')))):
+ obj.aperture = ap
+
+ # dedup aperture macros
+ macros = { m.to_gerber(): m
+ for m in [ GenericMacros.circle, GenericMacros.rect, GenericMacros.oblong, GenericMacros.polygon] }
+ for ap in new_apertures:
+ if isinstance(aperture, ApertureMacroInstance):
+ macro_grb = ap.macro.to_gerber() # use native units to compare macros
+ if macro_grb in macros:
+ ap.macro = macros[macro_grb]
+ else:
+ macros[macro_grb] = ap.macro
+
+ # make macro names unique
+ seen_macro_names = set()
+ for macro in macros.values():
+ i = 2
+ while (new_name := f'{macro.name}{i}') in seen_macro_names:
+ i += 1
+ macro.name = new_name
+ seen_macro_names.add(new_name)
+
+
@classmethod
def open(kls, filename, enable_includes=False, enable_include_dir=None):
with open(filename, "r") as f:
@@ -91,7 +134,9 @@ class GerberFile(CamFile):
for cmt in self.comments:
yield CommentStmt(cmt)
- # Emit gerbonara's generic, rotation-capable aperture macro replacements for the standard C/R/O/P shapes.
+ # Always emit gerbonara's generic, rotation-capable aperture macro replacements for the standard C/R/O/P shapes.
+ # Unconditionally emitting these here is easier than first trying to figure out if we need them later,
+ # and they are only a few bytes anyway.
yield ApertureMacroStmt(GenericMacros.circle)
yield ApertureMacroStmt(GenericMacros.rect)
yield ApertureMacroStmt(GenericMacros.oblong)
@@ -117,9 +162,12 @@ class GerberFile(CamFile):
yield EofStmt()
- def __str__(self):
+ def to_gerber(self):
return '\n'.join(self.generate_statements())
+ def __str__(self):
+ return f'<GerberFile with {len(self.apertures)} apertures, {len(self.objects)} objects>'
+
def save(self, filename):
with open(filename, 'w', encoding='utf-8') as f: # Encoding is specified as UTF-8 by spec.
for stmt in self.generate_statements():