1
/***************************************************************************
2
* Copyright (c) 2008 Werner Mayer <wmayer[at]users.sourceforge.net> *
4
* This file is part of the FreeCAD CAx development system. *
6
* This library is free software; you can redistribute it and/or *
7
* modify it under the terms of the GNU Library General Public *
8
* License as published by the Free Software Foundation; either *
9
* version 2 of the License, or (at your option) any later version. *
11
* This library is distributed in the hope that it will be useful, *
12
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
13
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
14
* GNU Library General Public License for more details. *
16
* You should have received a copy of the GNU Library General Public *
17
* License along with this library; see the file COPYING.LIB. If not, *
18
* write to the Free Software Foundation, Inc., 59 Temple Place, *
19
* Suite 330, Boston, MA 02111-1307, USA *
21
***************************************************************************/
23
#include "PreCompiled.h"
25
# include <Inventor/SbViewportRegion.h>
26
# include <Inventor/SoPickedPoint.h>
27
# include <Inventor/actions/SoGetBoundingBoxAction.h>
28
# include <Inventor/errors/SoDebugError.h>
29
# include <Inventor/nodes/SoSeparator.h>
30
# include <Inventor/nodes/SoCamera.h>
31
# include <Inventor/nodes/SoOrthographicCamera.h>
32
# include <Inventor/nodes/SoPerspectiveCamera.h>
33
# include <Inventor/projectors/SbSphereSheetProjector.h>
35
# include <QActionGroup>
36
# include <QApplication>
42
#include <App/Application.h>
44
#include "NavigationStyle.h"
45
#include "Application.h"
46
#include "MenuManager.h"
47
#include "MouseSelection.h"
48
#include "NavigationAnimator.h"
49
#include "NavigationAnimation.h"
50
#include "SoMouseWheelEvent.h"
51
#include "View3DInventorViewer.h"
55
class FCSphereSheetProjector : public SbSphereSheetProjector {
56
using inherited = SbSphereSheetProjector;
65
FCSphereSheetProjector(const SbSphere & sph, const SbBool orienttoeye = true)
66
: SbSphereSheetProjector(sph, orienttoeye)
70
void setViewVolume (const SbViewVolume &vol) override
72
inherited::setViewVolume(vol);
75
void setWorkingSpace (const SbMatrix &space) override
77
//inherited::setWorkingSpace(space);
78
this->worldToScreen = space.inverse();
81
SbVec3f project(const SbVec2f &point) override
83
return inherited::project(point);
86
SbRotation getRotation(const SbVec3f &point1, const SbVec3f &point2) override
88
SbRotation rot = inherited::getRotation(point1, point2);
89
if (orbit == Turntable) {
90
return getTurntable(rot, point1, point2);
92
if (orbit == FreeTurntable) {
93
return getFreeTurntable(point1, point2);
99
void setOrbitStyle(OrbitStyle style)
104
OrbitStyle getOrbitStyle() const
110
SbRotation getTurntable(SbRotation rot, const SbVec3f &point1, const SbVec3f &point2) const
112
// 0000333: Turntable camera rotation
115
rot.getValue(axis, angle);
116
SbVec3f dif = point1 - point2;
117
if (fabs(dif[1]) > fabs(dif[0])) {
118
SbVec3f xaxis(1,0,0);
122
rot.setValue(xaxis, angle);
125
SbVec3f zaxis(0,0,1);
126
this->worldToScreen.multDirMatrix(zaxis, zaxis);
137
rot.setValue(zaxis, angle);
143
SbRotation getFreeTurntable(const SbVec3f &point1, const SbVec3f &point2) const
145
// Turntable without constraints
148
SbVec3f dif = point1 - point2;
150
SbVec3f zaxis(1,0,0);
151
zrot.setValue(zaxis, dif[1]);
153
SbVec3f xaxis(0,0,1);
154
this->worldToScreen.multDirMatrix(xaxis, xaxis);
155
xrot.setValue(xaxis, -dif[0]);
161
SbMatrix worldToScreen;
162
OrbitStyle orbit{Trackball};
165
NavigationStyleEvent::NavigationStyleEvent(const Base::Type& s)
166
: QEvent(QEvent::User), t(s)
170
NavigationStyleEvent::~NavigationStyleEvent() = default;
172
const Base::Type& NavigationStyleEvent::style() const
177
TYPESYSTEM_SOURCE_ABSTRACT(Gui::NavigationStyle,Base::BaseClass)
179
NavigationStyle::NavigationStyle() : viewer(nullptr), mouseSelection(nullptr)
181
this->rotationCenterMode = NavigationStyle::RotationCenterMode::ScenePointAtCursor
182
| NavigationStyle::RotationCenterMode::FocalPointAtCursor;
186
NavigationStyle::~NavigationStyle()
189
delete this->animator;
192
NavigationStyle& NavigationStyle::operator = (const NavigationStyle& ns)
194
this->panningplane = ns.panningplane;
195
this->menuenabled = ns.menuenabled;
196
this->animationEnabled = ns.animationEnabled;
197
this->spinningAnimationEnabled = ns.spinningAnimationEnabled;
198
static_cast<FCSphereSheetProjector*>(this->spinprojector)->setOrbitStyle
199
(static_cast<FCSphereSheetProjector*>(ns.spinprojector)->getOrbitStyle());
203
void NavigationStyle::setViewer(View3DInventorViewer* view)
208
void NavigationStyle::initialize()
210
this->animator = new NavigationAnimator();
212
this->sensitivity = 2.0f;
213
this->resetcursorpos = false;
214
this->currentmode = NavigationStyle::IDLE;
215
this->animationEnabled = true;
216
this->spinningAnimationEnabled = false;
217
this->spinsamplecounter = 0;
218
this->spinincrement = SbRotation::identity();
219
this->rotationCenterFound = false;
221
// FIXME: use a smaller sphere than the default one to have a larger
222
// area close to the borders that gives us "z-axis rotation"?
224
this->spinprojector = new FCSphereSheetProjector(SbSphere(SbVec3f(0, 0, 0), 0.8f));
226
volume.ortho(-1, 1, -1, 1, -1, 1);
227
this->spinprojector->setViewVolume(volume);
230
this->log.position = new SbVec2s [ 16 ];
231
this->log.time = new SbTime [ 16 ];
232
this->log.historysize = 0;
234
this->menuenabled = true;
235
this->button1down = false;
236
this->button2down = false;
237
this->button3down = false;
238
this->ctrldown = false;
239
this->shiftdown = false;
240
this->altdown = false;
241
this->invertZoom = App::GetApplication().GetParameterGroupByPath
242
("User parameter:BaseApp/Preferences/View")->GetBool("InvertZoom",true);
243
this->zoomAtCursor = App::GetApplication().GetParameterGroupByPath
244
("User parameter:BaseApp/Preferences/View")->GetBool("ZoomAtCursor",true);
245
this->zoomStep = App::GetApplication().GetParameterGroupByPath
246
("User parameter:BaseApp/Preferences/View")->GetFloat("ZoomStep",0.2f);
247
long mode = App::GetApplication().GetParameterGroupByPath
248
("User parameter:BaseApp/Preferences/View")->GetInt("RotationMode", 1);
250
setRotationCenterMode(NavigationStyle::RotationCenterMode::WindowCenter);
252
else if (mode == 1) {
253
setRotationCenterMode(NavigationStyle::RotationCenterMode::ScenePointAtCursor |
254
NavigationStyle::RotationCenterMode::FocalPointAtCursor);
256
else if (mode == 2) {
257
setRotationCenterMode(NavigationStyle::RotationCenterMode::ScenePointAtCursor |
258
NavigationStyle::RotationCenterMode::BoundingBoxCenter);
261
this->hasDragged = false;
262
this->hasPanned = false;
263
this->hasZoomed = false;
266
void NavigationStyle::finalize()
268
delete this->spinprojector;
269
delete[] this->log.position;
270
delete[] this->log.time;
273
void NavigationStyle::interactiveCountInc()
275
viewer->interactiveCountInc();
278
void NavigationStyle::interactiveCountDec()
280
viewer->interactiveCountDec();
283
int NavigationStyle::getInteractiveCount() const
285
return viewer->getInteractiveCount();
288
void NavigationStyle::setOrbitStyle(NavigationStyle::OrbitStyle style)
290
auto projector = static_cast<FCSphereSheetProjector*>(this->spinprojector);
291
projector->setOrbitStyle(FCSphereSheetProjector::OrbitStyle(style));
294
NavigationStyle::OrbitStyle NavigationStyle::getOrbitStyle() const
296
auto projector = static_cast<FCSphereSheetProjector*>(this->spinprojector);
297
return NavigationStyle::OrbitStyle(projector->getOrbitStyle());
300
SbBool NavigationStyle::isViewing() const
302
return viewer->isViewing();
305
void NavigationStyle::setViewing(SbBool enable)
307
viewer->setViewing(enable);
310
SbBool NavigationStyle::isSeekMode() const
312
return viewer->isSeekMode();
315
void NavigationStyle::setSeekMode(SbBool enable)
317
viewer->setSeekMode(enable);
320
SbBool NavigationStyle::seekToPoint(const SbVec2s screenpos)
322
return viewer->seekToPoint(screenpos);
325
void NavigationStyle::seekToPoint(const SbVec3f& scenepos)
327
viewer->seekToPoint(scenepos);
330
SbBool NavigationStyle::lookAtPoint(const SbVec2s screenpos)
332
SoCamera* cam = viewer->getSoRenderManager()->getCamera();
336
SoRayPickAction rpaction(viewer->getSoRenderManager()->getViewportRegion());
337
rpaction.setPoint(screenpos);
338
rpaction.setRadius(viewer->getPickRadius());
339
rpaction.apply(viewer->getSoRenderManager()->getSceneGraph());
341
SoPickedPoint * picked = rpaction.getPickedPoint();
343
this->interactiveCountInc();
348
hitpoint = picked->getPoint();
349
lookAtPoint(hitpoint);
353
void NavigationStyle::lookAtPoint(const SbVec3f& position)
355
this->rotationCenterFound = false;
356
translateCamera(position - getFocalPoint());
359
SoCamera* NavigationStyle::getCamera() const
361
return this->viewer->getCamera();
364
void NavigationStyle::setCameraOrientation(const SbRotation& orientation, SbBool moveToCenter)
366
SoCamera* camera = getCamera();
372
SbVec3f focalPoint = getFocalPoint();
373
SbVec3f translation(0, 0, 0);
376
SoGetBoundingBoxAction action(viewer->getSoRenderManager()->getViewportRegion());
377
action.apply(viewer->getSceneGraph());
378
SbBox3f box = action.getBoundingBox();
379
if (!box.isEmpty()) {
380
translation = box.getCenter() - focalPoint;
384
// Start an animation or set the pose directly
385
if (isAnimationEnabled()) {
386
viewer->startAnimation(orientation, focalPoint, translation);
389
// Distance from rotation center to camera position in camera coordinate system
390
SbVec3f rotationCenterDistanceCam = camera->focalDistance.getValue() * SbVec3f(0, 0, 1);
392
// Set to the given orientation
393
camera->orientation = orientation;
395
// Distance from rotation center to new camera position in global coordinate system
396
SbVec3f newRotationCenterDistance;
397
camera->orientation.getValue().multVec(rotationCenterDistanceCam, newRotationCenterDistance);
399
// Reposition camera so the rotation center stays in the same place
400
// Optionally add translation to move to center
401
camera->position = focalPoint + newRotationCenterDistance + translation;
405
void NavigationStyle::translateCamera(const SbVec3f& translation)
407
SoCamera* camera = getCamera();
413
// Start an animation or set the pose directly
414
if (isAnimationEnabled()) {
415
viewer->startAnimation(camera->orientation.getValue(), SbVec3f(0, 0, 0), translation);
418
camera->position = camera->position.getValue() + translation;
422
void NavigationStyle::boxZoom(const SbBox2s& box)
424
SoCamera* cam = viewer->getSoRenderManager()->getCamera();
425
if (!cam) // no camera
427
const SbViewportRegion & vp = viewer->getSoRenderManager()->getViewportRegion();
428
SbViewVolume vv = cam->getViewVolume(vp.getViewportAspectRatio());
430
short sizeX{},sizeY{};
431
box.getSize(sizeX, sizeY);
432
SbVec2s size = vp.getViewportSizePixels();
434
// The bbox must not be empty i.e. width and length is zero, but it is possible that
435
// either width or length is zero
436
if (sizeX == 0 && sizeY == 0)
439
// Get the new center in normalized pixel coordinates
440
short xmin{},xmax{},ymin{},ymax{};
441
box.getBounds(xmin,ymin,xmax,ymax);
442
const SbVec2f center((float) ((xmin+xmax)/2) / (float) std::max((int)(size[0] - 1), 1),
443
(float) (size[1]-(ymin+ymax)/2) / (float) std::max((int)(size[1] - 1), 1));
445
SbPlane plane = vv.getPlane(cam->focalDistance.getValue());
446
panCamera(cam,vp.getViewportAspectRatio(),plane, SbVec2f(0.5,0.5), center);
448
// Set height or height angle of the camera
449
float scaleX = (float)sizeX/(float)size[0];
450
float scaleY = (float)sizeY/(float)size[1];
451
float scale = std::max<float>(scaleX, scaleY);
452
if (cam->getTypeId() == SoOrthographicCamera::getClassTypeId()) {
453
float height = static_cast<SoOrthographicCamera*>(cam)->height.getValue() * scale;
454
static_cast<SoOrthographicCamera*>(cam)->height = height;
456
else if (cam->getTypeId() == SoPerspectiveCamera::getClassTypeId()) {
457
float height = static_cast<SoPerspectiveCamera*>(cam)->heightAngle.getValue() / 2.0f;
458
height = 2.0f * atan(tan(height) * scale);
459
static_cast<SoPerspectiveCamera*>(cam)->heightAngle = height;
463
void NavigationStyle::viewAll()
465
// Get the bounding box of the scene
466
SoGetBoundingBoxAction action(viewer->getSoRenderManager()->getViewportRegion());
467
action.apply(viewer->getSceneGraph());
468
SbBox3f box = action.getBoundingBox();
473
SoCamera* cam = viewer->getSoRenderManager()->getCamera();
477
SbViewVolume vol = cam->getViewVolume();
478
if (vol.ulf == vol.llf)
479
return; // empty frustum (no view up vector defined)
480
SbVec2f s = vol.projectBox(box);
481
SbVec2s size = viewer->getSoRenderManager()->getSize();
483
SbVec3f pt1, pt2, pt3, tmp;
484
vol.projectPointToLine( SbVec2f(0.0f,0.0f), pt1, tmp );
485
vol.projectPointToLine( SbVec2f(s[0],0.0f), pt2, tmp );
486
vol.projectPointToLine( SbVec2f(0.0f,s[1]), pt3, tmp );
488
float cam_width = (pt2-pt1).length();
489
float cam_height = (pt3-pt1).length();
491
// add a small border
492
cam_height = 1.08f * std::max<float>((cam_width*(float)size[1])/(float)size[0],cam_height);
494
float aspect = cam->aspectRatio.getValue();
496
if (cam->getTypeId() == SoOrthographicCamera::getClassTypeId()) {
497
auto ocam = static_cast<SoOrthographicCamera *>(cam);
499
ocam->height = cam_height / aspect;
501
ocam->height = cam_height;
505
void NavigationStyle::findBoundingSphere() {
506
// Find a bounding sphere for the scene
507
SoGetBoundingBoxAction action(viewer->getSoRenderManager()->getViewportRegion());
508
action.apply(viewer->getSceneGraph());
509
boundingSphere.circumscribe(action.getBoundingBox());
512
/** Rotate the camera by the given amount, then reposition it so we're still pointing at the same
515
void NavigationStyle::reorientCamera(SoCamera* camera, const SbRotation& rotation)
517
reorientCamera(camera, rotation, getFocalPoint());
520
/** Rotate the camera by the given amount, then reposition it so the rotation center stays in the
523
void NavigationStyle::reorientCamera(SoCamera* camera, const SbRotation& rotation, const SbVec3f& rotationCenter)
529
// Distance from rotation center to camera position in camera coordinate system
530
SbVec3f rotationCenterDistanceCam;
531
camera->orientation.getValue().inverse().multVec(camera->position.getValue() - rotationCenter, rotationCenterDistanceCam);
533
// Set new orientation value by accumulating the new rotation
534
camera->orientation = rotation * camera->orientation.getValue();
536
// Distance from rotation center to new camera position in global coordinate system
537
SbVec3f newRotationCenterDistance;
538
camera->orientation.getValue().multVec(rotationCenterDistanceCam, newRotationCenterDistance);
540
// Reposition camera so the rotation center stays in the same place
541
camera->position = rotationCenter + newRotationCenterDistance;
543
// Fix issue with near clipping in orthogonal view
544
if (camera->getTypeId().isDerivedFrom(SoOrthographicCamera::getClassTypeId())) {
546
// The center of the bounding sphere in camera coordinate system
548
camera->orientation.getValue().inverse().multVec(boundingSphere.getCenter() - camera->position.getValue(), center);
551
camera->orientation.getValue().multVec(SbVec3f(0, 0, -1), dir);
553
// Reposition the camera but keep the focal point the same
554
// nearDistance is 0 and farDistance is the diameter of the bounding sphere
555
float repositionDistance = -center.getValue()[2] - boundingSphere.getRadius();
556
camera->position = camera->position.getValue() + repositionDistance * dir;
557
camera->nearDistance = 0;
558
camera->farDistance = 2 * boundingSphere.getRadius();
559
camera->focalDistance = camera->focalDistance.getValue() - repositionDistance;
563
void NavigationStyle::panCamera(SoCamera * cam, float aspectratio, const SbPlane & panplane,
564
const SbVec2f & currpos, const SbVec2f & prevpos)
566
if (!cam) // can happen for empty scenegraph
568
if (currpos == prevpos) // useless invocation
572
// Find projection points for the last and current mouse coordinates.
573
SbViewVolume vv = cam->getViewVolume(aspectratio);
575
// See note in Coin docs for SoCamera::getViewVolume re:viewport mapping
576
if(aspectratio < 1.0)
577
vv.scale(1.0 / aspectratio);
580
vv.projectPointToLine(currpos, line);
581
SbVec3f current_planept;
582
panplane.intersect(line, current_planept);
583
vv.projectPointToLine(prevpos, line);
585
panplane.intersect(line, old_planept);
587
// Reposition camera according to the vector difference between the
589
cam->position = cam->position.getValue() - (current_planept - old_planept);
591
if (this->currentmode != NavigationStyle::IDLE) {
596
void NavigationStyle::pan(SoCamera* camera)
598
// The plane we're projecting the mouse coordinates to get 3D
599
// coordinates should stay the same during the whole pan
600
// operation, so we should calculate this value here.
601
if (!camera) { // can happen for empty scenegraph
602
this->panningplane = SbPlane(SbVec3f(0, 0, 1), 0);
605
const SbViewportRegion & vp = viewer->getSoRenderManager()->getViewportRegion();
606
float aspectratio = vp.getViewportAspectRatio();
607
SbViewVolume vv = camera->getViewVolume(aspectratio);
609
// See note in Coin docs for SoCamera::getViewVolume re:viewport mapping
610
if(aspectratio < 1.0)
611
vv.scale(1.0 / aspectratio);
613
this->panningplane = vv.getPlane(camera->focalDistance.getValue());
617
void NavigationStyle::panToCenter(const SbPlane & pplane, const SbVec2f & currpos)
619
const SbViewportRegion & vp = viewer->getSoRenderManager()->getViewportRegion();
620
float ratio = vp.getViewportAspectRatio();
621
panCamera(viewer->getSoRenderManager()->getCamera(), ratio, pplane, SbVec2f(0.5,0.5), currpos);
622
this->rotationCenterFound = false;
625
/** Dependent on the camera type this will either shrink or expand the
626
* height of the viewport (orthogonal camera) or move the camera
627
* closer or further away from the focal point in the scene.
629
void NavigationStyle::zoom(SoCamera * cam, float diffvalue)
631
if (!cam) // can happen for empty scenegraph
636
SoType t = cam->getTypeId();
637
SbName tname = t.getName();
639
// This will be in the range of <0, ->>.
640
auto multiplicator = float(exp(diffvalue));
642
if (t.isDerivedFrom(SoOrthographicCamera::getClassTypeId())) {
644
// Since there's no perspective, "zooming" in the original sense
645
// of the word won't have any visible effect. So we just increase
646
// or decrease the field-of-view values of the camera instead, to
647
// "shrink" the projection size of the model / scene.
649
auto oc = static_cast<SoOrthographicCamera *>(cam);
650
oc->height = oc->height.getValue() * multiplicator;
654
// FrustumCamera can be found in the SmallChange CVS module (it's
655
// a camera that lets you specify (for instance) an off-center
656
// frustum (similar to glFrustum())
657
if (!t.isDerivedFrom(SoPerspectiveCamera::getClassTypeId()) &&
658
tname != "FrustumCamera") {
659
/* static SbBool first = true;
661
SoDebugError::postWarning("SoGuiFullViewerP::zoom",
662
"Unknown camera type, "
663
"will zoom by moving position, but this might not be correct.");
668
const float oldfocaldist = cam->focalDistance.getValue();
669
const float newfocaldist = oldfocaldist * multiplicator;
672
cam->orientation.getValue().multVec(SbVec3f(0, 0, -1), direction);
674
const SbVec3f oldpos = cam->position.getValue();
675
const SbVec3f newpos = oldpos + (newfocaldist - oldfocaldist) * -direction;
677
// This catches a rather common user interface "buglet": if the
678
// user zooms the camera out to a distance from origo larger than
679
// what we still can safely do floating point calculations on
680
// (i.e. without getting NaN or Inf values), the faulty floating
681
// point values will propagate until we start to get debug error
682
// messages and eventually an assert failure from core Coin code.
684
// With the below bounds check, this problem is avoided.
686
// (But note that we depend on the input argument ''diffvalue'' to
687
// be small enough that zooming happens gradually. Ideally, we
688
// should also check distorigo with isinf() and isnan() (or
689
// inversely; isinfite()), but those only became standardized with
691
const float distorigo = newpos.length();
692
// sqrt(FLT_MAX) == ~ 1e+19, which should be both safe for further
693
// calculations and ok for the end-user and app-programmer.
694
if (distorigo > float(sqrt(FLT_MAX))) {
698
cam->position = newpos;
699
cam->focalDistance = newfocaldist;
703
if (this->currentmode != NavigationStyle::IDLE) {
708
// Calculate a zoom/dolly factor from the difference of the current
709
// cursor position and the last.
710
void NavigationStyle::zoomByCursor(const SbVec2f & thispos, const SbVec2f & prevpos)
712
// There is no "geometrically correct" value, 20 just seems to give
713
// about the right "feel".
714
float value = (thispos[1] - prevpos[1]) * 10.0f/*20.0f*/;
715
if (this->invertZoom)
717
zoom(viewer->getSoRenderManager()->getCamera(), value);
720
void NavigationStyle::zoomIn()
722
zoom(viewer->getSoRenderManager()->getCamera(), -this->zoomStep);
725
void NavigationStyle::zoomOut()
727
zoom(viewer->getSoRenderManager()->getCamera(), this->zoomStep);
731
* Returns the steps if the mouse wheel is rotated
733
int NavigationStyle::getDelta() const
738
void NavigationStyle::doZoom(SoCamera* camera, int wheeldelta, const SbVec2f& pos)
740
float value = this->zoomStep * wheeldelta / float(getDelta());
741
if (this->invertZoom)
743
doZoom(camera, value, pos);
747
*\brief NavigationStyle::doZoom Zooms in or out by specified factor, keeping the point on screen specified by parameter pos fixed
748
* or not according to user preference (NavigationStyle::zoomAtCursor). Ignores invertZoom user preference.
750
void NavigationStyle::doZoom(SoCamera* camera, float logfactor, const SbVec2f& pos)
752
// something is asking for big zoom factor. This func is made for interactive zooming,
753
// where the changes are per mouse move and thus are small.
754
if (fabs(logfactor)>4.0)
756
SbBool zoomAtCur = this->zoomAtCursor;
758
const SbViewportRegion & vp = viewer->getSoRenderManager()->getViewportRegion();
759
float ratio = vp.getViewportAspectRatio();
760
SbViewVolume vv = camera->getViewVolume(vp.getViewportAspectRatio());
761
SbPlane panplane = vv.getPlane(camera->focalDistance.getValue());
762
panCamera(viewer->getSoRenderManager()->getCamera(), ratio, panplane, SbVec2f(0.5,0.5), pos);
765
zoom(camera, logfactor);
768
const SbViewportRegion & vp = viewer->getSoRenderManager()->getViewportRegion();
769
float ratio = vp.getViewportAspectRatio();
770
SbViewVolume vv = camera->getViewVolume(vp.getViewportAspectRatio());
771
SbPlane panplane = vv.getPlane(camera->focalDistance.getValue());
772
panCamera(viewer->getSoRenderManager()->getCamera(), ratio, panplane, pos, SbVec2f(0.5,0.5));
774
// Change the position of the rotation center indicator after zooming at cursor
775
// Rotation mode is WindowCenter
776
if (!rotationCenterMode) {
777
viewer->changeRotationCenterPosition(getFocalPoint());
778
findBoundingSphere();
783
void NavigationStyle::doRotate(SoCamera * camera, float angle, const SbVec2f& pos)
785
SbBool zoomAtCur = this->zoomAtCursor;
787
const SbViewportRegion & vp = viewer->getSoRenderManager()->getViewportRegion();
788
float ratio = vp.getViewportAspectRatio();
789
SbViewVolume vv = camera->getViewVolume(vp.getViewportAspectRatio());
790
SbPlane panplane = vv.getPlane(camera->focalDistance.getValue());
791
panCamera(viewer->getSoRenderManager()->getCamera(), ratio, panplane, SbVec2f(0.5,0.5), pos);
794
SbRotation rotcam = camera->orientation.getValue();
797
rotcam.multVec(SbVec3f(0,0,-1),vdir);
799
SbRotation drot(vdir,angle);
800
camera->orientation.setValue(rotcam * drot);
803
const SbViewportRegion & vp = viewer->getSoRenderManager()->getViewportRegion();
804
float ratio = vp.getViewportAspectRatio();
805
SbViewVolume vv = camera->getViewVolume(vp.getViewportAspectRatio());
806
SbPlane panplane = vv.getPlane(camera->focalDistance.getValue());
807
panCamera(viewer->getSoRenderManager()->getCamera(), ratio, panplane, pos, SbVec2f(0.5,0.5));
812
SbVec3f NavigationStyle::getRotationCenter(SbBool& found) const
814
found = this->rotationCenterFound;
815
return this->rotationCenter;
818
void NavigationStyle::setRotationCenter(const SbVec3f& cnt)
820
this->rotationCenter = cnt;
821
this->rotationCenterFound = true;
824
SbVec3f NavigationStyle::getFocalPoint() const
826
SoCamera* cam = viewer->getSoRenderManager()->getCamera();
830
// Find global coordinates of focal point.
832
cam->orientation.getValue().multVec(SbVec3f(0, 0, -1), direction);
833
SbVec3f focal = cam->position.getValue() +
834
cam->focalDistance.getValue() * direction;
838
/** Uses the sphere sheet projector to map the mouseposition onto
839
* a 3D point and find a rotation from this and the last calculated point.
841
void NavigationStyle::spin(const SbVec2f & pointerpos)
843
if (this->log.historysize < 2)
845
assert(this->spinprojector);
847
const SbViewportRegion & vp = viewer->getSoRenderManager()->getViewportRegion();
848
SbVec2s glsize(vp.getViewportSizePixels());
850
lastpos[0] = float(this->log.position[1][0]) / float(std::max((int)(glsize[0]-1), 1));
851
lastpos[1] = float(this->log.position[1][1]) / float(std::max((int)(glsize[1]-1), 1));
853
if (this->rotationCenterMode && this->rotationCenterFound) {
854
SbVec3f hitpoint = this->rotationCenter;
856
// set to the given position
858
viewer->getSoRenderManager()->getCamera()->orientation.getValue().multVec(SbVec3f(0, 0, -1), direction);
859
viewer->getSoRenderManager()->getCamera()->position = hitpoint - viewer->getSoRenderManager()->getCamera()->focalDistance.getValue() * direction;
862
// 0000333: Turntable camera rotation
864
viewer->getSoRenderManager()->getCamera()->orientation.getValue().getValue(mat);
865
this->spinprojector->setWorkingSpace(mat);
867
this->spinprojector->project(lastpos);
869
this->spinprojector->projectAndGetRotation(pointerpos, r);
870
float sensitivity = getSensitivity();
871
if (sensitivity > 1.0f) {
874
r.getValue(axis, radians);
875
radians = sensitivity * radians;
876
r.setValue(axis, radians);
879
this->reorientCamera(viewer->getSoRenderManager()->getCamera(), r);
881
if (this->rotationCenterMode && this->rotationCenterFound) {
882
float ratio = vp.getViewportAspectRatio();
883
SbViewVolume vv = viewer->getSoRenderManager()->getCamera()->getViewVolume(vp.getViewportAspectRatio());
884
SbPlane panplane = vv.getPlane(viewer->getSoRenderManager()->getCamera()->focalDistance.getValue());
886
posn[0] = float(this->localPos[0]) / float(std::max((int)(glsize[0]-1), 1));
887
posn[1] = float(this->localPos[1]) / float(std::max((int)(glsize[1]-1), 1));
888
panCamera(viewer->getSoRenderManager()->getCamera(), ratio, panplane, posn, SbVec2f(0.5,0.5));
891
// Calculate an average angle magnitude value to make the transition
892
// to a possible spin animation mode appear smooth.
894
SbVec3f dummy_axis, newaxis;
895
float acc_angle{}, newangle{};
896
this->spinincrement.getValue(dummy_axis, acc_angle);
897
acc_angle *= this->spinsamplecounter; // weight
898
r.getValue(newaxis, newangle);
899
acc_angle += newangle;
901
this->spinsamplecounter++;
902
acc_angle /= this->spinsamplecounter;
903
// FIXME: accumulate and average axis vectors as well? 19990501 mortene.
904
this->spinincrement.setValue(newaxis, acc_angle);
906
// Don't carry too much baggage, as that'll give unwanted results
907
// when the user quickly trigger (as in "click-drag-release") a spin
909
if (this->spinsamplecounter > 3) this->spinsamplecounter = 3;
911
if (this->currentmode != NavigationStyle::IDLE) {
917
* \brief NavigationStyle::spin_simplified is a simplified version of
918
* NavigationStyle::spin(..), which uses less global variables. Doesn't support
919
* starting an animated spinning.
921
* \param cam the camera to affect. The rotation amount is determined by delta
922
* (curpos-prevpos), and rotation axis is also affected by average pos.
923
* \param curpos current normalized position or mouse pointer
924
* \param prevpos previous normalized position of mouse pointer
926
void NavigationStyle::spin_simplified(SoCamera* cam, SbVec2f curpos, SbVec2f prevpos){
927
assert(this->spinprojector);
929
// 0000333: Turntable camera rotation
931
viewer->getSoRenderManager()->getCamera()->orientation.getValue().getValue(mat);
932
this->spinprojector->setWorkingSpace(mat);
934
this->spinprojector->project(prevpos);
936
this->spinprojector->projectAndGetRotation(curpos, r);
937
float sensitivity = getSensitivity();
938
if (sensitivity > 1.0f) {
941
r.getValue(axis, radians);
942
radians = sensitivity * radians;
943
r.setValue(axis, radians);
946
this->reorientCamera(cam, r);
951
SbBool NavigationStyle::doSpin()
953
if (this->log.historysize >= 3) {
954
SbTime stoptime = (SbTime::getTimeOfDay() - this->log.time[0]);
955
if (isSpinningAnimationEnabled() && stoptime.getValue() < 0.100) {
956
const SbViewportRegion & vp = viewer->getSoRenderManager()->getViewportRegion();
957
const SbVec2s glsize(vp.getViewportSizePixels());
958
SbVec3f from = this->spinprojector->project(SbVec2f(float(this->log.position[2][0]) / float(std::max(glsize[0]-1, 1)),
959
float(this->log.position[2][1]) / float(std::max(glsize[1]-1, 1))));
960
SbVec3f to = this->spinprojector->project(this->lastmouseposition);
961
SbRotation rot = this->spinprojector->getRotation(from, to);
963
SbTime delta = (this->log.time[0] - this->log.time[2]);
964
double deltatime = delta.getValue();
966
rot.scaleAngle(float(0.200 / deltatime));
970
rot.getValue(axis, radians);
971
if ((radians > 0.01f) && (deltatime < 0.300)) {
972
viewer->startSpinningAnimation(axis, radians * 5);
981
void NavigationStyle::saveCursorPosition(const SoEvent * const ev)
983
this->globalPos.setValue(QCursor::pos().x(), QCursor::pos().y());
984
this->localPos = ev->getPosition();
986
// mode is WindowCenter
987
if (!this->rotationCenterMode) {
988
setRotationCenter(getFocalPoint());
991
//Option to get point on model (slow) or always on focal plane (fast)
993
// mode is ScenePointAtCursor to get exact point if possible
994
if (this->rotationCenterMode & NavigationStyle::RotationCenterMode::ScenePointAtCursor) {
995
SoRayPickAction rpaction(viewer->getSoRenderManager()->getViewportRegion());
996
rpaction.setPoint(this->localPos);
997
rpaction.setRadius(viewer->getPickRadius());
998
rpaction.apply(viewer->getSoRenderManager()->getSceneGraph());
1000
SoPickedPoint * picked = rpaction.getPickedPoint();
1002
setRotationCenter(picked->getPoint());
1007
// mode is FocalPointAtCursor or a ScenePointAtCursor failed
1008
if (this->rotationCenterMode & NavigationStyle::RotationCenterMode::FocalPointAtCursor) {
1009
// get the intersection point of the ray and the focal plane
1010
const SbViewportRegion & vp = viewer->getSoRenderManager()->getViewportRegion();
1011
float ratio = vp.getViewportAspectRatio();
1013
SoCamera* cam = viewer->getSoRenderManager()->getCamera();
1014
if (!cam) // no camera
1016
SbViewVolume vv = cam->getViewVolume(ratio);
1019
SbVec2f currpos = ev->getNormalizedPosition(vp);
1020
vv.projectPointToLine(currpos, line);
1021
SbVec3f current_planept;
1022
SbPlane panplane = vv.getPlane(cam->focalDistance.getValue());
1023
panplane.intersect(line, current_planept);
1025
setRotationCenter(current_planept);
1028
// mode is BoundingBoxCenter or a ScenePointAtCursor failed
1029
if (this->rotationCenterMode & NavigationStyle::RotationCenterMode::BoundingBoxCenter) {
1030
const SbViewportRegion & vp = viewer->getSoRenderManager()->getViewportRegion();
1031
float ratio = vp.getViewportAspectRatio();
1033
SoCamera* cam = viewer->getSoRenderManager()->getCamera();
1034
if (!cam) // no camera
1037
// Get the bounding box center of the physical object group
1038
SoGetBoundingBoxAction action(viewer->getSoRenderManager()->getViewportRegion());
1039
action.apply(viewer->objectGroup);
1040
SbBox3f boundingBox = action.getBoundingBox();
1041
SbVec3f boundingBoxCenter = boundingBox.getCenter();
1042
setRotationCenter(boundingBoxCenter);
1044
// To drag around the center point of the bbox we have to determine
1045
// its projection on the screen because this information is used in
1046
// NavigationStyle::spin() for the panning
1047
SbViewVolume vv = cam->getViewVolume(ratio);
1048
vv.projectToScreen(boundingBoxCenter, boundingBoxCenter);
1049
SbVec2s size = vp.getViewportSizePixels();
1050
auto tox = static_cast<short>(boundingBoxCenter[0] * size[0]);
1051
auto toy = static_cast<short>(boundingBoxCenter[1] * size[1]);
1052
this->localPos.setValue(tox, toy);
1056
SbVec2f NavigationStyle::normalizePixelPos(SbVec2s pixpos)
1058
const SbViewportRegion & vp = viewer->getSoRenderManager()->getViewportRegion();
1059
const SbVec2s size(vp.getViewportSizePixels());
1060
return {(float) pixpos[0] / (float) std::max((int)(size[0] - 1), 1),
1061
(float) pixpos[1] / (float) std::max((int)(size[1] - 1), 1)};
1064
SbVec2f NavigationStyle::normalizePixelPos(SbVec2f pixpos)
1066
const SbViewportRegion & vp = viewer->getSoRenderManager()->getViewportRegion();
1067
const SbVec2s size(vp.getViewportSizePixels());
1068
return {pixpos[0] / (float) std::max((int)(size[0] - 1), 1),
1069
pixpos[1] / (float) std::max((int)(size[1] - 1), 1)};
1072
void NavigationStyle::moveCursorPosition()
1074
if (!isResetCursorPosition())
1077
QPoint cpos = QCursor::pos();
1078
if (abs(cpos.x()-globalPos[0]) > 10 ||
1079
abs(cpos.y()-globalPos[1]) > 10) {
1080
QCursor::setPos(globalPos[0], globalPos[1]-1);
1081
this->log.position[0] = localPos;
1086
SbBool NavigationStyle::handleEventInForeground(const SoEvent* const e)
1088
SoHandleEventAction action(viewer->getSoRenderManager()->getViewportRegion());
1090
action.setPickRadius(viewer->getPickRadius());
1091
action.apply(viewer->foregroundroot);
1092
return action.isHandled();
1096
* @brief Decide if it should be possible to start any animation
1098
* If the enable flag is false and we're currently animating, the animation will be stopped
1100
void NavigationStyle::setAnimationEnabled(const SbBool enable)
1102
animationEnabled = enable;
1103
if (!enable && isAnimating()) {
1109
* @brief Decide if it should be possible to start a spin animation of the model in the viewer by releasing the mouse button while dragging
1111
* If the enable flag is false and we're currently animating, the spin animation will be stopped
1113
void NavigationStyle::setSpinningAnimationEnabled(const SbBool enable)
1115
spinningAnimationEnabled = enable;
1116
if (!enable && isSpinning()) {
1122
* @return Whether or not it is possible to start any animation
1124
SbBool NavigationStyle::isAnimationEnabled() const
1126
return animationEnabled;
1130
* @return Whether or not it is possible to start a spinning animation e.g. after dragging
1132
SbBool NavigationStyle::isSpinningAnimationEnabled() const
1134
return animationEnabled && spinningAnimationEnabled;
1138
* @return Whether or not any animation is currently active
1140
SbBool NavigationStyle::isAnimating() const
1142
return animator->isAnimating();
1146
* @return Whether or not a spinning animation is currently active e.g. after a user drag
1148
SbBool NavigationStyle::isSpinning() const
1150
return currentmode == NavigationStyle::SPINNING;
1153
void NavigationStyle::startAnimating(const std::shared_ptr<NavigationAnimation>& animation, bool wait) const
1156
animator->startAndWait(animation);
1159
animator->start(animation);
1163
void NavigationStyle::stopAnimating() const
1168
void NavigationStyle::setSensitivity(float val)
1170
this->sensitivity = val;
1173
float NavigationStyle::getSensitivity() const
1175
return this->sensitivity;
1178
void NavigationStyle::setResetCursorPosition(SbBool on)
1180
this->resetcursorpos = on;
1183
SbBool NavigationStyle::isResetCursorPosition() const
1185
return this->resetcursorpos;
1188
void NavigationStyle::setZoomInverted(SbBool on)
1190
this->invertZoom = on;
1193
SbBool NavigationStyle::isZoomInverted() const
1195
return this->invertZoom;
1198
void NavigationStyle::setZoomStep(float val)
1200
this->zoomStep = val;
1203
void NavigationStyle::setZoomAtCursor(SbBool on)
1205
this->zoomAtCursor = on;
1208
SbBool NavigationStyle::isZoomAtCursor() const
1210
return this->zoomAtCursor;
1213
void NavigationStyle::setRotationCenterMode(NavigationStyle::RotationCenterModes mode)
1215
this->rotationCenterMode = mode;
1218
NavigationStyle::RotationCenterModes NavigationStyle::getRotationCenterMode() const
1220
return this->rotationCenterMode;
1223
void NavigationStyle::startSelection(AbstractMouseSelection* mouse)
1228
if (mouseSelection) {
1229
SoDebugError::postWarning("NavigationStyle::startSelection",
1230
"Set new mouse selection while an old is still active.");
1233
mouseSelection = mouse;
1234
mouseSelection->grabMouseModel(viewer);
1237
void NavigationStyle::startSelection(NavigationStyle::SelectionMode mode)
1247
mouseSelection = new PolyPickerSelection();
1250
mouseSelection = new RectangleSelection();
1253
mouseSelection = new RubberbandSelection();
1256
mouseSelection = new BoxZoomSelection();
1259
mouseSelection = new PolyClipSelection();
1266
mouseSelection->grabMouseModel(viewer);
1269
void NavigationStyle::abortSelection()
1272
if (mouseSelection) {
1273
mouseSelection->releaseMouseModel(true);
1274
delete mouseSelection;
1275
mouseSelection = nullptr;
1279
void NavigationStyle::stopSelection()
1282
if (mouseSelection) {
1283
mouseSelection->releaseMouseModel();
1284
delete mouseSelection;
1285
mouseSelection = nullptr;
1289
SbBool NavigationStyle::isSelecting() const
1291
return (mouseSelection ? true : false);
1294
const std::vector<SbVec2s>& NavigationStyle::getPolygon(SelectionRole* role) const
1297
*role = this->selectedRole;
1301
// This method adds another point to the mouse location log, used for spin
1302
// animation calculations.
1303
void NavigationStyle::addToLog(const SbVec2s pos, const SbTime time)
1305
// In case someone changes the const size setting at the top of this
1307
assert (this->log.size > 2 && "mouse log too small!");
1309
if (this->log.historysize > 0 && pos == this->log.position[0]) {
1313
int lastidx = this->log.historysize;
1314
// If we've filled up the log, we should throw away the last item:
1315
if (lastidx == this->log.size) { lastidx--; }
1317
assert(lastidx < this->log.size);
1318
for (int i = lastidx; i > 0; i--) {
1319
this->log.position[i] = this->log.position[i-1];
1320
this->log.time[i] = this->log.time[i-1];
1323
this->log.position[0] = pos;
1324
this->log.time[0] = time;
1325
if (this->log.historysize < this->log.size)
1326
this->log.historysize += 1;
1329
// This method "clears" the mouse location log, used for spin
1330
// animation calculations.
1331
void NavigationStyle::clearLog()
1333
this->log.historysize = 0;
1336
void NavigationStyle::syncModifierKeys(const SoEvent * const ev)
1338
// Mismatches in state of the modifier keys happens if the user
1339
// presses or releases them outside the viewer window.
1340
if (this->ctrldown != ev->wasCtrlDown()) {
1341
this->ctrldown = ev->wasCtrlDown();
1343
if (this->shiftdown != ev->wasShiftDown()) {
1344
this->shiftdown = ev->wasShiftDown();
1346
if (this->altdown != ev->wasAltDown()) {
1347
this->altdown = ev->wasAltDown();
1351
// The viewer is a state machine, and all changes to the current state
1352
// are made through this call.
1353
void NavigationStyle::setViewingMode(const ViewerMode newmode)
1355
const ViewerMode oldmode = this->currentmode;
1356
if (newmode == oldmode) {
1360
if (newmode == NavigationStyle::IDLE) {
1368
// Set up initial projection point for the projector object when
1369
// first starting a drag operation.
1371
viewer->showRotationCenter(true);
1372
findBoundingSphere();
1373
this->spinprojector->project(this->lastmouseposition);
1374
this->interactiveCountInc();
1379
this->interactiveCountInc();
1380
viewer->getSoRenderManager()->scheduleRedraw();
1385
pan(viewer->getSoRenderManager()->getCamera());
1386
this->interactiveCountInc();
1391
this->interactiveCountInc();
1396
this->interactiveCountInc();
1399
default: // include default to avoid compiler warnings.
1406
viewer->showRotationCenter(false);
1411
this->interactiveCountDec();
1418
viewer->setCursorRepresentation(newmode);
1419
this->currentmode = newmode;
1422
int NavigationStyle::getViewingMode() const
1424
return (int)this->currentmode;
1427
SbBool NavigationStyle::processEvent(const SoEvent * const ev)
1429
// If we're in picking mode then all events must be redirected to the
1430
// appropriate mouse model.
1431
if (mouseSelection) {
1432
int hd=mouseSelection->handleEvent(ev,viewer->getSoRenderManager()->getViewportRegion());
1433
if (hd==AbstractMouseSelection::Continue||
1434
hd==AbstractMouseSelection::Restart) {
1437
else if (hd==AbstractMouseSelection::Finish) {
1438
pcPolygon = mouseSelection->getPositions();
1439
selectedRole = mouseSelection->selectedRole();
1440
delete mouseSelection;
1441
mouseSelection = nullptr;
1443
return NavigationStyle::processSoEvent(ev);
1445
else if (hd==AbstractMouseSelection::Cancel) {
1447
delete mouseSelection;
1448
mouseSelection = nullptr;
1450
return NavigationStyle::processSoEvent(ev);
1454
const ViewerMode curmode = this->currentmode;
1456
SbBool processed = false;
1457
processed = this->processSoEvent(ev);
1459
// check for left click without selecting something
1460
if ((curmode == NavigationStyle::SELECTION || curmode == NavigationStyle::IDLE)
1462
if (SoMouseButtonEvent::isButtonReleaseEvent(ev, SoMouseButtonEvent::BUTTON1)) {
1463
if (!ev->wasCtrlDown()) {
1464
Gui::Selection().clearSelection();
1472
SbBool NavigationStyle::processSoEvent(const SoEvent * const ev)
1474
bool processed = false;
1475
bool offeredtoViewerEventBase = false;
1477
//handle mouse wheel zoom
1478
if (ev->isOfType(SoMouseWheelEvent::getClassTypeId())) {
1479
auto const event = static_cast<const SoMouseWheelEvent *>(ev);
1480
processed = processWheelEvent(event);
1481
viewer->processSoEventBase(ev);
1482
offeredtoViewerEventBase = true;
1485
if (!processed && !offeredtoViewerEventBase) {
1486
processed = viewer->processSoEventBase(ev);
1492
void NavigationStyle::syncWithEvent(const SoEvent * const ev)
1494
// Events when in "ready-to-seek" mode are ignored, except those
1495
// which influence the seek mode itself -- these are handled further
1496
// up the inheritance hierarchy.
1497
if (this->isSeekMode()) {
1501
const SoType type(ev->getTypeId());
1503
// Mismatches in state of the modifier keys happens if the user
1504
// presses or releases them outside the viewer window.
1505
syncModifierKeys(ev);
1507
// Keyboard handling
1508
if (type.isDerivedFrom(SoKeyboardEvent::getClassTypeId())) {
1509
auto const event = static_cast<const SoKeyboardEvent *>(ev);
1510
const SbBool press = event->getState() == SoButtonEvent::DOWN ? true : false;
1511
switch (event->getKey()) {
1512
case SoKeyboardEvent::LEFT_CONTROL:
1513
case SoKeyboardEvent::RIGHT_CONTROL:
1514
this->ctrldown = press;
1516
case SoKeyboardEvent::LEFT_SHIFT:
1517
case SoKeyboardEvent::RIGHT_SHIFT:
1518
this->shiftdown = press;
1520
case SoKeyboardEvent::LEFT_ALT:
1521
case SoKeyboardEvent::RIGHT_ALT:
1522
this->altdown = press;
1529
// Mouse Button / Spaceball Button handling
1530
if (type.isDerivedFrom(SoMouseButtonEvent::getClassTypeId())) {
1531
auto const event = static_cast<const SoMouseButtonEvent *>(ev);
1532
const int button = event->getButton();
1533
const SbBool press = event->getState() == SoButtonEvent::DOWN ? true : false;
1535
// SoDebugError::postInfo("processSoEvent", "button = %d", button);
1537
case SoMouseButtonEvent::BUTTON1:
1538
this->button1down = press;
1540
case SoMouseButtonEvent::BUTTON2:
1541
this->button2down = press;
1543
case SoMouseButtonEvent::BUTTON3:
1544
this->button3down = press;
1552
SbBool NavigationStyle::processMotionEvent(const SoMotion3Event * const ev)
1554
SoCamera * const camera = viewer->getSoRenderManager()->getCamera();
1558
SbViewVolume volume(camera->getViewVolume());
1559
SbVec3f center(volume.getSightPoint(camera->focalDistance.getValue()));
1560
float scale(volume.getWorldToScreenScale(center, 1.0));
1561
float translationFactor = scale * .0001;
1563
SbVec3f dir = ev->getTranslation();
1565
if (camera->getTypeId().isDerivedFrom(SoOrthographicCamera::getClassTypeId())){
1566
auto oCam = static_cast<SoOrthographicCamera *>(camera);
1567
oCam->scaleHeight(1.0 + (dir[2] * 0.0001));
1568
dir[2] = 0.0;//don't move the cam for z translation.
1571
SbRotation newRotation(ev->getRotation() * camera->orientation.getValue());
1572
SbVec3f newPosition, newDirection;
1573
newRotation.multVec(SbVec3f(0.0, 0.0, -1.0), newDirection);
1574
newPosition = center - (newDirection * camera->focalDistance.getValue());
1576
camera->orientation.setValue(newRotation);
1577
camera->orientation.getValue().multVec(dir,dir);
1578
camera->position = newPosition + (dir * translationFactor);
1583
SbBool NavigationStyle::processKeyboardEvent(const SoKeyboardEvent * const event)
1585
SbBool processed = false;
1586
const SbBool press = event->getState() == SoButtonEvent::DOWN ? true : false;
1587
switch (event->getKey()) {
1588
case SoKeyboardEvent::LEFT_CONTROL:
1589
case SoKeyboardEvent::RIGHT_CONTROL:
1590
this->ctrldown = press;
1592
case SoKeyboardEvent::LEFT_SHIFT:
1593
case SoKeyboardEvent::RIGHT_SHIFT:
1594
this->shiftdown = press;
1596
case SoKeyboardEvent::LEFT_ALT:
1597
case SoKeyboardEvent::RIGHT_ALT:
1598
this->altdown = press;
1600
case SoKeyboardEvent::S:
1601
case SoKeyboardEvent::HOME:
1602
case SoKeyboardEvent::LEFT_ARROW:
1603
case SoKeyboardEvent::UP_ARROW:
1604
case SoKeyboardEvent::RIGHT_ARROW:
1605
case SoKeyboardEvent::DOWN_ARROW:
1606
if (!this->isViewing())
1607
this->setViewing(true);
1609
case SoKeyboardEvent::PAGE_UP:
1612
const SbVec2f posn = normalizePixelPos(event->getPosition());
1613
doZoom(viewer->getSoRenderManager()->getCamera(), getDelta(), posn);
1616
case SoKeyboardEvent::PAGE_DOWN:
1619
const SbVec2f posn = normalizePixelPos(event->getPosition());
1620
doZoom(viewer->getSoRenderManager()->getCamera(), -getDelta(), posn);
1630
SbBool NavigationStyle::processClickEvent(const SoMouseButtonEvent * const event)
1632
// issue #0002433: avoid to swallow the UP event if down the
1633
// scene graph somewhere a dialog gets opened
1634
SbBool processed = false;
1635
const SbBool press = event->getState() == SoButtonEvent::DOWN ? true : false;
1637
SbTime tmp = (event->getTime() - mouseDownConsumedEvent.getTime());
1638
float dci = (float)QApplication::doubleClickInterval()/1000.0f;
1640
if (tmp.getValue() < dci) {
1641
mouseDownConsumedEvent = *event;
1642
mouseDownConsumedEvent.setTime(event->getTime());
1646
mouseDownConsumedEvent.setTime(event->getTime());
1647
// 'ANY' is used to mark that we don't know yet if it will
1648
// be a double-click event.
1649
mouseDownConsumedEvent.setButton(SoMouseButtonEvent::ANY);
1653
if (mouseDownConsumedEvent.getButton() == SoMouseButtonEvent::BUTTON1) {
1654
// now handle the postponed event
1655
NavigationStyle::processSoEvent(&mouseDownConsumedEvent);
1656
mouseDownConsumedEvent.setButton(SoMouseButtonEvent::ANY);
1663
SbBool NavigationStyle::processWheelEvent(const SoMouseWheelEvent * const event)
1665
const SbVec2s pos(event->getPosition());
1666
const SbVec2f posn = normalizePixelPos(pos);
1668
//handle mouse wheel zoom
1669
doZoom(viewer->getSoRenderManager()->getCamera(),
1670
event->getDelta(), posn);
1674
void NavigationStyle::setPopupMenuEnabled(const SbBool on)
1676
this->menuenabled = on;
1679
SbBool NavigationStyle::isPopupMenuEnabled() const
1681
return this->menuenabled;
1684
void NavigationStyle::openPopupMenu(const SbVec2s& position)
1687
// ask workbenches and view provider, ...
1688
auto view = new MenuItem;
1689
Gui::Application::Instance->setupContextMenu("View", view);
1691
QMenu contextMenu(viewer->getGLWidget());
1693
QActionGroup subMenuGroup(&subMenu);
1694
subMenuGroup.setExclusive(true);
1695
subMenu.setTitle(QObject::tr("Navigation styles"));
1697
MenuManager::getInstance()->setupContextMenu(view, contextMenu);
1698
contextMenu.addMenu(&subMenu);
1700
// add submenu at the end to select navigation style
1701
std::map<Base::Type, std::string> styles = UserNavigationStyle::getUserFriendlyNames();
1702
for (const auto & style : styles) {
1703
QByteArray data(style.first.getName());
1704
QString name = QApplication::translate(style.first.getName(), style.second.c_str());
1706
QAction* item = subMenuGroup.addAction(name);
1707
item->setData(data);
1708
item->setCheckable(true);
1709
if (style.first == this->getTypeId())
1710
item->setChecked(true);
1711
subMenu.addAction(item);
1715
QAction* used = contextMenu.exec(QCursor::pos());
1716
if (used && subMenuGroup.actions().indexOf(used) >= 0 && used->isChecked()) {
1717
QByteArray type = used->data().toByteArray();
1718
QWidget* widget = viewer->getWidget();
1719
while (widget && !widget->inherits("Gui::View3DInventor"))
1720
widget = widget->parentWidget();
1722
// this is the widget where the viewer is embedded
1723
Base::Type style = Base::Type::fromName((const char*)type);
1724
if (style != this->getTypeId()) {
1725
QEvent* event = new NavigationStyleEvent(style);
1726
QApplication::postEvent(widget, event);
1732
// ----------------------------------------------------------------------------------
1734
TYPESYSTEM_SOURCE_ABSTRACT(Gui::UserNavigationStyle,Gui::NavigationStyle)
1736
std::string UserNavigationStyle::userFriendlyName() const
1738
std::string name = this->getTypeId().getName();
1739
// remove namespaces
1740
std::size_t pos = name.rfind("::");
1741
if (pos != std::string::npos)
1742
name = name.substr(pos + 2);
1744
// remove 'NavigationStyle'
1745
pos = name.find("NavigationStyle");
1746
if (pos != std::string::npos)
1747
name = name.substr(0, pos);
1751
std::map<Base::Type, std::string> UserNavigationStyle::getUserFriendlyNames()
1753
std::map<Base::Type, std::string> names;
1754
std::vector<Base::Type> types;
1755
Base::Type::getAllDerivedFrom(UserNavigationStyle::getClassTypeId(), types);
1757
for (auto & type : types) {
1758
if (type != UserNavigationStyle::getClassTypeId()) {
1759
std::unique_ptr<UserNavigationStyle> inst(static_cast<UserNavigationStyle*>(type.createInstance()));
1761
names[type] = inst->userFriendlyName();