Toponaming: Remove all FC_USE_TNP_FIX protected old code
This commit is contained in:
@@ -62,9 +62,6 @@ Feature::Feature()
|
||||
BaseFeature.setStatus(App::Property::Hidden, true);
|
||||
|
||||
App::SuppressibleExtension::initExtension(this);
|
||||
#ifndef FC_USE_TNP_FIX
|
||||
Suppressed.setStatus(App::Property::Status::Hidden, true);
|
||||
#endif
|
||||
}
|
||||
|
||||
App::DocumentObjectExecReturn* Feature::recompute()
|
||||
|
||||
@@ -114,7 +114,6 @@ App::DocumentObjectExecReturn *Chamfer::execute()
|
||||
return new App::DocumentObjectExecReturn(e.what());
|
||||
}
|
||||
|
||||
#ifdef FC_USE_TNP_FIX
|
||||
TopShape.setTransform(Base::Matrix4D());
|
||||
|
||||
auto edges = UseAllEdges.getValue() ? TopShape.getSubTopoShapes(TopAbs_EDGE)
|
||||
@@ -124,24 +123,6 @@ App::DocumentObjectExecReturn *Chamfer::execute()
|
||||
return new App::DocumentObjectExecReturn(
|
||||
QT_TRANSLATE_NOOP("Exception", "No edges specified"));
|
||||
}
|
||||
#else
|
||||
std::vector<std::string> SubNames = std::vector<std::string>(Base.getSubValues());
|
||||
|
||||
if (UseAllEdges.getValue()){
|
||||
SubNames.clear();
|
||||
std::string edgeTypeName = Part::TopoShape::shapeName(TopAbs_EDGE); //"Edge"
|
||||
int count = TopShape.countSubElements(edgeTypeName.c_str());
|
||||
for (int ii = 0; ii < count; ii++){
|
||||
std::ostringstream edgeName;
|
||||
edgeName << edgeTypeName << ii+1;
|
||||
SubNames.push_back(edgeName.str());
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::string> FaceNames;
|
||||
|
||||
getContinuousEdges(TopShape, SubNames, FaceNames);
|
||||
#endif
|
||||
const int chamferType = ChamferType.getValue();
|
||||
const double size = Size.getValue();
|
||||
double size2 = Size2.getValue();
|
||||
@@ -155,22 +136,10 @@ App::DocumentObjectExecReturn *Chamfer::execute()
|
||||
|
||||
this->positionByBaseFeature();
|
||||
|
||||
#ifdef FC_USE_TNP_FIX
|
||||
if ( static_cast<Part::ChamferType>(chamferType) == Part::ChamferType::distanceAngle ) {
|
||||
size2 = angle;
|
||||
}
|
||||
#else
|
||||
//If no element is selected, then we use a copy of previous feature.
|
||||
if (SubNames.empty()) {
|
||||
this->Shape.setValue(TopShape);
|
||||
return App::DocumentObject::StdReturn;
|
||||
}
|
||||
// create an untransformed copy of the basefeature shape
|
||||
Part::TopoShape baseShape(TopShape);
|
||||
baseShape.setTransform(Base::Matrix4D());
|
||||
#endif
|
||||
try {
|
||||
#ifdef FC_USE_TNP_FIX
|
||||
TopoShape shape(0);
|
||||
shape.makeElementChamfer(TopShape,
|
||||
edges,
|
||||
@@ -207,81 +176,6 @@ App::DocumentObjectExecReturn *Chamfer::execute()
|
||||
QT_TRANSLATE_NOOP("Exception", "Resulting shape is invalid"));
|
||||
}
|
||||
return App::DocumentObject::StdReturn;
|
||||
|
||||
#else
|
||||
BRepFilletAPI_MakeChamfer mkChamfer(baseShape.getShape());
|
||||
|
||||
TopTools_IndexedDataMapOfShapeListOfShape mapEdgeFace;
|
||||
TopExp::MapShapesAndAncestors(baseShape.getShape(), TopAbs_EDGE, TopAbs_FACE, mapEdgeFace);
|
||||
|
||||
for (const auto &itSN : SubNames) {
|
||||
TopoDS_Edge edge = TopoDS::Edge(baseShape.getSubShape(itSN.c_str()));
|
||||
|
||||
const TopoDS_Shape& faceLast = mapEdgeFace.FindFromKey(edge).Last();
|
||||
const TopoDS_Shape& faceFirst = mapEdgeFace.FindFromKey(edge).First();
|
||||
|
||||
// Set the face based on flipDirection for all edges by default. Note for chamferType==0 it does not matter which face is used.
|
||||
TopoDS_Face face = TopoDS::Face( flipDirection ? faceLast : faceFirst );
|
||||
|
||||
// for chamfer types otherthan Equal (type = 0) check if one of the faces associated with the edge
|
||||
// is one of the originally selected faces. If so use the other face by default or the selected face if "flipDirection" is set
|
||||
if (chamferType != 0) {
|
||||
|
||||
// for each selected face
|
||||
for (const auto &itFN : FaceNames) {
|
||||
const TopoDS_Shape selFace = baseShape.getSubShape(itFN.c_str());
|
||||
|
||||
if ( faceLast.IsEqual(selFace) )
|
||||
face = TopoDS::Face( flipDirection ? faceFirst : faceLast );
|
||||
|
||||
else if ( faceFirst.IsEqual(selFace) )
|
||||
face = TopoDS::Face( flipDirection ? faceLast : faceFirst );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
switch (chamferType) {
|
||||
case 0: // Equal distance
|
||||
mkChamfer.Add(size, size, edge, face);
|
||||
break;
|
||||
case 1: // Two distances
|
||||
mkChamfer.Add(size, size2, edge, face);
|
||||
break;
|
||||
case 2: // Distance and angle
|
||||
mkChamfer.AddDA(size, Base::toRadians(angle), edge, face);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
mkChamfer.Build();
|
||||
if (!mkChamfer.IsDone())
|
||||
return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP("Exception", "Failed to create chamfer"));
|
||||
|
||||
TopoDS_Shape shape = mkChamfer.Shape();
|
||||
if (shape.IsNull())
|
||||
return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP("Exception", "Resulting shape is null"));
|
||||
|
||||
TopTools_ListOfShape aLarg;
|
||||
aLarg.Append(baseShape.getShape());
|
||||
if (!BRepAlgo::IsValid(aLarg, shape, Standard_False, Standard_False)) {
|
||||
ShapeFix_ShapeTolerance aSFT;
|
||||
aSFT.LimitTolerance(shape, Precision::Confusion(), Precision::Confusion(), TopAbs_SHAPE);
|
||||
Handle(ShapeFix_Shape) aSfs = new ShapeFix_Shape(shape);
|
||||
aSfs->Perform();
|
||||
shape = aSfs->Shape();
|
||||
if (!BRepAlgo::IsValid(aLarg, shape, Standard_False, Standard_False)) {
|
||||
return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP("Exception", "Resulting shape is invalid"));
|
||||
}
|
||||
}
|
||||
|
||||
if (!isSingleSolidRuleSatisfied(shape)) {
|
||||
return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP("Exception", "Result has multiple solids: that is not currently supported."));
|
||||
}
|
||||
|
||||
shape = refineShapeIfActive(shape);
|
||||
this->Shape.setValue(getSolid(shape));
|
||||
return App::DocumentObject::StdReturn;
|
||||
#endif
|
||||
}
|
||||
catch (Standard_Failure& e) {
|
||||
return new App::DocumentObjectExecReturn(e.GetMessageString());
|
||||
|
||||
@@ -325,11 +325,7 @@ void DressUp::getAddSubShape(Part::TopoShape &addShape, Part::TopoShape &subShap
|
||||
baseShape.move(base->getLocation().Inverted());
|
||||
if (base->getAddSubType() == Additive) {
|
||||
if(!baseShape.isNull() && baseShape.hasSubShape(TopAbs_SOLID))
|
||||
#ifdef FC_USE_TNP_FIX
|
||||
shapes.emplace_back(shape.makeElementCut(baseShape.getShape()));
|
||||
#else
|
||||
shapes.emplace_back(shape.cut(baseShape.getShape()));
|
||||
#endif
|
||||
else
|
||||
shapes.push_back(shape);
|
||||
} else {
|
||||
@@ -339,35 +335,22 @@ void DressUp::getAddSubShape(Part::TopoShape &addShape, Part::TopoShape &subShap
|
||||
// push an empty compound to indicate null additive shape
|
||||
shapes.emplace_back(comp);
|
||||
if(!baseShape.isNull() && baseShape.hasSubShape(TopAbs_SOLID))
|
||||
#ifdef FC_USE_TNP_FIX
|
||||
shapes.emplace_back(baseShape.makeElementCut(shape.getShape()));
|
||||
#else
|
||||
shapes.emplace_back(baseShape.cut(shape.getShape()));
|
||||
#endif
|
||||
else
|
||||
shapes.push_back(shape);
|
||||
}
|
||||
} else {
|
||||
baseShape = getBaseTopoShape();
|
||||
baseShape.move(getLocation().Inverted());
|
||||
#ifdef FC_USE_TNP_FIX
|
||||
shapes.emplace_back(shape.makeElementCut(baseShape.getShape()));
|
||||
shapes.emplace_back(baseShape.makeElementCut(shape.getShape()));
|
||||
#else
|
||||
shapes.emplace_back(shape.cut(baseShape.getShape()));
|
||||
shapes.emplace_back(baseShape.cut(shape.getShape()));
|
||||
#endif
|
||||
}
|
||||
|
||||
// Make a compound to contain both additive and subtractive shape,
|
||||
// bceause a dressing (e.g. a fillet) can either be additive or
|
||||
// subtractive. And the dressup feature can contain mixture of both.
|
||||
#ifdef FC_USE_TNP_FIX
|
||||
AddSubShape.setValue(Part::TopoShape().makeElementCompound(shapes));
|
||||
|
||||
#else
|
||||
AddSubShape.setValue(Part::TopoShape().makeCompound(shapes));
|
||||
#endif
|
||||
} catch (Standard_Failure &e) {
|
||||
FC_THROWM(Base::CADKernelError, "Failed to calculate AddSub shape: "
|
||||
<< e.GetMessageString());
|
||||
|
||||
@@ -65,7 +65,6 @@ short Fillet::mustExecute() const
|
||||
|
||||
App::DocumentObjectExecReturn *Fillet::execute()
|
||||
{
|
||||
#ifdef FC_USE_TNP_FIX
|
||||
Part::TopoShape baseShape;
|
||||
try {
|
||||
baseShape = getBaseTopoShape();
|
||||
@@ -127,87 +126,6 @@ App::DocumentObjectExecReturn *Fillet::execute()
|
||||
catch (Standard_Failure& e) {
|
||||
return new App::DocumentObjectExecReturn(e.GetMessageString());
|
||||
}
|
||||
|
||||
#else
|
||||
Part::TopoShape TopShape;
|
||||
try {
|
||||
TopShape = getBaseTopoShape();
|
||||
}
|
||||
catch (Base::Exception& e) {
|
||||
return new App::DocumentObjectExecReturn(e.what());
|
||||
}
|
||||
std::vector<std::string> SubNames = std::vector<std::string>(Base.getSubValues());
|
||||
|
||||
if (UseAllEdges.getValue()){
|
||||
SubNames.clear();
|
||||
std::string edgeTypeName = Part::TopoShape::shapeName(TopAbs_EDGE); //"Edge"
|
||||
int count = TopShape.countSubElements(edgeTypeName.c_str());
|
||||
for (int ii = 0; ii < count; ii++){
|
||||
std::ostringstream edgeName;
|
||||
edgeName << edgeTypeName << ii+1;
|
||||
SubNames.push_back(edgeName.str());
|
||||
}
|
||||
}
|
||||
|
||||
getContinuousEdges(TopShape, SubNames);
|
||||
|
||||
double radius = Radius.getValue();
|
||||
|
||||
if(radius <= 0)
|
||||
return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP("Exception", "Fillet radius must be greater than zero"));
|
||||
|
||||
this->positionByBaseFeature();
|
||||
|
||||
//If no element is selected, then we use a copy of previous feature.
|
||||
if (SubNames.empty()) {
|
||||
this->Shape.setValue(TopShape);
|
||||
return App::DocumentObject::StdReturn;
|
||||
}
|
||||
|
||||
// create an untransformed copy of the base shape
|
||||
Part::TopoShape baseShape(TopShape);
|
||||
baseShape.setTransform(Base::Matrix4D());
|
||||
try {
|
||||
BRepFilletAPI_MakeFillet mkFillet(baseShape.getShape());
|
||||
|
||||
for (const auto & it : SubNames) {
|
||||
TopoDS_Edge edge = TopoDS::Edge(baseShape.getSubShape(it.c_str()));
|
||||
mkFillet.Add(radius, edge);
|
||||
}
|
||||
|
||||
mkFillet.Build();
|
||||
if (!mkFillet.IsDone())
|
||||
return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP("Exception", "Failed to create fillet"));
|
||||
|
||||
TopoDS_Shape shape = mkFillet.Shape();
|
||||
if (shape.IsNull())
|
||||
return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP("Exception", "Resulting shape is null"));
|
||||
|
||||
TopTools_ListOfShape aLarg;
|
||||
aLarg.Append(baseShape.getShape());
|
||||
if (!BRepAlgo::IsValid(aLarg, shape, Standard_False, Standard_False)) {
|
||||
ShapeFix_ShapeTolerance aSFT;
|
||||
aSFT.LimitTolerance(shape, Precision::Confusion(), Precision::Confusion(), TopAbs_SHAPE);
|
||||
Handle(ShapeFix_Shape) aSfs = new ShapeFix_Shape(shape);
|
||||
aSfs->Perform();
|
||||
shape = aSfs->Shape();
|
||||
if (!BRepAlgo::IsValid(aLarg, shape, Standard_False, Standard_False)) {
|
||||
return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP("Exception", "Resulting shape is invalid"));
|
||||
}
|
||||
}
|
||||
|
||||
if (!isSingleSolidRuleSatisfied(shape)) {
|
||||
return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP("Exception", "Result has multiple solids: that is not currently supported."));
|
||||
}
|
||||
|
||||
shape = refineShapeIfActive(shape);
|
||||
this->Shape.setValue(getSolid(shape));
|
||||
return App::DocumentObject::StdReturn;
|
||||
}
|
||||
catch (Standard_Failure& e) {
|
||||
return new App::DocumentObjectExecReturn(e.GetMessageString());
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void Fillet::Restore(Base::XMLReader &reader)
|
||||
|
||||
@@ -79,170 +79,6 @@ short Groove::mustExecute() const
|
||||
return ProfileBased::mustExecute();
|
||||
}
|
||||
|
||||
#ifndef FC_USE_TNP_FIX
|
||||
App::DocumentObjectExecReturn *Groove::execute()
|
||||
{
|
||||
// Validate parameters
|
||||
// All angles are in radians unless explicitly stated
|
||||
double angleDeg = Angle.getValue();
|
||||
if (angleDeg > 360.0)
|
||||
return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP("Exception", "Angle of groove too large"));
|
||||
|
||||
double angle = Base::toRadians<double>(angleDeg);
|
||||
if (angle < Precision::Angular())
|
||||
return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP("Exception", "Angle of groove too small"));
|
||||
|
||||
double angle2 = Base::toRadians(Angle2.getValue());
|
||||
|
||||
TopoDS_Shape 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
|
||||
TopoDS_Shape base;
|
||||
try {
|
||||
base = getBaseShape();
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
// update Axis from ReferenceAxis
|
||||
try {
|
||||
updateAxis();
|
||||
} catch (const Base::Exception& e) {
|
||||
return new App::DocumentObjectExecReturn(e.what());
|
||||
}
|
||||
|
||||
// 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"));
|
||||
|
||||
RevolMethod method = methodFromString(Type.getValueAsString());
|
||||
|
||||
this->positionByPrevious();
|
||||
TopLoc_Location invObjLoc = this->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, 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"));
|
||||
}
|
||||
|
||||
// Create a fresh support even when base exists so that it can be used for patterns
|
||||
TopoDS_Shape result;
|
||||
TopoDS_Face supportface = getSupportFace();
|
||||
supportface.Move(invObjLoc);
|
||||
|
||||
if (method == RevolMethod::ToFace || method == RevolMethod::ToFirst) {
|
||||
TopoDS_Face upToFace;
|
||||
if (method == RevolMethod::ToFace) {
|
||||
getFaceFromLinkSub(upToFace, UpToFace);
|
||||
upToFace.Move(invObjLoc);
|
||||
}
|
||||
else
|
||||
throw Base::RuntimeError("ProfileBased: Groove up to first is not yet supported");
|
||||
|
||||
// TODO: This method is designed for extrusions. needs to be adapted for grooves.
|
||||
// getUpToFace(upToFace, base, supportface, sketchshape, method, dir);
|
||||
|
||||
TopoDS_Face supportface = getSupportFace();
|
||||
supportface.Move(invObjLoc);
|
||||
|
||||
if (Reversed.getValue())
|
||||
dir.Reverse();
|
||||
|
||||
TopExp_Explorer Ex(supportface,TopAbs_WIRE);
|
||||
if (!Ex.More())
|
||||
supportface = TopoDS_Face();
|
||||
RevolMode mode = RevolMode::CutFromBase;
|
||||
generateRevolution(result, base, sketchshape, supportface, upToFace, gp_Ax1(pnt, dir), method, mode, Standard_True);
|
||||
|
||||
result = refineShapeIfActive(result);
|
||||
|
||||
// the result we get here is the shape _after_ the operation is done
|
||||
// And the really expensive way to get the SubShape...
|
||||
BRepAlgoAPI_Cut mkCut(base, result);
|
||||
if (!mkCut.IsDone())
|
||||
return new App::DocumentObjectExecReturn("Groove: 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!!!!
|
||||
TopoDS_Shape subshape = refineShapeIfActive(mkCut.Shape());
|
||||
this->AddSubShape.setValue(subshape);
|
||||
|
||||
if (!isSingleSolidRuleSatisfied(result)) {
|
||||
return new App::DocumentObjectExecReturn("Groove: Result has multiple solids. This is not supported at this time.");
|
||||
}
|
||||
|
||||
this->Shape.setValue(getSolid(result));
|
||||
}
|
||||
else {
|
||||
bool midplane = Midplane.getValue();
|
||||
bool reversed = Reversed.getValue();
|
||||
generateRevolution(result, sketchshape, gp_Ax1(pnt, dir), angle, angle2, midplane, reversed, method);
|
||||
|
||||
if (result.IsNull())
|
||||
return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP("Exception", "Could not revolve the sketch!"));
|
||||
|
||||
// set the subtractive shape property for later usage in e.g. pattern
|
||||
result = refineShapeIfActive(result);
|
||||
this->AddSubShape.setValue(result);
|
||||
|
||||
// cut out groove to get one result object
|
||||
BRepAlgoAPI_Cut mkCut(base, result);
|
||||
// Let's check if the fusion has been successful
|
||||
if (!mkCut.IsDone())
|
||||
throw Base::CADKernelError(QT_TRANSLATE_NOOP("Exception", "Cut out of base feature failed"));
|
||||
|
||||
// we have to get the solids (fuse sometimes creates compounds)
|
||||
TopoDS_Shape solRes = this->getSolid(mkCut.Shape());
|
||||
if (solRes.IsNull())
|
||||
return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP("Exception", "Resulting shape is not a solid"));
|
||||
|
||||
solRes = refineShapeIfActive(solRes);
|
||||
this->Shape.setValue(getSolid(solRes));
|
||||
|
||||
if (!isSingleSolidRuleSatisfied(solRes)) {
|
||||
return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP("Exception", "Result has multiple solids: that is not currently supported."));
|
||||
}
|
||||
}
|
||||
|
||||
// eventually disable some settings that are not valid for the current method
|
||||
updateProperties(method);
|
||||
|
||||
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());
|
||||
}
|
||||
}
|
||||
#else
|
||||
App::DocumentObjectExecReturn *Groove::execute()
|
||||
{
|
||||
// Validate parameters
|
||||
@@ -372,8 +208,6 @@ App::DocumentObjectExecReturn *Groove::execute()
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
bool Groove::suggestReversed()
|
||||
{
|
||||
updateAxis();
|
||||
|
||||
@@ -65,273 +65,6 @@ short Loft::mustExecute() const
|
||||
return ProfileBased::mustExecute();
|
||||
}
|
||||
|
||||
#ifndef FC_USE_TNP_FIX
|
||||
App::DocumentObjectExecReturn *Loft::execute()
|
||||
{
|
||||
auto getSectionShape =
|
||||
[](App::DocumentObject* feature, const std::vector<std::string> &subs) -> TopoDS_Shape {
|
||||
if (!feature ||
|
||||
!feature->isDerivedFrom(Part::Feature::getClassTypeId()))
|
||||
throw Base::TypeError("Loft: Invalid profile/section");
|
||||
|
||||
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("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("Loft: 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;
|
||||
};
|
||||
|
||||
std::vector<TopoDS_Wire> wires;
|
||||
TopoDS_Shape profilePoint;
|
||||
|
||||
// if the Base property has a valid shape, fuse the pipe into it
|
||||
TopoDS_Shape base;
|
||||
try {
|
||||
base = getBaseShape();
|
||||
} catch (const Base::Exception&) {
|
||||
base = TopoDS_Shape();
|
||||
}
|
||||
|
||||
try {
|
||||
// setup the location
|
||||
this->positionByPrevious();
|
||||
TopLoc_Location 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"));
|
||||
|
||||
TopoDS_Shape profileShape = getSectionShape(Profile.getValue(),
|
||||
Profile.getSubValues());
|
||||
if (profileShape.IsNull())
|
||||
return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP("Exception", "Loft: Could not obtain profile shape"));
|
||||
|
||||
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 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", "Loft: When using points for profile/sections, the sketch should have a single point"));
|
||||
}
|
||||
|
||||
bool isLastSectionVertex = false;
|
||||
|
||||
size_t subSetCnt=0;
|
||||
for (const auto & subSet : multisections) {
|
||||
if (!subSet.first->isDerivedFrom(Part::Feature::getClassTypeId()))
|
||||
return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP("Exception", "Loft: All sections need to be part features"));
|
||||
|
||||
// if the selected subvalue is a point, pick that even if we have a sketch
|
||||
TopoDS_Shape shape = getSectionShape(subSet.first, subSet.second);
|
||||
if (shape.IsNull())
|
||||
return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP("Exception", "Loft: Could not obtain section shape"));
|
||||
|
||||
size_t numWiresAdded = addWiresToWireSections(shape, wiresections);
|
||||
if (numWiresAdded == 0) {
|
||||
// The shape of the given object doesn't contain any wires, though it still might be valid if it is a vertex (or a COMPOUND consisting a single vertex)
|
||||
TopoDS_Shape vertexShape;
|
||||
TopExp_Explorer ex{shape, TopAbs_VERTEX};
|
||||
if (ex.More()) {
|
||||
vertexShape = ex.Current();
|
||||
ex.Next();
|
||||
if (ex.More()) { // some additional vertexes in the shape, we shouldn't use it
|
||||
vertexShape.Nullify();
|
||||
}
|
||||
}
|
||||
|
||||
if (vertexShape.IsNull())
|
||||
return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP("Exception", "Loft: A section doesn't contain any wires nor is a single vertex"));
|
||||
if (subSetCnt != multisections.size()-1)
|
||||
return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP("Exception", "Loft: Only the profile and the last section can be vertices"));
|
||||
if (Closed.getValue())
|
||||
return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP("Exception", "Loft: For closed lofts only the profile can be a vertex"));
|
||||
|
||||
// all good; push vertex to all wiresection list
|
||||
for (auto &wires : wiresections)
|
||||
wires.push_back(vertexShape);
|
||||
isLastSectionVertex = true;
|
||||
} else if (numWiresAdded != wiresections.size())
|
||||
return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP("Exception", "Loft: all loft sections need to have the same amount of inner wires"));
|
||||
subSetCnt++;
|
||||
}
|
||||
|
||||
if (Closed.getValue()) {
|
||||
// For a closed loft add starting sketch again at the end
|
||||
if (profilePoint.IsNull()) {
|
||||
size_t numWiresAdded = addWiresToWireSections(profileShape, wiresections);
|
||||
assert (numWiresAdded == wiresections.size());
|
||||
boost::ignore_unused(numWiresAdded);
|
||||
} else { // !profilePoint.IsNull()
|
||||
for (auto &wires : wiresections)
|
||||
wires.push_back(profilePoint);
|
||||
}
|
||||
}
|
||||
|
||||
// build all shells
|
||||
std::vector<TopoDS_Shape> shells;
|
||||
|
||||
TopoDS_Shape copyProfilePoint(profilePoint);
|
||||
if (!profilePoint.IsNull())
|
||||
copyProfilePoint.Move(invObjLoc);
|
||||
|
||||
for (auto& wires : wiresections) {
|
||||
BRepOffsetAPI_ThruSections mkTS(false, Ruled.getValue(), Precision::Confusion());
|
||||
|
||||
if (!profilePoint.IsNull())
|
||||
mkTS.AddVertex(TopoDS::Vertex(copyProfilePoint));
|
||||
|
||||
for (auto& shape : wires) {
|
||||
shape.Move(invObjLoc);
|
||||
if (shape.ShapeType() == TopAbs_VERTEX)
|
||||
mkTS.AddVertex(TopoDS::Vertex(shape));
|
||||
else
|
||||
mkTS.AddWire(TopoDS::Wire(shape));
|
||||
}
|
||||
|
||||
mkTS.Build();
|
||||
if (!mkTS.IsDone())
|
||||
return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP("Exception", "Loft could not be built"));
|
||||
|
||||
// build the shell use simulate to get the top and bottom wires in an easy way
|
||||
shells.push_back(mkTS.Shape());
|
||||
}
|
||||
|
||||
// build the top and bottom faces (where possible), sew the shell,
|
||||
// and build the final solid
|
||||
BRepBuilderAPI_Sewing sewer;
|
||||
sewer.SetTolerance(Precision::Confusion());
|
||||
if (!Closed.getValue()) {
|
||||
if (profilePoint.IsNull()) {
|
||||
TopoDS_Shape front = getVerifiedFace();
|
||||
front.Move(invObjLoc);
|
||||
sewer.Add(front);
|
||||
}
|
||||
if (!isLastSectionVertex) {
|
||||
std::vector<TopoDS_Wire> backwires;
|
||||
for (auto& wires : wiresections)
|
||||
backwires.push_back(TopoDS::Wire(wires.back()));
|
||||
TopoDS_Shape back = Part::FaceMakerCheese::makeFace(backwires);
|
||||
sewer.Add(back);
|
||||
}
|
||||
}
|
||||
for (TopoDS_Shape& s : shells)
|
||||
sewer.Add(s);
|
||||
|
||||
sewer.Perform();
|
||||
|
||||
// build the solid
|
||||
BRepBuilderAPI_MakeSolid mkSolid;
|
||||
mkSolid.Add(TopoDS::Shell(sewer.SewedShape()));
|
||||
if (!mkSolid.IsDone())
|
||||
return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP("Exception", "Loft: 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();
|
||||
}
|
||||
|
||||
AddSubShape.setValue(result);
|
||||
|
||||
if (base.IsNull()) {
|
||||
if (getAddSubType() == FeatureAddSub::Subtractive)
|
||||
return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP("Exception", "Loft: There is nothing to subtract from"));
|
||||
|
||||
Shape.setValue(getSolid(result));
|
||||
return App::DocumentObject::StdReturn;
|
||||
}
|
||||
|
||||
if (getAddSubType() == FeatureAddSub::Additive) {
|
||||
|
||||
BRepAlgoAPI_Fuse mkFuse(base, result);
|
||||
if (!mkFuse.IsDone())
|
||||
return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP("Exception", "Loft: Adding the loft failed"));
|
||||
|
||||
// we have to get the solids (fuse sometimes creates compounds)
|
||||
TopoDS_Shape 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"));
|
||||
|
||||
if (!isSingleSolidRuleSatisfied(boolOp)) {
|
||||
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, result);
|
||||
if (!mkCut.IsDone())
|
||||
return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP("Exception", "Loft: Subtracting the loft failed"));
|
||||
|
||||
// we have to get the solids (fuse sometimes creates compounds)
|
||||
TopoDS_Shape 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"));
|
||||
|
||||
if (!isSingleSolidRuleSatisfied(boolOp)) {
|
||||
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 (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"));
|
||||
}
|
||||
}
|
||||
#else
|
||||
std::vector<Part::TopoShape>
|
||||
Loft::getSectionShape(const char *name,
|
||||
App::DocumentObject *obj,
|
||||
@@ -533,8 +266,6 @@ App::DocumentObjectExecReturn *Loft::execute()
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
PROPERTY_SOURCE(PartDesign::AdditiveLoft, PartDesign::Loft)
|
||||
AdditiveLoft::AdditiveLoft() {
|
||||
addSubType = Additive;
|
||||
|
||||
@@ -68,204 +68,8 @@ Pad::Pad()
|
||||
Length2.setConstraints(nullptr);
|
||||
}
|
||||
|
||||
#ifdef FC_USE_TNP_FIX
|
||||
|
||||
App::DocumentObjectExecReturn* Pad::execute()
|
||||
{
|
||||
return buildExtrusion(ExtrudeOption::MakeFace | ExtrudeOption::MakeFuse);
|
||||
}
|
||||
#else
|
||||
App::DocumentObjectExecReturn *Pad::execute()
|
||||
{
|
||||
double L = Length.getValue();
|
||||
double L2 = Length2.getValue();
|
||||
|
||||
// if midplane is true, disable reversed and vice versa
|
||||
bool hasMidplane = Midplane.getValue();
|
||||
bool hasReversed = Reversed.getValue();
|
||||
Midplane.setReadOnly(hasReversed);
|
||||
Reversed.setReadOnly(hasMidplane);
|
||||
|
||||
std::string method(Type.getValueAsString());
|
||||
|
||||
TopoDS_Shape 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
|
||||
TopoDS_Shape base;
|
||||
try {
|
||||
base = getBaseShape();
|
||||
}
|
||||
catch (const Base::Exception&) {
|
||||
if (method == "UpToShape") {
|
||||
return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP("Exception", "Pad: Can't pad up to shape without base shape."));
|
||||
}
|
||||
base = TopoDS_Shape();
|
||||
}
|
||||
|
||||
// get the normal vector of the sketch
|
||||
Base::Vector3d SketchVector = getProfileNormal();
|
||||
|
||||
try {
|
||||
this->positionByPrevious();
|
||||
TopLoc_Location invObjLoc = this->getLocation().Inverted();
|
||||
|
||||
base.Move(invObjLoc);
|
||||
|
||||
Base::Vector3d paddingDirection = computeDirection(SketchVector, false);
|
||||
|
||||
// create vector in padding direction with length 1
|
||||
gp_Dir dir(paddingDirection.x, paddingDirection.y, paddingDirection.z);
|
||||
|
||||
// The length of a gp_Dir is 1 so the resulting pad would have
|
||||
// the length L in the direction of dir. But we want to have its height in the
|
||||
// direction of the normal vector.
|
||||
// Therefore we must multiply L by the factor that is necessary
|
||||
// to make dir as long that its projection to the SketchVector
|
||||
// equals the SketchVector.
|
||||
// This is the scalar product of both vectors.
|
||||
// Since the pad length cannot be negative, the factor must not be negative.
|
||||
|
||||
double factor = fabs(dir * gp_Dir(SketchVector.x, SketchVector.y, SketchVector.z));
|
||||
|
||||
// factor would be zero if vectors are orthogonal
|
||||
if (factor < Precision::Confusion())
|
||||
return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP("Exception", "Pad: Creation failed because direction is orthogonal to sketch's normal vector"));
|
||||
|
||||
// perform the length correction if not along custom vector
|
||||
if (AlongSketchNormal.getValue()) {
|
||||
L = L / factor;
|
||||
L2 = L2 / factor;
|
||||
}
|
||||
|
||||
dir.Transform(invObjLoc.Transformation());
|
||||
|
||||
if (sketchshape.IsNull())
|
||||
return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP("Exception", "Pad: Creating a face from sketch failed"));
|
||||
sketchshape.Move(invObjLoc);
|
||||
|
||||
TopoDS_Shape prism;
|
||||
if (method == "UpToFirst" || method == "UpToLast" || method == "UpToFace" || method == "UpToShape") {
|
||||
// Note: This will return an unlimited planar face if support is a datum plane
|
||||
TopoDS_Face supportface = getSupportFace();
|
||||
supportface.Move(invObjLoc);
|
||||
|
||||
if (Reversed.getValue())
|
||||
dir.Reverse();
|
||||
|
||||
TopoDS_Face upToFace;
|
||||
if (method != "UpToShape") {
|
||||
// Find a valid face or datum plane to extrude up to
|
||||
if (method == "UpToFace") {
|
||||
getFaceFromLinkSub(upToFace, UpToFace);
|
||||
upToFace.Move(invObjLoc);
|
||||
}
|
||||
getUpToFace(upToFace, base, sketchshape, method, dir);
|
||||
addOffsetToFace(upToFace, dir, Offset.getValue());
|
||||
}
|
||||
|
||||
// TODO: Write our own PrismMaker which does not depend on a solid base shape
|
||||
if (base.IsNull()) {
|
||||
//generatePrism(prism, sketchshape, "Length", dir, length, 0.0, false, false);
|
||||
base = sketchshape;
|
||||
supportface = TopoDS::Face(sketchshape);
|
||||
TopExp_Explorer Ex(supportface,TopAbs_WIRE);
|
||||
if (!Ex.More())
|
||||
supportface = TopoDS_Face();
|
||||
|
||||
PrismMode mode = PrismMode::None;
|
||||
if (method == "UpToShape")
|
||||
generatePrism(prism, "UpToFace", base, sketchshape, supportface, base, dir, mode, Standard_True);
|
||||
else
|
||||
generatePrism(prism, method, base, sketchshape, supportface, upToFace, dir, mode, Standard_True);
|
||||
base.Nullify();
|
||||
}
|
||||
else {
|
||||
// A support object is always required and we need to use BRepFeat_MakePrism
|
||||
// Problem: For Pocket/UpToFirst (or an equivalent Pocket/UpToFace) the resulting shape is invalid
|
||||
// because the feature does not add any material. This only happens with the "2" option, though
|
||||
// Note: It might be possible to pass a shell or a compound containing multiple faces
|
||||
// as the Until parameter of Perform()
|
||||
// Note: Multiple independent wires are not supported, we should check for that and
|
||||
// warn the user
|
||||
// FIXME: If the support shape is not the previous solid in the tree, then there will be unexpected results
|
||||
// Check supportface for limits, otherwise Perform() throws an exception
|
||||
TopExp_Explorer Ex(supportface,TopAbs_WIRE);
|
||||
if (!Ex.More())
|
||||
supportface = TopoDS_Face();
|
||||
PrismMode mode = PrismMode::None;
|
||||
if (method == "UpToShape")
|
||||
generatePrism(prism, "UpToFace", base, sketchshape, supportface, base, dir, mode, Standard_True);
|
||||
else
|
||||
generatePrism(prism, method, base, sketchshape, supportface, upToFace, dir, mode, Standard_True);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (hasTaperedAngle()) {
|
||||
if (hasReversed)
|
||||
dir.Reverse();
|
||||
generateTaperedPrism(prism, sketchshape, method, dir, L, L2, TaperAngle.getValue(), TaperAngle2.getValue(), hasMidplane);
|
||||
}
|
||||
else {
|
||||
generatePrism(prism, sketchshape, method, dir, L, L2, hasMidplane, hasReversed);
|
||||
}
|
||||
}
|
||||
|
||||
if (prism.IsNull())
|
||||
return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP("Exception", "Pad: Resulting shape is empty"));
|
||||
|
||||
// set the additive shape property for later usage in e.g. pattern
|
||||
prism = refineShapeIfActive(prism);
|
||||
this->AddSubShape.setValue(prism);
|
||||
|
||||
if (!base.IsNull()) {
|
||||
// Let's call algorithm computing a fuse operation:
|
||||
BRepAlgoAPI_Fuse mkFuse(base, prism);
|
||||
// Let's check if the fusion has been successful
|
||||
if (!mkFuse.IsDone())
|
||||
return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP("Exception", "Pad: Fusion with base feature failed"));
|
||||
TopoDS_Shape result = mkFuse.Shape();
|
||||
// we have to get the solids (fuse sometimes creates compounds)
|
||||
TopoDS_Shape solRes = this->getSolid(result);
|
||||
// lets check if the result is a solid
|
||||
if (solRes.IsNull())
|
||||
return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP("Exception", "Resulting shape is not a solid"));
|
||||
|
||||
if (!isSingleSolidRuleSatisfied(result)) {
|
||||
return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP("Exception", "Result has multiple solids: that is not currently supported."));
|
||||
}
|
||||
|
||||
solRes = refineShapeIfActive(solRes);
|
||||
this->Shape.setValue(getSolid(solRes));
|
||||
}
|
||||
else {
|
||||
if (!isSingleSolidRuleSatisfied(prism)) {
|
||||
return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP("Exception", "Result has multiple solids: that is not currently supported."));
|
||||
}
|
||||
|
||||
this->Shape.setValue(getSolid(prism));
|
||||
}
|
||||
|
||||
// eventually disable some settings that are not valid for the current method
|
||||
updateProperties(method);
|
||||
|
||||
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 or multiple faces in a sketch are not allowed."));
|
||||
else
|
||||
return new App::DocumentObjectExecReturn(e.GetMessageString());
|
||||
}
|
||||
catch (Base::Exception& e) {
|
||||
return new App::DocumentObjectExecReturn(e.what());
|
||||
}
|
||||
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -102,341 +102,6 @@ short Pipe::mustExecute() const
|
||||
return ProfileBased::mustExecute();
|
||||
}
|
||||
|
||||
#ifndef FC_USE_TNP_FIX
|
||||
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");
|
||||
|
||||
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
|
||||
TopoDS_Shape base;
|
||||
try {
|
||||
base = getBaseShape();
|
||||
} catch (const Base::Exception&) {
|
||||
base = TopoDS_Shape();
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
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, 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)
|
||||
TopoDS_Shape 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"));
|
||||
|
||||
if (!isSingleSolidRuleSatisfied(boolOp)) {
|
||||
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, 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)
|
||||
TopoDS_Shape 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"));
|
||||
|
||||
if (!isSingleSolidRuleSatisfied(boolOp)) {
|
||||
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"));
|
||||
}
|
||||
}
|
||||
#else
|
||||
App::DocumentObjectExecReturn *Pipe::execute()
|
||||
{
|
||||
auto getSectionShape = [](App::DocumentObject* feature,
|
||||
@@ -772,7 +437,6 @@ App::DocumentObjectExecReturn *Pipe::execute()
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
void Pipe::setupAlgorithm(BRepOffsetAPI_MakePipeShell& mkPipeShell, const TopoDS_Shape& auxshape) {
|
||||
|
||||
mkPipeShell.SetTolerance(Precision::Confusion());
|
||||
|
||||
@@ -71,200 +71,17 @@ Pocket::Pocket()
|
||||
|
||||
App::DocumentObjectExecReturn *Pocket::execute()
|
||||
{
|
||||
#ifdef FC_USE_TNP_FIX
|
||||
// MakeFace|MakeFuse: because we want a solid.
|
||||
// InverseDirection: to inverse the auto detected extrusion direction for
|
||||
// backward compatibility to upstream
|
||||
ExtrudeOptions options(ExtrudeOption::MakeFace | ExtrudeOption::MakeFuse
|
||||
| ExtrudeOption::InverseDirection);
|
||||
return buildExtrusion(options);
|
||||
#else
|
||||
// Handle legacy features, these typically have Type set to 3 (previously NULL, now UpToFace),
|
||||
// empty FaceName (because it didn't exist) and a value for Length
|
||||
if (std::string(Type.getValueAsString()) == "UpToFace" &&
|
||||
(!UpToFace.getValue() && Length.getValue() > Precision::Confusion()))
|
||||
Type.setValue("Length");
|
||||
|
||||
double L = Length.getValue();
|
||||
double L2 = Length2.getValue();
|
||||
|
||||
TopoDS_Shape profileshape;
|
||||
try {
|
||||
profileshape = 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);
|
||||
}
|
||||
|
||||
// get the normal vector of the sketch
|
||||
Base::Vector3d SketchVector = getProfileNormal();
|
||||
|
||||
// turn around for pockets
|
||||
SketchVector *= -1;
|
||||
|
||||
try {
|
||||
this->positionByPrevious();
|
||||
TopLoc_Location invObjLoc = this->getLocation().Inverted();
|
||||
|
||||
base.move(invObjLoc);
|
||||
|
||||
Base::Vector3d pocketDirection = computeDirection(SketchVector, false);
|
||||
|
||||
// create vector in pocketing direction with length 1
|
||||
gp_Dir dir(pocketDirection.x, pocketDirection.y, pocketDirection.z);
|
||||
|
||||
// The length of a gp_Dir is 1 so the resulting pocket would have
|
||||
// the length L in the direction of dir. But we want to have its height in the
|
||||
// direction of the normal vector.
|
||||
// Therefore we must multiply L by the factor that is necessary
|
||||
// to make dir as long that its projection to the SketchVector
|
||||
// equals the SketchVector.
|
||||
// This is the scalar product of both vectors.
|
||||
// Since the pocket length cannot be negative, the factor must not be negative.
|
||||
|
||||
double factor = fabs(dir * gp_Dir(SketchVector.x, SketchVector.y, SketchVector.z));
|
||||
|
||||
// factor would be zero if vectors are orthogonal
|
||||
if (factor < Precision::Confusion())
|
||||
return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP("Exception", "Pocket: Creation failed because direction is orthogonal to sketch's normal vector"));
|
||||
|
||||
// perform the length correction if not along custom vector
|
||||
if (AlongSketchNormal.getValue()) {
|
||||
L = L / factor;
|
||||
L2 = L2 / factor;
|
||||
}
|
||||
|
||||
dir.Transform(invObjLoc.Transformation());
|
||||
|
||||
if (profileshape.IsNull())
|
||||
return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP("Exception", "Pocket: Creating a face from sketch failed"));
|
||||
profileshape.Move(invObjLoc);
|
||||
|
||||
std::string method(Type.getValueAsString());
|
||||
if (method == "UpToFirst" || method == "UpToFace") {
|
||||
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
|
||||
TopoDS_Face supportface = getSupportFace();
|
||||
supportface.Move(invObjLoc);
|
||||
|
||||
if (Reversed.getValue())
|
||||
dir.Reverse();
|
||||
|
||||
// Find a valid face or datum plane to extrude up to
|
||||
TopoDS_Face upToFace;
|
||||
if (method == "UpToFace") {
|
||||
getFaceFromLinkSub(upToFace, UpToFace);
|
||||
upToFace.Move(invObjLoc);
|
||||
}
|
||||
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
|
||||
// prism that is being created is contained completely inside the base solid
|
||||
// In this case the resulting shape is empty. This is not a problem for the Pad or Pocket itself
|
||||
// but it leads to an invalid SubShape
|
||||
// The bug only occurs when the upToFace is limited (by a wire), not for unlimited upToFace. But
|
||||
// other problems occur with unlimited concave upToFace so it is not an option to always unlimit upToFace
|
||||
// Check supportface for limits, otherwise Perform() throws an exception
|
||||
TopExp_Explorer Ex(supportface,TopAbs_WIRE);
|
||||
if (!Ex.More())
|
||||
supportface = TopoDS_Face();
|
||||
TopoDS_Shape prism;
|
||||
PrismMode mode = PrismMode::CutFromBase;
|
||||
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.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!!!!
|
||||
TopoDS_Shape result = refineShapeIfActive(mkCut.Shape());
|
||||
this->AddSubShape.setValue(result);
|
||||
|
||||
if (!isSingleSolidRuleSatisfied(result)) {
|
||||
return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP("Exception", "Result has multiple solids: that is not currently supported."));
|
||||
}
|
||||
|
||||
this->Shape.setValue(getSolid(prism));
|
||||
}
|
||||
else {
|
||||
TopoDS_Shape prism;
|
||||
if (hasTaperedAngle()) {
|
||||
if (Reversed.getValue())
|
||||
dir.Reverse();
|
||||
generateTaperedPrism(prism, profileshape, method, dir, L, L2, TaperAngle.getValue(), TaperAngle2.getValue(), Midplane.getValue());
|
||||
}
|
||||
else {
|
||||
generatePrism(prism, profileshape, method, dir, L, L2, Midplane.getValue(), Reversed.getValue());
|
||||
}
|
||||
|
||||
if (prism.IsNull())
|
||||
return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP("Exception", "Pocket: Resulting shape is empty"));
|
||||
|
||||
// set the subtractive shape property for later usage in e.g. pattern
|
||||
prism = refineShapeIfActive(prism);
|
||||
this->AddSubShape.setValue(prism);
|
||||
|
||||
// Cut the SubShape out of the base feature
|
||||
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();
|
||||
// we have to get the solids (fuse sometimes creates compounds)
|
||||
TopoDS_Shape solRes = this->getSolid(result);
|
||||
if (solRes.IsNull())
|
||||
return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP("Exception", "Resulting shape is not a solid"));
|
||||
|
||||
if (!isSingleSolidRuleSatisfied(result)) {
|
||||
return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP("Exception", "Result has multiple solids: that is not currently supported."));
|
||||
|
||||
}
|
||||
solRes = refineShapeIfActive(solRes);
|
||||
remapSupportShape(solRes);
|
||||
this->Shape.setValue(getSolid(solRes));
|
||||
}
|
||||
|
||||
// eventually disable some settings that are not valid for the current method
|
||||
updateProperties(method);
|
||||
|
||||
return App::DocumentObject::StdReturn;
|
||||
}
|
||||
catch (Standard_Failure& e) {
|
||||
if (std::string(e.GetMessageString()) == "TopoDS::Face" &&
|
||||
(std::string(Type.getValueAsString()) == "UpToFirst" || std::string(Type.getValueAsString()) == "UpToFace"))
|
||||
return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP("Exception", "Could not create face from sketch.\n"
|
||||
"Intersecting sketch entities or multiple faces in a sketch are not allowed "
|
||||
"for making a pocket up to a face."));
|
||||
else
|
||||
return new App::DocumentObjectExecReturn(e.GetMessageString());
|
||||
}
|
||||
catch (Base::Exception& e) {
|
||||
return new App::DocumentObjectExecReturn(e.what());
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
Base::Vector3d Pocket::getProfileNormal() const
|
||||
{
|
||||
auto res = FeatureExtrude::getProfileNormal();
|
||||
// turn around for pockets
|
||||
#ifdef FC_USE_TNP_FIX
|
||||
return res * -1;
|
||||
#else
|
||||
return res;
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -73,7 +73,6 @@ App::DocumentObjectExecReturn* FeaturePrimitive::execute(const TopoDS_Shape& pri
|
||||
FeatureAddSub::execute();
|
||||
|
||||
//if we have no base we just add the standard primitive shape
|
||||
#ifdef FC_USE_TNP_FIX
|
||||
TopoShape primitiveShape;
|
||||
primitiveShape.setShape(primitive);
|
||||
|
||||
@@ -85,15 +84,6 @@ App::DocumentObjectExecReturn* FeaturePrimitive::execute(const TopoDS_Shape& pri
|
||||
primitiveShape.Tag = -this->getID();
|
||||
}
|
||||
|
||||
#else
|
||||
auto primitiveShape = primitive;
|
||||
TopoDS_Shape base;
|
||||
try {
|
||||
//if we have a base shape we need to make sure that it does not get our transformation to
|
||||
BRepBuilderAPI_Transform trsf(getBaseShape(), getLocation().Transformation().Inverted(), true);
|
||||
base = trsf.Shape();
|
||||
}
|
||||
#endif
|
||||
catch (const Base::Exception&) {
|
||||
|
||||
//as we use this for preview we can add it even if useless for subtractive
|
||||
@@ -106,7 +96,6 @@ App::DocumentObjectExecReturn* FeaturePrimitive::execute(const TopoDS_Shape& pri
|
||||
|
||||
return App::DocumentObject::StdReturn;
|
||||
}
|
||||
#ifdef FC_USE_TNP_FIX
|
||||
AddSubShape.setValue(primitiveShape);
|
||||
|
||||
TopoShape boolOp(0);
|
||||
@@ -136,40 +125,6 @@ App::DocumentObjectExecReturn* FeaturePrimitive::execute(const TopoDS_Shape& pri
|
||||
return new App::DocumentObjectExecReturn(
|
||||
QT_TRANSLATE_NOOP("Exception", "Resulting shape is not a solid"));
|
||||
}
|
||||
#else
|
||||
TopoDS_Shape boolOp;
|
||||
if (getAddSubType() == FeatureAddSub::Additive) {
|
||||
|
||||
BRepAlgoAPI_Fuse mkFuse(base, primitiveShape);
|
||||
if (!mkFuse.IsDone())
|
||||
return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP("Exception", "Adding the primitive failed"));
|
||||
// we have to get the solids (fuse sometimes creates compounds)
|
||||
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"));
|
||||
|
||||
if (!isSingleSolidRuleSatisfied(boolOp)) {
|
||||
return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP("Exception", "Result has multiple solids: that is not currently supported."));
|
||||
}
|
||||
}
|
||||
else if (getAddSubType() == FeatureAddSub::Subtractive) {
|
||||
|
||||
BRepAlgoAPI_Cut mkCut(base, primitiveShape);
|
||||
if (!mkCut.IsDone())
|
||||
return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP("Exception", "Subtracting the primitive failed"));
|
||||
// we have to get the solids (fuse sometimes creates compounds)
|
||||
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"));
|
||||
|
||||
if (!isSingleSolidRuleSatisfied(boolOp)) {
|
||||
return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP("Exception", "Result has multiple solids: that is not currently supported."));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
boolOp = refineShapeIfActive(boolOp);
|
||||
Shape.setValue(getSolid(boolOp));
|
||||
AddSubShape.setValue(primitiveShape);
|
||||
|
||||
@@ -160,11 +160,7 @@ App::DocumentObjectExecReturn* Revolution::execute()
|
||||
}
|
||||
|
||||
// Create a fresh support even when base exists so that it can be used for patterns
|
||||
#ifdef FC_USE_TNP_FIX
|
||||
TopoShape result(0);
|
||||
#else
|
||||
TopoDS_Shape result;
|
||||
#endif
|
||||
TopoDS_Face supportface = getSupportFace();
|
||||
supportface.Move(invObjLoc);
|
||||
|
||||
@@ -194,27 +190,15 @@ App::DocumentObjectExecReturn* Revolution::execute()
|
||||
if (!Ex.More()) {
|
||||
supportface = TopoDS_Face();
|
||||
}
|
||||
#ifdef FC_USE_TNP_FIX
|
||||
RevolMode mode = RevolMode::None;
|
||||
// revolve the face to a solid
|
||||
// TopoShape result(0);
|
||||
try {
|
||||
result = base.makeElementRevolution(gp_Ax1(pnt, dir), supportface, upToFace);
|
||||
result = base.makeElementRevolution(gp_Ax1(pnt, dir), supportface, upToFace);
|
||||
}
|
||||
catch (Standard_Failure&) {
|
||||
return new App::DocumentObjectExecReturn("Could not revolve the sketch!");
|
||||
}
|
||||
#else
|
||||
RevolMode mode = RevolMode::None;
|
||||
generateRevolution(result,
|
||||
base.getShape(),
|
||||
sketchshape.getShape(),
|
||||
supportface,
|
||||
upToFace,
|
||||
gp_Ax1(pnt, dir),
|
||||
method,
|
||||
mode,
|
||||
Standard_True);
|
||||
#endif
|
||||
}
|
||||
else {
|
||||
bool midplane = Midplane.getValue();
|
||||
@@ -229,7 +213,6 @@ App::DocumentObjectExecReturn* Revolution::execute()
|
||||
method);
|
||||
}
|
||||
|
||||
#ifdef FC_USE_TNP_FIX
|
||||
if (!result.isNull()) {
|
||||
result = refineShapeIfActive(result);
|
||||
// set the additive shape property for later usage in e.g. pattern
|
||||
@@ -239,25 +222,6 @@ App::DocumentObjectExecReturn* Revolution::execute()
|
||||
result = result.makeElementFuse(base);
|
||||
result = refineShapeIfActive(result);
|
||||
}
|
||||
#else
|
||||
if (!result.IsNull()) {
|
||||
result = refineShapeIfActive(result);
|
||||
// set the additive shape property for later usage in e.g. pattern
|
||||
this->AddSubShape.setValue(result);
|
||||
|
||||
if (!base.isNull()) {
|
||||
// Let's call algorithm computing a fuse operation:
|
||||
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"));
|
||||
}
|
||||
result = mkFuse.Shape();
|
||||
result = refineShapeIfActive(result);
|
||||
}
|
||||
#endif
|
||||
|
||||
this->Shape.setValue(getSolid(result));
|
||||
}
|
||||
else {
|
||||
@@ -329,12 +293,7 @@ Revolution::RevolMethod Revolution::methodFromString(const std::string& methodSt
|
||||
return RevolMethod::Dimension;
|
||||
}
|
||||
|
||||
#ifdef FC_USE_TNP_FIX
|
||||
void Revolution::generateRevolution(TopoShape& revol,
|
||||
#else
|
||||
void Revolution::generateRevolution(TopoDS_Shape& revol,
|
||||
|
||||
#endif
|
||||
const TopoDS_Shape& sketchshape,
|
||||
const gp_Ax1& axis,
|
||||
const double angle,
|
||||
@@ -373,22 +332,8 @@ void Revolution::generateRevolution(TopoDS_Shape& revol,
|
||||
from.Move(loc);
|
||||
}
|
||||
|
||||
#ifdef FC_USE_TNP_FIX
|
||||
revol = TopoShape(from);
|
||||
// revol.Tag = getID();
|
||||
revol = revol.makeElementRevolve(revolAx,angleTotal);
|
||||
revol = TopoShape(from).makeElementRevolve(revolAx,angleTotal);
|
||||
revol.Tag = -getID();
|
||||
#else
|
||||
// revolve the face to a solid
|
||||
// BRepPrimAPI is the only option that allows use of this shape for patterns.
|
||||
// See https://forum.freecadweb.org/viewtopic.php?f=8&t=70185&p=611673#p611673.
|
||||
BRepPrimAPI_MakeRevol RevolMaker(from, revolAx, angleTotal);
|
||||
|
||||
if (!RevolMaker.IsDone())
|
||||
throw Base::RuntimeError("ProfileBased: RevolMaker failed! Could not revolve the sketch!");
|
||||
else
|
||||
revol = RevolMaker.Shape();
|
||||
#endif
|
||||
} else {
|
||||
std::stringstream str;
|
||||
str << "ProfileBased: Internal error: Unknown method for generateRevolution()";
|
||||
|
||||
@@ -95,11 +95,7 @@ protected:
|
||||
/**
|
||||
* Generates a revolution of the input sketchshape and stores it in the given \a revol.
|
||||
*/
|
||||
#ifdef FC_USE_TNP_FIX
|
||||
void generateRevolution(TopoShape& revol,
|
||||
#else
|
||||
void generateRevolution(TopoDS_Shape& revol,
|
||||
#endif
|
||||
const TopoDS_Shape& sketchshape,
|
||||
const gp_Ax1& ax1,
|
||||
const double angle,
|
||||
|
||||
@@ -1134,105 +1134,11 @@ bool ProfileBased::checkLineCrossesFace(const gp_Lin& line, const TopoDS_Face& f
|
||||
|
||||
void ProfileBased::remapSupportShape(const TopoDS_Shape & newShape)
|
||||
{
|
||||
#if FC_USE_TNP_FIX
|
||||
(void)newShape;
|
||||
// Realthunder: with the new topological naming, I don't think this function
|
||||
// is necessary. A missing element will cause an explicitly error, and the
|
||||
// user will be force to manually select the element. Various editors, such
|
||||
// as dress up editors, can perform element guessing when activated.
|
||||
#else
|
||||
TopTools_IndexedMapOfShape faceMap;
|
||||
TopExp::MapShapes(newShape, TopAbs_FACE, faceMap);
|
||||
|
||||
// here we must reset the placement otherwise the geometric matching doesn't work
|
||||
Part::TopoShape shape = this->Shape.getValue();
|
||||
TopoDS_Shape sh = shape.getShape();
|
||||
sh.Location(TopLoc_Location());
|
||||
shape.setShape(sh);
|
||||
|
||||
std::vector<App::DocumentObject*> refs = this->getInList();
|
||||
for (auto ref : refs) {
|
||||
std::vector<App::Property*> props;
|
||||
ref->getPropertyList(props);
|
||||
for (auto prop : props) {
|
||||
if (!prop->isDerivedFrom(App::PropertyLinkSub::getClassTypeId()))
|
||||
continue;
|
||||
App::PropertyLinkSub* link = static_cast<App::PropertyLinkSub*>(prop);
|
||||
if (link->getValue() != this)
|
||||
continue;
|
||||
std::vector<std::string> subValues = link->getSubValues();
|
||||
std::vector<std::string> newSubValues;
|
||||
|
||||
for (auto & subValue : subValues) {
|
||||
std::string shapetype;
|
||||
if (subValue.compare(0, 4, "Face") == 0) {
|
||||
shapetype = "Face";
|
||||
}
|
||||
else if (subValue.compare(0, 4, "Edge") == 0) {
|
||||
shapetype = "Edge";
|
||||
}
|
||||
else if (subValue.compare(0, 6, "Vertex") == 0) {
|
||||
shapetype = "Vertex";
|
||||
}
|
||||
else {
|
||||
newSubValues.push_back(subValue);
|
||||
continue;
|
||||
}
|
||||
|
||||
bool success = false;
|
||||
TopoDS_Shape element;
|
||||
try {
|
||||
element = shape.getSubShape(subValue.c_str());
|
||||
}
|
||||
catch (Standard_Failure&) {
|
||||
// This shape doesn't even exist, so no chance to do some tests
|
||||
newSubValues.push_back(subValue);
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
// as very first test check if old face and new face are parallel planes
|
||||
TopoDS_Shape newElement = Part::TopoShape(newShape).getSubShape(subValue.c_str());
|
||||
if (isParallelPlane(element, newElement)) {
|
||||
newSubValues.push_back(subValue);
|
||||
success = true;
|
||||
}
|
||||
}
|
||||
catch (Standard_Failure&) {
|
||||
}
|
||||
// try an exact matching
|
||||
if (!success) {
|
||||
for (int i = 1; i < faceMap.Extent(); i++) {
|
||||
if (isQuasiEqual(element, faceMap.FindKey(i))) {
|
||||
std::stringstream str;
|
||||
str << shapetype << i;
|
||||
newSubValues.push_back(str.str());
|
||||
success = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
// if an exact matching fails then try to compare only the geometries
|
||||
if (!success) {
|
||||
for (int i = 1; i < faceMap.Extent(); i++) {
|
||||
if (isEqualGeometry(element, faceMap.FindKey(i))) {
|
||||
std::stringstream str;
|
||||
str << shapetype << i;
|
||||
newSubValues.push_back(str.str());
|
||||
success = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// the new shape couldn't be found so keep the old sub-name
|
||||
if (!success)
|
||||
newSubValues.push_back(subValue);
|
||||
}
|
||||
|
||||
link->setValue(this, newSubValues);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
namespace PartDesign {
|
||||
@@ -1514,7 +1420,6 @@ Base::Vector3d ProfileBased::getProfileNormal() const {
|
||||
Base::Placement SketchPos = obj->Placement.getValue();
|
||||
Base::Rotation SketchOrientation = SketchPos.getRotation();
|
||||
SketchOrientation.multVec(SketchVector, SketchVector);
|
||||
#ifdef FC_USE_TNP_FIX
|
||||
return SketchVector;
|
||||
}
|
||||
|
||||
@@ -1592,31 +1497,6 @@ Base::Vector3d ProfileBased::getProfileNormal() const {
|
||||
}
|
||||
return dir.Normalize();
|
||||
}
|
||||
|
||||
#else
|
||||
}
|
||||
else {
|
||||
TopoDS_Shape shape = getVerifiedFace(true);
|
||||
if (shape.IsNull())
|
||||
return SketchVector;
|
||||
|
||||
// the shape can be a single face or a compound of faces, only consider the first face
|
||||
TopExp_Explorer ex(shape, TopAbs_FACE);
|
||||
if (ex.More()) {
|
||||
TopoDS_Face face = TopoDS::Face(ex.Current());
|
||||
BRepAdaptor_Surface adapt(face);
|
||||
double u = adapt.FirstUParameter() + (adapt.LastUParameter() - adapt.FirstUParameter()) / 2.;
|
||||
double v = adapt.FirstVParameter() + (adapt.LastVParameter() - adapt.FirstVParameter()) / 2.;
|
||||
BRepLProp_SLProps prop(adapt, u, v, 2, Precision::Confusion());
|
||||
if (prop.IsNormalDefined()) {
|
||||
gp_Pnt pnt; gp_Vec vec;
|
||||
// handles the orientation state of the shape
|
||||
BRepGProp_Face(face).Normal(u, v, pnt, vec);
|
||||
SketchVector = Base::Vector3d(vec.X(), vec.Y(), vec.Z());
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return SketchVector;
|
||||
}
|
||||
|
||||
|
||||
@@ -324,22 +324,12 @@ App::DocumentObjectExecReturn* Transformed::execute()
|
||||
"Shape of additive/subtractive feature is empty"));
|
||||
}
|
||||
gp_Trsf trsf = feature->getLocation().Transformation().Multiplied(trsfInv);
|
||||
#ifdef FC_USE_TNP_FIX
|
||||
if (!fuseShape.isNull()) {
|
||||
fuseShape = fuseShape.makeElementTransform(trsf);
|
||||
}
|
||||
if (!cutShape.isNull()) {
|
||||
cutShape = cutShape.makeElementTransform(trsf);
|
||||
}
|
||||
#else
|
||||
if (!fuseShape.isNull()) {
|
||||
fuseShape = fuseShape.makeTransform(trsf);
|
||||
}
|
||||
if (!cutShape.isNull()) {
|
||||
cutShape = cutShape.makeTransform(trsf);
|
||||
}
|
||||
|
||||
#endif
|
||||
if (!fuseShape.isNull()) {
|
||||
supportShape.makeElementFuse(getTransformedCompShape(supportShape, fuseShape));
|
||||
}
|
||||
|
||||
@@ -731,11 +731,7 @@ void SubShapeBinder::update(SubShapeBinder::UpdateOption options) {
|
||||
else {
|
||||
for (size_t i = 0; i < shapes.size(); ++i) {
|
||||
auto& shape = shapes[i];
|
||||
#ifdef FC_USE_TNP_FIX
|
||||
shape = shape.makeElementTransform(*shapeMats[i]);
|
||||
#else
|
||||
shape = shape.makeTransform(*shapeMats[i]);
|
||||
#endif
|
||||
// if(shape.Hasher
|
||||
// && shape.getElementMapSize()
|
||||
// && shape.Hasher != getDocument()->getStringHasher())
|
||||
@@ -750,11 +746,7 @@ void SubShapeBinder::update(SubShapeBinder::UpdateOption options) {
|
||||
// Shape.resetElementMapVersion();
|
||||
return;
|
||||
}
|
||||
#ifdef FC_USE_TNP_FIX
|
||||
result.makeElementCompound(shapes);
|
||||
#else
|
||||
result.makeCompound(shapes);
|
||||
#endif
|
||||
bool fused = false;
|
||||
if (Fuse.getValue()) {
|
||||
// If the compound has solid, fuse them together, and ignore other type of
|
||||
@@ -773,11 +765,7 @@ void SubShapeBinder::update(SubShapeBinder::UpdateOption options) {
|
||||
}
|
||||
else if (!solid.isNull()) {
|
||||
// wrap the single solid in compound to keep its placement
|
||||
#ifdef FC_USE_TNP_FIX
|
||||
result.makeElementCompound({ solid });
|
||||
#else
|
||||
result.makeCompound({ solid });
|
||||
#endif
|
||||
fused = true;
|
||||
}
|
||||
}
|
||||
@@ -786,7 +774,6 @@ void SubShapeBinder::update(SubShapeBinder::UpdateOption options) {
|
||||
&& !result.hasSubShape(TopAbs_FACE)
|
||||
&& result.hasSubShape(TopAbs_EDGE))
|
||||
{
|
||||
#ifdef FC_USE_TNP_FIX
|
||||
result = result.makeElementWires();
|
||||
if (MakeFace.getValue()) {
|
||||
try {
|
||||
@@ -794,33 +781,16 @@ void SubShapeBinder::update(SubShapeBinder::UpdateOption options) {
|
||||
}
|
||||
catch (...) {}
|
||||
}
|
||||
#else
|
||||
result = result.makeWires();
|
||||
if (MakeFace.getValue()) {
|
||||
try {
|
||||
result = result.makeFace(nullptr);
|
||||
}
|
||||
catch (...) {}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
if (!fused && result.hasSubShape(TopAbs_WIRE)
|
||||
&& Offset.getValue() != 0.0) {
|
||||
try {
|
||||
#ifdef FC_USE_TNP_FIX
|
||||
result = result.makeElementOffset2D(Offset.getValue(),
|
||||
(Part::JoinType) OffsetJoinType.getValue() ,
|
||||
OffsetFill.getValue() ? Part::FillType::fill : Part::FillType::noFill,
|
||||
OffsetOpenResult.getValue() ? Part::OpenResult::allowOpenResult : Part::OpenResult::noOpenResult,
|
||||
OffsetIntersection.getValue());
|
||||
#else
|
||||
result = result.makeOffset2D(Offset.getValue(),
|
||||
OffsetJoinType.getValue(),
|
||||
OffsetFill.getValue(),
|
||||
OffsetOpenResult.getValue(),
|
||||
OffsetIntersection.getValue());
|
||||
#endif
|
||||
}
|
||||
catch (...) {
|
||||
std::ostringstream msg;
|
||||
@@ -830,11 +800,7 @@ void SubShapeBinder::update(SubShapeBinder::UpdateOption options) {
|
||||
}
|
||||
|
||||
if (Refine.getValue())
|
||||
#ifdef FC_USE_TNP_FIX
|
||||
result = result.makeElementRefine();
|
||||
#else
|
||||
result = result.makeRefine();
|
||||
#endif
|
||||
result.setPlacement(Placement.getValue());
|
||||
Shape.setValue(result);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user