FreeCAD

Форк
0
/
NaviCube.cpp 
1248 строк · 40.5 Кб
1
/***************************************************************************
2
 *   Copyright (c) 2017 Kustaa Nyholm  <kustaa.nyholm@sparetimelabs.com>   *
3
 *                                                                         *
4
 *   This file is part of the FreeCAD CAx development system.              *
5
 *                                                                         *
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.      *
10
 *                                                                         *
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.                  *
15
 *                                                                         *
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                                *
20
 *                                                                         *
21
 ***************************************************************************/
22

23
#include "PreCompiled.h"
24
#ifndef _PreComp_
25
# include <algorithm>
26
# include <cfloat>
27
# ifdef FC_OS_WIN32
28
#  include <windows.h>
29
# endif
30
# ifdef FC_OS_MACOSX
31
#  include <OpenGL/gl.h>
32
# else
33
#  include <GL/gl.h>
34
# endif
35
# include <boost/math/constants/constants.hpp>
36
# include <Inventor/nodes/SoOrthographicCamera.h>
37
# include <Inventor/events/SoEvent.h>
38
# include <Inventor/events/SoLocation2Event.h>
39
# include <Inventor/events/SoMouseButtonEvent.h>
40
# include <QApplication>
41
# include <QCursor>
42
# include <QImage>
43
# include <QMenu>
44
# include <QOpenGLTexture>
45
# include <QPainterPath>
46
#endif
47

48
#include <App/Color.h>
49
#include <Base/Tools.h>
50
#include <Eigen/Dense>
51

52
#include "NaviCube.h"
53
#include "Application.h"
54
#include "Command.h"
55
#include "Action.h"
56
#include "MainWindow.h"
57
#include "View3DInventorViewer.h"
58
#include "View3DInventor.h"
59

60

61
using namespace Eigen;
62
using namespace std;
63
using namespace Gui;
64

65

66

67

68

69
class NaviCubeImplementation {
70
public:
71
    explicit NaviCubeImplementation(Gui::View3DInventorViewer*);
72
    ~NaviCubeImplementation();
73
    void drawNaviCube();
74
    void createContextMenu(const std::vector<std::string>& cmd);
75
    void createCubeFaceTextures();
76

77
    void moveToCorner(NaviCube::Corner c);
78
    void setLabels(const std::vector<std::string>& labels);
79

80
    bool processSoEvent(const SoEvent* ev);
81
    void setSize(int size);
82

83
private:
84
    enum class PickId {
85
        None,
86
        Front,
87
        Top,
88
        Right,
89
        Rear,
90
        Bottom,
91
        Left,
92
        FrontTop,
93
        FrontBottom,
94
        FrontRight,
95
        FrontLeft,
96
        RearTop,
97
        RearBottom,
98
        RearRight,
99
        RearLeft,
100
        TopRight,
101
        TopLeft,
102
        BottomRight,
103
        BottomLeft,
104
        FrontTopRight,
105
        FrontTopLeft,
106
        FrontBottomRight,
107
        FrontBottomLeft,
108
        RearTopRight,
109
        RearTopLeft,
110
        RearBottomRight,
111
        RearBottomLeft,
112
        ArrowNorth,
113
        ArrowSouth,
114
        ArrowEast,
115
        ArrowWest,
116
        ArrowRight,
117
        ArrowLeft,
118
        DotBackside,
119
        ViewMenu
120
    };
121
    enum class DirId{
122
        Custom, Up, Right, Out
123
    };
124
    enum class ShapeId{
125
        None, Main, Edge, Corner, Button
126
    };
127
    struct Face {
128
        ShapeId type;
129
        vector<Vector3f> vertexArray;
130
        // The rotation is the standard orientation for the faces of the cube
131
        // For the flat buttons the rotation contains the direction of the rotation
132
        // The standard orientation is the desired camera orientation when a face is selected and
133
        // rotate to nearest is disabled
134
        SbRotation rotation;
135
    };
136
    struct LabelTexture {
137
        vector<Vector3f> vertexArray;
138
        qreal fontSize;
139
        QOpenGLTexture *texture = nullptr;
140
        string label;
141
    };
142
    bool mousePressed(short x, short y);
143
    bool mouseReleased(short x, short y);
144
    bool mouseMoved(short x, short y);
145
    PickId pickFace(short x, short y);
146
    bool inDragZone(short x, short y);
147

148
    void prepare();
149
    void handleResize();
150
    void handleMenu();
151

152
    void setHilite(PickId);
153

154
    void addCubeFace(const Vector3f&, const Vector3f&, ShapeId, PickId, float rotZ = 0.0);
155
    void addButtonFace(PickId, const SbVec3f& direction = SbVec3f(0, 0, 0));
156

157
    QString str(const char* str);
158
    QMenu* createNaviCubeMenu();
159
    void drawNaviCube(bool picking, float opacity);
160

161
    SbRotation getNearestOrientation(PickId pickId);
162

163
public:
164

165
    static int m_CubeWidgetSize;
166
    QColor m_BaseColor;
167
    QColor m_EmphaseColor;
168
    QColor m_HiliteColor;
169
    bool m_ShowCS = true;
170
    PickId m_HiliteId = PickId::None;
171
    double m_BorderWidth = 1.1;
172
    bool m_RotateToNearest = true;
173
    int m_NaviStepByTurn = 8;
174
    float m_FontZoom = 0.3F;
175
    float m_Chamfer = 0.12F;
176
    std::string m_TextFont;
177
    int m_FontWeight = 0;
178
    int m_FontStretch = 0;
179
    float m_InactiveOpacity = 0.5;
180
    SbVec2s m_PosOffset = SbVec2s(0,0);
181

182
    bool m_Prepared = false;
183
    static vector<string> m_commands;
184
    bool m_Draggable = false;
185
    SbVec2s m_ViewSize = SbVec2s(0,0);
186

187
private:
188
    bool m_MouseDown = false;
189
    bool m_Dragging = false;
190
    bool m_MightDrag = false;
191
    bool m_Hovering = false;
192

193
    SbVec2f m_RelPos = SbVec2f(1.0f,1.0f);
194
    SbVec2s m_PosAreaBase = SbVec2s(0,0);
195
    SbVec2s m_PosAreaSize = SbVec2s(0,0);
196

197
    QtGLFramebufferObject* m_PickingFramebuffer;
198
    Gui::View3DInventorViewer* m_View3DInventorViewer;
199

200
    map<PickId, Face> m_Faces;
201
    map<PickId, LabelTexture> m_LabelTextures;
202

203
    QMenu* m_Menu;
204
};
205

206
int NaviCubeImplementation::m_CubeWidgetSize = 132;
207

208
int NaviCube::getNaviCubeSize()
209
{
210
    return NaviCubeImplementation::m_CubeWidgetSize;
211
}
212

