diff --git a/doc/salome/examples/repairing_operations_ex10.py b/doc/salome/examples/repairing_operations_ex10.py index 898991b4e..c2dbc160f 100644 --- a/doc/salome/examples/repairing_operations_ex10.py +++ b/doc/salome/examples/repairing_operations_ex10.py @@ -6,6 +6,8 @@ import GEOM from salome.geom import geomBuilder geompy = geomBuilder.New(salome.myStudy) +# Variant 1: using DivideEdge() + # create vertices p1 = geompy.MakeVertex(0,0,50) p2 = geompy.MakeVertex(60,0,50) @@ -27,4 +29,14 @@ edge_points = geompy.SubShapeAllSortedCentres(divide, geompy.ShapeType["VERTEX"] for point in edge_points: geompy.addToStudyInFather(divide, point, "Edge's point after divide") + +# Variant 2: using DivideEdgeByPoint() + +box = geompy.MakeBox(0,0,0, 10,10,10, theName="box") +p = geompy.MakeVertex( 3, -2, 1, theName="point to project" ) +edge = geompy.GetEdgeNearPoint( box, p, theName="edge to split") + +div = geompy.DivideEdgeByPoint( box, edge, p, theName="box (edge divided)") + + salome.sg.updateObjBrowser(1) diff --git a/doc/salome/gui/GEOM/images/divedgebypoint.png b/doc/salome/gui/GEOM/images/divedgebypoint.png new file mode 100644 index 000000000..cb80833cd Binary files /dev/null and b/doc/salome/gui/GEOM/images/divedgebypoint.png differ diff --git a/doc/salome/gui/GEOM/images/repair8.png b/doc/salome/gui/GEOM/images/repair8.png old mode 100755 new mode 100644 index d7654461b..5c30a9760 Binary files a/doc/salome/gui/GEOM/images/repair8.png and b/doc/salome/gui/GEOM/images/repair8.png differ diff --git a/doc/salome/gui/GEOM/input/add_point_on_edge_operation.doc b/doc/salome/gui/GEOM/input/add_point_on_edge_operation.doc index 4a58ee64a..587f1b968 100644 --- a/doc/salome/gui/GEOM/input/add_point_on_edge_operation.doc +++ b/doc/salome/gui/GEOM/input/add_point_on_edge_operation.doc @@ -5,44 +5,66 @@ \n To Add Point on Edge in the Main Menu select Repair - > Add Point on Edge. -\n This operation splits an edge in two new edges in accordance with the -specified mode (by length or by parameter) and a value specifying the -position of the point on edge (for example val =0.5; mode = -by length). This operation is available in OCC Viewer only. +This operation splits an edge in two new edges. +This operation is available in OCC Viewer only. -\n The \b Result will be a \b GEOM_Object. +The \b Result will be a \b GEOM_Object. -\n TUI Command: geompy.DivideEdge(Shape, EdgeID, Value, -IsByParameter) -- \em Shape is a shape which contains an edge to be divided -- \em EdgeID is the ID of the edge to be divided, if it is = -1, -then \em Shape should be an edge itself -- \em Value is a value of parameter on edge or length parameter, -depending on \em IsByParameter. -- \em IsByParameter is a boolean flag, specifying operation mode: - - \c True: \em Value is treated as a curve parameter [0..1] - - \c False: \em Value is treated as a length parameter [0..1] +\n Location of a new vertex on a selected edge can be defined two ways: +
    +
  1. We can specify a position (ranging from 0.0 to 1.0) of the + vertex on the selected edge either by length or by parameter. +

    + TUI Command: geompy.DivideEdge(Shape, EdgeID, Value, + IsByParameter) +

    + \b Arguments: Name + 1 Edge + 1 Value setting the position of + the point according to one of the selected modes. -Arguments: Name + 1 Edge + 1 Value setting the position of -the point according to one of the selected modes. + The difference between "by parameter" and "by length" modes becomes + apparent on the edges with irregular parametrization (for example, + b-splines which usually have irregular density by the length). + For example, value 0.5 "by length" on such edge will produce the point + in the middle of this edge (equidistant from both its ends); the same + 0.5 value "by parameter" will result in the point situated closer to + one of the ends (depending on the actual parametrization). -The difference between "by parameter" and "by length" modes becomes -apparent on the edges with irregular parametrization (for example, -b-splines which usually have irregular density by the length). -For example, value 0.5 "by length" on such edge will produce the point -in the middle of this edge (equidistant from both its ends); the same -0.5 value "by parameter" will result in the point situated closer to -one of the ends (depending on the actual parametrization). + \image html repair8.png + \n\n +
  2. +
  3. We can select a point that will be projected to the selected + edge to find the location of the new vertex. +

    + TUI Command: geompy.DivideEdgeByPoint(Shape, Edge, Point) +

    + \b Arguments: Name + 1 Edge + 1 Point. -\image html repair8.png + \image html divedgebypoint.png + +
  4. +
