Rename and fix towards_with_random_angle
@@ -2,6 +2,9 @@ from math import sqrt, pi, sin, cos, atan2
import random
import itertools
+FLOAT_DIGITS = 8
+EPSILON = 10**(-FLOAT_DIGITS)
+
def _sign(num):
if num == 0:
return 0
@@ -21,7 +24,7 @@ class Pointlike(tuple):
assert isinstance(p, Pointlike)
if self == p:
return 0
- return sqrt(sum(self.__class__((b-a)**2 for a, b in itertools.zip_longest(self, p[:len(self)], fillvalue=0))))
+ return sqrt(sum(self.__class__((b-a)**2 for a, b in itertools.zip_longest(self, p, fillvalue=0))))
def sort_by_distance(self, ps):
return sorted(ps, key=lambda p: self.distance_to(p))
@@ -36,11 +39,19 @@ class Pointlike(tuple):
return self.__class__(_sign(b - a) for a, b in itertools.zip_longest(self, p[:len(self)], fillvalue=0))
def towards(self, p, distance=1, limit=False):
+ assert self != p
d = self.distance_to(p)
if limit:
distance = min(d, distance)
return self.__class__(a + (b - a) / d * distance for a, b in itertools.zip_longest(self, p[:len(self)], fillvalue=0))
+ def __eq__(self, other):
+ assert isinstance(other, tuple)
+ return all(abs(a - b) < EPSILON for a, b in itertools.zip_longest(self, other, fillvalue=0))
+
+ def __hash__(self):
+ return hash(tuple(int(c * FLOAT_DIGITS) for c in self))
+
class Point2(Pointlike):
@classmethod
@@ -63,24 +74,21 @@ class Point2(Pointlike):
def to3(self):
return Point3((*self, 0))
- def random_on_distance(self, distance, angle=None):
- if isinstance(distance, tuple):
+ def random_on_distance(self, distance):
+ if isinstance(distance, (tuple, list)): # interval
distance = distance[0] + random.random() * (distance[1] - distance[0])
assert distance > 0
-
- if angle is None:
angle = random.random() * 2 * pi
dx, dy = cos(angle), sin(angle)
return Point2((self.x + dx * distance, self.y + dy * distance))
- def towards_random_angle(self, p, max_difference=(pi/4), distance=1):
- dx, dy = self.to2.towards(p.to2, 1)
- angle = atan2(dy, dx)
+ def towards_with_random_angle(self, p, distance=1, max_difference=(pi/4)):
+ tx, ty = self.to2.towards(p.to2, 1)
+ angle = atan2(ty - self.y, tx - self.x)
angle = (angle - max_difference) + max_difference * 2 * random.random()
- return self.random_on_distance(distance, angle)
-
+ return Point2((self.x + cos(angle) * distance, self.y + sin(angle) * distance))
class Point3(Point2):
@classmethod