213
NaviCube::NaviCube(Gui::View3DInventorViewer* viewer) {
214
    m_NaviCubeImplementation = new NaviCubeImplementation(viewer);
215
}
216

217
NaviCube::~NaviCube() {
218
    delete m_NaviCubeImplementation;
219
}
220

221
void NaviCube::drawNaviCube() {
222
    m_NaviCubeImplementation->drawNaviCube();
223
}
224

225
void NaviCube::createContextMenu(const std::vector<std::string>& cmd) {
226
    m_NaviCubeImplementation->createContextMenu(cmd);
227
}
228

229
bool NaviCube::processSoEvent(const SoEvent* ev) {
230
    return m_NaviCubeImplementation->processSoEvent(ev);
231
}
232

233
vector<string> NaviCubeImplementation::m_commands;
234

235
void NaviCube::setCorner(Corner c) {
236
    m_NaviCubeImplementation->moveToCorner(c);
237
}
238

239
void NaviCube::setOffset(int x, int y) {
240
    m_NaviCubeImplementation->m_PosOffset = SbVec2s(x, y);
241
    m_NaviCubeImplementation->m_ViewSize = SbVec2s(0,0);
242
}
243

244
bool NaviCube::isDraggable() {
245
    return m_NaviCubeImplementation->m_Draggable;
246
}
247

248
void NaviCube::setDraggable(bool draggable) {
249
    m_NaviCubeImplementation->m_Draggable = draggable;
250
}
251

252
void NaviCube::setSize(int size)
253
{
254
    m_NaviCubeImplementation->setSize(size);
255
}
256

257
void NaviCube::setChamfer(float chamfer)
258
{
259
    m_NaviCubeImplementation->m_Chamfer = min(max(0.05f, chamfer), 0.18f);
260
    m_NaviCubeImplementation->m_Prepared = false;
261
}
262

263
void NaviCube::setNaviRotateToNearest(bool toNearest)
264
{
265
    m_NaviCubeImplementation->m_RotateToNearest = toNearest;
266
}
267

268
void NaviCube::setNaviStepByTurn(int steps)
269
{
270
    m_NaviCubeImplementation->m_NaviStepByTurn = steps;
271
}
272

273
void NaviCube::setFont(std::string font)
274
{
275
    m_NaviCubeImplementation->m_TextFont = font;
276
    m_NaviCubeImplementation->m_Prepared = false;
277
}
278

279
void NaviCube::setFontWeight(int weight)
280
{
281
    m_NaviCubeImplementation->m_FontWeight = weight;
282
    m_NaviCubeImplementation->m_Prepared = false;
283
}
284

285
void NaviCube::setFontStretch(int stretch)
286
{
287
    m_NaviCubeImplementation->m_FontStretch = stretch;
288
    m_NaviCubeImplementation->m_Prepared = false;
289
}
290

291
void NaviCube::setFontZoom(float zoom)
292
{
293
    m_NaviCubeImplementation->m_FontZoom = zoom;
294
    m_NaviCubeImplementation->m_Prepared = false;
295
}
296

297
void NaviCube::setBaseColor(QColor bColor)
298
{
299
    m_NaviCubeImplementation->m_BaseColor = bColor;
300
}
301

302
void NaviCube::setEmphaseColor(QColor eColor)
303
{
304
    m_NaviCubeImplementation->m_EmphaseColor = eColor;
305
    m_NaviCubeImplementation->m_Prepared = false;
306
}
307

308
void NaviCube::setHiliteColor(QColor HiliteColor)
309
{
310
    m_NaviCubeImplementation->m_HiliteColor = HiliteColor;
311
}
312

313
void NaviCube::setBorderWidth(double BorderWidth)
314
{
315
    m_NaviCubeImplementation->m_BorderWidth = BorderWidth;
316
}
317

318
void NaviCube::setShowCS(bool showCS)
319
{
320
    m_NaviCubeImplementation->m_ShowCS = showCS;
321
}
322

323
void NaviCube::setNaviCubeLabels(const std::vector<std::string>& labels)
324
{
325
    m_NaviCubeImplementation->setLabels(labels);
326
}
327

328
void NaviCube::setInactiveOpacity(float opacity)
329
{
330
    m_NaviCubeImplementation->m_InactiveOpacity = opacity;
331
}
332

333
void NaviCubeImplementation::setLabels(const std::vector<std::string>& labels)
334
{
335
    m_LabelTextures[PickId::Front].label  = labels[0];
336
    m_LabelTextures[PickId::Top].label    = labels[1];
337
    m_LabelTextures[PickId::Right].label  = labels[2];
338
    m_LabelTextures[PickId::Rear].label   = labels[3];
339
    m_LabelTextures[PickId::Bottom].label = labels[4];
340
    m_LabelTextures[PickId::Left].label   = labels[5];
341
    m_Prepared = false;
342
}
343

344
NaviCubeImplementation::NaviCubeImplementation(Gui::View3DInventorViewer* viewer)
345
    : m_BaseColor{226, 232, 239}
346
    , m_HiliteColor{170, 226, 255}
347
{
348
    m_View3DInventorViewer = viewer;
349
    m_PickingFramebuffer = nullptr;
350
    m_Menu = createNaviCubeMenu();
351
}
352

353
NaviCubeImplementation::~NaviCubeImplementation()
354
{
355
    delete m_Menu;
356
    if (m_PickingFramebuffer)
357
        delete m_PickingFramebuffer;
358
    for (auto tex: m_LabelTextures) {
359
        delete tex.second.texture;
360
    }
361
}
362

363
void NaviCubeImplementation::moveToCorner(NaviCube::Corner c) {
364
    if      (c == NaviCube::TopLeftCorner)     m_RelPos = SbVec2f(0.0f, 1.0f);
365
    else if (c == NaviCube::TopRightCorner)    m_RelPos = SbVec2f(1.0f, 1.0f);
366
    else if (c == NaviCube::BottomLeftCorner)  m_RelPos = SbVec2f(0.0f, 0.0f);
367
    else if (c == NaviCube::BottomRightCorner) m_RelPos = SbVec2f(1.0f, 0.0f);
368
 }
369

370
auto convertWeights = [](int weight) -> QFont::Weight {
371
    if (weight >= 87)
372
        return QFont::Black;
373
    if (weight >= 81)
374
        return QFont::ExtraBold;
375
    if (weight >= 75)
376
        return QFont::Bold;
377
    if (weight >= 63)
378
        return QFont::DemiBold;
379
    if (weight >= 57)
380
        return QFont::Medium;
381
    if (weight >= 50)
382
        return QFont::Normal;
383
    if (weight >= 25)
384
        return QFont::Light;
385
    if (weight >= 12)
386
        return QFont::ExtraLight;
387
    return QFont::Thin;
388
};
389

