Instead of get_insersec_region
, I would call this method __and__
. This way you can use overlap = r1 & r2
, just like for set intersection.
You can add intersect = __and__
if you also want the expressiveness of overlap = r1.intersect(r2)
.
Instead of returning None
, I would consider returning an empty Rectangle
. This way you don't need any special code to handle e.g. (r1&r2).area
. For this, I would set default values for the coordinates, all 0
.
Incidentally, I would add an area method and make it a property:
@property
def area(self):
return (self.max_x - self.min_x) * (self.max_y - self.min_y)
The reason it should be a property is maintainability. If you decide at some point that you only want to compute the area at creation time because a Rectangle is not allowed to change size anymore, you could remove this function and just store self.area = ...
in the constructor. Also it is evidently true that the area of a rectangle is a property of that particular rectangle.
And an __or__
method to get the bounding rectangle. You might need this if you want a GUI and update only the region where things changed but still want to do only one update. Then you need to find a rectangular area which includes all objects, which you can then just get by |
ing all rectangles.
Your __str__
method can be simplified using map
and str.join
:
def __str__(self):
return '\t'.join(map(str, (self.min_x, self.max_x, self.min_y, self.max_y)))
Alternatively, you can use str.format
:
def __str__(self):
return '{self.min_x}\t{self.max_x}\t{self.min_y}\t{self.max_y}'.format(self=self)
The latter slightly more future proof, because you can replace it with this in Python 3.6, using f-strings:
def __str__(self):
return f'{self.min_x}\t{self.max_x}\t{self.min_y}\t{self.max_y}'
Final code:
class Rectangle:
def __init__(self, min_x=0, max_x=0, min_y=0, max_y=0):
self.min_x = min_x
self.max_x = max_x
self.min_y = min_y
self.max_y = max_y
def is_intersect(self, other):
if self.min_x > other.max_x or self.max_x < other.min_x:
return False
if self.min_y > other.max_y or self.max_y < other.min_y:
return False
return True
def __and__(self, other):
if not self.is_intersect(other):
return Rectangle()
min_x = max(self.min_x, other.min_x)
max_x = min(self.max_x, other.max_x)
min_y = max(self.min_y, other.min_y)
max_y = min(self.max_y, other.max_y)
return Rectangle(min_x, max_x, min_y, max_y)
intersect = __and__
def __or__(self, other):
min_x = min(self.min_x, other.min_x)
max_x = max(self.max_x, other.max_x)
min_y = min(self.min_y, other.min_y)
max_y = max(self.max_y, other.max_y)
return Rectangle(min_x, max_x, min_y, max_y)
union = __or__
def __str__(self):
return 'Rectangle({self.min_x},{self.max_x},{self.min_y},{self.max_y})'.format(self=self)
@property
def area(self):
return (self.max_x - self.min_x) * (self.max_y - self.min_y)
insersec
? ;) \$\endgroup\$object
; see stackoverflow.com/q/2588628/1558022 for more background. I’d also recommend adding a__repr__
for easy debuggability. \$\endgroup\$insersec
(2 typos) when the correct spelling isintersect
? \$\endgroup\$start
andend
. A rectangle r is a pair of line segments, call themtop
andleft
. Two rectanglesr1
andr2
overlap iffr1.top
overlapsr2.top
andr1.left
overlapsr2.left
. So you only have to solve the problem on line segments. \$\endgroup\$