From: dish Date: Wed, 22 May 2024 11:49:37 +0000 (+0000) Subject: [bos #35153][EDF](2023-T1) Construction grid. X-Git-Url: http://git.salome-platform.org/gitweb/?a=commitdiff_plain;h=bd75f8794994bd4632554bcbd8ce4fa944924ff5;p=modules%2Fshaper.git [bos #35153][EDF](2023-T1) Construction grid. Add rectangular and circular construction grids. --- diff --git a/src/GeomAPI/GeomAPI_Face.cpp b/src/GeomAPI/GeomAPI_Face.cpp index f0bfe7e97..d80a08e80 100644 --- a/src/GeomAPI/GeomAPI_Face.cpp +++ b/src/GeomAPI/GeomAPI_Face.cpp @@ -378,7 +378,7 @@ GeomPointPtr GeomAPI_Face::middlePoint() const return anInnerPoint; double aUMin, aUMax, aVMin, aVMax; - optimalBounds(aFace, aUMin, aUMax, aVMin, aVMax); + ::optimalBounds(aFace, aUMin, aUMax, aVMin, aVMax); Handle(Geom_Surface) aSurf = BRep_Tool::Surface(aFace); if (aSurf.IsNull()) @@ -389,6 +389,16 @@ GeomPointPtr GeomAPI_Face::middlePoint() const return anInnerPoint; } +bool GeomAPI_Face::optimalBounds(double& theUMin, double& theUMax, + double& theVMin, double& theVMax) const +{ + const TopoDS_Face& aFace = impl(); + if (aFace.IsNull()) + return false; + + ::optimalBounds(aFace, theUMin, theUMax, theVMin, theVMax); + return true; +} // ================== Auxiliary functions ======================== @@ -596,4 +606,4 @@ void optimalBounds(const TopoDS_Face& theFace, double& theUMin, double& theUMax, optimalBounds(theFace, TopoDS::Edge(anExp.Current()), aBB); aBB.Get(theUMin, theVMin, theUMax, theVMax); -} +} \ No newline at end of file diff --git a/src/GeomAPI/GeomAPI_Face.h b/src/GeomAPI/GeomAPI_Face.h index 4fb969047..81682ae23 100644 --- a/src/GeomAPI/GeomAPI_Face.h +++ b/src/GeomAPI/GeomAPI_Face.h @@ -71,8 +71,14 @@ public: /// Returns torus if the face is based on the toroidal surface GEOMAPI_EXPORT std::shared_ptr getTorus() const; - /// Return inner point in the face + /// Returns inner point in the face GEOMAPI_EXPORT virtual std::shared_ptr middlePoint() const; + + /// Returns bounding box in UV-space. + GEOMAPI_EXPORT virtual bool optimalBounds( + double& theUMin, double& theUMax, + double& theVMin, double& theVMax + ) const; }; //! Pointer on attribute object diff --git a/src/GeomAPI/GeomAPI_Pln.h b/src/GeomAPI/GeomAPI_Pln.h index eca7cec71..998693c68 100644 --- a/src/GeomAPI/GeomAPI_Pln.h +++ b/src/GeomAPI/GeomAPI_Pln.h @@ -27,10 +27,11 @@ class GeomAPI_Ax3; class GeomAPI_Pnt; class GeomAPI_Dir; class GeomAPI_Lin; +class gp_Pln; /**\class GeomAPI_Pln * \ingroup DataModel - * \brief 3D point defined by three coordinates + * \Plane in 3D place, defined by normal, center and x-direction. */ class GeomAPI_Pln : public GeomAPI_Interface @@ -38,7 +39,7 @@ class GeomAPI_Pln : public GeomAPI_Interface public: /// Creation of plane by the axis placement GEOMAPI_EXPORT - GeomAPI_Pln(const std::shared_ptr& theAxis); + GeomAPI_Pln(const std::shared_ptr& theAxes); /// Creation of plane by the point and normal GEOMAPI_EXPORT diff --git a/src/GeomAlgoAPI/GeomAlgoAPI_FaceBuilder.cpp b/src/GeomAlgoAPI/GeomAlgoAPI_FaceBuilder.cpp index 511dae1d4..60ac0ba12 100644 --- a/src/GeomAlgoAPI/GeomAlgoAPI_FaceBuilder.cpp +++ b/src/GeomAlgoAPI/GeomAlgoAPI_FaceBuilder.cpp @@ -98,6 +98,22 @@ std::shared_ptr GeomAlgoAPI_FaceBuilder::planarFace( return aRes; } +//================================================================================================== +/*static*/ std::shared_ptr GeomAlgoAPI_FaceBuilder::planarRectangularFace( + const gp_Ax3& theCS, double theWidth, double theHeight, double theOffsetX, double theOffsetY +) { + const gp_Pln plane = gp_Pln(theCS); + BRepBuilderAPI_MakeFace faceBuilder( + plane, + theOffsetX - theWidth/2 , theOffsetX + theWidth/2, + theOffsetY - theHeight/2, theOffsetY + theHeight/2 + ); + + std::shared_ptr res(new GeomAPI_Face()); + res->setImpl(new TopoDS_Face(faceBuilder.Face())); + return res; +} + //================================================================================================== std::shared_ptr GeomAlgoAPI_FaceBuilder::planarFaceByThreeVertices( const std::shared_ptr theVertex1, diff --git a/src/GeomAlgoAPI/GeomAlgoAPI_FaceBuilder.h b/src/GeomAlgoAPI/GeomAlgoAPI_FaceBuilder.h index 798e5a59b..0d8f7a921 100644 --- a/src/GeomAlgoAPI/GeomAlgoAPI_FaceBuilder.h +++ b/src/GeomAlgoAPI/GeomAlgoAPI_FaceBuilder.h @@ -29,6 +29,7 @@ class GeomAPI_Face; class GeomAPI_Pln; class GeomAPI_Pnt; class GeomAPI_Vertex; +class gp_Ax3; /// \class GeomAlgoAPI_FaceBuilder /// \ingroup DataAlgo @@ -37,12 +38,12 @@ class GEOMALGOAPI_EXPORT GeomAlgoAPI_FaceBuilder { public: /// Creates square planar face by given point of the center, - /// normal to the plane and size of square + /// normal to the plane and size of square. static std::shared_ptr squareFace(const std::shared_ptr theCenter, const std::shared_ptr theNormal, const double theSize); /// Creates square planar face by given point of the center, - /// normal to the plane and size of square + /// normal to the plane and size of square. static std::shared_ptr squareFace(const std::shared_ptr thePlane, const double theSize); @@ -50,11 +51,18 @@ class GEOMALGOAPI_EXPORT GeomAlgoAPI_FaceBuilder static std::shared_ptr planarFace(const std::shared_ptr theCenter, const std::shared_ptr theNormal); - /// Creates a planar face by given plane, left lower point and size. + /// Creates a planar face by given plane, left lower point and size. Does not take into account X & Y directions of thePlane! static std::shared_ptr planarFace(const std::shared_ptr thePlane, const double theX, const double theY, const double theWidth, const double theHeight); + /*! \brief Creates rectangular planar face, with normal, X and Y directions as of theCS. */ + static std::shared_ptr planarRectangularFace( + const gp_Ax3& theCS, + double theWidth, double theHeight, + double theOffsetX = 0, double theOffsetY = 0 + ); + /// Creates a planar face by three vertices. static std::shared_ptr planarFaceByThreeVertices( const std::shared_ptr theVertex1, diff --git a/src/ModuleBase/ModuleBase_ModelWidget.cpp b/src/ModuleBase/ModuleBase_ModelWidget.cpp index d70c6572a..df43f802b 100644 --- a/src/ModuleBase/ModuleBase_ModelWidget.cpp +++ b/src/ModuleBase/ModuleBase_ModelWidget.cpp @@ -52,7 +52,7 @@ ModuleBase_ModelWidget::ModuleBase_ModelWidget(QWidget* theParent, const Config_WidgetAPI* theData) : QWidget(theParent), - myWidgetValidator(0), + myWidgetValidator(nullptr), myState(Stored), myIsEditing(false), myIsValueStateBlocked(false), diff --git a/src/ModuleBase/ModuleBase_ModelWidget.h b/src/ModuleBase/ModuleBase_ModelWidget.h index 3af67f56d..04a69e12c 100644 --- a/src/ModuleBase/ModuleBase_ModelWidget.h +++ b/src/ModuleBase/ModuleBase_ModelWidget.h @@ -48,7 +48,7 @@ class QKeyEvent; /**\class ModuleBase_ModelWidget * \ingroup GUI - * \brief An abstract custom widget class. This class realization is assumed + * \brief An abstract custom widget class. This class realization is assumed * to create some controls. * The controls values modification should send signal about values change. * @@ -177,7 +177,7 @@ Q_OBJECT { return true; } /// Returns widget validator, by default it is NULL. To be created in a child if necessary - ModuleBase_WidgetValidator* widgetValidator() { return myWidgetValidator; } + ModuleBase_WidgetValidator* widgetValidator() { return myWidgetValidator.get(); } /// Restore value from attribute data to the widget's control. /// Emits signals before and after store @@ -430,7 +430,7 @@ protected: protected: /// own validator, by default it is zero - ModuleBase_WidgetValidator* myWidgetValidator; + std::unique_ptr myWidgetValidator; /// The attribute name of the model feature std::string myAttributeID; diff --git a/src/PartSet/CMakeLists.txt b/src/PartSet/CMakeLists.txt index a09d27086..8a07ba61a 100644 --- a/src/PartSet/CMakeLists.txt +++ b/src/PartSet/CMakeLists.txt @@ -93,6 +93,7 @@ SET(PROJECT_SOURCES PartSet_IconFactory.cpp PartSet_MenuMgr.cpp PartSet_Module.cpp + PartSet_MouseProcessor.cpp PartSet_OperationPrs.cpp PartSet_OverconstraintListener.cpp PartSet_PreviewPlanes.cpp diff --git a/src/PartSet/PartSet_Module.cpp b/src/PartSet/PartSet_Module.cpp index efc795171..684fe5f08 100644 --- a/src/PartSet/PartSet_Module.cpp +++ b/src/PartSet/PartSet_Module.cpp @@ -1783,9 +1783,9 @@ void PartSet_Module::processEvent(const std::shared_ptr& theMess CompositeFeaturePtr aSketch = mySketchMgr->activeSketch(); if (aSketch.get()) { ModuleBase_Operation* anOperation = myWorkshop->currentOperation(); - if (PartSet_SketcherMgr::isSketchOperation(anOperation) && - mySketchMgr->previewSketchPlane()->isDisplayed()) - mySketchMgr->previewSketchPlane()->createSketchPlane(aSketch, myWorkshop); + if (PartSet_SketcherMgr::isSketchOperation(anOperation)) { + mySketchMgr->previewSketchPlane()->setAllUsingSketch(aSketch); + } } } else if (theMessage->eventID() == Events_Loop::loop()->eventByName(EVENT_FEATURE_LICENSE_VALID)) { diff --git a/src/PartSet/PartSet_MouseProcessor.cpp b/src/PartSet/PartSet_MouseProcessor.cpp new file mode 100644 index 000000000..2a68166c4 --- /dev/null +++ b/src/PartSet/PartSet_MouseProcessor.cpp @@ -0,0 +1,128 @@ +// Copyright (C) 2014-2024 CEA, EDF +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com +// + +#include "PartSet_MouseProcessor.h" + +#include "ModuleBase_IViewWindow.h" +#include "ModuleBase_IWorkshop.h" +#include "ModuleBase_IViewer.h" +#include "PartSet_Module.h" +#include "PartSet_PreviewSketchPlane.h" +#include "ModelAPI_CompositeFeature.h" +#include +#include +#include +#include + + +/// @brief +/// During drawing of a line on a sketch, a crash happens if coordinates (theX, theY), returned by \ref PartSet_MouseProcessor::convertPointToLocal, +/// coincide with on-sketch plane coordinates of the closest grid node. What exactly causes this odd behaviour remains unknown. +/// The crash is manifested on all supported Linux OSes, except Debian 11 and, maybe, 12. +/// Even on problematic OSes, the crash does not happen if a debugger (at least LLDB) is attached to Salome with Shaper being compiled in Debug mode. +double slightlyChangeVal(double theVal) +{ + static const double eps = 1e-5; + + if (std::abs(theVal) > 1) + return (1 + eps) * theVal; + else + return theVal + eps; +} + + +bool PartSet_MouseProcessor::convertPointToLocal( + ModuleBase_IWorkshop* theWorkshop, + const std::shared_ptr& theSketch, + ModuleBase_IViewWindow* theWindow, + const QPoint& theEventPos, + double& theX, double& theY, + bool theHighlight, + bool theAddOffset +) const { + ModuleBase_IViewer* const viewer = theWorkshop->viewer(); + if (!viewer) + return false; + + const auto aV3dViewer = viewer->v3dViewer(); + const PartSet_Module* module = dynamic_cast(theWorkshop->module()); + if (!module) + return false; + + const Handle(V3d_View) view = theWindow->v3dView(); + + PartSet_PreviewSketchPlane* previewPlane = module->sketchMgr()->previewSketchPlane(); + if (!aV3dViewer || !aV3dViewer->Grid()->IsActive() || previewPlane->getGridSnappingMode() == PartSet_PreviewSketchPlane::GridSnappingMode::Off) { + const gp_Pnt mousePoint = PartSet_Tools::convertClickToPoint(theEventPos, view); + PartSet_Tools::convertTo2D(mousePoint, theSketch, view, theX, theY); + return true; + } + else { + double closestGridPointX, closestGridPointY, closestGridPointZ; + view->ConvertToGrid(theEventPos.x(), theEventPos.y(), closestGridPointX, closestGridPointY, closestGridPointZ); + const gp_Pnt gridPoint = gp_Pnt(closestGridPointX, closestGridPointY, closestGridPointZ); + + if (previewPlane->getGridSnappingMode() == PartSet_PreviewSketchPlane::GridSnappingMode::SnapAnyway) { + PartSet_Tools::convertTo2D(gridPoint, theSketch, view, theX, theY); + + if (theAddOffset) { + theX = slightlyChangeVal(theX); + theY = slightlyChangeVal(theY); + } + + if (theHighlight) { + view->Viewer()->ShowGridEcho(view, Graphic3d_Vertex(closestGridPointX, closestGridPointY, closestGridPointZ)); + view->Viewer()->SetGridEcho(true); + view->RedrawImmediate(); + } + else { + view->Viewer()->SetGridEcho(false); + view->RedrawImmediate(); + } + + return true; + } + else /* aPreviewPlane->getGridSnappingMode() == PartSet_PreviewSketchPlane::GridSnappingMode::SnapInProximity */ { + Standard_Integer aClosestPX, aClosestPY; // Unit is pixel. + view->Convert(closestGridPointX, closestGridPointY, closestGridPointZ, aClosestPX, aClosestPY); + const int squareDistanceP = std::pow(aClosestPX - theEventPos.x(), 2) + std::pow(aClosestPY - theEventPos.y(), 2); + static const int THRESHOLD = std::pow(PartSet_PreviewSketchPlane::SNAP_PROXIMITY_P, 2); + + if (squareDistanceP > THRESHOLD) { + const gp_Pnt mousePoint = PartSet_Tools::convertClickToPoint(theEventPos, view); + PartSet_Tools::convertTo2D(mousePoint, theSketch, view, theX, theY); + view->Viewer()->SetGridEcho(false); + view->RedrawImmediate(); + } + else { + PartSet_Tools::convertTo2D(gridPoint, theSketch, view, theX, theY); + + if (theAddOffset) { + theX = slightlyChangeVal(theX); + theY = slightlyChangeVal(theY); + } + + view->Viewer()->ShowGridEcho(view, Graphic3d_Vertex(closestGridPointX, closestGridPointY, closestGridPointZ)); + view->Viewer()->SetGridEcho(true); + view->RedrawImmediate(); + } + return true; + } + } +} \ No newline at end of file diff --git a/src/PartSet/PartSet_MouseProcessor.h b/src/PartSet/PartSet_MouseProcessor.h index 0ad72cae4..4f759249f 100644 --- a/src/PartSet/PartSet_MouseProcessor.h +++ b/src/PartSet/PartSet_MouseProcessor.h @@ -30,12 +30,12 @@ class ModuleBase_IViewWindow; class ModuleBase_ViewerPrs; +class ModuleBase_IWorkshop; +class ModelAPI_CompositeFeature; class QMouseEvent; +class QPoint; -/** - * This is an interface to allow processing of mouse events. Implementation of necessary methods -* should be done in a child. -*/ +/** Interface for mouse events processing. */ class PartSet_MouseProcessor { public: @@ -64,6 +64,24 @@ public: virtual void setPreSelection(const std::shared_ptr& thePreSelected, ModuleBase_IViewWindow* theWnd, QMouseEvent* theEvent) {} + + protected: + /// \brief Converts position of mouse cursor to local coordinates on sketch plane. + /// Snaps on-sketch-plane-coordinates to closest construction grid node. + /// \param theEventPos is position of mouse cursor. + /// \param theX and \param theY are local coordinates on sketch plane. + /// \param theHighlight If point is snapped, hightlight grid point. + /// \param theAddOffset If true, serves as a remedy for odd crash during drawing of a line on a sketch. + /// \return true on success. + virtual bool convertPointToLocal( + ModuleBase_IWorkshop* theWorkshop, + const std::shared_ptr& theSketch, // Passing by reference is intentionally. + ModuleBase_IViewWindow* theWindow, + const QPoint& theEventPos, + double& theX, double& theY, + bool theHighlight = false, + bool theAddOffset = false + ) const; }; #endif diff --git a/src/PartSet/PartSet_PreviewSketchPlane.cpp b/src/PartSet/PartSet_PreviewSketchPlane.cpp index 05cf99f72..a895b0d2d 100644 --- a/src/PartSet/PartSet_PreviewSketchPlane.cpp +++ b/src/PartSet/PartSet_PreviewSketchPlane.cpp @@ -18,17 +18,18 @@ // #include "PartSet_PreviewSketchPlane.h" +#include "PartSet_SketcherMgr.h" #include "PartSet_Tools.h" #include -#include -#include +#include +#include +#include #include -#include #include -#include +#include #include #include @@ -40,205 +41,788 @@ #include #include -#include - -PartSet_PreviewSketchPlane::PartSet_PreviewSketchPlane() - : myPreviewIsDisplayed(false), mySizeOfView(0), myIsUseSizeOfView(false) +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#define PI 3.14159265358979323846 + +#ifdef SKETCH_ACCESSORY_DBG +#include +#include +#include +const std::string PREFIX = "PreviewSketchPlane: "; +const std::wstring WPREFIX = L"PreviewSketchPlane: "; +#endif +/*static*/ bool PartSet_PreviewSketchPlane::SketchAccessoryDbg(const QString& theString) { +#ifdef SKETCH_ACCESSORY_DBG + std::wcout << WPREFIX << theString.toStdWString() << std::endl; + return true; +#else + return false; +#endif } - -void PartSet_PreviewSketchPlane::eraseSketchPlane(ModuleBase_IWorkshop* theWorkshop, - const bool isClearPlane) +/*static*/ bool PartSet_PreviewSketchPlane::SketchAccessoryDbg(const char* src) { - if (myPreviewIsDisplayed) { - XGUI_Displayer* aDisp = XGUI_Tools::workshop(theWorkshop)->displayer(); - aDisp->eraseAIS(myPlane, false); - myPreviewIsDisplayed = false; - } - if (isClearPlane) clearPlanePreview(); +#ifdef SKETCH_ACCESSORY_DBG + std::wcout << WPREFIX << std::wstring(src, src + strlen(src)) << std::endl; + return true; +#else + return false; +#endif; } - -void PartSet_PreviewSketchPlane::displaySketchPlane(ModuleBase_IWorkshop* theWorkshop) +/*static*/ bool PartSet_PreviewSketchPlane::SketchAccessoryDbg(const char* theCSDescription, const gp_Ax3& theCS) { - if (myPlane.get() && (!myPreviewIsDisplayed)) { - XGUI_Displayer* aDisp = XGUI_Tools::workshop(theWorkshop)->displayer(); - aDisp->displayAIS(myPlane, false/*load object in selection*/, 1/*shaded*/, false); - myPreviewIsDisplayed = true; - } +#ifdef SKETCH_ACCESSORY_DBG + std::stringstream s; + s << PREFIX << theCSDescription << "\n"; + theCS.DumpJson(s); + s << "\n"; + PartSet_PreviewSketchPlane::SketchAccessoryDbg(QString::fromStdString(s.str())); + return true; +#else + return false; +#endif +} +/*static*/ bool PartSet_PreviewSketchPlane::SketchAccessoryDbg(const char* theCSDescription, const gp_Pnt& thePnt) +{ +#ifdef SKETCH_ACCESSORY_DBG + std::stringstream s; + s << PREFIX << theCSDescription; + thePnt.DumpJson(s); + s << "\n"; + PartSet_PreviewSketchPlane::SketchAccessoryDbg(QString::fromStdString(s.str())); + return true; +#else + return false; +#endif +} +/*static*/ bool PartSet_PreviewSketchPlane::SketchAccessoryDbg(const char* theCSDescription, const gp_Trsf& theMatrix) +{ +#ifdef SKETCH_ACCESSORY_DBG + std::stringstream s; + s << PREFIX << theCSDescription << "\n"; + theMatrix.DumpJson(s); + s << "\n"; + PartSet_PreviewSketchPlane::SketchAccessoryDbg(QString::fromStdString(s.str())); + return true; +#else + return false; +#endif } -void PartSet_PreviewSketchPlane::clearPlanePreview() +PartSet_PreviewSketchPlane::PartSet_PreviewSketchPlane(PartSet_SketcherMgr* theManager) + : mySketcherMgr(theManager), myValid(false), + mySketchDimensions(0, 0), mySketchDefaultSize(0), + myShowTrihedron(false), myTrihedron(new AIS_PlaneTrihedron(new Geom_Plane(1, 0, 0, 0))), + myShowSubstrate(false), mySubstrate(nullptr), + myGridType(PartSet_Tools::SketchPlaneGridType::No), myGridDrawMode(Aspect_GridDrawMode::Aspect_GDM_Lines), + mySnappingMode(GridSnappingMode::SnapAnyway), + myRectangularGridSteps(1, 1), myCircularGridRadialStep(1) { - myPlane = std::shared_ptr(); - myShape = std::shared_ptr(); + myTrihedron->SetXLabel("X'"); + myTrihedron->SetYLabel("Y'"); + myTrihedron->SetColor(Quantity_Color(Quantity_NameOfColor::Quantity_NOC_MAGENTA)); // The color does not coincide with colors of world axes. } - -void PartSet_PreviewSketchPlane::createSketchPlane(const CompositeFeaturePtr& theSketch, - ModuleBase_IWorkshop* theWorkshop) +void PartSet_PreviewSketchPlane::savePreferencesIntoSketchData(std::shared_ptr theSketch) const { - // plane is visualized only if sketch plane is filled - if (!PartSet_Tools::sketchPlane(theSketch).get()) + if (!theSketch || !this->isValid()) return; - AttributeSelectionPtr aSelAttr = std::dynamic_pointer_cast - (theSketch->data()->attribute(SketchPlugin_SketchEntity::EXTERNAL_ID())); - if (!aSelAttr) + PartSet_Tools::sketchPlaneAxesEnabled(theSketch)->setValue(myShowTrihedron); + PartSet_Tools::sketchPlaneSubstrateEnabled(theSketch)->setValue(myShowSubstrate); + PartSet_Tools::setSketchPlaneGridType(theSketch, myGridType); + + saveRectangularGridPreferencesIntoSketchData(theSketch); +} + +void PartSet_PreviewSketchPlane::saveRectangularGridPreferencesIntoSketchData(std::shared_ptr theSketch) const +{ + if (!theSketch || !this->isValid()) return; - if (myShape.get() && myShape->isSame(aSelAttr->value()) && myPlane.get()) + PartSet_Tools::sketchPlaneRectangularGridStepX(theSketch)->setValue(myRectangularGridSteps.first); + PartSet_Tools::sketchPlaneRectangularGridStepY(theSketch)->setValue(myRectangularGridSteps.second); + + PartSet_Tools::sketchPlaneRectangularGridOffsetAngle(theSketch)->setValue(myRectangularGridOffsetAngle); + PartSet_Tools::sketchPlaneRectangularGridOffsetX(theSketch)->setValue(myRectangularGridTransOffset.first); + PartSet_Tools::sketchPlaneRectangularGridOffsetY(theSketch)->setValue(myRectangularGridTransOffset.second); +} + +void PartSet_PreviewSketchPlane::saveCircularGridPreferencesIntoSketchData(std::shared_ptr theSketch) const +{ + if (!theSketch || !this->isValid()) return; - XGUI_Displayer* aDisp = XGUI_Tools::workshop(theWorkshop)->displayer(); - if (myPreviewIsDisplayed) { - aDisp->eraseAIS(myPlane, false); - } - - // Create Preview - // selected linear face parameters - myShape = aSelAttr->value(); - // this case is needed by constructing sketch on a plane, where result shape is equal - // to context result, therefore value() returns NULL and we should use shape of context. - if (!myShape.get() && aSelAttr->context().get()) - myShape = aSelAttr->context()->shape(); - - if (!myShape.get()) { - // Create Preview for default planes - std::shared_ptr anOrigin = std::dynamic_pointer_cast( - theSketch->data()->attribute(SketchPlugin_Sketch::ORIGIN_ID())); - std::shared_ptr aNormal = std::dynamic_pointer_cast( - theSketch->data()->attribute(SketchPlugin_Sketch::NORM_ID())); - - double aFaceSize = myIsUseSizeOfView ? mySizeOfView - : Config_PropManager::real(SKETCH_TAB_NAME, "planes_size"); - if (aFaceSize <= Precision::Confusion()) - aFaceSize = 200; // Set default value - - myShape = GeomAlgoAPI_FaceBuilder::squareFace( - myViewCentralPoint.get() ? myViewCentralPoint : anOrigin->pnt(), aNormal->dir(), aFaceSize); - } - else if (myIsUseSizeOfView && (mySizeOfView > 0)) { - std::shared_ptr aFace(new GeomAPI_Face(myShape)); - std::shared_ptr aPlane = aFace->getPlane(); - if (aPlane.get()) { - double anA, aB, aC, aD; - aPlane->coefficients(anA, aB, aC, aD); - std::shared_ptr aNormDir(new GeomAPI_Dir(anA, aB, aC)); - std::shared_ptr aCoords = aNormDir->xyz(); - std::shared_ptr aZero(new GeomAPI_XYZ(0, 0, 0)); - aCoords = aCoords->multiplied(-aD * aCoords->distance(aZero)); - std::shared_ptr anOrigPnt(new GeomAPI_Pnt(aCoords)); - myShape = GeomAlgoAPI_FaceBuilder::squareFace( - myViewCentralPoint.get() ? myViewCentralPoint : anOrigPnt, aNormDir, mySizeOfView); - } - } - myPlane = createPreviewPlane(); + PartSet_Tools::sketchPlaneCircularGridStepR(theSketch)->setValue(myCircularGridRadialStep); + PartSet_Tools::sketchPlaneCircularGridNumOfAngSegments(theSketch)->setValue(myCircularGridNumOfAngularSections); - aDisp->displayAIS(myPlane, false/*load object in selection*/, 1/*shaded*/, false); - myPreviewIsDisplayed = true; + PartSet_Tools::sketchPlaneCircularGridOffsetAngle(theSketch)->setValue(myCircularGridOffsetAngle); + PartSet_Tools::sketchPlaneCircularGridOffsetX(theSketch)->setValue(myCircularGridTransOffset.first); + PartSet_Tools::sketchPlaneCircularGridOffsetY(theSketch)->setValue(myCircularGridTransOffset.second); } -double maximumSize(double theXmin, double theYmin, double theZmin, - double theXmax, double theYmax, double theZmax) +void PartSet_PreviewSketchPlane::setCSAndSize(const gp_Ax3& theCS, double theSize) { - double aSize = fabs(theXmax - theXmin); - double aSizeToCompare = fabs(theYmax - theYmin); - if (aSizeToCompare > aSize) - aSize = aSizeToCompare; - aSizeToCompare = fabs(theZmax - theZmin); - if (aSizeToCompare > aSize) - aSize = aSizeToCompare; + SketchAccessoryDbg("setCSAndSize(_)"); - return aSize; + mySketchCS = theCS; + mySketchDefaultSize = std::abs(theSize); + mySketchDimensions.first = mySketchDefaultSize; + mySketchDimensions.second = mySketchDefaultSize; + + configureTrihedron(); + + if (!mySubstrate) + initSubstrate(nullptr); + else { + const auto face = GeomAlgoAPI_FaceBuilder::planarRectangularFace(mySketchCS, mySketchDimensions.first, mySketchDimensions.second); + mySubstrate->createShape(face); + } + + reconfigureGrid(); + myValid = true; } -bool PartSet_PreviewSketchPlane::getDefaultSizeOfView( - const CompositeFeaturePtr& theSketch, double& theSizeOfView, - std::shared_ptr& theCentralPnt) +bool PartSet_PreviewSketchPlane::setAllUsingSketch(std::shared_ptr theSketch) { - if (!PartSet_Tools::sketchPlane(theSketch).get()) + SketchAccessoryDbg("setAllUsingSketch(theSketch)"); + + if (!theSketch || !PartSet_Tools::sketchPlane(theSketch)) { + SketchAccessoryDbg("invalid sketch - nullptr or normal/center is undefined."); + setInvalid(); return false; + } - AttributeSelectionPtr aSelAttr = std::dynamic_pointer_cast - (theSketch->data()->attribute(SketchPlugin_SketchEntity::EXTERNAL_ID())); + bool sketchIsBlank = false; + const auto aSelAttr = std::dynamic_pointer_cast(theSketch->data()->attribute(SketchPlugin_SketchEntity::EXTERNAL_ID())); if (aSelAttr) { - myShape = aSelAttr->value(); - // this case is needed by constructing sketch on a plane, where result shape is equal - // to context result, therefore value() returns NULL and we should use shape of context. - if (!myShape.get() && aSelAttr->context().get()) - myShape = aSelAttr->context()->shape(); + std::shared_ptr sketchShape; + if (aSelAttr->value()) { + sketchShape = aSelAttr->value(); + SketchAccessoryDbg("aSelAttr->value()"); + } + else if (aSelAttr->context()) { + sketchShape = aSelAttr->context()->shape(); + SketchAccessoryDbg("aSelAttr->context()->shape()"); + } + + if (!sketchShape) { + sketchIsBlank = true; + } + else { + if (!sketchShape->isPlanar()) { + SketchAccessoryDbg("invalid sketch - shape is not planar."); + setInvalid(); + return false; + } + const std::shared_ptr sketchFace = sketchShape->face(); + if (!sketchFace) { + SketchAccessoryDbg("invalid sketch - shape is not face."); + setInvalid(); + return false; + } + + { // Define CS as one at the center (Uc, Vc) of parametric domain. + // If the planar surface is non-convex, the center of parametric domain may + // lay outside of the surface. If X'(U, V) or Y'(U, V) are non-linear, + // X(Uc, Vc), Y(Uc, Vc) or its 1st derivative may not be defined. + // But: + // 1) The sketch widget only accepts Planes to start sketch. + // 2) The set of planar surfaces, being used in the app, does not include those described above. + double UMax, UMin, VMax, VMin; + const bool success = sketchFace->optimalBounds(UMin, UMax, VMin, VMax); + if (!success) { + SketchAccessoryDbg("Can't get sketch CS: UV-domain is undefined."); + setInvalid(); + return false; + } + + // X' and Y' directions and sketch center in the sketch->data() are not coincide with + // ones of the surface the sketch is started on. + const auto sketchFaceCS = PartSet_Tools::getWorldCSAt(*sketchFace, (UMin+UMax)/2, (VMin+VMax)/2); + if (!sketchFaceCS.first) { + SketchAccessoryDbg("Can't get sketch CS: sketch face is not 1-differentiable as function of U and V at the center of UV-domain."); + setInvalid(); + return false; + } + + mySketchCS = sketchFaceCS.second; + } + + mySketchDefaultSize = PartSet_Tools::sketchPlaneDefaultSize(theSketch)->value(); + + { // Calculate sketch dimensions. + // The only purpose of converting from UV to X'Y' during calculation of sketch dimensions + // is to take into account scale factor or if the a non-orthogonal transformation is applied (if it even possible). + const auto sketchFaceBox = PartSet_Tools::getBBoxAtCS(*sketchFace, mySketchCS); + if (!sketchFaceBox.first) { + SketchAccessoryDbg("Can't find bounding box for sketch face."); + setInvalid(); + mySketchDimensions.first = getDefaultSize(); + mySketchDimensions.second = getDefaultSize(); + } + else { + // Here X and Y are X' and Y', and Z is sketch normal. + double Xmin, Ymin, Zmin, Xmax, Ymax, Zmax; + sketchFaceBox.second.Get(Xmin, Ymin, Zmin, Xmax, Ymax, Zmax); + + mySketchDimensions.first = std::abs(Xmax - Xmin); + mySketchDimensions.second = std::abs(Ymax - Ymin); + } + } + + if (!mySubstrate) + initSubstrate(sketchFace); + else + mySubstrate->createShape(sketchFace); + +#ifdef SKETCH_ACCESSORY_DBG + auto sketchCS = gp_Ax3(); + { + const auto aData = theSketch->data(); + const auto aC = std::dynamic_pointer_cast(aData->attribute(SketchPlugin_Sketch::ORIGIN_ID())); + const auto aX = std::dynamic_pointer_cast(aData->attribute(SketchPlugin_Sketch::DIRX_ID())); + const auto aNorm = std::dynamic_pointer_cast(aData->attribute(SketchPlugin_Sketch::NORM_ID())); + try { + sketchCS = gp_Ax3(aC->pnt()->impl(), aX->dir()->impl(), aNorm->dir()->impl()); + } + catch(...) { + SketchAccessoryDbg("Sketch data contains invalid basis."); + } + } + + SketchAccessoryDbg("Sketch CS (sketch data):", sketchCS); + SketchAccessoryDbg("Sketch face CS:", mySketchCS); + SketchAccessoryDbg(QString("Sketch dimensions: ") + QString::number(mySketchDimensions.first) + ", " + QString::number(mySketchDimensions.second)); +#endif + } + } + + if (sketchIsBlank) { + SketchAccessoryDbg(QString("setAllUsingSketch(theSketch). Empty sketch")); + try + { + const auto sketchOrigin = std::dynamic_pointer_cast( + theSketch->data()->attribute(SketchPlugin_Sketch::ORIGIN_ID()) + ); + + const auto sketchNormal = std::dynamic_pointer_cast( + theSketch->data()->attribute(SketchPlugin_Sketch::NORM_ID()) + ); + + const auto sketchXDirection = std::dynamic_pointer_cast( + theSketch->data()->attribute(SketchPlugin_Sketch::DIRX_ID()) + ); + + mySketchCS.SetLocation(sketchOrigin->pnt()->impl()); + + const gp_Dir normal = sketchNormal->dir()->impl(); + mySketchCS.SetDirection(normal); + // Chi is synonym for X'. + const gp_Dir chiDirection = sketchXDirection->dir()->impl(); + mySketchCS.SetXDirection(chiDirection); + + mySketchDefaultSize = PartSet_Tools::sketchPlaneDefaultSize(theSketch)->value(); + const double defaultSize = getDefaultSize(); + mySketchDimensions.first = defaultSize; + mySketchDimensions.second = defaultSize; + } + catch(...) { + SketchAccessoryDbg("Invalid basis in sketch data."); + setInvalid(); + return false; + } } - if (myShape.get()) + configureTrihedron(); + + if (sketchIsBlank) { + if (!mySubstrate) + initSubstrate(nullptr); + else { + const auto face = GeomAlgoAPI_FaceBuilder::planarRectangularFace(mySketchCS, mySketchDimensions.first, mySketchDimensions.second); + mySubstrate->createShape(face); + } + } + + myValid = true; + showAxes(PartSet_Tools::sketchPlaneAxesEnabled(theSketch)->value()); + showSubstrate(PartSet_Tools::sketchPlaneSubstrateEnabled(theSketch)->value()); + reconfigureGridUsingSketch(theSketch); + return true; +} + +bool PartSet_PreviewSketchPlane::reconfigureGrid() +{ + return reconfigureGridUsingSketch(nullptr); +} + +bool PartSet_PreviewSketchPlane::reconfigureGridUsingSketch(std::shared_ptr theSketch) +{ + static const auto MARKER_COLOR = Quantity_Color(Quantity_NameOfColor::Quantity_NOC_TOMATO); + static const auto MARKER_SIZE = 2.0; + static const opencascade::handle MARKER = new Graphic3d_AspectMarker3d(Aspect_TypeOfMarker::Aspect_TOM_BALL, MARKER_COLOR, MARKER_SIZE); + static const double MARGINS = 1.005; // Makes grid surface bigger to display marginal grid lines. + + if (!isValid()) + return myGridType == PartSet_Tools::SketchPlaneGridType::No; + + const auto aV3DViewer = getV3DViewer(); + if (!aV3DViewer) { + SketchAccessoryDbg("can't retrieve V3d_Viewer to configure grid."); return false; + } - Bnd_Box aBox; - int aNumberOfSubs = theSketch->numberOfSubs(); - for (int aSubFeatureId = 0; aSubFeatureId < aNumberOfSubs; aSubFeatureId++) { - FeaturePtr aFeature = theSketch->subFeature(aSubFeatureId); - if (!aFeature.get()) - continue; - - std::list aResults = aFeature->results(); - std::list::const_iterator aResultIt; - for (aResultIt = aResults.begin(); aResultIt != aResults.end(); ++aResultIt) { - ResultPtr aResult = *aResultIt; - std::shared_ptr aShapePtr = aResult->shape(); - if (aShapePtr.get()) { - TopoDS_Shape aShape = aShapePtr->impl(); - if (aShape.IsNull()) - continue; - BRepBndLib::Add(aShape, aBox); - } + const auto dimensions = getDimensions(); + + if (theSketch) { + { // Rectangular grid. + const double stepXAttrVal = PartSet_Tools::sketchPlaneRectangularGridStepX(theSketch)->value(); + myRectangularGridSteps.first = stepXAttrVal < 0 ? dimensions.first / PartSet_PreviewSketchPlane::DEFAULT_RELATIVE_STEP_INVERSE : stepXAttrVal; + + const double stepYAttrVal = PartSet_Tools::sketchPlaneRectangularGridStepY(theSketch)->value(); + myRectangularGridSteps.second = stepYAttrVal < 0 ? dimensions.second / PartSet_PreviewSketchPlane::DEFAULT_RELATIVE_STEP_INVERSE : stepYAttrVal; + + myRectangularGridTransOffset.first = PartSet_Tools::sketchPlaneRectangularGridOffsetX(theSketch)->value(); + myRectangularGridTransOffset.second = PartSet_Tools::sketchPlaneRectangularGridOffsetY(theSketch)->value(); + myRectangularGridOffsetAngle = PartSet_Tools::sketchPlaneRectangularGridOffsetAngle(theSketch)->value(); + } + + { // Circular grid + myCircularGridTransOffset.first = PartSet_Tools::sketchPlaneCircularGridOffsetX(theSketch)->value(); + myCircularGridTransOffset.second = PartSet_Tools::sketchPlaneCircularGridOffsetY(theSketch)->value(); + myCircularGridOffsetAngle = PartSet_Tools::sketchPlaneCircularGridOffsetAngle(theSketch)->value(); + + const double stepRAttrVal = PartSet_Tools::sketchPlaneCircularGridStepR(theSketch)->value(); + myCircularGridRadialStep = stepRAttrVal < 0 ? + std::min(dimensions.first, dimensions.second) / PartSet_PreviewSketchPlane::DEFAULT_RELATIVE_STEP_INVERSE * 2 : + stepRAttrVal; + + const int aNAS = PartSet_Tools::sketchPlaneCircularGridNumOfAngSegments(theSketch)->value(); + myCircularGridNumOfAngularSections = aNAS <= 0 ? PartSet_PreviewSketchPlane::DEFAULT_NUM_OF_ANGULAR_SECTIONS : aNAS; + } + + myGridType = PartSet_Tools::getSketchPlaneGridType(theSketch); + } + + aV3DViewer->SetGridEcho(MARKER); + + if (myGridType == PartSet_Tools::SketchPlaneGridType::Rectangular) { + static const double MAX = std::numeric_limits::max(); + + auto steps = myRectangularGridSteps; + if (steps.first <= Precision::Confusion()) + steps.first = MAX; + + if (steps.second <= Precision::Confusion()) + steps.second = MAX; + + // Chi and Upsilon are synonyms for X' and Y'. + const double offsetChi = steps.first < MAX ? std::remainder(myRectangularGridTransOffset.first , steps.first) : myRectangularGridTransOffset.first; + const double offsetUpsilon = steps.second < MAX ? std::remainder(myRectangularGridTransOffset.second, steps.second) : myRectangularGridTransOffset.second; + const double offsetAngleRad = myRectangularGridOffsetAngle * PI / 180; + + aV3DViewer->SetPrivilegedPlane(mySketchCS); + aV3DViewer->SetRectangularGridValues(offsetChi, offsetUpsilon, steps.first, steps.second, offsetAngleRad); +#define V3DVIEWER_RECTANGULAR_GRID_OFFSET_BUGS +#ifdef V3DVIEWER_RECTANGULAR_GRID_OFFSET_BUGS + /* + How grid offsets should work? Imagine an invisible rectangular frame - only those elements of the the grid must be visible, + which are inside of the frame. + If an offset is adjusted, the frame remains intact and stays in place, but elements of the grid are shifted/rotated + appropriate to the offset direction and value. + The dimensions of the frame are ones of 2D bounding box of the sketch face. + + V3d_Viewer::SetRectangularGridGraphicValues adjusts grid elements' position, but also translates/rotates the frame of the grid! + Following equations enlarge grid dimensions, so that the entire surface of the 2D BBox is covered with grid. + */ + + const auto outerDims = PartSet_PreviewSketchPlane::dimsOfRectangleFittingRectangle( + dimensions.first, dimensions.second, -offsetAngleRad, offsetChi, offsetUpsilon + ); + + aV3DViewer->SetRectangularGridGraphicValues(outerDims.first / 2 * MARGINS, outerDims.second / 2 * MARGINS, 0); + + { + const int chiDim = outerDims.first / 2 * MARGINS / steps.first >= 1 ? 1 : 0; + const int upsilonDim = outerDims.second / 2 * MARGINS / steps.second >= 1 ? 1 : 0; + myRectangularGridDimOfNodeSpace = chiDim + upsilonDim; + } +#else + aV3DViewer->SetRectangularGridGraphicValues(dimensions.first / 2 * MARGINS, dimensions.second / 2 * MARGINS, 0); + + { + const int chiDim = dimensions.first / 2 * MARGINS / steps.first >= 1 ? 1 : 0; + const int upsilonDim = dimensions.second / 2 * MARGINS / steps.second >= 1 ? 1 : 0; + myRectangularGridDimOfNodeSpace = chiDim + upsilonDim; + } +#endif + } + else if (myGridType == PartSet_Tools::SketchPlaneGridType::Circular) { + const int& aNAS = myCircularGridNumOfAngularSections; + const int divisionNumber = aNAS == 1 ? 0 : aNAS / 2 + aNAS % 2; // V3d_Viewer::SetCircularGridValues is weird. + + aV3DViewer->SetPrivilegedPlane(mySketchCS); + aV3DViewer->SetCircularGridValues( + myCircularGridTransOffset.first, myCircularGridTransOffset.second, + myCircularGridRadialStep > Precision::Confusion() ? myCircularGridRadialStep : std::numeric_limits::max(), + divisionNumber, + myCircularGridOffsetAngle * PI / 180 + ); + + /* Circles of circular grid are not circles, but regular polygons and cover less surface than circles. */ + const double R = getCircularGridRaduis(dimensions); + int numOfEdges = aNAS < 12 ? 12 : aNAS + aNAS % 2; + const double polyR = R / std::cos(PI / numOfEdges) * MARGINS; + aV3DViewer->SetCircularGridGraphicValues(polyR, 0); + + { + const int angularDim = divisionNumber > 2 ? 2 : 1; + const int radialDim = myCircularGridRadialStep > Precision::Confusion() && polyR / myCircularGridRadialStep >=1 ? 1 : 0; + myRectangularGridDimOfNodeSpace = angularDim * radialDim; } } - if (aBox.IsVoid()) - return 0; - double aXmin, aXmax, anYmin, anYmax, aZmin, aZmax; - aBox.Get(aXmin, anYmin, aZmin, aXmax, anYmax, aZmax); + if (theSketch) { + if (myGridType == PartSet_Tools::SketchPlaneGridType::No) + aV3DViewer->DeactivateGrid(); + else { + Aspect_GridType type = myGridType == PartSet_Tools::SketchPlaneGridType::Rectangular ? + Aspect_GridType::Aspect_GT_Rectangular : Aspect_GridType::Aspect_GT_Circular; - theSizeOfView = maximumSize(aXmin, anYmin, aZmin, aXmax, anYmax, aZmax); - if (theSizeOfView > 0) { - gp_Pnt aCentre(aXmax-fabs(aXmax-aXmin)/2., anYmax-fabs(anYmax-anYmin)/2., - aZmax - fabs(aZmax-aZmin)/2.); - theCentralPnt = std::shared_ptr(new GeomAPI_Pnt(aCentre.X(), aCentre.Y(), - aCentre.Z())); + aV3DViewer->ActivateGrid(type, myGridDrawMode); + } } + + return true; +} + +bool PartSet_PreviewSketchPlane::showAxes(bool theShow) +{ + if (!isValid()) + return !theShow; + + XGUI_Displayer* const displayer = mySketcherMgr->workshop()->displayer(); + if (theShow) + displayer->displayAIS(myTrihedron, false /*load object in selection*/, 1 /*shaded*/, false /*update viewer*/); + else + displayer->eraseAIS(myTrihedron, false /*update viewer*/); + + myShowTrihedron = theShow; return true; } -void PartSet_PreviewSketchPlane::setSizeOfView(double theSizeOfView, bool isUseSizeOfView, - const std::shared_ptr& theCentralPoint) +bool PartSet_PreviewSketchPlane::showSubstrate(bool theShow) { - mySizeOfView = theSizeOfView; - myIsUseSizeOfView = isUseSizeOfView; + if (!isValid()) + return !theShow; - myViewCentralPoint = theCentralPoint; + XGUI_Displayer* const displayer = mySketcherMgr->workshop()->displayer(); + if (theShow) + displayer->displayAIS(mySubstrate, false /*load object in selection*/, 1 /*shaded*/, false /*update viewer*/); + else + displayer->eraseAIS(mySubstrate, false /*update viewer*/); + + myShowSubstrate = theShow; + return true; } -AISObjectPtr PartSet_PreviewSketchPlane::createPreviewPlane() +bool PartSet_PreviewSketchPlane::setGridType(PartSet_Tools::SketchPlaneGridType::Enum theType) { - if (myPlane.get()) { - myPlane->createShape(myShape); - return myPlane; + if (!isValid()) + return theType == PartSet_Tools::SketchPlaneGridType::No; + + const auto aV3DViewer = getV3DViewer(); + if (!aV3DViewer) { + SketchAccessoryDbg("can't retrieve V3d_Viewer to show/hide grid."); + return false; } + + myGridType = theType; + reconfigureGrid(); + + if (myGridType == PartSet_Tools::SketchPlaneGridType::No) + aV3DViewer->DeactivateGrid(); else { - AISObjectPtr aAIS = AISObjectPtr(new GeomAPI_AISObject()); - aAIS->createShape(myShape); - std::vector aColor = Config_PropManager::color("Visualization", "sketch_preview_plane"); - if (aColor.size() == 3) - aAIS->setColor(aColor[0], aColor[1], aColor[2]); - aAIS->setTransparensy(0.8); - - int aDispMode = 1; // shading - Handle(AIS_InteractiveObject) anAISIO = aAIS->impl(); - if (!anAISIO.IsNull()) { - //anAISIO->SetInfiniteState(Standard_True); - anAISIO->Attributes()->SetFaceBoundaryDraw( Standard_True ); - anAISIO->SetDisplayMode(aDispMode); + Aspect_GridType type = myGridType == PartSet_Tools::SketchPlaneGridType::Rectangular ? + Aspect_GridType::Aspect_GT_Rectangular : Aspect_GridType::Aspect_GT_Circular; + + aV3DViewer->ActivateGrid(type, myGridDrawMode); + } + + return true; +} + +void PartSet_PreviewSketchPlane::hideAll() +{ + showAxes(false); + showSubstrate(false); + setGridType(PartSet_Tools::SketchPlaneGridType::No); +} + +std::pair PartSet_PreviewSketchPlane::getDimensions() const +{ + const double defaultSize = getDefaultSize(); + // Chi and Upsilon are synonyms for X' and Y'. + const bool chiSizeOk = mySketchDimensions.first > Precision::Confusion(); + const bool upsilonSizeOk = mySketchDimensions.second > Precision::Confusion(); + + return std::pair( + chiSizeOk ? mySketchDimensions.first : upsilonSizeOk ? mySketchDimensions.second : defaultSize, + upsilonSizeOk ? mySketchDimensions.second : chiSizeOk ? mySketchDimensions.first : defaultSize + ); +} + +void PartSet_PreviewSketchPlane::resetRectangularGrid() +{ + const auto dimensions = getDimensions(); + myRectangularGridSteps.first = dimensions.first / PartSet_PreviewSketchPlane::DEFAULT_RELATIVE_STEP_INVERSE; + myRectangularGridSteps.second = dimensions.second / PartSet_PreviewSketchPlane::DEFAULT_RELATIVE_STEP_INVERSE; + myRectangularGridOffsetAngle = 0; + myRectangularGridTransOffset.first = 0; + myRectangularGridTransOffset.second = 0; +} + +void PartSet_PreviewSketchPlane::setRectangularGridStepX(double theStepX) +{ + myRectangularGridSteps.first = theStepX; +} + +void PartSet_PreviewSketchPlane::setRectangularGridStepY(double theStepY) +{ + myRectangularGridSteps.second = theStepY; +} + +std::pair, double> PartSet_PreviewSketchPlane::getRectangularGridOffsets() const +{ + return std::pair, double>(myRectangularGridTransOffset, myRectangularGridOffsetAngle); +} + +void PartSet_PreviewSketchPlane::setRectangularGridOffsetX(double theOffsetX) +{ + myRectangularGridTransOffset.first = theOffsetX; +} + +void PartSet_PreviewSketchPlane::setRectangularGridOffsetY(double theOffsetY) +{ + myRectangularGridTransOffset.second = theOffsetY; +} + +void PartSet_PreviewSketchPlane::setRectangularGridOffsetA(double theOffsetAngle) +{ + myRectangularGridOffsetAngle = theOffsetAngle; +} + +void PartSet_PreviewSketchPlane::resetCircularGrid() +{ + const auto d = getDimensions(); + double R = std::sqrt(std::pow(d.first, 2) + std::pow(d.second, 2)) / 2; + + myCircularGridRadialStep = R / PartSet_PreviewSketchPlane::DEFAULT_RELATIVE_STEP_INVERSE; + myCircularGridNumOfAngularSections = PartSet_PreviewSketchPlane::DEFAULT_NUM_OF_ANGULAR_SECTIONS; + myCircularGridOffsetAngle = 0; + myCircularGridTransOffset.first = 0; + myCircularGridTransOffset.second = 0; +} + +void PartSet_PreviewSketchPlane::setCircularGridRadialStep(double theStep) +{ + myCircularGridRadialStep = theStep; +} + +void PartSet_PreviewSketchPlane::setCircularGridNumOfAngularSegments(int theNum) +{ + myCircularGridNumOfAngularSections = theNum; +} + +std::pair PartSet_PreviewSketchPlane::getCircularGrid_dR_and_NAS() const +{ + return std::pair(myCircularGridRadialStep, myCircularGridNumOfAngularSections); +} + +void PartSet_PreviewSketchPlane::setCircularGridOffsetX(double theOffset) +{ + myCircularGridTransOffset.first = theOffset; +} + +void PartSet_PreviewSketchPlane::setCircularGridOffsetY(double theOffset) +{ + myCircularGridTransOffset.second = theOffset; +} + +void PartSet_PreviewSketchPlane::setCircularGridOffsetA(double theOffset) +{ + myCircularGridOffsetAngle = theOffset; +} + +std::pair, double> PartSet_PreviewSketchPlane::getCircularGridOffsets() const +{ + return std::pair, double>(myCircularGridTransOffset, myCircularGridOffsetAngle); +} + +void PartSet_PreviewSketchPlane::configureTrihedron() +{ + const opencascade::handle aGeom_Plane = new Geom_Plane(mySketchCS); + const auto dimensions = getDimensions(); + myTrihedron->SetComponent(aGeom_Plane); + myTrihedron->SetLength(std::min(dimensions.first, dimensions.second) / 2); +} + +void PartSet_PreviewSketchPlane::initSubstrate(std::shared_ptr theFace) +{ + if (!theFace) { + const auto dimensions = getDimensions(); + theFace = GeomAlgoAPI_FaceBuilder::planarRectangularFace(mySketchCS, dimensions.first, dimensions.second); + } + + AISObjectPtr aAIS = AISObjectPtr(new GeomAPI_AISObject()); + aAIS->createShape(theFace); + std::vector aColor = Config_PropManager::color("Visualization", "sketch_preview_plane"); + if (aColor.size() == 3) + aAIS->setColor(aColor[0], aColor[1], aColor[2]); + + aAIS->setTransparensy(0.8); + + int aDispMode = 1; // shading + Handle(AIS_InteractiveObject) anAISIO = aAIS->impl(); + if (!anAISIO.IsNull()) { + anAISIO->Attributes()->SetFaceBoundaryDraw( Standard_True ); + anAISIO->SetDisplayMode(aDispMode); + } + mySubstrate = std::move(aAIS); +} + +opencascade::handle PartSet_PreviewSketchPlane::getV3DViewer() const +{ + const ModuleBase_IViewer* const viewer = mySketcherMgr->workshop()->salomeViewer(); + if (!viewer) + return nullptr; + + return viewer->v3dViewer(); +} + +bool PartSet_PreviewSketchPlane::isValid() const +{ + return myValid; +} + +void PartSet_PreviewSketchPlane::setInvalid() +{ + myValid = false; + + XGUI_Displayer* const displayer = mySketcherMgr->workshop()->displayer(); + + displayer->eraseAIS(myTrihedron, false /*update viewer*/); + myShowTrihedron = false; + + if (mySubstrate && myShowSubstrate) { + displayer->eraseAIS(mySubstrate, false /*update viewer*/); + myShowSubstrate = false; + } + + if (myGridType != PartSet_Tools::SketchPlaneGridType::No) { + ModuleBase_IViewer* aViewer = mySketcherMgr->workshop()->salomeViewer(); + const auto aV3DViewer = getV3DViewer(); + if (aV3DViewer) { + aV3DViewer->DeactivateGrid(); + myGridType = PartSet_Tools::SketchPlaneGridType::No; } - return aAIS; + else { + SketchAccessoryDbg("can't retrieve V3d_Viewer to deactivate grid."); + } + } +} + +double PartSet_PreviewSketchPlane::getDefaultSize() const { + const double defaultSize = mySketchDefaultSize; + if (defaultSize <= Precision::Confusion()) + return PartSet_PreviewSketchPlane::defaultSketchSize(); + + return defaultSize; +} + +double PartSet_PreviewSketchPlane::getCircularGridRaduis(const std::pair& theBBox2Dimensions) const +{ + // Find big enough R to cover entire 2D-bounding of sketch face. + const double& Cx = myCircularGridTransOffset.first; + const double& Cy = myCircularGridTransOffset.second; + const double& W = theBBox2Dimensions.first / 2; + const double& H = theBBox2Dimensions.second / 2; + + /* Distances from center of circular grid to vertices of sketch face 2D rectangular box. */ + double squareDistances[4]; + squareDistances[0] = std::pow(Cx - W, 2) + std::pow(Cy - H, 2); + squareDistances[1] = std::pow(Cx + W, 2) + std::pow(Cy - H, 2); + squareDistances[2] = std::pow(Cx - W, 2) + std::pow(Cy + H, 2); + squareDistances[3] = std::pow(Cx + W, 2) + std::pow(Cy + H, 2); + double R2 = squareDistances[3]; + for (int i = 0; i < 3; i++) { + if (squareDistances[i] > R2) + R2 = squareDistances[i]; } + + return std::sqrt(R2); } + +/*static*/ double PartSet_PreviewSketchPlane::defaultSketchSize() +{ + const double defaultSize = Config_PropManager::real(SKETCH_TAB_NAME, "planes_size"); + if (defaultSize <= Precision::Confusion()) + return PartSet_PreviewSketchPlane::DEFAULT_SKETCH_SIZE; + + return defaultSize; +} + +/*static*/ const double PartSet_PreviewSketchPlane::DEFAULT_SKETCH_SIZE = 200; +/*static*/ const double PartSet_PreviewSketchPlane::DEFAULT_RELATIVE_STEP_INVERSE = 20; +/*static*/ const int PartSet_PreviewSketchPlane::DEFAULT_NUM_OF_ANGULAR_SECTIONS = 18; +/*static*/ const int PartSet_PreviewSketchPlane::SNAP_PROXIMITY_P = 10; + +/*static*/ std::pair PartSet_PreviewSketchPlane::dimsOfRectangleFittingRectangle( + double wInner, double hInner, double a, double cX, double cY +) { + const auto center = gp_Pnt2d(cX, cY); + const auto dirX = gp_Dir2d(std::cos(a), std::sin(a)); + const gp_Ax2d csOuter = gp_Ax2d(center, dirX); + + gp_Pnt2d vertexInner[4] = + {gp_Pnt2d(-wInner/2, -hInner/2), gp_Pnt2d(-wInner/2, hInner/2), gp_Pnt2d(wInner/2, -hInner/2), gp_Pnt2d(wInner/2, hInner/2)}; + + gp_Trsf2d trsf; + trsf.SetTransformation(csOuter); + + double maxXP = 0, minXN = 0, maxYP = 0, minYN = 0; + for (int i = 0; i < 4; i++) { + vertexInner[i].Transform(trsf); + const auto& v = vertexInner[i]; + if (v.X() > maxXP) + maxXP = v.X(); + else if (v.X() < minXN) + minXN = v.X(); + + if (v.Y() > maxYP) + maxYP = v.Y(); + else if (v.Y() < minYN) + minYN = v.Y(); + } + return std::pair(std::max(maxXP, -minXN) * 2, std::max(maxYP, -minYN) * 2); +} \ No newline at end of file diff --git a/src/PartSet/PartSet_PreviewSketchPlane.h b/src/PartSet/PartSet_PreviewSketchPlane.h index ae0784bae..ff76ddc9f 100644 --- a/src/PartSet/PartSet_PreviewSketchPlane.h +++ b/src/PartSet/PartSet_PreviewSketchPlane.h @@ -20,89 +20,194 @@ #ifndef PartSet_PreviewSketchPlane_H #define PartSet_PreviewSketchPlane_H -#include +#include +#include +#include +#include +#include +#include +#include -class GeomAPI_AISObject; -class GeomAPI_Shape; +#include "PartSet_Tools.h" -class ModuleBase_IWorkshop; +class PartSet_SketcherMgr; class ModelAPI_CompositeFeature; +class GeomAPI_AISObject; +class GeomAPI_Face; +class AIS_PlaneTrihedron; +class V3d_Viewer; +class QString; +class gp_Trsf; -#include +// #define SKETCH_ACCESSORY_DBG; /** * \class PartSet_PreviewSketchPlane * \ingroup Modules -* A class to show/hide sketch preview plane +* Visualization of 2D-bluebrint' accessories: translucent rectangular substrate, basis axes, grid. +* All methods do not modify sketch data, unless otherwise is stated. */ class PartSet_PreviewSketchPlane { public: - /// Constructor - PartSet_PreviewSketchPlane(); - - ~PartSet_PreviewSketchPlane() {}; - - /// Erase preview planes - /// \param theWorkshop the application workshop - /// \param isClearPlane flag whether the plane, origin and normal should be nullified - void eraseSketchPlane(ModuleBase_IWorkshop* theWorkshop, const bool isClearPlane = true); - - /// Show preview planes - /// \param theSketch source sketch to initialize plane - /// \param theWorkshop the application workshop - void createSketchPlane(const std::shared_ptr& theSketch, - ModuleBase_IWorkshop* theWorkshop); - - /// Returns bounding box size covered the sketch sub-elements. - /// If the sketch uses extenal face, it will not have default size and returns false. - /// \param theSketch sources sketch - /// \param [out] theSizeOfView maximum value in X, Y or Z direction - /// \param theCentralPoint central point of the sketch sub features - /// \return boolean value - bool getDefaultSizeOfView(const std::shared_ptr& theSketch, - double& theSizeOfView, - std::shared_ptr& theCentralPnt); - - /// Returns whether custom size of view is set - /// \return boolean value - bool isUseSizeOfView() const { return myIsUseSizeOfView; } - - /// Sets the size of default created face - /// \param theSizeOfView value - /// \param isUseSizeOfView state whether the size should be used - void setSizeOfView(double theSizeOfView, bool isUseSizeOfView, - const std::shared_ptr& theCentralPoint = std::shared_ptr()); - - /// Returns True if the plane preview is already created - bool isPlaneCreated() const { - return myPlane.get(); - } - - /// Returns current state of the plane preview visibility - bool isDisplayed() const { return myPreviewIsDisplayed; } - - /// Displays preview planes - /// \param theWorkshop the application workshop - void displaySketchPlane(ModuleBase_IWorkshop* theWorkshop); - - /// Nullyfies current plane preview object. - /// Important: Before call of this function the plane has to be erased from viewer - void clearPlanePreview(); + inline static bool SketchAccessoryDbg() + { +#ifdef SKETCH_ACCESSORY_DBG + return true; +#else + return false; +#endif + }; + + static bool SketchAccessoryDbg(const QString& theString); + static bool SketchAccessoryDbg(const char* theString); + static bool SketchAccessoryDbg(const char* theCSDescription, const gp_Ax3& theCS); + static bool SketchAccessoryDbg(const char* theCSDescription, const gp_Pnt& thePoint); + static bool SketchAccessoryDbg(const char* theCSDescription, const gp_Trsf& theMatrix); + + enum GridSnappingMode { + Off, + SnapAnyway, + SnapInProximity + }; + + /*! \param theManager must not be nullptr. */ + PartSet_PreviewSketchPlane(PartSet_SketcherMgr* theManager); + PartSet_PreviewSketchPlane(const PartSet_PreviewSketchPlane&) = delete; + PartSet_PreviewSketchPlane& operator=(const PartSet_PreviewSketchPlane&) = delete; + ~PartSet_PreviewSketchPlane() = default; + + void savePreferencesIntoSketchData(std::shared_ptr theSketch) const; + void saveRectangularGridPreferencesIntoSketchData(std::shared_ptr theSketch) const; + void saveCircularGridPreferencesIntoSketchData(std::shared_ptr theSketch) const; + + /*! \brief Updates CS (coordinate system), dimensions and accessories' configuration. */ + void setCSAndSize(const gp_Ax3& theCS, double theSize); + + /*! \brief Updates CS (coordinate system), dimensions and accessories' configuration. + Updates visibility of accessories - visibility preferences are also retrieved from theSketch data. + If sketch data contains unitialized grid steps, the method assigns default ones. + For rectangular grid default step is a fraction of corresponding dimension. + \returns true, on success (if theSketch is valid). */ + bool setAllUsingSketch(std::shared_ptr theSketch); + + /*! \brief Call it, after any preference of grid, except grid type, is changed using methods of this instance. + Updates grid configuration. Does not affect visibility of grid. + \returns true, if grid is successfuly configured. */ + bool reconfigureGrid(); + + /*! \brief Call it, after any preference of grid is changed in theSketch data directly. + Updates grid configuration. Updates grid visibility - preference is also retrieved from theSketch data. + If sketch data contains unitialized grid steps, the method assigns default ones. + For rectangular grid default step is a fraction of corresponding dimension. + \returns true, if grid is successfuly configured. */ + bool reconfigureGridUsingSketch(std::shared_ptr theSketch); + + /*! \returns true on success. */ + bool showAxes(bool theShow); + /*! \returns true on success. */ + bool showSubstrate(bool theShow); + /*! \brief Changes grid type and reconfigures grid. Shows grid, if theType != SketchPlaneGridType::No. + \returns true on success. */ + bool setGridType(PartSet_Tools::SketchPlaneGridType::Enum theType); + + PartSet_Tools::SketchPlaneGridType::Enum getGridType() const { return myGridType; } + + /*! \brief Hides all accessories. */ + void hideAll(); + + /*! \brief Substrate dimensions. Substrate dimension coincides with one of the sketch face, if the latter is non-zero. */ + std::pair getDimensions() const; + + bool isShowAxes() const { return myShowTrihedron; } + bool isShowSubstrate() const { return myShowSubstrate; } + bool isShowGrid() const { return myGridType != PartSet_Tools::SketchPlaneGridType::No; } + + GridSnappingMode getGridSnappingMode() const { return mySnappingMode; }; + void setGridSnappingMode(GridSnappingMode theMode) { mySnappingMode = theMode; }; + + /*! \brief Sets default steps and zero offsets. */ + void resetRectangularGrid(); + + void setRectangularGridStepX(double theStepX); + void setRectangularGridStepY(double theStepY); + std::pair getRectangularGridSteps() const { return myRectangularGridSteps; } + + void setRectangularGridOffsetX(double theOffsetX); + void setRectangularGridOffsetY(double theOffsetY); + void setRectangularGridOffsetA(double theOffsetAngle); + std::pair, double> getRectangularGridOffsets() const; + int getRectangularGridDimOfNodeSpace() const { return myRectangularGridDimOfNodeSpace; } + + /*! \brief Sets default steps and zero offsets. */ + void resetCircularGrid(); + + void setCircularGridRadialStep(double theStep); + void setCircularGridNumOfAngularSegments(int theNum); + std::pair getCircularGrid_dR_and_NAS() const; + + void setCircularGridOffsetX(double theOffset); + void setCircularGridOffsetY(double theOffset); + void setCircularGridOffsetA(double theOffset); + std::pair, double> getCircularGridOffsets() const; + int getCircularGridDimOfNodeSpace() const { return myCircularGridDimOfNodeSpace; } private: - /// Create a square face by parameters - std::shared_ptr createPreviewPlane(); + void configureTrihedron(); + void initSubstrate(std::shared_ptr theFace); + + opencascade::handle getV3DViewer() const; + bool isValid() const; + void setInvalid(); + + /*! \returns Default size from sketch data (if > 0), or the default size from Config_PropManager (if > 0), or DEFAULT_SKETCH_SIZE. */ + double getDefaultSize() const; + + double getCircularGridRaduis(const std::pair& theBBox2Dimensions) const; + + /*! \returns Dimensions of outer rectangle, center of which is shifted from center of inner rectangle by (cX, cY); + outer rectangle is rotated by angle (counterclockwise is positive) relative to inner one. */ + static std::pair dimsOfRectangleFittingRectangle(double wInner, double hInner, double angle, double cX, double cY); + +public: + /*! \returns Default size from Config_PropManager (if > 0), or DEFAULT_SKETCH_SIZE. */ + static double defaultSketchSize(); + + static const double DEFAULT_RELATIVE_STEP_INVERSE; + static const int DEFAULT_NUM_OF_ANGULAR_SECTIONS; + static const int SNAP_PROXIMITY_P; /// Distance [pixel], at which snapping to grid engages in SnapInProximity mode. private: - bool myPreviewIsDisplayed; - std::shared_ptr myPlane; //! visualized presentation - std::shared_ptr myShape; //! current shape to be displayed - std::shared_ptr myViewCentralPoint; //! central point of the default view - - double mySizeOfView; //! size that should be used by creating a default face - bool myIsUseSizeOfView; //! state if the size is custom or from preferences - std::shared_ptr myViewOrigin; //! origin point of sketch if default view is used + /** Is used to create default substrate, if plane size is 0 both in the sketch data and in Config_PropManager. */ + static const double DEFAULT_SKETCH_SIZE; + + const PartSet_SketcherMgr* const mySketcherMgr; + + bool myValid; + gp_Ax3 mySketchCS; + std::pair mySketchDimensions; // Width and height of sketch bounding box, aligned with sketch axes. + double mySketchDefaultSize; + + bool myShowTrihedron; + opencascade::handle myTrihedron; // Visualization of sketch axes and origin. + + bool myShowSubstrate; + std::shared_ptr mySubstrate; // Translucent substrate. + + PartSet_Tools::SketchPlaneGridType::Enum myGridType; + Aspect_GridDrawMode myGridDrawMode; + GridSnappingMode mySnappingMode; + + std::pair myRectangularGridSteps; + std::pair myRectangularGridTransOffset; + double myRectangularGridOffsetAngle; + int myRectangularGridDimOfNodeSpace; // Minimal dimension of space which can fit displayed grid nodes. + + double myCircularGridRadialStep; + int myCircularGridNumOfAngularSections; + std::pair myCircularGridTransOffset; + double myCircularGridOffsetAngle; + int myCircularGridDimOfNodeSpace; // Minimal dimension of space which can fit displayed grid nodes. }; #endif \ No newline at end of file diff --git a/src/PartSet/PartSet_SketcherMgr.cpp b/src/PartSet/PartSet_SketcherMgr.cpp index 1a34c5127..6b95b8a53 100644 --- a/src/PartSet/PartSet_SketcherMgr.cpp +++ b/src/PartSet/PartSet_SketcherMgr.cpp @@ -206,7 +206,7 @@ PartSet_SketcherMgr::PartSet_SketcherMgr(PartSet_Module* theModule) myIsConstraintsShown[PartSet_Tools::Dimensional] = true; myIsConstraintsShown[PartSet_Tools::Expressions] = false; - mySketchPlane = new PartSet_PreviewSketchPlane(); + mySketchPlane = new PartSet_PreviewSketchPlane(this); registerSelectionFilter(SF_SketchCirclePointFilter, new PartSet_CirclePointFilter(anIWorkshop)); registerSelectionFilter(SF_SketchPlaneFilter, new ModuleBase_ShapeInPlaneFilter()); @@ -535,13 +535,12 @@ void PartSet_SketcherMgr::onMouseReleased(ModuleBase_IViewWindow* theWnd, QMouse bool aWasDragging = myIsDragging; myIsDragging = false; - if (myModule->sketchReentranceMgr()->processMouseReleased(theWnd, theEvent)) { + if (myModule->sketchReentranceMgr()->processMouseReleased(theWnd, theEvent)) return; - } + // if mouse is pressed when it was over view and at release the mouse is out of view, do nothing - if (!myIsMouseOverViewProcessed) { + if (!myIsMouseOverViewProcessed) return; - } ModuleBase_OperationFeature* aOp = dynamic_cast(getCurrentOperation()); @@ -555,9 +554,45 @@ void PartSet_SketcherMgr::onMouseReleased(ModuleBase_IViewWindow* theWnd, QMouse myNoDragMoving = !myNoDragMoving; else myNoDragMoving = false; + if (myNoDragMoving) return; else { + ModuleBase_OperationFeature* aOp = + dynamic_cast(getCurrentOperation()); + + bool isEditing = false; + if (aOp) { + isEditing = aOp->isEditOperation(); + bool aStartNoDragOperation = !aViewer->canDragByMouse() && isEditing; + if (aStartNoDragOperation || myNoDragMoving) { + // Process edit operation without dragging + if (myCurrentSelection.size() > 0) + myNoDragMoving = !myNoDragMoving; + else + myNoDragMoving = false; + if (myNoDragMoving) + return; + else { + restoreSelection(myCurrentSelection); + myCurrentSelection.clear(); + } + } + else { + if (isNestedSketchOperation(aOp)) { + // Only for sketcher operations + if (aWasDragging) { + if (myDragDone) { + /// the previous selection is lost by mouse release in the viewer(Select method), but + /// it is still stored in myCurrentSelection. So, it is possible to restore selection + /// It is important for drag(edit with mouse) of sketch entities. + restoreSelection(myCurrentSelection); + myCurrentSelection.clear(); + } + } + } + } + } restoreSelection(myCurrentSelection); myCurrentSelection.clear(); } @@ -580,6 +615,7 @@ void PartSet_SketcherMgr::onMouseReleased(ModuleBase_IViewWindow* theWnd, QMouse ModuleBase_ModelWidget* anActiveWidget = getActiveWidget(); PartSet_MouseProcessor* aProcessor = dynamic_cast(anActiveWidget); + if (aProcessor) { ModuleBase_ISelection* aSelection = aWorkshop->selection(); QList aPreSelected = aSelection->getHighlighted(); @@ -593,6 +629,7 @@ void PartSet_SketcherMgr::onMouseReleased(ModuleBase_IViewWindow* theWnd, QMouse QString aOpId = aOp->id(); if (aOpId == "Sketch") return; + QPoint aPnt(theEvent->x(), theEvent->y()); anActiveWidget = getActiveWidget(); if ((aPnt == myMousePoint) && anActiveWidget) { @@ -630,6 +667,7 @@ void PartSet_SketcherMgr::onMouseMoved(ModuleBase_IViewWindow* theWnd, QMouseEve XGUI_Displayer* aDisplayer = aConnector->workshop()->displayer(); if (isNestedCreateOperation(getCurrentOperation(), activeSketch())) { + #ifdef DRAGGING_DEBUG QTime t; t.start(); @@ -639,8 +677,9 @@ void PartSet_SketcherMgr::onMouseMoved(ModuleBase_IViewWindow* theWnd, QMouseEve // presentation. These widgets correct the feature attribute according to the mouse position ModuleBase_ModelWidget* anActiveWidget = myModule->activeWidget(); PartSet_MouseProcessor* aProcessor = dynamic_cast(anActiveWidget); - if (aProcessor) + if (aProcessor) { aProcessor->mouseMoved(theWnd, theEvent); + } if (!myIsMouseOverViewProcessed) { myIsMouseOverViewProcessed = true; @@ -667,10 +706,12 @@ void PartSet_SketcherMgr::onMouseMoved(ModuleBase_IViewWindow* theWnd, QMouseEve // mouse press signal in the viewer(it call Select for AIS context and the dragged objects are // deselected). This flag should be restored in the slot, processed the mouse release signal. ModuleBase_Operation* aCurrentOperation = getCurrentOperation(); - if (!aCurrentOperation) + if (!aCurrentOperation) { return; - if (isSketchOperation(aCurrentOperation)) + } + if (isSketchOperation(aCurrentOperation)) { return; // No edit operation activated + } #ifdef DRAGGING_DEBUG QTime t; @@ -745,6 +786,7 @@ void PartSet_SketcherMgr::onMouseMoved(ModuleBase_IViewWindow* theWnd, QMouseEve } } } + // the modified state of the current operation should be updated if there are features, which // were changed here if (isModified) { @@ -1093,16 +1135,9 @@ void PartSet_SketcherMgr::startSketch(ModuleBase_Operation* theOperation) // Display all sketcher sub-Objects myCurrentSketch = std::dynamic_pointer_cast(aFOperation->feature()); - double aSizeOfView = 0; - std::shared_ptr aCentralPoint; - // Reset size of view from previous launches - mySketchPlane->setSizeOfView(aSizeOfView, false, aCentralPoint); - if (aFOperation->isEditOperation() && - mySketchPlane->getDefaultSizeOfView(myCurrentSketch, aSizeOfView, aCentralPoint)) { - mySketchPlane->setSizeOfView(aSizeOfView, true, aCentralPoint); - } + if (aFOperation->isEditOperation()) + mySketchPlane->setAllUsingSketch(myCurrentSketch); - mySketchPlane->createSketchPlane(myCurrentSketch, myModule->workshop()); XGUI_ModuleConnector* aConnector = dynamic_cast(myModule->workshop()); // Hide sketcher result @@ -1225,7 +1260,7 @@ void PartSet_SketcherMgr::stopSketch(ModuleBase_Operation* theOperation) XGUI_Displayer* aDisplayer = aConnector->workshop()->displayer(); // The sketch was aborted myCurrentSketch = CompositeFeaturePtr(); - mySketchPlane->eraseSketchPlane(myModule->workshop()); + mySketchPlane->hideAll(); // Erase all sketcher objects QObjectPtrList aObjects = aDisplayer->displayedObjects(); @@ -1267,7 +1302,7 @@ void PartSet_SketcherMgr::stopSketch(ModuleBase_Operation* theOperation) myCurrentSketch->setDisplayed(true); myCurrentSketch = CompositeFeaturePtr(); - mySketchPlane->eraseSketchPlane(myModule->workshop()); + mySketchPlane->hideAll(); Events_Loop::loop()->flush(aDispEvent); } diff --git a/src/PartSet/PartSet_SketcherMgr.h b/src/PartSet/PartSet_SketcherMgr.h index 12b9f4fce..312021601 100644 --- a/src/PartSet/PartSet_SketcherMgr.h +++ b/src/PartSet/PartSet_SketcherMgr.h @@ -487,6 +487,8 @@ private: std::vector colorOfObject(const ObjectPtr& theObject, const FeaturePtr& aFeature, bool isConstruction) const; + friend class PartSet_PreviewSketchPlane; + private: PartSet_Module* myModule; PartSet_PreviewSketchPlane* mySketchPlane; // display/erase sketch plane on start/stop sketch diff --git a/src/PartSet/PartSet_Tools.cpp b/src/PartSet/PartSet_Tools.cpp index fd71ff50b..62b743ee4 100644 --- a/src/PartSet/PartSet_Tools.cpp +++ b/src/PartSet/PartSet_Tools.cpp @@ -23,6 +23,9 @@ #include #include +#include +#include +#include #include #include #include @@ -81,6 +84,8 @@ #include #include +#include +#include #include #include #include @@ -88,6 +93,7 @@ #include #include #include +#include #include #include #include @@ -324,6 +330,404 @@ void PartSet_Tools::nullifySketchPlane(CompositeFeaturePtr theSketch) anOrigin->reset(); } +std::pair PartSet_Tools::getWorldCSAt(const GeomAPI_Face& theFace, double U, double V) +{ + auto res = std::pair(false, gp_Ax3()); + try { + const TopoDS_Face& face = theFace.impl(); + if (face.IsNull()) + return res; + + const opencascade::handle surface = BRep_Tool::Surface(face); + if (surface.IsNull()) + return res; + + gp_Pnt origin; + gp_Vec dirX; + gp_Vec dirY; + surface->D1(U, V, origin, dirX, dirY); + const gp_Vec normal = dirX.Crossed(dirY); + + res.second = gp_Ax3(origin, gp_Dir(normal), gp_Dir(dirX)); + res.first = true; + return res; + } + catch (...) { + return res; + } +} + +std::pair PartSet_Tools::getWorldPointByUV(const GeomAPI_Face& theFace, double U, double V) +{ + auto res = std::pair(false, gp_Pnt()); + try { + const TopoDS_Face& face = theFace.impl(); + if (face.IsNull()) + return res; + + const opencascade::handle surface = BRep_Tool::Surface(face); + if (surface.IsNull()) + return res; + + res.second = surface->Value(U, V); + res.first = true; + return res; + } + catch (...) { + return res; + } +} + +std::pair PartSet_Tools::getBBoxAtCS(const GeomAPI_Shape& theShape, const gp_Ax3 theCS) +{ + auto res = std::pair(false, Bnd_Box()); + + { // Get BBox if theShape is GeomAPI_Pln, which has linear dependency of X and Y on U and V. + const auto face = theShape.face(); + if (face) { + const auto plane = face->getPlane(); + if (plane) { + try { + double UMax, UMin, VMax, VMin = 0; + face->optimalBounds(UMin, UMax, VMin, VMax); + std::pair UVPairs[4]; + UVPairs[0] = std::pair(UMin, VMin); + UVPairs[1] = std::pair(UMax, VMin); + UVPairs[2] = std::pair(UMin, VMax); + UVPairs[3] = std::pair(UMax, VMax); + + gp_Pnt points[4]; + bool success = true; + for (int idx = 0; idx < 4; idx++) { + std::tie(success, points[idx]) = getWorldPointByUV(*face, UVPairs[idx].first, UVPairs[idx].second); + if (!success) + return res; + } + + gp_Trsf trsf; + trsf.SetTransformation(theCS); + for (int idx = 0; idx < 4; idx++) { + res.second.Add(points[idx].Transformed(trsf)); + } + res.first = true; + return res; + } + catch(...) { + return res; + } + } // If Pln. + } // If Face. + } // If theShape is GeomAPI_Pln. + + { // TODO Fill aligned (oriented) bounding box instead of tranforming unaligned one. + double Xmin, Ymin, Zmin, Xmax, Ymax, Zmax = 0; + const bool success = theShape.computeSize(Xmin, Ymin, Zmin, Xmax, Ymax, Zmax); + if (!success) + return res; + + res.second.Update(Xmin, Ymin, Zmin, Xmax, Ymax, Zmax); + + gp_Trsf trsf; + trsf.SetTransformation(theCS); + res.second = res.second.Transformed(trsf); + + return res; + } + + return res; +} + +std::shared_ptr PartSet_Tools::sketchPlaneDefaultSize(CompositeFeaturePtr theSketch) +{ + auto aSize = std::dynamic_pointer_cast( + theSketch->data()->attribute(SketchPlugin_Sketch::DEFAULT_SIZE_ID()) + ); + + if (!aSize) { + aSize = std::dynamic_pointer_cast(theSketch->data()->addAttribute( + SketchPlugin_Sketch::DEFAULT_SIZE_ID(), + ModelAPI_AttributeDouble::typeId() + )); + + aSize->setIsArgument(false); + aSize->setValue(0); + } + + return aSize; +} + +std::shared_ptr PartSet_Tools::sketchPlaneAxesEnabled(CompositeFeaturePtr theSketch) +{ + auto isEnabled = std::dynamic_pointer_cast( + theSketch->data()->attribute(SketchPlugin_Sketch::AXES_ENABLED_ID()) + ); + + if (!isEnabled) { + isEnabled = std::dynamic_pointer_cast(theSketch->data()->addAttribute( + SketchPlugin_Sketch::AXES_ENABLED_ID(), + ModelAPI_AttributeBoolean::typeId() + )); + + isEnabled->setIsArgument(false); + isEnabled->setValue(true); + } + + return isEnabled; +} + +std::shared_ptr PartSet_Tools::sketchPlaneSubstrateEnabled(CompositeFeaturePtr theSketch) +{ + auto isEnabled = std::dynamic_pointer_cast( + theSketch->data()->attribute(SketchPlugin_Sketch::SUBSTRATE_ENABLED_ID()) + ); + + if (!isEnabled) { + isEnabled = std::dynamic_pointer_cast(theSketch->data()->addAttribute( + SketchPlugin_Sketch::SUBSTRATE_ENABLED_ID(), + ModelAPI_AttributeBoolean::typeId() + )); + + isEnabled->setIsArgument(false); + isEnabled->setValue(true); + } + + return isEnabled; +} + +std::shared_ptr PartSet_Tools::sketchPlaneGridType(CompositeFeaturePtr theSketch) +{ + auto gridType = std::dynamic_pointer_cast( + theSketch->data()->attribute(SketchPlugin_Sketch::CONSTRUCTION_GRID_TYPE_ID()) + ); + + if (!gridType) { + gridType = std::dynamic_pointer_cast(theSketch->data()->addAttribute( + SketchPlugin_Sketch::CONSTRUCTION_GRID_TYPE_ID(), + ModelAPI_AttributeString::typeId() + )); + + gridType->setIsArgument(false); + gridType->setValue(PartSet_Tools::SketchPlaneGridType::toString(PartSet_Tools::SketchPlaneGridType::No)); + } + + return gridType; +} + +std::string PartSet_Tools::SketchPlaneGridType::toString(Enum iType) +{ + if (iType >= SketchPlaneGridType::STRINGS.size()) + return SketchPlaneGridType::STRINGS[0]; + + return SketchPlaneGridType::STRINGS[iType]; +} + +PartSet_Tools::SketchPlaneGridType::Enum PartSet_Tools::SketchPlaneGridType::fromString(const std::string& iTypeString) +{ + for (int i = 0; i < SketchPlaneGridType::STRINGS.size(); i++) { + if (SketchPlaneGridType::STRINGS[i] == iTypeString) { + return SketchPlaneGridType::Enum(i); + } + } + return SketchPlaneGridType::Enum::No; +} + +const std::array PartSet_Tools::SketchPlaneGridType::STRINGS = {"", "Rectangular", "Circular"}; + +void PartSet_Tools::setSketchPlaneGridType(CompositeFeaturePtr theSketch, PartSet_Tools::SketchPlaneGridType::Enum theType) +{ + PartSet_Tools::sketchPlaneGridType(theSketch)->setValue(PartSet_Tools::SketchPlaneGridType::toString(theType)); +} + +PartSet_Tools::SketchPlaneGridType::Enum PartSet_Tools::getSketchPlaneGridType(CompositeFeaturePtr theSketch) +{ + return PartSet_Tools::SketchPlaneGridType::fromString(PartSet_Tools::sketchPlaneGridType(theSketch)->value()); +} + +std::shared_ptr PartSet_Tools::sketchPlaneRectangularGridStepX(CompositeFeaturePtr theSketch) { + auto aStep = std::dynamic_pointer_cast( + theSketch->data()->attribute(SketchPlugin_Sketch::RECTANGULAR_CONSTRUCTION_GRID_STEP_X_ID()) + ); + + if (!aStep) { + aStep = std::dynamic_pointer_cast(theSketch->data()->addAttribute( + SketchPlugin_Sketch::RECTANGULAR_CONSTRUCTION_GRID_STEP_X_ID(), + ModelAPI_AttributeDouble::typeId() + )); + + aStep->setIsArgument(false); + aStep->setValue(-1); + } + + return aStep; +} + +std::shared_ptr PartSet_Tools::sketchPlaneRectangularGridStepY(CompositeFeaturePtr theSketch) { + auto aStep = std::dynamic_pointer_cast( + theSketch->data()->attribute(SketchPlugin_Sketch::RECTANGULAR_CONSTRUCTION_GRID_STEP_Y_ID()) + ); + + if (!aStep) { + aStep = std::dynamic_pointer_cast(theSketch->data()->addAttribute( + SketchPlugin_Sketch::RECTANGULAR_CONSTRUCTION_GRID_STEP_Y_ID(), + ModelAPI_AttributeDouble::typeId() + )); + + aStep->setIsArgument(false); + aStep->setValue(-1); + } + + return aStep; +} + +std::shared_ptr PartSet_Tools::sketchPlaneRectangularGridOffsetAngle(CompositeFeaturePtr theSketch) +{ + auto aAngle = std::dynamic_pointer_cast( + theSketch->data()->attribute(SketchPlugin_Sketch::RECTANGULAR_CONSTRUCTION_GRID_OFFSET_ANGLE_ID()) + ); + + if (!aAngle) { + aAngle = std::dynamic_pointer_cast(theSketch->data()->addAttribute( + SketchPlugin_Sketch::RECTANGULAR_CONSTRUCTION_GRID_OFFSET_ANGLE_ID(), + ModelAPI_AttributeDouble::typeId() + )); + + aAngle->setIsArgument(false); + aAngle->setValue(0); + } + + return aAngle; +} + +std::shared_ptr PartSet_Tools::sketchPlaneRectangularGridOffsetX(CompositeFeaturePtr theSketch) { + auto aShift = std::dynamic_pointer_cast( + theSketch->data()->attribute(SketchPlugin_Sketch::RECTANGULAR_CONSTRUCTION_GRID_OFFSET_X_ID()) + ); + + if (!aShift) { + aShift = std::dynamic_pointer_cast(theSketch->data()->addAttribute( + SketchPlugin_Sketch::RECTANGULAR_CONSTRUCTION_GRID_OFFSET_X_ID(), + ModelAPI_AttributeDouble::typeId() + )); + + aShift->setIsArgument(false); + aShift->setValue(0); + } + + return aShift; +} + +std::shared_ptr PartSet_Tools::sketchPlaneRectangularGridOffsetY(CompositeFeaturePtr theSketch) { + auto aShift = std::dynamic_pointer_cast( + theSketch->data()->attribute(SketchPlugin_Sketch::RECTANGULAR_CONSTRUCTION_GRID_OFFSET_Y_ID()) + ); + + if (!aShift) { + aShift = std::dynamic_pointer_cast(theSketch->data()->addAttribute( + SketchPlugin_Sketch::RECTANGULAR_CONSTRUCTION_GRID_OFFSET_Y_ID(), + ModelAPI_AttributeDouble::typeId() + )); + + aShift->setIsArgument(false); + aShift->setValue(0); + } + + return aShift; +} + + +std::shared_ptr PartSet_Tools::sketchPlaneCircularGridStepR(CompositeFeaturePtr theSketch) +{ + auto aStep = std::dynamic_pointer_cast( + theSketch->data()->attribute(SketchPlugin_Sketch::CIRCULAR_CONSTRUCTION_GRID_STEP_R_ID()) + ); + + if (!aStep) { + aStep = std::dynamic_pointer_cast(theSketch->data()->addAttribute( + SketchPlugin_Sketch::CIRCULAR_CONSTRUCTION_GRID_STEP_R_ID(), + ModelAPI_AttributeDouble::typeId() + )); + + aStep->setIsArgument(false); + aStep->setValue(-1); + } + + return aStep; +} + +std::shared_ptr PartSet_Tools::sketchPlaneCircularGridNumOfAngSegments(CompositeFeaturePtr theSketch) +{ + auto num = std::dynamic_pointer_cast( + theSketch->data()->attribute(SketchPlugin_Sketch::CIRCULAR_CONSTRUCTION_GRID_NUM_OF_ANG_SEGMENTS_ID()) + ); + + if (!num) { + num = std::dynamic_pointer_cast(theSketch->data()->addAttribute( + SketchPlugin_Sketch::CIRCULAR_CONSTRUCTION_GRID_NUM_OF_ANG_SEGMENTS_ID(), + ModelAPI_AttributeInteger::typeId() + )); + + num->setIsArgument(false); + num->setValue(-1); + } + + return num; +} + +std::shared_ptr PartSet_Tools::sketchPlaneCircularGridOffsetAngle(CompositeFeaturePtr theSketch) +{ + auto aAngle = std::dynamic_pointer_cast( + theSketch->data()->attribute(SketchPlugin_Sketch::CIRCULAR_CONSTRUCTION_GRID_OFFSET_ANGLE_ID()) + ); + + if (!aAngle) { + aAngle = std::dynamic_pointer_cast(theSketch->data()->addAttribute( + SketchPlugin_Sketch::CIRCULAR_CONSTRUCTION_GRID_OFFSET_ANGLE_ID(), + ModelAPI_AttributeDouble::typeId() + )); + + aAngle->setIsArgument(false); + aAngle->setValue(0); + } + + return aAngle; +} + +std::shared_ptr PartSet_Tools::sketchPlaneCircularGridOffsetX(CompositeFeaturePtr theSketch) { + auto aShift = std::dynamic_pointer_cast( + theSketch->data()->attribute(SketchPlugin_Sketch::CIRCULAR_CONSTRUCTION_GRID_OFFSET_X_ID()) + ); + + if (!aShift) { + aShift = std::dynamic_pointer_cast(theSketch->data()->addAttribute( + SketchPlugin_Sketch::CIRCULAR_CONSTRUCTION_GRID_OFFSET_X_ID(), + ModelAPI_AttributeDouble::typeId() + )); + + aShift->setIsArgument(false); + aShift->setValue(0); + } + + return aShift; +} + +std::shared_ptr PartSet_Tools::sketchPlaneCircularGridOffsetY(CompositeFeaturePtr theSketch) { + auto aShift = std::dynamic_pointer_cast( + theSketch->data()->attribute(SketchPlugin_Sketch::CIRCULAR_CONSTRUCTION_GRID_OFFSET_Y_ID()) + ); + + if (!aShift) { + aShift = std::dynamic_pointer_cast(theSketch->data()->addAttribute( + SketchPlugin_Sketch::CIRCULAR_CONSTRUCTION_GRID_OFFSET_Y_ID(), + ModelAPI_AttributeDouble::typeId() + )); + + aShift->setIsArgument(false); + aShift->setValue(0); + } + + return aShift; +} + std::shared_ptr PartSet_Tools::point3D(std::shared_ptr thePoint2D, CompositeFeaturePtr theSketch) { diff --git a/src/PartSet/PartSet_Tools.h b/src/PartSet/PartSet_Tools.h index f8ae5dba7..11e2da8c0 100644 --- a/src/PartSet/PartSet_Tools.h +++ b/src/PartSet/PartSet_Tools.h @@ -23,6 +23,8 @@ #include "PartSet.h" #include +#include +#include #include #include @@ -38,6 +40,9 @@ #include #include +#include +#include +#include class V3d_View; class ModuleBase_IViewWindow; @@ -45,6 +50,7 @@ class ModuleBase_ViewerPrs; class ModuleBase_IWorkshop; class GeomDataAPI_Point2D; class GeomAPI_Shape; +class GeomAPI_Face; class GeomAPI_Pln; class GeomAPI_Pnt2d; class GeomAPI_Pnt; @@ -149,6 +155,79 @@ public: /// \return API object of geom plane static void nullifySketchPlane(CompositeFeaturePtr theSketch); + /*! \returns Basis of theFace' surface, defined in world CS, at point, which corresponds to point (U, V) in parametric space. */ + static std::pair getWorldCSAt(const GeomAPI_Face& theFace, double U, double V); + + /*! \returns Point, defined in world CS, which corresponds to point (U, V) in parametric space. */ + static std::pair getWorldPointByUV(const GeomAPI_Face& theFace, double U, double V); + + /*! \brief Fills bounding box, which is aligned according to theCS. + Returns the smallest possible box only for GeomAPI_Plns, for other shapes + returns unaligned transformed bounding box (so its dimensions are bigger than the smallest)! + \returns true on success. */ + static std::pair getBBoxAtCS(const GeomAPI_Shape& theShape, const gp_Ax3 theCS); + + /// @returns 0, if the plane was initialized on a feature. Result is never nullptr. + static std::shared_ptr sketchPlaneDefaultSize(CompositeFeaturePtr theSketch); + + /// @returns never nullptr. + static std::shared_ptr sketchPlaneAxesEnabled(CompositeFeaturePtr theSketch); + + /// @returns never nullptr. + static std::shared_ptr sketchPlaneSubstrateEnabled(CompositeFeaturePtr theSketch); + + /// @returns empty string, if construction grid is disabled. Result is never nullptr. + static std::shared_ptr sketchPlaneGridType(CompositeFeaturePtr theSketch); + + class SketchPlaneGridType { + public: + typedef enum { + No, + Rectangular, + Circular + } Enum; + + static std::string toString(Enum iType); + static Enum fromString(const std::string& iTypeString); + + private: + static const std::array STRINGS; + }; + + static void setSketchPlaneGridType(CompositeFeaturePtr theSketch, SketchPlaneGridType::Enum theType); + static SketchPlaneGridType::Enum getSketchPlaneGridType(CompositeFeaturePtr theSketch); + + /// @returns never nullptr. + static std::shared_ptr sketchPlaneRectangularGridStepX(CompositeFeaturePtr theSketch); + + /// @returns never nullptr. + static std::shared_ptr sketchPlaneRectangularGridStepY(CompositeFeaturePtr theSketch); + + /// @returns never nullptr. + static std::shared_ptr sketchPlaneRectangularGridOffsetAngle(CompositeFeaturePtr theSketch); + + /// @returns never nullptr. + static std::shared_ptr sketchPlaneRectangularGridOffsetX(CompositeFeaturePtr theSketch); + + /// @returns never nullptr. + static std::shared_ptr sketchPlaneRectangularGridOffsetY(CompositeFeaturePtr theSketch); + + /// @returns never nullptr. + static std::shared_ptr sketchPlaneCircularGridStepR(CompositeFeaturePtr theSketch); + + /// @returns never nullptr. + static std::shared_ptr sketchPlaneCircularGridNumOfAngSegments(CompositeFeaturePtr theSketch); + + /// @returns never nullptr. + static std::shared_ptr sketchPlaneCircularGridOffsetAngle(CompositeFeaturePtr theSketch); + + /// @returns never nullptr. + static std::shared_ptr sketchPlaneCircularGridOffsetX(CompositeFeaturePtr theSketch); + + /// @returns never nullptr. + static std::shared_ptr sketchPlaneCircularGridOffsetY(CompositeFeaturePtr theSketch); + + /// Create a point 3D on a basis of point 2D and sketch feature /// \param thePoint2D a point on a sketch /// \param theSketch a sketch feature diff --git a/src/PartSet/PartSet_WidgetBSplinePoints.cpp b/src/PartSet/PartSet_WidgetBSplinePoints.cpp index fe01d920f..192078248 100644 --- a/src/PartSet/PartSet_WidgetBSplinePoints.cpp +++ b/src/PartSet/PartSet_WidgetBSplinePoints.cpp @@ -119,7 +119,7 @@ PartSet_WidgetBSplinePoints::PartSet_WidgetBSplinePoints(QWidget* theParent, myScrollArea->setWidget(aContainer); - myWidgetValidator = new ModuleBase_WidgetValidator(this, myWorkshop); + myWidgetValidator.reset(new ModuleBase_WidgetValidator(this, myWorkshop)); myExternalObjectMgr = new PartSet_ExternalObjectsMgr(theData->getProperty("use_external"), theData->getProperty("can_create_external"), true); } @@ -555,10 +555,19 @@ void PartSet_WidgetBSplinePoints::mouseMoved(ModuleBase_IViewWindow* theWindow, if (myFinished || isEditingMode() || aModule->sketchReentranceMgr()->isInternalEditActive()) return; - gp_Pnt aPoint = PartSet_Tools::convertClickToPoint(theEvent->pos(), theWindow->v3dView()); + double aX = 0, aY = 0; // Coords at sketch plane. + bool success = convertPointToLocal( + myWorkshop, + mySketch, + theWindow, + theEvent->pos(), + aX, aY, + true + ); + + if (!success) + return; - double aX = 0, aY = 0; - PartSet_Tools::convertTo2D(aPoint, mySketch, theWindow->v3dView(), aX, aY); if (myState != ModifiedInViewer) storeCurentValue(); // we need to block the value state change diff --git a/src/PartSet/PartSet_WidgetPoint2DFlyout.cpp b/src/PartSet/PartSet_WidgetPoint2DFlyout.cpp index bb6ffcfa6..2d4622ee6 100644 --- a/src/PartSet/PartSet_WidgetPoint2DFlyout.cpp +++ b/src/PartSet/PartSet_WidgetPoint2DFlyout.cpp @@ -37,7 +37,7 @@ PartSet_WidgetPoint2DFlyout::PartSet_WidgetPoint2DFlyout(QWidget* theParent, const Config_WidgetAPI* theData) : PartSet_WidgetPoint2D(theParent, theWorkshop, theData) { - myWidgetValidator = new ModuleBase_WidgetValidator(this, myWorkshop); + myWidgetValidator.reset(new ModuleBase_WidgetValidator(this, myWorkshop)); } bool PartSet_WidgetPoint2DFlyout::setSelection(QList& theValues, diff --git a/src/PartSet/PartSet_WidgetPoint2d.cpp b/src/PartSet/PartSet_WidgetPoint2d.cpp index e434c3d0f..dfaccf2ed 100644 --- a/src/PartSet/PartSet_WidgetPoint2d.cpp +++ b/src/PartSet/PartSet_WidgetPoint2d.cpp @@ -73,6 +73,7 @@ #include #include +#include #include #include #include @@ -126,7 +127,7 @@ PartSet_WidgetPoint2D::PartSet_WidgetPoint2D(QWidget* theParent, aLayout->addWidget(myGroupBox); setLayout(aLayout); - myWidgetValidator = new ModuleBase_WidgetValidator(this, myWorkshop); + myWidgetValidator.reset(new ModuleBase_WidgetValidator(this, myWorkshop)); myExternalObjectMgr = new PartSet_ExternalObjectsMgr(theData->getProperty("use_external"), theData->getProperty("can_create_external"), true); } @@ -548,7 +549,6 @@ void PartSet_WidgetPoint2D::mouseReleased(ModuleBase_IViewWindow* theWindow, QMo return; ModuleBase_ISelection* aSelection = myWorkshop->selection(); - Handle(V3d_View) aView = theWindow->v3dView(); QList aList = aSelection->getSelected(ModuleBase_ISelection::Viewer); ModuleBase_ViewerPrsPtr aFirstValue = @@ -563,6 +563,7 @@ void PartSet_WidgetPoint2D::mouseReleased(ModuleBase_IViewWindow* theWindow, QMo GeomShapePtr aShape = aFirstValue->shape(); if (aShape.get() && aShape->shapeType() == GeomAPI_Shape::VERTEX) { const TopoDS_Shape& aTDShape = aShape->impl(); + Handle(V3d_View) aView = theWindow->v3dView(); GeomPnt2dPtr aPnt = PartSet_Tools::getPnt2d(aView, aTDShape, mySketch); aX = aPnt->x(); aY = aPnt->y(); @@ -570,8 +571,9 @@ void PartSet_WidgetPoint2D::mouseReleased(ModuleBase_IViewWindow* theWindow, QMo } } if (!aHasPoint) { - gp_Pnt aPoint = PartSet_Tools::convertClickToPoint(theEvent->pos(), aView); - PartSet_Tools::convertTo2D(aPoint, mySketch, aView, aX, aY); + bool success = convertPointToLocal(myWorkshop, mySketch, theWindow, theEvent->pos(), aX, aY, false); + if (!success) + return; } processSelection(aFirstValue, aX, aY); } @@ -643,6 +645,7 @@ void PartSet_WidgetPoint2D::processSelection(const ModuleBase_ViewerPrsPtr& theV } } else { + if (!isFeatureContainsPoint(myFeature, theX, theY)) { double aX = 0, aY = 0; bool anOrphanPoint = isOrphanPoint(aSelectedFeature, mySketch, aX, aY); @@ -738,10 +741,11 @@ void PartSet_WidgetPoint2D::mouseMoved(ModuleBase_IViewWindow* theWindow, QMouse if (isEditingMode() || aModule->sketchReentranceMgr()->isInternalEditActive()) return; - gp_Pnt aPoint = PartSet_Tools::convertClickToPoint(theEvent->pos(), theWindow->v3dView()); + double aX = 0, aY = 0; // Coords at sketch plane. + bool success = convertPointToLocal(myWorkshop, mySketch, theWindow, theEvent->pos(), aX, aY, true, true); + if (!success) + return; - double aX = 0, aY = 0; - PartSet_Tools::convertTo2D(aPoint, mySketch, theWindow->v3dView(), aX, aY); if (myState != ModifiedInViewer) storeCurentValue(); // we need to block the value state change diff --git a/src/PartSet/PartSet_WidgetPoint2d.h b/src/PartSet/PartSet_WidgetPoint2d.h index 273a29fae..8f664c502 100644 --- a/src/PartSet/PartSet_WidgetPoint2d.h +++ b/src/PartSet/PartSet_WidgetPoint2d.h @@ -220,7 +220,7 @@ protected: /// Create a coincidence constraint between the attribute and the parameter object /// \theObject a result object - /// \return true if succed + /// \return true if succeed bool setConstraintToObject(const ObjectPtr& theObject); /// Process selected value @@ -229,7 +229,6 @@ protected: /// \param theY Y coordinate of clicked point void processSelection(const ModuleBase_ViewerPrsPtr& theValue, double theX, double theY); - public: /// Returns if the feature is an orphan point, circle or an arc. Returns true if it /// has no a coincident to other lines. It processes point, circle and arc features diff --git a/src/PartSet/PartSet_WidgetSketchCreator.cpp b/src/PartSet/PartSet_WidgetSketchCreator.cpp index 2f67e9db6..67d156a60 100644 --- a/src/PartSet/PartSet_WidgetSketchCreator.cpp +++ b/src/PartSet/PartSet_WidgetSketchCreator.cpp @@ -41,6 +41,7 @@ #include #include #include +#include #include #include #include @@ -254,10 +255,6 @@ bool PartSet_WidgetSketchCreator::isValidSelectionCustom(const ModuleBase_Viewer void PartSet_WidgetSketchCreator::activateSelectionControl() { - // reset previously set size of view needed on restart extrusion after Sketch - if (myModule->sketchMgr()->previewSketchPlane()->isUseSizeOfView()) - myModule->sketchMgr()->previewSketchPlane()->setSizeOfView(0, false); - // we need to call activate here as the widget has no focus accepted controls // if these controls are added here, activate will happens automatically after focusIn() XGUI_Workshop* aWorkshop = XGUI_Tools::workshop(myModule->workshop()); @@ -431,20 +428,18 @@ bool PartSet_WidgetSketchCreator::startSketchOperation( } aSketchStarted = true; // Set View size if a plane is selected - if (myPreviewPlanes->isPreviewDisplayed() && - myPreviewPlanes->isPreviewShape(aValue->shape())) { + if (myPreviewPlanes->isPreviewDisplayed() && myPreviewPlanes->isPreviewShape(aValue->shape())) { // set default plane size - bool isSetSizeOfView = false; - double aSizeOfView = 0; - QString aSizeOfViewStr = mySizeOfView->text(); - if (!aSizeOfViewStr.isEmpty()) { - aSizeOfView = aSizeOfViewStr.toDouble(&isSetSizeOfView); - if (isSetSizeOfView && aSizeOfView <= 0) { - isSetSizeOfView = false; - } - } - if (isSetSizeOfView) - myModule->sketchMgr()->previewSketchPlane()->setSizeOfView(aSizeOfView, true); + bool isValidSizeInput = true; + double aSizeOfView = mySizeOfView->text().toDouble(&isValidSizeInput); + if (aSizeOfView <= 0 || !isValidSizeInput) + aSizeOfView = PartSet_PreviewSketchPlane::defaultSketchSize(); + + const auto sketch = myModule->sketchMgr()->activeSketch(); + if (sketch) + PartSet_Tools::sketchPlaneDefaultSize(sketch)->setValue(aSizeOfView); + + myModule->sketchMgr()->previewSketchPlane()->setAllUsingSketch(sketch); } // manually deactivation because the widget was not activated as has no focus acceptin controls deactivate(); diff --git a/src/PartSet/PartSet_WidgetSketchLabel.cpp b/src/PartSet/PartSet_WidgetSketchLabel.cpp index e1f71f19a..b3806c7d2 100644 --- a/src/PartSet/PartSet_WidgetSketchLabel.cpp +++ b/src/PartSet/PartSet_WidgetSketchLabel.cpp @@ -37,6 +37,9 @@ #include #include +#include +#include +#include #include #include @@ -73,12 +76,22 @@ #include #include #include +#include #include #include #include #include +#include #include #include +#include +#include + +#include +#include +#include +#include + #ifdef WIN32 #pragma warning(disable : 4456) // for nested foreach @@ -88,12 +101,13 @@ #define DBL_MAX 1.7976931348623158e+308 #endif + PartSet_WidgetSketchLabel::PartSet_WidgetSketchLabel(QWidget* theParent, - ModuleBase_IWorkshop* theWorkshop, - const Config_WidgetAPI* theData, - const QMap& toShowConstraints) -: ModuleBase_WidgetValidated(theParent, theWorkshop, theData), myOpenTransaction(false), -myIsSelection(false) + ModuleBase_IWorkshop* theWorkshop, + const Config_WidgetAPI* theData, + const QMap& toShowConstraints +) : ModuleBase_WidgetValidated(theParent, theWorkshop, theData), + mySketchDataIsModified(false), myOpenTransaction(false), myIsSelection(false) { QVBoxLayout* aLayout = new QVBoxLayout(this); ModuleBase_Tools::zeroMargins(aLayout); @@ -155,37 +169,104 @@ myIsSelection(false) myStackWidget->addWidget(aFirstWgt); - // Define widget for sketch manmagement + // Define widget for sketch management QWidget* aSecondWgt = new QWidget(this); aLayout = new QVBoxLayout(aSecondWgt); ModuleBase_Tools::zeroMargins(aLayout); - QGroupBox* aViewBox = new QGroupBox(tr("Sketcher plane"), this); - QGridLayout* aViewLayout = new QGridLayout(aViewBox); + { // Sketch view controls. + mySketchViewGroupBox = new QGroupBox(tr("Sketcher plane"), this); + QGridLayout* aViewLayout = new QGridLayout(mySketchViewGroupBox); - myViewInverted = new QCheckBox(tr("Reversed"), aViewBox); - aViewLayout->addWidget(myViewInverted, 0, 0); + PartSet_Module* const aModule = dynamic_cast(myWorkshop->module()); + PartSet_PreviewSketchPlane* const aPreviewPlane = aModule->sketchMgr()->previewSketchPlane(); + const CompositeFeaturePtr sketch = std::dynamic_pointer_cast(myFeature); - // Sketch plane visibility - myViewVisible = new QCheckBox(tr("Visible"), aViewBox); - PartSet_Module* aModule = dynamic_cast(myWorkshop->module()); - PartSet_PreviewSketchPlane* aPreviewPlane = aModule->sketchMgr()->previewSketchPlane(); - if (aPreviewPlane->isPlaneCreated()) - // init with current state - myViewVisible->setChecked(aPreviewPlane->isDisplayed()); - else - // true by default (at start of sketch creation) - myViewVisible->setChecked(true); + { // Reverse flag. + myViewInverted = new QCheckBox(tr("Reversed"), mySketchViewGroupBox); + aViewLayout->addWidget(myViewInverted, 0, 0); + } + + { // Sketch axes visibility. + myAxesVisibleCheckBox = new QCheckBox(tr("Axes"), mySketchViewGroupBox); + myAxesVisibleCheckBox->setChecked(sketch ? PartSet_Tools::sketchPlaneAxesEnabled(sketch)->value() : false); + + aViewLayout->addWidget(myAxesVisibleCheckBox, 0, 1); + connect(myAxesVisibleCheckBox, SIGNAL(toggled(bool)), this, SLOT(onShowAxes(bool))); + } - aViewLayout->addWidget(myViewVisible, 0, 1, Qt::AlignRight); - connect(myViewVisible, SIGNAL(toggled(bool)), this, SLOT(onShowViewPlane(bool))); + { // Sketch substrate-plane visibility. + mySubstrateVisibleCheckBox = new QCheckBox(tr("Substrate"), mySketchViewGroupBox); + mySubstrateVisibleCheckBox->setChecked(sketch ? PartSet_Tools::sketchPlaneSubstrateEnabled(sketch)->value() : false); - QPushButton* aSetViewBtn = - new QPushButton(QIcon(":icons/plane_view.png"), tr("Set plane view"), aViewBox); - connect(aSetViewBtn, SIGNAL(clicked(bool)), this, SLOT(onSetPlaneView())); - aViewLayout->addWidget(aSetViewBtn, 1, 0, 1, 2); + aViewLayout->addWidget(mySubstrateVisibleCheckBox, 0, 2); + connect(mySubstrateVisibleCheckBox, SIGNAL(toggled(bool)), this, SLOT(onShowSubstrate(bool))); + } + + { + QPushButton* aSetViewBtn = new QPushButton(QIcon(":icons/plane_view.png"), tr("Set plane view"), mySketchViewGroupBox); + connect(aSetViewBtn, SIGNAL(clicked(bool)), this, SLOT(onSetPlaneView())); + aViewLayout->addWidget(aSetViewBtn, 1, 0, 1, 3); + } + + { // Sketch construction grid. + QGroupBox* aCGBox = new QGroupBox(tr("Construction grid"), this); + QGridLayout* aCGLayout = new QGridLayout(aCGBox); + + { + myGridTypeComboBox = new QComboBox(aCGBox); + myGridTypeComboBox->addItem(tr("Disabled"), PartSet_Tools::SketchPlaneGridType::No); + myGridTypeComboBox->addItem(tr("Rectangular"), PartSet_Tools::SketchPlaneGridType::Rectangular); + myGridTypeComboBox->addItem(tr("Circular"), PartSet_Tools::SketchPlaneGridType::Circular); + myGridTypeComboBox->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed); + myGridTypeComboBox->setCurrentIndex(0); + if (sketch) { + const int idx = myGridTypeComboBox->findData(PartSet_Tools::getSketchPlaneGridType(sketch)); + if (idx != -1) + myGridTypeComboBox->setCurrentIndex(idx); + } + + aCGLayout->addWidget(myGridTypeComboBox, 0, 0); + connect(myGridTypeComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(onGridTypeChanged(int))); + } + + { + myGridSnappingModeComboBox = new QComboBox(aCGBox); + myGridSnappingModeComboBox->addItem(tr("Don't snap"), PartSet_PreviewSketchPlane::GridSnappingMode::Off); + myGridSnappingModeComboBox->addItem(tr("Snap anyway"), PartSet_PreviewSketchPlane::GridSnappingMode::SnapAnyway); + myGridSnappingModeComboBox->addItem(tr("Snap in proximity"), PartSet_PreviewSketchPlane::GridSnappingMode::SnapInProximity); + myGridSnappingModeComboBox->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed); + myGridSnappingModeComboBox->setCurrentIndex(int(aPreviewPlane->getGridSnappingMode())); + + aCGLayout->addWidget(myGridSnappingModeComboBox, 0, 1); + connect(myGridSnappingModeComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(onGridSnappingModeChanged(int))); + } + + { + const auto widgetGrouper = new QWidget(aCGBox); + aCGLayout->addWidget(widgetGrouper, 1, 0, 1, 2); + const auto grouperLayout = new QVBoxLayout(widgetGrouper); + + const auto currentGridType = myGridTypeComboBox->currentData(); + + myWidgetRectangularGrid = new PartSet_WidgetSketchRectangularGrid(aCGBox, this); + grouperLayout->addWidget(myWidgetRectangularGrid); + myWidgetRectangularGrid->setVisible(currentGridType == PartSet_Tools::SketchPlaneGridType::Rectangular); + + myWidgetCircularGrid = new PartSet_WidgetSketchCircularGrid(aCGBox, this); + grouperLayout->addWidget(myWidgetCircularGrid); + myWidgetCircularGrid->setVisible(currentGridType == PartSet_Tools::SketchPlaneGridType::Circular); + } + + aViewLayout->addWidget(aCGBox, 2, 0, 1, 3); + } // Sketch construction grid. + + aLayout->addWidget(mySketchViewGroupBox); + + aPreviewPlane->setAllUsingSketch(sketch); + reconfigureSketchViewWidgets(); + } // View box. - aLayout->addWidget(aViewBox); QMap aStates; aStates[PartSet_Tools::Geometrical] = tr("Show geometrical constraints"); @@ -234,8 +315,16 @@ myIsSelection(false) myPreviewPlanes = new PartSet_PreviewPlanes(); } -PartSet_WidgetSketchLabel::~PartSet_WidgetSketchLabel() -{ +void PartSet_WidgetSketchLabel::setFeature( + const FeaturePtr& theFeature, + const bool theToStoreValue, + const bool isUpdateFlushed +) { + ModuleBase_WidgetValidated::setFeature(theFeature, theToStoreValue, isUpdateFlushed); + PartSet_Module* aModule = dynamic_cast(myWorkshop->module()); + const CompositeFeaturePtr sketch = std::dynamic_pointer_cast(myFeature); + aModule->sketchMgr()->previewSketchPlane()->setAllUsingSketch(sketch); + reconfigureSketchViewWidgets(); } bool PartSet_WidgetSketchLabel::setSelection(QList& theValues, @@ -363,24 +452,19 @@ void PartSet_WidgetSketchLabel::updateByPlaneSelected(const ModuleBase_ViewerPrs // 1. hide main planes if they have been displayed and display sketch preview plane myPreviewPlanes->erasePreviewPlanes(myWorkshop); - QString aSizeOfViewStr = mySizeOfView->text(); - bool isSetSizeOfView = false; - double aSizeOfView = 0; - if (!aSizeOfViewStr.isEmpty()) { - aSizeOfView = aSizeOfViewStr.toDouble(&isSetSizeOfView); - if (isSetSizeOfView && aSizeOfView <= 0) { - isSetSizeOfView = false; - } - } + bool isValidSizeInput = true; + double aSizeOfView = mySizeOfView->text().toDouble(&isValidSizeInput); + if (aSizeOfView <= 0 || !isValidSizeInput) + aSizeOfView = PartSet_PreviewSketchPlane::defaultSketchSize(); + PartSet_Module* aModule = dynamic_cast(myWorkshop->module()); if (aModule) { - CompositeFeaturePtr aSketch = std::dynamic_pointer_cast(myFeature); - aModule->sketchMgr()->previewSketchPlane()->setSizeOfView(aSizeOfView, isSetSizeOfView); - if (myViewVisible->isChecked()) - aModule->sketchMgr()->previewSketchPlane()->createSketchPlane(aSketch, myWorkshop); - else - aModule->sketchMgr()->previewSketchPlane()->clearPlanePreview(); + CompositeFeaturePtr sketch = std::dynamic_pointer_cast(myFeature); + PartSet_Tools::sketchPlaneDefaultSize(sketch)->setValue(aSizeOfView); + aModule->sketchMgr()->previewSketchPlane()->setAllUsingSketch(sketch); } + reconfigureSketchViewWidgets(); + // 2. if the planes were displayed, change the view projection std::shared_ptr aDir = aPlane->direction(); @@ -393,15 +477,15 @@ void PartSet_WidgetSketchLabel::updateByPlaneSelected(const ModuleBase_ViewerPrs if (aRotate) { myWorkshop->viewer()->setViewProjection(aXYZ.X(), aXYZ.Y(), aXYZ.Z(), aTwist); } - if (isSetSizeOfView && aSizeOfView > 0) { - Handle(V3d_View) aView3d = myWorkshop->viewer()->activeView(); - if (!aView3d.IsNull()) { - Bnd_Box aBndBox; - double aHalfSize = aSizeOfView/2.0; - aBndBox.Update(-aHalfSize, -aHalfSize, -aHalfSize, aHalfSize, aHalfSize, aHalfSize); - aView3d->FitAll(aBndBox, 0.01, false); - } + + Handle(V3d_View) aView3d = myWorkshop->viewer()->activeView(); + if (!aView3d.IsNull()) { + Bnd_Box aBndBox; + double aHalfSize = aSizeOfView/2.0; + aBndBox.Update(-aHalfSize, -aHalfSize, -aHalfSize, aHalfSize, aHalfSize, aHalfSize); + aView3d->FitAll(aBndBox, 0.01, false); } + if (myOpenTransaction) { SessionPtr aMgr = ModelAPI_Session::get(); aMgr->finishOperation(); @@ -621,6 +705,23 @@ void PartSet_WidgetSketchLabel::hideEvent(QHideEvent* theEvent) } +void PartSet_WidgetSketchLabel::onShowDOF() +{ + CompositeFeaturePtr aCompFeature = + std::dynamic_pointer_cast(myFeature); + if (aCompFeature.get()) { + static const Events_ID anEvent = Events_Loop::eventByName(EVENT_GET_DOF_OBJECTS); + ModelAPI_EventCreator::get()->sendUpdated(aCompFeature, anEvent); + Events_Loop::loop()->flush(anEvent); + + // Transfer focus to the current viewport for correct processing of a key event + QWidget* aViewPort = myWorkshop->viewer()->activeViewPort(); + if (aViewPort) + aViewPort->setFocus(); + } +} + + void PartSet_WidgetSketchLabel::onShowPanel() { //if (mySizeOfViewWidget->isVisible()) { @@ -639,6 +740,112 @@ void PartSet_WidgetSketchLabel::onShowPanel() } } +void PartSet_WidgetSketchLabel::onShowAxes(bool toShow) +{ + const auto sketch = std::static_pointer_cast(feature()); + if (sketch) { + PartSet_Tools::sketchPlaneAxesEnabled(sketch)->setValue(toShow); + mySketchDataIsModified = true; + } + + const auto module = dynamic_cast(myWorkshop->module()); + PartSet_PreviewSketchPlane* const previewPlane = module->sketchMgr()->previewSketchPlane(); + previewPlane->showAxes(toShow); + myWorkshop->viewer()->update(); +} + +void PartSet_WidgetSketchLabel::onShowSubstrate(bool toShow) +{ + const auto sketch = std::static_pointer_cast(feature()); + if (sketch) { + PartSet_Tools::sketchPlaneSubstrateEnabled(sketch)->setValue(toShow); + mySketchDataIsModified = true; + } + + const auto module = dynamic_cast(myWorkshop->module()); + PartSet_PreviewSketchPlane* const previewPlane = module->sketchMgr()->previewSketchPlane(); + previewPlane->showSubstrate(toShow); + myWorkshop->viewer()->update(); +} + +void PartSet_WidgetSketchLabel::onGridTypeChanged(int theComboBoxIdx) { + (void)theComboBoxIdx; + const auto module = dynamic_cast(myWorkshop->module()); + const auto previewPlane = module->sketchMgr()->previewSketchPlane(); + const auto gridType = PartSet_Tools::SketchPlaneGridType::Enum(myGridTypeComboBox->currentData().toInt()); + + const auto sketch = std::static_pointer_cast(feature()); + if (sketch) { + PartSet_Tools::setSketchPlaneGridType(sketch, gridType); + mySketchDataIsModified = true; + } + + previewPlane->setGridType(gridType); + myGridSnappingModeComboBox->setEnabled(gridType != PartSet_Tools::SketchPlaneGridType::No); + myWidgetRectangularGrid->setVisible(gridType == PartSet_Tools::SketchPlaneGridType::Rectangular); + myWidgetCircularGrid->setVisible(gridType == PartSet_Tools::SketchPlaneGridType::Circular); + myWorkshop->viewer()->update(); +} + +void PartSet_WidgetSketchLabel::onGridSnappingModeChanged(int theModeIdx) { + const auto module = dynamic_cast(myWorkshop->module()); + PartSet_PreviewSketchPlane* const previewPlane = module->sketchMgr()->previewSketchPlane(); + previewPlane->setGridSnappingMode(static_cast(theModeIdx)); +} + +void PartSet_WidgetSketchLabel::reconfigureSketchViewWidgets() +{ + const auto module = dynamic_cast(myWorkshop->module()); + if (module) { + const PartSet_PreviewSketchPlane* const previewPlane = module->sketchMgr()->previewSketchPlane(); + + myAxesVisibleCheckBox->setChecked(previewPlane->isShowAxes()); + mySubstrateVisibleCheckBox->setChecked(previewPlane->isShowSubstrate()); + + const auto gridType = previewPlane->getGridType(); + const int gridTypeIdx = myGridTypeComboBox->findData(gridType); + myGridTypeComboBox->setCurrentIndex(gridTypeIdx != -1 ? gridTypeIdx : 0); + myWidgetRectangularGrid->setVisible(gridType == PartSet_Tools::SketchPlaneGridType::Rectangular); + myWidgetRectangularGrid->recongifure(); + myWidgetCircularGrid->setVisible(gridType == PartSet_Tools::SketchPlaneGridType::Circular); + myWidgetCircularGrid->recongifure(); + + myGridSnappingModeComboBox->setEnabled(gridType != PartSet_Tools::SketchPlaneGridType::No); + myGridSnappingModeComboBox->setCurrentIndex(int(previewPlane->getGridSnappingMode())); + + mySketchViewGroupBox->setEnabled(true); + } + else { + mySketchViewGroupBox->setEnabled(false); + + myAxesVisibleCheckBox->setChecked(false); + mySubstrateVisibleCheckBox->setChecked(false); + + myGridTypeComboBox->setCurrentIndex(0); + myWidgetRectangularGrid->setVisible(false); + myWidgetRectangularGrid->recongifure(); + myWidgetCircularGrid->setVisible(false); + myWidgetCircularGrid->recongifure(); + + myGridSnappingModeComboBox->setEnabled(false); + } + + mySketchDataIsModified = false; + myWorkshop->viewer()->update(); +} + +void PartSet_WidgetSketchLabel::saveSketchViewPreferenceToSkethData() +{ + const auto module = dynamic_cast(myWorkshop->module()); + if (!module) + return; + + const PartSet_PreviewSketchPlane* const previewPlane = module->sketchMgr()->previewSketchPlane(); + const auto sketch = std::dynamic_pointer_cast(myFeature); + previewPlane->savePreferencesIntoSketchData(sketch); + mySketchDataIsModified = false; +} + void PartSet_WidgetSketchLabel::deactivate() { QWidget* aTopWidget = window(); @@ -750,7 +957,6 @@ void PartSet_WidgetSketchLabel::onSetPlaneView() } } - //****************************************************** QList PartSet_WidgetSketchLabel::findCircularEdgesInPlane() { @@ -837,6 +1043,14 @@ void PartSet_WidgetSketchLabel::setShowPointsState(bool theState) myShowPoints->blockSignals(aBlock); } +bool PartSet_WidgetSketchLabel::storeValueCustom() +{ + if (mySketchDataIsModified) + saveSketchViewPreferenceToSkethData(); + + return true; +} + bool PartSet_WidgetSketchLabel::restoreValueCustom() { if (myFeature.get()) { @@ -858,6 +1072,12 @@ bool PartSet_WidgetSketchLabel::restoreValueCustom() myShowDOFBtn->setEnabled(true); } } + + PartSet_Module* aModule = dynamic_cast(myWorkshop->module()); + if (aModule) + aModule->sketchMgr()->previewSketchPlane()->setAllUsingSketch(aSketch); + + reconfigureSketchViewWidgets(); } else { myDoFLabel->setText(""); @@ -867,23 +1087,6 @@ bool PartSet_WidgetSketchLabel::restoreValueCustom() return true; } - -void PartSet_WidgetSketchLabel::onShowDOF() -{ - CompositeFeaturePtr aCompFeature = - std::dynamic_pointer_cast(myFeature); - if (aCompFeature.get()) { - static const Events_ID anEvent = Events_Loop::eventByName(EVENT_GET_DOF_OBJECTS); - ModelAPI_EventCreator::get()->sendUpdated(aCompFeature, anEvent); - Events_Loop::loop()->flush(anEvent); - - // Transfer focus to the current viewport for correct processing of a key event - QWidget* aViewPort = myWorkshop->viewer()->activeViewPort(); - if (aViewPort) - aViewPort->setFocus(); - } -} - bool PartSet_WidgetSketchLabel::eventFilter(QObject* theObj, QEvent* theEvent) { if (theObj == window()) { @@ -900,19 +1103,453 @@ bool PartSet_WidgetSketchLabel::eventFilter(QObject* theObj, QEvent* theEvent) return ModuleBase_WidgetValidated::eventFilter(theObj, theEvent); } -void PartSet_WidgetSketchLabel::onShowViewPlane(bool toShow) + +PitchSpinBox::PitchSpinBox(QWidget* theParent) : QDoubleSpinBox(theParent) { - PartSet_Module* aModule = dynamic_cast(myWorkshop->module()); - PartSet_PreviewSketchPlane* aPreviewPlane = aModule->sketchMgr()->previewSketchPlane(); - if (toShow) { - CompositeFeaturePtr aSketch = std::dynamic_pointer_cast(myFeature); - if (aPreviewPlane->isPlaneCreated()) - aPreviewPlane->displaySketchPlane(myWorkshop); - else - aPreviewPlane->createSketchPlane(aSketch, myWorkshop); + setRange(std::pow(10, -PartSet_WidgetSketchGrid::NUM_OF_DECIMAL_DIGITS_TRANS), std::numeric_limits::max()); + setDecimals(PartSet_WidgetSketchGrid::NUM_OF_DECIMAL_DIGITS_TRANS); + setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed); + setMinimumWidth(PartSet_WidgetSketchGrid::SPIN_BOX_MIN_WIDTH); + myPrevVal = 1; + + connect(this, SIGNAL(textChanged(const QString&)), this, SLOT(onTextChanged())); + connect(this, SIGNAL(editingFinished()), this, SLOT(onEditingFinished())); +} + +void PitchSpinBox::setValue(double theVal) +{ + myPrevVal = theVal; + QDoubleSpinBox::setValue(theVal); +} + +void PitchSpinBox::onTextChanged() +{ + if (value() <= Precision::Confusion()) + return; + + setSingleStep(PartSet_WidgetSketchGrid::reasonablePitchIncrement(value())); + emit valueSet(value()); +} + +void PitchSpinBox::onEditingFinished() +{ + if (value() <= Precision::Confusion()) { + QDoubleSpinBox::setValue(myPrevVal); } else { - aPreviewPlane->eraseSketchPlane(myWorkshop, false); + myPrevVal = value(); + emit valueSet(value()); } - myWorkshop->viewer()->update(); } + + +PartSet_WidgetSketchGrid::PartSet_WidgetSketchGrid(QWidget* theParent, PartSet_WidgetSketchLabel* theSketchLabel) +: QWidget(theParent), mySketchLabel(theSketchLabel), myPreviewPlane(nullptr) +{ + static const double MAX_DOUBLE = std::numeric_limits::max(); + static const double MIN_DOUBLE = std::numeric_limits::lowest(); + + myResetButton = new QPushButton(tr("Reset"), this); + myResetButton->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed); + myResetButton->setToolTip(tr("Set default pitches and zero offsets.")); + + const auto offsetAngleLabel = new QLabel(this); + offsetAngleLabel->setText(tr("Offset angle,°")); + + myOffsetAngleSpinBox = new QDoubleSpinBox(this); + myOffsetAngleSpinBox->setRange(-180, 180); + myOffsetAngleSpinBox->setDecimals(PartSet_WidgetSketchGrid::NUM_OF_DECIMAL_DIGITS_ROTAT); + myOffsetAngleSpinBox->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed); + myOffsetAngleSpinBox->setMinimumWidth(PartSet_WidgetSketchGrid::SPIN_BOX_MIN_WIDTH); + + const auto offsetXLabel = new QLabel(this); + offsetXLabel->setText(tr("Offset") + " X'"); + + myOffsetXSpinBox = new QDoubleSpinBox(this); + myOffsetXSpinBox->setRange(MIN_DOUBLE, MAX_DOUBLE); + myOffsetXSpinBox->setDecimals(PartSet_WidgetSketchGrid::NUM_OF_DECIMAL_DIGITS_TRANS); + myOffsetXSpinBox->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed); + myOffsetXSpinBox->setMinimumWidth(PartSet_WidgetSketchGrid::SPIN_BOX_MIN_WIDTH); + + const auto offsetYLabel = new QLabel(this); + offsetYLabel->setText(tr("Offset") + " Y'"); + + myOffsetYSpinBox = new QDoubleSpinBox(this); + myOffsetYSpinBox->setRange(MIN_DOUBLE, MAX_DOUBLE); + myOffsetYSpinBox->setDecimals(PartSet_WidgetSketchGrid::NUM_OF_DECIMAL_DIGITS_TRANS); + myOffsetYSpinBox->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed); + myOffsetYSpinBox->setMinimumWidth(PartSet_WidgetSketchGrid::SPIN_BOX_MIN_WIDTH); + + myLayout = new QGridLayout(this); + myLayout->addWidget(myResetButton , 2, 0); + myLayout->addWidget(offsetAngleLabel, 3, 0); + myLayout->addWidget(offsetXLabel , 3, 1); + myLayout->addWidget(offsetYLabel , 3, 2); + myLayout->addWidget(myOffsetAngleSpinBox, 4, 0); + myLayout->addWidget(myOffsetXSpinBox , 4, 1); + myLayout->addWidget(myOffsetYSpinBox , 4, 2); + + connect(myResetButton, SIGNAL(clicked(bool)), this, SLOT(onResetClicked())); + connect(myOffsetAngleSpinBox, SIGNAL(valueChanged(double)), this, SLOT(onOffsetAngleChanged(double))); + connect(myOffsetXSpinBox, SIGNAL(valueChanged(double)), this, SLOT(onOffsetXChanged(double))); + connect(myOffsetYSpinBox, SIGNAL(valueChanged(double)), this, SLOT(onOffsetYChanged(double))); + + setEnabled(false); +} + +/*static*/ double PartSet_WidgetSketchGrid::clampValue(double theValue, double theIntervalWidth) +{ + theValue = std::remainder(theValue, theIntervalWidth); + if (theValue > theIntervalWidth/2) + theValue = theValue - theIntervalWidth; + else if (theValue <= -theIntervalWidth/2) + theValue = theIntervalWidth + theValue; + + return theValue; +} + +/*static*/ double PartSet_WidgetSketchGrid::reasonableOffsetIncrement(double theStep) +{ + const double stepTenth = theStep / 10; + if (std::abs(stepTenth) < Precision::Confusion()) + return 0; + + return std::pow(10, std::floor(std::log10(stepTenth))); +} + +/*static*/ double PartSet_WidgetSketchGrid::reasonablePitchIncrement(double theStep) +{ + return PartSet_WidgetSketchGrid::reasonableOffsetIncrement(theStep); +} + +void PartSet_WidgetSketchGrid::retrieveSketchAndPlane() +{ + const auto module = dynamic_cast(mySketchLabel->myWorkshop->module()); + if (!module) + myPreviewPlane = nullptr; + else + myPreviewPlane = module->sketchMgr()->previewSketchPlane(); +} + +/*static*/ const int PartSet_WidgetSketchGrid::NUM_OF_DECIMAL_DIGITS_TRANS = 3; +/*static*/ const int PartSet_WidgetSketchGrid::NUM_OF_DECIMAL_DIGITS_ROTAT = 4; // Fits angular second. +/*static*/ const int PartSet_WidgetSketchGrid::SPIN_BOX_MIN_WIDTH = 80; + + +PartSet_WidgetSketchRectangularGrid::PartSet_WidgetSketchRectangularGrid(QWidget* theParent, PartSet_WidgetSketchLabel* theSketchLabel) +: PartSet_WidgetSketchGrid(theParent, theSketchLabel) +{ + static const double MAX_DOUBLE = std::numeric_limits::max(); + static const double MIN_DOUBLE = std::numeric_limits::min(); + + const auto stepXLabel = new QLabel(this); + stepXLabel->setText(tr("Pitch") + " X'"); + + myStepXSpinBox = new PitchSpinBox(this); + + const auto stepYLabel = new QLabel(this); + stepYLabel->setText(tr("Pitch") + " Y'"); + + myStepYSpinBox = new PitchSpinBox(this); + + myLayout->addWidget(stepXLabel , 1, 1); + myLayout->addWidget(stepYLabel , 1, 2); + myLayout->addWidget(myStepXSpinBox, 2, 1); + myLayout->addWidget(myStepYSpinBox, 2, 2); + + connect(myStepXSpinBox, SIGNAL(valueSet(double)), this, SLOT(onStepXSet(double))); + connect(myStepYSpinBox, SIGNAL(valueSet(double)), this, SLOT(onStepYSet(double))); +} + +void PartSet_WidgetSketchRectangularGrid::recongifure() +{ + retrieveSketchAndPlane(); + + if (myPreviewPlane) { + const auto steps = myPreviewPlane->getRectangularGridSteps(); + myStepXSpinBox->setValue(steps.first); + myStepYSpinBox->setValue(steps.second); + + const auto offsets = myPreviewPlane->getRectangularGridOffsets(); + myOffsetAngleSpinBox->setValue(PartSet_WidgetSketchGrid::clampValue(offsets.second, 360)); + myOffsetXSpinBox->setValue(offsets.first.first); + myOffsetYSpinBox->setValue(offsets.first.second); + + setEnabled(true); + } + else { + setEnabled(false); + + myStepXSpinBox->setValue(0); + myStepYSpinBox->setValue(0); + myOffsetAngleSpinBox->setValue(0); + myOffsetXSpinBox->setValue(0); + myOffsetYSpinBox->setValue(0); + } +} + +void PartSet_WidgetSketchRectangularGrid::onStepXSet(double theStep) +{ + myOffsetXSpinBox->setSingleStep(PartSet_WidgetSketchGrid::reasonableOffsetIncrement(theStep)); + + const auto sketch = std::static_pointer_cast(mySketchLabel->myFeature); + if (sketch) { + PartSet_Tools::sketchPlaneRectangularGridStepX(sketch)->setValue(theStep); + mySketchLabel->mySketchDataIsModified = true; + } + + if (myPreviewPlane) { + myPreviewPlane->setRectangularGridStepX(theStep); + myPreviewPlane->reconfigureGrid(); + mySketchLabel->myWorkshop->viewer()->update(); + } +} + +void PartSet_WidgetSketchRectangularGrid::onStepYSet(double theStep) +{ + myOffsetYSpinBox->setSingleStep(PartSet_WidgetSketchGrid::reasonableOffsetIncrement(theStep)); + + const auto sketch = std::static_pointer_cast(mySketchLabel->myFeature); + if (sketch) { + PartSet_Tools::sketchPlaneRectangularGridStepY(sketch)->setValue(theStep); + mySketchLabel->mySketchDataIsModified = true; + } + + if (myPreviewPlane) { + myPreviewPlane->setRectangularGridStepY(theStep); + myPreviewPlane->reconfigureGrid(); + mySketchLabel->myWorkshop->viewer()->update(); + } +} + +void PartSet_WidgetSketchRectangularGrid::onResetClicked() +{ + if (!myPreviewPlane) + return; + + myPreviewPlane->resetRectangularGrid(); + recongifure(); + + const auto sketch = std::static_pointer_cast(mySketchLabel->feature()); + if (sketch) { + myPreviewPlane->saveRectangularGridPreferencesIntoSketchData(sketch); + mySketchLabel->mySketchDataIsModified = true; + } + + mySketchLabel->myWorkshop->viewer()->update(); +} + +void PartSet_WidgetSketchRectangularGrid::onOffsetAngleChanged(double theOffset) +{ + const auto sketch = std::static_pointer_cast(mySketchLabel->feature()); + if (sketch) { + PartSet_Tools::sketchPlaneRectangularGridOffsetAngle(sketch)->setValue(theOffset); + mySketchLabel->mySketchDataIsModified = true; + } + + if (myPreviewPlane) { + myPreviewPlane->setRectangularGridOffsetA(theOffset); + myPreviewPlane->reconfigureGrid(); + mySketchLabel->myWorkshop->viewer()->update(); + } +} + +void PartSet_WidgetSketchRectangularGrid::onOffsetXChanged(double theOffset) +{ + const auto sketch = std::static_pointer_cast(mySketchLabel->feature()); + if (sketch) { + PartSet_Tools::sketchPlaneRectangularGridOffsetX(sketch)->setValue(theOffset); + mySketchLabel->mySketchDataIsModified = true; + } + + if (myPreviewPlane) { + myPreviewPlane->setRectangularGridOffsetX(theOffset); + myPreviewPlane->reconfigureGrid(); + mySketchLabel->myWorkshop->viewer()->update(); + } +} + +void PartSet_WidgetSketchRectangularGrid::onOffsetYChanged(double theOffset) +{ + const auto sketch = std::static_pointer_cast(mySketchLabel->feature()); + if (sketch) { + PartSet_Tools::sketchPlaneRectangularGridOffsetY(sketch)->setValue(theOffset); + mySketchLabel->mySketchDataIsModified = true; + } + + if (myPreviewPlane) { + myPreviewPlane->setRectangularGridOffsetY(theOffset); + myPreviewPlane->reconfigureGrid(); + mySketchLabel->myWorkshop->viewer()->update(); + } +} + + +PartSet_WidgetSketchCircularGrid::PartSet_WidgetSketchCircularGrid(QWidget* theParent, PartSet_WidgetSketchLabel* theSketchLabel) +: PartSet_WidgetSketchGrid(theParent, theSketchLabel) +{ + static const double MAX_DOUBLE = std::numeric_limits::max(); + static const double MIN_DOUBLE = std::numeric_limits::min(); + + const auto stepRLabel = new QLabel(this); + stepRLabel->setText(tr("Pitch") + " R"); + + myStepRSpinBox = new PitchSpinBox(this); + + const auto aNASLabel = new QLabel(this); + aNASLabel->setText(tr("Num of angular segments")); + + myNASSpinBox = new QSpinBox(this); + myNASSpinBox->setRange(2, std::numeric_limits::max()); + myNASSpinBox->setSingleStep(2); + myNASSpinBox->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed); + myNASSpinBox->setToolTip(tr("Even numbers only.")); + myNASSpinBox->setMinimumWidth(PartSet_WidgetSketchGrid::SPIN_BOX_MIN_WIDTH); + + myLayout->addWidget(stepRLabel , 1, 1); + myLayout->addWidget(aNASLabel , 1, 2); + myLayout->addWidget(myStepRSpinBox, 2, 1); + myLayout->addWidget(myNASSpinBox , 2, 2); + + connect(myStepRSpinBox, SIGNAL(valueSet(double)), this, SLOT(onStepRChanged(double))); + connect(myNASSpinBox, SIGNAL(valueChanged(int)), this, SLOT(onNumOfAngularSegmentsChanged(int))); +} + +void PartSet_WidgetSketchCircularGrid::recongifure() +{ + retrieveSketchAndPlane(); + + if (myPreviewPlane) { + const auto stepAndNum = myPreviewPlane->getCircularGrid_dR_and_NAS(); + myStepRSpinBox->setValue(stepAndNum.first); + myNASSpinBox->setValue(stepAndNum.second); + updateSegmentsToolTip(); + + const auto offsets = myPreviewPlane->getCircularGridOffsets(); + myOffsetAngleSpinBox->setValue(PartSet_WidgetSketchGrid::clampValue(offsets.second, 360)); + myOffsetXSpinBox->setValue(offsets.first.first); + myOffsetYSpinBox->setValue(offsets.first.second); + + setEnabled(true); + } + else { + setEnabled(false); + + myStepRSpinBox->setValue(0); + myNASSpinBox->setValue(1); + updateSegmentsToolTip(); + myOffsetAngleSpinBox->setValue(0); + myOffsetXSpinBox->setValue(0); + myOffsetYSpinBox->setValue(0); + } +} + +void PartSet_WidgetSketchCircularGrid::onStepRChanged(double theStep) +{ + const double increment = PartSet_WidgetSketchGrid::reasonableOffsetIncrement(theStep); + myOffsetXSpinBox->setSingleStep(increment); + myOffsetYSpinBox->setSingleStep(increment); + + const auto sketch = std::static_pointer_cast(mySketchLabel->myFeature); + if (sketch) { + PartSet_Tools::sketchPlaneCircularGridStepR(sketch)->setValue(theStep); + mySketchLabel->mySketchDataIsModified = true; + } + + if (myPreviewPlane) { + myPreviewPlane->setCircularGridRadialStep(theStep); + myPreviewPlane->reconfigureGrid(); + mySketchLabel->myWorkshop->viewer()->update(); + } +} + +void PartSet_WidgetSketchCircularGrid::onNumOfAngularSegmentsChanged(int theNum) +{ + theNum = theNum + theNum % 2; + myNASSpinBox->setValue(theNum); + updateSegmentsToolTip(); + + const double increment = PartSet_WidgetSketchGrid::reasonableOffsetIncrement(double(360)/theNum); + myOffsetAngleSpinBox->setSingleStep(increment); + + const auto sketch = std::static_pointer_cast(mySketchLabel->myFeature); + if (sketch) { + PartSet_Tools::sketchPlaneCircularGridNumOfAngSegments(sketch)->setValue(theNum); + mySketchLabel->mySketchDataIsModified = true; + } + + if (myPreviewPlane) { + myPreviewPlane->setCircularGridNumOfAngularSegments(theNum); + myPreviewPlane->reconfigureGrid(); + mySketchLabel->myWorkshop->viewer()->update(); + } +} + +void PartSet_WidgetSketchCircularGrid::onResetClicked() +{ + if (!myPreviewPlane) + return; + + myPreviewPlane->resetCircularGrid(); + recongifure(); + + const auto sketch = std::static_pointer_cast(mySketchLabel->feature()); + if (sketch) { + myPreviewPlane->saveCircularGridPreferencesIntoSketchData(sketch); + mySketchLabel->mySketchDataIsModified = true; + } + + mySketchLabel->myWorkshop->viewer()->update(); +} + +void PartSet_WidgetSketchCircularGrid::onOffsetAngleChanged(double theOffset) +{ + const auto sketch = std::static_pointer_cast(mySketchLabel->feature()); + if (sketch) { + PartSet_Tools::sketchPlaneCircularGridOffsetAngle(sketch)->setValue(theOffset); + mySketchLabel->mySketchDataIsModified = true; + } + + if (myPreviewPlane) { + myPreviewPlane->setCircularGridOffsetA(theOffset); + myPreviewPlane->reconfigureGrid(); + mySketchLabel->myWorkshop->viewer()->update(); + } +} + +void PartSet_WidgetSketchCircularGrid::onOffsetXChanged(double theOffset) +{ + const auto sketch = std::static_pointer_cast(mySketchLabel->feature()); + if (sketch) { + PartSet_Tools::sketchPlaneCircularGridOffsetX(sketch)->setValue(theOffset); + mySketchLabel->mySketchDataIsModified = true; + } + + if (myPreviewPlane) { + myPreviewPlane->setCircularGridOffsetX(theOffset); + myPreviewPlane->reconfigureGrid(); + mySketchLabel->myWorkshop->viewer()->update(); + } +} + +void PartSet_WidgetSketchCircularGrid::onOffsetYChanged(double theOffset) +{ + const auto sketch = std::static_pointer_cast(mySketchLabel->feature()); + if (sketch) { + PartSet_Tools::sketchPlaneCircularGridOffsetY(sketch)->setValue(theOffset); + mySketchLabel->mySketchDataIsModified = true; + } + + if (myPreviewPlane) { + myPreviewPlane->setCircularGridOffsetY(theOffset); + myPreviewPlane->reconfigureGrid(); + mySketchLabel->myWorkshop->viewer()->update(); + } +} + +void PartSet_WidgetSketchCircularGrid::updateSegmentsToolTip() +{ + const int N = myNASSpinBox->value(); + QString toolTip = tr("Even numbers only.") + "\nAngle " + QString::number(180 - double(N-2)/N * 180, 'f', PartSet_WidgetSketchGrid::NUM_OF_DECIMAL_DIGITS_ROTAT) + "°"; + myNASSpinBox->setToolTip(toolTip); +} \ No newline at end of file diff --git a/src/PartSet/PartSet_WidgetSketchLabel.h b/src/PartSet/PartSet_WidgetSketchLabel.h index 2d1d1ae3a..e09c9f473 100644 --- a/src/PartSet/PartSet_WidgetSketchLabel.h +++ b/src/PartSet/PartSet_WidgetSketchLabel.h @@ -21,7 +21,6 @@ #define PartSet_WidgetSketchLabel_H #include "PartSet.h" - #include "PartSet_Tools.h" #include @@ -33,22 +32,36 @@ #include #include +#include #include class PartSet_PreviewPlanes; +class PartSet_PreviewSketchPlane; -class QLabel; class XGUI_OperationMgr; class XGUI_Workshop; class QCheckBox; -class QStackedWidget; +class QComboBox; +class QDialog; +class QSpinBox; +class QGroupBox; +class QLabel; class QLineEdit; class QPushButton; -class QDialog; +class QStackedWidget; +class QGridLayout; + +class PartSet_WidgetSketchLabel; +class ModelAPI_CompositeFeature; + + +class PartSet_WidgetSketchRectangularGrid; +class PartSet_WidgetSketchCircularGrid; + /** * \ingroup Modules -* A model widget implementation for a label which provides specific behaviour +* A model widget implementation for a label which provides specific behaviour * for sketcher starting and launching operations */ class PARTSET_EXPORT PartSet_WidgetSketchLabel : public ModuleBase_WidgetValidated @@ -65,7 +78,15 @@ public: const Config_WidgetAPI* theData, const QMap& toShowConstraints); - virtual ~PartSet_WidgetSketchLabel(); + virtual ~PartSet_WidgetSketchLabel() = default; + + virtual void setFeature( + const FeaturePtr& theFeature, + const bool theToStoreValue = false, + const bool isUpdateFlushed = true + ); + + virtual bool isModified() const { return mySketchDataIsModified; } /// Set the given wrapped value to the current widget /// This value should be processed in the widget according to the needs @@ -157,10 +178,7 @@ protected: /// Saves the internal parameters to the given feature /// \return True in success - virtual bool storeValueCustom() - { - return true; - } + virtual bool storeValueCustom(); virtual bool restoreValueCustom(); @@ -213,24 +231,32 @@ protected: virtual bool eventFilter(QObject* theObj, QEvent* theEvent); private slots: - /// A slot called on set sketch plane view + /// Called on set sketch plane view void onSetPlaneView(); /// Emits signal about check box state changed with information about ConstraintVisibleState /// \param theOn a flag show constraints or not void onShowConstraint(bool theOn); - /// A a slot called on "Change sketch plane" check box toggele + /// Called on "Change sketch plane" button is clicked. void onChangePlane(); - /// A a slot called on "Show remaining DOFs" check box toggele + /// Called on "Show remaining DOFs" button is clicked. void onShowDOF(); - /// A a slot called on changing the panel visibility + /// Called on changing the panel visibility void onShowPanel(); - /// A slot which is called on "Visible" plane checkbox toggle - void onShowViewPlane(bool); + void onShowAxes(bool); + void onShowSubstrate(bool); + + void onGridTypeChanged(int); + void onGridSnappingModeChanged(int); + + /*! \brief Must be called after PartSet_PreviewSketchPlane is configured. */ + void reconfigureSketchViewWidgets(); + + void saveSketchViewPreferenceToSkethData(); private: /// Set sketch plane by shape @@ -243,7 +269,7 @@ private: /** * Returns list of presentations which have displayed shapes with circular edges - * (circles, arcs) which are in pane of of the given sketch + * (circles, arcs) which are in plane of of the given sketch * \param theSketch - the sketch */ QList findCircularEdgesInPlane(); @@ -252,8 +278,22 @@ private: /// class to show/hide preview planes PartSet_PreviewPlanes* myPreviewPlanes; + bool mySketchDataIsModified; + QGroupBox* mySketchViewGroupBox; + QCheckBox* myViewInverted; - QCheckBox* myViewVisible; + QCheckBox* myAxesVisibleCheckBox; // Local sketch axes. + QCheckBox* mySubstrateVisibleCheckBox; + + QComboBox* myGridTypeComboBox; + QComboBox* myGridSnappingModeComboBox; + + friend class PartSet_WidgetSketchGrid; + friend class PartSet_WidgetSketchRectangularGrid; + friend class PartSet_WidgetSketchCircularGrid; + PartSet_WidgetSketchRectangularGrid* myWidgetRectangularGrid; + PartSet_WidgetSketchCircularGrid* myWidgetCircularGrid; + QCheckBox* myRemoveExternal; QCheckBox* myShowPoints; QCheckBox* myAutoConstraints; @@ -276,4 +316,134 @@ private: GeomPlanePtr myTmpPlane; }; + +class PARTSET_EXPORT PitchSpinBox : public QDoubleSpinBox +{ + Q_OBJECT +public: + PitchSpinBox(QWidget* theParent); + ~PitchSpinBox() = default; + + void setValue(double theVal); + +signals: + void valueSet(double theVal); + +private slots: + virtual void onTextChanged(); + virtual void onEditingFinished(); + +private: + double myPrevVal; +}; + + +class PARTSET_EXPORT PartSet_WidgetSketchGrid : public QWidget +{ + Q_OBJECT +public: +/*! \param theSketchLabel must not be nullptr. */ + PartSet_WidgetSketchGrid(QWidget* theParent, PartSet_WidgetSketchLabel* theSketchLabel); + virtual ~PartSet_WidgetSketchGrid() = default; + + virtual void recongifure() = 0; + +protected slots: + virtual void onResetClicked() = 0; + virtual void onOffsetAngleChanged(double theOffset) = 0; + virtual void onOffsetXChanged(double theOffset) = 0; + virtual void onOffsetYChanged(double theOffset) = 0; + +protected: + /*! \returns Modulo of theValue in (-theIntervalWidth/2; theIntervalWidth/2]. */ + static double clampValue(double theValue, double theIntervalWidth); + + /*! \returns Reasonable increment for offset spinbox for given grid pitch. */ + static double reasonableOffsetIncrement(double theStep); + + /*! \returns Reasonable increment for pitch spinbox for given grid pitch. */ + static double reasonablePitchIncrement(double theStep); + + void retrieveSketchAndPlane(); + +protected: + /** Num of digits in fractional part of translational values. */ + static const int NUM_OF_DECIMAL_DIGITS_TRANS; + + /** Num of digits in fractional part of rotational values. */ + static const int NUM_OF_DECIMAL_DIGITS_ROTAT; + + static const int SPIN_BOX_MIN_WIDTH; + +protected: + PartSet_WidgetSketchLabel* const mySketchLabel; + PartSet_PreviewSketchPlane* myPreviewPlane; + + QGridLayout* myLayout; + + QPushButton* myResetButton; + + QDoubleSpinBox* myOffsetXSpinBox; + QDoubleSpinBox* myOffsetYSpinBox; + QDoubleSpinBox* myOffsetAngleSpinBox; + + friend class PitchSpinBox; +}; + + +class PARTSET_EXPORT PartSet_WidgetSketchRectangularGrid : public PartSet_WidgetSketchGrid +{ + Q_OBJECT +public: + /*! \param theSketchLabel must not be nullptr. */ + PartSet_WidgetSketchRectangularGrid(QWidget* theParent, PartSet_WidgetSketchLabel* theSketchLabel); + virtual ~PartSet_WidgetSketchRectangularGrid() = default; + + /*! \brief Must be called after PartSet_PreviewSketchPlane is configured. */ + virtual void recongifure(); + +private slots: + void onStepXSet(double theStep); + void onStepYSet(double theStep); + +protected slots: + virtual void onResetClicked(); + virtual void onOffsetAngleChanged(double theOffset); + virtual void onOffsetXChanged(double theOffset); + virtual void onOffsetYChanged(double theOffset); + +private: + PitchSpinBox* myStepXSpinBox; + PitchSpinBox* myStepYSpinBox; +}; + + +class PARTSET_EXPORT PartSet_WidgetSketchCircularGrid : public PartSet_WidgetSketchGrid +{ + Q_OBJECT +public: + /*! \param theSketchLabel must not be nullptr. */ + PartSet_WidgetSketchCircularGrid(QWidget* theParent, PartSet_WidgetSketchLabel* theSketchLabel); + virtual ~PartSet_WidgetSketchCircularGrid() = default; + + /*! \brief Must be called after PartSet_PreviewSketchPlane is configured. */ + virtual void recongifure(); + +private slots: + void onStepRChanged(double theStep); + void onNumOfAngularSegmentsChanged(int theNum); + +protected slots: + virtual void onResetClicked(); + virtual void onOffsetAngleChanged(double theOffset); + virtual void onOffsetXChanged(double theOffset); + virtual void onOffsetYChanged(double theOffset); + +private: + void updateSegmentsToolTip(); + + PitchSpinBox* myStepRSpinBox; + QSpinBox* myNASSpinBox; +}; + #endif diff --git a/src/PartSet/PartSet_msg_fr.ts b/src/PartSet/PartSet_msg_fr.ts index 9e308a2fa..b9e730521 100644 --- a/src/PartSet/PartSet_msg_fr.ts +++ b/src/PartSet/PartSet_msg_fr.ts @@ -98,6 +98,47 @@ Error: %1 Erreur : %1 + + PartSet_WidgetSketchGrid + + Reset + Réinitialiser + + + Set default pitches and zero offsets. + Définissez le pas par défaut et les offsets zéro. + + + Offset angle,° + Offset angulaire,° + + + Offset + Offset + + + + PartSet_WidgetSketchRectangularGrid + + Pitch + Pas + + + + PartSet_WidgetSketchCircularGrid + + Pitch + Pas + + + Num of angular segments + Nombre de segments angulaires + + + Even numbers only. + Chiffres pairs uniquement. + + PartSet_WidgetSketchLabel @@ -116,10 +157,46 @@ Erreur : %1 Reversed Renversé + + Axes + Axes + + + Substrate + Substrat + Set plane view Définir la vue plane + + Construction grid + Grille de construction + + + Disabled + Désactivé + + + Rectangular + Rectangulaire + + + Circular + Circulaire + + + Don't snap + Ne t'accroche pas + + + Snap anyway + Pour s'accrocher quand même + + + Snap in proximity + Pour s'accrocher à proximité + Show geometrical constraints Afficher les contraintes géométriques diff --git a/src/SketchPlugin/SketchPlugin_Sketch.h b/src/SketchPlugin/SketchPlugin_Sketch.h index 537ff1303..87014f3ab 100644 --- a/src/SketchPlugin/SketchPlugin_Sketch.h +++ b/src/SketchPlugin/SketchPlugin_Sketch.h @@ -70,6 +70,84 @@ class SketchPlugin_Sketch : public ModelAPI_CompositeFeature//, public GeomAPI_I static const std::string MY_NORM_ID("Norm"); return MY_NORM_ID; } + + /// Size of a skectch, if it has just been created on a default plane. + inline static const std::string& DEFAULT_SIZE_ID() + { + static const std::string MY_DEFAULT_SIZE_ID_ID("DefaultSize"); + return MY_DEFAULT_SIZE_ID_ID; + } + + inline static const std::string& AXES_ENABLED_ID() + { + static const std::string MY_AXES_ENABLED_ID("AxesEnabled"); + return MY_AXES_ENABLED_ID; + } + + inline static const std::string& SUBSTRATE_ENABLED_ID() + { + static const std::string MY_SUBSTRATE_ENABLED_ID("SubstrateEnabled"); + return MY_SUBSTRATE_ENABLED_ID; + } + + inline static const std::string& CONSTRUCTION_GRID_TYPE_ID() + { + static const std::string MY_CONSTRUCTION_GRID_TYPE_ID("GridType"); + return MY_CONSTRUCTION_GRID_TYPE_ID; + } + + inline static const std::string& RECTANGULAR_CONSTRUCTION_GRID_STEP_X_ID() + { + static const std::string MY_RECTANGULAR_CONSTRUCTION_GRID_STEP_X_ID("RectangularGridStepX"); + return MY_RECTANGULAR_CONSTRUCTION_GRID_STEP_X_ID; + } + inline static const std::string& RECTANGULAR_CONSTRUCTION_GRID_STEP_Y_ID() + { + static const std::string MY_RECTANGULAR_CONSTRUCTION_GRID_STEP_Y_ID("RectangularGridStepY"); + return MY_RECTANGULAR_CONSTRUCTION_GRID_STEP_Y_ID; + } + inline static const std::string& RECTANGULAR_CONSTRUCTION_GRID_OFFSET_ANGLE_ID() + { + static const std::string MY_RECTANGULAR_CONSTRUCTION_GRID_OFFSET_ANGLE_ID("RectangularGridOffsetAngle"); + return MY_RECTANGULAR_CONSTRUCTION_GRID_OFFSET_ANGLE_ID; + } + inline static const std::string& RECTANGULAR_CONSTRUCTION_GRID_OFFSET_X_ID() + { + static const std::string MY_RECTANGULAR_CONSTRUCTION_GRID_OFFSET_X_ID("RectangularGridOffsetX"); + return MY_RECTANGULAR_CONSTRUCTION_GRID_OFFSET_X_ID; + } + inline static const std::string& RECTANGULAR_CONSTRUCTION_GRID_OFFSET_Y_ID() + { + static const std::string MY_RECTANGULAR_CONSTRUCTION_GRID_OFFSET_Y_ID("RectangularGridOffsetY"); + return MY_RECTANGULAR_CONSTRUCTION_GRID_OFFSET_Y_ID; + } + + inline static const std::string& CIRCULAR_CONSTRUCTION_GRID_STEP_R_ID() + { + static const std::string MY_CIRCULAR_CONSTRUCTION_GRID_STEP_R_ID("CircularGridStepR"); + return MY_CIRCULAR_CONSTRUCTION_GRID_STEP_R_ID; + } + inline static const std::string& CIRCULAR_CONSTRUCTION_GRID_NUM_OF_ANG_SEGMENTS_ID() + { + static const std::string MY_CIRCULAR_CONSTRUCTION_GRID_NUM_OF_ANG_SEGMENTS_ID("CircularGridNumOfAnularSegments"); + return MY_CIRCULAR_CONSTRUCTION_GRID_NUM_OF_ANG_SEGMENTS_ID; + } + inline static const std::string& CIRCULAR_CONSTRUCTION_GRID_OFFSET_ANGLE_ID() + { + static const std::string MY_CIRCULAR_CONSTRUCTION_GRID_OFFSET_ANGLE_ID("CircularGridOffsetAngle"); + return MY_CIRCULAR_CONSTRUCTION_GRID_OFFSET_ANGLE_ID; + } + inline static const std::string& CIRCULAR_CONSTRUCTION_GRID_OFFSET_X_ID() + { + static const std::string MY_CIRCULAR_CONSTRUCTION_GRID_OFFSET_X_ID("CircularGridOffsetX"); + return MY_CIRCULAR_CONSTRUCTION_GRID_OFFSET_X_ID; + } + inline static const std::string& CIRCULAR_CONSTRUCTION_GRID_OFFSET_Y_ID() + { + static const std::string MY_CIRCULAR_CONSTRUCTION_GRID_OFFSET_Y_ID("CircularGridOffsetY"); + return MY_CIRCULAR_CONSTRUCTION_GRID_OFFSET_Y_ID; + } + /// All features of this sketch (list of references) inline static const std::string& FEATURES_ID() { diff --git a/src/XGUI/XGUI_Displayer.cpp b/src/XGUI/XGUI_Displayer.cpp index 477544490..cbc346a05 100644 --- a/src/XGUI/XGUI_Displayer.cpp +++ b/src/XGUI/XGUI_Displayer.cpp @@ -771,26 +771,32 @@ Handle(SelectMgr_AndFilter) XGUI_Displayer::GetFilter() //************************************************************** bool XGUI_Displayer::displayAIS(AISObjectPtr theAIS, const bool toActivateInSelectionModes, const Standard_Integer theDisplayMode, bool theUpdateViewer) +{ + Handle(AIS_InteractiveObject) anAISIO = theAIS->impl(); + return displayAIS(anAISIO, toActivateInSelectionModes, theDisplayMode, theUpdateViewer); +} + +bool XGUI_Displayer::displayAIS(Handle(AIS_InteractiveObject) theAISIO, const bool toActivateInSelectionModes, + const Standard_Integer theDisplayMode, bool theUpdateViewer) { bool aDisplayed = false; Handle(AIS_InteractiveContext) aContext = AISContext(); - Handle(AIS_InteractiveObject) anAISIO = theAIS->impl(); - if (!aContext.IsNull() && !anAISIO.IsNull()) { - aContext->Display(anAISIO, theDisplayMode, 0, false/*update viewer*/, AIS_DS_Displayed); + if (!aContext.IsNull() && !theAISIO.IsNull()) { + aContext->Display(theAISIO, theDisplayMode, 0, false/*update viewer*/, AIS_DS_Displayed); #ifdef TINSPECTOR - if (getCallBack()) getCallBack()->Display(anAISIO); + if (getCallBack()) getCallBack()->Display(theAISIO); #endif aDisplayed = true; - aContext->Deactivate(anAISIO); + aContext->Deactivate(theAISIO); #ifdef TINSPECTOR - if (getCallBack()) getCallBack()->Deactivate(anAISIO); + if (getCallBack()) getCallBack()->Deactivate(theAISIO); #endif - aContext->Load(anAISIO); + aContext->Load(theAISIO); #ifdef TINSPECTOR - if (getCallBack()) getCallBack()->Load(anAISIO); + if (getCallBack()) getCallBack()->Load(theAISIO); #endif if (toActivateInSelectionModes) - myWorkshop->selectionActivate()->activateOnDisplay(anAISIO, theUpdateViewer); + myWorkshop->selectionActivate()->activateOnDisplay(theAISIO, theUpdateViewer); if (theUpdateViewer) updateViewer(); @@ -818,6 +824,25 @@ bool XGUI_Displayer::eraseAIS(AISObjectPtr theAIS, const bool theUpdateViewer) return aErased; } +//************************************************************** +bool XGUI_Displayer::eraseAIS(Handle(AIS_InteractiveObject) theAISIO, const bool theUpdateViewer) +{ + bool aErased = false; + Handle(AIS_InteractiveContext) aContext = AISContext(); + if (!aContext.IsNull()) { + if (!theAISIO.IsNull() && aContext->IsDisplayed(theAISIO)) { + aContext->Remove(theAISIO, false/*update viewer*/); + #ifdef TINSPECTOR + if (getCallBack()) getCallBack()->Remove(theAISIO); + #endif + aErased = true; + } + } + if (aErased && theUpdateViewer) + updateViewer(); + return aErased; +} + //************************************************************** void XGUI_Displayer::setDisplayMode(ObjectPtr theObject, DisplayMode theMode, bool theUpdateViewer) { diff --git a/src/XGUI/XGUI_Displayer.h b/src/XGUI/XGUI_Displayer.h index a720d6b58..fd31cc2bb 100644 --- a/src/XGUI/XGUI_Displayer.h +++ b/src/XGUI/XGUI_Displayer.h @@ -209,6 +209,19 @@ public: bool displayAIS(AISObjectPtr theAIS, const bool toActivateInSelectionModes, const Standard_Integer theDisplayMode = 0, bool theUpdateViewer = true); + /// Display the given AISIO. + /// This object is not added to the displayer internal map of objects + /// So, it can not be obtained from displayer. This is just a wrap method of OCC display in + /// order to perform the display with correct flags. + /// \param theAISIO is an object to display + /// \param toActivateInSelectionModes boolean value whether the presentation should be + /// activated in the current selection modes + /// \param theDisplayMode mode how the presentation should be displayed + /// \param theUpdateViewer the parameter whether the viewer should be update immediatelly + /// \return true if the object visibility state is changed + bool displayAIS(Handle(AIS_InteractiveObject) theAISIO, const bool toActivateInSelectionModes, + const Standard_Integer theDisplayMode = 0, bool theUpdateViewer = true); + /// Redisplay the shape if it was displayed /// \param theObject an object instance /// \param theUpdateViewer the parameter whether the viewer should be update immediatelly @@ -243,6 +256,11 @@ public: /// \return true if the object visibility state is changed bool eraseAIS(AISObjectPtr theAIS, const bool theUpdateViewer = true); + /// Erase the given AISIO displayed by corresponded display method + /// \param theUpdateViewer the parameter whether the viewer should be update immediatelly + /// \return true if the object visibility state is changed + bool eraseAIS(Handle(AIS_InteractiveObject) theAISIO, const bool theUpdateViewer = true); + /// Erase all presentations /// \param theUpdateViewer the parameter whether the viewer should be update immediatelly /// \return true if the object visibility state is changed