390
int imageVerticalBalance(QImage p, int sizeHint) {
391
    if (sizeHint < 0) {
392
        return 0;
393
    }
394

395
    int h = p.height();
396
    int startRow = (h - sizeHint) / 2;
397
    bool done = false;
398
    int x, bottom, top;
399
    for (top = startRow; top < h; top++){
400
        for (x = 0; x < p.width(); x++){
401
            if (qAlpha(p.pixel(x, top))) {
402
                done = true;
403
                break;
404
            }
405
        }
406
        if (done) break;
407
    }
408
    for (bottom = startRow; bottom < h; bottom++) {
409
        for (x = 0; x < p.width(); x++){
410
            if (qAlpha(p.pixel(x, h-1-bottom)))
411
                return (bottom-top)/2;
412
        }
413
    }
414
    return 0;
415
}
416

417
void NaviCubeImplementation::createCubeFaceTextures() {
418
    int texSize = 192; // Works well for the max cube size 1024
419
    QFont font;
420
    if (m_TextFont.empty()) font.fromString(QStringLiteral("Arial"));
421
    else font.fromString(QString::fromStdString(m_TextFont));
422
    font.setStyleHint(QFont::SansSerif);
423
    if (m_FontWeight > 0) {
424
        font.setWeight(convertWeights(m_FontWeight));
425
    }
426
    if (m_FontStretch > 0) {
427
        font.setStretch(m_FontStretch);
428
    }
429
    font.setPointSizeF(texSize);
430
    QFontMetrics fm(font);
431
    qreal minFontSize = texSize;
432
    qreal maxFontSize = 0.;
433
    vector<PickId> mains = {PickId::Front, PickId::Top, PickId::Right, PickId::Rear, PickId::Bottom, PickId::Left};
434
    for (PickId pickId : mains) {
435
        auto t = QString::fromUtf8(m_LabelTextures[pickId].label.c_str());
436
        QRect br = fm.boundingRect(t);
437
        float scale = (float)texSize / max(br.width(),br.height());
438
        m_LabelTextures[pickId].fontSize = texSize * scale;
439
        minFontSize = std::min(minFontSize, m_LabelTextures[pickId].fontSize);
440
        maxFontSize = std::max(maxFontSize, m_LabelTextures[pickId].fontSize);
441
    }
442
    if (m_FontZoom > 0.0)
443
        maxFontSize = minFontSize + (maxFontSize - minFontSize) * m_FontZoom;
444
    else {
445
        maxFontSize = minFontSize * std::pow(2.0, m_FontZoom);
446
    }
447
    for (PickId pickId : mains) {
448
        QImage image(texSize, texSize, QImage::Format_ARGB32);
449
        image.fill(qRgba(255, 255, 255, 0));
450
        if (m_LabelTextures[pickId].fontSize > 0.5) {
451
            // 5% margin looks nice and prevents some artifacts
452
            font.setPointSizeF(std::min(m_LabelTextures[pickId].fontSize, maxFontSize)*0.9);
453
            QPainter paint;
454
            paint.begin(&image);
455
            paint.setRenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing | QPainter::SmoothPixmapTransform);
456
            paint.setPen(Qt::white);
457
            QString text = QString::fromUtf8(m_LabelTextures[pickId].label.c_str());
458
            paint.setFont(font);
459
            paint.drawText(QRect(0, 0, texSize, texSize), Qt::AlignCenter, text);
460
            int offset = imageVerticalBalance(image, font.pointSize());
461
            image.fill(qRgba(255, 255, 255, 0));
462
            paint.drawText(QRect(0, offset, texSize, texSize), Qt::AlignCenter, text);
463
            paint.end();
464
        }
465

466
        if (m_LabelTextures[pickId].texture) {
467
            delete m_LabelTextures[pickId].texture;
468
        }
469
        m_LabelTextures[pickId].texture = new QOpenGLTexture(image.mirrored());
470
        m_LabelTextures[pickId].texture->setMaximumAnisotropy(4.0);
471
        m_LabelTextures[pickId].texture->setMinificationFilter(QOpenGLTexture::LinearMipMapLinear);
472
        m_LabelTextures[pickId].texture->setMagnificationFilter(QOpenGLTexture::Linear);
473
        m_LabelTextures[pickId].texture->generateMipMaps();
474
    }
475
}
476

477
void NaviCubeImplementation::addButtonFace(PickId pickId, const SbVec3f& direction)
478
{
479
    if (m_Faces[pickId].vertexArray.size())
480
        m_Faces[pickId].vertexArray.clear();
481
    float scale = 0.005F;
482
    float offx = 0.5F;
483
    float offy = 0.5F;
484
    vector<float> pointData;
485

486
    switch (pickId) {
487
        default:
488
            break;
489
        case PickId::ArrowRight:
490
        case PickId::ArrowLeft: {
491
            pointData = {
492
                66.6F, -66.6F,//outer curve
493
                58.3F, -74.0F,
494
                49.2F ,-80.3F,
495
                39.4F ,-85.5F,
496
                29.0F, -89.5F,
497
                25.3F, -78.1F,//inner curve
498
                34.3F, -74.3F,
499
                42.8F, -69.9F,
500
                50.8F, -64.4F,
501
                58.1F, -58.1F,
502
                53.8F, -53.8F,//arrowhead
503
                74.7F, -46.8F,
504
                70.7F, -70.4F
505
            };
506
            break;
507
        }
508
        case PickId::ArrowWest:
509
        case PickId::ArrowNorth:
510
        case PickId::ArrowSouth:
511
        case PickId::ArrowEast: {
512
            pointData = {
513
                100.,  0.,
514
                 80.,-18.,
515
                 80., 18.
516
             };
517
            break;
518
        }
519
        case PickId::ViewMenu: {
520
            offx = 0.84F;
521
            offy = 0.84F;
522
            pointData = {
523
                  0.,   0.,//top rhombus
524
                 15.,  -6.,
525
                  0., -12.,
526
                -15.,  -6.,
527
                  0.,   0.,//left rhombus
528
                -15.,  -6.,
529
                -15.,  12.,
530
                  0.,  18.,
531
                  0.,   0.,//right rhombus
532
                  0.,  18.,
533
                 15.,  12.,
534
                 15.,  -6.
535
            };
536
            break;
537
        }
538
        case PickId::DotBackside: {
539
            int steps = 16;
540
            for (int i = 0; i < steps; i++) {
541
                float angle = 2.0f * M_PI * ((float)i+0.5) / (float)steps;
542
                pointData.emplace_back(10. * cos(angle) + 87.);
543
                pointData.emplace_back(10. * sin(angle) - 87.);
544
            }
545
            break;
546
        }
547
    }
548

549
    int count = static_cast<int>(pointData.size())/2;
550
    m_Faces[pickId].vertexArray.reserve(count);
551
    for (int i = 0; i < count; i++) {
552
        float x = pointData[i*2]   * scale + offx;
553
        float y = pointData[i*2+1] * scale + offy;
554
        if (pickId == PickId::ArrowNorth || pickId == PickId::ArrowWest || pickId == PickId::ArrowLeft)
555
            x = 1.0 - x;
556
        if (pickId == PickId::ArrowSouth || pickId == PickId::ArrowNorth)
557
            m_Faces[pickId].vertexArray.emplace_back(Vector3f(y, x, 0.0));
558
        else
559
            m_Faces[pickId].vertexArray.emplace_back(Vector3f(x, y, 0.0));
560
    }
561
    m_Faces[pickId].type = ShapeId::Button;
562
    m_Faces[pickId].rotation = SbRotation(direction, 1).inverse();
563
}
564

