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

@@ -126,7 +126,9 @@
#include "ImportStep.h"
#include "edgecluster.h"
#include "FaceMaker.h"
#include "PartFeature.h"
#include "PartPyCXX.h"
#include "modelRefine.h"
#ifdef FCUseFreeType
# include "FT2FC.h"
@@ -134,10 +136,6 @@
extern const char* BRepBuilderAPI_FaceErrorText(BRepBuilderAPI_FaceError fe);
namespace Part {
extern Py::Object shape2pyshape(const TopoDS_Shape &shape);
}
#ifndef M_PI
#define M_PI 3.14159265358979323846 /* pi */
#endif
@@ -147,6 +145,35 @@ extern Py::Object shape2pyshape(const TopoDS_Shape &shape);
#endif
namespace Part {
PartExport void getPyShapes(PyObject *obj, std::vector<TopoShape> &shapes) {
if(!obj)
return;
if(PyObject_TypeCheck(obj,&Part::TopoShapePy::Type))
shapes.push_back(*static_cast<TopoShapePy*>(obj)->getTopoShapePtr());
else if (PyObject_TypeCheck(obj, &GeometryPy::Type))
shapes.push_back(TopoShape(static_cast<GeometryPy*>(obj)->getGeometryPtr()->toShape()));
else if(PySequence_Check(obj)) {
Py::Sequence list(obj);
for (Py::Sequence::iterator it = list.begin(); it != list.end(); ++it) {
if (PyObject_TypeCheck((*it).ptr(), &(Part::TopoShapePy::Type)))
shapes.push_back(*static_cast<TopoShapePy*>((*it).ptr())->getTopoShapePtr());
else if (PyObject_TypeCheck((*it).ptr(), &GeometryPy::Type))
shapes.push_back(TopoShape(static_cast<GeometryPy*>(
(*it).ptr())->getGeometryPtr()->toShape()));
else
throw Py::TypeError("expect shape in sequence");
}
}else
throw Py::TypeError("expect shape or sequence of shapes");
}
PartExport std::vector<TopoShape> getPyShapes(PyObject *obj) {
std::vector<TopoShape> ret;
getPyShapes(obj,ret);
return ret;
}
struct EdgePoints {
gp_Pnt v1, v2;
std::list<TopoDS_Edge>::iterator it;
@@ -425,12 +452,74 @@ public:
add_varargs_method("__fromPythonOCC__",&Module::fromPythonOCC,
"__fromPythonOCC__(occ) -- Helper method to convert a pythonocc shape to an internal shape"
);
add_varargs_method("clearShapeCache",&Module::clearShapeCache,
"clearShapeCache() -- Clears internal shape cache"
);
add_keyword_method("getShape",&Module::getShape,
"getShape(obj,subname=None,mat=None,needSubElement=False,transform=True,retType=0):\n"
"Obtain the the TopoShape of a given object with SubName reference\n\n"
"* obj: the input object\n"
"* subname: dot separated sub-object reference\n"
"* mat: the current transformation matrix\n"
"* needSubElement: if False, ignore the sub-element (e.g. Face1, Edge1) reference in 'subname'\n"
"* transform: if False, then skip obj's transformation. Use this if mat already include obj's\n"
" transformation matrix\n"
"* retType: 0: return TopoShape,\n"
" 1: return (shape,subObj,mat), where subObj is the object referenced in 'subname',\n"
" and 'mat' is the accumulated transformation matrix of that sub-object.\n"
" 2: same as 1, but make sure 'subObj' is resolved if it is a link.\n"
"* refine: refine the returned shape"
);
add_varargs_method("splitSubname",&Module::splitSubname,
"splitSubname(subname) -> list(sub,mapped,subElement)\n"
"Split the given subname into a list\n\n"
"sub: subname without any sub-element reference\n"
"mapped: mapped element name, or '' if none\n"
"subElement: old style element name, or '' if none"
);
add_varargs_method("joinSubname",&Module::joinSubname,
"joinSubname(sub,mapped,subElement) -> subname\n"
);
initialize("This is a module working with shapes."); // register with Python
}
virtual ~Module() {}
private:
virtual Py::Object invoke_method_keyword( void *method_def,
const Py::Tuple &args, const Py::Dict &keywords ) override
{
try {
return Py::ExtensionModule<Module>::invoke_method_keyword(method_def, args, keywords);
}
catch (const Standard_Failure &e) {
std::string str;
Standard_CString msg = e.GetMessageString();
str += typeid(e).name();
str += " ";
if (msg) {str += msg;}
else {str += "No OCCT Exception Message";}
Base::Console().Error("%s\n", str.c_str());
throw Py::Exception(Part::PartExceptionOCCError, str);
}
catch (const Base::Exception &e) {
std::string str;
str += "FreeCAD exception thrown (";
str += e.what();
str += ")";
e.ReportException();
throw Py::RuntimeError(str);
}
catch (const std::exception &e) {
std::string str;
str += "C++ exception thrown (";
str += e.what();
str += ")";
Base::Console().Error("%s\n", str.c_str());
throw Py::RuntimeError(str);
}
}
virtual Py::Object invoke_method_varargs(void *method_def, const Py::Tuple &args)
{
try {
@@ -638,21 +727,13 @@ private:
TopoDS_Compound Comp;
builder.MakeCompound(Comp);
try {
Py::Sequence list(pcObj);
for (Py::Sequence::iterator it = list.begin(); it != list.end(); ++it) {
if (PyObject_TypeCheck((*it).ptr(), &(Part::TopoShapePy::Type))) {
const TopoDS_Shape& sh = static_cast<TopoShapePy*>((*it).ptr())->
getTopoShapePtr()->getShape();
if (!sh.IsNull())
builder.Add(Comp, sh);
}
PY_TRY {
for(auto &s : getPyShapes(pcObj)) {
const auto &sh = s.getShape();
if (!sh.IsNull())
builder.Add(Comp, sh);
}
}
catch (Standard_Failure& e) {
throw Py::Exception(PartExceptionOCCError, e.GetMessageString());
}
} _PY_CATCH_OCC(throw Py::Exception())
return Py::asObject(new TopoShapeCompoundPy(new TopoShape(Comp)));
}
Py::Object makeShell(const Py::Tuple& args)
@@ -1980,6 +2061,95 @@ private:
throw Py::Exception(PartExceptionOCCError, e.what());
}
}
Py::Object getShape(const Py::Tuple& args, const Py::Dict &kwds) {
PyObject *pObj;
const char *subname = 0;
PyObject *pyMat = 0;
PyObject *needSubElement = Py_False;
PyObject *transform = Py_True;
PyObject *noElementMap = Py_False;
PyObject *refine = Py_False;
short retType = 0;
static char* kwd_list[] = {"obj", "subname", "mat",
"needSubElement","transform","retType","noElementMap","refine",0};
if(!PyArg_ParseTupleAndKeywords(args.ptr(), kwds.ptr(), "O!|sO!OOhOO", kwd_list,
&App::DocumentObjectPy::Type, &pObj, &subname, &Base::MatrixPy::Type, &pyMat,
&needSubElement,&transform,&retType,&noElementMap,&refine))
throw Py::Exception();
App::DocumentObject *obj =
static_cast<App::DocumentObjectPy*>(pObj)->getDocumentObjectPtr();
App::DocumentObject *subObj = 0;
Base::Matrix4D mat;
if(pyMat)
mat = *static_cast<Base::MatrixPy*>(pyMat)->getMatrixPtr();
auto shape = Feature::getTopoShape(obj,subname,PyObject_IsTrue(needSubElement),
&mat,&subObj,retType==2,PyObject_IsTrue(transform),PyObject_IsTrue(noElementMap));
if(PyObject_IsTrue(refine)) {
// shape = TopoShape(0,shape.Hasher).makERefine(shape);
BRepBuilderAPI_RefineModel mkRefine(shape.getShape());
shape.setShape(mkRefine.Shape());
}
Py::Object sret(shape2pyshape(shape));
if(retType==0)
return sret;
return Py::TupleN(sret,Py::Object(new Base::MatrixPy(new Base::Matrix4D(mat))),
subObj?Py::Object(subObj->getPyObject(),true):Py::Object());
}
Py::Object clearShapeCache(const Py::Tuple &args) {
if (!PyArg_ParseTuple(args.ptr(),""))
throw Py::Exception();
Part::Feature::clearShapeCache();
return Py::Object();
}
Py::Object splitSubname(const Py::Tuple& args) {
const char *subname;
if (!PyArg_ParseTuple(args.ptr(), "s",&subname))
throw Py::Exception();
auto element = Data::ComplexGeoData::findElementName(subname);
std::string sub(subname,element-subname);
Py::List list;
list.append(Py::String(sub));
const char *dot = strchr(element,'.');
if(!dot)
dot = element+strlen(element);
const char *mapped = Data::ComplexGeoData::isMappedElement(element);
if(mapped)
list.append(Py::String(std::string(mapped,dot-mapped)));
else
list.append(Py::String());
if(*dot=='.')
list.append(Py::String(dot+1));
else if(!mapped)
list.append(Py::String(element));
else
list.append(Py::String());
return list;
}
Py::Object joinSubname(const Py::Tuple& args) {
const char *sub;
const char *mapped;
const char *element;
if (!PyArg_ParseTuple(args.ptr(), "sss",&sub,&mapped,&element))
throw Py::Exception();
std::string subname(sub);
if(subname.size() && subname[subname.size()-1]!='.')
subname += '.';
if(mapped && mapped[0]) {
if(!Data::ComplexGeoData::isMappedElement(mapped))
subname += Data::ComplexGeoData::elementMapPrefix();
subname += mapped;
if(element && element[0] && subname[subname.size()-1]!='.')
subname += '.';
}
subname += element;
return Py::String(subname);
}
};
PyObject* initModule()