def test_smaller_inside_bigger_intersection(self): b1 = BoundingBox2d([(0, 0), (3, 3)]) b2 = BoundingBox2d([(1, 1), (2, 2)]) b = b1.intersection(b2) assert b.size.isclose((1, 1)) assert b.extmin.isclose((1, 1)) assert b.extmax.isclose((2, 2))
def test_contains_other_bounding_box(self): box_a = BoundingBox2d([(0, 0), (10, 10)]) box_b = BoundingBox2d([(1, 1), (9, 9)]) box_c = BoundingBox2d([(1, 1), (11, 11)]) assert box_a.contains(box_b) is True assert box_a.contains(box_a) is True # self contained! assert box_b.contains(box_a) is False assert box_a.contains(box_c) is False
def test_init(self): bbox = BoundingBox2d([(0, 0), (10, 10)]) assert bbox.extmin == (0, 0) assert bbox.extmax == (10, 10) bbox = BoundingBox2d([(7, -2), (-1, 8)]) assert bbox.extmin == (-1, -2) assert bbox.extmax == (7, 8)
def test_touching_2d_boxes(self): bbox1 = BoundingBox2d([(0, 0), (1, 1)]) bbox2 = BoundingBox2d([(1, 1), (2, 2)]) bbox3 = BoundingBox2d([(-1, -1), (0, 0)]) assert bbox1.has_intersection(bbox2) is False assert bbox1.has_overlap(bbox2) is True assert bbox2.has_intersection(bbox1) is False assert bbox2.has_overlap(bbox1) is True assert bbox1.has_intersection(bbox3) is False assert bbox1.has_overlap(bbox3) is True assert bbox3.has_intersection(bbox1) is False assert bbox3.has_overlap(bbox1) is True
def test_measure_mtext_word_size(cap_height, msp): # Matplotlib support disabled and using MonospaceFont() mtext = msp.add_mtext( "XXX\nYYYY", dxfattribs={ "char_height": cap_height, "line_spacing_factor": 1.0, }, ) word_size_detector = WordSizeDetector() mtext_size(mtext, tool=word_size_detector) boxes = word_size_detector.word_boxes() assert len(boxes) == 2 assert BoundingBox2d(boxes[0]).size.isclose((cap_height * 3, cap_height)) assert BoundingBox2d(boxes[1]).size.isclose((cap_height * 4, cap_height))
def test_3d_box_contains_2d_box(self): box_a = BoundingBox2d([(1, 1), (9, 9)]) # lives in the xy-plane, z-axis is 0 box_b = BoundingBox([(0, 0, 0), (10, 10, 10)]) assert box_b.contains(box_a) is True, "xy-plane is included" box_c = BoundingBox([(0, 0, 1), (10, 10, 10)]) assert box_c.contains(box_a) is False, "xy-plane is not included"
def window(layout: Layout, p1: Vertex, p2: Vertex): """ Resets the active viewport limits of `layout` to the lower left corner `p1` and the upper right corner `p2`. Replaces the current viewport configuration by a single window configuration. """ extents = BoundingBox2d([p1, p2]) center(layout, extents.center, extents.size)
def separate(exterior: BoundingBox2d, candidates: List[BoxStruct] ) -> Tuple[List[BoxStruct], List[BoxStruct]]: holes = [] outside = [] for candidate in candidates: # Fast inside check: (holes if exterior.inside(candidate.bbox.center) else outside).append(candidate) return holes, outside
def test_inside(self): bbox = BoundingBox2d([(0, 0), (10, 10)]) assert bbox.inside((0, 0)) is True assert bbox.inside((-1, 0)) is False assert bbox.inside((0, -1)) is False assert bbox.inside((5, 5)) is True assert bbox.inside((10, 10)) is True assert bbox.inside((11, 10)) is False assert bbox.inside((10, 11)) is False
def bbox(self) -> BoundingBox2d: """ Returns the 2D bounding box of the container. If the cell is not placed the top/left corner = (0, 0). """ try: x, y = self.final_location() except (ValueError, TypeError): x, y = 0, 0 return BoundingBox2d([(x, y), (x + self.total_width, y - self.total_height)])
def render_areas(extents, grid=(2, 2)) -> Iterator[BoundingBox2d]: """Returns a bounding box for each tile to render.""" rows, cols = grid tile_width = extents.size.x / cols tile_height = extents.size.y / rows for row in range(rows): for col in range(cols): x_min = extents.extmin.x + col * tile_width y_min = extents.extmin.y + row * tile_height # BoundingBox2d ignores the z-axis! yield BoundingBox2d([(x_min, y_min), (x_min + tile_width, y_min + tile_height)])
def test_extend(self): bbox = BoundingBox2d([(0, 0), (10, 10)]) bbox.extend([(5, 5)]) assert bbox.extmin == (0, 0) assert bbox.extmax == (10, 10) bbox.extend([(15, 16)]) assert bbox.extmin == (0, 0) assert bbox.extmax == (15, 16) bbox.extend([(-15, -16)]) assert bbox.extmin == (-15, -16) assert bbox.extmax == (15, 16)
def fast_bbox_detection(paths: Iterable[Path]) -> List[Polygon]: """ Create a nested polygon structure from iterable `paths`, using 2D bounding boxes as fast detection objects. """ # Implements fast bounding box construction and fast inside check. def area(item: BoxStruct) -> float: width, height = item.bbox.size return width * height def separate( exterior: BoundingBox2d, candidates: List[BoxStruct] ) -> Tuple[List[BoxStruct], List[BoxStruct]]: holes = [] outside = [] for candidate in candidates: # Fast inside check: (holes if exterior.inside(candidate.bbox.center) else outside).append(candidate) return holes, outside def polygon_structure(outside: List[BoxStruct]) -> List[List]: polygons = [] while outside: exterior = outside.pop() # path with largest area # Get holes inside of exterior and returns the remaining paths # outside of exterior: holes, outside = separate(exterior.bbox, outside) if holes: # build nested hole structure: # the largest hole could contain the smaller holes, # and so on ... holes = polygon_structure(holes) polygons.append([exterior, *holes]) return polygons def as_nested_paths(polygons) -> List: return [ polygon.path if isinstance(polygon, BoxStruct) else as_nested_paths(polygon) for polygon in polygons ] boxed_paths = [ # Fast bounding box construction: BoxStruct(BoundingBox2d(path.control_vertices()), path) for path in paths if len(path) ] boxed_paths.sort(key=area) return as_nested_paths(polygon_structure(boxed_paths))
def set_masking_area(self, vertices: Iterable["Vertex"]) -> None: """Set a new masking area, the area is placed in the layout xy-plane.""" self.update_dxf_attribs(self.DEFAULT_ATTRIBS) vertices = Vec2.list(vertices) bounds = BoundingBox2d(vertices) x_size, y_size = bounds.size dxf = self.dxf dxf.insert = Vec3(bounds.extmin) dxf.u_pixel = Vec3(x_size, 0, 0) dxf.v_pixel = Vec3(0, y_size, 0) def boundary_path(): extmin = bounds.extmin for vertex in vertices: v = vertex - extmin yield Vec2(v.x / x_size - 0.5, 0.5 - v.y / y_size) self.set_boundary_path(boundary_path())
def test_init_with_with_empty_list(self): assert BoundingBox2d([]).is_empty is True
def test_intersection_box_without_a_volume_is_empty(self): b1 = BoundingBox2d([(0, 0, 0), (0, 3, 0)]) # has no volume b2 = BoundingBox2d([(-1, 1, 0), (1, 1, 0)]) # has no volume b = b1.intersection(b2) assert b.is_empty is True # intersection has no volume
def test_has_overlap_accepts_2d_bounding_box(self): bbox1 = BoundingBox([(0, 0, 0), (10, 10, 10)]) bbox2 = BoundingBox2d([(1, 1), (9, 9)]) # z-axis are 0 assert bbox1.has_overlap(bbox2) is True
def test_init_none(self): bbox = BoundingBox2d() assert bbox.has_data is False bbox.extend([(0, 0), (10, 10)]) assert bbox.size == (10, 10) assert bbox.has_data is True
def test_touches_at_one_corner(self): b1 = BoundingBox2d([(0, 0), (1, 1)]) b2 = BoundingBox2d([(1, 1), (2, 2)]) b = b1.intersection(b2) assert b.is_empty is True
def test_center(self): bbox = BoundingBox2d([(-1, -1), (9, 9)]) assert bbox.center == (4, 4)
def test_size(self): bbox = BoundingBox2d([(-2, -2), (8, 8)]) assert bbox.size == (10, 10)
def test_has_intersection_accepts_2d_bounding_box(self): bbox1 = BoundingBox([(-1, -1, -1), (10, 10, 10)]) bbox2 = BoundingBox2d([(1, 1), (9, 9)]) # z-axis are 0 assert bbox1.has_intersection(bbox2) is True
def test_init_with_with_empty_list(self): with pytest.raises(ValueError): BoundingBox2d([])
def test_accept_3d_box(self): b1 = BoundingBox([(0, 0, -1), (10, 10, 10)]) b2 = BoundingBox2d([(1, 1), (9, 9)]) # z-axis is 0 b = b1.intersection(b2) assert b.is_empty is True # has no volume!
def _update_bbox(self) -> None: v1 = Vec2(self._position) self._bbox = BoundingBox2d([v1, v1 + Vec2(self.get_dimension())])
def test_multiple_intersections(self, v1, v2): b1 = BoundingBox2d(v1) b2 = BoundingBox2d(v2) b = b1.intersection(b2) assert b.size.isclose((1, 1))
def bbox(self, points: Sequence) -> AbstractBoundingBox: return BoundingBox2d(points)
def test_full_intersection(self): b1 = BoundingBox2d([(0, 0), (2, 2)]) b = b1.intersection(b1) assert b.size.isclose((2, 2)) assert b.extmin.isclose((0, 0)) assert b.extmax.isclose((2, 2))
def test_crossing_2d_boxes(self): # bboxes do overlap, but do not contain corner points of the other bbox bbox1 = BoundingBox2d([(0, 1), (3, 2)]) bbox2 = BoundingBox2d([(1, 0), (2, 3)]) assert bbox1.intersect(bbox2) is True
def test_intersection_box_without_an_area_is_empty(self): b1 = BoundingBox2d([(0, 0), (0, 3)]) # has no area b2 = BoundingBox2d([(-1, 1), (1, 1)]) # has no area b = b1.intersection(b2) assert b.is_empty is True # intersection has no area