565
void NaviCubeImplementation::addCubeFace(const Vector3f& x, const Vector3f& z, ShapeId shapeType, PickId pickId, float rotZ) {
566
    m_Faces[pickId].vertexArray.clear();
567
    m_Faces[pickId].type = shapeType;
568

569
    Vector3f y = x.cross(-z);
570

571
    // Determine the standard orientations based on vector x and vector z
572
    // Rotate by an additional rotZ if vector x and vector z are not already the standard orientation
573

574
    // Create normalized vectors for x, y and z
575
    SbVec3f xN(x.x(), x.y(), x.z());
576
    SbVec3f yN(y.x(), y.y(), y.z());
577
    SbVec3f zN(z.x(), z.y(), z.z());
578
    xN.normalize();
579
    yN.normalize();
580
    zN.normalize();
581

582
    // Create a rotation matrix
583
    SbMatrix R(xN[0], yN[0], zN[0], 0,
584
                 xN[1], yN[1], zN[1], 0,
585
                 xN[2], yN[2], zN[2], 0,
586
                 0,     0,     0,     1);
587

588
    // Store the standard orientation
589
    m_Faces[pickId].rotation = (SbRotation(R) * SbRotation(SbVec3f(0, 0, 1), rotZ)).inverse();
590

591
    if (shapeType == ShapeId::Corner) {
592
        auto xC = x * m_Chamfer;
593
        auto yC = y * m_Chamfer;
594
        auto zC = (1 - 2 * m_Chamfer) * z;
595
        m_Faces[pickId].vertexArray.reserve(6);
596
        m_Faces[pickId].vertexArray.emplace_back(zC - 2 * xC);
597
        m_Faces[pickId].vertexArray.emplace_back(zC - xC - yC);
598
        m_Faces[pickId].vertexArray.emplace_back(zC + xC - yC);
599
        m_Faces[pickId].vertexArray.emplace_back(zC + 2 * xC);
600
        m_Faces[pickId].vertexArray.emplace_back(zC + xC + yC);
601
        m_Faces[pickId].vertexArray.emplace_back(zC - xC + yC);
602
    }
603
    else if (shapeType == ShapeId::Edge) {
604
        auto x4 = x * (1 - m_Chamfer * 4);
605
        auto yE = y * m_Chamfer;
606
        auto zE = z * (1 - m_Chamfer);
607
        m_Faces[pickId].vertexArray.reserve(4);
608
        m_Faces[pickId].vertexArray.emplace_back(zE - x4 - yE);
609
        m_Faces[pickId].vertexArray.emplace_back(zE + x4 - yE);
610
        m_Faces[pickId].vertexArray.emplace_back(zE + x4 + yE);
611
        m_Faces[pickId].vertexArray.emplace_back(zE - x4 + yE);
612
    }
613
    else if (shapeType == ShapeId::Main) {
614
        auto x2 = x * (1 - m_Chamfer * 2);
615
        auto y2 = y * (1 - m_Chamfer * 2);
616
        auto x4 = x * (1 - m_Chamfer * 4);
617
        auto y4 = y * (1 - m_Chamfer * 4);
618
        m_Faces[pickId].vertexArray.reserve(8);
619
        m_Faces[pickId].vertexArray.emplace_back(z - x2 - y4);
620
        m_Faces[pickId].vertexArray.emplace_back(z - x4 - y2);
621
        m_Faces[pickId].vertexArray.emplace_back(z + x4 - y2);
622
        m_Faces[pickId].vertexArray.emplace_back(z + x2 - y4);
623

624
        m_Faces[pickId].vertexArray.emplace_back(z + x2 + y4);
625
        m_Faces[pickId].vertexArray.emplace_back(z + x4 + y2);
626
        m_Faces[pickId].vertexArray.emplace_back(z - x4 + y2);
627
        m_Faces[pickId].vertexArray.emplace_back(z - x2 + y4);
628

629
        m_LabelTextures[pickId].vertexArray.clear();
630
        m_LabelTextures[pickId].vertexArray.emplace_back(z - x2 - y2);
631
        m_LabelTextures[pickId].vertexArray.emplace_back(z + x2 - y2);
632
        m_LabelTextures[pickId].vertexArray.emplace_back(z + x2 + y2);
633
        m_LabelTextures[pickId].vertexArray.emplace_back(z - x2 + y2);
634

635
    }
636
}
637

638
void NaviCubeImplementation::setSize(int size)
639
{
640
    m_CubeWidgetSize = size;
641
    m_ViewSize = SbVec2s(0,0);
642
    m_Prepared = false;
643
}
644

