App/Toponaming: import SubObjectT dependencies for SubShapeBinder

* Added SubObjectT methods normalize(), normalized(), hasSubObject() and hasSubElement()
 * Updated DocumentObject::getSubObjectList()
 * Applied modifications to make the code compile
This commit is contained in:
Zheng, Lei
2024-04-06 09:59:23 +02:00
committed by Chris Hennes
parent db22476450
commit 138417c2a2
4 changed files with 188 additions and 5 deletions

View File

@@ -40,6 +40,7 @@
#include "DocumentObjectExtension.h"
#include "DocumentObjectGroup.h"
#include "GeoFeatureGroupExtension.h"
#include "Link.h"
#include "ObjectIdentifier.h"
#include "PropertyExpressionEngine.h"
#include "PropertyLinks.h"
@@ -887,19 +888,69 @@ DocumentObject *DocumentObject::getSubObject(const char *subname,
return ret;
}
std::vector<DocumentObject*> DocumentObject::getSubObjectList(const char *subname) const {
std::vector<DocumentObject*>
DocumentObject::getSubObjectList(const char *subname,
std::vector<int> *sublist,
bool flatten) const
{
std::vector<DocumentObject*> res;
res.push_back(const_cast<DocumentObject*>(this));
if (sublist) sublist->push_back(0);
if(!subname || !subname[0])
return res;
std::string sub(subname);
auto element = Data::findElementName(subname);
std::string sub(subname,element-subname);
App::DocumentObject *container = nullptr;
bool lastChild = false;
if (flatten) {
auto linked = getLinkedObject();
if (linked->getExtensionByType<App::GeoFeatureGroupExtension>(true))
container = const_cast<DocumentObject*>(this);
else if (auto grp = App::GeoFeatureGroupExtension::getGroupOfObject(linked)) {
container = grp;
lastChild = true;
}
}
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->isAttachedToDocument())
break;
continue;
if (flatten) {
auto linked = sobj->getLinkedObject();
if (container) {
auto grp = App::GeoFeatureGroupExtension::getGroupOfObject(linked);
if (grp != container)
container = nullptr;
else {
if (lastChild && res.size()) {
res.pop_back();
if (sublist)
sublist->pop_back();
}
lastChild = true;
}
}
if (linked->getExtensionByType<App::GeoFeatureGroupExtension>(true)) {
container = linked;
lastChild = false;
}
else if (linked != sobj || sobj->hasChildElement()) {
// Check for Link or LinkGroup
container = nullptr;
}
else if (auto ext = sobj->getExtensionByType<LinkBaseExtension>(true)) {
// check for Link array
if (ext->getElementCountValue())
container = nullptr;
}
}
res.push_back(sobj);
if (sublist)
sublist->push_back(pos+1);
sub[pos+1] = c;
}
return res;

View File

