From 8f00809a84ca43389ac7da19d50c55b9c51d7b9a Mon Sep 17 00:00:00 2001
From: Christophe Geuzaine <cgeuzaine@ulg.ac.be>
Date: Thu, 30 Mar 2017 19:37:19 +0100
Subject: [PATCH] GUI for boolean operations :-)

---
 Fltk/contextWindow.cpp     |   9 ++-
 Fltk/graphicWindow.cpp     | 116 ++++++++++++++++++++++++++++++++++++-
 Geo/GeoStringInterface.cpp |  25 ++++++++
 Geo/GeoStringInterface.h   |   5 ++
 4 files changed, 152 insertions(+), 3 deletions(-)

diff --git a/Fltk/contextWindow.cpp b/Fltk/contextWindow.cpp
index 8221a2431c..1201ede871 100644
--- a/Fltk/contextWindow.cpp
+++ b/Fltk/contextWindow.cpp
@@ -419,6 +419,8 @@ elementaryContextWindow::elementaryContextWindow(int deltaFontSize)
       }
       group[8]->end();
     }
+
+    /* FIXME: TODO
     // 9: Torus
     {
       group[9] = new Fl_Group
@@ -437,6 +439,7 @@ elementaryContextWindow::elementaryContextWindow(int deltaFontSize)
         (WB, WB + BH, width - 2 * WB, height - 2 * WB - BH, "Wedge");
       group[11]->end();
     }
+    */
     tab2->end();
   }
 
