OpenRAM/compiler/router/bbox_node.py

118 lines
4.4 KiB
Python

# See LICENSE for licensing information.
#
# Copyright (c) 2016-2023 Regents of the University of California, Santa Cruz
# All rights reserved.
#
from .bbox import bbox
class bbox_node:
"""
This class represents a node in the bbox tree structure. Bbox trees are
binary trees we use to partition the shapes in the routing region so that
we can detect overlaps faster in a binary search-like manner.
"""
def __init__(self, bbox, left=None, right=None):
self.bbox = bbox
self.is_leaf = not left and not right
self.left = left
self.right = right
def iterate_point(self, point, check_done=False):
""" Iterate over shapes in the tree that overlap the given point. """
px, py = point.x, point.y
# Return this shape if it's a leaf
if self.is_leaf:
ll, ur = self.bbox.rect
if check_done or (ll.x <= px and px <= ur.x and ll.y <= py and py <= ur.y):
yield self.bbox.shape
else:
# Check the left child
if self.left:
ll, ur = self.left.bbox.rect
if ll.x <= px and px <= ur.x and ll.y <= py and py <= ur.y:
yield from self.left.iterate_point(point, True)
# Check the right child
if self.right:
ll, ur = self.right.bbox.rect
if ll.x <= px and px <= ur.x and ll.y <= py and py <= ur.y:
yield from self.right.iterate_point(point, True)
def iterate_shape(self, shape, check_done=False):
""" Iterate over shapes in the tree that overlap the given shape. """
sll, sur = shape.rect
# Return this shape if it's a leaf
if self.is_leaf:
ll, ur = self.bbox.rect
if check_done or (ll.x <= sur.x and sll.x <= ur.x and ll.y <= sur.y and sll.y <= ur.y):
yield self.bbox.shape
else:
# Check the left child
if self.left:
ll, ur = self.left.bbox.rect
if ll.x <= sur.x and sll.x <= ur.x and ll.y <= sur.y and sll.y <= ur.y:
yield from self.left.iterate_shape(shape, True)
# Check the right child
if self.right:
ll, ur = self.right.bbox.rect
if ll.x <= sur.x and sll.x <= ur.x and ll.y <= sur.y and sll.y <= ur.y:
yield from self.right.iterate_shape(shape, True)
def get_costs(self, bbox):
""" Return the costs of bbox nodes after merging the given bbox. """
# Find the new areas for all possible cases
self_merge = bbox.merge(self.bbox)
left_merge = bbox.merge(self.left.bbox)
right_merge = bbox.merge(self.right.bbox)
# Add the change in areas as cost
self_cost = self_merge.area()
left_cost = self_merge.area() - self.bbox.area()
left_cost += left_merge.area() - self.left.bbox.area()
right_cost = self_merge.area() - self.bbox.area()
right_cost += right_merge.area() - self.right.bbox.area()
# Add the overlaps in areas as cost
self_overlap = self.bbox.overlap(bbox)
left_overlap = left_merge.overlap(self.right.bbox)
right_overlap = right_merge.overlap(self.left.bbox)
if self_overlap:
self_cost += self_overlap.area()
if left_overlap:
left_cost += left_overlap.area()
if right_overlap:
right_cost += right_overlap.area()
return self_cost, left_cost, right_cost
def insert(self, bbox):
""" Insert a bbox to the bbox tree. """
if self.is_leaf:
# Put the current bbox to the left child
self.left = bbox_node(self.bbox)
# Put the new bbox to the right child
self.right = bbox_node(bbox)
else:
# Calculate the costs of adding the new bbox
self_cost, left_cost, right_cost = self.get_costs(bbox)
if self_cost < left_cost and self_cost < right_cost: # Add here
self.left = bbox_node(self.bbox, left=self.left, right=self.right)
self.right = bbox_node(bbox)
elif left_cost < right_cost: # Add to the left
self.left.insert(bbox)
else: # Add to the right
self.right.insert(bbox)
# Update the current bbox
self.bbox = self.left.bbox.merge(self.right.bbox)
self.is_leaf = False