FreeCAD

Форк
0
/
SoFCUnifiedSelection.cpp 
1851 строка · 65.7 Кб
1
/***************************************************************************
2
 *   Copyright (c) 2005 Jürgen Riegel <juergen.riegel@web.de>              *
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

25
#ifndef _PreComp_
26
# include <Inventor/SoFullPath.h>
27
# include <Inventor/SoPickedPoint.h>
28
# include <Inventor/actions/SoCallbackAction.h>
29
# include <Inventor/actions/SoGetBoundingBoxAction.h>
30
# include <Inventor/actions/SoGetPrimitiveCountAction.h>
31
# include <Inventor/actions/SoGLRenderAction.h>
32
# include <Inventor/actions/SoHandleEventAction.h>
33
# include <Inventor/actions/SoWriteAction.h>
34
# include <Inventor/bundles/SoMaterialBundle.h>
35
# include <Inventor/details/SoFaceDetail.h>
36
# include <Inventor/details/SoLineDetail.h>
37
# include <Inventor/elements/SoCacheElement.h>
38
# include <Inventor/elements/SoCoordinateElement.h>
39
# include <Inventor/elements/SoDrawStyleElement.h>
40
# include <Inventor/elements/SoGLCacheContextElement.h>
41
# include <Inventor/elements/SoLazyElement.h>
42
# include <Inventor/elements/SoLineWidthElement.h>
43
# include <Inventor/elements/SoMaterialBindingElement.h>
44
# include <Inventor/elements/SoModelMatrixElement.h>
45
# include <Inventor/elements/SoOverrideElement.h>
46
# include <Inventor/elements/SoShapeStyleElement.h>
47
# include <Inventor/elements/SoSwitchElement.h>
48
# include <Inventor/elements/SoTextureEnabledElement.h>
49
# include <Inventor/events/SoLocation2Event.h>
50
# include <Inventor/events/SoMouseButtonEvent.h>
51
# include <Inventor/misc/SoChildList.h>
52
# include <Inventor/misc/SoState.h>
53
# include <Inventor/nodes/SoCoordinate3.h>
54
# include <Inventor/nodes/SoCube.h>
55
# include <Inventor/nodes/SoIndexedFaceSet.h>
56
# include <Inventor/nodes/SoIndexedLineSet.h>
57
# include <Inventor/nodes/SoMaterial.h>
58
# include <Inventor/nodes/SoMaterialBinding.h>
59
# include <Inventor/nodes/SoNormalBinding.h>
60
# include <Inventor/nodes/SoPointSet.h>
61
# include <Inventor/threads/SbStorage.h>
62
#endif
63

64
#ifdef FC_OS_MACOSX
65
# include <OpenGL/gl.h>
66
#else
67
# ifdef FC_OS_WIN32
68
#  include <windows.h>
69
# endif
70
# include <GL/gl.h>
71
#endif
72

73
#include <App/Document.h>
74
#include <App/GeoFeature.h>
75
#include <App/ElementNamingUtils.h>
76
#include <Base/Tools.h>
77
#include <Base/UnitsApi.h>
78

79
#include "SoFCUnifiedSelection.h"
80
#include "Application.h"
81
#include "Document.h"
82
#include "MainWindow.h"
83
#include "SoFCInteractiveElement.h"
84
#include "SoFCSelectionAction.h"
85
#include "ViewParams.h"
86
#include "ViewProvider.h"
87
#include "ViewProviderDocumentObject.h"
88

89

90
FC_LOG_LEVEL_INIT("SoFCUnifiedSelection",false,true,true)
91

92
using namespace Gui;
93

94
namespace Gui {
95
void printPreselectionInfo(const char* documentName,
96
                           const char* objectName,
97
                           const char* subElementName,
98
                           float x, float y, float z,
99
                           double precision);
100
}
101

102
SoFullPath * Gui::SoFCUnifiedSelection::currenthighlight = nullptr;
103

104
// *************************************************************************
105

106
SO_NODE_SOURCE(SoFCUnifiedSelection)
107

108
/*!
109
  Constructor.
110
*/
111
SoFCUnifiedSelection::SoFCUnifiedSelection()
112
{
113
    SO_NODE_CONSTRUCTOR(SoFCUnifiedSelection);
114

115
    SO_NODE_ADD_FIELD(colorHighlight, (SbColor(1.0f, 0.6f, 0.0f)));
116
    SO_NODE_ADD_FIELD(colorSelection, (SbColor(0.1f, 0.8f, 0.1f)));
117
    SO_NODE_ADD_FIELD(highlightMode,  (AUTO));
118
    SO_NODE_ADD_FIELD(selectionMode,  (ON));
119
    SO_NODE_ADD_FIELD(selectionRole,  (true));
120
    SO_NODE_ADD_FIELD(useNewSelection, (true));
121

122
    SO_NODE_DEFINE_ENUM_VALUE(HighlightModes, AUTO);
123
    SO_NODE_DEFINE_ENUM_VALUE(HighlightModes, ON);
124
    SO_NODE_DEFINE_ENUM_VALUE(HighlightModes, OFF);
125
    SO_NODE_SET_SF_ENUM_TYPE (highlightMode, HighlightModes);
126

127
    // Documentation of SoFullPath:
128
    // Since the SoFullPath is derived from SoPath and contains no private data, you can cast SoPath instances to the SoFullPath type.
129
    // This will allow you to examine hidden children. Actually, you are not supposed to allocate instances of this class at all.
130
    // It is only available as an "extended interface" into the superclass SoPath.
131
    detailPath = static_cast<SoFullPath*>(new SoPath(20));
132
    detailPath->ref();
133

134
    setPreSelection = false;
135
    preSelection = -1;
136
    useNewSelection = ViewParams::instance()->getUseNewSelection();
137
}
138

139
/*!
140
  Destructor.
141
*/
142
SoFCUnifiedSelection::~SoFCUnifiedSelection()
143
{
144
    // If we're being deleted and we're the current highlight,
145
    // NULL out that variable
146
    if (currenthighlight) {
147
        currenthighlight->unref();
148
        currenthighlight = nullptr;
149
    }
150
    if (detailPath) {
151
        detailPath->unref();
152
        detailPath = nullptr;
153
    }
154
}
155

156
// doc from parent
157
void
158
SoFCUnifiedSelection::initClass()
159
{
160
    SO_NODE_INIT_CLASS(SoFCUnifiedSelection,SoSeparator,"Separator");
161
}
162

163
void SoFCUnifiedSelection::finish()
164
{
165
    atexit_cleanup();
166
}
167

168
bool SoFCUnifiedSelection::hasHighlight() {
169
    return currenthighlight != nullptr;
170
}
171

172
void SoFCUnifiedSelection::applySettings()
173
{
174
    float transparency;
175
    ParameterGrp::handle hGrp = Gui::WindowParameter::getDefaultParameter()->GetGroup("View");
176
    bool enablePre = hGrp->GetBool("EnablePreselection", true);
177
    bool enableSel = hGrp->GetBool("EnableSelection", true);
178
    if (!enablePre) {
179
        this->highlightMode = SoFCUnifiedSelection::OFF;
180
    }
181
    else {
182
        // Search for a user defined value with the current color as default
183
        SbColor highlightColor = this->colorHighlight.getValue();
184
        auto highlight = (unsigned long)(highlightColor.getPackedValue());
185
        highlight = hGrp->GetUnsigned("HighlightColor", highlight);
186
        highlightColor.setPackedValue((uint32_t)highlight, transparency);
187
        this->colorHighlight.setValue(highlightColor);
188
    }
189

190
    if (!enableSel) {
191
        this->selectionMode = SoFCUnifiedSelection::OFF;
192
    }
193
    else {
194
        // Do the same with the selection color
195
        SbColor selectionColor = this->colorSelection.getValue();
196
        auto selection = (unsigned long)(selectionColor.getPackedValue());
197
        selection = hGrp->GetUnsigned("SelectionColor", selection);
198
        selectionColor.setPackedValue((uint32_t)selection, transparency);
199
        this->colorSelection.setValue(selectionColor);
200
    }
201
}
202

203
const char* SoFCUnifiedSelection::getFileFormatName() const
204
{
205
    return "Separator";
206
}
207

208
void SoFCUnifiedSelection::write(SoWriteAction * action)
209
{
210
    SoOutput * out = action->getOutput();
211
    if (out->getStage() == SoOutput::WRITE) {
212
        // Do not write out the fields of this class
213
        if (this->writeHeader(out, true, false))
214
            return;
215
        SoGroup::doAction(static_cast<SoAction *>(action));
216
        this->writeFooter(out);
217
    }
218
    else {
219
        inherited::write(action);
220
    }
221
}
222

223
int SoFCUnifiedSelection::getPriority(const SoPickedPoint* p)
224
{
225
    const SoDetail* detail = p->getDetail();
226
    if (!detail)
227
        return 0;
228
    if (detail->isOfType(SoFaceDetail::getClassTypeId()))
229
        return 1;
230
    if (detail->isOfType(SoLineDetail::getClassTypeId()))
231
        return 2;
232
    if (detail->isOfType(SoPointDetail::getClassTypeId()))
233
        return 3;
234
    return 0;
235
}
236

