Toponaming/Part: Fix all getBaseTopoShape calls. Tests and missing code.
This commit is contained in:
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user