Merge pull request #21713 from 3x380V/pd_fixes

PartDesign: Random fixes
This commit is contained in:
Chris Hennes
2025-09-29 10:54:46 -05:00
committed by GitHub
13 changed files with 466 additions and 266 deletions

View File

@@ -2697,8 +2697,15 @@ TopoShape& TopoShape::makeElementOffset2D(const TopoShape& shape,
if (shape.getShape().ShapeType() == TopAbs_COMPOUND) {
if (!intersection) {
// simply recursively process the children, independently
expandCompound(shape, shapesToProcess);
outputPolicy = SingleShapeCompoundCreationPolicy::forceCompound;
for(TopoDS_Iterator it(shape.getShape()); it.More() ; it.Next()) {
shapesToReturn.push_back(TopoShape(it.Value()).makeElementOffset2D(offset,
joinType,
fill,
allowOpenResult,
intersection,
op));
outputPolicy = SingleShapeCompoundCreationPolicy::forceCompound;
}
}
else {
// collect non-compounds from this compound for collective offset. Process other shapes

View File

@@ -126,9 +126,13 @@ App::DocumentObjectExecReturn *Draft::execute()
App::DocumentObject* refDirection = PullDirection.getValue();
if (refDirection) {
if (refDirection->isDerivedFrom<PartDesign::Line>()) {
PartDesign::Line* line = static_cast<PartDesign::Line*>(refDirection);
Base::Vector3d d = line->getDirection();
pullDirection = gp_Dir(d.x, d.y, d.z);
PartDesign::Line* line = static_cast<PartDesign::Line*>(refDirection);
Base::Vector3d d = line->getDirection();
pullDirection = gp_Dir(d.x, d.y, d.z);
} else if (refDirection->isDerivedFrom<App::Line>()) {
App::Line* line = static_cast<App::Line*>(refDirection);
Base::Vector3d d = line->getDirection();
pullDirection = gp_Dir(d.x, d.y, d.z);
} else if (refDirection->isDerivedFrom<Part::Feature>()) {
std::vector<std::string> subStrings = PullDirection.getSubValues();
if (subStrings.empty() || subStrings[0].empty())

View File

@@ -25,10 +25,9 @@
# include <BRepPrimAPI_MakeRevol.hxx>
# include <BRepFeat_MakeRevol.hxx>
# include <gp_Lin.hxx>
# include <TopoDS.hxx>
# include <TopExp_Explorer.hxx>
# include <Precision.hxx>
# include <TopExp_Explorer.hxx>
# include <TopoDS.hxx>
#include <Base/Exception.h>
#include <Base/Tools.h>
@@ -36,7 +35,6 @@
#include "FeatureGroove.h"
#include "Mod/Part/App/TopoShapeOpCode.h"
using namespace PartDesign;
namespace PartDesign {
@@ -47,7 +45,7 @@ const char* Groove::TypeEnums[]= {"Angle", "ThroughAll", "UpToFirst", "UpToFace"
PROPERTY_SOURCE(PartDesign::Groove, PartDesign::ProfileBased)
const App::PropertyAngle::Constraints Groove::floatAngle = { Base::toDegrees<double>(Precision::Angular()), 360.0, 1.0 };
const App::PropertyAngle::Constraints Groove::floatAngle = { 0.0, 360.0, 1.0 };
Groove::Groove()
{
@@ -58,10 +56,10 @@ Groove::Groove()
ADD_PROPERTY_TYPE(Base, (Base::Vector3d(0.0f,0.0f,0.0f)), "Groove", App::PropertyType(App::Prop_ReadOnly | App::Prop_Hidden), "Base");
ADD_PROPERTY_TYPE(Axis, (Base::Vector3d(0.0f,1.0f,0.0f)), "Groove", App::PropertyType(App::Prop_ReadOnly | App::Prop_Hidden), "Axis");
ADD_PROPERTY_TYPE(Angle, (360.0),"Groove", App::Prop_None, "Angle");
ADD_PROPERTY_TYPE(Angle2, (60.0), "Groove", App::Prop_None, "Groove length in 2nd direction");
ADD_PROPERTY_TYPE(Angle2, (0.0), "Groove", App::Prop_None, "Groove length in 2nd direction");
ADD_PROPERTY_TYPE(UpToFace, (nullptr), "Groove", App::Prop_None, "Face where groove will end");
Angle.setConstraints(&floatAngle);
ADD_PROPERTY_TYPE(ReferenceAxis, (nullptr), "Groove", (App::PropertyType)(App::Prop_None), "Reference axis of Groove");
ADD_PROPERTY_TYPE(ReferenceAxis, (nullptr), "Groove", (App::Prop_None), "Reference axis of groove");
}
short Groove::mustExecute() const
@@ -81,57 +79,63 @@ App::DocumentObjectExecReturn *Groove::execute()
{
if (onlyHaveRefined()) { return App::DocumentObject::StdReturn; }
constexpr double maxDegree = 360.0;
auto method = methodFromString(Type.getValueAsString());
// Validate parameters
double angle = Angle.getValue();
if (angle > 360.0)
return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP("Exception", "Angle of groove too large"));
angle = Base::toRadians<double>(angle);
if (angle < Precision::Angular())
return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP("Exception", "Angle of groove too small"));
// Reverse angle if selected
if (Reversed.getValue() && !Midplane.getValue())
angle *= (-1.0);
TopoShape sketchshape;
try {
sketchshape = getTopoShapeVerifiedFace();
} catch (const Base::Exception& e) {
return new App::DocumentObjectExecReturn(e.what());
double angleDeg = Angle.getValue();
if (angleDeg > maxDegree) {
return new App::DocumentObjectExecReturn(
QT_TRANSLATE_NOOP("Exception", "Angle of groove too large"));
}
// if the Base property has a valid shape, fuse the prism into it
double angle = Base::toRadians<double>(angleDeg);
if (angle < Precision::Angular() && method == RevolMethod::Angle) {
return new App::DocumentObjectExecReturn(
QT_TRANSLATE_NOOP("Exception", "Angle of groove too small"));
}
double angle2 = Base::toRadians(Angle2.getValue());
if (std::fabs(angle + angle2) < Precision::Angular() && method == RevolMethod::TwoAngles) {
return new App::DocumentObjectExecReturn(
QT_TRANSLATE_NOOP("Exception", "Angles of groove nullify each other"));
}
TopoShape sketchshape = getTopoShapeVerifiedFace();
// if the Base property has a valid shape, fuse the AddShape 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);
// fall back to support (for legacy features)
}
updateAxis();
// get revolve axis
Base::Vector3d b = Base.getValue();
gp_Pnt pnt(b.x,b.y,b.z);
Base::Vector3d v = Axis.getValue();
gp_Dir dir(v.x,v.y,v.z);
// update Axis from ReferenceAxis
try {
updateAxis();
}
catch (const Base::Exception& e) {
return new App::DocumentObjectExecReturn(e.what());
}
try {
if (sketchshape.isNull())
return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP("Exception", "Creating a face from sketch failed"));
// get revolve axis
Base::Vector3d b = Base.getValue();
gp_Pnt pnt(b.x, b.y, b.z);
Base::Vector3d v = Axis.getValue();
// Rotate the face by half the angle to get Groove symmetric to sketch plane
if (Midplane.getValue()) {
gp_Trsf mov;
mov.SetRotation(gp_Ax1(pnt, dir), Base::toRadians<double>(Angle.getValue()) * (-1.0) / 2.0);
TopLoc_Location loc(mov);
sketchshape.move(loc);
if (v.IsNull()) {
return new App::DocumentObjectExecReturn(
QT_TRANSLATE_NOOP("Exception", "Reference axis is invalid"));
}
gp_Dir dir(v.x, v.y, v.z);
if (sketchshape.isNull()) {
return new App::DocumentObjectExecReturn(
QT_TRANSLATE_NOOP("Exception", "Creating a face from sketch failed"));
}
this->positionByPrevious();
@@ -144,66 +148,112 @@ App::DocumentObjectExecReturn *Groove::execute()
// Check distance between sketchshape and axis - to avoid failures and crashes
TopExp_Explorer xp;
xp.Init(sketchshape.getShape(), TopAbs_FACE);
for (;xp.More(); xp.Next()) {
if (checkLineCrossesFace(gp_Lin(pnt, dir), TopoDS::Face(xp.Current())))
return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP("Exception", "Revolve axis intersects the sketch"));
}
// revolve the face to a solid
TopoShape result(0);
try {
result.makeElementRevolve(sketchshape, gp_Ax1(pnt, dir), angle);
}catch(Standard_Failure &) {
return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP("Exception", "Could not revolve the sketch!"));
}
this->AddSubShape.setValue(result);
if(base.isNull()) {
Shape.setValue(getSolid(result));
return App::DocumentObject::StdReturn;
}
result.Tag = -getID();
TopoShape boolOp(0);
try {
const char *maker;
switch (getAddSubType()) {
case Additive:
maker = Part::OpCodes::Fuse;
break;
// case Intersecting:
// maker = Part::OpCodes::Common;
// break;
default:
maker = Part::OpCodes::Cut;
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"));
}
// this->fixShape(result);
boolOp.makeElementBoolean(maker, {base,result});
}catch(Standard_Failure &) {
return new App::DocumentObjectExecReturn("Failed to cut base feature");
}
TopoShape solid = this->getSolid(boolOp);
if (solid.isNull())
return new App::DocumentObjectExecReturn("Resulting shape is not a solid");
// store shape before refinement
this->rawShape = boolOp;
boolOp = refineShapeIfActive(boolOp);
if (!isSingleSolidRuleSatisfied(boolOp.getShape())) {
return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP("Exception", "Result has multiple solids: enable 'Allow Compound' in the active body."));
// Create a fresh support even when base exists so that it can be used for patterns
TopoShape result(0);
TopoShape supportface(0);
try {
supportface = getSupportFace();
}
boolOp = getSolid(boolOp);
Shape.setValue(boolOp);
catch(...) {
// do nothing, null shape is handled below
}
supportface.move(invObjLoc);
if (method == RevolMethod::ToFace || method == RevolMethod::ToFirst) {
TopoShape upToFace;
if (method == RevolMethod::ToFace) {
getUpToFaceFromLinkSub(upToFace, UpToFace);
upToFace.move(invObjLoc);
}
else {
throw Base::RuntimeError(
"ProfileBased: Revolution up to first/last is not yet supported");
}
if (Reversed.getValue()) {
dir.Reverse();
}
TopExp_Explorer Ex(supportface.getShape(), TopAbs_WIRE);
if (!Ex.More()) {
supportface = TopoDS_Face();
}
try {
result = base.makeElementRevolution(base,
TopoDS::Face(sketchshape.getShape()),
gp_Ax1(pnt, dir),
TopoDS::Face(supportface.getShape()),
TopoDS::Face(upToFace.getShape()),
nullptr,
Part::RevolMode::CutFromBase,
Standard_True);
}
catch (Standard_Failure&) {
return new App::DocumentObjectExecReturn("Could not revolve the sketch!");
}
}
else {
bool midplane = Midplane.getValue();
bool reversed = Reversed.getValue();
generateRevolution(result,
sketchshape.getShape(),
gp_Ax1(pnt, dir),
angle,
angle2,
midplane,
reversed,
method);
}
if (!result.isNull()) {
// store shape before refinement
this->rawShape = result;
result = refineShapeIfActive(result);
// set the additive shape property for later usage in e.g. pattern
this->AddSubShape.setValue(result);
if (!base.isNull()) {
result = base.makeElementCut(result);
// store shape before refinement
this->rawShape = result;
result = refineShapeIfActive(result);
}
if (!isSingleSolidRuleSatisfied(result.getShape())) {
return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP("Exception", "Result has multiple solids: enable 'Allow Compound' in the active body."));
}
result = getSolid(result);
this->Shape.setValue(result);
}
else {
return new App::DocumentObjectExecReturn(
QT_TRANSLATE_NOOP("Exception", "Could not revolve the sketch!"));
}
// 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
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());
@@ -212,7 +262,12 @@ App::DocumentObjectExecReturn *Groove::execute()
bool Groove::suggestReversed()
{
updateAxis();
try {
updateAxis();
} catch (const Base::Exception&) {
return false;
}
return ProfileBased::getReversedAngle(Base.getValue(), Axis.getValue()) > 0.0;
}
@@ -224,16 +279,14 @@ void Groove::updateAxis()
Base::Vector3d dir;
getAxis(pcReferenceAxis, subReferenceAxis, base, dir, ForbiddenAxis::NotParallelWithNormal);
if (dir.Length() > Precision::Confusion()) {
Base.setValue(base.x,base.y,base.z);
Axis.setValue(dir.x,dir.y,dir.z);
}
Base.setValue(base.x,base.y,base.z);
Axis.setValue(dir.x,dir.y,dir.z);
}
Groove::RevolMethod Groove::methodFromString(const std::string& methodStr)
{
if (methodStr == "Angle")
return RevolMethod::Dimension;
return RevolMethod::Angle;
if (methodStr == "UpToLast")
return RevolMethod::ToLast;
if (methodStr == "ThroughAll")
@@ -243,14 +296,13 @@ Groove::RevolMethod Groove::methodFromString(const std::string& methodStr)
if (methodStr == "UpToFace")
return RevolMethod::ToFace;
if (methodStr == "TwoAngles")
return RevolMethod::TwoDimensions;
return RevolMethod::TwoAngles;
throw Base::ValueError("Groove:: No such method");
return RevolMethod::Dimension;
}
void Groove::generateRevolution(TopoDS_Shape& revol,
const TopoDS_Shape& sketchshape,
void Groove::generateRevolution(TopoShape& revol,
const TopoShape& sketchshape,
const gp_Ax1& axis,
const double angle,
const double angle2,
@@ -258,11 +310,11 @@ void Groove::generateRevolution(TopoDS_Shape& revol,
const bool reversed,
RevolMethod method)
{
if (method == RevolMethod::Dimension || method == RevolMethod::TwoDimensions || method == RevolMethod::ThroughAll) {
if (method == RevolMethod::Angle || method == RevolMethod::TwoAngles || method == RevolMethod::ThroughAll) {
double angleTotal = angle;
double angleOffset = 0.;
if (method == RevolMethod::TwoDimensions) {
if (method == RevolMethod::TwoAngles) {
// Rotate the face by `angle2`/`angle` to get "second" angle
angleTotal += angle2;
angleOffset = angle2 * -1.0;
@@ -271,43 +323,41 @@ void Groove::generateRevolution(TopoDS_Shape& revol,
angleTotal = 2 * std::numbers::pi;
}
else if (midplane) {
// Rotate the face by half the angle to get Groove symmetric to sketch plane
// Rotate the face by half the angle to get Revolution symmetric to sketch plane
angleOffset = -angle / 2;
}
if (fabs(angleTotal) < Precision::Angular())
throw Base::ValueError("Cannot create a revolution with zero angle.");
TopoDS_Shape from = sketchshape;
if (method == RevolMethod::TwoDimensions || midplane) {
gp_Trsf mov;
mov.SetRotation(axis, angleOffset);
TopLoc_Location loc(mov);
from.Move(loc);
gp_Ax1 revolAx(axis);
if (reversed) {
revolAx.Reverse();
}
else if (reversed) {
angleTotal *= -1.0;
TopoShape from = sketchshape;
if (method == RevolMethod::TwoAngles || midplane) {
gp_Trsf mov;
mov.SetRotation(revolAx, angleOffset);
TopLoc_Location loc(mov);
from.move(loc);
}
// 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, axis, angleTotal);
if (!RevolMaker.IsDone())
throw Base::RuntimeError("ProfileBased: RevolMaker failed! Could not revolve the sketch!");
else
revol = RevolMaker.Shape();
}
else {
revol = from;
revol = revol.makeElementRevolve(revolAx,angleTotal);
revol.Tag = -getID();
} else {
std::stringstream str;
str << "ProfileBased: Internal error: Unknown method for generateGroove()";
str << "ProfileBased: Internal error: Unknown method for generateRevolution()";
throw Base::RuntimeError(str.str());
}
}
void Groove::generateRevolution(TopoDS_Shape& revol,
const TopoDS_Shape& baseshape,
void Groove::generateRevolution(TopoShape& revol,
const TopoShape& baseshape,
const TopoDS_Shape& profileshape,
const TopoDS_Face& supportface,
const TopoDS_Face& uptoface,
@@ -317,20 +367,8 @@ void Groove::generateRevolution(TopoDS_Shape& revol,
Standard_Boolean Modify)
{
if (method == RevolMethod::ToFirst || method == RevolMethod::ToFace || method == RevolMethod::ToLast) {
BRepFeat_MakeRevol RevolMaker;
TopoDS_Shape base = baseshape;
for (TopExp_Explorer xp(profileshape, TopAbs_FACE); xp.More(); xp.Next()) {
RevolMaker.Init(base, xp.Current(), supportface, axis, Mode, Modify);
RevolMaker.Perform(uptoface);
if (!RevolMaker.IsDone())
throw Base::RuntimeError("ProfileBased: Up to face: Could not revolve the sketch!");
base = RevolMaker.Shape();
if (Mode == RevolMode::None)
Mode = RevolMode::FuseWithBase;
}
revol = base;
revol = revol.makeElementRevolution(baseshape, profileshape, axis, supportface, uptoface, nullptr,
static_cast<Part::RevolMode>(Mode), Modify, nullptr);
}
else {
std::stringstream str;
@@ -348,7 +386,7 @@ void Groove::updateProperties(RevolMethod method)
bool isMidplaneEnabled = false;
bool isReversedEnabled = false;
bool isUpToFaceEnabled = false;
if (method == RevolMethod::Dimension) {
if (method == RevolMethod::Angle) {
isAngleEnabled = true;
isMidplaneEnabled = true;
isReversedEnabled = !Midplane.getValue();
@@ -367,7 +405,7 @@ void Groove::updateProperties(RevolMethod method)
isReversedEnabled = true;
isUpToFaceEnabled = true;
}
else if (method == RevolMethod::TwoDimensions) {
else if (method == RevolMethod::TwoAngles) {
isAngleEnabled = true;
isAngle2Enabled = true;
isReversedEnabled = true;
@@ -380,7 +418,5 @@ void Groove::updateProperties(RevolMethod method)
UpToFace.setReadOnly(!isUpToFaceEnabled);
}
}

View File

@@ -69,12 +69,12 @@ public:
bool suggestReversed();
enum class RevolMethod {
Dimension,
Angle,
ThroughAll,
ToLast = ThroughAll,
ToFirst,
ToFace,
TwoDimensions
TwoAngles
};
protected:
@@ -95,8 +95,8 @@ protected:
/**
* Generates a [groove] of the input sketchshape and stores it in the given \a revol.
*/
void generateRevolution(TopoDS_Shape& revol,
const TopoDS_Shape& sketchshape,
void generateRevolution(TopoShape& revol,
const TopoShape& sketchshape,
const gp_Ax1& ax1,
const double angle,
const double angle2,
@@ -108,8 +108,8 @@ protected:
* Generates a [groove] of the input \a profileshape.
* It will be a stand-alone solid created with BRepFeat_MakeRevol.
*/
void generateRevolution(TopoDS_Shape& revol,
const TopoDS_Shape& baseshape,
void generateRevolution(TopoShape& revol,
const TopoShape& baseshape,
const TopoDS_Shape& profileshape,
const TopoDS_Face& supportface,
const TopoDS_Face& uptoface,

View File

@@ -393,6 +393,10 @@ gp_Dir LinearPattern::getDirectionFromProperty(const App::PropertyLinkSub& dirPr
Base::Vector3d d = line->getDirection();
dir = gp_Dir(d.x, d.y, d.z);
}
else if (auto* plane = freecad_cast<App::Plane*>(refObject)) {
Base::Vector3d d = plane->getDirection();
dir = gp_Dir(d.x, d.y, d.z);
}
else if (auto* line = freecad_cast<App::Line*>(refObject)) {
Base::Vector3d d = line->getDirection();
dir = gp_Dir(d.x, d.y, d.z);

View File

@@ -40,6 +40,7 @@
# include <TopTools_HSequenceOfShape.hxx>
#include <App/Document.h>
#include <App/DocumentObject.h>
#include <Base/Exception.h>
#include <Base/Reader.h>
@@ -151,7 +152,6 @@ App::DocumentObjectExecReturn *Pipe::execute()
// 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
@@ -162,6 +162,8 @@ App::DocumentObjectExecReturn *Pipe::execute()
base = TopoShape();
}
auto hasher = getDocument()->getStringHasher();
try {
// setup the location
this->positionByPrevious();
@@ -287,7 +289,7 @@ App::DocumentObjectExecReturn *Pipe::execute()
"Exception", "Path must not be a null shape"));
// build all shells
std::vector<TopoDS_Shape> shells;
std::vector<TopoShape> shells;
TopoDS_Shape copyProfilePoint(profilePoint);
if (!profilePoint.IsNull())
@@ -320,7 +322,7 @@ App::DocumentObjectExecReturn *Pipe::execute()
if (!mkPS.IsReady())
return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP("Exception", "Pipe could not be built"));
shells.push_back(mkPS.Shape());
shells.emplace_back(mkPS.Shape());
if (!mkPS.Shape().Closed()) {
// shell is not closed - use simulate to get the end wires
@@ -336,8 +338,7 @@ App::DocumentObjectExecReturn *Pipe::execute()
}
}
BRepBuilderAPI_MakeSolid mkSolid;
TopoShape result(0, hasher);
if (!frontwires.empty() || !backwires.empty()) {
BRepBuilderAPI_Sewing sewer;
sewer.SetTolerance(Precision::Confusion());
@@ -346,94 +347,103 @@ App::DocumentObjectExecReturn *Pipe::execute()
if (!frontwires.empty()) {
TopoDS_Shape front = Part::FaceMakerCheese::makeFace(frontwires);
sewer.Add(front);
shells.emplace_back(front);
}
if (!backwires.empty()) {
TopoDS_Shape back = Part::FaceMakerCheese::makeFace(backwires);
sewer.Add(back);
shells.emplace_back(back);
}
for (TopoDS_Shape& s : shells)
sewer.Add(s);
for (TopoShape& s : shells)
sewer.Add(s.getShape());
sewer.Perform();
mkSolid.Add(TopoDS::Shell(sewer.SewedShape())); } else {
result = result.makeShapeWithElementMap(sewer.SewedShape(), Part::MapperSewing(sewer), shells, Part::OpCodes::Sewing);
} else {
// shells are already closed - add them directly
for (TopoDS_Shape& s : shells) {
mkSolid.Add(TopoDS::Shell(s));
BRepBuilderAPI_MakeSolid mkSolid;
for (TopoShape& s : shells) {
mkSolid.Add(TopoDS::Shell(s.getShape()));
}
if (!mkSolid.IsDone())
return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP("Exception", "Result is not a solid"));
result.setShape(mkSolid.Shape());
}
if (!mkSolid.IsDone())
return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP("Exception", "Result is not a solid"));
if(!result.countSubShapes(TopAbs_SHELL))
return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP("Exception", "Loft: Failed to create shell"));
TopoDS_Shape result = mkSolid.Shape();
BRepClass3d_SolidClassifier SC(result);
SC.PerformInfinitePoint(Precision::Confusion());
if (SC.State() == TopAbs_IN) {
result.Reverse();
auto shapes = result.getSubTopoShapes(TopAbs_SHELL);
for (auto &s : shapes) {
// build the solid
s = s.makeElementSolid();
BRepClass3d_SolidClassifier SC(s.getShape());
SC.PerformInfinitePoint(Precision::Confusion());
if ( SC.State() == TopAbs_IN)
s.setShape(s.getShape().Reversed(),false);
}
//result.Move(invObjLoc);
AddSubShape.setValue(result); // Converts result to a TopoShape, but no tag.
AddSubShape.setValue(result.makeElementCompound(shapes, nullptr, Part::TopoShape::SingleShapeCompoundCreationPolicy::returnShape));
if (base.isNull()) {
if (shapes.size() > 1)
result.makeElementFuse(shapes);
else
result = shapes.front();
if(base.isNull()) {
if (getAddSubType() == FeatureAddSub::Subtractive)
return new App::DocumentObjectExecReturn(
QT_TRANSLATE_NOOP("Exception", "Pipe: There is nothing to subtract from"));
if (!isSingleSolidRuleSatisfied(result.getShape())) {
return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP("Exception", "Result has multiple solids: enable 'Allow Compound' in the active body."));
}
// store shape before refinement
this->rawShape = result;
auto ts_result = refineShapeIfActive(result);
Shape.setValue(getSolid(ts_result));
result = refineShapeIfActive(result);
Shape.setValue(getSolid(result));
return App::DocumentObject::StdReturn;
}
if (getAddSubType() == FeatureAddSub::Additive) {
FCBRepAlgoAPI_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"));
if (!isSingleSolidRuleSatisfied(boolOp.getShape())) {
return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP("Exception",
"Result has multiple solids: enable 'Allow Compound' in the active body."));
}
// store shape before refinement
this->rawShape = boolOp;
boolOp = refineShapeIfActive(boolOp);
Shape.setValue(getSolid(boolOp));
TopoShape boolOp(0, getDocument()->getStringHasher());
const char *maker;
switch (getAddSubType()) {
case Additive:
maker = Part::OpCodes::Fuse;
break;
case Subtractive:
maker = Part::OpCodes::Cut;
break;
default:
return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP("Exception", "Unknown operation type"));
}
else if (getAddSubType() == FeatureAddSub::Subtractive) {
FCBRepAlgoAPI_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"));
if (!isSingleSolidRuleSatisfied(boolOp.getShape())) {
return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP("Exception",
"Result has multiple solids: enable 'Allow Compound' in the active body."));
}
// store shape before refinement
this->rawShape = boolOp;
boolOp = refineShapeIfActive(boolOp);
Shape.setValue(getSolid(boolOp));
try {
boolOp.makeElementBoolean(maker, {base,result});
}
catch(Standard_Failure&) {
return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP("Exception", "Failed to perform boolean operation"));
}
TopoShape solid = getSolid(boolOp);
// lets check if the result is a solid
if (solid.isNull())
return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP("Exception", "Resulting shape is not a solid"));
// store shape before refinement
this->rawShape = boolOp;
boolOp = refineShapeIfActive(boolOp);
if (!isSingleSolidRuleSatisfied(boolOp.getShape())) {
return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP("Exception",
"Result has multiple solids: enable 'Allow Compound' in the active body."));
}
boolOp = getSolid(boolOp);
Shape.setValue(boolOp);
return App::DocumentObject::StdReturn;
}
catch (Standard_Failure& e) {
return new App::DocumentObjectExecReturn(e.GetMessageString());
}
catch (...) {
@@ -636,4 +646,3 @@ void Pipe::handleChangedPropertyName(Base::XMLReader& reader,
}
}

View File

@@ -30,9 +30,7 @@
# include <TopoDS.hxx>
#include <Base/Axis.h>
#include <Base/Exception.h>
#include <Base/Placement.h>
#include <Base/Tools.h>
#include "FeatureRevolution.h"
@@ -42,11 +40,13 @@ using namespace PartDesign;
namespace PartDesign {
/* TRANSLATOR PartDesign::Revolution */
const char* Revolution::TypeEnums[]= {"Angle", "UpToLast", "UpToFirst", "UpToFace", "TwoAngles", nullptr};
PROPERTY_SOURCE(PartDesign::Revolution, PartDesign::ProfileBased)
const App::PropertyAngle::Constraints Revolution::floatAngle = { Base::toDegrees<double>(Precision::Angular()), 360.0, 1.0 };
const App::PropertyAngle::Constraints Revolution::floatAngle = { 0.0, 360.0, 1.0 };
Revolution::Revolution()
{
@@ -54,14 +54,14 @@ Revolution::Revolution()
ADD_PROPERTY_TYPE(Type, (0L), "Revolution", App::Prop_None, "Revolution type");
Type.setEnums(TypeEnums);
ADD_PROPERTY_TYPE(Base,(Base::Vector3d(0.0,0.0,0.0)),"Revolution", App::PropertyType(App::Prop_ReadOnly | App::Prop_Hidden), "Base");
ADD_PROPERTY_TYPE(Axis,(Base::Vector3d(0.0,1.0,0.0)),"Revolution", App::PropertyType(App::Prop_ReadOnly | App::Prop_Hidden), "Axis");
ADD_PROPERTY_TYPE(Angle,(360.0),"Revolution", App::Prop_None, "Angle");
ADD_PROPERTY_TYPE(Angle2, (60.0), "Revolution", App::Prop_None, "Revolution length in 2nd direction");
ADD_PROPERTY_TYPE(Base, (Base::Vector3d(0.0,0.0,0.0)), "Revolution", App::PropertyType(App::Prop_ReadOnly | App::Prop_Hidden), "Base");
ADD_PROPERTY_TYPE(Axis, (Base::Vector3d(0.0,1.0,0.0)), "Revolution", App::PropertyType(App::Prop_ReadOnly | App::Prop_Hidden), "Axis");
ADD_PROPERTY_TYPE(Angle, (360.0), "Revolution", App::Prop_None, "Angle");
ADD_PROPERTY_TYPE(Angle2, (0.0), "Revolution", App::Prop_None, "Revolution length in 2nd direction");
ADD_PROPERTY_TYPE(UpToFace, (nullptr), "Revolution", App::Prop_None, "Face where revolution will end");
Angle.setConstraints(&floatAngle);
Angle2.setConstraints(&floatAngle);
ADD_PROPERTY_TYPE(ReferenceAxis,(nullptr),"Revolution",(App::Prop_None),"Reference axis of revolution");
ADD_PROPERTY_TYPE(ReferenceAxis, (nullptr), "Revolution", (App::Prop_None), "Reference axis of revolution");
}
short Revolution::mustExecute() const
@@ -82,21 +82,27 @@ App::DocumentObjectExecReturn* Revolution::execute()
if (onlyHaveRefined()) { return App::DocumentObject::StdReturn; }
constexpr double maxDegree = 360.0;
auto method = methodFromString(Type.getValueAsString());
// Validate parameters
// All angles are in radians unless explicitly stated
double angleDeg = Angle.getValue();
if (angleDeg > 360.0) {
if (angleDeg > maxDegree) {
return new App::DocumentObjectExecReturn(
QT_TRANSLATE_NOOP("Exception", "Angle of revolution too large"));
}
double angle = Base::toRadians<double>(angleDeg);
if (angle < Precision::Angular()) {
if (angle < Precision::Angular() && method == RevolMethod::Angle) {
return new App::DocumentObjectExecReturn(
QT_TRANSLATE_NOOP("Exception", "Angle of revolution too small"));
}
double angle2 = Base::toRadians(Angle2.getValue());
if (std::fabs(angle + angle2) < Precision::Angular() && method == RevolMethod::TwoAngles) {
return new App::DocumentObjectExecReturn(
QT_TRANSLATE_NOOP("Exception", "Angles of revolution nullify each other"));
}
TopoShape sketchshape = getTopoShapeVerifiedFace();
@@ -135,10 +141,8 @@ App::DocumentObjectExecReturn* Revolution::execute()
QT_TRANSLATE_NOOP("Exception", "Creating a face from sketch failed"));
}
RevolMethod method = methodFromString(Type.getValueAsString());
this->positionByPrevious();
TopLoc_Location invObjLoc = this->getLocation().Inverted();
auto invObjLoc = getLocation().Inverted();
pnt.Transform(invObjLoc.Transformation());
dir.Transform(invObjLoc.Transformation());
base.move(invObjLoc);
@@ -166,8 +170,7 @@ App::DocumentObjectExecReturn* Revolution::execute()
supportface.move(invObjLoc);
if (method == RevolMethod::ToFace || method == RevolMethod::ToFirst
|| method == RevolMethod::ToLast) {
if (method == RevolMethod::ToFace || method == RevolMethod::ToFirst) {
TopoShape upToFace;
if (method == RevolMethod::ToFace) {
getUpToFaceFromLinkSub(upToFace, UpToFace);
@@ -194,7 +197,7 @@ App::DocumentObjectExecReturn* Revolution::execute()
TopoDS::Face(supportface.getShape()),
TopoDS::Face(upToFace.getShape()),
nullptr,
Part::RevolMode::None,
Part::RevolMode::FuseWithBase,
Standard_True);
}
catch (Standard_Failure&) {
@@ -286,7 +289,7 @@ void Revolution::updateAxis()
Revolution::RevolMethod Revolution::methodFromString(const std::string& methodStr)
{
if (methodStr == "Angle")
return RevolMethod::Dimension;
return RevolMethod::Angle;
if (methodStr == "UpToLast")
return RevolMethod::ToLast;
if (methodStr == "ThroughAll")
@@ -296,10 +299,9 @@ Revolution::RevolMethod Revolution::methodFromString(const std::string& methodSt
if (methodStr == "UpToFace")
return RevolMethod::ToFace;
if (methodStr == "TwoAngles")
return RevolMethod::TwoDimensions;
return RevolMethod::TwoAngles;
throw Base::ValueError("Revolution:: No such method");
return RevolMethod::Dimension;
}
void Revolution::generateRevolution(TopoShape& revol,
@@ -311,15 +313,18 @@ void Revolution::generateRevolution(TopoShape& revol,
const bool reversed,
RevolMethod method)
{
if (method == RevolMethod::Dimension || method == RevolMethod::TwoDimensions || method == RevolMethod::ThroughAll) {
if (method == RevolMethod::Angle || method == RevolMethod::TwoAngles || method == RevolMethod::ThroughAll) {
double angleTotal = angle;
double angleOffset = 0.;
if (method == RevolMethod::TwoDimensions) {
if (method == RevolMethod::TwoAngles) {
// Rotate the face by `angle2`/`angle` to get "second" angle
angleTotal += angle2;
angleOffset = angle2 * -1.0;
}
else if (method == RevolMethod::ThroughAll) {
angleTotal = 2 * M_PI;
}
else if (midplane) {
// Rotate the face by half the angle to get Revolution symmetric to sketch plane
angleOffset = -angle / 2;
@@ -334,13 +339,16 @@ void Revolution::generateRevolution(TopoShape& revol,
}
TopoShape from = sketchshape;
if (method == RevolMethod::TwoDimensions || midplane) {
if (method == RevolMethod::TwoAngles || midplane) {
gp_Trsf mov;
mov.SetRotation(revolAx, angleOffset);
TopLoc_Location loc(mov);
from.move(loc);
}
// 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.
revol = from;
revol = revol.makeElementRevolve(revolAx,angleTotal);
revol.Tag = -getID();
@@ -381,7 +389,7 @@ void Revolution::updateProperties(RevolMethod method)
bool isMidplaneEnabled = false;
bool isReversedEnabled = false;
bool isUpToFaceEnabled = false;
if (method == RevolMethod::Dimension) {
if (method == RevolMethod::Angle) {
isAngleEnabled = true;
isMidplaneEnabled = true;
isReversedEnabled = !Midplane.getValue();
@@ -400,7 +408,7 @@ void Revolution::updateProperties(RevolMethod method)
isReversedEnabled = true;
isUpToFaceEnabled = true;
}
else if (method == RevolMethod::TwoDimensions) {
else if (method == RevolMethod::TwoAngles) {
isAngleEnabled = true;
isAngle2Enabled = true;
isReversedEnabled = true;

View File

@@ -69,12 +69,12 @@ public:
bool suggestReversed();
enum class RevolMethod {
Dimension,
Angle,
ThroughAll,
ToLast = ThroughAll,
ToFirst,
ToFace,
TwoDimensions
TwoAngles
};
protected:

View File

@@ -57,7 +57,6 @@ namespace sp = std::placeholders;
TaskHoleParameters::TaskHoleParameters(ViewProviderHole* HoleView, QWidget* parent)
: TaskSketchBasedParameters(HoleView, parent, "PartDesign_Hole", tr("Hole Parameters"))
, observer(new Observer(this, getObject<PartDesign::Hole>()))
, isApplying(false)
, ui(new Ui_TaskHoleParameters)
{
// we need a separate container widget to add all controls to
@@ -1087,8 +1086,6 @@ void TaskHoleParameters::apply()
{
auto hole = getObject<PartDesign::Hole>();
isApplying = true;
ui->Diameter->apply();
ui->HoleCutDiameter->apply();
ui->HoleCutDepth->apply();
@@ -1152,8 +1149,6 @@ void TaskHoleParameters::apply()
if (!hole->BaseProfileType.isReadOnly()) {
FCMD_OBJ_CMD(hole, "BaseProfileType = " << getBaseProfileType());
}
isApplying = false;
}
void TaskHoleParameters::updateHoleCutLimits(PartDesign::Hole* hole)

View File

@@ -143,7 +143,6 @@ private:
Connection connectPropChanged;
std::unique_ptr<Observer> observer;
bool isApplying;
QWidget* proxy;
std::unique_ptr<Ui_TaskHoleParameters> ui;

View File

@@ -188,7 +188,7 @@ void TaskRevolutionParameters::setupDialog()
void TaskRevolutionParameters::translateModeList(int index)
{
ui->changeMode->clear();
ui->changeMode->addItem(tr("Dimension"));
ui->changeMode->addItem(tr("Angle"));
if (!isGroove) {
ui->changeMode->addItem(tr("To last"));
}
@@ -197,7 +197,7 @@ void TaskRevolutionParameters::translateModeList(int index)
}
ui->changeMode->addItem(tr("To first"));
ui->changeMode->addItem(tr("Up to face"));
ui->changeMode->addItem(tr("Two dimensions"));
ui->changeMode->addItem(tr("Two angles"));
ui->changeMode->setCurrentIndex(index);
}
@@ -294,7 +294,7 @@ void TaskRevolutionParameters::setCheckboxes(PartDesign::Revolution::RevolMethod
bool isReversedEnabled = false;
bool isFaceEditEnabled = false;
if (mode == PartDesign::Revolution::RevolMethod::Dimension) {
if (mode == PartDesign::Revolution::RevolMethod::Angle) {
isRevolveAngleVisible = true;
ui->revolveAngle->selectNumber();
QMetaObject::invokeMethod(ui->revolveAngle, "setFocus", Qt::QueuedConnection);
@@ -320,7 +320,7 @@ void TaskRevolutionParameters::setCheckboxes(PartDesign::Revolution::RevolMethod
ui->buttonFace->setChecked(true);
}
}
else if (mode == PartDesign::Revolution::RevolMethod::TwoDimensions) {
else if (mode == PartDesign::Revolution::RevolMethod::TwoAngles) {
isRevolveAngleVisible = true;
isRevolveAngle2Visible = true;
isReversedEnabled = true;
@@ -624,7 +624,7 @@ void TaskRevolutionParameters::onModeChanged(int index)
: &(getObject<PartDesign::Revolution>()->Type);
switch (static_cast<PartDesign::Revolution::RevolMethod>(index)) {
case PartDesign::Revolution::RevolMethod::Dimension:
case PartDesign::Revolution::RevolMethod::Angle:
propEnum->setValue("Angle");
break;
case PartDesign::Revolution::RevolMethod::ToLast:
@@ -636,7 +636,7 @@ void TaskRevolutionParameters::onModeChanged(int index)
case PartDesign::Revolution::RevolMethod::ToFace:
propEnum->setValue("UpToFace");
break;
case PartDesign::Revolution::RevolMethod::TwoDimensions:
case PartDesign::Revolution::RevolMethod::TwoAngles:
propEnum->setValue("TwoAngles");
break;
}
@@ -809,12 +809,12 @@ void TaskRevolutionParameters::setGizmoVisibility()
auto type = static_cast<PartDesign::Revolution::RevolMethod>(ui->changeMode->currentIndex());
switch (type) {
case PartDesign::Revolution::RevolMethod::Dimension:
case PartDesign::Revolution::RevolMethod::Angle:
gizmoContainer->visible = true;
rotationGizmo->setVisibility(true);
rotationGizmo2->setVisibility(false);
break;
case PartDesign::Revolution::RevolMethod::TwoDimensions:
case PartDesign::Revolution::RevolMethod::TwoAngles:
gizmoContainer->visible = true;
rotationGizmo->setVisibility(true);
rotationGizmo2->setVisibility(true);

View File

@@ -27,7 +27,7 @@
<widget class="QComboBox" name="changeMode">
<item>
<property name="text">
<string>Dimension</string>
<string notr="true">Angle</string>
</property>
</item>
</widget>
@@ -139,7 +139,7 @@
<double>10.000000000000000</double>
</property>
<property name="value">
<double>60.000000000000000</double>
<double>0.000000000000000</double>
</property>
</widget>
</item>

View File

@@ -162,6 +162,19 @@ void Workbench::activated()
"PartDesign_Body"
));
const char* Vertex1[] = {
"PartDesign_Point",
"PartDesign_Line",
"PartDesign_Plane",
"PartDesign_CoordinateSystem",
nullptr};
Watcher.push_back(new Gui::TaskView::TaskWatcherCommands(
"SELECT Part::Feature SUBELEMENT Vertex COUNT 1..",
Vertex1,
"Datum objects",
"PartDesign_CoordinateSystem"
));
const char* Edge[] = {
"PartDesign_Fillet",
"PartDesign_Chamfer",
@@ -177,6 +190,19 @@ void Workbench::activated()
"PartDesign_Body"
));
const char* Edge1[] = {
"PartDesign_Point",
"PartDesign_Line",
"PartDesign_Plane",
"PartDesign_CoordinateSystem",
nullptr};
Watcher.push_back(new Gui::TaskView::TaskWatcherCommands(
"SELECT Part::Feature SUBELEMENT Edge COUNT 1..",
Edge1,
"Datum objects",
"PartDesign_CoordinateSystem"
));
const char* Face[] = {
"PartDesign_NewSketch",
"PartDesign_Fillet",
@@ -195,6 +221,19 @@ void Workbench::activated()
"PartDesign_Body"
));
const char* Face1[] = {
"PartDesign_Point",
"PartDesign_Line",
"PartDesign_Plane",
"PartDesign_CoordinateSystem",
nullptr};
Watcher.push_back(new Gui::TaskView::TaskWatcherCommands(
"SELECT Part::Feature SUBELEMENT Face COUNT 1",
Face1,
"Datum objects",
"PartDesign_CoordinateSystem"
));
const char* Body[] = {
"PartDesign_NewSketch",
nullptr};
@@ -228,6 +267,7 @@ void Workbench::activated()
"Helper Tools",
"PartDesign_Body"
));
const char* Plane2[] = {
"PartDesign_NewSketch",
"Part_DatumPoint",
@@ -242,6 +282,32 @@ void Workbench::activated()
"PartDesign_Body"
));
const char* Plane3[] = {
"PartDesign_Point",
"PartDesign_Line",
"PartDesign_Plane",
"PartDesign_CoordinateSystem",
nullptr};
Watcher.push_back(new Gui::TaskView::TaskWatcherCommands(
"SELECT App::Plane COUNT 1",
Plane3,
"Datum objects",
"PartDesign_CoordinateSystem"
));
const char* Plane4[] = {
"PartDesign_Point",
"PartDesign_Line",
"PartDesign_Plane",
"PartDesign_CoordinateSystem",
nullptr};
Watcher.push_back(new Gui::TaskView::TaskWatcherCommands(
"SELECT PartDesign::Plane COUNT 1",
Plane4,
"Datum objects",
"PartDesign_CoordinateSystem"
));
const char* Line[] = {
"Part_DatumPoint",
"Part_DatumLine",
@@ -254,6 +320,18 @@ void Workbench::activated()
"PartDesign_Body"
));
const char* Line1[] = {
"PartDesign_Point",
"PartDesign_Line",
"PartDesign_Plane",
nullptr};
Watcher.push_back(new Gui::TaskView::TaskWatcherCommands(
"SELECT PartDesign::Line COUNT 1",
Line1,
"Datum objects",
"PartDesign_CoordinateSystem"
));
const char* Point[] = {
"Part_DatumPoint",
"Part_DatumLine",
@@ -267,6 +345,19 @@ void Workbench::activated()
"PartDesign_Body"
));
const char* Point1[] = {
"PartDesign_Point",
"PartDesign_Line",
"PartDesign_Plane",
"PartDesign_CoordinateSystem",
nullptr};
Watcher.push_back(new Gui::TaskView::TaskWatcherCommands(
"SELECT PartDesign::Point COUNT 1",
Point1,
"Datum objects",
"PartDesign_CoordinateSystem"
));
const char* NoSel[] = {
"PartDesign_Body",
nullptr};
@@ -296,10 +387,10 @@ void Workbench::activated()
"PartDesign_Hole",
"PartDesign_Revolution",
"PartDesign_Groove",
"PartDesign_AdditivePipe",
"PartDesign_SubtractivePipe",
"PartDesign_AdditiveLoft",
"PartDesign_SubtractiveLoft",
"PartDesign_AdditivePipe",
"PartDesign_SubtractivePipe",
"PartDesign_AdditiveHelix",
"PartDesign_SubtractiveHelix",
nullptr};
@@ -310,6 +401,53 @@ void Workbench::activated()
"PartDesign_Body"
));
const char* Sketches[] = {
"PartDesign_AdditiveLoft",
"PartDesign_SubtractiveLoft",
"PartDesign_AdditivePipe",
"PartDesign_SubtractivePipe",
nullptr};
Watcher.push_back(new Gui::TaskView::TaskWatcherCommands(
"SELECT Sketcher::SketchObject COUNT 2..",
Sketches,
"Modeling tools",
"PartDesign_Body"
));
const char* ShapeBinder[] = {
"PartDesign_Pad",
"PartDesign_Pocket",
"PartDesign_Revolution",
"PartDesign_Groove",
"PartDesign_AdditiveLoft",
"PartDesign_SubtractiveLoft",
"PartDesign_AdditivePipe",
"PartDesign_SubtractivePipe",
nullptr};
Watcher.push_back(new Gui::TaskView::TaskWatcherCommands(
"SELECT PartDesign::ShapeBinder COUNT 1",
ShapeBinder,
"Modeling tools",
"PartDesign_Body"
));
const char* SubShapeBinder[] = {
"PartDesign_Pad",
"PartDesign_Pocket",
"PartDesign_Revolution",
"PartDesign_Groove",
"PartDesign_AdditiveLoft",
"PartDesign_SubtractiveLoft",
"PartDesign_AdditivePipe",
"PartDesign_SubtractivePipe",
nullptr};
Watcher.push_back(new Gui::TaskView::TaskWatcherCommands(
"SELECT PartDesign::SubShapeBinder COUNT 1",
SubShapeBinder,
"Modeling tools",
"PartDesign_Body"
));
const char* Transformed[] = {
"PartDesign_Mirrored",
"PartDesign_LinearPattern",