237
std::vector<SoFCUnifiedSelection::PickedInfo>
238
SoFCUnifiedSelection::getPickedList(SoHandleEventAction* action, bool singlePick) const
239
{
240
    ViewProvider *last_vp = nullptr;
241
    std::vector<PickedInfo> ret;
242
    const SoPickedPointList & points = action->getPickedPointList();
243
    for(int i=0,count=points.getLength();i<count;++i) {
244
        PickedInfo info;
245
        info.pp = points[i];
246
        info.vpd = nullptr;
247
        ViewProvider *vp = nullptr;
248
        auto path = static_cast<SoFullPath *>(info.pp->getPath());
249
        if (this->pcDocument && path && path->containsPath(action->getCurPath())) {
250
            vp = this->pcDocument->getViewProviderByPathFromHead(path);
251
            if(singlePick && last_vp && last_vp!=vp)
252
                return ret;
253
        }
254
        if(!vp || !vp->isDerivedFrom(ViewProviderDocumentObject::getClassTypeId())) {
255
            if(!singlePick) continue;
256
            if(ret.empty())
257
                ret.push_back(info);
258
            break;
259
        }
260
        info.vpd = static_cast<ViewProviderDocumentObject*>(vp);
261
        if(!(useNewSelection.getValue()||info.vpd->useNewSelectionModel()) || !info.vpd->isSelectable()) {
262
            if(!singlePick) continue;
263
            if(ret.empty()) {
264
                info.vpd = nullptr;
265
                ret.push_back(info);
266
            }
267
            break;
268
        }
269
        if(!info.vpd->getElementPicked(info.pp,info.element))
270
            continue;
271

272
        if(singlePick)
273
            last_vp = vp;
274
        ret.push_back(info);
275
    }
276

277
    if(ret.size()<=1)
278
        return ret;
279

280
    // To identify the picking of lines in a concave area we have to
281
    // get all intersection points. If we have two or more intersection
282
    // points where the first is of a face and the second of a line with
283
    // almost similar coordinates we use the second point, instead.
284

285
    int picked_prio = getPriority(ret[0].pp);
286
    auto last_vpd = ret[0].vpd;
287
    const SbVec3f& picked_pt = ret.front().pp->getPoint();
288
    auto itPicked = ret.begin();
289
    for(auto it=ret.begin()+1;it!=ret.end();++it) {
290
        auto &info = *it;
291
        if(last_vpd != info.vpd)
292
            break;
293

294
        int cur_prio = getPriority(info.pp);
295
        const SbVec3f& cur_pt = info.pp->getPoint();
296

297
        if ((cur_prio > picked_prio) && picked_pt.equals(cur_pt, 0.2F)) {
298
            itPicked = it;
299
            picked_prio = cur_prio;
300
        }
301
    }
302

303
    if(singlePick) {
304
        std::vector<PickedInfo> sret(itPicked,itPicked+1);
305
        return sret;
306
    }
307
    if(itPicked != ret.begin())
308
        std::swap(*itPicked, *ret.begin());
309
    return ret;
310
}
311

312
void SoFCUnifiedSelection::doAction(SoAction *action)
313
{
314
    if (action->getTypeId() == SoFCEnableHighlightAction::getClassTypeId()) {
315
        auto preaction = static_cast<SoFCEnableHighlightAction*>(action);
316
        if (preaction->highlight) {
317
            this->highlightMode = SoFCUnifiedSelection::AUTO;
318
        }
319
        else {
320
            this->highlightMode = SoFCUnifiedSelection::OFF;
321
        }
322
    }
323

324
    if (action->getTypeId() == SoFCEnableSelectionAction::getClassTypeId()) {
325
        auto selaction = static_cast<SoFCEnableSelectionAction*>(action);
326
        if (selaction->selection) {
327
            this->selectionMode = SoFCUnifiedSelection::ON;
328
        }
329
        else {
330
            this->selectionMode = SoFCUnifiedSelection::OFF;
331
        }
332
    }
333

334
    if (action->getTypeId() == SoFCSelectionColorAction::getClassTypeId()) {
335
        auto colaction = static_cast<SoFCSelectionColorAction*>(action);
336
        this->colorSelection = colaction->selectionColor;
337
    }
338

339
    if (action->getTypeId() == SoFCHighlightColorAction::getClassTypeId()) {
340
        auto colaction = static_cast<SoFCHighlightColorAction*>(action);
341
        this->colorHighlight = colaction->highlightColor;
342
    }
343

344
    if (action->getTypeId() == SoFCHighlightAction::getClassTypeId()) {
345
        auto hilaction = static_cast<SoFCHighlightAction*>(action);
346
        // Do not clear currently highlighted object when setting new pre-selection
347
        if (!setPreSelection && hilaction->SelChange.Type == SelectionChanges::RmvPreselect) {
348
            if (currenthighlight) {
349
                SoHighlightElementAction hlAction;
350
                hlAction.apply(currenthighlight);
351
                currenthighlight->unref();
352
                currenthighlight = nullptr;
353
            }
354
        }
355
        else if (highlightMode.getValue() != OFF
356
                    && hilaction->SelChange.Type == SelectionChanges::SetPreselect) {
357
            if (currenthighlight) {
358
                SoHighlightElementAction hlAction;
359
                hlAction.apply(currenthighlight);
360
                currenthighlight->unref();
361
                currenthighlight = nullptr;
362
            }
363

364
            App::Document* doc = App::GetApplication().getDocument(hilaction->SelChange.pDocName);
365
            App::DocumentObject* obj = doc->getObject(hilaction->SelChange.pObjectName);
366
            ViewProvider*vp = Application::Instance->getViewProvider(obj);
367
            SoDetail* detail = vp->getDetail(hilaction->SelChange.pSubName);
368

369
            SoHighlightElementAction hlAction;
370
            hlAction.setHighlighted(true);
371
            hlAction.setColor(this->colorHighlight.getValue());
372
            hlAction.setElement(detail);
373
            hlAction.apply(vp->getRoot());
374
            delete detail;
375

376
            SoSearchAction sa;
377
            sa.setNode(vp->getRoot());
378
            sa.apply(vp->getRoot());
379
            currenthighlight = static_cast<SoFullPath*>(sa.getPath()->copy());
380
            currenthighlight->ref();
381
        }
382

383
        if (useNewSelection.getValue())
384
            return;
385
    }
386

387
    if (action->getTypeId() == SoFCSelectionAction::getClassTypeId()) {
388
        auto selaction = static_cast<SoFCSelectionAction*>(action);
389
        if(selectionMode.getValue() == ON
390
            && (selaction->SelChange.Type == SelectionChanges::AddSelection
391
                || selaction->SelChange.Type == SelectionChanges::RmvSelection))
392
        {
393
            // selection changes inside the 3d view are handled in handleEvent()
394
            App::Document* doc = App::GetApplication().getDocument(selaction->SelChange.pDocName);
395
            App::DocumentObject* obj = doc->getObject(selaction->SelChange.pObjectName);
396
            ViewProvider*vp = Application::Instance->getViewProvider(obj);
397
            if (vp && (useNewSelection.getValue()||vp->useNewSelectionModel()) && vp->isSelectable()) {
398
                SoDetail *detail = nullptr;
399
                detailPath->truncate(0);
400
                auto subName = selaction->SelChange.pSubName;
401
                App::ElementNamePair elementName;;
402
                App::GeoFeature::resolveElement(obj, subName, elementName);
403
                if (Data::isMappedElement(subName)
404
                    && !elementName.oldName.empty()) {      // If we have a shortened element name
405
                    subName = elementName.oldName.c_str();  // use it.
406
                }
407
                if(!selaction->SelChange.pSubName || !selaction->SelChange.pSubName[0] ||
408
                    vp->getDetailPath(subName,detailPath,true,detail))
409
                {
410
                    SoSelectionElementAction::Type type = SoSelectionElementAction::None;
411
                    if (selaction->SelChange.Type == SelectionChanges::AddSelection) {
412
                        if (detail)
413
                            type = SoSelectionElementAction::Append;
414
                        else
415
                            type = SoSelectionElementAction::All;
416
                    }
417
                    else {
418
                        if (detail)
419
                            type = SoSelectionElementAction::Remove;
420
                        else
421
                            type = SoSelectionElementAction::None;
422
                    }
423

424
                    SoSelectionElementAction selectionAction(type);
425
                    selectionAction.setColor(this->colorSelection.getValue());
426
                    selectionAction.setElement(detail);
427
                    if(detailPath->getLength())
428
                        selectionAction.apply(detailPath);
429
                    else
430
                        selectionAction.apply(vp->getRoot());
431
                }
432
                detailPath->truncate(0);
433
                delete detail;
434
            }
435
        }
436
        else if (selaction->SelChange.Type == SelectionChanges::ClrSelection) {
437
            SoSelectionElementAction selectionAction(SoSelectionElementAction::None);
438
            for(int i=0;i<this->getNumChildren();++i)
439
                selectionAction.apply(this->getChild(i));
440
        }
441
        else if(selectionMode.getValue() == ON
442
                    && selaction->SelChange.Type == SelectionChanges::SetSelection) {
443
            std::vector<ViewProvider*> vps;
444
            if (this->pcDocument)
445
                vps = this->pcDocument->getViewProvidersOfType(ViewProviderDocumentObject::getClassTypeId());
446
            for (const auto & vp : vps) {
447
                auto vpd = static_cast<ViewProviderDocumentObject*>(vp);
448
                if (useNewSelection.getValue() || vpd->useNewSelectionModel()) {
449
                    SoSelectionElementAction::Type type;
450
                    if(Selection().isSelected(vpd->getObject()) && vpd->isSelectable())
451
                        type = SoSelectionElementAction::All;
452
                    else
453
                        type = SoSelectionElementAction::None;
454

455
                    SoSelectionElementAction selectionAction(type);
456
                    selectionAction.setColor(this->colorSelection.getValue());
457
                    selectionAction.apply(vpd->getRoot());
458
                }
459
            }
460
        }
461
        else if (selaction->SelChange.Type == SelectionChanges::SetPreselectSignal) {
462
            // selection changes inside the 3d view are handled in handleEvent()
463
            App::Document* doc = App::GetApplication().getDocument(selaction->SelChange.pDocName);
464
            App::DocumentObject* obj = doc->getObject(selaction->SelChange.pObjectName);
465
            ViewProvider*vp = Application::Instance->getViewProvider(obj);
466
            if (vp && vp->isDerivedFrom(ViewProviderDocumentObject::getClassTypeId()) &&
467
                (useNewSelection.getValue()||vp->useNewSelectionModel()) && vp->isSelectable())
468
            {
469
                detailPath->truncate(0);
470
                SoDetail *det = nullptr;
471
                if(vp->getDetailPath(selaction->SelChange.pSubName,detailPath,true,det)) {
472
                    setHighlight(detailPath,det,static_cast<ViewProviderDocumentObject*>(vp),
473
                            selaction->SelChange.pSubName,
474
                            selaction->SelChange.x,
475
                            selaction->SelChange.y,
476
                            selaction->SelChange.z);
477
                }
478
                delete det;
479
            }
480
        }
481

482
        if (useNewSelection.getValue())
483
            return;
484
    }
485

486
    inherited::doAction( action );
487
}
488