645
void NaviCubeImplementation::prepare()
646
{
647
    static const float pi = boost::math::constants::pi<float>();
648
    static const float pi1_2 = boost::math::constants::half_pi<float>();
649

650
    createCubeFaceTextures();
651

652
    Vector3f x(1, 0, 0);
653
    Vector3f y(0, 1, 0);
654
    Vector3f z(0, 0, 1);
655

656
    // create the main faces
657
    addCubeFace( x, z, ShapeId::Main, PickId::Top);
658
    addCubeFace( x,-y, ShapeId::Main, PickId::Front);
659
    addCubeFace(-y,-x, ShapeId::Main, PickId::Left);
660
    addCubeFace(-x, y, ShapeId::Main, PickId::Rear);
661
    addCubeFace( y, x, ShapeId::Main, PickId::Right);
662
    addCubeFace( x,-z, ShapeId::Main, PickId::Bottom);
663

664
    // create corner faces
665
    addCubeFace(-x-y, x-y+z, ShapeId::Corner, PickId::FrontTopRight, pi);
666
    addCubeFace(-x+y,-x-y+z, ShapeId::Corner, PickId::FrontTopLeft, pi);
667
    addCubeFace(x+y, x-y-z, ShapeId::Corner, PickId::FrontBottomRight);
668
    addCubeFace(x-y,-x-y-z, ShapeId::Corner, PickId::FrontBottomLeft);
669
    addCubeFace(x-y, x+y+z, ShapeId::Corner, PickId::RearTopRight, pi);
670
    addCubeFace(x+y,-x+y+z, ShapeId::Corner, PickId::RearTopLeft, pi);
671
    addCubeFace(-x+y, x+y-z, ShapeId::Corner, PickId::RearBottomRight);
672
    addCubeFace(-x-y,-x+y-z, ShapeId::Corner, PickId::RearBottomLeft);
673

674
    // create edge faces
675
    addCubeFace(x, z-y, ShapeId::Edge, PickId::FrontTop);
676
    addCubeFace(x,-z-y, ShapeId::Edge, PickId::FrontBottom);
677
    addCubeFace(x, y-z, ShapeId::Edge, PickId::RearBottom, pi);
678
    addCubeFace(x, y+z, ShapeId::Edge, PickId::RearTop, pi);
679
    addCubeFace(z, x+y, ShapeId::Edge, PickId::RearRight, pi1_2);
680
    addCubeFace(z, x-y, ShapeId::Edge, PickId::FrontRight, pi1_2);
681
    addCubeFace(z,-x-y, ShapeId::Edge, PickId::FrontLeft, pi1_2);
682
    addCubeFace(z, y-x, ShapeId::Edge, PickId::RearLeft, pi1_2);
683
    addCubeFace(y, z-x, ShapeId::Edge, PickId::TopLeft, pi);
684
    addCubeFace(y, x+z, ShapeId::Edge, PickId::TopRight);
685
    addCubeFace(y, x-z, ShapeId::Edge, PickId::BottomRight);
686
    addCubeFace(y,-z-x, ShapeId::Edge, PickId::BottomLeft, pi);
687

688
    // create the flat buttons
689
    addButtonFace(PickId::ArrowNorth, SbVec3f(-1, 0, 0));
690
    addButtonFace(PickId::ArrowSouth, SbVec3f(1, 0, 0));
691
    addButtonFace(PickId::ArrowEast, SbVec3f(0, 1, 0));
692
    addButtonFace(PickId::ArrowWest, SbVec3f(0, -1, 0));
693
    addButtonFace(PickId::ArrowLeft, SbVec3f(0, 0, 1));
694
    addButtonFace(PickId::ArrowRight, SbVec3f(0, 0, -1));
695
    addButtonFace(PickId::DotBackside, SbVec3f(0, 1, 0));
696
    addButtonFace(PickId::ViewMenu);
697

698
    if (m_PickingFramebuffer)
699
        delete m_PickingFramebuffer;
700
    m_PickingFramebuffer =
701
        new QtGLFramebufferObject(2 * m_CubeWidgetSize, 2 * m_CubeWidgetSize,
702
                                  QtGLFramebufferObject::CombinedDepthStencil);
703
    m_View3DInventorViewer->getSoRenderManager()->scheduleRedraw();
704
}
705

706
void NaviCubeImplementation::drawNaviCube() {
707
    handleResize();
708
    int posX = (int)(m_RelPos[0] * m_PosAreaSize[0]) + m_PosAreaBase[0] - m_CubeWidgetSize / 2;
709
    int posY = (int)(m_RelPos[1] * m_PosAreaSize[1]) + m_PosAreaBase[1] - m_CubeWidgetSize / 2;
710
    glViewport(posX, posY, m_CubeWidgetSize, m_CubeWidgetSize);
711
    drawNaviCube(false, m_Hovering ? 1.f : m_InactiveOpacity);
712
}
713

714
void NaviCubeImplementation::createContextMenu(const std::vector<std::string>& cmd) {
715
    CommandManager& rcCmdMgr = Application::Instance->commandManager();
716
    m_Menu->clear();
717

718
    for (const auto & i : cmd) {
719
        Command* cmd = rcCmdMgr.getCommandByName(i.c_str());
720
        if (cmd)
721
            cmd->addTo(m_Menu);
722
    }
723
}
724

725
void NaviCubeImplementation::handleResize() {
726
    SbVec2s viewSize = m_View3DInventorViewer->getSoRenderManager()->getSize();
727
    if (viewSize != m_ViewSize) {
728
        m_PosAreaBase[0] = std::min((int)(m_PosOffset[0] + m_CubeWidgetSize * 0.55), viewSize[0] / 2);
729
        m_PosAreaBase[1] = std::min((int)(m_PosOffset[1] + m_CubeWidgetSize * 0.55), viewSize[1] / 2);
730
        m_PosAreaSize[0] = viewSize[0] - 2 * m_PosAreaBase[0];
731
        m_PosAreaSize[1] = viewSize[1] - 2 * m_PosAreaBase[1];
732
        m_ViewSize = viewSize;
733
    }
734
}
735

