Main restructuring. added many new modes

This commit is contained in:
Paolo Cignoni 2007-05-15 14:59:10 +00:00
parent 9ed47e4afa
commit db4c4f0944
2 changed files with 855 additions and 266 deletions

View File

@ -24,6 +24,9 @@
History
$Log: not supported by cvs2svn $
Revision 1.19 2006/08/30 07:01:54 cignoni
Reverted to version 1.17. Version 1.18 was wrongly done starting from a outdated version.
Revision 1.17 2006/07/26 13:54:45 cignoni
Reversed the direction of wheel scaling and added middle mouse panning
@ -79,242 +82,617 @@ Adding copyright.
#include <wrap/gui/trackmode.h>
#include <wrap/gui/trackball.h>
#include <vcg/space/intersection3.h>
#include <vcg/math/similarity.h>
#include <iostream>
#include <wrap/gui/trackutils.h>
using namespace std;
using namespace vcg;
using namespace vcg::trackutils;
void TrackMode::Apply(Trackball *trackball, float WheelNotch) {
trackball->track.sca*=pow(1.2f,-WheelNotch);
}
void ScaleMode::Apply(Trackball *tb, Point3f new_point) {
float ScreenHeight= float(tb->camera.viewport[3]-tb->camera.viewport[1]);
float dist=(new_point[1]-tb->last_point[1])/ScreenHeight;
tb->track.sca= tb->last_track.sca*pow(3.0f,-dist);
}
/// Compute the plane plane perpedicular to view dir and passing through manip center
Plane3f TrackMode::GetViewPlane(const View<float> &camera, const Point3f &center) {
Point3f vp = camera.ViewPoint();
Plane3f pl;
Point3f plnorm= vp - center;
plnorm.Normalize();
pl.Set(plnorm, plnorm*center);
return pl;
}
/// Given a point p in window coordinate it compute the point where the lie p
/// over the plane paralell the viewplane and passing through the center of the trackball
Point3f TrackMode::HitViewPlane(Trackball *tb, const Point3f &p) {
// plane perpedicular to view direction and passing through manip center
Plane3f vp = GetViewPlane(tb->camera, tb->center);
Line3fN ln= tb->camera.ViewLineFromWindow(Point3f(p[0],p[1],0));
Point3f PonVP;
/*bool res = */IntersectionLinePlane<float>(vp,ln,PonVP);
return PonVP;
}
// the most important function; given a new point in window coord, it update the transformation computed by the trackball.
// General scheme : the transformation is a function of just the begin and current mouse positions, with greater precision is function of just two 3d points over the manipulator.
void SphereMode::Apply(Trackball *tb, Point3f new_point) {
Point3f hitOld=Hit(tb, tb->last_point);
Point3f hitNew=Hit(tb, new_point);
tb->Hits.push_back(hitNew);
// Point3f center = tb->track.tra+tb->center; // original 2006 01 12
Point3f center = tb->center;
Point3f axis = (hitNew - center)^(hitOld- center);
// Figure out how much to rotate around that axis.
//float phi=Angle((hitNew- tb->center),(hitOld- tb->center));
float phi = Distance(hitNew,hitOld) / tb->radius;
tb->track.rot = tb->last_track.rot * Quaternionf(phi,axis);
/* Codice Originale Ponchio
Point3f ref = (tb->camera.ViewPoint() - tb->center).Normalize();
Point3f axis = hitNew^ref;
axis.Normalize();
float dist = (hitNew - ref).Norm()/2;
float phi = 2 * math::Asin(dist);
Point3f oaxis = hitOld^ref;
oaxis.Normalize();
float odist = (hitOld - ref).Norm()/2;
float ophi = 2 * math::Asin(odist);
Quaternionf r = tb->last_track.rot;
Quaternionf diff = r * Quaternionf(phi, axis) *
Quaternionf(-ophi, oaxis) * Inverse(r);
tb->track = Similarityf().SetRotate(diff) * tb->last_track;*/
}
// the old implementation is not used anymore, some of the old support functions,
// like HitViewPlane, GetViewPlane, HitHyper and SphereMode::Hit were made
// class-independent and moved to trackutils.h
/*
dato un punto in coordinate di schermo e.g. in pixel stile opengl
calcola il punto di intersezione tra la viewline che passa per viewpoint e per hitplane e l'iperboloide.
l'iperboloide si assume essere quello di rotazione attorno alla retta viewpoint-center e di raggio rad
si assume come sistema di riferimento quello con l'origine su center ecome x la retta center-viewpoint
eq linea
hitplane.y
y = - ----------- * x + hitplane.y
viewpoint.x
eq hiperboloide di raggio r (e.g. che passa per (r/sqrt2,r/sqrt2)
1
y = --- * (r^2 /2.0)
x
hitplane.y
----------- * x^2 - hitplane.y *x + (r^2/2.0) == 0
viewpoint.x
*/
void TrackMode::Apply(Trackball *trackball, float WheelNotch) {
}
void ScaleMode::Apply(Trackball *tb, Point3f new_point) {
}
Plane3f TrackMode::GetViewPlane(const View<float> &camera, const Point3f &center) {
}
Point3f TrackMode::HitViewPlane(Trackball *tb, const Point3f &p) {
}
void SphereMode::Apply(Trackball *tb, Point3f new_point) {
}
bool SphereMode::HitHyper(Point3f center, float radius, Point3f viewpoint, Plane3f vp, Point3f hitplane, Point3f &hit)
{
float hitplaney = Distance(center,hitplane);
float viewpointx= Distance(center,viewpoint);
float a = hitplaney/viewpointx;
float b = -hitplaney;
float c = radius*radius/2.0f;
float delta = b*b - 4*a*c;
float x1,x2,xval,yval;
if(delta>0)
{
x1= (- b - sqrt(delta))/(2.0f*a);
x2= (- b + sqrt(delta))/(2.0f*a);
xval=x1; // always take the minimum value solution
yval=c/xval; // alternatively it also oould be the other part of the equation yval=-(hitplaney/viewpointx)*xval+hitplaney;
}
else
{
return false;
}
// Computing the result in 3d space;
Point3f dirRadial=hitplane-center;
dirRadial.Normalize();
Point3f dirView=vp.Direction();
dirView.Normalize();
hit= center +dirRadial*yval+dirView*xval;
return true;
}
/* dato un punto in coordinate di schermo e.g. in pixel stile opengl
restituisce un punto in coordinate di mondo sulla superficie
della trackball.
La superficie della trackball e' data da una sfera + una porzione
di iperboloide di rotazione.
Assumiamo la sfera di raggio unitario e centrata sull'origine e
di guardare lungo la y negativa.
X 0 sqrt(1/2) 1
eq sfera: y=sqrt(1-x*x); 1 sqrt(1/2) 0
eq iperboloide : y=1/2*x; inf sqrt(1/2) 1/2
eq cono y=x+sqrt(2);
*/
Point3f SphereMode::Hit(Trackball *tb, const Point3f &p) {
// const float Thr = tb->radius/math::Sqrt(2.0f);
Line3fN vn = tb->camera.ViewLineFromModel(tb->center);
Line3fN ln = tb->camera.ViewLineFromWindow(Point3f(p[0],p[1],0));
Point3f viewpoint = tb->camera.ViewPoint();
Plane3f vp = GetViewPlane(tb->camera, tb->center);
Point3f hit,hitPlane,hitSphere,hitSphere1,hitSphere2,hitHyper;
IntersectionLinePlane<float>(vp, ln, hitPlane);
Sphere3f sphere(tb->center,tb->radius);
bool resSp = IntersectionLineSphere<float>(sphere, ln, hitSphere1, hitSphere2);
if(resSp == true)
{
if(Distance(viewpoint,hitSphere1)<Distance(viewpoint,hitSphere2))
hitSphere=hitSphere1;
else hitSphere=hitSphere2;
}
/*float dl=*/Distance(ln,tb->center);
bool resHp = HitHyper(tb->center, tb->radius, viewpoint, vp, hitPlane, hitHyper) ;
// four cases
// 1) Degenerate line tangent to both sphere and hyperboloid!
if((!resSp && !resHp) )
{
hit=ClosestPoint(ln,tb->center);
//printf("closest point to line %f\n",Distance(hit,tb->center));
return hit;
}
if((resSp && !resHp) ) return hitSphere; // 2) line cross only the sphere
if((!resSp && resHp) ) return hitHyper; // 3) line cross only the hyperboloid
// 4) line cross both sphere and hyperboloid: choose according angle.
float angleDeg=math::ToDeg(Angle((viewpoint-tb->center),(hitSphere-tb->center)));
//printf("Angle %f (%5.2f %5.2f %5.2f) (%5.2f %5.2f %5.2f)\n",angleDeg,hitSphere[0],hitSphere[1],hitSphere[2],hitHyper[0],hitHyper[1],hitHyper[2]);
if(angleDeg<45) return hitSphere;
else return hitHyper;
//
// Codice ORIGINALE PONCHIO
//vp.SetOffset(vp.Offset() + Thr);
//Point3f hit;
//bool res = Intersection<float>(vp, ln, hit);
//float d = Distance(tb->center - vn.Direction()*Thr, hit);
//if(d < Thr) {
// Point3f hit2;
// Sphere3f sphere(tb->center, tb->radius);
// bool res = Intersection<float>(sphere, ln, hit, hit2);
// //find closest intersection to sphere
// float d = (hit - viewpoint).Norm();
// float d2 = (hit2 - viewpoint).Norm();
// if(d > d2) hit = hit2;
// hit -= tb->center;
//} else {
// if(d > 2.99 * Thr)
// d = 2.99 * Thr;
// Point3f norm = (hit - tb->center)^(viewpoint - tb->center);
// norm.Normalize();
// float phi = -M_PI/4 - 3*M_PI/8 *(d - Thr)/Thr;
// Quaternionf q(phi, norm);
// hit = q.Rotate((viewpoint - tb->center).Normalize() * tb->radius);
//}
// hit.Normalize();
// return hit;
}
void PlaneMode::Apply(Trackball *tb, Point3f new_point) {
Point3f hitOld = HitViewPlane(tb, tb->last_point);
Point3f hitNew = HitViewPlane(tb, new_point);
//Matrix44f m; tb->track.rot.ToMatrix(m);
//tb->track.tra = tb->last_track.tra + Inverse(m)*Point3f(hitNew- hitOld);// orig 2006 01 12
//tb->track.tra = tb->last_track.tra + Inverse(m)*Point3f(hitNew- hitOld)/tb->track.sca;;
tb->Translate(hitNew- hitOld);
}
}
void ZMode::Apply(Trackball *tb, Point3f new_point) {
float ScreenHeight= float(tb->camera.viewport[3]-tb->camera.viewport[1]);
float dist=(new_point[1]-tb->last_point[1])/ScreenHeight;
//Matrix44f m; tb->track.rot.ToMatrix(m);
//tb->track.tra = tb->last_track.tra + Inverse(m)*Point3f(0,0,-2*dist)/tb->track.sca;;
tb->Translate(Point3f(0,0,-2*dist));
}
*/
// Track mode implementation, dummy.
void TrackMode::Apply (Trackball * , float ){}
void TrackMode::Apply (Trackball * , Point3f ){}
void TrackMode::Draw(Trackball * ){}
void TrackMode::SetAction (){}
void TrackMode::Reset (){}
// draw an inactive trackball
void InactiveMode::Draw(Trackball * tb){
DrawSphereIcon(tb,false);
}
// Sphere mode implementation.
// the most important function; given a new point in window coord,
// it update the transformation computed by the trackball.
// General scheme : the transformation is a function of just
// the begin and current mouse positions, with greater precision
// is function of just two 3d points over the manipulator.
void SphereMode::Apply (Trackball * tb, Point3f new_point)
{
Point3f hitOld = HitSphere (tb, tb->last_point);
Point3f hitNew = HitSphere (tb, new_point);
tb->Hits.push_back (hitNew);
Point3f center = tb->center;
Point3f axis = (hitNew - center) ^ (hitOld - center);
// Figure out how much to rotate around that axis.
float phi = Distance (hitNew, hitOld) / tb->radius;
tb->track.rot = tb->last_track.rot * Quaternionf (phi, axis);
}
void SphereMode::Draw(Trackball * tb){
DrawSphereIcon(tb,true );
}
// Pan mode implementation.
void PanMode::Apply (Trackball * tb, Point3f new_point)
{
Point3f hitOld = HitViewPlane (tb, tb->last_point);
Point3f hitNew = HitViewPlane (tb, new_point);
tb->Translate (hitNew - hitOld);
}
void PanMode::Draw(Trackball * tb){
DrawSphereIcon(tb,true );
DrawUglyPanMode(tb);
}
// Z mode implementation.
void ZMode::Apply (Trackball * tb, float WheelNotch)
{
Point3f dir= (GetViewPlane (tb->camera, tb->center)).Direction();
dir.Normalize();
tb->Translate (dir * (-WheelNotch));
}
void ZMode::Apply (Trackball * tb, Point3f new_point)
{
Point3f dir= (GetViewPlane (tb->camera, tb->center)).Direction();
dir.Normalize();
tb->Translate (dir * ( -2.0f * getDeltaY(tb,new_point)));
}
void ZMode::Draw(Trackball * tb){
DrawSphereIcon(tb,true );
DrawUglyZMode(tb);
}
// Scale mode implementation.
void ScaleMode::Apply (Trackball * tb, float WheelNotch)
{
tb->track.sca *= pow (1.2f, -WheelNotch);
}
void ScaleMode::Apply (Trackball * tb, Point3f new_point)
{
tb->track.sca = tb->last_track.sca * pow (3.0f, -(getDeltaY(tb,new_point)));
}
void ScaleMode::Draw(Trackball * tb){
DrawSphereIcon(tb,true );
DrawUglyScaleMode(tb);
}
// Axis mode implementation.
void AxisMode::Apply (Trackball * tb, float WheelNotch)
{
tb->Translate (axis.Direction () * (WheelNotch / 10.0f));
}
void AxisMode::Apply (Trackball * tb, Point3f new_point)
{
pair< Point3f,bool > hitOld = HitNearestPointOnAxis (tb, axis, tb->last_point);
pair< Point3f,bool > hitNew = HitNearestPointOnAxis (tb, axis, new_point);
if (hitOld.second && hitNew.second){
tb->Translate (hitNew.first - hitOld.first);
}
}
void AxisMode::Draw(Trackball * tb){
DrawSphereIcon(tb,true );
DrawUglyAxisMode(tb,axis);
}
// Plane mode implementation.
void PlaneMode::Apply (Trackball * tb, Point3f new_point)
{
pair< Point3f, bool > hitOld = HitPlane(tb,tb->last_point,plane);
pair< Point3f, bool > hitNew = HitPlane(tb,new_point,plane);
if(hitOld.second && hitNew.second){
tb->Translate (hitNew.first - hitOld.first);
}
}
void PlaneMode::Draw(Trackball * tb){
DrawSphereIcon(tb,true );
DrawUglyPlaneMode(tb, plane);
}
// Cylinder mode implementation.
void CylinderMode::Apply (Trackball * tb, float WheelNotch)
{
const float PI2=6.283185307179586232;
tb->track.rot = tb->last_track.rot * Quaternionf (WheelNotch/(tb->radius * PI2),axis.Direction());
}
void CylinderMode::Apply (Trackball * tb, Point3f new_point)
{
Plane3f viewplane=GetViewPlane (tb->camera, tb->center);
Line3f axisproj;
axisproj=ProjectLineOnPlane(axis,viewplane);
float angle;
const float EPSILON=0.005f; // this IS scale independent
if(axisproj.Direction().Norm() < EPSILON){
angle=(10.0 * getDeltaY(tb,new_point)) / tb->radius;
} else {
Point3f hitOld = HitViewPlane (tb, tb->last_point);
Point3f hitNew = HitViewPlane (tb, new_point);
axisproj.Normalize();
Point3f plusdir= viewplane.Direction() ^ axisproj.Direction();
float distOld = signedDistance(axisproj,hitOld,plusdir);
float distNew = signedDistance(axisproj,hitNew,plusdir);
angle= (distNew-distOld) / tb->radius;
}
tb->track.rot = tb->last_track.rot * Quaternionf (angle,axis.Direction());
}
void CylinderMode::Draw(Trackball * tb){
DrawSphereIcon(tb,true );
DrawUglyCylinderMode(tb,axis);
}
// Path mode implementation.
void PathMode::Init(const vector < Point3f > &pts)
{
unsigned int npts=pts.size();
assert(npts >= 2);
points.reserve(npts);
for(unsigned int i=0;i<npts;i++){
points.push_back(pts[i]);
}
path_length=0.0f;
min_seg_length=Distance(points[0],points[1]);
float seg_length;
for(unsigned int i=1;i<npts;i++){
seg_length=Distance(points[i-1],points[i]);
path_length += seg_length;
min_seg_length = min(seg_length,min_seg_length);
}
if(wrap){
seg_length=Distance(points[npts-1],points[0]);
path_length += seg_length;
min_seg_length = min(seg_length,min_seg_length);
}
}
void PathMode::Reset()
{
current_state=initial_state;
}
Point3f PathMode::SetStartNear(Point3f point)
{
float p0_state=0;
Point3f p0,p1;
float nearest_state=0;
Point3f nearest_point=points[0];
float nearest_distance=Distance(nearest_point,point);
unsigned int npts=points.size();
for(unsigned int i = 1;i <= npts;i++){
if( i == npts){
if (wrap){
p0=points[npts-1];
p1=points[0];
} else {
break;
}
} else {
p0=points[i-1];
p1=points[i];
}
Point3f segment_point=ClosestPoint(Segment3f(p0,p1),point);
float distance=Distance(segment_point,point);
if(distance<nearest_distance){
nearest_point=segment_point;
nearest_distance=distance;
nearest_state=p0_state+(Distance(p0,nearest_point)/path_length);
}
float segment_norm= Distance(p0,p1) / path_length;
p0_state+=segment_norm;
}
assert( nearest_state >= 0.0 );
if(nearest_state > 1.0){
nearest_state=1.0;
nearest_point=( wrap ? points[0] : points[npts-1] );
}
initial_state=nearest_state;
return nearest_point;
}
void PathMode::GetPoints(float state, Point3f & point, Point3f & prev_point, Point3f & next_point)
{
assert(state >= 0.0f);
assert(state <= 1.0f);
float remaining_norm=state;
Point3f p0,p1;
unsigned int npts=points.size();
for(unsigned int i = 1;i <= npts;i++){
if( i == npts){
if (wrap){
p0=points[npts-1];
p1=points[0];
} else {
break;
}
} else {
p0=points[i-1];
p1=points[i];
}
float segment_norm= Distance(p0,p1) / path_length;
if (segment_norm < remaining_norm){
remaining_norm -= segment_norm;
continue;
}
prev_point = p0;
next_point = p1;
float ratio= remaining_norm / segment_norm;
point = prev_point + (( next_point - prev_point ) * ratio);
const float EPSILON=min_seg_length * 0.01f;
if(Distance(point,prev_point) < EPSILON){
point=prev_point;
if (i > 1){
prev_point=points[i-2];
} else if (wrap){
prev_point=points[npts-1];
}
} else if (Distance(point,next_point) < EPSILON){
point=next_point;
if( i < (npts-1)){
next_point=points[i+1];
} else {
if (wrap){
next_point=points[1];
} else {
next_point=points[npts-1];
}
}
}
return;
}
// rounding errors can lead out of the for..
prev_point = p0;
point = p1;
if (wrap){
next_point=points[1];
}else{
next_point = points[npts-1];
}
}
void PathMode::Apply (Trackball * tb, float WheelNotch)
{
const float STEP_COEFF = min_seg_length * 0.5f;
float delta=(WheelNotch*STEP_COEFF)/path_length;
Point3f old_point,new_point,prev_point,next_point;
GetPoints(current_state,old_point,prev_point,next_point);
current_state=Normalize(current_state+delta);
GetPoints(current_state,new_point,prev_point,next_point);
tb->Translate (new_point - old_point);
}
float PathMode::Normalize(float state)
{
if ( wrap ) {
double intpart;
float fractpart;
fractpart =(float) modf(state,&intpart);
if( fractpart < 0.0f )
fractpart += 1.0f;
return fractpart;
}
if ( state < 0.0f )
return 0.0f;
if ( state > 1.0f )
return 1.0f;
return state;
}
int PathMode::Verse(Point3f reference_point,Point3f current_point,Point3f prev_point,Point3f next_point)
{
Point3f reference_dir = reference_point - current_point ;
Point3f prev_dir = prev_point - current_point ;
Point3f next_dir = next_point - current_point ;
const float EPSILON=min_seg_length * 0.005f;
if (reference_dir.Norm() < EPSILON)
reference_dir = Point3f(0,0,0);
if (prev_dir.Norm() < EPSILON)
prev_dir = Point3f(0,0,0);
if (next_dir.Norm() < EPSILON)
next_dir = Point3f(0,0,0);
reference_dir.Normalize();
prev_dir.Normalize();
next_dir.Normalize();
float prev_coeff,next_coeff;
prev_coeff = prev_dir * reference_dir;
next_coeff = next_dir * reference_dir;
if (prev_coeff < 0.0f)
prev_coeff = 0.0f;
if (next_coeff < 0.0f)
next_coeff = 0.0f;
if( (prev_coeff == 0.0f) && (next_coeff == 0.0f)){
return 0;
}
if ( prev_coeff <= next_coeff ){
return 1;
}
return -1;
}
float PathMode::HitPoint(float state, Ray3fN ray, Point3f &hit_point)
{
Point3f current_point, next_point, prev_point;
GetPoints(state,current_point,prev_point,next_point);
Point3f closest_point;
closest_point=ray.ClosestPoint(current_point);
int verse=Verse(closest_point,current_point,prev_point,next_point);
if (verse == 0){
hit_point=current_point;
return 0.0f;
}
Segment3f active_segment;
if (verse > 0){
active_segment=Segment3f(current_point,next_point);
} else {
active_segment= Segment3f(current_point,prev_point);
}
hit_point=ClosestPoint(active_segment,closest_point);
return verse * ((hit_point-current_point).Norm() / path_length);
}
void PathMode::SetAction (){
Point3f temp1,temp2;
GetPoints(current_state,old_hitpoint,temp1,temp2);
}
void PathMode::Apply (Trackball * tb, Point3f new_point)
{
Ray3fN ray = line2ray(tb->camera.ViewLineFromWindow (new_point));
Point3f hit_point;
float delta_state=HitPoint(current_state,ray,hit_point);
current_state=Normalize(current_state+delta_state);
tb->Translate (hit_point - old_hitpoint);
}
void PathMode::Draw(Trackball * tb){
DrawSphereIcon(tb,true );
Point3f current_point,prev_point,next_point;
GetPoints(current_state,current_point,prev_point,next_point);
DrawUglyPathMode(tb,points,current_point,prev_point,
next_point,old_hitpoint,wrap);
}
// Area mode implementation.
void AreaMode::Init(const vector < Point3f > &pts)
{
unsigned int npts=pts.size();
assert(npts >= 3);
//get the plane
Point3f p0=pts[0];
unsigned int onethird=(unsigned int)floor(npts/3.0);
const float EPSILON=0.005;
bool pts_not_in_line=false;
Point3f a,b;
for(unsigned int i=0;i<onethird;i++){
a=(pts[(i+ onethird )%npts] - pts[i%npts]).Normalize();
b=(pts[(i+(2*onethird))%npts] - pts[i%npts]).Normalize();
pts_not_in_line = (a ^ b).Norm() > EPSILON;
if(pts_not_in_line){
plane.Init( pts[i%npts],
pts[(i+(onethird))%npts],
pts[(i+(2*onethird))%npts]);
break;
}
}
assert(pts_not_in_line);
float ncx,ncy,ncz;
ncx=fabs(plane.Direction()[0]);
ncy=fabs(plane.Direction()[1]);
ncz=fabs(plane.Direction()[2]);
if(( ncx > ncy ) && ( ncx > ncz )){
first_coord_kept=1;
second_coord_kept=2;
} else if(( ncy > ncx ) && ( ncy > ncz)){
first_coord_kept=0;
second_coord_kept=2;
} else {
first_coord_kept=0;
second_coord_kept=1;
}
points.reserve(npts);
for(unsigned int i=0;i<npts;i++){
points.push_back(plane.Projection(pts[i]));
}
min_side_length=Distance(points[0],points[1]);
for(unsigned int i=1;i<npts;i++){
min_side_length=min(Distance(points[i-1],points[i]),min_side_length);
}
rubberband_handle=old_status=status=initial_status=p0;
}
void AreaMode::Reset()
{
rubberband_handle=old_status=status=initial_status;
path.clear();
}
void AreaMode::Apply (Trackball * tb, Point3f new_point)
{
if(begin_action){
delta_mouse=tb->camera.Project(status)-new_point;
begin_action=false;
}
pair< Point3f, bool > hitNew = HitPlane(tb,new_point+delta_mouse,plane);
if(! hitNew.second){
return;
}
Point3f hit_point=hitNew.first;
Point3f delta_status=Move(status,hit_point);
status += delta_status;
tb->Translate (status - old_status);
rubberband_handle=hit_point;
}
void AreaMode::SetAction ()
{
begin_action=true;
old_status=status;
path.clear();
path.push_back(status);
rubberband_handle=status;
}
Point3f AreaMode::Move(Point3f start,Point3f end)
{
const float EPSILON=min_side_length*0.001f;
Point3f pt=start;
bool done=false;
bool end_inside=Inside(end);
while(!done){
path.push_back(pt);
Segment3f segment(pt,end);
bool p_on_side = false;
bool hit=false;
Point3f pside,phit;
bool slide,mid_inside;
int np=points.size(), i, j;
for (i = 0, j = np-1; i < np; j = i++) {
Segment3f side(points[i],points[j]);
Point3f pseg,psid;
pair<float,bool> res=SegmentSegmentDistance(segment,side,pseg,psid);
if(res.first < EPSILON && ! res.second){
float dist= Distance(pt,pseg);
if(dist < EPSILON){
Point3f pn=ClosestPoint(side,end);
if(!p_on_side || (Distance(pn,end)<Distance(end,pside))){
pside=pn;
p_on_side=true;
}
} else {
if (!hit || Distance(pt,pseg) < Distance(pt,phit)){
phit=pseg;
hit=true;
}
}
}
}
if (p_on_side)
slide = Distance(pside,pt) > EPSILON;
if (hit)
mid_inside = Inside( pt + ( ( phit - pt ) / 2) );
if ( !hit && end_inside ){
pt = end;
done = true;
} else if ( hit && (!p_on_side || (p_on_side && mid_inside))) {
pt = phit;
} else if ( p_on_side && slide) {
pt = pside;
} else {
done = true;
}
}
path.push_back(pt);
return pt - start;
}
// adapted from the original C code by W. Randolph Franklin
// http://www.ecse.rpi.edu/Homepages/wrf/Research/Short_Notes/pnpoly.html
bool AreaMode::Inside(Point3f point)
{
bool inside=false;
float x=point[first_coord_kept];
float y=point[second_coord_kept];
float yi, yj, xi, xj;
int i, j, np=points.size();
for (i = 0, j = np-1; i < np; j = i++) {
xi=points[i][first_coord_kept];
yi=points[i][second_coord_kept];
xj=points[j][first_coord_kept];
yj=points[j][second_coord_kept];
if ( ( ( (yi<=y) && (y<yj) ) || ( (yj<=y) && (y<yi) ) ) &&
( x < ( xj - xi ) * ( y - yi ) / ( yj - yi ) + xi ) )
{
inside=!inside;
}
}
return inside;
}
Point3f AreaMode::SetStartNear(Point3f point)
{
Point3f candidate=plane.Projection(point);
if (Inside(candidate)){
initial_status=candidate;
return initial_status;
}
Point3f nearest_point=initial_status;
float nearest_distance=Distance(nearest_point,candidate);
int i, j, np=points.size();
for (i = 0, j = np-1; i < np; j = i++) {
Segment3f side(points[i],points[j]);
Point3f side_point=ClosestPoint(side,candidate);
float distance=Distance(side_point,candidate);
if( distance < nearest_distance ){
nearest_point=side_point;
nearest_distance=distance;
}
}
initial_status=nearest_point;
return initial_status;
}
void AreaMode::Draw(Trackball * tb)
{
DrawSphereIcon(tb,true );
DrawUglyAreaMode(tb,points,status,old_status,plane,path,rubberband_handle);
}

