Part: changes to Part Module
* Added Part::Feature::getTopoShape/getShape() function that can obtain
shape from any object with proper implementation of getSubObject(). It
can even construct compound from group object with proper implementation
of getSubObjects().
* Modified ViewProviderExt to work on any object, because it now obtain
the shape using Part::Feature::getShape()
* Modified various Part features to obtain base/tool shapes using
Part::getShape(), which allows them to be any type of object,
including Link and groups.
* Modified various Part command to relax type requirement on selected
objects.
* Add support of link and group to dimension, and add dimension refresh
command
* Support link and group in simple command command, and add a few more
copy command variations.
* Add special handling of 'Shape' attribute in PropertyContainerPy and
use Part::Feature::getShape() to return shape for any object without
Shape property. This allows many python feature work with any object
without modification.
* GeometrySurface/CurvePy, add convenience attribute 'Rotation'
* TopoShapePy:
* Extended support of sub shape attribute, e.g. Compound1, Solid2,
SubShape3 ('SubShape' is used to access child shape of a compound)
* makeWires(), new API to sort and return wires given a list of edges.
* transformed/translated/rotated/scaled(), return a new shape with
some transformation.
* findPlane(), find the plane of a planar shape
* isCoplanar(), check if two shape are coplanar
This commit is contained in:
@@ -52,6 +52,8 @@
|
||||
# include <gce_MakeDir.hxx>
|
||||
#endif
|
||||
|
||||
#include <boost/algorithm/string/predicate.hpp>
|
||||
#include <boost/bind.hpp>
|
||||
#include <Base/Console.h>
|
||||
#include <Base/Writer.h>
|
||||
#include <Base/Reader.h>
|
||||
@@ -63,6 +65,8 @@
|
||||
#include <App/Application.h>
|
||||
#include <App/FeaturePythonPyImp.h>
|
||||
#include <App/Document.h>
|
||||
#include <App/Link.h>
|
||||
#include <App/GeoFeatureGroupExtension.h>
|
||||
|
||||
#include "PartPyCXX.h"
|
||||
#include "PartFeature.h"
|
||||
@@ -126,8 +130,10 @@ App::DocumentObject *Feature::getSubObject(const char *subname,
|
||||
if(subname && !Data::ComplexGeoData::isMappedElement(subname) && strchr(subname,'.'))
|
||||
return App::DocumentObject::getSubObject(subname,pyObj,pmat,transform,depth);
|
||||
|
||||
if(pmat && transform)
|
||||
*pmat *= Placement.getValue().toMatrix();
|
||||
Base::Matrix4D _mat;
|
||||
auto &mat = pmat?*pmat:_mat;
|
||||
if(transform)
|
||||
mat *= Placement.getValue().toMatrix();
|
||||
|
||||
if(!pyObj) {
|
||||
#if 0
|
||||
@@ -142,13 +148,11 @@ App::DocumentObject *Feature::getSubObject(const char *subname,
|
||||
|
||||
try {
|
||||
TopoShape ts(Shape.getShape());
|
||||
bool doTransform = pmat && *pmat!=ts.getTransform();
|
||||
if(doTransform) {
|
||||
ts.setShape(ts.getShape().Located(TopLoc_Location()),false);
|
||||
ts.initCache(1);
|
||||
}
|
||||
bool doTransform = mat!=ts.getTransform();
|
||||
if(doTransform)
|
||||
ts.setShape(ts.getShape().Located(TopLoc_Location()));
|
||||
if(subname && *subname && !ts.isNull())
|
||||
ts = ts.getSubTopoShape(subname,true);
|
||||
ts = ts.getSubShape(subname);
|
||||
if(doTransform && !ts.isNull()) {
|
||||
static int sCopy = -1;
|
||||
if(sCopy<0) {
|
||||
@@ -171,7 +175,7 @@ App::DocumentObject *Feature::getSubObject(const char *subname,
|
||||
}
|
||||
}
|
||||
}
|
||||
ts.transformShape(*pmat,copy,true);
|
||||
ts.transformShape(mat,copy,true);
|
||||
}
|
||||
*pyObj = Py::new_reference_to(shape2pyshape(ts));
|
||||
return const_cast<Feature*>(this);
|
||||
@@ -189,6 +193,330 @@ App::DocumentObject *Feature::getSubObject(const char *subname,
|
||||
}
|
||||
}
|
||||
|
||||
TopoDS_Shape Feature::getShape(const App::DocumentObject *obj, const char *subname,
|
||||
bool needSubElement, Base::Matrix4D *pmat, App::DocumentObject **powner,
|
||||
bool resolveLink, bool transform)
|
||||
{
|
||||
return getTopoShape(obj,subname,needSubElement,pmat,powner,resolveLink,transform,true).getShape();
|
||||
}
|
||||
|
||||
struct ShapeCache {
|
||||
|
||||
std::unordered_map<const App::Document*,
|
||||
std::map<std::pair<const App::DocumentObject*, std::string> ,TopoShape> > cache;
|
||||
|
||||
bool inited = false;
|
||||
void init() {
|
||||
if(inited)
|
||||
return;
|
||||
inited = true;
|
||||
App::GetApplication().signalDeleteDocument.connect(
|
||||
boost::bind(&ShapeCache::slotDeleteDocument, this, _1));
|
||||
App::GetApplication().signalDeletedObject.connect(
|
||||
boost::bind(&ShapeCache::slotClear, this, _1));
|
||||
App::GetApplication().signalChangedObject.connect(
|
||||
boost::bind(&ShapeCache::slotChanged, this, _1,_2));
|
||||
}
|
||||
|
||||
void slotDeleteDocument(const App::Document &doc) {
|
||||
cache.erase(&doc);
|
||||
}
|
||||
|
||||
void slotChanged(const App::DocumentObject &obj, const App::Property &prop) {
|
||||
const char *propName = prop.getName();
|
||||
if(!propName)
|
||||
return;
|
||||
if(strcmp(propName,"Shape")==0
|
||||
|| strcmp(propName,"Group")==0
|
||||
|| strstr(propName,"Touched")!=0)
|
||||
slotClear(obj);
|
||||
}
|
||||
|
||||
void slotClear(const App::DocumentObject &obj) {
|
||||
auto it = cache.find(obj.getDocument());
|
||||
if(it==cache.end())
|
||||
return;
|
||||
auto &map = it->second;
|
||||
for(auto it2=map.lower_bound(std::make_pair(&obj,std::string()));
|
||||
it2!=map.end() && it2->first.first==&obj;)
|
||||
{
|
||||
it2 = map.erase(it2);
|
||||
}
|
||||
}
|
||||
|
||||
bool getShape(const App::DocumentObject *obj, TopoShape &shape, const char *subname=0) {
|
||||
init();
|
||||
auto &entry = cache[obj->getDocument()];
|
||||
if(!subname) subname = "";
|
||||
auto it = entry.find(std::make_pair(obj,std::string(subname)));
|
||||
if(it!=entry.end()) {
|
||||
shape = it->second;
|
||||
return !shape.isNull();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void setShape(const App::DocumentObject *obj, const TopoShape &shape, const char *subname=0) {
|
||||
init();
|
||||
if(!subname) subname = "";
|
||||
cache[obj->getDocument()][std::make_pair(obj,std::string(subname))] = shape;
|
||||
}
|
||||
};
|
||||
static ShapeCache _ShapeCache;
|
||||
|
||||
void Feature::clearShapeCache() {
|
||||
_ShapeCache.cache.clear();
|
||||
}
|
||||
|
||||
static TopoShape _getTopoShape(const App::DocumentObject *obj, const char *subname,
|
||||
bool needSubElement, Base::Matrix4D *pmat, App::DocumentObject **powner,
|
||||
bool resolveLink, bool noElementMap, std::vector<App::DocumentObject*> &linkStack)
|
||||
|
||||
{
|
||||
TopoShape shape;
|
||||
|
||||
if(!obj) return shape;
|
||||
|
||||
PyObject *pyobj = 0;
|
||||
Base::Matrix4D mat;
|
||||
if(powner) *powner = 0;
|
||||
|
||||
std::string _subname;
|
||||
auto subelement = Data::ComplexGeoData::findElementName(subname);
|
||||
if(!needSubElement && subname) {
|
||||
// strip out element name if not needed
|
||||
if(subelement && *subelement) {
|
||||
_subname = std::string(subname,subelement);
|
||||
subname = _subname.c_str();
|
||||
}
|
||||
}
|
||||
|
||||
if(_ShapeCache.getShape(obj,shape,subname)) {
|
||||
if(noElementMap) {
|
||||
// shape.resetElementMap();
|
||||
// shape.Tag = 0;
|
||||
// shape.Hasher.reset();
|
||||
}
|
||||
}
|
||||
|
||||
App::DocumentObject *linked = 0;
|
||||
App::DocumentObject *owner = 0;
|
||||
Base::Matrix4D linkMat;
|
||||
// App::StringHasherRef hasher;
|
||||
// long tag;
|
||||
{
|
||||
Base::PyGILStateLocker lock;
|
||||
owner = obj->getSubObject(subname,shape.isNull()?&pyobj:0,&mat,false);
|
||||
if(!owner)
|
||||
return shape;
|
||||
// tag = owner->getID();
|
||||
// hasher = owner->getDocument()->getStringHasher();
|
||||
linked = owner->getLinkedObject(true,&linkMat,false);
|
||||
if(pmat) {
|
||||
if(resolveLink && obj!=owner)
|
||||
*pmat = mat * linkMat;
|
||||
else
|
||||
*pmat = mat;
|
||||
}
|
||||
if(!linked)
|
||||
linked = owner;
|
||||
if(powner)
|
||||
*powner = resolveLink?linked:owner;
|
||||
|
||||
if(!shape.isNull())
|
||||
return shape;
|
||||
|
||||
if(pyobj && PyObject_TypeCheck(pyobj,&TopoShapePy::Type)) {
|
||||
shape = *static_cast<TopoShapePy*>(pyobj)->getTopoShapePtr();
|
||||
if(!shape.isNull()) {
|
||||
if(obj->getDocument() != linked->getDocument())
|
||||
_ShapeCache.setShape(obj,shape,subname);
|
||||
if(noElementMap) {
|
||||
// shape.resetElementMap();
|
||||
// shape.Tag = 0;
|
||||
// shape.Hasher.reset();
|
||||
}
|
||||
Py_DECREF(pyobj);
|
||||
return shape;
|
||||
}
|
||||
}
|
||||
|
||||
Py_XDECREF(pyobj);
|
||||
}
|
||||
|
||||
// nothing can be done if there is sub-element references
|
||||
if(needSubElement && subelement && *subelement)
|
||||
return shape;
|
||||
|
||||
bool scaled = false;
|
||||
if(obj!=owner) {
|
||||
if(_ShapeCache.getShape(owner,shape)) {
|
||||
auto scaled = shape.transformShape(mat,false,true);
|
||||
if(owner->getDocument()!=obj->getDocument()) {
|
||||
// shape.reTagElementMap(obj->getID(),obj->getDocument()->getStringHasher());
|
||||
_ShapeCache.setShape(obj,shape,subname);
|
||||
} else if(scaled)
|
||||
_ShapeCache.setShape(obj,shape,subname);
|
||||
}
|
||||
if(!shape.isNull()) {
|
||||
if(noElementMap) {
|
||||
// shape.resetElementMap();
|
||||
// shape.Tag = 0;
|
||||
// shape.Hasher.reset();
|
||||
}
|
||||
return shape;
|
||||
}
|
||||
}
|
||||
|
||||
auto link = owner->getExtensionByType<App::LinkBaseExtension>(true);
|
||||
if(owner!=linked && (!link || !link->_ChildCache.getSize())) {
|
||||
// if there is a linked object, and there is no child cache (which is used
|
||||
// for special handling of plain group), obtain shape from the linked object
|
||||
shape = Feature::getTopoShape(linked,0,false,0,0,false,false);
|
||||
if(shape.isNull())
|
||||
return shape;
|
||||
if(owner==obj)
|
||||
shape.transformShape(mat*linkMat,false,true);
|
||||
else
|
||||
shape.transformShape(linkMat,false,true);
|
||||
// shape.reTagElementMap(tag,hasher);
|
||||
|
||||
} else {
|
||||
|
||||
if(link || owner->getExtensionByType<App::GeoFeatureGroupExtension>(true))
|
||||
linkStack.push_back(owner);
|
||||
|
||||
// Construct a compound of sub objects
|
||||
std::vector<TopoShape> shapes;
|
||||
|
||||
// Acceleration for link array. Unlike non-array link, a link array does
|
||||
// not return the linked object when calling getLinkedObject().
|
||||
// Therefore, it should be handled here.
|
||||
TopoShape baseShape;
|
||||
std::string op;
|
||||
if(link && link->getElementCountValue()) {
|
||||
linked = link->getTrueLinkedObject(false);
|
||||
if(linked && linked!=owner) {
|
||||
baseShape = Feature::getTopoShape(linked,0,false,0,0,false,false);
|
||||
// if(!link->getShowElementValue())
|
||||
// baseShape.reTagElementMap(owner->getID(),owner->getDocument()->getStringHasher());
|
||||
}
|
||||
}
|
||||
for(auto &sub : owner->getSubObjects()) {
|
||||
if(sub.empty()) continue;
|
||||
int visible;
|
||||
if(sub[sub.size()-1] != '.')
|
||||
sub += '.';
|
||||
std::string childName;
|
||||
App::DocumentObject *parent=0;
|
||||
Base::Matrix4D mat;
|
||||
auto subObj = owner->resolve(sub.c_str(), &parent, &childName,0,0,&mat,false);
|
||||
if(!parent || !subObj)
|
||||
continue;
|
||||
if(linkStack.size()
|
||||
&& parent->getExtensionByType<App::GroupExtension>(true,false))
|
||||
{
|
||||
visible = linkStack.back()->isElementVisible(childName.c_str());
|
||||
}else
|
||||
visible = parent->isElementVisible(childName.c_str());
|
||||
if(visible==0)
|
||||
continue;
|
||||
TopoShape shape;
|
||||
if(baseShape.isNull()) {
|
||||
shape = _getTopoShape(owner,sub.c_str(),false,0,&subObj,false,false,linkStack);
|
||||
if(shape.isNull())
|
||||
continue;
|
||||
if(visible<0 && subObj && !subObj->Visibility.getValue())
|
||||
continue;
|
||||
}else{
|
||||
if(link && !link->getShowElementValue())
|
||||
shape = baseShape.makETransform(mat,(TopoShape::indexPostfix()+childName).c_str());
|
||||
else {
|
||||
shape = baseShape.makETransform(mat);
|
||||
// shape.reTagElementMap(subObj->getID(),subObj->getDocument()->getStringHasher());
|
||||
}
|
||||
}
|
||||
shapes.push_back(shape);
|
||||
}
|
||||
|
||||
if(linkStack.size() && linkStack.back()==owner)
|
||||
linkStack.pop_back();
|
||||
|
||||
if(shapes.empty())
|
||||
return shape;
|
||||
|
||||
// shape.Tag = tag;
|
||||
// shape.Hasher = hasher;
|
||||
shape.makECompound(shapes);
|
||||
}
|
||||
|
||||
_ShapeCache.setShape(owner,shape);
|
||||
|
||||
if(owner!=obj) {
|
||||
scaled = shape.transformShape(mat,false,true);
|
||||
if(owner->getDocument()!=obj->getDocument()) {
|
||||
// shape.reTagElementMap(obj->getID(),obj->getDocument()->getStringHasher());
|
||||
_ShapeCache.setShape(obj,shape,subname);
|
||||
}else if(scaled)
|
||||
_ShapeCache.setShape(obj,shape,subname);
|
||||
}
|
||||
if(noElementMap) {
|
||||
// shape.resetElementMap();
|
||||
// shape.Tag = 0;
|
||||
// shape.Hasher.reset();
|
||||
}
|
||||
return shape;
|
||||
}
|
||||
|
||||
TopoShape Feature::getTopoShape(const App::DocumentObject *obj, const char *subname,
|
||||
bool needSubElement, Base::Matrix4D *pmat, App::DocumentObject **powner,
|
||||
bool resolveLink, bool transform, bool noElementMap)
|
||||
{
|
||||
if(!obj || !obj->getNameInDocument())
|
||||
return TopoShape();
|
||||
|
||||
std::vector<App::DocumentObject*> linkStack;
|
||||
|
||||
// NOTE! _getTopoShape() always return shape without top level
|
||||
// transformation for easy shape caching, i.e. with `transform` set
|
||||
// to false. So we manually apply the top level transform if asked.
|
||||
|
||||
Base::Matrix4D mat;
|
||||
auto shape = _getTopoShape(obj, subname, needSubElement, &mat,
|
||||
powner, resolveLink, noElementMap, linkStack);
|
||||
|
||||
Base::Matrix4D topMat;
|
||||
if(pmat || transform) {
|
||||
// Obtain top level transformation
|
||||
if(pmat)
|
||||
topMat = *pmat;
|
||||
if(transform)
|
||||
obj->getSubObject(0,0,&topMat);
|
||||
|
||||
// Apply the top level transformation
|
||||
if(!shape.isNull())
|
||||
shape.transformShape(topMat,false,true);
|
||||
|
||||
if(pmat)
|
||||
*pmat = topMat * mat;
|
||||
}
|
||||
|
||||
return shape;
|
||||
|
||||
}
|
||||
|
||||
App::DocumentObject *Feature::getShapeOwner(const App::DocumentObject *obj, const char *subname)
|
||||
{
|
||||
if(!obj) return 0;
|
||||
auto owner = obj->getSubObject(subname);
|
||||
if(owner) {
|
||||
auto linked = owner->getLinkedObject(true);
|
||||
if(linked)
|
||||
owner = linked;
|
||||
}
|
||||
return owner;
|
||||
}
|
||||
|
||||
void Feature::onChanged(const App::Property* prop)
|
||||
{
|
||||
// if the placement has changed apply the change to the point data as well
|
||||
|
||||
Reference in New Issue
Block a user