geometry.py

changeset 6
6da1e81c5652
parent 5
e340e35d6e4c
child 7
0ab0d61ccee8
--- a/geometry.py	Tue Dec 12 13:46:17 2017 +0200
+++ b/geometry.py	Wed Dec 20 17:25:09 2017 +0200
@@ -19,19 +19,19 @@
     def coordinates(self):
         return self.x, self.y, self.z
     def __add__(self, other):
-        return Vertex(self.x + other.x, self.y + other.y, self.z + other.z)
+        return type(self)(self.x + other.x, self.y + other.y, self.z + other.z)
     def __neg__(self):
-        return Vertex(-self.x, -self.y, -self.z)
+        return type(self)(-self.x, -self.y, -self.z)
     def __sub__(self, other):
         return self + (-other)
     def __mul__(self, scalar):
-        return Vertex(self.x * scalar, self.y * scalar, self.z * scalar)
+        return type(self)(self.x * scalar, self.y * scalar, self.z * scalar)
     def __rmul__(self, other):
         return self * other
     def __truediv__(self, scalar):
-        return Vertex(self.x / scalar, self.y / scalar, self.z / scalar)
+        return type(self)(self.x / scalar, self.y / scalar, self.z / scalar)
     def __floordiv__(self, scalar):
-        return Vertex(self.x // scalar, self.y // scalar, self.z // scalar)
+        return type(self)(self.x // scalar, self.y // scalar, self.z // scalar)
     def __matmul__(self, transformation_matrix):
         return transform(self, transformation_matrix)
     def __eq__(self, other):
@@ -55,17 +55,104 @@
     @property
     def vertices(self):
         return self.v1, self.v2
+    def __len__(self):
+        return 2
+    def __getitem__(self, index):
+        return self.vertices[index]
+
+class IndexRing:
+    def __init__(self, container):
+        self.container = container
+    def __getitem__(self, index):
+        return self.container[index % len(self.container)]
+
+class Vector(Vertex):
+    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)
+    def length(self):
+        from math import sqrt
+        return sqrt(self.x ** 2 + self.y ** 2 + self.z ** 2)
+    def normalized(self):
+        length = self.length()
+        if length > 0:
+            return Vector(self.x / length, self.y / length, self.z / length)
+        else:
+            return Vector(0, 0, 0)
+
+def dot_product(v1, v2):
+    ''' Returns the dot product v1 · v2. '''
+    return v1.x * v2.x + v1.y * v2.y + v1.z * v2.z
+
+def cross_product(v1, v2):
+    ''' Returns the cross product v1 × v2. '''
+    return Vector(
+        x = v1.y * v2.z - v1.z * v2.y,
+        y = v1.z * v2.x - v1.x * v2.z,
+        z = v1.x * v2.y - v1.y * v2.x,
+    )
+
+def unit_normal(a, b, c):
+    x = Matrix3x3([
+        1, a.y, a.z,
+        1, b.y, b.z,
+        1, c.y, c.z,
+    ]).determinant()
+    y = Matrix3x3([
+        a.x, 1, a.z,
+        b.x, 1, b.z,
+        c.x, 1, c.z,
+    ]).determinant()
+    z = Matrix3x3([
+        a.x, a.y, 1,
+        b.x, b.y, 1,
+        c.x, c.y, 1,
+    ]).determinant()
+    return Vector(x, y, z).normalized()
+
+def pairs(iterable, *, count = 2):
+    '''
+        Iterates the given iterable and returns tuples containing `count`
+        sequential values in the iterable.
+
+        Formally, for a vector A with indices 0…n and pair count k, this
+        function yields:
+            (A₀, A₁, …, Aₖ),
+            (A₁, A₂, …, Aₖ₊₁),
+            …,
+            (Aₙ₋₂, Aₙ₋₁, …, Aₖ₋₂),
+            (Aₙ₋₁, A₀, …, Aₖ₋₁).
+    '''
+    iterable_count = len(iterable)
+    for index in range(iterable_count):
+        yield tuple(
+            iterable[(index + offset) % iterable_count]
+            for offset in range(count)
+        )
 
 class Polygon:
     def __init__(self, vertices):
         self.vertices = vertices
     def __repr__(self):
         return str.format('Polygon({!r})', self.vertices)
+    def area(self):
+        total = Vector(0, 0, 0)
+        for v1, v2 in pairs(self.vertices):
+            total += cross_product(v1, v2)
+        return dot_product(total, unit_normal(self.vertices[0], self.vertices[1], self.vertices[2]))
+    def centroid(self):
+        ...
+        raise NotImplementedError
+    def __getitem__(self, index):
+        return self.vertices[index % len(self.vertices)]
+    def __len__(self):
+        return len(self.vertices)
 
 def is_real(number):
     return isinstance(number, int) or isinstance(number, float)
 
-class TransformationMatrix:
+class Matrix3x3:
     '''
         A 3×3 matrix forming the top-left portion of a full 4×4 transformation
         matrix.
@@ -75,9 +162,19 @@
         assert len(values) == 9
         self.values = values
     def __repr__(self):
-        return str.format('TransformationMatrix({!r})', self.values)
+        return str.format('Matrix3x3({!r})', self.values)
     def __getitem__(self, index):
         return self.values[index]
+    def determinant(self):
+        v = self.values
+        return sum([
+            +(v[0] * v[4] * v[8]),
+            +(v[1] * v[5] * v[6]),
+            +(v[2] * v[3] * v[7]),
+            -(v[2] * v[4] * v[6]),
+            -(v[1] * v[3] * v[8]),
+            -(v[0] * v[5] * v[7]),
+        ])
 
 def complete_matrix(matrix, anchor):
     '''

mercurial