vcglib/wrap/gui/coordinateframe.cpp

602 lines
19 KiB
C++

/****************************************************************************
* 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 <GL/glew.h>
#include <wrap/gl/math.h>
#include <wrap/gl/space.h>
#include <wrap/gl/addons.h>
#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;i<size;i+=slope_a){
glw->renderText( 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;i<size;i+=slope_b){
glw->renderText(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;i<size;i+=slope_c){
glw->renderText(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<dim;i+=tickDist)
glVertex3f(a[0] + i*v[0],a[1] + i*v[1],a[2] + i*v[2]);
glEnd();
glPushAttrib(GL_POINT_BIT);
glPointSize(linewidth*3);
glBegin(GL_POINTS);
glVertex3f(a[0] + dim*v[0],a[1] + dim*v[1],a[2] + dim*v[2]);
glEnd();
glPopAttrib();
}
float CoordinateFrame::calcSlope(const Point3d &a,const Point3d &b,float dim,int spacing,double *mm,double *mp,GLint *vp)
{
Point3d p1,p2;
gluProject(a[0],a[1],a[2],mm,mp,vp,&p1[0],&p1[1],&p1[2]);
gluProject(b[0],b[1],b[2],mm,mp,vp,&p2[0],&p2[1],&p2[2]);
p1[2]=p2[2]=0;
float tickNum = spacing/Distance(p2,p1);// pxl spacing
float slope = dim*tickNum;
float nslope = math::Min(
math::Min(niceRound(slope), 0.5f*niceRound(2.0f*slope)),
0.2f*niceRound(5.0f*slope));
nslope = math::Max<float>(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<int, TrackMode *>::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();
}