736
void NaviCubeImplementation::drawNaviCube(bool pickMode, float opacity)
737
{
738
    if (!m_Prepared) {
739
        if (!m_View3DInventorViewer->viewport())
740
            return;
741
        prepare();
742
        m_Prepared = true;
743
        m_View3DInventorViewer->getSoRenderManager()->scheduleRedraw();
744
        return;
745
    }
746

747
    SoCamera* cam = m_View3DInventorViewer->getSoRenderManager()->getCamera();
748
    if (!cam)
749
        return;
750

751
    // Store GL state.
752
    glPushAttrib(GL_ALL_ATTRIB_BITS);
753

754
    // configure
755
    glEnable(GL_DEPTH_TEST);
756
    glDepthMask(GL_TRUE);
757
    glDepthRange(0.f, 1.f);
758
    glClearDepth(1.f);
759
    glClear(GL_DEPTH_BUFFER_BIT);
760
    glDepthFunc(GL_LEQUAL);
761

762
    glDisable(GL_LIGHTING);
763

764
    glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
765
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
766
    glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
767

768
    glEnable(GL_CULL_FACE);
769
    glCullFace(GL_BACK);
770
    glFrontFace(GL_CCW);
771

772
    glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
773

774
    if (pickMode) {
775
        glDisable(GL_BLEND);
776
        glShadeModel(GL_FLAT);
777
        glDisable(GL_DITHER);
778
        glDisable(GL_POLYGON_SMOOTH);
779
        glClearColor(0, 0, 0, 1);
780
        glClear(GL_COLOR_BUFFER_BIT);
781
    }
782
    else {
783
        glEnable(GL_POLYGON_OFFSET_FILL);
784
        glPolygonOffset(1.0f, 1.0f);
785
        glEnable(GL_BLEND);
786
        glShadeModel(GL_SMOOTH);
787
    }
788

789
    // mimic 3d view projection
790
    glMatrixMode(GL_PROJECTION);
791
    glPushMatrix();
792
    glLoadIdentity();
793
    const float NEARVAL = 0.1f;
794
    const float FARVAL = 10.1f;
795
    if (cam->getTypeId().isDerivedFrom(SoOrthographicCamera::getClassTypeId())) {
796
        glOrtho(-2.1, 2.1, -2.1, 2.1, NEARVAL, FARVAL);
797
    }
798
    else {
799
        const float dim = NEARVAL * float(tan(M_PI / 8.0)) * 1.1;
800
        glFrustum(-dim, dim, -dim, dim, NEARVAL, FARVAL);
801
    }
802
    glMatrixMode(GL_MODELVIEW);
803
    glPushMatrix();
804
    SbMatrix mx;
805
    mx = cam->orientation.getValue();
806
    mx = mx.inverse();
807
    mx[3][2] = -5.1F;
808
    glLoadMatrixf((float*)mx);
809

810
    glEnableClientState(GL_VERTEX_ARRAY);
811
    QColor& cb = m_EmphaseColor;
812

813
    // Draw coordinate system
814
    if (!pickMode && m_ShowCS) {
815
        glLineWidth(m_BorderWidth*2.f);
816
        glPointSize(m_BorderWidth*2.f);
817
        float a = -1.1f;
818
        float b = -1.05f;
819
        float c =  0.5f;
820

821
        float pointData[] = {
822
            b, a, a, // X1
823
            c, a, a, // X2
824
            a, b, a, // Y1
825
            a, c, a, // Y2
826
            a, a, b, // Z1
827
            a, a, c, // Z2
828
            a, a, a  // 0
829
        };
830
        glVertexPointer(3, GL_FLOAT, 0, pointData);
831
        glColor4f(1, 0, 0, opacity);
832
        glDrawArrays(GL_LINES, 0, 2);
833
        glDrawArrays(GL_POINTS, 0, 2);
834
        glColor4f(0, 1, 0, opacity);
835
        glDrawArrays(GL_LINES, 2, 2);
836
        glDrawArrays(GL_POINTS, 2, 2);
837
        glColor4f(0, 0, 1, opacity);
838
        glDrawArrays(GL_LINES, 4, 2);
839
        glDrawArrays(GL_POINTS, 4, 2);
840
    }
841

842
    // cube faces
843
    for (const auto& pair : m_Faces) {
844
        auto f = pair.second;
845
        if (f.type == ShapeId::Button)
846
            continue;
847
        auto pickId = pair.first;
848
        if (pickMode) {
849
            glColor3ub(static_cast<GLubyte>(pickId), 0, 0);
850
        }
851
        else {
852
            QColor& c = m_HiliteId == pickId ? m_HiliteColor : m_BaseColor;
853
            glColor4f(c.redF(), c.greenF(), c.blueF(), c.alphaF() * opacity);
854
        }
855
        glVertexPointer(3, GL_FLOAT, 0, f.vertexArray.data());
856
        glDrawArrays(GL_TRIANGLE_FAN, 0, f.vertexArray.size());
857
    }
858
    if (!pickMode) {
859
        // cube borders
860
        glLineWidth(m_BorderWidth);
861
        for (const auto& pair : m_Faces) {
862
            auto f = pair.second;
863
            if (f.type == ShapeId::Button)
864
                continue;
865
            glColor4f(cb.redF(), cb.greenF(), cb.blueF(), cb.alphaF() * opacity);
866
            glVertexPointer(3, GL_FLOAT, 0, f.vertexArray.data());
867
            glDrawArrays(GL_LINES, 0, f.vertexArray.size());
868
        }
869

870
        // Label textures
871
        glDisable(GL_POLYGON_OFFSET_FILL); // make sure labels are on top
872
        glEnable(GL_TEXTURE_2D);
873
        glEnableClientState(GL_TEXTURE_COORD_ARRAY);
874
        float texCoords[] = {0.f,0.f,1.f,0.f,1.f,1.f,0.f,1.f};
875
        glTexCoordPointer(2, GL_FLOAT, 0, texCoords);
876
        QColor& c = m_EmphaseColor;
877
        glColor4f(c.redF(), c.greenF(), c.blueF(), c.alphaF() * opacity);
878
        for (const auto& pair : m_LabelTextures) {
879
            auto f = pair.second;
880
            PickId pickId = pair.first;
881
            glVertexPointer(3, GL_FLOAT, 0, m_LabelTextures[pickId].vertexArray.data());
882
            glBindTexture(GL_TEXTURE_2D, f.texture->textureId());
883
            glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
884
        }
885
        glDisableClientState(GL_TEXTURE_COORD_ARRAY);
886
        glDisable(GL_TEXTURE_2D);
887
        glEnable(GL_POLYGON_OFFSET_FILL);
888
    }
889

890
    // Draw the flat buttons
891
    glDisable(GL_CULL_FACE);
892
    glMatrixMode(GL_PROJECTION);
893
    glLoadIdentity();
894
    glOrtho(0.0, 1.0, 1.0, 0.0, 0.0, 1.0);
895
    glMatrixMode(GL_MODELVIEW);
896
    glLoadIdentity();
897

898
    for (const auto& pair : m_Faces) {
899
        auto f = pair.second;
900
        if (f.type != ShapeId::Button)
901
            continue;
902
        PickId pickId = pair.first;
903
        if (pickMode) {
904
            glColor3ub(static_cast<GLubyte>(pickId), 0, 0);
905
        }
906
        else {
907
            QColor& c = m_HiliteId == pickId ? m_HiliteColor : m_BaseColor;
908
            glColor4f(c.redF(), c.greenF(), c.blueF(), c.alphaF() * opacity);
909
        }
910
        glVertexPointer(3, GL_FLOAT, 0, f.vertexArray.data());
911
        glDrawArrays(GL_TRIANGLE_FAN, 0, f.vertexArray.size());
912
        if (!pickMode) {
913
            glColor4f(cb.redF(), cb.greenF(), cb.blueF(), cb.alphaF() * opacity);
914
            glDrawArrays(GL_LINE_LOOP, 0, f.vertexArray.size());
915
        }
916
    }
917

918
    // Restore original state.
919
    glPopMatrix();
920
    glMatrixMode(GL_PROJECTION);
921
    glPopMatrix();
922
    glPopAttrib();
923
}
924

925
NaviCubeImplementation::PickId NaviCubeImplementation::pickFace(short x, short y) {
926
    GLubyte pixels[4] = {0};
927
    if (m_PickingFramebuffer && std::abs(x) <= m_CubeWidgetSize / 2 &&
928
        std::abs(y) <= m_CubeWidgetSize / 2) {
929
        static_cast<QtGLWidget*>(m_View3DInventorViewer->viewport())->makeCurrent();
930
        m_PickingFramebuffer->bind();
931

932
        glViewport(0, 0, m_CubeWidgetSize * 2, m_CubeWidgetSize * 2);
933

934
        drawNaviCube(true, 1.f);
935

936
        glFinish();
937
        glReadPixels(2 * x + m_CubeWidgetSize, 2 * y + m_CubeWidgetSize, 1, 1,
938
                     GL_RGBA, GL_UNSIGNED_BYTE, &pixels);
939
        m_PickingFramebuffer->release();
940
        static_cast<QtGLWidget*>(m_View3DInventorViewer->viewport())->doneCurrent();
941
    }
942
    return pixels[3] == 255 ? static_cast<PickId>(pixels[0]) : PickId::None;
943
}
944

