geometry.py

changeset 9
fea8e9ae6f29
parent 7
0ab0d61ccee8
child 12
eb74680a5e43
equal deleted inserted replaced
8:303c51137cb2 9:fea8e9ae6f29
203 return max(lengths) / min(lengths) 203 return max(lengths) / min(lengths)
204 204
205 def is_real(number): 205 def is_real(number):
206 return isinstance(number, int) or isinstance(number, float) 206 return isinstance(number, int) or isinstance(number, float)
207 207
208 def matrix_indices():
209 '''
210 Yields index pairs for matrix iteration
211 '''
212 from itertools import product
213 yield from product(range(3), range(3))
214
208 class Matrix3x3: 215 class Matrix3x3:
209 ''' 216 '''
210 A 3×3 matrix forming the top-left portion of a full 4×4 transformation 217 A 3×3 matrix forming the top-left portion of a full 4×4 transformation
211 matrix. 218 matrix.
212 ''' 219 '''
213 def __init__(self, values): 220 class Row:
214 assert(all(is_real(x) for x in values)) 221 def __init__(self, matrix_ref, i1, i2, i3):
215 assert len(values) == 9 222 self.matrix_ref = matrix_ref
216 self.values = values 223 self.matrix_indices = i1, i2, i3
217 def __repr__(self): 224 def __getitem__(self, row_index):
218 return str.format('Matrix3x3({!r})', self.values) 225 return self.matrix_ref[self.matrix_indices[row_index]]
226 def __setitem__(self, row_index, value):
227 self.matrix_ref[self.matrix_indices[row_index]] = value
228 def __init__(self, values = None):
229 if values is not None:
230 assert(all(is_real(x) for x in values))
231 assert len(values) == 9
232 self.values = values
233 else:
234 self.values = [1, 0, 0, 0, 1, 0, 0, 0, 1]
235 def __repr__(self):
236 if self != Matrix3x3():
237 return str.format('Matrix3x3({!r})', self.values)
238 else:
239 return 'Matrix3x3()'
219 def __getitem__(self, index): 240 def __getitem__(self, index):
220 return self.values[index] 241 if isinstance(index, tuple):
242 assert all(k in [0, 1, 2] for k in index)
243 return self.values[index[0] * 3 + index[1]]
244 else:
245 return Matrix3x3.Row(self, index * 3, index * 3 + 1, index * 3 + 2)
221 def determinant(self): 246 def determinant(self):
222 v = self.values
223 return sum([ 247 return sum([
224 +(v[0] * v[4] * v[8]), 248 +(self[0, 0] * self[1, 1] * self[2, 2]),
225 +(v[1] * v[5] * v[6]), 249 +(self[0, 1] * self[1, 2] * self[2, 0]),
226 +(v[2] * v[3] * v[7]), 250 +(self[0, 2] * self[1, 0] * self[2, 1]),
227 -(v[2] * v[4] * v[6]), 251 -(self[0, 2] * self[1, 1] * self[2, 0]),
228 -(v[1] * v[3] * v[8]), 252 -(self[0, 1] * self[1, 0] * self[2, 2]),
229 -(v[0] * v[5] * v[7]), 253 -(self[0, 0] * self[1, 2] * self[2, 1]),
254 ])
255 @property
256 def scaling_vector(self):
257 '''
258 Extracts scaling factors from this transformation matrix.
259 '''
260 from math import sqrt
261 return Vector(
262 x = sqrt(self[0, 0] ** 2 + self[1, 0] ** 2 + self[2, 0] ** 2),
263 y = sqrt(self[0, 1] ** 2 + self[1, 1] ** 2 + self[2, 1] ** 2),
264 z = sqrt(self[0, 2] ** 2 + self[1, 2] ** 2 + self[2, 2] ** 2),
265 )
266 @property
267 def rotation_component(self):
268 '''
269 Extracts rotation from this matrix.
270 '''
271 return self.split()[0]
272 def split(self):
273 '''
274 Extracts the rotation matrix and scaling vector.
275 '''
276 vec = self.scaling_vector
277 return Matrix3x3([
278 self[i, j] / vec.coordinates[j]
279 for i, j in matrix_indices()
280 ]), vec
281 @property
282 def contains_scaling(self):
283 '''
284 Returns whether this matrix contains scaling factors.
285 '''
286 vec = self.scaling_vector
287 return abs((vec.x * vec.y * vec.z) - 1) >= 1e-10
288 @property
289 def contains_rotation(self):
290 '''
291 Returns whether this matrix contains rotation factors.
292 '''
293 return self.rotation_component != Matrix3x3()
294 def __eq__(self, other):
295 '''
296 Returns whether two matrices are equivalent.
297 '''
298 return all(
299 abs(self[(i, j)] - other[(i, j)]) < 1e-10
300 for i, j in matrix_indices()
301 )
302 def __matmul__(self, other):
303 '''
304 Computes the matrix multiplication self × other.
305 '''
306 return Matrix3x3([
307 sum(self[i, k] * other[k, j] for k in range(3))
308 for i, j in matrix_indices()
230 ]) 309 ])
231 310
232 def complete_matrix(matrix, anchor): 311 def complete_matrix(matrix, anchor):
233 ''' 312 '''
234 Combines a 3×3 matrix and an anchor vertex into a full 4×4 313 Combines a 3×3 matrix and an anchor vertex into a full 4×4
235 transformation matrix. 314 transformation matrix.
236 ''' 315 '''
237 return [ 316 return [
238 matrix[0], matrix[1], matrix[2], anchor.x, 317 matrix[0, 0], matrix[0, 1], matrix[0, 2], anchor.x,
239 matrix[3], matrix[4], matrix[5], anchor.y, 318 matrix[1, 0], matrix[1, 1], matrix[1, 2], anchor.y,
240 matrix[6], matrix[7], matrix[8], anchor.z, 319 matrix[2, 0], matrix[2, 1], matrix[2, 2], anchor.z,
241 0, 0, 0, 1, 320 0, 0, 0, 1,
242 ] 321 ]
243 322
244 def transform(vertex, transformation_matrix): 323 def transform(vertex, transformation_matrix):
245 ''' 324 '''
246 Transforms a vertex by a 4×4 transformation matrix. 325 Transforms a vertex by a 4×4 transformation matrix.
247 ''' 326 '''
248 u = transformation_matrix[0] * vertex.x \ 327 return Vertex(
249 + transformation_matrix[1] * vertex.y \ 328 x = transformation_matrix[0] * vertex.x \
250 + transformation_matrix[2] * vertex.z \ 329 + transformation_matrix[1] * vertex.y \
251 + transformation_matrix[3] 330 + transformation_matrix[2] * vertex.z \
252 v = transformation_matrix[4] * vertex.x \ 331 + transformation_matrix[3],
253 + transformation_matrix[5] * vertex.y \ 332 y = transformation_matrix[4] * vertex.x \
254 + transformation_matrix[6] * vertex.z \ 333 + transformation_matrix[5] * vertex.y \
255 + transformation_matrix[7] 334 + transformation_matrix[6] * vertex.z \
256 w = transformation_matrix[8] * vertex.x \ 335 + transformation_matrix[7],
257 + transformation_matrix[9] * vertex.y \ 336 z = transformation_matrix[8] * vertex.x \
258 + transformation_matrix[10] * vertex.z \ 337 + transformation_matrix[9] * vertex.y \
259 + transformation_matrix[11] 338 + transformation_matrix[10] * vertex.z \
260 return Vertex(u, v, w) 339 + transformation_matrix[11],
340 )

mercurial