Created
July 1, 2018 03:44
-
-
Save sudo-ben/7eb13dfd392fd4bc7de880979a53041e to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
''' | |
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