2 Boolean geometry four by four matrix.
6 from __future__ import absolute_import
7 #Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module.
10 from fabmetheus_utilities.geometry.geometry_utilities import evaluate
11 from fabmetheus_utilities.vector3 import Vector3
12 from fabmetheus_utilities import archive
13 from fabmetheus_utilities import euclidean
14 from fabmetheus_utilities import xml_simple_writer
19 __author__ = 'Enrique Perez (perez_enrique@yahoo.com)'
20 __credits__ = 'Art of Illusion <http://www.artofillusion.org/>'
21 __date__ = '$Date: 2008/02/05 $'
22 __license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html'
25 globalExecutionOrder = 300
28 def addVertexes(geometryOutput, vertexes):
30 if geometryOutput.__class__ == list:
31 for element in geometryOutput:
32 addVertexes(element, vertexes)
34 if geometryOutput.__class__ == dict:
35 for geometryOutputKey in geometryOutput.keys():
36 if geometryOutputKey == 'vertex':
37 vertexes += geometryOutput[geometryOutputKey]
39 addVertexes(geometryOutput[geometryOutputKey], vertexes)
41 def getBranchMatrix(elementNode):
42 'Get matrix starting from the object if it exists, otherwise get a matrix starting from stratch.'
43 branchMatrix = Matrix()
44 matrixChildElement = elementNode.getFirstChildByLocalName('matrix')
45 if matrixChildElement != None:
46 branchMatrix = branchMatrix.getFromElementNode(matrixChildElement, '')
47 branchMatrix = branchMatrix.getFromElementNode(elementNode, 'matrix.')
48 if elementNode.xmlObject == None:
50 elementNodeMatrix = elementNode.xmlObject.getMatrix4X4()
51 if elementNodeMatrix == None:
53 return elementNodeMatrix.getOtherTimesSelf(branchMatrix.tetragrid)
55 def getBranchMatrixSetElementNode(elementNode):
56 'Get matrix starting from the object if it exists, otherwise get a matrix starting from stratch.'
57 branchMatrix = getBranchMatrix(elementNode)
58 setElementNodeDictionaryMatrix(elementNode, branchMatrix)
61 def getCumulativeVector3Remove(defaultVector3, elementNode, prefix):
62 'Get cumulative vector3 and delete the prefixed attributes.'
64 defaultVector3.x = evaluate.getEvaluatedFloat(defaultVector3.x, elementNode, 'x')
65 defaultVector3.y = evaluate.getEvaluatedFloat(defaultVector3.y, elementNode, 'y')
66 defaultVector3.z = evaluate.getEvaluatedFloat(defaultVector3.z, elementNode, 'z')
67 euclidean.removeElementsFromDictionary(elementNode.attributes, ['x', 'y', 'z'])
69 defaultVector3 = evaluate.getVector3ByPrefix(defaultVector3, elementNode, prefix)
70 euclidean.removePrefixFromDictionary(elementNode.attributes, prefix)
73 def getDiagonalSwitchedTetragrid(angleDegrees, diagonals):
74 'Get the diagonals and switched matrix by degrees.'
75 return getDiagonalSwitchedTetragridByRadians(math.radians(angleDegrees), diagonals)
77 def getDiagonalSwitchedTetragridByPolar(diagonals, unitPolar):
78 'Get the diagonals and switched matrix by unitPolar.'
79 diagonalSwitchedTetragrid = getIdentityTetragrid()
80 for diagonal in diagonals:
81 diagonalSwitchedTetragrid[diagonal][diagonal] = unitPolar.real
82 diagonalSwitchedTetragrid[diagonals[0]][diagonals[1]] = -unitPolar.imag
83 diagonalSwitchedTetragrid[diagonals[1]][diagonals[0]] = unitPolar.imag
84 return diagonalSwitchedTetragrid
86 def getDiagonalSwitchedTetragridByRadians(angleRadians, diagonals):
87 'Get the diagonals and switched matrix by radians.'
88 return getDiagonalSwitchedTetragridByPolar(diagonals, euclidean.getWiddershinsUnitPolar(angleRadians))
90 def getIdentityTetragrid(tetragrid=None):
91 'Get four by four matrix with diagonal elements set to one.'
93 return [[1.0, 0.0, 0.0, 0.0], [0.0, 1.0, 0.0, 0.0], [0.0, 0.0, 1.0, 0.0], [0.0, 0.0, 0.0, 1.0]]
96 def getIsIdentityTetragrid(tetragrid):
97 'Determine if the tetragrid is the identity tetragrid.'
98 for column in xrange(4):
101 if tetragrid[column][row] != 1.0:
103 elif tetragrid[column][row] != 0.0:
107 def getIsIdentityTetragridOrNone(tetragrid):
108 'Determine if the tetragrid is None or if it is the identity tetragrid.'
109 if tetragrid == None:
111 return getIsIdentityTetragrid(tetragrid)
113 def getKeyA(row, column, prefix=''):
114 'Get the a format key string from row & column, counting from zero.'
115 return '%sa%s%s' % (prefix, row, column)
117 def getKeyM(row, column, prefix=''):
118 'Get the m format key string from row & column, counting from one.'
119 return '%sm%s%s' % (prefix, row + 1, column + 1)
121 def getKeysA(prefix=''):
122 'Get the matrix keys, counting from zero.'
124 for row in xrange(4):
125 for column in xrange(4):
126 key = getKeyA(row, column, prefix)
130 def getKeysM(prefix=''):
131 'Get the matrix keys, counting from one.'
133 for row in xrange(4):
134 for column in xrange(4):
135 key = getKeyM(row, column, prefix)
139 def getRemovedFloat(defaultFloat, elementNode, key, prefix):
140 'Get the float by the key and the prefix.'
141 prefixKey = prefix + key
142 if prefixKey in elementNode.attributes:
143 floatValue = evaluate.getEvaluatedFloat(None, elementNode, prefixKey)
144 if floatValue == None:
145 print('Warning, evaluated value in getRemovedFloatByKeys in matrix is None for key:')
147 print('for elementNode dictionary value:')
148 print(elementNode.attributes[prefixKey])
149 print('for elementNode dictionary:')
150 print(elementNode.attributes)
152 defaultFloat = floatValue
153 del elementNode.attributes[prefixKey]
156 def getRemovedFloatByKeys(defaultFloat, elementNode, keys, prefix):
157 'Get the float by the keys and the prefix.'
159 defaultFloat = getRemovedFloat(defaultFloat, elementNode, key, prefix)
162 def getRotateAroundAxisTetragrid(elementNode, prefix):
163 'Get rotate around axis tetragrid and delete the axis and angle attributes.'
164 angle = getRemovedFloatByKeys(0.0, elementNode, ['angle', 'counterclockwise'], prefix)
165 angle -= getRemovedFloat(0.0, elementNode, 'clockwise', prefix)
168 angleRadians = math.radians(angle)
169 axis = getCumulativeVector3Remove(Vector3(), elementNode, prefix + 'axis')
170 axisLength = abs(axis)
171 if axisLength <= 0.0:
172 print('Warning, axisLength was zero in getRotateAroundAxisTetragrid in matrix so nothing will be done for:')
176 tetragrid = getIdentityTetragrid()
177 cosAngle = math.cos(angleRadians)
178 sinAngle = math.sin(angleRadians)
179 oneMinusCos = 1.0 - math.cos(angleRadians)
186 tetragrid[0] = [cosAngle + xx * oneMinusCos, xy * oneMinusCos - axis.z * sinAngle, xz * oneMinusCos + axis.y * sinAngle, 0.0]
187 tetragrid[1] = [xy * oneMinusCos + axis.z * sinAngle, cosAngle + yy * oneMinusCos, yz * oneMinusCos - axis.x * sinAngle, 0.0]
188 tetragrid[2] = [xz * oneMinusCos - axis.y * sinAngle, yz * oneMinusCos + axis.x * sinAngle, cosAngle + zz * oneMinusCos, 0.0]
191 def getRotateTetragrid(elementNode, prefix):
192 'Get rotate tetragrid and delete the rotate attributes.'
193 # http://en.wikipedia.org/wiki/Rotation_matrix
194 rotateMatrix = Matrix()
195 rotateMatrix.tetragrid = getRotateAroundAxisTetragrid(elementNode, prefix)
196 zAngle = getRemovedFloatByKeys(0.0, elementNode, ['axisclockwisez', 'observerclockwisez', 'z'], prefix)
197 zAngle -= getRemovedFloatByKeys(0.0, elementNode, ['axiscounterclockwisez', 'observercounterclockwisez'], prefix)
199 rotateMatrix.tetragrid = getTetragridTimesOther(getDiagonalSwitchedTetragrid(-zAngle, [0, 1]), rotateMatrix.tetragrid)
200 xAngle = getRemovedFloatByKeys(0.0, elementNode, ['axisclockwisex', 'observerclockwisex', 'x'], prefix)
201 xAngle -= getRemovedFloatByKeys(0.0, elementNode, ['axiscounterclockwisex', 'observercounterclockwisex'], prefix)
203 rotateMatrix.tetragrid = getTetragridTimesOther(getDiagonalSwitchedTetragrid(-xAngle, [1, 2]), rotateMatrix.tetragrid)
204 yAngle = getRemovedFloatByKeys(0.0, elementNode, ['axiscounterclockwisey', 'observerclockwisey', 'y'], prefix)
205 yAngle -= getRemovedFloatByKeys(0.0, elementNode, ['axisclockwisey', 'observercounterclockwisey'], prefix)
207 rotateMatrix.tetragrid = getTetragridTimesOther(getDiagonalSwitchedTetragrid(yAngle, [0, 2]), rotateMatrix.tetragrid)
208 return rotateMatrix.tetragrid
210 def getScaleTetragrid(elementNode, prefix):
211 'Get scale matrix and delete the scale attributes.'
212 scaleDefaultVector3 = Vector3(1.0, 1.0, 1.0)
213 scale = getCumulativeVector3Remove(scaleDefaultVector3.copy(), elementNode, prefix)
214 if scale == scaleDefaultVector3:
216 return [[scale.x, 0.0, 0.0, 0.0], [0.0, scale.y, 0.0, 0.0], [0.0, 0.0, scale.z, 0.0], [0.0, 0.0, 0.0, 1.0]]
218 def getTetragridA(elementNode, prefix, tetragrid):
219 'Get the tetragrid from the elementNode letter a values.'
220 keysA = getKeysA(prefix)
221 evaluatedDictionary = evaluate.getEvaluatedDictionaryByEvaluationKeys(elementNode, keysA)
222 if len(evaluatedDictionary.keys()) < 1:
224 for row in xrange(4):
225 for column in xrange(4):
226 key = getKeyA(row, column, prefix)
227 if key in evaluatedDictionary:
228 value = evaluatedDictionary[key]
229 if value == None or value == 'None':
230 print('Warning, value in getTetragridA in matrix is None for key for dictionary:')
232 print(evaluatedDictionary)
234 tetragrid = getIdentityTetragrid(tetragrid)
235 tetragrid[row][column] = float(value)
236 euclidean.removeElementsFromDictionary(elementNode.attributes, keysA)
239 def getTetragridC(elementNode, prefix, tetragrid):
240 'Get the matrix Tetragrid from the elementNode letter c values.'
241 columnKeys = 'Pc1 Pc2 Pc3 Pc4'.replace('P', prefix).split()
242 evaluatedDictionary = evaluate.getEvaluatedDictionaryByEvaluationKeys(elementNode, columnKeys)
243 if len(evaluatedDictionary.keys()) < 1:
245 for columnKeyIndex, columnKey in enumerate(columnKeys):
246 if columnKey in evaluatedDictionary:
247 value = evaluatedDictionary[columnKey]
248 if value == None or value == 'None':
249 print('Warning, value in getTetragridC in matrix is None for columnKey for dictionary:')
251 print(evaluatedDictionary)
253 tetragrid = getIdentityTetragrid(tetragrid)
254 for elementIndex, element in enumerate(value):
255 tetragrid[elementIndex][columnKeyIndex] = element
256 euclidean.removeElementsFromDictionary(elementNode.attributes, columnKeys)
259 def getTetragridCopy(tetragrid):
260 'Get tetragrid copy.'
261 if tetragrid == None:
264 for tetragridRow in tetragrid:
265 tetragridCopy.append(tetragridRow[:])
268 def getTetragridM(elementNode, prefix, tetragrid):
269 'Get the tetragrid from the elementNode letter m values.'
270 keysM = getKeysM(prefix)
271 evaluatedDictionary = evaluate.getEvaluatedDictionaryByEvaluationKeys(elementNode, keysM)
272 if len(evaluatedDictionary.keys()) < 1:
274 for row in xrange(4):
275 for column in xrange(4):
276 key = getKeyM(row, column, prefix)
277 if key in evaluatedDictionary:
278 value = evaluatedDictionary[key]
279 if value == None or value == 'None':
280 print('Warning, value in getTetragridM in matrix is None for key for dictionary:')
282 print(evaluatedDictionary)
284 tetragrid = getIdentityTetragrid(tetragrid)
285 tetragrid[row][column] = float(value)
286 euclidean.removeElementsFromDictionary(elementNode.attributes, keysM)
289 def getTetragridMatrix(elementNode, prefix, tetragrid):
290 'Get the tetragrid from the elementNode matrix value.'
291 matrixKey = prefix + 'matrix'
292 evaluatedDictionary = evaluate.getEvaluatedDictionaryByEvaluationKeys(elementNode, [matrixKey])
293 if len(evaluatedDictionary.keys()) < 1:
295 value = evaluatedDictionary[matrixKey]
296 if value == None or value == 'None':
297 print('Warning, value in getTetragridMatrix in matrix is None for matrixKey for dictionary:')
299 print(evaluatedDictionary)
301 tetragrid = getIdentityTetragrid(tetragrid)
302 for rowIndex, row in enumerate(value):
303 for elementIndex, element in enumerate(row):
304 tetragrid[rowIndex][elementIndex] = element
305 euclidean.removeElementsFromDictionary(elementNode.attributes, [matrixKey])
308 def getTetragridR(elementNode, prefix, tetragrid):
309 'Get the tetragrid from the elementNode letter r values.'
310 rowKeys = 'Pr1 Pr2 Pr3 Pr4'.replace('P', prefix).split()
311 evaluatedDictionary = evaluate.getEvaluatedDictionaryByEvaluationKeys(elementNode, rowKeys)
312 if len(evaluatedDictionary.keys()) < 1:
314 for rowKeyIndex, rowKey in enumerate(rowKeys):
315 if rowKey in evaluatedDictionary:
316 value = evaluatedDictionary[rowKey]
317 if value == None or value == 'None':
318 print('Warning, value in getTetragridR in matrix is None for rowKey for dictionary:')
320 print(evaluatedDictionary)
322 tetragrid = getIdentityTetragrid(tetragrid)
323 for elementIndex, element in enumerate(value):
324 tetragrid[rowKeyIndex][elementIndex] = element
325 euclidean.removeElementsFromDictionary(elementNode.attributes, rowKeys)
328 def getTetragridTimesOther(firstTetragrid, otherTetragrid ):
329 'Get this matrix multiplied by the other matrix.'
330 #A down, B right from http://en.wikipedia.org/wiki/Matrix_multiplication
331 if firstTetragrid == None:
332 return otherTetragrid
333 if otherTetragrid == None:
334 return firstTetragrid
335 tetragridTimesOther = []
336 for row in xrange(4):
337 matrixRow = firstTetragrid[row]
338 tetragridTimesOtherRow = []
339 tetragridTimesOther.append(tetragridTimesOtherRow)
340 for column in xrange(4):
342 for elementIndex in xrange(4):
343 dotProduct += matrixRow[elementIndex] * otherTetragrid[elementIndex][column]
344 tetragridTimesOtherRow.append(dotProduct)
345 return tetragridTimesOther
347 def getTransformedByList(floatList, point):
348 'Get the point transformed by the array.'
349 return floatList[0] * point.x + floatList[1] * point.y + floatList[2] * point.z + floatList[3]
351 def getTransformedVector3(tetragrid, vector3):
352 'Get the vector3 multiplied by a matrix.'
353 if getIsIdentityTetragridOrNone(tetragrid):
354 return vector3.copy()
355 return getTransformedVector3Blindly(tetragrid, vector3)
357 def getTransformedVector3Blindly(tetragrid, vector3):
358 'Get the vector3 multiplied by a tetragrid without checking if the tetragrid exists.'
360 getTransformedByList(tetragrid[0], vector3),
361 getTransformedByList(tetragrid[1], vector3),
362 getTransformedByList(tetragrid[2], vector3))
364 def getTransformedVector3s(tetragrid, vector3s):
365 'Get the vector3s multiplied by a matrix.'
366 if getIsIdentityTetragridOrNone(tetragrid):
367 return euclidean.getPathCopy(vector3s)
368 transformedVector3s = []
369 for vector3 in vector3s:
370 transformedVector3s.append(getTransformedVector3Blindly(tetragrid, vector3))
371 return transformedVector3s
373 def getTransformTetragrid(elementNode, prefix):
374 'Get the tetragrid from the elementNode.'
375 tetragrid = getTetragridA(elementNode, prefix, None)
376 tetragrid = getTetragridC(elementNode, prefix, tetragrid)
377 tetragrid = getTetragridM(elementNode, prefix, tetragrid)
378 tetragrid = getTetragridMatrix(elementNode, prefix, tetragrid)
379 tetragrid = getTetragridR(elementNode, prefix, tetragrid)
382 def getTranslateTetragrid(elementNode, prefix):
383 'Get translate matrix and delete the translate attributes.'
384 translation = getCumulativeVector3Remove(Vector3(), elementNode, prefix)
385 if translation.getIsDefault():
387 return getTranslateTetragridByTranslation(translation)
389 def getTranslateTetragridByTranslation(translation):
390 'Get translate tetragrid by translation.'
391 return [[1.0, 0.0, 0.0, translation.x], [0.0, 1.0, 0.0, translation.y], [0.0, 0.0, 1.0, translation.z], [0.0, 0.0, 0.0, 1.0]]
393 def getVertexes(geometryOutput):
396 addVertexes(geometryOutput, vertexes)
399 def setAttributesToMultipliedTetragrid(elementNode, tetragrid):
400 'Set the element attribute dictionary and element matrix to the matrix times the tetragrid.'
401 setElementNodeDictionaryMatrix(elementNode, getBranchMatrix(elementNode).getOtherTimesSelf(tetragrid))
403 def setElementNodeDictionaryMatrix(elementNode, matrix4X4):
404 'Set the element attribute dictionary or element matrix to the matrix.'
405 if elementNode.xmlObject == None:
406 elementNode.attributes.update(matrix4X4.getAttributes('matrix.'))
408 elementNode.xmlObject.matrix4X4 = matrix4X4
410 def transformVector3Blindly(tetragrid, vector3):
411 'Transform the vector3 by a tetragrid without checking to see if it exists.'
412 x = getTransformedByList(tetragrid[0], vector3)
413 y = getTransformedByList(tetragrid[1], vector3)
414 z = getTransformedByList(tetragrid[2], vector3)
419 def transformVector3ByMatrix(tetragrid, vector3):
420 'Transform the vector3 by a matrix.'
421 if getIsIdentityTetragridOrNone(tetragrid):
423 transformVector3Blindly(tetragrid, vector3)
425 def transformVector3sByMatrix(tetragrid, vector3s):
426 'Transform the vector3s by a matrix.'
427 if getIsIdentityTetragridOrNone(tetragrid):
429 for vector3 in vector3s:
430 transformVector3Blindly(tetragrid, vector3)
434 'A four by four matrix.'
435 def __init__(self, tetragrid=None):
437 self.tetragrid = getTetragridCopy(tetragrid)
439 def __eq__(self, other):
440 'Determine whether this matrix is identical to other one.'
443 if other.__class__ != self.__class__:
445 return other.tetragrid == self.tetragrid
447 def __ne__(self, other):
448 'Determine whether this vector is not identical to other one.'
449 return not self.__eq__(other)
452 'Get the string representation of this four by four matrix.'
453 output = cStringIO.StringIO()
454 self.addXML(0, output)
455 return output.getvalue()
457 def addXML(self, depth, output):
458 'Add xml for this object.'
459 attributes = self.getAttributes()
460 if len(attributes) > 0:
461 xml_simple_writer.addClosedXMLTag(attributes, depth, self.__class__.__name__.lower(), output)
463 def getAttributes(self, prefix=''):
464 'Get the attributes from row column attribute strings, counting from one.'
466 if self.tetragrid == None:
468 for row in xrange(4):
469 for column in xrange(4):
470 default = float(column == row)
471 value = self.tetragrid[row][column]
472 if abs( value - default ) > 0.00000000000001:
473 if abs(value) < 0.00000000000001:
475 attributes[prefix + getKeyM(row, column)] = value
478 def getFromElementNode(self, elementNode, prefix):
479 'Get the values from row column attribute strings, counting from one.'
480 attributes = elementNode.attributes
481 if attributes == None:
483 self.tetragrid = getTetragridTimesOther(getTransformTetragrid(elementNode, prefix), self.tetragrid)
484 self.tetragrid = getTetragridTimesOther(getScaleTetragrid(elementNode, 'scale.'), self.tetragrid)
485 self.tetragrid = getTetragridTimesOther(getRotateTetragrid(elementNode, 'rotate.'), self.tetragrid)
486 self.tetragrid = getTetragridTimesOther(getTranslateTetragrid(elementNode, 'translate.'), self.tetragrid)
489 def getOtherTimesSelf(self, otherTetragrid):
490 'Get this matrix reverse multiplied by the other matrix.'
491 return Matrix(getTetragridTimesOther(otherTetragrid, self.tetragrid))
493 def getSelfTimesOther(self, otherTetragrid):
494 'Get this matrix multiplied by the other matrix.'
495 return Matrix(getTetragridTimesOther(self.tetragrid, otherTetragrid))