Merge pull request #7193 from AjinkyaDahale/pd-more-revol-options
PD: more options for revolution/groove
This commit is contained in:
@@ -25,6 +25,7 @@
|
||||
#ifndef _PreComp_
|
||||
# include <BRepAlgoAPI_Cut.hxx>
|
||||
# include <BRepPrimAPI_MakeRevol.hxx>
|
||||
# include <BRepFeat_MakeRevol.hxx>
|
||||
# include <gp_Lin.hxx>
|
||||
# include <TopoDS.hxx>
|
||||
# include <TopExp_Explorer.hxx>
|
||||
@@ -41,9 +42,10 @@ using namespace PartDesign;
|
||||
|
||||
namespace PartDesign {
|
||||
|
||||
|
||||
/* TRANSLATOR PartDesign::Groove */
|
||||
|
||||
const char* Groove::TypeEnums[]= {"Angle", "ThroughAll", "UpToFirst", "UpToFace", "TwoAngles", nullptr};
|
||||
|
||||
PROPERTY_SOURCE(PartDesign::Groove, PartDesign::ProfileBased)
|
||||
|
||||
const App::PropertyAngle::Constraints Groove::floatAngle = { Base::toDegrees<double>(Precision::Angular()), 360.0, 1.0 };
|
||||
@@ -52,11 +54,15 @@ Groove::Groove()
|
||||
{
|
||||
addSubType = FeatureAddSub::Subtractive;
|
||||
|
||||
ADD_PROPERTY_TYPE(Base,(Base::Vector3d(0.0f,0.0f,0.0f)),"Groove", App::Prop_ReadOnly, "Base");
|
||||
ADD_PROPERTY_TYPE(Axis,(Base::Vector3d(0.0f,1.0f,0.0f)),"Groove", App::Prop_ReadOnly, "Axis");
|
||||
ADD_PROPERTY_TYPE(Angle,(360.0),"Groove", App::Prop_None, "Angle");
|
||||
ADD_PROPERTY_TYPE(Type, (0L), "Groove", App::Prop_None, "Groove type");
|
||||
Type.setEnums(TypeEnums);
|
||||
ADD_PROPERTY_TYPE(Base, (Base::Vector3d(0.0f,0.0f,0.0f)), "Groove", App::Prop_ReadOnly, "Base");
|
||||
ADD_PROPERTY_TYPE(Axis, (Base::Vector3d(0.0f,1.0f,0.0f)), "Groove", App::Prop_ReadOnly, "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(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::PropertyType)(App::Prop_None), "Reference axis of Groove");
|
||||
}
|
||||
|
||||
short Groove::mustExecute() const
|
||||
@@ -65,7 +71,9 @@ short Groove::mustExecute() const
|
||||
ReferenceAxis.isTouched() ||
|
||||
Axis.isTouched() ||
|
||||
Base.isTouched() ||
|
||||
Angle.isTouched())
|
||||
UpToFace.isTouched() ||
|
||||
Angle.isTouched() ||
|
||||
Angle2.isTouched())
|
||||
return 1;
|
||||
return ProfileBased::mustExecute();
|
||||
}
|
||||
@@ -73,17 +81,16 @@ short Groove::mustExecute() const
|
||||
App::DocumentObjectExecReturn *Groove::execute()
|
||||
{
|
||||
// Validate parameters
|
||||
double angle = Angle.getValue();
|
||||
if (angle > 360.0)
|
||||
// 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"));
|
||||
|
||||
angle = Base::toRadians<double>(angle);
|
||||
double angle = Base::toRadians<double>(angleDeg);
|
||||
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);
|
||||
double angle2 = Base::toRadians(Angle2.getValue());
|
||||
|
||||
TopoDS_Shape sketchshape;
|
||||
try {
|
||||
@@ -105,7 +112,12 @@ App::DocumentObjectExecReturn *Groove::execute()
|
||||
return new App::DocumentObjectExecReturn(text);
|
||||
}
|
||||
|
||||
updateAxis();
|
||||
// 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();
|
||||
@@ -117,13 +129,7 @@ App::DocumentObjectExecReturn *Groove::execute()
|
||||
if (sketchshape.IsNull())
|
||||
return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP("Exception", "Creating a face from sketch failed"));
|
||||
|
||||
// Rotate the face by half the angle to get Groove symmetric to sketch plane
|
||||
if (Midplane.getValue()) {
|
||||
gp_Trsf mov;
|
||||
mov.SetRotation(gp_Ax1(pnt, dir), Base::toRadians<double>(Angle.getValue()) * (-1.0) / 2.0);
|
||||
TopLoc_Location loc(mov);
|
||||
sketchshape.Move(loc);
|
||||
}
|
||||
RevolMethod method = methodFromString(Type.getValueAsString());
|
||||
|
||||
this->positionByPrevious();
|
||||
TopLoc_Location invObjLoc = this->getLocation().Inverted();
|
||||
@@ -140,11 +146,61 @@ App::DocumentObjectExecReturn *Groove::execute()
|
||||
return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP("Exception", "Revolve axis intersects the sketch"));
|
||||
}
|
||||
|
||||
// revolve the face to a solid
|
||||
BRepPrimAPI_MakeRevol RevolMaker(sketchshape, gp_Ax1(pnt, dir), angle);
|
||||
// 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);
|
||||
|
||||
int resultCount = countSolids(result);
|
||||
if (resultCount > 1) {
|
||||
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!"));
|
||||
|
||||
if (RevolMaker.IsDone()) {
|
||||
TopoDS_Shape result = RevolMaker.Shape();
|
||||
// set the subtractive shape property for later usage in e.g. pattern
|
||||
result = refineShapeIfActive(result);
|
||||
this->AddSubShape.setValue(result);
|
||||
@@ -167,10 +223,10 @@ App::DocumentObjectExecReturn *Groove::execute()
|
||||
if (solidCount > 1) {
|
||||
return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP("Exception", "Result has multiple solids: that is not currently supported."));
|
||||
}
|
||||
|
||||
}
|
||||
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;
|
||||
}
|
||||
@@ -207,4 +263,155 @@ void Groove::updateAxis()
|
||||
}
|
||||
}
|
||||
|
||||
Groove::RevolMethod Groove::methodFromString(const std::string& methodStr)
|
||||
{
|
||||
if (methodStr == "Angle")
|
||||
return RevolMethod::Dimension;
|
||||
if (methodStr == "UpToLast")
|
||||
return RevolMethod::ToLast;
|
||||
if (methodStr == "ThroughAll")
|
||||
return RevolMethod::ThroughAll;
|
||||
if (methodStr == "UpToFirst")
|
||||
return RevolMethod::ToFirst;
|
||||
if (methodStr == "UpToFace")
|
||||
return RevolMethod::ToFace;
|
||||
if (methodStr == "TwoAngles")
|
||||
return RevolMethod::TwoDimensions;
|
||||
|
||||
throw Base::ValueError("Groove:: No such method");
|
||||
return RevolMethod::Dimension;
|
||||
}
|
||||
|
||||
void Groove::generateRevolution(TopoDS_Shape& revol,
|
||||
const TopoDS_Shape& sketchshape,
|
||||
const gp_Ax1& axis,
|
||||
const double angle,
|
||||
const double angle2,
|
||||
const bool midplane,
|
||||
const bool reversed,
|
||||
RevolMethod method)
|
||||
{
|
||||
if (method == RevolMethod::Dimension || method == RevolMethod::TwoDimensions || method == RevolMethod::ThroughAll) {
|
||||
double angleTotal = angle;
|
||||
double angleOffset = 0.;
|
||||
|
||||
if (method == RevolMethod::TwoDimensions) {
|
||||
// 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 Groove 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);
|
||||
}
|
||||
else if (reversed) {
|
||||
angleTotal *= -1.0;
|
||||
}
|
||||
|
||||
// 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 {
|
||||
std::stringstream str;
|
||||
str << "ProfileBased: Internal error: Unknown method for generateGroove()";
|
||||
throw Base::RuntimeError(str.str());
|
||||
}
|
||||
}
|
||||
|
||||
void Groove::generateRevolution(TopoDS_Shape& revol,
|
||||
const TopoDS_Shape& baseshape,
|
||||
const TopoDS_Shape& profileshape,
|
||||
const TopoDS_Face& supportface,
|
||||
const TopoDS_Face& uptoface,
|
||||
const gp_Ax1& axis,
|
||||
RevolMethod method,
|
||||
RevolMode Mode,
|
||||
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;
|
||||
}
|
||||
else {
|
||||
std::stringstream str;
|
||||
str << "ProfileBased: Internal error: Unknown method for generateRevolution()";
|
||||
throw Base::RuntimeError(str.str());
|
||||
}
|
||||
}
|
||||
|
||||
void Groove::updateProperties(RevolMethod method)
|
||||
{
|
||||
// disable settings that are not valid on the current method
|
||||
// disable everything unless we are sure we need it
|
||||
bool isAngleEnabled = false;
|
||||
bool isAngle2Enabled = false;
|
||||
bool isMidplaneEnabled = false;
|
||||
bool isReversedEnabled = false;
|
||||
bool isUpToFaceEnabled = false;
|
||||
if (method == RevolMethod::Dimension) {
|
||||
isAngleEnabled = true;
|
||||
isMidplaneEnabled = true;
|
||||
isReversedEnabled = !Midplane.getValue();
|
||||
}
|
||||
else if (method == RevolMethod::ToLast) {
|
||||
isReversedEnabled = true;
|
||||
}
|
||||
else if (method == RevolMethod::ThroughAll) {
|
||||
isMidplaneEnabled = true;
|
||||
isReversedEnabled = !Midplane.getValue();
|
||||
}
|
||||
else if (method == RevolMethod::ToFirst) {
|
||||
isReversedEnabled = true;
|
||||
}
|
||||
else if (method == RevolMethod::ToFace) {
|
||||
isReversedEnabled = true;
|
||||
isUpToFaceEnabled = true;
|
||||
}
|
||||
else if (method == RevolMethod::TwoDimensions) {
|
||||
isAngleEnabled = true;
|
||||
isAngle2Enabled = true;
|
||||
isReversedEnabled = true;
|
||||
}
|
||||
|
||||
Angle.setReadOnly(!isAngleEnabled);
|
||||
Angle2.setReadOnly(!isAngle2Enabled);
|
||||
Midplane.setReadOnly(!isMidplaneEnabled);
|
||||
Reversed.setReadOnly(!isReversedEnabled);
|
||||
UpToFace.setReadOnly(!isUpToFaceEnabled);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -37,9 +37,11 @@ class PartDesignExport Groove : public ProfileBased
|
||||
public:
|
||||
Groove();
|
||||
|
||||
App::PropertyEnumeration Type;
|
||||
App::PropertyVector Base;
|
||||
App::PropertyVector Axis;
|
||||
App::PropertyAngle Angle;
|
||||
App::PropertyAngle Angle2;
|
||||
|
||||
/** if this property is set to a valid link, both Axis and Base properties
|
||||
* are calculated according to the linked line
|
||||
@@ -65,11 +67,64 @@ public:
|
||||
|
||||
/// suggests a value for Reversed flag so that material is always removed from the support
|
||||
bool suggestReversed();
|
||||
|
||||
enum class RevolMethod {
|
||||
Dimension,
|
||||
ThroughAll,
|
||||
ToLast = ThroughAll,
|
||||
ToFirst,
|
||||
ToFace,
|
||||
TwoDimensions
|
||||
};
|
||||
|
||||
protected:
|
||||
/// updates Axis from ReferenceAxis
|
||||
void updateAxis();
|
||||
|
||||
static const App::PropertyAngle::Constraints floatAngle;
|
||||
|
||||
// See BRepFeat_MakeRevol
|
||||
enum RevolMode {
|
||||
CutFromBase = 0,
|
||||
FuseWithBase = 1,
|
||||
None = 2
|
||||
};
|
||||
|
||||
RevolMethod methodFromString(const std::string& methodStr);
|
||||
|
||||
/**
|
||||
* Generates a [groove] of the input sketchshape and stores it in the given \a revol.
|
||||
*/
|
||||
void generateRevolution(TopoDS_Shape& revol,
|
||||
const TopoDS_Shape& sketchshape,
|
||||
const gp_Ax1& ax1,
|
||||
const double angle,
|
||||
const double angle2,
|
||||
const bool midplane,
|
||||
const bool reversed,
|
||||
RevolMethod method);
|
||||
|
||||
/**
|
||||
* 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,
|
||||
const TopoDS_Shape& profileshape,
|
||||
const TopoDS_Face& supportface,
|
||||
const TopoDS_Face& uptoface,
|
||||
const gp_Ax1& ax1,
|
||||
RevolMethod method,
|
||||
RevolMode Mode,
|
||||
Standard_Boolean Modify);
|
||||
|
||||
/**
|
||||
* Disables settings that are not valid for the current method
|
||||
*/
|
||||
void updateProperties(RevolMethod method);
|
||||
|
||||
private:
|
||||
static const char* TypeEnums[];
|
||||
};
|
||||
|
||||
} //namespace PartDesign
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
#ifndef _PreComp_
|
||||
# include <BRepAlgoAPI_Fuse.hxx>
|
||||
# include <BRepPrimAPI_MakeRevol.hxx>
|
||||
# include <BRepFeat_MakeRevol.hxx>
|
||||
# include <gp_Lin.hxx>
|
||||
# include <Precision.hxx>
|
||||
# include <TopExp_Explorer.hxx>
|
||||
@@ -42,6 +43,7 @@ using namespace PartDesign;
|
||||
|
||||
namespace PartDesign {
|
||||
|
||||
const char* Revolution::TypeEnums[]= {"Angle", "UpToLast", "UpToFirst", "UpToFace", "TwoAngles", nullptr};
|
||||
|
||||
PROPERTY_SOURCE(PartDesign::Revolution, PartDesign::ProfileBased)
|
||||
|
||||
@@ -51,9 +53,14 @@ Revolution::Revolution()
|
||||
{
|
||||
addSubType = FeatureAddSub::Additive;
|
||||
|
||||
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::Prop_ReadOnly, "Base");
|
||||
ADD_PROPERTY_TYPE(Axis,(Base::Vector3d(0.0,1.0,0.0)),"Revolution", App::Prop_ReadOnly, "Axis");
|
||||
ADD_PROPERTY_TYPE(Angle,(360.0),"Revolution", App::Prop_None, "Angle");
|
||||
ADD_PROPERTY_TYPE(UpToFace, (nullptr), "Revolution", App::Prop_None, "Face where revolution will end");
|
||||
ADD_PROPERTY_TYPE(Angle2, (60.0), "Revolution", App::Prop_None, "Revolution length in 2nd direction");
|
||||
|
||||
Angle.setConstraints(&floatAngle);
|
||||
ADD_PROPERTY_TYPE(ReferenceAxis,(nullptr),"Revolution",(App::Prop_None),"Reference axis of revolution");
|
||||
}
|
||||
@@ -64,7 +71,9 @@ short Revolution::mustExecute() const
|
||||
ReferenceAxis.isTouched() ||
|
||||
Axis.isTouched() ||
|
||||
Base.isTouched() ||
|
||||
Angle.isTouched())
|
||||
UpToFace.isTouched() ||
|
||||
Angle.isTouched() ||
|
||||
Angle2.isTouched())
|
||||
return 1;
|
||||
return ProfileBased::mustExecute();
|
||||
}
|
||||
@@ -72,17 +81,16 @@ short Revolution::mustExecute() const
|
||||
App::DocumentObjectExecReturn *Revolution::execute()
|
||||
{
|
||||
// Validate parameters
|
||||
double angle = Angle.getValue();
|
||||
if (angle > 360.0)
|
||||
// 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 revolution too large"));
|
||||
|
||||
angle = Base::toRadians<double>(angle);
|
||||
double angle = Base::toRadians<double>(angleDeg);
|
||||
if (angle < Precision::Angular())
|
||||
return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP("Exception", "Angle of revolution too small"));
|
||||
|
||||
// Reverse angle if selected
|
||||
if (Reversed.getValue() && !Midplane.getValue())
|
||||
angle *= (-1.0);
|
||||
double angle2 = Base::toRadians(Angle2.getValue());
|
||||
|
||||
TopoDS_Shape sketchshape;
|
||||
try {
|
||||
@@ -117,13 +125,7 @@ App::DocumentObjectExecReturn *Revolution::execute()
|
||||
if (sketchshape.IsNull())
|
||||
return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP("Exception", "Creating a face from sketch failed"));
|
||||
|
||||
// Rotate the face by half the angle to get Revolution 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);
|
||||
}
|
||||
RevolMethod method = methodFromString(Type.getValueAsString());
|
||||
|
||||
this->positionByPrevious();
|
||||
TopLoc_Location invObjLoc = this->getLocation().Inverted();
|
||||
@@ -140,11 +142,42 @@ App::DocumentObjectExecReturn *Revolution::execute()
|
||||
return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP("Exception", "Revolve axis intersects the sketch"));
|
||||
}
|
||||
|
||||
// revolve the face to a solid
|
||||
BRepPrimAPI_MakeRevol RevolMaker(sketchshape, gp_Ax1(pnt, dir), angle);
|
||||
// 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 (RevolMaker.IsDone()) {
|
||||
TopoDS_Shape result = RevolMaker.Shape();
|
||||
if (method == RevolMethod::ToFace || method == RevolMethod::ToFirst || method == RevolMethod::ToLast) {
|
||||
TopoDS_Face upToFace;
|
||||
if (method == RevolMethod::ToFace) {
|
||||
getFaceFromLinkSub(upToFace, UpToFace);
|
||||
upToFace.Move(invObjLoc);
|
||||
}
|
||||
else
|
||||
throw Base::RuntimeError("ProfileBased: Revolution up to first/last is not yet supported");
|
||||
|
||||
// TODO: This method is designed for extrusions. needs to be adapted for revolutions.
|
||||
// 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::None;
|
||||
generateRevolution(result, base, sketchshape, supportface, upToFace, gp_Ax1(pnt, dir), method, mode, Standard_True);
|
||||
}
|
||||
else {
|
||||
bool midplane = Midplane.getValue();
|
||||
bool reversed = Reversed.getValue();
|
||||
generateRevolution(result, sketchshape, gp_Ax1(pnt, dir), angle, angle2, midplane, reversed, method);
|
||||
}
|
||||
|
||||
if (!result.IsNull()) {
|
||||
result = refineShapeIfActive(result);
|
||||
// set the additive shape property for later usage in e.g. pattern
|
||||
this->AddSubShape.setValue(result);
|
||||
@@ -164,6 +197,9 @@ App::DocumentObjectExecReturn *Revolution::execute()
|
||||
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) {
|
||||
@@ -202,4 +238,153 @@ void Revolution::updateAxis()
|
||||
Axis.setValue(dir.x,dir.y,dir.z);
|
||||
}
|
||||
|
||||
Revolution::RevolMethod Revolution::methodFromString(const std::string& methodStr)
|
||||
{
|
||||
if (methodStr == "Angle")
|
||||
return RevolMethod::Dimension;
|
||||
if (methodStr == "UpToLast")
|
||||
return RevolMethod::ToLast;
|
||||
if (methodStr == "ThroughAll")
|
||||
return RevolMethod::ThroughAll;
|
||||
if (methodStr == "UpToFirst")
|
||||
return RevolMethod::ToFirst;
|
||||
if (methodStr == "UpToFace")
|
||||
return RevolMethod::ToFace;
|
||||
if (methodStr == "TwoAngles")
|
||||
return RevolMethod::TwoDimensions;
|
||||
|
||||
throw Base::ValueError("Revolution:: No such method");
|
||||
return RevolMethod::Dimension;
|
||||
}
|
||||
|
||||
void Revolution::generateRevolution(TopoDS_Shape& revol,
|
||||
const TopoDS_Shape& sketchshape,
|
||||
const gp_Ax1& axis,
|
||||
const double angle,
|
||||
const double angle2,
|
||||
const bool midplane,
|
||||
const bool reversed,
|
||||
RevolMethod method)
|
||||
{
|
||||
if (method == RevolMethod::Dimension || method == RevolMethod::TwoDimensions || method == RevolMethod::ThroughAll) {
|
||||
double angleTotal = angle;
|
||||
double angleOffset = 0.;
|
||||
|
||||
if (method == RevolMethod::TwoDimensions) {
|
||||
// Rotate the face by `angle2`/`angle` to get "second" angle
|
||||
angleTotal += angle2;
|
||||
angleOffset = angle2 * -1.0;
|
||||
}
|
||||
else if (midplane) {
|
||||
// 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.");
|
||||
|
||||
gp_Ax1 revolAx(axis);
|
||||
if (reversed) {
|
||||
revolAx.Reverse();
|
||||
}
|
||||
|
||||
TopoDS_Shape from = sketchshape;
|
||||
if (method == RevolMethod::TwoDimensions || 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, revolAx, angleTotal);
|
||||
|
||||
if (!RevolMaker.IsDone())
|
||||
throw Base::RuntimeError("ProfileBased: RevolMaker failed! Could not revolve the sketch!");
|
||||
else
|
||||
revol = RevolMaker.Shape();
|
||||
}
|
||||
else {
|
||||
std::stringstream str;
|
||||
str << "ProfileBased: Internal error: Unknown method for generateRevolution()";
|
||||
throw Base::RuntimeError(str.str());
|
||||
}
|
||||
}
|
||||
|
||||
void Revolution::generateRevolution(TopoDS_Shape& revol,
|
||||
const TopoDS_Shape& baseshape,
|
||||
const TopoDS_Shape& profileshape,
|
||||
const TopoDS_Face& supportface,
|
||||
const TopoDS_Face& uptoface,
|
||||
const gp_Ax1& axis,
|
||||
RevolMethod method,
|
||||
RevolMode Mode,
|
||||
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;
|
||||
}
|
||||
else {
|
||||
std::stringstream str;
|
||||
str << "ProfileBased: Internal error: Unknown method for generateRevolution()";
|
||||
throw Base::RuntimeError(str.str());
|
||||
}
|
||||
}
|
||||
|
||||
void Revolution::updateProperties(RevolMethod method)
|
||||
{
|
||||
// disable settings that are not valid on the current method
|
||||
// disable everything unless we are sure we need it
|
||||
bool isAngleEnabled = false;
|
||||
bool isAngle2Enabled = false;
|
||||
bool isMidplaneEnabled = false;
|
||||
bool isReversedEnabled = false;
|
||||
bool isUpToFaceEnabled = false;
|
||||
if (method == RevolMethod::Dimension) {
|
||||
isAngleEnabled = true;
|
||||
isMidplaneEnabled = true;
|
||||
isReversedEnabled = !Midplane.getValue();
|
||||
}
|
||||
else if (method == RevolMethod::ToLast) {
|
||||
isReversedEnabled = true;
|
||||
}
|
||||
else if (method == RevolMethod::ThroughAll) {
|
||||
isMidplaneEnabled = true;
|
||||
isReversedEnabled = !Midplane.getValue();
|
||||
}
|
||||
else if (method == RevolMethod::ToFirst) {
|
||||
isReversedEnabled = true;
|
||||
}
|
||||
else if (method == RevolMethod::ToFace) {
|
||||
isReversedEnabled = true;
|
||||
isUpToFaceEnabled = true;
|
||||
}
|
||||
else if (method == RevolMethod::TwoDimensions) {
|
||||
isAngleEnabled = true;
|
||||
isAngle2Enabled = true;
|
||||
isReversedEnabled = true;
|
||||
}
|
||||
|
||||
Angle.setReadOnly(!isAngleEnabled);
|
||||
Angle2.setReadOnly(!isAngle2Enabled);
|
||||
Midplane.setReadOnly(!isMidplaneEnabled);
|
||||
Reversed.setReadOnly(!isReversedEnabled);
|
||||
UpToFace.setReadOnly(!isUpToFaceEnabled);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -37,9 +37,11 @@ class PartDesignExport Revolution : public ProfileBased
|
||||
public:
|
||||
Revolution();
|
||||
|
||||
App::PropertyEnumeration Type;
|
||||
App::PropertyVector Base;
|
||||
App::PropertyVector Axis;
|
||||
App::PropertyAngle Angle;
|
||||
App::PropertyAngle Angle2;
|
||||
|
||||
/** if this property is set to a valid link, both Axis and Base properties
|
||||
* are calculated according to the linked line
|
||||
@@ -65,11 +67,64 @@ public:
|
||||
|
||||
/// suggests a value for Reversed flag so that material is always added to the support
|
||||
bool suggestReversed();
|
||||
|
||||
enum class RevolMethod {
|
||||
Dimension,
|
||||
ThroughAll,
|
||||
ToLast = ThroughAll,
|
||||
ToFirst,
|
||||
ToFace,
|
||||
TwoDimensions
|
||||
};
|
||||
|
||||
protected:
|
||||
/// updates Axis from ReferenceAxis
|
||||
void updateAxis();
|
||||
|
||||
static const App::PropertyAngle::Constraints floatAngle;
|
||||
|
||||
// See BRepFeat_MakeRevol
|
||||
enum RevolMode {
|
||||
CutFromBase = 0,
|
||||
FuseWithBase = 1,
|
||||
None = 2
|
||||
};
|
||||
|
||||
RevolMethod methodFromString(const std::string& methodStr);
|
||||
|
||||
/**
|
||||
* Generates a revolution of the input sketchshape and stores it in the given \a revol.
|
||||
*/
|
||||
void generateRevolution(TopoDS_Shape& revol,
|
||||
const TopoDS_Shape& sketchshape,
|
||||
const gp_Ax1& ax1,
|
||||
const double angle,
|
||||
const double angle2,
|
||||
const bool midplane,
|
||||
const bool reversed,
|
||||
RevolMethod method);
|
||||
|
||||
/**
|
||||
* Generates a revolution 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,
|
||||
const TopoDS_Shape& profileshape,
|
||||
const TopoDS_Face& supportface,
|
||||
const TopoDS_Face& uptoface,
|
||||
const gp_Ax1& ax1,
|
||||
RevolMethod method,
|
||||
RevolMode Mode,
|
||||
Standard_Boolean Modify);
|
||||
|
||||
/**
|
||||
* Disables settings that are not valid for the current method
|
||||
*/
|
||||
void updateProperties(RevolMethod method);
|
||||
|
||||
private:
|
||||
static const char* TypeEnums[];
|
||||
};
|
||||
|
||||
} //namespace PartDesign
|
||||
|
||||
@@ -49,7 +49,8 @@ using namespace Gui;
|
||||
TaskRevolutionParameters::TaskRevolutionParameters(PartDesignGui::ViewProvider* RevolutionView, QWidget *parent)
|
||||
: TaskSketchBasedParameters(RevolutionView, parent, "PartDesign_Revolution", tr("Revolution parameters")),
|
||||
ui(new Ui_TaskRevolutionParameters),
|
||||
proxy(new QWidget(this))
|
||||
proxy(new QWidget(this)),
|
||||
isGroove(false)
|
||||
{
|
||||
// we need a separate container widget to add all controls to
|
||||
ui->setupUi(proxy);
|
||||
@@ -59,34 +60,36 @@ TaskRevolutionParameters::TaskRevolutionParameters(PartDesignGui::ViewProvider*
|
||||
// bind property mirrors
|
||||
if (auto *rev = dynamic_cast<PartDesign::Revolution *>(vp->getObject())) {
|
||||
this->propAngle = &(rev->Angle);
|
||||
this->propAngle2 = &(rev->Angle2);
|
||||
this->propMidPlane = &(rev->Midplane);
|
||||
this->propReferenceAxis = &(rev->ReferenceAxis);
|
||||
this->propReversed = &(rev->Reversed);
|
||||
this->propUpToFace = &(rev->UpToFace);
|
||||
ui->revolveAngle->bind(rev->Angle);
|
||||
ui->revolveAngle2->bind(rev->Angle2);
|
||||
}
|
||||
else if (auto *rev = dynamic_cast<PartDesign::Groove *>(vp->getObject())) {
|
||||
isGroove = true;
|
||||
this->propAngle = &(rev->Angle);
|
||||
this->propAngle2 = &(rev->Angle2);
|
||||
this->propMidPlane = &(rev->Midplane);
|
||||
this->propReferenceAxis = &(rev->ReferenceAxis);
|
||||
this->propReversed = &(rev->Reversed);
|
||||
this->propUpToFace = &(rev->UpToFace);
|
||||
ui->revolveAngle->bind(rev->Angle);
|
||||
ui->revolveAngle2->bind(rev->Angle2);
|
||||
}
|
||||
else {
|
||||
throw Base::TypeError("The object is neither a Groove nor a Revolution.");
|
||||
}
|
||||
|
||||
ui->checkBoxMidplane->setChecked(propMidPlane->getValue());
|
||||
ui->checkBoxReversed->setChecked(propReversed->getValue());
|
||||
|
||||
ui->revolveAngle->setValue(propAngle->getValue());
|
||||
ui->revolveAngle->setMaximum(propAngle->getMaximum());
|
||||
ui->revolveAngle->setMinimum(propAngle->getMinimum());
|
||||
setupDialog();
|
||||
|
||||
blockUpdate = false;
|
||||
updateUI();
|
||||
connectSignals();
|
||||
|
||||
setFocus ();
|
||||
setFocus();
|
||||
|
||||
// show the parts coordinate system axis for selection
|
||||
PartDesign::Body * body = PartDesign::Body::findBodyOf ( vp->getObject () );
|
||||
@@ -103,6 +106,54 @@ TaskRevolutionParameters::TaskRevolutionParameters(PartDesignGui::ViewProvider*
|
||||
}
|
||||
}
|
||||
|
||||
void TaskRevolutionParameters::setupDialog()
|
||||
{
|
||||
ui->checkBoxMidplane->setChecked(propMidPlane->getValue());
|
||||
ui->checkBoxReversed->setChecked(propReversed->getValue());
|
||||
|
||||
ui->revolveAngle->setValue(propAngle->getValue());
|
||||
ui->revolveAngle->setMaximum(propAngle->getMaximum());
|
||||
ui->revolveAngle->setMinimum(propAngle->getMinimum());
|
||||
|
||||
int index = 0;
|
||||
|
||||
// TODO: This should also be implemented for groove
|
||||
if (!isGroove) {
|
||||
PartDesign::Revolution* rev = static_cast<PartDesign::Revolution*>(vp->getObject());
|
||||
ui->revolveAngle2->setValue(propAngle2->getValue());
|
||||
ui->revolveAngle2->setMaximum(propAngle2->getMaximum());
|
||||
ui->revolveAngle2->setMinimum(propAngle2->getMinimum());
|
||||
|
||||
index = rev->Type.getValue();
|
||||
}
|
||||
else {
|
||||
PartDesign::Groove* rev = static_cast<PartDesign::Groove*>(vp->getObject());
|
||||
ui->revolveAngle2->setValue(propAngle2->getValue());
|
||||
ui->revolveAngle2->setMaximum(propAngle2->getMaximum());
|
||||
ui->revolveAngle2->setMinimum(propAngle2->getMinimum());
|
||||
|
||||
index = rev->Type.getValue();
|
||||
}
|
||||
|
||||
translateModeList(index);
|
||||
}
|
||||
|
||||
void TaskRevolutionParameters::translateModeList(int index)
|
||||
{
|
||||
ui->changeMode->clear();
|
||||
ui->changeMode->addItem(tr("Dimension"));
|
||||
if (!isGroove) {
|
||||
ui->changeMode->addItem(tr("To last"));
|
||||
}
|
||||
else {
|
||||
ui->changeMode->addItem(tr("Through all"));
|
||||
}
|
||||
ui->changeMode->addItem(tr("To first"));
|
||||
ui->changeMode->addItem(tr("Up to face"));
|
||||
ui->changeMode->addItem(tr("Two dimensions"));
|
||||
ui->changeMode->setCurrentIndex(index);
|
||||
}
|
||||
|
||||
void TaskRevolutionParameters::fillAxisCombo(bool forceRefill)
|
||||
{
|
||||
Base::StateLocker lock(blockUpdate, true);
|
||||
@@ -179,44 +230,213 @@ void TaskRevolutionParameters::addAxisToCombo(App::DocumentObject* linkObj,
|
||||
lnk.setValue(linkObj,std::vector<std::string>(1,linkSubname));
|
||||
}
|
||||
|
||||
void TaskRevolutionParameters::connectSignals()
|
||||
void TaskRevolutionParameters::setCheckboxes(PartDesign::Revolution::RevolMethod mode)
|
||||
{
|
||||
connect(ui->revolveAngle, qOverload<double>(&QuantitySpinBox::valueChanged), this,
|
||||
&TaskRevolutionParameters::onAngleChanged);
|
||||
connect(ui->axis, qOverload<int>(&QComboBox::activated), this,
|
||||
&TaskRevolutionParameters::onAxisChanged);
|
||||
connect(ui->checkBoxMidplane, &QCheckBox::toggled, this,
|
||||
&TaskRevolutionParameters::onMidplane);
|
||||
connect(ui->checkBoxReversed, &QCheckBox::toggled, this,
|
||||
&TaskRevolutionParameters::onReversed);
|
||||
connect(ui->checkBoxUpdateView, &QCheckBox::toggled, this,
|
||||
&TaskRevolutionParameters::onUpdateView);
|
||||
// disable/hide everything unless we are sure we don't need it
|
||||
// exception: the direction parameters are in any case visible
|
||||
bool isRevolveAngleVisible = false;
|
||||
bool isRevolveAngle2Visible = false;
|
||||
bool isMidplaneEnabled = false;
|
||||
bool isMidplaneVisible = false;
|
||||
bool isReversedEnabled = false;
|
||||
bool isFaceEditEnabled = false;
|
||||
|
||||
if (mode == PartDesign::Revolution::RevolMethod::Dimension) {
|
||||
isRevolveAngleVisible = true;
|
||||
ui->revolveAngle->selectNumber();
|
||||
QMetaObject::invokeMethod(ui->revolveAngle, "setFocus", Qt::QueuedConnection);
|
||||
isMidplaneVisible = true;
|
||||
isMidplaneEnabled = true;
|
||||
// Reverse only makes sense if Midplane is not true
|
||||
isReversedEnabled = !ui->checkBoxMidplane->isChecked();
|
||||
}
|
||||
else if (mode == PartDesign::Revolution::RevolMethod::ThroughAll && isGroove) {
|
||||
isMidplaneEnabled = true;
|
||||
isMidplaneVisible = true;
|
||||
isReversedEnabled = !ui->checkBoxMidplane->isChecked();
|
||||
}
|
||||
else if (mode == PartDesign::Revolution::RevolMethod::ToLast && !isGroove) {
|
||||
isReversedEnabled = true;
|
||||
}
|
||||
else if (mode == PartDesign::Revolution::RevolMethod::ToFirst) {
|
||||
isReversedEnabled = true;
|
||||
}
|
||||
else if (mode == PartDesign::Revolution::RevolMethod::ToFace) {
|
||||
isReversedEnabled = true;
|
||||
isFaceEditEnabled = true;
|
||||
QMetaObject::invokeMethod(ui->lineFaceName, "setFocus", Qt::QueuedConnection);
|
||||
// Go into reference selection mode if no face has been selected yet
|
||||
if (ui->lineFaceName->property("FeatureName").isNull())
|
||||
ui->buttonFace->setChecked(true);
|
||||
}
|
||||
else if (mode == PartDesign::Revolution::RevolMethod::TwoDimensions) {
|
||||
isRevolveAngleVisible = true;
|
||||
isRevolveAngle2Visible = true;
|
||||
isReversedEnabled = true;
|
||||
}
|
||||
|
||||
ui->revolveAngle->setVisible(isRevolveAngleVisible);
|
||||
ui->revolveAngle->setEnabled(isRevolveAngleVisible);
|
||||
ui->labelAngle->setVisible(isRevolveAngleVisible);
|
||||
|
||||
ui->revolveAngle2->setVisible(isRevolveAngle2Visible);
|
||||
ui->revolveAngle2->setEnabled(isRevolveAngle2Visible);
|
||||
ui->labelAngle2->setVisible(isRevolveAngle2Visible);
|
||||
|
||||
ui->checkBoxMidplane->setEnabled(isMidplaneEnabled);
|
||||
ui->checkBoxMidplane->setVisible(isMidplaneVisible);
|
||||
|
||||
ui->checkBoxReversed->setEnabled(isReversedEnabled);
|
||||
|
||||
ui->buttonFace->setEnabled(isFaceEditEnabled);
|
||||
ui->lineFaceName->setEnabled(isFaceEditEnabled);
|
||||
if (!isFaceEditEnabled) {
|
||||
ui->buttonFace->setChecked(false);
|
||||
}
|
||||
}
|
||||
|
||||
void TaskRevolutionParameters::updateUI()
|
||||
void TaskRevolutionParameters::connectSignals()
|
||||
{
|
||||
connect(ui->revolveAngle, qOverload<double>(&Gui::QuantitySpinBox::valueChanged),
|
||||
this, &TaskRevolutionParameters::onAngleChanged);
|
||||
connect(ui->revolveAngle2, qOverload<double>(&Gui::QuantitySpinBox::valueChanged),
|
||||
this, &TaskRevolutionParameters::onAngle2Changed);
|
||||
connect(ui->axis, qOverload<int>(&QComboBox::activated),
|
||||
this, &TaskRevolutionParameters::onAxisChanged);
|
||||
connect(ui->checkBoxMidplane, &QCheckBox::toggled,
|
||||
this, &TaskRevolutionParameters::onMidplane);
|
||||
connect(ui->checkBoxReversed, &QCheckBox::toggled,
|
||||
this, &TaskRevolutionParameters::onReversed);
|
||||
connect(ui->checkBoxUpdateView, &QCheckBox::toggled,
|
||||
this, &TaskRevolutionParameters::onUpdateView);
|
||||
connect(ui->changeMode, qOverload<int>(&QComboBox::currentIndexChanged),
|
||||
this, &TaskRevolutionParameters::onModeChanged);
|
||||
connect(ui->buttonFace, &QPushButton::toggled,
|
||||
this, &TaskRevolutionParameters::onButtonFace);
|
||||
connect(ui->lineFaceName, &QLineEdit::textEdited,
|
||||
this, &TaskRevolutionParameters::onFaceName);
|
||||
}
|
||||
|
||||
void TaskRevolutionParameters::updateUI(int index)
|
||||
{
|
||||
if (blockUpdate)
|
||||
return;
|
||||
Base::StateLocker lock(blockUpdate, true);
|
||||
fillAxisCombo();
|
||||
setCheckboxes(static_cast<PartDesign::Revolution::RevolMethod>(index));
|
||||
}
|
||||
|
||||
void TaskRevolutionParameters::onSelectionChanged(const Gui::SelectionChanges& msg)
|
||||
{
|
||||
if (msg.Type == Gui::SelectionChanges::AddSelection) {
|
||||
if (selectionFace) {
|
||||
QString refText = onAddSelection(msg);
|
||||
if (refText.length() > 0) {
|
||||
QSignalBlocker block(ui->lineFaceName);
|
||||
ui->lineFaceName->setText(refText);
|
||||
ui->lineFaceName->setProperty("FeatureName", QByteArray(msg.pObjectName));
|
||||
ui->lineFaceName->setProperty("FaceName", QByteArray(msg.pSubName));
|
||||
// Turn off reference selection mode
|
||||
ui->buttonFace->setChecked(false);
|
||||
}
|
||||
else {
|
||||
clearFaceName();
|
||||
}
|
||||
}
|
||||
else {
|
||||
exitSelectionMode();
|
||||
std::vector<std::string> axis;
|
||||
App::DocumentObject* selObj;
|
||||
if (getReferencedSelection(vp->getObject(), msg, selObj, axis) && selObj) {
|
||||
propReferenceAxis->setValue(selObj, axis);
|
||||
|
||||
exitSelectionMode();
|
||||
std::vector<std::string> axis;
|
||||
App::DocumentObject* selObj;
|
||||
if (getReferencedSelection(vp->getObject(), msg, selObj, axis) && selObj) {
|
||||
propReferenceAxis->setValue(selObj, axis);
|
||||
recomputeFeature();
|
||||
updateUI();
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (msg.Type == Gui::SelectionChanges::ClrSelection && selectionFace) {
|
||||
clearFaceName();
|
||||
}
|
||||
}
|
||||
|
||||
recomputeFeature();
|
||||
updateUI();
|
||||
void TaskRevolutionParameters::onButtonFace(const bool pressed)
|
||||
{
|
||||
// to distinguish that this is the direction selection
|
||||
selectionFace = true;
|
||||
|
||||
// only faces are allowed
|
||||
TaskSketchBasedParameters::onSelectReference(pressed ? AllowSelection::FACE : AllowSelection::NONE);
|
||||
}
|
||||
|
||||
void TaskRevolutionParameters::onFaceName(const QString& text)
|
||||
{
|
||||
if (text.isEmpty()) {
|
||||
// if user cleared the text field then also clear the properties
|
||||
ui->lineFaceName->setProperty("FeatureName", QVariant());
|
||||
ui->lineFaceName->setProperty("FaceName", QVariant());
|
||||
}
|
||||
else {
|
||||
// expect that the label of an object is used
|
||||
QStringList parts = text.split(QChar::fromLatin1(':'));
|
||||
QString label = parts[0];
|
||||
QVariant name = objectNameByLabel(label, ui->lineFaceName->property("FeatureName"));
|
||||
if (name.isValid()) {
|
||||
parts[0] = name.toString();
|
||||
QString uptoface = parts.join(QString::fromLatin1(":"));
|
||||
ui->lineFaceName->setProperty("FeatureName", name);
|
||||
ui->lineFaceName->setProperty("FaceName", setUpToFace(uptoface));
|
||||
}
|
||||
else {
|
||||
ui->lineFaceName->setProperty("FeatureName", QVariant());
|
||||
ui->lineFaceName->setProperty("FaceName", QVariant());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TaskRevolutionParameters::translateFaceName()
|
||||
{
|
||||
ui->lineFaceName->setPlaceholderText(tr("No face selected"));
|
||||
QVariant featureName = ui->lineFaceName->property("FeatureName");
|
||||
if (featureName.isValid()) {
|
||||
QStringList parts = ui->lineFaceName->text().split(QChar::fromLatin1(':'));
|
||||
QByteArray upToFace = ui->lineFaceName->property("FaceName").toByteArray();
|
||||
int faceId = -1;
|
||||
bool ok = false;
|
||||
if (upToFace.indexOf("Face") == 0) {
|
||||
faceId = upToFace.remove(0,4).toInt(&ok);
|
||||
}
|
||||
|
||||
if (ok) {
|
||||
ui->lineFaceName->setText(QString::fromLatin1("%1:%2%3")
|
||||
.arg(parts[0])
|
||||
.arg(tr("Face"))
|
||||
.arg(faceId));
|
||||
}
|
||||
else {
|
||||
ui->lineFaceName->setText(parts[0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QString TaskRevolutionParameters::getFaceName(void) const
|
||||
{
|
||||
QVariant featureName = ui->lineFaceName->property("FeatureName");
|
||||
if (featureName.isValid()) {
|
||||
QString faceName = ui->lineFaceName->property("FaceName").toString();
|
||||
return getFaceReference(featureName.toString(), faceName);
|
||||
}
|
||||
|
||||
return QString::fromLatin1("None");
|
||||
}
|
||||
|
||||
void TaskRevolutionParameters::clearFaceName()
|
||||
{
|
||||
QSignalBlocker block(ui->lineFaceName);
|
||||
ui->lineFaceName->clear();
|
||||
ui->lineFaceName->setProperty("FeatureName", QVariant());
|
||||
ui->lineFaceName->setProperty("FaceName", QVariant());
|
||||
}
|
||||
|
||||
void TaskRevolutionParameters::onAngleChanged(double len)
|
||||
{
|
||||
@@ -225,6 +445,14 @@ void TaskRevolutionParameters::onAngleChanged(double len)
|
||||
recomputeFeature();
|
||||
}
|
||||
|
||||
void TaskRevolutionParameters::onAngle2Changed(double len)
|
||||
{
|
||||
if (propAngle2)
|
||||
propAngle2->setValue(len);
|
||||
exitSelectionMode();
|
||||
recomputeFeature();
|
||||
}
|
||||
|
||||
void TaskRevolutionParameters::onAxisChanged(int num)
|
||||
{
|
||||
if (blockUpdate)
|
||||
@@ -301,6 +529,42 @@ void TaskRevolutionParameters::onReversed(bool on)
|
||||
recomputeFeature();
|
||||
}
|
||||
|
||||
void TaskRevolutionParameters::onModeChanged(int index)
|
||||
{
|
||||
App::PropertyEnumeration* pcType;
|
||||
if (!isGroove)
|
||||
pcType = &(static_cast<PartDesign::Revolution*>(vp->getObject())->Type);
|
||||
else
|
||||
pcType = &(static_cast<PartDesign::Groove*>(vp->getObject())->Type);
|
||||
|
||||
switch (static_cast<PartDesign::Revolution::RevolMethod>(index)) {
|
||||
case PartDesign::Revolution::RevolMethod::Dimension:
|
||||
pcType->setValue("Angle");
|
||||
// Avoid error message
|
||||
// if (ui->revolveAngle->value() < Base::Quantity(Precision::Angular(), Base::Unit::Angle)) // TODO: Ensure radians/degree consistency
|
||||
// ui->revolveAngle->setValue(5.0);
|
||||
break;
|
||||
case PartDesign::Revolution::RevolMethod::ToLast:
|
||||
if (!isGroove)
|
||||
pcType->setValue("UpToLast");
|
||||
else
|
||||
pcType->setValue("ThroughAll");
|
||||
break;
|
||||
case PartDesign::Revolution::RevolMethod::ToFirst:
|
||||
pcType->setValue("UpToFirst");
|
||||
break;
|
||||
case PartDesign::Revolution::RevolMethod::ToFace:
|
||||
pcType->setValue("UpToFace");
|
||||
break;
|
||||
case PartDesign::Revolution::RevolMethod::TwoDimensions:
|
||||
pcType->setValue("TwoAngles");
|
||||
break;
|
||||
}
|
||||
|
||||
updateUI(index);
|
||||
recomputeFeature();
|
||||
}
|
||||
|
||||
void TaskRevolutionParameters::getReferenceAxis(App::DocumentObject*& obj, std::vector<std::string>& sub) const
|
||||
{
|
||||
if (axesInList.empty())
|
||||
@@ -354,6 +618,9 @@ void TaskRevolutionParameters::changeEvent(QEvent *event)
|
||||
TaskBox::changeEvent(event);
|
||||
if (event->type() == QEvent::LanguageChange) {
|
||||
ui->retranslateUi(proxy);
|
||||
|
||||
// Translate mode items
|
||||
translateModeList(ui->changeMode->currentIndex());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -361,14 +628,22 @@ void TaskRevolutionParameters::apply()
|
||||
{
|
||||
//Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Revolution changed"));
|
||||
ui->revolveAngle->apply();
|
||||
ui->revolveAngle2->apply();
|
||||
std::vector<std::string> sub;
|
||||
App::DocumentObject* obj;
|
||||
getReferenceAxis(obj, sub);
|
||||
std::string axis = buildLinkSingleSubPythonStr(obj, sub);
|
||||
auto tobj = vp->getObject();
|
||||
FCMD_OBJ_CMD(tobj,"ReferenceAxis = " << axis);
|
||||
FCMD_OBJ_CMD(tobj,"Midplane = " << (getMidplane() ? 1 : 0));
|
||||
FCMD_OBJ_CMD(tobj,"Reversed = " << (getReversed() ? 1 : 0));
|
||||
FCMD_OBJ_CMD(tobj, "ReferenceAxis = " << axis);
|
||||
FCMD_OBJ_CMD(tobj, "Midplane = " << (getMidplane() ? 1 : 0));
|
||||
FCMD_OBJ_CMD(tobj, "Reversed = " << (getReversed() ? 1 : 0));
|
||||
int mode = ui->changeMode->currentIndex();
|
||||
FCMD_OBJ_CMD(tobj, "Type = " << mode);
|
||||
QString facename = QString::fromLatin1("None");
|
||||
if (static_cast<PartDesign::Revolution::RevolMethod>(mode) == PartDesign::Revolution::RevolMethod::ToFace) {
|
||||
facename = getFaceName();
|
||||
}
|
||||
FCMD_OBJ_CMD(tobj, "UpToFace = " << facename.toLatin1().data());
|
||||
}
|
||||
|
||||
//**************************************************************************
|
||||
|
||||
@@ -23,6 +23,8 @@
|
||||
#ifndef GUI_TASKVIEW_TaskRevolutionParameters_H
|
||||
#define GUI_TASKVIEW_TaskRevolutionParameters_H
|
||||
|
||||
#include <Mod/PartDesign/App/FeatureRevolution.h>
|
||||
#include <Mod/PartDesign/App/FeatureGroove.h>
|
||||
#include "TaskSketchBasedParameters.h"
|
||||
#include "ViewProviderRevolution.h"
|
||||
|
||||
@@ -61,9 +63,13 @@ public:
|
||||
|
||||
private Q_SLOTS:
|
||||
void onAngleChanged(double);
|
||||
void onAngle2Changed(double);
|
||||
void onAxisChanged(int);
|
||||
void onMidplane(bool);
|
||||
void onReversed(bool);
|
||||
void onModeChanged(int);
|
||||
void onButtonFace(const bool pressed = true);
|
||||
void onFaceName(const QString& text);
|
||||
|
||||
protected:
|
||||
void onSelectionChanged(const Gui::SelectionChanges& msg) override;
|
||||
@@ -71,21 +77,32 @@ protected:
|
||||
void getReferenceAxis(App::DocumentObject *&obj, std::vector<std::string> &sub) const;
|
||||
bool getMidplane() const;
|
||||
bool getReversed() const;
|
||||
QString getFaceName() const;
|
||||
void setupDialog(void);
|
||||
void setCheckboxes(PartDesign::Revolution::RevolMethod mode);
|
||||
|
||||
//mirrors of revolution's or groove's properties
|
||||
//should have been done by inheriting revolution and groove from common class...
|
||||
App::PropertyAngle* propAngle;
|
||||
App::PropertyAngle* propAngle2;
|
||||
App::PropertyBool* propReversed;
|
||||
App::PropertyBool* propMidPlane;
|
||||
App::PropertyLinkSub* propReferenceAxis;
|
||||
App::PropertyLinkSub* propUpToFace;
|
||||
|
||||
private:
|
||||
void connectSignals();
|
||||
void updateUI();
|
||||
void updateUI(int index=0); // TODO: Implement for index and remove default
|
||||
void translateModeList(int index);
|
||||
// TODO: This is common with extrude. Maybe send to superclass.
|
||||
void translateFaceName();
|
||||
void clearFaceName();
|
||||
|
||||
private:
|
||||
std::unique_ptr<Ui_TaskRevolutionParameters> ui;
|
||||
QWidget *proxy;
|
||||
bool selectionFace;
|
||||
bool isGroove;
|
||||
|
||||
/**
|
||||
* @brief axesInList is the list of links corresponding to axis combo; must
|
||||
|
||||
@@ -14,6 +14,26 @@
|
||||
<string notr="true">Form</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayoutMode">
|
||||
<item>
|
||||
<widget class="QLabel" name="textLabelMode">
|
||||
<property name="text">
|
||||
<string>Type</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="changeMode">
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Dimension</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
@@ -62,7 +82,7 @@
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_3">
|
||||
<item>
|
||||
<widget class="QLabel" name="label">
|
||||
<widget class="QLabel" name="labelAngle">
|
||||
<property name="text">
|
||||
<string>Angle:</string>
|
||||
</property>
|
||||
@@ -109,6 +129,56 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_4">
|
||||
<item>
|
||||
<widget class="QLabel" name="labelAngle2">
|
||||
<property name="text">
|
||||
<string>2nd angle</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="Gui::QuantitySpinBox" name="revolveAngle2">
|
||||
<property name="keyboardTracking">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="unit" stdset="0">
|
||||
<string notr="true">deg</string>
|
||||
</property>
|
||||
<property name="minimum">
|
||||
<double>0.000000000000000</double>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<double>360.000000000000000</double>
|
||||
</property>
|
||||
<property name="singleStep">
|
||||
<double>10.000000000000000</double>
|
||||
</property>
|
||||
<property name="value">
|
||||
<double>60.000000000000000</double>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QGridLayout" name="gridLayout_5">
|
||||
<item row="0" column="0">
|
||||
<widget class="QPushButton" name="buttonFace">
|
||||
<property name="text">
|
||||
<string>Face</string>
|
||||
</property>
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QLineEdit" name="lineFaceName"/>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="Line" name="line">
|
||||
<property name="orientation">
|
||||
|
||||
Reference in New Issue
Block a user