Files
create/src/Gui/Selection/SoFCUnifiedSelection.h
2025-10-08 16:39:12 -05:00

482 lines
16 KiB
C++

// SPDX-License-Identifier: LGPL-2.1-or-later
/***************************************************************************
* Copyright (c) 2005 Jürgen Riegel <juergen.riegel@web.de> *
* *
* This file is part of the FreeCAD CAx development system. *
* *
* This library is free software; you can redistribute it and/or *
* modify it under the terms of the GNU Library General Public *
* License as published by the Free Software Foundation; either *
* version 2 of the License, or (at your option) any later version. *
* *
* This library is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU Library General Public License for more details. *
* *
* You should have received a copy of the GNU Library General Public *
* License along with this library; see the file COPYING.LIB. If not, *
* write to the Free Software Foundation, Inc., 59 Temple Place, *
* Suite 330, Boston, MA 02111-1307, USA *
* *
***************************************************************************/
#ifndef GUI_SOFCUNIFIEDSELECTION_H
#define GUI_SOFCUNIFIEDSELECTION_H
#include <list>
#include <unordered_map>
#include <unordered_set>
#include <Inventor/elements/SoLazyElement.h>
#include <Inventor/fields/SoSFBool.h>
#include <Inventor/fields/SoSFColor.h>
#include <Inventor/fields/SoSFEnum.h>
#include <Inventor/nodes/SoSeparator.h>
#include "SoFCSelectionContext.h"
#include "../View3DInventorViewer.h"
class SoFullPath;
class SoPickedPoint;
class SoDetail;
namespace Gui {
class Document;
class ViewProviderDocumentObject;
/** Unified Selection node
* This is the new selection node for the 3D Viewer which will
* gradually remove all the low level selection nodes in the view
* provider. The handling of the preselection and the selection will
* be unified here.
* \author Jürgen Riegel
*/
class GuiExport SoFCUnifiedSelection : public SoSeparator {
using inherited = SoSeparator;
SO_NODE_HEADER(Gui::SoFCUnifiedSelection);
public:
static void initClass();
static void finish();
SoFCUnifiedSelection();
void applySettings();
enum SelectionModes {
AUTO, ON, OFF
};
const char* getFileFormatName() const override;
void write(SoWriteAction * action) override;
SoSFColor colorHighlight;
SoSFColor colorSelection;
SoSFEnum preselectionMode;
SoSFEnum selectionMode;
SoSFBool selectionEnabled;
SoSFBool useNewSelection;
void doAction(SoAction *action) override;
void handleEvent(SoHandleEventAction * action) override;
void GLRenderBelowPath(SoGLRenderAction * action) override;
static bool hasHighlight();
friend class View3DInventorViewer;
protected:
~SoFCUnifiedSelection() override;
private:
static int getPriority(const SoPickedPoint* p);
struct PickedInfo {
const SoPickedPoint *pp{nullptr};
ViewProviderDocumentObject *vpd{nullptr};
std::string element;
};
bool setPreselect(const PickedInfo &);
bool setPreselect(SoFullPath *path, const SoDetail *det,
ViewProviderDocumentObject *vpd, const char *element, float x, float y, float z);
bool setSelection(const std::vector<PickedInfo> &, bool ctrlDown=false);
std::vector<PickedInfo> getPickedList(SoHandleEventAction* action, bool singlePick) const;
Gui::Document *pcDocument{nullptr};
static SoFullPath * currentHighlightPath;
SoFullPath * detailPath;
SbBool setPreSelection;
// -1 = not handled, 0 = not selected, 1 = selected
int32_t preSelection;
SoColorPacker colorpacker;
};
class GuiExport SoFCPathAnnotation : public SoSeparator {
using inherited = SoSeparator;
SO_NODE_HEADER(Gui::SoFCPathAnnotation);
public:
static void initClass();
static void finish();
SoFCPathAnnotation();
void setPath(SoPath *);
SoPath *getPath() {return path;}
void setDetail(SoDetail *d);
SoDetail *getDetail() {return det;}
void GLRenderBelowPath(SoGLRenderAction * action) override;
void GLRender(SoGLRenderAction * action) override;
void GLRenderInPath(SoGLRenderAction * action) override;
void getBoundingBox(SoGetBoundingBoxAction * action) override;
protected:
~SoFCPathAnnotation() override;
protected:
SoPath *path;
SoTempPath *tmpPath;
SoDetail *det;
};
class GuiExport SoFCSeparator : public SoSeparator {
using inherited = SoSeparator;
SO_NODE_HEADER(Gui::SoFCSeparator);
public:
static void initClass();
static void finish();
explicit SoFCSeparator(bool trackCacheMode=true);
void GLRenderBelowPath(SoGLRenderAction * action) override;
static void setCacheMode(CacheEnabled mode) {
CacheMode = mode;
}
static CacheEnabled getCacheMode() {
return CacheMode;
}
private:
bool trackCacheMode;
static CacheEnabled CacheMode;
};
class GuiExport SoFCSelectionRoot : public SoFCSeparator {
using inherited = SoFCSeparator;
SO_NODE_HEADER(Gui::SoFCSelectionRoot);
public:
static void initClass();
static void finish();
explicit SoFCSelectionRoot(bool trackCacheMode=false);
void GLRenderBelowPath(SoGLRenderAction * action) override;
void GLRenderInPath(SoGLRenderAction * action) override;
void doAction(SoAction *action) override;
void pick(SoPickAction * action) override;
void rayPick(SoRayPickAction * action) override;
void handleEvent(SoHandleEventAction * action) override;
void search(SoSearchAction * action) override;
void getPrimitiveCount(SoGetPrimitiveCountAction * action) override;
void getBoundingBox(SoGetBoundingBoxAction * action) override;
void getMatrix(SoGetMatrixAction * action) override;
void callback(SoCallbackAction *action) override;
template<class T>
static std::shared_ptr<T> getRenderContext(SoNode *node, std::shared_ptr<T> def = std::shared_ptr<T>()) {
return std::dynamic_pointer_cast<T>(getNodeContext(SelStack,node,def));
}
/** Returns selection context for rendering.
*
* @param node: the querying node
* @param def: default context if none is found
* @param ctx2: secondary context output
*
* @return Returned the primary context for selection, and the context is
* always stored in the first encountered SoFCSelectionRoot in the path. It
* is keyed using the entire sequence of SoFCSelectionRoot along the path
* to \c node, replacing the first SoFCSelectionRoot with the given node.
*
* @return Secondary context returned in \c ctx2 is for customized
* highlighting, and is not affected by mouse event. The highlight is
* applied manually using SoSelectionElementAction. It is stored in the
* last encountered SoFCSelectionRoot, and is keyed using the querying
* \c node and (if there are more than one SoFCSelectionRoot along the
* path) the first SoFCSelectionRoot. The reason is so that any link to a
* node (new links means additional SoFCSelectionRoot added in front) with
* customized subelement highlight will also show the highlight. Secondary
* context can be chained, which why the secondary context type must provide
* an function called merge() for getRenderContext() to merge the context.
* See SoFCSelectionContext::merge() for an implementation of merging multiple
* context.
*
* @note For simplicity reason, currently secondary context is only freed
* when the storage SoFCSSelectionRoot node is freed.
*/
template<class T>
static std::shared_ptr<T> getRenderContext(SoNode *node, std::shared_ptr<T> def, std::shared_ptr<T> &ctx2)
{
ctx2 = std::dynamic_pointer_cast<T>(getNodeContext2(SelStack,node,T::merge));
return std::dynamic_pointer_cast<T>(getNodeContext(SelStack,node,def));
}
/** Get the selection context for an action.
*
* @param action: the action. SoSelectionElementAction has any option to
* query for secondary context. \sa getRenderContext for detail about
* secondary context
* @param node: the querying node
* @param def: default context if none is found, only used if querying
* non-secondary context
* @param create: create a new context if none is found
*
* @return If no SoFCSelectionRoot is found in the current path of action,
* \c def is returned. Otherwise a selection context returned. A new one
* will be created if none is found.
*/
template<class T>
static std::shared_ptr<T> getActionContext(
SoAction *action, SoNode *node, std::shared_ptr<T> def=std::shared_ptr<T>(), bool create=true)
{
auto res = findActionContext(action,node,create,false);
if(!res.second) {
if(res.first)
return std::shared_ptr<T>();
// default context is only applicable for non-secondary context query
return def;
}
// make a new context if there is none
auto &ctx = *res.second;
if(ctx) {
auto ret = std::dynamic_pointer_cast<T>(ctx);
if(!ret)
ctx.reset();
}
if(!ctx && create)
ctx = std::make_shared<T>();
return std::static_pointer_cast<T>(ctx);
}
static bool removeActionContext(SoAction *action, SoNode *node) {
return findActionContext(action,node,false,true).second!=0;
}
template<class T>
static std::shared_ptr<T> getSecondaryActionContext(SoAction *action, SoNode *node) {
auto it = ActionStacks.find(action);
if(it == ActionStacks.end())
return std::shared_ptr<T>();
return std::dynamic_pointer_cast<T>(getNodeContext2(it->second,node,T::merge));
}
static void checkSelection(bool &sel, SbColor &selColor, bool &hl, SbColor &hlColor);
static void moveActionStack(SoAction *from, SoAction *to, bool erase);
static SoNode *getCurrentRoot(bool front, SoNode *def);
void resetContext();
static bool checkColorOverride(SoState *state);
bool hasColorOverride() const {
return overrideColor;
}
void setColorOverride(Base::Color c) {
overrideColor = true;
colorOverride = SbColor(c.r,c.g,c.b);
transOverride = 1.0 - c.a;
}
void removeColorOverride() {
overrideColor = false;
}
enum SelectStyles {
Full, Box, PassThrough
};
SoSFEnum selectionStyle;
static bool renderBBox(SoGLRenderAction *action, SoNode *node, SbColor color);
protected:
~SoFCSelectionRoot() override;
void renderPrivate(SoGLRenderAction *, bool inPath);
bool _renderPrivate(SoGLRenderAction *, bool inPath);
class Stack : public std::vector<SoNode*> {
public:
std::unordered_set<SoNode*> nodeSet;
size_t offset = 0;
};
static SoFCSelectionContextBasePtr getNodeContext(
Stack &stack, SoNode *node, SoFCSelectionContextBasePtr def);
static SoFCSelectionContextBasePtr getNodeContext2(
Stack &stack, SoNode *node, SoFCSelectionContextBase::MergeFunc *merge);
static std::pair<bool,SoFCSelectionContextBasePtr*> findActionContext(
SoAction *action, SoNode *node, bool create, bool erase);
static Stack SelStack;
static std::unordered_map<SoAction*,Stack> ActionStacks;
struct StackComp {
bool operator()(const Stack &a, const Stack &b) const;
};
using ContextMap = std::map<Stack,SoFCSelectionContextBasePtr,StackComp>;
ContextMap contextMap;
ContextMap contextMap2;//holding secondary context
struct SelContext: SoFCSelectionContextBase {
public:
SbColor selColor;
SbColor hlColor;
bool selAll = false;
bool hlAll = false;
bool hideAll = false;
static MergeFunc merge;
};
using SelContextPtr = std::shared_ptr<SelContext>;
using ColorStack = std::vector<SbColor>;
static ColorStack SelColorStack;
static ColorStack HlColorStack;
static SoFCSelectionRoot *ShapeColorNode;
bool overrideColor = false;
SbColor colorOverride;
float transOverride = 0.0f;
SoColorPacker shapeColorPacker;
bool doActionPrivate(Stack &stack, SoAction *);
};
/**
* @author Werner Mayer
*/
class GuiExport SoHighlightElementAction : public SoAction
{
SO_ACTION_HEADER(SoHighlightElementAction);
public:
SoHighlightElementAction ();
~SoHighlightElementAction() override;
void setHighlighted(SbBool);
SbBool isHighlighted() const;
void setColor(const SbColor&);
const SbColor& getColor() const;
void setElement(const SoDetail*);
const SoDetail* getElement() const;
static void initClass();
protected:
void beginTraversal(SoNode *node) override;
private:
static void callDoAction(SoAction *action,SoNode *node);
private:
SbBool _highlight{false};
SbColor _color;
const SoDetail* _det{nullptr};
};
/**
* @author Werner Mayer
*/
class GuiExport SoSelectionElementAction : public SoAction
{
SO_ACTION_HEADER(SoSelectionElementAction);
public:
enum Type {None, Append, Remove, All, Color, Hide, Show};
explicit SoSelectionElementAction (Type=None, bool secondary = false);
~SoSelectionElementAction() override;
Type getType() const;
void setType(Type type) {
_type = type;
}
void setColor(const SbColor&);
const SbColor& getColor() const;
void setElement(const SoDetail*);
const SoDetail* getElement() const;
bool isSecondary() const {return _secondary;}
void setSecondary(bool enable) {
_secondary = enable;
}
const std::map<std::string,Base::Color> &getColors() const {
return _colors;
}
void setColors(const std::map<std::string,Base::Color> &colors) {
_colors = colors;
}
void swapColors(std::map<std::string,Base::Color> &colors) {
_colors.swap(colors);
}
static void initClass();
protected:
void beginTraversal(SoNode *node) override;
private:
static void callDoAction(SoAction *action,SoNode *node);
private:
Type _type;
SbColor _color;
const SoDetail* _det{nullptr};
std::map<std::string,Base::Color> _colors;
bool _secondary;
};
/**
* @author Werner Mayer
*/
class GuiExport SoVRMLAction : public SoAction
{
SO_ACTION_HEADER(SoVRMLAction);
public:
SoVRMLAction();
~SoVRMLAction() override;
void setOverrideMode(SbBool);
SbBool isOverrideMode() const;
static void initClass();
private:
SbBool overrideMode{true};
std::list<int> bindList;
static void callDoAction(SoAction *action,SoNode *node);
};
} // namespace Gui
#endif // !GUI_SOFCUNIFIEDSELECTION_H