489
bool SoFCUnifiedSelection::setHighlight(const PickedInfo &info) {
490
    if(!info.pp)
491
        return setHighlight(nullptr,nullptr,nullptr,nullptr,0.0,0.0,0.0);
492
    const auto &pt = info.pp->getPoint();
493
    return setHighlight(static_cast<SoFullPath*>(info.pp->getPath()),
494
            info.pp->getDetail(), info.vpd, info.element.c_str(), pt[0],pt[1],pt[2]);
495
}
496

497
bool SoFCUnifiedSelection::setHighlight(SoFullPath *path, const SoDetail *det,
498
        ViewProviderDocumentObject *vpd, const char *element, float x, float y, float z)
499
{
500
    Base::FlagToggler<SbBool> flag(setPreSelection);
501

502
    bool highlighted = false;
503
    if(path && path->getLength() &&
504
       vpd && vpd->getObject() && vpd->getObject()->isAttachedToDocument())
505
    {
506
        const char *docname = vpd->getObject()->getDocument()->getName();
507
        const char *objname = vpd->getObject()->getNameInDocument();
508

509
        this->preSelection = 1;
510

511
        printPreselectionInfo(docname, objname, element, x, y, z, 1e-7);
512

513

514
        int ret = Gui::Selection().setPreselect(docname,objname,element,x,y,z);
515
        if(ret<0 && currenthighlight)
516
            return true;
517

518
        if(ret) {
519
            if (currenthighlight) {
520
                SoHighlightElementAction action;
521
                action.setHighlighted(false);
522
                action.apply(currenthighlight);
523
                currenthighlight->unref();
524
                currenthighlight = nullptr;
525
            }
526
            currenthighlight = static_cast<SoFullPath*>(path->copy());
527
            currenthighlight->ref();
528
            highlighted = true;
529
        }
530
    }
531

532
    if(currenthighlight) {
533
        SoHighlightElementAction action;
534
        action.setHighlighted(highlighted);
535
        action.setColor(this->colorHighlight.getValue());
536
        action.setElement(det);
537
        action.apply(currenthighlight);
538
        if(!highlighted) {
539
            currenthighlight->unref();
540
            currenthighlight = nullptr;
541
            Selection().rmvPreselect();
542
        }
543
        this->touch();
544
    }
545
    return highlighted;
546
}
547

548
bool SoFCUnifiedSelection::setSelection(const std::vector<PickedInfo> &infos, bool ctrlDown) {
549
    if (infos.empty() || !infos[0].vpd)
550
        return false;
551

552
    std::vector<SelectionSingleton::SelObj> sels;
553
    if (infos.size() > 1) {
554
        for (auto &info: infos) {
555
            if (!info.vpd) continue;
556

557
            SelectionSingleton::SelObj sel;
558
            sel.pResolvedObject = nullptr;
559
            sel.pObject = info.vpd->getObject();
560
            sel.pDoc = sel.pObject->getDocument();
561
            sel.DocName = sel.pDoc->getName();
562
            sel.FeatName = sel.pObject->getNameInDocument();
563
            sel.TypeName = sel.pObject->getTypeId().getName();
564
            sel.SubName = info.element.c_str();
565
            const auto &pt = info.pp->getPoint();
566
            sel.x = pt[0];
567
            sel.y = pt[1];
568
            sel.z = pt[2];
569
            sels.push_back(sel);
570
        }
571
    }
572

573
    const auto &info = infos[0];
574
    auto vpd = info.vpd;
575
    if (!vpd)
576
        return false;
577
    if (!vpd->getObject()->isAttachedToDocument())
578
        return false;
579
    const char *objname = vpd->getObject()->getNameInDocument();
580
    const char *docname = vpd->getObject()->getDocument()->getName();
581

582
    auto getFullSubElementName = [vpd](std::string &subName) {
583
        App::ElementNamePair elementName;
584
        App::GeoFeature::resolveElement(vpd->getObject(), subName.c_str(), elementName);
585
        if (!elementName.newName.empty()) {      // If we have a mapped name use it
586
            auto elementNameSuffix = Data::findElementName(subName.c_str()); // Only suffix
587
            subName.erase(subName.find(elementNameSuffix)); // Everything except original suffix
588
            subName = subName.append(elementName.newName);  // Add the mapped name suffix,
589
        }
590
    };
591

592
    bool hasNext = false;
593
    const SoPickedPoint *pp = info.pp;
594
    const SoDetail *det = pp->getDetail();
595
    SoDetail *detNext = nullptr;
596
    auto pPath = static_cast<SoFullPath *>(pp->getPath());
597
    const auto &pt = pp->getPoint();
598
    SoSelectionElementAction::Type type = SoSelectionElementAction::None;
599
    auto mymode = static_cast<HighlightModes>(this->highlightMode.getValue());
600
    static char buf[513];
601
    auto subName = info.element;
602
    std::string objectName = objname;
603

604
    if (ctrlDown) {
605
        if (Gui::Selection().isSelected(docname, objname, info.element.c_str(), ResolveMode::NoResolve))
606
            Gui::Selection().rmvSelection(docname, objname, info.element.c_str(), &sels);
607
        else {
608
            getFullSubElementName(subName);
609
            bool ok = Gui::Selection().addSelection(docname, objname,
610
                                                    subName.c_str(), pt[0], pt[1], pt[2], &sels);
611
            if (ok && mymode == OFF) {
612
                snprintf(buf, 512, "Selected: %s.%s.%s (%g, %g, %g)",
613
                         docname, objname, info.element.c_str(), fabs(pt[0]) > 1e-7 ? pt[0] : 0.0,
614
                         fabs(pt[1]) > 1e-7 ? pt[1] : 0.0, fabs(pt[2]) > 1e-7 ? pt[2] : 0.0);
615

616
                getMainWindow()->showMessage(QString::fromLatin1(buf));
617
            }
618
            detailPath->truncate(0);
619
            if (vpd->getDetailPath(info.element.c_str(), detailPath, true, detNext) &&
620
                detailPath->getLength()) {
621
                pPath = detailPath;
622
                det = detNext;
623
                FC_TRACE("select next " << objectName << ", " << subName);
624
                if (ok)
625
                    type = hasNext ? SoSelectionElementAction::All : SoSelectionElementAction::Append;
626
            }
627
        }
628
    } else {
629
        // Hierarchy ascending
630
        //
631
        // If the clicked subelement is already selected, check if there is an
632
        // upper hierarchy, and select that hierarchy instead.
633
        //
634
        // For example, let's suppose PickedInfo above reports
635
        // 'link.link2.box.Face1', and below Selection().getSelectedElement returns
636
        // 'link.link2.box.', meaning that 'box' is the current selected hierarchy,
637
        // and the user is clicking the box again.  So we shall go up one level,
638
        // and select 'link.link2.'
639
        //
640

641

642
        // We need to convert the short name in the selection to a full element path to look it up
643
        // Ex:  Body.Pad.Face9  to Body.Pad.;g3;SKT;:H12dc,E;FAC;:H12dc:4,F;:G0;XTR;:H12dc:8,F.Face9
644
        getFullSubElementName(subName);
645
        const char *subSelected = Gui::Selection().getSelectedElement(
646
                vpd->getObject(), subName.c_str());
647

648
        FC_TRACE("select " << (subSelected ? subSelected : "'null'") << ", " <<
649
                           objectName << ", " << subName);
650
        std::string newElement;
651
        if (subSelected) {
652
            newElement = Data::newElementName(subSelected);
653
            subSelected = newElement.c_str();
654
            std::string nextsub;
655
            const char *next = strrchr(subSelected, '.');
656
            if (next && next != subSelected) {
657
                if (next[1] == 0) {
658
                    // The convention of dot separated SubName demands a mandatory
659
                    // ending dot for every object name reference inside SubName.
660
                    // The non-object sub-element, however, must not end with a dot.
661
                    // So, next[1]==0 here means current selection is a whole object
662
                    // selection (because no sub-element), so we shall search
663
                    // upwards for the second last dot, which is the end of the
664
                    // parent name of the current selected object
665
                    for (--next; next != subSelected; --next) {
666
                        if (*next == '.') break;
667
                    }
668
                }
669
                if (*next == '.')
670
                    nextsub = std::string(subSelected, next - subSelected + 1);
671
            }
672
            if (nextsub.length() || *subSelected != 0) {
673
                hasNext = true;
674
                subName = nextsub;
675
                detailPath->truncate(0);
676
                if (vpd->getDetailPath(subName.c_str(), detailPath, true, detNext) &&
677
                    detailPath->getLength()) {
678
                    pPath = detailPath;
679
                    det = detNext;
680
                    FC_TRACE("select next " << objectName << ", " << subName);
681
                }
682
            }
683
        }
684

685
        FC_TRACE("clearing selection");
686
        Gui::Selection().clearSelection();
687
        FC_TRACE("add selection");
688
        bool ok = Gui::Selection().addSelection(docname, objectName.c_str(), subName.c_str(),
689
                                                pt[0], pt[1], pt[2], &sels);
690
        if (ok)
691
            type = hasNext ? SoSelectionElementAction::All : SoSelectionElementAction::Append;
692

693
        if (mymode == OFF) {
694
            snprintf(buf, 512, "Selected: %s.%s.%s (%g, %g, %g)",
695
                     docname, objectName.c_str(), subName.c_str(), fabs(pt[0]) > 1e-7 ? pt[0] : 0.0,
696
                     fabs(pt[1]) > 1e-7 ? pt[1] : 0.0, fabs(pt[2]) > 1e-7 ? pt[2] : 0.0);
697

698
            getMainWindow()->showMessage(QString::fromLatin1(buf));
699
        }
700
    }
701
    if (pPath) {
702
        FC_TRACE("applying action");
703
        SoSelectionElementAction action(type);
704
        action.setColor(this->colorSelection.getValue());
705
        action.setElement(det);
706
        action.apply(pPath);
707
        FC_TRACE("applied action");
708
        this->touch();
709
    }
710

711
    if (detNext) delete detNext;
712
    return true;
713
}
714

