geometry.py

changeset 7
0ab0d61ccee8
parent 6
6da1e81c5652
child 9
fea8e9ae6f29
equal deleted inserted replaced
6:6da1e81c5652 7:0ab0d61ccee8
1 def degree_rep(angle):
2 from math import degrees
3 return '∠ %.2f°' % degrees(angle)
4
5 def position_vector(vertex):
6 assert isinstance(vertex, Vertex)
7 return Vector(*vertex.coordinates)
8
9 def angle_magnitude_key(angle):
10 '''
11 Returns how great an angle is.
12 '''
13 from math import pi as π
14 normalized_angle = abs(angle) % (2 * π)
15 if normalized_angle > π:
16 normalized_angle = (2 * π) - normalized_angle
17 return normalized_angle
18
1 class Vertex: 19 class Vertex:
2 def __init__(self, x, y, z): 20 def __init__(self, x, y, z):
3 if not all(is_real(coordinate) for coordinate in (x, y, z)): 21 if not all(is_real(coordinate) for coordinate in (x, y, z)):
4 raise ValueError(str.format('Bad vertex coordinates: {!r}', 22 raise ValueError(str.format('Bad vertex coordinates: {!r}',
5 (x, y, z), 23 (x, y, z),
17 ) 35 )
18 @property 36 @property
19 def coordinates(self): 37 def coordinates(self):
20 return self.x, self.y, self.z 38 return self.x, self.y, self.z
21 def __add__(self, other): 39 def __add__(self, other):
22 return type(self)(self.x + other.x, self.y + other.y, self.z + other.z) 40 assert not (type(self) == type(other) == Vertex)
41 if type(self) == Vector and type(other) == Vector:
42 result_type = Vector
43 else:
44 result_type = Vertex
45 return result_type(self.x + other.x, self.y + other.y, self.z + other.z)
46 def __radd__(self, other):
47 return self + other
23 def __neg__(self): 48 def __neg__(self):
24 return type(self)(-self.x, -self.y, -self.z) 49 return type(self)(-self.x, -self.y, -self.z)
25 def __sub__(self, other): 50 def __sub__(self, other):
26 return self + (-other) 51 result = self + (-position_vector(other))
52 if isinstance(other, Vertex):
53 return Vector(*result.coordinates)
54 else:
55 return result
27 def __mul__(self, scalar): 56 def __mul__(self, scalar):
57 assert is_real(scalar)
28 return type(self)(self.x * scalar, self.y * scalar, self.z * scalar) 58 return type(self)(self.x * scalar, self.y * scalar, self.z * scalar)
29 def __rmul__(self, other): 59 def __rmul__(self, other):
30 return self * other 60 return self * other
31 def __truediv__(self, scalar): 61 def __truediv__(self, scalar):
62 assert is_real(scalar)
32 return type(self)(self.x / scalar, self.y / scalar, self.z / scalar) 63 return type(self)(self.x / scalar, self.y / scalar, self.z / scalar)
33 def __floordiv__(self, scalar): 64 def __floordiv__(self, scalar):
65 assert is_real(scalar)
34 return type(self)(self.x // scalar, self.y // scalar, self.z // scalar) 66 return type(self)(self.x // scalar, self.y // scalar, self.z // scalar)
35 def __matmul__(self, transformation_matrix): 67 def __matmul__(self, transformation_matrix):
36 return transform(self, transformation_matrix) 68 return transform(self, transformation_matrix)
37 def __eq__(self, other): 69 def __eq__(self, other):
38 return self.coordinates == other.coordinates 70 return self.coordinates == other.coordinates
51 def __init__(self, v1, v2): 83 def __init__(self, v1, v2):
52 self.v1, self.v2 = v1, v2 84 self.v1, self.v2 = v1, v2
53 def __repr__(self): 85 def __repr__(self):
54 return str.format('LineSegment({!r}, {!r})', self.v1, self.v2) 86 return str.format('LineSegment({!r}, {!r})', self.v1, self.v2)
55 @property 87 @property
88 def length(self):
89 return self.v1.distance_to(self.v2)
90 @property
56 def vertices(self): 91 def vertices(self):
57 return self.v1, self.v2 92 return self.v1, self.v2
58 def __len__(self): 93 def __len__(self):
59 return 2 94 return 2
60 def __getitem__(self, index): 95 def __getitem__(self, index):
68 103
69 class Vector(Vertex): 104 class Vector(Vertex):
70 def __init__(self, x, y, z): 105 def __init__(self, x, y, z):
71 super().__init__(x, y, z) 106 super().__init__(x, y, z)
72 def __repr__(self): 107 def __repr__(self):
73 return str.format('Vector({!r}, {!r}. {!r})', self.x, self.y, self.z) 108 return str.format('Vector({!r}, {!r}, {!r})', self.x, self.y, self.z)
74 def length(self): 109 def length(self):
75 from math import sqrt 110 from math import sqrt
76 return sqrt(self.x ** 2 + self.y ** 2 + self.z ** 2) 111 return sqrt(self.x ** 2 + self.y ** 2 + self.z ** 2)
77 def normalized(self): 112 def normalized(self):
78 length = self.length() 113 length = self.length()
146 raise NotImplementedError 181 raise NotImplementedError
147 def __getitem__(self, index): 182 def __getitem__(self, index):
148 return self.vertices[index % len(self.vertices)] 183 return self.vertices[index % len(self.vertices)]
149 def __len__(self): 184 def __len__(self):
150 return len(self.vertices) 185 return len(self.vertices)
186 @property
187 def perimeter_lines(self):
188 for v1, v2 in pairs(self.vertices):
189 yield LineSegment(v1, v2)
190 @property
191 def smallest_angle(self):
192 from math import acos, inf
193 min_angle = inf
194 for v1, v2, v3 in pairs(self.vertices, count = 3):
195 vec1 = (position_vector(v3) - position_vector(v2)).normalized()
196 vec2 = (position_vector(v1) - position_vector(v2)).normalized()
197 cosine = dot_product(vec1, vec2) / vec1.length() / vec2.length()
198 min_angle = min(min_angle, angle_magnitude_key(acos(cosine)))
199 return min_angle
200 @property
201 def hairline_ratio(self):
202 lengths = [line.length for line in self.perimeter_lines]
203 return max(lengths) / min(lengths)
151 204
152 def is_real(number): 205 def is_real(number):
153 return isinstance(number, int) or isinstance(number, float) 206 return isinstance(number, int) or isinstance(number, float)
154 207
155 class Matrix3x3: 208 class Matrix3x3:

mercurial