summaryrefslogtreecommitdiff
path: root/gerbonara
diff options
context:
space:
mode:
Diffstat (limited to 'gerbonara')
-rw-r--r--gerbonara/cam.py5
-rwxr-xr-xgerbonara/excellon.py22
-rw-r--r--gerbonara/layers.py11
-rw-r--r--gerbonara/rs274x.py18
4 files changed, 45 insertions, 11 deletions
diff --git a/gerbonara/cam.py b/gerbonara/cam.py
index 9819087..14d4c61 100644
--- a/gerbonara/cam.py
+++ b/gerbonara/cam.py
@@ -444,5 +444,8 @@ class LazyCamFile:
def save(self, filename, *args, **kwargs):
""" Copy this Gerber file to the new path. """
- shutil.copy(self.original_path, filename)
+ if 'instance' in self.__dict__: # instance has been loaded, and might have been modified
+ self.instance.save(filename, *args, **kwargs)
+ else:
+ shutil.copy(self.original_path, filename)
diff --git a/gerbonara/excellon.py b/gerbonara/excellon.py
index fbbeb50..1ce9c64 100755
--- a/gerbonara/excellon.py
+++ b/gerbonara/excellon.py
@@ -245,6 +245,16 @@ class ExcellonFile(CamFile):
""" Test if there are multiple plating values used in this file. """
return len({obj.plated for obj in self.objects}) > 1
+ @property
+ def is_plated_tristate(self):
+ if self.is_plated:
+ return True
+
+ if self.is_nonplated:
+ return False
+
+ return None
+
def append(self, obj_or_comment):
""" Add a :py:class:`.GraphicObject` or a comment (str) to this file. """
if isinstance(obj_or_comment, str):
@@ -252,11 +262,11 @@ class ExcellonFile(CamFile):
else:
self.objects.append(obj_or_comment)
- def to_excellon(self):
+ def to_excellon(self, plated=None, errors='raise'):
""" Counterpart to :py:meth:`~.rs274x.GerberFile.to_excellon`. Does nothing and returns :py:obj:`self`. """
return self
- def to_gerber(self):
+ def to_gerber(self, errros='raise'):
""" Convert this excellon file into a :py:class:`~.rs274x.GerberFile`. """
apertures = {}
out = GerberFile()
@@ -274,14 +284,18 @@ class ExcellonFile(CamFile):
def generator(self):
return self.generator_hints[0] if self.generator_hints else None
- def merge(self, other):
+ def merge(self, other, mode='ignored', keep_settings=False):
if other is None:
return
+
+ if not isinstance(other, ExcellonFile):
+ other = other.to_excellon(plated=self.is_plated_tristate)
self.objects += other.objects
self.comments += other.comments
self.generator_hints = None
- self.import_settings = None
+ if not keep_settings:
+ self.import_settings = None
@classmethod
def open(kls, filename, plated=None, settings=None, external_tools=None):
diff --git a/gerbonara/layers.py b/gerbonara/layers.py
index abdd570..b2b3900 100644
--- a/gerbonara/layers.py
+++ b/gerbonara/layers.py
@@ -113,16 +113,22 @@ class NamingScheme:
def _match_files(filenames):
matches = {}
for generator, rules in MATCH_RULES.items():
+ already_matched = set()
gen = {}
matches[generator] = gen
for layer, regex in rules.items():
for fn in filenames:
+ if fn in already_matched:
+ continue
+
if (m := re.fullmatch(regex, fn.name, re.IGNORECASE)):
if layer == 'inner copper':
target = 'inner_' + ''.join(e or '' for e in m.groups()) + ' copper'
else:
target = layer
+
gen[target] = gen.get(target, []) + [fn]
+ already_matched.add(fn)
return matches
@@ -615,8 +621,10 @@ class LayerStack:
if self.drill_pth is not None:
yield get_name('plated drill', self.drill_pth), self.drill_pth
+
if self.drill_npth is not None:
yield get_name('nonplated drill', self.drill_npth), self.drill_npth
+
for layer in self._drill_layers:
yield get_name('unknown drill', layer), layer
@@ -872,8 +880,7 @@ class LayerStack:
npth_out.append(obj)
self.drill_pth, self.drill_npth = pth_out, npth_out
- self._drill_layers = unknown_out if unknown_out else None
- self._drill_layers = []
+ self._drill_layers = [unknown_out] if unknown_out else []
@property
def drill_layers(self):
diff --git a/gerbonara/rs274x.py b/gerbonara/rs274x.py
index 5956b53..5ef93dd 100644
--- a/gerbonara/rs274x.py
+++ b/gerbonara/rs274x.py
@@ -73,7 +73,7 @@ class GerberFile(CamFile):
self.apertures = [] # FIXME get rid of this? apertures are already in the objects.
self.file_attrs = file_attrs or {}
- def to_excellon(self, plated=None):
+ def to_excellon(self, plated=None, errors='raise'):
""" Convert this excellon file into a :py:class:`~.excellon.ExcellonFile`. This will convert interpolated lines
into slots, and circular aperture flashes into holes. Other features such as ``G36`` polygons or flashes with
non-circular apertures will result in a :py:obj:`ValueError`. You can, of course, programmatically remove such
@@ -83,7 +83,15 @@ class GerberFile(CamFile):
for obj in self.objects:
if not (isinstance(obj, go.Line) or isinstance(obj, go.Arc) or isinstance(obj, go.Flash)) or \
not isinstance(obj.aperture, apertures.CircleAperture):
- raise ValueError(f'Cannot convert {obj} to excellon!')
+ if errors == 'raise':
+ raise ValueError(f'Cannot convert {obj} to excellon.')
+ elif errors == 'warn':
+ warnings.warn(f'Gerber to Excellon conversion: Cannot convert {obj} to excellon.')
+ continue
+ elif errors == 'ignore':
+ continue
+ else:
+ raise ValueError('Invalid "errors" parameter. Allowed values: "raise", "warn" or "ignore".')
if not (new_tool := new_tools.get(id(obj.aperture))):
# TODO plating?
@@ -92,9 +100,9 @@ class GerberFile(CamFile):
return ExcellonFile(objects=new_objs, comments=self.comments)
- def to_gerber(self):
+ def to_gerber(self, errors='raise'):
""" Counterpart to :py:meth:`~.excellon.ExcellonFile.to_gerber`. Does nothing and returns :py:obj:`self`. """
- return
+ return self
def merge(self, other, mode='above', keep_settings=False):
""" Merge ``other`` into ``self``, i.e. add all objects that are in ``other`` to ``self``. This resets
@@ -110,6 +118,8 @@ class GerberFile(CamFile):
if other is None:
return
+ other = other.to_gerber()
+
if not keep_settings:
self.import_settings = None
self.comments += other.comments