Skip to content

Commit

Permalink
Part: changes to Part Module
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
realthunder committed Jul 14, 2019
1 parent e162dc5 commit 773c2b7
Show file tree
Hide file tree
Showing 58 changed files with 3,187 additions and 713 deletions.
2 changes: 2 additions & 0 deletions src/App/DocumentObject.h
Expand Up @@ -501,6 +501,8 @@ class AppExport DocumentObject: public App::TransactionalObject
*/
virtual int canLoadPartial() const {return 0;}

virtual void onUpdateElementReference(const Property *) {}

/** Allow object to redirect a subname path
*
* @param ss: input as the current subname path from \a topParent leading
Expand Down
21 changes: 21 additions & 0 deletions src/App/PropertyContainerPyImp.cpp
Expand Up @@ -506,6 +506,27 @@ PyObject *PropertyContainerPy::getCustomAttributes(const char* attr) const
}
}
return dict;
} else if(Base::streq(attr,"Shape")
&& getPropertyContainerPtr()->isDerivedFrom(App::DocumentObject::getClassTypeId()))
{
// Special treatment of Shape property
static PyObject *_getShape = 0;
if(!_getShape) {
_getShape = Py_None;
PyObject *mod = PyImport_ImportModule("Part");
if(!mod) {
PyErr_Clear();
} else {
Py::Object pyMod = Py::asObject(mod);
if(pyMod.hasAttr("getShape"))
_getShape = Py::new_reference_to(pyMod.getAttr("getShape"));
}
}
if(_getShape != Py_None) {
Py::Tuple args(1);
args.setItem(0,Py::Object(const_cast<PropertyContainerPy*>(this)));
return PyObject_CallObject(_getShape, args.ptr());
}
}

return 0;
Expand Down
1 change: 1 addition & 0 deletions src/Mod/Part/App/AppPart.cpp
Expand Up @@ -441,6 +441,7 @@ PyMOD_INIT_FUNC(Part)
Part::Fillet ::init();
Part::Chamfer ::init();
Part::Compound ::init();
Part::Compound2 ::init();
Part::Extrusion ::init();
Part::Revolution ::init();
Part::Mirroring ::init();
Expand Down
206 changes: 188 additions & 18 deletions src/Mod/Part/App/AppPartPy.cpp
Expand Up @@ -126,18 +126,16 @@
#include "ImportStep.h"
#include "edgecluster.h"
#include "FaceMaker.h"
#include "PartFeature.h"
#include "PartPyCXX.h"
#include "modelRefine.h"

#ifdef FCUseFreeType
# include "FT2FC.h"
#endif

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
Expand All @@ -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;
Expand Down Expand Up @@ -425,12 +452,74 @@ class Module : public Py::ExtensionModule<Module>
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 {
Expand Down Expand Up @@ -638,21 +727,13 @@ class Module : public Py::ExtensionModule<Module>
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)
Expand Down Expand Up @@ -1980,6 +2061,95 @@ class Module : public Py::ExtensionModule<Module>
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()
Expand Down
12 changes: 5 additions & 7 deletions src/Mod/Part/App/FeatureChamfer.cpp
Expand Up @@ -51,16 +51,14 @@ App::DocumentObjectExecReturn *Chamfer::execute(void)
App::DocumentObject* link = Base.getValue();
if (!link)
return new App::DocumentObjectExecReturn("No object linked");
if (!link->getTypeId().isDerivedFrom(Part::Feature::getClassTypeId()))
return new App::DocumentObjectExecReturn("Linked object is not a Part object");
Part::Feature *base = static_cast<Part::Feature*>(Base.getValue());

try {
BRepFilletAPI_MakeChamfer mkChamfer(base->Shape.getValue());
auto baseShape = Feature::getShape(link);
BRepFilletAPI_MakeChamfer mkChamfer(baseShape);
TopTools_IndexedMapOfShape mapOfEdges;
TopTools_IndexedDataMapOfShapeListOfShape mapEdgeFace;
TopExp::MapShapesAndAncestors(base->Shape.getValue(), TopAbs_EDGE, TopAbs_FACE, mapEdgeFace);
TopExp::MapShapes(base->Shape.getValue(), TopAbs_EDGE, mapOfEdges);
TopExp::MapShapesAndAncestors(baseShape, TopAbs_EDGE, TopAbs_FACE, mapEdgeFace);
TopExp::MapShapes(baseShape, TopAbs_EDGE, mapOfEdges);

std::vector<FilletElement> values = Edges.getValues();
for (std::vector<FilletElement>::iterator it = values.begin(); it != values.end(); ++it) {
Expand All @@ -75,7 +73,7 @@ App::DocumentObjectExecReturn *Chamfer::execute(void)
TopoDS_Shape shape = mkChamfer.Shape();
if (shape.IsNull())
return new App::DocumentObjectExecReturn("Resulting shape is null");
ShapeHistory history = buildHistory(mkChamfer, TopAbs_FACE, shape, base->Shape.getValue());
ShapeHistory history = buildHistory(mkChamfer, TopAbs_FACE, shape, baseShape);
this->Shape.setValue(shape);

// make sure the 'PropertyShapeHistory' is not safed in undo/redo (#0001889)
Expand Down
20 changes: 15 additions & 5 deletions src/Mod/Part/App/FeatureCompound.cpp
Expand Up @@ -72,12 +72,10 @@ App::DocumentObjectExecReturn *Compound::execute(void)

const std::vector<DocumentObject*>& links = Links.getValues();
for (std::vector<DocumentObject*>::const_iterator it = links.begin(); it != links.end(); ++it) {
if (*it && (*it)->getTypeId().isDerivedFrom(Part::Feature::getClassTypeId())) {
Part::Feature* fea = static_cast<Part::Feature*>(*it);

auto pos = tempLinks.insert(fea);
if (*it) {
auto pos = tempLinks.insert(*it);
if (pos.second) {
const TopoDS_Shape& sh = fea->Shape.getValue();
const TopoDS_Shape& sh = Feature::getShape(*it);
if (!sh.IsNull()) {
builder.Add(comp, sh);
TopTools_IndexedMapOfShape faceMap;
Expand Down Expand Up @@ -108,3 +106,15 @@ App::DocumentObjectExecReturn *Compound::execute(void)
}
}

////////////////////////////////////////////////////////////////////////

PROPERTY_SOURCE(Part::Compound2, Part::Compound)

Compound2::Compound2() {
Shape.setStatus(App::Property::Transient,true);
}

void Compound2::onDocumentRestored() {
auto res = execute();
delete res;
}
8 changes: 8 additions & 0 deletions src/Mod/Part/App/FeatureCompound.h
Expand Up @@ -52,6 +52,14 @@ class Compound : public Part::Feature
//@}
};

/// Same as Part::Compound, except it marks the Shape as transient, and rebuild it during restore
class Compound2 : public Compound {
PROPERTY_HEADER(Part::Compound2);
public:
Compound2();
virtual void onDocumentRestored() override;
};

} //namespace Part


Expand Down

0 comments on commit 773c2b7

Please sign in to comment.