Spaces:
Build error
Build error
#!/usr/bin/env python | |
# encoding: utf-8 | |
# Copyright (c) 2012 Max Planck Society. All rights reserved. | |
""" | |
Math utilities, vector, matrix types and ArcBall quaternion rotation class | |
========================================================================== | |
""" | |
import numpy as Numeric | |
import copy | |
from math import sqrt | |
# //assuming IEEE-754(GLfloat), which i believe has max precision of 7 bits | |
Epsilon = 1.0e-5 | |
class ArcBallT(object): | |
def __init__(self, NewWidth, NewHeight): | |
self.m_StVec = Vector3fT() | |
self.m_EnVec = Vector3fT() | |
self.m_AdjustWidth = 1.0 | |
self.m_AdjustHeight = 1.0 | |
self.setBounds(NewWidth, NewHeight) | |
def __str__(self): | |
str_rep = "" | |
str_rep += "StVec = " + str(self.m_StVec) | |
str_rep += "\nEnVec = " + str(self.m_EnVec) | |
str_rep += "\n scale coords %f %f" % (self.m_AdjustWidth, self.m_AdjustHeight) | |
return str_rep | |
def setBounds(self, NewWidth, NewHeight): | |
# //Set new bounds | |
assert (NewWidth > 1.0 and NewHeight > 1.0), "Invalid width or height for bounds." | |
# //Set adjustment factor for width/height | |
self.m_AdjustWidth = 1.0 / ((NewWidth - 1.0) * 0.5) | |
self.m_AdjustHeight = 1.0 / ((NewHeight - 1.0) * 0.5) | |
def _mapToSphere(self, NewPt): | |
# Given a new window coordinate, will modify NewVec in place | |
X = 0 | |
Y = 1 | |
Z = 2 | |
NewVec = Vector3fT() | |
# //Copy paramter into temp point | |
TempPt = copy.copy(NewPt) | |
# //Adjust point coords and scale down to range of [-1 ... 1] | |
TempPt[X] = (NewPt[X] * self.m_AdjustWidth) - 1.0 | |
TempPt[Y] = 1.0 - (NewPt[Y] * self.m_AdjustHeight) | |
# //Compute the square of the length of the vector to the point from the center | |
length = Numeric.sum(Numeric.dot(TempPt, TempPt)) | |
# //If the point is mapped outside of the sphere... (length > radius squared) | |
if (length > 1.0): | |
# //Compute a normalizing factor (radius / sqrt(length)) | |
norm = 1.0 / sqrt(length) | |
# //Return the "normalized" vector, a point on the sphere | |
NewVec[X] = TempPt[X] * norm | |
NewVec[Y] = TempPt[Y] * norm | |
NewVec[Z] = 0.0 | |
else: # //Else it's on the inside | |
# //Return a vector to a point mapped inside the sphere sqrt(radius squared - length) | |
NewVec[X] = TempPt[X] | |
NewVec[Y] = TempPt[Y] | |
NewVec[Z] = sqrt(1.0 - length) | |
return NewVec | |
def click(self, NewPt): | |
# Mouse down (Point2fT | |
self.m_StVec = self._mapToSphere(NewPt) | |
return | |
def drag(self, NewPt): | |
# Mouse drag, calculate rotation (Point2fT Quat4fT) | |
""" drag (Point2fT mouse_coord) -> new_quaternion_rotation_vec | |
""" | |
X = 0 | |
Y = 1 | |
Z = 2 | |
W = 3 | |
self.m_EnVec = self._mapToSphere(NewPt) | |
# //Compute the vector perpendicular to the begin and end vectors | |
# Perp = Vector3fT () | |
Perp = Vector3fCross(self.m_StVec, self.m_EnVec) | |
NewRot = Quat4fT() | |
# Compute the length of the perpendicular vector | |
if (Vector3fLength(Perp) > Epsilon): | |
# if its non-zero | |
# We're ok, so return the perpendicular vector as the transform after all | |
NewRot[X] = Perp[X] | |
NewRot[Y] = Perp[Y] | |
NewRot[Z] = Perp[Z] | |
# //In the quaternion values, w is cosine (theta / 2), where theta is rotation angle | |
NewRot[W] = Vector3fDot(self.m_StVec, self.m_EnVec) | |
else: | |
# if its zero | |
# The begin and end vectors coincide, so return a quaternion of zero matrix (no rotation) | |
NewRot[X] = NewRot[Y] = NewRot[Z] = NewRot[W] = 0.0 | |
return NewRot | |
def Matrix4fT(): | |
return Numeric.identity(4, 'f') | |
def Matrix3fT(): | |
return Numeric.identity(3, 'f') | |
def Quat4fT(): | |
return Numeric.zeros(4, 'f') | |
def Vector3fT(): | |
return Numeric.zeros(3, 'f') | |
def Point2fT(x=0.0, y=0.0): | |
pt = Numeric.zeros(2, 'f') | |
pt[0] = x | |
pt[1] = y | |
return pt | |
def Vector3fDot(u, v): | |
# Dot product of two 3f vectors | |
dotprod = Numeric.dot(u, v) | |
return dotprod | |
def Vector3fCross(u, v): | |
# Cross product of two 3f vectors | |
X = 0 | |
Y = 1 | |
Z = 2 | |
cross = Numeric.zeros(3, 'f') | |
cross[X] = (u[Y] * v[Z]) - (u[Z] * v[Y]) | |
cross[Y] = (u[Z] * v[X]) - (u[X] * v[Z]) | |
cross[Z] = (u[X] * v[Y]) - (u[Y] * v[X]) | |
return cross | |
def Vector3fLength(u): | |
mag_squared = Numeric.sum(Numeric.dot(u, u)) | |
mag = sqrt(mag_squared) | |
return mag | |
def Matrix3fSetIdentity(): | |
return Numeric.identity(3, 'f') | |
def Matrix3fMulMatrix3f(matrix_a, matrix_b): | |
return matrix_a.dot(matrix_b) | |
def Matrix4fSVD(NewObj): | |
X = 0 | |
Y = 1 | |
Z = 2 | |
s = sqrt(((NewObj[X][X] * NewObj[X][X]) + (NewObj[X][Y] * NewObj[X][Y]) + (NewObj[X][Z] * NewObj[X][Z]) + | |
(NewObj[Y][X] * NewObj[Y][X]) + (NewObj[Y][Y] * NewObj[Y][Y]) + (NewObj[Y][Z] * NewObj[Y][Z]) + | |
(NewObj[Z][X] * NewObj[Z][X]) + (NewObj[Z][Y] * NewObj[Z][Y]) + (NewObj[Z][Z] * NewObj[Z][Z])) / 3.0) | |
return s | |
def Matrix4fSetRotationScaleFromMatrix3f(NewObj, three_by_three_matrix): | |
"""Modifies NewObj in-place by replacing its upper 3x3 portion from the | |
passed in 3x3 matrix. | |
:param NewObj: a `Matrix4fT` | |
""" | |
NewObj[0:3, 0:3] = three_by_three_matrix | |
return NewObj | |
def Matrix4fSetRotationFromMatrix3f(NewObj, three_by_three_matrix): | |
""" | |
Sets the rotational component (upper 3x3) of this matrix to the matrix | |
values in the T precision Matrix3d argument; the other elements of | |
this matrix are unchanged; a singular value decomposition is performed | |
on this object's upper 3x3 matrix to factor out the scale, then this | |
object's upper 3x3 matrix components are replaced by the passed rotation | |
components, and then the scale is reapplied to the rotational | |
components. | |
:param three_by_three_matrix: T precision 3x3 matrix | |
""" | |
scale = Matrix4fSVD(NewObj) | |
NewObj = Matrix4fSetRotationScaleFromMatrix3f(NewObj, three_by_three_matrix) | |
scaled_NewObj = NewObj * scale # Matrix4fMulRotationScale(NewObj, scale); | |
return scaled_NewObj | |
def Matrix3fSetRotationFromQuat4f(q1): | |
"""Converts the H quaternion q1 into a new equivalent 3x3 rotation matrix.""" | |
X = 0 | |
Y = 1 | |
Z = 2 | |
W = 3 | |
NewObj = Matrix3fT() | |
n = Numeric.sum(Numeric.dot(q1, q1)) | |
s = 0.0 | |
if (n > 0.0): | |
s = 2.0 / n | |
xs = q1[X] * s | |
ys = q1[Y] * s | |
zs = q1[Z] * s | |
wx = q1[W] * xs | |
wy = q1[W] * ys | |
wz = q1[W] * zs | |
xx = q1[X] * xs | |
xy = q1[X] * ys | |
xz = q1[X] * zs | |
yy = q1[Y] * ys | |
yz = q1[Y] * zs | |
zz = q1[Z] * zs | |
# This math all comes about by way of algebra, complex math, and trig identities. | |
# See Lengyel pages 88-92 | |
NewObj[X][X] = 1.0 - (yy + zz) | |
NewObj[Y][X] = xy - wz | |
NewObj[Z][X] = xz + wy | |
NewObj[X][Y] = xy + wz | |
NewObj[Y][Y] = 1.0 - (xx + zz) | |
NewObj[Z][Y] = yz - wx | |
NewObj[X][Z] = xz - wy | |
NewObj[Y][Z] = yz + wx | |
NewObj[Z][Z] = 1.0 - (xx + yy) | |
return NewObj | |