Source code for klampt.vis.camera

"""This module defines a basic set of 3D cameras that can be controlled
by a GUI."""

from ..math import so3,se3,vectorops

basis_vectors = {'x':[1.0,0.0,0.0],
                 '-x':[-1.0,0.0,0.0],
                 'y':[0.0,1.0,0.0],
                 '-y':[0.0,-1.0,0.0],
                 'z':[0.0,0.0,1.0],
                 '-z':[0.0,0.0,-1.0]}

[docs]def orientation_matrix(axis1,axis2,axis3): """Returns the matrix that maps the camera's identity coordinate system (right,down,forward) to world axes 1,2,3 (assuming no camera translation). Each axis can be either a 3-tuple or any element of ['x','y','z','-x','-y','-z']""" if isinstance(axis1,str): axis1 = basis_vectors[axis1] if isinstance(axis2,str): axis2 = basis_vectors[axis2] if isinstance(axis3,str): axis3 = basis_vectors[axis3] return so3.from_matrix([axis1,axis2,axis3])
[docs]class free: """A free-floating camera that is controlled using a translation and euler angle rotation vector. Attributes: - pos: camera center position - rot: euler angle rotation - ori: orientation matrix type (see :func:`orientation_matrix`) """ def __init__(self): self.pos = [0.,0.,0.] self.rot = [0.,0.,0.] self.ori = ['x','-z','y']
[docs] def matrix(self): """Returns the camera transform. Applying this transform converts points in OpenGL camera coordinates (x right, y up, -z forward), to points in world coordinates.""" o = orientation_matrix(*self.ori) Rz = so3.rotation([0.,0.,1.],self.rot[2]) Rx = so3.rotation([1.,0.,0.],self.rot[1]) Ry = so3.rotation([0.,1.,0.],self.rot[0]) R = so3.mul(Rz,so3.mul(Rx,Ry)) R = so3.mul(R,o); raise R
[docs] def set_matrix(self,T): raise NotImplementedError("Can't set free camera matrix yet")
[docs]class target: """A look-at camera that is controlled using a translation, target point, and up vector Attributes: - pos: camera center position - tgt: target point - up: up direction """ def __init__(self): #center self.pos = [0.,0.,0.] #target point self.tgt = [1.,0.,0.] #up vector self.up = [0.,0.,1.]
[docs] def matrix(self): """Returns the camera transform. Applying this transform converts points in OpenGL camera coordinates (x right, y up, -z forward), to points in world coordinates.""" raise NotImplementedError()
[docs] def set_matrix(self,T): raise NotImplementedError("Can't set target camera matrix yet")
[docs]class orbit: """An orbit camera that is controlled using a rotation, target point, distance, and orientation. Attributes: - tgt: target point (in world coordinates) - rot: euler angle rotation (roll-pitch-yaw entries relative to default view with fwd = +y, right = +x, up = +z) - dist: target distance - ori: orientation matrix type (see :func:`orientation_matrix`) """ def __init__(self): #euler angle rotation self.rot = [0.,0.,0.] #target point self.tgt = [0.,0.,0.] #target distance self.dist = 1.0 #orientation self.ori = ['x','-z','y']
[docs] def matrix(self): """Returns the camera transform. Applying this transform converts points in OpenGL camera coordinates (x right, y up, -z forward), to points in world coordinates.""" o = orientation_matrix(*self.ori) Rz = so3.rotation([0.,0.,1.],self.rot[2]) Rx = so3.rotation([1.,0.,0.],self.rot[1]) Ry = so3.rotation([0.,1.,0.],self.rot[0]) R = so3.mul(Rz,so3.mul(Rx,Ry)) R = so3.mul(R,o) return (R,vectorops.add(self.tgt,so3.apply(R,[0.,0.,self.dist])))
[docs] def set_orientation(self,R,ori=None): """Sets the orientation of the camera to the so3 element R. If ori is provided, it is an orientation list (e.g., ['x','y','z']) that tells the function how to interpret the columns of R in terms of the right, down, and fwd axes of the camera. Its default value is None. """ import math #Rdes*oR*[right,down,fwd] = R_euler(rot)*o*[right,down,fwd] if ori is not None: o = orientation_matrix(*self.ori) oR = orientation_matrix(*ori) R = so3.mul(R,so3.mul(so3.inv(oR),o)) #Ry = so3.rotation([0,1,0],self.rot[0]) #Rx = so3.rotation([1,0,0],self.rot[1]) #Rz = so3.rotation([0,0,1],self.rot[2]) #set self.rot to fulfill constraint R = Rz*Rx*Ry # [cz -sz 0][1 0 0][cy 0 sy] [cz -szcx szsx][cy 0 sy] # R = [sz cz 0][0 cx -sx][0 1 0] = [sz czcx -czsx][0 1 0 ] # [0 0 1][0 sx cx][-sy 0 cy] [0 sx cx][-sy 0 cy] # [czcy-szsxsy -szcx czsy+szsxcy ] # = [szcy+czsxsy czcx szsy-czsxcy ] # [-cxsy sx cxcy ] m = so3.matrix(R) cx = math.sqrt(m[0][1]**2 + m[1][1]**2) #assume cx is positive (pitch in range [-pi/2,pi/2]) sx = m[2][1] self.rot[1] = math.atan2(sx,cx) if abs(cx) > 1e-5: sz = -m[0][1] cz = m[1][1] sy = -m[2][0] cy = m[2][2] self.rot[2] = math.atan2(sz,cz) self.rot[0] = math.atan2(sy,cy) else: #near vertical, have redundancy, have cx=0,sx = +/-1, set Ry=0 (so cy=1, sy=0) self.rot[0] = 0 cz = m[0][0] sz = m[1][0] self.rot[2] = math.atan2(sz,cz)
[docs] def set_matrix(self,T): """Restores from a matrix retrieved using matrix()""" R,t = T #self.rot fulfills constraint R = Rz*Rx*Ry*ori o = orientation_matrix(*self.ori) R2 = so3.mul(R,so3.inv(o)) # [czcy-szsxsy -szcx czsy+szsxcy ] #R2 = [szcy+czsxsy czcx szsy-czsxcy ] # [-cxsy sx cxcy ] self.set_orientation(R2,None) #tgt + R*[0,0,dist] = t self.tgt = vectorops.sub(t,so3.apply(R,[0,0,self.dist]))