Package gameobjects :: Module matrix44
[hide private]
[frames] | no frames]

Source Code for Module gameobjects.matrix44

   1   
   2  from util import format_number 
   3  from vector3 import Vector3 
   4   
   5  from math import sin, cos, tan, sqrt, pi, radians 
   6   
   7  #import psyco 
   8  #psyco.full() 
   9   
10 -class Matrix44Error(Exception):
11 12 """Matrix44 Exception class""" 13
14 - def __init__(self, code, description):
15 Exception.__init__(self) 16 self.code = code 17 self.description = description
18
19 - def __str__(self):
20 return "%s (%s)" % (self.description, self.code)
21 22
23 -class Row(tuple):
24 25 """Represents the contents of a row when accessed through a property. 26 27 """ 28 29 _type_error = "A vector type must be used to do math on rows" 30 31
32 - def __add__(self, rhs):
33 try: 34 return rhs.__radd__(self[:rhs._gameobjects_vector]) 35 except AttributeError: 36 raise TypeError(self._type_error)
37
38 - def __sub__(self, rhs):
39 try: 40 return rhs.__sub__(self[:rhs._gameobjects_vector]) 41 except AttributeError: 42 raise TypeError(self._type_error)
43
44 - def __mul__(self, rhs):
45 try: 46 return rhs.__mul__(self[:rhs._gameobjects_vector]) 47 except AttributeError: 48 raise TypeError(self._type_error)
49
50 - def __div__(self, rhs):
51 try: 52 return rhs.__div__(self[:rhs._gameobjects_vector]) 53 except AttributeError: 54 raise TypeError(self._type_error)
55 56
57 - def as_vec3(self):
58 """Shorthand for converting to a vector3.""" 59 return Vector3._from_float_sequence(self)
60 61
62 -class Matrix44(object):
63 64 _identity = ( (1.0, 0.0, 0.0, 0.0), 65 (0.0, 1.0, 0.0, 0.0), 66 (0.0, 0.0, 1.0, 0.0), 67 (0.0, 0.0, 0.0, 1.0) ) 68 69 __slots__ = ('_m',) 70
71 - def __init__(self, *args):
72 73 """If no parameteres are given, the Matrix44 is initialised to identity. 74 If 1 parameter is given it should be an iterable with the 16 components 75 of the matrix. 76 If 4 parameters are given they should be 4 sequences of up to 4 values. 77 Missing values in each row are padded out with values from the identity matix 78 (so you can use Vector3's or tuples of 3 values). 79 80 """ 81 82 83 if not args: 84 self._m = [1.,0.,0.,0., 0.,1.,0.,0., 0.,0.,1.,0., 0.,0.,0.,1.] 85 return 86 87 88 elif len(args) == 4: 89 self._m = [1.,0.,0.,0., 0.,1.,0.,0., 0.,0.,1.,0., 0.,0.,0.,1.] 90 91 row_0, row_1, row_2, row_3 = self._setters 92 r1, r2, r3, r4 = args 93 94 row_0(self, r1) 95 row_1(self, r2) 96 row_2(self, r3) 97 row_3(self, r4) 98 99 else: 100 raise TypeError("Matrix44.__init__() takes 0, or 4 arguments (%i given)"%len(args))
101 102
103 - def _get_row_0(self):
104 return Row(self._m[0:4])
105
106 - def _get_row_1(self):
107 return Row(self._m[4:8])
108
109 - def _get_row_2(self):
110 return Row(self._m[8:12])
111
112 - def _get_row_3(self):
113 return Row(self._m[12:16])
114
115 - def _set_row_0(self, values):
116 values = tuple(values)[:4] 117 self._m[0:len(values)] = map(float, values)
118
119 - def _set_row_1(self, values):
120 values = tuple(values)[:4] 121 self._m[4:4+len(values)] = map(float, values)
122
123 - def _set_row_2(self, values):
124 values = tuple(values)[:4] 125 self._m[8:8+len(values)] = map(float, values)
126
127 - def _set_row_3(self, values):
128 values = tuple(values)[:4] 129 self._m[12:12+len(values)] = map(float, values)
130 131 _getters = (_get_row_0, _get_row_1, _get_row_2, _get_row_3) 132 _setters = (_set_row_0, _set_row_1, _set_row_2, _set_row_3) 133 134 _row0 = property(_get_row_0, _set_row_0, None, "Row 0") 135 _row1 = property(_get_row_1, _set_row_1, None, "Row 1") 136 _row2 = property(_get_row_2, _set_row_2, None, "Row 2") 137 _row3 = property(_get_row_3, _set_row_3, None, "Row 3") 138 139 x_axis = _row0 140 right = _row0 141 y_axis = _row1 142 up = _row1 143 z_axis = _row2 144 forward = _row2 145 translate = _row3 146 147
148 - def to_opengl(self):
149 150 """Converts the matrix in to a list of values, suitable for using 151 with glLoadMatrix* 152 153 """ 154 155 return self._m[:]
156 157
158 - def set(self, row1, row2, row3, row4):
159 160 """Sets all four rows of the matrix, 161 162 @type row1: sequence 163 @param row1: First row 164 @type row2: sequence 165 @param row2: Second row 166 @type row3: sequence 167 @param row3: Third row 168 @type row4: sequence 169 @param row4: Fourth row 170 @return: None 171 172 """ 173 174 set_row_0, set_row_1, set_row_2, set_row_3 = self._setters 175 176 set_row_0(self, row1) 177 set_row_1(self, row2) 178 set_row_2(self, row3) 179 set_row_3(self, row4)
180 181
182 - def get_row(self, row_no):
183 """Gets a row of the matrix as a tuple 184 185 row_no -- Index of row 186 187 """ 188 try: 189 return self._getters[row_no](self) 190 except IndexError: 191 raise IndexError("Row must be 0, 1, 2 or 3")
192 193 194 @classmethod
195 - def from_iter(cls, iterable):
196 """Creates a Matrix44 from an iterable of 16 values.""" 197 198 m = cls.__new__(cls, object) 199 m._m = map(float, iterable) 200 if len(m._m) != 16: 201 raise ValueError("Iterable must have 16 values") 202 return m
203 204 205 @classmethod
206 - def clone(cls, copy_Matrix44):
207 """Creates a Matrix44 that is a copy of another.""" 208 209 m = cls.__new__(cls, object) 210 m._m = copy_Matrix44._m[:] 211 return m
212 213 214 @classmethod
215 - def blank(cls):
216 """Creates a blank Matrix44 (with no information). This is rarely 217 required, you may want to use an identity Matrix44, 218 see Matrix44.identity() 219 220 """ 221 222 m = cls.__new__(cls, object) 223 m._m = [0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,] 224 return m
225 226 227 @classmethod
228 - def identity(cls):
229 """Creates and identity Matrix44.""" 230 231 m = cls.__new__(cls, object) 232 m._m = [1., 0., 0., 0., 0., 1., 0., 0., 0., 0., 1., 0., 0., 0., 0., 1.] 233 return m
234 235 236 @classmethod
237 - def scale(cls, scale_x, scale_y= None, scale_z= None):
238 """Creates a scale Matrix44. 239 If one parameter is given the scale is uniform, 240 if three parameters are give the scale is different (potentialy) on each x axis. 241 242 """ 243 244 m = cls.__new__(cls, object) 245 return m.make_scale(scale_x, scale_y, scale_z)
246 247 248 @classmethod
249 - def translation(cls, x, y, z):
250 """Creates a translation Matrix44 to (x, y, z). 251 252 x -- X Coordinate 253 y -- Y Coordinate 254 z -- Z Coordinate 255 256 """ 257 258 m = cls.__new__(cls, object) 259 return m.make_translation(x, y, z)
260 261 262 @classmethod
263 - def x_rotation(cls, angle):
264 """Creates a Matrix44 that does a rotation about the x axis. 265 266 angle -- Angle of rotation (in radians) 267 268 """ 269 270 m = cls.__new__(cls, object) 271 return m.make_x_rotation(angle)
272 273 274 @classmethod
275 - def y_rotation(cls, angle):
276 """Creates a Matrix44 that does a rotation about the y axis. 277 278 angle -- Angle of rotation (in radians) 279 280 """ 281 282 m = cls.__new__(cls, object) 283 return m.make_y_rotation(angle)
284 285 286 @classmethod
287 - def z_rotation(cls, angle):
288 """Creates a Matrix44 that does a rotation about the z axis. 289 290 angle -- Angle of rotation (in radians) 291 292 """ 293 294 m = cls.__new__(cls, object) 295 return m.make_z_rotation(angle)
296 297 298 @classmethod
299 - def rotation_about_axis(cls, axis, angle):
300 """Creates a Matrix44 that does a rotation about an axis. 301 302 axis -- A vector of the axis 303 angle -- Angle of rotation 304 305 """ 306 307 m = cls.__new__(cls, object) 308 return m.make_rotation_about_axis(axis, angle)
309 310 311 @classmethod
312 - def xyz_rotation(cls, angle_x, angle_y, angle_z):
313 """Creates a Matrix44 that does a rotation about each axis. 314 315 angle_x -- Angle of rotation, about x 316 angle_y -- Angle of rotation, about y 317 angle_z -- Angle of rotation, about z 318 319 """ 320 321 m = cls.__new__(cls, object) 322 return m.make_xyz_rotation(angle_x, angle_y, angle_z)
323 324 325 @classmethod
326 - def perspective_projection(cls, left, right, top, bottom, near, far):
327 """Creates a Matrix44 that projects points in to 2d space. 328 329 left -- Coordinate of left of screen 330 right -- Coordination of right of screen 331 top -- Coordination of the top of the screen 332 bottom -- Coordination of the borrom of the screen 333 near -- Coordination of the near clipping plane 334 far -- Coordinate of the far clipping plane 335 336 """ 337 338 m = cls.__new__(cls, object) 339 return m.make_perspective_projection( left, 340 right, 341 top, 342 bottom, 343 near, 344 far)
345 346 347 @classmethod
348 - def perspective_projection_fov(cls, fov, aspect, near, far):
349 """Creates a Matrix44 that projects points in to 2d space 350 351 fov -- The field of view (in radians) 352 aspect -- The aspect ratio of the screen (width / height) 353 near -- Coordinate of the near clipping plane 354 far -- Coordinate of the far clipping plane 355 356 """ 357 358 m = cls.__new__(cls, object) 359 return m.make_perspective_projection_fov(fov, aspect, near, far)
360 361
362 - def __str__(self):
363 """'Pretty' formatting of the Matrix44.""" 364 365 # Pretty printing generates a lot of ugly code - oh the irony! 366 # Changed in release 0.0.3 so that the decimal point is always 367 # line up on the columns 368 369 cols = [ map(format_number, col) for col in self.columns() ] 370 371 def decimal_pos(n): 372 if '.' in n: 373 return n.index('.') 374 return len(n)
375 376 for col_no, col in enumerate(cols[:]): 377 378 decimal = max( decimal_pos(format_number(c)) for c in col ) 379 cols[col_no] = [" "*(decimal-decimal_pos(c))+c for c in col] 380 381 max_col_lengths = [ max(len(c) for c in col) for col in cols ] 382 rows = [[], [], [], []] 383 for row_no in xrange(4): 384 385 for col_no in xrange(4): 386 387 v = cols[col_no][row_no] 388 rows[row_no].append( v.ljust(max_col_lengths[col_no]) ) 389 390 rows = [" ".join(row).rstrip() for row in rows] 391 return "\n".join("[ %s ]"%row for row in rows) 392 393 return str(cols)
394 395
396 - def __repr__(self):
397 398 def format_row(row): 399 return "(%s)" % ", ".join( format_number(value) for value in row )
400 401 return "Matrix44(%s)" % \ 402 ", ".join(format_row(row) for row in self.rows()) 403 404
405 - def __hash__(self):
406 407 """Allows matrices to be used as keys in a dictionary.""" 408 409 return hash(tuple(self._m))
410 411
412 - def __setitem__(self, coord, value):
413 """Sets an individual element in the Matrix44. 414 coord is a tuple of (row, column) 415 416 eg. Matrix44[2,3] = 3. 417 418 """ 419 420 try: 421 row, col = coord 422 self._m[row * 4 + col] = 1.0 * value 423 except IndexError: 424 raise IndexError( "Row and Column should be 0, 1, 2 or 3" ) 425 except TypeError: 426 raise TypeError( "Must be a number" )
427 428
429 - def __getitem__(self, coord):
430 """Gets an individual element in the Matrix44. 431 coord is a tuple of (row, column) 432 433 eg. print Matrix44[2,3] 434 435 """ 436 437 try: 438 row, col = coord 439 return self._m[row * 4 + col] 440 except IndexError: 441 raise IndexError( "Row and Column should be 0, 1, 2 or 3" ) 442 except TypeError: 443 raise TypeError( "index should be two values containing"\ 444 " the row and column" )
445 446
447 - def __iter__(self):
448 """Iterates over all 16 values in the Matrix44.""" 449 450 return iter(self._m[:])
451 452
453 - def __neg__(self):
454 """Returns the inverse of the matrix.""" 455 456 return self.get_inverse()
457 458
459 - def __mul__(self, rhs):
460 """Returns the result of multiplying this Matrix44 by another, called 461 by the * (multiply) operator.""" 462 463 m1_0, m1_1, m1_2, m1_3, \ 464 m1_4, m1_5, m1_6, m1_7, \ 465 m1_8, m1_9, m1_10, m1_11, \ 466 m1_12, m1_13, m1_14, m1_15 = self._m 467 468 m2_0, m2_1, m2_2, m2_3, \ 469 m2_4, m2_5, m2_6, m2_7, \ 470 m2_8, m2_9, m2_10, m2_11, \ 471 m2_12, m2_13, m2_14, m2_15 = rhs._m 472 473 retm = [ m2_0 * m1_0 + m2_1 * m1_4 + m2_2 * m1_8 + m2_3 * m1_12, 474 m2_0 * m1_1 + m2_1 * m1_5 + m2_2 * m1_9 + m2_3 * m1_13, 475 m2_0 * m1_2 + m2_1 * m1_6 + m2_2 * m1_10 + m2_3 * m1_14, 476 m2_0 * m1_3 + m2_1 * m1_7 + m2_2 * m1_11 + m2_3 * m1_15, 477 478 m2_4 * m1_0 + m2_5 * m1_4 + m2_6 * m1_8 + m2_7 * m1_12, 479 m2_4 * m1_1 + m2_5 * m1_5 + m2_6 * m1_9 + m2_7 * m1_13, 480 m2_4 * m1_2 + m2_5 * m1_6 + m2_6 * m1_10 + m2_7 * m1_14, 481 m2_4 * m1_3 + m2_5 * m1_7 + m2_6 * m1_11 + m2_7 * m1_15, 482 483 m2_8 * m1_0 + m2_9 * m1_4 + m2_10 * m1_8 + m2_11 * m1_12, 484 m2_8 * m1_1 + m2_9 * m1_5 + m2_10 * m1_9 + m2_11 * m1_13, 485 m2_8 * m1_2 + m2_9 * m1_6 + m2_10 * m1_10 + m2_11 * m1_14, 486 m2_8 * m1_3 + m2_9 * m1_7 + m2_10 * m1_11 + m2_11 * m1_15, 487 488 m2_12 * m1_0 + m2_13 * m1_4 + m2_14 * m1_8 + m2_15 * m1_12, 489 m2_12 * m1_1 + m2_13 * m1_5 + m2_14 * m1_9 + m2_15 * m1_13, 490 m2_12 * m1_2 + m2_13 * m1_6 + m2_14 * m1_10 + m2_15 * m1_14, 491 m2_12 * m1_3 + m2_13 * m1_7 + m2_14 * m1_11 + m2_15 * m1_15 ] 492 493 ret = self.__new__(self.__class__, object) 494 ret._m = retm 495 496 return ret
497 498
499 - def __imul__(self, rhs):
500 501 """Multiplies this Matrix44 by another, called by the *= operator.""" 502 503 m1_0, m1_1, m1_2, m1_3, \ 504 m1_4, m1_5, m1_6, m1_7, \ 505 m1_8, m1_9, m1_10, m1_11, \ 506 m1_12, m1_13, m1_14, m1_15 = self._m 507 508 m2_0, m2_1, m2_2, m2_3, \ 509 m2_4, m2_5, m2_6, m2_7, \ 510 m2_8, m2_9, m2_10, m2_11, \ 511 m2_12, m2_13, m2_14, m2_15 = rhs._m 512 513 514 self._m = [ m2_0 * m1_0 + m2_1 * m1_4 + m2_2 * m1_8 + m2_3 * m1_12, 515 m2_0 * m1_1 + m2_1 * m1_5 + m2_2 * m1_9 + m2_3 * m1_13, 516 m2_0 * m1_2 + m2_1 * m1_6 + m2_2 * m1_10 + m2_3 * m1_14, 517 m2_0 * m1_3 + m2_1 * m1_7 + m2_2 * m1_11 + m2_3 * m1_15, 518 519 m2_4 * m1_0 + m2_5 * m1_4 + m2_6 * m1_8 + m2_7 * m1_12, 520 m2_4 * m1_1 + m2_5 * m1_5 + m2_6 * m1_9 + m2_7 * m1_13, 521 m2_4 * m1_2 + m2_5 * m1_6 + m2_6 * m1_10 + m2_7 * m1_14, 522 m2_4 * m1_3 + m2_5 * m1_7 + m2_6 * m1_11 + m2_7 * m1_15, 523 524 m2_8 * m1_0 + m2_9 * m1_4 + m2_10 * m1_8 + m2_11 * m1_12, 525 m2_8 * m1_1 + m2_9 * m1_5 + m2_10 * m1_9 + m2_11 * m1_13, 526 m2_8 * m1_2 + m2_9 * m1_6 + m2_10 * m1_10 + m2_11 * m1_14, 527 m2_8 * m1_3 + m2_9 * m1_7 + m2_10 * m1_11 + m2_11 * m1_15, 528 529 m2_12 * m1_0 + m2_13 * m1_4 + m2_14 * m1_8 + m2_15 * m1_12, 530 m2_12 * m1_1 + m2_13 * m1_5 + m2_14 * m1_9 + m2_15 * m1_13, 531 m2_12 * m1_2 + m2_13 * m1_6 + m2_14 * m1_10 + m2_15 * m1_14, 532 m2_12 * m1_3 + m2_13 * m1_7 + m2_14 * m1_11 + m2_15 * m1_15] 533 534 return self
535
536 - def fast_mul(self, rhs):
537 538 """Multiplies this matrix by another. Assumes that both matrices have 539 a right column of (0, 0, 0, 1). This is true for matrices composed 540 of rotations, translations and scales. fast_mul is approximately 25% 541 quicker than the *= operator. 542 543 rhs -- A matrix 544 545 """ 546 547 m1_0, m1_1, m1_2, m1_3, \ 548 m1_4, m1_5, m1_6, m1_7, \ 549 m1_8, m1_9, m1_10, m1_11, \ 550 m1_12, m1_13, m1_14, m1_15 = self._m 551 552 m2_0, m2_1, m2_2, m2_3, \ 553 m2_4, m2_5, m2_6, m2_7, \ 554 m2_8, m2_9, m2_10, m2_11, \ 555 m2_12, m2_13, m2_14, m2_15 = rhs._m 556 557 558 self._m = [ m2_0 * m1_0 + m2_1 * m1_4 + m2_2 * m1_8, 559 m2_0 * m1_1 + m2_1 * m1_5 + m2_2 * m1_9, 560 m2_0 * m1_2 + m2_1 * m1_6 + m2_2 * m1_10, 561 0.0, 562 563 m2_4 * m1_0 + m2_5 * m1_4 + m2_6 * m1_8, 564 m2_4 * m1_1 + m2_5 * m1_5 + m2_6 * m1_9, 565 m2_4 * m1_2 + m2_5 * m1_6 + m2_6 * m1_10, 566 0.0, 567 568 m2_8 * m1_0 + m2_9 * m1_4 + m2_10 * m1_8, 569 m2_8 * m1_1 + m2_9 * m1_5 + m2_10 * m1_9, 570 m2_8 * m1_2 + m2_9 * m1_6 + m2_10 * m1_10, 571 0.0, 572 573 m2_12 * m1_0 + m2_13 * m1_4 + m2_14 * m1_8 + m1_12, 574 m2_12 * m1_1 + m2_13 * m1_5 + m2_14 * m1_9 + m1_13, 575 m2_12 * m1_2 + m2_13 * m1_6 + m2_14 * m1_10 + m1_14, 576 1.0 ] 577 578 return self
579
580 - def copy(self):
581 """Returns a copy of this matrix.""" 582 return self.clone(self)
583 __copy__ = copy 584 585
586 - def components(self):
587 """Returns an iterator for the components in the Matrix44. ie 588 returns all 16 values.""" 589 590 return iter(self._m[:])
591 592
593 - def transposed_components(self):
594 """Returns an iterator for the components in the Matrix44 in 595 transposed order.""" 596 597 m00, m01, m02, m03, \ 598 m10, m11, m12, m13, \ 599 m20, m21, m22, m23, \ 600 m30, m31, m32, m33 = self._m 601 602 return iter( ( m00, m10, m20, m30, 603 m01, m11, m21, m31, 604 m02, m12, m22, m32, 605 m03, m13, m23, m33 ) )
606 607
608 - def rows(self):
609 """Returns an iterator for the rows in the Matrix44 (yields 4 tuples 610 of 4 values).""" 611 612 m = self._m 613 return iter(( tuple(m[0:4]), 614 tuple(m[4:8]), 615 tuple(m[8:12]), 616 tuple(m[12:16]) ))
617 618 619
620 - def columns(self):
621 """Returns an iterator for the columns in the Matrix44 (yields 4 622 tuples of 4 values).""" 623 624 col = self.get_column 625 return iter((col(0), col(1), col(2), col(3)))
626 627 628
629 - def get_row_vec3(self, row_no):
630 """Returns a Vector3 for a given row. 631 632 row_no -- The row index 633 634 """ 635 636 try: 637 r = row_no*4 638 x, y, z = self._m[r:r+3] 639 return Vector3.from_floats(x, y, z) 640 except IndexError: 641 raise IndexError( "Row and Column should be 0, 1, 2 or 3" )
642 643
644 - def get_column(self, col_no):
645 """Returns a column as a tuple of 4 values. 646 647 col_no -- The column index 648 649 """ 650 651 try: 652 m = self._m 653 return ( m[col_no], 654 m[col_no+4], 655 m[col_no+8], 656 m[col_no+12] ) 657 except IndexError: 658 raise IndexError( "Column should be 0, 1, 2 or 3" )
659 660
661 - def set_row(self, row_no, row):
662 """Sets the values in a row. 663 664 row_no -- The index of the row 665 row -- An container containing the new values 666 667 """ 668 669 try: 670 self._setters[row_no](self, row) 671 except IndexError: 672 raise IndexError( "Row should be 0, 1, 2 or 3" )
673 674
675 - def set_column(self, col_no, col):
676 """Sets the values in a column. 677 678 col_no -- The index of the column 679 col -- An sequence of 4 values 680 681 """ 682 683 try: 684 col_iter = iter(col) 685 m = self._m 686 a, b, c, d = col 687 m[col_no] = float(a) 688 m[col_no+4] = float(b) 689 m[col_no+8] = float(c) 690 m[col_no+12] = float(d) 691 692 except IndexError: 693 raise IndexError( "Column should be 0, 1, 2 or 3" )
694 695
696 - def transform_vec3(self, v):
697 """Transforms a vector and returns the result as a Vector3. 698 699 v -- Vector to transform 700 701 """ 702 703 m = self._m 704 x, y, z = v 705 return Vector3.from_floats( x * m[0] + y * m[4] + z * m[8] + m[12], 706 x * m[1] + y * m[5] + z * m[9] + m[13], 707 x * m[2] + y * m[6] + z * m[10] + m[14] )
708
709 - def transform(self, v):
710 """Transforms a Vector3 and returns the result as a tuple. 711 712 v -- Vector to transform 713 714 """ 715 716 m = self._m 717 x, y, z = v 718 return ( x * m[0] + y * m[4] + z * m[8] + m[12], 719 x * m[1] + y * m[5] + z * m[9] + m[13], 720 x * m[2] + y * m[6] + z * m[10] + m[14] )
721 722
723 - def transform4(self, v):
724 """Transforms a 4d vector and returns the result as a tuple. 725 726 v -- Vector to transform 727 728 """ 729 730 m = self._m 731 x, y, z, w = v 732 return ( x * m[0] + y * m[4] + z * m[8] + w * m[12], 733 x * m[1] + y * m[5] + z * m[9] + w * m[13], 734 x * m[2] + y * m[6] + z * m[10] + w * m[14] )
735 736
737 - def transform_sequence(self, points):
738 739 m_0, m_1, m_2, m_3, \ 740 m_4, m_5, m_6, m_7, \ 741 m_8, m_9, m_10, m_11, \ 742 m_12, m_13, m_14, m_15 = self._m 743 744 return [ ( x * m_0 + y * m_4 + z * m_8 + m_12, 745 x * m_1 + y * m_5 + z * m_9 + m_13, 746 x * m_2 + y * m_6 + z * m_10 + m_14 ) 747 for x,y,z in points ]
748 749
750 - def transform_sequence_vec3(self, points):
751 752 m_0, m_1, m_2, m_3, \ 753 m_4, m_5, m_6, m_7, \ 754 m_8, m_9, m_10, m_11, \ 755 m_12, m_13, m_14, m_15 = self._m 756 757 return [ Vector3.from_floats 758 ( x * m_0 + y * m_4 + z * m_8 + m_12, 759 x * m_1 + y * m_5 + z * m_9 + m_13, 760 x * m_2 + y * m_6 + z * m_10 + m_14 ) 761 for x,y,z in points ]
762 763
764 - def iter_transform_vec3(self, points):
765 766 """Transforms a sequence of points, and yields the result as Vector3s 767 768 points -- A sequence of vectors 769 770 """ 771 772 m_0, m_1, m_2, m_3, \ 773 m_4, m_5, m_6, m_7, \ 774 m_8, m_9, m_10, m_11, \ 775 m_12, m_13, m_14, m_15 = self._m 776 777 for x, y, z in points: 778 779 yield Vector3.from_floats( x * m_0 + y * m_4 + z * m_8 + m_12, 780 x * m_1 + y * m_5 + z * m_9 + m_13, 781 x * m_2 + y * m_6 + z * m_10 + m_14 )
782 783
784 - def iter_transform(self, points):
785 786 """Transforms a sequence of points and yields the result as tuples. 787 788 points -- A sequence of vectors 789 790 """ 791 792 m_0, m_1, m_2, m_3, \ 793 m_4, m_5, m_6, m_7, \ 794 m_8, m_9, m_10, m_11, \ 795 m_12, m_13, m_14, m_15 = self._m 796 797 for x, y, z in points: 798 799 yield ( x * m_0 + y * m_4 + z * m_8 + m_12, 800 x * m_1 + y * m_5 + z * m_9 + m_13, 801 x * m_2 + y * m_6 + z * m_10 + m_14 )
802 803 iter_transform3 = iter_transform 804 805
806 - def iter_transform4(self, points):
807 808 """Transforms a sequence of points and yields the result as tuples. 809 810 points -- A sequence of vectors 811 812 """ 813 814 m_0, m_1, m_2, m_3, \ 815 m_4, m_5, m_6, m_7, \ 816 m_8, m_9, m_10, m_11, \ 817 m_12, m_13, m_14, m_15 = self._m 818 819 for x, y, z, w in points: 820 821 yield ( x * m_0 + y * m_4 + z * m_8 + w * m_12, 822 x * m_1 + y * m_5 + z * m_9 + w * m_13, 823 x * m_2 + y * m_6 + z * m_10 + w * m_14 )
824 825
826 - def rotate_vec3(self, v):
827 """Rotates a Vector3 and returns the result. 828 The translation part of the Matrix44 is ignored. 829 830 v -- Vector to rotate 831 832 """ 833 834 m = self._m 835 x, y, z = v 836 return Vector3.from_floats( x * m[0] + y * m[4] + z * m[8], 837 x * m[1] + y * m[5] + z * m[9], 838 x * m[2] + y * m[6] + z * m[10] )
839 840
841 - def rotate(self, v):
842 """Rotates a Vector3 and returns the result as a tuple 843 The translation part of the Matrix44 is ignored. 844 845 v -- Vector to rotate 846 847 """ 848 849 m = self._m 850 x, y, z = v 851 return ( x * m[0] + y * m[4] + z * m[8], 852 x * m[1] + y * m[5] + z * m[9], 853 x * m[2] + y * m[6] + z * m[10] )
854 855
856 - def inverse_transform(self, v):
857 """Inverse trasforms a Vector3 and returns the result. 858 Warning: This is expensive, pre-calculate an inverse Matrix44 if you 859 can. 860 861 v -- Vector to transform 862 863 """ 864 865 return self.get_inverse().transform(v)
866 867
868 - def make_identity(self):
869 """Makes an identity Matrix44.""" 870 871 self._m = [1., 0., 0., 0., 872 0., 1., 0., 0., 873 0., 0., 1., 0., 874 0., 0., 0., 1.] 875 return self
876 877
878 - def make_copy(self, other):
879 """Makes a copy of another Matrix44.""" 880 881 self._m = other._m[:] 882 return self
883 884
885 - def make_scale(self, scale_x, scale_y= None, scale_z= None):
886 """Makes a scale Matrix44. 887 888 If the scale_y and scale_z parameters are not given they default to the same as scale_x. 889 890 """ 891 if scale_y is None: 892 scale_y = scale_x 893 if scale_z is None: 894 scale_z = scale_x 895 896 self._m = [float(scale_x), 0., 0., 0., 897 0., float(scale_y), 0., 0., 898 0., 0., float(scale_z), 0., 899 0., 0., 0., 1.] 900 return self
901 902
903 - def make_translation(self, x, y, z):
904 """Makes a translation Matrix44.""" 905 906 self._m = [1., 0., 0., 0., 907 0., 1., 0., 0., 908 0., 0., 1., 0., 909 float(x), float(y), float(z), 1.] 910 return self
911 912
913 - def make_x_rotation(self, angle):
914 """Makes a rotation Matrix44 around the x axis.""" 915 916 cos_a = cos(angle) 917 sin_a = sin(angle) 918 919 self._m = [1., 0., 0., 0., 920 0., cos_a, sin_a, 0., 921 0., -sin_a, cos_a, 0., 922 0., 0., 0., 1.] 923 return self
924
925 - def make_y_rotation(self, angle):
926 """Makes a rotation Matrix44 around the y axis.""" 927 928 cos_a = cos(angle) 929 sin_a = sin(angle) 930 931 self._m = [ cos_a, 0., -sin_a, 0., 932 0., 1., 0., 0., 933 sin_a, 0., cos_a, 0., 934 0., 0., 0., 1.] 935 return self
936 937
938 - def make_z_rotation(self, angle):
939 """Makes a rotation Matrix44 around the z axis.""" 940 941 cos_a = cos(angle) 942 sin_a = sin(angle) 943 944 self._m = [ cos_a, sin_a, 0., 0., 945 -sin_a, cos_a, 0., 0., 946 0., 0., 1., 0., 947 0., 0., 0., 1.] 948 return self
949 950
951 - def make_rotation_about_axis(self, axis, angle):
952 """Makes a rotation Matrix44 around an axis. 953 954 axis -- An iterable containing the axis (three values) 955 angle -- The angle to rotate (in radians) 956 957 """ 958 959 c = cos(angle) 960 s = sin(angle) 961 omc = 1. - c 962 x, y, z = axis 963 964 self._m = [x*x*omc+c, y*x*omc+z*s, x*z*omc-y*s, 0., 965 x*y*omc-z*s, y*y*omc+c, y*z*omc+x*s, 0., 966 x*z*omc+y*s, y*z*omc-x*s, z*z*omc+c, 0., 967 0., 0., 0., 1.] 968 return self
969 970
971 - def make_xyz_rotation(self, angle_x, angle_y, angle_z):
972 """Makes a rotation Matrix44 about 3 axis.""" 973 974 cx = cos(angle_x) 975 sx = sin(angle_x) 976 cy = cos(angle_y) 977 sy = sin(angle_y) 978 cz = cos(angle_z) 979 sz = sin(angle_z) 980 981 sxsy = sx*sy 982 cxsy = cx*sy 983 984 # http://web.archive.org/web/20041029003853/http:/www.j3d.org/matrix_faq/matrfaq_latest.html#Q35 985 #A = cos(angle_x) 986 #B = sin(angle_x) 987 #C = cos(angle_y) 988 #D = sin(angle_y) 989 #E = cos(angle_z) 990 #F = sin(angle_z) 991 992 # | CE -CF D 0 | 993 #M = | BDE+AF -BDF+AE -BC 0 | 994 # | -ADE+BF ADF+BE AC 0 | 995 # | 0 0 0 1 | 996 997 self._m = [ cy*cz, sxsy*cz+cx*sz, -cxsy*cz+sx*sz, 0., 998 -cy*sz, -sxsy*sz+cx*cz, cxsy*sz+sx*cz, 0., 999 sy, -sx*cy, cx*cy, 0., 1000 0., 0., 0., 1.] 1001 1002 return self
1003 1004
1005 - def make_perspective_projection(self, left, right, top, bottom, near, far):
1006 """Makes a perspective projection Matrix44. 1007 1008 left -- Coordinate of left of screen 1009 right -- Coordination of right of screen 1010 top -- Coordination of the top of the screen 1011 bottom -- Coordination of the borrom of the screen 1012 near -- Coordination of the near clipping plane 1013 far -- Coordinate of the far clipping plane 1014 1015 """ 1016 1017 self._m = [(2.*near)/(right-left), 0., 0., 0., 1018 0., (2.*near)/(top-bottom), 0., 0., 1019 (right+left)/(right-left), (top+bottom)/(top-bottom), -((far+near)/(far-near)), -1., 1020 0., 0., -((2.*far*near)/(far-near)), 0.] 1021 return self
1022 1023
1024 - def make_perspective_projection_fov(self, fov, aspect, near, far):
1025 """Creates a Matrix44 that projects points in to 2d space 1026 1027 fov -- The field of view (in radians) 1028 aspect -- The aspect ratio of the screen (width / height) 1029 near -- Coordinate of the near clipping plane 1030 far -- Coordinate of the far clipping plane 1031 1032 """ 1033 1034 assert fov < pi, "The field of view should be less than pi radians"\ 1035 " (180 degrees)" 1036 1037 range = near*tan(fov/2.); 1038 left = -range*aspect 1039 right = range*aspect 1040 bottom = -range 1041 top = range 1042 1043 #right = tan( fov/2. ) * near 1044 #left = - right 1045 #top = right / aspect 1046 #bottom = -top 1047 self.make_perspective_projection(left, right, bottom, top, near, far) 1048 return self
1049 1050
1051 - def transpose(self):
1052 """Swaps the rows for columns.""" 1053 1054 m00, m01, m02, m03, \ 1055 m10, m11, m12, m13, \ 1056 m20, m21, m22, m23, \ 1057 m30, m31, m32, m33 = self._m 1058 1059 self._m = [ m00, m10, m20, m30, 1060 m01, m11, m21, m31, 1061 m02, m12, m22, m32, 1062 m03, m13, m23, m33 ]
1063 1064
1065 - def get_transpose(self):
1066 """Returns a Matrix44 that is a copy of this, but with rows and 1067 columns swapped.""" 1068 1069 m00, m01, m02, m03, \ 1070 m10, m11, m12, m13, \ 1071 m20, m21, m22, m23, \ 1072 m30, m31, m32, m33 = self._m 1073 1074 ret = self.__new__(self.__class__, object) 1075 1076 ret._m = [ m00, m10, m20, m30, 1077 m01, m11, m21, m31, 1078 m02, m12, m22, m32, 1079 m03, m13, m23, m33 ] 1080 1081 return ret
1082 1083
1084 - def get_inverse_rot_trans(self):
1085 """Returns the inverse of a Matrix44 with only rotation and 1086 translation. This is faster than the general get_inverse method.""" 1087 1088 ret = self.copy() 1089 m = ret._m 1090 1091 i0, i1, i2, i3, \ 1092 i4, i5, i6, i7, \ 1093 i8, i9, i10, i11, \ 1094 i12, i13, i14, i15 = self._m 1095 1096 m[1] = i4 1097 m[4] = i1 1098 m[2] = i8 1099 m[8] = i2 1100 m[6] = i9 1101 m[9] = i6 1102 1103 m[12] = ( m[0] * -i12 + 1104 m[4] * -i13 + 1105 m[8] * -i14 ) 1106 1107 m[13] = ( m[1] * -i12 + 1108 m[5] * -i13 + 1109 m[9] * -i14 ) 1110 1111 m[14] = ( m[2] * -i12 + 1112 m[6] * -i13 + 1113 m[10] * -i14 ) 1114 1115 return ret
1116 1117
1118 - def get_inverse(self):
1119 1120 """Returns the inverse (matrix with the opposite effect) of this 1121 matrix.""" 1122 1123 ret = self.__new__(self.__class__, object) 1124 i = self._m 1125 1126 i0, i1, i2, i3, \ 1127 i4, i5, i6, i7, \ 1128 i8, i9, i10, i11, \ 1129 i12, i13, i14, i15 = i 1130 1131 negpos=[0., 0.] 1132 temp = i0 * i5 * i10 1133 negpos[temp > 0.] += temp 1134 1135 temp = i1 * i6 * i8 1136 negpos[temp > 0.] += temp 1137 1138 temp = i2 * i4 * i9 1139 negpos[temp > 0.] += temp 1140 1141 temp = -i2 * i5 * i8 1142 negpos[temp > 0.] += temp 1143 1144 temp = -i1 * i4 * i10 1145 negpos[temp > 0.] += temp 1146 1147 temp = -i0 * i6 * i9 1148 negpos[temp > 0.] += temp 1149 1150 det_1 = negpos[0]+negpos[1] 1151 1152 if (det_1 == 0.) or (abs(det_1 / (negpos[1] - negpos[0])) < \ 1153 (2. * 0.00000000000000001) ): 1154 raise Matrix44Error("notivertable", "This Matrix44 can not be inverted") 1155 1156 det_1 = 1. / det_1 1157 1158 ret._m = [ (i5*i10 - i6*i9)*det_1, 1159 -(i1*i10 - i2*i9)*det_1, 1160 (i1*i6 - i2*i5 )*det_1, 1161 0.0, 1162 -(i4*i10 - i6*i8)*det_1, 1163 (i0*i10 - i2*i8)*det_1, 1164 -(i0*i6 - i2*i4)*det_1, 1165 0.0, 1166 (i4*i9 - i5*i8 )*det_1, 1167 -(i0*i9 - i1*i8)*det_1, 1168 (i0*i5 - i1*i4)*det_1, 1169 0.0, 1170 0.0, 0.0, 0.0, 1.0 ] 1171 1172 m = ret._m 1173 m[12] = - ( i12 * m[0] + i13 * m[4] + i14 * m[8] ) 1174 m[13] = - ( i12 * m[1] + i13 * m[5] + i14 * m[9] ) 1175 m[14] = - ( i12 * m[2] + i13 * m[6] + i14 * m[10] ) 1176 1177 return ret
1178 1179
1180 - def invert(self):
1181 1182 """Inverts this matrix.""" 1183 1184 self._m[:] = self.get_inverse()._m
1185 1186
1187 - def move(self, forward=None, right=None, up=None):
1188 1189 """Changes the translation according to a direction vector. 1190 To move in opposite directions (i.e back, left and down), first 1191 negate the vector. 1192 1193 forward -- Units to move in the 'forward' direction 1194 right -- Units to move in the 'right' direction 1195 up -- Units to move in the 'up' direction 1196 1197 """ 1198 1199 if forward is not None: 1200 self.translate = Vector3(self.translate) + \ 1201 Vector3(self.forward) * forward 1202 1203 if right is not None: 1204 self.translate = Vector3(self.translate) + \ 1205 Vector3(self.right) * right 1206 1207 if up is not None: 1208 self.translate = Vector3(self.translate) + \ 1209 Vector3(self.up) * up
1210 1211 1212
1213 -def test():
1214 1215 m = Matrix44.xyz_rotation(radians(45), radians(20), radians(0)) 1216 1217 #m.translate += (1, 2, 3) 1218 print m.right 1219 print m.right.as_vec3() 1220 1221 n = m.copy() 1222 1223 r = Matrix44.z_rotation(radians(32)) 1224 m *= r 1225 print m 1226 n.fast_mul( r ) 1227 1228 print n 1229 1230 print "--Transpose" 1231 1232 print m.get_transpose() 1233 1234 print "--" 1235 1236 print m.get_row(2) 1237 1238 m.transpose() 1239 print m 1240 1241 print 1242 1243 #print (10, 20, 30) + Vector3(1,2,3) 1244 1245 m.translate = (m.translate[:3]) + Vector3(10, 20, 30) 1246 1247 print m 1248 1249 print 1250 1251 v = (1., 2., 3.) 1252 print v 1253 vt = m.transform(v) 1254 print vt 1255 1256 vit = m.get_inverse().transform(vt) 1257 1258 print vit 1259 1260 1261 print m.inverse_transform(vt) 1262 m[1,2] = 3. 1263 1264 print 1265 1266 print m.x_axis 1267 print m.translate 1268 m.translate = (1, 2, 3) 1269 1270 print m 1271 1272 identity = Matrix44() 1273 #identity.set_row(0, (1, 2, 3, 4)) 1274 print identity 1275 1276 identity[3, 1] = 456 1277 #identity *= Matrix44.scale(20.32, 764.2323, -23) 1278 1279 print identity 1280 print eval(repr(identity)) 1281 1282 print m 1283 1284 print m.translate + Vector3(1, 2, 3) 1285 1286 print m 1287 1288 m.translate = (0, 0, 0)
1289 1290 if __name__ == "__main__": 1291 1292 test() 1293