@@ -482,9 +485,11 @@ void elementaryContextWindow::updatePoint(double pt[3], int which)
 
 void elementaryContextWindow::show(int pane)
 {
-  if(pane < 0 || pane > 10) return;
+  // FIXME: TODO
+
+  if(pane < 0 || pane > 8) return;
 
-  for(int i = 0; i < 11; i++)
+  for(int i = 0; i < 9; i++)
     group[i]->hide();
 
   if(pane < 6){
diff --git a/Fltk/graphicWindow.cpp b/Fltk/graphicWindow.cpp
index c0c0caf6b3..b4eb683e2e 100644
--- a/Fltk/graphicWindow.cpp
+++ b/Fltk/graphicWindow.cpp
@@ -1580,6 +1580,110 @@ static void geometry_elementary_delete_cb(Fl_Widget *w, void *data)
   action_point_line_surface_volume(6, 0, (const char*)data);
 }
 
+static void geometry_elementary_boolean_cb(Fl_Widget *w, void *data)
+{
+  if(!data) return;
+  std::string mode((const char*)data);
+  bool selectObject = true;
+  std::vector<GEntity*> object, tool;
+
+  if(GModel::current()->getDim() == 3)
+    opt_geometry_volumes(0, GMSH_SET | GMSH_GUI, 1);
+  else if(GModel::current()->getDim() == 2)
+    opt_geometry_surfaces(0, GMSH_SET | GMSH_GUI, 1);
+
+  while(1) {
+    if(object.empty())
+      Msg::StatusGl("Select object\n"
+                    "[Press 'e' to end selection or 'q' to abort]");
+    else if(selectObject)
+      Msg::StatusGl("Select object\n"
+                    "[Press 'e' to end selection, 'u' to undo last selection or "
+                    "'q' to abort]");
+    else if(tool.empty())
+      Msg::StatusGl("Select tool\n"
+                    "[Press 'e' to end selection or 'q' to abort]");
+    else
+      Msg::StatusGl("Select tool\n"
+                    "[Press 'e' to end selection, 'u' to undo last selection or "
+                    "'q' to abort]");
+
+    char ib = FlGui::instance()->selectEntity(ENT_ALL);
+    if(ib == 'l') {
+      for(unsigned int i = 0; i < FlGui::instance()->selectedEdges.size(); i++){
+        if(FlGui::instance()->selectedEdges[i]->getSelection() != 1){
+          FlGui::instance()->selectedEdges[i]->setSelection(1);
+          if(selectObject)
+            object.push_back(FlGui::instance()->selectedEdges[i]);
+          else
+            tool.push_back(FlGui::instance()->selectedEdges[i]);
+        }
+      }
+      for(unsigned int i = 0; i < FlGui::instance()->selectedFaces.size(); i++){
+        if(FlGui::instance()->selectedFaces[i]->getSelection() != 1){
+          FlGui::instance()->selectedFaces[i]->setSelection(1);
+          if(selectObject)
+            object.push_back(FlGui::instance()->selectedFaces[i]);
+          else
+            tool.push_back(FlGui::instance()->selectedFaces[i]);
+        }
+      }
+      for(unsigned int i = 0; i < FlGui::instance()->selectedRegions.size(); i++){
+        if(FlGui::instance()->selectedRegions[i]->getSelection() != 1){
+          FlGui::instance()->selectedRegions[i]->setSelection(1);
+          if(selectObject)
+            object.push_back(FlGui::instance()->selectedRegions[i]);
+          else
+            tool.push_back(FlGui::instance()->selectedRegions[i]);
+        }
+      }
+    }
+    if(ib == 'r') {
+      for(unsigned int i = 0; i < FlGui::instance()->selectedEdges.size(); i++)
+        FlGui::instance()->selectedEdges[i]->setSelection(0);
+      for(unsigned int i = 0; i < FlGui::instance()->selectedFaces.size(); i++)
+        FlGui::instance()->selectedFaces[i]->setSelection(0);
+      for(unsigned int i = 0; i < FlGui::instance()->selectedRegions.size(); i++)
+        FlGui::instance()->selectedRegions[i]->setSelection(0);
+    }
+    if(ib == 'u') {
+      if(selectObject && object.size()){
+        object[object.size() - 1]->setSelection(0);
+        object.pop_back();
+      }
+      else if(tool.size()){
+        tool[tool.size() - 1]->setSelection(0);
+        tool.pop_back();
+      }
+    }
+    if(ib == 'e') {
+      if(selectObject){
+        if(object.empty())
+          Msg::Error("At least one object must be selected");
+        else
+          selectObject = false;
+      }
+      else if(tool.empty()){
+        Msg::Error("At least one tool must be selected");
+      }
+      else{
+        apply_boolean(GModel::current()->getFileName(), mode, object, tool);
+        GModel::current()->setSelection(0);
+        selectObject = true;
+        object.clear();
+        tool.clear();
+      }
+    }
+    if(ib == 'q') {
+      GModel::current()->setSelection(0);
+      break;
+    }
+  }
+
+  drawContext::global()->draw();
+  Msg::StatusGl("");
+}
+
 static void geometry_elementary_split_cb(Fl_Widget *w, void *data)
 {
   if(!data) return;
@@ -3792,12 +3896,14 @@ static menuItem static_modules[] = {
    (Fl_Callback *)geometry_elementary_add_new_cb, (void*)"Cylinder"} ,
   {"0Modules/Geometry/Elementary entities/Add/Block",
    (Fl_Callback *)geometry_elementary_add_new_cb, (void*)"Block"} ,
+  /* FIXME: TODO
   {"0Modules/Geometry/Elementary entities/Add/Torus",
    (Fl_Callback *)geometry_elementary_add_new_cb, (void*)"Torus"} ,
   {"0Modules/Geometry/Elementary entities/Add/Cone",
    (Fl_Callback *)geometry_elementary_add_new_cb, (void*)"Cone"} ,
   {"0Modules/Geometry/Elementary entities/Add/Wedge",
-   (Fl_Callback *)geometry_elementary_add_new_cb, (void*)"Wedge"} ,
+  (Fl_Callback *)geometry_elementary_add_new_cb, (void*)"Wedge"} ,
+  */
   {"0Modules/Geometry/Elementary entities/Add/Volume",
    (Fl_Callback *)geometry_elementary_add_new_cb, (void*)"Volume"} ,
   {"0Modules/Geometry/Elementary entities/Translate/Point",
@@ -3876,6 +3982,14 @@ static menuItem static_modules[] = {
    (Fl_Callback *)geometry_elementary_add_symmetry_cb, (void*)"Surface"} ,
   {"0Modules/Geometry/Elementary entities/Symmetry/Duplicate volume",
    (Fl_Callback *)geometry_elementary_add_symmetry_cb, (void*)"Volume"} ,
+  {"0Modules/Geometry/Elementary entities/Boolean/Intersection",
+   (Fl_Callback *)geometry_elementary_boolean_cb, (void*)"BooleanIntersection"} ,
+  {"0Modules/Geometry/Elementary entities/Boolean/Union",
+   (Fl_Callback *)geometry_elementary_boolean_cb, (void*)"BooleanUnion"} ,
+  {"0Modules/Geometry/Elementary entities/Boolean/Difference",
+   (Fl_Callback *)geometry_elementary_boolean_cb, (void*)"BooleanDifference"} ,
+  {"0Modules/Geometry/Elementary entities/Boolean/Fragments",
+   (Fl_Callback *)geometry_elementary_boolean_cb, (void*)"BooleanFragments"} ,
   {"0Modules/Geometry/Elementary entities/Delete/Point",
    (Fl_Callback *)geometry_elementary_delete_cb, (void*)"Point"} ,
   {"0Modules/Geometry/Elementary entities/Delete/Line",
diff --git a/Geo/GeoStringInterface.cpp b/Geo/GeoStringInterface.cpp
index 6de77b79c7..94410d0ff4 100644
--- a/Geo/GeoStringInterface.cpp
+++ b/Geo/GeoStringInterface.cpp
@@ -591,3 +591,28 @@ void split_edge(int edge_id, List_T *vertices, const std::string &fileName)
   sstream << "Split Line(" << edge_id << ") {" << list2string(vertices) << "};";
   add_infile(sstream.str(), fileName, true);
 }
+
+void apply_boolean(const std::string &fileName, const std::string &op,
+                   const std::vector<GEntity*> &object,
+                   const std::vector<GEntity*> &tool)
+{
+  std::ostringstream sstream;
+  sstream << op << "{ ";
+  for(unsigned int i = 0; i < object.size(); i++){
+    switch(object[i]->dim()){
+    case 3: sstream << "Volume{" << object[i]->tag() << "}; "; break;
+    case 2: sstream << "Surface{" << object[i]->tag() << "}; "; break;
+    case 1: sstream << "Line{" << object[i]->tag() << "}; "; break;
+    }
+  }
+  sstream << "Delete; }{ ";
+  for(unsigned int i = 0; i < tool.size(); i++){
+    switch(tool[i]->dim()){
+    case 3: sstream << "Volume{" << tool[i]->tag() << "}; "; break;
+    case 2: sstream << "Surface{" << tool[i]->tag() << "}; "; break;
+    case 1: sstream << "Line{" << tool[i]->tag() << "}; "; break;
+    }
+  }
+  sstream << "Delete; }";
+  add_infile(sstream.str(), fileName);
+}
diff --git a/Geo/GeoStringInterface.h b/Geo/GeoStringInterface.h
index 7672b9eb12..eb177c8941 100644
--- a/Geo/GeoStringInterface.h
+++ b/Geo/GeoStringInterface.h
@@ -10,6 +10,8 @@
 #include <vector>
 #include "ListUtils.h"
 
+class GEntity;
+
 void coherence(const std::string &fileName);
 void delet(List_T *list, const std::string &fileName, const std::string &what);
 void add_infile(const std::string &text, const std::string &fileName,
@@ -90,5 +92,8 @@ void protude(List_T *list, const std::string &fileName, const std::string &what,
              const std::string &px, const std::string &py, const std::string &pz,
              const std::string &angle);
 void split_edge(int edge_id, List_T *vertices, const std::string &fileName);
+void apply_boolean(const std::string &fileName, const std::string &op,
+                   const std::vector<GEntity*> &object,
+                   const std::vector<GEntity*> &tool);
 
 #endif
-- 
GitLab