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/GeoFeature.h>
75
#include <App/ElementNamingUtils.h>
76
#include <Base/Tools.h>
77
#include <Base/UnitsApi.h>
79
#include "SoFCUnifiedSelection.h"
80
#include "Application.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"
90
FC_LOG_LEVEL_INIT("SoFCUnifiedSelection",false,true,true)
95
void printPreselectionInfo(const char* documentName,
96
const char* objectName,
97
const char* subElementName,
98
float x, float y, float z,
102
SoFullPath * Gui::SoFCUnifiedSelection::currenthighlight = nullptr;
104
// *************************************************************************
106
SO_NODE_SOURCE(SoFCUnifiedSelection)
111
SoFCUnifiedSelection::SoFCUnifiedSelection()
113
SO_NODE_CONSTRUCTOR(SoFCUnifiedSelection);
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));
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);
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));
134
setPreSelection = false;
136
useNewSelection = ViewParams::instance()->getUseNewSelection();
142
SoFCUnifiedSelection::~SoFCUnifiedSelection()
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;
152
detailPath = nullptr;
158
SoFCUnifiedSelection::initClass()
160
SO_NODE_INIT_CLASS(SoFCUnifiedSelection,SoSeparator,"Separator");
163
void SoFCUnifiedSelection::finish()
168
bool SoFCUnifiedSelection::hasHighlight() {
169
return currenthighlight != nullptr;
172
void SoFCUnifiedSelection::applySettings()
175
ParameterGrp::handle hGrp = Gui::WindowParameter::getDefaultParameter()->GetGroup("View");
176
bool enablePre = hGrp->GetBool("EnablePreselection", true);
177
bool enableSel = hGrp->GetBool("EnableSelection", true);
179
this->highlightMode = SoFCUnifiedSelection::OFF;
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);
191
this->selectionMode = SoFCUnifiedSelection::OFF;
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);
203
const char* SoFCUnifiedSelection::getFileFormatName() const
208
void SoFCUnifiedSelection::write(SoWriteAction * action)
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))
215
SoGroup::doAction(static_cast<SoAction *>(action));
216
this->writeFooter(out);
219
inherited::write(action);
223
int SoFCUnifiedSelection::getPriority(const SoPickedPoint* p)
225
const SoDetail* detail = p->getDetail();
228
if (detail->isOfType(SoFaceDetail::getClassTypeId()))
230
if (detail->isOfType(SoLineDetail::getClassTypeId()))
232
if (detail->isOfType(SoPointDetail::getClassTypeId()))
237
std::vector<SoFCUnifiedSelection::PickedInfo>
238
SoFCUnifiedSelection::getPickedList(SoHandleEventAction* action, bool singlePick) const
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) {
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)
254
if(!vp || !vp->isDerivedFrom(ViewProviderDocumentObject::getClassTypeId())) {
255
if(!singlePick) continue;
260
info.vpd = static_cast<ViewProviderDocumentObject*>(vp);
261
if(!(useNewSelection.getValue()||info.vpd->useNewSelectionModel()) || !info.vpd->isSelectable()) {
262
if(!singlePick) continue;
269
if(!info.vpd->getElementPicked(info.pp,info.element))
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.
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) {
291
if(last_vpd != info.vpd)
294
int cur_prio = getPriority(info.pp);
295
const SbVec3f& cur_pt = info.pp->getPoint();
297
if ((cur_prio > picked_prio) && picked_pt.equals(cur_pt, 0.2F)) {
299
picked_prio = cur_prio;
304
std::vector<PickedInfo> sret(itPicked,itPicked+1);
307
if(itPicked != ret.begin())
308
std::swap(*itPicked, *ret.begin());
312
void SoFCUnifiedSelection::doAction(SoAction *action)
314
if (action->getTypeId() == SoFCEnableHighlightAction::getClassTypeId()) {
315
auto preaction = static_cast<SoFCEnableHighlightAction*>(action);
316
if (preaction->highlight) {
317
this->highlightMode = SoFCUnifiedSelection::AUTO;
320
this->highlightMode = SoFCUnifiedSelection::OFF;
324
if (action->getTypeId() == SoFCEnableSelectionAction::getClassTypeId()) {
325
auto selaction = static_cast<SoFCEnableSelectionAction*>(action);
326
if (selaction->selection) {
327
this->selectionMode = SoFCUnifiedSelection::ON;
330
this->selectionMode = SoFCUnifiedSelection::OFF;
334
if (action->getTypeId() == SoFCSelectionColorAction::getClassTypeId()) {
335
auto colaction = static_cast<SoFCSelectionColorAction*>(action);
336
this->colorSelection = colaction->selectionColor;
339
if (action->getTypeId() == SoFCHighlightColorAction::getClassTypeId()) {
340
auto colaction = static_cast<SoFCHighlightColorAction*>(action);
341
this->colorHighlight = colaction->highlightColor;
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;
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;
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);
369
SoHighlightElementAction hlAction;
370
hlAction.setHighlighted(true);
371
hlAction.setColor(this->colorHighlight.getValue());
372
hlAction.setElement(detail);
373
hlAction.apply(vp->getRoot());
377
sa.setNode(vp->getRoot());
378
sa.apply(vp->getRoot());
379
currenthighlight = static_cast<SoFullPath*>(sa.getPath()->copy());
380
currenthighlight->ref();
383
if (useNewSelection.getValue())
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))
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.
407
if(!selaction->SelChange.pSubName || !selaction->SelChange.pSubName[0] ||
408
vp->getDetailPath(subName,detailPath,true,detail))
410
SoSelectionElementAction::Type type = SoSelectionElementAction::None;
411
if (selaction->SelChange.Type == SelectionChanges::AddSelection) {
413
type = SoSelectionElementAction::Append;
415
type = SoSelectionElementAction::All;
419
type = SoSelectionElementAction::Remove;
421
type = SoSelectionElementAction::None;
424
SoSelectionElementAction selectionAction(type);
425
selectionAction.setColor(this->colorSelection.getValue());
426
selectionAction.setElement(detail);
427
if(detailPath->getLength())
428
selectionAction.apply(detailPath);
430
selectionAction.apply(vp->getRoot());
432
detailPath->truncate(0);
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));
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;
453
type = SoSelectionElementAction::None;
455
SoSelectionElementAction selectionAction(type);
456
selectionAction.setColor(this->colorSelection.getValue());
457
selectionAction.apply(vpd->getRoot());
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())
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);
482
if (useNewSelection.getValue())
486
inherited::doAction( action );
489
bool SoFCUnifiedSelection::setHighlight(const PickedInfo &info) {
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]);
497
bool SoFCUnifiedSelection::setHighlight(SoFullPath *path, const SoDetail *det,
498
ViewProviderDocumentObject *vpd, const char *element, float x, float y, float z)
500
Base::FlagToggler<SbBool> flag(setPreSelection);
502
bool highlighted = false;
503
if(path && path->getLength() &&
504
vpd && vpd->getObject() && vpd->getObject()->isAttachedToDocument())
506
const char *docname = vpd->getObject()->getDocument()->getName();
507
const char *objname = vpd->getObject()->getNameInDocument();
509
this->preSelection = 1;
511
printPreselectionInfo(docname, objname, element, x, y, z, 1e-7);
514
int ret = Gui::Selection().setPreselect(docname,objname,element,x,y,z);
515
if(ret<0 && currenthighlight)
519
if (currenthighlight) {
520
SoHighlightElementAction action;
521
action.setHighlighted(false);
522
action.apply(currenthighlight);
523
currenthighlight->unref();
524
currenthighlight = nullptr;
526
currenthighlight = static_cast<SoFullPath*>(path->copy());
527
currenthighlight->ref();
532
if(currenthighlight) {
533
SoHighlightElementAction action;
534
action.setHighlighted(highlighted);
535
action.setColor(this->colorHighlight.getValue());
536
action.setElement(det);
537
action.apply(currenthighlight);
539
currenthighlight->unref();
540
currenthighlight = nullptr;
541
Selection().rmvPreselect();
548
bool SoFCUnifiedSelection::setSelection(const std::vector<PickedInfo> &infos, bool ctrlDown) {
549
if (infos.empty() || !infos[0].vpd)
552
std::vector<SelectionSingleton::SelObj> sels;
553
if (infos.size() > 1) {
554
for (auto &info: infos) {
555
if (!info.vpd) continue;
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();
573
const auto &info = infos[0];
577
if (!vpd->getObject()->isAttachedToDocument())
579
const char *objname = vpd->getObject()->getNameInDocument();
580
const char *docname = vpd->getObject()->getDocument()->getName();
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,
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;
605
if (Gui::Selection().isSelected(docname, objname, info.element.c_str(), ResolveMode::NoResolve))
606
Gui::Selection().rmvSelection(docname, objname, info.element.c_str(), &sels);
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);
616
getMainWindow()->showMessage(QString::fromLatin1(buf));
618
detailPath->truncate(0);
619
if (vpd->getDetailPath(info.element.c_str(), detailPath, true, detNext) &&
620
detailPath->getLength()) {
623
FC_TRACE("select next " << objectName << ", " << subName);
625
type = hasNext ? SoSelectionElementAction::All : SoSelectionElementAction::Append;
629
// Hierarchy ascending
631
// If the clicked subelement is already selected, check if there is an
632
// upper hierarchy, and select that hierarchy instead.
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.'
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());
648
FC_TRACE("select " << (subSelected ? subSelected : "'null'") << ", " <<
649
objectName << ", " << subName);
650
std::string newElement;
652
newElement = Data::newElementName(subSelected);
653
subSelected = newElement.c_str();
655
const char *next = strrchr(subSelected, '.');
656
if (next && next != subSelected) {
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;
670
nextsub = std::string(subSelected, next - subSelected + 1);
672
if (nextsub.length() || *subSelected != 0) {
675
detailPath->truncate(0);
676
if (vpd->getDetailPath(subName.c_str(), detailPath, true, detNext) &&
677
detailPath->getLength()) {
680
FC_TRACE("select next " << objectName << ", " << subName);
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);
691
type = hasNext ? SoSelectionElementAction::All : SoSelectionElementAction::Append;
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);
698
getMainWindow()->showMessage(QString::fromLatin1(buf));
702
FC_TRACE("applying action");
703
SoSelectionElementAction action(type);
704
action.setColor(this->colorSelection.getValue());
705
action.setElement(det);
707
FC_TRACE("applied action");
711
if (detNext) delete detNext;
717
SoFCUnifiedSelection::handleEvent(SoHandleEventAction * action)
719
// If off then don't handle this event
720
if (!selectionRole.getValue()) {
721
inherited::handleEvent(action);
725
auto mymode = static_cast<HighlightModes>(this->highlightMode.getValue());
726
const SoEvent * event = action->getEvent();
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 );
739
// If this is a mouseMotion event, then check for locate highlighting
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);
749
setHighlight(infos[0]);
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
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();
775
inherited::handleEvent(action);
778
void SoFCUnifiedSelection::GLRenderBelowPath(SoGLRenderAction * action)
780
inherited::GLRenderBelowPath(action);
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;
788
SoState *state = action->getState();
789
SoGLWidgetElement::get(state, window);
790
QWidget* parent = window ? window->parentWidget() : nullptr;
792
QCursor c = parent->cursor();
793
if (c.shape() == Qt::ForbiddenCursor) {
794
c.setShape(Qt::ArrowCursor);
795
parent->setCursor(c);
801
// ---------------------------------------------------------------
803
SO_ACTION_SOURCE(SoHighlightElementAction)
805
void SoHighlightElementAction::initClass()
807
SO_ACTION_INIT_CLASS(SoHighlightElementAction,SoAction);
809
SO_ENABLE(SoHighlightElementAction, SoSwitchElement);
810
SO_ENABLE(SoHighlightElementAction, SoModelMatrixElement);
812
SO_ACTION_ADD_METHOD(SoNode,nullAction);
814
SO_ENABLE(SoHighlightElementAction, SoCoordinateElement);
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);
822
SoHighlightElementAction::SoHighlightElementAction ()
824
SO_ACTION_CONSTRUCTOR(SoHighlightElementAction);
827
SoHighlightElementAction::~SoHighlightElementAction() = default;
829
void SoHighlightElementAction::beginTraversal(SoNode *node)
834
void SoHighlightElementAction::callDoAction(SoAction *action,SoNode *node)
836
node->doAction(action);
839
void SoHighlightElementAction::setHighlighted(SbBool ok)
841
this->_highlight = ok;
844
SbBool SoHighlightElementAction::isHighlighted() const
846
return this->_highlight;
849
void SoHighlightElementAction::setColor(const SbColor& c)
854
const SbColor& SoHighlightElementAction::getColor() const
859
void SoHighlightElementAction::setElement(const SoDetail* det)
864
const SoDetail* SoHighlightElementAction::getElement() const
869
// ---------------------------------------------------------------
871
SO_ACTION_SOURCE(SoSelectionElementAction)
873
void SoSelectionElementAction::initClass()
875
SO_ACTION_INIT_CLASS(SoSelectionElementAction,SoAction);
877
SO_ENABLE(SoSelectionElementAction, SoSwitchElement);
878
SO_ENABLE(SoSelectionElementAction, SoModelMatrixElement);
880
SO_ACTION_ADD_METHOD(SoNode,nullAction);
882
SO_ENABLE(SoSelectionElementAction, SoCoordinateElement);
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);
891
SoSelectionElementAction::SoSelectionElementAction (Type t, bool secondary)
892
: _type(t), _secondary(secondary)
894
SO_ACTION_CONSTRUCTOR(SoSelectionElementAction);
897
SoSelectionElementAction::~SoSelectionElementAction() = default;
899
void SoSelectionElementAction::beginTraversal(SoNode *node)
904
void SoSelectionElementAction::callDoAction(SoAction *action,SoNode *node)
906
node->doAction(action);
909
SoSelectionElementAction::Type
910
SoSelectionElementAction::getType() const
915
void SoSelectionElementAction::setColor(const SbColor& c)
920
const SbColor& SoSelectionElementAction::getColor() const
925
void SoSelectionElementAction::setElement(const SoDetail* det)
930
const SoDetail* SoSelectionElementAction::getElement() const
935
// ---------------------------------------------------------------
937
SO_ACTION_SOURCE(SoVRMLAction)
939
void SoVRMLAction::initClass()
941
SO_ACTION_INIT_CLASS(SoVRMLAction,SoAction);
943
SO_ENABLE(SoVRMLAction, SoSwitchElement);
945
SO_ACTION_ADD_METHOD(SoNode,nullAction);
947
SO_ENABLE(SoVRMLAction, SoCoordinateElement);
948
SO_ENABLE(SoVRMLAction, SoMaterialBindingElement);
949
SO_ENABLE(SoVRMLAction, SoLazyElement);
950
SO_ENABLE(SoVRMLAction, SoShapeStyleElement);
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);
962
SoVRMLAction::SoVRMLAction()
964
SO_ACTION_CONSTRUCTOR(SoVRMLAction);
967
SoVRMLAction::~SoVRMLAction() = default;
969
void SoVRMLAction::setOverrideMode(SbBool on)
974
SbBool SoVRMLAction::isOverrideMode() const
979
void SoVRMLAction::callDoAction(SoAction *action, SoNode *node)
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;
991
else if (!vrmlAction->bindList.empty()) {
992
static_cast<SoNormalBinding*>(node)->value = static_cast<SoNormalBinding::Binding>(vrmlAction->bindList.front());
993
vrmlAction->bindList.pop_front();
997
node->doAction(action);
1000
// ---------------------------------------------------------------------------------
1002
bool SoFCSelectionRoot::StackComp::operator()(const Stack &a, const Stack &b) const {
1003
if(a.size()-a.offset < b.size()-b.offset)
1005
if(a.size()-a.offset > b.size()-b.offset)
1007
auto it1=a.rbegin(), end1=a.rend()-a.offset;
1008
auto it2=b.rbegin();
1009
for(;it1!=end1;++it1,++it2) {
1017
// ---------------------------------------------------------------------------------
1018
SoSeparator::CacheEnabled SoFCSeparator::CacheMode = SoSeparator::AUTO;
1019
SO_NODE_SOURCE(SoFCSeparator)
1021
SoFCSeparator::SoFCSeparator(bool trackCacheMode)
1022
:trackCacheMode(trackCacheMode)
1024
SO_NODE_CONSTRUCTOR(SoFCSeparator);
1025
if(!trackCacheMode) {
1026
renderCaching = SoSeparator::OFF;
1027
boundingBoxCaching = SoSeparator::OFF;
1031
void SoFCSeparator::GLRenderBelowPath(SoGLRenderAction * action) {
1032
if(trackCacheMode && renderCaching.getValue()!=CacheMode) {
1033
renderCaching = CacheMode;
1034
boundingBoxCaching = CacheMode;
1036
inherited::GLRenderBelowPath(action);
1039
void SoFCSeparator::initClass()
1041
SO_NODE_INIT_CLASS(SoFCSeparator,SoSeparator,"FCSeparator");
1044
void SoFCSeparator::finish()
1049
// ---------------------------------------------------------------------------------
1050
// Thread local data for bounding box rendering
1052
// The code is inspred by Coin SoLevelOfDetails.cpp.
1053
using SoFCBBoxRenderInfo = struct {
1054
SoGetBoundingBoxAction * bboxaction;
1056
SoColorPacker *packer;
1059
static void so_bbox_construct_data(void * closure)
1061
auto data = static_cast<SoFCBBoxRenderInfo*>(closure);
1062
data->bboxaction = nullptr;
1063
data->cube = nullptr;
1064
data->packer = nullptr;
1067
static void so_bbox_destruct_data(void * closure)
1069
auto data = static_cast<SoFCBBoxRenderInfo*>(closure);
1070
delete data->bboxaction;
1072
data->cube->unref();
1073
delete data->packer;
1076
static SbStorage * so_bbox_storage = nullptr;
1078
// called from atexit
1079
static void so_bbox_cleanup()
1081
delete so_bbox_storage;
1084
// ---------------------------------------------------------------------------------
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;
1092
SO_NODE_SOURCE(SoFCSelectionRoot)
1094
SoFCSelectionRoot::SoFCSelectionRoot(bool trackCacheMode)
1095
:SoFCSeparator(trackCacheMode)
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);
1105
SoFCSelectionRoot::~SoFCSelectionRoot() = default;
1107
void SoFCSelectionRoot::initClass()
1109
SO_NODE_INIT_CLASS(SoFCSelectionRoot,SoFCSeparator,"FCSelectionRoot");
1111
so_bbox_storage = new SbStorage(sizeof(SoFCBBoxRenderInfo),
1112
so_bbox_construct_data, so_bbox_destruct_data);
1114
// cc_coin_atexit((coin_atexit_f*) so_bbox_cleanup);
1117
void SoFCSelectionRoot::finish()
1123
SoNode *SoFCSelectionRoot::getCurrentRoot(bool front, SoNode *def) {
1124
if(!SelStack.empty())
1125
return front?SelStack.front():SelStack.back();
1129
SoFCSelectionContextBasePtr SoFCSelectionRoot::getNodeContext(
1130
Stack &stack, SoNode *node, SoFCSelectionContextBasePtr def)
1135
auto front = dynamic_cast<SoFCSelectionRoot *>(stack.front());
1136
if (front == nullptr) {
1137
return SoFCSelectionContextBasePtr();
1140
stack.front() = node;
1142
auto it = front->contextMap.find(stack);
1143
stack.front() = front;
1144
if(it!=front->contextMap.end())
1149
SoFCSelectionContextBasePtr
1150
SoFCSelectionRoot::getNodeContext2(Stack &stack, SoNode *node, SoFCSelectionContextBase::MergeFunc *merge)
1152
SoFCSelectionContextBasePtr ret;
1153
if (stack.empty()) {
1157
auto *back = dynamic_cast<SoFCSelectionRoot*>(stack.back());
1158
if (back == nullptr || back->contextMap2.empty()) {
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;
1170
status = merge(status,ret,ctx,stack.offset==stack.size()-1?nullptr:stack[stack.offset]);
1175
stack.back() = back;
1179
std::pair<bool,SoFCSelectionContextBasePtr*> SoFCSelectionRoot::findActionContext(
1180
SoAction *action, SoNode *_node, bool create, bool erase)
1182
std::pair<bool,SoFCSelectionContextBasePtr*> res(false,0);
1184
if(action->isOfType(SoSelectionElementAction::getClassTypeId()))
1185
res.first = static_cast<SoSelectionElementAction*>(action)->isSecondary();
1187
auto it = ActionStacks.find(action);
1188
if(it==ActionStacks.end() || it->second.empty())
1191
auto &stack = it->second;
1194
auto back = dynamic_cast<SoFCSelectionRoot*>(stack.back());
1195
if (back != nullptr) {
1196
stack.back() = _node;
1198
res.second = &back->contextMap2[stack];
1200
auto it = back->contextMap2.find(stack);
1201
if(it!=back->contextMap2.end()) {
1202
res.second = &it->second;
1204
back->contextMap2.erase(it);
1207
stack.back() = back;
1210
auto front = dynamic_cast<SoFCSelectionRoot*>(stack.front());
1211
if (front != nullptr) {
1212
stack.front() = _node;
1214
res.second = &front->contextMap[stack];
1216
auto it = front->contextMap.find(stack);
1217
if(it!=front->contextMap.end()) {
1218
res.second = &it->second;
1220
front->contextMap.erase(it);
1223
stack.front() = front;
1229
bool SoFCSelectionRoot::renderBBox(SoGLRenderAction *action, SoNode *node, SbColor color)
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;
1238
data->packer = new SoColorPacker;
1242
data->bboxaction->setViewportRegion(action->getViewportRegion());
1243
data->bboxaction->apply(node);
1244
bbox = data->bboxaction->getBoundingBox();
1248
auto state = action->getState();
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);
1258
const static float trans = 0.0;
1259
SoLazyElement::setTransparency(state, node, 1, &trans, data->packer);
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;
1267
SoModelMatrixElement::translateBy(state,node,bbox.getCenter());
1269
SoMaterialBundle mb(action);
1272
data->cube->GLRender(action);
1278
static std::time_t _CyclicLastReported;
1280
void SoFCSelectionRoot::renderPrivate(SoGLRenderAction * action, bool inPath) {
1281
if(ViewParams::instance()->getCoinCycleCheck()
1282
&& !SelStack.nodeSet.insert(this).second)
1284
std::time_t t = std::time(nullptr);
1285
if(_CyclicLastReported < t) {
1286
_CyclicLastReported = t+5;
1287
FC_ERR("Cyclic scene graph: " << getName());
1291
SelStack.push_back(this);
1292
if(_renderPrivate(action,inPath)) {
1294
SoSeparator::GLRenderInPath(action);
1296
SoSeparator::GLRenderBelowPath(action);
1298
SelStack.pop_back();
1299
SelStack.nodeSet.erase(this);
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)
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))
1313
if (style==SoFCSelectionRoot::PassThrough) {
1314
style = SoFCSelectionRoot::Box;
1317
renderBBox(action,this,ctx->hlAll?ctx->hlColor:ctx->selColor);
1322
// Here, we are not setting (pre)selection color override here.
1323
// Instead, we are checking and setting up for any secondary context
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.
1330
bool colorPushed = false;
1331
if(!ShapeColorNode && overrideColor &&
1332
!SoOverrideElement::getDiffuseColorOverride(state) &&
1333
(style==SoFCSelectionRoot::Box || !ctx || (!ctx->selAll && !ctx->hideAll)))
1335
ShapeColorNode = this;
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);
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);
1350
SoTextureEnabledElement::set(state,ShapeColorNode,false);
1355
SoSeparator::GLRenderInPath(action);
1357
SoSeparator::GLRenderBelowPath(action);
1361
if((selPushed = ctx->selAll)) {
1362
SelColorStack.push_back(ctx->selColor);
1364
if(style != SoFCSelectionRoot::Box) {
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);
1379
if((hlPushed = ctx->hlAll))
1380
HlColorStack.push_back(ctx->hlColor);
1383
SoSeparator::GLRenderInPath(action);
1385
SoSeparator::GLRenderBelowPath(action);
1388
SelColorStack.pop_back();
1390
if(style != SoFCSelectionRoot::Box)
1394
HlColorStack.pop_back();
1398
ShapeColorNode = nullptr;
1405
void SoFCSelectionRoot::GLRenderBelowPath(SoGLRenderAction * action) {
1406
renderPrivate(action,false);
1409
void SoFCSelectionRoot::GLRenderInPath(SoGLRenderAction * action) {
1410
if(action->getCurPathCode() == SoAction::BELOW_PATH)
1411
return GLRenderBelowPath(action);
1412
renderPrivate(action,true);
1415
bool SoFCSelectionRoot::checkColorOverride(SoState *state) {
1416
if(ShapeColorNode) {
1417
if(!SoOverrideElement::getDiffuseColorOverride(state)) {
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);
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);
1431
SoTextureEnabledElement::set(state,ShapeColorNode,false);
1438
void SoFCSelectionRoot::checkSelection(bool &sel, SbColor &selColor, bool &hl, SbColor &hlColor) {
1441
if((sel = !SelColorStack.empty()))
1442
selColor = SelColorStack.back();
1443
if((hl = !HlColorStack.empty()))
1444
hlColor = HlColorStack.back();
1447
void SoFCSelectionRoot::resetContext() {
1451
void SoFCSelectionRoot::moveActionStack(SoAction *from, SoAction *to, bool erase) {
1452
auto it = ActionStacks.find(from);
1453
if(it == ActionStacks.end())
1455
auto &stack = ActionStacks[to];
1456
assert(stack.empty());
1457
stack.swap(it->second);
1459
ActionStacks.erase(it);
1462
#define BEGIN_ACTION \
1463
auto &stack = ActionStacks[action];\
1464
if(ViewParams::instance()->getCoinCycleCheck() \
1465
&& !stack.nodeSet.insert(this).second) \
1467
std::time_t t = std::time(0);\
1468
if(_CyclicLastReported < t) {\
1469
_CyclicLastReported = t+5;\
1470
FC_ERR("Cyclic scene graph: " << getName());\
1474
stack.push_back(this);\
1475
auto size = stack.size();
1478
if(stack.size()!=size || stack.back()!=this)\
1479
FC_ERR("action stack fault");\
1481
stack.nodeSet.erase(this);\
1484
ActionStacks.erase(action);\
1487
void SoFCSelectionRoot::pick(SoPickAction * action) {
1489
if(doActionPrivate(stack,action))
1490
inherited::pick(action);
1494
void SoFCSelectionRoot::rayPick(SoRayPickAction * action) {
1496
if(doActionPrivate(stack,action))
1497
inherited::rayPick(action);
1501
void SoFCSelectionRoot::handleEvent(SoHandleEventAction * action) {
1503
inherited::handleEvent(action);
1507
void SoFCSelectionRoot::search(SoSearchAction * action) {
1509
inherited::search(action);
1513
void SoFCSelectionRoot::getPrimitiveCount(SoGetPrimitiveCountAction * action) {
1515
inherited::getPrimitiveCount(action);
1519
void SoFCSelectionRoot::getBoundingBox(SoGetBoundingBoxAction * action)
1522
if(doActionPrivate(stack,action))
1523
inherited::getBoundingBox(action);
1527
void SoFCSelectionRoot::getMatrix(SoGetMatrixAction * action) {
1529
if(doActionPrivate(stack,action))
1530
inherited::getMatrix(action);
1534
void SoFCSelectionRoot::callback(SoCallbackAction *action) {
1536
inherited::callback(action);
1540
void SoFCSelectionRoot::doAction(SoAction *action) {
1542
if(doActionPrivate(stack,action))
1543
inherited::doAction(action);
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
1556
bool ctx2Searched = false;
1557
bool isTail = false;
1558
if(action->getCurPathCode()==SoAction::IN_PATH) {
1559
auto path = action->getPathAppliedTo();
1561
isTail = path->getTail()==this ||
1562
(path->getLength()>1
1563
&& path->getNodeFromTail(1)==this
1564
&& path->getTail()->isOfType(SoSwitch::getClassTypeId()));
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)
1575
}else if(action->getWhatAppliedTo()!=SoAction::NODE && action->getCurPathCode()!=SoAction::BELOW_PATH)
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))
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);
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;
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;
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
1625
auto ctx = getActionContext(action,this,SelContextPtr(),false);
1626
if(ctx && ctx->selAll) {
1627
ctx->selAll = false;
1631
} else if(selAction->getType() == SoSelectionElementAction::All) {
1632
auto ctx = getActionContext(action,this,SelContextPtr());
1635
ctx->selColor = selAction->getColor();
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) {
1652
auto ctx = getActionContext(action,this,SelContextPtr());
1655
ctx->hlColor = hlAction->getColor();
1660
auto ctx = getActionContext(action,this,SelContextPtr(),false);
1661
if(ctx && ctx->hlAll) {
1671
ctx2 = std::static_pointer_cast<SelContext>(getNodeContext2(stack,this,SelContext::merge));
1672
if(ctx2 && ctx2->hideAll)
1678
int SoFCSelectionRoot::SelContext::merge(int status, SoFCSelectionContextBasePtr &output,
1679
SoFCSelectionContextBasePtr input, SoNode *)
1681
auto ctx = std::dynamic_pointer_cast<SelContext>(input);
1682
if(ctx && ctx->hideAll) {
1689
/////////////////////////////////////////////////////////////////////////////
1691
SO_NODE_SOURCE(SoFCPathAnnotation)
1693
SoFCPathAnnotation::SoFCPathAnnotation()
1695
SO_NODE_CONSTRUCTOR(SoFCPathAnnotation);
1701
SoFCPathAnnotation::~SoFCPathAnnotation()
1703
if(path) path->unref();
1704
if(tmpPath) tmpPath->unref();
1708
void SoFCPathAnnotation::finish()
1713
void SoFCPathAnnotation::initClass()
1715
SO_NODE_INIT_CLASS(SoFCPathAnnotation,SoSeparator,"Separator");
1718
void SoFCPathAnnotation::GLRender(SoGLRenderAction * action)
1720
switch (action->getCurPathCode()) {
1721
case SoAction::NO_PATH:
1722
case SoAction::BELOW_PATH:
1723
this->GLRenderBelowPath(action);
1725
case SoAction::OFF_PATH:
1727
case SoAction::IN_PATH:
1728
this->GLRenderInPath(action);
1733
void SoFCPathAnnotation::GLRenderBelowPath(SoGLRenderAction * action)
1735
if(!path || !path->getLength() || !tmpPath || !tmpPath->getLength())
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();
1745
int idx = children->find(tmpPath->getNode(i+1));
1757
SoState * state = action->getState();
1758
SoGLCacheContextElement::shouldAutoCache(state, SoGLCacheContextElement::DONT_AUTO_CACHE);
1760
if (action->isRenderingDelayedPaths()) {
1761
SbBool zbenabled = glIsEnabled(GL_DEPTH_TEST);
1762
if (zbenabled) glDisable(GL_DEPTH_TEST);
1765
inherited::GLRenderInPath(action);
1767
bool bbox = ViewParams::instance()->getShowSelectionBoundingBox();
1769
for(int i=0,count=path->getLength();i<count;++i) {
1770
if(!path->getNode(i)->isOfType(SoFCSelectionRoot::getClassTypeId()))
1772
auto node = dynamic_cast<SoFCSelectionRoot*>(path->getNode(i));
1774
&& node->selectionStyle.getValue() == SoFCSelectionRoot::Box) {
1781
inherited::GLRenderInPath(action);
1785
SbColor selColor,hlColor;
1786
SoFCSelectionRoot::checkSelection(sel,selColor,hl,hlColor);
1788
SoFCSelectionRoot::renderBBox(action,this,hl?hlColor:selColor);
1790
inherited::GLRenderInPath(action);
1794
if (zbenabled) glEnable(GL_DEPTH_TEST);
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);
1806
void SoFCPathAnnotation::GLRenderInPath(SoGLRenderAction * action)
1808
GLRenderBelowPath(action);
1811
void SoFCPathAnnotation::setDetail(SoDetail *d) {
1818
void SoFCPathAnnotation::setPath(SoPath *newPath) {
1821
coinRemoveAllChildren(this);
1828
if(!newPath || !newPath->getLength())
1831
tmpPath = new SoTempPath(newPath->getLength());
1833
for(int i=0;i<newPath->getLength();++i)
1834
tmpPath->append(newPath->getNode(i));
1835
path = newPath->copy();
1837
addChild(path->getNode(0));
1840
void SoFCPathAnnotation::getBoundingBox(SoGetBoundingBoxAction * action)
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();
1849
action->extendBy(bbox);