def __init__(self, M = np.eye(4), params = None): self.children = [] self.M = M if params is not None: rot_angles = np.array(params.get('rotation', [0., 0., 0.])) translate_amount = np.array(params.get('translation', [0., 0., 0.])) scale_amount = np.array(params.get('scale', [1., 1., 1.])) # compute the transformation matrix that gets applied to all children of this node Tform = GT.translate(translate_amount) * GT.rotateX(rot_angles[0]) * \ GT.rotateY(rot_angles[1]) * GT.rotateZ(rot_angles[2]) * \ GT.scale(scale_amount) self.M = Tform.getA() self.Minv = np.linalg.inv(self.M)
def __init__(self, M=np.eye(4), params=None): self.children = [] self.M = M if params is not None: rot_angles = np.array(params.get('rotation', [0., 0., 0.])) translate_amount = np.array(params.get('translation', [0., 0., 0.])) scale_amount = np.array(params.get('scale', [1., 1., 1.])) # compute the transformation matrix that # gets applied to all children of this node Tform = GT.translate(translate_amount) * GT.rotateX(rot_angles[0]) * \ GT.rotateY(rot_angles[1]) * GT.rotateZ(rot_angles[2]) * \ GT.scale(scale_amount) self.M = Tform.getA() self.Minv = np.linalg.inv(self.M)
def draw_volume(self): ########################## eye_dir = GT.normalize(self.view.eye); # 1. ANGLE-AXIS SOL BEGIN ''' get_angle_axis_matrix will return a rotation matrix after accounting for degenerate cases when axis of rotation is 0 i.e. angle 0 or 180 degs. In the case of 180 degs rotation is performed about the default rotation axis (last argument). Note that the 2nd rotation is necessary to prevent the quads from rotating. If the quads rotate then for certain view directions the quads will not cover the entire texture. ''' M, angle_deg, axis_of_rotation = get_angle_axis_matrix([0, 0, 1], eye_dir, [0, 1, 0]) new_up = np.dot(M[:3,:3], [0, 1, 0]) # orientation independent of view.up world_up_proj_on_quad = np.array([0, 1, 0]) - eye_dir[1] * eye_dir Mup, angle_deg, axis_of_rotation = get_angle_axis_matrix(new_up, world_up_proj_on_quad, eye_dir) M_angleaxis = np.dot(Mup, M) # ANGLE-AXIS SOL END # 2. Direct matrix computation BEGIN # directly compute the same matrix as above quad_right = GT.normalize(np.cross([0, 1, 0], eye_dir)) if np.linalg.norm(quad_right) < 1e-6: # eye_dir is parallel to [0, 1, 0] quad_right = [1, 0, 0] quad_up = GT.normalize(np.cross(eye_dir, quad_right)) M_direct = np.eye(4) M_direct[:3,2] = eye_dir M_direct[:3,0] = quad_right M_direct[:3,1] = quad_up # Direct matrix computation END # 3. Y-X rotation based solution BEGIN # rotation about the y-axis degY = np.rad2deg(np.arctan2(eye_dir[0], eye_dir[2])) # eye.z -> x and eye.x -> y # rotation about x-axis zx = np.linalg.norm([eye_dir[0], eye_dir[2]]) # length of the projection of unit dir vector on the xz plane degX = -np.rad2deg(np.arctan2(eye_dir[1], zx)) # Construct rotation matrix mX = GT.rotateX(degX) mY = GT.rotateY(degY) M_RotYX = np.dot(mY, mX) # Y-X rotation based solution END # compare all 3 solutions which should produce the same matrix except # when viewing from top/bottom the orientation of the quad may differ by 90 degs np.testing.assert_array_almost_equal(M_direct[:3,:3], M_angleaxis[:3,:3], decimal=6) np.testing.assert_array_almost_equal(M_direct[:3,:3], M_RotYX[:3,:3], decimal=6) M = M_direct glMatrixMode(GL_MODELVIEW) # switch back to modelview matrix glPushMatrix() glMultMatrixd(M.T) glPushAttrib(GL_ALL_ATTRIB_BITS) glColor3f(1, 0, 0) glutWireCube(1.0) glPopAttrib() glPopMatrix() ''' TO DO: have the quads (slices) face the viewer ''' glEnable(GL_BLEND) # glBlendFunc can be set during init but it's set here just to have all relevant things in one place glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) #glDisable(GL_CULL_FACE) glEnable(GL_TEXTURE_3D) # Yet another solution: using inverse lookat matrix # # compute view matrix # M = GT.lookAtMatrix(self.view.eye, self.view.eye + self.view.lookat, self.view.up).getA() # # compute inverse view matrix # Minv = np.linalg.inv(M) # # Minv is the view inverse and transforms the geometry to the camera's origin # # The slices will move and translate with the camera # # GT.translate applies translation so that geometry is in front of the camera # Tform = np.dot(Minv, GT.translate([0., 0., -2.]).getA()) ''' Draw all the NUMSLICES quads with corresponding texture coordinates. ''' # the z-coordinate represents the depth of each slice in the bounding box for z in np.linspace(-0.5, 0.5, self.NUMSLICES): # the 1st row corresponds to the x-coordinates and 2nd row y-coordinates of the slices. quad_coord = np.array([[0, 1, 1, 0], [0, 0, 1, 1]]) - 0.5 # add row for z-coord quad_coord = np.vstack((quad_coord,np.array([z,z,z,z]))) # add row for homogeneous coord quad_coord = np.vstack((quad_coord,np.array([1,1,1,1]))) # Apply rotation to slice coordinates quad_coord = np.dot(M, quad_coord) glBegin(GL_QUADS) for i in range(4): glTexCoord3f(quad_coord[0][i] + .5 , quad_coord[1][i] + .5 , quad_coord[2][i] + .5) glVertex3f(quad_coord[0][i], quad_coord[1][i], quad_coord[2][i]) glEnd() glDisable(GL_TEXTURE_3D) glDisable(GL_BLEND)