00001 
00002 
00003 
00004 
00005 
00006 
00007 
00008 
00009 
00010 
00011 
00012 
00013 
00014 
00015 
00016 
00017 
00018 
00019 
00020 
00021 
00022 
00023 
00024 #include "PreCompiled.h"
00025 #ifndef _PreComp_
00026 # include <cfloat>
00027 # include "InventorAll.h"
00028 # include <QAction>
00029 # include <QActionGroup>
00030 # include <QApplication>
00031 # include <QByteArray>
00032 # include <QCursor>
00033 # include <QList>
00034 # include <QMenu>
00035 # include <QMetaObject>
00036 # include <QRegExp>
00037 #endif
00038 
00039 #include <Inventor/sensors/SoTimerSensor.h>
00040 
00041 #include <App/Application.h>
00042 #include "NavigationStyle.h"
00043 #include "View3DInventorViewer.h"
00044 #include "Application.h"
00045 #include "MenuManager.h"
00046 #include "MouseSelection.h"
00047 
00048 using namespace Gui;
00049 
00050 namespace Gui {
00051 struct NavigationStyleP {
00052     int animationsteps;
00053     int animationdelta;
00054     SbVec3f focal1, focal2;
00055     SbRotation endRotation;
00056     SoTimerSensor * animsensor;
00057 
00058     NavigationStyleP()
00059     {
00060         this->animationsteps = 0;
00061     }
00062     static void viewAnimationCB(void * data, SoSensor * sensor);
00063 };
00064 }
00065 
00066 class FCSphereSheetProjector : public SbSphereSheetProjector {
00067     typedef SbSphereSheetProjector inherited;
00068 
00069 public:
00070     enum OrbitStyle {
00071         Turntable,
00072         Trackball
00073     };
00074 
00075     FCSphereSheetProjector(const SbSphere & sph, const SbBool orienttoeye = TRUE)
00076         : SbSphereSheetProjector(sph, orienttoeye), orbit(Trackball)
00077     {
00078     }
00079 
00080     void setViewVolume (const SbViewVolume &vol)
00081     {
00082         inherited::setViewVolume(vol);
00083     }
00084 
00085     void setWorkingSpace (const SbMatrix &space)
00086     {
00087         
00088         this->worldToScreen = space.inverse();
00089     }
00090 
00091     SbVec3f project(const SbVec2f &point)
00092     {
00093         return inherited::project(point);
00094     }
00095 
00096     SbRotation getRotation(const SbVec3f &point1, const SbVec3f &point2)
00097     {
00098         SbRotation rot = inherited::getRotation(point1, point2);
00099         if (orbit == Trackball)
00100             return rot;
00101 
00102         
00103         SbVec3f axis;
00104         float angle;
00105         rot.getValue(axis, angle);
00106         SbVec3f dif = point1 - point2;
00107         if (fabs(dif[1]) > fabs(dif[0])) {
00108             SbVec3f xaxis(1,0,0);
00109             if (dif[1] < 0)
00110                 angle = -angle;
00111             rot.setValue(xaxis, angle);
00112         }
00113         else {
00114             SbVec3f zaxis(0,0,1);
00115             this->worldToScreen.multDirMatrix(zaxis, zaxis);
00116             if (dif[0] > 0)
00117                 angle = -angle;
00118             rot.setValue(zaxis, angle);
00119         }
00120 
00121         return rot;
00122     }
00123 
00124     void setOrbitStyle(OrbitStyle style)
00125     {
00126         this->orbit = style;
00127     }
00128 
00129     OrbitStyle getOrbitStyle() const
00130     {
00131         return this->orbit;
00132     }
00133 
00134 private:
00135     SbMatrix worldToScreen;
00136     OrbitStyle orbit;
00137 };
00138 
00139 NavigationStyleEvent::NavigationStyleEvent(const Base::Type& s)
00140   : QEvent(QEvent::User), t(s)
00141 {
00142 }
00143 
00144 NavigationStyleEvent::~NavigationStyleEvent()
00145 {
00146 }
00147 
00148 const Base::Type& NavigationStyleEvent::style() const
00149 { 
00150     return t;
00151 }
00152 
00153 #define PRIVATE(ptr) (ptr->pimpl)
00154 #define PUBLIC(ptr) (ptr->pub)
00155 
00156 TYPESYSTEM_SOURCE_ABSTRACT(Gui::NavigationStyle,Base::BaseClass);
00157 
00158 NavigationStyle::NavigationStyle() : viewer(0), mouseSelection(0)
00159 {
00160     PRIVATE(this) = new NavigationStyleP();
00161     PRIVATE(this)->animsensor = new SoTimerSensor(NavigationStyleP::viewAnimationCB, this);
00162     initialize();
00163 }
00164 
00165 NavigationStyle::~NavigationStyle()
00166 {
00167     finalize();
00168     if (PRIVATE(this)->animsensor->isScheduled())
00169         PRIVATE(this)->animsensor->unschedule();
00170     delete PRIVATE(this)->animsensor;
00171     delete PRIVATE(this);
00172 }
00173 
00174 NavigationStyle& NavigationStyle::operator = (const NavigationStyle& ns)
00175 {
00176     this->panningplane = ns.panningplane;
00177     this->menuenabled = ns.menuenabled;
00178     this->spinanimatingallowed = ns.spinanimatingallowed;
00179     return *this;
00180 }
00181 
00182 void NavigationStyle::setViewer(View3DInventorViewer* view)
00183 {
00184     this->viewer = view;
00185 }
00186 
00187 void NavigationStyle::initialize()
00188 {
00189     this->currentmode = NavigationStyle::IDLE;
00190     this->prevRedrawTime = SbTime::getTimeOfDay();
00191     this->spinanimatingallowed = TRUE;
00192     this->spinsamplecounter = 0;
00193     this->spinincrement = SbRotation::identity();
00194     this->spinRotation.setValue(SbVec3f(0, 0, -1), 0);
00195 
00196     
00197     
00198     
00199     this->spinprojector = new FCSphereSheetProjector(SbSphere(SbVec3f(0, 0, 0), 0.8f));
00200     SbViewVolume volume;
00201     volume.ortho(-1, 1, -1, 1, -1, 1);
00202     this->spinprojector->setViewVolume(volume);
00203 
00204     this->log.size = 16;
00205     this->log.position = new SbVec2s [ 16 ];
00206     this->log.time = new SbTime [ 16 ];
00207     this->log.historysize = 0;
00208 
00209     this->menuenabled = TRUE;
00210     this->button1down = FALSE;
00211     this->button2down = FALSE;
00212     this->button3down = FALSE;
00213     this->ctrldown = FALSE;
00214     this->shiftdown = FALSE;
00215     this->altdown = FALSE;
00216     this->invertZoom = App::GetApplication().GetParameterGroupByPath
00217         ("User parameter:BaseApp/Preferences/View")->GetBool("InvertZoom",false);
00218 }
00219 
00220 void NavigationStyle::finalize()
00221 {
00222     delete this->spinprojector;
00223     delete[] this->log.position;
00224     delete[] this->log.time;
00225 }
00226 
00227 void NavigationStyle::interactiveCountInc(void)
00228 {
00229     viewer->interactiveCountInc();
00230 }
00231 
00232 void NavigationStyle::interactiveCountDec(void)
00233 {
00234     viewer->interactiveCountDec();
00235 }
00236 
00237 int NavigationStyle::getInteractiveCount(void) const
00238 {
00239     return viewer->getInteractiveCount();
00240 }
00241 
00242 void NavigationStyle::setOrbitStyle(NavigationStyle::OrbitStyle style)
00243 {
00244     FCSphereSheetProjector* projector = static_cast<FCSphereSheetProjector*>(this->spinprojector);
00245     projector->setOrbitStyle(FCSphereSheetProjector::OrbitStyle(style));
00246 }
00247 
00248 NavigationStyle::OrbitStyle NavigationStyle::getOrbitStyle() const
00249 {
00250     FCSphereSheetProjector* projector = static_cast<FCSphereSheetProjector*>(this->spinprojector);
00251     return NavigationStyle::OrbitStyle(projector->getOrbitStyle());
00252 }
00253 
00254 SbBool NavigationStyle::isViewing(void) const
00255 {
00256     return viewer->isViewing();
00257 }
00258 
00259 void NavigationStyle::setViewing(SbBool enable)
00260 {
00261     viewer->setViewing(enable);
00262 }
00263 
00264 SbBool NavigationStyle::isSeekMode(void) const
00265 {
00266     return viewer->isSeekMode();
00267 }
00268 
00269 void NavigationStyle::setSeekMode(SbBool enable)
00270 {
00271     viewer->setSeekMode(enable);
00272 }
00273 
00274 SbBool NavigationStyle::seekToPoint(const SbVec2s screenpos)
00275 {
00276     return viewer->seekToPoint(screenpos);
00277 }
00278 
00279 void NavigationStyle::seekToPoint(const SbVec3f& scenepos)
00280 {
00281     viewer->seekToPoint(scenepos);
00282 }
00283 
00284 SbBool NavigationStyle::lookAtPoint(const SbVec2s screenpos)
00285 {
00286     SoCamera* cam = viewer->getCamera();
00287     if (cam == 0) return FALSE;
00288 
00289     SoRayPickAction rpaction(viewer->getViewportRegion());
00290     rpaction.setPoint(screenpos);
00291     rpaction.setRadius(2);
00292     rpaction.apply(viewer->getSceneManager()->getSceneGraph());
00293 
00294     SoPickedPoint * picked = rpaction.getPickedPoint();
00295     if (!picked) {
00296         this->interactiveCountInc();
00297         return FALSE;
00298     }
00299 
00300     SbVec3f hitpoint;
00301     hitpoint = picked->getPoint();
00302     lookAtPoint(hitpoint);
00303     return TRUE;
00304 }
00305 
00306 void NavigationStyle::lookAtPoint(const SbVec3f& pos)
00307 {
00308     SoCamera* cam = viewer->getCamera();
00309     if (cam == 0) return;
00310 
00311     
00312     SbVec3f direction;
00313     cam->orientation.getValue().multVec(SbVec3f(0, 0, -1), direction);
00314     PRIVATE(this)->focal1 = cam->position.getValue() +
00315                             cam->focalDistance.getValue() * direction;
00316     PRIVATE(this)->focal2 = pos;
00317 
00318     
00319     if (this->isAnimating())
00320         this->stopAnimating();
00321 
00322     if (PRIVATE(this)->animsensor->isScheduled()) {
00323         PRIVATE(this)->animsensor->unschedule();
00324         this->interactiveCountDec();
00325     }
00326 
00327     if (isAnimationEnabled()) {
00328         SbRotation cam_rot = cam->orientation.getValue();
00329         
00330         SbVec3f dir1 = direction, dir2;
00331         dir2 = pos - cam->position.getValue();
00332         dir2.normalize();
00333         SbRotation rot(dir1, dir2);
00334         float val = 0.5f*(1.0f + dir1.dot(dir2)); 
00335         int div = (int)(val * 20.0f);
00336         int steps = 20-div; 
00337 
00338         
00339         if (steps > 0) {
00340             PRIVATE(this)->endRotation = cam_rot;
00341             this->spinRotation = cam_rot;
00342             PRIVATE(this)->animationsteps = 5;
00343             PRIVATE(this)->animationdelta = std::max<int>(100/steps, 5);
00344             PRIVATE(this)->animsensor->setBaseTime(SbTime::getTimeOfDay());
00345             PRIVATE(this)->animsensor->schedule();
00346             this->interactiveCountInc();
00347         }
00348         else {
00349             
00350             SbVec3f direction;
00351             cam->orientation.getValue().multVec(SbVec3f(0, 0, -1), direction);
00352             cam->position = pos - cam->focalDistance.getValue() * direction;
00353         }
00354     }
00355     else {
00356         
00357         SbVec3f direction;
00358         cam->orientation.getValue().multVec(SbVec3f(0, 0, -1), direction);
00359         cam->position = pos - cam->focalDistance.getValue() * direction;
00360     }
00361 }
00362 
00363 void NavigationStyle::setCameraOrientation(const SbRotation& rot)
00364 {
00365     SoCamera* cam = viewer->getCamera();
00366     if (cam == 0) return;
00367 
00368     
00369     SbVec3f direction;
00370     cam->orientation.getValue().multVec(SbVec3f(0, 0, -1), direction);
00371     PRIVATE(this)->focal1 = cam->position.getValue() +
00372                             cam->focalDistance.getValue() * direction;
00373     PRIVATE(this)->focal2 = PRIVATE(this)->focal1;
00374     SoGetBoundingBoxAction action(viewer->getViewportRegion());
00375     action.apply(viewer->getSceneGraph());
00376     SbBox3f box = action.getBoundingBox();
00377     if (!box.isEmpty()) {
00378         rot.multVec(SbVec3f(0, 0, -1), direction);
00379         
00380         
00381         
00382         
00383         PRIVATE(this)->focal2 = box.getCenter();
00384     }
00385 
00386     
00387     if (this->isAnimating())
00388         this->stopAnimating();
00389 
00390     if (PRIVATE(this)->animsensor->isScheduled()) {
00391         PRIVATE(this)->animsensor->unschedule();
00392         this->interactiveCountDec();
00393     }
00394 
00395     if (isAnimationEnabled()) {
00396         
00397         SbVec3f dir1, dir2;
00398         SbRotation cam_rot = cam->orientation.getValue();
00399         cam_rot.multVec(SbVec3f(0, 0, -1), dir1);
00400         rot.multVec(SbVec3f(0, 0, -1), dir2);
00401         float val = 0.5f*(1.0f + dir1.dot(dir2)); 
00402         int div = (int)(val * 20.0f);
00403         int steps = 20-div; 
00404 
00405         
00406         if (steps > 0) {
00407             PRIVATE(this)->endRotation = rot; 
00408             this->spinRotation = cam_rot;
00409             PRIVATE(this)->animationsteps = 5;
00410             PRIVATE(this)->animationdelta = std::max<int>(100/steps, 5);
00411             PRIVATE(this)->animsensor->setBaseTime(SbTime::getTimeOfDay());
00412             PRIVATE(this)->animsensor->schedule();
00413             this->interactiveCountInc();
00414         }
00415         else {
00416             
00417             
00418             cam->orientation.setValue(rot);
00419         }
00420     }
00421     else {
00422         
00423         cam->orientation.setValue(rot);
00424         cam->orientation.getValue().multVec(SbVec3f(0, 0, -1), direction);
00425         cam->position = PRIVATE(this)->focal2 - cam->focalDistance.getValue() * direction;
00426     }
00427 }
00428 
00429 void NavigationStyleP::viewAnimationCB(void * data, SoSensor * sensor)
00430 {
00431     NavigationStyle* that = reinterpret_cast<NavigationStyle*>(data);
00432     if (PRIVATE(that)->animationsteps > 0) {
00433         
00434         
00435         
00436         
00437         float step = std::min<float>((float)PRIVATE(that)->animationsteps/100.0f, 1.0f);
00438         SbRotation slerp = SbRotation::slerp(that->spinRotation, PRIVATE(that)->endRotation, step);
00439         SbVec3f focalpoint = (1.0f-step)*PRIVATE(that)->focal1 + step*PRIVATE(that)->focal2;
00440         SoCamera* cam = that->viewer->getCamera();
00441         SbVec3f direction;
00442         cam->orientation.setValue(slerp);
00443         cam->orientation.getValue().multVec(SbVec3f(0, 0, -1), direction);
00444         cam->position = focalpoint - cam->focalDistance.getValue() * direction;
00445 
00446         PRIVATE(that)->animationsteps += PRIVATE(that)->animationdelta;
00447         if (PRIVATE(that)->animationsteps > 100) {
00448             
00449             PRIVATE(that)->animationsteps=0;
00450             PRIVATE(that)->animsensor->unschedule();
00451             that->interactiveCountDec();
00452             
00453             cam->orientation.setValue(PRIVATE(that)->endRotation);
00454             cam->orientation.getValue().multVec(SbVec3f(0, 0, -1), direction);
00455             cam->position = PRIVATE(that)->focal2 - cam->focalDistance.getValue() * direction;
00456         }
00457     }
00458 }
00459 
00460 void NavigationStyle::boxZoom(const SbBox2s& box)
00461 {
00462     SoCamera* cam = viewer->getCamera();
00463     if (!cam) return; 
00464     const SbViewportRegion & vp = viewer->getViewportRegion();
00465     SbViewVolume vv = cam->getViewVolume(vp.getViewportAspectRatio());
00466 
00467     short sizeX,sizeY;
00468     box.getSize(sizeX, sizeY);
00469     SbVec2s size = vp.getViewportSizePixels();
00470 
00471     
00472     
00473     if (sizeX == 0 && sizeY == 0) 
00474         return;
00475 
00476     
00477     short xmin,xmax,ymin,ymax;
00478     box.getBounds(xmin,ymin,xmax,ymax);
00479     const SbVec2f center((float) ((xmin+xmax)/2) / (float) SoQtMax((int)(size[0] - 1), 1),
00480                          (float) (size[1]-(ymin+ymax)/2) / (float) SoQtMax((int)(size[1] - 1), 1));
00481 
00482     SbPlane plane = vv.getPlane(cam->focalDistance.getValue());
00483     panCamera(cam,vp.getViewportAspectRatio(),plane, SbVec2f(0.5,0.5), center);
00484 
00485     
00486     float scaleX = (float)sizeX/(float)size[0];
00487     float scaleY = (float)sizeY/(float)size[1];
00488     float scale = std::max<float>(scaleX, scaleY);
00489     if (cam && cam->getTypeId() == SoOrthographicCamera::getClassTypeId()) {
00490         float height = static_cast<SoOrthographicCamera*>(cam)->height.getValue() * scale;
00491         static_cast<SoOrthographicCamera*>(cam)->height = height;
00492     }
00493     else if (cam && cam->getTypeId() == SoPerspectiveCamera::getClassTypeId()) {
00494         float height = static_cast<SoPerspectiveCamera*>(cam)->heightAngle.getValue() / 2.0f;
00495         height = 2.0f * atan(tan(height) * scale);
00496         static_cast<SoPerspectiveCamera*>(cam)->heightAngle = height;
00497     }
00498 }
00499 
00500 void NavigationStyle::viewAll()
00501 {
00502     
00503     SoGetBoundingBoxAction action(viewer->getViewportRegion());
00504     action.apply(viewer->getSceneGraph());
00505     SbBox3f box = action.getBoundingBox();
00506     if (box.isEmpty()) return;
00507 
00508 #if 0
00509     
00510     float box_width, box_height, box_depth;
00511     box.getSize( box_width, box_height, box_depth );
00512     if (box_width < 5.0f*box_height && box_width < 5.0f*box_depth && 
00513         box_height < 5.0f*box_width && box_height < 5.0f*box_depth && 
00514         box_depth < 5.0f*box_width && box_depth < 5.0f*box_height )
00515         return;
00516 #endif
00517 
00518     SoCamera* cam = viewer->getCamera();
00519     if (!cam) return;
00520 
00521     SbViewVolume  vol = cam->getViewVolume();
00522     if (vol.ulf == vol.llf)
00523         return; 
00524     SbVec2f s = vol.projectBox(box);
00525     SbVec2s size = viewer->getSize();
00526 
00527     SbVec3f pt1, pt2, pt3, tmp;
00528     vol.projectPointToLine( SbVec2f(0.0f,0.0f), pt1, tmp );
00529     vol.projectPointToLine( SbVec2f(s[0],0.0f), pt2, tmp );
00530     vol.projectPointToLine( SbVec2f(0.0f,s[1]), pt3, tmp );
00531 
00532     float cam_width = (pt2-pt1).length();
00533     float cam_height = (pt3-pt1).length();
00534 
00535     
00536     cam_height = 1.08f * std::max<float>((cam_width*(float)size[1])/(float)size[0],cam_height);
00537 
00538     float aspect = cam->aspectRatio.getValue();
00539 
00540     if (cam->getTypeId() == SoPerspectiveCamera::getClassTypeId()) {
00541         
00542         
00543         
00544         
00545         
00546         
00547         
00548         
00549         
00550         
00551         
00552         
00553         
00554         
00555     }
00556     else if (cam->getTypeId() == SoOrthographicCamera::getClassTypeId()) {
00557         SoOrthographicCamera* ocam = (SoOrthographicCamera *)cam;  
00558         if (aspect < 1.0f)
00559             ocam->height = cam_height / aspect;
00560         else
00561             ocam->height = cam_height;
00562     }
00563 }
00564 
00568 void NavigationStyle::reorientCamera(SoCamera * cam, const SbRotation & rot)
00569 {
00570     if (cam == NULL) return;
00571 
00572     
00573     SbVec3f direction;
00574     cam->orientation.getValue().multVec(SbVec3f(0, 0, -1), direction);
00575     SbVec3f focalpoint = cam->position.getValue() +
00576                          cam->focalDistance.getValue() * direction;
00577 
00578     
00579     cam->orientation = rot * cam->orientation.getValue();
00580 
00581     
00582     cam->orientation.getValue().multVec(SbVec3f(0, 0, -1), direction);
00583     cam->position = focalpoint - cam->focalDistance.getValue() * direction;
00584 }
00585 
00586 void NavigationStyle::panCamera(SoCamera * cam, float aspectratio, const SbPlane & panplane,
00587                                 const SbVec2f & currpos, const SbVec2f & prevpos)
00588 {
00589     if (cam == NULL) return; 
00590     if (currpos == prevpos) return; 
00591 
00592 
00593     
00594     SbViewVolume vv = cam->getViewVolume(aspectratio);
00595     SbLine line;
00596     vv.projectPointToLine(currpos, line);
00597     SbVec3f current_planept;
00598     panplane.intersect(line, current_planept);
00599     vv.projectPointToLine(prevpos, line);
00600     SbVec3f old_planept;
00601     panplane.intersect(line, old_planept);
00602 
00603     
00604     
00605     cam->position = cam->position.getValue() - (current_planept - old_planept);
00606 }
00607 
00608 void NavigationStyle::pan(SoCamera* camera)
00609 {
00610     
00611     
00612     
00613     if (camera == NULL) { 
00614         this->panningplane = SbPlane(SbVec3f(0, 0, 1), 0);
00615     }
00616     else {
00617         const SbViewportRegion & vp = viewer->getViewportRegion();
00618         SbViewVolume vv = camera->getViewVolume(vp.getViewportAspectRatio());
00619         this->panningplane = vv.getPlane(camera->focalDistance.getValue());
00620     }
00621 }
00622 
00623 void NavigationStyle::panToCenter(const SbPlane & pplane, const SbVec2f & currpos)
00624 {
00625     const SbViewportRegion & vp = viewer->getViewportRegion();
00626     float ratio = vp.getViewportAspectRatio();
00627     panCamera(viewer->getCamera(), ratio, pplane, SbVec2f(0.5,0.5), currpos);
00628 }
00629 
00634 void NavigationStyle::zoom(SoCamera * cam, float diffvalue)
00635 {
00636     if (cam == NULL) return; 
00637     SoType t = cam->getTypeId();
00638     SbName tname = t.getName();
00639 
00640     
00641     float multiplicator = float(exp(diffvalue));
00642 
00643     if (t.isDerivedFrom(SoOrthographicCamera::getClassTypeId())) {
00644 
00645         
00646         
00647         
00648         
00649         SoOrthographicCamera * oc = (SoOrthographicCamera *)cam;
00650         oc->height = oc->height.getValue() * multiplicator;
00651 
00652     }
00653     else {
00654         
00655         
00656         
00657         if (!t.isDerivedFrom(SoPerspectiveCamera::getClassTypeId()) &&
00658             tname != "FrustumCamera") {
00659  
00660 
00661 
00662 
00663 
00664 
00665 
00666         }
00667 
00668         const float oldfocaldist = cam->focalDistance.getValue();
00669         const float newfocaldist = oldfocaldist * multiplicator;
00670 
00671         SbVec3f direction;
00672         cam->orientation.getValue().multVec(SbVec3f(0, 0, -1), direction);
00673 
00674         const SbVec3f oldpos = cam->position.getValue();
00675         const SbVec3f newpos = oldpos + (newfocaldist - oldfocaldist) * -direction;
00676 
00677         
00678         
00679         
00680         
00681         
00682         
00683         
00684         
00685         
00686         
00687         
00688         
00689         
00690         
00691         const float distorigo = newpos.length();
00692         
00693         
00694         if (distorigo > float(sqrt(FLT_MAX))) {
00695         }
00696         else {
00697             cam->position = newpos;
00698             cam->focalDistance = newfocaldist;
00699         }
00700     }
00701 }
00702 
00703 
00704 
00705 void NavigationStyle::zoomByCursor(const SbVec2f & thispos, const SbVec2f & prevpos)
00706 {
00707     
00708     
00709     zoom(viewer->getCamera(), (thispos[1] - prevpos[1]) * 10.0f);
00710 }
00711 
00715 void NavigationStyle::spin(const SbVec2f & pointerpos)
00716 {
00717     if (this->log.historysize < 2) return;
00718     assert(this->spinprojector != NULL);
00719 
00720     const SbViewportRegion & vp = viewer->getViewportRegion();
00721     SbVec2s glsize(vp.getViewportSizePixels());
00722     SbVec2f lastpos;
00723     lastpos[0] = float(this->log.position[1][0]) / float(SoQtMax((int)(glsize[0]-1), 1));
00724     lastpos[1] = float(this->log.position[1][1]) / float(SoQtMax((int)(glsize[1]-1), 1));
00725 
00726     
00727     SbMatrix mat;
00728     viewer->getCamera()->orientation.getValue().getValue(mat);
00729     this->spinprojector->setWorkingSpace(mat);
00730 
00731     this->spinprojector->project(lastpos);
00732     SbRotation r;
00733     this->spinprojector->projectAndGetRotation(pointerpos, r);
00734     r.invert();
00735     this->reorientCamera(viewer->getCamera(), r);
00736 
00737     
00738     
00739 
00740     SbVec3f dummy_axis, newaxis;
00741     float acc_angle, newangle;
00742     this->spinincrement.getValue(dummy_axis, acc_angle);
00743     acc_angle *= this->spinsamplecounter; 
00744     r.getValue(newaxis, newangle);
00745     acc_angle += newangle;
00746 
00747     this->spinsamplecounter++;
00748     acc_angle /= this->spinsamplecounter;
00749     
00750     this->spinincrement.setValue(newaxis, acc_angle);
00751 
00752     
00753     
00754     
00755     if (this->spinsamplecounter > 3) this->spinsamplecounter = 3;
00756 }
00757 
00758 SbBool NavigationStyle::doSpin()
00759 {
00760     if (this->log.historysize >= 3) {
00761         SbTime stoptime = (SbTime::getTimeOfDay() - this->log.time[0]);
00762         if (this->spinanimatingallowed && stoptime.getValue() < 0.100) {
00763             const SbViewportRegion & vp = viewer->getViewportRegion();
00764             const SbVec2s glsize(vp.getViewportSizePixels());
00765             SbVec3f from = this->spinprojector->project(SbVec2f(float(this->log.position[2][0]) / float(SoQtMax(glsize[0]-1, 1)),
00766                                                                 float(this->log.position[2][1]) / float(SoQtMax(glsize[1]-1, 1))));
00767             SbVec3f to = this->spinprojector->project(this->lastmouseposition);
00768             SbRotation rot = this->spinprojector->getRotation(from, to);
00769 
00770             SbTime delta = (this->log.time[0] - this->log.time[2]);
00771             double deltatime = delta.getValue();
00772             rot.invert();
00773             rot.scaleAngle(float(0.200 / deltatime));
00774 
00775             SbVec3f axis;
00776             float radians;
00777             rot.getValue(axis, radians);
00778             if ((radians > 0.01f) && (deltatime < 0.300)) {
00779                 this->spinRotation = rot;
00780                 return TRUE;
00781             }
00782         }
00783     }
00784 
00785     return FALSE;
00786 }
00787 
00788 void NavigationStyle::updateAnimation()
00789 {
00790     SbTime now = SbTime::getTimeOfDay();
00791     double secs = now.getValue() -  prevRedrawTime.getValue();
00792     this->prevRedrawTime = now;
00793 
00794     if (this->isAnimating()) {
00795         
00796         SbRotation deltaRotation = this->spinRotation;
00797         deltaRotation.scaleAngle(secs * 5.0);
00798         this->reorientCamera(viewer->getCamera(), deltaRotation);
00799     }
00800 }
00801 
00802 void NavigationStyle::redraw()
00803 {
00804     if (mouseSelection)
00805         mouseSelection->redraw();
00806 }
00807 
00808 SbBool NavigationStyle::handleEventInForeground(const SoEvent* const e)
00809 {
00810     SoHandleEventAction action(viewer->getViewportRegion());
00811     action.setEvent(e);
00812     action.apply(viewer->foregroundroot);
00813     return action.isHandled();
00814 }
00815 
00823 void
00824 NavigationStyle::setAnimationEnabled(const SbBool enable)
00825 {
00826     this->spinanimatingallowed = enable;
00827     if (!enable && this->isAnimating()) { this->stopAnimating(); }
00828 }
00829 
00835 SbBool
00836 NavigationStyle::isAnimationEnabled(void) const
00837 {
00838     return this->spinanimatingallowed;
00839 }
00840 
00845 SbBool NavigationStyle::isAnimating(void) const
00846 {
00847     return this->currentmode == NavigationStyle::SPINNING;
00848 }
00849 
00854 void NavigationStyle::startAnimating(const SbVec3f& axis, float velocity)
00855 {
00856     if (!isAnimationEnabled()) return;
00857 
00858     this->prevRedrawTime = SbTime::getTimeOfDay();
00859     this->spinincrement = SbRotation::identity();
00860     SbRotation rot;
00861     rot.setValue(axis, velocity);
00862 
00863     this->setViewing(true);
00864     this->setViewingMode(NavigationStyle::SPINNING);
00865     this->spinRotation = rot;
00866 }
00867 
00868 void NavigationStyle::stopAnimating(void)
00869 {
00870     if (this->currentmode != NavigationStyle::SPINNING) {
00871         return;
00872     }
00873     this->setViewingMode(this->isViewing() ? 
00874         NavigationStyle::IDLE : NavigationStyle::INTERACT);
00875 }
00876 
00877 void NavigationStyle::setZoomInverted(SbBool on)
00878 {
00879     this->invertZoom = on;
00880 }
00881 
00882 SbBool NavigationStyle::isZoomInverted() const
00883 {
00884     return this->invertZoom;
00885 }
00886 
00887 void NavigationStyle::startSelection(AbstractMouseSelection* mouse)
00888 {
00889     if (!mouse)
00890         return;
00891   
00892     mouseSelection = mouse;
00893     mouseSelection->grabMouseModel(viewer);
00894 }
00895 
00896 void NavigationStyle::startSelection(NavigationStyle::SelectionMode mode)
00897 {
00898     if (mouseSelection)
00899         return;
00900     if (isSelecting())
00901         stopSelection();
00902   
00903     switch (mode)
00904     {
00905     case Lasso:
00906         mouseSelection = new PolyPickerSelection();
00907         break;
00908     case Rectangle:
00909         mouseSelection = new RectangleSelection();
00910         break;
00911     case BoxZoom:
00912         mouseSelection = new BoxZoomSelection();
00913         break;
00914     case Clip:
00915         mouseSelection = new PolyClipSelection();
00916         break;
00917     default:
00918         break;
00919     }
00920 
00921     if (mouseSelection)
00922         mouseSelection->grabMouseModel(viewer);
00923 }
00924 
00925 void NavigationStyle::stopSelection()
00926 {
00927     pcPolygon.clear();
00928     delete mouseSelection; 
00929     mouseSelection = 0;
00930 }
00931 
00932 SbBool NavigationStyle::isSelecting() const
00933 {
00934     return (mouseSelection ? TRUE : FALSE);
00935 }
00936 
00937 const std::vector<SbVec2s>& NavigationStyle::getPolygon(SbBool* clip_inner) const
00938 {
00939     if (clip_inner)
00940         *clip_inner = this->clipInner;
00941     return pcPolygon;
00942 }
00943 
00944 
00945 
00946 void NavigationStyle::addToLog(const SbVec2s pos, const SbTime time)
00947 {
00948     
00949     
00950     assert (this->log.size > 2 && "mouse log too small!");
00951 
00952     if (this->log.historysize > 0 && pos == this->log.position[0]) {
00953 #if SOQt_DEBUG && 0 // debug
00954         
00955         SoDebugError::postInfo("NavigationStyle::addToLog", "got position already!");
00956 #endif // debug
00957         return;
00958     }
00959 
00960     int lastidx = this->log.historysize;
00961     
00962     if (lastidx == this->log.size) { lastidx--; }
00963 
00964     assert(lastidx < this->log.size);
00965     for (int i = lastidx; i > 0; i--) {
00966         this->log.position[i] = this->log.position[i-1];
00967         this->log.time[i] = this->log.time[i-1];
00968     }
00969 
00970     this->log.position[0] = pos;
00971     this->log.time[0] = time;
00972     if (this->log.historysize < this->log.size)
00973         this->log.historysize += 1;
00974 }
00975 
00976 
00977 
00978 void NavigationStyle::clearLog(void)
00979 {
00980     this->log.historysize = 0;
00981 }
00982 
00983 
00984 
00985 void NavigationStyle::setViewingMode(const ViewerMode newmode)
00986 {
00987     const ViewerMode oldmode = this->currentmode;
00988     if (newmode == oldmode) { return; }
00989 
00990     switch (newmode) {
00991     case DRAGGING:
00992         
00993         
00994         this->spinprojector->project(this->lastmouseposition);
00995         this->interactiveCountInc();
00996         this->clearLog();
00997         break;
00998 
00999     case SPINNING:
01000         this->interactiveCountInc();
01001         viewer->scheduleRedraw();
01002         break;
01003 
01004     case PANNING:
01005         pan(viewer->getCamera());
01006         this->interactiveCountInc();
01007         break;
01008 
01009     case ZOOMING:
01010         this->interactiveCountInc();
01011         break;
01012 
01013     case BOXZOOM:
01014         this->interactiveCountInc();
01015         break;
01016 
01017     default: 
01018         break;
01019     }
01020 
01021     switch (oldmode) {
01022     case SPINNING:
01023     case DRAGGING:
01024     case PANNING:
01025     case ZOOMING:
01026     case BOXZOOM:
01027         this->interactiveCountDec();
01028         break;
01029 
01030     default:
01031         break;
01032     }
01033 
01034     viewer->setCursorRepresentation(newmode);
01035     this->currentmode = newmode;
01036 }
01037 
01038 int NavigationStyle::getViewingMode() const
01039 {
01040     return (int)this->currentmode;
01041 }
01042 
01043 SbBool NavigationStyle::processEvent(const SoEvent * const ev)
01044 {
01045     
01046     
01047     if (mouseSelection) {
01048         int hd=mouseSelection->handleEvent(ev,viewer->getViewportRegion());
01049         if (hd==AbstractMouseSelection::Continue||
01050             hd==AbstractMouseSelection::Restart) {
01051             return TRUE;
01052         }
01053         else if (hd==AbstractMouseSelection::Finish) {
01054             pcPolygon = mouseSelection->getPositions();
01055             clipInner = mouseSelection->isInner();
01056             delete mouseSelection; mouseSelection = 0;
01057             return NavigationStyle::processSoEvent(ev);
01058         }
01059         else if (hd==AbstractMouseSelection::Cancel) {
01060             pcPolygon.clear();
01061             delete mouseSelection; mouseSelection = 0;
01062             return NavigationStyle::processSoEvent(ev);
01063         }
01064     }
01065 
01066     const ViewerMode curmode = this->currentmode;
01067 
01068     SbBool processed = FALSE;
01069     processed = this->processSoEvent(ev);
01070 
01071     
01072     if (curmode == NavigationStyle::SELECTION && !processed) {
01073         if (ev->getTypeId().isDerivedFrom(SoMouseButtonEvent::getClassTypeId())) {
01074             SoMouseButtonEvent * const e = (SoMouseButtonEvent *) ev;
01075             if (SoMouseButtonEvent::isButtonReleaseEvent(e,SoMouseButtonEvent::BUTTON1)) {
01076                 Gui::Selection().clearSelection();
01077             }
01078         }
01079     }
01080 
01081     return processed;
01082 }
01083 
01084 SbBool NavigationStyle::processSoEvent(const SoEvent * const ev)
01085 {
01086     return viewer->processSoEventBase(ev);
01087 }
01088 
01089 void NavigationStyle::setPopupMenuEnabled(const SbBool on)
01090 {
01091     this->menuenabled = on;
01092 }
01093 
01094 SbBool NavigationStyle::isPopupMenuEnabled(void) const
01095 {
01096     return this->menuenabled;
01097 }
01098 
01099 void NavigationStyle::openPopupMenu(const SbVec2s& position)
01100 {
01101     
01102     MenuItem* view = new MenuItem;
01103     Gui::Application::Instance->setupContextMenu("View", view);
01104 
01105     QMenu contextMenu(viewer->getGLWidget());
01106     QMenu subMenu;
01107     QActionGroup subMenuGroup(&subMenu);
01108     subMenuGroup.setExclusive(true);
01109     subMenu.setTitle(QObject::tr("Navigation styles"));
01110 
01111     MenuManager::getInstance()->setupContextMenu(view, contextMenu);
01112     contextMenu.addMenu(&subMenu);
01113 
01114     
01115     QRegExp rx(QString::fromAscii("^\\w+::(\\w+)Navigation\\w+$"));
01116     std::vector<Base::Type> types;
01117     Base::Type::getAllDerivedFrom(UserNavigationStyle::getClassTypeId(), types);
01118     for (std::vector<Base::Type>::iterator it = types.begin(); it != types.end(); ++it) {
01119         if (*it != UserNavigationStyle::getClassTypeId()) {
01120             QString data = QString::fromAscii(it->getName());
01121             QString name = data.mid(data.indexOf(QLatin1String("::"))+2);
01122             if (rx.indexIn(data) > -1) {
01123                 name = QObject::tr("%1 navigation").arg(rx.cap(1));
01124                 QAction* item = subMenuGroup.addAction(name);
01125                 item->setData(QByteArray(it->getName()));
01126                 item->setCheckable(true);
01127                 if (*it == this->getTypeId())
01128                     item->setChecked(true);
01129                 subMenu.addAction(item);
01130             }
01131         }
01132     }
01133 
01134     delete view;
01135     QAction* used = contextMenu.exec(QCursor::pos());
01136     if (used && subMenuGroup.actions().indexOf(used) >= 0 && used->isChecked()) {
01137         QByteArray type = used->data().toByteArray();
01138         QWidget* widget = viewer->getWidget();
01139         while (widget && !widget->inherits("Gui::View3DInventor"))
01140             widget = widget->parentWidget();
01141         if (widget) {
01142             
01143             Base::Type style = Base::Type::fromName((const char*)type);
01144             if (style != this->getTypeId()) {
01145                 QEvent* event = new NavigationStyleEvent(style);
01146                 QApplication::postEvent(widget, event);
01147             }
01148         }
01149     }
01150 }
01151 
01152 
01153 
01154 TYPESYSTEM_SOURCE_ABSTRACT(Gui::UserNavigationStyle,Gui::NavigationStyle);