715
// doc from parent
716
void
717
SoFCUnifiedSelection::handleEvent(SoHandleEventAction * action)
718
{
719
    // If off then don't handle this event
720
    if (!selectionRole.getValue()) {
721
        inherited::handleEvent(action);
722
        return;
723
    }
724

725
    auto mymode = static_cast<HighlightModes>(this->highlightMode.getValue());
726
    const SoEvent * event = action->getEvent();
727

728
    // If we don't need to pick for locate highlighting,
729
    // then just behave as separator and return.
730
    // NOTE: we still have to pick for ON even though we don't have
731
    // to re-render, because the app needs to be notified as the mouse
732
    // goes over locate highlight nodes.
733
    //if (highlightMode.getValue() == OFF) {
734
    //    inherited::handleEvent( action );
735
    //    return;
736
    //}
737

738
    //
739
    // If this is a mouseMotion event, then check for locate highlighting
740
    //
741
    if (event->isOfType(SoLocation2Event::getClassTypeId())) {
742
        // NOTE: If preselection is off then we do not check for a picked point because otherwise this search may slow
743
        // down extremely the system on really big data sets. In this case we just check for a picked point if the data
744
        // set has been selected.
745
        if (mymode == AUTO || mymode == ON) {
746
            // check to see if the mouse is over our geometry...
747
            auto infos = this->getPickedList(action,true);
748
            if(!infos.empty())
749
                setHighlight(infos[0]);
750
            else {
751
                setHighlight(PickedInfo());
752
                if (this->preSelection > 0) {
753
                    this->preSelection = 0;
754
                    // touch() makes sure to call GLRenderBelowPath so that the cursor can be updated
755
                    // because only from there the SoGLWidgetElement delivers the OpenGL window
756
                    this->touch();
757
                }
758
            }
759
        }
760
    }
761
    // mouse press events for (de)selection
762
    else if (event->isOfType(SoMouseButtonEvent::getClassTypeId()) &&
763
             selectionMode.getValue() == SoFCUnifiedSelection::ON) {
764
        const auto e = static_cast<const SoMouseButtonEvent *>(event);
765
        if (SoMouseButtonEvent::isButtonReleaseEvent(e,SoMouseButtonEvent::BUTTON1)) {
766
            // check to see if the mouse is over a geometry...
767
            auto infos = this->getPickedList(action,!Selection().needPickedList());
768
            bool greedySel = Gui::Selection().getSelectionStyle() == Gui::SelectionSingleton::SelectionStyle::GreedySelection;
769
            greedySel = greedySel || event->wasCtrlDown();
770
            if(setSelection(infos, greedySel) || greedySel)
771
                action->setHandled();
772
        } // mouse release
773
    }
774

775
    inherited::handleEvent(action);
776
}
777

778
void SoFCUnifiedSelection::GLRenderBelowPath(SoGLRenderAction * action)
779
{
780
    inherited::GLRenderBelowPath(action);
781

782
    // nothing picked, so restore the arrow cursor if needed
783
    if (this->preSelection == 0) {
784
        // this is called when a selection gate forbade to select an object
785
        // and the user moved the mouse to an empty area
786
        this->preSelection = -1;
787
        QtGLWidget* window;
788
        SoState *state = action->getState();
789
        SoGLWidgetElement::get(state, window);
790
        QWidget* parent = window ? window->parentWidget() : nullptr;
791
        if (parent) {
792
            QCursor c = parent->cursor();
793
            if (c.shape() == Qt::ForbiddenCursor) {
794
                c.setShape(Qt::ArrowCursor);
795
                parent->setCursor(c);
796
            }
797
        }
798
    }
799
}
800

801
// ---------------------------------------------------------------
802

803
SO_ACTION_SOURCE(SoHighlightElementAction)
804

805
void SoHighlightElementAction::initClass()
806
{
807
    SO_ACTION_INIT_CLASS(SoHighlightElementAction,SoAction);
808

809
    SO_ENABLE(SoHighlightElementAction, SoSwitchElement);
810
    SO_ENABLE(SoHighlightElementAction, SoModelMatrixElement);
811

812
    SO_ACTION_ADD_METHOD(SoNode,nullAction);
813

814
    SO_ENABLE(SoHighlightElementAction, SoCoordinateElement);
815

816
    SO_ACTION_ADD_METHOD(SoGroup,callDoAction);
817
    SO_ACTION_ADD_METHOD(SoIndexedLineSet,callDoAction);
818
    SO_ACTION_ADD_METHOD(SoIndexedFaceSet,callDoAction);
819
    SO_ACTION_ADD_METHOD(SoPointSet,callDoAction);
820
}
821

822
SoHighlightElementAction::SoHighlightElementAction ()
823
{
824
    SO_ACTION_CONSTRUCTOR(SoHighlightElementAction);
825
}
826

827
SoHighlightElementAction::~SoHighlightElementAction() = default;
828

829
void SoHighlightElementAction::beginTraversal(SoNode *node)
830
{
831
    traverse(node);
832
}
833

834
void SoHighlightElementAction::callDoAction(SoAction *action,SoNode *node)
835
{
836
    node->doAction(action);
837
}
838

839
void SoHighlightElementAction::setHighlighted(SbBool ok)
840
{
841
    this->_highlight = ok;
842
}
843

844
SbBool SoHighlightElementAction::isHighlighted() const
845
{
846
    return this->_highlight;
847
}
848

849
void SoHighlightElementAction::setColor(const SbColor& c)
850
{
851
    this->_color = c;
852
}
853

854
const SbColor& SoHighlightElementAction::getColor() const
855
{
856
    return this->_color;
857
}
858

859
void SoHighlightElementAction::setElement(const SoDetail* det)
860
{
861
    this->_det = det;
862
}
863

864
const SoDetail* SoHighlightElementAction::getElement() const
865
{
866
    return this->_det;
867
}
868

869
// ---------------------------------------------------------------
870

871
SO_ACTION_SOURCE(SoSelectionElementAction)
872

873
void SoSelectionElementAction::initClass()
874
{
875
    SO_ACTION_INIT_CLASS(SoSelectionElementAction,SoAction);
876

877
    SO_ENABLE(SoSelectionElementAction, SoSwitchElement);
878
    SO_ENABLE(SoSelectionElementAction, SoModelMatrixElement);
879

880
    SO_ACTION_ADD_METHOD(SoNode,nullAction);
881

882
    SO_ENABLE(SoSelectionElementAction, SoCoordinateElement);
883

884
    SO_ACTION_ADD_METHOD(SoCoordinate3,callDoAction);
885
    SO_ACTION_ADD_METHOD(SoGroup,callDoAction);
886
    SO_ACTION_ADD_METHOD(SoIndexedLineSet,callDoAction);
887
    SO_ACTION_ADD_METHOD(SoIndexedFaceSet,callDoAction);
888
    SO_ACTION_ADD_METHOD(SoPointSet,callDoAction);
889
}
890

891
SoSelectionElementAction::SoSelectionElementAction (Type t, bool secondary)
892
    : _type(t), _secondary(secondary)
893
{
894
    SO_ACTION_CONSTRUCTOR(SoSelectionElementAction);
895
}
896

897
SoSelectionElementAction::~SoSelectionElementAction() = default;
898

899
void SoSelectionElementAction::beginTraversal(SoNode *node)
900
{
901
    traverse(node);
902
}
903

904
void SoSelectionElementAction::callDoAction(SoAction *action,SoNode *node)
905
{
906
    node->doAction(action);
907
}
908

909
SoSelectionElementAction::Type
910
SoSelectionElementAction::getType() const
911
{
912
    return this->_type;
913
}
914

915
void SoSelectionElementAction::setColor(const SbColor& c)
916
{
917
    this->_color = c;
918
}
919

920
const SbColor& SoSelectionElementAction::getColor() const
921
{
922
    return this->_color;
923
}
924

925
void SoSelectionElementAction::setElement(const SoDetail* det)
926
{
927
    this->_det = det;
928
}
929

