# Created By: Virgil Dupras # Created On: 2011-08-05 # Copyright 2015 Hardcoded Software (http://www.hardcoded.net) # # This software is licensed under the "GPLv3" License as described in the "LICENSE" file, # which should be included with this package. The terms are also available at # http://www.gnu.org/licenses/gpl-3.0.html from sys import maxsize as INF from math import sqrt VERY_SMALL = 0.0000001 class Point: def __init__(self, x, y): self.x = x self.y = y def __repr__(self): return ''.format(*self) def __iter__(self): yield self.x yield self.y def distance_to(self, other): return Line(self, other).length() class Line: def __init__(self, p1, p2): self.p1 = p1 self.p2 = p2 def __repr__(self): return ''.format(*self) def __iter__(self): yield self.p1 yield self.p2 def dx(self): return self.p2.x - self.p1.x def dy(self): return self.p2.y - self.p1.y def length(self): return sqrt(self.dx() ** 2 + self.dy() ** 2) def slope(self): if self.dx() == 0: return INF if self.dy() > 0 else -INF else: return self.dy() / self.dx() def intersection_point(self, other): # with help from http://paulbourke.net/geometry/lineline2d/ if abs(self.slope() - other.slope()) < VERY_SMALL: # parallel. Even if coincident, we return nothing return None A, B = self C, D = other denom = (D.y-C.y) * (B.x-A.x) - (D.x-C.x) * (B.y-A.y) if denom == 0: return None numera = (D.x-C.x) * (A.y-C.y) - (D.y-C.y) * (A.x-C.x) numerb = (B.x-A.x) * (A.y-C.y) - (B.y-A.y) * (A.x-C.x) mua = numera / denom; mub = numerb / denom; if (0 <= mua <= 1) and (0 <= mub <= 1): x = A.x + mua * (B.x - A.x) y = A.y + mua * (B.y - A.y) return Point(x, y) else: return None class Rect: def __init__(self, x, y, w, h): self.x = x self.y = y self.w = w self.h = h def __iter__(self): yield self.x yield self.y yield self.w yield self.h def __repr__(self): return ''.format(*self) @classmethod def from_center(cls, center, width, height): x = center.x - width / 2 y = center.y - height / 2 return cls(x, y, width, height) @classmethod def from_corners(cls, pt1, pt2): x1, y1 = pt1 x2, y2 = pt2 return cls(min(x1, x2), min(y1, y2), abs(x1-x2), abs(y1-y2)) def center(self): return Point(self.x + self.w/2, self.y + self.h/2) def contains_point(self, point): x, y = point (x1, y1), (x2, y2) = self.corners() return (x1 <= x <= x2) and (y1 <= y <= y2) def contains_rect(self, rect): pt1, pt2 = rect.corners() return self.contains_point(pt1) and self.contains_point(pt2) def corners(self): return Point(self.x, self.y), Point(self.x+self.w, self.y+self.h) def intersects(self, other): r1pt1, r1pt2 = self.corners() r2pt1, r2pt2 = other.corners() if r1pt1.x < r2pt1.x: xinter = r1pt2.x >= r2pt1.x else: xinter = r2pt2.x >= r1pt1.x if not xinter: return False if r1pt1.y < r2pt1.y: yinter = r1pt2.y >= r2pt1.y else: yinter = r2pt2.y >= r1pt1.y return yinter def lines(self): pt1, pt4 = self.corners() pt2 = Point(pt4.x, pt1.y) pt3 = Point(pt1.x, pt4.y) l1 = Line(pt1, pt2) l2 = Line(pt2, pt4) l3 = Line(pt3, pt4) l4 = Line(pt1, pt3) return l1, l2, l3, l4 def scaled_rect(self, dx, dy): """Returns a rect that has the same borders at self, but grown/shrunk by dx/dy on each side. """ x, y, w, h = self x -= dx y -= dy w += dx * 2 h += dy * 2 return Rect(x, y, w, h) def united(self, other): """Returns the bounding rectangle of this rectangle and `other`. """ # ul=upper left lr=lower right ulcorner1, lrcorner1 = self.corners() ulcorner2, lrcorner2 = other.corners() corner1 = Point(min(ulcorner1.x, ulcorner2.x), min(ulcorner1.y, ulcorner2.y)) corner2 = Point(max(lrcorner1.x, lrcorner2.x), max(lrcorner1.y, lrcorner2.y)) return Rect.from_corners(corner1, corner2) #--- Properties @property def top(self): return self.y @top.setter def top(self, value): self.y = value @property def bottom(self): return self.y + self.h @bottom.setter def bottom(self, value): self.y = value - self.h @property def left(self): return self.x @left.setter def left(self, value): self.x = value @property def right(self): return self.x + self.w @right.setter def right(self, value): self.x = value - self.w @property def width(self): return self.w @width.setter def width(self, value): self.w = value @property def height(self): return self.h @height.setter def height(self, value): self.h = value