#include "StdAfx.H" #include #include #include #include typedef enum { NONE = 0, PAN = 1, ZOOM = 2, ROTATE = 3, LEFT = 4, RIGHT = 8, TOP = 16, BOTTOM = 32, ASPECT_RATIO = 64, SKEW = 128, ALL = 256 } HandleType; typedef enum { UNCOMMITTED = 0, SCALE_ONLY = 1, ANGLE_ONLY = 2, BOTH = 3 } CommitType; /* * Current state of interaction. * Stored staticly here, which is reasonably safe because (presumably) * the user can only mouse-down move in one window at a time. So we should * never have multiple IBars actually being manipulated at the same time. */ static HandleType s_handle = NONE; // Which part of the ibar is selected static CommitType s_commit = UNCOMMITTED; // Shift key pressed, y/n, constraints motion static WINbool s_bCameraMode = FALSE; // Use bottom limbs for camera mode static R2Pt s_ptDown; // Mouse down point in [-1,1] X [-1,1] static R2Pt s_ptDrag; // Mouse move point in [-1,1] X [-1,1] static int s_iClosest = -1; // Which cube point was selected static Array s_aptCube; // Centered at (0,0,0), 1 unit tall in y direction static R2Pt s_ptCross; static double s_dTStemCross = 0.0; static double s_dClickDist = 0.05; static OGLObjsCamera s_cameraOrig; double c_dCubeHeight = 0.5; double c_dLimbLen = 0.5; void OGLObjsCamera::SetFocusDistance( const double in_dFocusDist ) { m_dFocusDist = in_dFocusDist; m_ptIBarFocus = At(); c_dCubeHeight = 0.5; c_dLimbLen = 0.5; m_bCenterObj = FALSE; } void OGLObjsCamera::SetFocusDistance( const R3Pt &in_pt, const R3Pt &in_ptScale ) { const double dMaxScale = 0.75 * WINmax( in_ptScale[0], WINmax( in_ptScale[1], in_ptScale[2] ) ); const R2Pt ptMin = CameraPt( in_pt - Right() * dMaxScale - Up() * dMaxScale ); const R2Pt ptMax = CameraPt( in_pt + Right() * dMaxScale + Up() * dMaxScale ); c_dLimbLen = c_dCubeHeight = WINmin( 0.9, 0.5 / sqrt(2) * Length( ptMax - ptMin ) ); m_dFocusDist = Length( in_pt - From() ); m_ptIBarFocus = in_pt; m_bCenterObj = TRUE; } // Ordering of cube vertices // 0 1 2 3 4 5 6 // 7 8 9 // 10 11 12 13 14 15 16 void OGLObjsCamera::MakeCube( ) const { s_aptCube.need( 17 ); double dZoom = m_dZoom; double dFD = m_dFocusDist; R2Pt ptCOP = GetProjectionCenter(); R3Pt ptFocus = m_ptIBarFocus; R3Vec vecRight = Right(); R3Vec vecUp = Up(); R3Vec vecLook = Look(); R3Pt ptFrom = From(); R3Vec vecOffset = GetCOPOffset(); if ( s_handle != NONE ) { dZoom = s_cameraOrig.Zoom(); dFD = s_cameraOrig.m_dFocusDist; ptCOP = s_cameraOrig.GetProjectionCenter(); ptFocus = s_cameraOrig.m_ptIBarFocus; vecRight = s_cameraOrig.Right(); vecUp = s_cameraOrig.Up(); vecLook = s_cameraOrig.Look(); ptFrom = s_cameraOrig.From(); vecOffset = s_cameraOrig.GetCOPOffset(); } const double dScl = dFD * tan( dZoom / 2.0 ); const double dSclHeight = dFD * c_dCubeHeight * tan( dZoom / 2.0 ); const double dSclLimb = dFD * c_dLimbLen * tan( dZoom / 2.0 ); // Vertical bar if ( m_bCenterObj == TRUE ) { s_aptCube[8] = ptFocus + vecOffset; //R3Pt( 0, 0.0, 0.0 ); } else { s_aptCube[8] = ptFrom + vecLook * dFD + vecOffset; //R3Pt( 0, 0.0, 0.0 ); } s_aptCube[3] = s_aptCube[8] + vecUp * dSclHeight; //R3Pt( 0, dHeight, 0.0 ); s_aptCube[13] = s_aptCube[8] - vecUp * dSclHeight; //R3Pt( 0,-dHeight, 0.0 ); // limbs are broken into 3 pieces, in (+-, +-, -) direction const R3Pt ptUL = s_aptCube[3] - vecRight * dSclLimb + vecLook * dSclLimb; const R3Pt ptUR = s_aptCube[3] + vecRight * dSclLimb + vecLook * dSclLimb; const R3Pt ptLL = s_aptCube[13] - vecRight * dSclLimb + vecLook * dSclLimb; const R3Pt ptLR = s_aptCube[13] + vecRight * dSclLimb + vecLook * dSclLimb; for ( int i = 0; i < 3; i++ ) { const double dThird = i / 3.0; const double dThirdNext = (i+1.0) / 3.0; // top left, top right, bottom left, bottom right s_aptCube[i] = Lerp( s_aptCube[3], ptUL, 1.0 - dThird ); s_aptCube[4 + i] = Lerp( s_aptCube[3], ptUR, dThirdNext ); s_aptCube[10 + i] = Lerp( s_aptCube[13], ptLL, 1.0 - dThird ); s_aptCube[14 + i] = Lerp( s_aptCube[13], ptLR, dThirdNext ); } s_aptCube[7] = s_aptCube[8] - vecRight * 0.5 * dSclHeight; s_aptCube[9] = s_aptCube[8] + vecRight * 0.5 * dSclHeight; if ( ApproxEqual( s_aptCube[3], s_aptCube[0] ) ) return; // This calculation is all done with the current camera double dDist; R2Pt ptIntersectL, ptIntersectR, ptClosest; const R2Line line1( CameraPt( s_aptCube[3] ), CameraPt( s_aptCube[0] ) ); const R2Line line2( CameraPt( s_aptCube[13] ), CameraPt( s_aptCube[10] ) ); const R2Line line3( CameraPt( s_aptCube[3] ), CameraPt( s_aptCube[6] ) ); const R2Line line4( CameraPt( s_aptCube[13] ), CameraPt( s_aptCube[16] ) ); line1.Intersect( line2, ptIntersectL ); line3.Intersect( line4, ptIntersectR ); const R2Line_seg lineStem( CameraPt( s_aptCube[3] ), CameraPt( s_aptCube[13] ) ); const R2Line_seg lineHorizon( ptIntersectL, ptIntersectR ); lineStem.Intersect( lineHorizon, s_ptCross, s_dTStemCross, dDist ); // Horizontal bar s_aptCube[7] = Lerp( s_aptCube[3], s_aptCube[13], s_dTStemCross ) - vecRight * 0.5 * dSclHeight; s_aptCube[9] = Lerp( s_aptCube[3], s_aptCube[13], s_dTStemCross ) + vecRight * 0.5 * dSclHeight; } static int s_iState = 0; Array s_aopImages; char s_str[1024]; static void GetImage() { switch( s_handle ) { case PAN : sprintf(s_str, "c:/papers/2004/UISTCamera/Figs/PanC"); break; case ZOOM : sprintf(s_str, "c:/papers/2004/UISTCamera/Figs/Zoom"); break; case ROTATE : sprintf(s_str, "c:/papers/2004/UISTCamera/Figs/RotateC"); break; case LEFT : case RIGHT : if ( s_commit == SCALE_ONLY ) { sprintf(s_str, "c:/papers/2004/UISTCamera/Figs/RotateLR%cC", (s_handle == LEFT) ? 'l' : 'r'); } else if ( s_commit == ANGLE_ONLY ) { sprintf(s_str, "c:/papers/2004/UISTCamera/Figs/RotateCOP%c", (s_handle == LEFT) ? 'l' : 'r'); } else { sprintf(s_str, "c:/papers/2004/UISTCamera/Figs/LR%c", (s_handle == LEFT) ? 'l' : 'r'); } break; case TOP : case BOTTOM : if ( s_commit == SCALE_ONLY ) { sprintf(s_str, "c:/papers/2004/UISTCamera/Figs/RotateTB%cC", (s_handle == TOP) ? 't' : 'b'); } else if ( s_commit == ANGLE_ONLY ) { sprintf(s_str, "c:/papers/2004/UISTCamera/Figs/RotateCOP%c", (s_handle == TOP) ? 't' : 'b'); } else { sprintf(s_str, "c:/papers/2004/UISTCamera/Figs/TB%c", (s_handle == TOP) ? 't' : 'b'); } break; case ALL : if ( s_commit == SCALE_ONLY ) { sprintf(s_str, "c:/papers/2004/UISTCamera/Figs/ScaleC"); } else if ( s_commit == ANGLE_ONLY ) { sprintf(s_str, "c:/papers/2004/UISTCamera/Figs/DollyC"); } else { sprintf(s_str, "c:/papers/2004/UISTCamera/Figs/DollyZoomC"); } break; case ASPECT_RATIO : sprintf(s_str, "c:/papers/2004/UISTCamera/Figs/AspectRatio"); break; case SKEW : sprintf(s_str, "c:/papers/2004/UISTCamera/Figs/Skew"); break; default : break; } if ( s_iState == 0 && s_handle != NONE ) { s_aopImages += new IMAGERaw; OGLImages_GetImage( *s_aopImages.last() ); const int iX = (int) (s_aopImages.last()->Width() * ( s_ptDown[0] + 1.0 ) / 2.0); const int iY = (int) (s_aopImages.last()->Height() * ( -s_ptDown[1] + 1.0 ) / 2.0); for ( int iU = -3; iU < 4; iU++ ) { s_aopImages.last()->Set( iX + iU, iY, UTILSColor::BLACK ); s_aopImages.last()->Set( iX, iY + iU, UTILSColor::BLACK ); } s_iState = 1; s_aopImages += new IMAGERaw; (*s_aopImages.last()) = (*s_aopImages[0]); } else if ( s_iState == 1 && s_handle != NONE ) { OGLImages_GetImage( *s_aopImages.last() ); const int iX = (int) (s_aopImages.last()->Width() * ( s_ptDrag[0] + 1.0 ) / 2.0); const int iY = (int) (s_aopImages.last()->Height() * ( -s_ptDrag[1] + 1.0 ) / 2.0); for ( int iU = -3; iU < 4; iU++ ) { s_aopImages.last()->Set( iX + iU, iY, UTILSColor::BLACK ); s_aopImages.last()->Set( iX, iY + iU, UTILSColor::BLACK ); } } else if ( s_iState == 1 && s_handle == NONE ) { s_aopImages += new IMAGERaw; OGLImages_GetImage( *s_aopImages.last() ); s_iState = 2; } else if ( s_iState == 2 && s_handle == NONE ) { OGLImages_GetImage( *s_aopImages.last() ); s_iState = 3; } else if ( s_iState == 3 ) { s_iState++; } else if (s_iState == 4 ) { s_iState = 0; } TRACE("State %d\n", s_iState ); } static void WriteImage() { IMAGERaw image; char str[1024]; if ( s_iState == 2 && s_aopImages.num() ) { for ( int i = 0; i < s_aopImages.num(); i++ ) { image.SetSize( s_aopImages.last()->Width(), s_aopImages.last()->Height() ); for ( int x = 0; x < image.Width(); x++ ) for ( int j = 0; j < image.Height(); j++ ) image.Set( x, image.Height() - j - 1, (*s_aopImages[i])(x,j) ); sprintf(str, "%s%d.bmp", s_str, i ); image.Write( str ); } } } void OGLObjsCamera::SetOpenGLCameraIBar() const { if ( s_handle == NONE ) { SetOpenGLCamera(); } else { if ( s_bCameraMode == TRUE ) s_cameraOrig.SetOpenGLCamera(); else SetOpenGLCamera(); } } /// void OGLObjsCamera::DrawIBar( const WINbool in_bMouseOver ) const { MakeCube(); glPushAttrib( GL_ALL_ATTRIB_BITS ); ::glMatrixMode( GL_PROJECTION ); ::glPushMatrix(); ::glMatrixMode( GL_MODELVIEW ); ::glPushMatrix(); ::glDisable( GL_LIGHTING ); ::glDisable( GL_DEPTH_TEST ); ::glEnable( GL_LINE_SMOOTH ); if ( in_bMouseOver == TRUE ) { OGLBegin2D(); const double dScl1 = 0.01; const double dScl2 = -dScl1 / 4.0; Array apt; apt += CameraPt( s_aptCube[0] ); apt += CameraPt( s_aptCube[3] ); apt += CameraPt( s_aptCube[3] ); apt += CameraPt( s_aptCube[6] ); apt += CameraPt( s_aptCube[7] ); apt += CameraPt( s_aptCube[9] ); apt += CameraPt( s_aptCube[10] ); apt += CameraPt( s_aptCube[13] ); apt += CameraPt( s_aptCube[13] ); apt += CameraPt( s_aptCube[16] ); glColor3f(0.0,0.0,0.0); for ( int iY = -2; iY < 3; iY++ ) { glBegin( GL_LINES ); for ( int i = 0; i < apt.num(); i++ ) glVertex3d( apt[i][0], apt[i][1] + iY * 0.003, 0.0 ); glVertex3dv( &s_aptCube[16][0] ); glEnd(); } glColor3f(0,0,1); glLineWidth( 1.0f ); OGLDrawCircle( s_ptCross, 0.02 ); glColor3f(0,0,0); glLineWidth( 1.0f ); switch (s_iClosest) { case 0 : case 1 : case 2 : case 10 : case 11 : case 12 : OGLDrawCircle( Lerp( CameraPt( s_aptCube[s_iClosest] ), CameraPt( s_aptCube[s_iClosest+1] ), 0.5 ), 0.025 ); break; case 4 : case 5 : case 6 : case 14 : case 15 : case 16 : OGLDrawCircle( Lerp( CameraPt( s_aptCube[s_iClosest] ), CameraPt( s_aptCube[s_iClosest-1] ), 0.5 ), 0.025 ); break; case 7 : case 9 : OGLDrawCircle( CameraPt( s_aptCube[s_iClosest] ), 0.025 ); break; case 8 : OGLDrawCircle( CameraPt( Lerp( s_aptCube[3], s_aptCube[13], s_dTStemCross * 0.9 ) ), 0.025 ); break; case 3 : OGLDrawCircle( CameraPt( Lerp( s_aptCube[3], s_aptCube[13], WINmin( s_dTStemCross * 0.5, 0.1) ) ), 0.025 ); break; case 13 : OGLDrawCircle( CameraPt( Lerp( s_aptCube[3], s_aptCube[13], 0.9 ) ), 0.025 ); break; case 17 : OGLDrawCircle( CameraPt( Lerp( s_aptCube[3], s_aptCube[13], s_dTStemCross * 0.5 ) ), 0.025 ); break; case 18 : OGLDrawCircle( CameraPt( Lerp( s_aptCube[3], s_aptCube[13], s_dTStemCross * 1.1 ) ), 0.025 ); break; case -1 : break; } OGLEnd2D(); } // Perspective, focal length, and aspect ratio part of camera matrix SetOpenGLCamera(); const float fDef = (s_handle == PAN) ? 3.0f : 2.0f; const float fSel = 2.0f; ::glBegin( GL_LINES ); ::glColor3f( 1.0,0,1.0 ); if ( s_handle == LEFT || s_handle == TOP ) glLineWidth( fSel ); else glLineWidth( fDef ); ::glVertex3dv( &s_aptCube[0][0] ); ::glVertex3dv( &s_aptCube[1][0] ); if ( s_handle == LEFT || s_handle == BOTTOM ) glLineWidth( fSel ); else glLineWidth( fDef ); ::glVertex3dv( &s_aptCube[10][0] ); ::glVertex3dv( &s_aptCube[11][0] ); ::glColor3f( 0,1.0,0 ); if ( s_handle == TOP ) glLineWidth( fSel ); else glLineWidth( fDef ); ::glVertex3dv( &s_aptCube[1][0] ); ::glVertex3dv( &s_aptCube[2][0] ); ::glVertex3dv( &s_aptCube[4][0] ); ::glVertex3dv( &s_aptCube[5][0] ); ::glColor3f( 1.0,0,0 ); if ( s_handle == BOTTOM ) glLineWidth( fSel ); else glLineWidth( fDef ); ::glVertex3dv( &s_aptCube[11][0] ); ::glVertex3dv( &s_aptCube[12][0] ); ::glVertex3dv( &s_aptCube[14][0] ); ::glVertex3dv( &s_aptCube[15][0] ); ::glColor3f( 0,1.0,1.0 ); if ( s_handle == TOP || s_handle == RIGHT) glLineWidth( fSel ); else glLineWidth( fDef ); ::glVertex3dv( &s_aptCube[5][0] ); ::glVertex3dv( &s_aptCube[6][0] ); ::glVertex3dv( &s_aptCube[15][0] ); ::glVertex3dv( &s_aptCube[16][0] ); ::glColor3f( 0,0,1.0 ); if ( s_handle == SKEW || s_handle == ASPECT_RATIO ) glLineWidth( fSel ); else glLineWidth( fDef ); const R3Pt ptL = Lerp( s_aptCube[3], s_aptCube[13], s_dTStemCross * 0.5 ) + ( s_aptCube[9] - s_aptCube[7] ) * 0.1; const R3Pt ptR = Lerp( s_aptCube[3], s_aptCube[13], s_dTStemCross * 0.5 ) - ( s_aptCube[9] - s_aptCube[7] ) * 0.1; ::glVertex3dv( &ptL[0] ); ::glVertex3dv( &ptR[0] ); if ( s_handle == TOP || s_handle == LEFT ) glLineWidth( fSel ); else glLineWidth( fDef ); ::glVertex3dv( &s_aptCube[2][0] ); ::glVertex3dv( &s_aptCube[3][0] ); if ( s_handle == TOP || s_handle == RIGHT ) glLineWidth( fSel ); else glLineWidth( fDef ); ::glVertex3dv( &s_aptCube[3][0] ); ::glVertex3dv( &s_aptCube[4][0] ); if ( s_handle == BOTTOM || s_handle == LEFT ) glLineWidth( fSel ); else glLineWidth( fDef ); ::glVertex3dv( &s_aptCube[12][0] ); ::glVertex3dv( &s_aptCube[13][0] ); if ( s_handle == BOTTOM || s_handle == RIGHT ) glLineWidth( fSel ); else glLineWidth( fDef ); ::glVertex3dv( &s_aptCube[13][0] ); ::glVertex3dv( &s_aptCube[14][0] ); if ( s_handle == ZOOM ) glLineWidth( fSel ); else glLineWidth( fDef ); ::glVertex3dv( &s_aptCube[3][0] ); ::glVertex3dv( &s_aptCube[13][0] ); ::glVertex3dv( &s_aptCube[7][0] ); ::glVertex3dv( &s_aptCube[9][0] ); ::glEnd(); const double dScl = Length( At() - From() ) * tan( Zoom() / 2.0 ); const R3Pt ptAt = ( m_bCenterObj == TRUE ? m_ptIBarFocus : At() ) + GetCOPOffset(); const R3Pt ptLL = ptAt - Right() * 0.1 - Up() * 0.1; const R3Pt ptRR = ptAt + Right() * 0.1 - Up() * 0.1; const R3Pt ptUU = ptAt + Right() * 0.1 + Up() * 0.1; const R3Pt ptBB = ptAt - Right() * 0.1 + Up() * 0.1; ::glColor4f( 0.5, 0.5, 0.5, 0.5 ); ::glEnable( GL_DEPTH_TEST ); ::glEnable( GL_BLEND ); ::glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ); ::glBegin( GL_POLYGON ); ::glVertex3dv( &ptLL[0] ); ::glVertex3dv( &ptRR[0] ); ::glVertex3dv( &ptUU[0] ); ::glVertex3dv( &ptBB[0] ); ::glEnd( ); ::glMatrixMode( GL_PROJECTION ); :: glPopMatrix(); ::glMatrixMode( GL_MODELVIEW ); :: glPopMatrix(); glPopAttrib(); //GetImage(); //if ( s_iState == 2 ) // WriteImage(); } /// void OGLObjsCamera::SetClickDistance( const double in_d ) const { s_dClickDist = in_d; } static int s_aiMapLimb[6] = {0, 6, 10, 15, 9, 13}; static int s_aiMapLimbBase[6] = {3, 3, 13, 13, 7, 3}; WINbool OGLObjsCamera::IsMouseOver( const R2Pt_i &in_ipt, WINbool &out_bRedraw ) const { if ( s_aptCube.num() == 0 ) return FALSE; MakeCube(); // Convert to (-1,1) X (-1,1) const R2Pt ptClick = FlTkToCamera( in_ipt[0], in_ipt[1] ); const int iClosestSave = s_iClosest; double dDist; s_iClosest = -1; R2Pt ptClosest; double dT, dClosest = 1e30; for ( int i = 0; i < 6; i++ ) { const R2Line_seg segLimb( CameraPt( s_aptCube[ s_aiMapLimbBase[i]] ), CameraPt( s_aptCube[ s_aiMapLimb[i] ] ) ); segLimb.FindPtOnSeg( ptClick, ptClosest, dT, dDist ); dT = WINminmax( dT, 1e-16, 1.0 - 1e-16 ); if ( dDist < dClosest ) { switch (i) { case 0 : case 2 : s_iClosest = (int) ( dT * s_aiMapLimb[i] + (1.0 - dT) * s_aiMapLimbBase[i] ); break; case 1 : case 3 : s_iClosest = 1 + (int) ( dT * s_aiMapLimb[i] + (1.0 - dT) * s_aiMapLimbBase[i] ); break; case 4 : s_iClosest = 7 + (int) ( dT * 3.0 ); break; case 5 : if ( dT < ( 0.5 * s_dTStemCross - 0.05) ) { s_iClosest = 3; // Spin object-centric } else if ( dT < (0.5 * s_dTStemCross + 0.05) ) { s_iClosest = 17; // skew/aspect ratio } else if ( dT < s_dTStemCross ) { s_iClosest = 8; // Pan, either object or camera-centric } else if ( dT < 0.5 * (s_dTStemCross + 1.0) ) { s_iClosest = 18; // Pan, either object or camera-centric } else { s_iClosest = 13; // Spin camera-centric } break; } dClosest = dDist; } } if ( s_iClosest == iClosestSave || dClosest >= s_dClickDist ) { out_bRedraw = FALSE; } else { out_bRedraw = TRUE; } if ( dClosest < s_dClickDist ) { return TRUE; } s_iClosest = -1; return FALSE; } WINbool OGLObjsCamera::MouseDown( const R2Pt_i &in_ipt, const WINbool in_bShift ) const { if ( s_aptCube.num() == 0 ) return FALSE; MakeCube(); s_cameraOrig = *this; // Convert to (-1,1) X (-1,1) s_ptDown = FlTkToCamera( in_ipt[0], in_ipt[1] ); s_commit = UNCOMMITTED; s_bCameraMode = FALSE; // Project each point s_iClosest = -1; double dDist = 0.0, dT = 0.0, dClosest = 1e30; R2Pt ptClosest; for ( int i = 0; i < 6; i++ ) { const R2Line_seg segLimb( CameraPt( s_aptCube[ s_aiMapLimbBase[i]] ), CameraPt( s_aptCube[ s_aiMapLimb[i] ] ) ); segLimb.FindPtOnSeg( s_ptDown, ptClosest, dT, dDist ); dT = WINminmax( dT, 1e-16, 1.0 - 1e-16 ); if ( dDist < dClosest ) { switch (i) { case 0 : case 2 : s_iClosest = (int) ( dT * s_aiMapLimb[i] + (1.0 - dT) * s_aiMapLimbBase[i] ); break; case 1 : case 3 : s_iClosest = 1 + (int) ( dT * s_aiMapLimb[i] + (1.0 - dT) * s_aiMapLimbBase[i] ); break; case 4 : s_iClosest = 7 + (int) ( dT * 3.0 ); break; case 5 : if ( dT < ( 0.5 * s_dTStemCross - 0.05) ) { s_iClosest = 3; // Spin object-centric } else if ( dT < (0.5 * s_dTStemCross + 0.05) ) { s_iClosest = 17; // skew/aspect ratio } else if ( dT < s_dTStemCross ) { s_iClosest = 8; // Pan, either object or camera-centric } else if ( dT < 0.5 * (s_dTStemCross + 1.0) ) { s_iClosest = 18; // Pan, either object or camera-centric } else { s_iClosest = 13; // Spin camera-centric } break; } dClosest = dDist; } } if ( dClosest > s_dClickDist ) s_iClosest = -1; switch( s_iClosest ) { case -1 : s_handle = NONE; break; case 8 : s_handle = PAN; s_bCameraMode = FALSE; break; case 18 : s_handle = PAN; s_bCameraMode = TRUE; break; case 3 : s_handle = ROTATE; break; case 13 : s_handle = ROTATE; s_bCameraMode = TRUE; break; case 2 : case 4 : case 14 : case 12 : s_handle = ALL; break; case 7 : s_bCameraMode = TRUE; case 9 : s_handle = ZOOM; break; case 0 : case 10 : s_handle = LEFT; break; case 1 : case 5 : s_handle = TOP; break; case 6 : case 16 : s_handle = RIGHT; break; case 11 : case 15 : s_handle = BOTTOM; break; case 17 : if ( in_bShift ) s_handle = SKEW; else s_handle = PAN; break; default : ASSERT(FALSE); } if ( s_iClosest == -1 ) return FALSE; return TRUE; } static R2Vec Limb( const R4Matrix &in_mat ) { R3Vec vecProj(0,0,1); // Vector directions (* indicates arrow head) // // ---* ----* // * // | // ---------* // | // ---* ----* switch ( s_iClosest ) { case 0 : case 1 : case 2 : vecProj = in_mat * s_aptCube[3] - in_mat * s_aptCube[0]; break; case 4 : case 5 : case 6 : vecProj = in_mat * s_aptCube[6] - in_mat * s_aptCube[3]; break; case 10 : case 11 : case 12 : vecProj = in_mat * s_aptCube[13] - in_mat * s_aptCube[10]; break; case 14 : case 15 : case 16 : vecProj = in_mat * s_aptCube[16] - in_mat * s_aptCube[13]; break; case 7 : case 8 : case 9 : case 18 : vecProj = in_mat * s_aptCube[9] - in_mat * s_aptCube[7]; break; case 3 : case 13 : vecProj = in_mat * s_aptCube[3] - in_mat * s_aptCube[13]; break; case 17 : vecProj = in_mat * s_aptCube[3] - in_mat * s_aptCube[8]; break; default : ASSERT(FALSE); break; } return R2Vec( vecProj[0], vecProj[1] ); } static R2Pt LimbBase( const R4Matrix &in_mat ) { R3Pt ptLimb; if ( s_iClosest < 7 ) ptLimb = in_mat * s_aptCube[3]; else if ( s_iClosest < 10 ) ptLimb = in_mat * Lerp( s_aptCube[7], s_aptCube[9], 0.5 ); else if ( s_iClosest < s_aptCube.num() ) ptLimb = in_mat * s_aptCube[13]; else ptLimb = in_mat * s_aptCube[8]; return R2Pt( ptLimb[0], ptLimb[1] ); } void OGLObjsCamera::MouseMoveObject() { const R2Vec vec = s_ptDrag - s_ptDown; (*this) = s_cameraOrig; const R2Vec vecMiddle = UnitSafe( Limb( m_matProj ) ); const double dLenOrig = 2.0 * Dot( vecMiddle, s_ptDown - LimbBase( m_matProj ) ); const double dLenNew = 2.0 * Dot( vecMiddle, s_ptDrag - LimbBase( m_matProj ) ); const double dRatio = dLenOrig / dLenNew; if ( s_handle == PAN ) { // Since cube is at film plane, move by mouse movement in film plane const double dTrans = m_dFocusDist * tan( m_dZoom / 2.0 ); switch (s_commit) { case SCALE_ONLY : PanLeft( -vec[0] * dTrans ); break; case ANGLE_ONLY : PanUp( vec[1] * dTrans ); break; case BOTH : PanLeft( -vec[0] * dTrans ); PanUp( vec[1] * dTrans ); break; case UNCOMMITTED : break; } } else if ( s_handle == ZOOM ) { const double dFocusLen = m_iHeight / tan( m_dZoom / 2.0 ); const double dNewFL = dFocusLen / dRatio; SetZoom( WINminmax( 2.0 * atan( m_iHeight / dNewFL ), 1e-4, M_PI - 1e-4 ) ); } else if ( s_handle == ROTATE ) { // Angle of the current point to the center of the screen (minus 90 degrees) // is desired rotation of object const double dAng = atan2( s_ptDrag[1], s_ptDrag[0] ) + ((s_iClosest == 3) ? - M_PI / 2.0 : M_PI / 2.0); RotateAroundIBarFocus( 2, dAng ); } else if ( s_handle == LEFT || s_handle == RIGHT ) { switch ( s_commit ) { case SCALE_ONLY : RotateAroundIBarFocus( 1, -vec[0] * M_PI / 2.0 ); break; case ANGLE_ONLY : PanLeft( -vec[1] * m_dFocusDist * tan( m_dZoom / 2.0 ) ); SetProjectionCenter( GetProjectionCenter() + R2Vec( vec[1], 0 ) ); break; case BOTH : PanLeft( -vec[1] * m_dFocusDist * tan( m_dZoom / 2.0 ) ); SetProjectionCenter( GetProjectionCenter() + R2Vec( vec[1], 0 ) ); RotateAroundIBarFocus( 1, -vec[0] * M_PI / 2.0 ); break; case UNCOMMITTED : break; } } else if ( s_handle == TOP || s_handle == BOTTOM ) { switch ( s_commit ) { case SCALE_ONLY : RotateAroundIBarFocus( 0, ((s_handle == BOTTOM) ? -1.0 : 1.0) * vec[0] * M_PI / 2.0 ); break; case ANGLE_ONLY : PanUp( -vec[1] * AspectRatio() * m_dFocusDist * tan( m_dZoom / 2.0 ) ); SetProjectionCenter( GetProjectionCenter() + R2Vec( 0, -vec[1] ) ); break; case BOTH : RotateAroundIBarFocus( 0, ((s_handle == BOTTOM) ? -1.0 : 1.0) * vec[0] * M_PI / 2.0 ); break; case UNCOMMITTED : break; } } else if ( s_handle == ALL ) { double dNewFD = m_dFocusDist; const double dScalePerc = fabs( s_ptDown[1] - s_ptDrag[1] ) / 0.5; if ( s_ptDown[1] > s_ptDrag[1] ) dNewFD = WINmax(0.01 * m_dFocusDist, 1.0 - 0.5 * dScalePerc) * m_dFocusDist; else dNewFD = (1.0 + 2.0 * dScalePerc) * m_dFocusDist; // Adjust the focal length so that the IBar still shows up the same size const double dF = 1.0 / tan( m_dZoom / 2.0 ); const double dFNew = dF * dNewFD / m_dFocusDist; const double dAngNew = 2.0 * atan( 1.0 / dFNew ); switch ( s_commit ) { case SCALE_ONLY : SetZoom( WINminmax( Zoom() * dRatio, 0.0001, M_PI - 0.001 ) ); break; case ANGLE_ONLY : PanIn( dNewFD - m_dFocusDist ); break; case BOTH : SetZoom( dAngNew ); PanIn( dNewFD - m_dFocusDist ); break; case UNCOMMITTED : break; } } else if ( s_handle == ASPECT_RATIO ) { m_dAspectRatio *= dRatio; SetPerspectiveMatrix(); } else if ( s_handle == SKEW ) { m_dSkew = m_dSkew + vec[0]; SetPerspectiveMatrix(); } SetAllMatrices(); } /// void OGLObjsCamera::MouseMove( const R2Pt_i &in_ipt, const WINbool in_bShift ) { s_ptDrag = FlTkToCamera( in_ipt[0], in_ipt[1] ); if ( s_iClosest == -1 ) return; const R2Vec vec = s_ptDrag - s_ptDown; if ( (s_handle == LEFT || s_handle == RIGHT) && in_bShift == TRUE ) { s_commit = BOTH; } if ( (s_handle == TOP || s_handle == BOTTOM ) && in_bShift == TRUE ) { s_commit = BOTH; } if ( s_commit == UNCOMMITTED ) { if ( Length( vec ) > 0.02 ) { if ( fabs( vec[0] ) > fabs( vec[1] ) + 0.002 ) { s_commit = SCALE_ONLY; } else { s_commit = ANGLE_ONLY; } if (s_handle == ALL && in_bShift == FALSE) s_commit = BOTH; if ( s_handle == PAN && in_bShift == FALSE ) s_commit = BOTH; } } if ( s_commit != UNCOMMITTED && in_bShift == TRUE && s_handle == SKEW ) { if ( s_commit == ANGLE_ONLY ) { s_handle = ASPECT_RATIO; } else { s_handle = SKEW; } } MouseMoveObject(); } /// void OGLObjsCamera::MouseRelease( const R2Pt_i &in_ipt, const WINbool in_bShift ) { s_ptDrag = FlTkToCamera( in_ipt[0], in_ipt[1] ); if ( s_bCameraMode == TRUE ) { switch (s_handle) { case PAN : case ZOOM : s_ptDrag = s_ptDown; s_ptDown = FlTkToCamera( in_ipt[0], in_ipt[1] ); break; case ROTATE : s_ptDrag[0] = -s_ptDrag[0]; break; } } MouseMoveObject(); s_iClosest = -1; s_handle = NONE; s_commit = UNCOMMITTED; }