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 ) |