/**************************************************************************** * MeshLab o o * * A versatile mesh processing toolbox o o * * _ O _ * * Copyright(C) 2008 \/)\/ * * Visual Computing Lab /\/| * * ISTI - Italian National Research Council | * * \ * * All rights reserved. * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License (http://www.gnu.org/licenses/gpl.txt) * * for more details. * * * ****************************************************************************/ /**************************************************************************** History $Log: not supported by cvs2svn $ Revision 1.6 2008/02/22 20:34:35 benedetti corrected typo Revision 1.5 2008/02/22 20:04:02 benedetti many user interface improvements, cleaned up a little Revision 1.4 2008/02/17 20:52:53 benedetti some generalization made Revision 1.3 2008/02/16 14:12:30 benedetti first version ****************************************************************************/ #include #include #include #include #include "coordinateframe.h" using namespace vcg; CoordinateFrame::CoordinateFrame(float s) :basecolor(Color4b::White),xcolor(Color4b::Red) ,ycolor(Color4b::Green),zcolor(Color4b::Blue),size(s),linewidth(2.0) ,font(),drawaxis(true),drawlabels(true),drawvalues(false) { font.setFamily("Helvetica"); } void CoordinateFrame::Render(QGLWidget* glw) { assert( glw!= NULL); glPushAttrib(GL_ALL_ATTRIB_BITS); glDisable(GL_LIGHTING); glDisable(GL_TEXTURE_2D); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glEnable(GL_LINE_SMOOTH); glEnable(GL_POINT_SMOOTH); glLineWidth(linewidth); glPointSize(linewidth*1.5); Point3d o(0,0,0); Point3d a(size,0,0); Point3d b(0,size,0); Point3d c(0,0,size); // Get gl state values double mm[16],mp[16]; GLint vp[4]; glGetDoublev(GL_MODELVIEW_MATRIX,mm); glGetDoublev(GL_PROJECTION_MATRIX,mp); glGetIntegerv(GL_VIEWPORT,vp); float slope_a=calcSlope(-a,a,2*size,10,mm,mp,vp); float slope_b=calcSlope(-b,b,2*size,10,mm,mp,vp); float slope_c=calcSlope(-c,c,2*size,10,mm,mp,vp); float scalefactor = size*0.02f; if(drawaxis){ glBegin(GL_LINES); glColor(xcolor); glVertex(-a); glVertex(a); glColor(ycolor); glVertex(-b); glVertex(b); glColor(zcolor); glVertex(-c); glVertex(c); glEnd(); glColor(basecolor); // assi positivi drawTickedLine(o,a,size,slope_a,linewidth); // Draws x axis drawTickedLine(o,b,size,slope_b,linewidth); // Draws y axis drawTickedLine(o,c,size,slope_c,linewidth); // Draws z axis //assi negativi drawTickedLine(o,-a,size,slope_a,linewidth); // Draws x axis drawTickedLine(o,-b,size,slope_b,linewidth); // Draws y axis drawTickedLine(o,-c,size,slope_c,linewidth); // Draws z axis glPushMatrix(); glTranslate(a); glScalef(scalefactor,scalefactor,scalefactor); Add_Ons::Cone(10,linewidth*1.5,linewidth*0.5,true); glPopMatrix(); glPushMatrix(); glTranslate(b); glRotatef(90,0,0,1); glScalef(scalefactor,scalefactor,scalefactor); Add_Ons::Cone(10,linewidth*1.5,linewidth*0.5,true); glPopMatrix(); glPushMatrix(); glTranslate(c); glRotatef(-90,0,1,0); glScalef(scalefactor,scalefactor,scalefactor); Add_Ons::Cone(10,linewidth*1.5,linewidth*0.5,true); glPopMatrix(); } if(drawlabels){ font.setBold(true); font.setPixelSize(12); float d=size+scalefactor*linewidth*1.5; glColor(xcolor); glw->renderText(d,0,0,QString("X"),font); glColor(ycolor); glw->renderText(0,d,0,QString("Y"),font); glColor(zcolor); glw->renderText(0,0,d,QString("Z"),font); } if(drawvalues){ font.setBold(false); font.setPixelSize(8); float i; glColor(Color4b::LightGray); for(i=slope_a;irenderText( i,0,0,QString(" %1").arg(i,3,'f',1),font); glw->renderText(-i,0,0,QString("-%1").arg(i,3,'f',1),font); } for(i=slope_b;irenderText(0, i,0,QString(" %1").arg(i,3,'f',1),font); glw->renderText(0,-i,0,QString("-%1").arg(i,3,'f',1),font); } for(i=slope_c;irenderText(0,0, i,QString(" %1").arg(i,3,'f',1),font); glw->renderText(0,0,-i,QString("-%1").arg(i,3,'f',1),font); } } glPopAttrib(); assert(!glGetError()); } void CoordinateFrame::drawTickedLine(const Point3d &a,const Point3d &b, float dim,float tickDist,float linewidth) { Point3d v(b-a); v = v /dim; // normalize without computing square roots and powers glBegin(GL_POINTS); float i; for(i=tickDist;i(niceRound(dim*.001f),nslope); // prevent too small slope return nslope; } float CoordinateFrame::niceRound(float val) { return powf(10.f,ceil(log10(val))); } MovableCoordinateFrame::MovableCoordinateFrame(float size) :CoordinateFrame(size),position(0,0,0),rotation(0,Point3f(1,0,0)) { // nothing here } void MovableCoordinateFrame::Render(QGLWidget* gla) { glPushMatrix(); glTranslate(position); Matrix44f mrot; rotation.ToMatrix(mrot); glMultMatrix(Inverse(mrot)); CoordinateFrame::Render(gla); glPopMatrix(); } void MovableCoordinateFrame::GetTransform(Matrix44f & transform) { // costruisco la matrice che porta le coordinate in spazio di mondo // resetto la trasf transform.SetIdentity(); // ruoto Matrix44f rot; rotation.ToMatrix(rot); transform = Inverse(rot) * transform ; // sposto in posizione Matrix44f pos; pos.SetTranslate(position); transform = pos * transform; } void MovableCoordinateFrame::Reset(bool reset_position,bool reset_alignment) { if(reset_position) position = Point3f(0,0,0); if(reset_alignment) rotation = Quaternionf(0,Point3f(1,0,0)); } void MovableCoordinateFrame::SetPosition(const Point3f newpos) { position = newpos; } void MovableCoordinateFrame::SetRotation(const Quaternionf newrot) { rotation = newrot; } Point3f MovableCoordinateFrame::GetPosition() { return position; } Quaternionf MovableCoordinateFrame::GetRotation() { return rotation; } void MovableCoordinateFrame::Rot(float angle_deg,const Point3f axis) { Similarityf s; s.SetRotate(angle_deg*M_PI/180.0f,(rotation).Rotate(axis)); Move(s); } void MovableCoordinateFrame::AlignWith(const Point3f pri,const Point3f secondary,const char c1, const char c2) { const float EPSILON=1e-6; Point3f primary=pri; if( primary.Norm() < EPSILON*size ) return; primary.Normalize(); // ho l'asse primario, lo normalizzo Plane3f plane(0,primary); // piano di proiezione per la seconda rotazione Point3f x(1,0,0),y(0,1,0),z(0,0,1); Point3f first(0,0,0),second(0,0,0),third(0,0,0); if(c1=='X'){ first = x; if((c2=='Y')||(c2==' ')){ second = y; third = z; } else if(c2=='Z'){ second = z; third = y; } else assert (0); } else if(c1=='Y'){ first = y; if((c2=='Z')||(c2==' ')){ second = z; third = x; } else if(c2=='X'){ second = x; third = z; } else assert (0); } else if(c1=='Z'){ first = z; if((c2=='X')||(c2==' ')){ second = x; third = y; } else if(c2=='Y'){ second = y; third = x; } else assert (0); } else assert (0); Point3f old_first = Inverse(rotation).Rotate(first); // l'asse 1 Point3f old_second_pro = plane.Projection(Inverse(rotation).Rotate(second)); //la proiezione dell'asse 2 Point3f old_third_pro = plane.Projection(Inverse(rotation).Rotate(third)); //la proiezione dell'asse 3 // allinea l'asse 1 corrente all'asse primary RotateToAlign(old_first,primary); // prima rotazione Point3f secondary_pro = plane.Projection(secondary); // la proiezione di secondary Point3f new_second_pro = plane.Projection(Inverse(rotation).Rotate(second)); // la proiezione dell'asse 2 dopo la prima rotazione // se c'e` un asse secondary e la sua proiezione non e` 0 if( secondary.Norm() > EPSILON*size && secondary_pro.Norm() > EPSILON ){ // allinea la proiezione dell'asse 2 dopo la prima rotazione alla proiezione dell'asse secondary secondary_pro.Normalize(); RotateToAlign(new_second_pro,secondary_pro); return; } // creco di riallineare l'asse 2 if ( old_second_pro.Norm() > EPSILON ) { // allinea la proiezione dell'asse 2 dopo la prima rotazione alla proiezione dell'asse 2 old_second_pro.Normalize(); RotateToAlign(new_second_pro,old_second_pro); return; } // cerco di riallineare l'asse 3 Point3f new_third_pro = plane.Projection(Inverse(rotation).Rotate(third)); //la proiezione dell'asse 3 dopo la prima rotazione assert(old_third_pro.Norm() > EPSILON ); // la proiezione dell'asse 3 non dovrebbe essere 0 // allinea la proiezione dell'asse 3 dopo la prima rotazione alla proiezione dell'asse 3 old_third_pro.Normalize(); RotateToAlign(new_third_pro,old_third_pro); } void MovableCoordinateFrame::Move(const Similarityf track) { position = position + track.tra; rotation = rotation * Inverse(track.rot); } void MovableCoordinateFrame::RotateToAlign(const Point3f source, const Point3f dest) { const float EPSILON=1e-6; // source e dest devono essere versori assert( math::Abs(source.Norm() - 1) < EPSILON); assert( math::Abs(dest.Norm() - 1) < EPSILON); Point3f axis = dest ^ source; float sinangle = axis.Norm(); float cosangle = dest * source; float angle = math::Atan2(sinangle,cosangle); if( math::Abs(angle) < EPSILON ) return; // angolo ~ 0, annullo if( math::Abs(math::Abs(angle)-M_PI) < EPSILON){ // devo trovare un asse su cui flippare Plane3f plane(0,source); axis=plane.Projection(Point3f(1,0,0)); // proietto un punto a caso sul piano normale a source if(axis.Norm() < EPSILON){ // source era ~ [1,0,0]... axis=plane.Projection(Point3f(0,1,0)); assert(axis.Norm() > EPSILON); // quest'altro punto deve andare bene } } rotation = rotation * Quaternionf(angle,axis); } ActiveCoordinateFrame::ActiveCoordinateFrame(float size) :MovableCoordinateFrame(size),manipulator(NULL),drawmoves(true), drawrotations(true),move_button(Trackball::BUTTON_RIGHT), rotate_button(Trackball::BUTTON_LEFT),x_modifier(Trackball::BUTTON_NONE), y_modifier(Trackball::KEY_CTRL),z_modifier(Trackball::KEY_SHIFT), movx(move_button | x_modifier),movy(move_button | y_modifier), movz(move_button | z_modifier),rotx(rotate_button | x_modifier), roty(rotate_button | y_modifier),rotz(rotate_button | z_modifier), x_axis(1,0,0),y_axis(0,1,0),z_axis(0,0,1),rot_snap_rad(0.0f),mov_snap(0.0f) { manipulator=new Trackball(); Update(); } ActiveCoordinateFrame::~ActiveCoordinateFrame() { if(manipulator!=NULL) { delete manipulator; manipulator=NULL; } } void ActiveCoordinateFrame::Render(QGLWidget* glw) { glPushMatrix(); manipulator->radius=size; manipulator->center=position; manipulator->GetView(); manipulator->Apply(false); MovableCoordinateFrame::Render(glw); // non devo disegnare if(!drawmoves && !drawrotations){ glPopMatrix(); return; } int current_mode=manipulator->current_button; bool rotating=(current_mode==rotx)||(current_mode==roty)||(current_mode==rotz); bool moving=(current_mode==movx)||(current_mode==movy)||(current_mode==movz); //devo disegnare qualcosa glPushAttrib(GL_ALL_ATTRIB_BITS); glDisable(GL_LIGHTING); glDisable(GL_TEXTURE_2D); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glEnable(GL_LINE_SMOOTH); glEnable(GL_POINT_SMOOTH); QString message("sovrascrivimi"); char axis_name; float verse; if(current_mode==x_modifier){ glColor(xcolor); message = QString("move or rotate on X axis"); } else if(current_mode==y_modifier){ glColor(ycolor); message = QString("move or rotate on Y axis"); } else if(current_mode==z_modifier){ glColor(zcolor); message = QString("move or rotate on Z axis"); } else if(rotating && drawrotations){ // devo disegnare una rotazione Point3f axis, arc_point; float angle; manipulator->track.rot.ToAxis(angle,axis); angle = -angle; if(current_mode==rotx){ verse=((axis+x_axis).Norm()<1?-1:1); glColor(xcolor); axis_name='x'; arc_point=y_axis*(size*0.8); } else if(current_mode==roty) { verse=((axis+y_axis).Norm()<1?-1:1); glColor(ycolor); axis_name='y'; arc_point=z_axis*(size*0.8); } else if(current_mode==rotz) { verse=((axis+z_axis).Norm()<1?-1:1); glColor(zcolor); axis_name='z'; arc_point=x_axis*(size*0.8); } else assert(0); // doveva essere una rotazione // normalizzo la rotazione a [-180,180] float sign = ((angle*verse)<0) ? -1 : 1; float abs_angle = (angle<0) ? -angle : angle; angle = sign * ( (abs_angle>M_PI) ? 2*M_PI-abs_angle : abs_angle ); axis = axis * verse; message = QString("rotated %1 deg around %2") .arg(((angle*180.0)/M_PI),5,'f',3) .arg(axis_name); Quaternionf arc_rot; arc_rot.FromAxis(angle/18.0,axis); glBegin(GL_POLYGON); glVertex(position); glVertex(position+arc_point); for(int i=0;i<18;i++){ arc_point = arc_rot.Rotate(arc_point); glVertex(position+arc_point); } glEnd(); } else if(moving && drawmoves){// devo disegnare una traslazione Point3f ntra=manipulator->track.tra; ntra.Normalize(); if(current_mode==movx){ verse=((ntra+x_axis).Norm()<1?-1:1); glColor(xcolor); axis_name='x'; }else if(current_mode==movy){ verse=((ntra+y_axis).Norm()<1?-1:1); glColor(ycolor); axis_name='y'; }else if(current_mode==movz){ verse=((ntra+z_axis).Norm()<1?-1:1); glColor(zcolor); axis_name='z'; }else assert(0); // doveva essere una traslazione message = QString("moved %1 units along %2") .arg(verse*manipulator->track.tra.Norm(),5,'f',3) .arg(axis_name); Point3f old_pos = position-manipulator->track.tra; glLineWidth(2*linewidth); glPointSize(4*linewidth); glBegin(GL_LINES); glVertex(position); glVertex(old_pos); glEnd(); glBegin(GL_POINTS); glVertex(old_pos); glEnd(); } else { // non dovevo disegnare nulla glPopAttrib(); glPopMatrix(); return; } // disegno la stringa font.setBold(true); font.setPixelSize(12); QPoint cursor=glw->mapFromGlobal(glw->cursor().pos()); glw->renderText(cursor.x()+16,cursor.y()+16,message,font); glPopAttrib(); glPopMatrix(); //occhio } void ActiveCoordinateFrame::Reset(bool reset_position,bool reset_alignment) { MovableCoordinateFrame::Reset(reset_position, reset_alignment); Update(); manipulator->Reset(); } void ActiveCoordinateFrame::SetPosition(const Point3f newpos) { MovableCoordinateFrame::SetPosition(newpos); Update(); manipulator->Reset(); } void ActiveCoordinateFrame::SetRotation(const Quaternionf newrot) { MovableCoordinateFrame::SetRotation(newrot); Update(); manipulator->Reset(); } void ActiveCoordinateFrame::AlignWith(const Point3f primary,const Point3f secondary,const char c1,const char c2) { MovableCoordinateFrame::AlignWith(primary,secondary,c1,c2); Update(); manipulator->Reset(); } void ActiveCoordinateFrame::MouseDown(int x, int y, /*Button*/ int button) { Move(manipulator->track); manipulator->Reset(); manipulator->MouseDown(x,y,button); } void ActiveCoordinateFrame::MouseMove(int x, int y) { manipulator->MouseMove(x,y); } void ActiveCoordinateFrame::MouseUp(int x, int y, /*Button */ int button) { Move(manipulator->track); manipulator->Reset(); manipulator->MouseUp(x, y, button); } void ActiveCoordinateFrame::ButtonUp(int button) { Move(manipulator->track); manipulator->Reset(); manipulator->ButtonUp((Trackball::Button) button); } void ActiveCoordinateFrame::ButtonDown(int button) { Move(manipulator->track); manipulator->Reset(); manipulator->ButtonDown((Trackball::Button) button); } void ActiveCoordinateFrame::SetSnap(float rot_deg) { assert((rot_deg>=0.0)&&(rot_deg<=180)); rot_snap_rad=rot_deg*M_PI/180.0; Update(); } void ActiveCoordinateFrame::Move(const Similarityf track) { MovableCoordinateFrame::Move(track); Update(); } void ActiveCoordinateFrame::Update() { Point3f p=position; Quaternionf r=Inverse(rotation); x_axis=r.Rotate(Point3f(1,0,0)); y_axis=r.Rotate(Point3f(0,1,0)); z_axis=r.Rotate(Point3f(0,0,1)); std::map::iterator it; for(it = manipulator->modes.begin(); it != manipulator->modes.end(); it++) { if ((*it).second) delete (*it).second; } manipulator->modes.clear(); manipulator->modes[0] = NULL; manipulator->modes[movx] = new AxisMode(p,x_axis); manipulator->modes[movy] = new AxisMode(p,y_axis); manipulator->modes[movz] = new AxisMode(p,z_axis); manipulator->modes[rotx] = new CylinderMode(p,x_axis,rot_snap_rad); manipulator->modes[roty] = new CylinderMode(p,y_axis,rot_snap_rad); manipulator->modes[rotz] = new CylinderMode(p,z_axis,rot_snap_rad); manipulator->SetCurrentAction(); }