Skip to content

Instantly share code, notes, and snippets.

@sudo-ben
Created July 1, 2018 03:44
Show Gist options
  • Save sudo-ben/7eb13dfd392fd4bc7de880979a53041e to your computer and use it in GitHub Desktop.
Save sudo-ben/7eb13dfd392fd4bc7de880979a53041e to your computer and use it in GitHub Desktop.
'''
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANy WARRANTy; without even the implied warranty of
MERCHANTABILITy or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
you should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
'''
from vectors import Vector2
from random import uniform
try:
import cython
if cython.compiled:
print "rectangle.py compiled."
except ImportError:
pass
class Rect(object):
"""
>>> myRect = Rect(10,10,10,10)
>>> myRect.area()
100.0
>>> myRect.collides(myRect)
True
>>> myRect.width
10.0
>>> myRect.height
10.0
>>> myRect.x
10.0
>>> myRect.y
10.0
"""
__slots__ = ('x1', 'y1', 'x2', 'y2')
def __init__(self, x=0.0, y=0.0, width=0.0, height=0.0):
'''Create a rectangle from a minimum and maximum point'''
self.x1 = float(x)
self.y1 = float(y)
self.x2 = self.x1 + float(width)
self.y2 = self.y1 + float(height)
def __str__(self):
return 'x: ' + str(self.x) + ' y: ' + str(self.y) + \
' width: ' + str(self.width) + ' height: ' + str(self.height)
def __and__(self, otherRect):
"Like a set, s.intersectingArea(t) is the same as s & t"
return self.intersecting_area(otherRect)
def intersectingRect(self, rectangle):
"""
Compute the intersection of two rectangles:
>>> myRect = Rect(0,0,10,10)
>>> myRect.intersectingRect(myRect).area()
100.0
>>> myRect2 = Rect(15,15,20,20)
"""
if self.collides(rectangle):
rect = Rect(0, 0, 0, 0)
rect.x1 = max(self.x1, rectangle.x1)
rect.y1 = max(self.y1, rectangle.y1)
rect.x2 = min(self.x2, rectangle.x2)
rect.y2 = min(self.y2, rectangle.y2)
return rect
else:
return None
def move(self, delta):
x, y = delta
self.x1 += x
self.x2 += x
self.y1 += y
self.y2 += y
def interpolate(self, startRect, destRect, t):
"""
Compute the intersection of two rectangles:
>>> myRect = Rect(0,0,10,10)
>>> myRect.interpolate(Rect(0,0,10,10), Rect(0,0,20,20), 0.5)
>>> myRect.width
15.0
>>> myRect.height
15.0
>>> myRect.interpolate(Rect(0,0,10,10), Rect(20,20,20,20), 0.5)
>>> myRect.x
10.0
"""
assert t >= 0
diffx = destRect.x - startRect.x
diffy = destRect.y - startRect.y
diffwidth = destRect.width - startRect.width
diffheight = destRect.height - startRect.height
self.x = startRect.x + (diffx * t)
self.y = startRect.y + (diffy * t)
self.width = startRect.width + (diffwidth * t)
self.height = startRect.height + (diffheight * t)
def divideHorizontal(self, number, margin=0.0):
return self.divideHorizontalWeighted([1] * number, margin)
def divideHorizontalWeighted(self, weightList, margin=0.0):
"""
>>> subRects = list(Rect(0,0,40,10).divideHorizontalWeighted([1,2,1]))
>>> len(subRects)
3
>>> [r.width for r in subRects]
[10.0, 20.0, 10.0]
>>> [r.width for r in Rect(0,0,40,10).divideHorizontalWeighted([1,2,1], 1.0)]
[8.0, 18.0, 8.0]
"""
weightUnit = self.width / float(sum(weightList))
weightSum = 0
l = []
for w in weightList:
# yield
l.append(Rect(self.x + weightUnit * weightSum + margin, self.y, weightUnit * w - (2 * margin), self.height))
weightSum += w
return l
def divide_vertical(self, number, margin=0.0):
return self.divideVerticalWeighted([1] * number, margin)
def divideVerticalWeighted(self, weightList, margin=0.0):
"""
>>> subRects = list(Rect(0,0,10,40).divideVerticalWeighted([1,2,1]))
>>> len(subRects)
3
>>> [r.height for r in subRects]
[10.0, 20.0, 10.0]
>>> [r.height for r in Rect(0,0,10,40).divideVerticalWeighted([1,2,1], 1.0)]
[8.0, 18.0, 8.0]
"""
weightUnit = self.height / float(sum(weightList))
weightSum = 0
l = []
for w in weightList:
# yield
l.append(Rect(self.x, self.y + weightUnit * weightSum + margin, self.width, weightUnit * w - (2 * margin)))
weightSum += w
return l
def collides(self, rectangle):
"""
Determine whether two rectangles collide:
>>> myRect = Rect(0,0,10,10)
>>> myRect.collides(Rect(5,5,15,15))
True
"""
if self.x2 < rectangle.x1 or self.y2 < rectangle.y1 or self.x1 > rectangle.x2 or self.y1 > rectangle.y2:
return False
return True
def collitionNormal(self, rectangle):
"""
Determine whether two rectangles collide:
>>> myRect = Rect(0,0,10,10)
>>> myRect.collides(Rect(5,5,15,15))==True
True
"""
if self.x2 > rectangle.x1:
return Vector2(1, 0)
elif self.x1 < rectangle.x2:
return Vector2(-1, 0)
elif self.y2 > rectangle.y1:
return Vector2(0, 1)
elif self.y1 < rectangle.y2:
return Vector2(0, -1)
return False
def __contains__(self, otherRect):
"""
>>> myRect = Rect(0,0,10,10)
>>> Rect(2,2,8,8) in myRect
True
>>> Rect(-2,2,8,8) in myRect
False
>>> Rect(0,0,10,10) in myRect
True
>>> Rect(0,0,10,20) in myRect
False
"""
return self.fullyContains(otherRect)
def fullyContains(self, rectangle):
if self.x1 <= rectangle.x1 and \
self.x2 >= rectangle.x2 and \
self.y1 <= rectangle.y1 and \
self.y2 >= rectangle.y2:
return True
return False
def copy(self):
return self.__class__(self.x, self.y, self.width, self.height)
def __nonzero__(self):
return bool(self.width and self.height)
def pointInRect(self, point):
x, y = point
if self.x1 <= x and \
self.x2 >= x and \
self.y1 <= y and \
self.y2 >= y:
return True
return False
def containPoint(self, point):
x, y = point
if x < self.x:
x = self.x
if x > self.width:
x = self.width
if y < self.y:
y = self.y
if y > self.height:
y = self.height
def positionRectInRect(self, rectangle):
delta = Vector2(0, 0)
if self.x1 <= rectangle.x1:
delta.x += rectangle.x1 - self.x1
if self.x2 >= rectangle.x2:
delta.x -= self.x2 - rectangle.x2
if self.y1 <= rectangle.y1:
delta.y += rectangle.y1 - self.y1
if self.y2 >= rectangle.y2:
delta.y -= self.y2 - rectangle.y2
self.move(delta)
def resizeRectInsideRect(self, rectangle):
"""
>>> myRect = Rect(0,0,10,10)
>>> myRect.resizeRectInsideRect(Rect(2,2,8,8))
>>> myRect in Rect(2,2,8,8)
True
"""
if self.x1 <= rectangle.x1:
self.x1 = rectangle.x1
if self.x2 >= rectangle.x2:
self.x2 = rectangle.x2
if self.y1 <= rectangle.y1:
self.y1 = rectangle.y1
if self.y2 >= rectangle.y2:
self.y2 = rectangle.y2
def widthHeightTuple(self):
return (self.width, self.height)
def getRandPoint(self):
return Vector2(uniform(0, self.width), uniform(0, self.height))
def collideListAll(self, rectList):
for rectangle in rectList:
if rectangle is not self and self.collides(rectangle):
yield rectangle
def __ior__(self, otherRect):
"equivalent to a |= b"
self.union(otherRect)
def union(self, rectangle):
if self.collides(rectangle):
self.x = min(rectangle.x, self.x)
self.y = min(rectangle.y, self.y)
self.width = max(rectangle.width, self.width)
self.height = max(rectangle.height, self.height)
def __isub__(self, resize):
self.width -= resize
self.height -= resize
return self
def __iadd__(self, resize):
self.width += resize
self.height += resize
return self
def __mul__(self, factor):
newRect = Rect(self.x1, self.y1, self.width, self.height)
newRect *= factor
return newRect
def __imul__(self, factor):
self.width *= factor
self.height *= factor
return self
def __idiv__(self, factor):
self.width /= factor
self.height /= factor
return self
def area(self):
"""
>>> myRect = Rect(0,0,10,10)
>>> myRect.area()
100.0
"""
return self.width * self.height
def topLeft(self):
return Vector2(self.x1, self.y1)
def topRight(self):
return Vector2(self.x2, self.y1)
def bottomRight(self):
return Vector2(self.x2, self.y2)
def bottomLeft(self):
return Vector2(self.x1, self.y2)
def corners(self):
return [self.topLeft(), self.topRight() , self.bottomRight() , self.bottomLeft()]
def _getWidth(self): return self.x2 - self.x1
def _setWidth(self, width):
"""
>>> myRect = Rect(0,0,10,10)
>>> myRect.width = 5
>>> myRect.area()
50.0
"""
assert width >= 0
self.x2 = self.x1 + width
assert self.width >= 0
width = property(_getWidth, _setWidth, None, None)
def _getHeight(self): return self.y2 - self.y1
def _setHeight(self, height):
assert height >= 0
self.y2 = self.y1 + height
assert self.height >= 0
height = property(_getHeight, _setHeight, None, None)
def _getX(self): return self.x1
def _setX(self, x):
self.x2 += x - self.x1
self.x1 = x
x = property(_getX, _setX, None, None)
def _getY(self): return self.y1
def _setY(self, y):
self.y2 += y - self.y1
self.y1 = y
y = property(_getY, _setY, None, None)
def _getCenter(self):
"""
>>> myRect = Rect(0,0,10,10)
>>> myRect.center
Vector2(5.00, 5.00)
"""
return Vector2(self.x1 + (self.width / 2.0), self.y1 + (self.height / 2.0))
def _setCenter(self, center):
"""
>>> myRect = Rect(0,0,10,10)
>>> myRect.center = Vector2(10,10)
>>> myRect.x1
5.0
>>> myRect.y1
5.0
>>> myRect.x2
15.0
>>> myRect.x2
15.0
"""
x, y = center
self.x = x - (self.width / 2.0)
self.y = y - (self.height / 2.0)
center = property(_getCenter, _setCenter, None, None)
if __name__ == "__main__":
import doctest
doctest.testmod(verbose=True)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment