diff options
Diffstat (limited to 'gerbonara')
-rw-r--r-- | gerbonara/apertures.py | 5 | ||||
-rw-r--r-- | gerbonara/cad/kicad/footprints.py | 17 | ||||
-rw-r--r-- | gerbonara/cad/primitives.py | 20 | ||||
-rw-r--r-- | gerbonara/cad/protoboard.py | 2 | ||||
-rw-r--r-- | gerbonara/cad/protoserve_data/protoserve.html | 4 | ||||
-rw-r--r-- | gerbonara/graphic_objects.py | 13 | ||||
-rw-r--r-- | gerbonara/utils.py | 5 |
7 files changed, 54 insertions, 12 deletions
diff --git a/gerbonara/apertures.py b/gerbonara/apertures.py index feccb7f..73a6e9c 100644 --- a/gerbonara/apertures.py +++ b/gerbonara/apertures.py @@ -20,7 +20,7 @@ import math from dataclasses import dataclass, replace, field, fields, InitVar from .aperture_macros.parse import GenericMacros -from .utils import MM, Inch +from .utils import MM, Inch, sum_bounds from . import graphic_primitives as gp @@ -118,6 +118,9 @@ class Aperture: """ return self._primitives(x, y, unit, polarity_dark) + def bounding_box(self, unit=None): + return sum_bounds((prim.bounding_box() for prim in self.flash(0, 0, unit, True))) + def equivalent_width(self, unit=None): """ Get the width of a line interpolated using this aperture in the given :py:class:`~.LengthUnit`. diff --git a/gerbonara/cad/kicad/footprints.py b/gerbonara/cad/kicad/footprints.py index 86ba254..428d5ea 100644 --- a/gerbonara/cad/kicad/footprints.py +++ b/gerbonara/cad/kicad/footprints.py @@ -23,8 +23,9 @@ from ..primitives import Positioned from ... import graphic_primitives as gp from ... import graphic_objects as go from ... import apertures as ap +from ...layers import LayerStack from ...newstroke import Newstroke -from ...utils import MM, rotate_point +from ...utils import MM, rotate_point, offset_bounds, sum_bounds from ...aperture_macros.parse import GenericMacros, ApertureMacro from ...aperture_macros import primitive as amp @@ -591,6 +592,7 @@ class Footprint: models: List(Model) = field(default_factory=list) _ : SEXP_END = None original_filename: str = None + _bounding_box: tuple = None @property def version(self): @@ -701,6 +703,16 @@ class Footprint: layer_stack.drill_npth.append(fe) else: layer_stack.drill_pth.append(fe) + + def bounding_box(self, unit=MM): + if not self._bounding_box: + stack = LayerStack() + layer_map = {kc_id: gn_id for kc_id, gn_id in LAYER_MAP_K2G.items() if gn_id in stack} + self.render(stack, layer_map, x=0, y=0, rotation=0, side='top', text=False, variables={}) + self._bounding_box = stack.bounding_box(unit) + return self._bounding_box + + LAYER_MAP_K2G = { 'F.Cu': ('top', 'copper'), @@ -752,6 +764,9 @@ class FootprintInstance(Positioned): side=self.side, text=(not self.hide_text), variables=variables) + + def bounding_box(self, unit=MM): + return offset_bounds(self.sexp.bounding_box(unit), unit(self.x, self.unit), unit(self.y, self.unit)) if __name__ == '__main__': import sys diff --git a/gerbonara/cad/primitives.py b/gerbonara/cad/primitives.py index 80373c6..ce69bae 100644 --- a/gerbonara/cad/primitives.py +++ b/gerbonara/cad/primitives.py @@ -7,7 +7,7 @@ from itertools import zip_longest, chain from dataclasses import dataclass, field, KW_ONLY from collections import defaultdict -from ..utils import LengthUnit, MM, rotate_point, svg_arc, sum_bounds, bbox_intersect, Tag +from ..utils import LengthUnit, MM, rotate_point, svg_arc, sum_bounds, bbox_intersect, Tag, offset_bounds from ..layers import LayerStack from ..graphic_objects import Line, Arc, Flash from ..apertures import Aperture, CircleAperture, ObroundAperture, RectangleAperture, ExcellonTool @@ -215,6 +215,24 @@ class ObjectGroup(Positioned): fe.offset(x, y, self.unit) target.objects.append(fe) + def bounding_box(self, unit=MM): + if math.isclose(self.rotation, 0, abs_tol=1e-3): + return offset_bounds(sum_bounds((obj.bounding_box(unit=unit) for obj in chain( + self.top_copper, + self.top_mask, + self.top_silk, + self.top_paste, + self.bottom_copper, + self.bottom_mask, + self.bottom_silk, + self.bottom_paste, + self.drill_npth, + self.drill_pth, + self.objects, + ))), unit(self.x, self.unit), unit(self.y, self.unit)) + else: + return super().bounding_box(unit) + @property def single_sided(self): any_top = self.top_copper or self.top_mask or self.top_paste or self.top_silk diff --git a/gerbonara/cad/protoboard.py b/gerbonara/cad/protoboard.py index 2acc0f2..741d8a9 100644 --- a/gerbonara/cad/protoboard.py +++ b/gerbonara/cad/protoboard.py @@ -549,7 +549,7 @@ def _demo(): #pattern = PatternProtoArea(2.54*1.5, obj=THTFlowerProto()) #pattern = PatternProtoArea(2.54, obj=THTPad.circle(0, 0, 0.9, 1.8, paste=False)) #pattern = PatternProtoArea(2.54, obj=PoweredProto()) - pb = ProtoBoard(30, 30, pattern1, mounting_hole_dia=3.2, mounting_hole_offset=5) + pb = ProtoBoard(50, 50, pattern1, mounting_hole_dia=3.2, mounting_hole_offset=5) print(pb.pretty_svg()) pb.layer_stack().save_to_directory('/tmp/testdir') diff --git a/gerbonara/cad/protoserve_data/protoserve.html b/gerbonara/cad/protoserve_data/protoserve.html index 98a2192..215f3b7 100644 --- a/gerbonara/cad/protoserve_data/protoserve.html +++ b/gerbonara/cad/protoserve_data/protoserve.html @@ -598,7 +598,9 @@ input { <template id="tpl-g-spiky"> <div data-type="spiky" class="group spiky"> <h4>Spiky hybrid area</h4> - Layout by <a href="https://social.treehouse.systems/@electronic_eel">electroniceel</a> (<a href="https://github.com/electroniceel/protoboard">github</a>) + <div> + Layout by <a href="https://social.treehouse.systems/@electronic_eel">electroniceel</a> (<a href="https://github.com/electroniceel/protoboard">github</a>) + </div> <span class="content area-controls">(<a href="#" class="area-remove">Remove</a><a href="#" class="area-move">Move</a>)</span> <label class="proportion">Proportion <input type="text" name="layout_prop" value="1"> diff --git a/gerbonara/graphic_objects.py b/gerbonara/graphic_objects.py index 432a988..bc205db 100644 --- a/gerbonara/graphic_objects.py +++ b/gerbonara/graphic_objects.py @@ -21,7 +21,7 @@ import copy from dataclasses import dataclass, astuple, field, fields from itertools import zip_longest -from .utils import MM, InterpMode, to_unit, rotate_point +from .utils import MM, InterpMode, to_unit, rotate_point, sum_bounds from . import graphic_primitives as gp from .aperture_macros import primitive as amp @@ -152,12 +152,7 @@ class GraphicObject: :returns: tuple of tuples of floats: ``(min_x, min_y), (max_x, max_y)`` """ - bboxes = [ p.bounding_box() for p in self.to_primitives(unit) ] - min_x = min(min_x for (min_x, _min_y), _ in bboxes) - min_y = min(min_y for (_min_x, min_y), _ in bboxes) - max_x = max(max_x for _, (max_x, _max_y) in bboxes) - max_y = max(max_y for _, (_max_x, max_y) in bboxes) - return ((min_x, min_y), (max_x, max_y)) + return sum_bounds(p.bounding_box() for p in self.to_primitives(unit)) def to_primitives(self, unit=None): """ Render this object into low-level graphical primitives (subclasses of :py:class:`.GraphicPrimitive`). This @@ -219,6 +214,10 @@ class Flash(GraphicObject): def tool(self, value): self.aperture = value + def bounding_box(self, unit=None): + (min_x, min_y), (max_x, max_y) = self.aperture.bounding_box(unit) + return (min_x+self.x, min_y+self.y), (max_x+self.x, max_x+self.y) + @property def plated(self): """ (Excellon only) Returns if this is a plated hole. ``True`` (plated), ``False`` (non-plated) or ``None`` diff --git a/gerbonara/utils.py b/gerbonara/utils.py index 933b3ca..c1868af 100644 --- a/gerbonara/utils.py +++ b/gerbonara/utils.py @@ -264,6 +264,11 @@ def add_bounds(b1, b2): return sum_bounds((b1, b2)) +def offset_bounds(bounds, dx=0, dy=0): + (min_x, min_y), (max_x, max_y) = bounds + return (min_x+dx, min_y+dy), (max_x+dx, max_y+dy) + + def sum_bounds(bounds, *, default=None): """ Add/union multiple bounding boxes. |