930
const SoDetail* SoSelectionElementAction::getElement() const
931
{
932
    return this->_det;
933
}
934

935
// ---------------------------------------------------------------
936

937
SO_ACTION_SOURCE(SoVRMLAction)
938

939
void SoVRMLAction::initClass()
940
{
941
    SO_ACTION_INIT_CLASS(SoVRMLAction,SoAction);
942

943
    SO_ENABLE(SoVRMLAction, SoSwitchElement);
944

945
    SO_ACTION_ADD_METHOD(SoNode,nullAction);
946

947
    SO_ENABLE(SoVRMLAction, SoCoordinateElement);
948
    SO_ENABLE(SoVRMLAction, SoMaterialBindingElement);
949
    SO_ENABLE(SoVRMLAction, SoLazyElement);
950
    SO_ENABLE(SoVRMLAction, SoShapeStyleElement);
951

952
    SO_ACTION_ADD_METHOD(SoCoordinate3,callDoAction);
953
    SO_ACTION_ADD_METHOD(SoMaterialBinding,callDoAction);
954
    SO_ACTION_ADD_METHOD(SoMaterial,callDoAction);
955
    SO_ACTION_ADD_METHOD(SoNormalBinding,callDoAction);
956
    SO_ACTION_ADD_METHOD(SoGroup,callDoAction);
957
    SO_ACTION_ADD_METHOD(SoIndexedLineSet,callDoAction);
958
    SO_ACTION_ADD_METHOD(SoIndexedFaceSet,callDoAction);
959
    SO_ACTION_ADD_METHOD(SoPointSet,callDoAction);
960
}
961

962
SoVRMLAction::SoVRMLAction()
963
{
964
    SO_ACTION_CONSTRUCTOR(SoVRMLAction);
965
}
966

967
SoVRMLAction::~SoVRMLAction() = default;
968

969
void SoVRMLAction::setOverrideMode(SbBool on)
970
{
971
    overrideMode = on;
972
}
973

974
SbBool SoVRMLAction::isOverrideMode() const
975
{
976
    return overrideMode;
977
}
978

979
void SoVRMLAction::callDoAction(SoAction *action, SoNode *node)
980
{
981
    if (node->getTypeId().isDerivedFrom(SoNormalBinding::getClassTypeId()) && action->isOfType(SoVRMLAction::getClassTypeId())) {
982
        auto vrmlAction = static_cast<SoVRMLAction*>(action);
983
        if (vrmlAction->overrideMode) {
984
            auto bind = static_cast<SoNormalBinding*>(node);
985
            vrmlAction->bindList.push_back(bind->value.getValue());
986
            // this normal binding causes some problems for the part view provider
987
            // See also #0002222: Number of normals in exported VRML is wrong
988
            if (bind->value.getValue() == static_cast<int>(SoNormalBinding::PER_VERTEX_INDEXED))
989
                bind->value = SoNormalBinding::OVERALL;
990
        }
991
        else if (!vrmlAction->bindList.empty()) {
992
            static_cast<SoNormalBinding*>(node)->value = static_cast<SoNormalBinding::Binding>(vrmlAction->bindList.front());
993
            vrmlAction->bindList.pop_front();
994
        }
995
    }
996

997
    node->doAction(action);
998
}
999

1000
// ---------------------------------------------------------------------------------
1001

1002
bool SoFCSelectionRoot::StackComp::operator()(const Stack &a, const Stack &b) const {
1003
    if(a.size()-a.offset < b.size()-b.offset)
1004
        return true;
1005
    if(a.size()-a.offset > b.size()-b.offset)
1006
        return false;
1007
    auto it1=a.rbegin(), end1=a.rend()-a.offset;
1008
    auto it2=b.rbegin();
1009
    for(;it1!=end1;++it1,++it2) {
1010
        if(*it1 < *it2)
1011
            return true;
1012
        if(*it1 > *it2)
1013
            return false;
1014
    }
1015
    return false;
1016
}
1017
// ---------------------------------------------------------------------------------
1018
SoSeparator::CacheEnabled SoFCSeparator::CacheMode = SoSeparator::AUTO;
1019
SO_NODE_SOURCE(SoFCSeparator)
1020

1021
SoFCSeparator::SoFCSeparator(bool trackCacheMode)
1022
    :trackCacheMode(trackCacheMode)
1023
{
1024
    SO_NODE_CONSTRUCTOR(SoFCSeparator);
1025
    if(!trackCacheMode) {
1026
        renderCaching = SoSeparator::OFF;
1027
        boundingBoxCaching = SoSeparator::OFF;
1028
    }
1029
}
1030

1031
void SoFCSeparator::GLRenderBelowPath(SoGLRenderAction * action) {
1032
    if(trackCacheMode && renderCaching.getValue()!=CacheMode) {
1033
        renderCaching = CacheMode;
1034
        boundingBoxCaching = CacheMode;
1035
    }
1036
    inherited::GLRenderBelowPath(action);
1037
}
1038

1039
void SoFCSeparator::initClass()
1040
{
1041
    SO_NODE_INIT_CLASS(SoFCSeparator,SoSeparator,"FCSeparator");
1042
}
1043

1044
void SoFCSeparator::finish()
1045
{
1046
    atexit_cleanup();
1047
}
1048

1049
// ---------------------------------------------------------------------------------
1050
// Thread local data for bounding box rendering
1051
//
1052
// The code is inspred by Coin SoLevelOfDetails.cpp.
1053
using SoFCBBoxRenderInfo = struct {
1054
    SoGetBoundingBoxAction * bboxaction;
1055
    SoCube *cube;
1056
    SoColorPacker *packer;
1057
};
1058

1059
static void so_bbox_construct_data(void * closure)
1060
{
1061
    auto data = static_cast<SoFCBBoxRenderInfo*>(closure);
1062
    data->bboxaction = nullptr;
1063
    data->cube = nullptr;
1064
    data->packer = nullptr;
1065
}
1066

1067
static void so_bbox_destruct_data(void * closure)
1068
{
1069
    auto data = static_cast<SoFCBBoxRenderInfo*>(closure);
1070
    delete data->bboxaction;
1071
    if(data->cube)
1072
        data->cube->unref();
1073
    delete data->packer;
1074
}
1075

1076
static SbStorage * so_bbox_storage = nullptr;
1077

1078
// called from atexit
1079
static void so_bbox_cleanup()
1080
{
1081
    delete so_bbox_storage;
1082
}
1083

1084
// ---------------------------------------------------------------------------------
1085

1086
SoFCSelectionRoot::Stack SoFCSelectionRoot::SelStack;
1087
std::unordered_map<SoAction*,SoFCSelectionRoot::Stack> SoFCSelectionRoot::ActionStacks;
1088
SoFCSelectionRoot::ColorStack SoFCSelectionRoot::SelColorStack;
1089
SoFCSelectionRoot::ColorStack SoFCSelectionRoot::HlColorStack;
1090
SoFCSelectionRoot* SoFCSelectionRoot::ShapeColorNode;
1091

1092
SO_NODE_SOURCE(SoFCSelectionRoot)
1093

1094
SoFCSelectionRoot::SoFCSelectionRoot(bool trackCacheMode)
1095
    :SoFCSeparator(trackCacheMode)
1096
{
1097
    SO_NODE_CONSTRUCTOR(SoFCSelectionRoot);
1098
    SO_NODE_ADD_FIELD(selectionStyle,(Full));
1099
    SO_NODE_DEFINE_ENUM_VALUE(SelectStyles, Full);
1100
    SO_NODE_DEFINE_ENUM_VALUE(SelectStyles, Box);
1101
    SO_NODE_DEFINE_ENUM_VALUE(SelectStyles, PassThrough);
1102
    SO_NODE_SET_SF_ENUM_TYPE(selectionStyle, SelectStyles);
1103
}
1104

1105
SoFCSelectionRoot::~SoFCSelectionRoot() = default;
1106

1107
void SoFCSelectionRoot::initClass()
1108
{
1109
    SO_NODE_INIT_CLASS(SoFCSelectionRoot,SoFCSeparator,"FCSelectionRoot");
1110

1111
    so_bbox_storage = new SbStorage(sizeof(SoFCBBoxRenderInfo),
1112
            so_bbox_construct_data, so_bbox_destruct_data);
1113

1114
    // cc_coin_atexit((coin_atexit_f*) so_bbox_cleanup);
1115
}
1116

1117
void SoFCSelectionRoot::finish()
1118
{
1119
    so_bbox_cleanup();
1120
    atexit_cleanup();
1121
}
1122

1123
SoNode *SoFCSelectionRoot::getCurrentRoot(bool front, SoNode *def) {
1124
    if(!SelStack.empty())
1125
        return front?SelStack.front():SelStack.back();
1126
    return def;
1127
}
1128

1129
SoFCSelectionContextBasePtr SoFCSelectionRoot::getNodeContext(
1130
        Stack &stack, SoNode *node, SoFCSelectionContextBasePtr def)
1131
{
1132
    if(stack.empty())
1133
        return def;
1134

1135
    auto front = dynamic_cast<SoFCSelectionRoot *>(stack.front());
1136
    if (front == nullptr) {
1137
        return SoFCSelectionContextBasePtr();
1138
    }
1139

1140
    stack.front() = node;
1141

1142
    auto it = front->contextMap.find(stack);
1143
    stack.front() = front;
1144
    if(it!=front->contextMap.end())
1145
        return it->second;
1146
    return {};
1147
}
1148

