summaryrefslogtreecommitdiff
path: root/gerbonara/gerber/ipc356.py
diff options
context:
space:
mode:
Diffstat (limited to 'gerbonara/gerber/ipc356.py')
-rw-r--r--gerbonara/gerber/ipc356.py64
1 files changed, 52 insertions, 12 deletions
diff --git a/gerbonara/gerber/ipc356.py b/gerbonara/gerber/ipc356.py
index 043f2ab..dc3773b 100644
--- a/gerbonara/gerber/ipc356.py
+++ b/gerbonara/gerber/ipc356.py
@@ -25,12 +25,12 @@ from dataclasses import dataclass, KW_ONLY
from pathlib import Path
from .cam import CamFile, FileSettings
-from .utils import MM, Inch, LengthUnit
+from .utils import MM, Inch, LengthUnit, rotate_point
class Netlist(CamFile):
def __init__(self, test_records=None, conductors=None, outlines=None, comments=None, adjacency=None,
- params=None, import_settings=None, original_path=None):
+ params=None, import_settings=None, original_path=None, generator_hints=None):
super().__init__(original_path=original_path, layer_name='netlist', import_settings=import_settings)
self.test_records = test_records or []
self.conductors = conductors or []
@@ -38,10 +38,14 @@ class Netlist(CamFile):
self.comments = comments or []
self.adjacency = adjacency or {}
self.params = params or {}
+ self.generator_hints = generator_hints or []
def merge(self, other, our_prefix=None, their_prefix=None):
''' Merge other netlist into this netlist. The respective net names are prefixed with the given prefixes
(default: None). Garbles other. '''
+ if other is None:
+ return
+
if not isinstance(other, Netlist):
raise TypeError(f'Can only merge Netlist with other Netlist, not {type(other)}')
@@ -80,12 +84,14 @@ class Netlist(CamFile):
self.adjacency = new_adjacency
def offset(self, dx=0, dy=0, unit=MM):
- # FIXME
- pass
+ for obj in self.objects:
+ obj.offset(dx, dy, unit)
def rotate(self, angle:'radian', center=(0,0), unit=MM):
- # FIXME
- pass
+ cx, cy = center
+
+ for obj in self.objects:
+ obj.rotate(angle, cx, cy, unit)
@property
def objects(self):
@@ -232,7 +238,7 @@ class NetlistParser(object):
self.adjacency = {}
self.outlines = []
self.eof = False
- self.generator = None
+ self.generator_hints = []
def warn(self, msg, kls=SyntaxWarning):
warnings.warn(f'{self.filename}:{self.start_line}: {msg}', kls)
@@ -264,7 +270,8 @@ class NetlistParser(object):
raise SyntaxError(f'Error parsing {self.filename}:{lineno}: {e}') from e
return Netlist(self.test_records, self.conductors, self.outlines, self.comments, self.adjacency,
- params=self.params, import_settings=self.settings, original_path=path)
+ params=self.params, import_settings=self.settings, original_path=path,
+ generator_hints=self.generator_hints)
def _parse_line(self, line):
if not line:
@@ -278,7 +285,7 @@ class NetlistParser(object):
# +-- sic!
# v
if 'Ouptut' in line and 'Allegro' in line:
- self.generator = 'allegro'
+ self.generator_hints.append('allegro')
elif 'Ouptut' not in line and 'Allegro' in line:
self.warn('This seems to be a file generated by a newer allegro version. Please raise an issue on our '
@@ -286,13 +293,13 @@ class NetlistParser(object):
'so we can improve Gerbonara!')
elif 'EAGLE' in line and 'CadSoft' in line:
- self.generator = 'eagle'
+ self.generator_hints.append('eagle')
if line.strip().startswith('NNAME'):
name, *value = line.strip().split()
value = ' '.join(value)
self.warn('File contains non-standard Allegro-style net name alias definitions in comments.')
- if self.generator == 'allegro':
+ if 'allegro' in self.generator_hints:
# it's amazing how allegro always seems to have found a way to do the same thing everyone else is
# doing just in a different, slightly more messed up, completely incompatible way.
self.net_names[name] = value[5:] # strip NNAME because Allegro
@@ -328,7 +335,7 @@ class NetlistParser(object):
raise SyntaxError(f'Unsupported IPC-356 netlist unit specification "{line}"')
elif name.startswith('NNAME'):
- if self.generator == 'allegro':
+ if 'allegro' in self.generator_hints:
self.net_names[name] = value[5:]
else:
@@ -406,6 +413,19 @@ class TestRecord:
y = self.unit.format(self.y)
return f'<IPC-356 test record @ {x},{y} {self.net_name} {self.pad_type.name} at {self.ref_des}, pin {self.pin_num}>'
+ def rotate(self, angle, cx=0, cy=0, unit=None):
+ cx = self.unit(cx, unit)
+ cy = self.unit(cy, unit)
+
+ self.angle += angle
+ self.x, self.y = rotate_point(self.x, self.y, angle, center=(cx, cy))
+
+ def offset(self, dx=0, dy=0, unit=None):
+ dx = self.unit(dx, unit)
+ dy = self.unit(dy, unit)
+ self.x += dx
+ self.y += dy
+
@classmethod
def parse(kls, line, settings, net_name_map={}):
obj = kls()
@@ -552,6 +572,16 @@ class Outline:
def __str__(self):
return f'<IPC-356 {self.outline_type.name} outline with {len(self.outline)} points>'
+ def rotate(self, angle, cx=0, cy=0, unit=None):
+ cx = self.unit(cx, unit)
+ cy = self.unit(cy, unit)
+ self.outline = [ rotate_point(x, y, angle, center=(cx, cy)) for x, y in self.outline ]
+
+ def offset(self, dx=0, dy=0, unit=None):
+ dx = self.unit(dx, unit)
+ dy = self.unit(dy, unit)
+ self.outline = [ (x+dx, y+dy) for x, y in self.outline ]
+
@dataclass
class Conductor:
@@ -588,3 +618,13 @@ class Conductor:
def __str__(self):
return f'<IPC-356 conductor {self.net_name} with {len(self.coords)} points>'
+ def rotate(self, angle, cx=0, cy=0, unit=None):
+ cx = self.unit(cx, unit)
+ cy = self.unit(cy, unit)
+ self.coords = [ rotate_point(x, y, angle, center=(cx, cy)) for x, y in self.coords ]
+
+ def offset(self, dx=0, dy=0, unit=None):
+ dx = self.unit(dx, unit)
+ dy = self.unit(dy, unit)
+ self.coords = [ (x+dx, y+dy) for x, y in self.coords ]
+