2008-02-16 15:12:30 +01:00
|
|
|
/****************************************************************************
|
|
|
|
* 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 $
|
2008-03-14 18:15:59 +01:00
|
|
|
Revision 1.8 2008/03/02 16:44:18 benedetti
|
|
|
|
moved ActiveCoordinateFrame to its own files
|
|
|
|
|
2008-03-02 17:44:18 +01:00
|
|
|
Revision 1.7 2008/02/26 18:22:42 benedetti
|
|
|
|
corrected after quaternion/similarity/trackball changes
|
|
|
|
|
2008-02-26 19:22:42 +01:00
|
|
|
Revision 1.6 2008/02/22 20:34:35 benedetti
|
|
|
|
corrected typo
|
|
|
|
|
2008-02-22 21:34:35 +01:00
|
|
|
Revision 1.5 2008/02/22 20:04:02 benedetti
|
|
|
|
many user interface improvements, cleaned up a little
|
|
|
|
|
2008-02-22 21:04:02 +01:00
|
|
|
Revision 1.4 2008/02/17 20:52:53 benedetti
|
|
|
|
some generalization made
|
|
|
|
|
2008-02-17 21:52:53 +01:00
|
|
|
Revision 1.3 2008/02/16 14:12:30 benedetti
|
|
|
|
first version
|
|
|
|
|
2008-02-16 15:12:30 +01:00
|
|
|
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
#include <GL/glew.h>
|
|
|
|
#include <wrap/gl/math.h>
|
|
|
|
#include <wrap/gl/space.h>
|
|
|
|
#include <wrap/gl/addons.h>
|
2010-10-04 10:46:19 +02:00
|
|
|
#include <wrap/qt/gl_label.h>
|
2008-02-16 15:12:30 +01:00
|
|
|
|
|
|
|
#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");
|
|
|
|
}
|
|
|
|
|
2010-10-04 10:46:19 +02:00
|
|
|
void CoordinateFrame::Render(QGLWidget* glw,QPainter* p)
|
2008-02-16 15:12:30 +01:00
|
|
|
{
|
|
|
|
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);
|
2010-10-31 14:07:17 +01:00
|
|
|
glLabel::Mode md;
|
2008-02-16 15:12:30 +01:00
|
|
|
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);
|
2008-03-14 18:15:59 +01:00
|
|
|
// positive axes
|
2008-02-16 15:12:30 +01:00
|
|
|
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
|
2008-03-14 18:15:59 +01:00
|
|
|
// negative axes
|
2008-02-16 15:12:30 +01:00
|
|
|
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){
|
2010-10-31 14:07:17 +01:00
|
|
|
md.qFont.setBold(true);
|
|
|
|
md.qFont.setPixelSize(12);
|
2008-02-22 21:34:35 +01:00
|
|
|
float d=size+scalefactor*linewidth*1.5;
|
2010-10-31 14:07:17 +01:00
|
|
|
if (p) {
|
|
|
|
vcg::glLabel::render(p,vcg::Point3f(d,0,0),QString("X"),md);
|
|
|
|
vcg::glLabel::render(p,vcg::Point3f(0,d,0),QString("Y"),md);
|
|
|
|
vcg::glLabel::render(p,vcg::Point3f(0,0,d),QString("Z"),md);
|
|
|
|
}
|
|
|
|
}
|
2008-02-16 15:12:30 +01:00
|
|
|
if(drawvalues){
|
2010-10-31 14:07:17 +01:00
|
|
|
md.qFont.setBold(false);
|
|
|
|
md.qFont.setPixelSize(8);
|
|
|
|
md.color=Color4b(Color4b::LightGray);
|
2008-02-16 15:12:30 +01:00
|
|
|
float i;
|
|
|
|
glColor(Color4b::LightGray);
|
|
|
|
for(i=slope_a;i<size;i+=slope_a){
|
2010-10-31 14:07:17 +01:00
|
|
|
vcg::glLabel::render(p,vcg::Point3f( i,0,0),QString(" %1").arg(i,3,'f',1),md);
|
|
|
|
vcg::glLabel::render(p,vcg::Point3f(-i,0,0),QString(" %1").arg(i,3,'f',1),md);
|
2008-02-16 15:12:30 +01:00
|
|
|
}
|
|
|
|
for(i=slope_b;i<size;i+=slope_b){
|
2010-10-31 14:07:17 +01:00
|
|
|
vcg::glLabel::render(p,vcg::Point3f(0, i,0),QString(" %1").arg(i,3,'f',1),md);
|
|
|
|
vcg::glLabel::render(p,vcg::Point3f(0,-i,0),QString(" %1").arg(i,3,'f',1),md);
|
2008-02-16 15:12:30 +01:00
|
|
|
}
|
|
|
|
for(i=slope_c;i<size;i+=slope_c){
|
2010-10-31 14:07:17 +01:00
|
|
|
vcg::glLabel::render(p,vcg::Point3f(0,0, i),QString(" %1").arg(i,3,'f',1),md);
|
|
|
|
vcg::glLabel::render(p,vcg::Point3f(0,0,-i),QString(" %1").arg(i,3,'f',1),md);
|
2008-02-16 15:12:30 +01:00
|
|
|
}
|
|
|
|
}
|
2010-09-30 10:12:54 +02:00
|
|
|
glGetError(); // Patch to buggy qt rendertext;
|
2008-02-16 15:12:30 +01:00
|
|
|
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;
|
2010-09-22 00:09:13 +02:00
|
|
|
float nslope = math::Min(niceRound(slope), 0.5f*niceRound(2.0f*slope), 0.2f*niceRound(5.0f*slope));
|
|
|
|
nslope = std::max(niceRound(dim*.001f),nslope); // prevent too small slope
|
2008-02-16 15:12:30 +01:00
|
|
|
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);
|
2008-02-22 21:04:02 +01:00
|
|
|
|
|
|
|
glMultMatrix(Inverse(mrot));
|
2008-02-16 15:12:30 +01:00
|
|
|
|
|
|
|
CoordinateFrame::Render(gla);
|
|
|
|
|
|
|
|
glPopMatrix();
|
|
|
|
}
|
|
|
|
|
|
|
|
void MovableCoordinateFrame::GetTransform(Matrix44f & transform)
|
|
|
|
{
|
2008-03-14 18:15:59 +01:00
|
|
|
// build the matrix that moves points in world coordinates
|
2008-02-16 15:12:30 +01:00
|
|
|
|
2008-03-14 18:15:59 +01:00
|
|
|
// clean transform
|
2008-02-16 15:12:30 +01:00
|
|
|
transform.SetIdentity();
|
|
|
|
|
2008-03-14 18:15:59 +01:00
|
|
|
// apply rotation
|
2008-02-16 15:12:30 +01:00
|
|
|
Matrix44f rot;
|
|
|
|
rotation.ToMatrix(rot);
|
2008-02-22 21:04:02 +01:00
|
|
|
|
|
|
|
transform = Inverse(rot) * transform ;
|
2008-02-16 15:12:30 +01:00
|
|
|
|
2008-03-14 18:15:59 +01:00
|
|
|
// apply translation
|
2008-02-16 15:12:30 +01:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2008-02-17 21:52:53 +01:00
|
|
|
void MovableCoordinateFrame::Rot(float angle_deg,const Point3f axis)
|
2008-02-16 15:12:30 +01:00
|
|
|
{
|
|
|
|
Similarityf s;
|
2008-07-13 09:41:20 +02:00
|
|
|
s.SetRotate(math::ToRad(angle_deg),(rotation).Rotate(axis));
|
2008-02-16 15:12:30 +01:00
|
|
|
Move(s);
|
|
|
|
}
|
|
|
|
|
2008-02-17 21:52:53 +01:00
|
|
|
void MovableCoordinateFrame::AlignWith(const Point3f pri,const Point3f secondary,const char c1, const char c2)
|
2008-02-16 15:12:30 +01:00
|
|
|
{
|
2008-07-02 15:26:25 +02:00
|
|
|
const float EPSILON=1e-6f;
|
2008-02-16 15:12:30 +01:00
|
|
|
Point3f primary=pri;
|
|
|
|
|
|
|
|
if( primary.Norm() < EPSILON*size )
|
|
|
|
return;
|
|
|
|
|
2008-03-14 18:15:59 +01:00
|
|
|
primary.Normalize();
|
|
|
|
Plane3f plane(0,primary); // projection plane for the second rotation
|
2008-02-17 21:52:53 +01:00
|
|
|
|
|
|
|
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);
|
|
|
|
|
2008-03-14 18:15:59 +01:00
|
|
|
Point3f old_first = Inverse(rotation).Rotate(first); // axis 1
|
|
|
|
Point3f old_second_pro = plane.Projection(Inverse(rotation).Rotate(second)); // axis 2 projection
|
|
|
|
Point3f old_third_pro = plane.Projection(Inverse(rotation).Rotate(third)); // axis 3 projection
|
2008-02-17 21:52:53 +01:00
|
|
|
|
2008-03-14 18:15:59 +01:00
|
|
|
// align axis 1 to primary
|
|
|
|
RotateToAlign(old_first,primary);
|
2008-02-16 15:12:30 +01:00
|
|
|
|
2008-03-14 18:15:59 +01:00
|
|
|
Point3f secondary_pro = plane.Projection(secondary); // secondary's projection
|
|
|
|
Point3f new_second_pro = plane.Projection(Inverse(rotation).Rotate(second)); // axis 2 projection after the first rotation
|
2008-02-16 15:12:30 +01:00
|
|
|
|
2008-03-14 18:15:59 +01:00
|
|
|
if( secondary.Norm() > EPSILON*size && secondary_pro.Norm() > EPSILON ){ // secondary is not null nor parallel to primary
|
|
|
|
// align axis 2 projection after the first rotation to secondary's projection
|
2008-02-16 15:12:30 +01:00
|
|
|
secondary_pro.Normalize();
|
2008-02-17 21:52:53 +01:00
|
|
|
RotateToAlign(new_second_pro,secondary_pro);
|
2008-02-16 15:12:30 +01:00
|
|
|
return;
|
|
|
|
}
|
2008-03-14 18:15:59 +01:00
|
|
|
|
|
|
|
if ( old_second_pro.Norm() > EPSILON ) { // can realign axis 2
|
|
|
|
// align axis 2 projection after the first rotation to old axis 2 projection
|
2008-02-17 21:52:53 +01:00
|
|
|
old_second_pro.Normalize();
|
|
|
|
RotateToAlign(new_second_pro,old_second_pro);
|
2008-02-16 15:12:30 +01:00
|
|
|
return;
|
|
|
|
}
|
2008-03-14 18:15:59 +01:00
|
|
|
|
|
|
|
// realign axis 3
|
|
|
|
Point3f new_third_pro = plane.Projection(Inverse(rotation).Rotate(third));// axis 3 projection after the first rotation
|
|
|
|
assert(old_third_pro.Norm() > EPSILON ); // old axis 3 projection should not be null
|
|
|
|
// align axis 3 projection after the first rotation to old axis 3 projection
|
2008-02-17 21:52:53 +01:00
|
|
|
old_third_pro.Normalize();
|
|
|
|
RotateToAlign(new_third_pro,old_third_pro);
|
2008-02-16 15:12:30 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void MovableCoordinateFrame::Move(const Similarityf track)
|
|
|
|
{
|
|
|
|
position = position + track.tra;
|
2008-02-26 19:22:42 +01:00
|
|
|
rotation = rotation * Inverse(track.rot);
|
2008-02-16 15:12:30 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void MovableCoordinateFrame::RotateToAlign(const Point3f source, const Point3f dest)
|
|
|
|
{
|
2008-07-02 15:26:25 +02:00
|
|
|
const float EPSILON=1e-6f;
|
2008-03-14 18:15:59 +01:00
|
|
|
// source and dest must be versors
|
2008-02-16 15:12:30 +01:00
|
|
|
assert( math::Abs(source.Norm() - 1) < EPSILON);
|
|
|
|
assert( math::Abs(dest.Norm() - 1) < EPSILON);
|
2008-03-14 18:15:59 +01:00
|
|
|
|
2008-02-16 15:12:30 +01:00
|
|
|
Point3f axis = dest ^ source;
|
|
|
|
float sinangle = axis.Norm();
|
make point2 derived Eigen's Matrix, and a set of minimal fixes to make meshlab compile
with both old and new version. The fixes include:
- dot product: vec0 * vec1 => vec0.dot(vec1) (I added .dot() to the old Point classes too)
- Transpose: Transpose is an Eigen type, so we cannot keep it if Eigen is used. Therefore
I added a .tranpose() to old matrix classes, and modified most of the Transpose() to transpose()
both in vcg and meshlab. In fact, transpose() are free with Eigen, it simply returns a transpose
expression without copies. On the other be carefull: m = m.transpose() won't work as expected,
here me must evaluate to a temporary: m = m.transpose().eval(); However, this operation in very
rarely needed: you transpose at the same sime you set m, or you use m.transpose() directly.
- the last issue is Normalize which both modifies *this and return a ref to it. This behavior
don't make sense anymore when using expression template, e.g., in (a+b).Normalize(), the type
of a+b if not a Point (or whatever Vector types), it an expression of the addition of 2 points,
so we cannot modify the value of *this, since there is no value. Therefore I've already changed
all those .Normalize() of expressions to the Eigen's version .normalized().
- Finally I've changed the Zero to SetZero in the old Point classes too.
2008-10-28 01:59:46 +01:00
|
|
|
float cosangle = dest.dot(source);
|
2008-02-16 15:12:30 +01:00
|
|
|
float angle = math::Atan2(sinangle,cosangle);
|
2008-03-14 18:15:59 +01:00
|
|
|
|
2008-02-16 15:12:30 +01:00
|
|
|
if( math::Abs(angle) < EPSILON )
|
2008-03-14 18:15:59 +01:00
|
|
|
return; // angle ~ 0, aborting
|
|
|
|
|
2008-02-16 15:12:30 +01:00
|
|
|
if( math::Abs(math::Abs(angle)-M_PI) < EPSILON){
|
2008-03-14 18:15:59 +01:00
|
|
|
// must find a axis to flip on
|
2008-02-16 15:12:30 +01:00
|
|
|
Plane3f plane(0,source);
|
2008-03-14 18:15:59 +01:00
|
|
|
axis=plane.Projection(Point3f(1,0,0)); // project a "random" point on source's normal plane
|
|
|
|
if(axis.Norm() < EPSILON){ // source was ~ [1,0,0]...
|
2008-02-16 15:12:30 +01:00
|
|
|
axis=plane.Projection(Point3f(0,1,0));
|
2008-03-14 18:15:59 +01:00
|
|
|
assert(axis.Norm() > EPSILON); // this point must be good
|
2008-02-16 15:12:30 +01:00
|
|
|
}
|
2008-03-14 18:15:59 +01:00
|
|
|
}
|
|
|
|
rotation = rotation * Quaternionf(angle,axis);
|
2008-02-16 15:12:30 +01:00
|
|
|
}
|