[PD] fix pad uptoface and uptoshape (#16030)
* [PD] fix Pad UpToFace and UpToShape * specify struct pointers for Win * Rename variables for MSVC compatibility - windows.h defines 'near' and 'far' as macros * Add unit test --------- Co-authored-by: Chris Hennes <chennes@pioneerlibrarysystem.org>
This commit is contained in:
committed by
GitHub
parent
85082b72d4
commit
e157f0616a
@@ -40,6 +40,7 @@
|
||||
#include <Base/Tools.h>
|
||||
#include <Mod/Part/App/ExtrusionHelper.h>
|
||||
#include "Mod/Part/App/TopoShapeOpCode.h"
|
||||
#include <Mod/Part/App/PartFeature.h>
|
||||
|
||||
#include "FeatureExtrude.h"
|
||||
|
||||
@@ -131,6 +132,41 @@ bool FeatureExtrude::hasTaperedAngle() const
|
||||
fabs(TaperAngle2.getValue()) > Base::toRadians(Precision::Angular());
|
||||
}
|
||||
|
||||
TopoShape FeatureExtrude::makeShellFromUpToShape(TopoShape shape, TopoShape sketchshape, gp_Dir dir){
|
||||
|
||||
// Find nearest/furthest face
|
||||
std::vector<Part::cutTopoShapeFaces> cfaces =
|
||||
Part::findAllFacesCutBy(shape, sketchshape, dir);
|
||||
if (cfaces.empty()) {
|
||||
dir = -dir;
|
||||
cfaces = Part::findAllFacesCutBy(shape, sketchshape, dir);
|
||||
}
|
||||
struct Part::cutTopoShapeFaces *nearFace;
|
||||
struct Part::cutTopoShapeFaces *farFace;
|
||||
nearFace = farFace = &cfaces.front();
|
||||
for (auto &face : cfaces) {
|
||||
if (face.distsq > farFace->distsq) {
|
||||
farFace = &face;
|
||||
}
|
||||
else if (face.distsq < nearFace->distsq) {
|
||||
nearFace = &face;
|
||||
}
|
||||
}
|
||||
|
||||
if (nearFace != farFace) {
|
||||
std::vector<TopoShape> faceList;
|
||||
for (auto &face : shape.getSubTopoShapes(TopAbs_FACE)) {
|
||||
if (! (face == farFace->face)){
|
||||
// don't use the last face so the shell is open
|
||||
// and OCC works better
|
||||
faceList.push_back(face);
|
||||
}
|
||||
}
|
||||
return shape.makeElementCompound(faceList);
|
||||
}
|
||||
return shape;
|
||||
}
|
||||
|
||||
// TODO: Toponaming April 2024 Deprecated in favor of TopoShape method. Remove when possible.
|
||||
void FeatureExtrude::generatePrism(TopoDS_Shape& prism,
|
||||
const TopoDS_Shape& sketchshape,
|
||||
@@ -579,12 +615,10 @@ App::DocumentObjectExecReturn* FeatureExtrude::buildExtrusion(ExtrudeOptions opt
|
||||
faceCount = 1;
|
||||
}
|
||||
else if (method == "UpToShape") {
|
||||
try {
|
||||
faceCount = getUpToShapeFromLinkSubList(upToShape, UpToShape);
|
||||
upToShape.move(invObjLoc);
|
||||
}
|
||||
catch (Base::ValueError&){
|
||||
//no shape selected use the base
|
||||
faceCount = getUpToShapeFromLinkSubList(upToShape, UpToShape);
|
||||
upToShape.move(invObjLoc);
|
||||
if (faceCount == 0){
|
||||
// No shape selected, use the base
|
||||
upToShape = base;
|
||||
faceCount = 0;
|
||||
}
|
||||
@@ -594,8 +628,12 @@ App::DocumentObjectExecReturn* FeatureExtrude::buildExtrusion(ExtrudeOptions opt
|
||||
getUpToFace(upToShape, base, supportface, sketchshape, method, dir);
|
||||
addOffsetToFace(upToShape, dir, Offset.getValue());
|
||||
}
|
||||
else if (fabs(Offset.getValue()) > Precision::Confusion()){
|
||||
return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP("Exception", "Extrude: Can only offset one face"));
|
||||
else{
|
||||
if (fabs(Offset.getValue()) > Precision::Confusion()){
|
||||
return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP("Exception", "Extrude: Can only offset one face"));
|
||||
}
|
||||
// open the shell by removing the furthest face
|
||||
upToShape = makeShellFromUpToShape(upToShape, sketchshape, dir);
|
||||
}
|
||||
|
||||
if (!supportface.hasSubShape(TopAbs_WIRE)) {
|
||||
@@ -645,13 +683,22 @@ App::DocumentObjectExecReturn* FeatureExtrude::buildExtrusion(ExtrudeOptions opt
|
||||
this->Shape.setValue(getSolid(prism));
|
||||
return App::DocumentObject::StdReturn;
|
||||
}
|
||||
prism.makeElementPrismUntil(base,
|
||||
sketchshape,
|
||||
supportface,
|
||||
upToShape,
|
||||
dir,
|
||||
TopoShape::PrismMode::None,
|
||||
true /*CheckUpToFaceLimits.getValue()*/);
|
||||
try {
|
||||
prism.makeElementPrismUntil(base,
|
||||
sketchshape,
|
||||
supportface,
|
||||
upToShape,
|
||||
dir,
|
||||
TopoShape::PrismMode::None,
|
||||
true /*CheckUpToFaceLimits.getValue()*/);
|
||||
}
|
||||
catch (Base::Exception& e) {
|
||||
if (method == "UpToShape" && faceCount > 1){
|
||||
return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP(
|
||||
"Exception",
|
||||
"Unable to reach the selected shape, please select faces"));
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
Part::ExtrusionParameters params;
|
||||
|
||||
@@ -71,6 +71,7 @@ protected:
|
||||
Base::Vector3d computeDirection(const Base::Vector3d& sketchVector, bool inverse);
|
||||
bool hasTaperedAngle() const;
|
||||
|
||||
|
||||
/// Options for buildExtrusion()
|
||||
enum class ExtrudeOption
|
||||
{
|
||||
@@ -84,6 +85,13 @@ protected:
|
||||
|
||||
App::DocumentObjectExecReturn* buildExtrusion(ExtrudeOptions options);
|
||||
|
||||
/**
|
||||
* generate an open shell from a given shape
|
||||
* by removing the farthest face from the sketchshape in the direction
|
||||
* if farthest is nearest (circular) then return the initial shape
|
||||
*/
|
||||
TopoShape makeShellFromUpToShape(TopoShape shape, TopoShape sketchshape, gp_Dir dir);
|
||||
|
||||
/**
|
||||
* Generates an extrusion of the input sketchshape and stores it in the given \a prism
|
||||
*/
|
||||
|
||||
@@ -708,12 +708,10 @@ int ProfileBased::getUpToShapeFromLinkSubList(TopoShape& upToShape, const App::P
|
||||
}
|
||||
}
|
||||
if (ret == 0){
|
||||
throw Base::ValueError("SketchBased: No face selected");
|
||||
return 0;
|
||||
}
|
||||
|
||||
upToShape = faceList[0];
|
||||
|
||||
if (ret == 1){
|
||||
upToShape = faceList[0];
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
@@ -168,7 +168,7 @@ protected:
|
||||
|
||||
/// Create a shape with shapes and faces from a given LinkSubList
|
||||
/// return 0 if almost one full shape is selected else the face count
|
||||
int getUpToShapeFromLinkSubList(TopoShape& upToShape, const App::PropertyLinkSubList& refShape); // TODO static
|
||||
static int getUpToShapeFromLinkSubList(TopoShape& upToShape, const App::PropertyLinkSubList& refShape);
|
||||
|
||||
/// Find a valid face to extrude up to
|
||||
static void getUpToFace(TopoShape& upToFace,
|
||||
|
||||
@@ -184,6 +184,33 @@ class TestPad(unittest.TestCase):
|
||||
self.Doc.recompute()
|
||||
self.assertAlmostEqual(self.Pad1.Shape.Volume, 4.0)
|
||||
|
||||
def testPadToConcaveCase(self):
|
||||
self.Body = self.Doc.addObject('PartDesign::Body','Body')
|
||||
# Make a half revolution
|
||||
self.RevolutionSketch = self.Doc.addObject('Sketcher::SketchObject', 'SketchPad')
|
||||
self.Body.addObject(self.RevolutionSketch)
|
||||
TestSketcherApp.CreateRectangleSketch(self.RevolutionSketch, (9, 0), (10, 5))
|
||||
self.Doc.recompute()
|
||||
self.Revolution = self.Doc.addObject("PartDesign::Revolution", "Revolution")
|
||||
self.Body.addObject(self.Revolution)
|
||||
self.Revolution.Profile = self.RevolutionSketch
|
||||
self.Revolution.ReferenceAxis = (self.RevolutionSketch, ['V_Axis'])
|
||||
self.Revolution.Angle = 180
|
||||
self.Doc.recompute()
|
||||
# Make a sketch and pad to first
|
||||
self.PadSketch = self.Doc.addObject('Sketcher::SketchObject', 'SketchPad')
|
||||
self.Body.addObject(self.PadSketch)
|
||||
self.Doc.recompute()
|
||||
TestSketcherApp.CreateRectangleSketch(self.PadSketch, (0, 0), (1, 1))
|
||||
self.Doc.recompute()
|
||||
self.Pad = self.Doc.addObject("PartDesign::Pad", "Pad")
|
||||
self.Body.addObject(self.Pad)
|
||||
self.Pad.Profile = self.PadSketch
|
||||
self.Pad.Type = 2
|
||||
self.Pad.Reversed = True
|
||||
self.Doc.recompute()
|
||||
self.assertAlmostEqual(self.Pad.Shape.Volume, 2208.0963, places=4)
|
||||
|
||||
def tearDown(self):
|
||||
#closing doc
|
||||
FreeCAD.closeDocument("PartDesignTestPad")
|
||||
|
||||
Reference in New Issue
Block a user