Toponaming: bring in missing code fragments in App

This commit is contained in:
bgbsww
2024-05-11 22:09:35 -04:00
parent dbaaa95974
commit 71892a1d72
8 changed files with 147 additions and 108 deletions

View File

@@ -53,6 +53,12 @@ namespace Data
{
//struct MappedChildElements;
/// Option for App::GeoFeature::searchElementCache()
enum class SearchOptions {
/// Whether to compare shape geometry
CheckGeometry = 1,
SingleResult = 2,
};
/** Segments
* Sub-element type of the ComplexGeoData type

View File

@@ -30,6 +30,7 @@
#include "GeoFeature.h"
#include "GeoFeatureGroupExtension.h"
#include "ElementNamingUtils.h"
#include "Link.h"
using namespace App;
@@ -141,6 +142,10 @@ DocumentObject *GeoFeature::resolveElement(DocumentObject *obj, const char *subn
ElementNameType type, const DocumentObject *filter,
const char **_element, GeoFeature **geoFeature)
{
#ifdef FC_USE_TNP_FIX
elementName.first.clear();
elementName.second.clear();
#endif
if(!obj || !obj->isAttachedToDocument())
return nullptr;
if(!subname)
@@ -148,13 +153,16 @@ DocumentObject *GeoFeature::resolveElement(DocumentObject *obj, const char *subn
const char *element = Data::findElementName(subname);
if(_element) *_element = element;
#ifdef FC_USE_TNP_FIX
elementName.first.clear();
elementName.second.clear();
auto sobj = obj->getSubObject(std::string(subname, element).c_str());
if(!sobj)
return nullptr;
auto linked = sobj->getLinkedObject(true);
auto geo = Base::freecad_dynamic_cast<GeoFeature>(linked);
if(!geo && linked) {
auto ext = linked->getExtensionByType<LinkBaseExtension>(true);
if(ext)
geo = Base::freecad_dynamic_cast<GeoFeature>(ext->getTrueLinkedObject(true));
}
#else
auto sobj = obj->getSubObject(subname);
if(!sobj)
@@ -202,43 +210,73 @@ void GeoFeature::setMaterialAppearance(const App::Material& material)
}
#ifdef FC_USE_TNP_FIX
bool GeoFeature::hasMissingElement(const char *subname) {
bool GeoFeature::hasMissingElement(const char* subname)
{
return Data::hasMissingElement(subname);
if(!subname)
if (!subname) {
return false;
auto dot = strrchr(subname,'.');
if(!dot)
return subname[0]=='?';
return dot[1]=='?';
}
auto dot = strrchr(subname, '.');
if (!dot) {
return subname[0] == '?';
}
return dot[1] == '?';
}
void GeoFeature::updateElementReference() {
void GeoFeature::updateElementReference()
{
auto prop = getPropertyOfGeometry();
if(!prop) return;
if (!prop) {
return;
}
auto geo = prop->getComplexData();
if(!geo) return;
if (!geo) {
return;
}
bool reset = false;
PropertyLinkBase::updateElementReferences(this,reset);
PropertyLinkBase::updateElementReferences(this, reset);
}
void GeoFeature::onChanged(const Property *prop) {
if(prop==getPropertyOfGeometry()) {
if(getDocument() && !getDocument()->testStatus(Document::Restoring)
&& !getDocument()->isPerformingTransaction())
{
void GeoFeature::onChanged(const Property* prop)
{
if (prop == getPropertyOfGeometry()) {
if (getDocument() && !getDocument()->testStatus(Document::Restoring)
&& !getDocument()->isPerformingTransaction()) {
updateElementReference();
}
}
DocumentObject::onChanged(prop);
}
const std::vector<std::string>& GeoFeature::searchElementCache(const std::string& element,
Data::SearchOptions options,
double tol,
double atol) const
{
static std::vector<std::string> none;
(void)element;
(void)options;
(void)tol;
(void)atol;
return none;
}
std::vector<Data::IndexedName>
GeoFeature::getHigherElements(const char *element, bool silent) const
const std::vector<const char*>& GeoFeature::getElementTypes(bool /*all*/) const
{
static std::vector<const char*> nil;
auto prop = getPropertyOfGeometry();
if (!prop) {
return nil;
}
return prop->getComplexData()->getElementTypes();
}
std::vector<Data::IndexedName> GeoFeature::getHigherElements(const char* element, bool silent) const
{
auto prop = getPropertyOfGeometry();
if (!prop)
if (!prop) {
return {};
}
return prop->getComplexData()->getHigherElements(element, silent);
}
#endif

