]> SALOME platform Git repositories - modules/smesh.git/commitdiff
Salome HOME
bos #26454 [EDF] (2021) SMESH: interactive mesh modification
authoreap <eap@opencascade.com>
Wed, 15 Dec 2021 15:36:27 +0000 (18:36 +0300)
committerrnv <rnv@opencascade.com>
Sat, 5 Feb 2022 11:34:23 +0000 (14:34 +0300)
51 files changed:
_stash [new file with mode: 0644]
doc/salome/examples/modifying_meshes_cut_triangles.py [new file with mode: 0644]
doc/salome/examples/modifying_meshes_ex11.py
doc/salome/gui/SMESH/images/add_node_on_face-dlg.png [new file with mode: 0644]
doc/salome/gui/SMESH/images/add_node_on_face.png [new file with mode: 0644]
doc/salome/gui/SMESH/images/add_node_on_segment-dlg.png [new file with mode: 0644]
doc/salome/gui/SMESH/images/add_node_on_segment.png [new file with mode: 0644]
doc/salome/gui/SMESH/images/mesh_split_diag_interactive.png [new file with mode: 0644]
doc/salome/gui/SMESH/images/mesh_split_face_interactive.png [new file with mode: 0644]
doc/salome/gui/SMESH/images/meshtopass1.png
doc/salome/gui/SMESH/images/meshtopass2.png
doc/salome/gui/SMESH/images/meshtopass3.png [new file with mode: 0644]
doc/salome/gui/SMESH/images/remove_node_reconnection.png [new file with mode: 0644]
doc/salome/gui/SMESH/images/remove_node_reconnection_dlg.png [new file with mode: 0644]
doc/salome/gui/SMESH/images/remove_node_reconnection_icon.png [new file with mode: 0644]
doc/salome/gui/SMESH/input/add_node_on_face.rst [new file with mode: 0644]
doc/salome/gui/SMESH/input/add_node_on_segment.rst [new file with mode: 0644]
doc/salome/gui/SMESH/input/adding_quadratic_elements.rst
doc/salome/gui/SMESH/input/mesh_through_point.rst
doc/salome/gui/SMESH/input/modifying_meshes.rst
doc/salome/gui/SMESH/input/modules.rst
doc/salome/gui/SMESH/input/removing_nodes_and_elements.rst
doc/salome/gui/SMESH/input/tui_modifying_meshes.rst
idl/SMESH_MeshEditor.idl
resources/CMakeLists.txt
resources/mesh_move_node_interactive.png [new file with mode: 0644]
resources/mesh_rem_node_recon.png [new file with mode: 0644]
resources/mesh_split_diag_interactive.png [new file with mode: 0644]
resources/mesh_split_face_interactive.png [new file with mode: 0644]
src/SMDS/SMDS_ElementFactory.cxx
src/SMESH/SMESH_MeshEditor.cxx
src/SMESH/SMESH_MeshEditor.hxx
src/SMESHGUI/CMakeLists.txt
src/SMESHGUI/SMESHGUI.cxx
src/SMESHGUI/SMESHGUI_AddNodeOnFaceDlg.cxx [new file with mode: 0644]
src/SMESHGUI/SMESHGUI_AddNodeOnFaceDlg.h [new file with mode: 0644]
src/SMESHGUI/SMESHGUI_AddNodeOnSegmentDlg.cxx [new file with mode: 0644]
src/SMESHGUI/SMESHGUI_AddNodeOnSegmentDlg.h [new file with mode: 0644]
src/SMESHGUI/SMESHGUI_MakeNodeAtPointDlg.cxx
src/SMESHGUI/SMESHGUI_MakeNodeAtPointDlg.h
src/SMESHGUI/SMESHGUI_Operations.h
src/SMESHGUI/SMESHGUI_RemoveNodeReconnectionDlg.cxx [new file with mode: 0644]
src/SMESHGUI/SMESHGUI_RemoveNodeReconnectionDlg.h [new file with mode: 0644]
src/SMESHGUI/SMESH_images.ts
src/SMESHGUI/SMESH_msg_en.ts
src/SMESHUtils/SMESH_MeshAlgos.cxx
src/SMESHUtils/SMESH_MeshAlgos.hxx
src/SMESH_I/SMESH_2smeshpy.cxx
src/SMESH_I/SMESH_MeshEditor_i.cxx
src/SMESH_I/SMESH_MeshEditor_i.hxx
src/SMESH_SWIG/smeshBuilder.py

diff --git a/_stash b/_stash
new file mode 100644 (file)
index 0000000..2cb3cde
--- /dev/null
+++ b/_stash
@@ -0,0 +1,3 @@
+1) partial Fix of QuadFromMedialAxis algo
+2) Begin fix 19982 EDF 21954 - Compute mesh fails
+        
\ No newline at end of file
diff --git a/doc/salome/examples/modifying_meshes_cut_triangles.py b/doc/salome/examples/modifying_meshes_cut_triangles.py
new file mode 100644 (file)
index 0000000..250b559
--- /dev/null
@@ -0,0 +1,36 @@
+# Cutting Triangles
+
+import salome
+salome.salome_init_without_session()
+from salome.smesh import smeshBuilder
+smesh = smeshBuilder.New()
+
+# create 3 triangles and 1 segment all sharing edge 1-2
+mesh = smesh.Mesh()
+n1 = mesh.AddNode( 0, 0, 0)
+n2 = mesh.AddNode( 0, 0, -10)
+n3 = mesh.AddNode( 10, 0, 0)
+n4 = mesh.AddNode( 0, 10, 0)
+n5 = mesh.AddNode( 0, -10, 0)
+mesh.AddFace([ n1, n2, n3])
+mesh.AddFace([ n1, n2, n4])
+mesh.AddFace([ n1, n2, n5])
+mesh.AddEdge([ n1, n2] )
+
+# ===========================================================================
+# cut all the triangles and the segment by setting a new node on the segment
+# ===========================================================================
+
+mesh.AddNodeOnSegment( n1, n2, 0.6 )
+assert mesh.NbNodes() == 6     # one new node created
+assert mesh.NbTriangles() == 6 # each of the 3 triangles is split into two
+assert mesh.NbEdges() == 2     # a segment is split into two
+
+# ===============================================================
+# cut a triangle into three by adding a new node on the triangle
+# ===============================================================
+
+triangleID = 1
+mesh.AddNodeOnFace( triangleID, 2, 0, -6 )
+assert mesh.NbNodes() == 7     # one new node created
+assert mesh.NbTriangles() == 8 # the triangles is split into three
index 28b544bc0ed681898a247eb529f8d3b5488f764f..16ffe8777dd53eaf4a52d27f19781cabb1a40264 100644 (file)
@@ -1,10 +1,18 @@
 # Removing Nodes
 
 import SMESH_mechanic
-
 mesh = SMESH_mechanic.mesh
 
 # remove nodes #246 and #255
+
 res = mesh.RemoveNodes([246, 255])
 if res == 1: print("Nodes removing is OK!")
 else:        print("KO nodes removing.")
+
+
+# Removing a Node with Reconnection
+# ==================================
+
+print("Before RemoveNodeWithReconnection(): %s nodes, %s faces" % ( mesh.NbNodes(), mesh.NbFaces()))
+mesh.RemoveNodeWithReconnection( 600 )
+print("After  RemoveNodeWithReconnection(): %s nodes, %s faces" % ( mesh.NbNodes(), mesh.NbFaces()))
diff --git a/doc/salome/gui/SMESH/images/add_node_on_face-dlg.png b/doc/salome/gui/SMESH/images/add_node_on_face-dlg.png
new file mode 100644 (file)
index 0000000..64dc225
Binary files /dev/null and b/doc/salome/gui/SMESH/images/add_node_on_face-dlg.png differ
diff --git a/doc/salome/gui/SMESH/images/add_node_on_face.png b/doc/salome/gui/SMESH/images/add_node_on_face.png
new file mode 100644 (file)
index 0000000..8443493
Binary files /dev/null and b/doc/salome/gui/SMESH/images/add_node_on_face.png differ
diff --git a/doc/salome/gui/SMESH/images/add_node_on_segment-dlg.png b/doc/salome/gui/SMESH/images/add_node_on_segment-dlg.png
new file mode 100644 (file)
index 0000000..25cbb08
Binary files /dev/null and b/doc/salome/gui/SMESH/images/add_node_on_segment-dlg.png differ
diff --git a/doc/salome/gui/SMESH/images/add_node_on_segment.png b/doc/salome/gui/SMESH/images/add_node_on_segment.png
new file mode 100644 (file)
index 0000000..0601b8a
Binary files /dev/null and b/doc/salome/gui/SMESH/images/add_node_on_segment.png differ
diff --git a/doc/salome/gui/SMESH/images/mesh_split_diag_interactive.png b/doc/salome/gui/SMESH/images/mesh_split_diag_interactive.png
new file mode 100644 (file)
index 0000000..1342df9
Binary files /dev/null and b/doc/salome/gui/SMESH/images/mesh_split_diag_interactive.png differ
diff --git a/doc/salome/gui/SMESH/images/mesh_split_face_interactive.png b/doc/salome/gui/SMESH/images/mesh_split_face_interactive.png
new file mode 100644 (file)
index 0000000..5323e3f
Binary files /dev/null and b/doc/salome/gui/SMESH/images/mesh_split_face_interactive.png differ
index 8ea8af79591ee7b5fae4554d79411e325b051565..1705f5e72b556ad6c6585d8e53b491368989a969 100644 (file)
Binary files a/doc/salome/gui/SMESH/images/meshtopass1.png and b/doc/salome/gui/SMESH/images/meshtopass1.png differ
index b0bd28a8900ef08af28a396cbb0f51af982c0985..2bb53dcdaf4a7c5c39aeafc0ae617365ff7c956e 100644 (file)
Binary files a/doc/salome/gui/SMESH/images/meshtopass2.png and b/doc/salome/gui/SMESH/images/meshtopass2.png differ
diff --git a/doc/salome/gui/SMESH/images/meshtopass3.png b/doc/salome/gui/SMESH/images/meshtopass3.png
new file mode 100644 (file)
index 0000000..bdbf86f
Binary files /dev/null and b/doc/salome/gui/SMESH/images/meshtopass3.png differ
diff --git a/doc/salome/gui/SMESH/images/remove_node_reconnection.png b/doc/salome/gui/SMESH/images/remove_node_reconnection.png
new file mode 100644 (file)
index 0000000..02eef9c
Binary files /dev/null and b/doc/salome/gui/SMESH/images/remove_node_reconnection.png differ
diff --git a/doc/salome/gui/SMESH/images/remove_node_reconnection_dlg.png b/doc/salome/gui/SMESH/images/remove_node_reconnection_dlg.png
new file mode 100644 (file)
index 0000000..ecc57bd
Binary files /dev/null and b/doc/salome/gui/SMESH/images/remove_node_reconnection_dlg.png differ
diff --git a/doc/salome/gui/SMESH/images/remove_node_reconnection_icon.png b/doc/salome/gui/SMESH/images/remove_node_reconnection_icon.png
new file mode 100644 (file)
index 0000000..5b35eab
Binary files /dev/null and b/doc/salome/gui/SMESH/images/remove_node_reconnection_icon.png differ
diff --git a/doc/salome/gui/SMESH/input/add_node_on_face.rst b/doc/salome/gui/SMESH/input/add_node_on_face.rst
new file mode 100644 (file)
index 0000000..1b7cddf
--- /dev/null
@@ -0,0 +1,37 @@
+.. _add_node_on_face_page:
+
+*****************************
+Cutting a face into triangles
+*****************************
+
+This operation cuts a face into triangles by adding a node on the face and connecting the new node with face nodes.
+
+   .. image:: ../images/add_node_on_face.png
+      :align: center
+
+*To cut a face:*
+
+.. |img| image:: ../images/mesh_split_face_interactive.png
+
+#. Select a mesh and display it in the 3D Viewer.
+#. In the **Modification** menu select the **Add node to triangle** item or click *"Add node to triangle"* button |img| in the toolbar.
+
+   The following dialog box will appear:
+
+      .. image:: ../images/add_node_on_face-dlg.png
+         :align: center
+                  
+                  
+#. Enter an **ID** of the face to split either by picking it in the 3D viewer or by typing its ID.
+   
+#. Press *Selection* button in **Node location by mouse click** group to activate specifying location of a new node. You can specify it
+
+       * by clicking with your mouse on the face in the Viewer,
+       * by typing coordinates in **X, Y, Z** fields,
+       * by setting coordinates using arrows of spin boxes.
+
+#. Activate **Preview** to see a result of the operation.
+
+#. Click the **Apply** or **Apply and Close** button to confirm the operation.
+
+**See Also** a sample TUI Script of a :ref:`tui_cutting_triangles` operation.  
diff --git a/doc/salome/gui/SMESH/input/add_node_on_segment.rst b/doc/salome/gui/SMESH/input/add_node_on_segment.rst
new file mode 100644 (file)
index 0000000..9ab16bb
--- /dev/null
@@ -0,0 +1,37 @@
+.. _add_node_on_segment_page:
+
+*****************
+Cutting triangles
+*****************
+
+This operation cuts triangles into two by adding a node on an edge bounding these triangles.
+
+   .. image:: ../images/add_node_on_segment.png
+      :align: center
+
+*To cut triangles:*
+
+.. |img| image:: ../images/mesh_split_diag_interactive.png
+
+#. Select a mesh and display it in the 3D Viewer.
+#. In the **Modification** menu select the **Add node on segment** item or click *"Add node on segment"* button |img| in the toolbar.
+
+   The following dialog box will appear:
+
+      .. image:: ../images/add_node_on_segment-dlg.png
+         :align: center
+                  
+                  
+#. Enter IDs of nodes forming the edge in the **Edge** field (the node IDs must be separated by a dash) or select the edge in the 3D viewer.
+   
+#. Enter location of a new node on the edge which is defined as a real number in the range between 0.0 and 1.0. You can define it
+
+       * by clicking with your mouse on the edge in the Viewer,
+       * by typing a number in a field,
+       * by setting a number using arrows of a spin box.
+
+#. Activate **Preview** to see a result of the operation.
+
+#. Click the **Apply** or **Apply and Close** button to confirm the operation.
+
+**See Also** a sample TUI Script of a :ref:`tui_cutting_triangles` operation.  
index 2bcba5100a6e7088c5f783fea3d6156504be04ce..b706aa72432904500fcd559338ba153f1f2360e1 100644 (file)
@@ -28,10 +28,12 @@ There are several ways to create quadratic elements in your mesh:
        .. image:: ../images/image152.png
                :align: center
 
-.. note::
+   .. note::
        All dialogs for adding quadratic element to the mesh provide the possibility to automatically add an element to the specified group or to create the group anew using **Add to group** box, that allows choosing an existing group for the created node or element or giving the name to a new group. By default, the **Add to group** check box is switched off. If the user switches this check box on, the combo box listing all currently existing groups of the corresponding type becomes available. By default, no group is selected. In this case, when the user presses **Apply** or **Apply & Close** button, the warning message box informs the user about the necessity to input a new group name. The combo box lists groups of all the :ref:`three types <grouping_elements_page>`: both :ref:`standalone groups <standalone_group>`, :ref:`groups on filter <group_on_filter>`, and :ref:`groups on geometry <group_on_geom>`. If the user chooses a group on geometry or on filter, he is warned and proposed to convert this group to standalone. If the user rejects conversion operation, it is cancelled and a new node/element is not created!
 
-To create any **Quadratic Element** specify the nodes which will form your element by selecting them in the 3D viewer with pressed Shift button and click *Selection* button to the right of **Corner Nodes** label. Their numbers will appear in the dialog box as **Corner Nodes** (alternatively you can just input numbers in this field without selection; note that to use this way the mesh should be selected before invoking this operation). The edges formed by the corner nodes will appear in the table. To define the middle nodes for each edge, double-click on the respective field and input the number of the node (or pick the node in the viewer). For bi-quadratic and tri-quadratic elements, your also need to specify central nodes. As soon as all needed nodes are specified, a preview of a new quadratic element will be displayed in the 3D viewer. Then you will be able to click **Apply** or **Apply and Close** button to add the element to the mesh.
+#. To create any **Quadratic Element** specify the nodes which will form your element by selecting them in the 3D viewer with pressed Shift button and click *Selection* button to the right of **Corner Nodes** label. Their numbers will appear in the dialog box as **Corner Nodes** (alternatively you can just input numbers in this field without selection; note that to use this way the mesh should be selected before invoking this operation). The edges formed by the corner nodes will appear in the table. To define the middle nodes for each edge, double-click on the respective field and input the number of the node (or pick the node in the viewer). For bi-quadratic and tri-quadratic elements, your also need to specify central nodes. As soon as all needed nodes are specified, a preview of a new quadratic element will be displayed in the 3D viewer. 
+
+#. Click **Apply** or **Apply and Close** button to add the element to the mesh.
 
 .. image:: ../images/aqt.png
        :align: center
index 0065e2478693f117cd46f2b7acf93b4f373bcc3f..47bd8c47226f46e8619a910588e29d84817ab980 100644 (file)
@@ -8,6 +8,7 @@ In mesh you can define a node at a certain point either
 
 * by movement of the node closest to the point or 
 * by movement of a selected node to the point.
+* by movement of a selected node to the point specified by mouse click.
 
 *To displace a node:*
 
@@ -34,6 +35,11 @@ In mesh you can define a node at a certain point either
        .. centered::
                Automatic node selection
 
+       .. image:: ../images/meshtopass3.png
+               :align: center
+
+       .. centered::
+               Manual node selection with mouse location
 
 
 #. Specify the way of node selection: manually (the first radio button) or automatically (the second radio button).
index 3c9b7d1324a30465a255f83978fa8ae318022d61..60e5f84a019696225d8c650aba72a55b14785fac 100644 (file)
@@ -29,6 +29,8 @@ Salome provides a vast specter of mesh modification and transformation operation
 * :ref:`Unite two triangles <uniting_two_triangles_page>`.
 * :ref:`Unite several adjacent triangles <uniting_set_of_triangles_page>`.
 * :ref:`Cut a quadrangle <cutting_quadrangles_page>` into two triangles.
+* :ref:`Cut triangles <add_node_on_segment_page>` into two by adding a node on edge.
+* :ref:`Cut a face <add_node_on_face_page>` into triangles by adding a node on it.
 * :ref:`Split <split_to_tetra_page>` volumic elements into tetrahedra or prisms.
 * :ref:`Split bi-quadratic <split_biquad_to_linear_page>` elements into linear ones without creation of additional nodes.
 * :ref:`Smooth <smoothing_page>` elements, reducung distortions in them by adjusting the locations of nodes.
@@ -67,6 +69,8 @@ Salome provides a vast specter of mesh modification and transformation operation
    changing_orientation_of_elements.rst
    reorient_faces.rst
    cutting_quadrangles.rst
+   add_node_on_segment.rst
+   add_node_on_face.rst
    split_to_tetra.rst
    split_biquad_to_linear.rst
    smoothing.rst
index 41915c4f131f7eca8b30d0766f9fc13645a74ca2..6665aa1007ade4e706fd567a9d2b097c8e467682 100644 (file)
@@ -365,6 +365,7 @@ Removing nodes and elements
 
    Mesh.RemoveElements
    Mesh.RemoveNodes
+   Mesh.RemoveNodeWithReconnection
    Mesh.RemoveOrphanNodes
 
 Modifying nodes and elements
@@ -479,6 +480,8 @@ Cutting elements
    Mesh.SplitQuadsNearTriangularFacets
    Mesh.SplitHexaToTetras
    Mesh.SplitHexaToPrisms
+   Mesh.AddNodeOnSegment
+   Mesh.AddNodeOnFace
 
 Smoothing
 =========
index bfbb16d0c623d0b84854ae2d6e5b6b55d5349171..6d7281cfcfecf9b750e2e76caa42048dc9efb70d 100644 (file)
@@ -7,6 +7,7 @@ Removing nodes and elements
 In MESH you can remove nodes and all types of cells of your mesh.
 
 * :ref:`removing_nodes_anchor`
+* :ref:`removing_nodes_reconnect_anchor`
 * :ref:`removing_orphan_nodes_anchor`
 * :ref:`removing_elements_anchor`
 * :ref:`clear_mesh_anchor`
@@ -33,7 +34,7 @@ Removing nodes
    In this dialog box you can specify one or several nodes:
 
    * choose mesh nodes with the mouse in the 3D Viewer. It is possible to select a whole area with a mouse frame; or 
-   * input the node IDs directly in **ID Elements** field. The selected nodes will be highlighted in the viewer; or
+   * input the node IDs directly in **Node IDs** field. The selected nodes will be highlighted in the viewer; or
    * apply Filters. **Set filter** button allows to apply a filter to the selection of nodes. See more about filters in the :ref:`selection_filter_library_page` page.
 
 #. Click **Apply** or **Apply and Close** to confirm deletion of the specified nodes.
