1282 lines
42 KiB
C++
1282 lines
42 KiB
C++
/***************************************************************************
|
|
* Copyright (c) 2017 Kustaa Nyholm <kustaa.nyholm@sparetimelabs.com> *
|
|
* *
|
|
* 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 *
|
|
* *
|
|
***************************************************************************/
|
|
|
|
#include "PreCompiled.h"
|
|
#ifndef _PreComp_
|
|
# include <algorithm>
|
|
# include <cfloat>
|
|
# ifdef FC_OS_WIN32
|
|
# include <windows.h>
|
|
# endif
|
|
# ifdef FC_OS_MACOSX
|
|
# include <OpenGL/gl.h>
|
|
# else
|
|
# include <GL/gl.h>
|
|
# endif
|
|
# include <boost/math/constants/constants.hpp>
|
|
# include <Inventor/nodes/SoOrthographicCamera.h>
|
|
# include <Inventor/events/SoEvent.h>
|
|
# include <Inventor/events/SoLocation2Event.h>
|
|
# include <Inventor/events/SoMouseButtonEvent.h>
|
|
# include <QApplication>
|
|
# include <QCursor>
|
|
# include <QImage>
|
|
# include <QMenu>
|
|
# include <QOpenGLFramebufferObject>
|
|
# include <QOpenGLTexture>
|
|
# include <QOpenGLWidget>
|
|
# include <QPainterPath>
|
|
#endif
|
|
|
|
#include <App/Color.h>
|
|
#include <Base/Tools.h>
|
|
#include <Eigen/Dense>
|
|
|
|
#include "NaviCube.h"
|
|
#include "Application.h"
|
|
#include "Command.h"
|
|
#include "Action.h"
|
|
#include "MainWindow.h"
|
|
#include "Navigation/NavigationAnimation.h"
|
|
#include "View3DInventorViewer.h"
|
|
#include "View3DInventor.h"
|
|
#include "ViewParams.h"
|
|
|
|
|
|
using namespace Eigen;
|
|
using namespace std;
|
|
using namespace Gui;
|
|
|
|
class NaviCubeImplementation {
|
|
public:
|
|
explicit NaviCubeImplementation(Gui::View3DInventorViewer*);
|
|
~NaviCubeImplementation();
|
|
void drawNaviCube();
|
|
void createContextMenu(const std::vector<std::string>& cmd);
|
|
void createCubeFaceTextures();
|
|
|
|
void moveToCorner(NaviCube::Corner c);
|
|
void setLabels(const std::vector<std::string>& labels);
|
|
|
|
bool processSoEvent(const SoEvent* ev);
|
|
void setSize(int size);
|
|
|
|
private:
|
|
enum class PickId {
|
|
None,
|
|
Front,
|
|
Top,
|
|
Right,
|
|
Rear,
|
|
Bottom,
|
|
Left,
|
|
FrontTop,
|
|
FrontBottom,
|
|
FrontRight,
|
|
FrontLeft,
|
|
RearTop,
|
|
RearBottom,
|
|
RearRight,
|
|
RearLeft,
|
|
TopRight,
|
|
TopLeft,
|
|
BottomRight,
|
|
BottomLeft,
|
|
FrontTopRight,
|
|
FrontTopLeft,
|
|
FrontBottomRight,
|
|
FrontBottomLeft,
|
|
RearTopRight,
|
|
RearTopLeft,
|
|
RearBottomRight,
|
|
RearBottomLeft,
|
|
ArrowNorth,
|
|
ArrowSouth,
|
|
ArrowEast,
|
|
ArrowWest,
|
|
ArrowRight,
|
|
ArrowLeft,
|
|
DotBackside,
|
|
ViewMenu
|
|
};
|
|
enum class DirId{
|
|
Custom, Up, Right, Out
|
|
};
|
|
enum class ShapeId{
|
|
None, Main, Edge, Corner, Button
|
|
};
|
|
struct Face {
|
|
ShapeId type;
|
|
vector<Vector3f> vertexArray;
|
|
// The rotation is the standard orientation for the faces of the cube
|
|
// For the flat buttons the rotation contains the direction of the rotation
|
|
// The standard orientation is the desired camera orientation when a face is selected and
|
|
// rotate to nearest is disabled
|
|
SbRotation rotation;
|
|
};
|
|
struct LabelTexture {
|
|
vector<Vector3f> vertexArray;
|
|
qreal fontSize;
|
|
QOpenGLTexture *texture = nullptr;
|
|
string label;
|
|
};
|
|
bool mousePressed(short x, short y);
|
|
bool mouseReleased(short x, short y);
|
|
bool mouseMoved(short x, short y);
|
|
PickId pickFace(short x, short y);
|
|
bool inDragZone(short x, short y);
|
|
|
|
void prepare();
|
|
void handleResize();
|
|
void handleMenu();
|
|
|
|
void setHilite(PickId);
|
|
|
|
void addCubeFace(const Vector3f&, const Vector3f&, ShapeId, PickId, float rotZ = 0.0);
|
|
void addButtonFace(PickId, const SbVec3f& direction = SbVec3f(0, 0, 0));
|
|
|
|
QString str(const char* str);
|
|
QMenu* createNaviCubeMenu();
|
|
void drawNaviCube(bool picking, float opacity);
|
|
|
|
SbRotation getNearestOrientation(PickId pickId);
|
|
|
|
public:
|
|
|
|
static int m_CubeWidgetSize;
|
|
QColor m_BaseColor;
|
|
QColor m_EmphaseColor;
|
|
QColor m_HiliteColor;
|
|
bool m_ShowCS = true;
|
|
PickId m_HiliteId = PickId::None;
|
|
double m_BorderWidth = 1.1;
|
|
bool m_RotateToNearest = true;
|
|
int m_NaviStepByTurn = 8;
|
|
float m_FontZoom = 0.3F;
|
|
float m_Chamfer = 0.12F;
|
|
std::string m_TextFont;
|
|
int m_FontWeight = 0;
|
|
int m_FontStretch = 0;
|
|
float m_InactiveOpacity = 0.5;
|
|
SbVec2s m_PosOffset = SbVec2s(0,0);
|
|
|
|
App::Color m_xColor;
|
|
App::Color m_yColor;
|
|
App::Color m_zColor;
|
|
|
|
bool m_Prepared = false;
|
|
static vector<string> m_commands;
|
|
bool m_Draggable = false;
|
|
SbVec2s m_ViewSize = SbVec2s(0,0);
|
|
|
|
private:
|
|
bool m_MouseDown = false;
|
|
bool m_Dragging = false;
|
|
bool m_MightDrag = false;
|
|
bool m_Hovering = false;
|
|
|
|
SbVec2f m_RelPos = SbVec2f(1.0f,1.0f);
|
|
SbVec2s m_PosAreaBase = SbVec2s(0,0);
|
|
SbVec2s m_PosAreaSize = SbVec2s(0,0);
|
|
|
|
QOpenGLFramebufferObject* m_PickingFramebuffer;
|
|
Gui::View3DInventorViewer* m_View3DInventorViewer;
|
|
|
|
map<PickId, Face> m_Faces;
|
|
map<PickId, LabelTexture> m_LabelTextures;
|
|
|
|
QMenu* m_Menu;
|
|
|
|
std::shared_ptr<NavigationAnimation> m_flatButtonAnimation;
|
|
SbRotation m_flatButtonTargetOrientation;
|
|
};
|
|
|
|
int NaviCubeImplementation::m_CubeWidgetSize = 132;
|
|
|
|
int NaviCube::getNaviCubeSize()
|
|
{
|
|
return NaviCubeImplementation::m_CubeWidgetSize;
|
|
}
|
|
|
|
NaviCube::NaviCube(Gui::View3DInventorViewer* viewer) {
|
|
m_NaviCubeImplementation = new NaviCubeImplementation(viewer);
|
|
updateColors();
|
|
}
|
|
|
|
NaviCube::~NaviCube() {
|
|
delete m_NaviCubeImplementation;
|
|
}
|
|
|
|
void NaviCube::drawNaviCube() {
|
|
m_NaviCubeImplementation->drawNaviCube();
|
|
}
|
|
|
|
void NaviCube::createContextMenu(const std::vector<std::string>& cmd) {
|
|
m_NaviCubeImplementation->createContextMenu(cmd);
|
|
}
|
|
|
|
bool NaviCube::processSoEvent(const SoEvent* ev) {
|
|
return m_NaviCubeImplementation->processSoEvent(ev);
|
|
}
|
|
|
|
vector<string> NaviCubeImplementation::m_commands;
|
|
|
|
void NaviCube::setCorner(Corner c) {
|
|
m_NaviCubeImplementation->moveToCorner(c);
|
|
}
|
|
|
|
void NaviCube::setOffset(int x, int y) {
|
|
m_NaviCubeImplementation->m_PosOffset = SbVec2s(x, y);
|
|
m_NaviCubeImplementation->m_ViewSize = SbVec2s(0,0);
|
|
}
|
|
|
|
bool NaviCube::isDraggable() {
|
|
return m_NaviCubeImplementation->m_Draggable;
|
|
}
|
|
|
|
void NaviCube::setDraggable(bool draggable) {
|
|
m_NaviCubeImplementation->m_Draggable = draggable;
|
|
}
|
|
|
|
void NaviCube::setSize(int size)
|
|
{
|
|
m_NaviCubeImplementation->setSize(size);
|
|
}
|
|
|
|
void NaviCube::setChamfer(float chamfer)
|
|
{
|
|
m_NaviCubeImplementation->m_Chamfer = min(max(0.05f, chamfer), 0.18f);
|
|
m_NaviCubeImplementation->m_Prepared = false;
|
|
}
|
|
|
|
void NaviCube::setNaviRotateToNearest(bool toNearest)
|
|
{
|
|
m_NaviCubeImplementation->m_RotateToNearest = toNearest;
|
|
}
|
|
|
|
void NaviCube::setNaviStepByTurn(int steps)
|
|
{
|
|
m_NaviCubeImplementation->m_NaviStepByTurn = steps;
|
|
}
|
|
|
|
void NaviCube::setFont(std::string font)
|
|
{
|
|
m_NaviCubeImplementation->m_TextFont = font;
|
|
m_NaviCubeImplementation->m_Prepared = false;
|
|
}
|
|
|
|
void NaviCube::setFontWeight(int weight)
|
|
{
|
|
m_NaviCubeImplementation->m_FontWeight = weight;
|
|
m_NaviCubeImplementation->m_Prepared = false;
|
|
}
|
|
|
|
void NaviCube::setFontStretch(int stretch)
|
|
{
|
|
m_NaviCubeImplementation->m_FontStretch = stretch;
|
|
m_NaviCubeImplementation->m_Prepared = false;
|
|
}
|
|
|
|
void NaviCube::setFontZoom(float zoom)
|
|
{
|
|
m_NaviCubeImplementation->m_FontZoom = zoom;
|
|
m_NaviCubeImplementation->m_Prepared = false;
|
|
}
|
|
|
|
void NaviCube::setBaseColor(QColor bColor)
|
|
{
|
|
m_NaviCubeImplementation->m_BaseColor = bColor;
|
|
}
|
|
|
|
void NaviCube::setEmphaseColor(QColor eColor)
|
|
{
|
|
m_NaviCubeImplementation->m_EmphaseColor = eColor;
|
|
m_NaviCubeImplementation->m_Prepared = false;
|
|
}
|
|
|
|
void NaviCube::setHiliteColor(QColor HiliteColor)
|
|
{
|
|
m_NaviCubeImplementation->m_HiliteColor = HiliteColor;
|
|
}
|
|
|
|
void NaviCube::setBorderWidth(double BorderWidth)
|
|
{
|
|
m_NaviCubeImplementation->m_BorderWidth = BorderWidth;
|
|
}
|
|
|
|
void NaviCube::setShowCS(bool showCS)
|
|
{
|
|
m_NaviCubeImplementation->m_ShowCS = showCS;
|
|
}
|
|
|
|
void NaviCube::setNaviCubeLabels(const std::vector<std::string>& labels)
|
|
{
|
|
m_NaviCubeImplementation->setLabels(labels);
|
|
}
|
|
|
|
void NaviCube::setInactiveOpacity(float opacity)
|
|
{
|
|
m_NaviCubeImplementation->m_InactiveOpacity = opacity;
|
|
}
|
|
|
|
void NaviCubeImplementation::setLabels(const std::vector<std::string>& labels)
|
|
{
|
|
m_LabelTextures[PickId::Front].label = labels[0];
|
|
m_LabelTextures[PickId::Top].label = labels[1];
|
|
m_LabelTextures[PickId::Right].label = labels[2];
|
|
m_LabelTextures[PickId::Rear].label = labels[3];
|
|
m_LabelTextures[PickId::Bottom].label = labels[4];
|
|
m_LabelTextures[PickId::Left].label = labels[5];
|
|
m_Prepared = false;
|
|
}
|
|
|
|
NaviCubeImplementation::NaviCubeImplementation(Gui::View3DInventorViewer* viewer)
|
|
: m_BaseColor{226, 232, 239}
|
|
, m_HiliteColor{170, 226, 255}
|
|
{
|
|
m_View3DInventorViewer = viewer;
|
|
m_PickingFramebuffer = nullptr;
|
|
m_Menu = createNaviCubeMenu();
|
|
}
|
|
|
|
NaviCubeImplementation::~NaviCubeImplementation()
|
|
{
|
|
delete m_Menu;
|
|
if (m_PickingFramebuffer)
|
|
delete m_PickingFramebuffer;
|
|
for (auto tex: m_LabelTextures) {
|
|
delete tex.second.texture;
|
|
}
|
|
}
|
|
|
|
void NaviCubeImplementation::moveToCorner(NaviCube::Corner c) {
|
|
if (c == NaviCube::TopLeftCorner) m_RelPos = SbVec2f(0.0f, 1.0f);
|
|
else if (c == NaviCube::TopRightCorner) m_RelPos = SbVec2f(1.0f, 1.0f);
|
|
else if (c == NaviCube::BottomLeftCorner) m_RelPos = SbVec2f(0.0f, 0.0f);
|
|
else if (c == NaviCube::BottomRightCorner) m_RelPos = SbVec2f(1.0f, 0.0f);
|
|
}
|
|
|
|
auto convertWeights = [](int weight) -> QFont::Weight {
|
|
if (weight >= 87)
|
|
return QFont::Black;
|
|
if (weight >= 81)
|
|
return QFont::ExtraBold;
|
|
if (weight >= 75)
|
|
return QFont::Bold;
|
|
if (weight >= 63)
|
|
return QFont::DemiBold;
|
|
if (weight >= 57)
|
|
return QFont::Medium;
|
|
if (weight >= 50)
|
|
return QFont::Normal;
|
|
if (weight >= 25)
|
|
return QFont::Light;
|
|
if (weight >= 12)
|
|
return QFont::ExtraLight;
|
|
return QFont::Thin;
|
|
};
|
|
|
|
int imageVerticalBalance(QImage p, int sizeHint) {
|
|
if (sizeHint < 0) {
|
|
return 0;
|
|
}
|
|
|
|
int h = p.height();
|
|
int startRow = (h - sizeHint) / 2;
|
|
bool done = false;
|
|
int x, bottom, top;
|
|
for (top = startRow; top < h; top++){
|
|
for (x = 0; x < p.width(); x++){
|
|
if (qAlpha(p.pixel(x, top))) {
|
|
done = true;
|
|
break;
|
|
}
|
|
}
|
|
if (done) break;
|
|
}
|
|
for (bottom = startRow; bottom < h; bottom++) {
|
|
for (x = 0; x < p.width(); x++){
|
|
if (qAlpha(p.pixel(x, h-1-bottom)))
|
|
return (bottom-top)/2;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void NaviCubeImplementation::createCubeFaceTextures() {
|
|
int texSize = 192; // Works well for the max cube size 1024
|
|
QFont font;
|
|
if (m_TextFont.empty()) font.fromString(QStringLiteral("Arial"));
|
|
else font.fromString(QString::fromStdString(m_TextFont));
|
|
font.setStyleHint(QFont::SansSerif);
|
|
if (m_FontWeight > 0) {
|
|
font.setWeight(convertWeights(m_FontWeight));
|
|
}
|
|
if (m_FontStretch > 0) {
|
|
font.setStretch(m_FontStretch);
|
|
}
|
|
font.setPointSizeF(texSize);
|
|
QFontMetrics fm(font);
|
|
qreal minFontSize = texSize;
|
|
qreal maxFontSize = 0.;
|
|
vector<PickId> mains = {PickId::Front, PickId::Top, PickId::Right, PickId::Rear, PickId::Bottom, PickId::Left};
|
|
for (PickId pickId : mains) {
|
|
auto t = QString::fromUtf8(m_LabelTextures[pickId].label.c_str());
|
|
QRect br = fm.boundingRect(t);
|
|
float scale = (float)texSize / max(br.width(),br.height());
|
|
m_LabelTextures[pickId].fontSize = texSize * scale;
|
|
minFontSize = std::min(minFontSize, m_LabelTextures[pickId].fontSize);
|
|
maxFontSize = std::max(maxFontSize, m_LabelTextures[pickId].fontSize);
|
|
}
|
|
if (m_FontZoom > 0.0)
|
|
maxFontSize = minFontSize + (maxFontSize - minFontSize) * m_FontZoom;
|
|
else {
|
|
maxFontSize = minFontSize * std::pow(2.0, m_FontZoom);
|
|
}
|
|
for (PickId pickId : mains) {
|
|
QImage image(texSize, texSize, QImage::Format_ARGB32);
|
|
image.fill(qRgba(255, 255, 255, 0));
|
|
if (m_LabelTextures[pickId].fontSize > 0.5) {
|
|
// 5% margin looks nice and prevents some artifacts
|
|
font.setPointSizeF(std::min(m_LabelTextures[pickId].fontSize, maxFontSize)*0.9);
|
|
QPainter paint;
|
|
paint.begin(&image);
|
|
paint.setRenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing | QPainter::SmoothPixmapTransform);
|
|
paint.setPen(Qt::white);
|
|
QString text = QString::fromUtf8(m_LabelTextures[pickId].label.c_str());
|
|
paint.setFont(font);
|
|
paint.drawText(QRect(0, 0, texSize, texSize), Qt::AlignCenter, text);
|
|
int offset = imageVerticalBalance(image, font.pointSize());
|
|
image.fill(qRgba(255, 255, 255, 0));
|
|
paint.drawText(QRect(0, offset, texSize, texSize), Qt::AlignCenter, text);
|
|
paint.end();
|
|
}
|
|
|
|
if (m_LabelTextures[pickId].texture) {
|
|
delete m_LabelTextures[pickId].texture;
|
|
}
|
|
m_LabelTextures[pickId].texture = new QOpenGLTexture(image.mirrored());
|
|
m_LabelTextures[pickId].texture->setMaximumAnisotropy(4.0);
|
|
m_LabelTextures[pickId].texture->setMinificationFilter(QOpenGLTexture::LinearMipMapLinear);
|
|
m_LabelTextures[pickId].texture->setMagnificationFilter(QOpenGLTexture::Linear);
|
|
m_LabelTextures[pickId].texture->generateMipMaps();
|
|
}
|
|
}
|
|
|
|
void NaviCubeImplementation::addButtonFace(PickId pickId, const SbVec3f& direction)
|
|
{
|
|
if (m_Faces[pickId].vertexArray.size())
|
|
m_Faces[pickId].vertexArray.clear();
|
|
float scale = 0.005F;
|
|
float offx = 0.5F;
|
|
float offy = 0.5F;
|
|
vector<float> pointData;
|
|
|
|
switch (pickId) {
|
|
default:
|
|
break;
|
|
case PickId::ArrowRight:
|
|
case PickId::ArrowLeft: {
|
|
pointData = {
|
|
66.6F, -66.6F,//outer curve
|
|
58.3F, -74.0F,
|
|
49.2F ,-80.3F,
|
|
39.4F ,-85.5F,
|
|
29.0F, -89.5F,
|
|
25.3F, -78.1F,//inner curve
|
|
34.3F, -74.3F,
|
|
42.8F, -69.9F,
|
|
50.8F, -64.4F,
|
|
58.1F, -58.1F,
|
|
53.8F, -53.8F,//arrowhead
|
|
74.7F, -46.8F,
|
|
70.7F, -70.4F
|
|
};
|
|
break;
|
|
}
|
|
case PickId::ArrowWest:
|
|
case PickId::ArrowNorth:
|
|
case PickId::ArrowSouth:
|
|
case PickId::ArrowEast: {
|
|
pointData = {
|
|
100., 0.,
|
|
80.,-18.,
|
|
80., 18.
|
|
};
|
|
break;
|
|
}
|
|
case PickId::ViewMenu: {
|
|
offx = 0.84F;
|
|
offy = 0.84F;
|
|
pointData = {
|
|
0., 0.,//top rhombus
|
|
15., -6.,
|
|
0., -12.,
|
|
-15., -6.,
|
|
0., 0.,//left rhombus
|
|
-15., -6.,
|
|
-15., 12.,
|
|
0., 18.,
|
|
0., 0.,//right rhombus
|
|
0., 18.,
|
|
15., 12.,
|
|
15., -6.
|
|
};
|
|
break;
|
|
}
|
|
case PickId::DotBackside: {
|
|
int steps = 16;
|
|
for (int i = 0; i < steps; i++) {
|
|
float angle = 2.0f * M_PI * ((float)i+0.5) / (float)steps;
|
|
pointData.emplace_back(10. * cos(angle) + 87.);
|
|
pointData.emplace_back(10. * sin(angle) - 87.);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
int count = static_cast<int>(pointData.size())/2;
|
|
m_Faces[pickId].vertexArray.reserve(count);
|
|
for (int i = 0; i < count; i++) {
|
|
float x = pointData[i*2] * scale + offx;
|
|
float y = pointData[i*2+1] * scale + offy;
|
|
if (pickId == PickId::ArrowNorth || pickId == PickId::ArrowWest || pickId == PickId::ArrowLeft)
|
|
x = 1.0 - x;
|
|
if (pickId == PickId::ArrowSouth || pickId == PickId::ArrowNorth)
|
|
m_Faces[pickId].vertexArray.emplace_back(Vector3f(y, x, 0.0));
|
|
else
|
|
m_Faces[pickId].vertexArray.emplace_back(Vector3f(x, y, 0.0));
|
|
}
|
|
m_Faces[pickId].type = ShapeId::Button;
|
|
m_Faces[pickId].rotation = SbRotation(direction, 1).inverse();
|
|
}
|
|
|
|
void NaviCubeImplementation::addCubeFace(const Vector3f& x, const Vector3f& z, ShapeId shapeType, PickId pickId, float rotZ) {
|
|
m_Faces[pickId].vertexArray.clear();
|
|
m_Faces[pickId].type = shapeType;
|
|
|
|
Vector3f y = x.cross(-z);
|
|
|
|
// Determine the standard orientations based on vector x and vector z
|
|
// Rotate by an additional rotZ if vector x and vector z are not already the standard orientation
|
|
|
|
// Create normalized vectors for x, y and z
|
|
SbVec3f xN(x.x(), x.y(), x.z());
|
|
SbVec3f yN(y.x(), y.y(), y.z());
|
|
SbVec3f zN(z.x(), z.y(), z.z());
|
|
xN.normalize();
|
|
yN.normalize();
|
|
zN.normalize();
|
|
|
|
// Create a rotation matrix
|
|
SbMatrix R(xN[0], yN[0], zN[0], 0,
|
|
xN[1], yN[1], zN[1], 0,
|
|
xN[2], yN[2], zN[2], 0,
|
|
0, 0, 0, 1);
|
|
|
|
// Store the standard orientation
|
|
m_Faces[pickId].rotation = (SbRotation(R) * SbRotation(SbVec3f(0, 0, 1), rotZ)).inverse();
|
|
|
|
if (shapeType == ShapeId::Corner) {
|
|
auto xC = x * m_Chamfer;
|
|
auto yC = y * m_Chamfer;
|
|
auto zC = (1 - 2 * m_Chamfer) * z;
|
|
m_Faces[pickId].vertexArray.reserve(6);
|
|
m_Faces[pickId].vertexArray.emplace_back(zC - 2 * xC);
|
|
m_Faces[pickId].vertexArray.emplace_back(zC - xC - yC);
|
|
m_Faces[pickId].vertexArray.emplace_back(zC + xC - yC);
|
|
m_Faces[pickId].vertexArray.emplace_back(zC + 2 * xC);
|
|
m_Faces[pickId].vertexArray.emplace_back(zC + xC + yC);
|
|
m_Faces[pickId].vertexArray.emplace_back(zC - xC + yC);
|
|
}
|
|
else if (shapeType == ShapeId::Edge) {
|
|
auto x4 = x * (1 - m_Chamfer * 4);
|
|
auto yE = y * m_Chamfer;
|
|
auto zE = z * (1 - m_Chamfer);
|
|
m_Faces[pickId].vertexArray.reserve(4);
|
|
m_Faces[pickId].vertexArray.emplace_back(zE - x4 - yE);
|
|
m_Faces[pickId].vertexArray.emplace_back(zE + x4 - yE);
|
|
m_Faces[pickId].vertexArray.emplace_back(zE + x4 + yE);
|
|
m_Faces[pickId].vertexArray.emplace_back(zE - x4 + yE);
|
|
}
|
|
else if (shapeType == ShapeId::Main) {
|
|
auto x2 = x * (1 - m_Chamfer * 2);
|
|
auto y2 = y * (1 - m_Chamfer * 2);
|
|
auto x4 = x * (1 - m_Chamfer * 4);
|
|
auto y4 = y * (1 - m_Chamfer * 4);
|
|
m_Faces[pickId].vertexArray.reserve(8);
|
|
m_Faces[pickId].vertexArray.emplace_back(z - x2 - y4);
|
|
m_Faces[pickId].vertexArray.emplace_back(z - x4 - y2);
|
|
m_Faces[pickId].vertexArray.emplace_back(z + x4 - y2);
|
|
m_Faces[pickId].vertexArray.emplace_back(z + x2 - y4);
|
|
|
|
m_Faces[pickId].vertexArray.emplace_back(z + x2 + y4);
|
|
m_Faces[pickId].vertexArray.emplace_back(z + x4 + y2);
|
|
m_Faces[pickId].vertexArray.emplace_back(z - x4 + y2);
|
|
m_Faces[pickId].vertexArray.emplace_back(z - x2 + y4);
|
|
|
|
m_LabelTextures[pickId].vertexArray.clear();
|
|
m_LabelTextures[pickId].vertexArray.emplace_back(z - x2 - y2);
|
|
m_LabelTextures[pickId].vertexArray.emplace_back(z + x2 - y2);
|
|
m_LabelTextures[pickId].vertexArray.emplace_back(z + x2 + y2);
|
|
m_LabelTextures[pickId].vertexArray.emplace_back(z - x2 + y2);
|
|
|
|
}
|
|
}
|
|
|
|
void NaviCubeImplementation::setSize(int size)
|
|
{
|
|
m_CubeWidgetSize = size;
|
|
m_ViewSize = SbVec2s(0,0);
|
|
m_Prepared = false;
|
|
}
|
|
|
|
void NaviCubeImplementation::prepare()
|
|
{
|
|
static const float pi = boost::math::constants::pi<float>();
|
|
static const float pi1_2 = boost::math::constants::half_pi<float>();
|
|
|
|
createCubeFaceTextures();
|
|
|
|
Vector3f x(1, 0, 0);
|
|
Vector3f y(0, 1, 0);
|
|
Vector3f z(0, 0, 1);
|
|
|
|
// create the main faces
|
|
addCubeFace( x, z, ShapeId::Main, PickId::Top);
|
|
addCubeFace( x,-y, ShapeId::Main, PickId::Front);
|
|
addCubeFace(-y,-x, ShapeId::Main, PickId::Left);
|
|
addCubeFace(-x, y, ShapeId::Main, PickId::Rear);
|
|
addCubeFace( y, x, ShapeId::Main, PickId::Right);
|
|
addCubeFace( x,-z, ShapeId::Main, PickId::Bottom);
|
|
|
|
// create corner faces
|
|
addCubeFace(-x-y, x-y+z, ShapeId::Corner, PickId::FrontTopRight, pi);
|
|
addCubeFace(-x+y,-x-y+z, ShapeId::Corner, PickId::FrontTopLeft, pi);
|
|
addCubeFace(x+y, x-y-z, ShapeId::Corner, PickId::FrontBottomRight);
|
|
addCubeFace(x-y,-x-y-z, ShapeId::Corner, PickId::FrontBottomLeft);
|
|
addCubeFace(x-y, x+y+z, ShapeId::Corner, PickId::RearTopRight, pi);
|
|
addCubeFace(x+y,-x+y+z, ShapeId::Corner, PickId::RearTopLeft, pi);
|
|
addCubeFace(-x+y, x+y-z, ShapeId::Corner, PickId::RearBottomRight);
|
|
addCubeFace(-x-y,-x+y-z, ShapeId::Corner, PickId::RearBottomLeft);
|
|
|
|
// create edge faces
|
|
addCubeFace(x, z-y, ShapeId::Edge, PickId::FrontTop);
|
|
addCubeFace(x,-z-y, ShapeId::Edge, PickId::FrontBottom);
|
|
addCubeFace(x, y-z, ShapeId::Edge, PickId::RearBottom, pi);
|
|
addCubeFace(x, y+z, ShapeId::Edge, PickId::RearTop, pi);
|
|
addCubeFace(z, x+y, ShapeId::Edge, PickId::RearRight, pi1_2);
|
|
addCubeFace(z, x-y, ShapeId::Edge, PickId::FrontRight, pi1_2);
|
|
addCubeFace(z,-x-y, ShapeId::Edge, PickId::FrontLeft, pi1_2);
|
|
addCubeFace(z, y-x, ShapeId::Edge, PickId::RearLeft, pi1_2);
|
|
addCubeFace(y, z-x, ShapeId::Edge, PickId::TopLeft, pi);
|
|
addCubeFace(y, x+z, ShapeId::Edge, PickId::TopRight);
|
|
addCubeFace(y, x-z, ShapeId::Edge, PickId::BottomRight);
|
|
addCubeFace(y,-z-x, ShapeId::Edge, PickId::BottomLeft, pi);
|
|
|
|
// create the flat buttons
|
|
addButtonFace(PickId::ArrowNorth, SbVec3f(-1, 0, 0));
|
|
addButtonFace(PickId::ArrowSouth, SbVec3f(1, 0, 0));
|
|
addButtonFace(PickId::ArrowEast, SbVec3f(0, 1, 0));
|
|
addButtonFace(PickId::ArrowWest, SbVec3f(0, -1, 0));
|
|
addButtonFace(PickId::ArrowLeft, SbVec3f(0, 0, 1));
|
|
addButtonFace(PickId::ArrowRight, SbVec3f(0, 0, -1));
|
|
addButtonFace(PickId::DotBackside, SbVec3f(0, 1, 0));
|
|
addButtonFace(PickId::ViewMenu);
|
|
|
|
if (m_PickingFramebuffer)
|
|
delete m_PickingFramebuffer;
|
|
m_PickingFramebuffer =
|
|
new QOpenGLFramebufferObject(2 * m_CubeWidgetSize, 2 * m_CubeWidgetSize,
|
|
QOpenGLFramebufferObject::CombinedDepthStencil);
|
|
m_View3DInventorViewer->getSoRenderManager()->scheduleRedraw();
|
|
}
|
|
|
|
void NaviCubeImplementation::drawNaviCube() {
|
|
handleResize();
|
|
int posX = (int)(m_RelPos[0] * m_PosAreaSize[0]) + m_PosAreaBase[0] - m_CubeWidgetSize / 2;
|
|
int posY = (int)(m_RelPos[1] * m_PosAreaSize[1]) + m_PosAreaBase[1] - m_CubeWidgetSize / 2;
|
|
glViewport(posX, posY, m_CubeWidgetSize, m_CubeWidgetSize);
|
|
drawNaviCube(false, m_Hovering ? 1.f : m_InactiveOpacity);
|
|
}
|
|
|
|
void NaviCubeImplementation::createContextMenu(const std::vector<std::string>& cmd) {
|
|
CommandManager& rcCmdMgr = Application::Instance->commandManager();
|
|
m_Menu->clear();
|
|
|
|
for (const auto & i : cmd) {
|
|
Command* cmd = rcCmdMgr.getCommandByName(i.c_str());
|
|
if (cmd)
|
|
cmd->addTo(m_Menu);
|
|
}
|
|
}
|
|
|
|
void NaviCubeImplementation::handleResize() {
|
|
SbVec2s viewSize = m_View3DInventorViewer->getSoRenderManager()->getSize();
|
|
if (viewSize != m_ViewSize) {
|
|
m_PosAreaBase[0] = std::min((int)(m_PosOffset[0] + m_CubeWidgetSize * 0.55), viewSize[0] / 2);
|
|
m_PosAreaBase[1] = std::min((int)(m_PosOffset[1] + m_CubeWidgetSize * 0.55), viewSize[1] / 2);
|
|
m_PosAreaSize[0] = viewSize[0] - 2 * m_PosAreaBase[0];
|
|
m_PosAreaSize[1] = viewSize[1] - 2 * m_PosAreaBase[1];
|
|
m_ViewSize = viewSize;
|
|
}
|
|
}
|
|
|
|
void NaviCubeImplementation::drawNaviCube(bool pickMode, float opacity)
|
|
{
|
|
if (!m_Prepared) {
|
|
if (!m_View3DInventorViewer->viewport())
|
|
return;
|
|
prepare();
|
|
m_Prepared = true;
|
|
m_View3DInventorViewer->getSoRenderManager()->scheduleRedraw();
|
|
return;
|
|
}
|
|
|
|
SoCamera* cam = m_View3DInventorViewer->getSoRenderManager()->getCamera();
|
|
if (!cam)
|
|
return;
|
|
|
|
// Store GL state.
|
|
glPushAttrib(GL_ALL_ATTRIB_BITS);
|
|
|
|
// configure
|
|
glEnable(GL_DEPTH_TEST);
|
|
glDepthMask(GL_TRUE);
|
|
glDepthRange(0.f, 1.f);
|
|
glClearDepth(1.f);
|
|
glClear(GL_DEPTH_BUFFER_BIT);
|
|
glDepthFunc(GL_LEQUAL);
|
|
|
|
glDisable(GL_LIGHTING);
|
|
|
|
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
|
|
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
|
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
|
|
|
|
glEnable(GL_CULL_FACE);
|
|
glCullFace(GL_BACK);
|
|
glFrontFace(GL_CCW);
|
|
|
|
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
|
|
|
|
if (pickMode) {
|
|
glDisable(GL_BLEND);
|
|
glShadeModel(GL_FLAT);
|
|
glDisable(GL_DITHER);
|
|
glDisable(GL_POLYGON_SMOOTH);
|
|
glClearColor(0, 0, 0, 1);
|
|
glClear(GL_COLOR_BUFFER_BIT);
|
|
}
|
|
else {
|
|
glEnable(GL_POLYGON_OFFSET_FILL);
|
|
glPolygonOffset(1.0f, 1.0f);
|
|
glEnable(GL_BLEND);
|
|
glShadeModel(GL_SMOOTH);
|
|
}
|
|
|
|
// mimic 3d view projection
|
|
glMatrixMode(GL_PROJECTION);
|
|
glPushMatrix();
|
|
glLoadIdentity();
|
|
const float NEARVAL = 0.1f;
|
|
const float FARVAL = 10.1f;
|
|
if (cam->getTypeId().isDerivedFrom(SoOrthographicCamera::getClassTypeId())) {
|
|
glOrtho(-2.1, 2.1, -2.1, 2.1, NEARVAL, FARVAL);
|
|
}
|
|
else {
|
|
const float dim = NEARVAL * float(tan(M_PI / 8.0)) * 1.1;
|
|
glFrustum(-dim, dim, -dim, dim, NEARVAL, FARVAL);
|
|
}
|
|
glMatrixMode(GL_MODELVIEW);
|
|
glPushMatrix();
|
|
SbMatrix mx;
|
|
mx = cam->orientation.getValue();
|
|
mx = mx.inverse();
|
|
mx[3][2] = -5.1F;
|
|
glLoadMatrixf((float*)mx);
|
|
|
|
glEnableClientState(GL_VERTEX_ARRAY);
|
|
QColor& cb = m_EmphaseColor;
|
|
|
|
// Draw coordinate system
|
|
if (!pickMode && m_ShowCS) {
|
|
glLineWidth(m_BorderWidth*2.f);
|
|
glPointSize(m_BorderWidth*2.f);
|
|
float a = -1.1f;
|
|
float b = -1.05f;
|
|
float c = 0.5f;
|
|
|
|
float pointData[] = {
|
|
b, a, a, // X1
|
|
c, a, a, // X2
|
|
a, b, a, // Y1
|
|
a, c, a, // Y2
|
|
a, a, b, // Z1
|
|
a, a, c, // Z2
|
|
a, a, a // 0
|
|
};
|
|
glVertexPointer(3, GL_FLOAT, 0, pointData);
|
|
|
|
glColor4f(m_xColor.r, m_xColor.g, m_xColor.b, opacity);
|
|
glDrawArrays(GL_LINES, 0, 2);
|
|
glDrawArrays(GL_POINTS, 0, 2);
|
|
|
|
glColor4f(m_yColor.r, m_yColor.g, m_yColor.b, opacity);
|
|
glDrawArrays(GL_LINES, 2, 2);
|
|
glDrawArrays(GL_POINTS, 2, 2);
|
|
|
|
glColor4f(m_zColor.r, m_zColor.g, m_zColor.b, opacity);
|
|
glDrawArrays(GL_LINES, 4, 2);
|
|
glDrawArrays(GL_POINTS, 4, 2);
|
|
}
|
|
|
|
// cube faces
|
|
for (const auto& pair : m_Faces) {
|
|
auto f = pair.second;
|
|
if (f.type == ShapeId::Button)
|
|
continue;
|
|
auto pickId = pair.first;
|
|
if (pickMode) {
|
|
glColor3ub(static_cast<GLubyte>(pickId), 0, 0);
|
|
}
|
|
else {
|
|
QColor& c = m_HiliteId == pickId ? m_HiliteColor : m_BaseColor;
|
|
glColor4f(c.redF(), c.greenF(), c.blueF(), c.alphaF() * opacity);
|
|
}
|
|
glVertexPointer(3, GL_FLOAT, 0, f.vertexArray.data());
|
|
glDrawArrays(GL_TRIANGLE_FAN, 0, f.vertexArray.size());
|
|
}
|
|
if (!pickMode) {
|
|
// cube borders
|
|
glLineWidth(m_BorderWidth);
|
|
for (const auto& pair : m_Faces) {
|
|
auto f = pair.second;
|
|
if (f.type == ShapeId::Button)
|
|
continue;
|
|
glColor4f(cb.redF(), cb.greenF(), cb.blueF(), cb.alphaF() * opacity);
|
|
glVertexPointer(3, GL_FLOAT, 0, f.vertexArray.data());
|
|
glDrawArrays(GL_LINES, 0, f.vertexArray.size());
|
|
}
|
|
|
|
// Label textures
|
|
glDisable(GL_POLYGON_OFFSET_FILL); // make sure labels are on top
|
|
glEnable(GL_TEXTURE_2D);
|
|
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
|
|
float texCoords[] = {0.f,0.f,1.f,0.f,1.f,1.f,0.f,1.f};
|
|
glTexCoordPointer(2, GL_FLOAT, 0, texCoords);
|
|
QColor& c = m_EmphaseColor;
|
|
glColor4f(c.redF(), c.greenF(), c.blueF(), c.alphaF() * opacity);
|
|
for (const auto& pair : m_LabelTextures) {
|
|
auto f = pair.second;
|
|
PickId pickId = pair.first;
|
|
glVertexPointer(3, GL_FLOAT, 0, m_LabelTextures[pickId].vertexArray.data());
|
|
glBindTexture(GL_TEXTURE_2D, f.texture->textureId());
|
|
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
|
|
}
|
|
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
|
|
glDisable(GL_TEXTURE_2D);
|
|
glEnable(GL_POLYGON_OFFSET_FILL);
|
|
}
|
|
|
|
// Draw the flat buttons
|
|
glDisable(GL_CULL_FACE);
|
|
glMatrixMode(GL_PROJECTION);
|
|
glLoadIdentity();
|
|
glOrtho(0.0, 1.0, 1.0, 0.0, 0.0, 1.0);
|
|
glMatrixMode(GL_MODELVIEW);
|
|
glLoadIdentity();
|
|
|
|
for (const auto& pair : m_Faces) {
|
|
auto f = pair.second;
|
|
if (f.type != ShapeId::Button)
|
|
continue;
|
|
PickId pickId = pair.first;
|
|
if (pickMode) {
|
|
glColor3ub(static_cast<GLubyte>(pickId), 0, 0);
|
|
}
|
|
else {
|
|
QColor& c = m_HiliteId == pickId ? m_HiliteColor : m_BaseColor;
|
|
glColor4f(c.redF(), c.greenF(), c.blueF(), c.alphaF() * opacity);
|
|
}
|
|
glVertexPointer(3, GL_FLOAT, 0, f.vertexArray.data());
|
|
glDrawArrays(GL_TRIANGLE_FAN, 0, f.vertexArray.size());
|
|
if (!pickMode) {
|
|
glColor4f(cb.redF(), cb.greenF(), cb.blueF(), cb.alphaF() * opacity);
|
|
glDrawArrays(GL_LINE_LOOP, 0, f.vertexArray.size());
|
|
}
|
|
}
|
|
|
|
// Restore original state.
|
|
glPopMatrix();
|
|
glMatrixMode(GL_PROJECTION);
|
|
glPopMatrix();
|
|
glPopAttrib();
|
|
}
|
|
|
|
NaviCubeImplementation::PickId NaviCubeImplementation::pickFace(short x, short y) {
|
|
GLubyte pixels[4] = {0};
|
|
if (m_PickingFramebuffer && std::abs(x) <= m_CubeWidgetSize / 2 &&
|
|
std::abs(y) <= m_CubeWidgetSize / 2) {
|
|
static_cast<QOpenGLWidget*>(m_View3DInventorViewer->viewport())->makeCurrent();
|
|
m_PickingFramebuffer->bind();
|
|
|
|
glViewport(0, 0, m_CubeWidgetSize * 2, m_CubeWidgetSize * 2);
|
|
|
|
drawNaviCube(true, 1.f);
|
|
|
|
glFinish();
|
|
glReadPixels(2 * x + m_CubeWidgetSize, 2 * y + m_CubeWidgetSize, 1, 1,
|
|
GL_RGBA, GL_UNSIGNED_BYTE, &pixels);
|
|
m_PickingFramebuffer->release();
|
|
static_cast<QOpenGLWidget*>(m_View3DInventorViewer->viewport())->doneCurrent();
|
|
}
|
|
return pixels[3] == 255 ? static_cast<PickId>(pixels[0]) : PickId::None;
|
|
}
|
|
|
|
bool NaviCubeImplementation::mousePressed(short x, short y) {
|
|
m_MouseDown = true;
|
|
m_MightDrag = inDragZone(x, y);
|
|
PickId pick = pickFace(x, y);
|
|
setHilite(pick);
|
|
return pick != PickId::None;
|
|
}
|
|
|
|
void NaviCubeImplementation::handleMenu() {
|
|
m_Menu->exec(QCursor::pos());
|
|
}
|
|
|
|
SbRotation NaviCubeImplementation::getNearestOrientation(PickId pickId) {
|
|
SbRotation cameraOrientation = m_View3DInventorViewer->getCameraOrientation();
|
|
SbRotation standardOrientation = m_Faces[pickId].rotation;
|
|
|
|
SbVec3f cameraZ;
|
|
cameraOrientation.multVec(SbVec3f(0, 0, 1), cameraZ);
|
|
|
|
SbVec3f standardZ;
|
|
standardOrientation.multVec(SbVec3f(0, 0, 1), standardZ);
|
|
|
|
// Cleanup near zero values
|
|
for (int i = 0; i < 3; i++) {
|
|
if (abs(standardZ[i]) < 1e-6) standardZ[i] = 0.0F;
|
|
}
|
|
standardZ.normalize();
|
|
|
|
// Rotate the camera to the selected face by the smallest angle to align the z-axis
|
|
SbRotation intermediateOrientation = cameraOrientation * SbRotation(cameraZ, standardZ);
|
|
|
|
// Find an axis and angle to go from the intermediateOrientation to the standardOrientation
|
|
SbVec3f axis;
|
|
float angle;
|
|
SbRotation rotation = intermediateOrientation.inverse() * standardOrientation;
|
|
rotation.getValue(axis, angle);
|
|
|
|
// Make sure the found axis aligns with the standardZ axis
|
|
if (standardZ.dot(axis) < 0) {
|
|
axis.negate();
|
|
angle *= -1;
|
|
}
|
|
|
|
static const float pi = boost::math::constants::pi<float>();
|
|
static const float pi2 = boost::math::constants::two_pi<float>();
|
|
static const float pi1_2 = boost::math::constants::half_pi<float>();
|
|
static const float pi1_3 = boost::math::constants::third_pi<float>();
|
|
static const float pi2_3 = boost::math::constants::two_thirds_pi<float>();
|
|
|
|
// Make angle positive
|
|
if (angle < 0) {
|
|
angle += pi2;
|
|
}
|
|
|
|
// f is a small value used to control orientation priority when the camera is almost exactly between two
|
|
// orientations (e.g. +45 and -45 degrees). The standard orientation is preferred compared to
|
|
// +90 and -90 degree orientations and the +90 and -90 degree orientations are preferred compared to an
|
|
// upside down standard orientation
|
|
float f = 0.00001F;
|
|
|
|
// Find the angle to rotate to the nearest orientation
|
|
if (m_Faces[pickId].type == ShapeId::Corner) {
|
|
// 6 possible orientations for the corners
|
|
if (angle <= (M_PI / 6 + f)) {
|
|
angle = 0;
|
|
}
|
|
else if (angle <= (M_PI_2 + f)) {
|
|
angle = pi1_3;
|
|
}
|
|
else if (angle < (5 * M_PI / 6 - f)) {
|
|
angle = pi2_3;
|
|
}
|
|
else if (angle <= (M_PI + M_PI / 6 + f)) {
|
|
angle = pi;
|
|
}
|
|
else if (angle < (M_PI + M_PI_2 - f)) {
|
|
angle = pi + pi1_3;
|
|
}
|
|
else if (angle < (M_PI + 5 * M_PI / 6 - f)) {
|
|
angle = pi + pi2_3;
|
|
}
|
|
else {
|
|
angle = 0;
|
|
}
|
|
}
|
|
else {
|
|
// 4 possible orientations for the main and edge faces
|
|
if (angle <= (M_PI_4 + f)) {
|
|
angle = 0;
|
|
}
|
|
else if (angle <= (3 * M_PI_4 + f)) {
|
|
angle = pi1_2;
|
|
}
|
|
else if (angle < (M_PI + M_PI_4 - f)) {
|
|
angle = pi;
|
|
}
|
|
else if (angle < (M_PI + 3 * M_PI_4 - f)) {
|
|
angle = pi + pi1_2;
|
|
}
|
|
else {
|
|
angle = 0;
|
|
}
|
|
}
|
|
|
|
// Set the rotation to go from the standard orientation to the nearest orientation
|
|
rotation.setValue(standardZ, angle);
|
|
|
|
return standardOrientation * rotation.inverse();
|
|
}
|
|
|
|
bool NaviCubeImplementation::mouseReleased(short x, short y)
|
|
{
|
|
static const float pi = boost::math::constants::pi<float>();
|
|
|
|
setHilite(PickId::None);
|
|
m_MouseDown = false;
|
|
|
|
if (m_Dragging) {
|
|
m_Dragging = false;
|
|
} else {
|
|
PickId pickId = pickFace(x, y);
|
|
long step = Base::clamp(long(m_NaviStepByTurn), 4L, 36L);
|
|
float rotStepAngle = (2 * M_PI) / step;
|
|
|
|
if (m_Faces[pickId].type == ShapeId::Main || m_Faces[pickId].type == ShapeId::Edge || m_Faces[pickId].type == ShapeId::Corner) {
|
|
// Handle the cube faces
|
|
SbRotation orientation;
|
|
if (m_RotateToNearest) {
|
|
orientation = getNearestOrientation(pickId);
|
|
}
|
|
else {
|
|
orientation = m_Faces[pickId].rotation;
|
|
}
|
|
m_View3DInventorViewer->setCameraOrientation(orientation);
|
|
}
|
|
else if (m_Faces[pickId].type == ShapeId::Button) {
|
|
|
|
// Handle the menu
|
|
if (pickId == PickId::ViewMenu) {
|
|
handleMenu();
|
|
return true;
|
|
}
|
|
|
|
// Handle the flat buttons
|
|
SbRotation rotation = m_Faces[pickId].rotation;
|
|
if (pickId == PickId::DotBackside) {
|
|
rotation.scaleAngle(pi);
|
|
}
|
|
else {
|
|
rotation.scaleAngle(rotStepAngle);
|
|
}
|
|
|
|
// If the previous flat button animation is still active then apply the rotation to the
|
|
// previous target orientation, otherwise apply the rotation to the current camera orientation
|
|
if (m_flatButtonAnimation != nullptr && m_flatButtonAnimation->state() == QAbstractAnimation::Running) {
|
|
m_flatButtonTargetOrientation = rotation * m_flatButtonTargetOrientation;
|
|
}
|
|
else {
|
|
m_flatButtonTargetOrientation = rotation * m_View3DInventorViewer->getCameraOrientation();
|
|
}
|
|
|
|
m_flatButtonAnimation = m_View3DInventorViewer->setCameraOrientation(m_flatButtonTargetOrientation);
|
|
}
|
|
else {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void NaviCubeImplementation::setHilite(PickId hilite) {
|
|
if (hilite != m_HiliteId) {
|
|
m_HiliteId = hilite;
|
|
m_View3DInventorViewer->getSoRenderManager()->scheduleRedraw();
|
|
}
|
|
}
|
|
|
|
bool NaviCubeImplementation::inDragZone(short x, short y) {
|
|
int limit = m_CubeWidgetSize / 4;
|
|
return std::abs(x) < limit && std::abs(y) < limit;
|
|
}
|
|
|
|
bool NaviCubeImplementation::mouseMoved(short x, short y) {
|
|
bool hovering = std::abs(x) <= m_CubeWidgetSize / 2 &&
|
|
std::abs(y) <= m_CubeWidgetSize / 2;
|
|
|
|
if (hovering != m_Hovering) {
|
|
m_Hovering = hovering;
|
|
m_View3DInventorViewer->getSoRenderManager()->scheduleRedraw();
|
|
}
|
|
|
|
if (!m_Dragging)
|
|
setHilite(pickFace(x, y));
|
|
|
|
if (m_MouseDown && m_Draggable) {
|
|
if (m_MightDrag && !m_Dragging) {
|
|
m_Dragging = true;
|
|
setHilite(PickId::None);
|
|
}
|
|
if (m_Dragging && (std::abs(x) || std::abs(y))) {
|
|
float newX = m_RelPos[0] + (float)(x) / m_PosAreaSize[0];
|
|
float newY = m_RelPos[1] + (float)(y) / m_PosAreaSize[1];
|
|
m_RelPos[0] = std::min(std::max(newX, 0.0f), 1.0f);
|
|
m_RelPos[1] = std::min(std::max(newY, 0.0f), 1.0f);
|
|
|
|
m_View3DInventorViewer->getSoRenderManager()->scheduleRedraw();
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool NaviCubeImplementation::processSoEvent(const SoEvent* ev) {
|
|
short x, y;
|
|
ev->getPosition().getValue(x, y);
|
|
// translate to internal cube center based coordinates
|
|
short rx = x - (short)(m_PosAreaSize[0]*m_RelPos[0]) - m_PosAreaBase[0];
|
|
short ry = y - (short)(m_PosAreaSize[1]*m_RelPos[1]) - m_PosAreaBase[1];
|
|
if (ev->getTypeId().isDerivedFrom(SoMouseButtonEvent::getClassTypeId())) {
|
|
const auto mbev = static_cast<const SoMouseButtonEvent*>(ev);
|
|
if (mbev->isButtonPressEvent(mbev, SoMouseButtonEvent::BUTTON1))
|
|
return mousePressed(rx, ry);
|
|
if (mbev->isButtonReleaseEvent(mbev, SoMouseButtonEvent::BUTTON1))
|
|
return mouseReleased(rx, ry);
|
|
}
|
|
if (ev->getTypeId().isDerivedFrom(SoLocation2Event::getClassTypeId())) {
|
|
return mouseMoved(rx, ry);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
QString NaviCubeImplementation::str(const char* str) {
|
|
return QString::fromLatin1(str);
|
|
}
|
|
|
|
void NaviCube::updateColors()
|
|
{
|
|
unsigned long colorLong;
|
|
|
|
colorLong = Gui::ViewParams::instance()->getAxisXColor();
|
|
m_NaviCubeImplementation->m_xColor = App::Color(static_cast<uint32_t>(colorLong));
|
|
colorLong = Gui::ViewParams::instance()->getAxisYColor();
|
|
m_NaviCubeImplementation->m_yColor = App::Color(static_cast<uint32_t>(colorLong));
|
|
colorLong = Gui::ViewParams::instance()->getAxisZColor();
|
|
m_NaviCubeImplementation->m_zColor = App::Color(static_cast<uint32_t>(colorLong));
|
|
}
|
|
|
|
void NaviCube::setNaviCubeCommands(const std::vector<std::string>& cmd)
|
|
{
|
|
NaviCubeImplementation::m_commands = cmd;
|
|
}
|
|
|
|
DEF_STD_CMD_AC(NaviCubeDraggableCmd)
|
|
|
|
NaviCubeDraggableCmd::NaviCubeDraggableCmd()
|
|
: Command("NaviCubeDraggableCmd")
|
|
{
|
|
sGroup = "";
|
|
sMenuText = QT_TR_NOOP("Movable navigation cube");
|
|
sToolTipText = QT_TR_NOOP("Drag and place NaviCube");
|
|
sWhatsThis = "";
|
|
sStatusTip = sToolTipText;
|
|
eType = Alter3DView;
|
|
}
|
|
void NaviCubeDraggableCmd::activated(int iMsg)
|
|
{
|
|
auto view = qobject_cast<View3DInventor*>(getMainWindow()->activeWindow());
|
|
view->getViewer()->getNaviCube()->setDraggable(iMsg == 1 ? true : false);
|
|
}
|
|
bool NaviCubeDraggableCmd::isActive()
|
|
{
|
|
Gui::MDIView* view = Gui::getMainWindow()->activeWindow();
|
|
if (view && view->isDerivedFrom<Gui::View3DInventor>()) {
|
|
bool check = _pcAction->isChecked();
|
|
auto view = qobject_cast<View3DInventor*>(getMainWindow()->activeWindow());
|
|
bool mode = view->getViewer()->getNaviCube()->isDraggable();
|
|
if (mode != check)
|
|
_pcAction->setChecked(mode);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
Gui::Action * NaviCubeDraggableCmd::createAction()
|
|
{
|
|
Gui::Action *pcAction = Command::createAction();
|
|
pcAction->setCheckable(true);
|
|
return pcAction;
|
|
}
|
|
|
|
|
|
QMenu* NaviCubeImplementation::createNaviCubeMenu() {
|
|
auto menu = new QMenu(getMainWindow());
|
|
menu->setObjectName(str("NaviCube_Menu"));
|
|
|
|
CommandManager& rcCmdMgr = Application::Instance->commandManager();
|
|
static bool init = true;
|
|
if (init) {
|
|
init = false;
|
|
rcCmdMgr.addCommand(new NaviCubeDraggableCmd);
|
|
}
|
|
|
|
vector<string> commands = NaviCubeImplementation::m_commands;
|
|
if (commands.empty()) {
|
|
commands.emplace_back("Std_OrthographicCamera");
|
|
commands.emplace_back("Std_PerspectiveCamera");
|
|
commands.emplace_back("Std_ViewIsometric");
|
|
commands.emplace_back("Separator");
|
|
commands.emplace_back("Std_ViewFitAll");
|
|
commands.emplace_back("Std_ViewFitSelection");
|
|
commands.emplace_back("Std_AlignToSelection");
|
|
commands.emplace_back("Separator");
|
|
commands.emplace_back("NaviCubeDraggableCmd");
|
|
}
|
|
|
|
for (const auto & command : commands) {
|
|
if (command == "Separator") {
|
|
menu->addSeparator();
|
|
}
|
|
else {
|
|
Command* cmd = rcCmdMgr.getCommandByName(command.c_str());
|
|
if (cmd)
|
|
cmd->addTo(menu);
|
|
}
|
|
}
|
|
return menu;
|
|
}
|