View File

@@ -28,6 +28,7 @@
#include "PropertyGeo.h"
#include "MappedElement.h"
#include "Material.h"
#include "ComplexGeoData.h"
namespace App
{
@@ -141,12 +142,34 @@ public:
*/
virtual void setMaterialAppearance(const App::Material& material);
#ifdef FC_USE_TNP_FIX
/** Search sub element using internal cached geometry
*
* @param element: element name
* @param options: search options
* @param tol: coordinate tolerance
* @param atol: angle tolerance
*
* @return Returns a list of found element reference to the new geometry.
* The returned value will be invalidated when the geometry is changed.
*
* Before changing the property of geometry, GeoFeature will internally
* make a snapshot of all referenced element geometry. After change, user
* code may call this function to search for the new element name that
* reference to the same geometry of the old element.
*/
virtual const std::vector<std::string>& searchElementCache(const std::string &element,
Data::SearchOptions options = Data::SearchOptions::CheckGeometry,
double tol = 1e-7,
double atol = 1e-10) const;
static bool hasMissingElement(const char *subname);
/// Return the object that owns the shape that contains the give element name
virtual DocumentObject *getElementOwner(const Data::MappedName & /*name*/) const
{return nullptr;}
virtual const std::vector<const char *>& getElementTypes(bool all=true) const;
/// Return the higher level element names of the given element
virtual std::vector<Data::IndexedName> getHigherElements(const char *name, bool silent=false) const;

View File

@@ -1420,8 +1420,22 @@ void LinkBaseExtension::checkGeoElementMap(const App::DocumentObject *obj,
!PyObject_TypeCheck(*pyObj, &Data::ComplexGeoDataPy::Type))
return;
// auto geoData = static_cast<Data::ComplexGeoDataPy*>(*pyObj)->getComplexGeoDataPtr();
// geoData->reTagElementMap(obj->getID(),obj->getDocument()->Hasher,postfix);
// auto geoData = static_cast<Data::ComplexGeoDataPy*>(*pyObj)->getComplexGeoDataPtr();
// geoData->reTagElementMap(obj->getID(),obj->getDocument()->Hasher,postfix);
auto geoData = static_cast<Data::ComplexGeoDataPy*>(*pyObj)->getComplexGeoDataPtr();
std::string _postfix;
if (linked && obj && linked->getDocument() != obj->getDocument()) {
_postfix = Data::POSTFIX_EXTERNAL_TAG;
if (postfix) {
if (!boost::starts_with(postfix, Data::ComplexGeoData::elementMapPrefix()))
_postfix += Data::ComplexGeoData::elementMapPrefix();
_postfix += postfix;
}
postfix = _postfix.c_str();
}
geoData->reTagElementMap(obj->getID(),obj->getDocument()->getStringHasher(),postfix);
}
void LinkBaseExtension::onExtendedUnsetupObject() {

View File

@@ -218,6 +218,19 @@ static std::string propertyName(const Property *prop) {
return prop->getFullName();
}
const std::unordered_set<PropertyLinkBase*>&
PropertyLinkBase::getElementReferences(DocumentObject* feature)
{
static std::unordered_set<PropertyLinkBase*> none;
auto it = _ElementRefMap.find(feature);
if (it == _ElementRefMap.end()) {
return none;
}
return it->second;
}
void PropertyLinkBase::updateElementReferences(DocumentObject *feature, bool reverse) {
#ifdef FC_USE_TNP_FIX
if (!feature || !feature->getNameInDocument()) {
@@ -394,15 +407,15 @@ bool PropertyLinkBase::_updateElementReference(DocumentObject *feature,
}
}
bool missing = Data::hasMissingElement(elementName.second.c_str());
bool missing = GeoFeature::hasMissingElement(elementName.second.c_str());
if (feature == geo && (missing || reverse)) {
// If the referenced element is missing, or we are generating element
// map for the first time, or we are re-generating the element map due
// to version change, i.e. 'reverse', try search by geometry first
const char* oldElement = Data::findElementName(shadow.second.c_str());
if (!Data::hasMissingElement(oldElement)) {
// const auto& names = geo->searchElementCache(oldElement);
std::vector<std::string> names; // searchElementCache isn't implemented.
const auto& names = geo->searchElementCache(oldElement);
// std::vector<std::string> names; // searchElementCache isn't implemented.
if (names.size()) {
missing = false;
std::string newsub(subname, strlen(subname) - strlen(element));
@@ -502,11 +515,18 @@ bool PropertyLinkBase::_updateElementReference(DocumentObject *feature,
}
std::pair<DocumentObject*, std::string>
PropertyLinkBase::tryReplaceLink(const PropertyContainer *owner, DocumentObject *obj,
const DocumentObject *parent, DocumentObject *oldObj, DocumentObject *newObj, const char *subname)
PropertyLinkBase::tryReplaceLink(const PropertyContainer* owner,
DocumentObject* obj,
const DocumentObject* parent,
DocumentObject* oldObj,
DocumentObject* newObj,
const char* subname)
{
std::pair<DocumentObject*, std::string> res;
res.first = 0;
if (!obj) {
return res;
}
if (oldObj == obj) {
if (owner == parent) {
@@ -536,6 +556,9 @@ PropertyLinkBase::tryReplaceLink(const PropertyContainer *owner, DocumentObject
for (auto pos = sub.find('.'); pos != std::string::npos; pos = sub.find('.', pos)) {
++pos;
char c = sub[pos];
if (c == '.') {
continue;
}
sub[pos] = 0;
auto sobj = obj->getSubObject(sub.c_str());
sub[pos] = c;
@@ -577,6 +600,8 @@ PropertyLinkBase::tryReplaceLinkSubs(const PropertyContainer *owner,
{
std::pair<DocumentObject*,std::vector<std::string> > res;
res.first = 0;
if (!obj)
return res;
auto r = tryReplaceLink(owner,obj,parent,oldObj,newObj);
if(r.first) {
@@ -1545,8 +1570,12 @@ std::string PropertyLinkBase::tryImportSubName(const App::DocumentObject *obj, c
#define ATTR_SHADOW "shadow"
#define ATTR_MAPPED "mapped"
#ifdef FC_USE_TNP_FIX
#define IGNORE_SHADOW false
#else
// We do not have topo naming yet, ignore shadow sub for now
#define IGNORE_SHADOW true
#endif
void PropertyLinkSub::Save (Base::Writer &writer) const
{

View File

@@ -28,6 +28,9 @@
#include <map>
#include <string>
#include <vector>
#include <unordered_set>
#include <unordered_map>
#include "Property.h"
namespace Base {
@@ -38,6 +41,8 @@ namespace App
{
class DocumentObject;
class Document;
class GeoFeature;
class SubObjectT;
class DocInfo;
using DocInfoPtr = std::shared_ptr<DocInfo>;
@@ -262,7 +267,7 @@ public:
/// Helper function to return a map of linked object and its subname references
void getLinkedElements(std::map<App::DocumentObject*, std::vector<std::string> > &elements,
bool newStyle=true, bool all=true) const
bool newStyle=true, bool all=false) const
{
std::vector<App::DocumentObject*> ret;
std::vector<std::string> subs;
@@ -275,7 +280,7 @@ public:
/// Helper function to return a map of linked object and its subname references
std::map<App::DocumentObject*, std::vector<std::string> >
linkedElements(bool newStyle=true, bool all=true) const
linkedElements(bool newStyle=true, bool all=false) const
{
std::map<App::DocumentObject*, std::vector<std::string> > ret;
getLinkedElements(ret,newStyle,all);
@@ -346,6 +351,8 @@ public:
/// Update all element references in all link properties of \a feature
static void updateElementReferences(DocumentObject *feature, bool reverse=false);
/// Obtain link properties that contain element references to a given object
static const std::unordered_set<PropertyLinkBase*>& getElementReferences(DocumentObject *);
/** Helper function for update individual element reference
*

View File

@@ -259,84 +259,6 @@ std::vector<TopoShape> DressUp::getFaces(const TopoShape& shape)
return ret;
}
std::vector<TopoShape> DressUp::getContinuousEdges(const TopoShape &shape) {
std::vector<TopoShape> ret;
std::unordered_set<TopoDS_Shape, Part::ShapeHasher, Part::ShapeHasher> shapeSet;
auto addEdge = [&](const TopoDS_Shape &subshape, const std::string &ref) {
if (!shapeSet.insert(subshape).second)
return;
auto faces = shape.findAncestorsShapes(subshape, TopAbs_FACE);
if(faces.size() != 2) {
FC_WARN(getFullName() << ": skip edge "
<< ref << " with less two attaching faces");
return;
}
const TopoDS_Shape& face1 = faces.front();
const TopoDS_Shape& face2 = faces.back();
GeomAbs_Shape cont = BRep_Tool::Continuity(TopoDS::Edge(subshape),
TopoDS::Face(face1),
TopoDS::Face(face2));
if (cont != GeomAbs_C0) {
FC_WARN(getFullName() << ": skip edge "
<< ref << " that is not C0 continuous");
return;
}
ret.push_back(subshape);
};
for(const auto &v : Base.getShadowSubs()) {
TopoDS_Shape subshape;
const auto &ref = v.first.size()?v.first:v.second;
subshape = shape.getSubShape(ref.c_str(), true);
if(subshape.IsNull())
FC_THROWM(Base::CADKernelError, "Invalid edge link: " << v.second);
if (subshape.ShapeType() == TopAbs_EDGE)
addEdge(subshape, ref);
else if(subshape.ShapeType() == TopAbs_FACE || subshape.ShapeType() == TopAbs_WIRE) {
for(TopExp_Explorer exp(subshape,TopAbs_EDGE);exp.More();exp.Next())
addEdge(exp.Current(), std::string());
} else
FC_WARN(getFullName() << ": skip invalid shape '"
<< ref << "' with type " << TopoShape::shapeName(subshape.ShapeType()));
}
return ret;
}
std::vector<TopoShape> DressUp::getFaces(const TopoShape &shape) {
std::vector<TopoShape> ret;
const auto &vals = Base.getSubValues();
const auto &subs = Base.getShadowSubs();
size_t i=0;
for(auto &val : vals) {
if(!boost::starts_with(val,"Face"))
continue;
auto &sub = subs[i++];
auto &ref = sub.first.size()?sub.first:val;
TopoShape subshape;
try {
subshape = shape.getSubTopoShape(ref.c_str());
}catch(...)
{
}
if(subshape.isNull()) {
FC_ERR(getFullName() << ": invalid face reference '" << ref << "'");
throw Part::NullShapeException("Invalid Invalid face link");
}
if(subshape.shapeType() != TopAbs_FACE) {
FC_WARN(getFullName() << ": skip invalid shape '"
<< ref << "' with type " << subshape.shapeName());
continue;
}
ret.push_back(subshape);
}
return ret;
}
void DressUp::onChanged(const App::Property* prop)
{
// the BaseFeature property should track the Base and vice-versa as long as

View File

@@ -1362,4 +1362,4 @@ class TestTopologicalNamingProblem(unittest.TestCase):
def tearDown(self):
""" Close our test document """
# App.closeDocument("PartDesignTestTNP")
App.closeDocument("PartDesignTestTNP")