945
bool NaviCubeImplementation::mousePressed(short x, short y) {
946
    m_MouseDown = true;
947
    m_MightDrag = inDragZone(x, y);
948
    PickId pick = pickFace(x, y);
949
    setHilite(pick);
950
    return pick != PickId::None;
951
}
952

953
void NaviCubeImplementation::handleMenu() {
954
    m_Menu->exec(QCursor::pos());
955
}
956

957
SbRotation NaviCubeImplementation::getNearestOrientation(PickId pickId) {
958
    SbRotation cameraOrientation = m_View3DInventorViewer->getCameraOrientation();
959
    SbRotation standardOrientation = m_Faces[pickId].rotation;
960

961
    SbVec3f cameraZ;
962
    cameraOrientation.multVec(SbVec3f(0, 0, 1), cameraZ);
963

964
    SbVec3f standardZ;
965
    standardOrientation.multVec(SbVec3f(0, 0, 1), standardZ);
966

967
    // Cleanup near zero values
968
    for (int i = 0; i < 3; i++) {
969
        if (abs(standardZ[i]) < 1e-6) standardZ[i] = 0.0F;
970
    }
971
    standardZ.normalize();
972

973
    // Rotate the camera to the selected face by the smallest angle to align the z-axis
974
    SbRotation intermediateOrientation = cameraOrientation * SbRotation(cameraZ, standardZ);
975

976
    // Find an axis and angle to go from the intermediateOrientation to the standardOrientation
977
    SbVec3f axis;
978
    float angle;
979
    SbRotation rotation = intermediateOrientation.inverse() * standardOrientation;
980
    rotation.getValue(axis, angle);
981

982
    // Make sure the found axis aligns with the standardZ axis
983
    if (standardZ.dot(axis) < 0) {
984
        axis.negate();
985
        angle *= -1;
986
    }
987

988
    static const float pi = boost::math::constants::pi<float>();
989
    static const float pi2 = boost::math::constants::two_pi<float>();
990
    static const float pi1_2 = boost::math::constants::half_pi<float>();
991
    static const float pi1_3 = boost::math::constants::third_pi<float>();
992
    static const float pi2_3 = boost::math::constants::two_thirds_pi<float>();
993

994
    // Make angle positive
995
    if (angle < 0) {
996
        angle += pi2;
997
    }
998

999
    // f is a small value used to control orientation priority when the camera is almost exactly between two
1000
    // orientations (e.g. +45 and -45 degrees). The standard orientation is preferred compared to
1001
    // +90 and -90 degree orientations and the +90 and -90 degree orientations are preferred compared to an
1002
    // upside down standard orientation
1003
    float f = 0.00001F;
1004

1005
    // Find the angle to rotate to the nearest orientation
1006
    if (m_Faces[pickId].type == ShapeId::Corner) {
1007
        // 6 possible orientations for the corners
1008
        if (angle <= (M_PI / 6 + f)) {
1009
            angle = 0;
1010
        }
1011
        else if (angle <= (M_PI_2 + f)) {
1012
            angle = pi1_3;
1013
        }
1014
        else if (angle < (5 * M_PI / 6 - f)) {
1015
            angle = pi2_3;
1016
        }
1017
        else if (angle <= (M_PI + M_PI / 6 + f)) {
1018
            angle = pi;
1019
        }
1020
        else if (angle < (M_PI + M_PI_2 - f)) {
1021
            angle = pi + pi1_3;
1022
        }
1023
        else if (angle < (M_PI + 5 * M_PI / 6 - f)) {
1024
            angle = pi + pi2_3;
1025
        }
1026
        else {
1027
            angle = 0;
1028
        }
1029
    }
1030
    else {
1031
        // 4 possible orientations for the main and edge faces
1032
        if (angle <= (M_PI_4 + f)) {
1033
            angle = 0;
1034
        }
1035
        else if (angle <= (3 * M_PI_4 + f)) {
1036
            angle = pi1_2;
1037
        }
1038
        else if (angle < (M_PI + M_PI_4 - f)) {
1039
            angle = pi;
1040
        }
1041
        else if (angle < (M_PI + 3 * M_PI_4 - f)) {
1042
            angle = pi + pi1_2;
1043
        }
1044
        else {
1045
            angle = 0;
1046
        }
1047
    }
1048

1049
    // Set the rotation to go from the standard orientation to the nearest orientation
1050
    rotation.setValue(standardZ, angle);
1051

1052
    return standardOrientation * rotation.inverse();
1053
}
1054

1055
bool NaviCubeImplementation::mouseReleased(short x, short y)
1056
{
1057
    static const float pi = boost::math::constants::pi<float>();
1058

1059
    setHilite(PickId::None);
1060
    m_MouseDown = false;
1061

1062
    if (m_Dragging) {
1063
        m_Dragging = false;
1064
    } else {
1065
        PickId pickId = pickFace(x, y);
1066
        long step = Base::clamp(long(m_NaviStepByTurn), 4L, 36L);
1067
        float rotStepAngle = (2 * M_PI) / step;
1068

1069
        if (m_Faces[pickId].type == ShapeId::Main || m_Faces[pickId].type == ShapeId::Edge || m_Faces[pickId].type == ShapeId::Corner) {
1070
            // Handle the cube faces
1071
            SbRotation orientation;
1072
            if (m_RotateToNearest) {
1073
                orientation = getNearestOrientation(pickId);
1074
            }
1075
            else {
1076
                orientation = m_Faces[pickId].rotation;
1077
            }
1078
            m_View3DInventorViewer->setCameraOrientation(orientation);
1079
        }
1080
        else if (m_Faces[pickId].type == ShapeId::Button) {
1081

1082
            // Handle the menu
1083
            if (pickId == PickId::ViewMenu) {
1084
                handleMenu();
1085
                return true;
1086
            }
1087

1088
            // Handle the flat buttons
1089
            SbRotation rotation = m_Faces[pickId].rotation;
1090
            if (pickId == PickId::DotBackside) {
1091
                rotation.scaleAngle(pi);
1092
            }
1093
            else {
1094
                rotation.scaleAngle(rotStepAngle);
1095
            }
1096
            m_View3DInventorViewer->setCameraOrientation(rotation * m_View3DInventorViewer->getCameraOrientation());
1097
        }
1098
        else {
1099
            return false;
1100
        }
1101
    }
1102
    return true;
1103
}
1104

