geometry.py

changeset 7
0ab0d61ccee8
parent 6
6da1e81c5652
child 9
fea8e9ae6f29
--- a/geometry.py	Wed Dec 20 17:25:09 2017 +0200
+++ b/geometry.py	Thu Dec 21 10:46:41 2017 +0200
@@ -1,3 +1,21 @@
+def degree_rep(angle):
+    from math import degrees
+    return '∠ %.2f°' % degrees(angle)
+
+def position_vector(vertex):
+    assert isinstance(vertex, Vertex)
+    return Vector(*vertex.coordinates)
+
+def angle_magnitude_key(angle):
+    '''
+        Returns how great an angle is.
+    '''
+    from math import pi as π
+    normalized_angle = abs(angle) % (2 * π)
+    if normalized_angle > π:
+        normalized_angle = (2 * π) - normalized_angle
+    return normalized_angle
+
 class Vertex:
     def __init__(self, x, y, z):
         if not all(is_real(coordinate) for coordinate in (x, y, z)):
@@ -19,18 +37,32 @@
     def coordinates(self):
         return self.x, self.y, self.z
     def __add__(self, other):
-        return type(self)(self.x + other.x, self.y + other.y, self.z + other.z)
+        assert not (type(self) == type(other) == Vertex)
+        if type(self) == Vector and type(other) == Vector:
+            result_type = Vector
+        else:
+            result_type = Vertex
+        return result_type(self.x + other.x, self.y + other.y, self.z + other.z)
+    def __radd__(self, other):
+        return self + other
     def __neg__(self):
         return type(self)(-self.x, -self.y, -self.z)
     def __sub__(self, other):
-        return self + (-other)
+        result = self + (-position_vector(other))
+        if isinstance(other, Vertex):
+            return Vector(*result.coordinates)
+        else:
+            return result
     def __mul__(self, scalar):
+        assert is_real(scalar)
         return type(self)(self.x * scalar, self.y * scalar, self.z * scalar)
     def __rmul__(self, other):
         return self * other
     def __truediv__(self, scalar):
+        assert is_real(scalar)
         return type(self)(self.x / scalar, self.y / scalar, self.z / scalar)
     def __floordiv__(self, scalar):
+        assert is_real(scalar)
         return type(self)(self.x // scalar, self.y // scalar, self.z // scalar)
     def __matmul__(self, transformation_matrix):
         return transform(self, transformation_matrix)
@@ -53,6 +85,9 @@
     def __repr__(self):
         return str.format('LineSegment({!r}, {!r})', self.v1, self.v2)
     @property
+    def length(self):
+        return self.v1.distance_to(self.v2)
+    @property
     def vertices(self):
         return self.v1, self.v2
     def __len__(self):
@@ -70,7 +105,7 @@
     def __init__(self, x, y, z):
         super().__init__(x, y, z)
     def __repr__(self):
-        return str.format('Vector({!r}, {!r}. {!r})', self.x, self.y, self.z)
+        return str.format('Vector({!r}, {!r}, {!r})', self.x, self.y, self.z)
     def length(self):
         from math import sqrt
         return sqrt(self.x ** 2 + self.y ** 2 + self.z ** 2)
@@ -148,6 +183,24 @@
         return self.vertices[index % len(self.vertices)]
     def __len__(self):
         return len(self.vertices)
+    @property
+    def perimeter_lines(self):
+        for v1, v2 in pairs(self.vertices):
+            yield LineSegment(v1, v2)
+    @property
+    def smallest_angle(self):
+        from math import acos, inf
+        min_angle = inf
+        for v1, v2, v3 in pairs(self.vertices, count = 3):
+            vec1 = (position_vector(v3) - position_vector(v2)).normalized()
+            vec2 = (position_vector(v1) - position_vector(v2)).normalized()
+            cosine = dot_product(vec1, vec2) / vec1.length() / vec2.length()
+            min_angle = min(min_angle, angle_magnitude_key(acos(cosine)))
+        return min_angle
+    @property
+    def hairline_ratio(self):
+        lengths = [line.length for line in self.perimeter_lines]
+        return max(lengths) / min(lengths)
 
 def is_real(number):
     return isinstance(number, int) or isinstance(number, float)

mercurial