Merge pull request #13197 from bgbsww/bgbsww-toponamingAppPartPy

Toponaming/Part:  move in  AppPartPy
This commit is contained in:
Chris Hennes
2024-03-28 13:57:30 -05:00
committed by GitHub
5 changed files with 544 additions and 61 deletions

View File

@@ -103,6 +103,8 @@
#include "TopoShapeShellPy.h"
#include "TopoShapeSolidPy.h"
#include "TopoShapeWirePy.h"
#include "TopoShapeOpCode.h"
#include "TopoShapeMapper.h"
#ifdef FCUseFreeType
# include "FT2FC.h"
@@ -421,25 +423,47 @@ public:
add_varargs_method("getFacets",&Module::getFacets,
"getFacets(shape): simplified mesh generation"
);
add_varargs_method("makeCompound",&Module::makeCompound,
#ifdef FC_USE_TNP_FIX
add_keyword_method("makeCompound",&Module::makeCompound,
"makeCompound(list) -- Create a compound out of a list of shapes."
);
add_varargs_method("makeShell",&Module::makeShell,
add_keyword_method("makeShell",&Module::makeShell,
"makeShell(list) -- Create a shell out of a list of faces."
);
add_varargs_method("makeFace",&Module::makeFace,
add_keyword_method("makeFace",&Module::makeFace,
"makeFace(list_of_shapes_or_compound, maker_class_name) -- Create a face (faces) using facemaker class.\n"
"maker_class_name is a string like 'Part::FaceMakerSimple'."
);
add_varargs_method("makeFilledSurface",&Module::makeFilledSurface,
add_keyword_method("makeFilledSurface",&Module::makeFilledSurface,
"makeFilledSurface(list of curves, tolerance) -- Create a surface out of a list of curves."
);
add_varargs_method("makeFilledFace",&Module::makeFilledFace,
add_keyword_method("makeFilledFace",&Module::makeFilledFace,
"makeFilledFace(list) -- Create a face out of a list of edges."
);
add_varargs_method("makeSolid",&Module::makeSolid,
add_keyword_method("makeSolid",&Module::makeSolid,
"makeSolid(shape): Create a solid out of shells of shape. If shape is a compsolid, the overall volume solid is created."
);
#else
add_varargs_method("makeCompound",&Module::makeCompound,
"makeCompound(list) -- Create a compound out of a list of shapes."
);
add_varargs_method("makeShell",&Module::makeShell,
"makeShell(list) -- Create a shell out of a list of faces."
);
add_varargs_method("makeFace",&Module::makeFace,
"makeFace(list_of_shapes_or_compound, maker_class_name) -- Create a face (faces) using facemaker class.\n"
"maker_class_name is a string like 'Part::FaceMakerSimple'."
);
add_varargs_method("makeFilledSurface",&Module::makeFilledSurface,
"makeFilledSurface(list of curves, tolerance) -- Create a surface out of a list of curves."
);
add_varargs_method("makeFilledFace",&Module::makeFilledFace,
"makeFilledFace(list) -- Create a face out of a list of edges."
);
add_varargs_method("makeSolid",&Module::makeSolid,
"makeSolid(shape): Create a solid out of shells of shape. If shape is a compsolid, the overall volume solid is created."
);
#endif
add_varargs_method("makePlane",&Module::makePlane,
"makePlane(length,width,[pnt,dirZ,dirX]) -- Make a plane\n"
"By default pnt=Vector(0,0,0) and dirZ=Vector(0,0,1), dirX is ignored in this case"
@@ -520,15 +544,33 @@ public:
"By default vmin/vmax=bounds of the curve, angle=360, pnt=Vector(0,0,0),\n"
"dir=Vector(0,0,1) and shapetype=Part.Solid"
);
add_varargs_method("makeRuledSurface",&Module::makeRuledSurface,
#ifdef FC_USE_TNP_FIX
add_keyword_method("makeRuledSurface",&Module::makeRuledSurface,
"makeRuledSurface(Edge|Wire,Edge|Wire) -- Make a ruled surface\n"
"Create a ruled surface out of two edges or wires. If wires are used then"
"these must have the same number of edges."
);
add_varargs_method("makeShellFromWires",&Module::makeShellFromWires,
add_keyword_method("makeShellFromWires",&Module::makeShellFromWires,
"makeShellFromWires(Wires) -- Make a shell from wires.\n"
"The wires must have the same number of edges."
);
add_keyword_method("makeLoft",&Module::makeLoft,
"makeLoft(list of wires,[solid=False,ruled=False,closed=False,maxDegree=5]) -- Create a loft shape."
);
#else
add_varargs_method("makeRuledSurface",&Module::makeRuledSurface,
"makeRuledSurface(Edge|Wire,Edge|Wire) -- Make a ruled surface\n"
"Create a ruled surface out of two edges or wires. If wires are used then"
"these must have the same number of edges."
);
add_varargs_method("makeShellFromWires",&Module::makeShellFromWires,
"makeShellFromWires(Wires) -- Make a shell from wires.\n"
"The wires must have the same number of edges."
);
add_varargs_method("makeLoft",&Module::makeLoft,
"makeLoft(list of wires,[solid=False,ruled=False,closed=False,maxDegree=5]) -- Create a loft shape."
);
#endif
add_varargs_method("makeTube",&Module::makeTube,
"makeTube(edge,radius,[continuity,max degree,max segments]) -- Create a tube.\n"
"continuity is a string which must be 'C0','C1','C2','C3','CN','G1' or 'G1',"
@@ -536,9 +578,6 @@ public:
add_varargs_method("makeSweepSurface",&Module::makeSweepSurface,
"makeSweepSurface(edge(path),edge(profile),[float]) -- Create a profile along a path."
);
add_varargs_method("makeLoft",&Module::makeLoft,
"makeLoft(list of wires,[solid=False,ruled=False,closed=False,maxDegree=5]) -- Create a loft shape."
);
add_varargs_method("makeWireString",&Module::makeWireString,
"makeWireString(string,fontdir,fontfile,height,[track]) -- Make list of wires in the form of a string's characters."
);
@@ -885,8 +924,36 @@ private:
}
return list;
}
Py::Object makeCompound(const Py::Tuple& args)
#ifdef FC_USE_TNP_FIX
Py::Object makeCompound(const Py::Tuple& args, const Py::Dict &kwds)
{
PyObject* pcObj;
PyObject* force = Py_True;
TopoShape::SingleShapeCompoundCreationPolicy
policy; // = TopoShape::SingleShapeCompoundCreationPolicy::forceCompound;
PyObject* module = PyImport_ImportModule("PartEnums");
PyObject* policyEnum =
PyObject_GetAttrString(module, (char*)"SingleShapeCompoundCreationPolicy");
const char* op = nullptr;
const std::array<const char*, 4> kwd_list = {"shapes", "force", "op", nullptr};
if (!Base::Wrapped_ParseTupleAndKeywords(args.ptr(),
kwds.ptr(),
"O|O!s",
kwd_list,
&pcObj,
policyEnum,
&force,
&op)) {
throw Py::Exception();
}
policy = static_cast<TopoShape::SingleShapeCompoundCreationPolicy>(PyLong_AsLong(force));
Py_DECREF(policyEnum);
return shape2pyshape(Part::TopoShape().makeElementCompound(getPyShapes(pcObj), op, policy));
#else
Py::Object makeCompound(const Py::Tuple& args)
{
PyObject *pcObj;
if (!PyArg_ParseTuple(args.ptr(), "O", &pcObj))
throw Py::Exception();
@@ -903,9 +970,27 @@ private:
}
} _PY_CATCH_OCC(throw Py::Exception())
return Py::asObject(new TopoShapeCompoundPy(new TopoShape(Comp)));
#endif
}
Py::Object makeShell(const Py::Tuple& args)
#ifdef FC_USE_TNP_FIX
Py::Object makeShell(const Py::Tuple& args, const Py::Dict &kwds)
{
PyObject* obj;
const char* op = nullptr;
const std::array<const char*, 3> kwd_list = {"shapes", "op", nullptr};
if (!Base::Wrapped_ParseTupleAndKeywords(args.ptr(),
kwds.ptr(),
"O|s",
kwd_list,
&obj,
&op)) {
throw Py::Exception();
}
return shape2pyshape(
Part::TopoShape().makeElementBoolean(Part::OpCodes::Shell, getPyShapes(obj), op));
#else
Py::Object makeShell(const Py::Tuple& args)
{
PyObject *obj;
if (!PyArg_ParseTuple(args.ptr(), "O", &obj))
throw Py::Exception();
@@ -939,9 +1024,28 @@ private:
}
return Py::asObject(new TopoShapeShellPy(new TopoShape(shape)));
#endif
}
Py::Object makeFace(const Py::Tuple& args)
#ifdef FC_USE_TNP_FIX
Py::Object makeFace(const Py::Tuple& args, const Py::Dict &kwds)
{
PyObject* obj;
const char* className = nullptr;
const char* op = nullptr;
const std::array<const char*, 4> kwd_list = {"shapes", "class_name", "op", nullptr};
if (!Base::Wrapped_ParseTupleAndKeywords(args.ptr(),
kwds.ptr(),
"O|ss",
kwd_list,
&obj,
&className,
&op)) {
throw Py::Exception();
}
return shape2pyshape(TopoShape().makeElementFace(getPyShapes(obj), op, className));
#else
Py::Object makeFace(const Py::Tuple& args)
{
try {
char* className = nullptr;
PyObject* pcPyShapeOrList = nullptr;
@@ -987,9 +1091,111 @@ private:
e.setPyException();
throw Py::Exception();
}
#endif
}
Py::Object makeFilledSurface(const Py::Tuple &args)
template<class F>
void parseSequence(PyObject *pyObj, const char *err, F f)
{
if (pyObj != Py_None) {
if (!PySequence_Check(pyObj))
throw Py::TypeError(err);
Py::Sequence seq(pyObj);
for (Py::Sequence::iterator it = seq.begin(); it != seq.end(); ++it) {
if (!PySequence_Check((*it).ptr()))
throw Py::TypeError(err);
Py::Sequence tuple((*it).ptr());
if (tuple.size() != 2)
throw Py::TypeError(err);
auto iter = tuple.begin();
if (!PyObject_TypeCheck((*iter).ptr(), &(Part::TopoShapePy::Type)))
throw Py::TypeError(err);
const TopoDS_Shape& sh = static_cast<TopoShapePy*>((*iter).ptr())->getTopoShapePtr()->getShape();
f(sh, (*iter).ptr(), err);
}
}
}
#ifdef FC_USE_TNP_FIX
Py::Object makeFilledSurface(const Py::Tuple &args, const Py::Dict &kwds)
{
TopoShape::BRepFillingParams params;
PyObject* obj = nullptr;
PyObject* pySurface = Py_None;
PyObject* supports = Py_None;
PyObject* orders = Py_None;
PyObject* anisotropy = params.anisotropy ? Py_True : Py_False;
const char* op = nullptr;
const std::array<const char*, 16> kwd_list = {"shapes",
"surface",
"supports",
"orders",
"degree",
"ptsOnCurve",
"numIter",
"anisotropy",
"tol2d",
"tol3d",
"tolG1",
"tolG2",
"maxDegree",
"maxSegments",
"op",
nullptr};
if (!Base::Wrapped_ParseTupleAndKeywords(args.ptr(),
kwds.ptr(),
"O|O!OOIIIOddddIIs",
kwd_list,
&obj,
&pySurface,
&orders,
&params.degree,
&params.ptsoncurve,
&params.numiter,
&anisotropy,
&params.tol2d,
&params.tol3d,
&params.tolG1,
&params.tolG2,
&params.maxdeg,
&params.maxseg,
&op)) {
throw Py::Exception();
}
params.anisotropy = PyObject_IsTrue(anisotropy);
TopoShape surface;
if (pySurface != Py_None) {
surface = *static_cast<TopoShapePy*>(pySurface)->getTopoShapePtr();
}
parseSequence(supports,
"Expects 'supports' to be a sequence of tuple(shape, shape)",
[&](const TopoDS_Shape& s, PyObject* value, const char* err) {
if (!PyObject_TypeCheck(value, &(Part::TopoShapePy::Type))) {
throw Py::TypeError(err);
}
params.supports[s] =
static_cast<TopoShapePy*>(value)->getTopoShapePtr()->getShape();
});
parseSequence(orders,
"Expects 'orders' to be a sequence of tuple(shape, PartEnums.Shape)",
[&](const TopoDS_Shape& s, PyObject* value, const char* err) {
if (!PyLong_Check(value)) {
throw Py::ValueError(err);
}
int order = Py::Int(value);
params.orders[s] = static_cast<TopoShape::Continuity>(order);
return;
});
auto shapes = getPyShapes(obj);
if (shapes.empty()) {
throw Py::ValueError("No input shape");
}
return shape2pyshape(
TopoShape(0, shapes.front().Hasher).makeElementFilledFace(shapes, params, op));
#else
Py::Object makeFilledSurface(const Py::Tuple &args)
{
PyObject *obj;
double tolerance;
if (!PyArg_ParseTuple(args.ptr(), "Od", &obj, &tolerance))
@@ -1019,9 +1225,87 @@ private:
catch (Standard_Failure& e) {
throw Py::Exception(PartExceptionOCCError, e.GetMessageString());
}
#endif
}
Py::Object makeFilledFace(const Py::Tuple& args)
#ifdef FC_USE_TNP_FIX
Py::Object makeFilledFace(const Py::Tuple& args, const Py::Dict &kwds)
{
TopoShape::BRepFillingParams params;
PyObject* obj = nullptr;
PyObject* pySurface = Py_None;
PyObject* supports = Py_None;
PyObject* orders = Py_None;
PyObject* anisotropy = params.anisotropy ? Py_True : Py_False;
const char* op = nullptr;
const std::array<const char*, 16> kwd_list = {"shapes",
"surface",
"supports",
"orders",
"degree",
"ptsOnCurve",
"numIter",
"anisotropy",
"tol2d",
"tol3d",
"tolG1",
"tolG2",
"maxDegree",
"maxSegments",
"op",
nullptr};
if (!Base::Wrapped_ParseTupleAndKeywords(args.ptr(),
kwds.ptr(),
"O|O!OOIIIOddddIIs",
kwd_list,
&obj,
&pySurface,
&orders,
&params.degree,
&params.ptsoncurve,
&params.numiter,
&anisotropy,
&params.tol2d,
&params.tol3d,
&params.tolG1,
&params.tolG2,
&params.maxdeg,
&params.maxseg,
&op)) {
throw Py::Exception();
}
params.anisotropy = PyObject_IsTrue(anisotropy);
TopoShape surface;
if (pySurface != Py_None) {
surface = *static_cast<TopoShapePy*>(pySurface)->getTopoShapePtr();
}
parseSequence(supports,
"Expects 'supports' to be a sequence of tuple(shape, shape)",
[&](const TopoDS_Shape& s, PyObject* value, const char* err) {
if (!PyObject_TypeCheck(value, &(Part::TopoShapePy::Type))) {
throw Py::TypeError(err);
}
params.supports[s] =
static_cast<TopoShapePy*>(value)->getTopoShapePtr()->getShape();
});
parseSequence(orders,
"Expects 'orders' to be a sequence of tuple(shape, PartEnums.Shape)",
[&](const TopoDS_Shape& s, PyObject* value, const char* err) {
if (!PyLong_Check(value)) {
throw Py::ValueError(err);
}
int order = Py::Int(value);
params.orders[s] = static_cast<TopoShape::Continuity>(order);
return;
});
auto shapes = getPyShapes(obj);
if (shapes.empty()) {
throw Py::ValueError("No input shape");
}
return shape2pyshape(
TopoShape(0, shapes.front().Hasher).makeElementFilledFace(shapes, params, op));
#else
Py::Object makeFilledFace(const Py::Tuple& args)
{
// TODO: BRepFeat_SplitShape
PyObject *obj;
PyObject *surf=nullptr;
@@ -1078,9 +1362,28 @@ private:
catch (Standard_Failure& e) {
throw Py::Exception(PartExceptionOCCError, e.GetMessageString());
}
#endif
}
Py::Object makeSolid(const Py::Tuple& args)
#ifdef FC_USE_TNP_FIX
Py::Object makeSolid(const Py::Tuple& args, const Py::Dict &kwds)
{
PyObject* obj;
const char* op = nullptr;
const std::array<const char*, 3> kwd_list = {"shape", "op", nullptr};
if (!Base::Wrapped_ParseTupleAndKeywords(args.ptr(),
kwds.ptr(),
"O!|s",
kwd_list,
&(TopoShapePy::Type),
&obj,
&op)) {
throw Py::Exception();
}
return shape2pyshape(
TopoShape().makeElementSolid(*static_cast<TopoShapePy*>(obj)->getTopoShapePtr(), op));
#else
Py::Object makeSolid(const Py::Tuple& args)
{
PyObject *obj;
if (!PyArg_ParseTuple(args.ptr(), "O!", &(TopoShapePy::Type), &obj))
throw Py::Exception();
@@ -1128,6 +1431,7 @@ private:
errmsg << "Creation of solid failed: " << err.GetMessageString();
throw Py::Exception(PartExceptionOCCError, errmsg.str().c_str());
}
#endif
}
Py::Object makePlane(const Py::Tuple& args)
{
@@ -1684,8 +1988,37 @@ private:
throw Py::Exception(PartExceptionOCCDomainError, "creation of revolved shape failed");
}
}
Py::Object makeRuledSurface(const Py::Tuple& args)
#ifdef FC_USE_TNP_FIX
Py::Object makeRuledSurface(const Py::Tuple& args, const Py::Dict &kwds)
{
const char* op = nullptr;
int orientation = 0;
PyObject *sh1, *sh2;
const std::array<const char*, 5> kwd_list = {"path",
"profile",
"orientation",
"op",
nullptr};
if (!Base::Wrapped_ParseTupleAndKeywords(args.ptr(),
kwds.ptr(),
"O!O!|is",
kwd_list,
&(TopoShapePy::Type),
&sh1,
&(TopoShapePy::Type),
&sh2,
&orientation,
&op)) {
throw Py::Exception();
}
std::vector<TopoShape> shapes;
shapes.push_back(*static_cast<TopoShapePy*>(sh1)->getTopoShapePtr());
shapes.push_back(*static_cast<TopoShapePy*>(sh2)->getTopoShapePtr());
return shape2pyshape(TopoShape().makeElementRuledSurface(shapes, orientation, op));
#else
Py::Object makeRuledSurface(const Py::Tuple& args)
{
// http://opencascade.blogspot.com/2009/10/surface-modeling-part1.html
PyObject *sh1, *sh2;
if (!PyArg_ParseTuple(args.ptr(), "O!O!", &(TopoShapePy::Type), &sh1,
@@ -1711,10 +2044,33 @@ private:
catch (Standard_Failure&) {
throw Py::Exception(PartExceptionOCCError, "creation of ruled surface failed");
}
#endif
}
Py::Object makeShellFromWires(const Py::Tuple& args)
#ifdef FC_USE_TNP_FIX
Py::Object makeShellFromWires(const Py::Tuple& args, const Py::Dict &kwds)
{
PyObject *pylist;
const char* op = nullptr;
const std::array<const char*, 3> kwd_list = {"shape", "op", nullptr};
if (!Base::Wrapped_ParseTupleAndKeywords(args.ptr(),
kwds.ptr(),
"O|s",
kwd_list,
&pylist,
&op)) {
throw Py::Exception();
}
try {
return shape2pyshape(
TopoShape().makeElementShellFromWires(getPyShapes(pylist), /*silent*/ false, op));
}
catch (Standard_Failure&) {
throw Py::Exception(PartExceptionOCCError, "creation of shell failed");
}
#else
Py::Object makeShellFromWires(const Py::Tuple& args)
{
PyObject *pylist;
if (!PyArg_ParseTuple(args.ptr(), "O", &pylist))
throw Py::Exception();
@@ -1735,6 +2091,7 @@ private:
catch (Standard_Failure&) {
throw Py::Exception(PartExceptionOCCError, "creation of shell failed");
}
#endif
}
Py::Object makeTube(const Py::Tuple& args)
{
@@ -1791,24 +2148,77 @@ private:
throw Py::Exception();
try {
#ifdef FC_USE_TNP_FIX
TopoShape mShape = *static_cast<TopoShapePy*>(path)->getTopoShapePtr();
// makeSweep uses GeomFill_Pipe which does not support shape
// history. So use makEPipeShell() as a replacement
return shape2pyshape(
TopoShape(0, mShape.Hasher)
.makeElementPipeShell(
{mShape, *static_cast<TopoShapePy*>(profile)->getTopoShapePtr()},
Part::MakeSolid::noSolid,
Standard_False,
TransitionMode::Transformed,
nullptr,
tolerance));
#else
const TopoDS_Shape& path_shape = static_cast<TopoShapePy*>(path)->getTopoShapePtr()->getShape();
const TopoDS_Shape& prof_shape = static_cast<TopoShapePy*>(profile)->getTopoShapePtr()->getShape();
TopoShape myShape(path_shape);
TopoDS_Shape face = myShape.makeSweep(prof_shape, tolerance, fillMode);
return Py::asObject(new TopoShapeFacePy(new TopoShape(face)));
#endif
}
catch (Standard_Failure& e) {
throw Py::Exception(PartExceptionOCCError, e.GetMessageString());
}
}
Py::Object makeLoft(const Py::Tuple& args)
# ifdef FC_USE_TNP_FIX
Py::Object makeLoft(const Py::Tuple& args, const Py::Dict &kwds)
{
PyObject *pcObj;
PyObject *psolid=Py_False;
PyObject *pruled=Py_False;
PyObject *pclosed=Py_False;
int degMax = 5;
const char* op = nullptr;
const std::array<const char*, 7> kwd_list =
{"shapes", "solid", "ruled", "closed", "max_degree", "op", nullptr};
if (!Base::Wrapped_ParseTupleAndKeywords(args.ptr(),
kwds.ptr(),
"O!|O!O!O!is",
kwd_list,
&(PyList_Type),
&pcObj,
&(PyBool_Type),
&psolid,
&(PyBool_Type),
&pruled,
&(PyBool_Type),
&pclosed,
&degMax,
&op)) {
throw Py::Exception();
}
Standard_Boolean anIsSolid = PyObject_IsTrue(psolid) ? Standard_True : Standard_False;
Standard_Boolean anIsRuled = PyObject_IsTrue(pruled) ? Standard_True : Standard_False;
Standard_Boolean anIsClosed = PyObject_IsTrue(pclosed) ? Standard_True : Standard_False;
return shape2pyshape(TopoShape().makeElementLoft(
getPyShapes(pcObj),
anIsSolid ? Part::IsSolid::solid : Part::IsSolid::notSolid,
anIsRuled ? Part::IsRuled::ruled : Part::IsRuled::notRuled,
anIsClosed ? Part::IsClosed::closed : Part::IsClosed::notClosed,
degMax,
op));
#else
Py::Object makeLoft(const Py::Tuple& args)
{
PyObject *pcObj;
PyObject *psolid=Py_False;
PyObject *pruled=Py_False;
PyObject *pclosed=Py_False;
int degMax = 5;
if (!PyArg_ParseTuple(args.ptr(), "O|O!O!O!i", &pcObj,
&(PyBool_Type), &psolid,
&(PyBool_Type), &pruled,
@@ -1834,7 +2244,7 @@ private:
Standard_Boolean anIsClosed = Base::asBoolean(pclosed);
TopoDS_Shape aResult = myShape.makeLoft(profiles, anIsSolid, anIsRuled, anIsClosed, degMax);
return Py::asObject(new TopoShapePy(new TopoShape(aResult)));
#endif
}
Py::Object makeSplitShape(const Py::Tuple& args)
{
@@ -1846,6 +2256,8 @@ private:
throw Py::Exception();
try {
std::vector<TopoShape> sources;
sources.push_back(*static_cast<TopoShapePy*>(shape)->getTopoShapePtr());
TopoDS_Shape initShape = static_cast<TopoShapePy*>
(shape)->getTopoShapePtr()->getShape();
BRepFeat_SplitShape splitShape(initShape);
@@ -1856,6 +2268,7 @@ private:
Py::Tuple tuple(*it);
Py::TopoShape sh1(tuple[0]);
Py::TopoShape sh2(tuple[1]);
sources.push_back(*sh1.extensionObject()->getTopoShapePtr());
const TopoDS_Shape& shape1= sh1.extensionObject()->getTopoShapePtr()->getShape();
const TopoDS_Shape& shape2= sh2.extensionObject()->getTopoShapePtr()->getShape();
if (shape1.IsNull() || shape2.IsNull())
@@ -1892,15 +2305,28 @@ private:
const TopTools_ListOfShape& l = splitShape.Left();
Py::List list1;
Py::List list2;
#ifdef FC_USE_TNP_FIX
MapperMaker mapper(splitShape);
for (TopTools_ListIteratorOfListOfShape it(d); it.More(); it.Next()) {
TopoShape s(0, sources.front().Hasher);
list1.append(shape2pyshape(
s.makeShapeWithElementMap(it.Value(), mapper, sources, Part::OpCodes::Split)));
}
for (TopTools_ListIteratorOfListOfShape it(l); it.More(); it.Next()) {
TopoShape s(0, sources.front().Hasher);
list2.append(shape2pyshape(
s.makeShapeWithElementMap(it.Value(), mapper, sources, Part::OpCodes::Split)));
}
#else
for (TopTools_ListIteratorOfListOfShape it(d); it.More(); it.Next()) {
list1.append(shape2pyshape(it.Value()));
}
Py::List list2;
for (TopTools_ListIteratorOfListOfShape it(l); it.More(); it.Next()) {
list2.append(shape2pyshape(it.Value()));
}
#endif
Py::Tuple tuple(2);
tuple.setItem(0, list1);
tuple.setItem(1, list2);
@@ -2234,10 +2660,12 @@ private:
"needSubElement", "transform", "retType", "noElementMap",
"refine", nullptr};
if (!Base::Wrapped_ParseTupleAndKeywords(args.ptr(), kwds.ptr(), "O!|sO!O!O!hO!O!", kwd_list,
&App::DocumentObjectPy::Type, &pObj, &subname, &Base::MatrixPy::Type,
&pyMat,
&PyBool_Type, &needSubElement, &PyBool_Type, &transform, &retType,
&PyBool_Type, &noElementMap, &PyBool_Type, &refine)) {
&App::DocumentObjectPy::Type, &pObj, &subname,
&Base::MatrixPy::Type, &pyMat,
&PyBool_Type, &needSubElement,
&PyBool_Type, &transform, &retType,
&PyBool_Type, &noElementMap,
&PyBool_Type, &refine)) {
throw Py::Exception();
}
@@ -2251,9 +2679,12 @@ private:
&mat,&subObj,retType==2,Base::asBoolean(transform),
Base::asBoolean(noElementMap));
if (Base::asBoolean(refine)) {
// shape = TopoShape(0,shape.Hasher).makERefine(shape);
#ifdef FC_USE_TNP_FIX
shape = TopoShape(0, shape.Hasher).makeElementRefine(shape);
#else
BRepBuilderAPI_RefineModel mkRefine(shape.getShape());
shape.setShape(mkRefine.Shape());
#endif
}
Py::Object sret(shape2pyshape(shape));
if(retType==0)

View File

@@ -71,7 +71,7 @@ App::DocumentObjectExecReturn *Box::execute()
// Build a box using the dimension attributes
BRepPrimAPI_MakeBox mkBox(L, W, H);
TopoDS_Shape ResultShape = mkBox.Shape();
this->Shape.setValue(ResultShape);
this->Shape.setValue(ResultShape, false);
return Primitive::execute();
}
catch (Standard_Failure& e) {

View File

@@ -71,3 +71,7 @@ class HLRBRep_TypeOfResultingEdge(IntEnum):
RgNLine = 4
Sharp = 5
class SingleShapeCompoundCreationPolicy(IntEnum):
ReturnShape = 0
ForceCompound = 1

View File

@@ -467,7 +467,7 @@ class PartTestRuledSurface(unittest.TestCase):
self.assertEqual(len(same1), 2)
self.assertEqual(len(same2), 2)
def testRuledSurfaceFromOneObjects(self):
def testRuledSurfaceFromOneObject(self):
sketch = self.Doc.addObject('Sketcher::SketchObject', 'Sketch')
sketch.Placement = FreeCAD.Placement(FreeCAD.Vector(0.000000, 0.000000, 0.000000), App.Rotation(0.707107, 0.000000, 0.000000, 0.707107))
sketch.MapMode = "Deactivated"

View File

@@ -4,6 +4,7 @@ import Part
import unittest
class TopoShapeAssertions:
def assertAttrEqual(self, toposhape, attr_value_list, msg=None):
for attr, value in attr_value_list:
result = toposhape.__getattribute__(
@@ -45,10 +46,22 @@ class TopoShapeTest(unittest.TestCase, TopoShapeAssertions):
def setUp(self):
"""Create a document and some TopoShapes of various types"""
self.doc = App.newDocument("TopoShape")
self.box = Part.makeBox(1, 2, 2)
Part.show(self.box, "Box1")
self.box2 = Part.makeBox(2, 1, 2)
Part.show(self.box2, "Box2")
# self.box = Part.makeBox(1, 2, 2)
# Part.show(self.box, "Box1")
# self.box2 = Part.makeBox(2, 1, 2)
# Part.show(self.box2, "Box2")
# Even on LS3 these boxes have no element maps.
self.doc.addObject("Part::Box", "Box1")
self.doc.Box1.Length = 1
self.doc.Box1.Width = 2
self.doc.Box1.Height = 2
self.doc.addObject("Part::Box", "Box2")
self.doc.Box2.Length = 2
self.doc.Box2.Width = 1
self.doc.Box2.Height = 2
self.doc.recompute()
self.box = self.doc.Box1.Shape
self.box2 = self.doc.Box2.Shape
def tearDown(self):
App.closeDocument("TopoShape")
@@ -155,39 +168,15 @@ class TopoShapeTest(unittest.TestCase, TopoShapeAssertions):
# Todo: This should contain something as soon as the Python interface for Part.Compound TNP exists
# self.assertEqual(len(compound1.ElementMap), 52, "ElementMap is Incorrect: {0}".format(compound1.ElementMap))
self.assertEqual(
len(compound2.ElementReverseMap),
compound2.ElementMapSize,
52,
"ElementMap is Incorrect: {0}".format(compound2.ElementMap),
)
# def testTopoShapeOperations(self):
# compound1 = Part.Compound([self.doc.Box1.Shape, self.doc.Box2.Shape])
# box1ts = self.doc.Box1.Shape
# box2ts = self.doc.Box2.Shape
# face1 = Part.Face(Part.Wire([Part.makeCircle(10)]))
# cut1 = box1ts.cut(box2ts)
# common1 = box1ts.common(box2ts)
# fuse1 = box1ts.fuse(box2ts)
# fuse2 = box1ts.generalFuse([box2ts])
# fuse3 = box1ts.multiFuse([box2ts])
# mirror1 = box1ts.mirror(App.Vector(0, 0, 0), App.Vector(1, 0, 0))
# clean1 = box1ts.cleaned()
# # complement1 = box1ts.complement()
# # fix1 = box1ts.fix()
# rotate1 = box1ts.rotated(App.Vector(0, 0, 0), App.Vector(1, 0, 0), 45)
# scale1 = box1ts.scaled(2)
# translate1 = box1ts.translated((2, 0, 0))
# section1 = box1ts.section(face1)
# slice1 = box1ts.slice(App.Vector(1, 0, 0), 2)
# slice2 = box1ts.slices(App.Vector(1, 0, 0), [2, 3])
# # clean, complement, fix, reverse, scale,
def testPartCommon(self):
self.doc.addObject("Part::MultiCommon", "Common")
self.doc.Common.Shapes = [self.doc.Box1, self.doc.Box2]
self.doc.recompute()
names = list(self.doc.Common.Shape.ElementReverseMap.keys())
names.sort()
if self.doc.Common.Shape.ElementMapVersion != "": # Should be '4' as of Mar 2023.
self.assertKeysInMap(self.doc.Common.Shape.ElementReverseMap,
[
@@ -263,12 +252,71 @@ class TopoShapeTest(unittest.TestCase, TopoShapeAssertions):
self.doc.Fuse.Tool = self.doc.Box2
self.doc.recompute()
if self.doc.Fuse.Shape.ElementMapVersion != "": # Should be '4' as of Mar 2023.
self.assertEqual(len(self.doc.Fuse.Shape.ElementReverseMap), 58)
self.assertEqual(self.doc.Fuse.Shape.ElementMapSize, 58)
self.doc.Fuse.Refine = True
self.doc.recompute()
self.assertEqual(len(self.doc.Fuse.Shape.ElementReverseMap), 38)
self.assertEqual(self.doc.Fuse.Shape.ElementMapSize, 38)
# Shape is an extruded L, with 8 Faces, 12 Vertexes, 18 Edges
def testAppPartmakeCompound(self):
# This doesn't do element maps.
# compound1 = Part.Compound([self.doc.Box1.Shape, self.doc.Box2.Shape])
compound1 = Part.makeCompound([self.doc.Box1.Shape, self.doc.Box2.Shape])
if compound1.ElementMapVersion != "": # Should be '4' as of Mar 2023.
self.assertEqual(compound1.ElementMapSize, 52)
def testAppPartmakeShell(self):
shell1 = Part.makeShell(self.doc.Box1.Shape.Faces)
if shell1.ElementMapVersion != "": # Should be '4' as of Mar 2023.
self.assertEqual(shell1.ElementMapSize, 26)
def testAppPartmakeFace(self):
face1 = Part.makeFace(self.doc.Box1.Shape.Faces[0],"Part::FaceMakerCheese")
if face1.ElementMapVersion != "": # Should be '4' as of Mar 2023.
self.assertEqual(face1.ElementMapSize, 10)
def testAppPartmakeFilledFace(self):
face1 = Part.makeFilledFace(self.doc.Box1.Shape.Faces[3].Edges)
if face1.ElementMapVersion != "": # Should be '4' as of Mar 2023.
self.assertEqual(face1.ElementMapSize, 9)
def testAppPartmakeSolid(self):
solid1 = Part.makeSolid(self.doc.Box1.Shape.Shells[0])
if solid1.ElementMapVersion != "": # Should be '4' as of Mar 2023.
self.assertEqual(solid1.ElementMapSize, 26)
def testAppPartmakeRuled(self):
surface1 = Part.makeRuledSurface(*self.doc.Box1.Shape.Edges[3:5])
if surface1.ElementMapVersion != "": # Should be '4' as of Mar 2023.
self.assertEqual(surface1.ElementMapSize, 9)
def testAppPartmakeShellFromWires(self):
wire1 = self.doc.Box1.Shape.Wires[0] #.copy() Todo: prints double generated/modified warning because
wire2 = self.doc.Box1.Shape.Wires[1] #.copy() Todo: copy() isn't TNP ready yet. Fix when it is.
shell1 = Part.makeShellFromWires([wire1,wire2])
if shell1.ElementMapVersion != "": # Should be '4' as of Mar 2023.
self.assertEqual(shell1.ElementMapSize, 24)
def testAppPartmakeSweepSurface(self):
pass # Todo: This is already fixed in a future commit
# surface1 = Part.makeSweepSurface(*self.doc.Box1.Shape.Faces[3].Edges[0:2],1)
# if surface1.ElementMapVersion != "": # Should be '4' as of Mar 2023.
# self.assertEqual(surface1.ElementMapSize, 7)
def testAppPartmakeLoft(self):
solid2 = Part.makeLoft(self.doc.Box1.Shape.Wires[0:2])
if solid2.ElementMapVersion != "": # Should be '4' as of Mar 2023.
self.assertEqual(solid2.ElementMapSize, 24)
def testAppPartmakeSplitShape(self):
# Todo: Refine this test after all TNP code in place to elimate warnings.
edge1 = self.doc.Box1.Shape.Faces[0].Edges[0].translated(App.Vector(0,0.5,0))
face1 = self.doc.Box1.Shape.Faces[0]
solids1 = Part.makeSplitShape(face1,[(edge1,face1)])
if solids1[0][0].ElementMapVersion != "": # Should be '4' as of Mar 2023.
self.assertEqual(solids1[0][0].ElementMapSize, 9)
self.assertEqual(solids1[1][0].ElementMapSize, 9)
# TODO: Consider the following possible test objects:
# Part::AttachExtension ::init();