View File

@ -24,6 +24,9 @@
History
$Log: not supported by cvs2svn $
Revision 1.10 2007/02/26 01:30:02 cignoni
Added reflection Name
Revision 1.9 2006/02/13 13:10:27 cignoni
Added Zmode for moving objects along the perpendicular to the viewplane
@ -56,75 +59,283 @@ Adding copyright.
#include <vcg/space/line3.h>
#include <vcg/space/plane3.h>
#include <vcg/space/segment3.h>
#include <vcg/space/ray3.h>
#include <wrap/gui/view.h>
using namespace std;
namespace vcg {
class Trackball;
// Base class for all the track modes.
// This class' functions does nothing.
class TrackMode {
public:
virtual ~TrackMode() {}
virtual void Apply(Trackball *trackball, Point3f new_point) = 0;
virtual void Apply(Trackball *trackball, float WheelNotch);
virtual const char *Name()=0;
virtual void Draw() {}
protected:
Plane3f GetViewPlane(const View<float> &view, const Point3f &center);
Point3f HitViewPlane(Trackball *trackball, const Point3f &p);
virtual ~TrackMode () {
}
virtual void Apply (Trackball * trackball, Point3f new_point);
virtual void Apply (Trackball * trackball, float WheelNotch);
virtual void SetAction ();
virtual void Reset ();
virtual const char *Name (){
return "TrackMode";
};
virtual void Draw (Trackball * trackball);
};
// Inactive mode.
// useful only for drawing the inactive trackball
class InactiveMode:public TrackMode {
public:
const char *Name () {
return "InactiveMode";
};
void Draw (Trackball * trackball);
};
/* View space modes */
// old interfaces
/*
class SphereMode: public TrackMode {
public:
void Apply(Trackball *trackball, Point3f new_point);
const char* Name() {return "SphereMode";};
protected:
Point3f Hit(Trackball *trackball, const Point3f &p);
bool HitHyper(Point3f center, float radius, Point3f viewpoint, Plane3f vp, Point3f hitplane, Point3f &hit) ;
};
}
class CylinderMode: public TrackMode {
}
class PlaneMode: public TrackMode {
}
class ZMode: public TrackMode {
}
class LineMode: public TrackMode {
}
class LineMode: public TrackMode {
}
class ScaleMode: public TrackMode {
*/
// Sphere mode.
// The classic trackball.
class SphereMode:public TrackMode {
public:
CylinderMode(const Line3f &/*line*/, float /*radius = 1*/) {}
void Apply(Trackball * /*trackball*/, Point3f /*new_point*/) {}
const char* Name() {return "CylinderMode";};
protected:
Line3f line;
float radius;
void Apply (Trackball * trackball, Point3f new_point);
const char *Name () {
return "SphereMode";
};
void Draw (Trackball * trackball);
};
class PlaneMode: public TrackMode {
// Panning mode.
// The user can drag the model on the view plane.
class PanMode:public TrackMode {
public:
PlaneMode(const Plane3f &pl): plane(pl) {}
void Apply(Trackball *trackball, Point3f new_point);
const char* Name() {return "PlaneMode";};
protected:
void Apply (Trackball * trackball, Point3f new_point);
const char *Name () {
return "PanMode";
};
void Draw (Trackball * trackball);
};
// Z mode.
// Dragging the mouse up and down or scrolling the
// mouse wheel will move the object along the Z of the camera.
class ZMode:public TrackMode {
public:
const char *Name () {
return "ZMode";
};
void Apply (Trackball * trackball, Point3f new_point);
void Apply (Trackball * trackball, float WheelNotch);
void Draw (Trackball * trackball);
};
// Scale Mode.
// Dragging the mouse up and down or scrolling the
// mouse wheel will scale the object.
class ScaleMode:public TrackMode {
public:
const char *Name () {
return "ScaleMode";
};
void Apply (Trackball * trackball, Point3f new_point);
void Apply (Trackball * trackball, float WheelNotch);
void Draw (Trackball * trackball);
};
// Axis mode.
// Moves the object in a costrained direction.
// The user can either drag the mouse or scroll the wheel.
// The direction can be specified either with a line
// or a origin and a direction.
// the object posistion is not needed to be on the line.
class AxisMode:public TrackMode {
public:
AxisMode (const Line3f & ln)
: axis (ln) {
}
AxisMode (const Point3f & origin, const Point3f & direction) {
axis = Line3fN (origin, direction);
}
const char *Name () {
return "AxisMode";
};
void Apply (Trackball * trackball, Point3f new_point);
void Apply (Trackball * trackball, float WheelNotch);
void Draw (Trackball * trackball);
private:
Line3fN axis;
};
// Plane mode.
// The user can drag the object in a costrained plane.
// The plane can be specified either with a plane
// or the plane's equation parameters
// the object posistion is not needed to be on the plane.
class PlaneMode:public TrackMode {
public:
PlaneMode (float a, float b, float c, float d)
: plane(Plane3f(d,Point3f(a,b,c))){
}
PlaneMode (Plane3f & pl)
: plane(pl) {
}
const char *Name () {
return "PlaneMode";
};
void Apply (Trackball * trackball, Point3f new_point);
void Draw (Trackball * trackball);
private:
Plane3f plane;
};
// Move the object along the Z of the Camera
// complement of the Plane mode
class ZMode: public TrackMode {
const char* Name() {return "ZMode";};
// Cylinder mode.
// Rotates the object along a fixed axis
// The user can either drag the mouse or scroll the wheel,
// in either cases the rotation's angle is influenced by
// the radius of the trackball.
// The axis can be specified either with a line
// or a origin and a direction
// when the user drags the mouse, if the axis is too
// perpendicular to view plane, the angle is specified
// only by the vertical component of the mouse drag and the radius.
class CylinderMode:public TrackMode {
public:
void Apply(Trackball *trackball, Point3f new_point);
CylinderMode (Line3fN & ln)
: axis (ln){
}
CylinderMode (const Point3f & origin, const Point3f & direction)
: axis (Line3fN(origin,direction)){
}
const char *Name () {
return "CylinderMode";
};
void Apply (Trackball * trackball, Point3f new_point);
void Apply (Trackball * trackball, float WheelNotch);
void Draw (Trackball * trackball);
private:
Line3fN axis;
};
class LineMode: public TrackMode {
// Path mode.
// move the object along an eventually closed path.
// The user can either drag the mouse or scroll the wheel,
// when the user drags the mouse, the object tries to slide toward it.
// if the path is a simple segment, it can be specified just with the endpoints,
// otherwise it's specified with a point vector and, eventually, a boolean value used for closing the path.
// the object is assumed to initially be on the same position of the first point on the path.
// you can try to set the starting point calling SetStartNear(Point3f)
// the path is NOT assumed to have 0-length segments, so, if you want to close the path, please DO NOT add
// a copy of the first point on the end of the vector...
// the vector passed to build the path is copied locally.
class PathMode:public TrackMode {
public:
LineMode(const Line3f &/*line*/) {}
void Apply(Trackball * /*trackball*/, Point3f /*new_point*/) {}
const char* Name() {return "LineMode";};
protected:
Line3f line;
PathMode ( const vector < Point3f > &pts, bool w = false)
: points(), wrap(w), current_state(0), initial_state(0), old_hitpoint()
{
Init(pts);
assert(min_seg_length > 0.0f);
}
PathMode ( const Point3f &start, const Point3f &end )
: points(), wrap(false), current_state(0), initial_state(0), old_hitpoint()
{
points.push_back(start);
points.push_back(end);
path_length=Distance(start,end);
min_seg_length=path_length;
assert(min_seg_length > 0.0f);
}
const char *Name () {
return "PathMode";
};
void Apply (Trackball * trackball, Point3f new_point);
void Apply (Trackball * trackball, float WheelNotch);
void Draw (Trackball * trackball);
void SetAction ();
void Reset ();
Point3f SetStartNear(Point3f p);
private:
void Init(const vector < Point3f > &points);
void GetPoints(float state, Point3f & point, Point3f & prev_point, Point3f & next_point);
float Normalize(float state);
float HitPoint(float state, Ray3fN ray, Point3f &hit_point);
int Verse(Point3f reference_point,Point3f current_point,Point3f prev_point,Point3f next_point);
vector < Point3f > points;
bool wrap;
float current_state;
float initial_state;
float path_length;
float min_seg_length;
Point3f old_hitpoint;
};
class ScaleMode: public TrackMode {
// Area mode.
// The user can drag the object inside a planar area, defined by a polygon.
// the polygon can be non convex, and is specified with a vector of vertexes
// if the object's trajectory intersects some poligon side, it tries to slide
// around it, in a "rubber band flavoured" way.
// for the vertexes vector its calculated the plane of the polygon, and then
// every point in the vector is projected on this plane.
// the object is assumed to initially be on the same position of the first vertex.
// you can try to set the starting point calling SetStartNear(Point3f)
// the vector is assumed to be formed of NON collinear points
// the polygon is NOT assumed to have 0-length sides, so please DO NOT add
// a copy of the first point on the end of the vector...
// the vector passed to build the polygon is copied locally.
class AreaMode:public TrackMode {
public:
const char* Name() {return "ScaleMode";};
void Apply(Trackball *trackball, Point3f new_point);
AreaMode (const vector < Point3f > &pts)
{
Init(pts);
assert(min_side_length > 0.0f);
}
const char *Name () {
return "AreaMode";
};
void Apply (Trackball * trackball, Point3f new_point);
void Draw (Trackball * trackball);
void SetAction ();
void Reset ();
Point3f SetStartNear(Point3f p);
private:
void Init(const vector < Point3f > &pts);
bool Inside(Point3f point);
Point3f Move(Point3f start,Point3f end);
vector < Point3f > points;
bool begin_action;
int first_coord_kept;
int second_coord_kept;
float min_side_length;
Point3f status,delta_mouse,old_status,initial_status;
Plane3f plane;
Point3f rubberband_handle ;
vector < Point3f > path;
};
}//namespace