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:
Zheng, Lei
2019-07-12 10:10:03 +08:00
committed by wmayer
parent ebd60c8595
commit f028ba42ff
58 changed files with 3250 additions and 727 deletions

View File

@@ -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