1149
SoFCSelectionContextBasePtr
1150
SoFCSelectionRoot::getNodeContext2(Stack &stack, SoNode *node, SoFCSelectionContextBase::MergeFunc *merge)
1151
{
1152
    SoFCSelectionContextBasePtr ret;
1153
    if (stack.empty()) {
1154
        return ret;
1155
    }
1156

1157
    auto *back = dynamic_cast<SoFCSelectionRoot*>(stack.back());
1158
    if (back == nullptr || back->contextMap2.empty()) {
1159
        return ret;
1160
    }
1161

1162
    int status = 0;
1163
    auto &map = back->contextMap2;
1164
    stack.back() = node;
1165
    for(stack.offset=0;stack.offset<stack.size();++stack.offset) {
1166
        auto it = map.find(stack);
1167
        SoFCSelectionContextBasePtr ctx;
1168
        if(it!=map.end())
1169
            ctx = it->second;
1170
        status = merge(status,ret,ctx,stack.offset==stack.size()-1?nullptr:stack[stack.offset]);
1171
        if(status<0)
1172
            break;
1173
    }
1174
    stack.offset = 0;
1175
    stack.back() = back;
1176
    return ret;
1177
}
1178

1179
std::pair<bool,SoFCSelectionContextBasePtr*> SoFCSelectionRoot::findActionContext(
1180
        SoAction *action, SoNode *_node, bool create, bool erase)
1181
{
1182
    std::pair<bool,SoFCSelectionContextBasePtr*> res(false,0);
1183

1184
    if(action->isOfType(SoSelectionElementAction::getClassTypeId()))
1185
        res.first = static_cast<SoSelectionElementAction*>(action)->isSecondary();
1186

1187
    auto it = ActionStacks.find(action);
1188
    if(it==ActionStacks.end() || it->second.empty())
1189
        return res;
1190

1191
    auto &stack = it->second;
1192

1193
    if(res.first) {
1194
        auto back = dynamic_cast<SoFCSelectionRoot*>(stack.back());
1195
        if (back != nullptr) {
1196
            stack.back() = _node;
1197
            if(create)
1198
                res.second = &back->contextMap2[stack];
1199
            else {
1200
                auto it = back->contextMap2.find(stack);
1201
                if(it!=back->contextMap2.end()) {
1202
                    res.second = &it->second;
1203
                    if(erase)
1204
                        back->contextMap2.erase(it);
1205
                }
1206
            }
1207
            stack.back() = back;
1208
        }
1209
    }else{
1210
        auto front = dynamic_cast<SoFCSelectionRoot*>(stack.front());
1211
        if (front != nullptr) {
1212
            stack.front() = _node;
1213
            if(create)
1214
                res.second = &front->contextMap[stack];
1215
            else {
1216
                auto it = front->contextMap.find(stack);
1217
                if(it!=front->contextMap.end()) {
1218
                    res.second = &it->second;
1219
                    if(erase)
1220
                        front->contextMap.erase(it);
1221
                }
1222
            }
1223
            stack.front() = front;
1224
        }
1225
    }
1226
    return res;
1227
}
1228

1229
bool SoFCSelectionRoot::renderBBox(SoGLRenderAction *action, SoNode *node, SbColor color)
1230
{
1231
    auto data = static_cast<SoFCBBoxRenderInfo*>(so_bbox_storage->get());
1232
    if (!data->bboxaction) {
1233
        // The viewport region will be replaced every time the action is
1234
        // used, so we can just feed it a dummy here.
1235
        data->bboxaction = new SoGetBoundingBoxAction(SbViewportRegion());
1236
        data->cube = new SoCube;
1237
        data->cube->ref();
1238
        data->packer = new SoColorPacker;
1239
    }
1240

1241
    SbBox3f bbox;
1242
    data->bboxaction->setViewportRegion(action->getViewportRegion());
1243
    data->bboxaction->apply(node);
1244
    bbox = data->bboxaction->getBoundingBox();
1245
    if(bbox.isEmpty())
1246
        return false;
1247

1248
    auto state = action->getState();
1249

1250
    state->push();
1251

1252
    SoMaterialBindingElement::set(state,SoMaterialBindingElement::OVERALL);
1253
    SoLazyElement::setEmissive(state, &color);
1254
    SoLazyElement::setDiffuse(state, node,1, &color, data->packer);
1255
    SoDrawStyleElement::set(state, node, SoDrawStyleElement::LINES);
1256
    SoLineWidthElement::set(state, node, 1.0f);
1257

1258
    const static float trans = 0.0;
1259
    SoLazyElement::setTransparency(state, node, 1, &trans, data->packer);
1260

1261
    float x, y, z;
1262
    bbox.getSize(x, y, z);
1263
    data->cube->width  = x+0.001;
1264
    data->cube->height  = y+0.001;
1265
    data->cube->depth = z+0.001;
1266

1267
    SoModelMatrixElement::translateBy(state,node,bbox.getCenter());
1268

1269
    SoMaterialBundle mb(action);
1270
    mb.sendFirst();
1271

1272
    data->cube->GLRender(action);
1273

1274
    state->pop();
1275
    return true;
1276
}
1277

1278
static std::time_t _CyclicLastReported;
1279

1280
void SoFCSelectionRoot::renderPrivate(SoGLRenderAction * action, bool inPath) {
1281
    if(ViewParams::instance()->getCoinCycleCheck()
1282
            && !SelStack.nodeSet.insert(this).second)
1283
    {
1284
        std::time_t t = std::time(nullptr);
1285
        if(_CyclicLastReported < t) {
1286
            _CyclicLastReported = t+5;
1287
            FC_ERR("Cyclic scene graph: " << getName());
1288
        }
1289
        return;
1290
    }
1291
    SelStack.push_back(this);
1292
    if(_renderPrivate(action,inPath)) {
1293
        if(inPath)
1294
            SoSeparator::GLRenderInPath(action);
1295
        else
1296
            SoSeparator::GLRenderBelowPath(action);
1297
    }
1298
    SelStack.pop_back();
1299
    SelStack.nodeSet.erase(this);
1300
}
1301

1302
bool SoFCSelectionRoot::_renderPrivate(SoGLRenderAction * action, bool inPath) {
1303
    auto ctx2 = std::static_pointer_cast<SelContext>(getNodeContext2(SelStack,this,SelContext::merge));
1304
    if(ctx2 && ctx2->hideAll)
1305
        return false;
1306

1307
    auto state = action->getState();
1308
    SelContextPtr ctx = getRenderContext<SelContext>(this);
1309
    int style = selectionStyle.getValue();
1310
    if((style==SoFCSelectionRoot::Box || ViewParams::instance()->getShowSelectionBoundingBox())
1311
       && ctx && !ctx->hideAll && (ctx->selAll || ctx->hlAll))
1312
    {
1313
        if (style==SoFCSelectionRoot::PassThrough) {
1314
            style = SoFCSelectionRoot::Box;
1315
        }
1316
        else {
1317
            renderBBox(action,this,ctx->hlAll?ctx->hlColor:ctx->selColor);
1318
            return true;
1319
        }
1320
    }
1321

1322
    // Here, we are not setting (pre)selection color override here.
1323
    // Instead, we are checking and setting up for any secondary context
1324
    // color override.
1325
    //
1326
    // When the current selection style is full highlight, we should ignore any
1327
    // secondary override. If the style is bounding box, however, we should
1328
    // honour the secondary color override.
1329

1330
    bool colorPushed = false;
1331
    if(!ShapeColorNode && overrideColor &&
1332
        !SoOverrideElement::getDiffuseColorOverride(state) &&
1333
        (style==SoFCSelectionRoot::Box || !ctx || (!ctx->selAll && !ctx->hideAll)))
1334
    {
1335
        ShapeColorNode = this;
1336
        colorPushed = true;
1337
        state->push();
1338
        auto &packer = ShapeColorNode->shapeColorPacker;
1339
        auto &trans = ShapeColorNode->transOverride;
1340
        auto &color = ShapeColorNode->colorOverride;
1341
        if(!SoOverrideElement::getTransparencyOverride(state) && trans) {
1342
            SoLazyElement::setTransparency(state, ShapeColorNode, 1, &trans, &packer);
1343
            SoOverrideElement::setTransparencyOverride(state,ShapeColorNode,true);
1344
        }
1345
        SoLazyElement::setDiffuse(state, ShapeColorNode, 1, &color, &packer);
1346
        SoOverrideElement::setDiffuseColorOverride(state,ShapeColorNode,true);
1347
        SoMaterialBindingElement::set(state, ShapeColorNode, SoMaterialBindingElement::OVERALL);
1348
        SoOverrideElement::setMaterialBindingOverride(state,ShapeColorNode,true);
1349

1350
        SoTextureEnabledElement::set(state,ShapeColorNode,false);
1351
    }
1352

1353
    if(!ctx) {
1354
        if(inPath)
1355
            SoSeparator::GLRenderInPath(action);
1356
        else
1357
            SoSeparator::GLRenderBelowPath(action);
1358
    } else {
1359
        bool selPushed;
1360
        bool hlPushed;
1361
        if((selPushed = ctx->selAll)) {
1362
            SelColorStack.push_back(ctx->selColor);
1363

1364
            if(style != SoFCSelectionRoot::Box) {
1365
                state->push();
1366
                auto &color = SelColorStack.back();
1367
                SoLazyElement::setEmissive(state, &color);
1368
                SoOverrideElement::setEmissiveColorOverride(state,this,true);
1369
                if (SoLazyElement::getLightModel(state) == SoLazyElement::BASE_COLOR) {
1370
                    auto &packer = shapeColorPacker;
1371
                    SoLazyElement::setDiffuse(state, this, 1, &color, &packer);
1372
                    SoOverrideElement::setDiffuseColorOverride(state,this,true);
1373
                    SoMaterialBindingElement::set(state, this, SoMaterialBindingElement::OVERALL);
1374
                    SoOverrideElement::setMaterialBindingOverride(state,this,true);
1375
                }
1376
            }
1377
        }
1378

1379
        if((hlPushed = ctx->hlAll))
1380
            HlColorStack.push_back(ctx->hlColor);
1381

1382
        if(inPath)
1383
            SoSeparator::GLRenderInPath(action);
1384
        else
1385
            SoSeparator::GLRenderBelowPath(action);
1386

1387
        if(selPushed) {
1388
            SelColorStack.pop_back();
1389

1390
            if(style != SoFCSelectionRoot::Box)
1391
                state->pop();
1392
        }
1393
        if(hlPushed)
1394
            HlColorStack.pop_back();
1395
    }
1396

1397
    if(colorPushed) {
1398
        ShapeColorNode = nullptr;
1399
        state->pop();
1400
    }
1401

1402
    return false;
1403
}
1404

