ViewProvider(DocumentObject): new APIs for context aware selection

Context aware selection makes it possible to select the same Coin3D node
in different hierarchies (i.e. context) without ambiguity.

New/modified APIs in ViewProvider (the first two are the most crucial
APIs for context aware selection to work):

* getElementPicked(), supercedes getElement(). Given a Coin3D pick
  point, this function returns a dot separated subname reference as a
  path leads to the selected object.

* getDetailPath(), supercedes getDetail(). Given a subname reference,
  this function returns an SoFullPath leads to the Coin3D node of the
  selected object or sub-element (with SoDetail).

* (has)HiddenMarker(), check/return a special text marker for context
  aware override of object visibility.

* partialRender(), render only part of the object based on given
  subname references. It can render, e.g. some faces of a solid, or
  some children of a container. It uses the 'secondary' extension of
  SoSelectionElementAction to select which elements to render or hide.
  The actually rendering functionality will be added in the following
  patch.

* update()/onChanged(), modified to sync Visibility property from
  DocumentObject.

* useNewSelectionModel(), modified to return the default true view
  parameter. This is for test in the early stage of Link development,
  probably not needed anymore.

* getBoundingBox(), a convenience function to obtain the bounding box
  of a sub-object/element regardless of object's visibility. It uses
  getDetailPath() and SoGetBoundingBoxAction to obtain bounding box
  through Coin3D. It will be used in later sub-element box selection
  functionality.

New/modified APIs in ViewProviderDocumentObject:

* getElementPicked()/getDetailPath() provides actual implementation to
  support container like (sub)object selection without ambiguity. It
  relies on DocumentObject::getSubObject() to walk the path.

* reattach(), called when undo deleteion

* forceUpdate()/isUpdateForced(), force update even if object is
  invisible. These are used by Link to force update the visual of a
  linked object regardless of its visibility.

* getLinkedViewProvider(), return the linked view provider with
  hierarchy.

ViewProviderDocumentObjectPy:

* Object attribute is made writtable. Assigning it is equaivalant of
  calling ViewProviderDocumentObject::attach() in Python.
This commit is contained in:
Zheng, Lei
2019-06-22 10:23:35 +08:00
committed by wmayer
parent 2c12e1d3af
commit f5d92fdae7
8 changed files with 713 additions and 14 deletions

View File