@@ -377,8 +377,18 @@ public:
virtual DocumentObject *getSubObject(const char *subname, PyObject **pyObj=nullptr,
Base::Matrix4D *mat=nullptr, 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;
/** Return a list of objects referenced by a given subname including this object
* @param subname: the sub name path
* @param subsizes: optional sub name sizes for each returned object, that is,
* ret[i] = getSubObject(std::string(subname, subsizes[i]).c_str());
* @param flatten: whether to flatten the object hierarchies that belong to
* the same geo feature group, e.g. (Part.Fusion.Box -> Part.Box)
*
* @return Return a list of object along the path.
*/
std::vector<DocumentObject*> getSubObjectList(const char *subname,
std::vector<int> *subsizes = nullptr,
bool flatten = false) const;
/// reason of calling getSubObjects()
enum GSReason {

View File

@@ -30,6 +30,7 @@
#include "Document.h"
#include "DocumentObserver.h"
#include "GeoFeature.h"
#include "Link.h"
using namespace App;
namespace sp = std::placeholders;
@@ -341,6 +342,79 @@ bool SubObjectT::operator==(const SubObjectT &other) const {
&& subname == other.subname;
}
bool SubObjectT::normalize(NormalizeOptions options)
{
bool noElement = options.testFlag(NormalizeOption::NoElement);
bool flatten = !options.testFlag(NormalizeOption::NoFlatten);
bool keepSub = options.testFlag(NormalizeOption::KeepSubName);
bool convertIndex = options.testFlag(NormalizeOption::ConvertIndex);
std::ostringstream ss;
std::vector<int> subs;
auto obj = getObject();
if(!obj)
return false;
auto objs = obj->getSubObjectList(subname.c_str(), &subs, flatten);
if (objs.empty())
return false;
for (unsigned i=1; i<objs.size(); ++i) {
// Keep digit only subname, as it maybe an index to an array, which does
// not expand its elements as objects.
const char *end = subname.c_str() + subs[i];
const char *sub = end - 2;
for(;;--sub) {
if (sub < subname.c_str()) {
sub = subname.c_str();
break;
}
if (*sub == '.') {
++sub;
break;
}
}
bool _keepSub;
if (!std::isdigit(sub[0]))
_keepSub = keepSub;
else if (!convertIndex)
_keepSub = true;
else {
_keepSub = false;
if (auto ext = objs[i-1]->getExtensionByType<LinkBaseExtension>(true)) {
if (ext->getElementCountValue() && !ext->getShowElementValue()) {
// if the parent is a collapsed link array element, then we
// have to keep the index no matter what, because there is
// no sub-object corresponding to an array element.
_keepSub = true;
}
}
}
if (_keepSub)
ss << std::string(sub, end);
else
ss << objs[i]->getNameInDocument() << ".";
}
if (objs.size() > 1 && objs.front()->getSubObject(ss.str().c_str()) != objs.back()) {
// something went wrong
return false;
}
if (!noElement)
ss << getOldElementName();
std::string sub = ss.str();
if (objs.front() != obj || subname != sub) {
*this = objs.front();
subname = std::move(sub);
return true;
}
return false;
}
SubObjectT App::SubObjectT::normalized(NormalizeOptions options) const
{
SubObjectT res(*this);
res.normalize(options);
return res;
}
void SubObjectT::setSubName(const char *s) {
subname = s?s:"";
}
@@ -357,6 +431,15 @@ const char *SubObjectT::getElementName() const {
return Data::findElementName(subname.c_str());
}
bool SubObjectT::hasSubObject() const {
return Data::findElementName(subname.c_str()) != subname.c_str();
}
bool SubObjectT::hasSubElement() const {
auto element = getElementName();
return element && element[0];
}
std::string SubObjectT::getNewElementName() const {
std::pair<std::string, std::string> element;
auto obj = getObject();

View File

@@ -25,6 +25,7 @@
#define APP_DOCUMENTOBSERVER_H
#include <Base/BaseClass.h>
#include <Base/Bitmask.h>
#include <boost_signals2.hpp>
#include <memory>
#include <set>
@@ -226,6 +227,12 @@ public:
/// Return the sub-element (Face, Edge, etc) of the subname path
const char *getElementName() const;
/// Check if there is any sub object reference
bool hasSubObject() const;
/// Check if there is any sub element reference
bool hasSubElement() const;
/// Return the new style sub-element name
std::string getNewElementName() const;
@@ -244,6 +251,36 @@ public:
std::string getSubObjectPython(bool force=true) const;
/// Options used by normalize()
enum class NormalizeOption {
/// Do not include sub-element reference in the output path
NoElement = 0x01,
/** Do not flatten the output path. If not specified, the output path
* will be flatten to exclude intermediate objects that belong to the
* same geo feature group before resolving. For example,
* Part.Fusion.Box. -> Part.Box.
*/
NoFlatten = 0x02,
/** Do not change the sub-object component inside the path. Each
* component of the subname object path can be either the object
* internal name, the label of the object if starts with '$', or an
* integer index. If this option is not specified, each component will
* be converted to object internal name, except for integer index.
*/
KeepSubName = 0x04,
/** Convert integer index in the path to sub-object internal name */
ConvertIndex = 0x08,
};
using NormalizeOptions = Base::Flags<NormalizeOption>;
/** Normalize the subname path to use only the object internal name and old style element name
* @return Return whether the subname has been changed
*/
bool normalize(NormalizeOptions options = NormalizeOption::NoElement);
/// Return a normalize copy of itself
SubObjectT normalized(NormalizeOptions options = NormalizeOption::NoElement) const;
private:
std::string subname;
};
@@ -558,4 +595,6 @@ private:
} //namespace App
ENABLE_BITMASK_OPERATORS(App::SubObjectT::NormalizeOption)
#endif // APP_DOCUMENTOBSERVER_H