1405
void SoFCSelectionRoot::GLRenderBelowPath(SoGLRenderAction * action) {
1406
    renderPrivate(action,false);
1407
}
1408

1409
void SoFCSelectionRoot::GLRenderInPath(SoGLRenderAction * action) {
1410
    if(action->getCurPathCode() == SoAction::BELOW_PATH)
1411
        return GLRenderBelowPath(action);
1412
    renderPrivate(action,true);
1413
}
1414

1415
bool SoFCSelectionRoot::checkColorOverride(SoState *state) {
1416
    if(ShapeColorNode) {
1417
        if(!SoOverrideElement::getDiffuseColorOverride(state)) {
1418
            state->push();
1419
            auto &packer = ShapeColorNode->shapeColorPacker;
1420
            auto &trans = ShapeColorNode->transOverride;
1421
            auto &color = ShapeColorNode->colorOverride;
1422
            if(!SoOverrideElement::getTransparencyOverride(state) && trans) {
1423
                SoLazyElement::setTransparency(state, ShapeColorNode, 1, &trans, &packer);
1424
                SoOverrideElement::setTransparencyOverride(state,ShapeColorNode,true);
1425
            }
1426
            SoLazyElement::setDiffuse(state, ShapeColorNode, 1, &color, &packer);
1427
            SoOverrideElement::setDiffuseColorOverride(state,ShapeColorNode,true);
1428
            SoMaterialBindingElement::set(state, ShapeColorNode, SoMaterialBindingElement::OVERALL);
1429
            SoOverrideElement::setMaterialBindingOverride(state,ShapeColorNode,true);
1430

1431
            SoTextureEnabledElement::set(state,ShapeColorNode,false);
1432
            return true;
1433
        }
1434
    }
1435
    return false;
1436
}
1437

1438
void SoFCSelectionRoot::checkSelection(bool &sel, SbColor &selColor, bool &hl, SbColor &hlColor) {
1439
    sel = false;
1440
    hl = false;
1441
    if((sel = !SelColorStack.empty()))
1442
        selColor = SelColorStack.back();
1443
    if((hl = !HlColorStack.empty()))
1444
        hlColor = HlColorStack.back();
1445
}
1446

1447
void SoFCSelectionRoot::resetContext() {
1448
    contextMap.clear();
1449
}
1450

1451
void SoFCSelectionRoot::moveActionStack(SoAction *from, SoAction *to, bool erase) {
1452
    auto it = ActionStacks.find(from);
1453
    if(it == ActionStacks.end())
1454
        return;
1455
    auto &stack = ActionStacks[to];
1456
    assert(stack.empty());
1457
    stack.swap(it->second);
1458
    if(erase)
1459
        ActionStacks.erase(it);
1460
}
1461

1462
#define BEGIN_ACTION \
1463
    auto &stack = ActionStacks[action];\
1464
    if(ViewParams::instance()->getCoinCycleCheck() \
1465
        && !stack.nodeSet.insert(this).second) \
1466
    {\
1467
        std::time_t t = std::time(0);\
1468
        if(_CyclicLastReported < t) {\
1469
            _CyclicLastReported = t+5;\
1470
            FC_ERR("Cyclic scene graph: " << getName());\
1471
        }\
1472
        return;\
1473
    }\
1474
    stack.push_back(this);\
1475
    auto size = stack.size();
1476

1477
#define END_ACTION \
1478
    if(stack.size()!=size || stack.back()!=this)\
1479
        FC_ERR("action stack fault");\
1480
    else {\
1481
        stack.nodeSet.erase(this);\
1482
        stack.pop_back();\
1483
        if(stack.empty())\
1484
            ActionStacks.erase(action);\
1485
    }
1486

1487
void SoFCSelectionRoot::pick(SoPickAction * action) {
1488
    BEGIN_ACTION;
1489
    if(doActionPrivate(stack,action))
1490
        inherited::pick(action);
1491
    END_ACTION;
1492
}
1493

1494
void SoFCSelectionRoot::rayPick(SoRayPickAction * action) {
1495
    BEGIN_ACTION;
1496
    if(doActionPrivate(stack,action))
1497
        inherited::rayPick(action);
1498
    END_ACTION;
1499
}
1500

1501
void SoFCSelectionRoot::handleEvent(SoHandleEventAction * action) {
1502
    BEGIN_ACTION;
1503
    inherited::handleEvent(action);
1504
    END_ACTION;
1505
}
1506

1507
void SoFCSelectionRoot::search(SoSearchAction * action) {
1508
    BEGIN_ACTION;
1509
    inherited::search(action);
1510
    END_ACTION;
1511
}
1512

1513
void SoFCSelectionRoot::getPrimitiveCount(SoGetPrimitiveCountAction * action) {
1514
    BEGIN_ACTION;
1515
    inherited::getPrimitiveCount(action);
1516
    END_ACTION;
1517
}
1518

1519
void SoFCSelectionRoot::getBoundingBox(SoGetBoundingBoxAction * action)
1520
{
1521
    BEGIN_ACTION;
1522
    if(doActionPrivate(stack,action))
1523
        inherited::getBoundingBox(action);
1524
    END_ACTION;
1525
}
1526

1527
void SoFCSelectionRoot::getMatrix(SoGetMatrixAction * action) {
1528
    BEGIN_ACTION;
1529
    if(doActionPrivate(stack,action))
1530
        inherited::getMatrix(action);
1531
    END_ACTION;
1532
}
1533

1534
void SoFCSelectionRoot::callback(SoCallbackAction *action) {
1535
    BEGIN_ACTION;
1536
    inherited::callback(action);
1537
    END_ACTION;
1538
}
1539

1540
void SoFCSelectionRoot::doAction(SoAction *action) {
1541
    BEGIN_ACTION
1542
    if(doActionPrivate(stack,action))
1543
        inherited::doAction(action);
1544
    END_ACTION
1545
}
1546

