PartDesign: Added interactive gizmo for draft operation (#27111)
* PartDesign: Add interactive gizmo for draft operation * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
This commit is contained in:
@@ -461,13 +461,13 @@ void RotationGizmo::draggingContinued()
|
||||
|
||||
void RotationGizmo::orientAlongCamera(SoCamera* camera)
|
||||
{
|
||||
if (linearGizmo == nullptr || automaticOrientation == false) {
|
||||
if (!automaticOrientation) {
|
||||
return;
|
||||
}
|
||||
|
||||
SbVec3f cameraDir {0, 0, 1};
|
||||
camera->orientation.getValue().multVec(cameraDir, cameraDir);
|
||||
SbVec3f pointerDir = linearGizmo->getDraggerContainer()->getPointerDirection();
|
||||
SbVec3f pointerDir = getDraggerContainer()->getPointerDirection();
|
||||
|
||||
pointerDir.normalize();
|
||||
auto proj = cameraDir - cameraDir.dot(pointerDir) * pointerDir;
|
||||
|
||||
@@ -140,6 +140,9 @@ public:
|
||||
|
||||
// Distance between the linear gizmo base and rotation gizmo
|
||||
double sepDistance = 0;
|
||||
// Controls if the gizmo is automatically rotated around the pointer in the
|
||||
// to always face the camera
|
||||
bool automaticOrientation = false;
|
||||
|
||||
// Returns the position and rotation of the base of the dragger
|
||||
GizmoPlacement getDraggerPlacement() override;
|
||||
@@ -164,7 +167,6 @@ private:
|
||||
SoRotationDraggerContainer* draggerContainer = nullptr;
|
||||
SoFieldSensor translationSensor;
|
||||
LinearGizmo* linearGizmo = nullptr;
|
||||
bool automaticOrientation = false;
|
||||
QMetaObject::Connection quantityChangedConnection;
|
||||
QMetaObject::Connection formulaDialogConnection;
|
||||
|
||||
|
||||
@@ -399,6 +399,14 @@ void SoRotationDraggerContainer::setArcNormalDirection(const SbVec3f& dir)
|
||||
auto currentRot = rotation.getValue();
|
||||
currentRot.multVec(currentNormal, currentNormal);
|
||||
|
||||
// If the two directions are collinear and opposite then ensure that the
|
||||
// dragger is rotated along the pointer axis
|
||||
if (dir.equals(-currentNormal, 1e-5)) {
|
||||
SbRotation rot {getPointerDirection(), std::numbers::pi_v<float>};
|
||||
rotation = currentRot * rot;
|
||||
return;
|
||||
}
|
||||
|
||||
SbRotation rot {currentNormal, dir};
|
||||
rotation = currentRot * rot;
|
||||
}
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
#include <BOPTools_AlgoTools3D.hxx>
|
||||
#include <BRep_Tool.hxx>
|
||||
#include <BRepGProp.hxx>
|
||||
#include <ElCLib.hxx>
|
||||
#include <GProp_GProps.hxx>
|
||||
#include <GeomAPI_ProjectPointOnSurf.hxx>
|
||||
#include <TopExp_Explorer.hxx>
|
||||
@@ -35,6 +36,8 @@
|
||||
#include <TopoDS.hxx>
|
||||
#include <Precision.hxx>
|
||||
#include <IntTools_Context.hxx>
|
||||
#include <IntAna_IntConicQuad.hxx>
|
||||
#include <IntAna_QuadQuadGeo.hxx>
|
||||
|
||||
#include <Base/BoundBox.h>
|
||||
#include <Base/Converter.h>
|
||||
@@ -170,7 +173,7 @@ DraggerPlacementProps getDraggerPlacementFromEdgeAndFace(Part::TopoShape& edge,
|
||||
dir = -dir;
|
||||
}
|
||||
|
||||
return {position, dir, tangent};
|
||||
return {position, dir};
|
||||
}
|
||||
|
||||
DraggerPlacementProps getDraggerPlacementFromEdgeAndFace(Part::TopoShape& edge, Part::TopoShape& face)
|
||||
@@ -245,3 +248,74 @@ Base::Vector3d getMidPointFromProfile(Part::TopoShape& profile)
|
||||
|
||||
return midPoint;
|
||||
}
|
||||
|
||||
std::optional<DraggerPlacementPropsWithNormals> getDraggerPlacementFromPlaneAndFace(
|
||||
Part::TopoShape& face,
|
||||
gp_Pln& plane
|
||||
)
|
||||
{
|
||||
TopoDS_Face TDSFace = TopoDS::Face(face.getShape());
|
||||
if (TDSFace.IsNull()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
auto cog = getCentreOfMassFromFace(TDSFace);
|
||||
auto orientation = TDSFace.Orientation();
|
||||
|
||||
auto getPropsFromShapePlaneIntersection =
|
||||
[&cog](auto&& shape, const gp_Pln& plane) -> std::optional<DraggerPlacementPropsWithNormals> {
|
||||
if (plane.Axis().IsNormal(shape.Axis(), Precision::Angular())) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
gp_Lin line(shape.Axis());
|
||||
IntAna_IntConicQuad intersector(line, plane, Precision::Confusion());
|
||||
if (intersector.IsDone() && intersector.NbPoints() > 0) {
|
||||
auto pos = Base::convertTo<Base::Vector3d>(intersector.Point(1));
|
||||
return DraggerPlacementPropsWithNormals {
|
||||
.placementProps = {.position = pos, .dir = cog - pos},
|
||||
.normalProps = std::nullopt
|
||||
};
|
||||
}
|
||||
return std::nullopt;
|
||||
};
|
||||
|
||||
auto getPropsFromPlanePlaneIntersection = [&cog, orientation](
|
||||
const gp_Pln&& facePlane,
|
||||
const gp_Pln& plane
|
||||
) -> std::optional<DraggerPlacementPropsWithNormals> {
|
||||
if (plane.Axis().IsParallel(facePlane.Axis(), Precision::Angular())) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
IntAna_QuadQuadGeo intersector(facePlane, plane, Precision::Angular(), Precision::Confusion());
|
||||
if (intersector.IsDone() && intersector.NbSolutions() > 0) {
|
||||
gp_Lin line = intersector.Line(1);
|
||||
Standard_Real u = ElCLib::Parameter(line, Base::convertTo<gp_Pnt>(cog));
|
||||
auto pos = Base::convertTo<Base::Vector3d>(ElCLib::Value(u, line));
|
||||
auto lineDir = Base::convertTo<Base::Vector3d>(line.Direction());
|
||||
auto faceNormal = Base::convertTo<Base::Vector3d>(facePlane.Axis().Direction());
|
||||
if (orientation == TopAbs_REVERSED) {
|
||||
faceNormal *= -1;
|
||||
}
|
||||
|
||||
return DraggerPlacementPropsWithNormals {
|
||||
.placementProps = {.position = pos, .dir = cog - pos},
|
||||
.normalProps = DraggerNormalProps {.normal = lineDir, .faceNormal = faceNormal}
|
||||
};
|
||||
}
|
||||
return std::nullopt;
|
||||
};
|
||||
|
||||
BRepAdaptor_Surface adapt(TDSFace);
|
||||
switch (adapt.GetType()) {
|
||||
case GeomAbs_Cylinder:
|
||||
return getPropsFromShapePlaneIntersection(adapt.Cylinder(), plane);
|
||||
case GeomAbs_Cone:
|
||||
return getPropsFromShapePlaneIntersection(adapt.Cone(), plane);
|
||||
case GeomAbs_Plane:
|
||||
return getPropsFromPlanePlaneIntersection(adapt.Plane(), plane);
|
||||
default:
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,7 +64,6 @@ struct PartExport DraggerPlacementProps
|
||||
{
|
||||
Base::Vector3d position;
|
||||
Base::Vector3d dir;
|
||||
Base::Vector3d tangent;
|
||||
};
|
||||
DraggerPlacementProps PartExport
|
||||
getDraggerPlacementFromEdgeAndFace(Part::TopoShape& edge, TopoDS_Face& face);
|
||||
@@ -78,4 +77,17 @@ Base::Vector3d PartExport getMidPointFromFace(Part::TopoShape& face);
|
||||
|
||||
Base::Vector3d PartExport getMidPointFromProfile(Part::TopoShape& profile);
|
||||
|
||||
struct PartExport DraggerNormalProps
|
||||
{
|
||||
Base::Vector3d normal;
|
||||
Base::Vector3d faceNormal;
|
||||
};
|
||||
struct PartExport DraggerPlacementPropsWithNormals
|
||||
{
|
||||
DraggerPlacementProps placementProps;
|
||||
std::optional<DraggerNormalProps> normalProps;
|
||||
};
|
||||
std::optional<DraggerPlacementPropsWithNormals> PartExport
|
||||
getDraggerPlacementFromPlaneAndFace(Part::TopoShape& face, gp_Pln& plane);
|
||||
|
||||
#endif /* GIZMO_HELPER_H */
|
||||
|
||||
@@ -214,14 +214,12 @@ App::DocumentObjectExecReturn* Draft::execute()
|
||||
if (!intersector.IsDone() || intersector.NbLines() < 1) {
|
||||
continue;
|
||||
}
|
||||
Handle(Geom_Curve) icurve = intersector.Line(1);
|
||||
const Handle(Geom_Curve) & icurve = intersector.Line(1);
|
||||
if (!icurve->IsKind(STANDARD_TYPE(Geom_Line))) {
|
||||
continue;
|
||||
}
|
||||
// TODO: How to extract the line from icurve without creating an edge first?
|
||||
TopoDS_Edge edge = BRepBuilderAPI_MakeEdge(icurve);
|
||||
BRepAdaptor_Curve c(edge);
|
||||
neutralPlane = gp_Pln(pm, c.Line().Direction());
|
||||
Handle(Geom_Line) line = Handle(Geom_Line)::DownCast(icurve);
|
||||
neutralPlane = gp_Pln(pm, line->Lin().Direction());
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
@@ -312,6 +310,8 @@ App::DocumentObjectExecReturn* Draft::execute()
|
||||
angle *= -1.0;
|
||||
}
|
||||
|
||||
computeProps = {pullDirection, neutralPlane};
|
||||
|
||||
this->positionByBaseFeature();
|
||||
// create an untransformed copy of the base shape
|
||||
Part::TopoShape baseShape(TopShape);
|
||||
|
||||
@@ -25,6 +25,9 @@
|
||||
#ifndef PARTDESIGN_FEATUREDRAFT_H
|
||||
#define PARTDESIGN_FEATUREDRAFT_H
|
||||
|
||||
#include <gp_Pln.hxx>
|
||||
#include <gp_Dir.hxx>
|
||||
|
||||
#include <App/PropertyStandard.h>
|
||||
#include <App/PropertyUnits.h>
|
||||
#include <App/PropertyLinks.h>
|
||||
@@ -32,6 +35,11 @@
|
||||
|
||||
namespace PartDesign
|
||||
{
|
||||
struct PartDesignExport DraftComputeProps
|
||||
{
|
||||
gp_Dir pullDirection;
|
||||
gp_Pln neutralPlane;
|
||||
};
|
||||
|
||||
class PartDesignExport Draft: public DressUp
|
||||
{
|
||||
@@ -57,6 +65,17 @@ public:
|
||||
}
|
||||
//@}
|
||||
|
||||
/**
|
||||
* @brief getLastComputedProps: Returns the Pull Direction and Neutral Plane
|
||||
* computed during the last call of the execute method.
|
||||
* Note: The returned values might be in the default initialized state if
|
||||
* they were not computed or computation failed
|
||||
*/
|
||||
DraftComputeProps getLastComputedProps() const
|
||||
{
|
||||
return computeProps;
|
||||
}
|
||||
|
||||
private:
|
||||
void handleChangedPropertyType(
|
||||
Base::XMLReader& reader,
|
||||
@@ -64,6 +83,8 @@ private:
|
||||
App::Property* prop
|
||||
) override;
|
||||
static const App::PropertyAngle::Constraints floatAngle;
|
||||
|
||||
DraftComputeProps computeProps;
|
||||
};
|
||||
|
||||
} // namespace PartDesign
|
||||
|
||||
@@ -27,17 +27,20 @@
|
||||
#include <QListWidget>
|
||||
#include <QMessageBox>
|
||||
|
||||
|
||||
#include <Base/Interpreter.h>
|
||||
#include <App/Document.h>
|
||||
#include <App/DocumentObject.h>
|
||||
#include <Base/Converter.h>
|
||||
#include <Gui/Command.h>
|
||||
#include <Base/Interpreter.h>
|
||||
#include <Gui/Selection/Selection.h>
|
||||
#include <Gui/ViewProvider.h>
|
||||
#include <Gui/Inventor/Draggers/Gizmo.h>
|
||||
#include <Gui/Inventor/Draggers/SoRotationDragger.h>
|
||||
#include <Gui/Utilities.h>
|
||||
#include <Mod/PartDesign/App/FeatureDraft.h>
|
||||
#include <Mod/PartDesign/Gui/ReferenceSelection.h>
|
||||
#include <Mod/Part/App/GizmoHelper.h>
|
||||
#include <Mod/Part/App/Tools.h>
|
||||
|
||||
#include "ui_TaskDraftParameters.h"
|
||||
#include "TaskDraftParameters.h"
|
||||
@@ -117,6 +120,8 @@ TaskDraftParameters::TaskDraftParameters(ViewProviderDressUp* DressUpView, QWidg
|
||||
else {
|
||||
hideOnError();
|
||||
}
|
||||
|
||||
setupGizmos(DressUpView);
|
||||
}
|
||||
|
||||
void TaskDraftParameters::onSelectionChanged(const Gui::SelectionChanges& msg)
|
||||
@@ -145,11 +150,12 @@ void TaskDraftParameters::onSelectionChanged(const Gui::SelectionChanges& msg)
|
||||
getDressUpView()->highlightReferences(true);
|
||||
// hide the draft if there was a computation error
|
||||
hideOnError();
|
||||
setGizmoPositions();
|
||||
}
|
||||
else if (selectionMode == line) {
|
||||
auto pcDraft = getObject<PartDesign::Draft>();
|
||||
std::vector<std::string> edges;
|
||||
App::DocumentObject* selObj;
|
||||
App::DocumentObject* selObj = nullptr;
|
||||
getReferencedSelection(pcDraft, msg, selObj, edges);
|
||||
if (!selObj) {
|
||||
return;
|
||||
@@ -163,8 +169,14 @@ void TaskDraftParameters::onSelectionChanged(const Gui::SelectionChanges& msg)
|
||||
getDressUpView()->highlightReferences(true);
|
||||
// hide the draft if there was a computation error
|
||||
hideOnError();
|
||||
setGizmoPositions();
|
||||
}
|
||||
}
|
||||
else if (msg.Type == Gui::SelectionChanges::ClrSelection) {
|
||||
// TODO: the gizmo position should be only recalculated when the feature associated
|
||||
// with the gizmo is removed from the list
|
||||
setGizmoPositions();
|
||||
}
|
||||
}
|
||||
|
||||
void TaskDraftParameters::setButtons(const selectionModes mode)
|
||||
@@ -244,15 +256,17 @@ double TaskDraftParameters::getAngle() const
|
||||
return ui->draftAngle->value().getValue();
|
||||
}
|
||||
|
||||
void TaskDraftParameters::onReversedChanged(const bool on)
|
||||
void TaskDraftParameters::onReversedChanged(const bool reversed)
|
||||
{
|
||||
if (auto draft = getObject<PartDesign::Draft>()) {
|
||||
setButtons(none);
|
||||
setupTransaction();
|
||||
draft->Reversed.setValue(on);
|
||||
draft->Reversed.setValue(reversed);
|
||||
draft->recomputeFeature();
|
||||
// hide the draft if there was a computation error
|
||||
hideOnError();
|
||||
|
||||
setGizmoPositions();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -291,15 +305,83 @@ void TaskDraftParameters::apply()
|
||||
TaskDressUpParameters::apply();
|
||||
}
|
||||
|
||||
|
||||
void TaskDraftParameters::setupGizmos(ViewProvider* vp)
|
||||
{
|
||||
if (!GizmoContainer::isEnabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
angleGizmo = new Gui::RotationGizmo(ui->draftAngle);
|
||||
|
||||
gizmoContainer = GizmoContainer::create({angleGizmo}, vp);
|
||||
|
||||
setGizmoPositions();
|
||||
}
|
||||
|
||||
void TaskDraftParameters::setGizmoPositions()
|
||||
{
|
||||
if (!gizmoContainer) {
|
||||
return;
|
||||
}
|
||||
gizmoContainer->visible = false;
|
||||
|
||||
auto draft = getObject<PartDesign::Draft>();
|
||||
if (!draft || draft->isError()) {
|
||||
return;
|
||||
}
|
||||
Part::TopoShape baseShape = draft->getBaseTopoShape(true);
|
||||
auto faces = draft->getFaces(baseShape);
|
||||
if (faces.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto [pullDirection, neutralPlane] = draft->getLastComputedProps();
|
||||
|
||||
std::optional<DraggerPlacementPropsWithNormals> props
|
||||
= getDraggerPlacementFromPlaneAndFace(faces[0], neutralPlane);
|
||||
if (!props) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (auto normalProps = props->normalProps) {
|
||||
auto pos = Base::convertTo<SbVec3f>(props->placementProps.position);
|
||||
auto dir = Base::convertTo<SbVec3f>(props->placementProps.dir);
|
||||
auto lineDir = Base::convertTo<SbVec3f>(normalProps->normal);
|
||||
auto pp = Base::convertTo<SbVec3f>(pullDirection);
|
||||
|
||||
angleGizmo->setDraggerPlacement(pos, (dir.dot(pp) < 0) ? -pp : pp);
|
||||
|
||||
auto rotDir = Base::convertTo<SbVec3f>(normalProps->faceNormal).cross(pp);
|
||||
if (lineDir.dot(rotDir) < 0) {
|
||||
lineDir *= -1;
|
||||
}
|
||||
if (draft->Reversed.getValue()) {
|
||||
lineDir = -lineDir;
|
||||
}
|
||||
angleGizmo->getDraggerContainer()->setArcNormalDirection(lineDir);
|
||||
angleGizmo->automaticOrientation = false;
|
||||
}
|
||||
else {
|
||||
// The face is cone or cylinder
|
||||
angleGizmo->setDraggerPlacement(
|
||||
Base::convertTo<SbVec3f>(props->placementProps.position),
|
||||
Base::convertTo<SbVec3f>(props->placementProps.dir)
|
||||
);
|
||||
angleGizmo->automaticOrientation = true;
|
||||
}
|
||||
gizmoContainer->visible = true;
|
||||
}
|
||||
|
||||
//**************************************************************************
|
||||
//**************************************************************************
|
||||
// TaskDialog
|
||||
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
||||
|
||||
TaskDlgDraftParameters::TaskDlgDraftParameters(ViewProviderDraft* DressUpView)
|
||||
: TaskDlgDressUpParameters(DressUpView)
|
||||
TaskDlgDraftParameters::TaskDlgDraftParameters(ViewProviderDraft* DraftView)
|
||||
: TaskDlgDressUpParameters(DraftView)
|
||||
{
|
||||
parameter = new TaskDraftParameters(DressUpView);
|
||||
parameter = new TaskDraftParameters(DraftView);
|
||||
|
||||
Content.push_back(parameter);
|
||||
Content.push_back(preview);
|
||||
@@ -319,7 +401,7 @@ bool TaskDlgDraftParameters::accept()
|
||||
parameter->apply();
|
||||
|
||||
std::vector<std::string> strings;
|
||||
App::DocumentObject* obj;
|
||||
App::DocumentObject* obj = nullptr;
|
||||
TaskDraftParameters* draftparameter = static_cast<TaskDraftParameters*>(parameter);
|
||||
|
||||
draftparameter->getPlane(obj, strings);
|
||||
|
||||
@@ -30,6 +30,12 @@
|
||||
|
||||
class Ui_TaskDraftParameters;
|
||||
|
||||
namespace Gui
|
||||
{
|
||||
class RotationGizmo;
|
||||
class GizmoContainer;
|
||||
} // namespace Gui
|
||||
|
||||
namespace PartDesignGui
|
||||
{
|
||||
|
||||
@@ -52,17 +58,22 @@ public:
|
||||
private Q_SLOTS:
|
||||
void onAngleChanged(double angle);
|
||||
void onReversedChanged(bool reversed);
|
||||
void onButtonPlane(const bool checked);
|
||||
void onButtonLine(const bool checked);
|
||||
void onButtonPlane(bool checked);
|
||||
void onButtonLine(bool checked);
|
||||
void onRefDeleted() override;
|
||||
|
||||
protected:
|
||||
void setButtons(const selectionModes mode) override;
|
||||
void setButtons(selectionModes mode) override;
|
||||
void changeEvent(QEvent* e) override;
|
||||
void onSelectionChanged(const Gui::SelectionChanges& msg) override;
|
||||
|
||||
private:
|
||||
std::unique_ptr<Ui_TaskDraftParameters> ui;
|
||||
|
||||
std::unique_ptr<Gui::GizmoContainer> gizmoContainer;
|
||||
Gui::RotationGizmo* angleGizmo = nullptr;
|
||||
void setupGizmos(ViewProvider* vp);
|
||||
void setGizmoPositions();
|
||||
};
|
||||
|
||||
/// simulation dialog for the TaskView
|
||||
|
||||
Reference in New Issue
Block a user