\n Example: \image html image167.png "The initial edge" -\image html image168.png "The edge split in two segments" +\image html image168.png "The edge split in two segments" -Our TUI Scripts provide you with useful examples of the use of +Our TUI Scripts provide you with useful examples of the use of \ref tui_add_point_on_edge "Repairing Operations". */ diff --git a/idl/GEOM_Gen.idl b/idl/GEOM_Gen.idl index 9004a8981..4e2fd3f8f 100644 --- a/idl/GEOM_Gen.idl +++ b/idl/GEOM_Gen.idl @@ -3722,7 +3722,7 @@ module GEOM GEOM_Object RemoveInternalFaces (in ListOfGO theSolids); /*! - * \brief Addition of a point to a given edge object. + * \brief Addition of a point to a given edge of \a theObject. * \param theObject Shape to be processed. * \param theEdgeIndex Index of edge to be divided within theObject's shape, * if -1, then theObject itself is the edge. @@ -3730,11 +3730,24 @@ module GEOM * depending on \a isByParameter. * \param isByParameter If TRUE : \a theValue is treated as a curve parameter [0..1], * if FALSE : \a theValue is treated as a length parameter [0..1] - * \return New GEOM_Object, containing processed shape. + * \return New GEOM_Object, containing the processed shape. */ GEOM_Object DivideEdge (in GEOM_Object theObject, in short theEdgeIndex, in double theValue, in boolean isByParameter); + /*! + * \brief Addition of a point to a given edge of \a theObject by projecting + * another point to the given edge. + * \param theObject Shape to be processed. + * \param theEdgeIndex Index of edge to be divided within theObject's shape, + * if -1, then theObject itself is the edge. + * \param thePoint Point to project to theEdgeIndex-th edge. + * \return New GEOM_Object, containing the processed shape. + */ + GEOM_Object DivideEdgeByPoint (in GEOM_Object theObject, + in short theEdgeIndex, + in GEOM_Object thePoint); + /*! * \brief Suppress the vertices in the wire in case if adjacent edges are C1 continuous. * \param theWire Wire to minimize the number of C1 continuous edges in. diff --git a/resources/CMakeLists.txt b/resources/CMakeLists.txt index e3afdf6cb..1dd296155 100755 --- a/resources/CMakeLists.txt +++ b/resources/CMakeLists.txt @@ -145,6 +145,7 @@ SET( _res_files planeworkingorigin.png point2.png pointonedge.png + pointonedgebypoint.png pointonface.png point_line_intersection.png polyline.png diff --git a/resources/pointonedgebypoint.png b/resources/pointonedgebypoint.png new file mode 100644 index 000000000..206851711 Binary files /dev/null and b/resources/pointonedgebypoint.png differ diff --git a/src/GEOMGUI/GEOM_images.ts b/src/GEOMGUI/GEOM_images.ts index a125bcec8..2e8f2ebdc 100644 --- a/src/GEOMGUI/GEOM_images.ts +++ b/src/GEOMGUI/GEOM_images.ts @@ -291,6 +291,10 @@ ICON_DLG_DIVIDE_EDGE pointonedge.png + + ICON_DLG_DIVIDE_EDGE_BY_PNT + pointonedgebypoint.png + ICON_DLG_ELLIPSE_PV ellipsepointvector.png diff --git a/src/GEOMGUI/GEOM_msg_en.ts b/src/GEOMGUI/GEOM_msg_en.ts index 0cba96179..f013b33de 100644 --- a/src/GEOMGUI/GEOM_msg_en.ts +++ b/src/GEOMGUI/GEOM_msg_en.ts @@ -38,6 +38,14 @@ Do you still want to delete these objects? DEVIDE_EDGE_NEW_OBJECT_NAME NewObject + + DEVIDE_EDGE_BAD_PROJ_MSG + Projection outside the edge + + + DEVIDE_EDGE_BY_PROJ_POINT + Point to project + ERROR_SHAPE_TYPE Object of incorrect type selected! diff --git a/src/GEOMImpl/GEOMImpl_HealingDriver.cxx b/src/GEOMImpl/GEOMImpl_HealingDriver.cxx index 5cb3b922c..48bca4095 100644 --- a/src/GEOMImpl/GEOMImpl_HealingDriver.cxx +++ b/src/GEOMImpl/GEOMImpl_HealingDriver.cxx @@ -145,6 +145,7 @@ Standard_Integer GEOMImpl_HealingDriver::Execute(TFunction_Logbook& log) const RemoveInternalFaces(&HI, anOriginalShape, aShape); break; case DIVIDE_EDGE: + case DIVIDE_EDGE_BY_POINT: AddPointOnEdge(&HI, anOriginalShape, aShape); break; case CHANGE_ORIENTATION: @@ -533,26 +534,44 @@ GEOMImpl_HealingDriver::RemoveInternalFaces (GEOMImpl_IHealing* theHI, //function : AddPointOnEdge //purpose : //======================================================================= -Standard_Boolean GEOMImpl_HealingDriver::AddPointOnEdge (GEOMImpl_IHealing* theHI, +Standard_Boolean GEOMImpl_HealingDriver::AddPointOnEdge (GEOMImpl_IHealing* theHI, const TopoDS_Shape& theOriginalShape, - TopoDS_Shape& theOutShape) const + TopoDS_Shape& theOutShape) const { Standard_Boolean isByParameter = theHI->GetIsByParameter(); - Standard_Integer anIndex = theHI->GetIndex(); - Standard_Real aValue = theHI->GetDevideEdgeValue(); + Standard_Integer anIndex = theHI->GetIndex(); + Standard_Real aValue = theHI->GetDevideEdgeValue(); + TopoDS_Shape pointToProject; + { + Handle(TColStd_HSequenceOfTransient) funs = theHI->GetShapes(); + if ( !funs.IsNull() && funs->Length() > 0 ) { + Handle(GEOM_Function) fun = Handle(GEOM_Function)::DownCast( funs->Value(1) ); + if ( !fun.IsNull() ) + pointToProject = fun->GetValue(); + } + } + ShHealOper_EdgeDivide aHealer (theOriginalShape); Standard_Boolean aResult = Standard_False; - if (anIndex == -1) { // apply algorythm for the whole shape which is EDGE - if (theOriginalShape.ShapeType() == TopAbs_EDGE) - aResult = aHealer.Perform(TopoDS::Edge(theOriginalShape), aValue, isByParameter); + if (anIndex == -1) { // apply algorithm for the whole shape which is EDGE + if (theOriginalShape.ShapeType() == TopAbs_EDGE) { + if ( pointToProject.IsNull() ) + aResult = aHealer.Perform(TopoDS::Edge(theOriginalShape), aValue, isByParameter); + else + aResult = aHealer.Perform(TopoDS::Edge(theOriginalShape), pointToProject); + } } else { TopTools_IndexedMapOfShape aShapes; TopExp::MapShapes(theOriginalShape, aShapes); TopoDS_Shape aEdgeShape = aShapes.FindKey(anIndex); - if (aEdgeShape.ShapeType() == TopAbs_EDGE) - aResult = aHealer.Perform(TopoDS::Edge(aEdgeShape), aValue, isByParameter); + if (aEdgeShape.ShapeType() == TopAbs_EDGE) { + if ( pointToProject.IsNull() ) + aResult = aHealer.Perform(TopoDS::Edge(aEdgeShape), aValue, isByParameter); + else + aResult = aHealer.Perform(TopoDS::Edge(aEdgeShape), pointToProject); + } } if (aResult) @@ -946,6 +965,14 @@ GetCreationInformation(std::string& theOperationName, AddParam( theParams, "By parameter", aCI.GetIsByParameter() ); AddParam( theParams, "Value", aCI.GetDevideEdgeValue() ); break; + case DIVIDE_EDGE_BY_POINT: + theOperationName = "POINT_ON_EDGE"; + if ( aCI.GetIndex() > 0 ) + AddParam( theParams, "Edge", "#" ) << aCI.GetIndex() << " of " << aCI.GetOriginal(); + else + AddParam( theParams, "Edge", aCI.GetOriginal() ); + AddParam( theParams, "Point", aCI.GetShapes() ); + break; case CHANGE_ORIENTATION: theOperationName = "CHANGE_ORIENTATION"; AddParam( theParams, "Selected shape", aCI.GetOriginal() ); diff --git a/src/GEOMImpl/GEOMImpl_IHealingOperations.cxx b/src/GEOMImpl/GEOMImpl_IHealingOperations.cxx index ab4975aa2..3a2b13305 100644 --- a/src/GEOMImpl/GEOMImpl_IHealingOperations.cxx +++ b/src/GEOMImpl/GEOMImpl_IHealingOperations.cxx @@ -764,6 +764,70 @@ Handle(GEOM_Object) GEOMImpl_IHealingOperations::DivideEdge (Handle(GEOM_Object) return aNewObject; } +//============================================================================= +/*! + * DivideEdgeByPoint + */ +//============================================================================= +Handle(GEOM_Object) +GEOMImpl_IHealingOperations::DivideEdgeByPoint (Handle(GEOM_Object) theObject, + int theIndex, + Handle(GEOM_Object) thePoint) +{ + // set error code, check parameters + SetErrorCode(KO); + + if (theObject.IsNull() || thePoint.IsNull()) + return NULL; + + Handle(GEOM_Function) aFunction, aLastFunction = theObject->GetLastFunction(); + Handle(GEOM_Function) aPointFunc = thePoint->GetLastFunction(); + if (aLastFunction.IsNull() || aPointFunc.IsNull()) + return NULL; //There is no function which creates an object to be processed + + // Add a new object + Handle(GEOM_Object) aNewObject = GetEngine()->AddObject( GetDocID(), GEOM_COPY ); + + //Add the function + aFunction = aNewObject->AddFunction(GEOMImpl_HealingDriver::GetID(), DIVIDE_EDGE_BY_POINT); + + if (aFunction.IsNull()) return NULL; + + //Check if the function is set correctly + if (aFunction->GetDriverGUID() != GEOMImpl_HealingDriver::GetID()) return NULL; + + // prepare "data container" class IHealing + GEOMImpl_IHealing HI(aFunction); + HI.SetIndex( theIndex ); + HI.SetOriginal( aLastFunction ); + + Handle(TColStd_HSequenceOfTransient) funSeq = new TColStd_HSequenceOfTransient; + funSeq->Append( aPointFunc ); + HI.SetShapes( funSeq ); + + //Compute the translation + try { + OCC_CATCH_SIGNALS; + if (!GetSolver()->ComputeFunction(aFunction)) { + SetErrorCode("Healing driver failed"); + return NULL; + } + } + catch (Standard_Failure) { + Handle(Standard_Failure) aFail = Standard_Failure::Caught(); + SetErrorCode(aFail->GetMessageString()); + return NULL; + } + + //Make a Python command + GEOM::TPythonDump(aFunction) + << aNewObject << " = geompy.DivideEdgeByPoint(" << theObject + << ", " << theIndex << ", " << thePoint << ")"; + + SetErrorCode(OK); + return aNewObject; +} + //============================================================================= /*! * FuseCollinearEdgesWithinWire diff --git a/src/GEOMImpl/GEOMImpl_IHealingOperations.hxx b/src/GEOMImpl/GEOMImpl_IHealingOperations.hxx index f8b17b371..e2af51e10 100644 --- a/src/GEOMImpl/GEOMImpl_IHealingOperations.hxx +++ b/src/GEOMImpl/GEOMImpl_IHealingOperations.hxx @@ -77,9 +77,13 @@ class GEOMImpl_IHealingOperations : public GEOM_IOperations { Standard_EXPORT Handle(GEOM_Object) RemoveInternalFaces (std::list< Handle(GEOM_Object)> & theSolids); Standard_EXPORT Handle(GEOM_Object) DivideEdge( Handle(GEOM_Object) theObject, - int theIndex, - double theValue, - bool isByParameter ); + int theIndex, + double theValue, + bool isByParameter ); + + Standard_EXPORT Handle(GEOM_Object) DivideEdgeByPoint( Handle(GEOM_Object) theObject, + int theIndex, + Handle(GEOM_Object) thePoint ); Standard_EXPORT Handle(GEOM_Object) FuseCollinearEdgesWithinWire (Handle(GEOM_Object) theWire, diff --git a/src/GEOMImpl/GEOMImpl_Types.hxx b/src/GEOMImpl/GEOMImpl_Types.hxx index 5a4be204c..715fddf09 100644 --- a/src/GEOMImpl/GEOMImpl_Types.hxx +++ b/src/GEOMImpl/GEOMImpl_Types.hxx @@ -205,10 +205,10 @@ #define DISK_THREE_PNT 2 #define DISK_R 3 -#define CYLINDER_R_H 1 -#define CYLINDER_PNT_VEC_R_H 2 -#define CYLINDER_R_H_A 3 -#define CYLINDER_PNT_VEC_R_H_A 4 +#define CYLINDER_R_H 1 +#define CYLINDER_PNT_VEC_R_H 2 +#define CYLINDER_R_H_A 3 +#define CYLINDER_PNT_VEC_R_H_A 4 #define CONE_R1_R2_H 1 #define CONE_PNT_VEC_R1_R2_H 2 @@ -321,6 +321,7 @@ #define FUSE_COLLINEAR_EDGES 10 #define SEWING_NON_MANIFOLD 11 #define REMOVE_INTERNAL_FACES 12 +#define DIVIDE_EDGE_BY_POINT 13 #define BASIC_FILLING 1 diff --git a/src/GEOM_I/GEOM_IHealingOperations_i.cc b/src/GEOM_I/GEOM_IHealingOperations_i.cc index 366875588..cd04c7626 100644 --- a/src/GEOM_I/GEOM_IHealingOperations_i.cc +++ b/src/GEOM_I/GEOM_IHealingOperations_i.cc @@ -431,6 +431,40 @@ GEOM::GEOM_Object_ptr GEOM_IHealingOperations_i::DivideEdge (GEOM::GEOM_Object_p return GetObject(aNewObject); } +//============================================================================= +/*! + * DivideEdgeByPoint + */ +//============================================================================= +GEOM::GEOM_Object_ptr +GEOM_IHealingOperations_i::DivideEdgeByPoint (GEOM::GEOM_Object_ptr theObject, + CORBA::Short theIndex, + GEOM::GEOM_Object_ptr thePoint) +{ + GEOM::GEOM_Object_var aGEOMObject; + + // Set a not done flag + GetOperations()->SetNotDone(); + + // Get the object itself + Handle(GEOM_Object) anObject = GetObjectImpl(theObject); + if (anObject.IsNull()) + return aGEOMObject._retn(); + + // Get the point + Handle(GEOM_Object) aPoint = GetObjectImpl(thePoint); + if (aPoint.IsNull()) + return aGEOMObject._retn(); + + // Perform + Handle(GEOM_Object) aNewObject = + GetOperations()->DivideEdgeByPoint( anObject, theIndex, aPoint ); + if (!GetOperations()->IsDone() || aNewObject.IsNull()) + return aGEOMObject._retn(); + + return GetObject(aNewObject); +} + //============================================================================= /*! * FuseCollinearEdgesWithinWire diff --git a/src/GEOM_I/GEOM_IHealingOperations_i.hh b/src/GEOM_I/GEOM_IHealingOperations_i.hh index 04802f639..4d851973d 100644 --- a/src/GEOM_I/GEOM_IHealingOperations_i.hh +++ b/src/GEOM_I/GEOM_IHealingOperations_i.hh @@ -78,9 +78,13 @@ class GEOM_I_EXPORT GEOM_IHealingOperations_i : GEOM::GEOM_Object_ptr RemoveInternalFaces (const GEOM::ListOfGO& theSolids); GEOM::GEOM_Object_ptr DivideEdge (GEOM::GEOM_Object_ptr theObject, - CORBA::Short theIndex, - CORBA::Double theValue, - CORBA::Boolean isByParameter); + CORBA::Short theIndex, + CORBA::Double theValue, + CORBA::Boolean isByParameter); + + GEOM::GEOM_Object_ptr DivideEdgeByPoint (GEOM::GEOM_Object_ptr theObject, + CORBA::Short theIndex, + GEOM::GEOM_Object_ptr thePoint); GEOM::GEOM_Object_ptr FuseCollinearEdgesWithinWire (GEOM::GEOM_Object_ptr theWire, const GEOM::ListOfGO& theVertices); diff --git a/src/GEOM_SWIG/GEOM_TestHealing.py b/src/GEOM_SWIG/GEOM_TestHealing.py index 7d399a252..1df42aeef 100644 --- a/src/GEOM_SWIG/GEOM_TestHealing.py +++ b/src/GEOM_SWIG/GEOM_TestHealing.py @@ -323,6 +323,12 @@ def TestDivideEdge (geompy): Id_Box = geompy.addToStudy(Box, "Box") Id_Divide = geompy.addToStudy(Divide, "Box with Divided Edge") + # using geompy.DivideEdgeByPoint() + p = geompy.MakeVertex( 30, -5, 10, theName="Point to project" ) + edge = geompy.GetEdgeNearPoint( Box, p, theName="Edge to split") + div = geompy.DivideEdgeByPoint( Box, edge, p, theName="Box (edge divided)") + assert geompy.NumberOfEdges( Box ) == geompy.NumberOfEdges( div ) - 1 + def TestFuseEdges (geompy): # create vertices diff --git a/src/GEOM_SWIG/geomBuilder.py b/src/GEOM_SWIG/geomBuilder.py index 7ba751d53..44d27b6e4 100644 --- a/src/GEOM_SWIG/geomBuilder.py +++ b/src/GEOM_SWIG/geomBuilder.py @@ -6609,6 +6609,45 @@ class geomBuilder(object, GEOM._objref_GEOM_Gen): self._autoPublish(anObj, theName, "divideEdge") return anObj + ## Addition of a point to a given edge of \a theObject by projecting + # another point to the given edge. + # @param theObject Shape to be processed. + # @param theEdgeIndex Index of edge to be divided within theObject's shape, + # if -1, then theObject itself is the edge. + # @param thePoint Point to project to theEdgeIndex-th edge. + # @param theName Object name; when specified, this parameter is used + # for result publication in the study. Otherwise, if automatic + # publication is switched on, default value is used for result name. + # + # @return New GEOM.GEOM_Object, containing processed shape. + # + # @ref tui_add_point_on_edge "Example" + @ManageTransactions("HealOp") + def DivideEdgeByPoint(self, theObject, theEdgeIndex, thePoint, theName=None): + """ + Addition of a point to a given edge of \a theObject by projecting + another point to the given edge. + + Parameters: + theObject Shape to be processed. + theEdgeIndex The edge or its index to be divided within theObject's shape, + if -1, then theObject itself is the edge. + thePoint Point to project to theEdgeIndex-th edge. + theName Object name; when specified, this parameter is used + for result publication in the study. Otherwise, if automatic + publication is switched on, default value is used for result name. + + Returns: + New GEOM.GEOM_Object, containing processed shape. + """ + # Example: see GEOM_TestHealing.py + if isinstance( theEdgeIndex, GEOM._objref_GEOM_Object ): + theEdgeIndex = self.GetSubShapeID( theObject, theEdgeIndex ) + anObj = self.HealOp.DivideEdgeByPoint(theObject, theEdgeIndex, thePoint) + RaiseIfFailed("DivideEdgeByPoint", self.HealOp) + self._autoPublish(anObj, theName, "divideEdge") + return anObj + ## Suppress the vertices in the wire in case if adjacent edges are C1 continuous. # @param theWire Wire to minimize the number of C1 continuous edges in. # @param theVertices A list of vertices to suppress. If the list @@ -9707,7 +9746,7 @@ class geomBuilder(object, GEOM._objref_GEOM_Gen): ## Perform an Archimde operation on the given shape with given parameters. # The object presenting the resulting face is returned. # @param theShape Shape to be put in water. - # @param theWeight Weight og the shape. + # @param theWeight Weight of the shape. # @param theWaterDensity Density of the water. # @param theMeshDeflection Deflection of the mesh, using to compute the section. # @param theName Object name; when specified, this parameter is used @@ -9726,7 +9765,7 @@ class geomBuilder(object, GEOM._objref_GEOM_Gen): Parameters: theShape Shape to be put in water. - theWeight Weight og the shape. + theWeight Weight of the shape. theWaterDensity Density of the water. theMeshDeflection Deflection of the mesh, using to compute the section. theName Object name; when specified, this parameter is used diff --git a/src/RepairGUI/RepairGUI_DivideEdgeDlg.cxx b/src/RepairGUI/RepairGUI_DivideEdgeDlg.cxx index 19e10b070..62715108b 100644 --- a/src/RepairGUI/RepairGUI_DivideEdgeDlg.cxx +++ b/src/RepairGUI/RepairGUI_DivideEdgeDlg.cxx @@ -38,16 +38,23 @@ #include -#include -#include -#include -#include -#include -#include -#include -#include #include +#include +#include +#include +#include #include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +enum { BY_PARAM, BY_POINT_PROJ }; //================================================================================= // class : RepairGUI_DivideEdgeDlg() @@ -61,6 +68,7 @@ RepairGUI_DivideEdgeDlg::RepairGUI_DivideEdgeDlg( GeometryGUI* theGeometryGUI, Q : GEOMBase_Skeleton( theGeometryGUI, parent, modal ) { QPixmap image0( SUIT_Session::session()->resourceMgr()->loadPixmap( "GEOM", tr( "ICON_DLG_DIVIDE_EDGE" ) ) ); + QPixmap image2( SUIT_Session::session()->resourceMgr()->loadPixmap( "GEOM", tr( "ICON_DLG_DIVIDE_EDGE_BY_PNT" ) ) ); QPixmap image1( SUIT_Session::session()->resourceMgr()->loadPixmap( "GEOM", tr( "ICON_SELECT" ) ) ); setWindowTitle( tr( "GEOM_DIVIDE_EDGE_TITLE" ) ); @@ -68,16 +76,18 @@ RepairGUI_DivideEdgeDlg::RepairGUI_DivideEdgeDlg( GeometryGUI* theGeometryGUI, Q /***************************************************************/ mainFrame()->GroupConstructors->setTitle(tr("GEOM_DIVIDE_EDGE_TITLE")); mainFrame()->RadioButton1->setIcon( image0 ); - mainFrame()->RadioButton2->setAttribute( Qt::WA_DeleteOnClose ); - mainFrame()->RadioButton2->close(); + mainFrame()->RadioButton2->setIcon( image2 ); mainFrame()->RadioButton3->setAttribute( Qt::WA_DeleteOnClose ); mainFrame()->RadioButton3->close(); - GroupPoints = new DlgRef_1SelExt( centralWidget() ); + GroupPoints = new DlgRef_2SelExt( centralWidget() ); GroupPoints->GroupBox1->setTitle( tr( "GEOM_ADD_POINT" ) ); GroupPoints->TextLabel1->setText( tr( "GEOM_EDGE" ) ); GroupPoints->PushButton1->setIcon( image1 ); GroupPoints->LineEdit1->setReadOnly( true ); + GroupPoints->TextLabel2->setText( tr( "DEVIDE_EDGE_BY_PROJ_POINT" ) ); + GroupPoints->PushButton2->setIcon( image1 ); + GroupPoints->LineEdit2->setReadOnly( true ); QRadioButton* rb1 = new QRadioButton( tr( "GEOM_BY_PARAMETER" ), GroupPoints->Box ); QRadioButton* rb2 = new QRadioButton( tr( "GEOM_BY_LENGTH" ), GroupPoints->Box ); @@ -90,13 +100,13 @@ RepairGUI_DivideEdgeDlg::RepairGUI_DivideEdgeDlg( GeometryGUI* theGeometryGUI, Q myValEdt = new SalomeApp_DoubleSpinBox( GroupPoints->Box ); initSpinBox( myValEdt, 0., 1., 0.1, "parametric_precision" ); myValEdt->setValue( 0.5 ); - QLabel* aLbl1 = new QLabel( tr( "GEOM_VALUE" ), GroupPoints->Box ); + myValLbl = new QLabel( tr( "GEOM_VALUE" ), GroupPoints->Box ); QGridLayout* l = new QGridLayout( GroupPoints->Box ); l->setMargin( 0 ); l->setSpacing( 6 ); l->addWidget( rb1, 0, 0, 1, 2 ); l->addWidget( rb2, 1, 0, 1, 2 ); - l->addWidget( aLbl1, 2, 0 ); + l->addWidget( myValLbl, 2, 0 ); l->addWidget( myValEdt, 2, 1 ); QVBoxLayout* layout = new QVBoxLayout( centralWidget() ); @@ -110,6 +120,29 @@ RepairGUI_DivideEdgeDlg::RepairGUI_DivideEdgeDlg( GeometryGUI* theGeometryGUI, Q Init(); } +//======================================================================= +//function : ConstructorsClicked +//purpose : hide/show widgets depending on a selected constructor +//======================================================================= +void RepairGUI_DivideEdgeDlg::ConstructorsClicked( int constructorId ) +{ + myIsParameterGr->button( 0 )->setShown( constructorId == BY_PARAM ); + myIsParameterGr->button( 1 )->setShown( constructorId == BY_PARAM ); + myValEdt ->setShown( constructorId == BY_PARAM ); + myValLbl ->setShown( constructorId == BY_PARAM ); + GroupPoints->TextLabel2 ->setShown( constructorId == BY_POINT_PROJ ); + GroupPoints->PushButton2 ->setShown( constructorId == BY_POINT_PROJ ); + GroupPoints->LineEdit2 ->setShown( constructorId == BY_POINT_PROJ ); + + initSelection(); + + if ( constructorId == BY_PARAM && + !GroupPoints->PushButton1->isDown() ) + GroupPoints->PushButton1->click(); + + displayPreview(); +} + //================================================================================= // function : ~RepairGUI_DivideEdgeDlg() // purpose : Destroys the object and frees any allocated resources @@ -128,25 +161,31 @@ void RepairGUI_DivideEdgeDlg::Init() myEditCurrentArgument = GroupPoints->LineEdit1; myObject = GEOM::GEOM_Object::_nil(); - myIndex = -1; + myPoint.nullify(); + myIndex = -1; + myProjectionOK = false; //myGeomGUI->SetState( 0 ); - initSelection(); /* signals and slots connections */ connect( buttonOk(), SIGNAL( clicked() ), this, SLOT( ClickOnOk() ) ); connect( buttonApply(), SIGNAL( clicked() ), this, SLOT( ClickOnApply() ) ); + connect(this, SIGNAL(constructorsClicked(int)), this, SLOT(ConstructorsClicked(int))); + connect( myValEdt, SIGNAL( valueChanged( double ) ), this, SLOT( ValueChangedInSpinBox() ) ); connect( GroupPoints->PushButton1, SIGNAL( clicked() ), this, SLOT( SetEditCurrentArgument() ) ); connect( GroupPoints->LineEdit1, SIGNAL( returnPressed() ), this, SLOT( LineEditReturnPressed() ) ); + connect( GroupPoints->PushButton2, SIGNAL( clicked() ), this, SLOT( SetEditCurrentArgument() ) ); + connect( GroupPoints->LineEdit2, SIGNAL( returnPressed() ), this, SLOT( LineEditReturnPressed() ) ); connect( ( (SalomeApp_Application*)( SUIT_Session::session()->activeApplication() ) )->selectionMgr(), SIGNAL( currentSelectionChanged() ), this, SLOT( SelectionIntoArgument() ) ); initName( tr( "DEVIDE_EDGE_NEW_OBJECT_NAME" ) ); resize(100,100); + ConstructorsClicked( BY_PARAM ); SelectionIntoArgument(); } @@ -165,6 +204,7 @@ void RepairGUI_DivideEdgeDlg::ValueChangedInSpinBox() //================================================================================= void RepairGUI_DivideEdgeDlg::displayPreview() { + myProjectionOK = false; erasePreview(); if ( myObject->_is_nil() ) return; @@ -173,7 +213,7 @@ void RepairGUI_DivideEdgeDlg::displayPreview() gp_Pnt aPnt; GEOMBase::GetShape( myObject, aShape, TopAbs_SHAPE ); - if ( myIndex != -1) { + if ( myIndex != -1 ) { TopTools_IndexedMapOfShape aShapes; TopExp::MapShapes(aShape, aShapes); aShape = aShapes.FindKey(myIndex); @@ -182,15 +222,55 @@ void RepairGUI_DivideEdgeDlg::displayPreview() if (aShape.ShapeType() == TopAbs_EDGE) { Standard_Real aFP, aLP, aP; Handle(Geom_Curve) aCurve = BRep_Tool::Curve(TopoDS::Edge(aShape), aFP, aLP); - aP = aFP + (aLP - aFP) * myValEdt->value(); - aPnt = aCurve->Value(aP); - BRepBuilderAPI_MakeVertex mkVertex (aPnt); - aShape = mkVertex.Shape(); + if ( aCurve.IsNull() ) return; + + if ( getConstructorId() == BY_PARAM ) + { + aP = aFP + (aLP - aFP) * myValEdt->value(); + aPnt = aCurve->Value(aP); + BRepBuilderAPI_MakeVertex mkVertex (aPnt); + aShape = mkVertex.Shape(); + } + else if ( getConstructorId() == BY_POINT_PROJ && myPoint ) + { + TopoDS_Shape aPoints; + GEOMBase::GetShape( myPoint.get(), aPoints, TopAbs_SHAPE ); + + BRep_Builder builder; + TopoDS_Compound compoundOfVV; + builder.MakeCompound(compoundOfVV); + aShape = compoundOfVV; + + GeomAPI_ProjectPointOnCurve aProjector; + aProjector.Init( aCurve, aFP, aLP ); + + TopTools_MapOfShape vMap; + TopExp_Explorer vertex( aPoints, TopAbs_VERTEX ); + for ( ; vertex.More(); vertex.Next() ) + { + if ( !vMap.Add( vertex.Current() )) continue; + gp_Pnt p = BRep_Tool::Pnt( TopoDS::Vertex( vertex.Current() )); + aProjector.Perform( p ); + if ( aProjector.NbPoints() > 0 ) + { + double u = aProjector.LowerDistanceParameter(); + if ( Min( u - aFP, aLP - u ) > Precision::Confusion() ) + { + builder.Add( compoundOfVV, BRepBuilderAPI_MakeVertex( aProjector.NearestPoint())); + myProjectionOK = true; + } + } + } + } + else + { + return; + } // Build prs SALOME_Prs* aPrs = getDisplayer()->BuildPrs( aShape ); if ( aPrs != 0 && !aPrs->IsNull() ) GEOMBase_Helper::displayPreview( aPrs, false, true ); - } + } } //================================================================================= @@ -218,9 +298,11 @@ bool RepairGUI_DivideEdgeDlg::ClickOnApply() myEditCurrentArgument->setText( "" ); myObject = GEOM::GEOM_Object::_nil(); + myPoint.nullify(); myIndex = -1; + myProjectionOK = false; - initSelection(); + ConstructorsClicked(getConstructorId()); return true; } @@ -235,8 +317,17 @@ void RepairGUI_DivideEdgeDlg::SelectionIntoArgument() { myEditCurrentArgument->setText( "" ); - myObject = GEOM::GEOM_Object::_nil(); - myIndex = -1; + const bool toSelectObject = ( myEditCurrentArgument == GroupPoints->LineEdit1 ); + if ( toSelectObject ) + { + myObject = GEOM::GEOM_Object::_nil(); + myIndex = -1; + } + else //if ( myEditCurrentArgument == GroupPoints->LineEdit2 ) + { + myPoint.nullify(); + myProjectionOK = false; + } LightApp_SelectionMgr* aSelMgr = myGeomGUI->getApp()->selectionMgr(); SALOME_ListIO aSelList; @@ -252,7 +343,7 @@ void RepairGUI_DivideEdgeDlg::SelectionIntoArgument() if ( GEOMBase::GetShape( aSelectedObj, aShape, TopAbs_SHAPE ) ) { const int aType = aShape.ShapeType(); - if ( aType <= TopAbs_EDGE ) // edge, wire, face, shell, solid, compound + if ( aType <= TopAbs_EDGE || !toSelectObject ) // edge, wire, face, shell, solid, compound { GEOM::short_array anIndexes; @@ -263,20 +354,29 @@ void RepairGUI_DivideEdgeDlg::SelectionIntoArgument() if ( !aMap.IsEmpty() ) // sub-shape selection { - myIndex = aMap( 1 ); - myObject = aSelectedObj; - myEditCurrentArgument->setText( aName += QString( ":edge_%1" ).arg( myIndex ) ); + if ( toSelectObject ) { + myIndex = aMap( 1 ); + myObject = aSelectedObj; + myEditCurrentArgument->setText( aName += QString( ":edge_%1" ).arg( myIndex ) ); + } + else if (( myPoint = getSelected( TopAbs_VERTEX ))) + { + myEditCurrentArgument->setText( aName += QString( ":vertex_%1" ).arg( aMap( 1 ))); + } } - else if ( aType == TopAbs_EDGE ) // single shape selection + else if ( aType == TopAbs_EDGE && toSelectObject ) // single shape selection { myIndex = -1; myObject = aSelectedObj; - myEditCurrentArgument->setText( GEOMBase::GetName( myObject ) ); + myEditCurrentArgument->setText( aName ); + } + else if ( aType == TopAbs_VERTEX && !toSelectObject ) // single shape selection + { + myPoint = aSelectedObj; + myEditCurrentArgument->setText( aName ); } else // face, shell, solid or compound was selected, and NOT its sub-shape. { - myIndex = -1; - myObject = GEOM::GEOM_Object::_nil(); } } } @@ -292,10 +392,29 @@ void RepairGUI_DivideEdgeDlg::SelectionIntoArgument() //================================================================================= void RepairGUI_DivideEdgeDlg::SetEditCurrentArgument() { - if ( sender() == GroupPoints->PushButton1 ) { + QPushButton* send = (QPushButton*)sender(); + + if ( send == GroupPoints->PushButton1 ) { GroupPoints->LineEdit1->setFocus(); myEditCurrentArgument = GroupPoints->LineEdit1; + + GroupPoints->PushButton2->setDown(false); + GroupPoints->LineEdit2->setEnabled(false); } + if ( send == GroupPoints->PushButton2 ) { + GroupPoints->LineEdit2->setFocus(); + myEditCurrentArgument = GroupPoints->LineEdit2; + + GroupPoints->PushButton1->setDown(false); + GroupPoints->LineEdit1->setEnabled(false); + } + // enable line edit + myEditCurrentArgument->setEnabled(true); + myEditCurrentArgument->setFocus(); + // after setFocus(), because it will be setDown(false) when loses focus + send->setDown(true); + + initSelection(); SelectionIntoArgument(); } @@ -310,6 +429,11 @@ void RepairGUI_DivideEdgeDlg::LineEditReturnPressed() myEditCurrentArgument = GroupPoints->LineEdit1; GEOMBase_Skeleton::LineEditReturnPressed(); } + if ( sender() == GroupPoints->LineEdit2 && + !GroupPoints->LineEdit2->isHidden() ) { + myEditCurrentArgument = GroupPoints->LineEdit2; + GEOMBase_Skeleton::LineEditReturnPressed(); + } } @@ -327,7 +451,7 @@ void RepairGUI_DivideEdgeDlg::ActivateThisDialog() myIndex = -1; //myGeomGUI->SetState( 0 ); - initSelection(); + ConstructorsClicked(getConstructorId()); } //================================================================================= @@ -356,7 +480,16 @@ GEOM::GEOM_IOperations_ptr RepairGUI_DivideEdgeDlg::createOperation() //================================================================================= bool RepairGUI_DivideEdgeDlg::isValid( QString& msg ) { - bool ok = myValEdt->isValid( msg, !IsPreview() ); + bool ok = false; + if ( getConstructorId() == BY_PARAM ) + { + ok = myValEdt->isValid( msg, !IsPreview() ); + } + else if ( getConstructorId() == BY_POINT_PROJ ) + { + if (( ok = myPoint ) && !( ok = myProjectionOK )) + msg = tr("DEVIDE_EDGE_BAD_PROJ_MSG"); + } return !myObject->_is_nil() && ok; } @@ -367,11 +500,16 @@ bool RepairGUI_DivideEdgeDlg::isValid( QString& msg ) bool RepairGUI_DivideEdgeDlg::execute( ObjectList& objects ) { GEOM::GEOM_IHealingOperations_var anOper = GEOM::GEOM_IHealingOperations::_narrow( getOperation() ); - GEOM::GEOM_Object_var anObj = anOper->DivideEdge( myObject, myIndex, myValEdt->value(), getIsByParameter() ); + GEOM::GEOM_Object_var anObj; + if ( getConstructorId() == BY_PARAM ) + anObj = anOper->DivideEdge( myObject, myIndex, myValEdt->value(), getIsByParameter() ); + else + anObj = anOper->DivideEdgeByPoint( myObject, myIndex, myPoint.get() ); + bool aResult = !anObj->_is_nil(); if ( aResult ) { - if ( !IsPreview() ) + if ( !IsPreview() && ( getConstructorId() == BY_PARAM )) { QStringList aParameters; aParameters << ""; @@ -400,6 +538,19 @@ bool RepairGUI_DivideEdgeDlg::getIsByParameter() const //================================================================================= void RepairGUI_DivideEdgeDlg::initSelection() { - GEOM::GEOM_Object_var aNullGeomObject; - localSelection( aNullGeomObject, TopAbs_EDGE ); // load local selection on ALL objects + TopAbs_ShapeEnum type = TopAbs_EDGE; + if ( myEditCurrentArgument == GroupPoints->LineEdit2 ) + type = TopAbs_VERTEX; + + globalSelection(); // close local contexts, if any + localSelection( GEOM::GEOM_Object::_nil(), type ); // load local selection on ALL objects +} + +//================================================================================= +// function : addSubshapeToStudy +// purpose : virtual method to add new SubObjects if local selection +//================================================================================= +void RepairGUI_DivideEdgeDlg::addSubshapesToStudy() +{ + GEOMBase::PublishSubObject( myPoint.get() ); } diff --git a/src/RepairGUI/RepairGUI_DivideEdgeDlg.h b/src/RepairGUI/RepairGUI_DivideEdgeDlg.h index 4b1adc3c9..129c9895c 100644 --- a/src/RepairGUI/RepairGUI_DivideEdgeDlg.h +++ b/src/RepairGUI/RepairGUI_DivideEdgeDlg.h @@ -29,7 +29,7 @@ #include -class DlgRef_1SelExt; +class DlgRef_2SelExt; class SalomeApp_DoubleSpinBox; class QButtonGroup; @@ -50,7 +50,8 @@ protected: virtual GEOM::GEOM_IOperations_ptr createOperation(); virtual bool isValid( QString& ); virtual bool execute( ObjectList& ); - + virtual void addSubshapesToStudy(); + private: void Init(); void enterEvent( QEvent* ); @@ -58,13 +59,16 @@ private: bool getIsByParameter() const; int myIndex; - + private: GEOM::GEOM_Object_var myObject; - - DlgRef_1SelExt* GroupPoints; + GEOM::GeomObjPtr myPoint; + bool myProjectionOK; + + DlgRef_2SelExt* GroupPoints; QButtonGroup* myIsParameterGr; SalomeApp_DoubleSpinBox* myValEdt; + QLabel* myValLbl; protected slots: void ClickOnOk(); @@ -76,6 +80,7 @@ protected slots: void LineEditReturnPressed(); void SelectionIntoArgument(); void SetEditCurrentArgument(); + void ConstructorsClicked( int ); }; #endif // REPAIRGUI_DIVIDEEDGEDLG_H diff --git a/src/ShHealOper/ShHealOper_EdgeDivide.cxx b/src/ShHealOper/ShHealOper_EdgeDivide.cxx index 3a910c55d..99de5efe5 100644 --- a/src/ShHealOper/ShHealOper_EdgeDivide.cxx +++ b/src/ShHealOper/ShHealOper_EdgeDivide.cxx @@ -24,24 +24,28 @@ // Created: 30.04.04 16:44:47 // Author: Galina KULIKOVA // -#include -#include -#include -#include -#include -#include #include -#include -#include -#include -#include -#include -#include -#include -#include #include -#include +#include +#include +#include +#include +#include #include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + //#include <.hxx> //#include <.hxx> //======================================================================= @@ -113,10 +117,51 @@ Standard_Boolean ShHealOper_EdgeDivide::Perform(const TopoDS_Shape& theEdge, return myDone; } //======================================================================= -//function : build +//function : Perform //purpose : //======================================================================= +Standard_Boolean ShHealOper_EdgeDivide::Perform(const TopoDS_Shape& theEdge, + const TopoDS_Shape& thePoints) +{ + myDone = Standard_False; + myErrorStatus = ShHealOper_NotError; + if(theEdge.ShapeType() != TopAbs_EDGE) { + myErrorStatus = ShHealOper_InvalidParameters; + return myDone; + } + myDivideParamMode = true; + myEdge = TopoDS::Edge(theEdge); + Handle(TColStd_HSequenceOfReal) aSeqValues = new TColStd_HSequenceOfReal; + + double aFirst,aLast; + Handle(Geom_Curve) aCurve = BRep_Tool::Curve(myEdge,aFirst,aLast); + if ( aCurve.IsNull() ) return false; + GeomAPI_ProjectPointOnCurve aProjector; + aProjector.Init( aCurve, aFirst, aLast ); + + TopTools_MapOfShape vMap; + TopExp_Explorer vertex( thePoints, TopAbs_VERTEX ); + for ( ; vertex.More(); vertex.Next() ) + { + if ( !vMap.Add( vertex.Current() )) continue; + gp_Pnt p = BRep_Tool::Pnt( TopoDS::Vertex( vertex.Current() )); + aProjector.Perform( p ); + if ( aProjector.NbPoints() > 0 ) + { + double u = double( aProjector.LowerDistanceParameter() ); + double param = ( u - aFirst ) / ( aLast - aFirst ); + aSeqValues->Append( param ); + } + } + myDone = build(aSeqValues); + return myDone; +} +//======================================================================= +//function : build +//purpose : +//======================================================================= + Standard_Boolean ShHealOper_EdgeDivide::build(const Handle(TColStd_HSequenceOfReal)& theValues) { if(myEdge.IsNull() || !theValues->Length()) { @@ -124,16 +169,16 @@ Standard_Boolean ShHealOper_EdgeDivide::build(const Handle(TColStd_HSequenceOfRe return Standard_False; } - Standard_Boolean has3d = Standard_False, - has2d = Standard_False, - hasPCurves = Standard_False; - + Standard_Boolean has3d = Standard_False, + has2d = Standard_False, + hasPCurves = Standard_False; + //computation of the split values in dependance from specified mode and values. if(!computeValues(theValues, has3d,has2d,hasPCurves)) { myErrorStatus = ShHealOper_InvalidParameters; return Standard_False; } - + //setting split values in the splitting curve tools. Handle(ShapeUpgrade_WireDivide) aSplitTool = new ShapeUpgrade_WireDivide; aSplitTool->Load(myEdge); @@ -152,7 +197,7 @@ Standard_Boolean ShHealOper_EdgeDivide::build(const Handle(TColStd_HSequenceOfRe myErrorStatus = ShHealOper_InvalidParameters; return Standard_False; } - + //split 3d curve and pcurve for each face reffering to edge. Standard_Boolean isDone = Standard_True; if(hasPCurves) { diff --git a/src/ShHealOper/ShHealOper_EdgeDivide.hxx b/src/ShHealOper/ShHealOper_EdgeDivide.hxx index 4fd3ef0d6..d552149c8 100644 --- a/src/ShHealOper/ShHealOper_EdgeDivide.hxx +++ b/src/ShHealOper/ShHealOper_EdgeDivide.hxx @@ -27,12 +27,11 @@ #ifndef ShHealOper_EdgeDivide_HeaderFile #define ShHealOper_EdgeDivide_HeaderFile +#include #include +#include #include #include -#include -#include -#include /// Class ShHealOper_EdgeDivide //Intended for spitting edge in accordance to the specified mode and value. @@ -59,7 +58,7 @@ class ShHealOper_EdgeDivide : public ShHealOper_Tool //specified mode and value. //If theDivideParamMode is equal to true edge will be splitted by parameter. //Else edge will be spliited by length (default true). - //theValue is koefficient for splitting from 0 to 1. + //theValue is coefficient for splitting from 0 to 1. Standard_EXPORT Standard_Boolean Perform(const TopoDS_Shape& theEdge, const TColStd_SequenceOfReal& theValues, @@ -67,7 +66,11 @@ class ShHealOper_EdgeDivide : public ShHealOper_Tool //Performs spitting of the specified edge in the accoradnce to //specified mode and sequence of values the same way as previous. - protected: + Standard_EXPORT Standard_Boolean Perform(const TopoDS_Shape& theEdge, + const TopoDS_Shape& thePoint); + //Performs spitting of the specified edge by projecting a point to it. + +protected: // ---------- PROTECTED METHODS ----------