1547
bool SoFCSelectionRoot::doActionPrivate(Stack &stack, SoAction *action) {
1548
    // Selection action short-circuit optimization. In case of whole object
1549
    // selection/pre-selection, we shall store a SelContext keyed by ourself.
1550
    // And the action traversal can be short-curcuited once the first targeted
1551
    // SoFCSelectionRoot is found here. New function checkSelection() is exposed
1552
    // to check for whole object selection. This greatly improve performance on
1553
    // large group.
1554

1555
    SelContextPtr ctx2;
1556
    bool ctx2Searched = false;
1557
    bool isTail = false;
1558
    if(action->getCurPathCode()==SoAction::IN_PATH) {
1559
        auto path = action->getPathAppliedTo();
1560
        if(path) {
1561
            isTail = path->getTail()==this ||
1562
                     (path->getLength()>1
1563
                      && path->getNodeFromTail(1)==this
1564
                      && path->getTail()->isOfType(SoSwitch::getClassTypeId()));
1565
        }
1566

1567
        if(!action->isOfType(SoSelectionElementAction::getClassTypeId())) {
1568
            ctx2Searched = true;
1569
            ctx2 = std::static_pointer_cast<SelContext>(getNodeContext2(stack,this,SelContext::merge));
1570
            if(ctx2 && ctx2->hideAll)
1571
                return false;
1572
        }
1573
        if(!isTail)
1574
            return true;
1575
    }else if(action->getWhatAppliedTo()!=SoAction::NODE && action->getCurPathCode()!=SoAction::BELOW_PATH)
1576
        return true;
1577

1578
    if(action->isOfType(SoSelectionElementAction::getClassTypeId())) {
1579
        auto selAction = static_cast<SoSelectionElementAction*>(action);
1580
        if(selAction->isSecondary()) {
1581
            if(selAction->getType() == SoSelectionElementAction::Show ||
1582
               (selAction->getType() == SoSelectionElementAction::Color &&
1583
                selAction->getColors().empty() &&
1584
                action->getWhatAppliedTo()==SoAction::NODE))
1585
            {
1586
                auto ctx = getActionContext(action,this,SelContextPtr(),false);
1587
                if(ctx && ctx->hideAll) {
1588
                    ctx->hideAll = false;
1589
                    if(!ctx->hlAll && !ctx->selAll)
1590
                        removeActionContext(action,this);
1591
                    touch();
1592
                }
1593
                // applied to a node means clear all visibility setting, so
1594
                // return true to propagate the action
1595
                return selAction->getType()==SoSelectionElementAction::Color ||
1596
                       action->getWhatAppliedTo()==SoAction::NODE;
1597

1598
            }else if(selAction->getType() == SoSelectionElementAction::Hide) {
1599
                if(action->getCurPathCode()==SoAction::BELOW_PATH || isTail) {
1600
                    auto ctx = getActionContext(action,this,SelContextPtr());
1601
                    if(ctx && !ctx->hideAll) {
1602
                        ctx->hideAll = true;
1603
                        touch();
1604
                    }
1605
                    return false;
1606
                }
1607
            }
1608
            return true;
1609
        }
1610

1611
        if(selAction->getType() == SoSelectionElementAction::None) {
1612
            if(action->getWhatAppliedTo() == SoAction::NODE) {
1613
                // Here the 'select none' action is applied to a node, and we
1614
                // are the first SoFCSelectionRoot encountered (which means all
1615
                // children stores selection context here, both whole object
1616
                // and element selection), then we can simply perform the
1617
                // action by clearing the selection context here, and save the
1618
                // time for traversing a potentially large amount of children
1619
                // nodes.
1620
                resetContext();
1621
                touch();
1622
                return false;
1623
            }
1624

1625
            auto ctx = getActionContext(action,this,SelContextPtr(),false);
1626
            if(ctx && ctx->selAll) {
1627
                ctx->selAll = false;
1628
                touch();
1629
                return false;
1630
            }
1631
        } else if(selAction->getType() == SoSelectionElementAction::All) {
1632
            auto ctx = getActionContext(action,this,SelContextPtr());
1633
            assert(ctx);
1634
            ctx->selAll = true;
1635
            ctx->selColor = selAction->getColor();
1636
            touch();
1637
            return false;
1638
        }
1639
        return true;
1640
    }
1641

1642
    if(action->isOfType(SoHighlightElementAction::getClassTypeId())) {
1643
        auto hlAction = static_cast<SoHighlightElementAction*>(action);
1644
        if(hlAction->isHighlighted()) {
1645
            if(hlAction->getElement()) {
1646
                auto ctx = getActionContext(action,this,SelContextPtr(),false);
1647
                if(ctx && ctx->hlAll) {
1648
                    ctx->hlAll = false;
1649
                    touch();
1650
                }
1651
            } else {
1652
                auto ctx = getActionContext(action,this,SelContextPtr());
1653
                assert(ctx);
1654
                ctx->hlAll = true;
1655
                ctx->hlColor = hlAction->getColor();
1656
                touch();
1657
                return false;
1658
            }
1659
        } else {
1660
            auto ctx = getActionContext(action,this,SelContextPtr(),false);
1661
            if(ctx && ctx->hlAll) {
1662
                ctx->hlAll = false;
1663
                touch();
1664
                return false;
1665
            }
1666
        }
1667
        return true;
1668
    }
1669

1670
    if(!ctx2Searched) {
1671
        ctx2 = std::static_pointer_cast<SelContext>(getNodeContext2(stack,this,SelContext::merge));
1672
        if(ctx2 && ctx2->hideAll)
1673
            return false;
1674
    }
1675
    return true;
1676
}
1677

1678
int SoFCSelectionRoot::SelContext::merge(int status, SoFCSelectionContextBasePtr &output,
1679
        SoFCSelectionContextBasePtr input, SoNode *)
1680
{
1681
    auto ctx = std::dynamic_pointer_cast<SelContext>(input);
1682
    if(ctx && ctx->hideAll) {
1683
        output = ctx;
1684
        return -1;
1685
    }
1686
    return status;
1687
}
1688

1689
/////////////////////////////////////////////////////////////////////////////
1690

1691
SO_NODE_SOURCE(SoFCPathAnnotation)
1692

1693
SoFCPathAnnotation::SoFCPathAnnotation()
1694
{
1695
    SO_NODE_CONSTRUCTOR(SoFCPathAnnotation);
1696
    path = nullptr;
1697
    tmpPath = nullptr;
1698
    det = nullptr;
1699
}
1700

1701
SoFCPathAnnotation::~SoFCPathAnnotation()
1702
{
1703
    if(path) path->unref();
1704
    if(tmpPath) tmpPath->unref();
1705
    delete det;
1706
}
1707

1708
void SoFCPathAnnotation::finish()
1709
{
1710
    atexit_cleanup();
1711
}
1712

1713
void SoFCPathAnnotation::initClass()
1714
{
1715
    SO_NODE_INIT_CLASS(SoFCPathAnnotation,SoSeparator,"Separator");
1716
}
1717

1718
void SoFCPathAnnotation::GLRender(SoGLRenderAction * action)
1719
{
1720
    switch (action->getCurPathCode()) {
1721
    case SoAction::NO_PATH:
1722
    case SoAction::BELOW_PATH:
1723
        this->GLRenderBelowPath(action);
1724
        break;
1725
    case SoAction::OFF_PATH:
1726
        break;
1727
    case SoAction::IN_PATH:
1728
        this->GLRenderInPath(action);
1729
        break;
1730
    }
1731
}
1732

1733
void SoFCPathAnnotation::GLRenderBelowPath(SoGLRenderAction * action)
1734
{
1735
    if(!path || !path->getLength() || !tmpPath || !tmpPath->getLength())
1736
        return;
1737

1738
    if(path->getLength() != tmpPath->getLength()) {
1739
        // The auditing SoPath may be truncated due to harmless things such as
1740
        // flipping a SoSwitch sibling node. So we keep an unauditing SoTempPath
1741
        // around to try to restore the path.
1742
        for(int i=path->getLength()-1;i<tmpPath->getLength()-1;++i) {
1743
            auto children = path->getNode(i)->getChildren();
1744
            if(children) {
1745
                int idx = children->find(tmpPath->getNode(i+1));
1746
                if(idx >= 0) {
1747
                    path->append(idx);
1748
                    continue;
1749
                }
1750
            }
1751
            tmpPath->unref();
1752
            tmpPath = nullptr;
1753
            return;
1754
        }
1755
    }
1756

1757
    SoState * state = action->getState();
1758
    SoGLCacheContextElement::shouldAutoCache(state, SoGLCacheContextElement::DONT_AUTO_CACHE);
1759

1760
    if (action->isRenderingDelayedPaths()) {
1761
        SbBool zbenabled = glIsEnabled(GL_DEPTH_TEST);
1762
        if (zbenabled) glDisable(GL_DEPTH_TEST);
1763

1764
        if(det)
1765
            inherited::GLRenderInPath(action);
1766
        else {
1767
            bool bbox = ViewParams::instance()->getShowSelectionBoundingBox();
1768
            if(!bbox) {
1769
                for(int i=0,count=path->getLength();i<count;++i) {
1770
                    if(!path->getNode(i)->isOfType(SoFCSelectionRoot::getClassTypeId()))
1771
                        continue;
1772
                    auto node = dynamic_cast<SoFCSelectionRoot*>(path->getNode(i));
1773
                    if (node != nullptr
1774
                        && node->selectionStyle.getValue() == SoFCSelectionRoot::Box) {
1775
                        bbox = true;
1776
                        break;
1777
                    }
1778
                }
1779
            }
1780
            if(!bbox)
1781
                inherited::GLRenderInPath(action);
1782
            else {
1783
                bool sel = false;
1784
                bool hl = false;
1785
                SbColor selColor,hlColor;
1786
                SoFCSelectionRoot::checkSelection(sel,selColor,hl,hlColor);
1787
                if(sel || hl)
1788
                    SoFCSelectionRoot::renderBBox(action,this,hl?hlColor:selColor);
1789
                else
1790
                    inherited::GLRenderInPath(action);
1791
            }
1792
        }
1793

1794
        if (zbenabled) glEnable(GL_DEPTH_TEST);
1795

1796
    } else {
1797
        SoCacheElement::invalidate(action->getState());
1798
        auto curPath = action->getCurPath();
1799
        auto newPath = new SoPath(curPath->getLength()+path->getLength());
1800
        newPath->append(curPath);
1801
        newPath->append(path);
1802
        action->addDelayedPath(newPath);
1803
    }
1804
}
1805

1806
void SoFCPathAnnotation::GLRenderInPath(SoGLRenderAction * action)
1807
{
1808
    GLRenderBelowPath(action);
1809
}
1810

1811
void SoFCPathAnnotation::setDetail(SoDetail *d) {
1812
    if(d!=det) {
1813
        delete det;
1814
        det = d;
1815
    }
1816
}
1817

1818
void SoFCPathAnnotation::setPath(SoPath *newPath) {
1819
    if(path) {
1820
        path->unref();
1821
        coinRemoveAllChildren(this);
1822
        path = nullptr;
1823
        if(tmpPath) {
1824
            tmpPath->unref();
1825
            tmpPath = nullptr;
1826
        }
1827
    }
1828
    if(!newPath || !newPath->getLength())
1829
        return;
1830

1831
    tmpPath = new SoTempPath(newPath->getLength());
1832
    tmpPath->ref();
1833
    for(int i=0;i<newPath->getLength();++i)
1834
        tmpPath->append(newPath->getNode(i));
1835
    path = newPath->copy();
1836
    path->ref();
1837
    addChild(path->getNode(0));
1838
}
1839

1840
void SoFCPathAnnotation::getBoundingBox(SoGetBoundingBoxAction * action)
1841
{
1842
    if(path) {
1843
        SoGetBoundingBoxAction bboxAction(action->getViewportRegion());
1844
        SoFCSelectionRoot::moveActionStack(action,&bboxAction,false);
1845
        bboxAction.apply(path);
1846
        SoFCSelectionRoot::moveActionStack(&bboxAction,action,true);
1847
        auto bbox = bboxAction.getBoundingBox();
1848
        if(!bbox.isEmpty())
1849
            action->extendBy(bbox);
1850
    }
1851
}
1852

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

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

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

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