@@ -42,6 +43,38 @@ Removing nodes
        Be careful while removing nodes because if you remove a definite node of your mesh all adjacent elements will be also deleted.
 
 
+.. _removing_nodes_reconnect_anchor:
+
+Removing node with reconnection
+###############################
+
+This operation removes a node and changes surrounding faces in order to cover a hole appearing in the mesh. This operation applies to 2D triangle mesh only.
+
+       .. image:: ../images/remove_node_reconnection.png
+               :align: center
+
+**To remove a node:**
+
+.. |rmnr| image:: ../images/remove_node_reconnection_icon.png
+
+#. Select your mesh in the Object Browser or in the 3D viewer.
+#. From the **Modification** menu choose **Remove** and from the associated submenu select the **Node with reconnection**, or just click *"Remove node with reconnection"* button |rmnr| in the toolbar.
+   The following dialog box will appear:
+
+       .. image:: ../images/remove_node_reconnection_dlg.png
+               :align: center
+
+
+   In this dialog box you can specify one node to remove:
+
+   * choose a mesh node with the mouse in the 3D Viewer or 
+   * input the node ID directly in **ID** field. The selected node will be highlighted in the viewer.
+
+   Activate **Preview** to see how faces will change.
+
+#. Click **Apply** or **Apply and Close** to confirm deletion of the specified node.
+
+
 .. _removing_orphan_nodes_anchor:
 
 Removing orphan nodes
index 1509e8d9fd01ce22f733a2a99d415a2b989b698f..72e541c4dbb56f4ca674e2550e976f8eae966f2d 100644 (file)
@@ -207,6 +207,16 @@ Cutting Quadrangles
 
 :download:`Download this script <../../../examples/modifying_meshes_ex20.py>`
 
+.. _tui_cutting_triangles: 
+
+Cutting Triangles
+=================
+
+.. literalinclude:: ../../../examples/modifying_meshes_cut_triangles.py
+    :language: python
+
+:download:`Download this script <../../../examples/modifying_meshes_cut_triangles.py>`
+
 .. _modifying_meshes_split_vol: 
 
 Split Volumes into Tetrahedra
index 69f159dc224b0782663a42c4cb2b125446765a28..18ebe2a36376d26d9cb9de2181d9c7448d3e5815 100644 (file)
@@ -159,6 +159,13 @@ module SMESH
      */
     smIdType RemoveOrphanNodes() raises (SALOME::SALOME_Exception);
 
+    /*!
+     * \brief Remove a mesh node and change surrounding faces to close a hole
+     * \param nodeID node identifier
+     * \throw if mesh is not a triangle one
+     */
+    void RemoveNodeWithReconnection(in smIdType nodeID) raises (SALOME::SALOME_Exception);
+
     /*!
      * \brief Add a new node.
      * \param x X coordinate of new node
@@ -291,18 +298,44 @@ module SMESH
       raises (SALOME::SALOME_Exception);
 
 
+    /*!
+     * \brief Change node location
+     */
     boolean MoveNode(in smIdType NodeID, in double x, in double y, in double z)
       raises (SALOME::SALOME_Exception);
 
+    /*!
+     * \brief Swap a diagonal of a quadrangle formed by two adjacent triangles
+     */
     boolean InverseDiag(in smIdType NodeID1, in smIdType NodeID2) 
       raises (SALOME::SALOME_Exception);
-
+    /*!
+     * \brief Delete a diagonal of a quadrangle formed by two adjacent triangles
+     *        so that a new quadrangle appears in place of the triangles
+     */
     boolean DeleteDiag(in smIdType NodeID1, in smIdType NodeID2) 
       raises (SALOME::SALOME_Exception);
+    /*!
+     * \brief Replace each triangle bound by Node1-Node2 segment with
+     *        two triangles by connecting a node made on the segment with a node opposite
+     *        to the segment.
+     */
+    void AddNodeOnSegment(in smIdType Node1, in smIdType Node2, in double position)
+      raises (SALOME::SALOME_Exception);
+    /*!
+     * \brief Split a face into triangles by adding a new node onto the face
+     *        and connecting the new node with face nodes
+     */
+    void AddNodeOnFace(in smIdType triangle, in double x, in double y, in double z);
 
+    /*!
+     * \brief Change orientation of cells
+     */
     boolean Reorient(in smIdType_array IDsOfElements) 
       raises (SALOME::SALOME_Exception);
-
+    /*!
+     * \brief Change orientation of cells
+     */
     boolean ReorientObject(in SMESH_IDSource theObject) 
       raises (SALOME::SALOME_Exception);
     /*!
@@ -408,8 +441,8 @@ module SMESH
      *  \return 1 if 1-3 diagonal is better, 2 if 2-4
      *          diagonal is better, 0 if error occurs.
      */
-    long BestSplit (in long            IDOfQuad,
-                    in NumericalFunctor Criterion) raises (SALOME::SALOME_Exception);
+    short BestSplit (in smIdType         IDOfQuad,
+                     in NumericalFunctor Criterion) raises (SALOME::SALOME_Exception);
 
     /*!
      * \brief Split volumic elements into tetrahedrons
index d1436578a5a96e725ca04d68d2480d85727dbe12..076d1bdb9770c924a8b631553557a910939cd341 100644 (file)
@@ -122,6 +122,9 @@ SET(SMESH_RESOURCES_FILES
   mesh_minus.png
   mesh_move_node.png
   mesh_move_without_node.png
+  mesh_move_node_interactive.png
+  mesh_split_diag_interactive.png
+  mesh_split_face_interactive.png
   mesh_multi_edges.png
   mesh_multi_edges_2d.png
   mesh_node_to_point.png
@@ -158,6 +161,7 @@ SET(SMESH_RESOURCES_FILES
   mesh_rem_element.png
   mesh_rem_node.png
   mesh_rem_orphan_nodes.png
+  mesh_rem_node_recon.png
   mesh_remove.png
   mesh_renumbering_elements.png
   mesh_renumbering_nodes.png
diff --git a/resources/mesh_move_node_interactive.png b/resources/mesh_move_node_interactive.png
new file mode 100644 (file)
index 0000000..6e28f28
Binary files /dev/null and b/resources/mesh_move_node_interactive.png differ
diff --git a/resources/mesh_rem_node_recon.png b/resources/mesh_rem_node_recon.png
new file mode 100644 (file)
index 0000000..df07f47
Binary files /dev/null and b/resources/mesh_rem_node_recon.png differ
diff --git a/resources/mesh_split_diag_interactive.png b/resources/mesh_split_diag_interactive.png
new file mode 100644 (file)
index 0000000..1342df9
Binary files /dev/null and b/resources/mesh_split_diag_interactive.png differ
diff --git a/resources/mesh_split_face_interactive.png b/resources/mesh_split_face_interactive.png
new file mode 100644 (file)
index 0000000..5323e3f
Binary files /dev/null and b/resources/mesh_split_face_interactive.png differ
index 611511916dd3fd57ae6767f97408eb145436f6ad..dda4337c0ca814553eb4a25c0a064790717bbecc 100644 (file)
@@ -649,6 +649,13 @@ void SMDS_ElementChunk::SetVTKID( const SMDS_MeshElement* e, const vtkIdType vtk
     }
     myFactory->mySmdsIDs[ vtkID ] = e->GetID() - 1;
   }
+  else
+  {
+    if ((size_t) e->GetID() <= myFactory->myVtkIDs.size() )
+      myFactory->myVtkIDs[ e->GetID() - 1 ] = vtkID;
+    if ((size_t) vtkID < myFactory->mySmdsIDs.size() )
+      myFactory->mySmdsIDs[ vtkID ] = e->GetID() - 1;
+  }
 }
 
 //================================================================================
index 853d59b3e8428070ca9dc8cde48fb3a4b11db31a..0ef9f48c7ad72bdb9c9b207e75e3199462eafe3a 100644 (file)
@@ -412,8 +412,8 @@ SMDS_MeshElement* SMESH_MeshEditor::AddElement(const vector<smIdType> & nodeIDs,
 //           Modify a compute state of sub-meshes which become empty
 //=======================================================================
 
-smIdType SMESH_MeshEditor::Remove (const list< smIdType >& theIDs,
-                              const bool         isNodes )
+smIdType SMESH_MeshEditor::Remove (const std::list< smIdType >& theIDs,
+                                   const bool                   isNodes )
 {
   ClearLastCreated();
 
@@ -474,10 +474,87 @@ smIdType SMESH_MeshEditor::Remove (const list< smIdType >& theIDs,
   return removed;
 }
 
+//================================================================================
+/*!
+ * \brief Remove a node and fill a hole appeared, by changing surrounding faces
+ */
+//================================================================================
+
+void SMESH_MeshEditor::RemoveNodeWithReconnection( const SMDS_MeshNode* node )
+{
+  if ( ! node )
+    return;
+
+  if ( node->NbInverseElements( SMDSAbs_Volume ) > 0 )
+    throw SALOME_Exception( "RemoveNodeWithReconnection() applies to 2D mesh only" );
+
+  // check that only triangles surround the node
+  for ( SMDS_ElemIteratorPtr fIt = node->GetInverseElementIterator( SMDSAbs_Face ); fIt->more(); )
+  {
+    const SMDS_MeshElement* face = fIt->next();
+    if ( face->GetGeomType() != SMDSGeom_TRIANGLE )
+      throw SALOME_Exception( "RemoveNodeWithReconnection() applies to triangle mesh only" );
+    if ( face->IsQuadratic() )
+      throw SALOME_Exception( "RemoveNodeWithReconnection() applies to linear mesh only" );
+  }
+
+  std::vector< const SMDS_MeshNode*> neighbours(2);
+  SMESH_MeshAlgos::IsOn2DBoundary( node, & neighbours );
+
+  bool toRemove = ( neighbours.size() > 2 ); // non-manifold ==> just remove
+
+  // if ( neighbours.size() == 2 ) // on boundary
+  // {
+  //   // check if theNode and neighbours are on a line
+  //   gp_Pnt pN = SMESH_NodeXYZ( node );
+  //   gp_Pnt p0 = SMESH_NodeXYZ( neighbours[0] );
+  //   gp_Pnt p1 = SMESH_NodeXYZ( neighbours[1] );
+  //   double dist01 = p0.Distance( p1 );
+  //   double    tol = 0.01 * dist01;
+  //   double  distN = ( gp_Vec( p0, p1 ) ^ gp_Vec( p0, pN )).Magnitude() / dist01;
+  //   bool   onLine = distN < tol;
+  //   toRemove = !onLine;
+  // }
+
+  if ( neighbours.empty() ) // not on boundary
+  {
+    TIDSortedElemSet linkedNodes;
+    GetLinkedNodes( node, linkedNodes, SMDSAbs_Face );
+    for ( const SMDS_MeshElement* e : linkedNodes ) neighbours.push_back( cast2Node( e ));
+    if ( neighbours.empty() )
+      toRemove = true;
+  }
+
+  if ( toRemove )
+  {
+    this->Remove( std::list< smIdType >( 1, node->GetID() ), /*isNode=*/true );
+    return;
+  }
+
+  // choose a node to replace by
+  const SMDS_MeshNode* nToReplace = nullptr;
+  SMESH_NodeXYZ           nodeXYZ = node;
+  double                  minDist = Precision::Infinite();
+  for ( const SMDS_MeshNode* n : neighbours )
+  {
+    double dist = nodeXYZ.SquareDistance( n );
+    if ( dist < minDist )
+    {
+      minDist = dist;
+      nToReplace = n;
+    }
+  }
+
+  // remove node + replace by nToReplace
+  std::list< const SMDS_MeshNode* > nodeGroup = { nToReplace, node };
+  TListOfListOfNodes nodesToMerge( 1, nodeGroup );
+  this->MergeNodes( nodesToMerge );
+}
+
 //================================================================================
 /*!
  * \brief Create 0D elements on all nodes of the given object.
- *  \param elements - Elements on whose nodes to create 0D elements; if empty, 
+ *  \param elements - Elements on whose nodes to create 0D elements; if empty,
  *                    the all mesh is treated
  *  \param all0DElems - returns all 0D elements found or created on nodes of \a elements
  *  \param duplicateElements - to add one more 0D element to a node or not
@@ -947,24 +1024,24 @@ bool getQuadrangleNodes(const SMDS_MeshNode *    theQuadNodes [],
 {
   if( tr1->NbNodes() != tr2->NbNodes() )
     return false;
+
   // find the 4-th node to insert into tr1
   const SMDS_MeshNode* n4 = 0;
   SMDS_ElemIteratorPtr it = tr2->nodesIterator();
-  int i=0;
-  while ( !n4 && i<3 ) {
+  for ( int i = 0; !n4 && i < 3; ++i )
+  {
     const SMDS_MeshNode * n = cast2Node( it->next() );
-    i++;
     bool isDiag = ( n == theNode1 || n == theNode2 );
     if ( !isDiag )
       n4 = n;
   }
+
   // Make an array of nodes to be in a quadrangle
   int iNode = 0, iFirstDiag = -1;
   it = tr1->nodesIterator();
-  i=0;
-  while ( i<3 ) {
+  for ( int i = 0; i < 3; ++i )
+  {
     const SMDS_MeshNode * n = cast2Node( it->next() );
-    i++;
     bool isDiag = ( n == theNode1 || n == theNode2 );
     if ( isDiag ) {
       if ( iFirstDiag < 0 )
@@ -1079,6 +1156,210 @@ bool SMESH_MeshEditor::DeleteDiag (const SMDS_MeshNode * theNode1,
   return true;
 }
 
+//=======================================================================
+//function : SplitEdge
+//purpose  : Replace each triangle bound by theNode1-theNode2 segment with
+//           two triangles by connecting a node made on the link with a node opposite to the link.
+//=======================================================================
+
+void SMESH_MeshEditor::SplitEdge (const SMDS_MeshNode * theNode1,
+                                  const SMDS_MeshNode * theNode2,
+                                  double                thePosition)
+{
+  ClearLastCreated();
+
+  SMESHDS_Mesh * mesh = GetMeshDS();
+
+  // Get triangles and segments to divide
+
+  std::vector<const SMDS_MeshNode *> diagNodes = { theNode1, theNode2 };
+  std::vector<const SMDS_MeshElement *> foundElems;
+  if ( !mesh->GetElementsByNodes( diagNodes, foundElems ) || foundElems.empty() )
+    throw SALOME_Exception( SMESH_Comment("No triangle is bound by the edge ")
+                            << theNode1->GetID() << " - " << theNode2->GetID());
+
+  SMESH_MesherHelper helper( *GetMesh() );
+
+  for ( const SMDS_MeshElement * elem : foundElems )
+  {
+    SMDSAbs_ElementType type = elem->GetType();
+    switch ( type ) {
+    case SMDSAbs_Volume:
+      throw SALOME_Exception( "Can't split an edge of a volume");
+      break;
+
+    case SMDSAbs_Face:
+      if ( elem->GetGeomType() != SMDSGeom_TRIANGLE )
+        throw SALOME_Exception( "Can't split an edge of a face of type other than triangle");
+      if ( elem->IsQuadratic() )
+      {
+        helper.SetIsQuadratic( true );
+        helper.AddTLinks( static_cast< const SMDS_MeshFace*>( elem ));
+        helper.SetIsBiQuadratic( elem->GetEntityType() == SMDSEntity_BiQuad_Triangle );
+      }
+      break;
+
+    case SMDSAbs_Edge:
+      if ( elem->IsQuadratic() )
+      {
+        helper.SetIsQuadratic( true );
+        helper.AddTLinks( static_cast< const SMDS_MeshEdge*>( elem ));
+      }
+      break;
+    default:;
+    }
+  }
+
+  // Make a new node
+
+  const SMDS_MeshNode* nodeOnLink = helper.GetMediumNode( theNode1, theNode2,/*force3d=*/false );
+
+  gp_Pnt newNodeXYZ = ( SMESH_NodeXYZ( theNode1 ) * ( 1 - thePosition ) +
+                        SMESH_NodeXYZ( theNode2 ) * thePosition );
+
+  const TopoDS_Shape& S = mesh->IndexToShape( nodeOnLink->GetShapeID() );
+  if ( !S.IsNull() && S.ShapeType() == TopAbs_FACE ) // find newNodeXYZ by UV on FACE
+  {
+    Handle(ShapeAnalysis_Surface) surface = helper.GetSurface( TopoDS::Face( S ));
+    double  tol = 100 * helper.MaxTolerance( S );
+    gp_Pnt2d uv = surface->ValueOfUV( newNodeXYZ, tol );
+    if ( surface->Gap() < SMESH_NodeXYZ( theNode1 ).Distance( theNode2 ))
+    {
+      newNodeXYZ = surface->Value( uv );
+      if ( SMDS_FacePositionPtr nPos = nodeOnLink->GetPosition())
+        nPos->SetParameters( uv.X(), uv.Y() );
+    }
+  }
+  if ( !S.IsNull() && S.ShapeType() == TopAbs_EDGE ) // find newNodeXYZ by param on EDGE
+  {
+    mesh->MoveNode( nodeOnLink, newNodeXYZ.X(), newNodeXYZ.Y(), newNodeXYZ.Z() );
+    double u = Precision::Infinite(), tol = 100 * helper.MaxTolerance( S ), distXYZ[4];
+    helper.ToFixNodeParameters( true );
+    if ( helper.CheckNodeU( TopoDS::Edge( S ), nodeOnLink, u, tol, /*force3D=*/false, distXYZ ))
+      newNodeXYZ.SetCoord( distXYZ[1], distXYZ[2], distXYZ[3] );
+  }
+  mesh->MoveNode( nodeOnLink, newNodeXYZ.X(), newNodeXYZ.Y(), newNodeXYZ.Z() );
+
+  // Split triangles and segments
+
+  std::vector<const SMDS_MeshNode *> nodes( 7 );
+  for ( const SMDS_MeshElement * elem : foundElems )
+  {
+    nodes.assign( elem->begin_nodes(), elem->end_nodes() );
+    nodes.resize( elem->NbCornerNodes() + 1 );
+    nodes.back() = nodes[0];
+
+    smIdType id = elem->GetID();
+    int shapeID = elem->GetShapeID();
+
+    const SMDS_MeshNode* centralNode = nullptr;
+    if ( elem->GetEntityType() == SMDSEntity_BiQuad_Triangle )
+      centralNode = elem->GetNode( 6 );
+
+    mesh->RemoveFreeElement( elem, /*sm=*/0, /*fromGroups=*/false );
+    if ( centralNode )
+      mesh->RemoveFreeNode( centralNode, /*sm=*/0, /*fromGroups=*/true );
+
+    for ( size_t i = 1; i < nodes.size(); ++i )
+    {
+      const SMDS_MeshNode* n1 = nodes[i-1];
+      const SMDS_MeshNode* n2 = nodes[i];
+      const SMDS_MeshElement* newElem;
+      if ( nodes.size() == 4 ) //    triangle
+      {
+        bool isDiag1 = ( n1 == theNode1 || n1 == theNode2 );
+        bool isDiag2 = ( n2 == theNode1 || n2 == theNode2 );
+        if ( isDiag1 && isDiag2 )
+          continue;
+
+        newElem = helper.AddFace( n1, n2, nodeOnLink, id );
+      }
+      else //    segment
+      {
+        newElem = helper.AddEdge( n1, nodeOnLink, id );
+      }
+      myLastCreatedElems.push_back( newElem );
+      AddToSameGroups( newElem, elem, mesh );
+      if ( shapeID )
+        mesh->SetMeshElementOnShape( newElem, shapeID );
+      id = 0;
+    }
+  }
+  return;
+}
+
+//=======================================================================
+//function : SplitFace
+//purpose  : Split a face into triangles each formed by two nodes of the 
+//           face and a new node added at the given coordinates.
+//=======================================================================
+
+void SMESH_MeshEditor::SplitFace (const SMDS_MeshElement * theFace,
+                                  double                   theX,
+                                  double                   theY,
+                                  double                   theZ )
+{
+  ClearLastCreated();
+
+  if ( !theFace )
+    throw SALOME_Exception("Null face given");
+  if ( theFace->GetType() != SMDSAbs_Face )
+    throw SALOME_Exception("Not a face given");
+
+  SMESHDS_Mesh * mesh = GetMeshDS();
+
+  SMESH_MesherHelper helper( *GetMesh() );
+  if ( theFace->IsQuadratic() )
+  {
+    helper.SetIsQuadratic( true );
+    helper.AddTLinks( static_cast< const SMDS_MeshFace*>( theFace ));
+  }
+  const TopoDS_Shape& shape = mesh->IndexToShape( theFace->GetShapeID() );
+  helper.SetSubShape( shape );
+  helper.SetElementsOnShape( true );
+
+  // Make a new node
+
+  const SMDS_MeshNode* centralNode = nullptr;
+  if (      theFace->GetEntityType() == SMDSEntity_BiQuad_Triangle )
+    centralNode = theFace->GetNode( 6 );
+  else if ( theFace->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
+    centralNode = theFace->GetNode( 8 );
+
+  if ( centralNode )
+  {
+    helper.SetIsBiQuadratic( true );
+    mesh->MoveNode( centralNode, theX, theY, theZ );
+  }
+  else
+    centralNode = helper.AddNode( theX, theY, theZ );
+
+
+  // Split theFace
+
+  std::vector<const SMDS_MeshNode *> nodes( theFace->NbNodes() + 1 );
+  nodes.assign( theFace->begin_nodes(), theFace->end_nodes() );
+  nodes.resize( theFace->NbCornerNodes() + 1 );
+  nodes.back() = nodes[0];
+
+  smIdType id = theFace->GetID();
+  int shapeID = theFace->GetShapeID();
+
+  mesh->RemoveFreeElement( theFace, /*sm=*/0, /*fromGroups=*/false );
+
+  for ( size_t i = 1; i < nodes.size(); ++i )
+  {
+    const SMDS_MeshElement* newElem = helper.AddFace( nodes[i-1], nodes[i], centralNode, id );
+
+    myLastCreatedElems.push_back( newElem );
+    AddToSameGroups( newElem, theFace, mesh );
+    if ( shapeID )
+      mesh->SetMeshElementOnShape( newElem, shapeID );
+    id = 0;
+  }
+  return;
+}
+
 //=======================================================================
 //function : Reorient
 //purpose  : Reverse theElement orientation
index 6962f2c2db4d14a56b03c023cd78cc71f36d6686..5bbc914ed4759ca0ecdc5f27b1b346aa4b382241 100644 (file)
@@ -126,6 +126,10 @@ public:
    */
   SMDS_MeshElement* AddElement(const std::vector<smIdType> & nodeIDs,
                                const ElemFeatures&           features);
+  /*!
+   * \brief Remove a node and fill a hole appeared by changing surrounding faces
+   */
+  void RemoveNodeWithReconnection( const SMDS_MeshNode* node );
 
   smIdType Remove (const std::list< smIdType >& theElemIDs, const bool isNodes);
   // Remove a node or an element.
@@ -155,6 +159,20 @@ public:
   // with a quadrangle built on the same 4 nodes.
   // Return false if proper faces not found
 
+  void SplitEdge (const SMDS_MeshNode * theNode1,
+                  const SMDS_MeshNode * theNode2,
+                  double                thePosition);
+  // Replace each triangle bound by theNode1-theNode2 link
+  // with two triangles by connecting a node made on the link with a node opposite to the link.
+
+  void SplitFace (const SMDS_MeshElement * theFace,
+                  double                   theX,
+                  double                   theY,
+                  double                   theZ );
+  // Split a face into triangles each formed by two nodes of the face and a new node added
+  // at the given coordinates.
+
+
   bool Reorient (const SMDS_MeshElement * theElement);
   // Reverse theElement orientation
 
index 30179b73a120c96612dc4338047a2aabe2a1848e..16eda3a3d0d7b9a50b2ae0b6ff4a068229c3b720 100644 (file)
@@ -153,6 +153,9 @@ SET(_moc_HEADERS
   SMESHGUI_HomardAdaptDlg.h
   SMESHGUI_HomardBoundaryDlg.h
   SMESHGUI_HomardListGroup.h
+  SMESHGUI_RemoveNodeReconnectionDlg.h
+  SMESHGUI_AddNodeOnSegmentDlg.h
+  SMESHGUI_AddNodeOnFaceDlg.h
 )
 
 # header files / no moc processing
@@ -275,6 +278,9 @@ SET(_other_SOURCES
   SMESHGUI_HomardAdaptDlg.cxx
   SMESHGUI_HomardBoundaryDlg.cxx
   SMESHGUI_HomardListGroup.cxx
+  SMESHGUI_RemoveNodeReconnectionDlg.cxx
+  SMESHGUI_AddNodeOnSegmentDlg.cxx
+  SMESHGUI_AddNodeOnFaceDlg.cxx
 )
 
 # sources / to compile
index 0c2778ad5ba3d82f47ccbfc1bff115cc6f2b538f..25b5b8762b8f4216505d04c35f87bf896d43a154 100644 (file)
@@ -32,6 +32,8 @@
 #include "SMESHGUI.h"
 #include "SMESHGUI_Add0DElemsOnAllNodesDlg.h"
 #include "SMESHGUI_AddMeshElementDlg.h"
+#include "SMESHGUI_AddNodeOnSegmentDlg.h"
+#include "SMESHGUI_AddNodeOnFaceDlg.h"
 #include "SMESHGUI_AddQuadraticElementDlg.h"
 #include "SMESHGUI_BuildCompoundDlg.h"
 #include "SMESHGUI_ClippingDlg.h"
@@ -79,6 +81,7 @@
 #include "SMESHGUI_Preferences_ScalarBarDlg.h"
 #include "SMESHGUI_PropertiesDlg.h"
 #include "SMESHGUI_RemoveElementsDlg.h"
+#include "SMESHGUI_RemoveNodeReconnectionDlg.h"
 #include "SMESHGUI_RemoveNodesDlg.h"
 #include "SMESHGUI_RenumberingDlg.h"
 #include "SMESHGUI_ReorientFacesDlg.h"
@@ -3549,6 +3552,14 @@ bool SMESHGUI::OnGUIEvent( int theCommandID )
       }
       break;
     }
