""" Boolean geometry four by four matrix. """ from __future__ import absolute_import #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. import __init__ from fabmetheus_utilities.geometry.geometry_utilities import evaluate from fabmetheus_utilities.vector3 import Vector3 from fabmetheus_utilities import archive from fabmetheus_utilities import euclidean from fabmetheus_utilities import xml_simple_writer import cStringIO import math __author__ = 'Enrique Perez (perez_enrique@yahoo.com)' __credits__ = 'Art of Illusion ' __date__ = '$Date: 2008/02/05 $' __license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html' globalExecutionOrder = 300 def addVertexes(geometryOutput, vertexes): 'Add the vertexes.' if geometryOutput.__class__ == list: for element in geometryOutput: addVertexes(element, vertexes) return if geometryOutput.__class__ == dict: for geometryOutputKey in geometryOutput.keys(): if geometryOutputKey == 'vertex': vertexes += geometryOutput[geometryOutputKey] else: addVertexes(geometryOutput[geometryOutputKey], vertexes) def getBranchMatrix(elementNode): 'Get matrix starting from the object if it exists, otherwise get a matrix starting from stratch.' branchMatrix = Matrix() matrixChildElement = elementNode.getFirstChildByLocalName('matrix') if matrixChildElement != None: branchMatrix = branchMatrix.getFromElementNode(matrixChildElement, '') branchMatrix = branchMatrix.getFromElementNode(elementNode, 'matrix.') if elementNode.xmlObject == None: return branchMatrix elementNodeMatrix = elementNode.xmlObject.getMatrix4X4() if elementNodeMatrix == None: return branchMatrix return elementNodeMatrix.getOtherTimesSelf(branchMatrix.tetragrid) def getBranchMatrixSetElementNode(elementNode): 'Get matrix starting from the object if it exists, otherwise get a matrix starting from stratch.' branchMatrix = getBranchMatrix(elementNode) setElementNodeDictionaryMatrix(elementNode, branchMatrix) return branchMatrix def getCumulativeVector3Remove(defaultVector3, elementNode, prefix): 'Get cumulative vector3 and delete the prefixed attributes.' if prefix == '': defaultVector3.x = evaluate.getEvaluatedFloat(defaultVector3.x, elementNode, 'x') defaultVector3.y = evaluate.getEvaluatedFloat(defaultVector3.y, elementNode, 'y') defaultVector3.z = evaluate.getEvaluatedFloat(defaultVector3.z, elementNode, 'z') euclidean.removeElementsFromDictionary(elementNode.attributes, ['x', 'y', 'z']) prefix = 'cartesian' defaultVector3 = evaluate.getVector3ByPrefix(defaultVector3, elementNode, prefix) euclidean.removePrefixFromDictionary(elementNode.attributes, prefix) return defaultVector3 def getDiagonalSwitchedTetragrid(angleDegrees, diagonals): 'Get the diagonals and switched matrix by degrees.' return getDiagonalSwitchedTetragridByRadians(math.radians(angleDegrees), diagonals) def getDiagonalSwitchedTetragridByPolar(diagonals, unitPolar): 'Get the diagonals and switched matrix by unitPolar.' diagonalSwitchedTetragrid = getIdentityTetragrid() for diagonal in diagonals: diagonalSwitchedTetragrid[diagonal][diagonal] = unitPolar.real diagonalSwitchedTetragrid[diagonals[0]][diagonals[1]] = -unitPolar.imag diagonalSwitchedTetragrid[diagonals[1]][diagonals[0]] = unitPolar.imag return diagonalSwitchedTetragrid def getDiagonalSwitchedTetragridByRadians(angleRadians, diagonals): 'Get the diagonals and switched matrix by radians.' return getDiagonalSwitchedTetragridByPolar(diagonals, euclidean.getWiddershinsUnitPolar(angleRadians)) def getIdentityTetragrid(tetragrid=None): 'Get four by four matrix with diagonal elements set to one.' if tetragrid == None: 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]] return tetragrid def getIsIdentityTetragrid(tetragrid): 'Determine if the tetragrid is the identity tetragrid.' for column in xrange(4): for row in xrange(4): if column == row: if tetragrid[column][row] != 1.0: return False elif tetragrid[column][row] != 0.0: return False return True def getIsIdentityTetragridOrNone(tetragrid): 'Determine if the tetragrid is None or if it is the identity tetragrid.' if tetragrid == None: return True return getIsIdentityTetragrid(tetragrid) def getKeyA(row, column, prefix=''): 'Get the a format key string from row & column, counting from zero.' return '%sa%s%s' % (prefix, row, column) def getKeyM(row, column, prefix=''): 'Get the m format key string from row & column, counting from one.' return '%sm%s%s' % (prefix, row + 1, column + 1) def getKeysA(prefix=''): 'Get the matrix keys, counting from zero.' keysA = [] for row in xrange(4): for column in xrange(4): key = getKeyA(row, column, prefix) keysA.append(key) return keysA def getKeysM(prefix=''): 'Get the matrix keys, counting from one.' keysM = [] for row in xrange(4): for column in xrange(4): key = getKeyM(row, column, prefix) keysM.append(key) return keysM def getRemovedFloat(defaultFloat, elementNode, key, prefix): 'Get the float by the key and the prefix.' prefixKey = prefix + key if prefixKey in elementNode.attributes: floatValue = evaluate.getEvaluatedFloat(None, elementNode, prefixKey) if floatValue == None: print('Warning, evaluated value in getRemovedFloatByKeys in matrix is None for key:') print(prefixKey) print('for elementNode dictionary value:') print(elementNode.attributes[prefixKey]) print('for elementNode dictionary:') print(elementNode.attributes) else: defaultFloat = floatValue del elementNode.attributes[prefixKey] return defaultFloat def getRemovedFloatByKeys(defaultFloat, elementNode, keys, prefix): 'Get the float by the keys and the prefix.' for key in keys: defaultFloat = getRemovedFloat(defaultFloat, elementNode, key, prefix) return defaultFloat def getRotateAroundAxisTetragrid(elementNode, prefix): 'Get rotate around axis tetragrid and delete the axis and angle attributes.' angle = getRemovedFloatByKeys(0.0, elementNode, ['angle', 'counterclockwise'], prefix) angle -= getRemovedFloat(0.0, elementNode, 'clockwise', prefix) if angle == 0.0: return None angleRadians = math.radians(angle) axis = getCumulativeVector3Remove(Vector3(), elementNode, prefix + 'axis') axisLength = abs(axis) if axisLength <= 0.0: print('Warning, axisLength was zero in getRotateAroundAxisTetragrid in matrix so nothing will be done for:') print(elementNode) return None axis /= axisLength tetragrid = getIdentityTetragrid() cosAngle = math.cos(angleRadians) sinAngle = math.sin(angleRadians) oneMinusCos = 1.0 - math.cos(angleRadians) xx = axis.x * axis.x xy = axis.x * axis.y xz = axis.x * axis.z yy = axis.y * axis.y yz = axis.y * axis.z zz = axis.z * axis.z tetragrid[0] = [cosAngle + xx * oneMinusCos, xy * oneMinusCos - axis.z * sinAngle, xz * oneMinusCos + axis.y * sinAngle, 0.0] tetragrid[1] = [xy * oneMinusCos + axis.z * sinAngle, cosAngle + yy * oneMinusCos, yz * oneMinusCos - axis.x * sinAngle, 0.0] tetragrid[2] = [xz * oneMinusCos - axis.y * sinAngle, yz * oneMinusCos + axis.x * sinAngle, cosAngle + zz * oneMinusCos, 0.0] return tetragrid def getRotateTetragrid(elementNode, prefix): 'Get rotate tetragrid and delete the rotate attributes.' # http://en.wikipedia.org/wiki/Rotation_matrix rotateMatrix = Matrix() rotateMatrix.tetragrid = getRotateAroundAxisTetragrid(elementNode, prefix) zAngle = getRemovedFloatByKeys(0.0, elementNode, ['axisclockwisez', 'observerclockwisez', 'z'], prefix) zAngle -= getRemovedFloatByKeys(0.0, elementNode, ['axiscounterclockwisez', 'observercounterclockwisez'], prefix) if zAngle != 0.0: rotateMatrix.tetragrid = getTetragridTimesOther(getDiagonalSwitchedTetragrid(-zAngle, [0, 1]), rotateMatrix.tetragrid) xAngle = getRemovedFloatByKeys(0.0, elementNode, ['axisclockwisex', 'observerclockwisex', 'x'], prefix) xAngle -= getRemovedFloatByKeys(0.0, elementNode, ['axiscounterclockwisex', 'observercounterclockwisex'], prefix) if xAngle != 0.0: rotateMatrix.tetragrid = getTetragridTimesOther(getDiagonalSwitchedTetragrid(-xAngle, [1, 2]), rotateMatrix.tetragrid) yAngle = getRemovedFloatByKeys(0.0, elementNode, ['axiscounterclockwisey', 'observerclockwisey', 'y'], prefix) yAngle -= getRemovedFloatByKeys(0.0, elementNode, ['axisclockwisey', 'observercounterclockwisey'], prefix) if yAngle != 0.0: rotateMatrix.tetragrid = getTetragridTimesOther(getDiagonalSwitchedTetragrid(yAngle, [0, 2]), rotateMatrix.tetragrid) return rotateMatrix.tetragrid def getScaleTetragrid(elementNode, prefix): 'Get scale matrix and delete the scale attributes.' scaleDefaultVector3 = Vector3(1.0, 1.0, 1.0) scale = getCumulativeVector3Remove(scaleDefaultVector3.copy(), elementNode, prefix) if scale == scaleDefaultVector3: return None 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]] def getTetragridA(elementNode, prefix, tetragrid): 'Get the tetragrid from the elementNode letter a values.' keysA = getKeysA(prefix) evaluatedDictionary = evaluate.getEvaluatedDictionaryByEvaluationKeys(elementNode, keysA) if len(evaluatedDictionary.keys()) < 1: return tetragrid for row in xrange(4): for column in xrange(4): key = getKeyA(row, column, prefix) if key in evaluatedDictionary: value = evaluatedDictionary[key] if value == None or value == 'None': print('Warning, value in getTetragridA in matrix is None for key for dictionary:') print(key) print(evaluatedDictionary) else: tetragrid = getIdentityTetragrid(tetragrid) tetragrid[row][column] = float(value) euclidean.removeElementsFromDictionary(elementNode.attributes, keysA) return tetragrid def getTetragridC(elementNode, prefix, tetragrid): 'Get the matrix Tetragrid from the elementNode letter c values.' columnKeys = 'Pc1 Pc2 Pc3 Pc4'.replace('P', prefix).split() evaluatedDictionary = evaluate.getEvaluatedDictionaryByEvaluationKeys(elementNode, columnKeys) if len(evaluatedDictionary.keys()) < 1: return tetragrid for columnKeyIndex, columnKey in enumerate(columnKeys): if columnKey in evaluatedDictionary: value = evaluatedDictionary[columnKey] if value == None or value == 'None': print('Warning, value in getTetragridC in matrix is None for columnKey for dictionary:') print(columnKey) print(evaluatedDictionary) else: tetragrid = getIdentityTetragrid(tetragrid) for elementIndex, element in enumerate(value): tetragrid[elementIndex][columnKeyIndex] = element euclidean.removeElementsFromDictionary(elementNode.attributes, columnKeys) return tetragrid def getTetragridCopy(tetragrid): 'Get tetragrid copy.' if tetragrid == None: return None tetragridCopy = [] for tetragridRow in tetragrid: tetragridCopy.append(tetragridRow[:]) return tetragridCopy def getTetragridM(elementNode, prefix, tetragrid): 'Get the tetragrid from the elementNode letter m values.' keysM = getKeysM(prefix) evaluatedDictionary = evaluate.getEvaluatedDictionaryByEvaluationKeys(elementNode, keysM) if len(evaluatedDictionary.keys()) < 1: return tetragrid for row in xrange(4): for column in xrange(4): key = getKeyM(row, column, prefix) if key in evaluatedDictionary: value = evaluatedDictionary[key] if value == None or value == 'None': print('Warning, value in getTetragridM in matrix is None for key for dictionary:') print(key) print(evaluatedDictionary) else: tetragrid = getIdentityTetragrid(tetragrid) tetragrid[row][column] = float(value) euclidean.removeElementsFromDictionary(elementNode.attributes, keysM) return tetragrid def getTetragridMatrix(elementNode, prefix, tetragrid): 'Get the tetragrid from the elementNode matrix value.' matrixKey = prefix + 'matrix' evaluatedDictionary = evaluate.getEvaluatedDictionaryByEvaluationKeys(elementNode, [matrixKey]) if len(evaluatedDictionary.keys()) < 1: return tetragrid value = evaluatedDictionary[matrixKey] if value == None or value == 'None': print('Warning, value in getTetragridMatrix in matrix is None for matrixKey for dictionary:') print(matrixKey) print(evaluatedDictionary) else: tetragrid = getIdentityTetragrid(tetragrid) for rowIndex, row in enumerate(value): for elementIndex, element in enumerate(row): tetragrid[rowIndex][elementIndex] = element euclidean.removeElementsFromDictionary(elementNode.attributes, [matrixKey]) return tetragrid def getTetragridR(elementNode, prefix, tetragrid): 'Get the tetragrid from the elementNode letter r values.' rowKeys = 'Pr1 Pr2 Pr3 Pr4'.replace('P', prefix).split() evaluatedDictionary = evaluate.getEvaluatedDictionaryByEvaluationKeys(elementNode, rowKeys) if len(evaluatedDictionary.keys()) < 1: return tetragrid for rowKeyIndex, rowKey in enumerate(rowKeys): if rowKey in evaluatedDictionary: value = evaluatedDictionary[rowKey] if value == None or value == 'None': print('Warning, value in getTetragridR in matrix is None for rowKey for dictionary:') print(rowKey) print(evaluatedDictionary) else: tetragrid = getIdentityTetragrid(tetragrid) for elementIndex, element in enumerate(value): tetragrid[rowKeyIndex][elementIndex] = element euclidean.removeElementsFromDictionary(elementNode.attributes, rowKeys) return tetragrid def getTetragridTimesOther(firstTetragrid, otherTetragrid ): 'Get this matrix multiplied by the other matrix.' #A down, B right from http://en.wikipedia.org/wiki/Matrix_multiplication if firstTetragrid == None: return otherTetragrid if otherTetragrid == None: return firstTetragrid tetragridTimesOther = [] for row in xrange(4): matrixRow = firstTetragrid[row] tetragridTimesOtherRow = [] tetragridTimesOther.append(tetragridTimesOtherRow) for column in xrange(4): dotProduct = 0 for elementIndex in xrange(4): dotProduct += matrixRow[elementIndex] * otherTetragrid[elementIndex][column] tetragridTimesOtherRow.append(dotProduct) return tetragridTimesOther def getTransformedByList(floatList, point): 'Get the point transformed by the array.' return floatList[0] * point.x + floatList[1] * point.y + floatList[2] * point.z + floatList[3] def getTransformedVector3(tetragrid, vector3): 'Get the vector3 multiplied by a matrix.' if getIsIdentityTetragridOrNone(tetragrid): return vector3.copy() return getTransformedVector3Blindly(tetragrid, vector3) def getTransformedVector3Blindly(tetragrid, vector3): 'Get the vector3 multiplied by a tetragrid without checking if the tetragrid exists.' return Vector3( getTransformedByList(tetragrid[0], vector3), getTransformedByList(tetragrid[1], vector3), getTransformedByList(tetragrid[2], vector3)) def getTransformedVector3s(tetragrid, vector3s): 'Get the vector3s multiplied by a matrix.' if getIsIdentityTetragridOrNone(tetragrid): return euclidean.getPathCopy(vector3s) transformedVector3s = [] for vector3 in vector3s: transformedVector3s.append(getTransformedVector3Blindly(tetragrid, vector3)) return transformedVector3s def getTransformTetragrid(elementNode, prefix): 'Get the tetragrid from the elementNode.' tetragrid = getTetragridA(elementNode, prefix, None) tetragrid = getTetragridC(elementNode, prefix, tetragrid) tetragrid = getTetragridM(elementNode, prefix, tetragrid) tetragrid = getTetragridMatrix(elementNode, prefix, tetragrid) tetragrid = getTetragridR(elementNode, prefix, tetragrid) return tetragrid def getTranslateTetragrid(elementNode, prefix): 'Get translate matrix and delete the translate attributes.' translation = getCumulativeVector3Remove(Vector3(), elementNode, prefix) if translation.getIsDefault(): return None return getTranslateTetragridByTranslation(translation) def getTranslateTetragridByTranslation(translation): 'Get translate tetragrid by translation.' 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]] def getVertexes(geometryOutput): 'Get the vertexes.' vertexes = [] addVertexes(geometryOutput, vertexes) return vertexes def setAttributesToMultipliedTetragrid(elementNode, tetragrid): 'Set the element attribute dictionary and element matrix to the matrix times the tetragrid.' setElementNodeDictionaryMatrix(elementNode, getBranchMatrix(elementNode).getOtherTimesSelf(tetragrid)) def setElementNodeDictionaryMatrix(elementNode, matrix4X4): 'Set the element attribute dictionary or element matrix to the matrix.' if elementNode.xmlObject == None: elementNode.attributes.update(matrix4X4.getAttributes('matrix.')) else: elementNode.xmlObject.matrix4X4 = matrix4X4 def transformVector3Blindly(tetragrid, vector3): 'Transform the vector3 by a tetragrid without checking to see if it exists.' x = getTransformedByList(tetragrid[0], vector3) y = getTransformedByList(tetragrid[1], vector3) z = getTransformedByList(tetragrid[2], vector3) vector3.x = x vector3.y = y vector3.z = z def transformVector3ByMatrix(tetragrid, vector3): 'Transform the vector3 by a matrix.' if getIsIdentityTetragridOrNone(tetragrid): return transformVector3Blindly(tetragrid, vector3) def transformVector3sByMatrix(tetragrid, vector3s): 'Transform the vector3s by a matrix.' if getIsIdentityTetragridOrNone(tetragrid): return for vector3 in vector3s: transformVector3Blindly(tetragrid, vector3) class Matrix: 'A four by four matrix.' def __init__(self, tetragrid=None): 'Add empty lists.' self.tetragrid = getTetragridCopy(tetragrid) def __eq__(self, other): 'Determine whether this matrix is identical to other one.' if other == None: return False if other.__class__ != self.__class__: return False return other.tetragrid == self.tetragrid def __ne__(self, other): 'Determine whether this vector is not identical to other one.' return not self.__eq__(other) def __repr__(self): 'Get the string representation of this four by four matrix.' output = cStringIO.StringIO() self.addXML(0, output) return output.getvalue() def addXML(self, depth, output): 'Add xml for this object.' attributes = self.getAttributes() if len(attributes) > 0: xml_simple_writer.addClosedXMLTag(attributes, depth, self.__class__.__name__.lower(), output) def getAttributes(self, prefix=''): 'Get the attributes from row column attribute strings, counting from one.' attributes = {} if self.tetragrid == None: return attributes for row in xrange(4): for column in xrange(4): default = float(column == row) value = self.tetragrid[row][column] if abs( value - default ) > 0.00000000000001: if abs(value) < 0.00000000000001: value = 0.0 attributes[prefix + getKeyM(row, column)] = value return attributes def getFromElementNode(self, elementNode, prefix): 'Get the values from row column attribute strings, counting from one.' attributes = elementNode.attributes if attributes == None: return self self.tetragrid = getTetragridTimesOther(getTransformTetragrid(elementNode, prefix), self.tetragrid) self.tetragrid = getTetragridTimesOther(getScaleTetragrid(elementNode, 'scale.'), self.tetragrid) self.tetragrid = getTetragridTimesOther(getRotateTetragrid(elementNode, 'rotate.'), self.tetragrid) self.tetragrid = getTetragridTimesOther(getTranslateTetragrid(elementNode, 'translate.'), self.tetragrid) return self def getOtherTimesSelf(self, otherTetragrid): 'Get this matrix reverse multiplied by the other matrix.' return Matrix(getTetragridTimesOther(otherTetragrid, self.tetragrid)) def getSelfTimesOther(self, otherTetragrid): 'Get this matrix multiplied by the other matrix.' return Matrix(getTetragridTimesOther(self.tetragrid, otherTetragrid))