New navigation style: GestureNavigationStyle

This commit is contained in:
DeepSOIC 2015-03-28 01:06:20 +03:00 committed by wmayer
parent 7085e1ec0c
commit 4f5747ebef
5 changed files with 739 additions and 6 deletions

View File

@ -657,6 +657,7 @@ SET(View3D_CPP_SRCS
CADNavigationStyle.cpp
BlenderNavigationStyle.cpp
TouchpadNavigationStyle.cpp
GestureNavigationStyle.cpp
SplitView3DInventor.cpp
View.cpp
View3DInventor.cpp

View File

@ -0,0 +1,592 @@
/***************************************************************************
* Copyright (c) Victor Titov (DeepSOIC) *
* (vv.titov@gmail.com) 2015 *
* *
* This file is part of the FreeCAD CAx development system. *
* *
* This library is free software; you can redistribute it and/or *
* modify it under the terms of the GNU Library General Public *
* License as published by the Free Software Foundation; either *
* version 2 of the License, or (at your option) any later version. *
* *
* This library 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 Library General Public License for more details. *
* *
* You should have received a copy of the GNU Library General Public *
* License along with this library; see the file COPYING.LIB. If not, *
* write to the Free Software Foundation, Inc., 59 Temple Place, *
* Suite 330, Boston, MA 02111-1307, USA *
* *
***************************************************************************/
/*
*A few notes on this style. (by DeepSOIC)
*
* In this style, LMB serves dual purpose. It is selecting objects, as well as
* spinning the view. The trick that enables it is as follows: The mousedown
* event is consumed an saved, but otherwise remains unprocessed. If a drag is
* detected while the button is down, the event is finally consumed (the saved
* one is discarded), and spinning starts. If there is no drag detected before
* the button is released, the saved mousedown is propagated to inherited,
* followed by the mouseup. The same trick is used for RMB, so up to two
* mousedown can be postponed.
*
* This navigation style does not exactly follow the structure of other
* navigation styles, it does not fill many of the global variables defined in
* NavigationStyle.
*
* This mode does not support locking cursor position on screen when
* navigating, since with absolute pointing devices like pen and touch it makes
* no sense (this style was specifically crafted for such devices).
*
* In this style, setViewing is not used (because I could not figure out how to
* use it properly, and it seems to just work without it).
*
* This style wasn't tested with space during development (I don't have one).
*/
#include "PreCompiled.h"
#ifndef _PreComp_
# include <cfloat>
# include "InventorAll.h"
# include <QAction>
# include <QActionGroup>
# include <QApplication>
# include <QByteArray>
# include <QCursor>
# include <QList>
# include <QMenu>
# include <QMetaObject>
# include <QRegExp>
#endif
#include <Inventor/sensors/SoTimerSensor.h>
#include <App/Application.h>
#include "NavigationStyle.h"
#include "View3DInventorViewer.h"
#include "Application.h"
#include "MenuManager.h"
#include "MouseSelection.h"
#include "SoTouchEvents.h"
using namespace Gui;
// ----------------------------------------------------------------------------------
/* TRANSLATOR Gui::GestureNavigationStyle */
TYPESYSTEM_SOURCE(Gui::GestureNavigationStyle, Gui::UserNavigationStyle);
GestureNavigationStyle::GestureNavigationStyle()
{
mouseMoveThreshold = QApplication::startDragDistance();
mouseMoveThresholdBroken = false;
mousedownConsumedCount = 0;
thisClickIsComplex = false;
inGesture = false;
}
GestureNavigationStyle::~GestureNavigationStyle()
{
}
const char* GestureNavigationStyle::mouseButtons(ViewerMode mode)
{
switch (mode) {
case NavigationStyle::SELECTION:
return QT_TR_NOOP("Tap. Or click left mouse button.");
case NavigationStyle::PANNING:
return QT_TR_NOOP("Drag screen with two fingers. Or press right mouse button.");
case NavigationStyle::DRAGGING:
return QT_TR_NOOP("Drag the screen with one finger. Or press left mouse button. In Sketcher and other edit modes, hold Alt in addition.");
case NavigationStyle::ZOOMING:
return QT_TR_NOOP("Pinch (put two fingers on the screen and drag them apart/to each other). Or scroll middle mouse button. Or PgUp/PgDown on keyboard.");
default:
return "No description";
}
}
/*!
* \brief GestureNavigationStyle::testMoveThreshold tests if the mouse has moved far enough to constder it a drag.
* \param currentPos current position of mouse cursor, in local pixel coordinates.
* \return true if the mouse was moved far enough. False if it's within the boundary. Ignores GestureNavigationStyle::mouseMoveThresholdBroken flag.
*/
bool GestureNavigationStyle::testMoveThreshold(const SbVec2s currentPos) const {
SbVec2s movedBy = currentPos - this->mousedownPos;
return SbVec2f(movedBy).length() >= this->mouseMoveThreshold;
}
SbBool GestureNavigationStyle::processSoEvent(const SoEvent * const ev)
{
// Events when in "ready-to-seek" mode are ignored, except those
// which influence the seek mode itself -- these are handled further
// up the inheritance hierarchy.
if (this->isSeekMode()) { return inherited::processSoEvent(ev); }
// Switch off viewing mode (Bug #0000911)
if (!this->isSeekMode()&& !this->isAnimating() && this->isViewing() )
this->setViewing(false); // by default disable viewing mode to render the scene
//setViewing() is never used in this style, so the previous if is very unlikely to be hit.
const SoType type(ev->getTypeId());
//define some shortcuts...
bool evIsButton = type.isDerivedFrom(SoMouseButtonEvent::getClassTypeId());
bool evIsKeyboard = type.isDerivedFrom(SoKeyboardEvent::getClassTypeId());
bool evIsLoc2 = type.isDerivedFrom(SoLocation2Event::getClassTypeId());//mouse movement
bool evIsLoc3 = type.isDerivedFrom(SoMotion3Event::getClassTypeId());//spaceball/joystick movement
bool evIsGesture = type.isDerivedFrom(SoGestureEvent::getClassTypeId());//touchscreen gesture
const SbVec2f prevnormalized = this->lastmouseposition;
const SbVec2s pos(ev->getPosition());//not valid for gestures
const SbVec2f posn = this->normalizePixelPos(pos);
//pos: local coordinates of event, in pixels
//posn: normalized local coordinates of event ((0,0) = lower left corner, (1,1) = upper right corner)
float ratio = viewer->getSoRenderManager()->getViewportRegion().getViewportAspectRatio();
if (evIsButton || evIsLoc2){
this->lastmouseposition = posn;
}
const ViewerMode curmode = this->currentmode;
//ViewerMode newmode = curmode;
//make a unified mouse+modifiers state value (combo)
enum {
BUTTON1DOWN = 1 << 0,
BUTTON2DOWN = 1 << 1,
BUTTON3DOWN = 1 << 2,
CTRLDOWN = 1 << 3,
SHIFTDOWN = 1 << 4,
ALTDOWN = 1 << 5,
MASKBUTTONS = BUTTON1DOWN | BUTTON2DOWN | BUTTON3DOWN,
MASKMODIFIERS = CTRLDOWN | SHIFTDOWN | ALTDOWN
};
unsigned int comboBefore = //before = state before this event
(this->button1down ? BUTTON1DOWN : 0) |
(this->button2down ? BUTTON2DOWN : 0) |
(this->button3down ? BUTTON3DOWN : 0) |
(this->ctrldown ? CTRLDOWN : 0) |
(this->shiftdown ? SHIFTDOWN : 0) |
(this->altdown ? ALTDOWN : 0);
//test for complex clicks
int cntMBBefore = (comboBefore & BUTTON1DOWN ? 1 : 0 ) //cntMBBefore = how many buttons were down when this event arrived?
+(comboBefore & BUTTON2DOWN ? 1 : 0 )
+(comboBefore & BUTTON3DOWN ? 1 : 0 );
if (cntMBBefore>=2) this->thisClickIsComplex = true;
if (cntMBBefore==0) {//a good chance to reset some click-related stuff
this->thisClickIsComplex = false;
this->mousedownConsumedCount = 0;//shouldn't be necessary, just a fail-safe.
}
// Mismatches in state of the modifier keys happens if the user
// presses or releases them outside the viewer window.
this->ctrldown = ev->wasCtrlDown();
this->shiftdown = ev->wasShiftDown();
this->altdown = ev->wasAltDown();
//before this block, mouse button states in NavigationStyle::buttonXdown reflected those before current event arrived.
//track mouse button states
if (evIsButton) {
const SoMouseButtonEvent * const event = (const SoMouseButtonEvent *) ev;
const int button = event->getButton();
const SbBool press //the button was pressed (if false -> released)
= event->getState() == SoButtonEvent::DOWN ? TRUE : FALSE;
switch (button) {
case SoMouseButtonEvent::BUTTON1:
this->button1down = press;
break;
case SoMouseButtonEvent::BUTTON2:
this->button2down = press;
break;
case SoMouseButtonEvent::BUTTON3:
this->button3down = press;
break;
//whatever else, we don't track
}
}
//after this block, the new states of the buttons are already in.
unsigned int comboAfter = //after = state after this event (current, essentially)
(this->button1down ? BUTTON1DOWN : 0) |
(this->button2down ? BUTTON2DOWN : 0) |
(this->button3down ? BUTTON3DOWN : 0) |
(this->ctrldown ? CTRLDOWN : 0) |
(this->shiftdown ? SHIFTDOWN : 0) |
(this->altdown ? ALTDOWN : 0);
//test for complex clicks (again)
int cntMBAfter = (comboAfter & BUTTON1DOWN ? 1 : 0 ) //cntMBAfter = how many buttons were down when this event arrived?
+(comboAfter & BUTTON2DOWN ? 1 : 0 )
+(comboAfter & BUTTON3DOWN ? 1 : 0 );
if (cntMBAfter>=2) this->thisClickIsComplex = true;
//if (cntMBAfter==0) this->thisClickIsComplex = false;//don't reset the flag now, we need to know that this mouseUp was an end of a click that was complex. The flag will reset by the before-check in the next event.
//test for move detection
if (evIsLoc2 || evIsButton){
this->mouseMoveThresholdBroken |= this->testMoveThreshold(pos);
}
//track gestures
if (evIsGesture) {
const SoGestureEvent* gesture = static_cast<const SoGestureEvent*>(ev);
switch(gesture->state) {
case SoGestureEvent::SbGSStart:
//assert(!inGesture);//start of another gesture before the first finished? Happens all the time for Pan gesture... No idea why! --DeepSOIC
inGesture = true;
break;
case SoGestureEvent::SbGSUpdate:
assert(inGesture);//gesture update without start?
inGesture = true;
break;
case SoGestureEvent::SbGSEnd:
assert(inGesture);//gesture ended without starting?
inGesture = false;
break;
case SoGestureEvent::SbGsCanceled:
assert(inGesture);//gesture canceled without starting?
inGesture=false;
break;
default:
assert(0);//shouldn't happen
inGesture = false;
}
}
if (evIsButton) {
if(inGesture){
inGesture = false;//reset the flag when mouse clicks are received, to ensure enabling mouse navigation back.
setViewingMode(NavigationStyle::SELECTION);//exit navigation asap, to proceed with regular processing of the click
}
}
bool suppressLMBDrag = false;
if(viewer->isEditing()){
//in edit mode, disable lmb dragging (spinning). Holding Alt enables it.
suppressLMBDrag = !(comboAfter & ALTDOWN);
}
//----------all this were preparations. Now comes the event handling! ----------
SbBool processed = FALSE;//a return value for the BlahblahblahNavigationStyle::processSoEvent
bool propagated = false;//an internal flag indicating that the event has been already passed to inherited, to suppress the automatic doing of this at the end.
//goto finalize = return processed. Might be important to do something before done (none now).
// give the nodes in the foreground root the chance to handle events (e.g color bar)
if (!viewer->isEditing()) {
processed = handleEventInForeground(ev);
}
if (processed)
goto finalize;
// Mode-independent keyboard handling
if (evIsKeyboard) {
const SoKeyboardEvent * const event = (const SoKeyboardEvent *) ev;
const SbBool press = event->getState() == SoButtonEvent::DOWN ? TRUE : FALSE;
switch (event->getKey()) {
case SoKeyboardEvent::H:
processed = TRUE;
if(!press){
SbBool ret = NavigationStyle::lookAtPoint(event->getPosition());
if(!ret){
QMessageBox::information(viewer,QObject::tr("Set focus"),
QObject::tr("Aim mouse pointer at a point on some object, and hit H on keyboard. The camera's focus point will jump there.\nIf using touchscreen, tap the point to aim the cursor."));
}
}
break;
}
}
if (processed)
goto finalize;
//mode-independent spaceball/joystick handling
if (evIsLoc3) {
const SoMotion3Event * const event = static_cast<const SoMotion3Event * const>(ev);
if (event)
this->processMotionEvent(event);
processed = TRUE;
}
if (processed)
goto finalize;
//all mode-dependent stuff is within this switch.
switch(curmode){
case NavigationStyle::IDLE:
case NavigationStyle::SELECTION:
case NavigationStyle::INTERACT: {
//idle and interaction
//keyboard
if (evIsKeyboard) {
const SoKeyboardEvent * const event = (const SoKeyboardEvent *) ev;
const SbBool press = event->getState() == SoButtonEvent::DOWN ? TRUE : FALSE;
switch(event->getKey()){
case SoKeyboardEvent::S:
case SoKeyboardEvent::HOME:
case SoKeyboardEvent::LEFT_ARROW:
case SoKeyboardEvent::UP_ARROW:
case SoKeyboardEvent::RIGHT_ARROW:
case SoKeyboardEvent::DOWN_ARROW:
processed = inherited::processSoEvent(ev);
propagated = true;
break;
case SoKeyboardEvent::PAGE_UP:
if(press){
doZoom(viewer->getSoRenderManager()->getCamera(), TRUE, posn);
}
processed = TRUE;
break;
case SoKeyboardEvent::PAGE_DOWN:
if(press){
doZoom(viewer->getSoRenderManager()->getCamera(), FALSE, posn);
}
processed = TRUE;
break;
}//switch key
}
if (processed)
goto finalize;
// Mouse Button / Spaceball Button handling
if (evIsButton) {
const SoMouseButtonEvent * const event = (const SoMouseButtonEvent *) ev;
const int button = event->getButton();
const SbBool press //the button was pressed (if false -> released)
= event->getState() == SoButtonEvent::DOWN ? TRUE : FALSE;
switch(button){
case SoMouseButtonEvent::BUTTON1:
case SoMouseButtonEvent::BUTTON2:
if(press){
if(button == SoMouseButtonEvent::BUTTON1 && suppressLMBDrag){
//LMB drag suppressed. The event will be propagated, don't do anything.
} else {
//on left-mouse-button-down, enter dragging mode upon move detetion, or the event will need refiring if the mouse is released with no move.
//reset/start move detection machine
this->mousedownPos = pos;
this->mouseMoveThresholdBroken = false;
pan(viewer->getSoRenderManager()->getCamera());//set up panningplane
int &cnt = this->mousedownConsumedCount;
this->mousedownConsumedEvent[cnt] = *event;//hopefully, a shallow copy is enough. There are no pointers stored in events, apparently. Will loose a subclass, though.
cnt++;
assert(cnt<=2);
if(cnt>sizeof(mousedownConsumedEvent)){
cnt=sizeof(mousedownConsumedEvent);//we are in trouble
}
processed = true;//just consume this event, and wait for the move threshold to be broken to start dragging/panning
}
} else {//either a release, or another press in a complex click
if (this->mouseMoveThresholdBroken) {
assert(this->mousedownConsumedCount == 0);
//we typically end up here if suppressLMBDrag was true when mousedown.
//do nothing (propagate)
} else {
if (button == SoMouseButtonEvent::BUTTON2 && !this->thisClickIsComplex) {
if (!viewer->isEditing() && this->isPopupMenuEnabled()) {
processed=true;
if (!press) { // release right mouse button
this->openPopupMenu(event->getPosition());
}
}
}
if(! processed) {
//a left-click without drag (or tap) has happened.
//re-synthesize all previously-consumed mouseDown.
for( int i=0; i < this->mousedownConsumedCount; i++ ){
inherited::processSoEvent(& (this->mousedownConsumedEvent[i]));//simulate the previously-comsumed mousedown.
}
this->mousedownConsumedCount = 0;
processed = inherited::processSoEvent(ev);//explicitly, just for clarity that we are sending a full click sequence.
propagated = true;
}
} // end else of if mouseMoveThresholdBroken
}
break;
case SoMouseButtonEvent::BUTTON3://press the wheel
processed = TRUE;
if(press){
SbBool ret = NavigationStyle::lookAtPoint(event->getPosition());
if(!ret){
//no object under point or other failure.
//ignore...
//QMessageBox::information(viewer,QObject::tr("Set focus"),
// QObject::tr("Aim mouse pointer at a point on some object, and hit H on keyboard. The camera's focus point will jump there.\nIf using touchscreen, tap the point to aim the cursor."));
}
}
break;
case SoMouseButtonEvent::BUTTON4: //(wheel?)
doZoom(viewer->getSoRenderManager()->getCamera(), TRUE, posn);
processed = TRUE;
break;
case SoMouseButtonEvent::BUTTON5: //(wheel?)
doZoom(viewer->getSoRenderManager()->getCamera(), FALSE, posn);
processed = TRUE;
break;
}
}
//mouse moves - test for move threshold breaking
if (evIsLoc2) {
if ((this->button1down && !suppressLMBDrag) || this->button2down) {
if (this->mouseMoveThresholdBroken) {
//dupm all consumed mousedowns, we have processed them for navigation.
mousedownConsumedCount = 0;
setViewingMode(this->button1down ? NavigationStyle::DRAGGING : NavigationStyle::PANNING);
processed = true;
}
if (mousedownConsumedCount > 0)
processed = true;//if we are still deciding if it's a drag or not, consume mouseMoves.
}
}
//gesture start
if (evIsGesture && !this->button1down && !this->button2down){//ignore gestures when mouse buttons are down.
const SoGestureEvent* gesture = static_cast<const SoGestureEvent*>(ev);
if (gesture->state == SoGestureEvent::SbGSStart
|| gesture->state == SoGestureEvent::SbGSUpdate) {//even if we didn't get a start, assume the first update is a start (sort-of fail-safe).
if (type.isDerivedFrom(SoGesturePanEvent::getClassTypeId())) {
pan(viewer->getSoRenderManager()->getCamera());//set up panning plane
setViewingMode(NavigationStyle::PANNING);
processed = true;
} else if (type.isDerivedFrom(SoGesturePinchEvent::getClassTypeId())) {
pan(viewer->getSoRenderManager()->getCamera());//set up panning plane
setViewingMode(NavigationStyle::DRAGGING);
processed = true;
} //all other gestures - ignore!
}
}
//loc2 (mousemove) - ignore.
} break;//end of idle and interaction
case NavigationStyle::DRAGGING:
case NavigationStyle::ZOOMING:
case NavigationStyle::PANNING:{
//actual navigation
//no keyboard.
// Mouse Button / Spaceball Button handling
if (evIsButton) {
const SoMouseButtonEvent * const event = (const SoMouseButtonEvent *) ev;
const int button = event->getButton();
const SbBool press //the button was pressed (if false -> released)
= event->getState() == SoButtonEvent::DOWN ? TRUE : FALSE;
switch(button){
case SoMouseButtonEvent::BUTTON1:
case SoMouseButtonEvent::BUTTON2:
if(comboAfter & BUTTON1DOWN || comboAfter & BUTTON2DOWN) {
//don't leave navigation till all buttons have been released
setViewingMode((comboAfter & BUTTON1DOWN) ? NavigationStyle::DRAGGING : NavigationStyle::PANNING);
processed = true;
} else { //all buttons are released
//end of dragging/panning/whatever
setViewingMode(NavigationStyle::SELECTION);
processed = true;
} //else of if (some bottons down)
break;
} //switch(button)
} //if(evIsButton)
//the essence part 1!
//mouse movement into camera motion. Suppress if in gesture. Ignore until threshold is surpassed.
if (evIsLoc2 && ! this->inGesture && this->mouseMoveThresholdBroken) {
const SoLocation2Event * const event = (const SoLocation2Event *) ev;
if (curmode == NavigationStyle::ZOOMING) {//doesn't happen
this->zoomByCursor(posn, prevnormalized);
processed = TRUE;
} else if (curmode == NavigationStyle::PANNING) {
panCamera(viewer->getSoRenderManager()->getCamera(), ratio, this->panningplane, posn, prevnormalized);
processed = TRUE;
} else if (curmode == NavigationStyle::DRAGGING) {
if (comboAfter & BUTTON1DOWN && comboAfter & BUTTON2DOWN) {
//two mouse buttons down - tilting!
NavigationStyle::doRotate(viewer->getSoRenderManager()->getCamera(),
(posn - prevnormalized)[0]*(-2),
SbVec2f(0.5,0.5));
processed = TRUE;
} else {//one mouse button - normal spinning
//this will also handle the single-finger drag (there's no gesture used, pseudomouse is enough)
//this->addToLog(event->getPosition(), event->getTime());
this->spin_simplified(viewer->getSoRenderManager()->getCamera(),
posn, prevnormalized);
processed = TRUE;
}
}
}
//the essence part 2!
//gesture into camera motion
if (evIsGesture){
const SoGestureEvent* gesture = static_cast<const SoGestureEvent*>(ev);
assert(gesture);
if (gesture->state == SoGestureEvent::SbGSEnd) {
setViewingMode(NavigationStyle::SELECTION);
processed=true;
} else if (gesture->state == SoGestureEvent::SbGSUpdate){
if(type.isDerivedFrom(SoGesturePinchEvent::getClassTypeId())){
const SoGesturePinchEvent* const event = static_cast<const SoGesturePinchEvent* const>(ev);
if (this->zoomAtCursor){
//this is just dealing with the pan part of pinch gesture. Taking care of zooming to pos is done in doZoom.
SbVec2f panDist = this->normalizePixelPos(event->deltaCenter.getValue());
NavigationStyle::panCamera(viewer->getSoRenderManager()->getCamera(), ratio, this->panningplane, panDist, SbVec2f(0,0));
}
NavigationStyle::doZoom(viewer->getSoRenderManager()->getCamera(),-logf(event->deltaZoom),this->normalizePixelPos(event->curCenter));
if (event->deltaAngle != 0)
NavigationStyle::doRotate(viewer->getSoRenderManager()->getCamera(),event->deltaAngle,this->normalizePixelPos(event->curCenter));
processed = true;
}
if(type.isDerivedFrom(SoGesturePanEvent::getClassTypeId())){
const SoGesturePanEvent* const event = static_cast<const SoGesturePanEvent* const>(ev);
//this is just dealing with the pan part of pinch gesture. Taking care of zooming to pos is done in doZoom.
SbVec2f panDist = this->normalizePixelPos(event->deltaOffset);
NavigationStyle::panCamera(viewer->getSoRenderManager()->getCamera(), ratio, this->panningplane, panDist, SbVec2f(0,0));
processed = true;
}
} else {
//shouldn't happen. Gestures are not expected to start in the middle of navigation.
//we'll consume it, without reacting.
processed=true;
//This does, unfortunately, happen on regular basis for pan gesture on Windows8.1+Qt4.8
}
}
} break;//end of actual navigation
case NavigationStyle::SEEK_WAIT_MODE:{
if (evIsButton) {
const SoMouseButtonEvent * const event = (const SoMouseButtonEvent *) ev;
const int button = event->getButton();
const SbBool press = event->getState() == SoButtonEvent::DOWN ? TRUE : FALSE;
if (button == SoMouseButtonEvent::BUTTON1 && press) {
this->seekToPoint(pos); // implicitly calls interactiveCountInc()
this->setViewingMode(NavigationStyle::SEEK_MODE);
processed = true;
}
}
} ; //not end of SEEK_WAIT_MODE. Fall through by design!!!
case NavigationStyle::SPINNING:
case NavigationStyle::SEEK_MODE: {
//animation modes
if (!processed) {
if (evIsButton || evIsGesture || evIsKeyboard || evIsLoc3)
setViewingMode(NavigationStyle::SELECTION);
}
} break; //end of animation modes
case NavigationStyle::BOXZOOM:
default:
//all the rest - will be pass on to inherited, later.
break;
}
if (! processed && ! propagated) {
processed = inherited::processSoEvent(ev);
propagated = true;
}
//-----------------------end of event handling---------------------
finalize:
return processed;
}

View File

@ -761,6 +761,39 @@ void NavigationStyle::zoomOut()
void NavigationStyle::doZoom(SoCamera* camera, SbBool forward, const SbVec2f& pos)
{
// SbBool zoomAtCur = this->zoomAtCursor;
// if (zoomAtCur) {
// const SbViewportRegion & vp = viewer->getSoRenderManager()->getViewportRegion();
// float ratio = vp.getViewportAspectRatio();
// SbViewVolume vv = camera->getViewVolume(vp.getViewportAspectRatio());
// SbPlane panplane = vv.getPlane(camera->focalDistance.getValue());
// panCamera(viewer->getSoRenderManager()->getCamera(), ratio, panplane, SbVec2f(0.5,0.5), pos);
// }
float value = this->zoomStep;
if (!forward)
value = -value;
if (this->invertZoom)
value = -value;
doZoom(camera, value, pos);
// if (zoomAtCur) {
// const SbViewportRegion & vp = viewer->getSoRenderManager()->getViewportRegion();
// float ratio = vp.getViewportAspectRatio();
// SbViewVolume vv = camera->getViewVolume(vp.getViewportAspectRatio());
// SbPlane panplane = vv.getPlane(camera->focalDistance.getValue());
// panCamera(viewer->getSoRenderManager()->getCamera(), ratio, panplane, pos, SbVec2f(0.5,0.5));
// }
}
/*!
*\brief NavigationStyle::doZoom Zooms in or out by specified factor, keeping the point on screen specified by parameter pos fixed
* or not according to user preference (NavigationStyle::zoomAtCursor). Ignores invertZoom user preference.
*/
void NavigationStyle::doZoom(SoCamera* camera, float logfactor, const SbVec2f& pos)
{
if (abs(logfactor)>4.0) return;//something is asking for big zoom factor. This func is made for interactive zooming, where the changes are per mouse move and thus are small.
SbBool zoomAtCur = this->zoomAtCursor;
if (zoomAtCur) {
const SbViewportRegion & vp = viewer->getSoRenderManager()->getViewportRegion();
@ -770,12 +803,7 @@ void NavigationStyle::doZoom(SoCamera* camera, SbBool forward, const SbVec2f& po
panCamera(viewer->getSoRenderManager()->getCamera(), ratio, panplane, SbVec2f(0.5,0.5), pos);
}
float value = this->zoomStep;
if (!forward)
value = -value;
if (this->invertZoom)
value = -value;
zoom(camera, value);
zoom(camera, logfactor);
if (zoomAtCur) {
const SbViewportRegion & vp = viewer->getSoRenderManager()->getViewportRegion();
@ -786,6 +814,35 @@ void NavigationStyle::doZoom(SoCamera* camera, SbBool forward, const SbVec2f& po
}
}
void NavigationStyle::doRotate(SoCamera * camera, float angle, const SbVec2f& pos)
{
SbBool zoomAtCur = this->zoomAtCursor;
if (zoomAtCur) {
const SbViewportRegion & vp = viewer->getSoRenderManager()->getViewportRegion();
float ratio = vp.getViewportAspectRatio();
SbViewVolume vv = camera->getViewVolume(vp.getViewportAspectRatio());
SbPlane panplane = vv.getPlane(camera->focalDistance.getValue());
panCamera(viewer->getSoRenderManager()->getCamera(), ratio, panplane, SbVec2f(0.5,0.5), pos);
}
SbRotation rotcam = camera->orientation.getValue();
//get view direction
SbVec3f vdir;
rotcam.multVec(SbVec3f(0,0,-1),vdir);
//rotate
SbRotation drot(vdir,angle);
camera->orientation.setValue(rotcam * drot);
if (zoomAtCur) {
const SbViewportRegion & vp = viewer->getSoRenderManager()->getViewportRegion();
float ratio = vp.getViewportAspectRatio();
SbViewVolume vv = camera->getViewVolume(vp.getViewportAspectRatio());
SbPlane panplane = vv.getPlane(camera->focalDistance.getValue());
panCamera(viewer->getSoRenderManager()->getCamera(), ratio, panplane, pos, SbVec2f(0.5,0.5));
}
}
/** Uses the sphere sheet projector to map the mouseposition onto
* a 3D point and find a rotation from this and the last calculated point.
*/
@ -859,6 +916,40 @@ void NavigationStyle::spin(const SbVec2f & pointerpos)
if (this->spinsamplecounter > 3) this->spinsamplecounter = 3;
}
/*!
* \brief NavigationStyle::spin_simplified is a simplified version of
* NavigationStyle::spin(..), which uses less global variables. Doesn't support
* starting an animated spinning.
*
* \param cam the camera to affect. The rotation amount is determined by delta
* (curpos-prevpos), and rotation axis is also affected by average pos.
* \param curpos current normalized position or mouse pointer
* \param prevpos previous normalized position of mouse pointer
*/
void NavigationStyle::spin_simplified(SoCamera* cam, SbVec2f curpos, SbVec2f prevpos){
assert(this->spinprojector != NULL);
// 0000333: Turntable camera rotation
SbMatrix mat;
viewer->getSoRenderManager()->getCamera()->orientation.getValue().getValue(mat);
this->spinprojector->setWorkingSpace(mat);
this->spinprojector->project(prevpos);
SbRotation r;
this->spinprojector->projectAndGetRotation(curpos, r);
float sensitivity = getSensitivity();
if (sensitivity > 1.0f) {
SbVec3f axis;
float radians;
r.getValue(axis, radians);
radians = sensitivity * radians;
r.setValue(axis, radians);
}
r.invert();
this->reorientCamera(cam, r);
}
SbBool NavigationStyle::doSpin()
{
if (this->log.historysize >= 3) {
@ -909,6 +1000,22 @@ void NavigationStyle::saveCursorPosition(const SoEvent * const ev)
}
}
SbVec2f NavigationStyle::normalizePixelPos(SbVec2s pixpos)
{
const SbViewportRegion & vp = viewer->getSoRenderManager()->getViewportRegion();
const SbVec2s size(vp.getViewportSizePixels());
return SbVec2f ((float) pixpos[0] / (float) std::max((int)(size[0] - 1), 1),
(float) pixpos[1] / (float) std::max((int)(size[1] - 1), 1));
}
SbVec2f NavigationStyle::normalizePixelPos(SbVec2f pixpos)
{
const SbViewportRegion & vp = viewer->getSoRenderManager()->getViewportRegion();
const SbVec2s size(vp.getViewportSizePixels());
return SbVec2f ( pixpos[0] / (float) std::max((int)(size[0] - 1), 1),
pixpos[1] / (float) std::max((int)(size[1] - 1), 1));
}
void NavigationStyle::moveCursorPosition()
{
if (!isResetCursorPosition())

View File

@ -32,6 +32,7 @@
#include <Inventor/SbPlane.h>
#include <Inventor/SbRotation.h>
#include <Inventor/SbTime.h>
#include <Inventor/events/SoEvents.h>
#include <QCursor>
#include <QEvent>
#include <Base/BaseClass.h>
@ -177,11 +178,17 @@ protected:
void zoom(SoCamera * camera, float diffvalue);
void zoomByCursor(const SbVec2f & thispos, const SbVec2f & prevpos);
void doZoom(SoCamera * camera, SbBool forward, const SbVec2f& pos);
void doZoom(SoCamera * camera, float logzoomfactor, const SbVec2f& pos);
void doRotate(SoCamera * camera, float angle, const SbVec2f& pos);
void spin(const SbVec2f & pointerpos);
SbBool doSpin();
void spin_simplified(SoCamera *cam, SbVec2f curpos, SbVec2f prevpos);
void moveCursorPosition();
void saveCursorPosition(const SoEvent * const ev);
SbVec2f normalizePixelPos(SbVec2s pixpos);
SbVec2f normalizePixelPos(SbVec2f pixpos);
SbBool handleEventInForeground(const SoEvent* const e);
virtual SbBool processSoEvent(const SoEvent * const ev);
void syncWithEvent(const SoEvent * const ev);
@ -190,6 +197,7 @@ protected:
void clearLog(void);
void addToLog(const SbVec2s pos, const SbTime time);
protected:
struct { // tracking mouse movement in a log
short size;
@ -315,6 +323,30 @@ protected:
SbBool processSoEvent(const SoEvent * const ev);
};
class GuiExport GestureNavigationStyle : public UserNavigationStyle {
typedef UserNavigationStyle inherited;
TYPESYSTEM_HEADER();
public:
GestureNavigationStyle();
~GestureNavigationStyle();
const char* mouseButtons(ViewerMode);
protected:
SbBool processSoEvent(const SoEvent * const ev);
SbVec2s mousedownPos;//the position where some mouse button was pressed (local pixel coordinates).
short mouseMoveThreshold;//setting. Minimum move required to consider it a move (in pixels).
bool mouseMoveThresholdBroken;//a flag that the move threshold was surpassed since last mousedown.
int mousedownConsumedCount;//a flag for remembering that a mousedown of button1/button2 was consumed.
SoMouseButtonEvent mousedownConsumedEvent[5];//the event that was consumed and is to be refired. 2 should be enough, but just for a case of the maximum 5 buttons...
bool testMoveThreshold(const SbVec2s currentPos) const;
bool thisClickIsComplex;//a flag that becomes set when a complex clicking pattern is detected (i.e., two or more mouse buttons were down at the same time).
bool inGesture; //a flag that is used to filter out mouse events during gestures.
};
} // namespace Gui
#endif // GUI_NAVIGATIONSTYLE_H

View File

@ -134,6 +134,7 @@ void Gui::SoFCDB::init()
CADNavigationStyle ::init();
BlenderNavigationStyle ::init();
TouchpadNavigationStyle ::init();
GestureNavigationStyle ::init();
GLGraphicsItem ::init();
GLFlagWindow ::init();