@@ -30,12 +30,14 @@
# include <Inventor/SoPickedPoint.h>
# include <Inventor/nodes/SoSeparator.h>
# include <Inventor/nodes/SoSwitch.h>
# include <Inventor/details/SoDetail.h>
# include <Inventor/nodes/SoTransform.h>
# include <Inventor/nodes/SoCamera.h>
# include <Inventor/events/SoMouseButtonEvent.h>
# include <Inventor/events/SoLocation2Event.h>
# include <Inventor/actions/SoGetMatrixAction.h>
# include <Inventor/actions/SoSearchAction.h>
# include <Inventor/actions/SoGetBoundingBoxAction.h>
#endif
/// Here the FreeCAD includes sorted by Base,App,Gui......
@@ -55,6 +57,7 @@
#include "View3DInventorViewer.h"
#include "SoFCDB.h"
#include "ViewProviderExtension.h"
#include "ViewParams.h"
#include <boost/bind.hpp>
@@ -883,6 +886,87 @@ std::vector< App::DocumentObject* > ViewProvider::claimChildren3D(void) const
}
return vec;
}
bool ViewProvider::getElementPicked(const SoPickedPoint *pp, std::string &subname) const {
if(!isSelectable()) return false;
auto vector = getExtensionsDerivedFromType<Gui::ViewProviderExtension>();
for(Gui::ViewProviderExtension* ext : vector)
if(ext->extensionGetElementPicked(pp,subname))
return true;
subname = getElement(pp?pp->getDetail():0);
return true;
}
bool ViewProvider::getDetailPath(const char *subname, SoFullPath *pPath, bool append, SoDetail *&det) const {
if(pcRoot->findChild(pcModeSwitch) < 0) {
// this is possible in case of editing, where the switch node
// of the linked view object is temporarily removed from its root
// if(append)
// pPath->append(pcRoot);
return false;
}
if(append) {
pPath->append(pcRoot);
pPath->append(pcModeSwitch);
}
auto vector = getExtensionsDerivedFromType<Gui::ViewProviderExtension>();
for(Gui::ViewProviderExtension* ext : vector)
if(ext->extensionGetDetailPath(subname,pPath,det))
return true;
det = getDetail(subname);
return true;
}
const std::string &ViewProvider::hiddenMarker() {
return App::DocumentObject::hiddenMarker();
}
const char *ViewProvider::hasHiddenMarker(const char *subname) {
return App::DocumentObject::hasHiddenMarker(subname);
}
int ViewProvider::partialRender(const std::vector<std::string> &elements, bool clear) {
if(elements.empty()) {
auto node = pcModeSwitch->getChild(_iActualMode);
if(node) {
FC_LOG("partial render clear");
SoSelectionElementAction action(SoSelectionElementAction::None,true);
action.apply(node);
}
}
int count = 0;
SoFullPath *path = static_cast<SoFullPath*>(new SoPath);
path->ref();
SoSelectionElementAction action;
action.setSecondary(true);
for(auto element : elements) {
bool hidden = hasHiddenMarker(element.c_str());
if(hidden)
element.resize(element.size()-hiddenMarker().size());
path->truncate(0);
SoDetail *det = 0;
if(getDetailPath(element.c_str(),path,false,det)) {
if(!hidden && !det) {
FC_LOG("partial render element not found: " << element);
continue;
}
FC_LOG("partial render (" << path->getLength() << "): " << element);
if(!hidden)
action.setType(clear?SoSelectionElementAction::Remove:SoSelectionElementAction::Append);
else
action.setType(clear?SoSelectionElementAction::Show:SoSelectionElementAction::Hide);
action.setElement(det);
action.apply(path);
++count;
}
delete det;
}
path->unref();
return count;
}
bool ViewProvider::useNewSelectionModel() const {
return ViewParams::instance()->getUseNewSelection();
}
void ViewProvider::beforeDelete() {
auto vector = getExtensionsDerivedFromType<Gui::ViewProviderExtension>();
@@ -890,6 +974,67 @@ void ViewProvider::beforeDelete() {
ext->extensionBeforeDelete();
}
Base::BoundBox3d ViewProvider::getBoundingBox(const char *subname, bool transform, MDIView *view) const {
if(!pcRoot || !pcModeSwitch || pcRoot->findChild(pcModeSwitch)<0)
return Base::BoundBox3d();
if(!view)
view = Application::Instance->activeView();
auto iview = dynamic_cast<View3DInventor*>(view);
if(!iview) {
auto doc = Application::Instance->activeDocument();
if(doc) {
auto views = doc->getMDIViewsOfType(View3DInventor::getClassTypeId());
if(views.size())
iview = dynamic_cast<View3DInventor*>(views.front());
}
if(!iview) {
FC_ERR("no view");
return Base::BoundBox3d();
}
}
View3DInventorViewer* viewer = iview->getViewer();
SoGetBoundingBoxAction bboxAction(viewer->getSoRenderManager()->getViewportRegion());
auto mode = pcModeSwitch->whichChild.getValue();
if(mode < 0)
pcModeSwitch->whichChild = getDefaultMode();
SoTempPath path(20);
path.ref();
if(subname && subname[0]) {
SoDetail *det=0;
if(!getDetailPath(subname,&path,true,det)) {
if(mode < 0)
pcModeSwitch->whichChild = mode;
path.unrefNoDelete();
return Base::BoundBox3d();
}
delete det;
}
SoTempPath resetPath(3);
resetPath.ref();
if(!transform) {
resetPath.append(pcRoot);
resetPath.append(pcModeSwitch);
bboxAction.setResetPath(&resetPath,true,SoGetBoundingBoxAction::TRANSFORM);
}
if(path.getLength())
bboxAction.apply(&path);
else
bboxAction.apply(pcRoot);
if(mode < 0)
pcModeSwitch->whichChild = mode;
resetPath.unrefNoDelete();
path.unrefNoDelete();
auto bbox = bboxAction.getBoundingBox();
float minX,minY,minZ,maxX,maxY,maxZ;
bbox.getMax().getValue(maxX,maxY,maxZ);
bbox.getMin().getValue(minX,minY,minZ);
return Base::BoundBox3d(minX,minY,minZ,maxX,maxY,maxZ);
}
bool ViewProvider::isLinkVisible() const {
auto ext = getExtensionByType<ViewProviderLinkObserver>(true);
if(!ext)

View File

@@ -35,6 +35,7 @@
#include <App/TransactionalObject.h>
#include <App/Material.h>
#include <Base/Vector3D.h>
#include <Base/BoundBox.h>
class SbVec2s;
class SbVec3f;
@@ -48,6 +49,7 @@ class SbMatrix;
class SoEventCallback;
class SoPickedPoint;
class SoDetail;
class SoFullPath;
class QString;
class QMenu;
class QObject;
@@ -151,12 +153,44 @@ public:
//@{
/// indicates if the ViewProvider use the new Selection model
virtual bool useNewSelectionModel(void) const {return false;}
/// indicates if the ViewProvider can be selected
virtual bool useNewSelectionModel(void) const;
virtual bool isSelectable(void) const {return true;}
/// return a hit element given the picked point which contains the full node path
virtual bool getElementPicked(const SoPickedPoint *, std::string &subname) const;
/// return a hit element to the selection path or 0
virtual std::string getElement(const SoDetail *) const { return std::string(); }
virtual SoDetail* getDetail(const char*) const { return 0; }
/// return the coin node detail of the subelement
virtual SoDetail* getDetail(const char *) const { return 0; }
/** return the coin node detail and path to the node of the subelement
*
* @param subname: dot separated string reference to the sub element
* @param pPath: output coin path leading to the returned element detail
* @param append: If true, pPath will be first appended with the root node and
* the mode switch node of this view provider.
*
* @return the coint detail of the subelement
*
* If this view provider links to other view provider, then the
* implementation of getDetailPath() shall also append all intermediate
* nodes starting just after the mode switch node up till the mode switch of
* the linked view provider.
*/
virtual bool getDetailPath(const char *subname, SoFullPath *pPath, bool append, SoDetail *&det) const;
/** partial rendering setup
*
* @param subelements: a list of dot separated string refer to the sub element
* @param clear: if true, remove the the subelement from partial rendering.
* If else, add the subelement for rendering.
*
* @return Return the number of subelement found
*
* Partial rendering only works if there is at least one SoFCSelectRoot node
* in this view provider
*/
int partialRender(const std::vector<std::string> &subelements, bool clear);
virtual std::vector<Base::Vector3d> getModelPoints(const SoPickedPoint *) const;
/// return the highlight lines for a given element or the whole shape
virtual std::vector<Base::Vector3d> getSelectionShape(const char* Element) const {
@@ -164,6 +198,13 @@ public:
return std::vector<Base::Vector3d>();
}
/** Return the bound box of this view object
*
* This method shall work regardless whether the current view object is
* visible or not.
*/
Base::BoundBox3d getBoundingBox(const char *subname=0, bool transform=true, MDIView *view=0) const;
/**
* Get called if the object is about to get deleted.
* Here you can delete other objects, switch their visibility or prevent the deletion of the object.
@@ -346,6 +387,19 @@ public:
const std::string getOverrideMode();
//@}
/** @name Color mangement methods
*/
//@{
virtual std::map<std::string, App::Color> getElementColors(const char *element=0) const {
(void)element;
return {};
}
virtual void setElementColors(const std::map<std::string, App::Color> &colors) {
(void)colors;
}
static const std::string &hiddenMarker();
static const char *hasHiddenMarker(const char *subname);
//@}
/** @name Edit methods
* if the Viewprovider goes in edit mode
@@ -441,7 +495,7 @@ public:
void setDefaultMode(int);
int getDefaultMode() const;
//@}
protected:
/** Helper method to check that the node is valid, i.e. it must not cause
* and infinite recursion.

View File

@@ -30,22 +30,34 @@
# include <Inventor/nodes/SoDrawStyle.h>
# include <Inventor/nodes/SoMaterial.h>
# include <Inventor/nodes/SoSeparator.h>
# include <Inventor/nodes/SoSwitch.h>
# include <Inventor/nodes/SoTransform.h>
# include <Inventor/SoPickedPoint.h>
# include <Inventor/SoFullPath.h>
# include <Inventor/misc/SoChildList.h>
# include <Inventor/details/SoDetail.h>
#endif
/// Here the FreeCAD includes sorted by Base,App,Gui......
#include <Base/Tools.h>
#include <Base/Console.h>
#include <Base/Tools.h>
#include <Base/BoundBox.h>
#include <App/Material.h>
#include <App/DocumentObjectGroup.h>
#include <App/DocumentObserver.h>
#include <App/Origin.h>
#include "Application.h"
#include "Document.h"
#include "Selection.h"
#include "MainWindow.h"
#include "MDIView.h"
#include "View3DInventor.h"
#include "View3DInventorViewer.h"
#include "TaskView/TaskAppearance.h"
#include "ViewProviderDocumentObject.h"
#include "ViewProviderExtension.h"
#include "Tree.h"
#include <Gui/ViewProviderDocumentObjectPy.h>
FC_LOG_LEVEL_INIT("Gui",true,true)
@@ -61,6 +73,15 @@ ViewProviderDocumentObject::ViewProviderDocumentObject()
{
ADD_PROPERTY(DisplayMode,((long)0));
ADD_PROPERTY(Visibility,(true));
ADD_PROPERTY(ShowInTree,(true));
static const char* OnTopEnum[]= {"Disabled","Enabled","Object","Element",NULL};
ADD_PROPERTY(OnTopWhenSelected,((long int)0));
ADD_PROPERTY_TYPE(OnTopWhenSelected,((long int)0), "Base", App::Prop_None,
"Enabled: Display the object on top of any other object when selected\n"
"Object: On top only if the whole object is selected\n"
"Element: On top only if some sub-element of the object is selected");
OnTopWhenSelected.setEnums(OnTopEnum);
sPixmap = "Feature";
}
@@ -79,10 +100,16 @@ void ViewProviderDocumentObject::getTaskViewContent(std::vector<Gui::TaskView::T
void ViewProviderDocumentObject::startRestoring()
{
hide();
auto vector = getExtensionsDerivedFromType<Gui::ViewProviderExtension>();
for(Gui::ViewProviderExtension* ext : vector)
ext->extensionStartRestoring();
}
void ViewProviderDocumentObject::finishRestoring()
{
auto vector = getExtensionsDerivedFromType<Gui::ViewProviderExtension>();
for(Gui::ViewProviderExtension* ext : vector)
ext->extensionFinishRestoring();
}
bool ViewProviderDocumentObject::isAttachedToDocument() const
@@ -100,6 +127,10 @@ const char* ViewProviderDocumentObject::detachFromDocument()
bool ViewProviderDocumentObject::removeDynamicProperty(const char* name)
{
App::Property* prop = getDynamicPropertyByName(name);
if(!prop || prop->testStatus(App::Property::LockDynamic))
return false;
// transactions of view providers are also managed in App::Document.
App::DocumentObject* docobject = getObject();
App::Document* document = docobject ? docobject->getDocument() : nullptr;
@@ -147,6 +178,8 @@ void ViewProviderDocumentObject::onChanged(const App::Property* prop)
Visibility.getValue() ? show() : hide();
Visibility.setStatus(App::Property::User2, false);
}
if(getObject() && getObject()->Visibility.getValue()!=Visibility.getValue())
getObject()->Visibility.setValue(Visibility.getValue());
}
if (pcDocument && !pcDocument->isModified()) {
@@ -160,13 +193,13 @@ void ViewProviderDocumentObject::onChanged(const App::Property* prop)
void ViewProviderDocumentObject::hide(void)
{
ViewProvider::hide();
// use this bit to check whether 'Visibility' must be adjusted
if (Visibility.testStatus(App::Property::User2) == false) {
Visibility.setStatus(App::Property::User2, true);
Visibility.setValue(false);
Visibility.setStatus(App::Property::User2, false);
}
ViewProvider::hide();
}
void ViewProviderDocumentObject::show(void)
@@ -182,7 +215,7 @@ void ViewProviderDocumentObject::show(void)
void ViewProviderDocumentObject::updateView()
{
if(testStatus(ViewStatus::UpdatingView))
if(!pcObject || testStatus(ViewStatus::UpdatingView))
return;
Base::ObjectStatusLocker<ViewStatus,ViewProviderDocumentObject> lock(ViewStatus::UpdatingView,this);
@@ -196,7 +229,7 @@ void ViewProviderDocumentObject::updateView()
for (std::map<std::string, App::Property*>::iterator it = Map.begin(); it != Map.end(); ++it) {
updateData(it->second);
}
if (vis) ViewProvider::show();
if (vis && Visibility.getValue()) ViewProvider::show();
}
void ViewProviderDocumentObject::attach(App::DocumentObject *pcObj)
@@ -204,6 +237,10 @@ void ViewProviderDocumentObject::attach(App::DocumentObject *pcObj)
// save Object pointer
pcObject = pcObj;
if(pcObj && pcObj->getNameInDocument() &&
Visibility.getValue()!=pcObj->Visibility.getValue())
pcObj->Visibility.setValue(Visibility.getValue());
// Retrieve the supported display modes of the view provider
aDisplayModesArray = this->getDisplayModes();
@@ -229,19 +266,35 @@ void ViewProviderDocumentObject::attach(App::DocumentObject *pcObj)
ext->extensionAttach(pcObj);
}
void ViewProviderDocumentObject::updateData(const App::Property* prop)
void ViewProviderDocumentObject::reattach(App::DocumentObject *pcObj) {
auto vector = getExtensionsDerivedFromType<Gui::ViewProviderExtension>();
for (Gui::ViewProviderExtension* ext : vector)
ext->extensionReattach(pcObj);
}
void ViewProviderDocumentObject::update(const App::Property* prop)
{
ViewProvider::updateData(prop);
// bypass view provider update to always allow changing visibility from
// document object
if(prop == &getObject()->Visibility) {
if(!isRestoring() && Visibility.getValue()!=getObject()->Visibility.getValue())
Visibility.setValue(!Visibility.getValue());
}else
ViewProvider::update(prop);
}
Gui::Document* ViewProviderDocumentObject::getDocument() const
{
if(!pcObject)
throw Base::RuntimeError("View provider detached");
App::Document* pAppDoc = pcObject->getDocument();
return Gui::Application::Instance->getDocument(pAppDoc);
}
Gui::MDIView* ViewProviderDocumentObject::getActiveView() const
{
if(!pcObject)
throw Base::RuntimeError("View provider detached");
App::Document* pAppDoc = pcObject->getDocument();
Gui::Document* pGuiDoc = Gui::Application::Instance->getDocument(pAppDoc);
return pGuiDoc->getActiveView();
@@ -249,6 +302,8 @@ Gui::MDIView* ViewProviderDocumentObject::getActiveView() const
Gui::MDIView* ViewProviderDocumentObject::getEditingView() const
{
if(!pcObject)
throw Base::RuntimeError("View provider detached");
App::Document* pAppDoc = pcObject->getDocument();
Gui::Document* pGuiDoc = Gui::Application::Instance->getDocument(pAppDoc);
return pGuiDoc->getEditingViewOfViewProvider(const_cast<ViewProviderDocumentObject*>(this));
@@ -256,6 +311,8 @@ Gui::MDIView* ViewProviderDocumentObject::getEditingView() const
Gui::MDIView* ViewProviderDocumentObject::getInventorView() const
{
if(!pcObject)
throw Base::RuntimeError("View provider detached");
App::Document* pAppDoc = pcObject->getDocument();
Gui::Document* pGuiDoc = Gui::Application::Instance->getDocument(pAppDoc);
@@ -269,6 +326,8 @@ Gui::MDIView* ViewProviderDocumentObject::getInventorView() const
Gui::MDIView* ViewProviderDocumentObject::getViewOfNode(SoNode* node) const
{
if(!pcObject)
throw Base::RuntimeError("View provider detached");
App::Document* pAppDoc = pcObject->getDocument();
Gui::Document* pGuiDoc = Gui::Application::Instance->getDocument(pAppDoc);
return pGuiDoc->getViewOfNode(node);
@@ -276,6 +335,8 @@ Gui::MDIView* ViewProviderDocumentObject::getViewOfNode(SoNode* node) const
SoNode* ViewProviderDocumentObject::findFrontRootOfType(const SoType& type) const
{
if(!pcObject)
return 0;
// first get the document this object is part of and get its GUI counterpart
App::Document* pAppDoc = pcObject->getDocument();
Gui::Document* pGuiDoc = Gui::Application::Instance->getDocument(pAppDoc);
@@ -336,6 +397,160 @@ PyObject* ViewProviderDocumentObject::getPyObject()
return pyViewObject;
}
bool ViewProviderDocumentObject::canDropObjectEx(App::DocumentObject* obj, App::DocumentObject *owner,
const char *subname, const std::vector<std::string> &elements) const
{
auto vector = getExtensionsDerivedFromType<Gui::ViewProviderExtension>();
for(Gui::ViewProviderExtension* ext : vector){
if(ext->extensionCanDropObjectEx(obj,owner,subname,elements))
return true;
}
if(obj && obj->getDocument()!=getObject()->getDocument())
return false;
return canDropObject(obj);
}
int ViewProviderDocumentObject::replaceObject(
App::DocumentObject *oldObj, App::DocumentObject *newObj)
{
if(!oldObj || !oldObj->getNameInDocument()
|| !newObj || !newObj->getNameInDocument())
{
FC_THROWM(Base::RuntimeError,"Invalid object");
}
auto obj = getObject();
if(!obj || !obj->getNameInDocument())
FC_THROWM(Base::RuntimeError,"View provider not attached");
int res = ViewProvider::replaceObject(oldObj,newObj);
if(res>=0)
return res;
std::vector<std::pair<App::DocumentObjectT, std::unique_ptr<App::Property> > > propChanges;
std::vector<App::Property*> props;
obj->getPropertyList(props);
for(auto prop : props) {
auto linkProp = Base::freecad_dynamic_cast<App::PropertyLinkBase>(prop);
if(!linkProp)
continue;
std::unique_ptr<App::Property> copy(linkProp->CopyOnLinkReplace(obj, oldObj,newObj));
if(!copy)
continue;
propChanges.emplace_back(prop,std::move(copy));
}
if(propChanges.empty())
return 0;
// Global search for affected links
for(auto doc : App::GetApplication().getDocuments()) {
for(auto o : doc->getObjects()) {
if(o == obj)
continue;
std::vector<App::Property*> props;
o->getPropertyList(props);
for(auto prop : props) {
auto linkProp = Base::freecad_dynamic_cast<App::PropertyLinkBase>(prop);
if(!linkProp)
continue;
std::unique_ptr<App::Property> copy(linkProp->CopyOnLinkReplace(obj,oldObj,newObj));
if(!copy)
continue;
propChanges.emplace_back(App::DocumentObjectT(prop),std::move(copy));
}
}
}
for(auto &v : propChanges) {
auto prop = v.first.getProperty();
if(prop)
prop->Paste(*v.second.get());
}
return 1;
}
bool ViewProviderDocumentObject::showInTree() const {
return ShowInTree.getValue();
}
bool ViewProviderDocumentObject::getElementPicked(const SoPickedPoint *pp, std::string &subname) const
{
if(!isSelectable()) return false;
auto vector = getExtensionsDerivedFromType<Gui::ViewProviderExtension>();
for(Gui::ViewProviderExtension* ext : vector)
if(ext->extensionGetElementPicked(pp,subname))
return true;
auto childRoot = getChildRoot();
int idx;
if(!childRoot ||
(idx=pcModeSwitch->whichChild.getValue())<0 ||
pcModeSwitch->getChild(idx)!=childRoot)
{
return ViewProvider::getElementPicked(pp,subname);
}
SoPath* path = pp->getPath();
idx = path->findNode(childRoot);
if(idx<0 || idx+1>=path->getLength())
return false;
auto vp = getDocument()->getViewProvider(path->getNode(idx+1));
if(!vp) return false;
auto obj = vp->getObject();
if(!obj || !obj->getNameInDocument())
return false;
std::ostringstream str;
str << obj->getNameInDocument() << '.';
if(vp->getElementPicked(pp,subname))
str << subname;
subname = str.str();
return true;
}
bool ViewProviderDocumentObject::getDetailPath(const char *subname, SoFullPath *path, bool append, SoDetail *&det) const
{
auto len = path->getLength();
if(!append && len>=2)
len -= 2;
if(ViewProvider::getDetailPath(subname,path,append,det)) {
if(det || !subname || !*subname)
return true;
}
if(det) {
delete det;
det = 0;
}
const char *dot = strchr(subname,'.');
if(!dot) return false;
auto obj = getObject();
if(!obj || !obj->getNameInDocument()) return false;
auto sobj = obj->getSubObject(std::string(subname,dot-subname+1).c_str());
if(!sobj) return false;
auto vp = Application::Instance->getViewProvider(sobj);
if(!vp) return false;
auto childRoot = getChildRoot();
if(!childRoot)
path->truncate(len);
else {
auto idx = pcModeSwitch->whichChild.getValue();
if(idx < 0 || pcModeSwitch->getChild(idx)!=childRoot)
return false;
path->append(childRoot);
}
bool ret = false;
if(path->getLength()) {
SoNode * tail = path->getTail();
const SoChildList * children = tail->getChildren();
if(children && children->find(vp->getRoot())>=0)
ret = vp->getDetailPath(dot+1,path,true,det);
}
return ret;
}
void ViewProviderDocumentObject::onPropertyStatusChanged(
const App::Property &prop, unsigned long oldStatus)
{
@@ -344,6 +559,23 @@ void ViewProviderDocumentObject::onPropertyStatusChanged(
pcObject->getDocument()->signalChangePropertyEditor(*pcObject->getDocument(),prop);
}
ViewProviderDocumentObject *ViewProviderDocumentObject::getLinkedViewProvider(
std::string *subname, bool recursive) const
{
(void)subname;
auto self = const_cast<ViewProviderDocumentObject*>(this);
if(!pcObject || !pcObject->getNameInDocument())
return self;
auto linked = pcObject->getLinkedObject(recursive);
if(!linked || linked == pcObject)
return self;
auto res = Base::freecad_dynamic_cast<ViewProviderDocumentObject>(
Application::Instance->getViewProvider(linked));
if(!res)
res = self;
return res;
}
std::string ViewProviderDocumentObject::getFullName() const {
if(pcObject)
return pcObject->getFullName() + ".ViewObject";

View File

@@ -24,9 +24,10 @@
#ifndef GUI_VIEWPROVIDER_DOCUMENTOBJECT_H
#define GUI_VIEWPROVIDER_DOCUMENTOBJECT_H
#include <Inventor/SoType.h>
#include "ViewProvider.h"
#include <App/DocumentObject.h>
#include <Inventor/SoType.h>
class SoMaterial;
class SoDrawStyle;
@@ -59,9 +60,12 @@ public:
// Display properties
App::PropertyEnumeration DisplayMode;
App::PropertyBool Visibility;
App::PropertyBool ShowInTree;
App::PropertyEnumeration OnTopWhenSelected;
virtual void attach(App::DocumentObject *pcObject);
virtual void updateData(const App::Property*);
virtual void reattach(App::DocumentObject *);
virtual void update(const App::Property*) override;
/// Set the active mode, i.e. the first item of the 'Display' property.
void setActiveMode();
/// Hide the object in the view
@@ -69,6 +73,13 @@ public:
/// Show the object in the view
virtual void show(void);
virtual bool canDropObjectEx(App::DocumentObject *, App::DocumentObject *,
const char *, const std::vector<std::string> &) const override;
virtual int replaceObject(App::DocumentObject*, App::DocumentObject*) override;
virtual bool showInTree() const;
/// Get a list of TaskBoxes associated with this object
virtual void getTaskViewContent(std::vector<Gui::TaskView::TaskContent*>&) const;
@@ -83,6 +94,22 @@ public:
/// Get the python wrapper for that ViewProvider
PyObject* getPyObject();
/// return a hit element given the picked point which contains the full node path
virtual bool getElementPicked(const SoPickedPoint *, std::string &subname) const override;
/// return the coin node detail and path to the node of the subname
virtual bool getDetailPath(const char *subname, SoFullPath *pPath, bool append, SoDetail *&det) const override;
/* Force update visual
*
* These method exists because some view provider skips visual update when
* hidden (e.g. PartGui::ViewProviderPartExt). Call this function to force
* visual update.
*/
//@{
virtual void forceUpdate(bool enable = true) {(void)enable;}
virtual bool isUpdateForced() const {return false;}
//@}
/** @name Restoring view provider from document load */
//@{
virtual void startRestoring();
@@ -96,6 +123,20 @@ public:
const char* group=0, const char* doc=0,
short attr=0, bool ro=false, bool hidden=false) override;
/** Return the linked view object
*
* This function is mainly used for GUI navigation (e.g.
* StdCmdLinkSelectLinked).
*
* @param subname: output as the subname referencing the linked object
* @param recursive: whether to follow the link recursively
*
* @return Returns the linked view provider. If none, it shall return
* itself.
*/
virtual ViewProviderDocumentObject *getLinkedViewProvider(
std::string *subname=0, bool recursive=false) const;
virtual std::string getFullName() const override;
protected:

View File

@@ -18,12 +18,18 @@
<UserDocu>Update the view representation of the object</UserDocu>
</Documentation>
</Methode>
<Attribute Name="Object" ReadOnly="true">
<Attribute Name="Object">
<Documentation>
<UserDocu>Return the associated data object</UserDocu>
<UserDocu>Set/Get the associated data object</UserDocu>
</Documentation>
<Parameter Name="Object" Type="Object" />
</Attribute>
<Attribute Name="ForceUpdate">
<Documentation>
<UserDocu>Reference count to force update visual</UserDocu>
</Documentation>
<Parameter Name="ForceUpdate" Type="Boolean" />
</Attribute>
<Attribute Name="Document" ReadOnly="true">
<Documentation>
<UserDocu>Return the document the view provider is part of</UserDocu>

View File

@@ -29,7 +29,7 @@
#include <Gui/ViewProviderDocumentObject.h>
#include <Gui/Document.h>
#include <App/DocumentObject.h>
#include <App/DocumentObjectPy.h>
// inclusion of the generated files (generated out of ViewProviderDocumentObjectPy.xml)
#include "ViewProviderDocumentObjectPy.h"
@@ -62,6 +62,27 @@ Py::Object ViewProviderDocumentObjectPy::getObject(void) const
return Py::Object(obj->getPyObject(), true); // do not inc'ref twice
}
void ViewProviderDocumentObjectPy::setObject(Py::Object pyobj)
{
if(!PyObject_TypeCheck(*pyobj,&App::DocumentObjectPy::Type))
throw Py::TypeError("Expect document object");
App::DocumentObject* obj = getViewProviderDocumentObjectPtr()->getObject();
if(obj)
throw Py::RuntimeError("View object already attached");
getViewProviderDocumentObjectPtr()->attach(
static_cast<App::DocumentObjectPy*>(*pyobj)->getDocumentObjectPtr());
}
Py::Boolean ViewProviderDocumentObjectPy::getForceUpdate() const
{
return Py::Boolean(getViewProviderDocumentObjectPtr()->isUpdateForced());
}
void ViewProviderDocumentObjectPy::setForceUpdate(Py::Boolean arg)
{
getViewProviderDocumentObjectPtr()->forceUpdate(arg);
}
Py::Object ViewProviderDocumentObjectPy::getDocument(void) const
{
Document* doc = getViewProviderDocumentObjectPtr()->getDocument();

View File

@@ -120,11 +120,66 @@ Returns 1 if succeed, 0 if not found, -1 if not supported
<UserDocu>Returns list of objects that are to be grouped in tree under this object.</UserDocu>
</Documentation>
</Methode>
<Methode Name="partialRender">
<Documentation>
<UserDocu>
partialRender(sub=None,clear=False): render only part of the object
sub: string or list of string refer to the subelement. If it is None then
reset the partial rendering.
clear: true to add, or false to remove the subelement(s) for rendering.
</UserDocu>
</Documentation>
</Methode>
<Methode Name="getElementColors">
<Documentation>
<UserDocu>
getElementColors(elementName=None) -> dict(elementName:color)
</UserDocu>
</Documentation>
</Methode>
<Methode Name="setElementColors">
<Documentation>
<UserDocu>
setElementColors(colors): set element colors
colors: color dictonary of type elementName:(r,g,b,a)
</UserDocu>
</Documentation>
</Methode>
<Methode Name="getElementPicked" Const="true">
<Documentation>
<UserDocu>getElementPicked(pickPoint): return the picked subelement</UserDocu>
</Documentation>
</Methode>
<Methode Name="getDetailPath" Const="true">
<Documentation>
<UserDocu>
getDetailPath(subname,path,append=True): return Coin detail and path of an subelement
subelement: dot separated string reference to the sub element
pPath: output coin path leading to the returned element detail
append: If true, path will be first appended with the root node and the mode
switch node of this view provider.
</UserDocu>
</Documentation>
</Methode>
<Methode Name="signalChangeIcon" Const="true">
<Documentation>
<UserDocu>Trigger icon changed signal</UserDocu>
</Documentation>
</Methode>
<Methode Name="getBoundingBox">
<Documentation>
<UserDocu>
getBoundingBox(subname=None, transform=True, view=None): obtain the bounding box of this view object
* subname: the optional subname referring a sub-object
* transform: whether to apply the transformation matrix of this view provider
* view: the MDIView, default to active view
</UserDocu>
</Documentation>
</Methode>
<Attribute Name="Annotation" ReadOnly="false">
<Documentation>
<UserDocu>A pivy Separator to add a custom scene graph to this ViewProvider</UserDocu>

View File

@@ -377,6 +377,135 @@ PyObject* ViewProviderPy::claimChildren(PyObject* args)
return Py::new_reference_to(ret);
}
PyObject* ViewProviderPy::partialRender(PyObject* args)
{
PyObject *value = Py_None;
PyObject *clear = Py_False;
if (!PyArg_ParseTuple(args, "|OO",&value,&clear))
return NULL; // NULL triggers exception
std::vector<std::string> values;
if(value != Py_None) {
PyObject *item = 0;
Py_ssize_t nSize;
if (PyList_Check(value) || PyTuple_Check(value))
nSize = PySequence_Size(value);
else {
item = value;
value = 0;
nSize = 1;
}
values.resize(nSize);
for (Py_ssize_t i = 0; i < nSize; ++i) {
if(value) item = PySequence_GetItem(value, i);
if (PyUnicode_Check(item)) {
#if PY_MAJOR_VERSION >= 3
values[i] = PyUnicode_AsUTF8(item);
#else
PyObject* unicode = PyUnicode_AsUTF8String(item);
values[i] = PyString_AsString(unicode);
Py_DECREF(unicode);
#endif
}
#if PY_MAJOR_VERSION < 3
else if (PyString_Check(item)) {
values[i] = PyString_AsString(item);
}
#endif
else {
std::string error = std::string("type must be str or unicode");
if(item) {
error += " not, ";
error += item->ob_type->tp_name;
}
throw Base::TypeError(error + item->ob_type->tp_name);
}
}
}
Py::Int ret(getViewProviderPtr()->partialRender(values,PyObject_IsTrue(clear)));
return Py::new_reference_to(ret);
}
PyObject* ViewProviderPy::getElementColors(PyObject* args)
{
const char *element = 0;
if (!PyArg_ParseTuple(args, "|s", &element))
return 0;
Py::Dict dict;
for(auto &v : getViewProviderPtr()->getElementColors(element)) {
auto &c = v.second;
dict.setItem(Py::String(v.first),
Py::TupleN(Py::Float(c.r),Py::Float(c.g),Py::Float(c.b),Py::Float(c.a)));
}
return Py::new_reference_to(dict);
}
PyObject* ViewProviderPy::setElementColors(PyObject* args)
{
PyObject *pyObj;
if (!PyArg_ParseTuple(args, "O", &pyObj))
return 0;
if(!PyDict_Check(pyObj))
throw Py::TypeError("Expect a dict");
std::map<std::string,App::Color> colors;
Py::Dict dict(pyObj);
for(auto it=dict.begin();it!=dict.end();++it) {
const auto &value = *it;
if(!value.first.isString() || !value.second.isSequence())
throw Py::TypeError("Expect the dictonary contain items of type elementName:(r,g,b,a)");
App::PropertyColor prop;
prop.setPyObject(value.second.ptr());
colors[value.first.as_string()] = prop.getValue();
}
getViewProviderPtr()->setElementColors(colors);
Py_Return;
}
PyObject* ViewProviderPy::getElementPicked(PyObject* args)
{
PyObject *obj;
if (!PyArg_ParseTuple(args, "O",&obj))
return NULL;
void *ptr = 0;
Base::Interpreter().convertSWIGPointerObj("pivy.coin", "_p_SoPickedPoint", obj, &ptr, 0);
SoPickedPoint *pp = reinterpret_cast<SoPickedPoint*>(ptr);
if(!pp)
throw Base::TypeError("type must be of coin.SoPickedPoint");
std::string name;
if(!getViewProviderPtr()->getElementPicked(pp,name))
Py_Return;
return Py::new_reference_to(Py::String(name));
}
PyObject* ViewProviderPy::getDetailPath(PyObject* args)
{
const char *sub;
PyObject *path;
PyObject *append = Py_True;
if (!PyArg_ParseTuple(args, "sO|O",&sub,&path,&append))
return NULL;
void *ptr = 0;
Base::Interpreter().convertSWIGPointerObj("pivy.coin", "_p_SoPath", path, &ptr, 0);
SoPath *pPath = reinterpret_cast<SoPath*>(ptr);
if(!pPath)
throw Base::TypeError("type must be of coin.SoPath");
SoDetail *det = 0;
if(!getViewProviderPtr()->getDetailPath(
sub,static_cast<SoFullPath*>(pPath),PyObject_IsTrue(append),det))
{
if(det) delete det;
Py_Return;
}
if(!det)
return Py::new_reference_to(Py::True());
return Base::Interpreter().createSWIGPointerObj("pivy.coin", "_p_SoDetail", (void*)det, 0);
}
PyObject *ViewProviderPy::signalChangeIcon(PyObject *args)
{
if (!PyArg_ParseTuple(args, ""))
@@ -385,6 +514,22 @@ PyObject *ViewProviderPy::signalChangeIcon(PyObject *args)
Py_Return;
}
PyObject *ViewProviderPy::getBoundingBox(PyObject *args) {
PyObject *transform=Py_True;
PyObject *pyView = 0;
const char *subname = 0;
if (!PyArg_ParseTuple(args, "|sOO!", &subname,&transform,View3DInventorPy::type_object(),&pyView))
return NULL;
PY_TRY {
View3DInventor *view = 0;
if(pyView)
view = static_cast<View3DInventorPy*>(pyView)->getView3DIventorPtr();
auto bbox = getViewProviderPtr()->getBoundingBox(subname,PyObject_IsTrue(transform),view);
Py::Object ret(new Base::BoundBoxPy(new Base::BoundBox3d(bbox)));
return Py::new_reference_to(ret);
} PY_CATCH;
}
PyObject *ViewProviderPy::doubleClicked(PyObject *args) {
if(!PyArg_ParseTuple(args, ""))
return 0;