simetri.graphics.bbox
Bounding box class. Shape and Batch objects have a bounding box. Bounding box is axis-aligned. Provides reference edges and points.
1"""Bounding box class. Shape and Batch objects have a bounding box. 2Bounding box is axis-aligned. Provides reference edges and points. 3""" 4import warnings 5 6import numpy as np 7from .common import Point, common_properties, defaults 8from .all_enums import Side, Types, Anchor 9from ..geometry.geometry import ( 10 distance, 11 mid_point, 12 offset_line, 13 line_angle, 14 intersect, 15 positive_angle, 16 polar_to_cartesian 17) 18 19 20class BoundingBox: 21 """Rectangular bounding box. 22 If the object is a Shape, it contains all points. 23 If the object is a Batch, it contains all points of all Shapes. 24 25 Provides reference edges and points as shown in the Book page ???. 26 """ 27 28 def __init__(self, southwest: Point, northeast: Point): 29 """ 30 Initialize a BoundingBox object. 31 32 Args: 33 southwest (Point): The southwest corner of the bounding box. 34 northeast (Point): The northeast corner of the bounding box. 35 """ 36 # define the four corners 37 self.__dict__["southwest"] = southwest 38 self.__dict__["northeast"] = northeast 39 self.__dict__["northwest"] = (southwest[0], northeast[1]) 40 self.__dict__["southeast"] = (northeast[0], southwest[1]) 41 self._aliases = { 42 "s": "south", 43 "n": "north", 44 "w": "west", 45 "e": "east", 46 "sw": "southwest", 47 "se": "southeast", 48 "nw": "northwest", 49 "ne": "northeast", 50 "d1": "diagonal1", 51 "d2": "diagonal2", 52 "m": "midpoint", 53 "vcl": "vert_centerline", 54 "hcl": "horiz_centerline", 55 "center": "midpoint", 56 } 57 58 common_properties(self) 59 self.type = Types.BOUNDING_BOX 60 self.subtype = Types.BOUNDING_BOX 61 62 def __getattr__(self, name): 63 """ 64 Get the attribute with the given name. 65 66 Args: 67 name (str): The name of the attribute. 68 69 Returns: 70 Any: The attribute with the given name. 71 """ 72 if name in self._aliases: 73 if name == "center": 74 warnings.warn('"center" is deprecated use "midpoint" instead.', DeprecationWarning) 75 res = getattr(self, self._aliases[name]) 76 else: 77 res = self.__dict__[name] 78 return res 79 80 def angle_point(self, angle: float) -> float: 81 """ 82 Return the intersection point of the angled line starting 83 from the midpoint and the bounding box. angle is in radians. 84 85 Args: 86 angle (float): The angle in radians. 87 88 Returns: 89 float: The intersection point. 90 """ 91 angle = positive_angle(angle) 92 line = ((0, 0), (np.cos(angle), np.sin(angle))) 93 94 angle1 = line_angle(self.midpoint, self.northeast) 95 angle2 = -angle1 # midpoint, southeast 96 angle3 = np.pi - angle1 # midpoint, northwest 97 angle4 = -angle3 # midpoint, southwest 98 if angle3 >= angle >= angle1: 99 res = intersect(line, self.top) 100 elif angle4 <= angle <= angle2: 101 res = intersect(line, self.bottom) 102 elif angle1 <= angle <= angle2: 103 res = intersect(line, self.right) 104 else: 105 res = intersect(line, self.left) 106 107 return res 108 109 @property 110 def left(self): 111 """ 112 Return the left edge. 113 114 Returns: 115 tuple: The left edge. 116 """ 117 return (self.northwest, self.southwest) 118 119 @property 120 def right(self): 121 """ 122 Return the right edge. 123 124 Returns: 125 tuple: The right edge. 126 """ 127 return (self.northeast, self.southeast) 128 129 @property 130 def top(self): 131 """ 132 Return the top edge. 133 134 Returns: 135 tuple: The top edge. 136 """ 137 return (self.northwest, self.northeast) 138 139 @property 140 def bottom(self): 141 """ 142 Return the bottom edge. 143 144 Returns: 145 tuple: The bottom edge. 146 """ 147 return (self.southwest, self.southeast) 148 149 @property 150 def vert_centerline(self): 151 """ 152 Return the vertical centerline. 153 154 Returns: 155 tuple: The vertical centerline. 156 """ 157 return (self.north, self.south) 158 159 @property 160 def horiz_centerline(self): 161 """ 162 Return the horizontal centerline. 163 164 Returns: 165 tuple: The horizontal centerline. 166 """ 167 return (self.west, self.east) 168 169 @property 170 def midpoint(self): 171 """ 172 Return the center of the bounding box. 173 174 Returns: 175 tuple: The center of the bounding box. 176 """ 177 x1, y1 = self.southwest 178 x2, y2 = self.northeast 179 180 xc = (x1 + x2) / 2 181 yc = (y1 + y2) / 2 182 183 return (xc, yc) 184 185 @property 186 def corners(self): 187 """ 188 Return the four corners of the bounding box. 189 190 Returns: 191 tuple: The four corners of the bounding box. 192 """ 193 return (self.northwest, self.southwest, self.southeast, self.northeast) 194 195 @property 196 def diamond(self): 197 """ 198 Return the four center points of the bounding box in a diamond shape. 199 200 Returns: 201 tuple: The four center points of the bounding box in a diamond shape. 202 """ 203 return (self.north, self.west, self.south, self.east) 204 205 @property 206 def all_anchors(self): 207 """ 208 Return all anchors of the bounding box. 209 210 Returns: 211 tuple: All anchors of the bounding box. 212 """ 213 return ( 214 self.northwest, 215 self.west, 216 self.southwest, 217 self.south, 218 self.northeast, 219 self.east, 220 self.northeast, 221 self.north, 222 self.midpoint, 223 ) 224 225 @property 226 def width(self): 227 """ 228 Return the width of the bounding box. 229 230 Returns: 231 float: The width of the bounding box. 232 """ 233 return distance(self.northwest, self.northeast) 234 235 @property 236 def height(self): 237 """ 238 Return the height of the bounding box. 239 240 Returns: 241 float: The height of the bounding box. 242 """ 243 return distance(self.northwest, self.southwest) 244 245 @property 246 def size(self): 247 """ 248 Return the size of the bounding box. 249 250 Returns: 251 tuple: The size of the bounding box. 252 """ 253 return (self.width, self.height) 254 255 @property 256 def west(self): 257 """ 258 Return the left edge midpoint. 259 260 Returns: 261 tuple: The left edge midpoint. 262 """ 263 return mid_point(*self.left) 264 265 @property 266 def south(self): 267 """ 268 Return the bottom edge midpoint. 269 270 Returns: 271 tuple: The bottom edge midpoint. 272 """ 273 return mid_point(*self.bottom) 274 275 @property 276 def east(self): 277 """ 278 Return the right edge midpoint. 279 280 Returns: 281 tuple: The right edge midpoint. 282 """ 283 return mid_point(*self.right) 284 285 @property 286 def north(self): 287 """ 288 Return the top edge midpoint. 289 290 Returns: 291 tuple: The top edge midpoint. 292 """ 293 return mid_point(*self.top) 294 295 @property 296 def northwest(self): 297 """ 298 Return the top left corner. 299 300 Returns: 301 tuple: The top left corner. 302 """ 303 return self.__dict__["northwest"] 304 305 @property 306 def northeast(self): 307 """ 308 Return the top right corner. 309 310 Returns: 311 tuple: The top right corner. 312 """ 313 return self.__dict__["northeast"] 314 315 @property 316 def southwest(self): 317 """ 318 Return the bottom left corner. 319 320 Returns: 321 tuple: The bottom left corner. 322 """ 323 return self.__dict__["southwest"] 324 325 @property 326 def southeast(self): 327 """ 328 Return the bottom right corner. 329 330 Returns: 331 tuple: The bottom right corner. 332 """ 333 return self.__dict__["southeast"] 334 335 @property 336 def diagonal1(self): 337 """ 338 Return the first diagonal. From the top left to the bottom right. 339 340 Returns: 341 tuple: The first diagonal. 342 """ 343 return (self.southwest, self.northeast) 344 345 @property 346 def diagonal2(self): 347 """ 348 Return the second diagonal. From the top right to the bottom left. 349 350 Returns: 351 tuple: The second diagonal. 352 """ 353 return (self.southeast, self.northwest) 354 355 def get_inflated_b_box( 356 self, left_margin=None, bottom_margin=None, right_margin=None, top_margin=None 357 ): 358 """ 359 Return a bounding box with offset edges. 360 361 Args: 362 left_margin (float, optional): The left margin. 363 bottom_margin (float, optional): The bottom margin. 364 right_margin (float, optional): The right margin. 365 top_margin (float, optional): The top margin. 366 367 Returns: 368 BoundingBox: The inflated bounding box. 369 """ 370 371 if bottom_margin is None: 372 bottom_margin = left_margin 373 if right_margin is None: 374 right_margin = left_margin 375 if top_margin is None: 376 top_margin = bottom_margin 377 378 x, y = self.southwest[:2] 379 southwest = (x - left_margin, y - bottom_margin) 380 381 x, y = self.northeast[:2] 382 northeast = (x + right_margin, y + top_margin) 383 384 return BoundingBox(southwest, northeast) 385 386 def offset_line(self, side, offset): 387 """ 388 Offset is applied outwards. Use negative values for inward offset. 389 390 Args: 391 side (Side): The side to offset. 392 offset (float): The offset distance. 393 394 Returns: 395 tuple: The offset line. 396 """ 397 if isinstance(side, str): 398 side = Side[side.upper()] 399 400 if side == Side.RIGHT: 401 x1, y1 = self.southeast 402 x2, y2 = self.northeast 403 res = ((x1 + offset, y1), (x2 + offset, y2)) 404 elif side == Side.LEFT: 405 x1, y1 = self.southwest 406 x2, y2 = self.northwest 407 res = ((x1 - offset, y1), (x2 - offset, y2)) 408 elif side == Side.TOP: 409 x1, y1 = self.northwest 410 x2, y2 = self.northeast 411 res = ((x1, y1 + offset), (x2, y2 + offset)) 412 elif side == Side.BOTTOM: 413 x1, y1 = self.southwest 414 x2, y2 = self.southeast 415 res = ((x1, y1 - offset), (x2, y2 - offset)) 416 elif side == Side.DIAGONAL1: 417 res = offset_line(self.diagonal1, offset) 418 elif side == Side.DIAGONAL2: 419 res = offset_line(self.diagonal2, offset) 420 elif side == Side.H_CENTERLINE: 421 res = offset_line(self.horiz_center_line, offset) 422 elif side == Side.V_CENTERLINE: 423 res = offset_line(self.vert_center_line, offset) 424 else: 425 raise ValueError(f"Unknown side: {side}") 426 res = None 427 428 return res 429 430 def offset_point(self, anchor, dx, dy): 431 """ 432 Return an offset point from the given corner. 433 434 Args: 435 anchor (Anchor): The anchor point. 436 dx (float): The x offset. 437 dy (float): The y offset. 438 439 Returns: 440 list: The offset point. 441 """ 442 if isinstance(anchor, str): 443 anchor = Anchor[anchor.upper()] 444 x, y = getattr(self, anchor.value)[:2] 445 elif isinstance(anchor, Anchor): 446 x, y = anchor.value[:2] 447 else: 448 raise ValueError(f"Unknown anchor: {anchor}") 449 return [x + dx, y + dy] 450 451 452 def centered(self, item:'Union[Shape, Batch]', dx:float = 0, dy:float = 0)->Point: 453 """ 454 Get the center of the reference item. 455 456 Args: 457 item (object): The reference item. Shape or Batch. 458 dx (float): The x offset. 459 dy (float): The y offset. 460 461 Returns: 462 Point: The item.midpoint of the reference item's bounding-box. 463 """ 464 465 x, y = item.midpoint 466 x += dx 467 y += dy 468 return x, y 469 470 def left_of(self, item:'Union[Shape, Batch]', dx:float = 0, dy:float = 0)->Point: 471 """ 472 Get the item.west of the reference item. 473 474 Args: 475 item (object): The reference item. Shape or Batch. 476 dx (float): The x offset. 477 dy (float): The y offset. 478 479 Returns: 480 Point: The item.west of the reference item's bounding-box. 481 """ 482 x, y = item.west 483 w2 = self.width / 2 484 x += (dx - w2) 485 y += dy 486 return x, y 487 488 def right_of(self, item:'Union[Shape, Batch]', dx:float = 0, dy:float = 0)->Point: 489 """ 490 Get the item.east of the reference item. 491 492 Args: 493 item (object): The reference item. Shape or Batch. 494 dx (float): The x offset. 495 dy (float): The y offset. 496 497 Returns: 498 Point: The item.east of the reference item's bounding-box. 499 """ 500 x, y = item.east 501 w2 = self.width / 2 502 x += (dx + w2) 503 y += dy 504 return x, y 505 506 def above(self, item:'Union[Shape, Batch]', dx:float = 0, dy:float = 0)->Point: 507 """ 508 Get the item.north of the reference item. 509 510 Args: 511 item (object): The reference item. Shape or Batch. 512 dx (float): The x offset. 513 dy (float): The y offset. 514 515 Returns: 516 Point: The item.north of the reference item's bounding-box. 517 """ 518 x, y = item.north 519 h2 = self.height / 2 520 x += dx 521 y += (dy + h2) 522 return x, y 523 524 def below(self, item:'Union[Shape, Batch]', dx:float = 0, dy:float = 0)->Point: 525 """ 526 Get the item.south of the reference item. 527 528 Args: 529 item (object): The reference item. Shape or Batch. 530 dx (float): The x offset. 531 dy (float): The y offset. 532 533 Returns: 534 Point: The item.south of the reference item's bounding-box. 535 """ 536 x, y = item.south 537 h2 = self.height / 2 538 x += dx 539 y += (dy - h2) 540 return x, y 541 542 def above_left(self, item:'Union[Shape, Batch]', dx:float = 0, dy:float = 0)->Point: 543 """ 544 Get the item.northwest of the reference item. 545 546 Args: 547 item (object): The reference item. Shape or Batch. 548 dx (float): The x offset. 549 dy (float): The y offset. 550 551 Returns: 552 Point: The item.northwest of the reference item's bounding-box. 553 """ 554 x, y = item.northwest 555 w2 = self.width / 2 556 h2 = self.height / 2 557 x += (dx - w2) 558 y += (dy + h2) 559 560 return x, y 561 562 563 def above_right(self, item:'Union[Shape, Batch]', dx:float = 0, dy:float = 0)->Point: 564 """ 565 Get the item.northeast of the reference item. 566 567 Args: 568 item (object): The reference item. Shape or Batch. 569 dx (float): The x offset. 570 dy (float): The y offset. 571 572 Returns: 573 Point: The item.northeast of the reference item's bounding-box. 574 """ 575 x, y = item.northeast 576 w2 = self.width / 2 577 h2 = self.height / 2 578 x += (dx + w2) 579 y += (dy + h2) 580 581 return x, y 582 583 584 def below_left(self, item:'Union[Shape, Batch]', dx:float = 0, dy:float = 0)->Point: 585 """ 586 Get the item.southwest of the reference item. 587 588 Args: 589 item (object): The reference item. Shape or Batch. 590 dx (float): The x offset. 591 dy (float): The y offset. 592 593 Returns: 594 Point: The item.southwest of the reference item's bounding-box. 595 """ 596 x, y = item.southwest 597 w2 = self.width / 2 598 h2 = self.height / 2 599 x += (dx - w2) 600 y += (dy - h2) 601 602 return x, y 603 604 605 def below_right(self, item:'Union[Shape, Batch]', dx:float = 0, dy:float = 0)->Point: 606 """ 607 Get the item.southeast of the reference item. 608 609 Args: 610 item (object): The reference item. Shape or Batch. 611 dx (float): The x offset. 612 dy (float): The y offset. 613 614 Returns: 615 Point: The item.southeast of the reference item's bounding-box. 616 """ 617 x, y = item.southeast 618 w2 = self.width / 2 619 h2 = self.height / 2 620 x += (dx + w2) 621 y += (dy - h2) 622 623 return x, y 624 625 626 def polar_pos(self, item:'Union[Shape, Batch]', theta:float, radius:float)->Point: 627 """ 628 Get the polar position of the reference item. 629 630 Args: 631 item (object): The reference item. Shape or Batch. 632 theta (float): The angle in radians. 633 radius (float): The radius. 634 635 Returns: 636 Point: The polar position of the reference item. 637 """ 638 639 x, y = item.midpoint 640 641 x1, y1 = polar_to_cartesian(radius, theta) 642 x += x1 643 y += y1 644 645 return x, y 646 647 648def bounding_box(points): 649 """ 650 Given a list of (x, y) points return the corresponding BoundingBox object. 651 652 Args: 653 points (list): The list of points. 654 655 Returns: 656 BoundingBox: The corresponding BoundingBox object. 657 658 Raises: 659 ValueError: If the list of points is empty. 660 """ 661 if isinstance(points, np.ndarray): 662 points = points[:, :2] 663 else: 664 points = np.array(points) # numpy array of points 665 n_points = len(points) 666 BB_EPSILON = defaults["BB_EPSILON"] 667 if n_points == 0: # empty list of points 668 raise ValueError("Empty list of points") 669 670 if len(points.shape) == 1: 671 # single point 672 min_x, min_y = points 673 max_x = min_x + BB_EPSILON 674 max_y = min_y + BB_EPSILON 675 else: 676 # find minimum and maximum coordinates 677 min_x, min_y = points.min(axis=0) 678 max_x, max_y = points.max(axis=0) 679 if min_x == max_x: # this could be a vertical line or degenerate points 680 max_x += BB_EPSILON 681 if min_y == max_y: # this could be a horizontal line or degenerate points 682 max_y += BB_EPSILON 683 # bounding box corners 684 bottom_left = (min_x, min_y) 685 top_right = (max_x, max_y) 686 return BoundingBox(southwest=bottom_left, northeast=top_right)
21class BoundingBox: 22 """Rectangular bounding box. 23 If the object is a Shape, it contains all points. 24 If the object is a Batch, it contains all points of all Shapes. 25 26 Provides reference edges and points as shown in the Book page ???. 27 """ 28 29 def __init__(self, southwest: Point, northeast: Point): 30 """ 31 Initialize a BoundingBox object. 32 33 Args: 34 southwest (Point): The southwest corner of the bounding box. 35 northeast (Point): The northeast corner of the bounding box. 36 """ 37 # define the four corners 38 self.__dict__["southwest"] = southwest 39 self.__dict__["northeast"] = northeast 40 self.__dict__["northwest"] = (southwest[0], northeast[1]) 41 self.__dict__["southeast"] = (northeast[0], southwest[1]) 42 self._aliases = { 43 "s": "south", 44 "n": "north", 45 "w": "west", 46 "e": "east", 47 "sw": "southwest", 48 "se": "southeast", 49 "nw": "northwest", 50 "ne": "northeast", 51 "d1": "diagonal1", 52 "d2": "diagonal2", 53 "m": "midpoint", 54 "vcl": "vert_centerline", 55 "hcl": "horiz_centerline", 56 "center": "midpoint", 57 } 58 59 common_properties(self) 60 self.type = Types.BOUNDING_BOX 61 self.subtype = Types.BOUNDING_BOX 62 63 def __getattr__(self, name): 64 """ 65 Get the attribute with the given name. 66 67 Args: 68 name (str): The name of the attribute. 69 70 Returns: 71 Any: The attribute with the given name. 72 """ 73 if name in self._aliases: 74 if name == "center": 75 warnings.warn('"center" is deprecated use "midpoint" instead.', DeprecationWarning) 76 res = getattr(self, self._aliases[name]) 77 else: 78 res = self.__dict__[name] 79 return res 80 81 def angle_point(self, angle: float) -> float: 82 """ 83 Return the intersection point of the angled line starting 84 from the midpoint and the bounding box. angle is in radians. 85 86 Args: 87 angle (float): The angle in radians. 88 89 Returns: 90 float: The intersection point. 91 """ 92 angle = positive_angle(angle) 93 line = ((0, 0), (np.cos(angle), np.sin(angle))) 94 95 angle1 = line_angle(self.midpoint, self.northeast) 96 angle2 = -angle1 # midpoint, southeast 97 angle3 = np.pi - angle1 # midpoint, northwest 98 angle4 = -angle3 # midpoint, southwest 99 if angle3 >= angle >= angle1: 100 res = intersect(line, self.top) 101 elif angle4 <= angle <= angle2: 102 res = intersect(line, self.bottom) 103 elif angle1 <= angle <= angle2: 104 res = intersect(line, self.right) 105 else: 106 res = intersect(line, self.left) 107 108 return res 109 110 @property 111 def left(self): 112 """ 113 Return the left edge. 114 115 Returns: 116 tuple: The left edge. 117 """ 118 return (self.northwest, self.southwest) 119 120 @property 121 def right(self): 122 """ 123 Return the right edge. 124 125 Returns: 126 tuple: The right edge. 127 """ 128 return (self.northeast, self.southeast) 129 130 @property 131 def top(self): 132 """ 133 Return the top edge. 134 135 Returns: 136 tuple: The top edge. 137 """ 138 return (self.northwest, self.northeast) 139 140 @property 141 def bottom(self): 142 """ 143 Return the bottom edge. 144 145 Returns: 146 tuple: The bottom edge. 147 """ 148 return (self.southwest, self.southeast) 149 150 @property 151 def vert_centerline(self): 152 """ 153 Return the vertical centerline. 154 155 Returns: 156 tuple: The vertical centerline. 157 """ 158 return (self.north, self.south) 159 160 @property 161 def horiz_centerline(self): 162 """ 163 Return the horizontal centerline. 164 165 Returns: 166 tuple: The horizontal centerline. 167 """ 168 return (self.west, self.east) 169 170 @property 171 def midpoint(self): 172 """ 173 Return the center of the bounding box. 174 175 Returns: 176 tuple: The center of the bounding box. 177 """ 178 x1, y1 = self.southwest 179 x2, y2 = self.northeast 180 181 xc = (x1 + x2) / 2 182 yc = (y1 + y2) / 2 183 184 return (xc, yc) 185 186 @property 187 def corners(self): 188 """ 189 Return the four corners of the bounding box. 190 191 Returns: 192 tuple: The four corners of the bounding box. 193 """ 194 return (self.northwest, self.southwest, self.southeast, self.northeast) 195 196 @property 197 def diamond(self): 198 """ 199 Return the four center points of the bounding box in a diamond shape. 200 201 Returns: 202 tuple: The four center points of the bounding box in a diamond shape. 203 """ 204 return (self.north, self.west, self.south, self.east) 205 206 @property 207 def all_anchors(self): 208 """ 209 Return all anchors of the bounding box. 210 211 Returns: 212 tuple: All anchors of the bounding box. 213 """ 214 return ( 215 self.northwest, 216 self.west, 217 self.southwest, 218 self.south, 219 self.northeast, 220 self.east, 221 self.northeast, 222 self.north, 223 self.midpoint, 224 ) 225 226 @property 227 def width(self): 228 """ 229 Return the width of the bounding box. 230 231 Returns: 232 float: The width of the bounding box. 233 """ 234 return distance(self.northwest, self.northeast) 235 236 @property 237 def height(self): 238 """ 239 Return the height of the bounding box. 240 241 Returns: 242 float: The height of the bounding box. 243 """ 244 return distance(self.northwest, self.southwest) 245 246 @property 247 def size(self): 248 """ 249 Return the size of the bounding box. 250 251 Returns: 252 tuple: The size of the bounding box. 253 """ 254 return (self.width, self.height) 255 256 @property 257 def west(self): 258 """ 259 Return the left edge midpoint. 260 261 Returns: 262 tuple: The left edge midpoint. 263 """ 264 return mid_point(*self.left) 265 266 @property 267 def south(self): 268 """ 269 Return the bottom edge midpoint. 270 271 Returns: 272 tuple: The bottom edge midpoint. 273 """ 274 return mid_point(*self.bottom) 275 276 @property 277 def east(self): 278 """ 279 Return the right edge midpoint. 280 281 Returns: 282 tuple: The right edge midpoint. 283 """ 284 return mid_point(*self.right) 285 286 @property 287 def north(self): 288 """ 289 Return the top edge midpoint. 290 291 Returns: 292 tuple: The top edge midpoint. 293 """ 294 return mid_point(*self.top) 295 296 @property 297 def northwest(self): 298 """ 299 Return the top left corner. 300 301 Returns: 302 tuple: The top left corner. 303 """ 304 return self.__dict__["northwest"] 305 306 @property 307 def northeast(self): 308 """ 309 Return the top right corner. 310 311 Returns: 312 tuple: The top right corner. 313 """ 314 return self.__dict__["northeast"] 315 316 @property 317 def southwest(self): 318 """ 319 Return the bottom left corner. 320 321 Returns: 322 tuple: The bottom left corner. 323 """ 324 return self.__dict__["southwest"] 325 326 @property 327 def southeast(self): 328 """ 329 Return the bottom right corner. 330 331 Returns: 332 tuple: The bottom right corner. 333 """ 334 return self.__dict__["southeast"] 335 336 @property 337 def diagonal1(self): 338 """ 339 Return the first diagonal. From the top left to the bottom right. 340 341 Returns: 342 tuple: The first diagonal. 343 """ 344 return (self.southwest, self.northeast) 345 346 @property 347 def diagonal2(self): 348 """ 349 Return the second diagonal. From the top right to the bottom left. 350 351 Returns: 352 tuple: The second diagonal. 353 """ 354 return (self.southeast, self.northwest) 355 356 def get_inflated_b_box( 357 self, left_margin=None, bottom_margin=None, right_margin=None, top_margin=None 358 ): 359 """ 360 Return a bounding box with offset edges. 361 362 Args: 363 left_margin (float, optional): The left margin. 364 bottom_margin (float, optional): The bottom margin. 365 right_margin (float, optional): The right margin. 366 top_margin (float, optional): The top margin. 367 368 Returns: 369 BoundingBox: The inflated bounding box. 370 """ 371 372 if bottom_margin is None: 373 bottom_margin = left_margin 374 if right_margin is None: 375 right_margin = left_margin 376 if top_margin is None: 377 top_margin = bottom_margin 378 379 x, y = self.southwest[:2] 380 southwest = (x - left_margin, y - bottom_margin) 381 382 x, y = self.northeast[:2] 383 northeast = (x + right_margin, y + top_margin) 384 385 return BoundingBox(southwest, northeast) 386 387 def offset_line(self, side, offset): 388 """ 389 Offset is applied outwards. Use negative values for inward offset. 390 391 Args: 392 side (Side): The side to offset. 393 offset (float): The offset distance. 394 395 Returns: 396 tuple: The offset line. 397 """ 398 if isinstance(side, str): 399 side = Side[side.upper()] 400 401 if side == Side.RIGHT: 402 x1, y1 = self.southeast 403 x2, y2 = self.northeast 404 res = ((x1 + offset, y1), (x2 + offset, y2)) 405 elif side == Side.LEFT: 406 x1, y1 = self.southwest 407 x2, y2 = self.northwest 408 res = ((x1 - offset, y1), (x2 - offset, y2)) 409 elif side == Side.TOP: 410 x1, y1 = self.northwest 411 x2, y2 = self.northeast 412 res = ((x1, y1 + offset), (x2, y2 + offset)) 413 elif side == Side.BOTTOM: 414 x1, y1 = self.southwest 415 x2, y2 = self.southeast 416 res = ((x1, y1 - offset), (x2, y2 - offset)) 417 elif side == Side.DIAGONAL1: 418 res = offset_line(self.diagonal1, offset) 419 elif side == Side.DIAGONAL2: 420 res = offset_line(self.diagonal2, offset) 421 elif side == Side.H_CENTERLINE: 422 res = offset_line(self.horiz_center_line, offset) 423 elif side == Side.V_CENTERLINE: 424 res = offset_line(self.vert_center_line, offset) 425 else: 426 raise ValueError(f"Unknown side: {side}") 427 res = None 428 429 return res 430 431 def offset_point(self, anchor, dx, dy): 432 """ 433 Return an offset point from the given corner. 434 435 Args: 436 anchor (Anchor): The anchor point. 437 dx (float): The x offset. 438 dy (float): The y offset. 439 440 Returns: 441 list: The offset point. 442 """ 443 if isinstance(anchor, str): 444 anchor = Anchor[anchor.upper()] 445 x, y = getattr(self, anchor.value)[:2] 446 elif isinstance(anchor, Anchor): 447 x, y = anchor.value[:2] 448 else: 449 raise ValueError(f"Unknown anchor: {anchor}") 450 return [x + dx, y + dy] 451 452 453 def centered(self, item:'Union[Shape, Batch]', dx:float = 0, dy:float = 0)->Point: 454 """ 455 Get the center of the reference item. 456 457 Args: 458 item (object): The reference item. Shape or Batch. 459 dx (float): The x offset. 460 dy (float): The y offset. 461 462 Returns: 463 Point: The item.midpoint of the reference item's bounding-box. 464 """ 465 466 x, y = item.midpoint 467 x += dx 468 y += dy 469 return x, y 470 471 def left_of(self, item:'Union[Shape, Batch]', dx:float = 0, dy:float = 0)->Point: 472 """ 473 Get the item.west of the reference item. 474 475 Args: 476 item (object): The reference item. Shape or Batch. 477 dx (float): The x offset. 478 dy (float): The y offset. 479 480 Returns: 481 Point: The item.west of the reference item's bounding-box. 482 """ 483 x, y = item.west 484 w2 = self.width / 2 485 x += (dx - w2) 486 y += dy 487 return x, y 488 489 def right_of(self, item:'Union[Shape, Batch]', dx:float = 0, dy:float = 0)->Point: 490 """ 491 Get the item.east of the reference item. 492 493 Args: 494 item (object): The reference item. Shape or Batch. 495 dx (float): The x offset. 496 dy (float): The y offset. 497 498 Returns: 499 Point: The item.east of the reference item's bounding-box. 500 """ 501 x, y = item.east 502 w2 = self.width / 2 503 x += (dx + w2) 504 y += dy 505 return x, y 506 507 def above(self, item:'Union[Shape, Batch]', dx:float = 0, dy:float = 0)->Point: 508 """ 509 Get the item.north of the reference item. 510 511 Args: 512 item (object): The reference item. Shape or Batch. 513 dx (float): The x offset. 514 dy (float): The y offset. 515 516 Returns: 517 Point: The item.north of the reference item's bounding-box. 518 """ 519 x, y = item.north 520 h2 = self.height / 2 521 x += dx 522 y += (dy + h2) 523 return x, y 524 525 def below(self, item:'Union[Shape, Batch]', dx:float = 0, dy:float = 0)->Point: 526 """ 527 Get the item.south of the reference item. 528 529 Args: 530 item (object): The reference item. Shape or Batch. 531 dx (float): The x offset. 532 dy (float): The y offset. 533 534 Returns: 535 Point: The item.south of the reference item's bounding-box. 536 """ 537 x, y = item.south 538 h2 = self.height / 2 539 x += dx 540 y += (dy - h2) 541 return x, y 542 543 def above_left(self, item:'Union[Shape, Batch]', dx:float = 0, dy:float = 0)->Point: 544 """ 545 Get the item.northwest of the reference item. 546 547 Args: 548 item (object): The reference item. Shape or Batch. 549 dx (float): The x offset. 550 dy (float): The y offset. 551 552 Returns: 553 Point: The item.northwest of the reference item's bounding-box. 554 """ 555 x, y = item.northwest 556 w2 = self.width / 2 557 h2 = self.height / 2 558 x += (dx - w2) 559 y += (dy + h2) 560 561 return x, y 562 563 564 def above_right(self, item:'Union[Shape, Batch]', dx:float = 0, dy:float = 0)->Point: 565 """ 566 Get the item.northeast of the reference item. 567 568 Args: 569 item (object): The reference item. Shape or Batch. 570 dx (float): The x offset. 571 dy (float): The y offset. 572 573 Returns: 574 Point: The item.northeast of the reference item's bounding-box. 575 """ 576 x, y = item.northeast 577 w2 = self.width / 2 578 h2 = self.height / 2 579 x += (dx + w2) 580 y += (dy + h2) 581 582 return x, y 583 584 585 def below_left(self, item:'Union[Shape, Batch]', dx:float = 0, dy:float = 0)->Point: 586 """ 587 Get the item.southwest of the reference item. 588 589 Args: 590 item (object): The reference item. Shape or Batch. 591 dx (float): The x offset. 592 dy (float): The y offset. 593 594 Returns: 595 Point: The item.southwest of the reference item's bounding-box. 596 """ 597 x, y = item.southwest 598 w2 = self.width / 2 599 h2 = self.height / 2 600 x += (dx - w2) 601 y += (dy - h2) 602 603 return x, y 604 605 606 def below_right(self, item:'Union[Shape, Batch]', dx:float = 0, dy:float = 0)->Point: 607 """ 608 Get the item.southeast of the reference item. 609 610 Args: 611 item (object): The reference item. Shape or Batch. 612 dx (float): The x offset. 613 dy (float): The y offset. 614 615 Returns: 616 Point: The item.southeast of the reference item's bounding-box. 617 """ 618 x, y = item.southeast 619 w2 = self.width / 2 620 h2 = self.height / 2 621 x += (dx + w2) 622 y += (dy - h2) 623 624 return x, y 625 626 627 def polar_pos(self, item:'Union[Shape, Batch]', theta:float, radius:float)->Point: 628 """ 629 Get the polar position of the reference item. 630 631 Args: 632 item (object): The reference item. Shape or Batch. 633 theta (float): The angle in radians. 634 radius (float): The radius. 635 636 Returns: 637 Point: The polar position of the reference item. 638 """ 639 640 x, y = item.midpoint 641 642 x1, y1 = polar_to_cartesian(radius, theta) 643 x += x1 644 y += y1 645 646 return x, y
Rectangular bounding box. If the object is a Shape, it contains all points. If the object is a Batch, it contains all points of all Shapes.
Provides reference edges and points as shown in the Book page ???.
29 def __init__(self, southwest: Point, northeast: Point): 30 """ 31 Initialize a BoundingBox object. 32 33 Args: 34 southwest (Point): The southwest corner of the bounding box. 35 northeast (Point): The northeast corner of the bounding box. 36 """ 37 # define the four corners 38 self.__dict__["southwest"] = southwest 39 self.__dict__["northeast"] = northeast 40 self.__dict__["northwest"] = (southwest[0], northeast[1]) 41 self.__dict__["southeast"] = (northeast[0], southwest[1]) 42 self._aliases = { 43 "s": "south", 44 "n": "north", 45 "w": "west", 46 "e": "east", 47 "sw": "southwest", 48 "se": "southeast", 49 "nw": "northwest", 50 "ne": "northeast", 51 "d1": "diagonal1", 52 "d2": "diagonal2", 53 "m": "midpoint", 54 "vcl": "vert_centerline", 55 "hcl": "horiz_centerline", 56 "center": "midpoint", 57 } 58 59 common_properties(self) 60 self.type = Types.BOUNDING_BOX 61 self.subtype = Types.BOUNDING_BOX
Initialize a BoundingBox object.
Arguments:
- southwest (Point): The southwest corner of the bounding box.
- northeast (Point): The northeast corner of the bounding box.
81 def angle_point(self, angle: float) -> float: 82 """ 83 Return the intersection point of the angled line starting 84 from the midpoint and the bounding box. angle is in radians. 85 86 Args: 87 angle (float): The angle in radians. 88 89 Returns: 90 float: The intersection point. 91 """ 92 angle = positive_angle(angle) 93 line = ((0, 0), (np.cos(angle), np.sin(angle))) 94 95 angle1 = line_angle(self.midpoint, self.northeast) 96 angle2 = -angle1 # midpoint, southeast 97 angle3 = np.pi - angle1 # midpoint, northwest 98 angle4 = -angle3 # midpoint, southwest 99 if angle3 >= angle >= angle1: 100 res = intersect(line, self.top) 101 elif angle4 <= angle <= angle2: 102 res = intersect(line, self.bottom) 103 elif angle1 <= angle <= angle2: 104 res = intersect(line, self.right) 105 else: 106 res = intersect(line, self.left) 107 108 return res
Return the intersection point of the angled line starting from the midpoint and the bounding box. angle is in radians.
Arguments:
- angle (float): The angle in radians.
Returns:
float: The intersection point.
110 @property 111 def left(self): 112 """ 113 Return the left edge. 114 115 Returns: 116 tuple: The left edge. 117 """ 118 return (self.northwest, self.southwest)
Return the left edge.
Returns:
tuple: The left edge.
120 @property 121 def right(self): 122 """ 123 Return the right edge. 124 125 Returns: 126 tuple: The right edge. 127 """ 128 return (self.northeast, self.southeast)
Return the right edge.
Returns:
tuple: The right edge.
130 @property 131 def top(self): 132 """ 133 Return the top edge. 134 135 Returns: 136 tuple: The top edge. 137 """ 138 return (self.northwest, self.northeast)
Return the top edge.
Returns:
tuple: The top edge.
140 @property 141 def bottom(self): 142 """ 143 Return the bottom edge. 144 145 Returns: 146 tuple: The bottom edge. 147 """ 148 return (self.southwest, self.southeast)
Return the bottom edge.
Returns:
tuple: The bottom edge.
150 @property 151 def vert_centerline(self): 152 """ 153 Return the vertical centerline. 154 155 Returns: 156 tuple: The vertical centerline. 157 """ 158 return (self.north, self.south)
Return the vertical centerline.
Returns:
tuple: The vertical centerline.
160 @property 161 def horiz_centerline(self): 162 """ 163 Return the horizontal centerline. 164 165 Returns: 166 tuple: The horizontal centerline. 167 """ 168 return (self.west, self.east)
Return the horizontal centerline.
Returns:
tuple: The horizontal centerline.
170 @property 171 def midpoint(self): 172 """ 173 Return the center of the bounding box. 174 175 Returns: 176 tuple: The center of the bounding box. 177 """ 178 x1, y1 = self.southwest 179 x2, y2 = self.northeast 180 181 xc = (x1 + x2) / 2 182 yc = (y1 + y2) / 2 183 184 return (xc, yc)
Return the center of the bounding box.
Returns:
tuple: The center of the bounding box.
186 @property 187 def corners(self): 188 """ 189 Return the four corners of the bounding box. 190 191 Returns: 192 tuple: The four corners of the bounding box. 193 """ 194 return (self.northwest, self.southwest, self.southeast, self.northeast)
Return the four corners of the bounding box.
Returns:
tuple: The four corners of the bounding box.
196 @property 197 def diamond(self): 198 """ 199 Return the four center points of the bounding box in a diamond shape. 200 201 Returns: 202 tuple: The four center points of the bounding box in a diamond shape. 203 """ 204 return (self.north, self.west, self.south, self.east)
Return the four center points of the bounding box in a diamond shape.
Returns:
tuple: The four center points of the bounding box in a diamond shape.
206 @property 207 def all_anchors(self): 208 """ 209 Return all anchors of the bounding box. 210 211 Returns: 212 tuple: All anchors of the bounding box. 213 """ 214 return ( 215 self.northwest, 216 self.west, 217 self.southwest, 218 self.south, 219 self.northeast, 220 self.east, 221 self.northeast, 222 self.north, 223 self.midpoint, 224 )
Return all anchors of the bounding box.
Returns:
tuple: All anchors of the bounding box.
226 @property 227 def width(self): 228 """ 229 Return the width of the bounding box. 230 231 Returns: 232 float: The width of the bounding box. 233 """ 234 return distance(self.northwest, self.northeast)
Return the width of the bounding box.
Returns:
float: The width of the bounding box.
236 @property 237 def height(self): 238 """ 239 Return the height of the bounding box. 240 241 Returns: 242 float: The height of the bounding box. 243 """ 244 return distance(self.northwest, self.southwest)
Return the height of the bounding box.
Returns:
float: The height of the bounding box.
246 @property 247 def size(self): 248 """ 249 Return the size of the bounding box. 250 251 Returns: 252 tuple: The size of the bounding box. 253 """ 254 return (self.width, self.height)
Return the size of the bounding box.
Returns:
tuple: The size of the bounding box.
256 @property 257 def west(self): 258 """ 259 Return the left edge midpoint. 260 261 Returns: 262 tuple: The left edge midpoint. 263 """ 264 return mid_point(*self.left)
Return the left edge midpoint.
Returns:
tuple: The left edge midpoint.
266 @property 267 def south(self): 268 """ 269 Return the bottom edge midpoint. 270 271 Returns: 272 tuple: The bottom edge midpoint. 273 """ 274 return mid_point(*self.bottom)
Return the bottom edge midpoint.
Returns:
tuple: The bottom edge midpoint.
276 @property 277 def east(self): 278 """ 279 Return the right edge midpoint. 280 281 Returns: 282 tuple: The right edge midpoint. 283 """ 284 return mid_point(*self.right)
Return the right edge midpoint.
Returns:
tuple: The right edge midpoint.
286 @property 287 def north(self): 288 """ 289 Return the top edge midpoint. 290 291 Returns: 292 tuple: The top edge midpoint. 293 """ 294 return mid_point(*self.top)
Return the top edge midpoint.
Returns:
tuple: The top edge midpoint.
296 @property 297 def northwest(self): 298 """ 299 Return the top left corner. 300 301 Returns: 302 tuple: The top left corner. 303 """ 304 return self.__dict__["northwest"]
Return the top left corner.
Returns:
tuple: The top left corner.
306 @property 307 def northeast(self): 308 """ 309 Return the top right corner. 310 311 Returns: 312 tuple: The top right corner. 313 """ 314 return self.__dict__["northeast"]
Return the top right corner.
Returns:
tuple: The top right corner.
316 @property 317 def southwest(self): 318 """ 319 Return the bottom left corner. 320 321 Returns: 322 tuple: The bottom left corner. 323 """ 324 return self.__dict__["southwest"]
Return the bottom left corner.
Returns:
tuple: The bottom left corner.
326 @property 327 def southeast(self): 328 """ 329 Return the bottom right corner. 330 331 Returns: 332 tuple: The bottom right corner. 333 """ 334 return self.__dict__["southeast"]
Return the bottom right corner.
Returns:
tuple: The bottom right corner.
336 @property 337 def diagonal1(self): 338 """ 339 Return the first diagonal. From the top left to the bottom right. 340 341 Returns: 342 tuple: The first diagonal. 343 """ 344 return (self.southwest, self.northeast)
Return the first diagonal. From the top left to the bottom right.
Returns:
tuple: The first diagonal.
346 @property 347 def diagonal2(self): 348 """ 349 Return the second diagonal. From the top right to the bottom left. 350 351 Returns: 352 tuple: The second diagonal. 353 """ 354 return (self.southeast, self.northwest)
Return the second diagonal. From the top right to the bottom left.
Returns:
tuple: The second diagonal.
356 def get_inflated_b_box( 357 self, left_margin=None, bottom_margin=None, right_margin=None, top_margin=None 358 ): 359 """ 360 Return a bounding box with offset edges. 361 362 Args: 363 left_margin (float, optional): The left margin. 364 bottom_margin (float, optional): The bottom margin. 365 right_margin (float, optional): The right margin. 366 top_margin (float, optional): The top margin. 367 368 Returns: 369 BoundingBox: The inflated bounding box. 370 """ 371 372 if bottom_margin is None: 373 bottom_margin = left_margin 374 if right_margin is None: 375 right_margin = left_margin 376 if top_margin is None: 377 top_margin = bottom_margin 378 379 x, y = self.southwest[:2] 380 southwest = (x - left_margin, y - bottom_margin) 381 382 x, y = self.northeast[:2] 383 northeast = (x + right_margin, y + top_margin) 384 385 return BoundingBox(southwest, northeast)
Return a bounding box with offset edges.
Arguments:
- left_margin (float, optional): The left margin.
- bottom_margin (float, optional): The bottom margin.
- right_margin (float, optional): The right margin.
- top_margin (float, optional): The top margin.
Returns:
BoundingBox: The inflated bounding box.
387 def offset_line(self, side, offset): 388 """ 389 Offset is applied outwards. Use negative values for inward offset. 390 391 Args: 392 side (Side): The side to offset. 393 offset (float): The offset distance. 394 395 Returns: 396 tuple: The offset line. 397 """ 398 if isinstance(side, str): 399 side = Side[side.upper()] 400 401 if side == Side.RIGHT: 402 x1, y1 = self.southeast 403 x2, y2 = self.northeast 404 res = ((x1 + offset, y1), (x2 + offset, y2)) 405 elif side == Side.LEFT: 406 x1, y1 = self.southwest 407 x2, y2 = self.northwest 408 res = ((x1 - offset, y1), (x2 - offset, y2)) 409 elif side == Side.TOP: 410 x1, y1 = self.northwest 411 x2, y2 = self.northeast 412 res = ((x1, y1 + offset), (x2, y2 + offset)) 413 elif side == Side.BOTTOM: 414 x1, y1 = self.southwest 415 x2, y2 = self.southeast 416 res = ((x1, y1 - offset), (x2, y2 - offset)) 417 elif side == Side.DIAGONAL1: 418 res = offset_line(self.diagonal1, offset) 419 elif side == Side.DIAGONAL2: 420 res = offset_line(self.diagonal2, offset) 421 elif side == Side.H_CENTERLINE: 422 res = offset_line(self.horiz_center_line, offset) 423 elif side == Side.V_CENTERLINE: 424 res = offset_line(self.vert_center_line, offset) 425 else: 426 raise ValueError(f"Unknown side: {side}") 427 res = None 428 429 return res
Offset is applied outwards. Use negative values for inward offset.
Arguments:
- side (Side): The side to offset.
- offset (float): The offset distance.
Returns:
tuple: The offset line.
431 def offset_point(self, anchor, dx, dy): 432 """ 433 Return an offset point from the given corner. 434 435 Args: 436 anchor (Anchor): The anchor point. 437 dx (float): The x offset. 438 dy (float): The y offset. 439 440 Returns: 441 list: The offset point. 442 """ 443 if isinstance(anchor, str): 444 anchor = Anchor[anchor.upper()] 445 x, y = getattr(self, anchor.value)[:2] 446 elif isinstance(anchor, Anchor): 447 x, y = anchor.value[:2] 448 else: 449 raise ValueError(f"Unknown anchor: {anchor}") 450 return [x + dx, y + dy]
Return an offset point from the given corner.
Arguments:
- anchor (Anchor): The anchor point.
- dx (float): The x offset.
- dy (float): The y offset.
Returns:
list: The offset point.
453 def centered(self, item:'Union[Shape, Batch]', dx:float = 0, dy:float = 0)->Point: 454 """ 455 Get the center of the reference item. 456 457 Args: 458 item (object): The reference item. Shape or Batch. 459 dx (float): The x offset. 460 dy (float): The y offset. 461 462 Returns: 463 Point: The item.midpoint of the reference item's bounding-box. 464 """ 465 466 x, y = item.midpoint 467 x += dx 468 y += dy 469 return x, y
Get the center of the reference item.
Arguments:
- item (object): The reference item. Shape or Batch.
- dx (float): The x offset.
- dy (float): The y offset.
Returns:
Point: The item.midpoint of the reference item's bounding-box.
471 def left_of(self, item:'Union[Shape, Batch]', dx:float = 0, dy:float = 0)->Point: 472 """ 473 Get the item.west of the reference item. 474 475 Args: 476 item (object): The reference item. Shape or Batch. 477 dx (float): The x offset. 478 dy (float): The y offset. 479 480 Returns: 481 Point: The item.west of the reference item's bounding-box. 482 """ 483 x, y = item.west 484 w2 = self.width / 2 485 x += (dx - w2) 486 y += dy 487 return x, y
Get the item.west of the reference item.
Arguments:
- item (object): The reference item. Shape or Batch.
- dx (float): The x offset.
- dy (float): The y offset.
Returns:
Point: The item.west of the reference item's bounding-box.
489 def right_of(self, item:'Union[Shape, Batch]', dx:float = 0, dy:float = 0)->Point: 490 """ 491 Get the item.east of the reference item. 492 493 Args: 494 item (object): The reference item. Shape or Batch. 495 dx (float): The x offset. 496 dy (float): The y offset. 497 498 Returns: 499 Point: The item.east of the reference item's bounding-box. 500 """ 501 x, y = item.east 502 w2 = self.width / 2 503 x += (dx + w2) 504 y += dy 505 return x, y
Get the item.east of the reference item.
Arguments:
- item (object): The reference item. Shape or Batch.
- dx (float): The x offset.
- dy (float): The y offset.
Returns:
Point: The item.east of the reference item's bounding-box.
507 def above(self, item:'Union[Shape, Batch]', dx:float = 0, dy:float = 0)->Point: 508 """ 509 Get the item.north of the reference item. 510 511 Args: 512 item (object): The reference item. Shape or Batch. 513 dx (float): The x offset. 514 dy (float): The y offset. 515 516 Returns: 517 Point: The item.north of the reference item's bounding-box. 518 """ 519 x, y = item.north 520 h2 = self.height / 2 521 x += dx 522 y += (dy + h2) 523 return x, y
Get the item.north of the reference item.
Arguments:
- item (object): The reference item. Shape or Batch.
- dx (float): The x offset.
- dy (float): The y offset.
Returns:
Point: The item.north of the reference item's bounding-box.
525 def below(self, item:'Union[Shape, Batch]', dx:float = 0, dy:float = 0)->Point: 526 """ 527 Get the item.south of the reference item. 528 529 Args: 530 item (object): The reference item. Shape or Batch. 531 dx (float): The x offset. 532 dy (float): The y offset. 533 534 Returns: 535 Point: The item.south of the reference item's bounding-box. 536 """ 537 x, y = item.south 538 h2 = self.height / 2 539 x += dx 540 y += (dy - h2) 541 return x, y
Get the item.south of the reference item.
Arguments:
- item (object): The reference item. Shape or Batch.
- dx (float): The x offset.
- dy (float): The y offset.
Returns:
Point: The item.south of the reference item's bounding-box.
543 def above_left(self, item:'Union[Shape, Batch]', dx:float = 0, dy:float = 0)->Point: 544 """ 545 Get the item.northwest of the reference item. 546 547 Args: 548 item (object): The reference item. Shape or Batch. 549 dx (float): The x offset. 550 dy (float): The y offset. 551 552 Returns: 553 Point: The item.northwest of the reference item's bounding-box. 554 """ 555 x, y = item.northwest 556 w2 = self.width / 2 557 h2 = self.height / 2 558 x += (dx - w2) 559 y += (dy + h2) 560 561 return x, y
Get the item.northwest of the reference item.
Arguments:
- item (object): The reference item. Shape or Batch.
- dx (float): The x offset.
- dy (float): The y offset.
Returns:
Point: The item.northwest of the reference item's bounding-box.
564 def above_right(self, item:'Union[Shape, Batch]', dx:float = 0, dy:float = 0)->Point: 565 """ 566 Get the item.northeast of the reference item. 567 568 Args: 569 item (object): The reference item. Shape or Batch. 570 dx (float): The x offset. 571 dy (float): The y offset. 572 573 Returns: 574 Point: The item.northeast of the reference item's bounding-box. 575 """ 576 x, y = item.northeast 577 w2 = self.width / 2 578 h2 = self.height / 2 579 x += (dx + w2) 580 y += (dy + h2) 581 582 return x, y
Get the item.northeast of the reference item.
Arguments:
- item (object): The reference item. Shape or Batch.
- dx (float): The x offset.
- dy (float): The y offset.
Returns:
Point: The item.northeast of the reference item's bounding-box.
585 def below_left(self, item:'Union[Shape, Batch]', dx:float = 0, dy:float = 0)->Point: 586 """ 587 Get the item.southwest of the reference item. 588 589 Args: 590 item (object): The reference item. Shape or Batch. 591 dx (float): The x offset. 592 dy (float): The y offset. 593 594 Returns: 595 Point: The item.southwest of the reference item's bounding-box. 596 """ 597 x, y = item.southwest 598 w2 = self.width / 2 599 h2 = self.height / 2 600 x += (dx - w2) 601 y += (dy - h2) 602 603 return x, y
Get the item.southwest of the reference item.
Arguments:
- item (object): The reference item. Shape or Batch.
- dx (float): The x offset.
- dy (float): The y offset.
Returns:
Point: The item.southwest of the reference item's bounding-box.
606 def below_right(self, item:'Union[Shape, Batch]', dx:float = 0, dy:float = 0)->Point: 607 """ 608 Get the item.southeast of the reference item. 609 610 Args: 611 item (object): The reference item. Shape or Batch. 612 dx (float): The x offset. 613 dy (float): The y offset. 614 615 Returns: 616 Point: The item.southeast of the reference item's bounding-box. 617 """ 618 x, y = item.southeast 619 w2 = self.width / 2 620 h2 = self.height / 2 621 x += (dx + w2) 622 y += (dy - h2) 623 624 return x, y
Get the item.southeast of the reference item.
Arguments:
- item (object): The reference item. Shape or Batch.
- dx (float): The x offset.
- dy (float): The y offset.
Returns:
Point: The item.southeast of the reference item's bounding-box.
627 def polar_pos(self, item:'Union[Shape, Batch]', theta:float, radius:float)->Point: 628 """ 629 Get the polar position of the reference item. 630 631 Args: 632 item (object): The reference item. Shape or Batch. 633 theta (float): The angle in radians. 634 radius (float): The radius. 635 636 Returns: 637 Point: The polar position of the reference item. 638 """ 639 640 x, y = item.midpoint 641 642 x1, y1 = polar_to_cartesian(radius, theta) 643 x += x1 644 y += y1 645 646 return x, y
Get the polar position of the reference item.
Arguments:
- item (object): The reference item. Shape or Batch.
- theta (float): The angle in radians.
- radius (float): The radius.
Returns:
Point: The polar position of the reference item.
649def bounding_box(points): 650 """ 651 Given a list of (x, y) points return the corresponding BoundingBox object. 652 653 Args: 654 points (list): The list of points. 655 656 Returns: 657 BoundingBox: The corresponding BoundingBox object. 658 659 Raises: 660 ValueError: If the list of points is empty. 661 """ 662 if isinstance(points, np.ndarray): 663 points = points[:, :2] 664 else: 665 points = np.array(points) # numpy array of points 666 n_points = len(points) 667 BB_EPSILON = defaults["BB_EPSILON"] 668 if n_points == 0: # empty list of points 669 raise ValueError("Empty list of points") 670 671 if len(points.shape) == 1: 672 # single point 673 min_x, min_y = points 674 max_x = min_x + BB_EPSILON 675 max_y = min_y + BB_EPSILON 676 else: 677 # find minimum and maximum coordinates 678 min_x, min_y = points.min(axis=0) 679 max_x, max_y = points.max(axis=0) 680 if min_x == max_x: # this could be a vertical line or degenerate points 681 max_x += BB_EPSILON 682 if min_y == max_y: # this could be a horizontal line or degenerate points 683 max_y += BB_EPSILON 684 # bounding box corners 685 bottom_left = (min_x, min_y) 686 top_right = (max_x, max_y) 687 return BoundingBox(southwest=bottom_left, northeast=top_right)
Given a list of (x, y) points return the corresponding BoundingBox object.
Arguments:
- points (list): The list of points.
Returns:
BoundingBox: The corresponding BoundingBox object.
Raises:
- ValueError: If the list of points is empty.