2 Boolean geometry four by four matrix.
6 from __future__ import absolute_import
8 from fabmetheus_utilities.geometry.geometry_utilities import evaluate
9 from fabmetheus_utilities.vector3 import Vector3
10 from fabmetheus_utilities import euclidean
11 from fabmetheus_utilities import xml_simple_writer
16 __author__ = 'Enrique Perez (perez_enrique@yahoo.com)'
17 __credits__ = 'Art of Illusion <http://www.artofillusion.org/>'
18 __date__ = '$Date: 2008/02/05 $'
19 __license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html'
22 globalExecutionOrder = 300
25 def addVertexes(geometryOutput, vertexes):
27 if geometryOutput.__class__ == list:
28 for element in geometryOutput:
29 addVertexes(element, vertexes)
31 if geometryOutput.__class__ == dict:
32 for geometryOutputKey in geometryOutput.keys():
33 if geometryOutputKey == 'vertex':
34 vertexes += geometryOutput[geometryOutputKey]
36 addVertexes(geometryOutput[geometryOutputKey], vertexes)
38 def getBranchMatrix(elementNode):
39 'Get matrix starting from the object if it exists, otherwise get a matrix starting from stratch.'
40 branchMatrix = Matrix()
41 matrixChildElement = elementNode.getFirstChildByLocalName('matrix')
42 if matrixChildElement != None:
43 branchMatrix = branchMatrix.getFromElementNode(matrixChildElement, '')
44 branchMatrix = branchMatrix.getFromElementNode(elementNode, 'matrix.')
45 if elementNode.xmlObject == None:
47 elementNodeMatrix = elementNode.xmlObject.getMatrix4X4()
48 if elementNodeMatrix == None:
50 return elementNodeMatrix.getOtherTimesSelf(branchMatrix.tetragrid)
52 def getBranchMatrixSetElementNode(elementNode):
53 'Get matrix starting from the object if it exists, otherwise get a matrix starting from stratch.'
54 branchMatrix = getBranchMatrix(elementNode)
55 setElementNodeDictionaryMatrix(elementNode, branchMatrix)
58 def getCumulativeVector3Remove(defaultVector3, elementNode, prefix):
59 'Get cumulative vector3 and delete the prefixed attributes.'
61 defaultVector3.x = evaluate.getEvaluatedFloat(defaultVector3.x, elementNode, 'x')
62 defaultVector3.y = evaluate.getEvaluatedFloat(defaultVector3.y, elementNode, 'y')
63 defaultVector3.z = evaluate.getEvaluatedFloat(defaultVector3.z, elementNode, 'z')
64 euclidean.removeElementsFromDictionary(elementNode.attributes, ['x', 'y', 'z'])
66 defaultVector3 = evaluate.getVector3ByPrefix(defaultVector3, elementNode, prefix)
67 euclidean.removePrefixFromDictionary(elementNode.attributes, prefix)
70 def getDiagonalSwitchedTetragrid(angleDegrees, diagonals):
71 'Get the diagonals and switched matrix by degrees.'
72 return getDiagonalSwitchedTetragridByRadians(math.radians(angleDegrees), diagonals)
74 def getDiagonalSwitchedTetragridByPolar(diagonals, unitPolar):
75 'Get the diagonals and switched matrix by unitPolar.'
76 diagonalSwitchedTetragrid = getIdentityTetragrid()
77 for diagonal in diagonals:
78 diagonalSwitchedTetragrid[diagonal][diagonal] = unitPolar.real
79 diagonalSwitchedTetragrid[diagonals[0]][diagonals[1]] = -unitPolar.imag
80 diagonalSwitchedTetragrid[diagonals[1]][diagonals[0]] = unitPolar.imag
81 return diagonalSwitchedTetragrid
83 def getDiagonalSwitchedTetragridByRadians(angleRadians, diagonals):
84 'Get the diagonals and switched matrix by radians.'
85 return getDiagonalSwitchedTetragridByPolar(diagonals, euclidean.getWiddershinsUnitPolar(angleRadians))
87 def getIdentityTetragrid(tetragrid=None):
88 'Get four by four matrix with diagonal elements set to one.'
90 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]]
93 def getIsIdentityTetragrid(tetragrid):
94 'Determine if the tetragrid is the identity tetragrid.'
95 for column in xrange(4):
98 if tetragrid[column][row] != 1.0:
100 elif tetragrid[column][row] != 0.0:
104 def getIsIdentityTetragridOrNone(tetragrid):
105 'Determine if the tetragrid is None or if it is the identity tetragrid.'
106 if tetragrid == None:
108 return getIsIdentityTetragrid(tetragrid)
110 def getKeyA(row, column, prefix=''):
111 'Get the a format key string from row & column, counting from zero.'
112 return '%sa%s%s' % (prefix, row, column)
114 def getKeyM(row, column, prefix=''):
115 'Get the m format key string from row & column, counting from one.'
116 return '%sm%s%s' % (prefix, row + 1, column + 1)
118 def getKeysA(prefix=''):
119 'Get the matrix keys, counting from zero.'
121 for row in xrange(4):
122 for column in xrange(4):
123 key = getKeyA(row, column, prefix)
127 def getKeysM(prefix=''):
128 'Get the matrix keys, counting from one.'
130 for row in xrange(4):
131 for column in xrange(4):
132 key = getKeyM(row, column, prefix)
136 def getRemovedFloat(defaultFloat, elementNode, key, prefix):
137 'Get the float by the key and the prefix.'
138 prefixKey = prefix + key
139 if prefixKey in elementNode.attributes:
140 floatValue = evaluate.getEvaluatedFloat(None, elementNode, prefixKey)
141 if floatValue == None:
142 print('Warning, evaluated value in getRemovedFloatByKeys in matrix is None for key:')
144 print('for elementNode dictionary value:')
145 print(elementNode.attributes[prefixKey])
146 print('for elementNode dictionary:')
147 print(elementNode.attributes)
149 defaultFloat = floatValue
150 del elementNode.attributes[prefixKey]
153 def getRemovedFloatByKeys(defaultFloat, elementNode, keys, prefix):
154 'Get the float by the keys and the prefix.'
156 defaultFloat = getRemovedFloat(defaultFloat, elementNode, key, prefix)
159 def getRotateAroundAxisTetragrid(elementNode, prefix):
160 'Get rotate around axis tetragrid and delete the axis and angle attributes.'
161 angle = getRemovedFloatByKeys(0.0, elementNode, ['angle', 'counterclockwise'], prefix)
162 angle -= getRemovedFloat(0.0, elementNode, 'clockwise', prefix)
165 angleRadians = math.radians(angle)
166 axis = getCumulativeVector3Remove(Vector3(), elementNode, prefix + 'axis')
167 axisLength = abs(axis)
168 if axisLength <= 0.0:
169 print('Warning, axisLength was zero in getRotateAroundAxisTetragrid in matrix so nothing will be done for:')
173 tetragrid = getIdentityTetragrid()
174 cosAngle = math.cos(angleRadians)
175 sinAngle = math.sin(angleRadians)
176 oneMinusCos = 1.0 - math.cos(angleRadians)
183 tetragrid[0] = [cosAngle + xx * oneMinusCos, xy * oneMinusCos - axis.z * sinAngle, xz * oneMinusCos + axis.y * sinAngle, 0.0]
184 tetragrid[1] = [xy * oneMinusCos + axis.z * sinAngle, cosAngle + yy * oneMinusCos, yz * oneMinusCos - axis.x * sinAngle, 0.0]
185 tetragrid[2] = [xz * oneMinusCos - axis.y * sinAngle, yz * oneMinusCos + axis.x * sinAngle, cosAngle + zz * oneMinusCos, 0.0]
188 def getRotateTetragrid(elementNode, prefix):
189 'Get rotate tetragrid and delete the rotate attributes.'
190 # http://en.wikipedia.org/wiki/Rotation_matrix
191 rotateMatrix = Matrix()
192 rotateMatrix.tetragrid = getRotateAroundAxisTetragrid(elementNode, prefix)
193 zAngle = getRemovedFloatByKeys(0.0, elementNode, ['axisclockwisez', 'observerclockwisez', 'z'], prefix)
194 zAngle -= getRemovedFloatByKeys(0.0, elementNode, ['axiscounterclockwisez', 'observercounterclockwisez'], prefix)
196 rotateMatrix.tetragrid = getTetragridTimesOther(getDiagonalSwitchedTetragrid(-zAngle, [0, 1]), rotateMatrix.tetragrid)
197 xAngle = getRemovedFloatByKeys(0.0, elementNode, ['axisclockwisex', 'observerclockwisex', 'x'], prefix)
198 xAngle -= getRemovedFloatByKeys(0.0, elementNode, ['axiscounterclockwisex', 'observercounterclockwisex'], prefix)
200 rotateMatrix.tetragrid = getTetragridTimesOther(getDiagonalSwitchedTetragrid(-xAngle, [1, 2]), rotateMatrix.tetragrid)
201 yAngle = getRemovedFloatByKeys(0.0, elementNode, ['axiscounterclockwisey', 'observerclockwisey', 'y'], prefix)
202 yAngle -= getRemovedFloatByKeys(0.0, elementNode, ['axisclockwisey', 'observercounterclockwisey'], prefix)
204 rotateMatrix.tetragrid = getTetragridTimesOther(getDiagonalSwitchedTetragrid(yAngle, [0, 2]), rotateMatrix.tetragrid)
205 return rotateMatrix.tetragrid
207 def getScaleTetragrid(elementNode, prefix):
208 'Get scale matrix and delete the scale attributes.'
209 scaleDefaultVector3 = Vector3(1.0, 1.0, 1.0)
210 scale = getCumulativeVector3Remove(scaleDefaultVector3.copy(), elementNode, prefix)
211 if scale == scaleDefaultVector3:
213 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]]
215 def getTetragridA(elementNode, prefix, tetragrid):
216 'Get the tetragrid from the elementNode letter a values.'
217 keysA = getKeysA(prefix)
218 evaluatedDictionary = evaluate.getEvaluatedDictionaryByEvaluationKeys(elementNode, keysA)
219 if len(evaluatedDictionary.keys()) < 1:
221 for row in xrange(4):
222 for column in xrange(4):
223 key = getKeyA(row, column, prefix)
224 if key in evaluatedDictionary:
225 value = evaluatedDictionary[key]
226 if value == None or value == 'None':
227 print('Warning, value in getTetragridA in matrix is None for key for dictionary:')
229 print(evaluatedDictionary)
231 tetragrid = getIdentityTetragrid(tetragrid)
232 tetragrid[row][column] = float(value)
233 euclidean.removeElementsFromDictionary(elementNode.attributes, keysA)
236 def getTetragridC(elementNode, prefix, tetragrid):
237 'Get the matrix Tetragrid from the elementNode letter c values.'
238 columnKeys = 'Pc1 Pc2 Pc3 Pc4'.replace('P', prefix).split()
239 evaluatedDictionary = evaluate.getEvaluatedDictionaryByEvaluationKeys(elementNode, columnKeys)
240 if len(evaluatedDictionary.keys()) < 1:
242 for columnKeyIndex, columnKey in enumerate(columnKeys):
243 if columnKey in evaluatedDictionary:
244 value = evaluatedDictionary[columnKey]
245 if value == None or value == 'None':
246 print('Warning, value in getTetragridC in matrix is None for columnKey for dictionary:')
248 print(evaluatedDictionary)
250 tetragrid = getIdentityTetragrid(tetragrid)
251 for elementIndex, element in enumerate(value):
252 tetragrid[elementIndex][columnKeyIndex] = element
253 euclidean.removeElementsFromDictionary(elementNode.attributes, columnKeys)
256 def getTetragridCopy(tetragrid):
257 'Get tetragrid copy.'
258 if tetragrid == None:
261 for tetragridRow in tetragrid:
262 tetragridCopy.append(tetragridRow[:])
265 def getTetragridM(elementNode, prefix, tetragrid):
266 'Get the tetragrid from the elementNode letter m values.'
267 keysM = getKeysM(prefix)
268 evaluatedDictionary = evaluate.getEvaluatedDictionaryByEvaluationKeys(elementNode, keysM)
269 if len(evaluatedDictionary.keys()) < 1:
271 for row in xrange(4):
272 for column in xrange(4):
273 key = getKeyM(row, column, prefix)
274 if key in evaluatedDictionary:
275 value = evaluatedDictionary[key]
276 if value == None or value == 'None':
277 print('Warning, value in getTetragridM in matrix is None for key for dictionary:')
279 print(evaluatedDictionary)
281 tetragrid = getIdentityTetragrid(tetragrid)
282 tetragrid[row][column] = float(value)
283 euclidean.removeElementsFromDictionary(elementNode.attributes, keysM)
286 def getTetragridMatrix(elementNode, prefix, tetragrid):
287 'Get the tetragrid from the elementNode matrix value.'
288 matrixKey = prefix + 'matrix'
289 evaluatedDictionary = evaluate.getEvaluatedDictionaryByEvaluationKeys(elementNode, [matrixKey])
290 if len(evaluatedDictionary.keys()) < 1:
292 value = evaluatedDictionary[matrixKey]
293 if value == None or value == 'None':
294 print('Warning, value in getTetragridMatrix in matrix is None for matrixKey for dictionary:')
296 print(evaluatedDictionary)
298 tetragrid = getIdentityTetragrid(tetragrid)
299 for rowIndex, row in enumerate(value):
300 for elementIndex, element in enumerate(row):
301 tetragrid[rowIndex][elementIndex] = element
302 euclidean.removeElementsFromDictionary(elementNode.attributes, [matrixKey])
305 def getTetragridR(elementNode, prefix, tetragrid):
306 'Get the tetragrid from the elementNode letter r values.'
307 rowKeys = 'Pr1 Pr2 Pr3 Pr4'.replace('P', prefix).split()
308 evaluatedDictionary = evaluate.getEvaluatedDictionaryByEvaluationKeys(elementNode, rowKeys)
309 if len(evaluatedDictionary.keys()) < 1:
311 for rowKeyIndex, rowKey in enumerate(rowKeys):
312 if rowKey in evaluatedDictionary:
313 value = evaluatedDictionary[rowKey]
314 if value == None or value == 'None':
315 print('Warning, value in getTetragridR in matrix is None for rowKey for dictionary:')
317 print(evaluatedDictionary)
319 tetragrid = getIdentityTetragrid(tetragrid)
320 for elementIndex, element in enumerate(value):
321 tetragrid[rowKeyIndex][elementIndex] = element
322 euclidean.removeElementsFromDictionary(elementNode.attributes, rowKeys)
325 def getTetragridTimesOther(firstTetragrid, otherTetragrid ):
326 'Get this matrix multiplied by the other matrix.'
327 #A down, B right from http://en.wikipedia.org/wiki/Matrix_multiplication
328 if firstTetragrid == None:
329 return otherTetragrid
330 if otherTetragrid == None:
331 return firstTetragrid
332 tetragridTimesOther = []
333 for row in xrange(4):
334 matrixRow = firstTetragrid[row]
335 tetragridTimesOtherRow = []
336 tetragridTimesOther.append(tetragridTimesOtherRow)
337 for column in xrange(4):
339 for elementIndex in xrange(4):
340 dotProduct += matrixRow[elementIndex] * otherTetragrid[elementIndex][column]
341 tetragridTimesOtherRow.append(dotProduct)
342 return tetragridTimesOther
344 def getTransformedByList(floatList, point):
345 'Get the point transformed by the array.'
346 return floatList[0] * point.x + floatList[1] * point.y + floatList[2] * point.z + floatList[3]
348 def getTransformedVector3(tetragrid, vector3):
349 'Get the vector3 multiplied by a matrix.'
350 if getIsIdentityTetragridOrNone(tetragrid):
351 return vector3.copy()
352 return getTransformedVector3Blindly(tetragrid, vector3)
354 def getTransformedVector3Blindly(tetragrid, vector3):
355 'Get the vector3 multiplied by a tetragrid without checking if the tetragrid exists.'
357 getTransformedByList(tetragrid[0], vector3),
358 getTransformedByList(tetragrid[1], vector3),
359 getTransformedByList(tetragrid[2], vector3))
361 def getTransformedVector3s(tetragrid, vector3s):
362 'Get the vector3s multiplied by a matrix.'
363 if getIsIdentityTetragridOrNone(tetragrid):
364 return euclidean.getPathCopy(vector3s)
365 transformedVector3s = []
366 for vector3 in vector3s:
367 transformedVector3s.append(getTransformedVector3Blindly(tetragrid, vector3))
368 return transformedVector3s
370 def getTransformTetragrid(elementNode, prefix):
371 'Get the tetragrid from the elementNode.'
372 tetragrid = getTetragridA(elementNode, prefix, None)
373 tetragrid = getTetragridC(elementNode, prefix, tetragrid)
374 tetragrid = getTetragridM(elementNode, prefix, tetragrid)
375 tetragrid = getTetragridMatrix(elementNode, prefix, tetragrid)
376 tetragrid = getTetragridR(elementNode, prefix, tetragrid)
379 def getTranslateTetragrid(elementNode, prefix):
380 'Get translate matrix and delete the translate attributes.'
381 translation = getCumulativeVector3Remove(Vector3(), elementNode, prefix)
382 if translation.getIsDefault():
384 return getTranslateTetragridByTranslation(translation)
386 def getTranslateTetragridByTranslation(translation):
387 'Get translate tetragrid by translation.'
388 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]]
390 def getVertexes(geometryOutput):
393 addVertexes(geometryOutput, vertexes)
396 def setAttributesToMultipliedTetragrid(elementNode, tetragrid):
397 'Set the element attribute dictionary and element matrix to the matrix times the tetragrid.'
398 setElementNodeDictionaryMatrix(elementNode, getBranchMatrix(elementNode).getOtherTimesSelf(tetragrid))
400 def setElementNodeDictionaryMatrix(elementNode, matrix4X4):
401 'Set the element attribute dictionary or element matrix to the matrix.'
402 if elementNode.xmlObject == None:
403 elementNode.attributes.update(matrix4X4.getAttributes('matrix.'))
405 elementNode.xmlObject.matrix4X4 = matrix4X4
407 def transformVector3Blindly(tetragrid, vector3):
408 'Transform the vector3 by a tetragrid without checking to see if it exists.'
409 x = getTransformedByList(tetragrid[0], vector3)
410 y = getTransformedByList(tetragrid[1], vector3)
411 z = getTransformedByList(tetragrid[2], vector3)
416 def transformVector3ByMatrix(tetragrid, vector3):
417 'Transform the vector3 by a matrix.'
418 if getIsIdentityTetragridOrNone(tetragrid):
420 transformVector3Blindly(tetragrid, vector3)
422 def transformVector3sByMatrix(tetragrid, vector3s):
423 'Transform the vector3s by a matrix.'
424 if getIsIdentityTetragridOrNone(tetragrid):
426 for vector3 in vector3s:
427 transformVector3Blindly(tetragrid, vector3)
430 class Matrix(object):
431 'A four by four matrix.'
432 def __init__(self, tetragrid=None):
434 self.tetragrid = getTetragridCopy(tetragrid)
436 def __eq__(self, other):
437 'Determine whether this matrix is identical to other one.'
440 if other.__class__ != self.__class__:
442 return other.tetragrid == self.tetragrid
444 def __ne__(self, other):
445 'Determine whether this vector is not identical to other one.'
446 return not self.__eq__(other)
449 'Get the string representation of this four by four matrix.'
450 output = cStringIO.StringIO()
451 self.addXML(0, output)
452 return output.getvalue()
454 def addXML(self, depth, output):
455 'Add xml for this object.'
456 attributes = self.getAttributes()
457 if len(attributes) > 0:
458 xml_simple_writer.addClosedXMLTag(attributes, depth, self.__class__.__name__.lower(), output)
460 def getAttributes(self, prefix=''):
461 'Get the attributes from row column attribute strings, counting from one.'
463 if self.tetragrid == None:
465 for row in xrange(4):
466 for column in xrange(4):
467 default = float(column == row)
468 value = self.tetragrid[row][column]
469 if abs( value - default ) > 0.00000000000001:
470 if abs(value) < 0.00000000000001:
472 attributes[prefix + getKeyM(row, column)] = value
475 def getFromElementNode(self, elementNode, prefix):
476 'Get the values from row column attribute strings, counting from one.'
477 attributes = elementNode.attributes
478 if attributes == None:
480 self.tetragrid = getTetragridTimesOther(getTransformTetragrid(elementNode, prefix), self.tetragrid)
481 self.tetragrid = getTetragridTimesOther(getScaleTetragrid(elementNode, 'scale.'), self.tetragrid)
482 self.tetragrid = getTetragridTimesOther(getRotateTetragrid(elementNode, 'rotate.'), self.tetragrid)
483 self.tetragrid = getTetragridTimesOther(getTranslateTetragrid(elementNode, 'translate.'), self.tetragrid)
486 def getOtherTimesSelf(self, otherTetragrid):
487 'Get this matrix reverse multiplied by the other matrix.'
488 return Matrix(getTetragridTimesOther(otherTetragrid, self.tetragrid))
490 def getSelfTimesOther(self, otherTetragrid):
491 'Get this matrix multiplied by the other matrix.'
492 return Matrix(getTetragridTimesOther(self.tetragrid, otherTetragrid))