1import math 2 3class Point(object): 4 """A representation of a point within the Beziers world. 5 6 Here are some things you can do with points. You can interpret 7 them as vectors, and add them together:: 8 9 >>> a = Point(5,5) 10 >>> b = Point(10,10) 11 >>> a + b 12 <15.0,15.0> 13 14 You can multiply them by a scalar to scale them:: 15 16 >>> a * 2 17 <10.0,10.0> 18 19 You can adjust them:: 20 21 >>> a += b 22 >>> a 23 <15.0,15.0> 24 25 If you're using Python 3, you can abuse operator overloading 26 and compute the dot product of two vectors: 27 28 >>> a = Point(5,5) 29 >>> b = Point(10,10) 30 >>> a @ b 31 100.0 32 33""" 34 35 def __init__(self, x,y): 36 self.x = float(x) 37 self.y = float(y) 38 39 def __repr__(self): 40 return "<%s,%s>" % (self.x,self.y) 41 42 @classmethod 43 def fromRepr(klass,text): 44 import re 45 p = re.compile("^<([^,]+),([^>]+)>$") 46 m = p.match(text) 47 return klass(m.group(1), m.group(2)) 48 49 def __eq__(self, other): 50 def isclose(a, b, rel_tol=1e-09, abs_tol=0.0): 51 return abs(a-b) <= max(rel_tol * max(abs(a), abs(b)), abs_tol) 52 return isclose(self.x, other.x) and isclose(self.y, other.y) 53 54 def __hash__(self): 55 return hash(self.x) << 32 ^ hash(self.y) 56 57 def __mul__(self, other): 58 """Multiply a point by a scalar.""" 59 return Point(self.x * other, self.y * other) 60 61 def __div__(self, other): 62 return Point(self.x / other, self.y / other) 63 64 def __truediv__(self, other): 65 return Point(self.x / other, self.y / other) 66 67 def __add__(self, other): 68 return Point(self.x + other.x, self.y + other.y) 69 70 def __sub__(self, other): 71 return Point(self.x - other.x, self.y - other.y) 72 73 def __iadd__(self, other): 74 self.x += other.x 75 self.y += other.y 76 return self 77 78 def __isub__(self, other): 79 self.x -= other.x 80 self.y -= other.y 81 return self 82 83 def __matmul__(self,other): # Dot product. Abusing overloading. Sue me. 84 return self.dot(other) 85 86 def dot(self, other): 87 return self.x * other.x + self.y * other.y 88 89 def clone(self): 90 """Clone a point, returning a new object with the same co-ordinates.""" 91 return Point(self.x,self.y) 92 93 def rounded(self): 94 """Return a point with the co-ordinates truncated to integers""" 95 return Point(int(self.x),int(self.y)) 96 97 def lerp(self, other, t): 98 """Interpolate between two points, at time t.""" 99 return self * (1-t) + other * (t) 100 101 @property 102 def squareMagnitude(self): 103 """Interpreting this point as a vector, returns the squared magnitude (Euclidean length) of the vector.""" 104 return self.x*self.x + self.y*self.y 105 106 @property 107 def magnitude(self): 108 """Interpreting this point as a vector, returns the magnitude (Euclidean length) of the vector.""" 109 return math.sqrt(self.squareMagnitude) 110 111 def toUnitVector(self): 112 """Divides this point by its magnitude, returning a vector of length 1.""" 113 mag = self.magnitude 114 if mag == 0.0: mag = 1.0 115 return Point(self.x/mag, self.y/mag) 116 117 @property 118 def angle(self): 119 """Interpreting this point as a vector, returns the angle in radians of the vector.""" 120 return math.atan2(self.y,self.x) 121 122 @property 123 def slope(self): 124 """Returns slope y/x""" 125 if self.x == 0: return 0 126 return self.y / self.x 127 128 @classmethod 129 def fromAngle(self,angle): 130 """Given an angle in radians, return a unit vector representing that angle.""" 131 return Point(math.cos(angle), math.sin(angle)).toUnitVector() 132 133 def rotated(self,around,by): 134 """Return a new point found by rotating this point around another point, by an angle given in radians.""" 135 delta = around - self 136 oldangle = delta.angle 137 newangle = oldangle + by 138 unitvector = Point.fromAngle(newangle) 139 new = around - unitvector * delta.magnitude 140 return new 141 142 def rotate(self,around,by): 143 """Mutate this point by rotating it around another point, by an angle given in radians.""" 144 new = self.rotated(around, by) 145 self.x = new.x 146 self.y = new.y 147 148 def squareDistanceFrom(self,other): 149 """Returns the squared Euclidean distance between this point and another.""" 150 return (self.x - other.x) * (self.x - other.x) + (self.y - other.y) * (self.y - other.y) 151 152 def distanceFrom(self,other): 153 """Returns the Euclidean distance between this point and another.""" 154 return math.sqrt(self.squareDistanceFrom(other)) 155 156 def transformed(self, transformation): 157 m = transformation.matrix 158 x, y = self.x, self.y 159 a1, a2, b1 = m[0] 160 a3, a4, b2 = m[1] 161 xPrime = a1 * x + a2 * y + b1 162 yPrime = a3 * x + a4 * y + b2 163 return Point(xPrime, yPrime) 164 165 def transform(self, transformation): 166 new = self.transformed(transformation) 167 self.x = new.x 168 self.y = new.y 169