750 lines
21 KiB
C++
750 lines
21 KiB
C++
/****************************************************************************
|
|
* VCGLib o o *
|
|
* Visual and Computer Graphics Library o o *
|
|
* _ O _ *
|
|
* Copyright(C) 2004 \/)\/ *
|
|
* 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.22 2007/05/28 08:10:47 fiorin
|
|
Removed type cast warnings
|
|
|
|
Revision 1.21 2007/05/16 08:44:05 ganovelli
|
|
added inclusion of glew.h
|
|
|
|
Revision 1.20 2007/05/15 14:58:57 benedetti
|
|
Main restructuring. added many new modes
|
|
|
|
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
|
|
|
|
Revision 1.16 2006/02/13 13:10:27 cignoni
|
|
Added Zmode for moving objects along the perpendicular to the viewplane
|
|
|
|
Revision 1.15 2006/01/12 15:40:05 cignoni
|
|
Corrected small bugs on rotating after scaling+translating
|
|
changed void PlaneMode::Apply and void SphereMode::Apply
|
|
|
|
Revision 1.14 2005/07/15 16:39:30 callieri
|
|
in SphereMode::Hit added a check on the sphere intersection, if no intersection, calculating distance could generate a NAN exception
|
|
|
|
Revision 1.13 2005/06/29 15:22:26 callieri
|
|
changed the name of some intersection functions to avoid ambiguity
|
|
|
|
Revision 1.12 2005/02/11 11:44:07 tommyfranken
|
|
Trackball translation corrected
|
|
|
|
Revision 1.11 2005/02/02 16:46:41 pietroni
|
|
some warning corrected
|
|
|
|
Revision 1.10 2005/01/14 15:44:03 ganovelli
|
|
PlaneMode completed
|
|
|
|
Revision 1.9 2004/09/09 22:59:21 cignoni
|
|
Removed many small warnings
|
|
|
|
Revision 1.8 2004/07/18 06:54:08 cignoni
|
|
Added Scaling
|
|
|
|
Revision 1.7 2004/07/11 22:06:56 cignoni
|
|
Added scaling by wheel
|
|
|
|
Revision 1.6 2004/06/09 14:01:13 cignoni
|
|
Heavily restructured. To be completed only rotation works...
|
|
|
|
Revision 1.5 2004/05/14 03:15:09 ponchio
|
|
Redesigned partial version.
|
|
|
|
Revision 1.4 2004/05/07 12:46:08 cignoni
|
|
Restructured and adapted in a better way to opengl
|
|
|
|
Revision 1.3 2004/04/07 10:54:11 cignoni
|
|
Commented out unused parameter names and other minor warning related issues
|
|
|
|
Revision 1.2 2004/03/25 14:55:25 ponchio
|
|
Adding copyright.
|
|
|
|
|
|
****************************************************************************/
|
|
|
|
#include <gl/glew.h>
|
|
|
|
#include <wrap/gui/trackmode.h>
|
|
#include <wrap/gui/trackball.h>
|
|
#include <wrap/gui/trackutils.h>
|
|
|
|
|
|
using namespace std;
|
|
using namespace vcg;
|
|
using namespace vcg::trackutils;
|
|
|
|
// 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
|
|
/*
|
|
void TrackMode::Apply(Trackball *trackball, float WheelNotch) {
|
|
}
|
|
void ScaleMode::Apply(Trackball *tb, Point3f new_point) {
|
|
}
|
|
Plane3f TrackMode::GetViewPlane(const View<float> &camera, const Point3f ¢er) {
|
|
}
|
|
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)
|
|
{
|
|
}
|
|
Point3f SphereMode::Hit(Trackball *tb, const Point3f &p) {
|
|
}
|
|
void PlaneMode::Apply(Trackball *tb, Point3f new_point) {
|
|
|
|
}
|
|
void ZMode::Apply(Trackball *tb, Point3f new_point) {
|
|
}
|
|
*/
|
|
|
|
// Track mode implementation, dummy.
|
|
void TrackMode::Apply (Trackball * , float ){}
|
|
|
|
void TrackMode::Apply (Trackball * , Point3f ){}
|
|
|
|
void TrackMode::Draw(Trackball * ){}
|
|
|
|
void TrackMode::SetAction (){}
|
|
|
|
void TrackMode::Reset (){}
|
|
|
|
bool TrackMode::isSticky() {
|
|
return false;
|
|
}
|
|
|
|
void TrackMode::Undo(){}
|
|
|
|
// 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.283185307179586232f;
|
|
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.0f * 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 = int(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 = int(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 = int(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)
|
|
{
|
|
undo_current_state=current_state;
|
|
undo_old_hitpoint=old_hitpoint;
|
|
|
|
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)
|
|
{
|
|
undo_current_state=current_state;
|
|
undo_old_hitpoint=old_hitpoint;
|
|
|
|
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);
|
|
}
|
|
|
|
bool PathMode::isSticky() {
|
|
return true;
|
|
}
|
|
|
|
void PathMode::Undo(){
|
|
current_state=undo_current_state;
|
|
old_hitpoint=undo_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 = int(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.005f;
|
|
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)
|
|
{
|
|
undo_begin_action=begin_action;
|
|
undo_status=status;
|
|
undo_delta_mouse=delta_mouse;
|
|
undo_old_status=old_status;
|
|
undo_rubberband_handle=rubberband_handle;
|
|
undo_path_index=path.size();
|
|
|
|
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 = int(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=int(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=int(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;
|
|
}
|
|
|
|
bool AreaMode::isSticky() {
|
|
return true;
|
|
}
|
|
|
|
void AreaMode::Undo(){
|
|
begin_action=undo_begin_action;
|
|
status=undo_status;
|
|
delta_mouse=undo_delta_mouse;
|
|
old_status=undo_old_status;
|
|
rubberband_handle=undo_rubberband_handle;
|
|
for(unsigned int i=path.size() - 1; i > undo_path_index; --i)
|
|
path.pop_back();
|
|
}
|
|
|
|
void AreaMode::Draw(Trackball * tb)
|
|
{
|
|
DrawSphereIcon(tb,true );
|
|
DrawUglyAreaMode(tb,points,status,old_status,plane,path,rubberband_handle);
|
|
}
|