Fix handling of zero-length line segments during pretty SVG export
Fixess gitlab issue #8
This commit is contained in:
parent
bcc4aeefa7
commit
5ccfd7a259
4 changed files with 49 additions and 2 deletions
|
|
@ -62,6 +62,12 @@ class GraphicPrimitive:
|
|||
|
||||
raise NotImplementedError()
|
||||
|
||||
def is_zero_size(self):
|
||||
""" Return whether this primitive is zero size
|
||||
|
||||
:rtype: bool
|
||||
"""
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class Circle(GraphicPrimitive):
|
||||
|
|
@ -84,6 +90,9 @@ class Circle(GraphicPrimitive):
|
|||
[(True, (self.x, self.y)), (True, (self.x, self.y))],
|
||||
polarity_dark=self.polarity_dark)
|
||||
|
||||
def is_zero_size(self):
|
||||
return math.isclose(self.r, 0)
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class ArcPoly(GraphicPrimitive):
|
||||
|
|
@ -180,6 +189,20 @@ class ArcPoly(GraphicPrimitive):
|
|||
return self
|
||||
|
||||
|
||||
def is_zero_size(self):
|
||||
for (x1, y1), (x2, y2), (clockwise, (cx, cy)) in self.segments:
|
||||
if clockwise is not None: # arc
|
||||
if math.isclose(cx, x1) and math.isclose(cy, y1):
|
||||
continue
|
||||
|
||||
if math.isclose(x1, x2) and math.isclose(y1, y2):
|
||||
return False
|
||||
|
||||
if math.isclose(polygon_area(self.outline), 0):
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class Line(GraphicPrimitive):
|
||||
""" Straight line with round end caps. """
|
||||
|
|
@ -244,6 +267,9 @@ class Line(GraphicPrimitive):
|
|||
None,
|
||||
], polarity_dark=self.polarity_dark)
|
||||
|
||||
def is_zero_size(self):
|
||||
return math.isclose(self.x1, self.x2) and math.isclose(self.y1, self.y2)
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class Arc(GraphicPrimitive):
|
||||
|
|
@ -310,6 +336,9 @@ class Arc(GraphicPrimitive):
|
|||
(not self.clockwise, (self.cx, self.cy)),
|
||||
], polarity_dark=self.polarity_dark)
|
||||
|
||||
def is_zero_size(self):
|
||||
return False # an arc with identical start and end points is defined as a circle
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class Rectangle(GraphicPrimitive):
|
||||
|
|
@ -345,3 +374,6 @@ class Rectangle(GraphicPrimitive):
|
|||
return tag('rect', x=prec(x), y=prec(y), width=prec(self.w), height=prec(self.h),
|
||||
**svg_rotation(self.rotation, self.x, self.y), fill=color)
|
||||
|
||||
def is_zero_size(self):
|
||||
return math.isclose(self.w, 0) or math.isclose(self.h, 0)
|
||||
|
||||
|
|
|
|||
|
|
@ -1012,7 +1012,7 @@ class LayerStack:
|
|||
if use == 'mask':
|
||||
objects.insert(0, tag('path', id='outline-path', d=self.outline_svg_d(unit=svg_unit), fill='white'))
|
||||
layers.append(tag('g', objects, id=f'l-{side}-{use}', filter=f'url(#f-{use})',
|
||||
fill=default_fill, stroke=default_stroke, **stroke_attrs,
|
||||
fill=default_fill, stroke=default_stroke, **stroke_attrs, fill_rule='evenodd',
|
||||
**inkscape_attrs(f'{side} {use}'), transform=layer_transform))
|
||||
|
||||
for i, layer in enumerate(self.drill_layers):
|
||||
|
|
@ -1284,6 +1284,7 @@ class LayerStack:
|
|||
maybe_allegro_hint = '' if self.generator != 'allegro' else ' This file looks like it was generated by Allegro/OrCAD. These tools produce quite mal-formed gerbers, and often export text on the outline layer. If you generated this file yourself, maybe try twiddling with the export settings.'
|
||||
polygons = []
|
||||
lines = [ obj.as_primitive(unit) for obj in self.outline.instance.objects if isinstance(obj, (go.Line, go.Arc)) ]
|
||||
lines = [ prim for prim in lines if not prim.is_zero_size() ]
|
||||
|
||||
by_x = sorted([ (obj.x1, obj) for obj in lines ] + [ (obj.x2, obj) for obj in lines ], key=lambda x: x[0])
|
||||
dist_sq = lambda x1, y1, x2, y2: (x2-x1)**2 + (y2-y1)**2
|
||||
|
|
|
|||
|
|
@ -605,6 +605,18 @@ def point_in_polygon(point, poly):
|
|||
return res
|
||||
|
||||
|
||||
def polygon_area(poly):
|
||||
# https://en.wikipedia.org/wiki/Shoelace_formula
|
||||
|
||||
if not poly or len(poly) < 3:
|
||||
return 0
|
||||
|
||||
acc = 0
|
||||
for (x1, y1), (x2, y2) in zip(poly, poly[-1:] + poly):
|
||||
acc += (y1 + y2) * (x1 - x2)
|
||||
return acc/2
|
||||
|
||||
|
||||
def bbox_intersect(a, b):
|
||||
if a is None or b is None:
|
||||
return False
|
||||
|
|
|
|||
|
|
@ -330,10 +330,12 @@ REFERENCE_DIRS = {
|
|||
'wc2-F_SilkS.gbr': 'top silk',
|
||||
'wc2.kicad_pcb': None,
|
||||
},
|
||||
'zero-length-lines': {
|
||||
}
|
||||
}
|
||||
|
||||
@filter_syntax_warnings
|
||||
@pytest.mark.parametrize('ref_dir,file_map', list(REFERENCE_DIRS.items()))
|
||||
@pytest.mark.parametrize('ref_dir,file_map', [(k, v) for k, v in REFERENCE_DIRS.items() if v])
|
||||
def test_layer_classifier(ref_dir, file_map):
|
||||
path = reference_path(ref_dir)
|
||||
print('Reference path is', path)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue