1
/***************************************************************************
2
* Copyright (c) 2005 Jürgen Riegel <juergen.riegel@web.de> *
4
* This file is part of the FreeCAD CAx development system. *
6
* This library is free software; you can redistribute it and/or *
7
* modify it under the terms of the GNU Library General Public *
8
* License as published by the Free Software Foundation; either *
9
* version 2 of the License, or (at your option) any later version. *
11
* This library is distributed in the hope that it will be useful, *
12
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
13
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
14
* GNU Library General Public License for more details. *
16
* You should have received a copy of the GNU Library General Public *
17
* License along with this library; see the file COPYING.LIB. If not, *
18
* write to the Free Software Foundation, Inc., 59 Temple Place, *
19
* Suite 330, Boston, MA 02111-1307, USA *
21
***************************************************************************/
23
#include "PreCompiled.h"
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>
65
# include <OpenGL/gl.h>
73
#include <App/Document.h>
74
#include <App/ElementNamingUtils.h>
75
#include <Base/Tools.h>
76
#include <Base/UnitsApi.h>
78
#include "SoFCUnifiedSelection.h"
79
#include "Application.h"
81
#include "MainWindow.h"
82
#include "SoFCInteractiveElement.h"
83
#include "SoFCSelectionAction.h"
84
#include "ViewParams.h"
85
#include "ViewProvider.h"
86
#include "ViewProviderDocumentObject.h"
89
FC_LOG_LEVEL_INIT("SoFCUnifiedSelection",false,true,true)
94
void printPreselectionInfo(const char* documentName,
95
const char* objectName,
96
const char* subElementName,
97
float x, float y, float z,
101
SoFullPath * Gui::SoFCUnifiedSelection::currenthighlight = nullptr;
103
// *************************************************************************
105
SO_NODE_SOURCE(SoFCUnifiedSelection)
110
SoFCUnifiedSelection::SoFCUnifiedSelection()
112
SO_NODE_CONSTRUCTOR(SoFCUnifiedSelection);
114
SO_NODE_ADD_FIELD(colorHighlight, (SbColor(1.0f, 0.6f, 0.0f)));
115
SO_NODE_ADD_FIELD(colorSelection, (SbColor(0.1f, 0.8f, 0.1f)));
116
SO_NODE_ADD_FIELD(highlightMode, (AUTO));
117
SO_NODE_ADD_FIELD(selectionMode, (ON));
118
SO_NODE_ADD_FIELD(selectionRole, (true));
119
SO_NODE_ADD_FIELD(useNewSelection, (true));
121
SO_NODE_DEFINE_ENUM_VALUE(HighlightModes, AUTO);
122
SO_NODE_DEFINE_ENUM_VALUE(HighlightModes, ON);
123
SO_NODE_DEFINE_ENUM_VALUE(HighlightModes, OFF);
124
SO_NODE_SET_SF_ENUM_TYPE (highlightMode, HighlightModes);
126
// Documentation of SoFullPath:
127
// Since the SoFullPath is derived from SoPath and contains no private data, you can cast SoPath instances to the SoFullPath type.
128
// This will allow you to examine hidden children. Actually, you are not supposed to allocate instances of this class at all.
129
// It is only available as an "extended interface" into the superclass SoPath.
130
detailPath = static_cast<SoFullPath*>(new SoPath(20));
133
setPreSelection = false;
135
useNewSelection = ViewParams::instance()->getUseNewSelection();
141
SoFCUnifiedSelection::~SoFCUnifiedSelection()
143
// If we're being deleted and we're the current highlight,
144
// NULL out that variable
145
if (currenthighlight) {
146
currenthighlight->unref();
147
currenthighlight = nullptr;
151
detailPath = nullptr;
157
SoFCUnifiedSelection::initClass()
159
SO_NODE_INIT_CLASS(SoFCUnifiedSelection,SoSeparator,"Separator");
162
void SoFCUnifiedSelection::finish()
167
bool SoFCUnifiedSelection::hasHighlight() {
168
return currenthighlight != nullptr;
171
void SoFCUnifiedSelection::applySettings()
174
ParameterGrp::handle hGrp = Gui::WindowParameter::getDefaultParameter()->GetGroup("View");
175
bool enablePre = hGrp->GetBool("EnablePreselection", true);
176
bool enableSel = hGrp->GetBool("EnableSelection", true);
178
this->highlightMode = SoFCUnifiedSelection::OFF;
181
// Search for a user defined value with the current color as default
182
SbColor highlightColor = this->colorHighlight.getValue();
183
auto highlight = (unsigned long)(highlightColor.getPackedValue());
184
highlight = hGrp->GetUnsigned("HighlightColor", highlight);
185
highlightColor.setPackedValue((uint32_t)highlight, transparency);
186
this->colorHighlight.setValue(highlightColor);
190
this->selectionMode = SoFCUnifiedSelection::OFF;
193
// Do the same with the selection color
194
SbColor selectionColor = this->colorSelection.getValue();
195
auto selection = (unsigned long)(selectionColor.getPackedValue());
196
selection = hGrp->GetUnsigned("SelectionColor", selection);
197
selectionColor.setPackedValue((uint32_t)selection, transparency);
198
this->colorSelection.setValue(selectionColor);
202
const char* SoFCUnifiedSelection::getFileFormatName() const
207
void SoFCUnifiedSelection::write(SoWriteAction * action)
209
SoOutput * out = action->getOutput();
210
if (out->getStage() == SoOutput::WRITE) {
211
// Do not write out the fields of this class
212
if (this->writeHeader(out, true, false))
214
SoGroup::doAction(static_cast<SoAction *>(action));
215
this->writeFooter(out);
218
inherited::write(action);
222
int SoFCUnifiedSelection::getPriority(const SoPickedPoint* p)
224
const SoDetail* detail = p->getDetail();
227
if (detail->isOfType(SoFaceDetail::getClassTypeId()))
229
if (detail->isOfType(SoLineDetail::getClassTypeId()))
231
if (detail->isOfType(SoPointDetail::getClassTypeId()))
236
std::vector<SoFCUnifiedSelection::PickedInfo>
237
SoFCUnifiedSelection::getPickedList(SoHandleEventAction* action, bool singlePick) const
239
ViewProvider *last_vp = nullptr;
240
std::vector<PickedInfo> ret;
241
const SoPickedPointList & points = action->getPickedPointList();
242
for(int i=0,count=points.getLength();i<count;++i) {
246
ViewProvider *vp = nullptr;
247
auto path = static_cast<SoFullPath *>(info.pp->getPath());
248
if (this->pcDocument && path && path->containsPath(action->getCurPath())) {
249
vp = this->pcDocument->getViewProviderByPathFromHead(path);
250
if(singlePick && last_vp && last_vp!=vp)
253
if(!vp || !vp->isDerivedFrom(ViewProviderDocumentObject::getClassTypeId())) {
254
if(!singlePick) continue;
259
info.vpd = static_cast<ViewProviderDocumentObject*>(vp);
260
if(!(useNewSelection.getValue()||info.vpd->useNewSelectionModel()) || !info.vpd->isSelectable()) {
261
if(!singlePick) continue;
268
if(!info.vpd->getElementPicked(info.pp,info.element))
279
// To identify the picking of lines in a concave area we have to
280
// get all intersection points. If we have two or more intersection
281
// points where the first is of a face and the second of a line with
282
// almost similar coordinates we use the second point, instead.
284
int picked_prio = getPriority(ret[0].pp);
285
auto last_vpd = ret[0].vpd;
286
const SbVec3f& picked_pt = ret.front().pp->getPoint();
287
auto itPicked = ret.begin();
288
for(auto it=ret.begin()+1;it!=ret.end();++it) {
290
if(last_vpd != info.vpd)
293
int cur_prio = getPriority(info.pp);
294
const SbVec3f& cur_pt = info.pp->getPoint();
296
if ((cur_prio > picked_prio) && picked_pt.equals(cur_pt, 0.2F)) {
298
picked_prio = cur_prio;
303
std::vector<PickedInfo> sret(itPicked,itPicked+1);
306
if(itPicked != ret.begin())
307
std::swap(*itPicked, *ret.begin());
311
void SoFCUnifiedSelection::doAction(SoAction *action)
313
if (action->getTypeId() == SoFCEnableHighlightAction::getClassTypeId()) {
314
auto preaction = static_cast<SoFCEnableHighlightAction*>(action);
315
if (preaction->highlight) {
316
this->highlightMode = SoFCUnifiedSelection::AUTO;
319
this->highlightMode = SoFCUnifiedSelection::OFF;
323
if (action->getTypeId() == SoFCEnableSelectionAction::getClassTypeId()) {
324
auto selaction = static_cast<SoFCEnableSelectionAction*>(action);
325
if (selaction->selection) {
326
this->selectionMode = SoFCUnifiedSelection::ON;
329
this->selectionMode = SoFCUnifiedSelection::OFF;
333
if (action->getTypeId() == SoFCSelectionColorAction::getClassTypeId()) {
334
auto colaction = static_cast<SoFCSelectionColorAction*>(action);
335
this->colorSelection = colaction->selectionColor;
338
if (action->getTypeId() == SoFCHighlightColorAction::getClassTypeId()) {
339
auto colaction = static_cast<SoFCHighlightColorAction*>(action);
340
this->colorHighlight = colaction->highlightColor;
343
if (action->getTypeId() == SoFCHighlightAction::getClassTypeId()) {
344
auto hilaction = static_cast<SoFCHighlightAction*>(action);
345
// Do not clear currently highlighted object when setting new pre-selection
346
if (!setPreSelection && hilaction->SelChange.Type == SelectionChanges::RmvPreselect) {
347
if (currenthighlight) {
348
SoHighlightElementAction hlAction;
349
hlAction.apply(currenthighlight);
350
currenthighlight->unref();
351
currenthighlight = nullptr;
354
else if (highlightMode.getValue() != OFF
355
&& hilaction->SelChange.Type == SelectionChanges::SetPreselect) {
356
if (currenthighlight) {
357
SoHighlightElementAction hlAction;
358
hlAction.apply(currenthighlight);
359
currenthighlight->unref();
360
currenthighlight = nullptr;
363
App::Document* doc = App::GetApplication().getDocument(hilaction->SelChange.pDocName);
364
App::DocumentObject* obj = doc->getObject(hilaction->SelChange.pObjectName);
365
ViewProvider*vp = Application::Instance->getViewProvider(obj);
366
SoDetail* detail = vp->getDetail(hilaction->SelChange.pSubName);
368
SoHighlightElementAction hlAction;
369
hlAction.setHighlighted(true);
370
hlAction.setColor(this->colorHighlight.getValue());
371
hlAction.setElement(detail);
372
hlAction.apply(vp->getRoot());
376
sa.setNode(vp->getRoot());
377
sa.apply(vp->getRoot());
378
currenthighlight = static_cast<SoFullPath*>(sa.getPath()->copy());
379
currenthighlight->ref();
382
if (useNewSelection.getValue())
386
if (action->getTypeId() == SoFCSelectionAction::getClassTypeId()) {
387
auto selaction = static_cast<SoFCSelectionAction*>(action);
388
if(selectionMode.getValue() == ON
389
&& (selaction->SelChange.Type == SelectionChanges::AddSelection
390
|| selaction->SelChange.Type == SelectionChanges::RmvSelection))
392
// selection changes inside the 3d view are handled in handleEvent()
393
App::Document* doc = App::GetApplication().getDocument(selaction->SelChange.pDocName);
394
App::DocumentObject* obj = doc->getObject(selaction->SelChange.pObjectName);
395
ViewProvider*vp = Application::Instance->getViewProvider(obj);
396
if (vp && (useNewSelection.getValue()||vp->useNewSelectionModel()) && vp->isSelectable()) {
397
SoDetail *detail = nullptr;
398
detailPath->truncate(0);
399
if(!selaction->SelChange.pSubName || !selaction->SelChange.pSubName[0] ||
400
vp->getDetailPath(selaction->SelChange.pSubName,detailPath,true,detail))
402
SoSelectionElementAction::Type type = SoSelectionElementAction::None;
403
if (selaction->SelChange.Type == SelectionChanges::AddSelection) {
405
type = SoSelectionElementAction::Append;
407
type = SoSelectionElementAction::All;
411
type = SoSelectionElementAction::Remove;
413
type = SoSelectionElementAction::None;
416
SoSelectionElementAction selectionAction(type);
417
selectionAction.setColor(this->colorSelection.getValue());
418
selectionAction.setElement(detail);
419
if(detailPath->getLength())
420
selectionAction.apply(detailPath);
422
selectionAction.apply(vp->getRoot());
424
detailPath->truncate(0);
428
else if (selaction->SelChange.Type == SelectionChanges::ClrSelection) {
429
SoSelectionElementAction selectionAction(SoSelectionElementAction::None);
430
for(int i=0;i<this->getNumChildren();++i)
431
selectionAction.apply(this->getChild(i));
433
else if(selectionMode.getValue() == ON
434
&& selaction->SelChange.Type == SelectionChanges::SetSelection) {
435
std::vector<ViewProvider*> vps;
436
if (this->pcDocument)
437
vps = this->pcDocument->getViewProvidersOfType(ViewProviderDocumentObject::getClassTypeId());
438
for (const auto & vp : vps) {
439
auto vpd = static_cast<ViewProviderDocumentObject*>(vp);
440
if (useNewSelection.getValue() || vpd->useNewSelectionModel()) {
441
SoSelectionElementAction::Type type;
442
if(Selection().isSelected(vpd->getObject()) && vpd->isSelectable())
443
type = SoSelectionElementAction::All;
445
type = SoSelectionElementAction::None;
447
SoSelectionElementAction selectionAction(type);
448
selectionAction.setColor(this->colorSelection.getValue());
449
selectionAction.apply(vpd->getRoot());
453
else if (selaction->SelChange.Type == SelectionChanges::SetPreselectSignal) {
454
// selection changes inside the 3d view are handled in handleEvent()
455
App::Document* doc = App::GetApplication().getDocument(selaction->SelChange.pDocName);
456
App::DocumentObject* obj = doc->getObject(selaction->SelChange.pObjectName);
457
ViewProvider*vp = Application::Instance->getViewProvider(obj);
458
if (vp && vp->isDerivedFrom(ViewProviderDocumentObject::getClassTypeId()) &&
459
(useNewSelection.getValue()||vp->useNewSelectionModel()) && vp->isSelectable())
461
detailPath->truncate(0);
462
SoDetail *det = nullptr;
463
if(vp->getDetailPath(selaction->SelChange.pSubName,detailPath,true,det)) {
464
setHighlight(detailPath,det,static_cast<ViewProviderDocumentObject*>(vp),
465
selaction->SelChange.pSubName,
466
selaction->SelChange.x,
467
selaction->SelChange.y,
468
selaction->SelChange.z);
474
if (useNewSelection.getValue())
478
inherited::doAction( action );
481
bool SoFCUnifiedSelection::setHighlight(const PickedInfo &info) {
483
return setHighlight(nullptr,nullptr,nullptr,nullptr,0.0,0.0,0.0);
484
const auto &pt = info.pp->getPoint();
485
return setHighlight(static_cast<SoFullPath*>(info.pp->getPath()),
486
info.pp->getDetail(), info.vpd, info.element.c_str(), pt[0],pt[1],pt[2]);
489
bool SoFCUnifiedSelection::setHighlight(SoFullPath *path, const SoDetail *det,
490
ViewProviderDocumentObject *vpd, const char *element, float x, float y, float z)
492
Base::FlagToggler<SbBool> flag(setPreSelection);
494
bool highlighted = false;
495
if(path && path->getLength() &&
496
vpd && vpd->getObject() && vpd->getObject()->isAttachedToDocument())
498
const char *docname = vpd->getObject()->getDocument()->getName();
499
const char *objname = vpd->getObject()->getNameInDocument();
501
this->preSelection = 1;
503
printPreselectionInfo(docname, objname, element, x, y, z, 1e-7);
506
int ret = Gui::Selection().setPreselect(docname,objname,element,x,y,z);
507
if(ret<0 && currenthighlight)
511
if (currenthighlight) {
512
SoHighlightElementAction action;
513
action.setHighlighted(false);
514
action.apply(currenthighlight);
515
currenthighlight->unref();
516
currenthighlight = nullptr;
518
currenthighlight = static_cast<SoFullPath*>(path->copy());
519
currenthighlight->ref();
524
if(currenthighlight) {
525
SoHighlightElementAction action;
526
action.setHighlighted(highlighted);
527
action.setColor(this->colorHighlight.getValue());
528
action.setElement(det);
529
action.apply(currenthighlight);
531
currenthighlight->unref();
532
currenthighlight = nullptr;
533
Selection().rmvPreselect();
540
bool SoFCUnifiedSelection::setSelection(const std::vector<PickedInfo> &infos, bool ctrlDown) {
541
if(infos.empty() || !infos[0].vpd)
544
std::vector<SelectionSingleton::SelObj> sels;
546
for(auto &info : infos) {
547
if(!info.vpd) continue;
549
SelectionSingleton::SelObj sel;
550
sel.pResolvedObject = nullptr;
551
sel.pObject = info.vpd->getObject();
552
sel.pDoc = sel.pObject->getDocument();
553
sel.DocName = sel.pDoc->getName();
554
sel.FeatName = sel.pObject->getNameInDocument();
555
sel.TypeName = sel.pObject->getTypeId().getName();
556
sel.SubName = info.element.c_str();
557
const auto &pt = info.pp->getPoint();
565
const auto &info = infos[0];
569
if(!vpd->getObject()->isAttachedToDocument())
571
const char *objname = vpd->getObject()->getNameInDocument();
572
const char *docname = vpd->getObject()->getDocument()->getName();
574
bool hasNext = false;
575
const SoPickedPoint * pp = info.pp;
576
const SoDetail *det = pp->getDetail();
577
SoDetail *detNext = nullptr;
578
auto pPath = static_cast<SoFullPath*>(pp->getPath());
579
const auto &pt = pp->getPoint();
580
SoSelectionElementAction::Type type = SoSelectionElementAction::None;
581
auto mymode = static_cast<HighlightModes>(this->highlightMode.getValue());
582
static char buf[513];
585
if(Gui::Selection().isSelected(docname, objname, info.element.c_str(), ResolveMode::NoResolve))
586
Gui::Selection().rmvSelection(docname, objname,info.element.c_str(), &sels);
588
bool ok = Gui::Selection().addSelection(docname,objname,
589
info.element.c_str(), pt[0] ,pt[1] ,pt[2], &sels);
590
if (ok && mymode == OFF) {
591
snprintf(buf,512,"Selected: %s.%s.%s (%g, %g, %g)",
592
docname,objname,info.element.c_str()
593
,fabs(pt[0])>1e-7?pt[0]:0.0
594
,fabs(pt[1])>1e-7?pt[1]:0.0
595
,fabs(pt[2])>1e-7?pt[2]:0.0);
597
getMainWindow()->showMessage(QString::fromLatin1(buf));
603
// Hierarchy ascending
605
// If the clicked subelement is already selected, check if there is an
606
// upper hierarchy, and select that hierarchy instead.
608
// For example, let's suppose PickedInfo above reports
609
// 'link.link2.box.Face1', and below Selection().getSelectedElement returns
610
// 'link.link2.box.', meaning that 'box' is the current selected hierarchy,
611
// and the user is clicking the box again. So we shall go up one level,
612
// and select 'link.link2.'
615
std::string subName = info.element;
616
std::string objectName = objname;
618
const char *subSelected = Gui::Selection().getSelectedElement(
619
vpd->getObject(),subName.c_str());
621
FC_TRACE("select " << (subSelected?subSelected:"'null'") << ", " <<
622
objectName << ", " << subName);
623
std::string newElement;
625
newElement = Data::newElementName(subSelected);
626
subSelected = newElement.c_str();
628
const char *next = strrchr(subSelected,'.');
629
if(next && next!=subSelected) {
631
// The convention of dot separated SubName demands a mandatory
632
// ending dot for every object name reference inside SubName.
633
// The non-object sub-element, however, must not end with a dot.
634
// So, next[1]==0 here means current selection is a whole object
635
// selection (because no sub-element), so we shall search
636
// upwards for the second last dot, which is the end of the
637
// parent name of the current selected object
638
for(--next;next!=subSelected;--next) {
639
if(*next == '.') break;
643
nextsub = std::string(subSelected,next-subSelected+1);
645
if(nextsub.length() || *subSelected!=0) {
648
detailPath->truncate(0);
649
if(vpd->getDetailPath(subName.c_str(),detailPath,true,detNext) &&
650
detailPath->getLength())
654
FC_TRACE("select next " << objectName << ", " << subName);
659
FC_TRACE("clearing selection");
660
Gui::Selection().clearSelection();
661
FC_TRACE("add selection");
662
bool ok = Gui::Selection().addSelection(docname, objectName.c_str() ,subName.c_str(),
663
pt[0] ,pt[1] ,pt[2], &sels);
665
type = hasNext?SoSelectionElementAction::All:SoSelectionElementAction::Append;
668
snprintf(buf,512,"Selected: %s.%s.%s (%g, %g, %g)",
669
docname, objectName.c_str() ,subName.c_str()
670
,fabs(pt[0])>1e-7?pt[0]:0.0
671
,fabs(pt[1])>1e-7?pt[1]:0.0
672
,fabs(pt[2])>1e-7?pt[2]:0.0);
674
getMainWindow()->showMessage(QString::fromLatin1(buf));
678
FC_TRACE("applying action");
679
SoSelectionElementAction action(type);
680
action.setColor(this->colorSelection.getValue());
681
action.setElement(det);
683
FC_TRACE("applied action");
687
if(detNext) delete detNext;
693
SoFCUnifiedSelection::handleEvent(SoHandleEventAction * action)
695
// If off then don't handle this event
696
if (!selectionRole.getValue()) {
697
inherited::handleEvent(action);
701
auto mymode = static_cast<HighlightModes>(this->highlightMode.getValue());
702
const SoEvent * event = action->getEvent();
704
// If we don't need to pick for locate highlighting,
705
// then just behave as separator and return.
706
// NOTE: we still have to pick for ON even though we don't have
707
// to re-render, because the app needs to be notified as the mouse
708
// goes over locate highlight nodes.
709
//if (highlightMode.getValue() == OFF) {
710
// inherited::handleEvent( action );
715
// If this is a mouseMotion event, then check for locate highlighting
717
if (event->isOfType(SoLocation2Event::getClassTypeId())) {
718
// NOTE: If preselection is off then we do not check for a picked point because otherwise this search may slow
719
// down extremely the system on really big data sets. In this case we just check for a picked point if the data
720
// set has been selected.
721
if (mymode == AUTO || mymode == ON) {
722
// check to see if the mouse is over our geometry...
723
auto infos = this->getPickedList(action,true);
725
setHighlight(infos[0]);
727
setHighlight(PickedInfo());
728
if (this->preSelection > 0) {
729
this->preSelection = 0;
730
// touch() makes sure to call GLRenderBelowPath so that the cursor can be updated
731
// because only from there the SoGLWidgetElement delivers the OpenGL window
737
// mouse press events for (de)selection
738
else if (event->isOfType(SoMouseButtonEvent::getClassTypeId()) &&
739
selectionMode.getValue() == SoFCUnifiedSelection::ON) {
740
const auto e = static_cast<const SoMouseButtonEvent *>(event);
741
if (SoMouseButtonEvent::isButtonReleaseEvent(e,SoMouseButtonEvent::BUTTON1)) {
742
// check to see if the mouse is over a geometry...
743
auto infos = this->getPickedList(action,!Selection().needPickedList());
744
bool greedySel = Gui::Selection().getSelectionStyle() == Gui::SelectionSingleton::SelectionStyle::GreedySelection;
745
if(setSelection(infos, event->wasCtrlDown() || greedySel))
746
action->setHandled();
750
inherited::handleEvent(action);
753
void SoFCUnifiedSelection::GLRenderBelowPath(SoGLRenderAction * action)
755
inherited::GLRenderBelowPath(action);
757
// nothing picked, so restore the arrow cursor if needed
758
if (this->preSelection == 0) {
759
// this is called when a selection gate forbade to select an object
760
// and the user moved the mouse to an empty area
761
this->preSelection = -1;
763
SoState *state = action->getState();
764
SoGLWidgetElement::get(state, window);
765
QWidget* parent = window ? window->parentWidget() : nullptr;
767
QCursor c = parent->cursor();
768
if (c.shape() == Qt::ForbiddenCursor) {
769
c.setShape(Qt::ArrowCursor);
770
parent->setCursor(c);
776
// ---------------------------------------------------------------
778
SO_ACTION_SOURCE(SoHighlightElementAction)
780
void SoHighlightElementAction::initClass()
782
SO_ACTION_INIT_CLASS(SoHighlightElementAction,SoAction);
784
SO_ENABLE(SoHighlightElementAction, SoSwitchElement);
785
SO_ENABLE(SoHighlightElementAction, SoModelMatrixElement);
787
SO_ACTION_ADD_METHOD(SoNode,nullAction);
789
SO_ENABLE(SoHighlightElementAction, SoCoordinateElement);
791
SO_ACTION_ADD_METHOD(SoGroup,callDoAction);
792
SO_ACTION_ADD_METHOD(SoIndexedLineSet,callDoAction);
793
SO_ACTION_ADD_METHOD(SoIndexedFaceSet,callDoAction);
794
SO_ACTION_ADD_METHOD(SoPointSet,callDoAction);
797
SoHighlightElementAction::SoHighlightElementAction ()
799
SO_ACTION_CONSTRUCTOR(SoHighlightElementAction);
802
SoHighlightElementAction::~SoHighlightElementAction() = default;
804
void SoHighlightElementAction::beginTraversal(SoNode *node)
809
void SoHighlightElementAction::callDoAction(SoAction *action,SoNode *node)
811
node->doAction(action);
814
void SoHighlightElementAction::setHighlighted(SbBool ok)
816
this->_highlight = ok;
819
SbBool SoHighlightElementAction::isHighlighted() const
821
return this->_highlight;
824
void SoHighlightElementAction::setColor(const SbColor& c)
829
const SbColor& SoHighlightElementAction::getColor() const
834
void SoHighlightElementAction::setElement(const SoDetail* det)
839
const SoDetail* SoHighlightElementAction::getElement() const
844
// ---------------------------------------------------------------
846
SO_ACTION_SOURCE(SoSelectionElementAction)
848
void SoSelectionElementAction::initClass()
850
SO_ACTION_INIT_CLASS(SoSelectionElementAction,SoAction);
852
SO_ENABLE(SoSelectionElementAction, SoSwitchElement);
853
SO_ENABLE(SoSelectionElementAction, SoModelMatrixElement);
855
SO_ACTION_ADD_METHOD(SoNode,nullAction);
857
SO_ENABLE(SoSelectionElementAction, SoCoordinateElement);
859
SO_ACTION_ADD_METHOD(SoCoordinate3,callDoAction);
860
SO_ACTION_ADD_METHOD(SoGroup,callDoAction);
861
SO_ACTION_ADD_METHOD(SoIndexedLineSet,callDoAction);
862
SO_ACTION_ADD_METHOD(SoIndexedFaceSet,callDoAction);
863
SO_ACTION_ADD_METHOD(SoPointSet,callDoAction);
866
SoSelectionElementAction::SoSelectionElementAction (Type t, bool secondary)
867
: _type(t), _secondary(secondary)
869
SO_ACTION_CONSTRUCTOR(SoSelectionElementAction);
872
SoSelectionElementAction::~SoSelectionElementAction() = default;
874
void SoSelectionElementAction::beginTraversal(SoNode *node)
879
void SoSelectionElementAction::callDoAction(SoAction *action,SoNode *node)
881
node->doAction(action);
884
SoSelectionElementAction::Type
885
SoSelectionElementAction::getType() const
890
void SoSelectionElementAction::setColor(const SbColor& c)
895
const SbColor& SoSelectionElementAction::getColor() const
900
void SoSelectionElementAction::setElement(const SoDetail* det)
905
const SoDetail* SoSelectionElementAction::getElement() const
910
// ---------------------------------------------------------------
912
SO_ACTION_SOURCE(SoVRMLAction)
914
void SoVRMLAction::initClass()
916
SO_ACTION_INIT_CLASS(SoVRMLAction,SoAction);
918
SO_ENABLE(SoVRMLAction, SoSwitchElement);
920
SO_ACTION_ADD_METHOD(SoNode,nullAction);
922
SO_ENABLE(SoVRMLAction, SoCoordinateElement);
923
SO_ENABLE(SoVRMLAction, SoMaterialBindingElement);
924
SO_ENABLE(SoVRMLAction, SoLazyElement);
925
SO_ENABLE(SoVRMLAction, SoShapeStyleElement);
927
SO_ACTION_ADD_METHOD(SoCoordinate3,callDoAction);
928
SO_ACTION_ADD_METHOD(SoMaterialBinding,callDoAction);
929
SO_ACTION_ADD_METHOD(SoMaterial,callDoAction);
930
SO_ACTION_ADD_METHOD(SoNormalBinding,callDoAction);
931
SO_ACTION_ADD_METHOD(SoGroup,callDoAction);
932
SO_ACTION_ADD_METHOD(SoIndexedLineSet,callDoAction);
933
SO_ACTION_ADD_METHOD(SoIndexedFaceSet,callDoAction);
934
SO_ACTION_ADD_METHOD(SoPointSet,callDoAction);
937
SoVRMLAction::SoVRMLAction()
939
SO_ACTION_CONSTRUCTOR(SoVRMLAction);
942
SoVRMLAction::~SoVRMLAction() = default;
944
void SoVRMLAction::setOverrideMode(SbBool on)
949
SbBool SoVRMLAction::isOverrideMode() const
954
void SoVRMLAction::callDoAction(SoAction *action, SoNode *node)
956
if (node->getTypeId().isDerivedFrom(SoNormalBinding::getClassTypeId()) && action->isOfType(SoVRMLAction::getClassTypeId())) {
957
auto vrmlAction = static_cast<SoVRMLAction*>(action);
958
if (vrmlAction->overrideMode) {
959
auto bind = static_cast<SoNormalBinding*>(node);
960
vrmlAction->bindList.push_back(bind->value.getValue());
961
// this normal binding causes some problems for the part view provider
962
// See also #0002222: Number of normals in exported VRML is wrong
963
if (bind->value.getValue() == static_cast<int>(SoNormalBinding::PER_VERTEX_INDEXED))
964
bind->value = SoNormalBinding::OVERALL;
966
else if (!vrmlAction->bindList.empty()) {
967
static_cast<SoNormalBinding*>(node)->value = static_cast<SoNormalBinding::Binding>(vrmlAction->bindList.front());
968
vrmlAction->bindList.pop_front();
972
node->doAction(action);
975
// ---------------------------------------------------------------------------------
977
bool SoFCSelectionRoot::StackComp::operator()(const Stack &a, const Stack &b) const {
978
if(a.size()-a.offset < b.size()-b.offset)
980
if(a.size()-a.offset > b.size()-b.offset)
982
auto it1=a.rbegin(), end1=a.rend()-a.offset;
984
for(;it1!=end1;++it1,++it2) {
992
// ---------------------------------------------------------------------------------
993
SoSeparator::CacheEnabled SoFCSeparator::CacheMode = SoSeparator::AUTO;
994
SO_NODE_SOURCE(SoFCSeparator)
996
SoFCSeparator::SoFCSeparator(bool trackCacheMode)
997
:trackCacheMode(trackCacheMode)
999
SO_NODE_CONSTRUCTOR(SoFCSeparator);
1000
if(!trackCacheMode) {
1001
renderCaching = SoSeparator::OFF;
1002
boundingBoxCaching = SoSeparator::OFF;
1006
void SoFCSeparator::GLRenderBelowPath(SoGLRenderAction * action) {
1007
if(trackCacheMode && renderCaching.getValue()!=CacheMode) {
1008
renderCaching = CacheMode;
1009
boundingBoxCaching = CacheMode;
1011
inherited::GLRenderBelowPath(action);
1014
void SoFCSeparator::initClass()
1016
SO_NODE_INIT_CLASS(SoFCSeparator,SoSeparator,"FCSeparator");
1019
void SoFCSeparator::finish()
1024
// ---------------------------------------------------------------------------------
1025
// Thread local data for bounding box rendering
1027
// The code is inspred by Coin SoLevelOfDetails.cpp.
1028
using SoFCBBoxRenderInfo = struct {
1029
SoGetBoundingBoxAction * bboxaction;
1031
SoColorPacker *packer;
1034
static void so_bbox_construct_data(void * closure)
1036
auto data = static_cast<SoFCBBoxRenderInfo*>(closure);
1037
data->bboxaction = nullptr;
1038
data->cube = nullptr;
1039
data->packer = nullptr;
1042
static void so_bbox_destruct_data(void * closure)
1044
auto data = static_cast<SoFCBBoxRenderInfo*>(closure);
1045
delete data->bboxaction;
1047
data->cube->unref();
1048
delete data->packer;
1051
static SbStorage * so_bbox_storage = nullptr;
1053
// called from atexit
1054
static void so_bbox_cleanup()
1056
delete so_bbox_storage;
1059
// ---------------------------------------------------------------------------------
1061
SoFCSelectionRoot::Stack SoFCSelectionRoot::SelStack;
1062
std::unordered_map<SoAction*,SoFCSelectionRoot::Stack> SoFCSelectionRoot::ActionStacks;
1063
SoFCSelectionRoot::ColorStack SoFCSelectionRoot::SelColorStack;
1064
SoFCSelectionRoot::ColorStack SoFCSelectionRoot::HlColorStack;
1065
SoFCSelectionRoot* SoFCSelectionRoot::ShapeColorNode;
1067
SO_NODE_SOURCE(SoFCSelectionRoot)
1069
SoFCSelectionRoot::SoFCSelectionRoot(bool trackCacheMode)
1070
:SoFCSeparator(trackCacheMode)
1072
SO_NODE_CONSTRUCTOR(SoFCSelectionRoot);
1073
SO_NODE_ADD_FIELD(selectionStyle,(Full));
1074
SO_NODE_DEFINE_ENUM_VALUE(SelectStyles, Full);
1075
SO_NODE_DEFINE_ENUM_VALUE(SelectStyles, Box);
1076
SO_NODE_DEFINE_ENUM_VALUE(SelectStyles, PassThrough);
1077
SO_NODE_SET_SF_ENUM_TYPE(selectionStyle, SelectStyles);
1080
SoFCSelectionRoot::~SoFCSelectionRoot() = default;
1082
void SoFCSelectionRoot::initClass()
1084
SO_NODE_INIT_CLASS(SoFCSelectionRoot,SoFCSeparator,"FCSelectionRoot");
1086
so_bbox_storage = new SbStorage(sizeof(SoFCBBoxRenderInfo),
1087
so_bbox_construct_data, so_bbox_destruct_data);
1089
// cc_coin_atexit((coin_atexit_f*) so_bbox_cleanup);
1092
void SoFCSelectionRoot::finish()
1098
SoNode *SoFCSelectionRoot::getCurrentRoot(bool front, SoNode *def) {
1099
if(!SelStack.empty())
1100
return front?SelStack.front():SelStack.back();
1104
SoFCSelectionContextBasePtr SoFCSelectionRoot::getNodeContext(
1105
Stack &stack, SoNode *node, SoFCSelectionContextBasePtr def)
1110
auto front = dynamic_cast<SoFCSelectionRoot *>(stack.front());
1111
if (front == nullptr) {
1112
return SoFCSelectionContextBasePtr();
1115
stack.front() = node;
1117
auto it = front->contextMap.find(stack);
1118
stack.front() = front;
1119
if(it!=front->contextMap.end())
1124
SoFCSelectionContextBasePtr
1125
SoFCSelectionRoot::getNodeContext2(Stack &stack, SoNode *node, SoFCSelectionContextBase::MergeFunc *merge)
1127
SoFCSelectionContextBasePtr ret;
1128
if (stack.empty()) {
1132
auto *back = dynamic_cast<SoFCSelectionRoot*>(stack.back());
1133
if (back == nullptr || back->contextMap2.empty()) {
1138
auto &map = back->contextMap2;
1139
stack.back() = node;
1140
for(stack.offset=0;stack.offset<stack.size();++stack.offset) {
1141
auto it = map.find(stack);
1142
SoFCSelectionContextBasePtr ctx;
1145
status = merge(status,ret,ctx,stack.offset==stack.size()-1?nullptr:stack[stack.offset]);
1150
stack.back() = back;
1154
std::pair<bool,SoFCSelectionContextBasePtr*> SoFCSelectionRoot::findActionContext(
1155
SoAction *action, SoNode *_node, bool create, bool erase)
1157
std::pair<bool,SoFCSelectionContextBasePtr*> res(false,0);
1159
if(action->isOfType(SoSelectionElementAction::getClassTypeId()))
1160
res.first = static_cast<SoSelectionElementAction*>(action)->isSecondary();
1162
auto it = ActionStacks.find(action);
1163
if(it==ActionStacks.end() || it->second.empty())
1166
auto &stack = it->second;
1169
auto back = dynamic_cast<SoFCSelectionRoot*>(stack.back());
1170
if (back != nullptr) {
1171
stack.back() = _node;
1173
res.second = &back->contextMap2[stack];
1175
auto it = back->contextMap2.find(stack);
1176
if(it!=back->contextMap2.end()) {
1177
res.second = &it->second;
1179
back->contextMap2.erase(it);
1182
stack.back() = back;
1185
auto front = dynamic_cast<SoFCSelectionRoot*>(stack.front());
1186
if (front != nullptr) {
1187
stack.front() = _node;
1189
res.second = &front->contextMap[stack];
1191
auto it = front->contextMap.find(stack);
1192
if(it!=front->contextMap.end()) {
1193
res.second = &it->second;
1195
front->contextMap.erase(it);
1198
stack.front() = front;
1204
bool SoFCSelectionRoot::renderBBox(SoGLRenderAction *action, SoNode *node, SbColor color)
1206
auto data = static_cast<SoFCBBoxRenderInfo*>(so_bbox_storage->get());
1207
if (!data->bboxaction) {
1208
// The viewport region will be replaced every time the action is
1209
// used, so we can just feed it a dummy here.
1210
data->bboxaction = new SoGetBoundingBoxAction(SbViewportRegion());
1211
data->cube = new SoCube;
1213
data->packer = new SoColorPacker;
1217
data->bboxaction->setViewportRegion(action->getViewportRegion());
1218
data->bboxaction->apply(node);
1219
bbox = data->bboxaction->getBoundingBox();
1223
auto state = action->getState();
1227
SoMaterialBindingElement::set(state,SoMaterialBindingElement::OVERALL);
1228
SoLazyElement::setEmissive(state, &color);
1229
SoLazyElement::setDiffuse(state, node,1, &color, data->packer);
1230
SoDrawStyleElement::set(state, node, SoDrawStyleElement::LINES);
1231
SoLineWidthElement::set(state, node, 1.0f);
1233
const static float trans = 0.0;
1234
SoLazyElement::setTransparency(state, node, 1, &trans, data->packer);
1237
bbox.getSize(x, y, z);
1238
data->cube->width = x+0.001;
1239
data->cube->height = y+0.001;
1240
data->cube->depth = z+0.001;
1242
SoModelMatrixElement::translateBy(state,node,bbox.getCenter());
1244
SoMaterialBundle mb(action);
1247
data->cube->GLRender(action);
1253
static std::time_t _CyclicLastReported;
1255
void SoFCSelectionRoot::renderPrivate(SoGLRenderAction * action, bool inPath) {
1256
if(ViewParams::instance()->getCoinCycleCheck()
1257
&& !SelStack.nodeSet.insert(this).second)
1259
std::time_t t = std::time(nullptr);
1260
if(_CyclicLastReported < t) {
1261
_CyclicLastReported = t+5;
1262
FC_ERR("Cyclic scene graph: " << getName());
1266
SelStack.push_back(this);
1267
if(_renderPrivate(action,inPath)) {
1269
SoSeparator::GLRenderInPath(action);
1271
SoSeparator::GLRenderBelowPath(action);
1273
SelStack.pop_back();
1274
SelStack.nodeSet.erase(this);
1277
bool SoFCSelectionRoot::_renderPrivate(SoGLRenderAction * action, bool inPath) {
1278
auto ctx2 = std::static_pointer_cast<SelContext>(getNodeContext2(SelStack,this,SelContext::merge));
1279
if(ctx2 && ctx2->hideAll)
1282
auto state = action->getState();
1283
SelContextPtr ctx = getRenderContext<SelContext>(this);
1284
int style = selectionStyle.getValue();
1285
if((style==SoFCSelectionRoot::Box || ViewParams::instance()->getShowSelectionBoundingBox())
1286
&& ctx && !ctx->hideAll && (ctx->selAll || ctx->hlAll))
1288
if (style==SoFCSelectionRoot::PassThrough) {
1289
style = SoFCSelectionRoot::Box;
1292
renderBBox(action,this,ctx->hlAll?ctx->hlColor:ctx->selColor);
1297
// Here, we are not setting (pre)selection color override here.
1298
// Instead, we are checking and setting up for any secondary context
1301
// When the current selection style is full highlight, we should ignore any
1302
// secondary override. If the style is bounding box, however, we should
1303
// honour the secondary color override.
1305
bool colorPushed = false;
1306
if(!ShapeColorNode && overrideColor &&
1307
!SoOverrideElement::getDiffuseColorOverride(state) &&
1308
(style==SoFCSelectionRoot::Box || !ctx || (!ctx->selAll && !ctx->hideAll)))
1310
ShapeColorNode = this;
1313
auto &packer = ShapeColorNode->shapeColorPacker;
1314
auto &trans = ShapeColorNode->transOverride;
1315
auto &color = ShapeColorNode->colorOverride;
1316
if(!SoOverrideElement::getTransparencyOverride(state) && trans) {
1317
SoLazyElement::setTransparency(state, ShapeColorNode, 1, &trans, &packer);
1318
SoOverrideElement::setTransparencyOverride(state,ShapeColorNode,true);
1320
SoLazyElement::setDiffuse(state, ShapeColorNode, 1, &color, &packer);
1321
SoOverrideElement::setDiffuseColorOverride(state,ShapeColorNode,true);
1322
SoMaterialBindingElement::set(state, ShapeColorNode, SoMaterialBindingElement::OVERALL);
1323
SoOverrideElement::setMaterialBindingOverride(state,ShapeColorNode,true);
1325
SoTextureEnabledElement::set(state,ShapeColorNode,false);
1330
SoSeparator::GLRenderInPath(action);
1332
SoSeparator::GLRenderBelowPath(action);
1336
if((selPushed = ctx->selAll)) {
1337
SelColorStack.push_back(ctx->selColor);
1339
if(style != SoFCSelectionRoot::Box) {
1341
auto &color = SelColorStack.back();
1342
SoLazyElement::setEmissive(state, &color);
1343
SoOverrideElement::setEmissiveColorOverride(state,this,true);
1344
if (SoLazyElement::getLightModel(state) == SoLazyElement::BASE_COLOR) {
1345
auto &packer = shapeColorPacker;
1346
SoLazyElement::setDiffuse(state, this, 1, &color, &packer);
1347
SoOverrideElement::setDiffuseColorOverride(state,this,true);
1348
SoMaterialBindingElement::set(state, this, SoMaterialBindingElement::OVERALL);
1349
SoOverrideElement::setMaterialBindingOverride(state,this,true);
1354
if((hlPushed = ctx->hlAll))
1355
HlColorStack.push_back(ctx->hlColor);
1358
SoSeparator::GLRenderInPath(action);
1360
SoSeparator::GLRenderBelowPath(action);
1363
SelColorStack.pop_back();
1365
if(style != SoFCSelectionRoot::Box)
1369
HlColorStack.pop_back();
1373
ShapeColorNode = nullptr;
1380
void SoFCSelectionRoot::GLRenderBelowPath(SoGLRenderAction * action) {
1381
renderPrivate(action,false);
1384
void SoFCSelectionRoot::GLRenderInPath(SoGLRenderAction * action) {
1385
if(action->getCurPathCode() == SoAction::BELOW_PATH)
1386
return GLRenderBelowPath(action);
1387
renderPrivate(action,true);
1390
bool SoFCSelectionRoot::checkColorOverride(SoState *state) {
1391
if(ShapeColorNode) {
1392
if(!SoOverrideElement::getDiffuseColorOverride(state)) {
1394
auto &packer = ShapeColorNode->shapeColorPacker;
1395
auto &trans = ShapeColorNode->transOverride;
1396
auto &color = ShapeColorNode->colorOverride;
1397
if(!SoOverrideElement::getTransparencyOverride(state) && trans) {
1398
SoLazyElement::setTransparency(state, ShapeColorNode, 1, &trans, &packer);
1399
SoOverrideElement::setTransparencyOverride(state,ShapeColorNode,true);
1401
SoLazyElement::setDiffuse(state, ShapeColorNode, 1, &color, &packer);
1402
SoOverrideElement::setDiffuseColorOverride(state,ShapeColorNode,true);
1403
SoMaterialBindingElement::set(state, ShapeColorNode, SoMaterialBindingElement::OVERALL);
1404
SoOverrideElement::setMaterialBindingOverride(state,ShapeColorNode,true);
1406
SoTextureEnabledElement::set(state,ShapeColorNode,false);
1413
void SoFCSelectionRoot::checkSelection(bool &sel, SbColor &selColor, bool &hl, SbColor &hlColor) {
1416
if((sel = !SelColorStack.empty()))
1417
selColor = SelColorStack.back();
1418
if((hl = !HlColorStack.empty()))
1419
hlColor = HlColorStack.back();
1422
void SoFCSelectionRoot::resetContext() {
1426
void SoFCSelectionRoot::moveActionStack(SoAction *from, SoAction *to, bool erase) {
1427
auto it = ActionStacks.find(from);
1428
if(it == ActionStacks.end())
1430
auto &stack = ActionStacks[to];
1431
assert(stack.empty());
1432
stack.swap(it->second);
1434
ActionStacks.erase(it);
1437
#define BEGIN_ACTION \
1438
auto &stack = ActionStacks[action];\
1439
if(ViewParams::instance()->getCoinCycleCheck() \
1440
&& !stack.nodeSet.insert(this).second) \
1442
std::time_t t = std::time(0);\
1443
if(_CyclicLastReported < t) {\
1444
_CyclicLastReported = t+5;\
1445
FC_ERR("Cyclic scene graph: " << getName());\
1449
stack.push_back(this);\
1450
auto size = stack.size();
1453
if(stack.size()!=size || stack.back()!=this)\
1454
FC_ERR("action stack fault");\
1456
stack.nodeSet.erase(this);\
1459
ActionStacks.erase(action);\
1462
void SoFCSelectionRoot::pick(SoPickAction * action) {
1464
if(doActionPrivate(stack,action))
1465
inherited::pick(action);
1469
void SoFCSelectionRoot::rayPick(SoRayPickAction * action) {
1471
if(doActionPrivate(stack,action))
1472
inherited::rayPick(action);
1476
void SoFCSelectionRoot::handleEvent(SoHandleEventAction * action) {
1478
inherited::handleEvent(action);
1482
void SoFCSelectionRoot::search(SoSearchAction * action) {
1484
inherited::search(action);
1488
void SoFCSelectionRoot::getPrimitiveCount(SoGetPrimitiveCountAction * action) {
1490
inherited::getPrimitiveCount(action);
1494
void SoFCSelectionRoot::getBoundingBox(SoGetBoundingBoxAction * action)
1497
if(doActionPrivate(stack,action))
1498
inherited::getBoundingBox(action);
1502
void SoFCSelectionRoot::getMatrix(SoGetMatrixAction * action) {
1504
if(doActionPrivate(stack,action))
1505
inherited::getMatrix(action);
1509
void SoFCSelectionRoot::callback(SoCallbackAction *action) {
1511
inherited::callback(action);
1515
void SoFCSelectionRoot::doAction(SoAction *action) {
1517
if(doActionPrivate(stack,action))
1518
inherited::doAction(action);
1522
bool SoFCSelectionRoot::doActionPrivate(Stack &stack, SoAction *action) {
1523
// Selection action short-circuit optimization. In case of whole object
1524
// selection/pre-selection, we shall store a SelContext keyed by ourself.
1525
// And the action traversal can be short-curcuited once the first targeted
1526
// SoFCSelectionRoot is found here. New function checkSelection() is exposed
1527
// to check for whole object selection. This greatly improve performance on
1531
bool ctx2Searched = false;
1532
bool isTail = false;
1533
if(action->getCurPathCode()==SoAction::IN_PATH) {
1534
auto path = action->getPathAppliedTo();
1536
isTail = path->getTail()==this ||
1537
(path->getLength()>1
1538
&& path->getNodeFromTail(1)==this
1539
&& path->getTail()->isOfType(SoSwitch::getClassTypeId()));
1542
if(!action->isOfType(SoSelectionElementAction::getClassTypeId())) {
1543
ctx2Searched = true;
1544
ctx2 = std::static_pointer_cast<SelContext>(getNodeContext2(stack,this,SelContext::merge));
1545
if(ctx2 && ctx2->hideAll)
1550
}else if(action->getWhatAppliedTo()!=SoAction::NODE && action->getCurPathCode()!=SoAction::BELOW_PATH)
1553
if(action->isOfType(SoSelectionElementAction::getClassTypeId())) {
1554
auto selAction = static_cast<SoSelectionElementAction*>(action);
1555
if(selAction->isSecondary()) {
1556
if(selAction->getType() == SoSelectionElementAction::Show ||
1557
(selAction->getType() == SoSelectionElementAction::Color &&
1558
selAction->getColors().empty() &&
1559
action->getWhatAppliedTo()==SoAction::NODE))
1561
auto ctx = getActionContext(action,this,SelContextPtr(),false);
1562
if(ctx && ctx->hideAll) {
1563
ctx->hideAll = false;
1564
if(!ctx->hlAll && !ctx->selAll)
1565
removeActionContext(action,this);
1568
// applied to a node means clear all visibility setting, so
1569
// return true to propagate the action
1570
return selAction->getType()==SoSelectionElementAction::Color ||
1571
action->getWhatAppliedTo()==SoAction::NODE;
1573
}else if(selAction->getType() == SoSelectionElementAction::Hide) {
1574
if(action->getCurPathCode()==SoAction::BELOW_PATH || isTail) {
1575
auto ctx = getActionContext(action,this,SelContextPtr());
1576
if(ctx && !ctx->hideAll) {
1577
ctx->hideAll = true;
1586
if(selAction->getType() == SoSelectionElementAction::None) {
1587
if(action->getWhatAppliedTo() == SoAction::NODE) {
1588
// Here the 'select none' action is applied to a node, and we
1589
// are the first SoFCSelectionRoot encountered (which means all
1590
// children stores selection context here, both whole object
1591
// and element selection), then we can simply perform the
1592
// action by clearing the selection context here, and save the
1593
// time for traversing a potentially large amount of children
1600
auto ctx = getActionContext(action,this,SelContextPtr(),false);
1601
if(ctx && ctx->selAll) {
1602
ctx->selAll = false;
1606
} else if(selAction->getType() == SoSelectionElementAction::All) {
1607
auto ctx = getActionContext(action,this,SelContextPtr());
1610
ctx->selColor = selAction->getColor();
1617
if(action->isOfType(SoHighlightElementAction::getClassTypeId())) {
1618
auto hlAction = static_cast<SoHighlightElementAction*>(action);
1619
if(hlAction->isHighlighted()) {
1620
if(hlAction->getElement()) {
1621
auto ctx = getActionContext(action,this,SelContextPtr(),false);
1622
if(ctx && ctx->hlAll) {
1627
auto ctx = getActionContext(action,this,SelContextPtr());
1630
ctx->hlColor = hlAction->getColor();
1635
auto ctx = getActionContext(action,this,SelContextPtr(),false);
1636
if(ctx && ctx->hlAll) {
1646
ctx2 = std::static_pointer_cast<SelContext>(getNodeContext2(stack,this,SelContext::merge));
1647
if(ctx2 && ctx2->hideAll)
1653
int SoFCSelectionRoot::SelContext::merge(int status, SoFCSelectionContextBasePtr &output,
1654
SoFCSelectionContextBasePtr input, SoNode *)
1656
auto ctx = std::dynamic_pointer_cast<SelContext>(input);
1657
if(ctx && ctx->hideAll) {
1664
/////////////////////////////////////////////////////////////////////////////
1666
SO_NODE_SOURCE(SoFCPathAnnotation)
1668
SoFCPathAnnotation::SoFCPathAnnotation()
1670
SO_NODE_CONSTRUCTOR(SoFCPathAnnotation);
1676
SoFCPathAnnotation::~SoFCPathAnnotation()
1678
if(path) path->unref();
1679
if(tmpPath) tmpPath->unref();
1683
void SoFCPathAnnotation::finish()
1688
void SoFCPathAnnotation::initClass()
1690
SO_NODE_INIT_CLASS(SoFCPathAnnotation,SoSeparator,"Separator");
1693
void SoFCPathAnnotation::GLRender(SoGLRenderAction * action)
1695
switch (action->getCurPathCode()) {
1696
case SoAction::NO_PATH:
1697
case SoAction::BELOW_PATH:
1698
this->GLRenderBelowPath(action);
1700
case SoAction::OFF_PATH:
1702
case SoAction::IN_PATH:
1703
this->GLRenderInPath(action);
1708
void SoFCPathAnnotation::GLRenderBelowPath(SoGLRenderAction * action)
1710
if(!path || !path->getLength() || !tmpPath || !tmpPath->getLength())
1713
if(path->getLength() != tmpPath->getLength()) {
1714
// The auditing SoPath may be truncated due to harmless things such as
1715
// flipping a SoSwitch sibling node. So we keep an unauditing SoTempPath
1716
// around to try to restore the path.
1717
for(int i=path->getLength()-1;i<tmpPath->getLength()-1;++i) {
1718
auto children = path->getNode(i)->getChildren();
1720
int idx = children->find(tmpPath->getNode(i+1));
1732
SoState * state = action->getState();
1733
SoGLCacheContextElement::shouldAutoCache(state, SoGLCacheContextElement::DONT_AUTO_CACHE);
1735
if (action->isRenderingDelayedPaths()) {
1736
SbBool zbenabled = glIsEnabled(GL_DEPTH_TEST);
1737
if (zbenabled) glDisable(GL_DEPTH_TEST);
1740
inherited::GLRenderInPath(action);
1742
bool bbox = ViewParams::instance()->getShowSelectionBoundingBox();
1744
for(int i=0,count=path->getLength();i<count;++i) {
1745
if(!path->getNode(i)->isOfType(SoFCSelectionRoot::getClassTypeId()))
1747
auto node = dynamic_cast<SoFCSelectionRoot*>(path->getNode(i));
1749
&& node->selectionStyle.getValue() == SoFCSelectionRoot::Box) {
1756
inherited::GLRenderInPath(action);
1760
SbColor selColor,hlColor;
1761
SoFCSelectionRoot::checkSelection(sel,selColor,hl,hlColor);
1763
SoFCSelectionRoot::renderBBox(action,this,hl?hlColor:selColor);
1765
inherited::GLRenderInPath(action);
1769
if (zbenabled) glEnable(GL_DEPTH_TEST);
1772
SoCacheElement::invalidate(action->getState());
1773
auto curPath = action->getCurPath();
1774
auto newPath = new SoPath(curPath->getLength()+path->getLength());
1775
newPath->append(curPath);
1776
newPath->append(path);
1777
action->addDelayedPath(newPath);
1781
void SoFCPathAnnotation::GLRenderInPath(SoGLRenderAction * action)
1783
GLRenderBelowPath(action);
1786
void SoFCPathAnnotation::setDetail(SoDetail *d) {
1793
void SoFCPathAnnotation::setPath(SoPath *newPath) {
1796
coinRemoveAllChildren(this);
1803
if(!newPath || !newPath->getLength())
1806
tmpPath = new SoTempPath(newPath->getLength());
1808
for(int i=0;i<newPath->getLength();++i)
1809
tmpPath->append(newPath->getNode(i));
1810
path = newPath->copy();
1812
addChild(path->getNode(0));
1815
void SoFCPathAnnotation::getBoundingBox(SoGetBoundingBoxAction * action)
1818
SoGetBoundingBoxAction bboxAction(action->getViewportRegion());
1819
SoFCSelectionRoot::moveActionStack(action,&bboxAction,false);
1820
bboxAction.apply(path);
1821
SoFCSelectionRoot::moveActionStack(&bboxAction,action,true);
1822
auto bbox = bboxAction.getBoundingBox();
1824
action->extendBy(bbox);