Toponaming/Part: Fix all getBaseTopoShape calls. Tests and missing code.
This commit is contained in:
@@ -893,12 +893,34 @@ private:
|
||||
App::Document *pcDoc = App::GetApplication().getActiveDocument();
|
||||
if (!pcDoc)
|
||||
pcDoc = App::GetApplication().newDocument();
|
||||
#ifndef FC_USE_TNP_FIX
|
||||
TopoShapePy* pShape = static_cast<TopoShapePy*>(pcObj);
|
||||
Part::Feature *pcFeature = static_cast<Part::Feature*>(pcDoc->addObject("Part::Feature", name));
|
||||
// copy the data
|
||||
pcFeature->Shape.setValue(pShape->getTopoShapePtr()->getShape());
|
||||
pcDoc->recompute();
|
||||
return Py::asObject(pcFeature->getPyObject());
|
||||
#else
|
||||
App::Document *pcSrcDoc = nullptr;
|
||||
TopoShape shape;
|
||||
if (PyObject_TypeCheck(pcObj, &TopoShapePy::Type))
|
||||
shape = *static_cast<TopoShapePy*>(pcObj)->getTopoShapePtr();
|
||||
else if (PyObject_TypeCheck(pcObj, &GeometryPy::Type))
|
||||
shape = static_cast<GeometryPy*>(pcObj)->getGeometryPtr()->toShape();
|
||||
else if (PyObject_TypeCheck(pcObj, &App::DocumentObjectPy::Type)) {
|
||||
auto obj = static_cast<App::DocumentObjectPy*>(pcObj)->getDocumentObjectPtr();
|
||||
pcSrcDoc = obj->getDocument();
|
||||
shape = Feature::getTopoShape(obj);
|
||||
}
|
||||
else
|
||||
throw Py::TypeError("Expects argument of type DocumentObject, Shape, or Geometry");
|
||||
Part::Feature *pcFeature = static_cast<Part::Feature*>(pcDoc->addObject("Part::Feature", name));
|
||||
// copy the data
|
||||
pcFeature->Shape.setValue(shape);
|
||||
pcFeature->purgeTouched();
|
||||
return Py::asObject(pcFeature->getPyObject());
|
||||
#endif
|
||||
|
||||
}
|
||||
Py::Object getFacets(const Py::Tuple& args)
|
||||
{
|
||||
|
||||
@@ -1015,6 +1015,22 @@ TopoShape Feature::getTopoShape(const App::DocumentObject* obj,
|
||||
hiddens,
|
||||
lastLink);
|
||||
|
||||
if (needSubElement && shape.shapeType(true) == TopAbs_COMPOUND) {
|
||||
if (shape.countSubShapes(TopAbs_SOLID) == 1)
|
||||
shape = shape.getSubTopoShape(TopAbs_SOLID, 1);
|
||||
else if (shape.countSubShapes(TopAbs_COMPSOLID) == 1)
|
||||
shape = shape.getSubTopoShape(TopAbs_COMPSOLID, 1);
|
||||
else if (shape.countSubShapes(TopAbs_FACE) == 1)
|
||||
shape = shape.getSubTopoShape(TopAbs_FACE, 1);
|
||||
else if (shape.countSubShapes(TopAbs_SHELL) == 1)
|
||||
shape = shape.getSubTopoShape(TopAbs_SHELL, 1);
|
||||
else if (shape.countSubShapes(TopAbs_EDGE) == 1)
|
||||
shape = shape.getSubTopoShape(TopAbs_EDGE, 1);
|
||||
else if (shape.countSubShapes(TopAbs_WIRE) == 1)
|
||||
shape = shape.getSubTopoShape(TopAbs_WIRE, 1);
|
||||
else if (shape.countSubShapes(TopAbs_VERTEX) == 1)
|
||||
shape = shape.getSubTopoShape(TopAbs_VERTEX, 1);
|
||||
}
|
||||
Base::Matrix4D topMat;
|
||||
if (pmat || transform) {
|
||||
// Obtain top level transformation
|
||||
|
||||
@@ -5574,6 +5574,9 @@ TopoShape& TopoShape::makeElementBoolean(const char* maker,
|
||||
for (auto it = shapes.begin(); it != shapes.end(); ++it) {
|
||||
auto& s = *it;
|
||||
if (s.isNull()) {
|
||||
if ( it == shapes.begin() ) {
|
||||
return *this; // Compatible with pre-TNP allowing <null shape>.fuse() behavior
|
||||
}
|
||||
FC_THROWM(NullShapeException, "Null input shape");
|
||||
}
|
||||
if (s.shapeType() == TopAbs_COMPOUND) {
|
||||
|
||||
@@ -66,9 +66,9 @@ Feature::Feature()
|
||||
App::DocumentObjectExecReturn* Feature::recompute()
|
||||
{
|
||||
try {
|
||||
auto baseShape = getBaseShape();
|
||||
auto baseShape = getBaseTopoShape();
|
||||
if (Suppressed.getValue()) {
|
||||
this->Shape.setValue(baseShape);
|
||||
this->Shape.setValue(baseShape.getShape());
|
||||
return StdReturn;
|
||||
}
|
||||
}
|
||||
@@ -106,9 +106,13 @@ TopoShape Feature::getSolid(const TopoShape& shape)
|
||||
if (shape.isNull()) {
|
||||
throw Part::NullShapeException("Null shape");
|
||||
}
|
||||
auto res = shape.getSubTopoShape(TopAbs_SOLID, 1);
|
||||
res.fixSolidOrientation();
|
||||
return res;
|
||||
int count = shape.countSubShapes(TopAbs_SOLID);
|
||||
if(count) {
|
||||
auto res = shape.getSubTopoShape(TopAbs_SOLID,1);
|
||||
res.fixSolidOrientation();
|
||||
return res;
|
||||
}
|
||||
return shape;
|
||||
}
|
||||
|
||||
int Feature::countSolids(const TopoDS_Shape& shape, TopAbs_ShapeEnum type)
|
||||
|
||||
@@ -108,7 +108,7 @@ App::DocumentObjectExecReturn *Chamfer::execute()
|
||||
// The only difference is that the Base property also stores the edges that are to be chamfered
|
||||
Part::TopoShape TopShape;
|
||||
try {
|
||||
TopShape = getBaseShape();
|
||||
TopShape = getBaseTopoShape();
|
||||
}
|
||||
catch (Base::Exception& e) {
|
||||
return new App::DocumentObjectExecReturn(e.what());
|
||||
|
||||
@@ -101,7 +101,7 @@ App::DocumentObjectExecReturn *Draft::execute()
|
||||
// Base shape
|
||||
Part::TopoShape TopShape;
|
||||
try {
|
||||
TopShape = getBaseShape();
|
||||
TopShape = getBaseTopoShape();
|
||||
}
|
||||
catch (Base::Exception& e) {
|
||||
return new App::DocumentObjectExecReturn(e.what());
|
||||
|
||||
@@ -67,7 +67,7 @@ App::DocumentObjectExecReturn *Fillet::execute()
|
||||
{
|
||||
Part::TopoShape TopShape;
|
||||
try {
|
||||
TopShape = getBaseShape();
|
||||
TopShape = getBaseTopoShape();
|
||||
}
|
||||
catch (Base::Exception& e) {
|
||||
return new App::DocumentObjectExecReturn(e.what());
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
#include <Base/Tools.h>
|
||||
|
||||
#include "FeatureGroove.h"
|
||||
#include "Mod/Part/App/TopoShapeOpCode.h"
|
||||
|
||||
|
||||
using namespace PartDesign;
|
||||
@@ -78,6 +79,7 @@ short Groove::mustExecute() const
|
||||
return ProfileBased::mustExecute();
|
||||
}
|
||||
|
||||
#ifndef FC_USE_TNP_FIX
|
||||
App::DocumentObjectExecReturn *Groove::execute()
|
||||
{
|
||||
// Validate parameters
|
||||
@@ -242,6 +244,133 @@ App::DocumentObjectExecReturn *Groove::execute()
|
||||
return new App::DocumentObjectExecReturn(e.what());
|
||||
}
|
||||
}
|
||||
#else
|
||||
App::DocumentObjectExecReturn *Groove::execute()
|
||||
{
|
||||
// Validate parameters
|
||||
double angle = Angle.getValue();
|
||||
if (angle > 360.0)
|
||||
return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP("Exception", "Angle of groove too large"));
|
||||
|
||||
angle = Base::toRadians<double>(angle);
|
||||
if (angle < Precision::Angular())
|
||||
return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP("Exception", "Angle of groove too small"));
|
||||
|
||||
// Reverse angle if selected
|
||||
if (Reversed.getValue() && !Midplane.getValue())
|
||||
angle *= (-1.0);
|
||||
|
||||
TopoShape sketchshape;
|
||||
try {
|
||||
sketchshape = getVerifiedFace();
|
||||
} catch (const Base::Exception& e) {
|
||||
return new App::DocumentObjectExecReturn(e.what());
|
||||
}
|
||||
|
||||
// if the Base property has a valid shape, fuse the prism into it
|
||||
TopoShape base;
|
||||
try {
|
||||
base = getBaseTopoShape();
|
||||
}
|
||||
catch (const Base::Exception&) {
|
||||
std::string text(QT_TRANSLATE_NOOP("Exception", "The requested feature cannot be created. The reason may be that:\n"
|
||||
" - the active Body does not contain a base shape, so there is no\n"
|
||||
" material to be removed;\n"
|
||||
" - the selected sketch does not belong to the active Body."));
|
||||
return new App::DocumentObjectExecReturn(text);
|
||||
}
|
||||
|
||||
updateAxis();
|
||||
|
||||
// get revolve axis
|
||||
Base::Vector3d b = Base.getValue();
|
||||
gp_Pnt pnt(b.x,b.y,b.z);
|
||||
Base::Vector3d v = Axis.getValue();
|
||||
gp_Dir dir(v.x,v.y,v.z);
|
||||
|
||||
try {
|
||||
if (sketchshape.isNull())
|
||||
return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP("Exception", "Creating a face from sketch failed"));
|
||||
|
||||
// Rotate the face by half the angle to get Groove symmetric to sketch plane
|
||||
if (Midplane.getValue()) {
|
||||
gp_Trsf mov;
|
||||
mov.SetRotation(gp_Ax1(pnt, dir), Base::toRadians<double>(Angle.getValue()) * (-1.0) / 2.0);
|
||||
TopLoc_Location loc(mov);
|
||||
sketchshape.move(loc);
|
||||
}
|
||||
|
||||
this->positionByPrevious();
|
||||
auto invObjLoc = getLocation().Inverted();
|
||||
pnt.Transform(invObjLoc.Transformation());
|
||||
dir.Transform(invObjLoc.Transformation());
|
||||
base.move(invObjLoc);
|
||||
sketchshape.move(invObjLoc);
|
||||
|
||||
// Check distance between sketchshape and axis - to avoid failures and crashes
|
||||
TopExp_Explorer xp;
|
||||
xp.Init(sketchshape.getShape(), TopAbs_FACE);
|
||||
for (;xp.More(); xp.Next()) {
|
||||
if (checkLineCrossesFace(gp_Lin(pnt, dir), TopoDS::Face(xp.Current())))
|
||||
return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP("Exception", "Revolve axis intersects the sketch"));
|
||||
}
|
||||
|
||||
// revolve the face to a solid
|
||||
TopoShape result(0);
|
||||
try {
|
||||
result.makeElementRevolve(sketchshape, gp_Ax1(pnt, dir), angle);
|
||||
}catch(Standard_Failure &) {
|
||||
return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP("Exception", "Could not revolve the sketch!"));
|
||||
}
|
||||
this->AddSubShape.setValue(result);
|
||||
|
||||
if(base.isNull()) {
|
||||
Shape.setValue(getSolid(result));
|
||||
return App::DocumentObject::StdReturn;
|
||||
}
|
||||
|
||||
result.Tag = -getID();
|
||||
TopoShape boolOp(0);
|
||||
|
||||
try {
|
||||
const char *maker;
|
||||
switch (getAddSubType()) {
|
||||
case Additive:
|
||||
maker = Part::OpCodes::Fuse;
|
||||
break;
|
||||
// case Intersecting:
|
||||
// maker = Part::OpCodes::Common;
|
||||
// break;
|
||||
default:
|
||||
maker = Part::OpCodes::Cut;
|
||||
}
|
||||
// this->fixShape(result);
|
||||
boolOp.makeElementBoolean(maker, {base,result});
|
||||
}catch(Standard_Failure &) {
|
||||
return new App::DocumentObjectExecReturn("Failed to cut base feature");
|
||||
}
|
||||
boolOp = this->getSolid(boolOp);
|
||||
if (boolOp.isNull())
|
||||
return new App::DocumentObjectExecReturn("Resulting shape is not a solid");
|
||||
|
||||
boolOp = refineShapeIfActive(boolOp);
|
||||
Shape.setValue(getSolid(boolOp));
|
||||
return App::DocumentObject::StdReturn;
|
||||
}
|
||||
catch (Standard_Failure& e) {
|
||||
|
||||
if (std::string(e.GetMessageString()) == "TopoDS::Face")
|
||||
return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP("Exception", "Could not create face from sketch.\n"
|
||||
"Intersecting sketch entities in a sketch are not allowed."));
|
||||
else
|
||||
return new App::DocumentObjectExecReturn(e.GetMessageString());
|
||||
}
|
||||
catch (Base::Exception& e) {
|
||||
return new App::DocumentObjectExecReturn(e.what());
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
bool Groove::suggestReversed()
|
||||
{
|
||||
|
||||
@@ -192,13 +192,13 @@ App::DocumentObjectExecReturn* Helix::execute()
|
||||
}
|
||||
|
||||
// if the Base property has a valid shape, fuse the AddShape into it
|
||||
TopoDS_Shape base;
|
||||
TopoShape base;
|
||||
try {
|
||||
base = getBaseShape();
|
||||
base = getBaseTopoShape();
|
||||
}
|
||||
catch (const Base::Exception&) {
|
||||
// fall back to support (for legacy features)
|
||||
base = TopoDS_Shape();
|
||||
base = TopoShape();
|
||||
}
|
||||
|
||||
// update Axis from ReferenceAxis
|
||||
@@ -213,7 +213,7 @@ App::DocumentObjectExecReturn* Helix::execute()
|
||||
this->positionByPrevious();
|
||||
TopLoc_Location invObjLoc = this->getLocation().Inverted();
|
||||
|
||||
base.Move(invObjLoc);
|
||||
base.move(invObjLoc);
|
||||
|
||||
|
||||
std::vector<TopoDS_Wire> wires;
|
||||
@@ -246,7 +246,7 @@ App::DocumentObjectExecReturn* Helix::execute()
|
||||
|
||||
AddSubShape.setValue(result);
|
||||
|
||||
if (base.IsNull()) {
|
||||
if (base.isNull()) {
|
||||
|
||||
if (getAddSubType() == FeatureAddSub::Subtractive)
|
||||
return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP("Exception", "Error: There is nothing to subtract"));
|
||||
@@ -261,7 +261,7 @@ App::DocumentObjectExecReturn* Helix::execute()
|
||||
|
||||
if (getAddSubType() == FeatureAddSub::Additive) {
|
||||
|
||||
BRepAlgoAPI_Fuse mkFuse(base, result);
|
||||
BRepAlgoAPI_Fuse mkFuse(base.getShape(), result);
|
||||
if (!mkFuse.IsDone())
|
||||
return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP("Exception", "Error: Adding the helix failed"));
|
||||
// we have to get the solids (fuse sometimes creates compounds)
|
||||
@@ -284,14 +284,14 @@ App::DocumentObjectExecReturn* Helix::execute()
|
||||
TopoDS_Shape boolOp;
|
||||
|
||||
if (Outside.getValue()) { // are we subtracting the inside or the outside of the profile.
|
||||
BRepAlgoAPI_Common mkCom(result, base);
|
||||
BRepAlgoAPI_Common mkCom(result, base.getShape());
|
||||
if (!mkCom.IsDone())
|
||||
return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP("Exception", "Error: Intersecting the helix failed"));
|
||||
boolOp = this->getSolid(mkCom.Shape());
|
||||
|
||||
}
|
||||
else {
|
||||
BRepAlgoAPI_Cut mkCut(base, result);
|
||||
BRepAlgoAPI_Cut mkCut(base.getShape(), result);
|
||||
if (!mkCut.IsDone())
|
||||
return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP("Exception", "Error: Subtracting the helix failed"));
|
||||
boolOp = this->getSolid(mkCut.Shape());
|
||||
|
||||
@@ -1652,7 +1652,7 @@ static gp_Pnt toPnt(gp_Vec dir)
|
||||
|
||||
App::DocumentObjectExecReturn* Hole::execute()
|
||||
{
|
||||
TopoDS_Shape profileshape;
|
||||
TopoShape profileshape;
|
||||
try {
|
||||
profileshape = getVerifiedFace();
|
||||
}
|
||||
@@ -1661,9 +1661,9 @@ App::DocumentObjectExecReturn* Hole::execute()
|
||||
}
|
||||
|
||||
// Find the base shape
|
||||
TopoDS_Shape base;
|
||||
TopoShape base;
|
||||
try {
|
||||
base = getBaseShape();
|
||||
base = getBaseTopoShape();
|
||||
}
|
||||
catch (const Base::Exception&) {
|
||||
std::string text(QT_TRANSLATE_NOOP("Exception", "The requested feature cannot be created. The reason may be that:\n"
|
||||
@@ -1680,12 +1680,12 @@ App::DocumentObjectExecReturn* Hole::execute()
|
||||
this->positionByPrevious();
|
||||
TopLoc_Location invObjLoc = this->getLocation().Inverted();
|
||||
|
||||
base.Move(invObjLoc);
|
||||
base.move(invObjLoc);
|
||||
|
||||
if (profileshape.IsNull())
|
||||
if (profileshape.isNull())
|
||||
return new App::DocumentObjectExecReturn(
|
||||
QT_TRANSLATE_NOOP("Exception", "Hole error: Creating a face from sketch failed"));
|
||||
profileshape.Move(invObjLoc);
|
||||
profileshape.move(invObjLoc);
|
||||
|
||||
/* Build the prototype hole */
|
||||
|
||||
@@ -1883,11 +1883,11 @@ App::DocumentObjectExecReturn* Hole::execute()
|
||||
protoHole = mkFuse.Shape();
|
||||
}
|
||||
|
||||
TopoDS_Compound holes = findHoles(profileshape, protoHole);
|
||||
TopoDS_Compound holes = findHoles(profileshape.getShape(), protoHole);
|
||||
this->AddSubShape.setValue(holes);
|
||||
|
||||
// For some reason it is faster to do the cut through a BooleanOperation.
|
||||
BRepAlgoAPI_Cut mkBool(base, holes);
|
||||
BRepAlgoAPI_Cut mkBool(base.getShape(), holes);
|
||||
if (!mkBool.IsDone()) {
|
||||
return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP("Exception", "Boolean operation failed"));
|
||||
}
|
||||
@@ -1896,13 +1896,13 @@ App::DocumentObjectExecReturn* Hole::execute()
|
||||
|
||||
// We have to get the solids (fuse sometimes creates compounds)
|
||||
base = getSolid(result);
|
||||
if (base.IsNull())
|
||||
if (base.isNull())
|
||||
return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP("Exception", "Resulting shape is not a solid"));
|
||||
base = refineShapeIfActive(base);
|
||||
|
||||
|
||||
|
||||
int solidCount = countSolids(base);
|
||||
int solidCount = countSolids(base.getShape());
|
||||
if (solidCount > 1) {
|
||||
return new App::DocumentObjectExecReturn(
|
||||
QT_TRANSLATE_NOOP("Exception", "Result has multiple solids: that is not currently supported."));
|
||||
|
||||
@@ -43,6 +43,7 @@
|
||||
#include <Mod/Part/App/FaceMakerCheese.h>
|
||||
|
||||
#include "FeatureLoft.h"
|
||||
#include "Mod/Part/App/TopoShapeOpCode.h"
|
||||
|
||||
|
||||
using namespace PartDesign;
|
||||
@@ -69,6 +70,7 @@ short Loft::mustExecute() const
|
||||
return ProfileBased::mustExecute();
|
||||
}
|
||||
|
||||
#ifndef FC_USE_TNP_FIX
|
||||
App::DocumentObjectExecReturn *Loft::execute()
|
||||
{
|
||||
auto getSectionShape =
|
||||
@@ -330,7 +332,215 @@ App::DocumentObjectExecReturn *Loft::execute()
|
||||
return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP("Exception", "Loft: A fatal error occurred when making the loft"));
|
||||
}
|
||||
}
|
||||
#else
|
||||
std::vector<Part::TopoShape>
|
||||
Loft::getSectionShape(const char *name,
|
||||
App::DocumentObject *obj,
|
||||
const std::vector<std::string> &subs,
|
||||
size_t expected_size)
|
||||
{
|
||||
std::vector<TopoShape> shapes;
|
||||
if (subs.empty() || std::find(subs.begin(), subs.end(), std::string()) != subs.end()) {
|
||||
shapes.push_back(Part::Feature::getTopoShape(obj));
|
||||
if (shapes.back().isNull())
|
||||
FC_THROWM(Part::NullShapeException, "Failed to get shape of "
|
||||
<< name << " " << App::SubObjectT(obj, "").getSubObjectFullName(obj->getDocument()->getName()));
|
||||
} else {
|
||||
for (const auto &sub : subs) {
|
||||
shapes.push_back(Part::Feature::getTopoShape(obj, sub.c_str(), /*needSubElement*/true));
|
||||
if (shapes.back().isNull())
|
||||
FC_THROWM(Part::NullShapeException, "Failed to get shape of " << name << " "
|
||||
<< App::SubObjectT(obj, sub.c_str()).getSubObjectFullName(obj->getDocument()->getName()));
|
||||
}
|
||||
}
|
||||
auto compound = TopoShape().makeElementCompound(shapes, "", TopoShape::SingleShapeCompoundCreationPolicy::returnShape);
|
||||
auto wires = compound.getSubTopoShapes(TopAbs_WIRE);
|
||||
auto edges = compound.getSubTopoShapes(TopAbs_EDGE, TopAbs_WIRE); // get free edges and make wires from it
|
||||
if (edges.size()) {
|
||||
auto extra = TopoShape().makeElementWires(edges).getSubTopoShapes(TopAbs_WIRE);
|
||||
wires.insert(wires.end(), extra.begin(), extra.end());
|
||||
}
|
||||
const char *msg = "Sections need to have the same amount of wires or vertices as the base section";
|
||||
if (!wires.empty()) {
|
||||
if (expected_size && expected_size != wires.size())
|
||||
FC_THROWM(Base::CADKernelError, msg);
|
||||
return wires;
|
||||
}
|
||||
auto vertices = compound.getSubTopoShapes(TopAbs_VERTEX);
|
||||
if (vertices.empty())
|
||||
FC_THROWM(Base::CADKernelError, "Invalid " << name << " shape, expecting either wires or vertices");
|
||||
if (expected_size && expected_size != vertices.size())
|
||||
FC_THROWM(Base::CADKernelError, msg);
|
||||
return vertices;
|
||||
}
|
||||
|
||||
App::DocumentObjectExecReturn *Loft::execute(void)
|
||||
{
|
||||
std::vector<TopoShape> wires;
|
||||
try {
|
||||
wires = getSectionShape("Profile", Profile.getValue(), Profile.getSubValues());
|
||||
} catch (const Base::Exception& e) {
|
||||
return new App::DocumentObjectExecReturn(e.what());
|
||||
}
|
||||
|
||||
// if the Base property has a valid shape, fuse the pipe into it
|
||||
TopoShape base;
|
||||
try {
|
||||
base = getBaseTopoShape();
|
||||
} catch (const Base::Exception&) {
|
||||
}
|
||||
|
||||
try {
|
||||
//setup the location
|
||||
this->positionByPrevious();
|
||||
auto invObjLoc = this->getLocation().Inverted();
|
||||
if(!base.isNull())
|
||||
base.move(invObjLoc);
|
||||
|
||||
//build up multisections
|
||||
auto multisections = Sections.getSubListValues();
|
||||
if(multisections.empty())
|
||||
return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP("Exception", "Loft: At least one section is needed"));
|
||||
|
||||
std::vector<std::vector<TopoShape>> wiresections;
|
||||
wiresections.reserve(wires.size());
|
||||
for(auto& wire : wires)
|
||||
wiresections.emplace_back(1, wire);
|
||||
|
||||
for (const auto &subSet : multisections) {
|
||||
int i=0;
|
||||
for (const auto &s : getSectionShape("Section", subSet.first, subSet.second, wiresections.size()))
|
||||
wiresections[i++].push_back(s);
|
||||
}
|
||||
|
||||
TopoShape result(0);
|
||||
std::vector<TopoShape> shapes;
|
||||
|
||||
// if (SplitProfile.getValue()) {
|
||||
// for (auto &wires : wiresections) {
|
||||
// for(auto& wire : wires)
|
||||
// wire.move(invObjLoc);
|
||||
// shapes.push_back(TopoShape(0).makeElementLoft(
|
||||
// wires, Part::IsSolid::solid, Ruled.getValue(), Closed.getValue()));
|
||||
// }
|
||||
// } else {
|
||||
//build all shells
|
||||
std::vector<TopoShape> shells;
|
||||
for (auto &wires : wiresections) {
|
||||
for(auto& wire : wires)
|
||||
wire.move(invObjLoc);
|
||||
shells.push_back(TopoShape(0).makeElementLoft(
|
||||
wires, Part::IsSolid::notSolid, Ruled.getValue()? Part::IsRuled::ruled : Part::IsRuled::notRuled, Closed.getValue() ? Part::IsClosed::closed : Part::IsClosed::notClosed));
|
||||
// }
|
||||
|
||||
//build the top and bottom face, sew the shell and build the final solid
|
||||
TopoShape front;
|
||||
if (wiresections[0].front().shapeType() != TopAbs_VERTEX) {
|
||||
front = getVerifiedFace();
|
||||
if (front.isNull())
|
||||
return new App::DocumentObjectExecReturn(
|
||||
QT_TRANSLATE_NOOP("Exception", "Loft: Creating a face from sketch failed"));
|
||||
front.move(invObjLoc);
|
||||
}
|
||||
|
||||
TopoShape back;
|
||||
if (wiresections[0].back().shapeType() != TopAbs_VERTEX) {
|
||||
std::vector<TopoShape> backwires;
|
||||
for(auto& wires : wiresections)
|
||||
backwires.push_back(wires.back());
|
||||
back = TopoShape(0).makeElementFace(backwires);
|
||||
}
|
||||
|
||||
if (!front.isNull() || !back.isNull()) {
|
||||
BRepBuilderAPI_Sewing sewer;
|
||||
sewer.SetTolerance(Precision::Confusion());
|
||||
if (!front.isNull())
|
||||
sewer.Add(front.getShape());
|
||||
if (!back.isNull())
|
||||
sewer.Add(back.getShape());
|
||||
for(auto& s : shells)
|
||||
sewer.Add(s.getShape());
|
||||
|
||||
sewer.Perform();
|
||||
|
||||
if (!front.isNull())
|
||||
shells.push_back(front);
|
||||
if (!back.isNull())
|
||||
shells.push_back(back);
|
||||
// result = result.makeElementShape(sewer,shells);
|
||||
result = result.makeShapeWithElementMap(sewer.SewedShape(), Part::MapperSewing(sewer), shells, Part::OpCodes::Sewing);
|
||||
}
|
||||
|
||||
if(!result.countSubShapes(TopAbs_SHELL))
|
||||
return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP("Exception", "Loft: Failed to create shell"));
|
||||
shapes = result.getSubTopoShapes(TopAbs_SHELL);
|
||||
}
|
||||
|
||||
for (auto &s : shapes) {
|
||||
//build the solid
|
||||
s = s.makeElementSolid();
|
||||
BRepClass3d_SolidClassifier SC(s.getShape());
|
||||
SC.PerformInfinitePoint(Precision::Confusion());
|
||||
if ( SC.State() == TopAbs_IN)
|
||||
s.setShape(s.getShape().Reversed(),false);
|
||||
}
|
||||
|
||||
AddSubShape.setValue(result.makeElementCompound(shapes, nullptr, Part::TopoShape::SingleShapeCompoundCreationPolicy::returnShape));
|
||||
|
||||
if (shapes.size() > 1)
|
||||
result.makeElementFuse(shapes);
|
||||
else
|
||||
result = shapes.front();
|
||||
|
||||
if(base.isNull()) {
|
||||
Shape.setValue(getSolid(result));
|
||||
return App::DocumentObject::StdReturn;
|
||||
}
|
||||
|
||||
result.Tag = -getID();
|
||||
TopoShape boolOp(0,getDocument()->getStringHasher());
|
||||
|
||||
const char *maker;
|
||||
switch(getAddSubType()) {
|
||||
case Additive:
|
||||
maker = Part::OpCodes::Fuse;
|
||||
break;
|
||||
case Subtractive:
|
||||
maker = Part::OpCodes::Cut;
|
||||
break;
|
||||
// case Intersecting:
|
||||
// maker = Part::OpCodes::Common;
|
||||
// break;
|
||||
default:
|
||||
return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP("Exception", "Unknown operation type"));
|
||||
}
|
||||
try {
|
||||
boolOp.makeElementBoolean(maker, {base,result});
|
||||
}
|
||||
catch(Standard_Failure &e) {
|
||||
return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP("Exception", "Failed to perform boolean operation"));
|
||||
}
|
||||
boolOp = this->getSolid(boolOp);
|
||||
// lets check if the result is a solid
|
||||
if (boolOp.isNull())
|
||||
return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP("Exception", "Resulting shape is not a solid"));
|
||||
|
||||
boolOp = refineShapeIfActive(boolOp);
|
||||
Shape.setValue(getSolid(boolOp));
|
||||
return App::DocumentObject::StdReturn;
|
||||
}
|
||||
catch (Standard_Failure& e) {
|
||||
return new App::DocumentObjectExecReturn(e.GetMessageString());
|
||||
}
|
||||
catch (const Base::Exception& e) {
|
||||
return new App::DocumentObjectExecReturn(e.what());
|
||||
}
|
||||
catch (...) {
|
||||
return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP("Exception", "Loft: A fatal error occurred when making the loft"));
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
PROPERTY_SOURCE(PartDesign::AdditiveLoft, PartDesign::Loft)
|
||||
AdditiveLoft::AdditiveLoft() {
|
||||
|
||||
@@ -50,6 +50,11 @@ public:
|
||||
}
|
||||
//@}
|
||||
|
||||
static std::vector<Part::TopoShape> getSectionShape(const char *name,
|
||||
App::DocumentObject *obj,
|
||||
const std::vector<std::string> &subname,
|
||||
size_t expected_size = 0);
|
||||
|
||||
protected:
|
||||
// handle changed property
|
||||
void handleChangedPropertyType(Base::XMLReader& reader, const char* TypeName, App::Property* prop) override;
|
||||
|
||||
@@ -48,7 +48,11 @@
|
||||
#include <Mod/Part/App/FaceMakerCheese.h>
|
||||
|
||||
#include "FeaturePipe.h"
|
||||
#include "Mod/Part/App/TopoShapeOpCode.h"
|
||||
#include "Mod/Part/App/TopoShapeMapper.h"
|
||||
#include "FeatureLoft.h"
|
||||
|
||||
FC_LOG_LEVEL_INIT("PartDesign",true,true);
|
||||
|
||||
using namespace PartDesign;
|
||||
|
||||
@@ -98,6 +102,7 @@ short Pipe::mustExecute() const
|
||||
return ProfileBased::mustExecute();
|
||||
}
|
||||
|
||||
#ifndef FC_USE_TNP_FIX
|
||||
App::DocumentObjectExecReturn *Pipe::execute()
|
||||
{
|
||||
auto getSectionShape = [](App::DocumentObject* feature,
|
||||
@@ -433,8 +438,345 @@ App::DocumentObjectExecReturn *Pipe::execute()
|
||||
return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP("Exception", "A fatal error occurred when making the pipe"));
|
||||
}
|
||||
}
|
||||
#else
|
||||
App::DocumentObjectExecReturn *Pipe::execute()
|
||||
{
|
||||
auto getSectionShape = [](App::DocumentObject* feature,
|
||||
const std::vector<std::string>& subs) -> TopoDS_Shape {
|
||||
if (!feature || !feature->isDerivedFrom(Part::Feature::getClassTypeId()))
|
||||
throw Base::TypeError("Pipe: Invalid profile/section");
|
||||
|
||||
void Pipe::setupAlgorithm(BRepOffsetAPI_MakePipeShell& mkPipeShell, TopoDS_Shape& auxshape) {
|
||||
auto subName = subs.empty() ? "" : subs.front();
|
||||
|
||||
// only take the entire shape when we have a sketch selected, but
|
||||
// not a point of the sketch
|
||||
if (feature->isDerivedFrom(Part::Part2DObject::getClassTypeId())
|
||||
&& subName.compare(0, 6, "Vertex") != 0)
|
||||
return static_cast<Part::Part2DObject*>(feature)->Shape.getValue();
|
||||
else {
|
||||
if (subName.empty())
|
||||
throw Base::ValueError("Pipe: No valid subelement linked in Part::Feature");
|
||||
return static_cast<Part::Feature*>(feature)->Shape.getShape().getSubShape(
|
||||
subName.c_str());
|
||||
}
|
||||
};
|
||||
|
||||
auto addWiresToWireSections =
|
||||
[](TopoDS_Shape& section, std::vector<std::vector<TopoDS_Shape>>& wiresections) -> size_t {
|
||||
TopExp_Explorer ex;
|
||||
size_t i = 0;
|
||||
bool initialWireSectionsEmpty = wiresections.empty();
|
||||
for (ex.Init(section, TopAbs_WIRE); ex.More(); ex.Next(), ++i) {
|
||||
// if profile was just a point then this is where we can first set our list
|
||||
if (i >= wiresections.size()) {
|
||||
if (initialWireSectionsEmpty)
|
||||
wiresections.emplace_back(1, ex.Current());
|
||||
else
|
||||
throw Base::ValueError(
|
||||
"Pipe: Sections need to have the same amount of inner wires (except "
|
||||
"profile and last section, which can be points)");
|
||||
}
|
||||
else
|
||||
wiresections[i].push_back(TopoDS::Wire(ex.Current()));
|
||||
}
|
||||
return i;
|
||||
};
|
||||
|
||||
// TODO: currently we can only allow planar faces, so add that check.
|
||||
// The reason for this is that with other faces in front, we could not use the
|
||||
// current simulate approach and build the start and end face from the wires.
|
||||
// As the shell begins always at the spine and not the profile, the sketchshape
|
||||
// cannot be used directly as front face. We would need a method to translate
|
||||
// the front shape to match the shell starting position somehow...
|
||||
std::vector<TopoDS_Wire> wires;
|
||||
TopoDS_Shape profilePoint;
|
||||
|
||||
// if the Base property has a valid shape, fuse the pipe into it
|
||||
TopoShape base;
|
||||
try {
|
||||
base = getBaseTopoShape();
|
||||
} catch (const Base::Exception&) {
|
||||
base = TopoShape();
|
||||
}
|
||||
|
||||
try {
|
||||
// setup the location
|
||||
this->positionByPrevious();
|
||||
TopLoc_Location invObjLoc = this->getLocation().Inverted();
|
||||
if (!base.isNull())
|
||||
base.move(invObjLoc);
|
||||
|
||||
// setup the profile section
|
||||
TopoDS_Shape profileShape = getSectionShape(Profile.getValue(),
|
||||
Profile.getSubValues());
|
||||
if (profileShape.IsNull())
|
||||
return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP("Exception", "Pipe: Could not obtain profile shape"));
|
||||
|
||||
// build the paths
|
||||
App::DocumentObject* spine = Spine.getValue();
|
||||
if (!(spine && spine->isDerivedFrom<Part::Feature>()))
|
||||
return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP("Exception", "No spine linked"));
|
||||
|
||||
std::vector<std::string> subedge = Spine.getSubValues();
|
||||
TopoDS_Shape path;
|
||||
const Part::TopoShape& shape = static_cast<Part::Feature*>(spine)->Shape.getValue();
|
||||
buildPipePath(shape, subedge, path);
|
||||
path.Move(invObjLoc);
|
||||
|
||||
// auxiliary
|
||||
TopoDS_Shape auxpath;
|
||||
if (Mode.getValue() == 3) {
|
||||
App::DocumentObject* auxspine = AuxillerySpine.getValue();
|
||||
if (!(auxspine && auxspine->isDerivedFrom<Part::Feature>()))
|
||||
return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP("Exception", "No auxiliary spine linked."));
|
||||
std::vector<std::string> auxsubedge = AuxillerySpine.getSubValues();
|
||||
|
||||
const Part::TopoShape& auxshape =
|
||||
static_cast<Part::Feature*>(auxspine)->Shape.getValue();
|
||||
buildPipePath(auxshape, auxsubedge, auxpath);
|
||||
auxpath.Move(invObjLoc);
|
||||
}
|
||||
|
||||
// build up multisections
|
||||
auto multisections = Sections.getSubListValues();
|
||||
std::vector<std::vector<TopoDS_Shape>> wiresections;
|
||||
|
||||
size_t numWires = addWiresToWireSections(profileShape, wiresections);
|
||||
if (numWires == 0) {
|
||||
// profileShape had no wires so only other valid option is single point section
|
||||
TopExp_Explorer ex;
|
||||
size_t i = 0;
|
||||
for (ex.Init(profileShape, TopAbs_VERTEX); ex.More(); ex.Next(), ++i)
|
||||
profilePoint = ex.Current();
|
||||
if (i > 1)
|
||||
return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP("Exception",
|
||||
"Pipe: Only one isolated point is needed if using a sketch with isolated "
|
||||
"points for section"));
|
||||
}
|
||||
|
||||
if (!profilePoint.IsNull() && (Transformation.getValue() != 1 || multisections.empty()))
|
||||
return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP("Exception",
|
||||
"Pipe: At least one section is needed when using a single point for profile"));
|
||||
|
||||
// maybe we need a scaling law
|
||||
Handle(Law_Function) scalinglaw;
|
||||
|
||||
bool isLastSectionVertex = false;
|
||||
|
||||
// see if we shall use multiple sections
|
||||
if (Transformation.getValue() == 1) {
|
||||
// TODO: we need to order the sections to prevent occ from crashing,
|
||||
// as makepipeshell connects the sections in the order of adding
|
||||
for (auto& subSet : multisections) {
|
||||
if (!subSet.first->isDerivedFrom(Part::Feature::getClassTypeId()))
|
||||
return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP("Exception",
|
||||
"Pipe: All sections need to be part features"));
|
||||
|
||||
// if the section is an object's face then take just the face
|
||||
TopoDS_Shape shape = getSectionShape(subSet.first, subSet.second);
|
||||
if (shape.IsNull())
|
||||
return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP("Exception",
|
||||
"Pipe: Could not obtain section shape"));
|
||||
|
||||
size_t nWiresAdded = addWiresToWireSections(shape, wiresections);
|
||||
if (nWiresAdded == 0) {
|
||||
TopExp_Explorer ex;
|
||||
size_t i = 0;
|
||||
for (ex.Init(shape, TopAbs_VERTEX); ex.More(); ex.Next(), ++i) {
|
||||
if (isLastSectionVertex)
|
||||
return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP("Exception",
|
||||
"Pipe: Only the profile and last section can be vertices"));
|
||||
isLastSectionVertex = true;
|
||||
for (auto& wires : wiresections)
|
||||
wires.push_back(ex.Current());
|
||||
}
|
||||
}
|
||||
|
||||
if (!isLastSectionVertex && nWiresAdded < wiresections.size())
|
||||
return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP("Exception",
|
||||
"Multisections need to have the same amount of inner wires as the base "
|
||||
"section"));
|
||||
}
|
||||
}
|
||||
/*//build the law functions instead
|
||||
else if (Transformation.getValue() == 2) {
|
||||
if (ScalingData.getValues().size()<1)
|
||||
return new App::DocumentObjectExecReturn("No valid data given for linear scaling mode");
|
||||
|
||||
Handle(Law_Linear) lin = new Law_Linear();
|
||||
lin->Set(0, 1, 1, ScalingData[0].x);
|
||||
|
||||
scalinglaw = lin;
|
||||
}
|
||||
else if (Transformation.getValue() == 3) {
|
||||
if (ScalingData.getValues().size()<1)
|
||||
return new App::DocumentObjectExecReturn("No valid data given for S-shape scaling mode");
|
||||
|
||||
Handle(Law_S) s = new Law_S();
|
||||
s->Set(0, 1, ScalingData[0].y, 1, ScalingData[0].x, ScalingData[0].z);
|
||||
|
||||
scalinglaw = s;
|
||||
}*/
|
||||
|
||||
// Verify that path is not a null shape
|
||||
if (path.IsNull())
|
||||
return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP(
|
||||
"Exception", "Path must not be a null shape"));
|
||||
|
||||
// build all shells
|
||||
std::vector<TopoDS_Shape> shells;
|
||||
|
||||
TopoDS_Shape copyProfilePoint(profilePoint);
|
||||
if (!profilePoint.IsNull())
|
||||
copyProfilePoint.Move(invObjLoc);
|
||||
|
||||
std::vector<TopoDS_Wire> frontwires, backwires;
|
||||
for (auto& wires : wiresections) {
|
||||
BRepOffsetAPI_MakePipeShell mkPS(TopoDS::Wire(path));
|
||||
setupAlgorithm(mkPS, auxpath);
|
||||
|
||||
if (!scalinglaw) {
|
||||
if (!profilePoint.IsNull())
|
||||
mkPS.Add(copyProfilePoint);
|
||||
|
||||
for (auto& wire : wires) {
|
||||
wire.Move(invObjLoc);
|
||||
mkPS.Add(wire);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (!profilePoint.IsNull())
|
||||
mkPS.SetLaw(copyProfilePoint, scalinglaw);
|
||||
|
||||
for (auto& wire : wires) {
|
||||
wire.Move(invObjLoc);
|
||||
mkPS.SetLaw(wire, scalinglaw);
|
||||
}
|
||||
}
|
||||
|
||||
if (!mkPS.IsReady())
|
||||
return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP("Exception", "Pipe could not be built"));
|
||||
|
||||
shells.push_back(mkPS.Shape());
|
||||
|
||||
if (!mkPS.Shape().Closed()) {
|
||||
// shell is not closed - use simulate to get the end wires
|
||||
TopTools_ListOfShape sim;
|
||||
mkPS.Simulate(2, sim);
|
||||
|
||||
// Note that while we call them front and back, these sections
|
||||
// appear to correspond to the front or back of the path. When one
|
||||
// or both ends of the pipe are points, one or both of these wires
|
||||
// (and eventually faces) will be null.
|
||||
frontwires.push_back(TopoDS::Wire(sim.First()));
|
||||
backwires.push_back(TopoDS::Wire(sim.Last()));
|
||||
}
|
||||
}
|
||||
|
||||
BRepBuilderAPI_MakeSolid mkSolid;
|
||||
|
||||
if (!frontwires.empty() || !backwires.empty()) {
|
||||
BRepBuilderAPI_Sewing sewer;
|
||||
sewer.SetTolerance(Precision::Confusion());
|
||||
|
||||
// build the end faces, sew the shell and build the final solid
|
||||
if (!frontwires.empty()) {
|
||||
TopoDS_Shape front = Part::FaceMakerCheese::makeFace(frontwires);
|
||||
sewer.Add(front);
|
||||
}
|
||||
if (!backwires.empty()) {
|
||||
TopoDS_Shape back = Part::FaceMakerCheese::makeFace(backwires);
|
||||
sewer.Add(back);
|
||||
}
|
||||
for (TopoDS_Shape& s : shells)
|
||||
sewer.Add(s);
|
||||
|
||||
sewer.Perform();
|
||||
mkSolid.Add(TopoDS::Shell(sewer.SewedShape()));
|
||||
} else {
|
||||
// shells are already closed - add them directly
|
||||
for (TopoDS_Shape& s : shells) {
|
||||
mkSolid.Add(TopoDS::Shell(s));
|
||||
}
|
||||
}
|
||||
|
||||
if (!mkSolid.IsDone())
|
||||
return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP("Exception", "Result is not a solid"));
|
||||
|
||||
TopoDS_Shape result = mkSolid.Shape();
|
||||
BRepClass3d_SolidClassifier SC(result);
|
||||
SC.PerformInfinitePoint(Precision::Confusion());
|
||||
if (SC.State() == TopAbs_IN) {
|
||||
result.Reverse();
|
||||
}
|
||||
|
||||
//result.Move(invObjLoc);
|
||||
AddSubShape.setValue(result); // TODO: Do we need to toposhape here?
|
||||
|
||||
if (base.isNull()) {
|
||||
if (getAddSubType() == FeatureAddSub::Subtractive)
|
||||
return new App::DocumentObjectExecReturn(
|
||||
QT_TRANSLATE_NOOP("Exception", "Pipe: There is nothing to subtract from"));
|
||||
|
||||
result = refineShapeIfActive(result);
|
||||
Shape.setValue(getSolid(result));
|
||||
return App::DocumentObject::StdReturn;
|
||||
}
|
||||
|
||||
if (getAddSubType() == FeatureAddSub::Additive) {
|
||||
|
||||
BRepAlgoAPI_Fuse mkFuse(base.getShape(), result);
|
||||
if (!mkFuse.IsDone())
|
||||
return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP("Exception", "Adding the pipe failed"));
|
||||
// we have to get the solids (fuse sometimes creates compounds)
|
||||
TopoShape boolOp = this->getSolid(mkFuse.Shape());
|
||||
// lets check if the result is a solid
|
||||
if (boolOp.isNull())
|
||||
return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP("Exception", "Resulting shape is not a solid"));
|
||||
|
||||
int solidCount = countSolids(boolOp.getShape());
|
||||
if (solidCount > 1) {
|
||||
return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP("Exception",
|
||||
"Result has multiple solids: that is not currently supported."));
|
||||
}
|
||||
|
||||
boolOp = refineShapeIfActive(boolOp);
|
||||
Shape.setValue(getSolid(boolOp));
|
||||
}
|
||||
else if (getAddSubType() == FeatureAddSub::Subtractive) {
|
||||
|
||||
BRepAlgoAPI_Cut mkCut(base.getShape(), result);
|
||||
if (!mkCut.IsDone())
|
||||
return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP("Exception", "Subtracting the pipe failed"));
|
||||
// we have to get the solids (fuse sometimes creates compounds)
|
||||
TopoShape boolOp = this->getSolid(mkCut.Shape());
|
||||
// lets check if the result is a solid
|
||||
if (boolOp.isNull())
|
||||
return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP("Exception", "Resulting shape is not a solid"));
|
||||
|
||||
int solidCount = countSolids(boolOp.getShape());
|
||||
if (solidCount > 1) {
|
||||
return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP("Exception",
|
||||
"Result has multiple solids: that is not currently supported."));
|
||||
}
|
||||
|
||||
boolOp = refineShapeIfActive(boolOp);
|
||||
Shape.setValue(getSolid(boolOp));
|
||||
}
|
||||
|
||||
return App::DocumentObject::StdReturn;
|
||||
}
|
||||
catch (Standard_Failure& e) {
|
||||
|
||||
return new App::DocumentObjectExecReturn(e.GetMessageString());
|
||||
}
|
||||
catch (...) {
|
||||
return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP("Exception", "A fatal error occurred when making the pipe"));
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
void Pipe::setupAlgorithm(BRepOffsetAPI_MakePipeShell& mkPipeShell, const TopoDS_Shape& auxshape) {
|
||||
|
||||
mkPipeShell.SetTolerance(Precision::Confusion());
|
||||
|
||||
@@ -581,7 +923,6 @@ void Pipe::buildPipePath(const Part::TopoShape& shape, const std::vector<std::st
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PROPERTY_SOURCE(PartDesign::AdditivePipe, PartDesign::Pipe)
|
||||
AdditivePipe::AdditivePipe() {
|
||||
addSubType = Additive;
|
||||
|
||||
@@ -54,12 +54,24 @@ public:
|
||||
const char* getViewProviderName() const override {
|
||||
return "PartDesignGui::ViewProviderPipe";
|
||||
}
|
||||
App::DocumentObjectExecReturn *_execute(ProfileBased *feat,
|
||||
const TopoShape &path,
|
||||
const TopLoc_Location &invObjLoc = TopLoc_Location(),
|
||||
int transition = 0,
|
||||
const TopoShape &auxpath = TopoShape(),
|
||||
bool auxCurveLinear = true,
|
||||
int mode = 2,
|
||||
const Base::Vector3d &binormalVector = Base::Vector3d(),
|
||||
int transformation = 0,
|
||||
const std::vector<App::PropertyLinkSubList::SubSet> &multisections = {},
|
||||
bool moveProfile = false,
|
||||
bool rotateProfile = false);
|
||||
|
||||
protected:
|
||||
/// get the given edges and all their tangent ones
|
||||
void getContinuousEdges(Part::TopoShape TopShape, std::vector< std::string >& SubNames);
|
||||
void buildPipePath(const Part::TopoShape& input, const std::vector<std::string>& edges, TopoDS_Shape& result);
|
||||
void setupAlgorithm(BRepOffsetAPI_MakePipeShell& mkPipeShell, TopoDS_Shape& auxshape);
|
||||
void setupAlgorithm(BRepOffsetAPI_MakePipeShell& mkPipeShell, const TopoDS_Shape& auxshape);
|
||||
/// handle changed property
|
||||
void handleChangedPropertyType(Base::XMLReader& reader, const char* TypeName, App::Property* prop) override;
|
||||
|
||||
|
||||
@@ -87,9 +87,9 @@ App::DocumentObjectExecReturn *Pocket::execute()
|
||||
}
|
||||
|
||||
// if the Base property has a valid shape, fuse the prism into it
|
||||
TopoDS_Shape base;
|
||||
TopoShape base;
|
||||
try {
|
||||
base = getBaseShape();
|
||||
base = getBaseTopoShape();
|
||||
}
|
||||
catch (const Base::Exception&) {
|
||||
std::string text(QT_TRANSLATE_NOOP("Exception", ("The requested feature cannot be created. The reason may be that:\n"
|
||||
@@ -109,7 +109,7 @@ App::DocumentObjectExecReturn *Pocket::execute()
|
||||
this->positionByPrevious();
|
||||
TopLoc_Location invObjLoc = this->getLocation().Inverted();
|
||||
|
||||
base.Move(invObjLoc);
|
||||
base.move(invObjLoc);
|
||||
|
||||
Base::Vector3d pocketDirection = computeDirection(SketchVector);
|
||||
|
||||
@@ -145,7 +145,7 @@ App::DocumentObjectExecReturn *Pocket::execute()
|
||||
|
||||
std::string method(Type.getValueAsString());
|
||||
if (method == "UpToFirst" || method == "UpToFace") {
|
||||
if (base.IsNull())
|
||||
if (base.isNull())
|
||||
return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP("Exception", "Pocket: Extruding up to a face is only possible if the sketch is located on a face"));
|
||||
|
||||
// Note: This will return an unlimited planar face if support is a datum plane
|
||||
@@ -161,7 +161,7 @@ App::DocumentObjectExecReturn *Pocket::execute()
|
||||
getFaceFromLinkSub(upToFace, UpToFace);
|
||||
upToFace.Move(invObjLoc);
|
||||
}
|
||||
getUpToFace(upToFace, base, profileshape, method, dir);
|
||||
getUpToFace(upToFace, base.getShape(), profileshape, method, dir);
|
||||
addOffsetToFace(upToFace, dir, Offset.getValue());
|
||||
|
||||
// BRepFeat_MakePrism(..., 2, 1) in combination with PerForm(upToFace) is buggy when the
|
||||
@@ -176,10 +176,10 @@ App::DocumentObjectExecReturn *Pocket::execute()
|
||||
supportface = TopoDS_Face();
|
||||
TopoDS_Shape prism;
|
||||
PrismMode mode = PrismMode::CutFromBase;
|
||||
generatePrism(prism, method, base, profileshape, supportface, upToFace, dir, mode, Standard_True);
|
||||
generatePrism(prism, method, base.getShape(), profileshape, supportface, upToFace, dir, mode, Standard_True);
|
||||
|
||||
// And the really expensive way to get the SubShape...
|
||||
BRepAlgoAPI_Cut mkCut(base, prism);
|
||||
BRepAlgoAPI_Cut mkCut(base.getShape(), prism);
|
||||
if (!mkCut.IsDone())
|
||||
return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP("Exception", "Pocket: Up to face: Could not get SubShape!"));
|
||||
// FIXME: In some cases this affects the Shape property: It is set to the same shape as the SubShape!!!!
|
||||
@@ -212,7 +212,7 @@ App::DocumentObjectExecReturn *Pocket::execute()
|
||||
this->AddSubShape.setValue(prism);
|
||||
|
||||
// Cut the SubShape out of the base feature
|
||||
BRepAlgoAPI_Cut mkCut(base, prism);
|
||||
BRepAlgoAPI_Cut mkCut(base.getShape(), prism);
|
||||
if (!mkCut.IsDone())
|
||||
return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP("Exception", "Pocket: Cut out of base feature failed"));
|
||||
TopoDS_Shape result = mkCut.Shape();
|
||||
|
||||
@@ -100,12 +100,12 @@ App::DocumentObjectExecReturn *Revolution::execute()
|
||||
}
|
||||
|
||||
// if the Base property has a valid shape, fuse the AddShape into it
|
||||
TopoDS_Shape base;
|
||||
TopoShape base;
|
||||
try {
|
||||
base = getBaseShape();
|
||||
base = getBaseTopoShape();
|
||||
} catch (const Base::Exception&) {
|
||||
// fall back to support (for legacy features)
|
||||
base = TopoDS_Shape();
|
||||
base = TopoShape();
|
||||
}
|
||||
|
||||
// update Axis from ReferenceAxis
|
||||
@@ -131,7 +131,7 @@ App::DocumentObjectExecReturn *Revolution::execute()
|
||||
TopLoc_Location invObjLoc = this->getLocation().Inverted();
|
||||
pnt.Transform(invObjLoc.Transformation());
|
||||
dir.Transform(invObjLoc.Transformation());
|
||||
base.Move(invObjLoc);
|
||||
base.move(invObjLoc);
|
||||
sketchshape.Move(invObjLoc);
|
||||
|
||||
// Check distance between sketchshape and axis - to avoid failures and crashes
|
||||
@@ -169,7 +169,7 @@ App::DocumentObjectExecReturn *Revolution::execute()
|
||||
if (!Ex.More())
|
||||
supportface = TopoDS_Face();
|
||||
RevolMode mode = RevolMode::None;
|
||||
generateRevolution(result, base, sketchshape, supportface, upToFace, gp_Ax1(pnt, dir), method, mode, Standard_True);
|
||||
generateRevolution(result, base.getShape(), sketchshape, supportface, upToFace, gp_Ax1(pnt, dir), method, mode, Standard_True);
|
||||
}
|
||||
else {
|
||||
bool midplane = Midplane.getValue();
|
||||
@@ -182,9 +182,9 @@ App::DocumentObjectExecReturn *Revolution::execute()
|
||||
// set the additive shape property for later usage in e.g. pattern
|
||||
this->AddSubShape.setValue(result);
|
||||
|
||||
if (!base.IsNull()) {
|
||||
if (!base.isNull()) {
|
||||
// Let's call algorithm computing a fuse operation:
|
||||
BRepAlgoAPI_Fuse mkFuse(base, result);
|
||||
BRepAlgoAPI_Fuse mkFuse(base.getShape(), result);
|
||||
// Let's check if the fusion has been successful
|
||||
if (!mkFuse.IsDone())
|
||||
throw Part::BooleanException(QT_TRANSLATE_NOOP("Exception", "Fusion with base feature failed"));
|
||||
|
||||
@@ -908,11 +908,11 @@ void ProfileBased::addOffsetToFace(TopoShape& upToFace, const gp_Dir& dir, doubl
|
||||
double ProfileBased::getThroughAllLength() const
|
||||
{
|
||||
TopoDS_Shape profileshape;
|
||||
TopoDS_Shape base;
|
||||
TopoShape base;
|
||||
profileshape = getVerifiedFace();
|
||||
base = getBaseShape();
|
||||
base = getBaseTopoShape();
|
||||
Bnd_Box box;
|
||||
BRepBndLib::Add(base, box);
|
||||
BRepBndLib::Add(base.getShape(), box);
|
||||
BRepBndLib::Add(profileshape, box);
|
||||
box.SetGap(0.0);
|
||||
// The diagonal of the bounding box, plus 1% extra to eliminate risk of
|
||||
|
||||
@@ -66,7 +66,7 @@ App::DocumentObjectExecReturn *Thickness::execute()
|
||||
// Base shape
|
||||
Part::TopoShape TopShape;
|
||||
try {
|
||||
TopShape = getBaseShape();
|
||||
TopShape = getBaseTopoShape();
|
||||
}
|
||||
catch (Base::Exception &e) {
|
||||
return new App::DocumentObjectExecReturn(e.what());
|
||||
|
||||
@@ -549,7 +549,7 @@ class TestTopologicalNamingProblem(unittest.TestCase):
|
||||
self.Doc.recompute()
|
||||
# Assert
|
||||
self.assertEqual(len(body.Shape.childShapes()), 1)
|
||||
self.assertEqual(body.Shape.childShapes()[0].ElementMapSize, 26)
|
||||
self.assertEqual(body.Shape.childShapes()[0].ElementMapSize, 30)
|
||||
|
||||
def testPartDesignElementMapPipe(self):
|
||||
# Arrange
|
||||
|
||||
Reference in New Issue
Block a user