Toposhape/Part: Transfer in PropoertyTopoShape and related
This commit is contained in:
@@ -189,6 +189,17 @@ const std::string &ComplexGeoData::elementMapPrefix() {
|
||||
return prefix;
|
||||
}
|
||||
|
||||
std::string ComplexGeoData::getElementMapVersion() const {
|
||||
return "4";
|
||||
}
|
||||
|
||||
bool ComplexGeoData::checkElementMapVersion(const char * ver) const
|
||||
{
|
||||
return !boost::equals(ver, "3")
|
||||
&& !boost::equals(ver, "4")
|
||||
&& !boost::starts_with(ver, "3.");
|
||||
}
|
||||
|
||||
size_t ComplexGeoData::getElementMapSize(bool flush) const
|
||||
{
|
||||
if (flush) {
|
||||
@@ -653,5 +664,18 @@ void ComplexGeoData::beforeSave() const
|
||||
}
|
||||
}
|
||||
|
||||
void ComplexGeoData::hashChildMaps()
|
||||
{
|
||||
flushElementMap();
|
||||
if (_elementMap)
|
||||
_elementMap->hashChildMaps(Tag);
|
||||
}
|
||||
|
||||
bool ComplexGeoData::hasChildElementMap() const
|
||||
{
|
||||
flushElementMap();
|
||||
return _elementMap && _elementMap->hasChildElementMap();
|
||||
}
|
||||
|
||||
|
||||
// NOLINTEND(cppcoreguidelines-pro-bounds-pointer-arithmetic)
|
||||
|
||||
@@ -242,6 +242,12 @@ public:
|
||||
std::vector<std::pair<MappedName, ElementIDRefs> >
|
||||
getElementMappedNames(const IndexedName & element, bool needUnmapped=false) const;
|
||||
|
||||
/// Hash the child element map postfixes to shorten element name from hierarchical maps
|
||||
void hashChildMaps();
|
||||
|
||||
/// Check if there is child element map
|
||||
bool hasChildElementMap() const;
|
||||
|
||||
/// Append the Tag (if and only if it is non zero) into the element map
|
||||
virtual void reTagElementMap(long tag,
|
||||
App::StringHasherRef hasher,
|
||||
@@ -289,6 +295,12 @@ public:
|
||||
/// Get the current element map size
|
||||
size_t getElementMapSize(bool flush=true) const;
|
||||
|
||||
/// Return the current element map version
|
||||
virtual std::string getElementMapVersion() const;
|
||||
|
||||
/// Return true to signal element map version change
|
||||
virtual bool checkElementMapVersion(const char * ver) const;
|
||||
|
||||
/// Check if the given sub-name only contains an element name
|
||||
static bool isElementName(const char *subName) {
|
||||
return (subName != nullptr) && (*subName != 0) && findElementName(subName)==subName;
|
||||
|
||||
@@ -1244,6 +1244,40 @@ PropertyComplexGeoData::PropertyComplexGeoData() = default;
|
||||
|
||||
PropertyComplexGeoData::~PropertyComplexGeoData() = default;
|
||||
|
||||
std::string PropertyComplexGeoData::getElementMapVersion(bool) const {
|
||||
auto data = getComplexData();
|
||||
if(!data)
|
||||
return std::string();
|
||||
auto owner = Base::freecad_dynamic_cast<DocumentObject>(getContainer());
|
||||
std::ostringstream ss;
|
||||
if(owner && owner->getDocument()
|
||||
&& owner->getDocument()->getStringHasher()==data->Hasher)
|
||||
ss << "1.";
|
||||
else
|
||||
ss << "0.";
|
||||
ss << data->getElementMapVersion();
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
bool PropertyComplexGeoData::checkElementMapVersion(const char * ver) const
|
||||
{
|
||||
auto data = getComplexData();
|
||||
if(!data)
|
||||
return false;
|
||||
auto owner = Base::freecad_dynamic_cast<DocumentObject>(getContainer());
|
||||
std::ostringstream ss;
|
||||
const char *prefix;
|
||||
if(owner && owner->getDocument()
|
||||
&& owner->getDocument()->getStringHasher() == data->Hasher)
|
||||
prefix = "1.";
|
||||
else
|
||||
prefix = "0.";
|
||||
if (!boost::starts_with(ver, prefix))
|
||||
return true;
|
||||
return data->checkElementMapVersion(ver+2);
|
||||
}
|
||||
|
||||
|
||||
void PropertyComplexGeoData::afterRestore()
|
||||
{
|
||||
auto data = getComplexData();
|
||||
|
||||
@@ -541,6 +541,16 @@ public:
|
||||
Base::BoundBox3d getBoundingBox() const override = 0;
|
||||
//@}
|
||||
|
||||
/** Return the element map version
|
||||
*
|
||||
* @param persisted: if true, return the restored element map version. Or
|
||||
* else, return the current element map version
|
||||
*/
|
||||
virtual std::string getElementMapVersion(bool restored=false) const;
|
||||
|
||||
/// Return true to signal element map version change
|
||||
virtual bool checkElementMapVersion(const char * ver) const;
|
||||
|
||||
void afterRestore() override;
|
||||
};
|
||||
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
#endif // _PreComp_
|
||||
|
||||
#include <App/Application.h>
|
||||
#include <App/Document.h>
|
||||
#include <App/DocumentObject.h>
|
||||
#include <App/ObjectIdentifier.h>
|
||||
#include <Base/Console.h>
|
||||
@@ -45,10 +46,14 @@
|
||||
#include <Base/Stream.h>
|
||||
#include <Base/Writer.h>
|
||||
|
||||
#include "PartFeature.h"
|
||||
#include "PartPyCXX.h"
|
||||
#include "PropertyTopoShape.h"
|
||||
#include "TopoShapePy.h"
|
||||
|
||||
FC_LOG_LEVEL_INIT("App", true, true)
|
||||
|
||||
namespace sp = std::placeholders;
|
||||
using namespace Part;
|
||||
|
||||
TYPESYSTEM_SOURCE(Part::PropertyPartShape , App::PropertyComplexGeoData)
|
||||
@@ -61,14 +66,32 @@ void PropertyPartShape::setValue(const TopoShape& sh)
|
||||
{
|
||||
aboutToSetValue();
|
||||
_Shape = sh;
|
||||
auto obj = Base::freecad_dynamic_cast<App::DocumentObject>(getContainer());
|
||||
if(obj) {
|
||||
auto tag = obj->getID();
|
||||
if(_Shape.Tag && tag!=_Shape.Tag) {
|
||||
auto hasher = _Shape.Hasher?_Shape.Hasher:obj->getDocument()->getStringHasher();
|
||||
_Shape.reTagElementMap(tag,hasher);
|
||||
} else
|
||||
_Shape.Tag = obj->getID();
|
||||
if (!_Shape.Hasher && _Shape.hasChildElementMap()) {
|
||||
_Shape.Hasher = obj->getDocument()->getStringHasher();
|
||||
_Shape.hashChildMaps();
|
||||
}
|
||||
}
|
||||
hasSetValue();
|
||||
_Ver.clear();
|
||||
}
|
||||
|
||||
void PropertyPartShape::setValue(const TopoDS_Shape& sh)
|
||||
void PropertyPartShape::setValue(const TopoDS_Shape& sh, bool resetElementMap)
|
||||
{
|
||||
aboutToSetValue();
|
||||
_Shape.setShape(sh);
|
||||
auto obj = dynamic_cast<App::DocumentObject*>(getContainer());
|
||||
if(obj)
|
||||
_Shape.Tag = obj->getID();
|
||||
_Shape.setShape(sh,resetElementMap);
|
||||
hasSetValue();
|
||||
_Ver.clear();
|
||||
}
|
||||
|
||||
const TopoDS_Shape& PropertyPartShape::getValue() const
|
||||
@@ -76,13 +99,23 @@ const TopoDS_Shape& PropertyPartShape::getValue() const
|
||||
return _Shape.getShape();
|
||||
}
|
||||
|
||||
const TopoShape& PropertyPartShape::getShape() const
|
||||
TopoShape PropertyPartShape::getShape() const
|
||||
{
|
||||
return this->_Shape;
|
||||
_Shape.initCache(-1);
|
||||
auto res = _Shape;
|
||||
// if (Feature::isElementMappingDisabled(getContainer()))
|
||||
if ( false ) // TODO Implement
|
||||
res.Tag = -1;
|
||||
else if (!res.Tag) {
|
||||
if (auto parent = Base::freecad_dynamic_cast<App::DocumentObject>(getContainer()))
|
||||
res.Tag = parent->getID();
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
const Data::ComplexGeoData* PropertyPartShape::getComplexData() const
|
||||
{
|
||||
_Shape.initCache(-1);
|
||||
return &(this->_Shape);
|
||||
}
|
||||
|
||||
@@ -140,8 +173,21 @@ PyObject *PropertyPartShape::getPyObject()
|
||||
void PropertyPartShape::setPyObject(PyObject *value)
|
||||
{
|
||||
if (PyObject_TypeCheck(value, &(TopoShapePy::Type))) {
|
||||
TopoShapePy *pcObject = static_cast<TopoShapePy*>(value);
|
||||
setValue(*pcObject->getTopoShapePtr());
|
||||
auto shape = *static_cast<TopoShapePy*>(value)->getTopoShapePtr();
|
||||
auto owner = dynamic_cast<App::DocumentObject*>(getContainer());
|
||||
if(owner && owner->getDocument()) {
|
||||
if(shape.Tag || shape.getElementMapSize()) {
|
||||
// We can't trust the meaning of the input shape tag, so we
|
||||
// remap anyway
|
||||
TopoShape res(owner->getID(),owner->getDocument()->getStringHasher(),shape.getShape());
|
||||
res.mapSubElement(shape);
|
||||
shape = res;
|
||||
}else{
|
||||
shape.Tag = owner->getID();
|
||||
shape.Hasher->clear(); // reset();
|
||||
}
|
||||
}
|
||||
setValue(shape);
|
||||
}
|
||||
else {
|
||||
std::string error = std::string("type must be 'Shape', not ");
|
||||
@@ -153,6 +199,15 @@ void PropertyPartShape::setPyObject(PyObject *value)
|
||||
App::Property *PropertyPartShape::Copy() const
|
||||
{
|
||||
PropertyPartShape *prop = new PropertyPartShape();
|
||||
|
||||
// if (PartParams::getShapePropertyCopy()) {
|
||||
if ( false ) { // TODO: PartParams
|
||||
// makeElementCopy() consume too much memory for complex geometry.
|
||||
prop->_Shape = this->_Shape.makeElementCopy();
|
||||
} else
|
||||
prop->_Shape = this->_Shape;
|
||||
prop->_Ver = this->_Ver;
|
||||
|
||||
prop->_Shape = this->_Shape;
|
||||
if (!_Shape.getShape().IsNull()) {
|
||||
BRepBuilderAPI_Copy copy(_Shape.getShape());
|
||||
@@ -164,9 +219,11 @@ App::Property *PropertyPartShape::Copy() const
|
||||
|
||||
void PropertyPartShape::Paste(const App::Property &from)
|
||||
{
|
||||
aboutToSetValue();
|
||||
_Shape = dynamic_cast<const PropertyPartShape&>(from)._Shape;
|
||||
hasSetValue();
|
||||
auto prop = Base::freecad_dynamic_cast<const PropertyPartShape>(&from);
|
||||
if(prop) {
|
||||
setValue(prop->_Shape);
|
||||
_Ver = prop->_Ver;
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int PropertyPartShape::getMemSize () const
|
||||
@@ -188,6 +245,19 @@ void PropertyPartShape::getPaths(std::vector<App::ObjectIdentifier> &paths) cons
|
||||
<< App::ObjectIdentifier::Component::SimpleComponent(App::ObjectIdentifier::String("Volume")));
|
||||
}
|
||||
|
||||
void PropertyPartShape::beforeSave() const
|
||||
{
|
||||
_HasherIndex = 0;
|
||||
_SaveHasher = false;
|
||||
auto owner = Base::freecad_dynamic_cast<App::DocumentObject>(getContainer());
|
||||
if(owner && !_Shape.isNull() && _Shape.getElementMapSize()>0) {
|
||||
auto ret = owner->getDocument()->addStringHasher(_Shape.Hasher);
|
||||
_HasherIndex = ret.second;
|
||||
_SaveHasher = ret.first;
|
||||
_Shape.beforeSave();
|
||||
}
|
||||
}
|
||||
|
||||
void PropertyPartShape::Save (Base::Writer &writer) const
|
||||
{
|
||||
if(!writer.isForceXML()) {
|
||||
@@ -205,6 +275,69 @@ void PropertyPartShape::Save (Base::Writer &writer) const
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef NOT_YET_AND_MAYBE_NEVER
|
||||
void PropertyPartShape::Save (Base::Writer &writer) const
|
||||
{
|
||||
//See SaveDocFile(), RestoreDocFile()
|
||||
writer.Stream() << writer.ind() << "<Part";
|
||||
auto owner = dynamic_cast<App::DocumentObject*>(getContainer());
|
||||
if(owner && !_Shape.isNull()
|
||||
&& _Shape.getElementMapSize()>0
|
||||
&& !_Shape.Hasher.isNull()) {
|
||||
writer.Stream() << " HasherIndex=\"" << _HasherIndex << '"';
|
||||
if(_SaveHasher)
|
||||
writer.Stream() << " SaveHasher=\"1\"";
|
||||
}
|
||||
std::string version;
|
||||
// If exporting, do not export mapped element name, but still make a mark
|
||||
if(owner) {
|
||||
if(!owner->isExporting())
|
||||
version = _Ver.size()?_Ver:owner->getElementMapVersion(this);
|
||||
}else
|
||||
version = _Ver.size()?_Ver:_Shape.getElementMapVersion();
|
||||
writer.Stream() << " ElementMap=\"" << version << '"';
|
||||
|
||||
bool binary = writer.getMode("BinaryBrep");
|
||||
bool toXML = writer.getFileVersion()>1 && writer.isForceXML()>=(binary?3:2);
|
||||
if(!toXML) {
|
||||
writer.Stream() << " file=\""
|
||||
<< writer.addFile(getFileName(binary?".bin":".brp"), this)
|
||||
<< "\"/>\n";
|
||||
} else if(binary) {
|
||||
writer.Stream() << " binary=\"1\">\n";
|
||||
TopoShape shape;
|
||||
shape.setShape(_Shape.getShape());
|
||||
shape.exportBinary(writer.beginCharStream(true));
|
||||
writer.endCharStream() << writer.ind() << "</Part>\n";
|
||||
} else {
|
||||
writer.Stream() << " brep=\"1\">\n";
|
||||
_Shape.exportBrep(writer.beginCharStream(false)<<'\n');
|
||||
writer.endCharStream() << '\n' << writer.ind() << "</Part>\n";
|
||||
}
|
||||
|
||||
if(_SaveHasher) {
|
||||
if(!toXML)
|
||||
_Shape.Hasher->setPersistenceFileName(getFileName(".Table").c_str());
|
||||
else
|
||||
_Shape.Hasher->setPersistenceFileName(0);
|
||||
_Shape.Hasher->Save(writer);
|
||||
}
|
||||
if(version.size()) {
|
||||
if(!toXML)
|
||||
_Shape.setPersistenceFileName(getFileName(".Map").c_str());
|
||||
else
|
||||
_Shape.setPersistenceFileName(0);
|
||||
_Shape.Save(writer);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
std::string PropertyPartShape::getElementMapVersion(bool restored) const {
|
||||
if(restored)
|
||||
return _Ver;
|
||||
return PropertyComplexGeoData::getElementMapVersion(false);
|
||||
}
|
||||
|
||||
void PropertyPartShape::Restore(Base::XMLReader &reader)
|
||||
{
|
||||
reader.readElement("Part");
|
||||
@@ -215,6 +348,105 @@ void PropertyPartShape::Restore(Base::XMLReader &reader)
|
||||
reader.addFile(file.c_str(),this);
|
||||
}
|
||||
}
|
||||
#ifdef NOT_YET_AND_MAYBE_NEVER
|
||||
void PropertyPartShape::Restore(Base::XMLReader &reader)
|
||||
{
|
||||
reader.readElement("Part");
|
||||
|
||||
auto owner = Base::freecad_dynamic_cast<App::DocumentObject>(getContainer());
|
||||
_Ver = "?";
|
||||
bool has_ver = reader.hasAttribute("ElementMap");
|
||||
if(has_ver)
|
||||
_Ver = reader.getAttribute("ElementMap");
|
||||
|
||||
int hasher_idx = reader.getAttributeAsInteger("HasherIndex","-1");
|
||||
int save_hasher = reader.getAttributeAsInteger("SaveHasher","");
|
||||
|
||||
TopoDS_Shape sh;
|
||||
|
||||
if(reader.hasAttribute("file")) {
|
||||
std::string file = reader.getAttribute("file");
|
||||
if (!file.empty()) {
|
||||
// initiate a file read
|
||||
reader.addFile(file.c_str(),this);
|
||||
}
|
||||
} else if(reader.getAttributeAsInteger("binary","")) {
|
||||
TopoShape shape;
|
||||
shape.importBinary(reader.beginCharStream(true));
|
||||
sh = shape.getShape();
|
||||
} else if(reader.getAttributeAsInteger("brep","")) {
|
||||
BRep_Builder builder;
|
||||
BRepTools::Read(sh, reader.beginCharStream(false), builder);
|
||||
}
|
||||
|
||||
reader.readEndElement("Part");
|
||||
|
||||
if(owner && hasher_idx>=0) {
|
||||
_Shape.Hasher = owner->getDocument()->getStringHasher(hasher_idx);
|
||||
if(save_hasher)
|
||||
_Shape.Hasher->Restore(reader);
|
||||
}
|
||||
|
||||
if(has_ver) {
|
||||
// The file name here is not used for restore, but just a way to get
|
||||
// more useful error message if something wrong when restoring
|
||||
_Shape.setPersistenceFileName(getFileName().c_str());
|
||||
if(owner && owner->getDocument()->testStatus(App::Document::PartialDoc))
|
||||
_Shape.Restore(reader);
|
||||
else if(_Ver == "?" || _Ver.empty()) {
|
||||
// This indicate the shape is saved by legacy version without
|
||||
// element map info.
|
||||
if(owner) {
|
||||
// This will ask user for recompute after import
|
||||
owner->getDocument()->addRecomputeObject(owner);
|
||||
}
|
||||
}else{
|
||||
_Shape.Restore(reader);
|
||||
if (owner ? owner->checkElementMapVersion(this, _Ver.c_str())
|
||||
: _Shape.checkElementMapVersion(_Ver.c_str())) {
|
||||
auto ver = owner?owner->getElementMapVersion(this):_Shape.getElementMapVersion();
|
||||
if(!owner || !owner->getNameInDocument() || !_Shape.getElementMapSize()) {
|
||||
_Ver = ver;
|
||||
} else {
|
||||
// version mismatch, signal for regenerating.
|
||||
static const char *warnedDoc=0;
|
||||
if(warnedDoc != owner->getDocument()->getName()) {
|
||||
warnedDoc = owner->getDocument()->getName();
|
||||
FC_WARN("Recomputation required for document '" << warnedDoc
|
||||
<< "' on geo element version change in " << getFullName()
|
||||
<< ": " << _Ver << " -> " << ver);
|
||||
}
|
||||
owner->getDocument()->addRecomputeObject(owner);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if(owner && !owner->getDocument()->testStatus(App::Document::PartialDoc)) {
|
||||
if(App::DocumentParams::getWarnRecomputeOnRestore()) {
|
||||
FC_WARN("Pending recompute for generating element map: " << owner->getFullName());
|
||||
owner->getDocument()->addRecomputeObject(owner);
|
||||
}
|
||||
}
|
||||
|
||||
if (!sh.IsNull() || !_Shape.isNull()) {
|
||||
aboutToSetValue();
|
||||
_Shape.setShape(sh,false);
|
||||
hasSetValue();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void PropertyPartShape::afterRestore()
|
||||
{
|
||||
if (_Shape.isRestoreFailed()) {
|
||||
// this cause GeoFeature::updateElementReference() to call
|
||||
// PropertyLinkBase::updateElementReferences() with reverse = true, in
|
||||
// order to try to regenerate the element map
|
||||
_Ver = "?";
|
||||
}
|
||||
else if (_Shape.getElementMapSize() == 0)
|
||||
_Shape.Hasher->clear(); //reset();
|
||||
PropertyComplexGeoData::afterRestore();
|
||||
}
|
||||
|
||||
// The following function is copied from OCCT BRepTools.cxx and modified
|
||||
// to disable saving of triangulation
|
||||
@@ -406,6 +638,88 @@ void PropertyPartShape::RestoreDocFile(Base::Reader &reader)
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
ShapeHistory::ShapeHistory(BRepBuilderAPI_MakeShape& mkShape, TopAbs_ShapeEnum type,
|
||||
const TopoDS_Shape& newS, const TopoDS_Shape& oldS)
|
||||
{
|
||||
reset(mkShape,type,newS,oldS);
|
||||
}
|
||||
|
||||
void ShapeHistory::reset(BRepBuilderAPI_MakeShape& mkShape, TopAbs_ShapeEnum type,
|
||||
const TopoDS_Shape& newS, const TopoDS_Shape& oldS)
|
||||
{
|
||||
shapeMap.clear();
|
||||
this->type = type;
|
||||
|
||||
TopTools_IndexedMapOfShape newM, oldM;
|
||||
TopExp::MapShapes(newS, type, newM); // map containing all old objects of type "type"
|
||||
TopExp::MapShapes(oldS, type, oldM); // map containing all new objects of type "type"
|
||||
|
||||
// Look at all objects in the old shape and try to find the modified object in the new shape
|
||||
for (int i=1; i<=oldM.Extent(); i++) {
|
||||
bool found = false;
|
||||
TopTools_ListIteratorOfListOfShape it;
|
||||
// Find all new objects that are a modification of the old object (e.g. a face was resized)
|
||||
for (it.Initialize(mkShape.Modified(oldM(i))); it.More(); it.Next()) {
|
||||
found = true;
|
||||
for (int j=1; j<=newM.Extent(); j++) { // one old object might create several new ones!
|
||||
if (newM(j).IsPartner(it.Value())) {
|
||||
shapeMap[i-1].push_back(j-1); // adjust indices to start at zero
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Find all new objects that were generated from an old object (e.g. a face generated from an edge)
|
||||
for (it.Initialize(mkShape.Generated(oldM(i))); it.More(); it.Next()) {
|
||||
found = true;
|
||||
for (int j=1; j<=newM.Extent(); j++) {
|
||||
if (newM(j).IsPartner(it.Value())) {
|
||||
shapeMap[i-1].push_back(j-1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
// Find all old objects that don't exist any more (e.g. a face was completely cut away)
|
||||
if (mkShape.IsDeleted(oldM(i))) {
|
||||
shapeMap[i-1] = std::vector<int>();
|
||||
}
|
||||
else {
|
||||
// Mop up the rest (will this ever be reached?)
|
||||
for (int j=1; j<=newM.Extent(); j++) {
|
||||
if (newM(j).IsPartner(oldM(i))) {
|
||||
shapeMap[i-1].push_back(j-1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ShapeHistory::join(const ShapeHistory& newH)
|
||||
{
|
||||
ShapeHistory join;
|
||||
|
||||
for (ShapeHistory::MapList::const_iterator it = shapeMap.begin(); it != shapeMap.end(); ++it) {
|
||||
int old_shape_index = it->first;
|
||||
if (it->second.empty())
|
||||
join.shapeMap[old_shape_index] = ShapeHistory::List();
|
||||
for (ShapeHistory::List::const_iterator jt = it->second.begin(); jt != it->second.end(); ++jt) {
|
||||
ShapeHistory::MapList::const_iterator kt = newH.shapeMap.find(*jt);
|
||||
if (kt != newH.shapeMap.end()) {
|
||||
ShapeHistory::List& ary = join.shapeMap[old_shape_index];
|
||||
ary.insert(ary.end(), kt->second.begin(), kt->second.end());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
shapeMap.swap(join.shapeMap);
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
TYPESYSTEM_SOURCE(Part::PropertyShapeHistory , App::PropertyLists)
|
||||
|
||||
PropertyShapeHistory::PropertyShapeHistory() = default;
|
||||
@@ -577,3 +891,104 @@ void PropertyFilletEdges::Paste(const Property &from)
|
||||
_lValueList = dynamic_cast<const PropertyFilletEdges&>(from)._lValueList;
|
||||
hasSetValue();
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
TYPESYSTEM_SOURCE(Part::PropertyShapeCache, App::Property);
|
||||
|
||||
App::Property *PropertyShapeCache::Copy(void) const {
|
||||
return new PropertyShapeCache();
|
||||
}
|
||||
|
||||
void PropertyShapeCache::Paste(const App::Property &) {
|
||||
cache.clear();
|
||||
}
|
||||
|
||||
void PropertyShapeCache::Save (Base::Writer &) const
|
||||
{
|
||||
}
|
||||
|
||||
void PropertyShapeCache::Restore(Base::XMLReader &)
|
||||
{
|
||||
}
|
||||
|
||||
PyObject *PropertyShapeCache::getPyObject() {
|
||||
Py::List res;
|
||||
for(auto &v : cache)
|
||||
res.append(Py::TupleN(Py::String(v.first),shape2pyshape(v.second)));
|
||||
return Py::new_reference_to(res);
|
||||
}
|
||||
|
||||
void PropertyShapeCache::setPyObject(PyObject *value) {
|
||||
if(!value)
|
||||
return;
|
||||
if(value == Py_None) {
|
||||
cache.clear();
|
||||
return;
|
||||
}
|
||||
App::PropertyStringList prop;
|
||||
prop.setPyObject(value);
|
||||
for(const auto &sub : prop.getValues())
|
||||
cache.erase(sub);
|
||||
}
|
||||
|
||||
#define SHAPE_CACHE_NAME "_Part_ShapeCache"
|
||||
PropertyShapeCache *PropertyShapeCache::get(const App::DocumentObject *obj, bool create) {
|
||||
auto prop = Base::freecad_dynamic_cast<PropertyShapeCache>(
|
||||
obj->getDynamicPropertyByName(SHAPE_CACHE_NAME));
|
||||
if(prop && prop->getContainer()==obj)
|
||||
return prop;
|
||||
if(!create)
|
||||
return 0;
|
||||
|
||||
prop = static_cast<PropertyShapeCache*>(
|
||||
const_cast<App::DocumentObject*>(obj)->addDynamicProperty("Part::PropertyShapeCache",
|
||||
SHAPE_CACHE_NAME,"Part","Shape cache",
|
||||
App::Prop_NoPersist|App::Prop_Output|App::Prop_Hidden));
|
||||
if(!prop)
|
||||
FC_ERR("Failed to add shape cache for " << obj->getFullName());
|
||||
else
|
||||
prop->connChanged = const_cast<App::DocumentObject*>(obj)->signalEarlyChanged.connect(
|
||||
std::bind(&PropertyShapeCache::slotChanged,prop,sp::_1,sp::_2));
|
||||
return prop;
|
||||
}
|
||||
|
||||
bool PropertyShapeCache::getShape(const App::DocumentObject *obj, TopoShape &shape, const char *subname) {
|
||||
// if (PartParams::getDisableShapeCache())
|
||||
// return false; //TODO PartParams
|
||||
auto prop = get(obj,false);
|
||||
if(!prop)
|
||||
return false;
|
||||
if(!subname) subname = "";
|
||||
auto it = prop->cache.find(subname);
|
||||
if(it!=prop->cache.end()) {
|
||||
shape = it->second;
|
||||
return !shape.isNull();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void PropertyShapeCache::setShape(
|
||||
const App::DocumentObject *obj, const TopoShape &shape, const char *subname)
|
||||
{
|
||||
// if (PartParams::getDisableShapeCache())
|
||||
// return; // TODO: Part Params
|
||||
auto prop = get(obj,true);
|
||||
if(!prop)
|
||||
return;
|
||||
if(!subname) subname = "";
|
||||
prop->cache[subname] = shape;
|
||||
}
|
||||
|
||||
void PropertyShapeCache::slotChanged(const App::DocumentObject &, const App::Property &prop) {
|
||||
auto propName = prop.getName();
|
||||
if(!propName) return;
|
||||
if(strcmp(propName,"Group")==0 ||
|
||||
strcmp(propName,"Shape")==0 ||
|
||||
strstr(propName,"Touched")!=0)
|
||||
{
|
||||
FC_LOG("clear shape cache on changed " << prop.getFullName());
|
||||
cache.clear();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -35,6 +35,7 @@
|
||||
namespace Part
|
||||
{
|
||||
|
||||
class Feature;
|
||||
/** The part shape property class.
|
||||
* @author Werner Mayer
|
||||
*/
|
||||
@@ -51,10 +52,10 @@ public:
|
||||
/// set the part shape
|
||||
void setValue(const TopoShape&);
|
||||
/// set the part shape
|
||||
void setValue(const TopoDS_Shape&);
|
||||
void setValue(const TopoDS_Shape&, bool resetElementMap=true);
|
||||
/// get the part shape
|
||||
const TopoDS_Shape& getValue() const;
|
||||
const TopoShape& getShape() const;
|
||||
TopoShape getShape() const;
|
||||
const Data::ComplexGeoData* getComplexData() const override;
|
||||
//@}
|
||||
|
||||
@@ -85,6 +86,8 @@ public:
|
||||
void Save (Base::Writer &writer) const override;
|
||||
void Restore(Base::XMLReader &reader) override;
|
||||
|
||||
virtual void beforeSave() const override;
|
||||
|
||||
void SaveDocFile (Base::Writer &writer) const override;
|
||||
void RestoreDocFile(Base::Reader &reader) override;
|
||||
|
||||
@@ -96,6 +99,13 @@ public:
|
||||
/// Get valid paths for this property; used by auto completer
|
||||
void getPaths(std::vector<App::ObjectIdentifier> & paths) const override;
|
||||
|
||||
virtual std::string getElementMapVersion(bool restored=false) const override;
|
||||
void resetElementMapVersion() {_Ver.clear();}
|
||||
|
||||
virtual void afterRestore() override;
|
||||
|
||||
friend class Feature;
|
||||
|
||||
private:
|
||||
void saveToFile(Base::Writer &writer) const;
|
||||
void loadFromFile(Base::Reader &reader);
|
||||
@@ -103,6 +113,9 @@ private:
|
||||
|
||||
private:
|
||||
TopoShape _Shape;
|
||||
std::string _Ver;
|
||||
mutable int _HasherIndex = 0;
|
||||
mutable bool _SaveHasher = false;
|
||||
};
|
||||
|
||||
struct PartExport ShapeHistory {
|
||||
@@ -115,6 +128,20 @@ struct PartExport ShapeHistory {
|
||||
|
||||
TopAbs_ShapeEnum type;
|
||||
MapList shapeMap;
|
||||
ShapeHistory() {}
|
||||
/**
|
||||
* Build a history of changes
|
||||
* MakeShape: The operation that created the changes, e.g. BRepAlgoAPI_Common
|
||||
* type: The type of object we are interested in, e.g. TopAbs_FACE
|
||||
* newS: The new shape that was created by the operation
|
||||
* oldS: The original shape prior to the operation
|
||||
*/
|
||||
ShapeHistory(BRepBuilderAPI_MakeShape& mkShape, TopAbs_ShapeEnum type,
|
||||
const TopoDS_Shape& newS, const TopoDS_Shape& oldS);
|
||||
void reset(BRepBuilderAPI_MakeShape& mkShape, TopAbs_ShapeEnum type,
|
||||
const TopoDS_Shape& newS, const TopoDS_Shape& oldS);
|
||||
void join(const ShapeHistory &newH);
|
||||
|
||||
};
|
||||
|
||||
class PartExport PropertyShapeHistory : public App::PropertyLists
|
||||
@@ -168,6 +195,20 @@ private:
|
||||
struct PartExport FilletElement {
|
||||
int edgeid;
|
||||
double radius1, radius2;
|
||||
|
||||
FilletElement(int id=0,double r1=1.0,double r2=1.0)
|
||||
:edgeid(id),radius1(r1),radius2(r2)
|
||||
{}
|
||||
|
||||
bool operator<(const FilletElement &other) const {
|
||||
return edgeid < other.edgeid;
|
||||
}
|
||||
|
||||
bool operator==(const FilletElement &other) const {
|
||||
return edgeid == other.edgeid
|
||||
&& radius1 == other.radius1
|
||||
&& radius2 == other.radius2;
|
||||
}
|
||||
};
|
||||
|
||||
class PartExport PropertyFilletEdges : public App::PropertyLists
|
||||
@@ -215,6 +256,34 @@ private:
|
||||
std::vector<FilletElement> _lValueList;
|
||||
};
|
||||
|
||||
|
||||
class PartExport PropertyShapeCache: public App::Property {
|
||||
TYPESYSTEM_HEADER_WITH_OVERRIDE();
|
||||
public:
|
||||
virtual App::Property *Copy(void) const override;
|
||||
|
||||
virtual void Paste(const App::Property &) override;
|
||||
|
||||
virtual PyObject *getPyObject() override;
|
||||
|
||||
virtual void setPyObject(PyObject *value) override;
|
||||
|
||||
virtual void Save (Base::Writer &writer) const override;
|
||||
|
||||
virtual void Restore(Base::XMLReader &reader) override;
|
||||
|
||||
static PropertyShapeCache *get(const App::DocumentObject *obj, bool create);
|
||||
static bool getShape(const App::DocumentObject *obj, TopoShape &shape, const char *subname=0);
|
||||
static void setShape(const App::DocumentObject *obj, const TopoShape &shape, const char *subname=0);
|
||||
|
||||
private:
|
||||
void slotChanged(const App::DocumentObject &, const App::Property &prop);
|
||||
|
||||
private:
|
||||
std::unordered_map<std::string, TopoShape> cache;
|
||||
boost::signals2::scoped_connection connChanged;
|
||||
};
|
||||
|
||||
} //namespace Part
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user