+  case SMESHOp::OpRemoveNodeWithReconn:
+    {
+      if(isStudyLocked()) break;
+      if ( warnOnGeomModif() )
+        break; // action forbidden as geometry modified
+      startOperation( SMESHOp::OpRemoveNodeWithReconn );
+      break;
+    }
   case SMESHOp::OpRemoveElements:                                    // REMOVES ELEMENTS
     {
       if(isStudyLocked()) break;
@@ -3807,6 +3818,24 @@ bool SMESHGUI::OnGUIEvent( int theCommandID )
     startOperation( SMESHOp::OpMoveNode );
     break;
 
+  case SMESHOp::OpMoveNodeInteractive:
+    if ( warnOnGeomModif() )
+      break; // action forbidden as geometry modified
+    startOperation( SMESHOp::OpMoveNodeInteractive );
+    break;
+
+  case SMESHOp::OpSplitEdgeInteract:
+    if ( warnOnGeomModif() )
+      break; // action forbidden as geometry modified
+    startOperation( SMESHOp::OpSplitEdgeInteract );
+    break;
+
+  case SMESHOp::OpSplitFaceInteract:
+    if ( warnOnGeomModif() )
+      break; // action forbidden as geometry modified
+    startOperation( SMESHOp::OpSplitFaceInteract );
+    break;
+
   case SMESHOp::OpDuplicateNodes:
     {
       if(isStudyLocked()) break;
@@ -4214,6 +4243,7 @@ void SMESHGUI::initialize( CAM_Application* app )
   createSMESHAction( SMESHOp::OpRemoveNodes,       "REMOVE_NODES",          "ICON_DLG_REM_NODE" );
   createSMESHAction( SMESHOp::OpRemoveElements,    "REMOVE_ELEMENTS",       "ICON_DLG_REM_ELEMENT" );
   createSMESHAction( SMESHOp::OpRemoveOrphanNodes, "REMOVE_ORPHAN_NODES",   "ICON_DLG_REM_ORPHAN_NODES" );
+  createSMESHAction( SMESHOp::OpRemoveNodeWithReconn, "REMOVE_NODE_RECON",  "ICON_REM_NODE_RECON" );
   createSMESHAction( SMESHOp::OpClearMesh,         "CLEAR_MESH",            "ICON_CLEAR_MESH" );
 
   //createSMESHAction( SMESHOp::OpRenumberingNodes,    "RENUM_NODES",     "ICON_DLG_RENUMBERING_NODES" );
@@ -4228,6 +4258,9 @@ void SMESHGUI::initialize( CAM_Application* app )
   createSMESHAction( SMESHOp::OpMergeNodes,             "MERGE",           "ICON_SMESH_MERGE_NODES" );
   createSMESHAction( SMESHOp::OpMergeElements,          "MERGE_ELEMENTS",  "ICON_DLG_MERGE_ELEMENTS" );
   createSMESHAction( SMESHOp::OpMoveNode,               "MESH_THROU_POINT","ICON_DLG_MOVE_NODE" );
+  createSMESHAction( SMESHOp::OpMoveNodeInteractive,    "MOVE_NODE_INTRCT","ICON_DLG_MOVE_NODE_INTERACTIVE" );
+  createSMESHAction( SMESHOp::OpSplitEdgeInteract,  "SPLIT_DIAG_INTRC","ICON_SPLIT_DIAG_INTERACTIVE" );
+  createSMESHAction( SMESHOp::OpSplitFaceInteract,      "SPLIT_FACE_INTRC","ICON_SPLIT_FACE_INTERACTIVE" );
   createSMESHAction( SMESHOp::OpDuplicateNodes,         "DUPLICATE_NODES", "ICON_SMESH_DUPLICATE_NODES" );
   createSMESHAction( SMESHOp::OpDiagonalInversion,      "INV",             "ICON_DLG_MESH_DIAGONAL" );
   createSMESHAction( SMESHOp::OpUnionOfTwoTriangle,     "UNION2",          "ICON_UNION2TRI" );
@@ -4465,13 +4498,14 @@ void SMESHGUI::initialize( CAM_Application* app )
   createMenu( SMESHOp::OpQuadraticHexahedron,    addId, -1 );
   createMenu( SMESHOp::OpTriQuadraticHexahedron, addId, -1 );
 
-  createMenu( SMESHOp::OpRemoveNodes,       removeId, -1 );
-  createMenu( SMESHOp::OpRemoveElements,    removeId, -1 );
-  createMenu( SMESHOp::OpRemoveOrphanNodes, removeId, -1 );
-  createMenu( separator(),                  removeId, -1 );
-  createMenu( SMESHOp::OpDeleteGroup,       removeId, -1 );
-  createMenu( separator(),                  removeId, -1 );
-  createMenu( SMESHOp::OpClearMesh,         removeId, -1 );
+  createMenu( SMESHOp::OpRemoveNodes,          removeId, -1 );
+  createMenu( SMESHOp::OpRemoveElements,       removeId, -1 );
+  createMenu( SMESHOp::OpRemoveOrphanNodes,    removeId, -1 );
+  createMenu( SMESHOp::OpRemoveNodeWithReconn, removeId, -1 );
+  createMenu( separator(),                     removeId, -1 );
+  createMenu( SMESHOp::OpDeleteGroup,          removeId, -1 );
+  createMenu( separator(),                     removeId, -1 );
+  createMenu( SMESHOp::OpClearMesh,            removeId, -1 );
 
   //createMenu( SMESHOp::OpRenumberingNodes,    renumId, -1 );
   //createMenu( SMESHOp::OpRenumberingElements, renumId, -1 );
@@ -4500,6 +4534,8 @@ void SMESHGUI::initialize( CAM_Application* app )
   createMenu( SMESHOp::OpCuttingOfQuadrangles,   modifyId, -1 );
   createMenu( SMESHOp::OpSplitVolumes,           modifyId, -1 );
   createMenu( SMESHOp::OpSplitBiQuadratic,       modifyId, -1 );
+  createMenu( SMESHOp::OpSplitEdgeInteract,  modifyId, -1 );
+  createMenu( SMESHOp::OpSplitFaceInteract,      modifyId, -1 );
   createMenu( SMESHOp::OpSmoothing,              modifyId, -1 );
   createMenu( SMESHOp::OpPatternMapping,         modifyId, -1 );
 
@@ -4655,6 +4691,12 @@ void SMESHGUI::initialize( CAM_Application* app )
   createTool( SMESHOp::OpSmoothing,              modifyTb );
   createTool( SMESHOp::OpPatternMapping,         modifyTb );
 
+  int interactTb = createTool( tr( "TB_INTERACT" ), QString( "SMESHInteractiveToolbar" ) ) ;
+  createTool( SMESHOp::OpMoveNodeInteractive,   interactTb );
+  createTool( SMESHOp::OpRemoveNodeWithReconn,  interactTb );
+  createTool( SMESHOp::OpSplitEdgeInteract, interactTb );
+  createTool( SMESHOp::OpSplitFaceInteract,     interactTb );
+
   // Adaptation - begin
 #if !defined(DISABLE_MG_ADAPT) || !defined(DISABLE_HOMARD_ADAPT)
   int adaptTb = createTool( tr( "TB_ADAPTATION" ), QString( "SMESHAdaptationToolbar" ) ) ;
@@ -6022,6 +6064,18 @@ LightApp_Operation* SMESHGUI::createOperation( const int id ) const
   case SMESHOp::OpMoveNode: // Make mesh pass through point
     op = new SMESHGUI_MakeNodeAtPointOp();
     break;
+  case SMESHOp::OpMoveNodeInteractive: // Make mesh pass through point / by mouse
+    op = new SMESHGUI_MakeNodeAtPointOp( 2 );
+    break;
+  case SMESHOp::OpRemoveNodeWithReconn:
+    op = new SMESHGUI_RemoveNodeReconnectionOp();
+    break;
+  case SMESHOp::OpSplitEdgeInteract:
+    op = new SMESHGUI_AddNodeOnSegmentOp();
+    break;
+  case SMESHOp::OpSplitFaceInteract:
+    op = new SMESHGUI_AddNodeOnFaceOp();
+    break;
   case SMESHOp::OpElem0DOnElemNodes: // Create 0D elements on all nodes
     op = new SMESHGUI_Add0DElemsOnAllNodesOp();
     break;
diff --git a/src/SMESHGUI/SMESHGUI_AddNodeOnFaceDlg.cxx b/src/SMESHGUI/SMESHGUI_AddNodeOnFaceDlg.cxx
new file mode 100644 (file)
index 0000000..975c536
--- /dev/null
@@ -0,0 +1,606 @@
+// Copyright (C) 2007-2021  CEA/DEN, EDF R&D, OPEN CASCADE
+//
+// Copyright (C) 2003-2007  OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
+// CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
+//
+// 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
+//
+
+// File   : SMESHGUI_AddNodeOnFaceDlg.cxx
+// Author : Edward AGAPOV, Open CASCADE S.A.S.
+//
+#include "SMESHGUI_AddNodeOnFaceDlg.h"
+
+#include "SMESHGUI.h"
+#include "SMESHGUI_Filter.h"
+#include "SMESHGUI_IdValidator.h"
+#include "SMESHGUI_MeshEditPreview.h"
+#include "SMESHGUI_MeshUtils.h"
+#include "SMESHGUI_SpinBox.h"
+#include "SMESHGUI_VTKUtils.h"
+
+#include <SMDS_Mesh.hxx>
+#include <SMESH_Actor.h>
+#include <SMESH_ActorUtils.h>
+#include <SMESH_TypeDefs.hxx>
+
+// SALOME GUI includes
+#include <LightApp_SelectionMgr.h>
+#include <SALOME_ListIO.hxx>
+#include <SUIT_Desktop.h>
+#include <SVTK_ViewModel.h>
+#include <SVTK_ViewWindow.h>
+#include <SalomeApp_Tools.h>
+#include <SalomeApp_TypeFilter.h>
+#include <SUIT_ResourceMgr.h>
+#include <SUIT_OverrideCursor.h>
+#include <SUIT_MessageBox.h>
+
+// Qt includes
+#include <QApplication>
+#include <QGroupBox>
+#include <QGridLayout>
+#include <QHBoxLayout>
+#include <QVBoxLayout>
+#include <QLineEdit>
+#include <QPushButton>
+#include <QLabel>
+#include <QRadioButton>
+#include <QCheckBox>
+#include <QButtonGroup>
+
+// VTK includes
+#include <vtkProperty.h>
+
+// IDL includes
+#include <SALOMEconfig.h>
+#include CORBA_SERVER_HEADER(SMESH_Mesh)
+#include CORBA_SERVER_HEADER(SMESH_MeshEditor)
+
+#define SPACING 6
+#define MARGIN  11
+
+//=======================================================================
+/*!
+ * \brief Dialog to publish a sub-shape of the mesh main shape
+ *        by selecting mesh elements
+ */
+//=======================================================================
+
+SMESHGUI_AddNodeOnFaceDlg::SMESHGUI_AddNodeOnFaceDlg()
+  : SMESHGUI_Dialog( 0, false, true )
+{
+  setWindowTitle(tr("CAPTION"));
+
+  QVBoxLayout* aDlgLay = new QVBoxLayout (mainFrame());
+  aDlgLay->setMargin(0);
+  aDlgLay->setSpacing(SPACING);
+  myMainFrame = createMainFrame(mainFrame());
+
+  aDlgLay->addWidget(myMainFrame);
+
+  aDlgLay->setStretchFactor(myMainFrame, 1);
+}
+
+//=======================================================================
+// function : createMainFrame()
+// purpose  : Create frame containing dialog's input fields
+//=======================================================================
+
+QWidget* SMESHGUI_AddNodeOnFaceDlg::createMainFrame (QWidget* theParent)
+{
+  QWidget* aFrame = new QWidget(theParent);
+
+  SUIT_ResourceMgr* rm = SMESH::GetResourceMgr( SMESHGUI::GetSMESHGUI() );
+  QPixmap iconSelect( rm->loadPixmap("SMESH", tr("ICON_SELECT")));
+
+  // Face to split
+
+  QGroupBox* faceGrp = new QGroupBox(tr("FACE_GROUP"), aFrame);
+  faceGrp->setSizePolicy( QSizePolicy( QSizePolicy::Expanding, QSizePolicy::Fixed ) );
+  QLabel* idLabel = new QLabel(tr("FACE_ID"), faceGrp);
+  myIdBtn = new QPushButton(faceGrp);
+  myIdBtn->setIcon(iconSelect);
+  myIdBtn->setCheckable(true);
+  myId = new QLineEdit(faceGrp);
+  myId->setValidator(new SMESHGUI_IdValidator(this, 1));
+
+  QGridLayout* faceGrpLayout = new QGridLayout(faceGrp);
+  faceGrpLayout->setSpacing(SPACING);
+  faceGrpLayout->setMargin(MARGIN);
+
+  faceGrpLayout->addWidget( idLabel, 0, 0 );
+  faceGrpLayout->addWidget( myIdBtn, 0, 1 );
+  faceGrpLayout->addWidget( myId,    0, 2 );
+
+  // Node location
+
+  QGroupBox* xyzGrp = new QGroupBox(tr("XYZ_GROUP"), aFrame);
+  xyzGrp->setSizePolicy( QSizePolicy( QSizePolicy::Expanding, QSizePolicy::Fixed ) );
+
+  myDestBtn = new QPushButton(xyzGrp);
+  myDestBtn->setIcon(iconSelect);
+  myDestBtn->setCheckable(true);
+
+  QLabel* locationXLabel = new QLabel(tr("SMESH_X"), xyzGrp);
+  myDestinationX = new SMESHGUI_SpinBox(xyzGrp);
+  QLabel* locationYLabel = new QLabel(tr("SMESH_Y"), xyzGrp);
+  myDestinationY = new SMESHGUI_SpinBox(xyzGrp);
+  QLabel* locationZLabel = new QLabel(tr("SMESH_Z"), xyzGrp);
+  myDestinationZ = new SMESHGUI_SpinBox(xyzGrp);
+
+  myDestinationX->RangeStepAndValidator(COORD_MIN, COORD_MAX, 10.0, "length_precision");
+  myDestinationY->RangeStepAndValidator(COORD_MIN, COORD_MAX, 10.0, "length_precision");
+  myDestinationZ->RangeStepAndValidator(COORD_MIN, COORD_MAX, 10.0, "length_precision");
+
+  QGridLayout* aDestLayout = new QGridLayout(xyzGrp);
+  aDestLayout->setMargin(MARGIN);
+  aDestLayout->setSpacing(SPACING);
+  aDestLayout->addWidget(myDestBtn, 0, 0);
+  aDestLayout->addWidget(locationXLabel, 0, 1);
+  aDestLayout->addWidget(myDestinationX, 0, 2);
+  aDestLayout->addWidget(locationYLabel, 0, 3);
+  aDestLayout->addWidget(myDestinationY, 0, 4);
+  aDestLayout->addWidget(locationZLabel, 0, 5);
+  aDestLayout->addWidget(myDestinationZ, 0, 6);
+  aDestLayout->setColumnStretch(2, 1);
+  aDestLayout->setColumnStretch(4, 1);
+  aDestLayout->setColumnStretch(6, 1);
+
+  // Preview
+
+  myPreviewChkBox = new QCheckBox( tr("PREVIEW"), aFrame);
+  myPreviewChkBox->setChecked( true );
+
+  QVBoxLayout* aLay = new QVBoxLayout(aFrame);
+  aLay->addWidget(faceGrp);
+  aLay->addWidget(xyzGrp);
+  aLay->addWidget(myPreviewChkBox);
+
+  connect(myDestBtn, SIGNAL (toggled(bool)), this, SLOT(ButtonToggled(bool)));
+  connect(myIdBtn,   SIGNAL (toggled(bool)), this, SLOT(ButtonToggled(bool)));
+
+  myIdBtn->setChecked(true);
+
+  return aFrame;
+}
+
+//================================================================================
+/*!
+ * \brief SLOT called when any button is toggled
+  * \param bool - on or off
+ */
+//================================================================================
+
+void SMESHGUI_AddNodeOnFaceDlg::ButtonToggled (bool on)
+{
+  const QObject* aSender = sender();
+  if ( on ) {
+    if ( aSender == myDestBtn ) // button to set coord by node selection
+    {
+      if ( myIdBtn->isEnabled() )
+        myIdBtn->setChecked( !on );
+    }
+    else if ( aSender == myIdBtn ) // button to select a node to move
+    {
+      myDestBtn->setChecked( !on );
+    }
+  }
+  emit selTypeChanged();
+}
+
+//================================================================================
+/*!
+ * \brief Constructor
+*/
+//================================================================================
+
+SMESHGUI_AddNodeOnFaceOp::SMESHGUI_AddNodeOnFaceOp()
+{
+  mySimulation = 0;
+  mySMESHGUI = 0;
+  myDlg = new SMESHGUI_AddNodeOnFaceDlg;
+  myHelpFileName = "add_node_on_face.html";
+
+  myNoPreview = false;
+
+  // connect signals and slots
+  connect(myDlg->myDestinationX,  SIGNAL (valueChanged(double)), this, SLOT(redisplayPreview()));
+  connect(myDlg->myDestinationY,  SIGNAL (valueChanged(double)), this, SLOT(redisplayPreview()));
+  connect(myDlg->myDestinationZ,  SIGNAL (valueChanged(double)), this, SLOT(redisplayPreview()));
+  connect(myDlg->myId,            SIGNAL (textChanged(const QString&)),SLOT(redisplayPreview()));
+  connect(myDlg->myPreviewChkBox, SIGNAL (toggled(bool)),              SLOT(redisplayPreview()));
+  connect(myDlg,                  SIGNAL (selTypeChanged() ),          SLOT(onSelTypeChange()));
+  connect(myDlg->myId,            SIGNAL (textChanged(const QString&)),SLOT(onTextChange(const QString&)));
+}
+
+//================================================================================
+/*!
+ * \brief SLOT. Called upon change of selection type
+ */
+//================================================================================
+
+void SMESHGUI_AddNodeOnFaceOp::onSelTypeChange()
+{
+  if ( myDlg->myIdBtn->isChecked() )
+  {
+    setSelectionMode( FaceSelection );
+  }
+  else if ( myDlg->myDestBtn->isChecked() )
+  {
+    // TODO: activate picking a point on a selected face
+  }
+  else
+  {
+    setSelectionMode( ActorSelection );
+  }
+}
+
+//=======================================================================
+// function : startOperation()
+// purpose  : Init dialog fields, connect signals and slots, show dialog
+//=======================================================================
+
+void SMESHGUI_AddNodeOnFaceOp::startOperation()
+{
+  myNoPreview = false;
+  myMeshActor = 0;
+
+  // init simulation with a current View
+  if ( mySimulation ) delete mySimulation;
+  mySMESHGUI = getSMESHGUI();
+  mySimulation = new SMESHGUI_MeshEditPreview(SMESH::GetViewWindow( mySMESHGUI ) );
+  connect(mySMESHGUI, SIGNAL (SignalActivatedViewManager()), this, SLOT(onOpenView()));
+  connect(mySMESHGUI, SIGNAL (SignalCloseView()), this, SLOT(onCloseView()));
+  vtkProperty* aProp = vtkProperty::New();
+  aProp->SetRepresentationToWireframe();
+  aProp->SetColor(250, 0, 250);
+  aProp->SetPointSize(5);
+  aProp->SetLineWidth( SMESH::GetFloat("SMESH:element_width",1) + 1);
+  mySimulation->GetActor()->SetProperty(aProp);
+  aProp->Delete();
+
+  SMESHGUI_SelectionOp::startOperation();
+  myDlg->myId->setText("");
+  myDlg->myDestinationX->SetValue(0);
+  myDlg->myDestinationY->SetValue(0);
+  myDlg->myDestinationZ->SetValue(0);
+
+  myDlg->show();
+
+  onSelectionDone(); // init myMeshActor
+}
+
+//================================================================================
+/*!
+ * \brief Stops operation
+ */
+//================================================================================
+
+void SMESHGUI_AddNodeOnFaceOp::stopOperation()
+{
+  myNoPreview = true;
+  if ( mySimulation )
+  {
+    mySimulation->SetVisibility(false);
+    delete mySimulation;
+    mySimulation = 0;
+  }
+  if ( myMeshActor ) {
+    myMeshActor = 0;
+  }
+  // SMESH::SetPointRepresentation( false );
+  // SMESH::RepaintCurrentView();
+
+  disconnect(mySMESHGUI, SIGNAL (SignalActivatedViewManager()), this, SLOT(onOpenView()));
+  disconnect(mySMESHGUI, SIGNAL (SignalCloseView()),            this, SLOT(onCloseView()));
+  SMESHGUI_SelectionOp::stopOperation();
+}
+
+//================================================================================
+/*!
+ * \brief perform it's intention action: split a face
+ */
+//================================================================================
+
+bool SMESHGUI_AddNodeOnFaceOp::onApply()
+{
+  if( SMESHGUI::isStudyLocked() )
+    return false;
+
+  if ( !myMeshActor ) {
+    SUIT_MessageBox::warning( dlg(), tr( "SMESH_WRN_WARNING" ), tr("INVALID_MESH") );
+    dlg()->show();
+    return false;
+  }
+
+  QString msg;
+  if ( !isValid( msg ) ) { // node id is invalid
+    if( !msg.isEmpty() )
+      SUIT_MessageBox::warning( dlg(), tr( "SMESH_WRN_WARNING" ), tr("INVALID_ID") );
+    dlg()->show();
+    return false;
+  }
+
+  QStringList aParameters;
+  aParameters << myDlg->myDestinationX->text();
+  aParameters << myDlg->myDestinationY->text();
+  aParameters << myDlg->myDestinationZ->text();
+
+  try {
+    SMESH::SMESH_Mesh_var aMesh = SMESH::GetMeshByIO(myMeshActor->getIO());
+    if (aMesh->_is_nil()) {
+      SUIT_MessageBox::information(SMESHGUI::desktop(), tr("SMESH_ERROR"), tr("SMESHG_NO_MESH") );
+      return true;
+    }
+    SMESH::SMESH_MeshEditor_var aMeshEditor = aMesh->GetMeshEditor();
+    if (aMeshEditor->_is_nil())
+      return true;
+
+    aMesh->SetParameters( aParameters.join(":").toUtf8().constData() );
+
+    bool ok;
+    SMESH::smIdType anId = myDlg->myId->text().toLong( &ok );
+
+    aMeshEditor->AddNodeOnFace( anId,
+                                myDlg->myDestinationX->GetValue(),
+                                myDlg->myDestinationY->GetValue(),
+                                myDlg->myDestinationZ->GetValue() );
+    SALOME_ListIO aList;
+    selectionMgr()->setSelectedObjects(aList,false);
+    aList.Append(myMeshActor->getIO());
+    selectionMgr()->setSelectedObjects(aList,false);
+    onSelectionDone();
+    SMESH::UpdateView();
+    SMESHGUI::Modified();
+  }
+  catch (const SALOME::SALOME_Exception& S_ex) {
+    SalomeApp_Tools::QtCatchCorbaException(S_ex);
+  }
+  catch (...) {
+  }
+
+  return true;
+}
+
+//================================================================================
+/*!
+ * \brief Check selected face id validity
+ */
+//================================================================================
+
+bool SMESHGUI_AddNodeOnFaceOp::isValid( QString& msg )
+{
+  bool ok = false;
+  if ( myMeshActor )
+  {
+    SMESH::smIdType id = myDlg->myId->text().toLong();
+    if ( id > 0 )
+      if (SMDS_Mesh* aMesh = myMeshActor->GetObject()->GetMesh())
+        if ( const SMDS_MeshElement* face = aMesh->FindElement( id ))
+        {
+          if ( face->GetType() == SMDSAbs_Face )
+            ok = true;
+          else
+            msg += tr("NOT_FACE") + "\n";
+        }
+    if ( !ok )
+      msg += tr("INVALID_ID") + "\n";
+  }
+
+  ok = myDlg->myDestinationX->isValid( msg, !myNoPreview ) && ok;
+  ok = myDlg->myDestinationY->isValid( msg, !myNoPreview ) && ok;
+  ok = myDlg->myDestinationZ->isValid( msg, !myNoPreview ) && ok;
+
+  return ok;
+}
+
+//================================================================================
+/*!
+ * \brief SLOT called when selection changed
+ */
+//================================================================================
+
+void SMESHGUI_AddNodeOnFaceOp::onSelectionDone()
+{
+  if ( !myDlg->isVisible() || !myDlg->isEnabled() )
+    return;
+
+  myNoPreview = true;
+  QString idStr;
+  try {
+    SALOME_ListIO aList;
+    selectionMgr()->selectedObjects(aList, SVTK_Viewer::Type());
+    if (aList.Extent() != 1)
+      return;
+    Handle(SALOME_InteractiveObject) anIO = aList.First();
+    myMeshActor = SMESH::FindActorByEntry(anIO->getEntry());
+
+    QString aString;
+    int nbElems = SMESH::GetNameOfSelectedElements(selector(),anIO, aString);
+    if (nbElems == 1)
+      if ( SMDS_Mesh* aMesh = myMeshActor->GetObject()->GetMesh())
+        if ( const SMDS_MeshElement* face = aMesh->FindElement( aString.toLong() ))
+        {
+          idStr = aString;
+          // set coordinates to face gravity center
+          gp_XYZ faceGC( 0,0,0 );
+          for ( int i = 0; i < face->NbCornerNodes(); ++i )
+            faceGC += SMESH_NodeXYZ( face->GetNode( i ));
+          faceGC /= face->NbCornerNodes();
+          myDlg->myDestinationX->SetValue(faceGC.X());
+          myDlg->myDestinationY->SetValue(faceGC.Y());
+          myDlg->myDestinationZ->SetValue(faceGC.Z());
+        }
+  } catch (...) {
+  }
+  myDlg->myId->setText( idStr );
+
+  myNoPreview = false;
+  redisplayPreview();
+}
+
+//================================================================================
+/*!
+ * \brief update preview
+ */
+//================================================================================
+
+void SMESHGUI_AddNodeOnFaceOp::redisplayPreview()
+{
+  if ( myNoPreview )
+    return;
+  myNoPreview = true;
+
+  SMESH::MeshPreviewStruct_var aMeshPreviewStruct;
+
+  if ( myMeshActor && myDlg->myPreviewChkBox->isChecked() )
+  {
+    QString msg;
+    if ( isValid( msg ))
+    {
+      try {
+        SMESH::SMESH_Mesh_var aMesh = SMESH::GetMeshByIO(myMeshActor->getIO());
+        if (!aMesh->_is_nil()) {
+          SMESH::SMESH_MeshEditor_var aPreviewer = aMesh->GetMeshEditPreviewer();
+          if (!aPreviewer->_is_nil())
+          {
+            SUIT_OverrideCursor aWaitCursor;
+
+            SMESH::smIdType anId = myDlg->myId->text().toLong();
+            aPreviewer->AddNodeOnFace(anId,
+                                      myDlg->myDestinationX->GetValue(),
+                                      myDlg->myDestinationY->GetValue(),
+                                      myDlg->myDestinationZ->GetValue());
+            aMeshPreviewStruct = aPreviewer->GetPreviewData();
+          }
+        }
+      }
+      catch (...) {
+      }
+    }
+  }
+  if (!mySimulation)
+    mySimulation = new SMESHGUI_MeshEditPreview(SMESH::GetViewWindow( mySMESHGUI ));
+  // display data
+  if ( & aMeshPreviewStruct.in() )
+  {
+    mySimulation->SetData( aMeshPreviewStruct.in() );
+  }
+  else
+  {
+    mySimulation->SetVisibility(false);
+  }
+
+  myNoPreview = false;
+}
+
+//=================================================================================
+/*!
+ * \brief SLOT called when the viewer opened
+ */
+//=================================================================================
+
+void SMESHGUI_AddNodeOnFaceOp::onOpenView()
+{
+  if ( mySimulation ) {
+    mySimulation->SetVisibility(false);
+    SMESH::SetPointRepresentation(false);
+  }
+  else {
+    mySimulation = new SMESHGUI_MeshEditPreview(SMESH::GetViewWindow( mySMESHGUI ));
+  }
+}
+
+//=================================================================================
+/*!
+ * \brief SLOT called when the viewer closed
+ */
+//=================================================================================
+
+void SMESHGUI_AddNodeOnFaceOp::onCloseView()
+{
+  delete mySimulation;
+  mySimulation = 0;
+}
+
+//================================================================================
+/*!
+ * \brief SLOT called when the face id is manually changed
+ */
+//================================================================================
+
+void SMESHGUI_AddNodeOnFaceOp::onTextChange( const QString& theText )
+{
+  if( myMeshActor )
+  {
+    if( SMDS_Mesh* aMesh = myMeshActor->GetObject()->GetMesh() )
+    {
+      Handle(SALOME_InteractiveObject) anIO = myMeshActor->getIO();
+      SALOME_ListIO aList;
+      aList.Append( anIO );
+      selectionMgr()->setSelectedObjects( aList, false );
+
+      if( const SMDS_MeshElement* face = aMesh->FindElement( theText.toLong() ) )
+      {
+        SVTK_TVtkIDsMap aListInd;
+        aListInd.Add( FromSmIdType<int>( face->GetID()) );
+        selector()->AddOrRemoveIndex( anIO, aListInd, false );
+        if( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow( SMESHGUI::GetSMESHGUI() ) )
+          aViewWindow->highlight( anIO, true, true );
+      }
+    }
+  }
+}
+
+//================================================================================
+/*!
+ * \brief Activate selection
+ */
+//================================================================================
+
+void SMESHGUI_AddNodeOnFaceOp::activateSelection()
+{
+  selectionMgr()->clearFilters();
+  SMESH::SetPointRepresentation( false );
+  onSelTypeChange();
+}
+
+//================================================================================
+/*!
+ * \brief Destructor
+*/
+//================================================================================
+
+SMESHGUI_AddNodeOnFaceOp::~SMESHGUI_AddNodeOnFaceOp()
+{
+  if ( myDlg )        delete myDlg;
+  if ( mySimulation ) delete mySimulation;
+}
+
+//================================================================================
+/*!
+ * \brief Gets dialog of this operation
+ * \retval LightApp_Dialog* - pointer to dialog of this operation
+ */
+//================================================================================
+
+LightApp_Dialog* SMESHGUI_AddNodeOnFaceOp::dlg() const
+{
+  return myDlg;
+}
+
diff --git a/src/SMESHGUI/SMESHGUI_AddNodeOnFaceDlg.h b/src/SMESHGUI/SMESHGUI_AddNodeOnFaceDlg.h
new file mode 100644 (file)
index 0000000..7dc8864
--- /dev/null
@@ -0,0 +1,128 @@
+// Copyright (C) 2007-2021  CEA/DEN, EDF R&D, OPEN CASCADE
+//
+// Copyright (C) 2003-2007  OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
+// CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
+//
+// 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
+//
+
+// File   : SMESHGUI_AddNodeOnFaceDlg.h
+// Author : Edward AGAPOV, Open CASCADE S.A.S.
+//
+#ifndef SMESHGUI_AddNodeOnFaceDLG_H
+#define SMESHGUI_AddNodeOnFaceDLG_H
+
+// SMESH includes
+#include "SMESH_SMESHGUI.hxx"
+
+#include "SMESHGUI_Dialog.h"
+#include "SMESHGUI_SelectionOp.h"
+
+class QButtonGroup;
+class QCheckBox;
+class QGroupBox;
+class QLineEdit;
+class QPushButton;
+class QRadioButton;
+class SMESHGUI_SpinBox;
+class SMESHGUI_MeshEditPreview;
+class SMESHGUI_AddNodeOnFaceDlg;
+
+/*!
+ * \brief Operation to split a face into triangles by creating a new node
+ *        on the face and connecting it to the face nodes
+ */
+class SMESHGUI_EXPORT SMESHGUI_AddNodeOnFaceOp: public SMESHGUI_SelectionOp
+{
+  Q_OBJECT
+
+public:
+  SMESHGUI_AddNodeOnFaceOp();
+  virtual ~SMESHGUI_AddNodeOnFaceOp();
+
+  virtual LightApp_Dialog*      dlg() const;  
+
+protected:
+
+  virtual void                  startOperation();
+  virtual void                  stopOperation();
+
+  virtual void                  activateSelection();
+
+  bool                          isValid( QString& );
+
+protected slots:
+  virtual bool                  onApply();
+
+private slots:
+  void                          onSelectionDone();
+  void                          redisplayPreview();
+  void                          onSelTypeChange();
+  void                          onTextChange( const QString& );
+  void                          onDestCoordChanged();
+  void                          onOpenView();
+  void                          onCloseView();
+
+private:
+  SMESHGUI_AddNodeOnFaceDlg*    myDlg;
+
+  SUIT_SelectionFilter*         myFilter;
+  SMESHGUI*                     mySMESHGUI;
+  SMESHGUI_MeshEditPreview*     mySimulation;
+  SMESH_Actor*                  myMeshActor;
+  bool                          myNoPreview;
+  bool                          myUpdateDestination;
+  bool                          myDestCoordChanged;
+};
+
+/*!
+ * \brief Dialog to split a face into triangles by creating a new node
+ *        on the face and connecting it to the face nodes
+ */
+
+class SMESHGUI_EXPORT SMESHGUI_AddNodeOnFaceDlg : public SMESHGUI_Dialog
+{
+  Q_OBJECT
+
+public:
+  SMESHGUI_AddNodeOnFaceDlg();
+
+private:
+  QWidget*                      createMainFrame( QWidget* );
+
+  QWidget*                      myMainFrame;
+
+  QPushButton*                  myDestBtn;
+  QPushButton*                  myIdBtn;
+  QLineEdit*                    myId;
+  SMESHGUI_SpinBox*             myDestinationX;
+  SMESHGUI_SpinBox*             myDestinationY;
+  SMESHGUI_SpinBox*             myDestinationZ;
+  QCheckBox*                    myPreviewChkBox;
+
+  QString                       myHelpFileName;
+
+  friend class SMESHGUI_AddNodeOnFaceOp;
+
+signals:
+  void                          selTypeChanged();
+
+ private slots:
+  void                          ButtonToggled( bool );
+};
+
+#endif // SMESHGUI_AddNodeOnFaceDLG_H
diff --git a/src/SMESHGUI/SMESHGUI_AddNodeOnSegmentDlg.cxx b/src/SMESHGUI/SMESHGUI_AddNodeOnSegmentDlg.cxx
new file mode 100644 (file)
index 0000000..81aac27
--- /dev/null
@@ -0,0 +1,605 @@
+// Copyright (C) 2007-2021  CEA/DEN, EDF R&D, OPEN CASCADE
+//
+// Copyright (C) 2003-2007  OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
+// CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
+//
+// 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
+//
+
+// File   : SMESHGUI_AddNodeOnSegmentDlg.cxx
+// Author : Edward AGAPOV, Open CASCADE S.A.S.
+//
+#include "SMESHGUI_AddNodeOnSegmentDlg.h"
+
+#include "SMESHGUI.h"
+#include "SMESHGUI_MeshUtils.h"
+#include "SMESHGUI_VTKUtils.h"
+#include "SMESHGUI_SpinBox.h"
+#include "SMESHGUI_MeshEditPreview.h"
+
+#include <SMDS_Mesh.hxx>
+#include <SMESH_Actor.h>
+#include <SMESH_ActorUtils.h>
+#include <SMESH_TypeDefs.hxx>
+
+#include <LightApp_SelectionMgr.h>
+#include <SALOME_ListIO.hxx>
+#include <SUIT_Desktop.h>
+#include <SUIT_MessageBox.h>
+#include <SUIT_OverrideCursor.h>
+#include <SUIT_ResourceMgr.h>
+#include <SVTK_ViewModel.h>
+#include <SVTK_ViewWindow.h>
+#include <SalomeApp_Tools.h>
+
+// Qt includes
+#include <QApplication>
+#include <QGroupBox>
+#include <QGridLayout>
+#include <QHBoxLayout>
+#include <QVBoxLayout>
+#include <QLineEdit>
+#include <QPushButton>
+#include <QLabel>
+#include <QRadioButton>
+#include <QCheckBox>
+#include <QButtonGroup>
+
+// VTK includes
+#include <vtkProperty.h>
+
+// IDL includes
+#include <SALOMEconfig.h>
+#include CORBA_SERVER_HEADER(SMESH_Mesh)
+#include CORBA_SERVER_HEADER(SMESH_MeshEditor)
+
+#define SPACING 6
+#define MARGIN  11
+
+//=======================================================================
+/*!
+ * \brief Dialog to split a diagonal of a quadrangle formed by two adjacent triangles
+ */
+//=======================================================================
+
+SMESHGUI_AddNodeOnSegmentDlg::SMESHGUI_AddNodeOnSegmentDlg()
+: SMESHGUI_Dialog( 0, false, true )
+{
+  setWindowTitle(tr("CAPTION"));
+
+  QVBoxLayout* aDlgLay = new QVBoxLayout (mainFrame());
+  aDlgLay->setMargin(0);
+  aDlgLay->setSpacing(SPACING);
+  QWidget* mainFr = createMainFrame(mainFrame());
+
+  aDlgLay->addWidget( mainFr );
+
+  aDlgLay->setStretchFactor( mainFr, 1);
+}
+
+//=======================================================================
+// function : createMainFrame()
+// purpose  : Create frame containing dialog's input fields
+//=======================================================================
+
+QWidget* SMESHGUI_AddNodeOnSegmentDlg::createMainFrame (QWidget* theParent)
+{
+  QWidget* aFrame = new QWidget(theParent);
+
+  SUIT_ResourceMgr* rm = SMESH::GetResourceMgr( SMESHGUI::GetSMESHGUI() );
+  QPixmap iconSelect( rm->loadPixmap("SMESH", tr("ICON_SELECT")));
+
+  // Segment
+
+  QGroupBox* segmentGrp = new QGroupBox(tr("SEGMENT_GROUP"), aFrame);
+  segmentGrp->setSizePolicy( QSizePolicy( QSizePolicy::Expanding, QSizePolicy::Fixed ) );
+  QLabel* segmentLabel = new QLabel(tr("SEGMENT"), segmentGrp);
+  mySegmentBtn = new QPushButton(segmentGrp);
+  mySegmentBtn->setIcon(iconSelect);
+  mySegmentBtn->setCheckable(true);
+  mySegment = new QLineEdit(segmentGrp);
+  mySegment->setValidator(new QRegExpValidator(QRegExp("[\\d]*-[\\d]*"), this));
+
+  QGridLayout* segmentGrpLayout = new QGridLayout(segmentGrp);
+  segmentGrpLayout->setSpacing(SPACING);
+  segmentGrpLayout->setMargin(MARGIN);
+
+  segmentGrpLayout->addWidget( segmentLabel, 0, 0 );
+  segmentGrpLayout->addWidget( mySegmentBtn, 0, 1 );
+  segmentGrpLayout->addWidget( mySegment,    0, 2 );
+
+  // Position on segment
+
+  QGroupBox* positionGrp = new QGroupBox(tr("POSITION_GROUP"), aFrame);
+  positionGrp->setSizePolicy( QSizePolicy( QSizePolicy::Expanding, QSizePolicy::Fixed ) );
+
+  myPositionBtn = new QPushButton(positionGrp);
+  myPositionBtn->setIcon(iconSelect);
+  myPositionBtn->setCheckable(true);
+
+  QLabel* positionLbl = new QLabel(tr("POSITION"), positionGrp);
+
+  myPositionSpin = new SMESHGUI_SpinBox(positionGrp);
+  myPositionSpin->setReadOnly(false);
+  const double tol = 1e-3;
+  myPositionSpin->RangeStepAndValidator( tol, 1-tol, 0.1, "length_precision");
+
+  QGridLayout* positionLayout = new QGridLayout(positionGrp);
+  positionLayout->setMargin(MARGIN);
+  positionLayout->setSpacing(SPACING);
+  positionLayout->addWidget(positionLbl, 0, 0);
+  positionLayout->addWidget(myPositionBtn, 0, 1);
+  positionLayout->addWidget(myPositionSpin, 0, 2);
+  positionLayout->setColumnStretch(2, 1);
+
+  // Preview
+
+  myPreviewChkBox = new QCheckBox( tr("PREVIEW"), aFrame);
+  myPreviewChkBox->setChecked( true );
+
+  QVBoxLayout* aLay = new QVBoxLayout(aFrame);
+  aLay->addWidget(segmentGrp);
+  aLay->addWidget(positionGrp);
+  aLay->addWidget(myPreviewChkBox);
+
+  connect(myPositionBtn, SIGNAL (toggled(bool)), this, SLOT(ButtonToggled(bool)));
+  connect(mySegmentBtn,  SIGNAL (toggled(bool)), this, SLOT(ButtonToggled(bool)));
+
+  mySegmentBtn->setChecked(true);
+
+  return aFrame;
+}
+
+//================================================================================
+/*!
+ * \brief SLOT called when any button is toggled
+ * \param bool - on or off
+ */
+//================================================================================
+
+void SMESHGUI_AddNodeOnSegmentDlg::ButtonToggled (bool on)
+{
+  const QObject* aSender = sender();
+  if ( on ) {
+    if ( aSender == myPositionBtn )
+    {
+      mySegmentBtn->setChecked( !on );
+    }
+    else if ( aSender == mySegmentBtn )
+    {
+      myPositionBtn->setChecked( !on );
+    }
+  }
+  emit selTypeChanged();
+}
+
+//================================================================================
+/*!
+ * \brief Constructor
+ */
+//================================================================================
+
+SMESHGUI_AddNodeOnSegmentOp::SMESHGUI_AddNodeOnSegmentOp()
+{
+  mySimulation = 0;
+  mySMESHGUI = 0;
+  myDlg = new SMESHGUI_AddNodeOnSegmentDlg;
+  myHelpFileName = "add_node_on_segment.html";
+
+  myNoPreview = false;
+
+  // connect signals and slots
+  connect(myDlg->myPreviewChkBox, SIGNAL (toggled(bool)),              SLOT(redisplayPreview()));
+  connect(myDlg->myPositionSpin,  SIGNAL (valueChanged(double)),       SLOT(redisplayPreview()));
+  connect(myDlg->myPositionSpin,  SIGNAL (textChanged(const QString&)),SLOT(redisplayPreview()));
+  connect(myDlg,                  SIGNAL (selTypeChanged() ),          SLOT(onSelTypeChange()));
+  connect(myDlg->mySegment,       SIGNAL (textChanged(const QString&)),SLOT(onTextChange(const QString&)));
+}
+
+//================================================================================
+/*!
+ * \brief SLOT. Called upon change of selection type
+ */
+//================================================================================
+
+void SMESHGUI_AddNodeOnSegmentOp::onSelTypeChange()
+{
+  if ( myDlg->mySegmentBtn->isChecked() )
+  {
+    setSelectionMode( EdgeOfCellSelection );
+  }
+  else if ( myDlg->myPositionBtn->isChecked() )
+  {
+    // TODO: activate picking a point on a selected segment
+  }
+  else
+  {
+    setSelectionMode( ActorSelection );
+  }
+}
+
+//=======================================================================
+// function : startOperation()
+// purpose  : Init dialog fields, connect signals and slots, show dialog
+//=======================================================================
+
+void SMESHGUI_AddNodeOnSegmentOp::startOperation()
+{
+  myNoPreview = false;
+  myMeshActor = 0;
+
+  // init simulation with a current View
+  if ( mySimulation ) delete mySimulation;
+  mySMESHGUI = getSMESHGUI();
+  mySimulation = new SMESHGUI_MeshEditPreview(SMESH::GetViewWindow( mySMESHGUI ) );
+  connect(mySMESHGUI, SIGNAL (SignalActivatedViewManager()), this, SLOT(onOpenView()));
+  connect(mySMESHGUI, SIGNAL (SignalCloseView()), this, SLOT(onCloseView()));
+  vtkProperty* aProp = vtkProperty::New();
+  aProp->SetRepresentationToWireframe();
+  aProp->SetColor(250, 0, 250);
+  aProp->SetPointSize(5);
+  aProp->SetLineWidth( SMESH::GetFloat("SMESH:element_width",1) + 1);
+  mySimulation->GetActor()->SetProperty(aProp);
+  aProp->Delete();
+
+  SMESHGUI_SelectionOp::startOperation(); // this method should be called only after filter creation
+  myDlg->mySegment->setText("");
+  myDlg->myPositionSpin->SetValue(0.5);
+  myDlg->myPositionSpin->setReadOnly(false);
+
+  myDlg->show();
+
+  onSelectionDone(); // init myMeshActor
+}
+
+//================================================================================
+/*!
+ * \brief Stops operation
+ */
+//================================================================================
+
+void SMESHGUI_AddNodeOnSegmentOp::stopOperation()
+{
+  myNoPreview = true;
+  if ( mySimulation )
+  {
+    mySimulation->SetVisibility(false);
+    delete mySimulation;
+    mySimulation = 0;
+  }
+  if ( myMeshActor ) {
+    myMeshActor = 0;
+  }
+  SMESH::SetPointRepresentation( false );
+  SMESH::RepaintCurrentView();
+
+  disconnect(mySMESHGUI, SIGNAL (SignalActivatedViewManager()), this, SLOT(onOpenView()));
+  disconnect(mySMESHGUI, SIGNAL (SignalCloseView()),            this, SLOT(onCloseView()));
+  //selectionMgr()->removeFilter( myFilter );
+  SMESHGUI_SelectionOp::stopOperation();
+}
+
+//================================================================================
+/*!
+ * \brief perform it's intention action: move or create a node
+ */
+//================================================================================
+
+bool SMESHGUI_AddNodeOnSegmentOp::onApply()
+{
+  if( SMESHGUI::isStudyLocked() )
+    return false;
+
+  QString msg;
+  SMESH::smIdType node1= 0, node2 = 0;
+  if ( !isValid( msg, node1, node2 ))
+  {
+    SUIT_MessageBox::warning( dlg(), tr( "SMESH_WRN_WARNING" ),
+                              msg.isEmpty() ? tr("INVALID_ID") : msg );
+    dlg()->show();
+    return false;
+  }
+
+  QStringList aParameters;
+  aParameters << myDlg->myPositionSpin->text();
+
+  try {
+    SMESH::SMESH_Mesh_var aMesh = SMESH::GetMeshByIO( myMeshActor->getIO() );
+    if (aMesh->_is_nil()) {
+      SUIT_MessageBox::information(SMESHGUI::desktop(), tr("SMESH_ERROR"), tr("INVALID_MESH") );
+      return true;
+    }
+    SMESH::SMESH_MeshEditor_var aMeshEditor = aMesh->GetMeshEditor();
+    if (aMeshEditor->_is_nil())
+      return true;
+
+    aMesh->SetParameters( aParameters.join(":").toUtf8().constData() );
+
+    aMeshEditor->AddNodeOnSegment( node1, node2, myDlg->myPositionSpin->GetValue() );
+
+    selector()->ClearIndex();
+    selector()->ClearCompositeIndex();
+    SALOME_ListIO aList;
+    aList.Append( myMeshActor->getIO() );
+    selectionMgr()->setSelectedObjects(aList,false);
+    onSelectionDone();
+    SMESH::UpdateView();
+    SMESHGUI::Modified();
+  }
+  catch (const SALOME::SALOME_Exception& S_ex) {
+    SalomeApp_Tools::QtCatchCorbaException(S_ex);
+  }
+  catch (...) {
+  }
+
+  return true;
+}
+
+//================================================================================
+/*!
+ * \brief Check selected node id validity
+ */
+//================================================================================
+
+bool SMESHGUI_AddNodeOnSegmentOp::isValid( QString&          msg,
+                                           SMESH::smIdType & node1,
+                                           SMESH::smIdType & node2 )
+{
+  bool ok = false;
+  if ( !myMeshActor )
+  {
+    msg = tr("INVALID_MESH");
+  }
+  else
+  {
+    if ( SMDS_Mesh* mesh = myMeshActor->GetObject()->GetMesh() )
+    {
+      QString txt = myDlg->mySegment->text();
+      if ( txt.contains('-'))
+      {
+        QString str1 = txt.section('-', 0, 0, QString::SectionSkipEmpty);
+        QString str2 = txt.section('-', 1, 1, QString::SectionSkipEmpty);
+        node1 = str1.toLong();
+        node2 = str2.toLong();
+        const SMDS_MeshNode* n1 = mesh->FindNode( node1 );
+        const SMDS_MeshNode* n2 = mesh->FindNode( node2 );
+        std::vector<const SMDS_MeshNode *> nodes = { n1, n2 };
+        std::vector<const SMDS_MeshElement *> foundElems;
+        if ( !mesh->GetElementsByNodes( nodes, foundElems ))
+          msg = tr("NO_ELEMENTS");
+        else
+        {
+          for ( const SMDS_MeshElement * elem : foundElems )
+          {
+            if ( elem->GetGeomType() == SMDSGeom_TRIANGLE )
+              ok = true;
+            else
+            {
+              if ( elem->GetType() == SMDSAbs_Volume )
+                msg = tr("VOLUME_FOUND");
+              if ( elem->GetType() == SMDSAbs_Face )
+                msg = tr("NOT_TRIANGLE_FACE_FOUND");
+            }
+          }
+          if ( !msg.isEmpty() )
+            ok = false;
+        }
+      }
+    }
+  }
+  if ( !ok && msg.isEmpty() )
+  {
+    node1 = node2 = 0;
+    msg += tr("INVALID_EDGE") + "\n";
+  }
+
+  if ( ok && ! myDlg->myPositionSpin->isValid( msg, /*toCorrect=*/false ))
+  {
+    msg = tr("BAD_POSITION");
+    ok = false;
+  }
+
+  return ok;
+}
+
+//================================================================================
+/*!
+ * \brief SLOT called when selection changed
+ */
+//================================================================================
+
+void SMESHGUI_AddNodeOnSegmentOp::onSelectionDone()
+{
+  if ( !myDlg->isVisible() || !myDlg->isEnabled() )
+    return;
+
+  myNoPreview = true;
+  QString segmentStr;
+  try {
+    SALOME_ListIO aList;
+    selectionMgr()->selectedObjects(aList, SVTK_Viewer::Type());
+    if (aList.Extent() != 1)
+      return;
+    Handle(SALOME_InteractiveObject) anIO = aList.First();
+    if (( myMeshActor = SMESH::FindActorByEntry(anIO->getEntry()) ))
+    {
+      SVTK_IndexedMapOfVtkIds IDs;
+      selector()->GetCompositeIndex( anIO, IDs );
+      if ( IDs.Extent() == 1 && IDs(1).size() == 2 )
+      {
+        SMESH::smIdType id1 = IDs(1)[0];
+        SMESH::smIdType id2 = IDs(1)[1];
+        segmentStr = QString("%1-%2").arg( id1 ).arg( id2 );
+      }
+    }
+  } catch (...) {
+  }
+  myDlg->mySegment->setText( segmentStr );
+
+  myNoPreview = false;
+  redisplayPreview();
+}
+
+//================================================================================
+/*!
+ * \brief update preview
+ */
+//================================================================================
+
+void SMESHGUI_AddNodeOnSegmentOp::redisplayPreview()
+{
+  if ( myNoPreview || !myDlg->myPreviewChkBox->isChecked() )
+  {
+    if ( mySimulation )
+      mySimulation->SetVisibility(false);
+    return;
+  }
+  myNoPreview = true;
+
+  SMESH::MeshPreviewStruct_var aMeshPreviewStruct;
+
+  QString msg;
+  SMESH::smIdType node1, node2;
+  try {
+    if ( isValid( msg, node1, node2 ))
+    {
+      SMESH::SMESH_Mesh_var aMesh = SMESH::GetMeshByIO(myMeshActor->getIO());
+      if ( !aMesh->_is_nil() )
+      {
+        SMESH::SMESH_MeshEditor_var aPreviewer = aMesh->GetMeshEditPreviewer();
+        if ( !aPreviewer->_is_nil() )
+        {
+          SUIT_OverrideCursor aWaitCursor;
+          double pos = myDlg->myPositionSpin->value();
+          aPreviewer->AddNodeOnSegment( node1, node2, pos );
+
+          aMeshPreviewStruct = aPreviewer->GetPreviewData();
+        }
+      }
+    }
+  }
+  catch (...) {
+  }
+
+  if (!mySimulation)
+    mySimulation = new SMESHGUI_MeshEditPreview(SMESH::GetViewWindow( mySMESHGUI ));
+  // display data
+  if ( & aMeshPreviewStruct.in() )
+  {
+    mySimulation->SetData( aMeshPreviewStruct.in() );
+  }
+  else
+  {
+    mySimulation->SetVisibility(false);
+  }
+
+  myNoPreview = false;
+}
+
+//=================================================================================
+/*!
+ * \brief SLOT called when the viewer opened
+ */
+//=================================================================================
+
+void SMESHGUI_AddNodeOnSegmentOp::onOpenView()
+{
+  if ( mySimulation ) {
+    mySimulation->SetVisibility(false);
+    SMESH::SetPointRepresentation(false);
+  }
+  else {
+    mySimulation = new SMESHGUI_MeshEditPreview(SMESH::GetViewWindow( mySMESHGUI ));
+  }
+}
+
+//=================================================================================
+/*!
+ * \brief SLOT called when the viewer closed
+ */
+//=================================================================================
+
+void SMESHGUI_AddNodeOnSegmentOp::onCloseView()
+{
+  delete mySimulation;
+  mySimulation = 0;
+}
+
+//================================================================================
+/*!
+ * \brief SLOT called when the node ids are manually changed
+ */
+//================================================================================
+
+void SMESHGUI_AddNodeOnSegmentOp::onTextChange( const QString& /*theText*/ )
+{
+  QString msg;
+  SMESH::smIdType node1= 0, node2 = 0;
+
+  if (( isValid( msg, node1, node2 )) ||
+      ( node1 && node2 )) // position only can be invalid
+  {
+    // highlight entered segment
+
+    Handle(SALOME_InteractiveObject) anIO = myMeshActor->getIO();
+    SALOME_ListIO aList;
+    aList.Append( anIO );
+    selectionMgr()->setSelectedObjects( aList, false );
+
+    SVTK_ListOfVtk newIndices = { node1, node2 };
+    selector()->AddOrRemoveCompositeIndex( anIO, newIndices, false );
+    SMESH::GetViewWindow(mySMESHGUI)->highlight( anIO, true, true );
+  }
+}
+
+//================================================================================
+/*!
+ * \brief Activate Node selection
+ */
+//================================================================================
+
+void SMESHGUI_AddNodeOnSegmentOp::activateSelection()
+{
+  selectionMgr()->clearFilters();
+  SMESH::SetPointRepresentation( false );
+  onSelTypeChange();
+}
+
+//================================================================================
+/*!
+ * \brief Destructor
+ */
+//================================================================================
+
+SMESHGUI_AddNodeOnSegmentOp::~SMESHGUI_AddNodeOnSegmentOp()
+{
+  if ( myDlg )        delete myDlg;
+  if ( mySimulation ) delete mySimulation;
+}
+
+//================================================================================
+/*!
+ * \brief Gets dialog of this operation
+ * \retval LightApp_Dialog* - pointer to dialog of this operation
+ */
+//================================================================================
+
+LightApp_Dialog* SMESHGUI_AddNodeOnSegmentOp::dlg() const
+{
+  return myDlg;
+}
+
diff --git a/src/SMESHGUI/SMESHGUI_AddNodeOnSegmentDlg.h b/src/SMESHGUI/SMESHGUI_AddNodeOnSegmentDlg.h
new file mode 100644 (file)
index 0000000..7c9caac
--- /dev/null
@@ -0,0 +1,119 @@
+// Copyright (C) 2007-2021  CEA/DEN, EDF R&D, OPEN CASCADE
+//
+// Copyright (C) 2003-2007  OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
+// CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
+//
+// 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
+//
+
+// File   : SMESHGUI_AddNodeOnSegmentDlg.h
+// Author : Edward AGAPOV, Open CASCADE S.A.S.
+//
+#ifndef SMESHGUI_AddNodeOnSegmentDLG_H
+#define SMESHGUI_AddNodeOnSegmentDLG_H
+
+#include "SMESHGUI_Dialog.h"
+#include "SMESHGUI_SelectionOp.h"
+
+class QButtonGroup;
+class QCheckBox;
+class QGroupBox;
+class QLineEdit;
+class QPushButton;
+class QRadioButton;
+class SMESHGUI_SpinBox;
+class SMESHGUI_MeshEditPreview;
+class SMESHGUI_AddNodeOnSegmentDlg;
+
+/*!
+ * \brief Operation to make a mesh pass through a point
+ */
+class SMESHGUI_EXPORT SMESHGUI_AddNodeOnSegmentOp: public SMESHGUI_SelectionOp
+{
+  Q_OBJECT
+
+public:
+  SMESHGUI_AddNodeOnSegmentOp();
+  virtual ~SMESHGUI_AddNodeOnSegmentOp();
+
+  virtual LightApp_Dialog*       dlg() const;  
+
+protected:
+
+  virtual void                   startOperation();
+  virtual void                   stopOperation();
+
+  virtual void                   activateSelection();
+
+  bool                           isValid( QString&, SMESH::smIdType& n1, SMESH::smIdType& n2 );
+
+protected slots:
+  virtual bool                   onApply();
+
+private slots:
+  void                           onSelectionDone();
+  void                           redisplayPreview();
+  void                           onTextChange( const QString& );
+  void                           onPositionClick();
+  void                           onPositionChanged();
+  void                           onSelTypeChange();
+  void                           onOpenView();
+  void                           onCloseView();
+
+private:
+
+  SMESHGUI_AddNodeOnSegmentDlg* myDlg;
+
+  SMESHGUI*                     mySMESHGUI;
+  SMESHGUI_MeshEditPreview*     mySimulation;
+  SMESH_Actor*                  myMeshActor;
+  bool                          myNoPreview;
+};
+
+/*!
+ * \brief Dialog to make a mesh pass through a point
+ */
+
+class SMESHGUI_EXPORT SMESHGUI_AddNodeOnSegmentDlg : public SMESHGUI_Dialog
+{
+  Q_OBJECT
+
+public:
+  SMESHGUI_AddNodeOnSegmentDlg();
+
+signals:
+
+  void                          selTypeChanged();
+
+private:
+  QWidget*                      createMainFrame( QWidget* );
+
+  QPushButton*                  mySegmentBtn;
+  QLineEdit*                    mySegment;
+  QPushButton*                  myPositionBtn;
+  SMESHGUI_SpinBox*             myPositionSpin;
+  QCheckBox*                    myPreviewChkBox;
+
+  QString                       myHelpFileName;
+
+  friend class SMESHGUI_AddNodeOnSegmentOp;
+
+private slots:
+  void                          ButtonToggled( bool );
+};
+
+#endif // SMESHGUI_AddNodeOnSegmentDLG_H
index 6b1b7c140536d60458a4a51493944ce053323d47..5745d8483dacbdcd28f7c6c8b6c18e415420f08b 100644 (file)
 
 namespace
 {
-  enum { MANUAL_MODE = 0, SEARCH_MODE }; // how a node to move is specified
+  enum { MANUAL_MODE = 0, SEARCH_MODE, INTERACTIVE_MODE };
 }
 
+//=======================================================================
 /*!
  * \brief Dialog to publish a sub-shape of the mesh main shape
  *        by selecting mesh elements
  */
+//=======================================================================
+
 SMESHGUI_MakeNodeAtPointDlg::SMESHGUI_MakeNodeAtPointDlg()
   : SMESHGUI_Dialog( 0, false, true )
 {
@@ -112,6 +115,7 @@ SMESHGUI_MakeNodeAtPointDlg::SMESHGUI_MakeNodeAtPointDlg()
 // function : createMainFrame()
 // purpose  : Create frame containing dialog's input fields
 //=======================================================================
+
 QWidget* SMESHGUI_MakeNodeAtPointDlg::createMainFrame (QWidget* theParent)
 {
   QWidget* aFrame = new QWidget(theParent);
@@ -119,6 +123,7 @@ QWidget* SMESHGUI_MakeNodeAtPointDlg::createMainFrame (QWidget* theParent)
   SUIT_ResourceMgr* rm = SMESH::GetResourceMgr( SMESHGUI::GetSMESHGUI() );
   QPixmap iconMoveNode        (rm->loadPixmap("SMESH", tr("ICON_DLG_MOVE_NODE")));
   QPixmap iconMoveWithoutNode (rm->loadPixmap("SMESH", tr("ICON_DLG_MOVE_WITHOUT_NODE")));
+  QPixmap iconMoveInteractive (rm->loadPixmap("SMESH", tr("ICON_DLG_MOVE_NODE_INTERACTIVE")));
   QPixmap iconSelect          (rm->loadPixmap("SMESH", tr("ICON_SELECT")));
 
   // constructor
@@ -129,16 +134,20 @@ QWidget* SMESHGUI_MakeNodeAtPointDlg::createMainFrame (QWidget* theParent)
   aPixGrpLayout->setMargin(MARGIN);
   aPixGrpLayout->setSpacing(SPACING);
 
-  myRButNodeToMove = new QRadioButton(aPixGrp);
+  myRButNodeToMove      = new QRadioButton(aPixGrp);
   myRButMoveWithoutNode = new QRadioButton(aPixGrp);
+  myRButMoveInteractive = new QRadioButton(aPixGrp);
   myRButNodeToMove->setIcon(iconMoveNode);
   myRButMoveWithoutNode->setIcon(iconMoveWithoutNode);
+  myRButMoveInteractive->setIcon(iconMoveInteractive);
   myRButNodeToMove->setChecked(true);
 
   aPixGrpLayout->addWidget(myRButNodeToMove);
   aPixGrpLayout->addWidget(myRButMoveWithoutNode);
-  myButtonGroup->addButton(myRButNodeToMove, 0);
+  aPixGrpLayout->addWidget(myRButMoveInteractive);
+  myButtonGroup->addButton(myRButNodeToMove,      0);
   myButtonGroup->addButton(myRButMoveWithoutNode, 1);
+  myButtonGroup->addButton(myRButMoveInteractive, 2);
 
   // Node to move
 
@@ -302,11 +311,15 @@ void SMESHGUI_MakeNodeAtPointDlg::ButtonToggled (bool on)
   * \param int - number of the button
  */
 //================================================================================
+
 void SMESHGUI_MakeNodeAtPointDlg::ConstructorsClicked (int constructorId)
 {
   switch (constructorId) {
-  case 0:
+  case MANUAL_MODE:
+  case INTERACTIVE_MODE:
     {
+      myUpdateBtn->setVisible( constructorId == MANUAL_MODE );
+      myDestinationGrp->setTitle( tr( constructorId == MANUAL_MODE ? "DESTINATION" : "DESTINATION_BY_MOUSE"));
       myDestDXLabel->show();
       myDestDYLabel->show();
       myDestDZLabel->show();
@@ -319,7 +332,7 @@ void SMESHGUI_MakeNodeAtPointDlg::ConstructorsClicked (int constructorId)
       if (!myNodeToMoveGrp->isVisible()) myNodeToMoveGrp->show();
       break;
     }
-  case 1:
+  case SEARCH_MODE:
     {
       myId->setText("");
       myCurrentX->SetValue(0);
@@ -336,6 +349,7 @@ void SMESHGUI_MakeNodeAtPointDlg::ConstructorsClicked (int constructorId)
       break;
     }
   }
+
   QApplication::instance()->processEvents();
   myMainFrame->hide();
   myMainFrame->show();
@@ -349,8 +363,9 @@ void SMESHGUI_MakeNodeAtPointDlg::ConstructorsClicked (int constructorId)
 */
 //================================================================================
 
-SMESHGUI_MakeNodeAtPointOp::SMESHGUI_MakeNodeAtPointOp()
+SMESHGUI_MakeNodeAtPointOp::SMESHGUI_MakeNodeAtPointOp(int defaultConstructor)
 {
+  myDefaultConstructor = defaultConstructor;
   mySimulation = 0;
   mySMESHGUI = 0;
   myDlg = new SMESHGUI_MakeNodeAtPointDlg;
@@ -378,6 +393,12 @@ SMESHGUI_MakeNodeAtPointOp::SMESHGUI_MakeNodeAtPointOp()
   connect(myDlg->myUpdateBtn, SIGNAL (clicked()), this, SLOT(onUpdateDestination()));
 }
 
+//================================================================================
+/*!
+ * \brief SLOT. Update preview upon [Update destination] clicked
+ */
+//================================================================================
+
 void SMESHGUI_MakeNodeAtPointOp::onUpdateDestination()
 {
   myUpdateDestination = true;
@@ -385,6 +406,12 @@ void SMESHGUI_MakeNodeAtPointOp::onUpdateDestination()
   myUpdateDestination = false;
 }
 
+//================================================================================
+/*!
+ * \brief SLOT. Update preview upon Destination coordinates change
+ */
+//================================================================================
+
 void SMESHGUI_MakeNodeAtPointOp::onDestCoordChanged()
 {
   myDestCoordChanged = false;
@@ -396,6 +423,7 @@ void SMESHGUI_MakeNodeAtPointOp::onDestCoordChanged()
 // function : startOperation()
 // purpose  : Init dialog fields, connect signals and slots, show dialog
 //=======================================================================
+
 void SMESHGUI_MakeNodeAtPointOp::startOperation()
 {
   myNoPreview = false;
@@ -442,6 +470,8 @@ void SMESHGUI_MakeNodeAtPointOp::startOperation()
   myDlg->myDestDZ->setReadOnly(true);
   myDlg->myRButNodeToMove->setChecked(true);
 
+  if ( myDefaultConstructor == INTERACTIVE_MODE )
+    myDlg->myButtonGroup->button( INTERACTIVE_MODE )->setChecked(true);
   myDlg->ConstructorsClicked( GetConstructorId() );
 
   myDlg->show();
@@ -453,6 +483,7 @@ void SMESHGUI_MakeNodeAtPointOp::startOperation()
 // function : GetConstructorId()
 // purpose  :
 //=================================================================================
+
 int SMESHGUI_MakeNodeAtPointOp::GetConstructorId()
 {
   return myDlg->myButtonGroup->checkedId();
@@ -828,6 +859,7 @@ void SMESHGUI_MakeNodeAtPointOp::redisplayPreview()
  * \brief SLOT called when the viewer opened
  */
 //=================================================================================
+
 void SMESHGUI_MakeNodeAtPointOp::onOpenView()
 {
   if ( mySimulation ) {
@@ -844,6 +876,7 @@ void SMESHGUI_MakeNodeAtPointOp::onOpenView()
  * \brief SLOT called when the viewer closed
  */
 //=================================================================================
+
 void SMESHGUI_MakeNodeAtPointOp::onCloseView()
 {
   delete mySimulation;
index d1d0f09ba5528b4f5f92424c54418631702a8a62..32cb9f0c17d5491245d814230a294d0b02421ad3 100644 (file)
@@ -50,7 +50,7 @@ class SMESHGUI_EXPORT SMESHGUI_MakeNodeAtPointOp: public SMESHGUI_SelectionOp
   Q_OBJECT
 
 public:
-  SMESHGUI_MakeNodeAtPointOp();
+  SMESHGUI_MakeNodeAtPointOp(int defaultConstructor = 0);
   virtual ~SMESHGUI_MakeNodeAtPointOp();
 
   virtual LightApp_Dialog*       dlg() const;  
@@ -79,10 +79,10 @@ private slots:
 private:
   int                           GetConstructorId();
 
+  int                           myDefaultConstructor;
   SMESHGUI_MakeNodeAtPointDlg*  myDlg;
 
   SUIT_SelectionFilter*         myFilter;
-  int                           myMeshOldDisplayMode;
   SMESHGUI*                     mySMESHGUI;
   SMESHGUI_MeshEditPreview*     mySimulation;
   SMESH_Actor*                  myMeshActor;
@@ -110,6 +110,7 @@ private:
   QButtonGroup*                 myButtonGroup;
   QRadioButton*                 myRButNodeToMove;
   QRadioButton*                 myRButMoveWithoutNode;
+  QRadioButton*                 myRButMoveInteractive;
   QPushButton*                  myDestBtn;
   QPushButton*                  myUpdateBtn;
   QGroupBox*                    myDestinationGrp;
index b91a927877113f6f85b771c764a6ce3826842f5d..f9b6a32db18f8c27851d6b3446075f23b17c50e7 100644 (file)
@@ -153,6 +153,7 @@ namespace SMESHOp {
     OpRemoveNodes            = 4200,   // MENU MODIFICATION - REMOVE - NODE
     OpRemoveElements         = 4201,   // MENU MODIFICATION - REMOVE - ELEMENTS
     OpRemoveOrphanNodes      = 4202,   // MENU MODIFICATION - REMOVE - ORPHAN NODES
+    OpRemoveNodeWithReconn   = 4203,   // MENU MODIFICATION - REMOVE - NODE, WITH RECONNECTION
     OpDeleteGroup            = 4210,   // MENU MODIFICATION - REMOVE - DELETE GROUPS WITH CONTENTS
     OpClearMesh              = 4220,   // MENU MODIFICATION - REMOVE - CLEAR MESH DATA
     OpRenumberingNodes       = 4300,   // MENU MODIFICATION - RENUMBERING - NODES
@@ -182,6 +183,9 @@ namespace SMESHOp {
     OpConvertMeshToQuadratic = 4513,   // MENU MODIFICATION - CONVERT TO/FROM QUADRATIC
     OpCreateBoundaryElements = 4514,   // MENU MODIFICATION - CREATE BOUNDARY ELEMENTS
     OpSplitBiQuadratic       = 4515,   // MENU MODIFICATION - SPLIT BI-QUADRATIC TO LINEAR
+    OpMoveNodeInteractive    = 4516,   // MENU MODIFICATION - MOVE NODE INTERACTIVE
+    OpSplitEdgeInteract      = 4517,   // MENU MODIFICATION - INTERACTIVE ADD NODE ON EDGE
+    OpSplitFaceInteract      = 4518,   // MENU MODIFICATION - INTERACTIVE ADD NODE ON FACE
     // Adaptation ---------------------//--------------------------------
     OpMGAdapt                = 8020,   // MENU ADAPTATION - MG-ADAPT
     OpHomardAdapt            = 8021,   // MENU ADAPTATION - HOMARD-ADAPT
diff --git a/src/SMESHGUI/SMESHGUI_RemoveNodeReconnectionDlg.cxx b/src/SMESHGUI/SMESHGUI_RemoveNodeReconnectionDlg.cxx
new file mode 100644 (file)
index 0000000..890c630
--- /dev/null
@@ -0,0 +1,485 @@
+// Copyright (C) 2007-2021  CEA/DEN, EDF R&D, OPEN CASCADE
+//
+// Copyright (C) 2003-2007  OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
+// CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
+//
+// 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
+//
+
+// File   : SMESHGUI_MakeNodeAtPointDlg.cxx
+// Author : Edward AGAPOV, Open CASCADE S.A.S.
+// SMESH includes
+//
+#include "SMESHGUI_RemoveNodeReconnectionDlg.h"
+
+#include "SMESHGUI.h"
+#include "SMESHGUI_IdValidator.h"
+#include "SMESHGUI_MeshUtils.h"
+#include "SMESHGUI_VTKUtils.h"
+#include "SMESHGUI_MeshEditPreview.h"
+
+#include <SMDS_Mesh.hxx>
+#include <SMESH_Actor.h>
+#include <SMESH_ActorUtils.h>
+#include <SMESH_LogicalFilter.hxx>
+
+// SALOME GUI includes
+#include <LightApp_SelectionMgr.h>
+#include <SALOME_ListIO.hxx>
+#include <SUIT_Desktop.h>
+#include <SVTK_ViewModel.h>
+#include <SVTK_ViewWindow.h>
+#include <SalomeApp_Tools.h>
+#include <SalomeApp_TypeFilter.h>
+#include <SUIT_ResourceMgr.h>
+#include <SUIT_OverrideCursor.h>
+#include <SUIT_MessageBox.h>
+
+// Qt includes
+#include <QApplication>
+#include <QGroupBox>
+#include <QGridLayout>
+#include <QHBoxLayout>
+#include <QVBoxLayout>
+#include <QLineEdit>
+#include <QPushButton>
+#include <QLabel>
+#include <QRadioButton>
+#include <QCheckBox>
+#include <QButtonGroup>
+
+// VTK includes
+#include <vtkProperty.h>
+
+// IDL includes
+#include <SALOMEconfig.h>
+#include CORBA_SERVER_HEADER(SMESH_Mesh)
+#include CORBA_SERVER_HEADER(SMESH_MeshEditor)
+
+#define SPACING 6
+#define MARGIN  11
+
+//=======================================================================
+/*!
+ * \brief Dialog to Remove a node with elements re-connection
+ */
+//=======================================================================
+
+SMESHGUI_RemoveNodeReconnectionDlg::SMESHGUI_RemoveNodeReconnectionDlg()
+  : SMESHGUI_Dialog( 0, false, true )
+{
+  setWindowTitle(tr("CAPTION"));
+
+  QVBoxLayout* aDlgLay = new QVBoxLayout (mainFrame());
+  aDlgLay->setMargin(0);
+  aDlgLay->setSpacing(SPACING);
+  myMainFrame = createMainFrame(mainFrame());
+
+  aDlgLay->addWidget(myMainFrame);
+
+  aDlgLay->setStretchFactor(myMainFrame, 1);
+}
+
+//=======================================================================
+// function : createMainFrame()
+// purpose  : Create frame containing dialog's input fields
+//=======================================================================
+
+QWidget* SMESHGUI_RemoveNodeReconnectionDlg::createMainFrame (QWidget* theParent)
+{
+  QWidget* aFrame = new QWidget(theParent);
+
+  // Node to remove
+
+  myNodeToMoveGrp = new QGroupBox(tr("NODE_2REMOVE"), aFrame);
+  myNodeToMoveGrp->setSizePolicy( QSizePolicy( QSizePolicy::Expanding, QSizePolicy::Fixed ) );
+  QLabel* idLabel = new QLabel(tr("NODE_2REMOVE_ID"), myNodeToMoveGrp);
+  myId = new QLineEdit(myNodeToMoveGrp);
+  myId->setValidator(new SMESHGUI_IdValidator(this, 1));
+
+  QGridLayout* myNodeToMoveGrpLayout = new QGridLayout(myNodeToMoveGrp);
+  myNodeToMoveGrpLayout->setSpacing(SPACING);
+  myNodeToMoveGrpLayout->setMargin(MARGIN);
+
+  myNodeToMoveGrpLayout->addWidget( idLabel, 0, 0 );
+  myNodeToMoveGrpLayout->addWidget( myId,    0, 2 );
+
+  // Preview
+
+  myPreviewChkBox = new QCheckBox( tr("PREVIEW"), aFrame);
+
+  QVBoxLayout* aLay = new QVBoxLayout(aFrame);
+  aLay->addWidget(myNodeToMoveGrp);
+  aLay->addWidget(myPreviewChkBox);
+
+  return aFrame;
+}
+
+//================================================================================
+/*!
+ * \brief Constructor
+ */
+//================================================================================
+
+SMESHGUI_RemoveNodeReconnectionOp::SMESHGUI_RemoveNodeReconnectionOp()
+{
+  mySimulation = 0;
+  mySMESHGUI = 0;
+  myDlg = new SMESHGUI_RemoveNodeReconnectionDlg;
+  myFilter = 0;
+  myHelpFileName = "removing_nodes_and_elements.html#removing-nodes-reconnect-anchor";
+
+  myNoPreview = false;
+
+  // connect signals and slots
+  connect(myDlg->myId,            SIGNAL (textChanged(const QString&)),SLOT(redisplayPreview()));
+  connect(myDlg->myPreviewChkBox, SIGNAL (toggled(bool)),              SLOT(redisplayPreview()));
+  connect(myDlg->myId,            SIGNAL (textChanged(const QString&)),SLOT(onTextChange(const QString&)));
+}
+
+//=======================================================================
+// function : startOperation()
+// purpose  : Init dialog fields, connect signals and slots, show dialog
+//=======================================================================
+
+void SMESHGUI_RemoveNodeReconnectionOp::startOperation()
+{
+  myNoPreview = false;
+  myMeshActor = 0;
+
+  // init simulation with a current View
+  if ( mySimulation ) delete mySimulation;
+  mySMESHGUI = getSMESHGUI();
+  mySimulation = new SMESHGUI_MeshEditPreview(SMESH::GetViewWindow( mySMESHGUI ) );
+  connect(mySMESHGUI, SIGNAL (SignalActivatedViewManager()), this, SLOT(onOpenView()));
+  connect(mySMESHGUI, SIGNAL (SignalCloseView()), this, SLOT(onCloseView()));
+  vtkProperty* aProp = vtkProperty::New();
+  aProp->SetRepresentationToWireframe();
+  aProp->SetColor(250, 0, 250);
+  aProp->SetPointSize(5);
+  aProp->SetLineWidth( SMESH::GetFloat("SMESH:element_width",1) + 1);
+  mySimulation->GetActor()->SetProperty(aProp);
+  aProp->Delete();
+
+  // SalomeApp_TypeFilter depends on a current study
+  if ( myFilter ) delete myFilter;
+  // QList<SUIT_SelectionFilter*> filters;
+  // filters.append( new SalomeApp_TypeFilter((SalomeApp_Study*)study(), "SMESH" ));
+  // myFilter = new SMESH_LogicalFilter( filters, SMESH_LogicalFilter::LO_OR );
+
+  // IPAL19360
+  SMESHGUI_SelectionOp::startOperation(); // this method should be called only after filter creation
+  myDlg->myId->setText("");
+
+  myDlg->show();
+
+  onSelectionDone(); // init myMeshActor
+}
+
+//================================================================================
+/*!
+ * \brief Stops operation
+ */
+//================================================================================
+
+void SMESHGUI_RemoveNodeReconnectionOp::stopOperation()
+{
+  myNoPreview = true;
+  if ( mySimulation )
+  {
+    mySimulation->SetVisibility(false);
+    delete mySimulation;
+    mySimulation = 0;
+  }
+  if ( myMeshActor ) {
+    myMeshActor = 0;
+  }
+  SMESH::SetPointRepresentation( false );
+  SMESH::RepaintCurrentView();
+
+  disconnect(mySMESHGUI, SIGNAL (SignalActivatedViewManager()), this, SLOT(onOpenView()));
+  disconnect(mySMESHGUI, SIGNAL (SignalCloseView()),            this, SLOT(onCloseView()));
+  //selectionMgr()->removeFilter( myFilter );
+  SMESHGUI_SelectionOp::stopOperation();
+}
+
+//================================================================================
+/*!
+ * \brief perform it's intention action: move or create a node
+ */
+//================================================================================
+
+bool SMESHGUI_RemoveNodeReconnectionOp::onApply()
+{
+  if( SMESHGUI::isStudyLocked() )
+    return false;
+
+  if ( !myMeshActor ) {
+    SUIT_MessageBox::warning( dlg(), tr( "SMESH_WRN_WARNING" ), tr("INVALID_MESH") );
+    dlg()->show();
+    return false;
+  }
+
+  QString msg;
+  if ( !isValid( msg ) ) { // node id is invalid
+    if ( !msg.isEmpty() )
+      SUIT_MessageBox::warning( dlg(), tr( "SMESH_WRN_WARNING" ), tr("INVALID_ID") );
+    dlg()->show();
+    return false;
+  }
+
+  try {
+    SMESH::SMESH_Mesh_var aMesh = SMESH::GetMeshByIO( myMeshActor->getIO() );
+    if ( aMesh->_is_nil() )
+    {
+      SUIT_MessageBox::information(SMESHGUI::desktop(), tr("SMESH_ERROR"), tr("SMESHG_NO_MESH") );
+      return true;
+    }
+    SMESH::SMESH_MeshEditor_var aMeshEditor = aMesh->GetMeshEditor();
+    if ( aMeshEditor->_is_nil() )
+      return true;
+
+    bool ok;
+    int anId = myDlg->myId->text().toInt( &ok );
+    myDlg->myId->setText( "" );
+
+    aMeshEditor->RemoveNodeWithReconnection( anId );
+
+    SALOME_ListIO aList;
+    selectionMgr()->setSelectedObjects(aList,false);
+    aList.Append( myMeshActor->getIO() );
+    selectionMgr()->setSelectedObjects(aList,false);
+    SMESH::UpdateView();
+    SMESHGUI::Modified();
+
+  }
+  catch (const SALOME::SALOME_Exception& S_ex) {
+    SalomeApp_Tools::QtCatchCorbaException(S_ex);
+  }
+  catch (...) {
+  }
+
+  return true;
+}
+
+//================================================================================
+/*!
+ * \brief Check selected node id validity
+ */
+//================================================================================
+
+bool SMESHGUI_RemoveNodeReconnectionOp::isValid( QString& msg )
+{
+  bool ok = true;
+  if ( myMeshActor )
+  {
+    ok = false;
+    int id = myDlg->myId->text().toInt();
+    if ( id > 0 )
+      if ( SMDS_Mesh* aMesh = myMeshActor->GetObject()->GetMesh() )
+        ok = aMesh->FindNode( id );
+    if ( !ok )
+      msg += tr("INVALID_ID") + "\n";
+  }
+
+  return ok;
+}
+
+//================================================================================
+/*!
+ * \brief SLOT called when selection changed
+ */
+//================================================================================
+
+void SMESHGUI_RemoveNodeReconnectionOp::onSelectionDone()
+{
+  if ( !myDlg->isVisible() || !myDlg->isEnabled() )
+    return;
+
+  myDlg->myId->setText("");
+  myNoPreview = true;
+  try
+  {
+    SALOME_ListIO aList;
+    selectionMgr()->selectedObjects( aList, SVTK_Viewer::Type() );
+    if ( aList.Extent() != 1)
+      return;
+    Handle(SALOME_InteractiveObject) anIO = aList.First();
+    myMeshActor = SMESH::FindActorByEntry( anIO->getEntry() );
+
+    QString aString;
+    int nbElems = SMESH::GetNameOfSelectedElements( selector(), anIO, aString );
+    if ( nbElems == 1 )
+      myDlg->myId->setText( aString );
+
+  } catch (...) {
+  }
+
+  myNoPreview = false;
+  redisplayPreview();
+}
+
+//================================================================================
+/*!
+ * \brief update preview
+ */
+//================================================================================
+
+void SMESHGUI_RemoveNodeReconnectionOp::redisplayPreview()
+{
+  if ( myNoPreview )
+    return;
+  myNoPreview = true;
+
+  if ( !myMeshActor )
+    onSelectionDone();
+
+  SMESH::MeshPreviewStruct_var aMeshPreviewStruct;
+
+  QString msg;
+  if ( myMeshActor && isValid( msg ) && myDlg->myPreviewChkBox->isChecked() )
+    try {
+      SMESH::SMESH_Mesh_var aMesh = SMESH::GetMeshByIO( myMeshActor->getIO() );
+      if ( !aMesh->_is_nil() )
+      {
+        SMESH::SMESH_MeshEditor_var aPreviewer = aMesh->GetMeshEditPreviewer();
+        if (!aPreviewer->_is_nil())
+        {
+          int anId = myDlg->myId->text().toInt();
+          aPreviewer->RemoveNodeWithReconnection( anId );
+          aMeshPreviewStruct = aPreviewer->GetPreviewData();
+        }
+      }
+    }
+    catch (...) {
+    }
+
+  if (!mySimulation)
+    mySimulation = new SMESHGUI_MeshEditPreview(SMESH::GetViewWindow( mySMESHGUI ));
+
+  // display data
+  if ( & aMeshPreviewStruct.in() )
+  {
+    mySimulation->SetData( aMeshPreviewStruct.in() );
+  }
+  else
+  {
+    mySimulation->SetVisibility( false );
+  }
+
+  myNoPreview = false;
+}
+
+//=================================================================================
+/*!
+ * \brief SLOT called when the viewer opened
+ */
+//=================================================================================
+
+void SMESHGUI_RemoveNodeReconnectionOp::onOpenView()
+{
+  if ( mySimulation )
+  {
+    mySimulation->SetVisibility( false );
+    SMESH::SetPointRepresentation( false );
+  }
+  else
+  {
+    mySimulation = new SMESHGUI_MeshEditPreview(SMESH::GetViewWindow( mySMESHGUI ));
+  }
+}
+
+//=================================================================================
+/*!
+ * \brief SLOT called when the viewer closed
+ */
+//=================================================================================
+
+void SMESHGUI_RemoveNodeReconnectionOp::onCloseView()
+{
+  delete mySimulation;
+  mySimulation = 0;
+}
+
+//================================================================================
+/*!
+ * \brief SLOT called when the node id is manually changed
+ */
+//================================================================================
+
+void SMESHGUI_RemoveNodeReconnectionOp::onTextChange( const QString& theText )
+{
+  if( myMeshActor )
+  {
+    if( SMDS_Mesh* aMesh = myMeshActor->GetObject()->GetMesh() )
+    {
+      Handle(SALOME_InteractiveObject) anIO = myMeshActor->getIO();
+      SALOME_ListIO aList;
+      aList.Append( anIO );
+      selectionMgr()->setSelectedObjects( aList, false );
+
+      if ( const SMDS_MeshNode* aNode = aMesh->FindNode( theText.toInt() ))
+      {
+        SVTK_TVtkIDsMap aListInd;
+        aListInd.Add( FromSmIdType<int>( aNode->GetID()) );
+        selector()->AddOrRemoveIndex( anIO, aListInd, false );
+        if( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow( SMESHGUI::GetSMESHGUI() ) )
+          aViewWindow->highlight( anIO, true, true );
+      }
+    }
+  }
+}
+
+//================================================================================
+/*!
+ * \brief Activate Node selection
+ */
+//================================================================================
+
+void SMESHGUI_RemoveNodeReconnectionOp::activateSelection()
+{
+  selectionMgr()->clearFilters();
+  SMESH::SetPointRepresentation( true );
+  //selectionMgr()->installFilter( myFilter );
+  setSelectionMode( NodeSelection );
+}
+
+//================================================================================
+/*!
+ * \brief Destructor
+ */
+//================================================================================
+
+SMESHGUI_RemoveNodeReconnectionOp::~SMESHGUI_RemoveNodeReconnectionOp()
+{
+  if ( myDlg )        delete myDlg;
+  if ( mySimulation ) delete mySimulation;
+  if ( myFilter )     delete myFilter;
+}
+
+//================================================================================
+/*!
+ * \brief Gets dialog of this operation
+ * \retval LightApp_Dialog* - pointer to dialog of this operation
+ */
+//================================================================================
+
+LightApp_Dialog* SMESHGUI_RemoveNodeReconnectionOp::dlg() const
+{
+  return myDlg;
+}
+
diff --git a/src/SMESHGUI/SMESHGUI_RemoveNodeReconnectionDlg.h b/src/SMESHGUI/SMESHGUI_RemoveNodeReconnectionDlg.h
new file mode 100644 (file)
index 0000000..9f55519
--- /dev/null
@@ -0,0 +1,110 @@
+// Copyright (C) 2007-2021  CEA/DEN, EDF R&D, OPEN CASCADE
+//
+// Copyright (C) 2003-2007  OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
+// CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
+//
+// 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
+//
+
+// File   : SMESHGUI_RemoveNodeReconnectionDlg.h
+// Author : Edward AGAPOV, Open CASCADE S.A.S.
+//
+#ifndef __SMESHGUI_RemoveNodeReconnection_HXX__
+#define __SMESHGUI_RemoveNodeReconnection_HXX__
+
+#include "SMESHGUI_Dialog.h"
+#include "SMESHGUI_SelectionOp.h"
+
+class QButtonGroup;
+class QCheckBox;
+class QGroupBox;
+class QLineEdit;
+class QPushButton;
+class QRadioButton;
+class SMESHGUI_SpinBox;
+class SMESHGUI_MeshEditPreview;
+class SMESHGUI_RemoveNodeReconnectionDlg;
+
+/*!
+ * \brief Remove a node with elements re-connection
+ */
+class SMESHGUI_EXPORT SMESHGUI_RemoveNodeReconnectionOp: public SMESHGUI_SelectionOp
+{
+  Q_OBJECT
+
+public:
+  SMESHGUI_RemoveNodeReconnectionOp();
+  virtual ~SMESHGUI_RemoveNodeReconnectionOp();
+
+  virtual LightApp_Dialog*       dlg() const;  
+
+protected:
+
+  virtual void                   startOperation();
+  virtual void                   stopOperation();
+
+  virtual void                   activateSelection();
+
+  bool                           isValid( QString& );
+
+protected slots:
+  virtual bool                   onApply();
+
+private slots:
+  void                           onSelectionDone();
+  void                           redisplayPreview();
+  void                           onTextChange( const QString& );
+  void                           onOpenView();
+  void                           onCloseView();
+
+private:
+
+  SMESHGUI_RemoveNodeReconnectionDlg*  myDlg;
+
+  SUIT_SelectionFilter*         myFilter;
+  SMESHGUI*                     mySMESHGUI;
+  SMESHGUI_MeshEditPreview*     mySimulation;
+  SMESH_Actor*                  myMeshActor;
+  bool                          myNoPreview;
+};
+
+/*!
+ * \brief Dialog to make a mesh pass through a point
+ */
+
+class SMESHGUI_EXPORT SMESHGUI_RemoveNodeReconnectionDlg : public SMESHGUI_Dialog
+{
+  Q_OBJECT
+
+public:
+  SMESHGUI_RemoveNodeReconnectionDlg();
+
+private:
+  QWidget*                      createMainFrame( QWidget* );
+
+  QWidget*                      myMainFrame;
+  QGroupBox*                    myNodeToMoveGrp;
+  QLineEdit*                    myId;
+  QCheckBox*                    myPreviewChkBox;
+
+  QString                       myHelpFileName;
+
+  friend class SMESHGUI_RemoveNodeReconnectionOp;
+
+};
+
+#endif // SMESHGUI_RemoveNodeReconnectionDLG_H
index 676c0aad89b4b25e8f93f6f239661f6da820d75a..ee189aa913654d547edba149462c42a7b2c976a6 100644 (file)
             <source>ICON_DLG_MOVE_WITHOUT_NODE</source>
             <translation>mesh_move_without_node.png</translation>
         </message>
+        <message>
+            <source>ICON_DLG_MOVE_NODE_INTERACTIVE</source>
+            <translation>mesh_move_node_interactive.png</translation>
+        </message>
+        <message>
+            <source>ICON_SPLIT_DIAG_INTERACTIVE</source>
+            <translation>mesh_split_diag_interactive.png</translation>
+        </message>
+        <message>
+            <source>ICON_SPLIT_FACE_INTERACTIVE</source>
+            <translation>mesh_split_face_interactive.png</translation>
+        </message>
         <message>
             <source>ICON_DLG_NODE</source>
             <translation>mesh_vertex.png</translation>
             <source>ICON_DLG_REM_NODE</source>
             <translation>mesh_rem_node.png</translation>
         </message>
+        <message>
+            <source>ICON_REM_NODE_RECON</source>
+            <translation>mesh_rem_node_recon.png</translation>
+        </message>
         <message>
             <source>ICON_DLG_REM_ORPHAN_NODES</source>
             <translation>mesh_rem_orphan_nodes.png</translation>
index 871b155b0fa2ef69b1a2cd48dcaee84954318134..f5db909ea6d9ddcb8a5e676c9abb49f7a7aa6148 100644 (file)
         <source>MEN_MESH_THROU_POINT</source>
         <translation>Move Node</translation>
     </message>
+    <message>
+        <source>MEN_MOVE_NODE_INTRCT</source>
+        <translation>Move node by mouse</translation>
+    </message>
+    <message>
+        <source>MEN_SPLIT_DIAG_INTRC</source>
+        <translation>Add node on segment</translation>
+    </message>
+    <message>
+        <source>MEN_SPLIT_FACE_INTRC</source>
+        <translation>Add node to triangle</translation>
+    </message>
     <message>
         <source>MEN_MIN_ANG</source>
         <translation>Minimum Angle</translation>
         <source>MEN_REMOVE_NODES</source>
         <translation>Nodes</translation>
     </message>
+    <message>
+        <source>MEN_REMOVE_NODE_RECON</source>
+        <translation>Node with reconnection</translation>
+    </message>
     <message>
         <source>MEN_REMOVE_ORPHAN_NODES</source>
         <translation>Orphan Nodes</translation>
@@ -3612,6 +3628,18 @@ Use Display Entity menu command to show them.
         <source>STB_MESH_THROU_POINT</source>
         <translation>Move Node</translation>
     </message>
+    <message>
+        <source>STB_MOVE_NODE_INTRCT</source>
+        <translation>Move Node by mouse</translation>
+    </message>
+    <message>
+        <source>STB_SPLIT_DIAG_INTRC</source>
+        <translation>Add node on segment</translation>
+    </message>
+    <message>
+        <source>STB_SPLIT_FACE_INTRC</source>
+        <translation>Add node to triangle</translation>
+    </message>
     <message>
         <source>STB_MIN_ANG</source>
         <translation>Minimum Angle</translation>
@@ -3700,6 +3728,10 @@ Use Display Entity menu command to show them.
         <source>STB_REMOVE_NODES</source>
         <translation>Remove nodes</translation>
     </message>
+    <message>
+        <source>STB_REMOVE_NODE_RECON</source>
+        <translation>Remove node with reconnection</translation>
+    </message>
     <message>
         <source>STB_REMOVE_ORPHAN_NODES</source>
         <translation>Remove orphan nodes</translation>
@@ -3896,6 +3928,10 @@ Use Display Entity menu command to show them.
         <source>TB_RENUMBER</source>
         <translation>Renumbering Toolbar</translation>
     </message>
+    <message>
+        <source>TB_INTERACT</source>
+        <translation>Interactive modifications Toolbar</translation>
+    </message>
     <message>
         <source>TB_TRANSFORM</source>
         <translation>Transformation Toolbar</translation>
@@ -4308,6 +4344,18 @@ Use Display Entity menu command to show them.
         <source>TOP_MESH_THROU_POINT</source>
         <translation>Move Node</translation>
     </message>
+    <message>
+        <source>TOP_MOVE_NODE_INTRCT</source>
+        <translation>Move node by mouse</translation>
+    </message>
+    <message>
+        <source>TOP_SPLIT_DIAG_INTRC</source>
+        <translation>Add node on segment</translation>
+    </message>
+    <message>
+        <source>TOP_SPLIT_FACE_INTRC</source>
+        <translation>Add node to triangle</translation>
+    </message>
     <message>
         <source>TOP_MIN_ANG</source>
         <translation>Minimum Angle</translation>
@@ -4396,6 +4444,10 @@ Use Display Entity menu command to show them.
         <source>TOP_REMOVE_NODES</source>
         <translation>Remove nodes</translation>
     </message>
+    <message>
+        <source>TOP_REMOVE_NODE_RECON</source>
+        <translation>Remove node with reconnection</translation>
+    </message>
     <message>
         <source>TOP_REMOVE_ORPHAN_NODES</source>
         <translation>Remove orphan nodes</translation>
@@ -6713,6 +6765,10 @@ Please specify them and try again</translation>
         <source>DESTINATION</source>
         <translation>Destination</translation>
     </message>
+    <message>
+        <source>DESTINATION_BY_MOUSE</source>
+        <translation>Destination by mouse click</translation>
+    </message>
     <message>
         <source>MOVE_NODE</source>
         <translation>Move node</translation>
@@ -6741,6 +6797,116 @@ Please specify them and try again</translation>
         <translation>Mesh to modify not selected</translation>
     </message>
 </context>
+<context>
+    <name>SMESHGUI_AddNodeOnSegmentDlg</name>
+    <message>
+        <source>CAPTION</source>
+        <translation>Add node to segment</translation>
+    </message>
+    <message>
+        <source>SEGMENT_GROUP</source>
+        <translation>Edge between neighboring triangles</translation>
+    </message>
+    <message>
+        <source>SEGMENT</source>
+        <translation>Edge</translation>
+    </message>
+    <message>
+        <source>POSITION_GROUP</source>
+        <translation>Node on Edge</translation>
+    </message>
+    <message>
+        <source>POSITION</source>
+        <translation>Location by mouse click</translation>
+    </message>
+</context>
+<context>
+    <name>SMESHGUI_AddNodeOnSegmentOp</name>
+    <message>
+        <source>INVALID_EDGE</source>
+        <translation>Edge is invalid</translation>
+    </message>
+    <message>
+        <source>INVALID_MESH</source>
+        <translation>Mesh to modify not selected</translation>
+    </message>
+    <message>
+        <source>NO_ELEMENTS</source>
+        <translation>Edge belongs to none face</translation>
+    </message>
+    <message>
+        <source>VOLUME_FOUND</source>
+        <translation>Can't split an edge of a volume</translation>
+    </message>
+    <message>
+        <source>NOT_TRIANGLE_FACE_FOUND</source>
+        <translation>Operation applies to triangles only</translation>
+    </message>
+    <message>
+        <source>BAD_POSITION</source>
+        <translation>Position on edge is incorrect</translation>
+    </message>
+</context>
+<context>
+    <name>SMESHGUI_AddNodeOnFaceDlg</name>
+    <message>
+        <source>CAPTION</source>
+        <translation>Add node to triangle</translation>
+    </message>
+    <message>
+        <source>FACE_GROUP</source>
+        <translation>Triangle to split</translation>
+    </message>
+    <message>
+        <source>FACE_ID</source>
+        <translation>ID</translation>
+    </message>
+    <message>
+        <source>XYZ_GROUP</source>
+        <translation>Node location by mouse click</translation>
+    </message>
+</context>
+<context>
+    <name>SMESHGUI_AddNodeOnFaceOp</name>
+    <message>
+        <source>INVALID_MESH</source>
+        <translation>Mesh to modify not selected</translation>
+    </message>
+    <message>
+        <source>NOT_FACE</source>
+        <translation>Not a face selected</translation>
+    </message>
+    <message>
+        <source>INVALID_ID</source>
+        <translation>Face ID is invalid</translation>
+    </message>
+</context>
+<context>
+    <name>SMESHGUI_RemoveNodeReconnectionDlg</name>
+    <message>
+        <source>CAPTION</source>
+        <translation>Remove Node with Reconnection</translation>
+    </message>
+    <message>
+        <source>NODE_2REMOVE_ID</source>
+        <translation>ID</translation>
+    </message>
+    <message>
+        <source>NODE_2REMOVE</source>
+        <translation>Node</translation>
+    </message>
+</context>
+<context>
+    <name>SMESHGUI_RemoveNodeReconnectionOp</name>
+    <message>
+        <source>INVALID_ID</source>
+        <translation>Node ID is invalid</translation>
+    </message>
+    <message>
+        <source>INVALID_MESH</source>
+        <translation>Mesh to modify not selected</translation>
+    </message>
+</context>
 <context>
     <name>SMESHGUI_FindElemByPointDlg</name>
     <message>
index 9f0c88e20e9c849f9e1efe4dc7c1c24ea94853f7..9bc76d69d4da63c1da3ed8857a3c0a7b9a2688cd 100644 (file)
@@ -2252,6 +2252,56 @@ std::vector< const SMDS_MeshNode*> SMESH_MeshAlgos::GetCommonNodes(const SMDS_Me
   return common;
 }
 
+//================================================================================
+/*!
+ * \brief Return true if a node is on a boundary of 2D mesh.
+ *        Optionally returns two neighboring boundary nodes (or more in non-manifold mesh)
+ */
+//================================================================================
+
+bool SMESH_MeshAlgos::IsOn2DBoundary( const SMDS_MeshNode*                 theNode,
+                                      std::vector< const SMDS_MeshNode*> * theNeibors )
+{
+  typedef NCollection_DataMap< SMESH_TLink, int, SMESH_TLink > TLinkCountMap;
+  TLinkCountMap linkCountMap( 10 );
+
+  int nbFreeLinks = 0;
+  for ( SMDS_ElemIteratorPtr fIt = theNode->GetInverseElementIterator(SMDSAbs_Face); fIt->more(); )
+  {
+    const SMDS_MeshElement* face = fIt->next();
+    const int          nbCorners = face->NbCornerNodes();
+
+    int    iN = face->GetNodeIndex( theNode );
+    int iPrev = ( iN - 1 + nbCorners ) % nbCorners;
+    int iNext = ( iN + 1 ) % nbCorners;
+
+    for ( int i : { iPrev, iNext } )
+    {
+      SMESH_TLink link( theNode, face->GetNode( i ));
+      int* count = linkCountMap.ChangeSeek( link );
+      if ( count )  ++( *count );
+      else          linkCountMap.Bind( link, 1 );
+
+      if ( !count ) ++nbFreeLinks;
+      else          --nbFreeLinks;
+    }
+  }
+
+  if ( theNeibors )
+  {
+    theNeibors->clear();
+    theNeibors->reserve( nbFreeLinks );
+    for ( TLinkCountMap::Iterator linkIt( linkCountMap ); linkIt.More(); linkIt.Next() )
+      if ( linkIt.Value() == 1 )
+      {
+        theNeibors->push_back( linkIt.Key().node1() );
+        if ( theNeibors->back() == theNode )
+          theNeibors->back() = linkIt.Key().node2();
+      }
+  }
+  return nbFreeLinks > 0;
+}
+
 //================================================================================
 /*!
  * \brief Return true if node1 encounters first in the face and node2, after
index 5c5df659b554a609544285f3d2bb885dc7a0ac80..36bbeeb45de14057748ed74445e84eb5a6e15d18 100644 (file)
@@ -199,6 +199,12 @@ namespace SMESH_MeshAlgos
   SMESHUtils_EXPORT
   std::vector< const SMDS_MeshNode*> GetCommonNodes(const SMDS_MeshElement* e1,
                                                     const SMDS_MeshElement* e2);
+  /*!
+   * \brief Return true if a node is on a boundary of 2D mesh.
+   *        Optionally returns two neighboring boundary nodes (or more in non-manifold mesh)
+   */
+  SMESHUtils_EXPORT bool IsOn2DBoundary( const SMDS_MeshNode* node,
+                                         std::vector< const SMDS_MeshNode*> * neibors = nullptr );
   /*!
    * \brief Return true if node1 encounters first in the face and node2, after.
    *        The nodes are supposed to be neighbor nodes in the face.
@@ -225,6 +231,7 @@ namespace SMESH_MeshAlgos
 
   /*!
    * \brief Mark elements given by SMDS_Iterator
+   * \sa SMDS_Mesh::SetAllNodesNotMarked() and SMDS_Mesh::SetAllCellsNotMarked()
    */
   template< class ElemIter >
   void MarkElems( ElemIter it, const bool isMarked )
index f69c70eef5c606baa4691bdfb280b2fe8b59b91d..67150c6e457bad44df2181f29d7cebab40210d9f 100644 (file)
@@ -2491,10 +2491,10 @@ void _pyMeshEditor::Process( const Handle(_pyCommand)& theCommand)
   static TStringSet sameMethods;
   if ( sameMethods.empty() ) {
     const char * names[] = {
-      "RemoveElements","RemoveNodes","RemoveOrphanNodes",
+      "RemoveElements","RemoveNodes","RemoveOrphanNodes","RemoveNodeWithReconnection",
       "AddNode","Add0DElement","AddEdge","AddFace","AddPolygonalFace","AddBall",
-      "AddVolume","AddPolyhedralVolume","AddPolyhedralVolumeByFaces",
-      "MoveNode", "MoveClosestNodeToPoint","InverseDiag","DeleteDiag",
+      "AddVolume","AddPolyhedralVolume","AddPolyhedralVolumeByFaces","AddNodeOnSegment",
+      "MoveNode", "MoveClosestNodeToPoint","InverseDiag","DeleteDiag","AddNodeOnFace",
       "Reorient","ReorientObject","Reorient2DBy3D","Reorient2DByNeighbours",
       "TriToQuad","TriToQuadObject", "QuadTo4Tri", "SplitQuad","SplitQuadObject",
       "BestSplit","Smooth","SmoothObject","SmoothParametric","SmoothParametricObject",
index 323a6603adf24d1994d9f0635c9964bc8c757539..9efa484fff15a1504e7f589da8bfecb6b6d9096b 100644 (file)
@@ -834,7 +834,7 @@ CORBA::Boolean SMESH_MeshEditor_i::RemoveNodes(const SMESH::smIdType_array & IDs
 
 //=============================================================================
 /*!
- *
+ * Remove orphan nodes
  */
 //=============================================================================
 
@@ -865,6 +865,58 @@ SMESH::smIdType SMESH_MeshEditor_i::RemoveOrphanNodes()
   return 0;
 }
 
+//=============================================================================
+/*!
+ * Remove a node and fill a hole appeared by changing surrounding faces
+ */
+//=============================================================================
+
+void SMESH_MeshEditor_i::RemoveNodeWithReconnection( SMESH::smIdType nodeID )
+{
+  SMESH_TRY;
+  initData();
+
+  const SMDS_MeshNode * node = getMeshDS()->FindNode( nodeID );
+  if ( ! node )
+    THROW_SALOME_CORBA_EXCEPTION( SMESH_Comment( "Invalid node ID ") << nodeID,
+                                  SALOME::BAD_PARAM);
+  if ( node->NbInverseElements( SMDSAbs_Volume ) > 0 )
+    THROW_SALOME_CORBA_EXCEPTION( "RemoveNodeWithReconnection() applies to 2D mesh only",
+                                  SALOME::BAD_PARAM);
+
+  if ( myIsPreviewMode ) // make preview data
+  {
+    // in a preview mesh, make edges linked to a node
+    TPreviewMesh& tmpMesh = *getPreviewMesh( SMDSAbs_Edge );
+    TIDSortedElemSet linkedNodes;
+    ::SMESH_MeshEditor::GetLinkedNodes( node, linkedNodes );
+    SMDS_MeshNode *nodeCpy1 = tmpMesh.Copy( node );
+    for ( const SMDS_MeshElement* n : linkedNodes )
+    {
+      SMDS_MeshNode *nodeCpy2 = tmpMesh.Copy ( cast2Node( n ));
+      tmpMesh.GetMeshDS()->AddEdge( nodeCpy1, nodeCpy2 );
+    }
+    // copy surrounding faces
+    for ( SMDS_ElemIteratorPtr fIt = node->GetInverseElementIterator( SMDSAbs_Face ); fIt->more(); )
+      tmpMesh.Copy ( fIt->next() );
+    
+    // remove copied node
+    if ( nodeCpy1 )
+      getEditor().RemoveNodeWithReconnection( nodeCpy1 );
+  }
+  else
+  {
+    getEditor().RemoveNodeWithReconnection( node );
+
+    // Update Python script
+    TPythonDump() << this << ".RemoveNodeWithReconnection( " << nodeID << " )";
+
+    declareMeshModified( /*isReComputeSafe=*/ true );
+  }
+
+  SMESH_CATCH( SMESH::throwCorbaException );
+}
+
 //=============================================================================
 /*!
  * Add a new node.
@@ -1567,6 +1619,121 @@ CORBA::Boolean SMESH_MeshEditor_i::DeleteDiag(SMESH::smIdType NodeID1,
   return 0;
 }
 
+//=============================================================================
+/*!
+ * \brief Split a diagonal of a quadrangle formed by two adjacent triangles
+ *        so that four new triangles appear in place of the two triangles
+ */
+//=============================================================================
+
+void SMESH_MeshEditor_i::AddNodeOnSegment(SMESH::smIdType nodeID1,
+                                          SMESH::smIdType nodeID2,
+                                          CORBA::Double   position)
+{
+  SMESH_TRY;
+  initData();
+
+  const SMDS_MeshNode * n1 = getMeshDS()->FindNode( nodeID1 );
+  const SMDS_MeshNode * n2 = getMeshDS()->FindNode( nodeID2 );
+  if ( !n1 )
+    THROW_SALOME_CORBA_EXCEPTION( SMESH_Comment( "Invalid node ID: ") << nodeID1,
+                                  SALOME::BAD_PARAM);
+  if ( !n2 )
+    THROW_SALOME_CORBA_EXCEPTION( SMESH_Comment( "Invalid node ID: ") << nodeID2,
+                                  SALOME::BAD_PARAM);
+
+  if ( myIsPreviewMode ) // make preview data
+  {
+    TPreviewMesh* tmpMesh = getPreviewMesh();
+    TIDSortedElemSet elemSet, avoidSet;
+    TopoDS_Shape shape;
+    while ( const SMDS_MeshElement* face = SMESH_MeshAlgos::FindFaceInSet( n1, n2,
+                                                                           elemSet, avoidSet ))
+    {
+      if ( avoidSet.empty() )
+      {
+        shape = getMeshDS()->IndexToShape( face->GetShapeID() );
+        if ( !shape.IsNull() )
+        {
+          tmpMesh->ShapeToMesh( TopoDS_Shape() );
+          tmpMesh->ShapeToMesh( shape );
+        }
+      }
+      SMDS_MeshElement* faceCopy = tmpMesh->Copy ( face );
+      avoidSet.insert( face );
+
+      if ( !shape.IsNull() )
+        tmpMesh->GetMeshDS()->SetMeshElementOnShape( faceCopy, shape );
+    }
+    n1 = tmpMesh->GetMeshDS()->FindNode( nodeID1 );
+    n2 = tmpMesh->GetMeshDS()->FindNode( nodeID2 );
+
+    if ( !shape.IsNull() )
+    {
+      tmpMesh->GetMeshDS()->SetMeshElementOnShape( n1, shape );
+      tmpMesh->GetMeshDS()->SetMeshElementOnShape( n2, shape );
+    }
+  }
+
+  getEditor().SplitEdge( n1, n2, position );
+
+  if ( !myIsPreviewMode )
+  {
+    // Update Python script
+    TPythonDump() << this << ".AddNodeOnSegment( "
+                  << nodeID1 << ", " << nodeID2 << ", " << position << " )";
+
+    declareMeshModified( /*isReComputeSafe=*/true );
+  }
+
+  SMESH_CATCH( SMESH::throwCorbaException );
+}
+
+//=============================================================================
+/*!
+ * \brief Split a face into triangles by adding a new node onto the face
+ *        and connecting the new node with face nodes
+ */
+//=============================================================================
+
+void  SMESH_MeshEditor_i::AddNodeOnFace(SMESH::smIdType theFaceID,
+                                        CORBA::Double   theX,
+                                        CORBA::Double   theY,
+                                        CORBA::Double   theZ)
+{
+  SMESH_TRY;
+  initData();
+
+  const SMDS_MeshElement * face = getMeshDS()->FindElement( theFaceID );
+  if ( !face )
+    THROW_SALOME_CORBA_EXCEPTION( SMESH_Comment( "Invalid face ID: ") << theFaceID,
+                                  SALOME::BAD_PARAM);
+  if ( face->GetType() != SMDSAbs_Face )
+    THROW_SALOME_CORBA_EXCEPTION( "The element is not a face ", SALOME::BAD_PARAM );
+
+  if ( myIsPreviewMode ) // make preview data
+  {
+    TPreviewMesh* tmpMesh = getPreviewMesh();
+    face = tmpMesh->Copy ( face );
+  }
+
+  getEditor().SplitFace( face, theX, theY, theZ );
+
+  if ( !myIsPreviewMode )
+  {
+    // Update Python script
+    TPythonDump() << this << ".AddNodeOnFace( "
+                  << theFaceID << ", "
+                  << theX << ", "
+                  << theY << ", "
+                  << theZ << " )";
+
+    declareMeshModified( /*isReComputeSafe=*/true );
+  }
+
+  SMESH_CATCH( SMESH::throwCorbaException );
+}
+
 //=============================================================================
 /*!
  *
@@ -2065,8 +2232,8 @@ CORBA::Boolean SMESH_MeshEditor_i::SplitQuadObject (SMESH::SMESH_IDSource_ptr th
  */
 //=============================================================================
 
-CORBA::Long SMESH_MeshEditor_i::BestSplit (CORBA::Long                 IDOfQuad,
-                                           SMESH::NumericalFunctor_ptr Criterion)
+CORBA::Short SMESH_MeshEditor_i::BestSplit (SMESH::smIdType             IDOfQuad,
+                                            SMESH::NumericalFunctor_ptr Criterion)
 {
   SMESH_TRY;
   initData();
@@ -4489,7 +4656,6 @@ CORBA::Boolean SMESH_MeshEditor_i::MoveNode(SMESH::smIdType NodeID,
     // move copied node
     if ( nodeCpy1 )
       tmpMesh.GetMeshDS()->MoveNode(nodeCpy1, x, y, z);
-    // fill preview data
   }
   else if ( theNodeSearcher ) // move node and update theNodeSearcher data accordingly
     theNodeSearcher->MoveNode(node, gp_Pnt( x,y,z ));
index 3d7072ed42a88e79c478f628dd958863266b3729..58c55686b46756a5a5e0fab35c39039948ba1714 100644 (file)
@@ -100,9 +100,10 @@ public:
    */
   std::string GenerateGroupName(const std::string& thePrefix);
 
-  CORBA::Boolean RemoveElements(const SMESH::smIdType_array & IDsOfElements);
-  CORBA::Boolean RemoveNodes   (const SMESH::smIdType_array & IDsOfNodes);
-  SMESH::smIdType    RemoveOrphanNodes();
+  CORBA::Boolean  RemoveElements(const SMESH::smIdType_array & IDsOfElements);
+  CORBA::Boolean  RemoveNodes   (const SMESH::smIdType_array & IDsOfNodes);
+  SMESH::smIdType RemoveOrphanNodes();
+  void            RemoveNodeWithReconnection(SMESH::smIdType nodeID);
 
   /*!
    * Methods for creation new elements.
@@ -170,15 +171,42 @@ public:
    */
   void SetMeshElementOnShape(SMESH::smIdType ElementID, CORBA::Long ShapeID);
 
-
+  /*!
+   * \brief Change node location
+   */
   CORBA::Boolean MoveNode(SMESH::smIdType NodeID,
                           CORBA::Double x, CORBA::Double y, CORBA::Double z);
 
+  /*!
+   * \brief Swap a diagonal of a quadrangle formed by two adjacent triangles
+   */
   CORBA::Boolean InverseDiag(SMESH::smIdType NodeID1, SMESH::smIdType NodeID2);
+  /*!
+   * \brief Delete a diagonal of a quadrangle formed by two adjacent triangles
+   *        so that a new quadrangle appears in place of the triangles
+   */
   CORBA::Boolean DeleteDiag(SMESH::smIdType NodeID1, SMESH::smIdType NodeID2);
+  /*!
+   * \brief Split a diagonal of a quadrangle formed by two adjacent triangles
+   *        so that four new triangles appear in place of the two triangles
+   */
+  void AddNodeOnSegment(SMESH::smIdType segmentNode1, SMESH::smIdType segmentNode2,
+                        CORBA::Double   position);
+  /*!
+   * \brief Split a face into triangles by adding a new node onto the face
+   *        and connecting the new node with face nodes
+   */
+  void AddNodeOnFace(SMESH::smIdType triangle,
+                     CORBA::Double x, CORBA::Double y, CORBA::Double z);
+
+  /*!
+   * \brief Change orientation of cells
+   */
   CORBA::Boolean Reorient(const SMESH::smIdType_array & IDsOfElements);
+  /*!
+   * \brief Change orientation of cells
+   */
   CORBA::Boolean ReorientObject(SMESH::SMESH_IDSource_ptr theObject);
-
   /*!
    * \brief Reorient faces contained in \a the2Dgroup.
    * \param the2Dgroup - the mesh or its part to reorient
@@ -231,7 +259,7 @@ public:
                                   CORBA::Boolean                Diag13);
   CORBA::Boolean SplitQuadObject (SMESH::SMESH_IDSource_ptr     theObject,
                                   CORBA::Boolean                Diag13);
-  CORBA::Long    BestSplit       (CORBA::Long                   IDOfQuad,
+  CORBA::Short   BestSplit       (SMESH::smIdType               IDOfQuad,
                                   SMESH::NumericalFunctor_ptr   Criterion);
   void           SplitVolumesIntoTetra(SMESH::SMESH_IDSource_ptr elems,
                                        CORBA::Short             methodFlags);
index 1d5269c19c3d0df8f324f56a9ff8c948725260c0..80f2c6d213d4a3a93267c16be8612d7036d46c8c 100644 (file)
@@ -4098,6 +4098,16 @@ class Mesh(metaclass = MeshMeta):
 
         return self.editor.RemoveNodes(IDsOfNodes)
 
+    def RemoveNodeWithReconnection(self, nodeID ):
+        """
+        Remove a node along with changing surrounding faces to cover a hole.
+
+        Parameters:
+                nodeID: ID of node to remove
+        """
+
+        return self.editor.RemoveNodeWithReconnection( nodeID )
+
     def RemoveOrphanNodes(self):
         """
         Remove all orphan (free) nodes from mesh
@@ -4591,6 +4601,30 @@ class Mesh(metaclass = MeshMeta):
 
         return self.editor.DeleteDiag(NodeID1, NodeID2)
 
+    def AddNodeOnSegment(self, Node1, Node2, position = 0.5):
+        """
+        Replace each triangle bound by Node1-Node2 segment with
+        two triangles by connecting a node made on the link with a node 
+        opposite to the link.
+
+        Parameters:
+                Node1: ID of the first node
+                Node2: ID of the second node
+                position: location [0,1] of the new node on the segment
+        """
+        return self.editor.AddNodeOnSegment(Node1, Node2, position)
+
+    def AddNodeOnFace(self, face, x, y, z):
+        """
+        Split a face into triangles by adding a new node onto the face
+        and connecting the new node with face nodes
+
+        Parameters:
+                face: ID of the face
+                x,y,z: coordinates of the new node
+        """
+        return self.editor.AddNodeOnFace(face, x, y, z)
+
     def Reorient(self, IDsOfElements=None):
         """
         Reorient elements by ids