App: add New APIs for future Link function
DocumentObject: * getSubObject(): the most important API for Link to work with hierarchies. The function is a inspired from and replaces the getPySubObjects(). It returns a child object following a dot separated subname reference, and can optionally return accumulated transformation, and/or a python object of the refered sub-object/element. The default implementation here is to look for link type property, and search for the referenced object. This patch also include other specialized implementation of this API, such as (GeoFeature)GroupExtension (through extensionGetSubObject()), PartDesign::Body, and so on. A link type object is expected to call the linked object's getSubObject() for resolving. * getSubObjectList(): helper function to return a list of object referenced in the given subname. * getSubObjects(): return a list of subname references of all children objects. The purpose of this function is similar to ViewProvider::claimChildren(). Container type object is expected to implement this function. The reason it returns subname references instead of just object is to allow the container to skip hierarchies. For example, the Assembly3 container uses this to skip the constraint and element group. * getLinkedObject(), obtain the linked object, and optionally with the accumulated transformation. It is expected to return a linked object or the object itself if it is not a link. In case there are multiple levels of linking involved, this function allows the caller to retrieve the linked object recursively. * hasChildElement(), set/isElementVisible(), controls the children visibility for a group type object. Because the child object may be claimed by other objects, it is essential to have independent control of children visibilities. These APIs are designed to abstract how group manages the child visibility. For performance reason, these function are meant to control only the immediate child object. * resolve(), helper function to parse subname reference and resolve the final object, and optionally the immediate parent of the final object, the final object reference name (for calling `set/isElementVisible()`), and the subname reference if there is one. * touch(), add optional argument 'noRecompute' for better backward compatibility with the NoRecompute flag. By default, touch() causes recompute unless noRecompute is true * signalChanged/signalBeforeChange, two new signal for tracking changes of a specific object. * getViewProviderNameOverride(), return a string of the view provider type of this object. This allows Python class to override the view provider of an object. This feature will be used by ViewProviderLink which is designed to work with any object that has LinkBaseExtension. * canLinkProperties(), will be used by Gui::PropertyView to display linked object properties together with the object's own properties. * redirectSubname(), will be used by Gui::Tree to allow an object to redirect selection to some other object when (pre)selected in the tree view. * Visibility, new property serve as the same purpose as view provider property of the same name. It is added here so that App namespace code can check for visibility without Gui module. This is useful, for example, when constructing a compound shape of a container that respects the children visibility. * (has)hasHiddenMarker(), return or check for a special sub-element name used as marker for overriding sub-object visibility. Will be used by Gui::ViewProvider, it is put here for the same reason as adding Visibility property. * getID(), return object internal identifier. Each object is now assigned an integer identifier that is unique within its containing document. Document: * ShowHidden, new property to tell tree view whether to show hidden object items. * signalTouchedObject, new signal triggered when manually touch an object when calling its touch() function * getObjectByID(), get object by its identifier * addObject() is modified to allow overriding view provider * has/getLinksTo(), helper function to obtain links to a given object. Application: * checkLinkDepth(), helper function to check recursive depth for link traversal. The depth is checked against the total object count of all opened documents. The count (_objCount) is internally updated whenever object is added or removed. * has/getLinksTo(), same as Document::has/getLinksTo() but return links from all opened documents. GroupExtension/OriginGroupExtension/DatumFeature/DatumCS/Part::Feature: implement sepcialized getSubObject/getSubObjects().
This commit is contained in:
@@ -148,6 +148,7 @@ using namespace boost::program_options;
|
||||
# include <new>
|
||||
#endif
|
||||
|
||||
FC_LOG_LEVEL_INIT("App",true,true);
|
||||
|
||||
//using Base::GetConsole;
|
||||
using namespace Base;
|
||||
@@ -249,7 +250,7 @@ init_freecad_module(void)
|
||||
#endif
|
||||
|
||||
Application::Application(std::map<std::string,std::string> &mConfig)
|
||||
: _mConfig(mConfig), _pActiveDoc(0)
|
||||
: _mConfig(mConfig), _pActiveDoc(0), _objCount(-1)
|
||||
{
|
||||
//_hApp = new ApplicationOCC;
|
||||
mpcPramManager["System parameter"] = _pcSysParamMngr;
|
||||
@@ -481,6 +482,8 @@ bool Application::closeDocument(const char* name)
|
||||
std::unique_ptr<Document> delDoc (pos->second);
|
||||
DocMap.erase( pos );
|
||||
|
||||
_objCount = -1;
|
||||
|
||||
// Trigger observers after removing the document from the internal map.
|
||||
signalDeletedDocument();
|
||||
|
||||
@@ -704,6 +707,51 @@ std::string Application::getHelpDir()
|
||||
#endif
|
||||
}
|
||||
|
||||
int Application::checkLinkDepth(int depth, bool no_throw) {
|
||||
if(_objCount<0) {
|
||||
_objCount = 0;
|
||||
for(auto &v : DocMap)
|
||||
_objCount += v.second->countObjects();
|
||||
}
|
||||
if(depth > _objCount+2) {
|
||||
const char *msg = "Link recursion limit reached. "
|
||||
"Please check for cyclic reference.";
|
||||
if(no_throw) {
|
||||
FC_ERR(msg);
|
||||
return 0;
|
||||
}else
|
||||
throw Base::RuntimeError(msg);
|
||||
}
|
||||
return _objCount+2;
|
||||
}
|
||||
|
||||
std::set<DocumentObject *> Application::getLinksTo(
|
||||
const DocumentObject *obj, int options, int maxCount) const
|
||||
{
|
||||
std::set<DocumentObject *> links;
|
||||
if(!obj) {
|
||||
for(auto &v : DocMap) {
|
||||
v.second->getLinksTo(links,obj,options,maxCount);
|
||||
if(maxCount && (int)links.size()>=maxCount)
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
std::set<Document*> docs;
|
||||
for(auto o : obj->getInList()) {
|
||||
if(o && o->getNameInDocument() && docs.insert(o->getDocument()).second) {
|
||||
o->getDocument()->getLinksTo(links,obj,options,maxCount);
|
||||
if(maxCount && (int)links.size()>=maxCount)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return links;
|
||||
}
|
||||
|
||||
bool Application::hasLinksTo(const DocumentObject *obj) const {
|
||||
return !getLinksTo(obj,0,1).empty();
|
||||
}
|
||||
|
||||
ParameterManager & Application::GetSystemParameter(void)
|
||||
{
|
||||
return *_pcSysParamMngr;
|
||||
@@ -1009,11 +1057,13 @@ void Application::slotChangedDocument(const App::Document& doc, const Property&
|
||||
void Application::slotNewObject(const App::DocumentObject&O)
|
||||
{
|
||||
this->signalNewObject(O);
|
||||
_objCount = -1;
|
||||
}
|
||||
|
||||
void Application::slotDeletedObject(const App::DocumentObject&O)
|
||||
{
|
||||
this->signalDeletedObject(O);
|
||||
_objCount = -1;
|
||||
}
|
||||
|
||||
void Application::slotBeforeChangeObject(const DocumentObject& O, const Property& Prop)
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
#include <boost/signals2.hpp>
|
||||
|
||||
#include <vector>
|
||||
#include <deque>
|
||||
|
||||
#include <Base/PyObjectBase.h>
|
||||
#include <Base/Parameter.h>
|
||||
@@ -48,6 +49,16 @@ class DocumentObject;
|
||||
class ApplicationObserver;
|
||||
class Property;
|
||||
|
||||
enum GetLinkOption {
|
||||
/// Get all links (both directly and in directly) linked to the given object
|
||||
GetLinkRecursive = 1,
|
||||
/// Get link array element instead of the array
|
||||
GetLinkArrayElement = 2,
|
||||
/// Get linked object instead of the link, no effect if GetLinkRecursive
|
||||
GetLinkedObject = 4,
|
||||
/// Get only external links, no effect if GetLinkRecursive
|
||||
GetLinkExternal = 8,
|
||||
};
|
||||
|
||||
|
||||
/** The Application
|
||||
@@ -124,6 +135,8 @@ public:
|
||||
boost::signals2::signal<void (const Document&)> signalUndoDocument;
|
||||
/// signal on redo in document
|
||||
boost::signals2::signal<void (const Document&)> signalRedoDocument;
|
||||
/// signal on show hidden items
|
||||
boost::signals2::signal<void (const Document&)> signalShowHidden;
|
||||
//@}
|
||||
|
||||
|
||||
@@ -274,6 +287,34 @@ public:
|
||||
static std::string getHelpDir();
|
||||
//@}
|
||||
|
||||
/** @name Link handling */
|
||||
//@{
|
||||
|
||||
/** Check for link recursion depth
|
||||
*
|
||||
* @param depth: current depth
|
||||
* @param no_throw: whether to throw exception
|
||||
*
|
||||
* @return Return the maximum remaining depth.
|
||||
*
|
||||
* The function uses an internal count of all objects in all documents as
|
||||
* the limit of recursion depth.
|
||||
*/
|
||||
int checkLinkDepth(int depth, bool no_throw=true);
|
||||
|
||||
/** Return the links to a given object
|
||||
*
|
||||
* @param obj: the linked object. If NULL, then all links are returned.
|
||||
* @param option: @sa App::GetLinkOptions
|
||||
* @param maxCount: limit the number of links returned, 0 means no limit
|
||||
*/
|
||||
std::set<DocumentObject*> getLinksTo(
|
||||
const DocumentObject *, int options, int maxCount=0) const;
|
||||
|
||||
/// Check if there is any link to the given object
|
||||
bool hasLinksTo(const DocumentObject *obj) const;
|
||||
//@}
|
||||
|
||||
friend class App::Document;
|
||||
|
||||
protected:
|
||||
@@ -315,7 +356,6 @@ private:
|
||||
static ParameterManager *_pcUserParamMngr;
|
||||
//@}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// python exports goes here +++++++++++++++++++++++++++++++++++++++++++
|
||||
//---------------------------------------------------------------------
|
||||
@@ -353,7 +393,10 @@ private:
|
||||
static PyObject *sSetLogLevel (PyObject *self,PyObject *args);
|
||||
static PyObject *sGetLogLevel (PyObject *self,PyObject *args);
|
||||
|
||||
static PyMethodDef Methods[];
|
||||
static PyObject *sCheckLinkDepth (PyObject *self,PyObject *args);
|
||||
static PyObject *sGetLinksTo (PyObject *self,PyObject *args);
|
||||
|
||||
static PyMethodDef Methods[];
|
||||
|
||||
friend class ApplicationObserver;
|
||||
|
||||
@@ -401,6 +444,9 @@ private:
|
||||
std::map<std::string,std::string> &_mConfig;
|
||||
App::Document* _pActiveDoc;
|
||||
|
||||
// for estimate max link depth
|
||||
int _objCount;
|
||||
|
||||
static Base::ConsoleObserverStd *_pConsoleObserverStd;
|
||||
static Base::ConsoleObserverFile *_pConsoleObserverFile;
|
||||
};
|
||||
|
||||
@@ -35,6 +35,7 @@
|
||||
#include "Document.h"
|
||||
#include "DocumentPy.h"
|
||||
#include "DocumentObserverPython.h"
|
||||
#include "DocumentObjectPy.h"
|
||||
|
||||
// FreeCAD Base header
|
||||
#include <Base/Interpreter.h>
|
||||
@@ -144,7 +145,12 @@ PyMethodDef Application::Methods[] = {
|
||||
"'level' can either be string 'Log', 'Msg', 'Wrn', 'Error', or an integer value"},
|
||||
{"getLogLevel", (PyCFunction) Application::sGetLogLevel, METH_VARARGS,
|
||||
"getLogLevel(tag) -- Get the log level of a string tag"},
|
||||
|
||||
{"checkLinkDepth", (PyCFunction) Application::sCheckLinkDepth, METH_VARARGS,
|
||||
"checkLinkDepth(depth) -- check link recursion depth"},
|
||||
{"getLinksTo", (PyCFunction) Application::sGetLinksTo, METH_VARARGS,
|
||||
"getLinksTo(obj,options=0,maxCount=0) -- return the objects linked to 'obj'\n\n"
|
||||
"options: 1: recursive, 2: check link array. Options can combine.\n"
|
||||
"maxCount: to limit the number of links returned\n"},
|
||||
{NULL, NULL, 0, NULL} /* Sentinel */
|
||||
};
|
||||
|
||||
@@ -763,4 +769,40 @@ PyObject *Application::sGetLogLevel(PyObject * /*self*/, PyObject *args)
|
||||
} PY_CATCH;
|
||||
}
|
||||
|
||||
PyObject *Application::sCheckLinkDepth(PyObject * /*self*/, PyObject *args)
|
||||
{
|
||||
short depth = 0;
|
||||
if (!PyArg_ParseTuple(args, "h", &depth))
|
||||
return NULL;
|
||||
|
||||
PY_TRY {
|
||||
return Py::new_reference_to(Py::Int(GetApplication().checkLinkDepth(depth,false)));
|
||||
}PY_CATCH;
|
||||
}
|
||||
|
||||
PyObject *Application::sGetLinksTo(PyObject * /*self*/, PyObject *args)
|
||||
{
|
||||
PyObject *pyobj = Py_None;
|
||||
int options = 0;
|
||||
short count = 0;
|
||||
if (!PyArg_ParseTuple(args, "|Oih",&pyobj,&options, &count))
|
||||
return NULL;
|
||||
|
||||
PY_TRY {
|
||||
DocumentObject *obj = 0;
|
||||
if(pyobj!=Py_None) {
|
||||
if(!PyObject_TypeCheck(pyobj,&DocumentObjectPy::Type)) {
|
||||
PyErr_SetString(PyExc_TypeError, "Expect the first argument of type document object");
|
||||
return 0;
|
||||
}
|
||||
obj = static_cast<DocumentObjectPy*>(pyobj)->getDocumentObjectPtr();
|
||||
}
|
||||
auto links = GetApplication().getLinksTo(obj,options,count);
|
||||
Py::Tuple ret(links.size());
|
||||
int i=0;
|
||||
for(auto o : links)
|
||||
ret.setItem(i++,Py::Object(o->getPyObject(),true));
|
||||
return Py::new_reference_to(ret);
|
||||
}PY_CATCH;
|
||||
}
|
||||
|
||||
|
||||
@@ -68,6 +68,8 @@ recompute path. Also, it enables more complicated dependencies beyond trees.
|
||||
# include <random>
|
||||
#endif
|
||||
|
||||
#include <boost/algorithm/string.hpp>
|
||||
|
||||
#include <boost/graph/adjacency_list.hpp>
|
||||
#include <boost/graph/subgraph.hpp>
|
||||
#include <boost/graph/graphviz.hpp>
|
||||
@@ -83,6 +85,7 @@ recompute path. Also, it enables more complicated dependencies beyond trees.
|
||||
#include <boost/regex.hpp>
|
||||
#include <unordered_set>
|
||||
#include <unordered_map>
|
||||
#include <random>
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QCryptographicHash>
|
||||
@@ -154,7 +157,9 @@ struct DocumentP
|
||||
{
|
||||
// Array to preserve the creation order of created objects
|
||||
std::vector<DocumentObject*> objectArray;
|
||||
std::map<std::string,DocumentObject*> objectMap;
|
||||
std::unordered_map<std::string,DocumentObject*> objectMap;
|
||||
std::unordered_map<long,DocumentObject*> objectIdMap;
|
||||
long lastObjectId;
|
||||
DocumentObject* activeObject;
|
||||
Transaction *activeUndoTransaction;
|
||||
int iTransactionMode;
|
||||
@@ -171,6 +176,13 @@ struct DocumentP
|
||||
#endif //USE_OLD_DAG
|
||||
|
||||
DocumentP() {
|
||||
static std::random_device _RD;
|
||||
static std::mt19937 _RGEN(_RD());
|
||||
static std::uniform_int_distribution<> _RDIST(0,5000);
|
||||
// Set some random offset to reduce likelyhood of ID collison when
|
||||
// copying shape from other document. It is probably better to randomize
|
||||
// on each object ID.
|
||||
lastObjectId = _RDIST(_RGEN);
|
||||
activeObject = 0;
|
||||
activeUndoTransaction = 0;
|
||||
iTransactionMode = 0;
|
||||
@@ -218,7 +230,7 @@ void Document::writeDependencyGraphViz(std::ostream &out)
|
||||
out << "\tordering=out;" << endl;
|
||||
out << "\tnode [shape = box];" << endl;
|
||||
|
||||
for (std::map<std::string,DocumentObject*>::const_iterator It = d->objectMap.begin(); It != d->objectMap.end();++It) {
|
||||
for (auto It = d->objectMap.begin(); It != d->objectMap.end();++It) {
|
||||
out << "\t" << It->first << ";" <<endl;
|
||||
std::vector<DocumentObject*> OutList = It->second->getOutList();
|
||||
for (std::vector<DocumentObject*>::const_iterator It2=OutList.begin();It2!=OutList.end();++It2)
|
||||
@@ -573,11 +585,11 @@ void Document::exportGraphviz(std::ostream& out) const
|
||||
}
|
||||
|
||||
// Internal document objects
|
||||
for (std::map<std::string,DocumentObject*>::const_iterator It = d->objectMap.begin(); It != d->objectMap.end();++It)
|
||||
for (auto It = d->objectMap.begin(); It != d->objectMap.end();++It)
|
||||
addExpressionSubgraphIfNeeded(It->second, CSSubgraphs);
|
||||
|
||||
// Add external document objects
|
||||
for (std::map<std::string,DocumentObject*>::const_iterator It = d->objectMap.begin(); It != d->objectMap.end();++It) {
|
||||
for (auto It = d->objectMap.begin(); It != d->objectMap.end();++It) {
|
||||
std::vector<DocumentObject*> OutList = It->second->getOutList();
|
||||
for (std::vector<DocumentObject*>::const_iterator It2=OutList.begin();It2!=OutList.end();++It2) {
|
||||
if (*It2) {
|
||||
@@ -598,11 +610,11 @@ void Document::exportGraphviz(std::ostream& out) const
|
||||
bool CSSubgraphs = depGrp->GetBool("GeoFeatureSubgraphs", true);
|
||||
|
||||
// Add internal document objects
|
||||
for (std::map<std::string,DocumentObject*>::const_iterator It = d->objectMap.begin(); It != d->objectMap.end();++It)
|
||||
for (auto It = d->objectMap.begin(); It != d->objectMap.end();++It)
|
||||
add(It->second, It->second->getNameInDocument(), It->second->Label.getValue(), CSSubgraphs);
|
||||
|
||||
// Add external document objects
|
||||
for (std::map<std::string,DocumentObject*>::const_iterator It = d->objectMap.begin(); It != d->objectMap.end();++It) {
|
||||
for (auto It = d->objectMap.begin(); It != d->objectMap.end();++It) {
|
||||
std::vector<DocumentObject*> OutList = It->second->getOutList();
|
||||
for (std::vector<DocumentObject*>::const_iterator It2=OutList.begin();It2!=OutList.end();++It2) {
|
||||
if (*It2) {
|
||||
@@ -664,7 +676,7 @@ void Document::exportGraphviz(std::ostream& out) const
|
||||
bool omitGeoFeatureGroups = depGrp->GetBool("GeoFeatureSubgraphs", true);
|
||||
|
||||
// Add edges between document objects
|
||||
for (std::map<std::string, DocumentObject*>::const_iterator It = d->objectMap.begin(); It != d->objectMap.end();++It) {
|
||||
for (auto It = d->objectMap.begin(); It != d->objectMap.end();++It) {
|
||||
|
||||
if(omitGeoFeatureGroups) {
|
||||
//coordinate systems are represented by subgraphs
|
||||
@@ -1132,8 +1144,9 @@ void Document::onChanged(const Property* prop)
|
||||
// the Name property is a label for display purposes
|
||||
if (prop == &Label) {
|
||||
App::GetApplication().signalRelabelDocument(*this);
|
||||
}
|
||||
else if (prop == &Uid) {
|
||||
} else if(prop == &ShowHidden) {
|
||||
App::GetApplication().signalShowHidden(*this);
|
||||
} else if (prop == &Uid) {
|
||||
std::string new_dir = getTransientDirectoryName(this->Uid.getValueStr(),this->FileName.getStrValue());
|
||||
std::string old_dir = this->TransientDir.getStrValue();
|
||||
Base::FileInfo TransDirNew(new_dir);
|
||||
@@ -1279,6 +1292,8 @@ Document::Document(void)
|
||||
|
||||
ADD_PROPERTY_TYPE(License,(license.c_str()),0,Prop_None,"License string of the Item");
|
||||
ADD_PROPERTY_TYPE(LicenseURL,(licenseUrl.c_str()),0,Prop_None,"URL to the license text/contract");
|
||||
ADD_PROPERTY_TYPE(ShowHidden,(false), 0,PropertyType(Prop_None),
|
||||
"Whether to show hidden object items in the tree view");
|
||||
|
||||
// this creates and sets 'TransientDir' in onChanged()
|
||||
ADD_PROPERTY_TYPE(TransientDir,(""),0,PropertyType(Prop_Transient|Prop_ReadOnly),
|
||||
@@ -1302,14 +1317,12 @@ Document::~Document()
|
||||
catch (const boost::exception&) {
|
||||
}
|
||||
|
||||
std::map<std::string,DocumentObject*>::iterator it;
|
||||
|
||||
#ifdef FC_LOGUPDATECHAIN
|
||||
Console().Log("-Delete Features of %s \n",getName());
|
||||
#endif
|
||||
|
||||
d->objectArray.clear();
|
||||
for (it = d->objectMap.begin(); it != d->objectMap.end(); ++it) {
|
||||
for (auto it = d->objectMap.begin(); it != d->objectMap.end(); ++it) {
|
||||
it->second->setStatus(ObjectStatus::Destroy, true);
|
||||
delete(it->second);
|
||||
}
|
||||
@@ -1413,7 +1426,6 @@ void Document::Restore(Base::XMLReader &reader)
|
||||
reader.readElement("Feature");
|
||||
string type = reader.getAttribute("type");
|
||||
string name = reader.getAttribute("name");
|
||||
|
||||
try {
|
||||
addObject(type.c_str(), name.c_str(), /*isNew=*/ false);
|
||||
}
|
||||
@@ -1477,6 +1489,7 @@ void Document::exportObjects(const std::vector<App::DocumentObject*>& obj,
|
||||
writer.writeFiles();
|
||||
}
|
||||
|
||||
|
||||
void Document::writeObjects(const std::vector<App::DocumentObject*>& obj,
|
||||
Base::Writer &writer) const
|
||||
{
|
||||
@@ -1490,12 +1503,18 @@ void Document::writeObjects(const std::vector<App::DocumentObject*>& obj,
|
||||
writer.Stream() << writer.ind() << "<Object "
|
||||
<< "type=\"" << (*it)->getTypeId().getName() << "\" "
|
||||
<< "name=\"" << (*it)->getNameInDocument() << "\" ";
|
||||
<< "id=\"" << (*it)->getID() << "\" "
|
||||
<< "ViewType=\"" << (*it)->getViewProviderNameStored() << "\" ";
|
||||
|
||||
// See DocumentObjectPy::getState
|
||||
if ((*it)->testStatus(ObjectStatus::Touch))
|
||||
writer.Stream() << "Touched=\"1\" ";
|
||||
if ((*it)->testStatus(ObjectStatus::Error))
|
||||
if ((*it)->testStatus(ObjectStatus::Error)) {
|
||||
writer.Stream() << "Invalid=\"1\" ";
|
||||
auto desc = getErrorDescription(*it);
|
||||
if(desc)
|
||||
writer.Stream() << "Error=\"" << Property::encodeAttribute(desc) << "\" ";
|
||||
}
|
||||
writer.Stream() << "/>" << endl;
|
||||
}
|
||||
|
||||
@@ -1528,21 +1547,33 @@ Document::readObjects(Base::XMLReader& reader)
|
||||
setStatus(Document::KeepTrailingDigits, !reader.doNameMapping());
|
||||
std::vector<App::DocumentObject*> objs;
|
||||
|
||||
|
||||
// read the object types
|
||||
reader.readElement("Objects");
|
||||
int Cnt = reader.getAttributeAsInteger("Count");
|
||||
|
||||
long lastId = 0;
|
||||
for (int i=0 ;i<Cnt ;i++) {
|
||||
reader.readElement("Object");
|
||||
std::string type = reader.getAttribute("type");
|
||||
std::string name = reader.getAttribute("name");
|
||||
const char *viewType = reader.hasAttribute("ViewType")?reader.getAttribute("ViewType"):0;
|
||||
|
||||
if(!testStatus(Status::Importing) && reader.hasAttribute("id")) {
|
||||
// if not importing, then temporary reset lastObjectId and make the
|
||||
// following addObject() generate the correct id for this object.
|
||||
d->lastObjectId = reader.getAttributeAsInteger("id")-1;
|
||||
}
|
||||
|
||||
try {
|
||||
// Use name from XML as is and do NOT remove trailing digits because
|
||||
// otherwise we may cause a dependency to itself
|
||||
// Example: Object 'Cut001' references object 'Cut' and removing the
|
||||
// digits we make an object 'Cut' referencing itself.
|
||||
App::DocumentObject* obj = addObject(type.c_str(), name.c_str(), /*isNew=*/ false);
|
||||
App::DocumentObject* obj = addObject(type.c_str(), obj_name, /*isNew=*/ false, viewType, partial);
|
||||
if (obj) {
|
||||
if(lastId < obj->_Id)
|
||||
lastId = obj->_Id;
|
||||
objs.push_back(obj);
|
||||
// use this name for the later access because an object with
|
||||
// the given name may already exist
|
||||
@@ -1559,6 +1590,8 @@ Document::readObjects(Base::XMLReader& reader)
|
||||
Base::Console().Error("Cannot create object '%s': (%s)\n", name.c_str(), e.what());
|
||||
}
|
||||
}
|
||||
if(!testStatus(Status::Importing))
|
||||
d->lastObjectId = lastId;
|
||||
|
||||
reader.readEndElement("Objects");
|
||||
setStatus(Document::KeepTrailingDigits, keepDigits);
|
||||
@@ -1627,6 +1660,7 @@ Document::importObjects(Base::XMLReader& reader)
|
||||
std::vector<App::DocumentObject*> objs = readObjects(reader);
|
||||
|
||||
reader.readEndElement("Document");
|
||||
|
||||
signalImportObjects(objs, reader);
|
||||
|
||||
// reset all touched
|
||||
@@ -1846,7 +1880,8 @@ void Document::restore (void)
|
||||
}
|
||||
d->objectArray.clear();
|
||||
d->objectMap.clear();
|
||||
d->activeObject = 0;
|
||||
d->objectIdMap.clear();
|
||||
d->lastObjectId = 0;
|
||||
|
||||
Base::FileInfo fi(FileName.getValue());
|
||||
Base::ifstream file(fi, std::ios::in | std::ios::binary);
|
||||
@@ -1966,12 +2001,75 @@ int Document::countObjects(void) const
|
||||
return static_cast<int>(d->objectArray.size());
|
||||
}
|
||||
|
||||
void Document::getLinksTo(std::set<DocumentObject*> &links,
|
||||
const DocumentObject *obj, int options, int maxCount,
|
||||
const std::vector<DocumentObject*> &objs) const
|
||||
{
|
||||
std::map<const App::DocumentObject*,App::DocumentObject*> linkMap;
|
||||
|
||||
for(auto o : objs.size()?objs:d->objectArray) {
|
||||
if(o == obj) continue;
|
||||
auto linked = o;
|
||||
if(options & GetLinkArrayElement)
|
||||
linked = o->getLinkedObject(false);
|
||||
else {
|
||||
auto ext = o->getExtensionByType<LinkBaseExtension>(true);
|
||||
if(ext)
|
||||
linked = ext->getTrueLinkedObject(false,0,0,true);
|
||||
else
|
||||
linked = o->getLinkedObject(false);
|
||||
}
|
||||
|
||||
if(linked && linked!=o) {
|
||||
if(options & GetLinkRecursive)
|
||||
linkMap[linked] = o;
|
||||
else if(linked == obj || !obj) {
|
||||
if((options & GetLinkExternal)
|
||||
&& linked->getDocument()==o->getDocument())
|
||||
continue;
|
||||
else if(options & GetLinkedObject)
|
||||
links.insert(linked);
|
||||
else
|
||||
links.insert(o);
|
||||
if(maxCount && maxCount<=(int)links.size())
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(!(options & GetLinkRecursive))
|
||||
return;
|
||||
|
||||
std::vector<const DocumentObject*> current(1,obj);
|
||||
for(int depth=0;current.size();++depth) {
|
||||
if(!GetApplication().checkLinkDepth(depth,true))
|
||||
break;
|
||||
std::vector<const DocumentObject*> next;
|
||||
for(auto o : current) {
|
||||
auto iter = linkMap.find(o);
|
||||
if(iter!=linkMap.end() && links.insert(iter->second).second) {
|
||||
if(maxCount && maxCount<=(int)links.size())
|
||||
return;
|
||||
next.push_back(iter->second);
|
||||
}
|
||||
}
|
||||
current.swap(next);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
bool Document::hasLinksTo(const DocumentObject *obj) const {
|
||||
std::set<DocumentObject *> links;
|
||||
getLinksTo(links,obj,0,1);
|
||||
return !links.empty();
|
||||
}
|
||||
|
||||
std::vector<App::DocumentObject*> Document::getInList(const DocumentObject* me) const
|
||||
{
|
||||
// result list
|
||||
std::vector<App::DocumentObject*> result;
|
||||
// go through all objects
|
||||
for (std::map<std::string,DocumentObject*>::const_iterator It = d->objectMap.begin(); It != d->objectMap.end();++It) {
|
||||
for (auto It = d->objectMap.begin(); It != d->objectMap.end();++It) {
|
||||
// get the outList and search if me is in that list
|
||||
std::vector<DocumentObject*> OutList = It->second->getOutList();
|
||||
for (std::vector<DocumentObject*>::const_iterator It2=OutList.begin();It2!=OutList.end();++It2)
|
||||
@@ -2606,7 +2704,8 @@ void Document::recomputeFeature(DocumentObject* Feat)
|
||||
}
|
||||
}
|
||||
|
||||
DocumentObject * Document::addObject(const char* sType, const char* pObjectName, bool isNew)
|
||||
DocumentObject * Document::addObject(const char* sType, const char* pObjectName,
|
||||
bool isNew, const char *viewType, bool isPartial)
|
||||
{
|
||||
Base::BaseClass* base = static_cast<Base::BaseClass*>(Base::Type::createInstanceByName(sType,true));
|
||||
|
||||
@@ -2641,6 +2740,9 @@ DocumentObject * Document::addObject(const char* sType, const char* pObjectName,
|
||||
|
||||
// insert in the name map
|
||||
d->objectMap[ObjectName] = pcObject;
|
||||
// generate object id and add to id map;
|
||||
pcObject->_Id = ++d->lastObjectId;
|
||||
d->objectIdMap[pcObject->_Id] = pcObject;
|
||||
// cache the pointer to the name string in the Object (for performance of DocumentObject::getNameInDocument())
|
||||
pcObject->pcNameInDocument = &(d->objectMap.find(ObjectName)->first);
|
||||
// insert in the vector
|
||||
@@ -2660,6 +2762,12 @@ DocumentObject * Document::addObject(const char* sType, const char* pObjectName,
|
||||
|
||||
// mark the object as new (i.e. set status bit 2) and send the signal
|
||||
pcObject->setStatus(ObjectStatus::New, true);
|
||||
|
||||
if(!viewType)
|
||||
viewType = pcObject->getViewProviderNameOverride();
|
||||
if(viewType)
|
||||
pcObject->_pcViewProviderName = viewType;
|
||||
|
||||
signalNewObject(*pcObject);
|
||||
|
||||
// do no transactions if we do a rollback!
|
||||
@@ -2730,6 +2838,9 @@ std::vector<DocumentObject *> Document::addObjects(const char* sType, const std:
|
||||
|
||||
// insert in the name map
|
||||
d->objectMap[ObjectName] = pcObject;
|
||||
// generate object id and add to id map;
|
||||
pcObject->_Id = ++d->lastObjectId;
|
||||
d->objectIdMap[pcObject->_Id] = pcObject;
|
||||
// cache the pointer to the name string in the Object (for performance of DocumentObject::getNameInDocument())
|
||||
pcObject->pcNameInDocument = &(d->objectMap.find(ObjectName)->first);
|
||||
// insert in the vector
|
||||
@@ -2744,6 +2855,10 @@ std::vector<DocumentObject *> Document::addObjects(const char* sType, const std:
|
||||
|
||||
// mark the object as new (i.e. set status bit 2) and send the signal
|
||||
pcObject->setStatus(ObjectStatus::New, true);
|
||||
|
||||
const char *viewType = pcObject->getViewProviderNameOverride();
|
||||
pcObject->_pcViewProviderName = viewType?viewType:"";
|
||||
|
||||
signalNewObject(*pcObject);
|
||||
|
||||
// do no transactions if we do a rollback!
|
||||
@@ -2786,6 +2901,9 @@ void Document::addObject(DocumentObject* pcObject, const char* pObjectName)
|
||||
|
||||
// insert in the name map
|
||||
d->objectMap[ObjectName] = pcObject;
|
||||
// generate object id and add to id map;
|
||||
if(!pcObject->_Id) pcObject->_Id = ++d->lastObjectId;
|
||||
d->objectIdMap[pcObject->_Id] = pcObject;
|
||||
// cache the pointer to the name string in the Object (for performance of DocumentObject::getNameInDocument())
|
||||
pcObject->pcNameInDocument = &(d->objectMap.find(ObjectName)->first);
|
||||
// insert in the vector
|
||||
@@ -2795,6 +2913,10 @@ void Document::addObject(DocumentObject* pcObject, const char* pObjectName)
|
||||
|
||||
// mark the object as new (i.e. set status bit 2) and send the signal
|
||||
pcObject->setStatus(ObjectStatus::New, true);
|
||||
|
||||
const char *viewType = pcObject->getViewProviderNameOverride();
|
||||
pcObject->_pcViewProviderName = viewType?viewType:"";
|
||||
|
||||
signalNewObject(*pcObject);
|
||||
|
||||
// do no transactions if we do a rollback!
|
||||
@@ -2809,6 +2931,9 @@ void Document::_addObject(DocumentObject* pcObject, const char* pObjectName)
|
||||
{
|
||||
std::string ObjectName = getUniqueObjectName(pObjectName);
|
||||
d->objectMap[ObjectName] = pcObject;
|
||||
// generate object id and add to id map;
|
||||
if(!pcObject->_Id) pcObject->_Id = ++d->lastObjectId;
|
||||
d->objectIdMap[pcObject->_Id] = pcObject;
|
||||
d->objectArray.push_back(pcObject);
|
||||
// cache the pointer to the name string in the Object (for performance of DocumentObject::getNameInDocument())
|
||||
pcObject->pcNameInDocument = &(d->objectMap.find(ObjectName)->first);
|
||||
@@ -2820,6 +2945,9 @@ void Document::_addObject(DocumentObject* pcObject, const char* pObjectName)
|
||||
d->activeUndoTransaction->addObjectDel(pcObject);
|
||||
}
|
||||
|
||||
const char *viewType = pcObject->getViewProviderNameOverride();
|
||||
pcObject->_pcViewProviderName = viewType?viewType:"";
|
||||
|
||||
// send the signal
|
||||
signalNewObject(*pcObject);
|
||||
|
||||
@@ -2835,7 +2963,7 @@ void Document::_addObject(DocumentObject* pcObject, const char* pObjectName)
|
||||
/// Remove an object out of the document
|
||||
void Document::removeObject(const char* sName)
|
||||
{
|
||||
std::map<std::string,DocumentObject*>::iterator pos = d->objectMap.find(sName);
|
||||
auto pos = d->objectMap.find(sName);
|
||||
|
||||
// name not found?
|
||||
if (pos == d->objectMap.end())
|
||||
@@ -2909,6 +3037,7 @@ void Document::removeObject(const char* sName)
|
||||
}
|
||||
|
||||
pos->second->setStatus(ObjectStatus::Remove, false); // Unset the bit to be on the safe side
|
||||
d->objectIdMap.erase(pos->second->_Id);
|
||||
d->objectMap.erase(pos);
|
||||
}
|
||||
|
||||
@@ -2918,7 +3047,21 @@ void Document::_removeObject(DocumentObject* pcObject)
|
||||
// TODO Refactoring: share code with Document::removeObject() (2015-09-01, Fat-Zer)
|
||||
_checkTransaction(pcObject);
|
||||
|
||||
std::map<std::string,DocumentObject*>::iterator pos = d->objectMap.find(pcObject->getNameInDocument());
|
||||
auto pos = d->objectMap.find(pcObject->getNameInDocument());
|
||||
|
||||
if(!d->rollback && d->activeUndoTransaction && pos->second->hasChildElement()) {
|
||||
// Preserve link group children global visibility. See comments in
|
||||
// removeObject() for more details.
|
||||
for(auto &sub : pos->second->getSubObjects()) {
|
||||
if(sub.empty())
|
||||
continue;
|
||||
if(sub[sub.size()-1]!='.')
|
||||
sub += '.';
|
||||
auto sobj = pos->second->getSubObject(sub.c_str());
|
||||
if(sobj && sobj->getDocument()==this && !sobj->Visibility.getValue())
|
||||
d->activeUndoTransaction->addObjectChange(sobj,&sobj->Visibility);
|
||||
}
|
||||
}
|
||||
|
||||
if (d->activeObject == pcObject)
|
||||
d->activeObject = 0;
|
||||
@@ -2951,6 +3094,7 @@ void Document::_removeObject(DocumentObject* pcObject)
|
||||
|
||||
// remove from map
|
||||
pcObject->setStatus(ObjectStatus::Remove, false); // Unset the bit to be on the safe side
|
||||
d->objectIdMap.erase(pcObject->_Id);
|
||||
d->objectMap.erase(pos);
|
||||
|
||||
for (std::vector<DocumentObject*>::iterator it = d->objectArray.begin(); it != d->objectArray.end(); ++it) {
|
||||
@@ -3138,9 +3282,7 @@ DocumentObject * Document::getActiveObject(void) const
|
||||
|
||||
DocumentObject * Document::getObject(const char *Name) const
|
||||
{
|
||||
std::map<std::string,DocumentObject*>::const_iterator pos;
|
||||
|
||||
pos = d->objectMap.find(Name);
|
||||
auto pos = d->objectMap.find(Name);
|
||||
|
||||
if (pos != d->objectMap.end())
|
||||
return pos->second;
|
||||
@@ -3148,10 +3290,19 @@ DocumentObject * Document::getObject(const char *Name) const
|
||||
return 0;
|
||||
}
|
||||
|
||||
DocumentObject * Document::getObjectByID(long id) const
|
||||
{
|
||||
auto it = d->objectIdMap.find(id);
|
||||
if(it!=d->objectIdMap.end())
|
||||
return it->second;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
// Note: This method is only used in Tree.cpp slotChangeObject(), see explanation there
|
||||
bool Document::isIn(const DocumentObject *pFeat) const
|
||||
{
|
||||
for (std::map<std::string,DocumentObject*>::const_iterator o = d->objectMap.begin(); o != d->objectMap.end(); ++o) {
|
||||
for (auto o = d->objectMap.begin(); o != d->objectMap.end(); ++o) {
|
||||
if (o->second == pFeat)
|
||||
return true;
|
||||
}
|
||||
@@ -3161,9 +3312,7 @@ bool Document::isIn(const DocumentObject *pFeat) const
|
||||
|
||||
const char * Document::getObjectName(DocumentObject *pFeat) const
|
||||
{
|
||||
std::map<std::string,DocumentObject*>::const_iterator pos;
|
||||
|
||||
for (pos = d->objectMap.begin();pos != d->objectMap.end();++pos) {
|
||||
for (auto pos = d->objectMap.begin();pos != d->objectMap.end();++pos) {
|
||||
if (pos->second == pFeat)
|
||||
return pos->first.c_str();
|
||||
}
|
||||
@@ -3178,8 +3327,7 @@ std::string Document::getUniqueObjectName(const char *Name) const
|
||||
std::string CleanName = Base::Tools::getIdentifier(Name);
|
||||
|
||||
// name in use?
|
||||
std::map<std::string,DocumentObject*>::const_iterator pos;
|
||||
pos = d->objectMap.find(CleanName);
|
||||
auto pos = d->objectMap.find(CleanName);
|
||||
|
||||
if (pos == d->objectMap.end()) {
|
||||
// if not, name is OK
|
||||
@@ -3222,6 +3370,7 @@ std::vector<DocumentObject*> Document::getObjects() const
|
||||
return d->objectArray;
|
||||
}
|
||||
|
||||
|
||||
std::vector<DocumentObject*> Document::getObjectsOfType(const Base::Type& typeId) const
|
||||
{
|
||||
std::vector<DocumentObject*> Objects;
|
||||
@@ -3260,7 +3409,7 @@ std::vector<DocumentObject*> Document::findObjects(const Base::Type& typeId, con
|
||||
int Document::countObjectsOfType(const Base::Type& typeId) const
|
||||
{
|
||||
int ct=0;
|
||||
for (std::map<std::string,DocumentObject*>::const_iterator it = d->objectMap.begin(); it != d->objectMap.end(); ++it) {
|
||||
for (auto it = d->objectMap.begin(); it != d->objectMap.end(); ++it) {
|
||||
if (it->second->getTypeId().isDerivedFrom(typeId))
|
||||
ct++;
|
||||
}
|
||||
|
||||
@@ -69,7 +69,7 @@ public:
|
||||
Closable = 2,
|
||||
Restoring = 3,
|
||||
Recomputing = 4,
|
||||
PartialRestore = 5
|
||||
PartialRestore = 5,
|
||||
};
|
||||
|
||||
/** @name Properties */
|
||||
@@ -109,6 +109,8 @@ public:
|
||||
PropertyLink Tip;
|
||||
/// Tip object of the document (if any)
|
||||
PropertyString TipName;
|
||||
/// Whether to show hidden items in TreeView
|
||||
PropertyBool ShowHidden;
|
||||
//@}
|
||||
|
||||
/** @name Signals of the document */
|
||||
@@ -126,6 +128,8 @@ public:
|
||||
boost::signals2::signal<void (const App::DocumentObject&, const App::Property&)> signalBeforeChangeObject;
|
||||
/// signal on changed Object
|
||||
boost::signals2::signal<void (const App::DocumentObject&, const App::Property&)> signalChangedObject;
|
||||
/// signal on manually called DocumentObject::touch()
|
||||
boost::signals2::signal<void (const App::DocumentObject&)> signalTouchedObject;
|
||||
/// signal on relabeled Object
|
||||
boost::signals2::signal<void (const App::DocumentObject&)> signalRelabelObject;
|
||||
/// signal on activated Object
|
||||
@@ -201,8 +205,11 @@ public:
|
||||
* @param sType the type of created object
|
||||
* @param pObjectName if nonNULL use that name otherwise generate a new unique name based on the \a sType
|
||||
* @param isNew if false don't call the \c DocumentObject::setupObject() callback (default is true)
|
||||
* @param viewType override object's view provider name
|
||||
* @param isPartial indicate if this object is meant to be partially loaded
|
||||
*/
|
||||
DocumentObject *addObject(const char* sType, const char* pObjectName=0, bool isNew=true);
|
||||
DocumentObject *addObject(const char* sType, const char* pObjectName=0,
|
||||
bool isNew=true, const char *viewType=0, bool isPartial=false);
|
||||
/** Add an array of features of the given types and names.
|
||||
* Unicode names are set through the Label property.
|
||||
* @param sType The type of created object
|
||||
@@ -239,6 +246,8 @@ public:
|
||||
DocumentObject *getActiveObject(void) const;
|
||||
/// Returns a Object of this document
|
||||
DocumentObject *getObject(const char *Name) const;
|
||||
/// Returns a Object of this document by its id
|
||||
DocumentObject *getObjectByID(long id) const;
|
||||
/// Returns true if the DocumentObject is contained in this document
|
||||
bool isIn(const DocumentObject *pFeat) const;
|
||||
/// Returns a Name of an Object or 0
|
||||
@@ -248,7 +257,7 @@ public:
|
||||
/// Returns a name of the form prefix_number. d specifies the number of digits.
|
||||
std::string getStandardObjectName(const char *Name, int d) const;
|
||||
/// Returns a list of all Objects
|
||||
std::vector<DocumentObject*> getObjects() const;
|
||||
const std::vector<DocumentObject*> &getObjects() const;
|
||||
std::vector<DocumentObject*> getObjectsOfType(const Base::Type& typeId) const;
|
||||
/// Returns all object with given extensions. If derived=true also all objects with extensions derived from the given one
|
||||
std::vector<DocumentObject*> getObjectsWithExtension(const Base::Type& typeId, bool derived = true) const;
|
||||
@@ -359,6 +368,22 @@ public:
|
||||
(const App::DocumentObject* from, const App::DocumentObject* to) const;
|
||||
//@}
|
||||
|
||||
/** Return the links to a given object
|
||||
*
|
||||
* @param links: holds the links found
|
||||
* @param obj: the linked object. If NULL, then all links are returned.
|
||||
* @param option: @sa App::GetLinkOptions
|
||||
* @param maxCount: limit the number of links returned, 0 means no limit
|
||||
* @param objs: optional objects to search for, if empty, then all objects
|
||||
* of this document are searched.
|
||||
*/
|
||||
void getLinksTo(std::set<DocumentObject*> &links,
|
||||
const DocumentObject *obj, int options, int maxCount=0,
|
||||
const std::vector<DocumentObject*> &objs = {}) const;
|
||||
|
||||
/// Check if there is any link to the given object
|
||||
bool hasLinksTo(const DocumentObject *obj) const;
|
||||
|
||||
/// Function called to signal that an object identifier has been renamed
|
||||
void renameObjectIdentifiers(const std::map<App::ObjectIdentifier, App::ObjectIdentifier> & paths, const std::function<bool(const App::DocumentObject*)> &selector = [](const App::DocumentObject *) { return true; });
|
||||
|
||||
|
||||
@@ -29,17 +29,22 @@
|
||||
#include <Base/Writer.h>
|
||||
#include <Base/Tools.h>
|
||||
#include <Base/Console.h>
|
||||
#include <Base/Exception.h>
|
||||
|
||||
#include "Application.h"
|
||||
#include "Document.h"
|
||||
#include "DocumentObject.h"
|
||||
#include "DocumentObjectGroup.h"
|
||||
#include "PropertyLinks.h"
|
||||
#include "PropertyGeo.h"
|
||||
#include "PropertyExpressionEngine.h"
|
||||
#include "DocumentObjectExtension.h"
|
||||
#include "GeoFeatureGroupExtension.h"
|
||||
#include <App/DocumentObjectPy.h>
|
||||
#include <boost/bind.hpp>
|
||||
|
||||
FC_LOG_LEVEL_INIT("App",true,true)
|
||||
|
||||
using namespace App;
|
||||
|
||||
/** \defgroup DocObject Document Object
|
||||
@@ -56,11 +61,22 @@ DocumentObjectExecReturn *DocumentObject::StdReturn = 0;
|
||||
//===========================================================================
|
||||
|
||||
DocumentObject::DocumentObject(void)
|
||||
: ExpressionEngine(),_pDoc(0),pcNameInDocument(0)
|
||||
: ExpressionEngine(),_pDoc(0),pcNameInDocument(0),_Id(0)
|
||||
{
|
||||
// define Label of type 'Output' to avoid being marked as touched after relabeling
|
||||
ADD_PROPERTY_TYPE(Label,("Unnamed"),"Base",Prop_Output,"User name of the object (UTF8)");
|
||||
ADD_PROPERTY_TYPE(Label2,(""),"Base",Prop_Hidden,"User description of the object (UTF8)");
|
||||
Label2.setStatus(App::Property::Output,true);
|
||||
ADD_PROPERTY_TYPE(ExpressionEngine,(),"Base",Prop_Hidden,"Property expressions");
|
||||
|
||||
ADD_PROPERTY(Visibility, (true));
|
||||
|
||||
// default set Visibility status to hidden and output (no touch) for
|
||||
// compatibitily reason. We use setStatus instead of PropertyType to
|
||||
// allow user to change its status later
|
||||
Visibility.setStatus(Property::Output,true);
|
||||
Visibility.setStatus(Property::Hidden,true);
|
||||
Visibility.setStatus(Property::NoModify,true);
|
||||
}
|
||||
|
||||
DocumentObject::~DocumentObject(void)
|
||||
@@ -122,9 +138,13 @@ bool DocumentObject::recomputeFeature()
|
||||
* If it should be forced to recompute a document object then use
|
||||
* \ref enforceRecompute() instead.
|
||||
*/
|
||||
void DocumentObject::touch(void)
|
||||
void DocumentObject::touch(bool noRecompute)
|
||||
{
|
||||
if(!noRecompute)
|
||||
StatusBits.set(ObjectStatus::Enforce);
|
||||
StatusBits.set(ObjectStatus::Touch);
|
||||
if (_pDoc)
|
||||
_pDoc->signalTouchedObject(*this);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -143,8 +163,7 @@ bool DocumentObject::isTouched() const
|
||||
*/
|
||||
void DocumentObject::enforceRecompute(void)
|
||||
{
|
||||
StatusBits.set(ObjectStatus::Enforce);
|
||||
StatusBits.set(ObjectStatus::Touch);
|
||||
touch(false);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -188,7 +207,16 @@ const char* DocumentObject::getStatusString(void) const
|
||||
return "Valid";
|
||||
}
|
||||
|
||||
const char *DocumentObject::getNameInDocument(void) const
|
||||
std::string DocumentObject::getFullName() const {
|
||||
if(!getDocument() || !pcNameInDocument)
|
||||
return "?";
|
||||
std::string name(getDocument()->getName());
|
||||
name += '#';
|
||||
name += *pcNameInDocument;
|
||||
return name;
|
||||
}
|
||||
|
||||
const char *DocumentObject::getNameInDocument() const
|
||||
{
|
||||
// Note: It can happen that we query the internal name of an object even if it is not
|
||||
// part of a document (anymore). This is the case e.g. if we have a reference in Python
|
||||
@@ -322,7 +350,8 @@ void _getInListRecursive(std::set<DocumentObject*>& objSet,
|
||||
std::vector<App::DocumentObject*> DocumentObject::getInListRecursive(void) const
|
||||
{
|
||||
// number of objects in document is a good estimate in result size
|
||||
int maxDepth = getDocument()->countObjects() + 2;
|
||||
// int maxDepth = getDocument()->countObjects() +2;
|
||||
int maxDepth = GetApplication().checkLinkDepth(0);
|
||||
std::set<App::DocumentObject*> result;
|
||||
|
||||
// using a rcursie helper to collect all InLists
|
||||
@@ -353,7 +382,7 @@ void _getOutListRecursive(std::set<DocumentObject*>& objSet,
|
||||
std::vector<App::DocumentObject*> DocumentObject::getOutListRecursive(void) const
|
||||
{
|
||||
// number of objects in document is a good estimate in result size
|
||||
int maxDepth = getDocument()->countObjects() + 2;
|
||||
int maxDepth = GetApplication().checkLinkDepth(0);
|
||||
std::set<App::DocumentObject*> result;
|
||||
|
||||
// using a recursive helper to collect all OutLists
|
||||
@@ -532,13 +561,20 @@ void DocumentObject::onBeforeChange(const Property* prop)
|
||||
|
||||
if (_pDoc)
|
||||
onBeforeChangeProperty(_pDoc, prop);
|
||||
|
||||
signalBeforeChange(*this,*prop);
|
||||
}
|
||||
|
||||
/// get called by the container when a Property was changed
|
||||
void DocumentObject::onChanged(const Property* prop)
|
||||
{
|
||||
if (_pDoc)
|
||||
_pDoc->onChangedProperty(this,prop);
|
||||
if(GetApplication().isClosingAll())
|
||||
return;
|
||||
|
||||
// Delay signaling view provider until the document object has handled the
|
||||
// change
|
||||
// if (_pDoc)
|
||||
// _pDoc->onChangedProperty(this,prop);
|
||||
|
||||
if (prop == &Label && _pDoc && oldLabel != Label.getStrValue())
|
||||
_pDoc->signalRelabelObject(*this);
|
||||
@@ -553,6 +589,12 @@ void DocumentObject::onChanged(const Property* prop)
|
||||
|
||||
//call the parent for appropriate handling
|
||||
TransactionalObject::onChanged(prop);
|
||||
|
||||
// Now signal the view provider
|
||||
if (_pDoc)
|
||||
_pDoc->onChangedProperty(this,prop);
|
||||
|
||||
signalChanged(*this,*prop);
|
||||
}
|
||||
|
||||
PyObject *DocumentObject::getPyObject(void)
|
||||
@@ -564,10 +606,126 @@ PyObject *DocumentObject::getPyObject(void)
|
||||
return Py::new_reference_to(PythonObject);
|
||||
}
|
||||
|
||||
std::vector<PyObject *> DocumentObject::getPySubObjects(const std::vector<std::string>&) const
|
||||
DocumentObject *DocumentObject::getSubObject(const char *subname,
|
||||
PyObject **pyObj, Base::Matrix4D *mat, bool transform, int depth) const
|
||||
{
|
||||
// default implementation returns nothing
|
||||
return std::vector<PyObject *>();
|
||||
DocumentObject *ret = 0;
|
||||
auto exts = getExtensionsDerivedFromType<App::DocumentObjectExtension>();
|
||||
for(auto ext : exts) {
|
||||
if(ext->extensionGetSubObject(ret,subname,pyObj,mat,transform, depth))
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::string name;
|
||||
const char *dot=0;
|
||||
if(!subname || !(dot=strchr(subname,'.'))) {
|
||||
ret = const_cast<DocumentObject*>(this);
|
||||
}else if(subname[0]=='$') {
|
||||
name = std::string(subname+1,dot);
|
||||
for(auto obj : getOutList()) {
|
||||
if(name == obj->Label.getValue()) {
|
||||
ret = obj;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}else{
|
||||
name = std::string(subname,dot);
|
||||
const auto &outList = getOutList();
|
||||
if(outList.size()!=_outListMap.size()) {
|
||||
_outListMap.clear();
|
||||
for(auto obj : outList)
|
||||
_outListMap[obj->getNameInDocument()] = obj;
|
||||
}
|
||||
auto it = _outListMap.find(name.c_str());
|
||||
if(it != _outListMap.end())
|
||||
ret = it->second;
|
||||
}
|
||||
|
||||
// TODO: By right, normal object's placement does not transform its sub
|
||||
// objects (think of the claimed children of a Fusion). But I do think we
|
||||
// should change that.
|
||||
if(transform && mat) {
|
||||
auto pla = dynamic_cast<PropertyPlacement*>(getPropertyByName("Placement"));
|
||||
if(pla)
|
||||
*mat *= pla->getValue().toMatrix();
|
||||
}
|
||||
|
||||
if(ret && dot)
|
||||
return ret->getSubObject(dot+1,pyObj,mat,true,depth+1);
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::vector<DocumentObject*> DocumentObject::getSubObjectList(const char *subname) const {
|
||||
std::vector<DocumentObject*> res;
|
||||
res.push_back(const_cast<DocumentObject*>(this));
|
||||
if(!subname || !subname[0])
|
||||
return res;
|
||||
std::string sub(subname);
|
||||
for(auto pos=sub.find('.');pos!=std::string::npos;pos=sub.find('.',pos+1)) {
|
||||
char c = sub[pos+1];
|
||||
sub[pos+1] = 0;
|
||||
auto sobj = getSubObject(sub.c_str());
|
||||
if(!sobj || !sobj->getNameInDocument())
|
||||
break;
|
||||
res.push_back(sobj);
|
||||
sub[pos+1] = c;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
std::vector<std::string> DocumentObject::getSubObjects(int reason) const {
|
||||
std::vector<std::string> ret;
|
||||
auto exts = getExtensionsDerivedFromType<App::DocumentObjectExtension>();
|
||||
for(auto ext : exts) {
|
||||
if(ext->extensionGetSubObjects(ret,reason))
|
||||
return ret;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::vector<std::pair<App::DocumentObject *,std::string> > DocumentObject::getParents(int depth) const {
|
||||
std::vector<std::pair<App::DocumentObject *,std::string> > ret;
|
||||
if(!getNameInDocument() || !GetApplication().checkLinkDepth(depth))
|
||||
return ret;
|
||||
std::string name(getNameInDocument());
|
||||
name += ".";
|
||||
for(auto parent : getInList()) {
|
||||
if(!parent || !parent->getNameInDocument())
|
||||
continue;
|
||||
if(!parent->hasChildElement() &&
|
||||
!parent->hasExtension(GeoFeatureGroupExtension::getExtensionClassTypeId()))
|
||||
continue;
|
||||
if(!parent->getSubObject(name.c_str()))
|
||||
continue;
|
||||
|
||||
auto links = GetApplication().getLinksTo(parent,App::GetLinkRecursive);
|
||||
links.insert(parent);
|
||||
for(auto parent : links) {
|
||||
auto parents = parent->getParents(depth+1);
|
||||
if(parents.empty())
|
||||
parents.emplace_back(parent,std::string());
|
||||
for(auto &v : parents)
|
||||
ret.emplace_back(v.first,v.second+name);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
DocumentObject *DocumentObject::getLinkedObject(
|
||||
bool recursive, Base::Matrix4D *mat, bool transform, int depth) const
|
||||
{
|
||||
DocumentObject *ret = 0;
|
||||
auto exts = getExtensionsDerivedFromType<App::DocumentObjectExtension>();
|
||||
for(auto ext : exts) {
|
||||
if(ext->extensionGetLinkedObject(ret,recursive,mat,transform,depth))
|
||||
return ret;
|
||||
}
|
||||
if(transform && mat) {
|
||||
auto pla = dynamic_cast<PropertyPlacement*>(getPropertyByName("Placement"));
|
||||
if(pla)
|
||||
*mat *= pla->getValue().toMatrix();
|
||||
}
|
||||
return const_cast<DocumentObject*>(this);
|
||||
}
|
||||
|
||||
void DocumentObject::Save (Base::Writer &writer) const
|
||||
@@ -664,6 +822,8 @@ void DocumentObject::onDocumentRestored()
|
||||
auto vector = getExtensionsDerivedFromType<App::DocumentObjectExtension>();
|
||||
for(auto ext : vector)
|
||||
ext->onExtendedDocumentRestored();
|
||||
if(Visibility.testStatus(Property::Output))
|
||||
Visibility.setStatus(Property::NoModify,true);
|
||||
}
|
||||
|
||||
void DocumentObject::onSettingDocument()
|
||||
@@ -715,3 +875,136 @@ void App::DocumentObject::_addBackLink(DocumentObject* newObj)
|
||||
(void)newObj;
|
||||
#endif //USE_OLD_DAG
|
||||
}
|
||||
|
||||
int DocumentObject::setElementVisible(const char *element, bool visible) {
|
||||
for(auto ext : getExtensionsDerivedFromType<DocumentObjectExtension>()) {
|
||||
int ret = ext->extensionSetElementVisible(element,visible);
|
||||
if(ret>=0) return ret;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
int DocumentObject::isElementVisible(const char *element) const {
|
||||
for(auto ext : getExtensionsDerivedFromType<DocumentObjectExtension>()) {
|
||||
int ret = ext->extensionIsElementVisible(element);
|
||||
if(ret>=0) return ret;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
bool DocumentObject::hasChildElement() const {
|
||||
for(auto ext : getExtensionsDerivedFromType<DocumentObjectExtension>()) {
|
||||
if(ext->extensionHasChildElement())
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
DocumentObject *DocumentObject::resolve(const char *subname,
|
||||
App::DocumentObject **parent, std::string *childName, const char **subElement,
|
||||
PyObject **pyObj, Base::Matrix4D *pmat, bool transform, int depth) const
|
||||
{
|
||||
auto self = const_cast<DocumentObject*>(this);
|
||||
if(parent) *parent = 0;
|
||||
if(subElement) *subElement = 0;
|
||||
|
||||
auto obj = getSubObject(subname,pyObj,pmat,transform,depth);
|
||||
if(!obj || !subname || *subname==0)
|
||||
return self;
|
||||
|
||||
if(!parent && !subElement)
|
||||
return obj;
|
||||
|
||||
// NOTE, the convension of '.' separated SubName demands a mandatory ending
|
||||
// '.' for each object name in SubName, even if there is no subelement
|
||||
// following it. So finding the last dot will give us the end of the last
|
||||
// object name.
|
||||
const char *dot=0;
|
||||
if(Data::ComplexGeoData::isMappedElement(subname) ||
|
||||
!(dot=strrchr(subname,'.')) ||
|
||||
dot == subname)
|
||||
{
|
||||
if(subElement)
|
||||
*subElement = dot?dot+1:subname;
|
||||
return obj; // this means no parent object reference in SubName
|
||||
}
|
||||
|
||||
if(parent)
|
||||
*parent = self;
|
||||
|
||||
bool elementMapChecked = false;
|
||||
const char *lastDot = dot;
|
||||
for(--dot;;--dot) {
|
||||
// check for the second last dot, which is the end of the last parent object
|
||||
if(*dot == '.' || dot == subname) {
|
||||
// We can't get parent object by its name, because the object may be
|
||||
// externally linked (i.e. in a different document). So go through
|
||||
// getSubObject again.
|
||||
if(!elementMapChecked) {
|
||||
elementMapChecked = true;
|
||||
const char *sub = dot==subname?dot:dot+1;
|
||||
if(Data::ComplexGeoData::isMappedElement(sub)) {
|
||||
lastDot = dot;
|
||||
if(dot==subname)
|
||||
break;
|
||||
else
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if(dot==subname)
|
||||
break;
|
||||
auto sobj = getSubObject(std::string(subname,dot-subname+1).c_str());
|
||||
if(sobj!=obj) {
|
||||
if(parent) {
|
||||
// Link/LinkGroup has special visiblility handling of plain
|
||||
// group, so keep ascending
|
||||
if(!sobj->hasExtension(GroupExtension::getExtensionClassTypeId(),false)) {
|
||||
*parent = sobj;
|
||||
break;
|
||||
}
|
||||
for(auto ddot=dot-1;ddot!=subname;--ddot) {
|
||||
if(*ddot != '.') continue;
|
||||
auto sobj = getSubObject(std::string(subname,ddot-subname+1).c_str());
|
||||
if(!sobj->hasExtension(GroupExtension::getExtensionClassTypeId(),false)) {
|
||||
*parent = sobj;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(childName && lastDot!=dot) {
|
||||
if(*dot == '.')
|
||||
++dot;
|
||||
const char *nextDot = strchr(dot,'.');
|
||||
assert(nextDot);
|
||||
*childName = std::string(dot,nextDot-dot);
|
||||
}
|
||||
if(subElement)
|
||||
*subElement = *lastDot=='.'?lastDot+1:lastDot;
|
||||
return obj;
|
||||
}
|
||||
|
||||
const std::string &DocumentObject::hiddenMarker() {
|
||||
static std::string marker("!hide");
|
||||
return marker;
|
||||
}
|
||||
|
||||
const char *DocumentObject::hasHiddenMarker(const char *subname) {
|
||||
if(!subname) return 0;
|
||||
const char *marker = strrchr(subname,'.');
|
||||
if(!marker)
|
||||
marker = subname;
|
||||
else
|
||||
++marker;
|
||||
return hiddenMarker()==marker?marker:0;
|
||||
}
|
||||
|
||||
bool DocumentObject::redirectSubName(std::ostringstream &, DocumentObject *, DocumentObject *) const {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
#include <App/PropertyExpressionEngine.h>
|
||||
|
||||
#include <Base/TimeInfo.h>
|
||||
#include <Base/Matrix.h>
|
||||
#include <CXX/Objects.hxx>
|
||||
|
||||
#include <bitset>
|
||||
@@ -46,7 +47,7 @@ enum ObjectStatus {
|
||||
Touch = 0,
|
||||
Error = 1,
|
||||
New = 2,
|
||||
Recompute = 3,
|
||||
Recompute = 3, // set when the object is currently being recomputed
|
||||
Restore = 4,
|
||||
Remove = 5,
|
||||
PythonCall = 6,
|
||||
@@ -86,18 +87,35 @@ class AppExport DocumentObject: public App::TransactionalObject
|
||||
public:
|
||||
|
||||
PropertyString Label;
|
||||
PropertyString Label2;
|
||||
PropertyExpressionEngine ExpressionEngine;
|
||||
|
||||
/// Allow control visibility status in App name space
|
||||
PropertyBool Visibility;
|
||||
|
||||
/// signal before changing a property of this object
|
||||
boost::signals2::signal<void (const App::DocumentObject&, const App::Property&)> signalBeforeChange;
|
||||
/// signal on changed property of this object
|
||||
boost::signals2::signal<void (const App::DocumentObject&, const App::Property&)> signalChanged;
|
||||
|
||||
/// returns the type name of the ViewProvider
|
||||
virtual const char* getViewProviderName(void) const {
|
||||
return "";
|
||||
}
|
||||
/// This function is introduced to allow Python feature override its view provider
|
||||
virtual const char *getViewProviderNameOverride() const {
|
||||
return getViewProviderName();
|
||||
}
|
||||
/// Constructor
|
||||
DocumentObject(void);
|
||||
virtual ~DocumentObject();
|
||||
|
||||
/// returns the name which is set in the document for this object (not the name property!)
|
||||
const char *getNameInDocument(void) const;
|
||||
/// Return the object ID that is unique within its owner document
|
||||
long getID() const {return _Id;}
|
||||
/// Return the object full name of the form DocName#ObjName
|
||||
std::string getFullName() const;
|
||||
virtual bool isAttachedToDocument() const;
|
||||
virtual const char* detachFromDocument();
|
||||
/// gets the document in which this Object is handled
|
||||
@@ -107,7 +125,7 @@ public:
|
||||
*/
|
||||
//@{
|
||||
/// set this document object touched (cause recomputation on dependent features)
|
||||
void touch(void);
|
||||
void touch(bool noRecompute=false);
|
||||
/// test if this document object is touched
|
||||
bool isTouched(void) const;
|
||||
/// Enforce this document object to be recomputed
|
||||
@@ -137,6 +155,31 @@ public:
|
||||
void setStatus(ObjectStatus pos, bool on) {StatusBits.set((size_t)pos, on);}
|
||||
//@}
|
||||
|
||||
/** Child element handling
|
||||
*/
|
||||
//@{
|
||||
/** Set sub-element visibility
|
||||
*
|
||||
* For performance reason, \c element must not contain any further
|
||||
* sub-elements, i.e. there should be no '.' inside \c element.
|
||||
*
|
||||
* @return -1 if element visiblity is not supported, 0 if element is not
|
||||
* found, 1 if success
|
||||
*/
|
||||
virtual int setElementVisible(const char *element, bool visible);
|
||||
|
||||
/** Get sub-element visibility
|
||||
*
|
||||
* @return -1 if element visiblity is not supported or element not found, 0
|
||||
* if element is invisible, or else 1
|
||||
*/
|
||||
virtual int isElementVisible(const char *element) const;
|
||||
|
||||
/// return true to activate tree view group object handling and element visibility
|
||||
virtual bool hasChildElement() const;
|
||||
//@}
|
||||
|
||||
|
||||
/** DAG handling
|
||||
This part of the interface deals with viewing the document as
|
||||
a DAG (directed acyclic graph).
|
||||
@@ -214,8 +257,89 @@ public:
|
||||
*/
|
||||
virtual void onLostLinkToObject(DocumentObject*);
|
||||
virtual PyObject *getPyObject(void);
|
||||
/// its used to get the python sub objects by name (e.g. by the selection)
|
||||
virtual std::vector<PyObject *> getPySubObjects(const std::vector<std::string>&) const;
|
||||
|
||||
/** Get the sub element/object by name
|
||||
*
|
||||
* @param subname: a string which is dot separated name to refer to a sub
|
||||
* element or object. An empty string can be used to refer to the object
|
||||
* itself
|
||||
*
|
||||
* @param pyObj: if non zero, returns the python object corresponding to
|
||||
* this sub object. The actual type of this python object is implementation
|
||||
* dependent. For example, The current implementation of Part::Feature will
|
||||
* return the TopoShapePy, event if there is no sub-element reference, in
|
||||
* which case it returns the whole shape.
|
||||
*
|
||||
* @param mat: If non zero, it is used as the current transformation matrix
|
||||
* on input. And output as the accumulated transformation up until and
|
||||
* include the transformation applied by the final object reference in \c
|
||||
* subname. For Part::Feature, the transformation is applied to the
|
||||
* TopoShape inside \c pyObj before returning.
|
||||
*
|
||||
* @param transform: if false, then it will not apply the object's own
|
||||
* transformation to \c mat, which lets you override the object's placement
|
||||
* (and possibly scale).
|
||||
*
|
||||
* @param depth: depth limitation as hint for cyclic link detection
|
||||
*
|
||||
* @return The last document object refered in subname. If subname is empty,
|
||||
* then it shall return itself. If subname is invalid, then it shall return
|
||||
* zero.
|
||||
*/
|
||||
virtual DocumentObject *getSubObject(const char *subname, PyObject **pyObj=0,
|
||||
Base::Matrix4D *mat=0, bool transform=true, int depth=0) const;
|
||||
|
||||
/// Return a list of objects referenced by a given subname including this object
|
||||
std::vector<DocumentObject*> getSubObjectList(const char *subname) const;
|
||||
|
||||
/// reason of calling getSubObjects()
|
||||
enum GSReason {
|
||||
/// default, mostly for exporting shape objects
|
||||
GS_DEFAULT,
|
||||
/// for element selection
|
||||
GS_SELECT,
|
||||
};
|
||||
|
||||
/** Return name reference of all sub-objects
|
||||
*
|
||||
* @param reason: indicate reason of obtaining the sub objects
|
||||
*
|
||||
* The default implementation returns all object references in
|
||||
* PropertyLink, and PropertyLinkList, if any
|
||||
*
|
||||
* @return Return a vector of subname references for all sub-objects. In
|
||||
* most cases, the name returned will be the object name plus an ending
|
||||
* '.', which can be passed directly to getSubObject() to retrieve the
|
||||
* name. The reason to return the name reference instead of the sub object
|
||||
* itself is because there may be no real sub object, or the sub object
|
||||
* need special transformation. For example, sub objects of an array type
|
||||
* of object.
|
||||
*/
|
||||
virtual std::vector<std::string> getSubObjects(int reason=0) const;
|
||||
|
||||
///Obtain top parents and subnames of this object using its InList
|
||||
std::vector<std::pair<App::DocumentObject*,std::string> > getParents(int depth=0) const;
|
||||
|
||||
/** Return the linked object with optional transformation
|
||||
*
|
||||
* @param recurse: If false, return the immediate linked object, or else
|
||||
* recursively call this function to return the final linked object.
|
||||
*
|
||||
* @param mat: If non zero, it is used as the current transformation matrix
|
||||
* on input. And output as the accumulated transformation till the final
|
||||
* linked object.
|
||||
*
|
||||
* @param transform: if false, then it will not accumulate the object's own
|
||||
* placement into \c mat, which lets you override the object's placement.
|
||||
*
|
||||
* @return Return the linked object. This function must return itself if the
|
||||
* it is not a link or the link is invalid.
|
||||
*/
|
||||
virtual DocumentObject *getLinkedObject(bool recurse=true,
|
||||
Base::Matrix4D *mat=0, bool transform=false, int depth=0) const;
|
||||
|
||||
/* Return true to cause PropertyView to show linked object's property */
|
||||
virtual bool canLinkProperties() const {return true;}
|
||||
|
||||
friend class Document;
|
||||
friend class Transaction;
|
||||
@@ -237,6 +361,51 @@ public:
|
||||
|
||||
const std::string & getOldLabel() const { return oldLabel; }
|
||||
|
||||
const char *getViewProviderNameStored() const {
|
||||
return _pcViewProviderName.c_str();
|
||||
}
|
||||
|
||||
/** Resolve the last document object referenced in the subname
|
||||
*
|
||||
* @param subname: dot separated subname
|
||||
* @param parent: return the direct parent of the object
|
||||
* @param childName: return child name to be passed to is/setElementVisible()
|
||||
* @param subElement: return non-object sub-element name if found. The
|
||||
* pointer is guaranteed to be within the buffer pointed to by 'subname'
|
||||
*
|
||||
* @sa getSubObject()
|
||||
* @return Returns the last referenced document object in the subname. If no
|
||||
* such object in subname, return pObject.
|
||||
*/
|
||||
App::DocumentObject *resolve(const char *subname, App::DocumentObject **parent=0,
|
||||
std::string *childName=0, const char **subElement=0,
|
||||
PyObject **pyObj=0, Base::Matrix4D *mat=0, bool transform=true, int depth=0) const;
|
||||
|
||||
/** Allow object to redirect a subname path
|
||||
*
|
||||
* @param ss: input as the current subname path from \a topParent leading
|
||||
* just before this object, i.e. ends at the parent of this object. This
|
||||
* function should append its own name to this path, or redirect the
|
||||
* subname to other place.
|
||||
* @param topParent: top parent of this subname path
|
||||
* @param child: the immediate child object in the path
|
||||
*
|
||||
* This function is called by tree view to generate a subname path when an
|
||||
* item is selected in the tree. Document object can use this function to
|
||||
* redirect the selection to some other objects.
|
||||
*/
|
||||
virtual bool redirectSubName(std::ostringstream &ss,
|
||||
DocumentObject *topParent, DocumentObject *child) const;
|
||||
|
||||
/** Sepecial marker to mark the object has hidden
|
||||
*
|
||||
* It is used by Gui::ViewProvider::getElementColors(), but exposed here
|
||||
* for convenience
|
||||
*/
|
||||
static const std::string &hiddenMarker();
|
||||
/// Check if the subname reference ends with hidden marker.
|
||||
static const char *hasHiddenMarker(const char *subname);
|
||||
|
||||
protected:
|
||||
/// recompute only this object
|
||||
virtual App::DocumentObjectExecReturn *recompute(void);
|
||||
@@ -301,6 +470,13 @@ protected: // attributes
|
||||
// pointer to the document name string (for performance)
|
||||
const std::string *pcNameInDocument;
|
||||
|
||||
private:
|
||||
// accessed by App::Document to record and restore the correct view provider type
|
||||
std::string _pcViewProviderName;
|
||||
|
||||
// unique identifier (ammong a document) of this object.
|
||||
long _Id;
|
||||
|
||||
private:
|
||||
// Back pointer to all the fathers in a DAG of the document
|
||||
// this is used by the document (via friend) to have a effective DAG handling
|
||||
|
||||
@@ -91,3 +91,20 @@ DocumentObject* DocumentObjectExtension::getExtendedObject() {
|
||||
assert(getExtendedContainer()->isDerivedFrom(DocumentObject::getClassTypeId()));
|
||||
return static_cast<DocumentObject*>(getExtendedContainer());
|
||||
}
|
||||
|
||||
bool DocumentObjectExtension::extensionGetSubObject(DocumentObject *&,
|
||||
const char *, PyObject **, Base::Matrix4D *, bool, int) const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool DocumentObjectExtension::extensionGetSubObjects(std::vector<std::string>&, int) const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool DocumentObjectExtension::extensionGetLinkedObject(
|
||||
DocumentObject *&, bool, Base::Matrix4D *, bool, int) const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -61,12 +61,39 @@ public:
|
||||
virtual void onExtendedSetupObject();
|
||||
/// get called when object is going to be removed from the document
|
||||
virtual void onExtendedUnsetupObject();
|
||||
|
||||
|
||||
virtual PyObject* getExtensionPyObject(void) override;
|
||||
|
||||
/// returns the type name of the ViewProviderExtension which is automatically attached
|
||||
/// to the viewprovider object when it is initiated
|
||||
virtual const char* getViewProviderExtensionName(void) const {return "";}
|
||||
|
||||
/** Get the sub object by name
|
||||
* @sa DocumentObject::getSubObject()
|
||||
*
|
||||
* @return Return turn if handled, the sub object is returned in \c ret
|
||||
*/
|
||||
virtual bool extensionGetSubObject( DocumentObject *&ret, const char *subname,
|
||||
PyObject **pyObj, Base::Matrix4D *mat, bool transform, int depth) const;
|
||||
|
||||
/** Get name references of all sub objects
|
||||
* @sa DocumentObject::getSubObjects()
|
||||
*
|
||||
* @return Return turn if handled, the sub object is returned in \c ret
|
||||
*/
|
||||
virtual bool extensionGetSubObjects(std::vector<std::string> &ret, int reason) const;
|
||||
|
||||
/** Get the linked object
|
||||
* @sa DocumentObject::getLinkedObject()
|
||||
*
|
||||
* @return Return turn if handled, the linked object is returned in \c ret
|
||||
*/
|
||||
virtual bool extensionGetLinkedObject(DocumentObject *&ret, bool recursive,
|
||||
Base::Matrix4D *mat, bool transform, int depth) const;
|
||||
|
||||
virtual int extensionSetElementVisible(const char *, bool) {return -1;}
|
||||
virtual int extensionIsElementVisible(const char *) {return -1;}
|
||||
virtual bool extensionHasChildElement() const {return false;}
|
||||
};
|
||||
|
||||
} //App
|
||||
|
||||
@@ -60,6 +60,90 @@
|
||||
<UserDocu>Recomputes this object</UserDocu>
|
||||
</Documentation>
|
||||
</Methode>
|
||||
<Methode Name="getSubObject" Keyword="true">
|
||||
<Documentation>
|
||||
<UserDocu>
|
||||
getSubObject(subname, retType=0, matrix=None, transform=True, depth=0)
|
||||
|
||||
* subname(string|list|tuple): dot separated string or sequence of strings
|
||||
referencing subobject.
|
||||
|
||||
* retType: return type, 0=PyObject, 1=DocObject, 2=DocAndPyObject, 3=Placement
|
||||
|
||||
PyObject: return a python binding object for the (sub)object referenced in
|
||||
each 'subname' The actual type of 'PyObject' is impelementation dependent.
|
||||
For Part::Feature compatible objects, this will be of type TopoShapePy and
|
||||
pre-transformed by accumulated transformation matrix along the object path.
|
||||
|
||||
DocObject: return the document object referenced in subname, if 'matrix' is
|
||||
None. Or, return a tuple (object, matrix) for each 'subname' and 'matrix' is
|
||||
the accumulated transformation matrix for the sub object.
|
||||
|
||||
DocAndPyObject: return a tuple (object, matrix, pyobj) for each subname
|
||||
|
||||
Placement: return a trasformed placement of the sub-object
|
||||
|
||||
* matrix: the initial transformation to be applied to the sub object.
|
||||
|
||||
* transform: whether to transform the sub object using this object's placement
|
||||
|
||||
* depth: current recursive depth
|
||||
</UserDocu>
|
||||
</Documentation>
|
||||
</Methode>
|
||||
<Methode Name="getSubObjectList">
|
||||
<Documentation>
|
||||
<UserDocu>
|
||||
getSubObjectList(subname)
|
||||
|
||||
Return a list of objects referenced by a given subname including this object
|
||||
</UserDocu>
|
||||
</Documentation>
|
||||
</Methode>
|
||||
<Methode Name="getSubObjects">
|
||||
<Documentation>
|
||||
<UserDocu>getSubObjects(reason=0): Return subname reference of all sub-objects</UserDocu>
|
||||
</Documentation>
|
||||
</Methode>
|
||||
<Methode Name="getLinkedObject" Keyword="true">
|
||||
<Documentation>
|
||||
<UserDocu>
|
||||
getLinkedObject(recursive=True, matrix=None, transform=True, depth=0)
|
||||
Returns the linked object if there is one, or else return itself
|
||||
|
||||
* recusive: whether to recursively resolve the links
|
||||
|
||||
* transform: whether to transform the sub object using this object's placement
|
||||
|
||||
* matrix: If not none, this sepcifies the initial transformation to be applied
|
||||
to the sub object. And cause the method to return a tuple (object, matrix)
|
||||
containing the accumulated transformation matrix
|
||||
|
||||
* depth: current recursive depth
|
||||
</UserDocu>
|
||||
</Documentation>
|
||||
</Methode>
|
||||
<Methode Name="setElementVisible">
|
||||
<Documentation>
|
||||
<UserDocu>
|
||||
setElementVisible(element,visible): Set the visibility of a child element
|
||||
Return -1 if element visibility is not supported, 0 if element not found, 1 if success
|
||||
</UserDocu>
|
||||
</Documentation>
|
||||
</Methode>
|
||||
<Methode Name="isElementVisible">
|
||||
<Documentation>
|
||||
<UserDocu>
|
||||
isElementVisible(element): Check if a child element is visible
|
||||
Return -1 if element visibility is not supported or element not found, 0 if invisible, or else 1
|
||||
</UserDocu>
|
||||
</Documentation>
|
||||
</Methode>
|
||||
<Methode Name="hasChildElement">
|
||||
<Documentation>
|
||||
<UserDocu>Return true to indicate the object having child elements</UserDocu>
|
||||
</Documentation>
|
||||
</Methode>
|
||||
<Methode Name="getParentGroup">
|
||||
<Documentation>
|
||||
<UserDocu>Returns the group the object is in or None if it is not part of a group.
|
||||
@@ -79,6 +163,19 @@
|
||||
<UserDocu>Get all paths from this object to another object following the OutList.</UserDocu>
|
||||
</Documentation>
|
||||
</Methode>
|
||||
<Methode Name="resolve" Const="true">
|
||||
<Documentation>
|
||||
<UserDocu>
|
||||
resolve(subname) -- resolve the sub object
|
||||
|
||||
Returns a tuple (subobj,parent,elementName,subElement), where 'subobj' is the
|
||||
last object referenced in 'subname', and 'parent' is the direct parent of
|
||||
'subobj', and 'elementName' is the name of the subobj, which can be used
|
||||
to call parent.isElementVisible/setElementVisible(). 'subElement' is the
|
||||
non-object sub-element name if any.
|
||||
</UserDocu>
|
||||
</Documentation>
|
||||
</Methode>
|
||||
<Attribute Name="OutList" ReadOnly="true">
|
||||
<Documentation>
|
||||
<UserDocu>A list of all objects this object links to.</UserDocu>
|
||||
@@ -103,6 +200,12 @@
|
||||
</Documentation>
|
||||
<Parameter Name="InListRecursive" Type="List"/>
|
||||
</Attribute>
|
||||
<Attribute Name="FullName" ReadOnly="true">
|
||||
<Documentation>
|
||||
<UserDocu>Return the document name and internal name of this object</UserDocu>
|
||||
</Documentation>
|
||||
<Parameter Name="FullName" Type="String"/>
|
||||
</Attribute>
|
||||
<Attribute Name="Name" ReadOnly="true">
|
||||
<Documentation>
|
||||
<UserDocu>Return the internal name of this object</UserDocu>
|
||||
@@ -129,5 +232,17 @@ or None if the GUI is not up</UserDocu>
|
||||
<Parameter Name="ViewObject" Type="Object"/>
|
||||
</Attribute>
|
||||
<CustomAttributes />
|
||||
<Attribute Name="ID" ReadOnly="true">
|
||||
<Documentation>
|
||||
<UserDocu>The unique identifier (among its document) of this object</UserDocu>
|
||||
</Documentation>
|
||||
<Parameter Name="ID" Type="Int"/>
|
||||
</Attribute>
|
||||
<Attribute Name="Parents" ReadOnly="true">
|
||||
<Documentation>
|
||||
<UserDocu>A List of tuple(parent,subname) holding all parents to this object</UserDocu>
|
||||
</Documentation>
|
||||
<Parameter Name="Parents" Type="List"/>
|
||||
</Attribute>
|
||||
</PythonExport>
|
||||
</GenerateModel>
|
||||
|
||||
@@ -22,6 +22,8 @@
|
||||
|
||||
#include "PreCompiled.h"
|
||||
|
||||
#include <Base/GeometryPyCXX.h>
|
||||
#include <Base/MatrixPy.h>
|
||||
#include "DocumentObject.h"
|
||||
#include "Document.h"
|
||||
#include "Expression.h"
|
||||
@@ -53,6 +55,11 @@ Py::String DocumentObjectPy::getName(void) const
|
||||
return Py::String(std::string(internal));
|
||||
}
|
||||
|
||||
Py::String DocumentObjectPy::getFullName(void) const
|
||||
{
|
||||
return Py::String(getDocumentObjectPtr()->getFullName());
|
||||
}
|
||||
|
||||
Py::Object DocumentObjectPy::getDocument(void) const
|
||||
{
|
||||
DocumentObject* object = this->getDocumentObjectPtr();
|
||||
@@ -132,8 +139,21 @@ PyObject* DocumentObjectPy::supportedProperties(PyObject *args)
|
||||
|
||||
PyObject* DocumentObjectPy::touch(PyObject * args)
|
||||
{
|
||||
if (!PyArg_ParseTuple(args, "")) // convert args: Python->C
|
||||
char *propName = 0;
|
||||
if (!PyArg_ParseTuple(args, "|s",&propName)) // convert args: Python->C
|
||||
return NULL; // NULL triggers exception
|
||||
if(propName) {
|
||||
if(!propName[0]) {
|
||||
getDocumentObjectPtr()->touch(true);
|
||||
Py_Return;
|
||||
}
|
||||
auto prop = getDocumentObjectPtr()->getPropertyByName(propName);
|
||||
if(!prop)
|
||||
throw Py::RuntimeError("Property not found");
|
||||
prop->touch();
|
||||
Py_Return;
|
||||
}
|
||||
|
||||
getDocumentObjectPtr()->touch();
|
||||
Py_Return;
|
||||
}
|
||||
@@ -204,17 +224,19 @@ Py::Object DocumentObjectPy::getViewObject(void) const
|
||||
// in v0.14+, the GUI module can be loaded in console mode (but doesn't have all its document methods)
|
||||
return Py::None();
|
||||
}
|
||||
if(!getDocumentObjectPtr()->getDocument()) {
|
||||
throw Py::RuntimeError("Object has no document");
|
||||
}
|
||||
const char* internalName = getDocumentObjectPtr()->getNameInDocument();
|
||||
if (!internalName) {
|
||||
throw Py::RuntimeError("Object has been removed from document");
|
||||
}
|
||||
|
||||
Py::Callable method(module.getAttr("getDocument"));
|
||||
Py::Tuple arg(1);
|
||||
arg.setItem(0, Py::String(getDocumentObjectPtr()->getDocument()->getName()));
|
||||
Py::Object doc = method.apply(arg);
|
||||
method = doc.getAttr("getObject");
|
||||
const char* internalName = getDocumentObjectPtr()->getNameInDocument();
|
||||
if (!internalName) {
|
||||
throw Py::RuntimeError("Object has been removed from document");
|
||||
}
|
||||
|
||||
arg.setItem(0, Py::String(internalName));
|
||||
Py::Object obj = method.apply(arg);
|
||||
return obj;
|
||||
@@ -346,6 +368,247 @@ PyObject* DocumentObjectPy::recompute(PyObject *args)
|
||||
}
|
||||
}
|
||||
|
||||
PyObject* DocumentObjectPy::getSubObject(PyObject *args, PyObject *keywds)
|
||||
{
|
||||
PyObject *obj;
|
||||
short retType = 0;
|
||||
PyObject *pyMat = Py_None;
|
||||
PyObject *doTransform = Py_True;
|
||||
short depth = 0;
|
||||
static char *kwlist[] = {"subname","retType","matrix","transform","depth", NULL};
|
||||
if (!PyArg_ParseTupleAndKeywords(args, keywds, "O|hOOh", kwlist,
|
||||
&obj,&retType,&pyMat,&doTransform,&depth))
|
||||
return 0;
|
||||
|
||||
if(retType<0 || retType>6) {
|
||||
PyErr_SetString(PyExc_TypeError, "invalid retType, can only be integer 0~6");
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::vector<std::string> subs;
|
||||
bool single=true;
|
||||
if (PyUnicode_Check(obj)) {
|
||||
#if PY_MAJOR_VERSION >= 3
|
||||
subs.push_back(PyUnicode_AsUTF8(obj));
|
||||
#else
|
||||
PyObject* unicode = PyUnicode_AsUTF8String(obj);
|
||||
subs.push_back(PyString_AsString(unicode));
|
||||
Py_DECREF(unicode);
|
||||
}
|
||||
else if (PyString_Check(obj)) {
|
||||
subs.push_back(PyString_AsString(obj));
|
||||
#endif
|
||||
} else if (PySequence_Check(obj)) {
|
||||
single=false;
|
||||
Py::Sequence shapeSeq(obj);
|
||||
for (Py::Sequence::iterator it = shapeSeq.begin(); it != shapeSeq.end(); ++it) {
|
||||
PyObject* item = (*it).ptr();
|
||||
if (PyUnicode_Check(item)) {
|
||||
#if PY_MAJOR_VERSION >= 3
|
||||
subs.push_back(PyUnicode_AsUTF8(item));
|
||||
#else
|
||||
PyObject* unicode = PyUnicode_AsUTF8String(item);
|
||||
subs.push_back(PyString_AsString(unicode));
|
||||
Py_DECREF(unicode);
|
||||
}
|
||||
else if (PyString_Check(item)) {
|
||||
subs.push_back(PyString_AsString(item));
|
||||
#endif
|
||||
}else{
|
||||
PyErr_SetString(PyExc_TypeError, "non-string object in sequence");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}else{
|
||||
PyErr_SetString(PyExc_TypeError, "subname must be either a string or sequence of string");
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool transform = PyObject_IsTrue(doTransform);
|
||||
|
||||
struct SubInfo {
|
||||
App::DocumentObject *sobj;
|
||||
Py::Object obj;
|
||||
Py::Object pyObj;
|
||||
Base::Matrix4D mat;
|
||||
SubInfo(const Base::Matrix4D &mat):mat(mat){}
|
||||
};
|
||||
|
||||
Base::Matrix4D mat;
|
||||
if(pyMat!=Py_None) {
|
||||
if(!PyObject_TypeCheck(pyMat,&Base::MatrixPy::Type)) {
|
||||
PyErr_SetString(PyExc_TypeError, "expect argument 'matrix' to be of type Base.Matrix");
|
||||
return 0;
|
||||
}
|
||||
mat = *static_cast<Base::MatrixPy*>(pyMat)->getMatrixPtr();
|
||||
}
|
||||
|
||||
PY_TRY {
|
||||
std::vector<SubInfo> ret;
|
||||
for(const auto &sub : subs) {
|
||||
ret.emplace_back(mat);
|
||||
auto &info = ret.back();
|
||||
PyObject *pyObj = 0;
|
||||
info.sobj = getDocumentObjectPtr()->getSubObject(
|
||||
sub.c_str(),retType!=0&&retType!=2?0:&pyObj,&info.mat,transform,depth);
|
||||
if(pyObj)
|
||||
info.pyObj = Py::Object(pyObj,true);
|
||||
if(info.sobj)
|
||||
info.obj = Py::Object(info.sobj->getPyObject(),true);
|
||||
}
|
||||
if(ret.empty())
|
||||
Py_Return;
|
||||
|
||||
if(single) {
|
||||
if(retType==0)
|
||||
return Py::new_reference_to(ret[0].pyObj);
|
||||
else if(retType==1 && pyMat==Py_None)
|
||||
return Py::new_reference_to(ret[0].obj);
|
||||
else if(!ret[0].sobj)
|
||||
Py_Return;
|
||||
else if(retType==3)
|
||||
return Py::new_reference_to(Py::Placement(Base::Placement(ret[0].mat)));
|
||||
else if(retType==4)
|
||||
return Py::new_reference_to(Py::Matrix(ret[0].mat));
|
||||
else if(retType==5 || retType==6) {
|
||||
ret[0].sobj->getLinkedObject(true,&ret[0].mat,false);
|
||||
if(retType==5)
|
||||
return Py::new_reference_to(Py::Placement(Base::Placement(ret[0].mat)));
|
||||
else
|
||||
return Py::new_reference_to(Py::Matrix(ret[0].mat));
|
||||
}
|
||||
Py::Tuple rret(retType==1?2:3);
|
||||
rret.setItem(0,ret[0].obj);
|
||||
rret.setItem(1,Py::Object(new Base::MatrixPy(ret[0].mat)));
|
||||
if(retType!=1)
|
||||
rret.setItem(2,ret[0].pyObj);
|
||||
return Py::new_reference_to(rret);
|
||||
}
|
||||
Py::Tuple tuple(ret.size());
|
||||
for(size_t i=0;i<ret.size();++i) {
|
||||
if(retType==0)
|
||||
tuple.setItem(i,ret[i].pyObj);
|
||||
else if(retType==1 && pyMat==Py_None)
|
||||
tuple.setItem(i,ret[i].obj);
|
||||
else if(!ret[i].sobj)
|
||||
tuple.setItem(i, Py::Object());
|
||||
else if(retType==3)
|
||||
tuple.setItem(i,Py::Placement(Base::Placement(ret[0].mat)));
|
||||
else if(retType==4)
|
||||
tuple.setItem(i,Py::Matrix(ret[0].mat));
|
||||
else if(retType==5 || retType==6) {
|
||||
ret[i].sobj->getLinkedObject(true,&ret[i].mat,false);
|
||||
if(retType==5)
|
||||
tuple.setItem(i,Py::Placement(Base::Placement(ret[i].mat)));
|
||||
else
|
||||
tuple.setItem(i,Py::Matrix(ret[i].mat));
|
||||
} else {
|
||||
Py::Tuple rret(retType==1?2:3);
|
||||
rret.setItem(0,ret[i].obj);
|
||||
rret.setItem(1,Py::Object(new Base::MatrixPy(ret[i].mat)));
|
||||
if(retType!=1)
|
||||
rret.setItem(2,ret[i].pyObj);
|
||||
tuple.setItem(i,rret);
|
||||
}
|
||||
}
|
||||
return Py::new_reference_to(tuple);
|
||||
}PY_CATCH
|
||||
}
|
||||
|
||||
PyObject* DocumentObjectPy::getSubObjectList(PyObject *args) {
|
||||
const char *subname;
|
||||
if (!PyArg_ParseTuple(args, "s", &subname))
|
||||
return NULL;
|
||||
Py::List res;
|
||||
PY_TRY {
|
||||
for(auto o : getDocumentObjectPtr()->getSubObjectList(subname))
|
||||
res.append(Py::asObject(o->getPyObject()));
|
||||
return Py::new_reference_to(res);
|
||||
}PY_CATCH
|
||||
}
|
||||
|
||||
PyObject* DocumentObjectPy::getSubObjects(PyObject *args) {
|
||||
int reason = 0;
|
||||
if (!PyArg_ParseTuple(args, "|i", &reason))
|
||||
return NULL;
|
||||
|
||||
PY_TRY {
|
||||
auto names = getDocumentObjectPtr()->getSubObjects(reason);
|
||||
Py::Tuple pyObjs(names.size());
|
||||
for(size_t i=0;i<names.size();++i)
|
||||
pyObjs.setItem(i,Py::String(names[i]));
|
||||
return Py::new_reference_to(pyObjs);
|
||||
}PY_CATCH;
|
||||
}
|
||||
|
||||
PyObject* DocumentObjectPy::getLinkedObject(PyObject *args, PyObject *keywds)
|
||||
{
|
||||
PyObject *recursive = Py_True;
|
||||
PyObject *pyMat = Py_None;
|
||||
PyObject *transform = Py_True;
|
||||
short depth = 0;
|
||||
static char *kwlist[] = {"recursive","matrix","transform","depth", NULL};
|
||||
if (!PyArg_ParseTupleAndKeywords(args, keywds, "|OOOh", kwlist,
|
||||
&recursive,&pyMat,&transform,&depth))
|
||||
return NULL;
|
||||
|
||||
Base::Matrix4D _mat;
|
||||
Base::Matrix4D *mat = 0;
|
||||
if(pyMat!=Py_None) {
|
||||
if(!PyObject_TypeCheck(pyMat,&Base::MatrixPy::Type)) {
|
||||
PyErr_SetString(PyExc_TypeError, "expect argument 'matrix' to be of type Base.Matrix");
|
||||
return 0;
|
||||
}
|
||||
_mat = *static_cast<Base::MatrixPy*>(pyMat)->getMatrixPtr();
|
||||
mat = &_mat;
|
||||
}
|
||||
|
||||
PY_TRY {
|
||||
auto linked = getDocumentObjectPtr()->getLinkedObject(
|
||||
PyObject_IsTrue(recursive), mat, PyObject_IsTrue(transform),depth);
|
||||
if(!linked)
|
||||
linked = getDocumentObjectPtr();
|
||||
auto pyObj = Py::Object(linked->getPyObject(),true);
|
||||
if(mat) {
|
||||
Py::Tuple ret(2);
|
||||
ret.setItem(0,pyObj);
|
||||
ret.setItem(1,Py::Object(new Base::MatrixPy(*mat)));
|
||||
return Py::new_reference_to(ret);
|
||||
}
|
||||
return Py::new_reference_to(pyObj);
|
||||
} PY_CATCH;
|
||||
}
|
||||
|
||||
PyObject* DocumentObjectPy::isElementVisible(PyObject *args)
|
||||
{
|
||||
char *element = 0;
|
||||
if (!PyArg_ParseTuple(args, "s", &element))
|
||||
return NULL;
|
||||
PY_TRY {
|
||||
return Py_BuildValue("h", getDocumentObjectPtr()->isElementVisible(element));
|
||||
} PY_CATCH;
|
||||
}
|
||||
|
||||
PyObject* DocumentObjectPy::setElementVisible(PyObject *args)
|
||||
{
|
||||
char *element = 0;
|
||||
PyObject *visible = Py_True;
|
||||
if (!PyArg_ParseTuple(args, "s|O", &element,&visible))
|
||||
return NULL;
|
||||
PY_TRY {
|
||||
return Py_BuildValue("h", getDocumentObjectPtr()->setElementVisible(element,PyObject_IsTrue(visible)));
|
||||
} PY_CATCH;
|
||||
}
|
||||
|
||||
PyObject* DocumentObjectPy::hasChildElement(PyObject *args)
|
||||
{
|
||||
if (!PyArg_ParseTuple(args, ""))
|
||||
return NULL;
|
||||
PY_TRY {
|
||||
return Py_BuildValue("O", getDocumentObjectPtr()->hasChildElement()?Py_True:Py_False);
|
||||
} PY_CATCH;
|
||||
}
|
||||
|
||||
PyObject* DocumentObjectPy::getParentGroup(PyObject *args)
|
||||
{
|
||||
if (!PyArg_ParseTuple(args, ""))
|
||||
@@ -467,3 +730,41 @@ int DocumentObjectPy::setCustomAttributes(const char* attr, PyObject *obj)
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
Py::Int DocumentObjectPy::getID() const {
|
||||
return Py::Int(getDocumentObjectPtr()->getID());
|
||||
}
|
||||
|
||||
Py::Boolean DocumentObjectPy::getRemoving() const {
|
||||
return Py::Boolean(getDocumentObjectPtr()->testStatus(ObjectStatus::Remove));
|
||||
}
|
||||
|
||||
PyObject *DocumentObjectPy::resolve(PyObject *args)
|
||||
{
|
||||
const char *subname;
|
||||
if (!PyArg_ParseTuple(args, "s",&subname))
|
||||
return NULL; // NULL triggers exception
|
||||
|
||||
PY_TRY {
|
||||
std::string elementName;
|
||||
const char *subElement = 0;
|
||||
App::DocumentObject *parent = 0;
|
||||
auto obj = getDocumentObjectPtr()->resolve(subname,&parent,&elementName,&subElement);
|
||||
|
||||
Py::Tuple ret(4);
|
||||
ret.setItem(0,obj?Py::Object(obj->getPyObject(),true):Py::None());
|
||||
ret.setItem(1,parent?Py::Object(parent->getPyObject(),true):Py::None());
|
||||
ret.setItem(2,Py::String(elementName.c_str()));
|
||||
ret.setItem(3,Py::String(subElement?subElement:""));
|
||||
return Py::new_reference_to(ret);
|
||||
} PY_CATCH;
|
||||
|
||||
Py_Return;
|
||||
}
|
||||
|
||||
Py::List DocumentObjectPy::getParents() const {
|
||||
Py::List ret;
|
||||
for(auto &v : getDocumentObjectPtr()->getParents())
|
||||
ret.append(Py::TupleN(Py::Object(v.first->getPyObject(),true),Py::String(v.second)));
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -63,9 +63,20 @@
|
||||
<UserDocu>Commit an Undo/Redo transaction</UserDocu>
|
||||
</Documentation>
|
||||
</Methode>
|
||||
<Methode Name="addObject">
|
||||
<Methode Name="addObject" Keyword="true">
|
||||
<Documentation>
|
||||
<UserDocu>Add an object with given type and name to the document</UserDocu>
|
||||
<UserDocu>addObject(type, name=None, objProxy=None, viewProxy=None, attach=False, viewType=None)
|
||||
|
||||
Add an object to document
|
||||
|
||||
type (String): the type of the document object to create.
|
||||
name (String): the optional name of the new object.
|
||||
objProxy (Object): the Python binding object to attach to the new document object.
|
||||
viewProxy (Object): the Python binding object to attach the view provider of this object.
|
||||
attach (Boolean): if True, then bind the document object first before adding to the document
|
||||
to allow Python code to override view provider type. Once bound, and before adding to
|
||||
the document, it will try to call Python binding object's attach(obj) method.
|
||||
viewType (String): override the view provider type directly, only effective when attach is False.</UserDocu>
|
||||
</Documentation>
|
||||
</Methode>
|
||||
<Methode Name="removeObject">
|
||||
@@ -123,6 +134,16 @@ Return a list of objects that match the specified type and name.
|
||||
Both parameters are optional.</UserDocu>
|
||||
</Documentation>
|
||||
</Methode>
|
||||
<Methode Name="getLinksTo">
|
||||
<Documentation>
|
||||
<UserDocu>
|
||||
getLinksTo(obj, options=0, maxCount=0): return objects linked to 'obj'
|
||||
|
||||
options: 1: recursive, 2: check link array. Options can combine.
|
||||
maxCount: to limit the number of links returned
|
||||
</UserDocu>
|
||||
</Documentation>
|
||||
</Methode>
|
||||
<Methode Name="supportedTypes">
|
||||
<Documentation>
|
||||
<UserDocu>A list of supported types of objects</UserDocu>
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
#include "DocumentObject.h"
|
||||
#include "DocumentObjectPy.h"
|
||||
#include "MergeDocuments.h"
|
||||
#include "PropertyLinks.h"
|
||||
|
||||
// inclusion of the generated files (generated By DocumentPy.xml)
|
||||
#include "DocumentPy.h"
|
||||
@@ -55,20 +56,12 @@ PyObject* DocumentPy::save(PyObject * args)
|
||||
if (!PyArg_ParseTuple(args, "")) // convert args: Python->C
|
||||
return NULL; // NULL triggers exception
|
||||
|
||||
try {
|
||||
PY_TRY {
|
||||
if (!getDocumentPtr()->save()) {
|
||||
PyErr_SetString(PyExc_ValueError, "Object attribute 'FileName' is not set");
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
catch (const Base::FileException& e) {
|
||||
PyErr_SetString(PyExc_IOError, e.what());
|
||||
return 0;
|
||||
}
|
||||
catch (const Base::Exception& e) {
|
||||
PyErr_SetString(PyExc_RuntimeError, e.what());
|
||||
return 0;
|
||||
}
|
||||
} PY_CATCH;
|
||||
|
||||
const char* filename = getDocumentPtr()->FileName.getValue();
|
||||
Base::FileInfo fi(filename);
|
||||
@@ -89,28 +82,10 @@ PyObject* DocumentPy::saveAs(PyObject * args)
|
||||
std::string utf8Name = fn;
|
||||
PyMem_Free(fn);
|
||||
|
||||
try {
|
||||
if (!getDocumentPtr()->saveAs(utf8Name.c_str())) {
|
||||
PyErr_SetString(PyExc_ValueError, "Object attribute 'FileName' is not set");
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
catch (const Base::FileException& e) {
|
||||
PyErr_SetString(PyExc_IOError, e.what());
|
||||
return 0;
|
||||
}
|
||||
catch (const Base::Exception& e) {
|
||||
PyErr_SetString(PyExc_RuntimeError, e.what());
|
||||
return 0;
|
||||
}
|
||||
|
||||
Base::FileInfo fi(utf8Name);
|
||||
if (!fi.isReadable()) {
|
||||
PyErr_Format(PyExc_IOError, "No such file or directory: '%s'", utf8Name.c_str());
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Py_Return;
|
||||
PY_TRY {
|
||||
getDocumentPtr()->saveAs(utf8Name.c_str());
|
||||
Py_Return;
|
||||
}PY_CATCH
|
||||
}
|
||||
|
||||
PyObject* DocumentPy::saveCopy(PyObject * args)
|
||||
@@ -118,18 +93,11 @@ PyObject* DocumentPy::saveCopy(PyObject * args)
|
||||
char* fn;
|
||||
if (!PyArg_ParseTuple(args, "s", &fn)) // convert args: Python->C
|
||||
return NULL; // NULL triggers exception
|
||||
if (!getDocumentPtr()->saveCopy(fn)) {
|
||||
PyErr_Format(PyExc_ValueError, "Object attribute 'FileName' is not set");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Base::FileInfo fi(fn);
|
||||
if (!fi.isReadable()) {
|
||||
PyErr_Format(PyExc_IOError, "No such file or directory: '%s'", fn);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Py_Return;
|
||||
PY_TRY {
|
||||
getDocumentPtr()->saveCopy(fn);
|
||||
Py_Return;
|
||||
}PY_CATCH
|
||||
}
|
||||
|
||||
PyObject* DocumentPy::load(PyObject * args)
|
||||
@@ -219,17 +187,33 @@ PyObject* DocumentPy::exportGraphviz(PyObject * args)
|
||||
}
|
||||
}
|
||||
|
||||
PyObject* DocumentPy::addObject(PyObject *args)
|
||||
PyObject* DocumentPy::addObject(PyObject *args, PyObject *kwd)
|
||||
{
|
||||
char *sType,*sName=0;
|
||||
char *sType,*sName=0,*sViewType=0;
|
||||
PyObject* obj=0;
|
||||
PyObject* view=0;
|
||||
if (!PyArg_ParseTuple(args, "s|sOO", &sType,&sName,&obj,&view)) // convert args: Python->C
|
||||
return NULL; // NULL triggers exception
|
||||
PyObject *attach=Py_False;
|
||||
static char *kwlist[] = {"type","name","objProxy","viewProxy","attach","viewType",NULL};
|
||||
if (!PyArg_ParseTupleAndKeywords(args,kwd,"s|sOOOs",
|
||||
kwlist, &sType,&sName,&obj,&view,&attach,&sViewType))
|
||||
return NULL;
|
||||
|
||||
DocumentObject *pcFtr;
|
||||
DocumentObject *pcFtr = 0;
|
||||
|
||||
pcFtr = getDocumentPtr()->addObject(sType,sName);
|
||||
if(!obj || !PyObject_IsTrue(attach)) {
|
||||
pcFtr = getDocumentPtr()->addObject(sType,sName,true,sViewType);
|
||||
}else{
|
||||
Base::BaseClass* base = static_cast<Base::BaseClass*>(Base::Type::createInstanceByName(sType,true));
|
||||
if(base) {
|
||||
if (!base->getTypeId().isDerivedFrom(App::DocumentObject::getClassTypeId())) {
|
||||
delete base;
|
||||
std::stringstream str;
|
||||
str << "'" << sType << "' is not a document object type";
|
||||
throw Base::TypeError(str.str());
|
||||
}
|
||||
pcFtr = static_cast<DocumentObject*>(base);
|
||||
}
|
||||
}
|
||||
if (pcFtr) {
|
||||
// Allows to hide the handling with Proxy in client python code
|
||||
if (obj) {
|
||||
@@ -243,6 +227,21 @@ PyObject* DocumentPy::addObject(PyObject *args)
|
||||
}
|
||||
pyftr.setAttr("Proxy", pyobj);
|
||||
|
||||
if(PyObject_IsTrue(attach)) {
|
||||
getDocumentPtr()->addObject(pcFtr,sName);
|
||||
|
||||
try {
|
||||
Py::Callable method(pyobj.getAttr("attach"));
|
||||
if(!method.isNone()) {
|
||||
Py::TupleN arg(pyftr);
|
||||
method.apply(arg);
|
||||
}
|
||||
}catch (Py::Exception&) {
|
||||
Base::PyException e;
|
||||
e.ReportException();
|
||||
}
|
||||
}
|
||||
|
||||
// if a document class is set we also need a view provider defined which must be
|
||||
// something different to None
|
||||
Py::Object pyvp;
|
||||
@@ -416,11 +415,14 @@ PyObject* DocumentPy::recompute(PyObject * args)
|
||||
|
||||
PyObject* DocumentPy::getObject(PyObject *args)
|
||||
{
|
||||
char *sName;
|
||||
if (!PyArg_ParseTuple(args, "s",&sName)) // convert args: Python->C
|
||||
return NULL; // NULL triggers exception
|
||||
long id = -1;
|
||||
char *sName = 0;
|
||||
if (!PyArg_ParseTuple(args, "s",&sName)) { // convert args: Python->C
|
||||
if(!PyArg_ParseTuple(args, "l", &id))
|
||||
return NULL; // NULL triggers exception
|
||||
}
|
||||
|
||||
DocumentObject *pcFtr = getDocumentPtr()->getObject(sName);
|
||||
DocumentObject *pcFtr = sName?getDocumentPtr()->getObject(sName):getDocumentPtr()->getObjectByID(id);
|
||||
if (pcFtr)
|
||||
return pcFtr->getPyObject();
|
||||
else
|
||||
@@ -695,3 +697,31 @@ int DocumentPy::setCustomAttributes(const char* attr, PyObject *)
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
PyObject* DocumentPy::getLinksTo(PyObject *args)
|
||||
{
|
||||
PyObject *pyobj = Py_None;
|
||||
int options = 0;
|
||||
short count = 0;
|
||||
if (!PyArg_ParseTuple(args, "|Oih", &pyobj,&options, &count))
|
||||
return NULL;
|
||||
|
||||
PY_TRY {
|
||||
DocumentObject *obj = 0;
|
||||
if(pyobj!=Py_None) {
|
||||
if(!PyObject_TypeCheck(pyobj,&DocumentObjectPy::Type)) {
|
||||
PyErr_SetString(PyExc_TypeError, "Expect the first argument of type document object");
|
||||
return 0;
|
||||
}
|
||||
obj = static_cast<DocumentObjectPy*>(pyobj)->getDocumentObjectPtr();
|
||||
}
|
||||
std::set<DocumentObject *> links;
|
||||
getDocumentPtr()->getLinksTo(links,obj,options,count);
|
||||
Py::Tuple ret(links.size());
|
||||
int i=0;
|
||||
for(auto o : links)
|
||||
ret.setItem(i++,Py::Object(o->getPyObject(),true));
|
||||
return Py::new_reference_to(ret);
|
||||
}PY_CATCH
|
||||
}
|
||||
|
||||
|
||||
@@ -301,17 +301,24 @@ private:
|
||||
};
|
||||
|
||||
// Property define
|
||||
#define EXTENSION_ADD_PROPERTY(_prop_, _defaultval_) \
|
||||
#define _EXTENSION_ADD_PROPERTY(_name, _prop_, _defaultval_) \
|
||||
do { \
|
||||
this->_prop_.setValue _defaultval_;\
|
||||
propertyData.addProperty(static_cast<App::Extension*>(this), #_prop_, &this->_prop_); \
|
||||
propertyData.addProperty(static_cast<App::Extension*>(this), _name, &this->_prop_); \
|
||||
} while (0)
|
||||
|
||||
|
||||
#define EXTENSION_ADD_PROPERTY(_prop_, _defaultval_) \
|
||||
_EXTENSION_ADD_PROPERTY(#_prop_, _prop_, _defaultval_)
|
||||
|
||||
#define _EXTENSION_ADD_PROPERTY_TYPE(_name, _prop_, _defaultval_, _group_,_type_,_Docu_) \
|
||||
do { \
|
||||
this->_prop_.setValue _defaultval_;\
|
||||
propertyData.addProperty(static_cast<App::Extension*>(this), _name, &this->_prop_, (_group_),(_type_),(_Docu_)); \
|
||||
} while (0)
|
||||
|
||||
#define EXTENSION_ADD_PROPERTY_TYPE(_prop_, _defaultval_, _group_,_type_,_Docu_) \
|
||||
do { \
|
||||
this->_prop_.setValue _defaultval_;\
|
||||
propertyData.addProperty(static_cast<App::Extension*>(this), #_prop_, &this->_prop_, (_group_),(_type_),(_Docu_)); \
|
||||
} while (0)
|
||||
_EXTENSION_ADD_PROPERTY_TYPE(#_prop_, _prop_, _defaultval_, _group_,_type_,_Docu_)
|
||||
|
||||
|
||||
/**
|
||||
|
||||
@@ -94,8 +94,8 @@ bool ExtensionContainer::hasExtension(const std::string& name) const {
|
||||
}
|
||||
|
||||
|
||||
Extension* ExtensionContainer::getExtension(Base::Type t, bool derived) const {
|
||||
|
||||
Extension* ExtensionContainer::getExtension(Base::Type t, bool derived, bool no_except) const {
|
||||
|
||||
auto result = _extensions.find(t);
|
||||
if((result == _extensions.end()) && derived) {
|
||||
//we need to check for derived types
|
||||
@@ -103,7 +103,7 @@ Extension* ExtensionContainer::getExtension(Base::Type t, bool derived) const {
|
||||
if(entry.first.isDerivedFrom(t))
|
||||
return entry.second;
|
||||
}
|
||||
|
||||
if(no_except) return 0;
|
||||
//if we arrive here we don't have anything matching
|
||||
throw Base::TypeError("ExtensionContainer::getExtension: No extension of given type available");
|
||||
}
|
||||
@@ -111,6 +111,7 @@ Extension* ExtensionContainer::getExtension(Base::Type t, bool derived) const {
|
||||
return result->second;
|
||||
}
|
||||
else {
|
||||
if(no_except) return 0;
|
||||
//if we arrive here we don't have anything matching
|
||||
throw Base::TypeError("ExtensionContainer::getExtension: No extension of given type available");
|
||||
}
|
||||
|
||||
@@ -127,24 +127,24 @@ public:
|
||||
bool hasExtension(Base::Type, bool derived=true) const; //returns first of type (or derived from if set to true) and throws otherwise
|
||||
bool hasExtension(const std::string& name) const; //this version does not check derived classes
|
||||
bool hasExtensions() const;
|
||||
App::Extension* getExtension(Base::Type, bool derived = true) const;
|
||||
App::Extension* getExtension(Base::Type, bool derived = true, bool no_except=false) const;
|
||||
App::Extension* getExtension(const std::string& name) const; //this version does not check derived classes
|
||||
|
||||
//returns first of type (or derived from) and throws otherwise
|
||||
template<typename ExtensionT>
|
||||
ExtensionT* getExtensionByType() const {
|
||||
return dynamic_cast<ExtensionT*>(getExtension(ExtensionT::getExtensionClassTypeId()));
|
||||
ExtensionT* getExtensionByType(bool no_except=false, bool derived=true) const {
|
||||
return static_cast<ExtensionT*>(getExtension(ExtensionT::getExtensionClassTypeId(),derived,no_except));
|
||||
};
|
||||
|
||||
//get all extensions which have the given base class
|
||||
std::vector<Extension*> getExtensionsDerivedFrom(Base::Type type) const;
|
||||
template<typename ExtensionT>
|
||||
std::vector<ExtensionT*> getExtensionsDerivedFromType() const {
|
||||
auto vec = getExtensionsDerivedFrom(ExtensionT::getExtensionClassTypeId());
|
||||
std::vector<ExtensionT*> typevec;
|
||||
for(auto ext : vec)
|
||||
typevec.push_back(dynamic_cast<ExtensionT*>(ext));
|
||||
|
||||
for(auto entry : _extensions) {
|
||||
if(entry.first.isDerivedFrom(ExtensionT::getExtensionClassTypeId()))
|
||||
typevec.push_back(static_cast<ExtensionT*>(entry.second));
|
||||
}
|
||||
return typevec;
|
||||
};
|
||||
|
||||
|
||||
@@ -34,6 +34,7 @@
|
||||
#include "Origin.h"
|
||||
#include "OriginGroupExtension.h"
|
||||
#include <Base/Console.h>
|
||||
#include <Base/Tools.h>
|
||||
//#include "GeoFeatureGroupPy.h"
|
||||
//#include "FeaturePythonPyImp.h"
|
||||
|
||||
@@ -100,11 +101,9 @@ DocumentObject* GeoFeatureGroupExtension::getGroupOfObject(const DocumentObject*
|
||||
//There is a chance that a derived geofeaturegroup links with a local link and hence is not
|
||||
//the parent group even though it links to the object. We use hasObject as one and only truth
|
||||
//if it has the object within the group
|
||||
if(inObj->hasExtension(App::GeoFeatureGroupExtension::getExtensionClassTypeId()) &&
|
||||
inObj->getExtensionByType<GeoFeatureGroupExtension>()->hasObject(obj)) {
|
||||
|
||||
auto group = inObj->getExtensionByType<GeoFeatureGroupExtension>(true);
|
||||
if(group && group->hasObject(obj))
|
||||
return inObj;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
@@ -124,10 +123,9 @@ Base::Placement GeoFeatureGroupExtension::recursiveGroupPlacement(GeoFeatureGrou
|
||||
|
||||
auto inList = group->getExtendedObject()->getInList();
|
||||
for(auto* link : inList) {
|
||||
if(link->hasExtension(App::GeoFeatureGroupExtension::getExtensionClassTypeId())){
|
||||
if (link->getExtensionByType<GeoFeatureGroupExtension>()->hasObject(group->getExtendedObject()))
|
||||
return recursiveGroupPlacement(link->getExtensionByType<GeoFeatureGroupExtension>()) * group->placement().getValue();
|
||||
}
|
||||
auto parent = link->getExtensionByType<GeoFeatureGroupExtension>(true);
|
||||
if(parent && parent->hasObject(group->getExtendedObject()))
|
||||
return recursiveGroupPlacement(parent) * group->placement().getValue();
|
||||
}
|
||||
|
||||
return group->placement().getValue();
|
||||
@@ -205,9 +203,10 @@ void GeoFeatureGroupExtension::extensionOnChanged(const Property* p) {
|
||||
//would return anyone of it and hence it is possible that we miss an error. We need a custom check
|
||||
auto list = obj->getInList();
|
||||
for (auto in : list) {
|
||||
if(in->hasExtension(App::GeoFeatureGroupExtension::getExtensionClassTypeId()) && //is GeoFeatureGroup?
|
||||
in != getExtendedObject() && //is a different one?
|
||||
in->getExtensionByType<App::GeoFeatureGroupExtension>()->hasObject(obj)) { //is not a non-grouping link?
|
||||
if(in == getExtendedObject())
|
||||
continue;
|
||||
auto parent = in->getExtensionByType<GeoFeatureGroupExtension>(true);
|
||||
if(parent && parent->hasObject(obj)) {
|
||||
error = true;
|
||||
corrected.erase(std::remove(corrected.begin(), corrected.end(), obj), corrected.end());
|
||||
}
|
||||
@@ -285,8 +284,6 @@ std::vector< DocumentObject* > GeoFeatureGroupExtension::getScopedObjectsFromLin
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void GeoFeatureGroupExtension::getCSOutList(const App::DocumentObject* obj,
|
||||
std::vector< DocumentObject* >& vec) {
|
||||
|
||||
@@ -373,6 +370,59 @@ void GeoFeatureGroupExtension::recursiveCSRelevantLinks(const DocumentObject* ob
|
||||
}
|
||||
}
|
||||
|
||||
bool GeoFeatureGroupExtension::extensionGetSubObject(DocumentObject *&ret, const char *subname,
|
||||
PyObject **pyObj, Base::Matrix4D *mat, bool transform, int depth) const
|
||||
{
|
||||
ret = 0;
|
||||
const char *dot;
|
||||
if(!subname || *subname==0) {
|
||||
auto obj = dynamic_cast<const DocumentObject*>(getExtendedContainer());
|
||||
ret = const_cast<DocumentObject*>(obj);
|
||||
if(mat && transform)
|
||||
*mat *= const_cast<GeoFeatureGroupExtension*>(this)->placement().getValue().toMatrix();
|
||||
}else if((dot=strchr(subname,'.'))) {
|
||||
if(subname[0]!='$')
|
||||
ret = Group.find(std::string(subname,dot));
|
||||
else{
|
||||
std::string name = std::string(subname+1,dot);
|
||||
for(auto child : Group.getValues()) {
|
||||
if(name == child->Label.getStrValue()){
|
||||
ret = child;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(ret) {
|
||||
if(dot) ++dot;
|
||||
if(dot && *dot && !ret->hasExtension(App::GeoFeatureGroupExtension::getExtensionClassTypeId())) {
|
||||
// Consider this
|
||||
// Body
|
||||
// | -- Pad
|
||||
// | -- Sketch
|
||||
//
|
||||
// Suppose we want to getSubObject(Body,"Pad.Sketch.")
|
||||
// Because of the special property of geo feature group, both
|
||||
// Pad and Sketch are children of Body, so we can't call
|
||||
// getSubObject(Pad,"Sketch.") as this will transform Sketch
|
||||
// using Pad's placement.
|
||||
//
|
||||
const char *next = strchr(dot,'.');
|
||||
if(next) {
|
||||
App::DocumentObject *nret=0;
|
||||
extensionGetSubObject(nret,dot,pyObj,mat,transform,depth+1);
|
||||
if(nret) {
|
||||
ret = nret;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(mat && transform)
|
||||
*mat *= const_cast<GeoFeatureGroupExtension*>(this)->placement().getValue().toMatrix();
|
||||
ret = ret->getSubObject(dot?dot:"",pyObj,mat,true,depth+1);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GeoFeatureGroupExtension::areLinksValid(const DocumentObject* obj) {
|
||||
|
||||
@@ -448,6 +498,14 @@ void GeoFeatureGroupExtension::getInvalidLinkObjects(const DocumentObject* obj,
|
||||
}
|
||||
}
|
||||
|
||||
bool GeoFeatureGroupExtension::extensionGetSubObjects(std::vector<std::string> &ret, int) const {
|
||||
for(auto obj : Group.getValues()) {
|
||||
if(obj && obj->getNameInDocument() && !obj->testStatus(ObjectStatus::GeoExcluded))
|
||||
ret.push_back(std::string(obj->getNameInDocument())+'.');
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// Python feature ---------------------------------------------------------
|
||||
|
||||
|
||||
@@ -50,6 +50,7 @@ namespace App
|
||||
*/
|
||||
class AppExport GeoFeatureGroupExtension : public App::GroupExtension
|
||||
{
|
||||
typedef App::GroupExtension inherited;
|
||||
EXTENSION_PROPERTY_HEADER_WITH_OVERRIDE(App::GeoFeatureGroupExtension);
|
||||
|
||||
public:
|
||||
@@ -95,6 +96,11 @@ public:
|
||||
return obj->hasExtension(GroupExtension::getExtensionClassTypeId()) &&
|
||||
!obj->hasExtension(GeoFeatureGroupExtension::getExtensionClassTypeId());
|
||||
}
|
||||
|
||||
virtual bool extensionGetSubObject(DocumentObject *&ret, const char *subname, PyObject **pyObj,
|
||||
Base::Matrix4D *mat, bool transform, int depth) const override;
|
||||
|
||||
virtual bool extensionGetSubObjects(std::vector<std::string> &ret, int reason) const override;
|
||||
|
||||
virtual std::vector< DocumentObject* > addObjects(std::vector< DocumentObject* > obj) override;
|
||||
virtual std::vector< DocumentObject* > removeObjects(std::vector< DocumentObject* > obj) override;
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
#include "FeaturePythonPyImp.h"
|
||||
#include "GeoFeatureGroupExtension.h"
|
||||
#include <Base/Console.h>
|
||||
#include <Base/Tools.h>
|
||||
|
||||
using namespace App;
|
||||
|
||||
@@ -251,15 +252,12 @@ bool GroupExtension::recursiveHasObject(const DocumentObject* obj, const GroupEx
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool GroupExtension::isChildOf(const GroupExtension* group, bool recursive) const
|
||||
{
|
||||
return group->hasObject(getExtendedObject(), recursive);
|
||||
}
|
||||
|
||||
|
||||
|
||||
std::vector<DocumentObject*> GroupExtension::getObjects() const
|
||||
const std::vector<DocumentObject*> &GroupExtension::getObjects() const
|
||||
{
|
||||
return Group.getValues();
|
||||
}
|
||||
@@ -348,6 +346,41 @@ void GroupExtension::extensionOnChanged(const Property* p) {
|
||||
App::Extension::extensionOnChanged(p);
|
||||
}
|
||||
|
||||
bool GroupExtension::extensionGetSubObject(DocumentObject *&ret, const char *subname,
|
||||
PyObject **pyObj, Base::Matrix4D *mat, bool /*transform*/, int depth) const
|
||||
{
|
||||
const char *dot;
|
||||
if(!subname || *subname==0) {
|
||||
auto obj = Base::freecad_dynamic_cast<const DocumentObject>(getExtendedContainer());
|
||||
ret = const_cast<DocumentObject*>(obj);
|
||||
return true;
|
||||
}
|
||||
dot=strchr(subname,'.');
|
||||
if(!dot)
|
||||
return false;
|
||||
if(subname[0]!='$')
|
||||
ret = Group.find(std::string(subname,dot));
|
||||
else{
|
||||
std::string name = std::string(subname+1,dot);
|
||||
for(auto child : Group.getValues()) {
|
||||
if(name == child->Label.getStrValue()){
|
||||
ret = child;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(!ret)
|
||||
return false;
|
||||
return ret->getSubObject(dot+1,pyObj,mat,true,depth+1);
|
||||
}
|
||||
|
||||
bool GroupExtension::extensionGetSubObjects(std::vector<std::string> &ret, int) const {
|
||||
for(auto obj : Group.getValues()) {
|
||||
if(obj && obj->getNameInDocument())
|
||||
ret.push_back(std::string(obj->getNameInDocument())+'.');
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
namespace App {
|
||||
EXTENSION_PROPERTY_SOURCE_TEMPLATE(App::GroupExtensionPython, App::GroupExtension)
|
||||
|
||||
@@ -38,6 +38,7 @@ class GroupExtensionPy;
|
||||
class AppExport GroupExtension : public DocumentObjectExtension
|
||||
{
|
||||
EXTENSION_PROPERTY_HEADER_WITH_OVERRIDE(App::GroupExtension);
|
||||
typedef DocumentObjectExtension inherited;
|
||||
|
||||
public:
|
||||
/// Constructor
|
||||
@@ -91,7 +92,7 @@ public:
|
||||
bool isChildOf(const GroupExtension* group, bool recursive = true) const;
|
||||
/** Returns a list of all objects this group does have.
|
||||
*/
|
||||
std::vector<DocumentObject*> getObjects() const;
|
||||
const std::vector<DocumentObject*> &getObjects() const;
|
||||
/** Returns a list of all objects of \a typeId this group does have.
|
||||
*/
|
||||
std::vector<DocumentObject*> getObjectsOfType(const Base::Type& typeId) const;
|
||||
@@ -109,7 +110,12 @@ public:
|
||||
virtual PyObject* getExtensionPyObject(void) override;
|
||||
|
||||
virtual void extensionOnChanged(const Property* p) override;
|
||||
|
||||
|
||||
virtual bool extensionGetSubObject(DocumentObject *&ret, const char *subname,
|
||||
PyObject **pyObj, Base::Matrix4D *mat, bool transform, int depth) const override;
|
||||
|
||||
virtual bool extensionGetSubObjects(std::vector<std::string> &ret, int reason) const override;
|
||||
|
||||
/// Properties
|
||||
PropertyLinkList Group;
|
||||
|
||||
|
||||
@@ -69,7 +69,7 @@ App::OriginFeature *Origin::getOriginFeature( const char *role) const {
|
||||
} else {
|
||||
|
||||
std::stringstream err;
|
||||
err << "Origin \"" << getNameInDocument () << "\" doesn't contain feature with role \""
|
||||
err << "Origin \"" << getFullName () << "\" doesn't contain feature with role \""
|
||||
<< role << '"';
|
||||
throw Base::RuntimeError ( err.str().c_str () );
|
||||
}
|
||||
@@ -81,7 +81,7 @@ App::Line *Origin::getAxis( const char *role ) const {
|
||||
return static_cast<App::Line *> (feat);
|
||||
} else {
|
||||
std::stringstream err;
|
||||
err << "Origin \"" << getNameInDocument () << "\" contains bad Axis object for role \""
|
||||
err << "Origin \"" << getFullName () << "\" contains bad Axis object for role \""
|
||||
<< role << '"';
|
||||
throw Base::RuntimeError ( err.str().c_str () );
|
||||
}
|
||||
@@ -93,7 +93,7 @@ App::Plane *Origin::getPlane( const char *role ) const {
|
||||
return static_cast<App::Plane *> (feat);
|
||||
} else {
|
||||
std::stringstream err;
|
||||
err << "Origin \"" << getNameInDocument () << "\" comtains bad Plane object for role \""
|
||||
err << "Origin \"" << getFullName () << "\" comtains bad Plane object for role \""
|
||||
<< role << '"';
|
||||
throw Base::RuntimeError ( err.str().c_str () );
|
||||
}
|
||||
|
||||
@@ -53,19 +53,42 @@ App::Origin *OriginGroupExtension::getOrigin () const {
|
||||
|
||||
if ( !originObj ) {
|
||||
std::stringstream err;
|
||||
err << "Can't find Origin for \"" << getExtendedObject()->getNameInDocument () << "\"";
|
||||
err << "Can't find Origin for \"" << getExtendedObject()->getFullName () << "\"";
|
||||
throw Base::RuntimeError ( err.str().c_str () );
|
||||
|
||||
} else if (! originObj->isDerivedFrom ( App::Origin::getClassTypeId() ) ) {
|
||||
std::stringstream err;
|
||||
err << "Bad object \"" << originObj->getNameInDocument () << "\"(" << originObj->getTypeId().getName()
|
||||
<< ") linked to the Origin of \"" << getExtendedObject()->getNameInDocument () << "\"";
|
||||
err << "Bad object \"" << originObj->getFullName () << "\"(" << originObj->getTypeId().getName()
|
||||
<< ") linked to the Origin of \"" << getExtendedObject()->getFullName () << "\"";
|
||||
throw Base::RuntimeError ( err.str().c_str () );
|
||||
} else {
|
||||
return static_cast<App::Origin *> ( originObj );
|
||||
}
|
||||
}
|
||||
|
||||
bool OriginGroupExtension::extensionGetSubObject(DocumentObject *&ret, const char *subname,
|
||||
PyObject **pyObj, Base::Matrix4D *mat, bool transform, int depth) const
|
||||
{
|
||||
App::DocumentObject *originObj = Origin.getValue ();
|
||||
const char *dot;
|
||||
if(originObj && originObj->getNameInDocument() &&
|
||||
subname && (dot=strchr(subname,'.')))
|
||||
{
|
||||
bool found;
|
||||
if(subname[0] == '$')
|
||||
found = std::string(subname+1,dot)==originObj->Label.getValue();
|
||||
else
|
||||
found = std::string(subname,dot)==originObj->getNameInDocument();
|
||||
if(found) {
|
||||
if(mat && transform)
|
||||
*mat *= const_cast<OriginGroupExtension*>(this)->placement().getValue().toMatrix();
|
||||
ret = originObj->getSubObject(dot+1,pyObj,mat,true,depth+1);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return GeoFeatureGroupExtension::extensionGetSubObject(ret,subname,pyObj,mat,transform,depth);
|
||||
}
|
||||
|
||||
App::DocumentObject *OriginGroupExtension::getGroupOfObject (const DocumentObject* obj) {
|
||||
|
||||
if(!obj)
|
||||
|
||||
@@ -67,6 +67,9 @@ public:
|
||||
virtual std::vector<DocumentObject*> addObjects(std::vector<DocumentObject*> obj) override;
|
||||
virtual bool hasObject(const DocumentObject* obj, bool recursive = false) const override;
|
||||
|
||||
virtual bool extensionGetSubObject(DocumentObject *&ret, const char *subname, PyObject **pyObj,
|
||||
Base::Matrix4D *mat, bool transform, int depth) const override;
|
||||
|
||||
protected:
|
||||
/// Checks integrity of the Origin
|
||||
virtual App::DocumentObjectExecReturn *extensionExecute () override;
|
||||
|
||||
@@ -26,6 +26,8 @@
|
||||
#ifndef _PreComp_
|
||||
#endif
|
||||
|
||||
#include "OCCError.h"
|
||||
#include "PartPyCXX.h"
|
||||
#include "DatumFeature.h"
|
||||
|
||||
|
||||
@@ -58,6 +60,31 @@ TopoDS_Shape Datum::getShape() const
|
||||
return sh.getShape();
|
||||
}
|
||||
|
||||
App::DocumentObject *Datum::getSubObject(const char *subname,
|
||||
PyObject **pyObj, Base::Matrix4D *pmat, bool transform, int depth) const
|
||||
{
|
||||
// For the sake of simplicity, we don't bother to check for subname, just
|
||||
// return the shape as it is, because a datum object only holds shape with
|
||||
// one single geometry element.
|
||||
(void)subname;
|
||||
(void)depth;
|
||||
|
||||
if(pmat && transform)
|
||||
*pmat *= Placement.getValue().toMatrix();
|
||||
|
||||
if(!pyObj)
|
||||
return const_cast<Datum*>(this);
|
||||
|
||||
Base::PyGILStateLocker lock;
|
||||
PY_TRY {
|
||||
TopoShape ts(getShape().Located(TopLoc_Location()));
|
||||
if(pmat && !ts.isNull())
|
||||
ts.transformShape(*pmat,false,true);
|
||||
*pyObj = Py::new_reference_to(shape2pyshape(ts.getShape()));
|
||||
return const_cast<Datum*>(this);
|
||||
} PY_CATCH_OCC
|
||||
}
|
||||
|
||||
Base::Vector3d Datum::getBasePoint () const {
|
||||
return Placement.getValue().getPosition();
|
||||
}
|
||||
|
||||
@@ -49,11 +49,13 @@ public:
|
||||
virtual const char* getViewProviderName(void) const = 0;
|
||||
|
||||
/// Return a shape including Placement representing the datum feature
|
||||
TopoDS_Shape getShape() const;
|
||||
virtual TopoDS_Shape getShape() const;
|
||||
|
||||
/// Returns a point of the feature it counts as it's base
|
||||
virtual Base::Vector3d getBasePoint () const;
|
||||
|
||||
virtual App::DocumentObject *getSubObject(const char *subname, PyObject **pyObj,
|
||||
Base::Matrix4D *mat, bool transform, int depth) const override;
|
||||
protected:
|
||||
void onDocumentRestored();
|
||||
void handleChangedPropertyName(Base::XMLReader &reader, const char* TypeName, const char* PropName);
|
||||
|
||||
@@ -42,7 +42,8 @@
|
||||
# include <Bnd_Box.hxx>
|
||||
# include <BRepBndLib.hxx>
|
||||
# include <BRepExtrema_DistShapeShape.hxx>
|
||||
|
||||
# include <BRepAdaptor_Curve.hxx>
|
||||
# include <TopoDS.hxx>
|
||||
# include <GProp_GProps.hxx>
|
||||
# include <BRepGProp.hxx>
|
||||
# include <gce_MakeLin.hxx>
|
||||
@@ -59,13 +60,18 @@
|
||||
#include <Base/Stream.h>
|
||||
#include <Base/Placement.h>
|
||||
#include <Base/Rotation.h>
|
||||
#include <App/Application.h>
|
||||
#include <App/FeaturePythonPyImp.h>
|
||||
#include <App/Document.h>
|
||||
|
||||
#include "PartPyCXX.h"
|
||||
#include "PartFeature.h"
|
||||
#include "PartFeaturePy.h"
|
||||
#include "TopoShapePy.h"
|
||||
|
||||
using namespace Part;
|
||||
|
||||
FC_LOG_LEVEL_INIT("Part",true,true);
|
||||
|
||||
PROPERTY_SOURCE(Part::Feature, App::GeoFeature)
|
||||
|
||||
@@ -112,20 +118,74 @@ PyObject *Feature::getPyObject(void)
|
||||
return Py::new_reference_to(PythonObject);
|
||||
}
|
||||
|
||||
std::vector<PyObject *> Feature::getPySubObjects(const std::vector<std::string>& NameVec) const
|
||||
App::DocumentObject *Feature::getSubObject(const char *subname,
|
||||
PyObject **pyObj, Base::Matrix4D *pmat, bool transform, int depth) const
|
||||
{
|
||||
try {
|
||||
std::vector<PyObject *> temp;
|
||||
for (std::vector<std::string>::const_iterator it=NameVec.begin();it!=NameVec.end();++it) {
|
||||
PyObject *obj = Shape.getShape().getPySubShape((*it).c_str());
|
||||
if (obj)
|
||||
temp.push_back(obj);
|
||||
}
|
||||
return temp;
|
||||
// having '.' inside subname means it is referencing some children object,
|
||||
// instead of any sub-element from ourself
|
||||
if(subname && !Data::ComplexGeoData::isMappedElement(subname) && strchr(subname,'.'))
|
||||
return App::DocumentObject::getSubObject(subname,pyObj,pmat,transform,depth);
|
||||
|
||||
if(pmat && transform)
|
||||
*pmat *= Placement.getValue().toMatrix();
|
||||
|
||||
if(!pyObj) {
|
||||
#if 0
|
||||
if(subname==0 || *subname==0 || Shape.getShape().hasSubShape(subname))
|
||||
return const_cast<Feature*>(this);
|
||||
return nullptr;
|
||||
#else
|
||||
// TopoShape::hasSubShape is kind of slow, let's cut outself some slack here.
|
||||
return const_cast<Feature*>(this);
|
||||
#endif
|
||||
}
|
||||
catch (Standard_Failure&) {
|
||||
//throw Py::ValueError(e.GetMessageString());
|
||||
return std::vector<PyObject *>();
|
||||
|
||||
try {
|
||||
TopoShape ts(Shape.getShape());
|
||||
bool doTransform = pmat && *pmat!=ts.getTransform();
|
||||
if(doTransform) {
|
||||
ts.setShape(ts.getShape().Located(TopLoc_Location()),false);
|
||||
ts.initCache(1);
|
||||
}
|
||||
if(subname && *subname && !ts.isNull())
|
||||
ts = ts.getSubTopoShape(subname,true);
|
||||
if(doTransform && !ts.isNull()) {
|
||||
static int sCopy = -1;
|
||||
if(sCopy<0) {
|
||||
ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath(
|
||||
"User parameter:BaseApp/Preferences/Mod/Part/General");
|
||||
sCopy = hGrp->GetBool("CopySubShape",false)?1:0;
|
||||
}
|
||||
bool copy = sCopy?true:false;
|
||||
if(!copy) {
|
||||
// Work around OCC bug on transforming circular edge with an
|
||||
// offseted surface. The bug probably affect other shape type,
|
||||
// too.
|
||||
TopExp_Explorer exp(ts.getShape(),TopAbs_EDGE);
|
||||
if(exp.More()) {
|
||||
auto edge = TopoDS::Edge(exp.Current());
|
||||
exp.Next();
|
||||
if(!exp.More()) {
|
||||
BRepAdaptor_Curve curve(edge);
|
||||
copy = curve.GetType() == GeomAbs_Circle;
|
||||
}
|
||||
}
|
||||
}
|
||||
ts.transformShape(*pmat,copy,true);
|
||||
}
|
||||
*pyObj = Py::new_reference_to(shape2pyshape(ts));
|
||||
return const_cast<Feature*>(this);
|
||||
}catch(Standard_Failure &e) {
|
||||
std::ostringstream str;
|
||||
Standard_CString msg = e.GetMessageString();
|
||||
str << typeid(e).name() << " ";
|
||||
if (msg) {str << msg;}
|
||||
else {str << "No OCCT Exception Message";}
|
||||
str << ": " << getFullName();
|
||||
if (subname)
|
||||
str << '.' << subname;
|
||||
FC_ERR(str.str());
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -31,6 +31,8 @@
|
||||
#include <App/PropertyGeo.h>
|
||||
// includes for findAllFacesCutBy()
|
||||
#include <TopoDS_Face.hxx>
|
||||
#include <BRep_Builder.hxx>
|
||||
#include <TopoDS_Compound.hxx>
|
||||
class gp_Dir;
|
||||
|
||||
class BRepBuilderAPI_MakeShape;
|
||||
@@ -63,10 +65,12 @@ public:
|
||||
virtual const App::PropertyComplexGeoData* getPropertyOfGeometry() const;
|
||||
|
||||
virtual PyObject* getPyObject(void);
|
||||
virtual std::vector<PyObject *> getPySubObjects(const std::vector<std::string>&) const;
|
||||
|
||||
TopLoc_Location getLocation() const;
|
||||
|
||||
|
||||
virtual DocumentObject *getSubObject(const char *subname, PyObject **pyObj,
|
||||
Base::Matrix4D *mat, bool transform, int depth) const override;
|
||||
|
||||
protected:
|
||||
/// recompute only this object
|
||||
virtual App::DocumentObjectExecReturn *recompute(void);
|
||||
|
||||
@@ -497,3 +497,29 @@ PyObject *Body::getPyObject(void)
|
||||
}
|
||||
return Py::new_reference_to(PythonObject);
|
||||
}
|
||||
|
||||
std::vector<std::string> Body::getSubObjects(int reason) const {
|
||||
if(reason==GS_SELECT && !showTip)
|
||||
return Part::BodyBase::getSubObjects(reason);
|
||||
return {};
|
||||
}
|
||||
|
||||
App::DocumentObject *Body::getSubObject(const char *subname,
|
||||
PyObject **pyObj, Base::Matrix4D *pmat, bool transform, int depth) const
|
||||
{
|
||||
if(!pyObj || showTip ||
|
||||
(subname && !Data::ComplexGeoData::isMappedElement(subname) && strchr(subname,'.')))
|
||||
return Part::BodyBase::getSubObject(subname,pyObj,pmat,transform,depth);
|
||||
|
||||
// We return the shape only if there are feature visibile inside
|
||||
for(auto obj : Group.getValues()) {
|
||||
if(obj->Visibility.getValue() &&
|
||||
obj->isDerivedFrom(PartDesign::Feature::getClassTypeId()))
|
||||
{
|
||||
return Part::BodyBase::getSubObject(subname,pyObj,pmat,transform,depth);
|
||||
}
|
||||
}
|
||||
if(pmat && transform)
|
||||
*pmat *= Placement.getValue().toMatrix();
|
||||
return const_cast<Body*>(this);
|
||||
}
|
||||
|
||||
@@ -120,6 +120,13 @@ public:
|
||||
|
||||
PyObject *getPyObject(void) override;
|
||||
|
||||
virtual std::vector<std::string> getSubObjects(int reason=0) const override;
|
||||
virtual App::DocumentObject *getSubObject(const char *subname,
|
||||
PyObject **pyObj, Base::Matrix4D *pmat, bool transform, int depth) const override;
|
||||
|
||||
void setShowTip(bool enable) {
|
||||
showTip = enable;
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void onSettingDocument() override;
|
||||
@@ -146,6 +153,7 @@ protected:
|
||||
|
||||
private:
|
||||
boost::signals2::scoped_connection connection;
|
||||
bool showTip = false;
|
||||
};
|
||||
|
||||
} //namespace PartDesign
|
||||
|
||||
@@ -28,6 +28,8 @@
|
||||
# include <gp_Pln.hxx>
|
||||
#endif
|
||||
|
||||
#include <Mod/Part/App/PartPyCXX.h>
|
||||
#include <Mod/Part/App/OCCError.h>
|
||||
#include "DatumCS.h"
|
||||
|
||||
using namespace PartDesign;
|
||||
@@ -77,3 +79,32 @@ Base::Vector3d CoordinateSystem::getZAxis()
|
||||
rot.multVec(Base::Vector3d(0,0,1), normal);
|
||||
return normal;
|
||||
}
|
||||
|
||||
App::DocumentObject *CoordinateSystem::getSubObject(const char *subname,
|
||||
PyObject **pyObj, Base::Matrix4D *pmat, bool transform, int) const
|
||||
{
|
||||
if(pmat && transform)
|
||||
*pmat *= Placement.getValue().toMatrix();
|
||||
|
||||
if(!pyObj)
|
||||
return const_cast<CoordinateSystem*>(this);
|
||||
|
||||
gp_Dir dir(0,0,1);
|
||||
if(subname) {
|
||||
if(strcmp(subname,"X")==0)
|
||||
dir = gp_Dir(1,0,0);
|
||||
else if(strcmp(subname,"Y")==0)
|
||||
dir = gp_Dir(0,1,0);
|
||||
}
|
||||
|
||||
Base::PyGILStateLocker lock;
|
||||
PY_TRY {
|
||||
BRepBuilderAPI_MakeFace builder(gp_Pln(gp_Pnt(0,0,0), dir));
|
||||
Part::TopoShape ts(builder.Shape());
|
||||
if(pmat)
|
||||
ts.transformShape(*pmat,false,true);
|
||||
*pyObj = Py::new_reference_to(Part::shape2pyshape(ts));
|
||||
return const_cast<CoordinateSystem*>(this);
|
||||
} PY_CATCH_OCC
|
||||
}
|
||||
|
||||
|
||||
@@ -44,6 +44,9 @@ public:
|
||||
Base::Vector3d getXAxis();
|
||||
Base::Vector3d getYAxis();
|
||||
Base::Vector3d getZAxis();
|
||||
|
||||
virtual App::DocumentObject *getSubObject(const char *subname,
|
||||
PyObject **pyObj, Base::Matrix4D *pmat, bool transform, int depth) const override;
|
||||
};
|
||||
|
||||
} //namespace PartDesign
|
||||
|
||||
Reference in New Issue
Block a user