1105
void NaviCubeImplementation::setHilite(PickId hilite) {
1106
    if (hilite != m_HiliteId) {
1107
        m_HiliteId = hilite;
1108
        m_View3DInventorViewer->getSoRenderManager()->scheduleRedraw();
1109
    }
1110
}
1111

1112
bool NaviCubeImplementation::inDragZone(short x, short y) {
1113
    int limit = m_CubeWidgetSize / 4;
1114
    return std::abs(x) < limit && std::abs(y) < limit;
1115
}
1116

1117
bool NaviCubeImplementation::mouseMoved(short x, short y) {
1118
    bool hovering = std::abs(x) <= m_CubeWidgetSize / 2 &&
1119
            std::abs(y) <= m_CubeWidgetSize / 2;
1120

1121
    if (hovering != m_Hovering) {
1122
        m_Hovering = hovering;
1123
        m_View3DInventorViewer->getSoRenderManager()->scheduleRedraw();
1124
    }
1125

1126
    if (!m_Dragging)
1127
        setHilite(pickFace(x, y));
1128

1129
    if (m_MouseDown && m_Draggable) {
1130
        if (m_MightDrag && !m_Dragging) {
1131
            m_Dragging = true;
1132
            setHilite(PickId::None);
1133
        }
1134
        if (m_Dragging && (std::abs(x) || std::abs(y))) {
1135
            float newX = m_RelPos[0] + (float)(x) / m_PosAreaSize[0];
1136
            float newY = m_RelPos[1] + (float)(y) / m_PosAreaSize[1];
1137
            m_RelPos[0] = std::min(std::max(newX, 0.0f), 1.0f);
1138
            m_RelPos[1] = std::min(std::max(newY, 0.0f), 1.0f);
1139

1140
            m_View3DInventorViewer->getSoRenderManager()->scheduleRedraw();
1141
            return true;
1142
        }
1143
    }
1144
    return false;
1145
}
1146

1147
bool NaviCubeImplementation::processSoEvent(const SoEvent* ev) {
1148
    short x, y;
1149
    ev->getPosition().getValue(x, y);
1150
    // translate to internal cube center based coordinates
1151
    short rx = x - (short)(m_PosAreaSize[0]*m_RelPos[0]) - m_PosAreaBase[0];
1152
    short ry = y - (short)(m_PosAreaSize[1]*m_RelPos[1]) - m_PosAreaBase[1];
1153
    if (ev->getTypeId().isDerivedFrom(SoMouseButtonEvent::getClassTypeId())) {
1154
        const auto mbev = static_cast<const SoMouseButtonEvent*>(ev);
1155
        if (mbev->isButtonPressEvent(mbev, SoMouseButtonEvent::BUTTON1))
1156
            return mousePressed(rx, ry);
1157
        if (mbev->isButtonReleaseEvent(mbev, SoMouseButtonEvent::BUTTON1))
1158
            return mouseReleased(rx, ry);
1159
    }
1160
    if (ev->getTypeId().isDerivedFrom(SoLocation2Event::getClassTypeId())) {
1161
        return mouseMoved(rx, ry);
1162
    }
1163
    return false;
1164
}
1165

1166
QString NaviCubeImplementation::str(const char* str) {
1167
    return QString::fromLatin1(str);
1168
}
1169

1170
void NaviCube::setNaviCubeCommands(const std::vector<std::string>& cmd)
1171
{
1172
    NaviCubeImplementation::m_commands = cmd;
1173
}
1174

1175
DEF_STD_CMD_AC(NaviCubeDraggableCmd)
1176

1177
NaviCubeDraggableCmd::NaviCubeDraggableCmd()
1178
    : Command("NaviCubeDraggableCmd")
1179
{
1180
    sGroup        = "";
1181
    sMenuText     = QT_TR_NOOP("Movable navigation cube");
1182
    sToolTipText  = QT_TR_NOOP("Drag and place NaviCube");
1183
    sWhatsThis    = "";
1184
    sStatusTip    = sToolTipText;
1185
    eType         = Alter3DView;
1186
}
1187
void NaviCubeDraggableCmd::activated(int iMsg)
1188
{
1189
    auto view = qobject_cast<View3DInventor*>(getMainWindow()->activeWindow());
1190
    view->getViewer()->getNaviCube()->setDraggable(iMsg == 1 ? true : false);
1191
}
1192
bool NaviCubeDraggableCmd::isActive()
1193
{
1194
    Gui::MDIView* view = Gui::getMainWindow()->activeWindow();
1195
    if (view && view->isDerivedFrom(Gui::View3DInventor::getClassTypeId())) {
1196
        bool check = _pcAction->isChecked();
1197
        auto view = qobject_cast<View3DInventor*>(getMainWindow()->activeWindow());
1198
        bool mode = view->getViewer()->getNaviCube()->isDraggable();
1199
        if (mode != check)
1200
            _pcAction->setChecked(mode);
1201
        return true;
1202
    }
1203
    return false;
1204
}
1205
Gui::Action * NaviCubeDraggableCmd::createAction()
1206
{
1207
    Gui::Action *pcAction = Command::createAction();
1208
    pcAction->setCheckable(true);
1209
    return pcAction;
1210
}
1211

1212

1213
QMenu* NaviCubeImplementation::createNaviCubeMenu() {
1214
    auto menu = new QMenu(getMainWindow());
1215
    menu->setObjectName(str("NaviCube_Menu"));
1216

1217
    CommandManager& rcCmdMgr = Application::Instance->commandManager();
1218
    static bool init = true;
1219
    if (init) {
1220
        init = false;
1221
        rcCmdMgr.addCommand(new NaviCubeDraggableCmd);
1222
    }
1223

1224
    vector<string> commands = NaviCubeImplementation::m_commands;
1225
    if (commands.empty()) {
1226
        commands.emplace_back("Std_OrthographicCamera");
1227
        commands.emplace_back("Std_PerspectiveCamera");
1228
        commands.emplace_back("Std_ViewIsometric");
1229
        commands.emplace_back("Separator");
1230
        commands.emplace_back("Std_ViewFitAll");
1231
        commands.emplace_back("Std_ViewFitSelection");
1232
        commands.emplace_back("Std_AlignToSelection");
1233
        commands.emplace_back("Separator");
1234
        commands.emplace_back("NaviCubeDraggableCmd");
1235
    }
1236

1237
    for (const auto & command : commands) {
1238
        if (command == "Separator") {
1239
            menu->addSeparator();
1240
        }
1241
        else {
1242
            Command* cmd = rcCmdMgr.getCommandByName(command.c_str());
1243
            if (cmd)
1244
                cmd->addTo(menu);
1245
        }
1246
    }
1247
    return menu;
1248
}
1249

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.