From 8e86849c7b21967cb0b2fcb619671f6f95b462b5 Mon Sep 17 00:00:00 2001
From: Christophe Geuzaine <cgeuzaine@ulg.ac.be>
Date: Fri, 19 Nov 2010 16:18:33 +0000
Subject: [PATCH] a funny new plugin to create bubbles inscribed in the Voronoi
 of triangulations (useful for simulating synthetic foam-like materials)

---
 Fltk/FlGui.cpp           |   9 +-
 Fltk/optionWindow.cpp    |  14 ++--
 Geo/GModelIO_Mesh.cpp    |  18 ++--
 Geo/discreteEdge.cpp     |   3 +
 Plugin/Bubbles.cpp       | 175 +++++++++++++++++++++++++++++++++++++++
 Plugin/Bubbles.h         |  33 ++++++++
 Plugin/CMakeLists.txt    |   1 +
 Plugin/PluginManager.cpp |   3 +
 8 files changed, 238 insertions(+), 18 deletions(-)
 create mode 100644 Plugin/Bubbles.cpp
 create mode 100644 Plugin/Bubbles.h

diff --git a/Fltk/FlGui.cpp b/Fltk/FlGui.cpp
index 82bde297b5..f99de795ba 100644
--- a/Fltk/FlGui.cpp
+++ b/Fltk/FlGui.cpp
@@ -185,8 +185,13 @@ FlGui::FlGui(int argc, char **argv)
   // set default font size
   FL_NORMAL_SIZE = drawContext::global()->getFontSize();
 
-  // test for fltk 1.3 in 64 bit mode on MacOSX
-  //gl_texture_pile_height(1000);
+#if defined(__APPLE__) && defined(HAVE_64BIT_SIZE_T)
+#if (FL_MAJOR_VERSION == 1) && (FL_MINOR_VERSION == 3)
+  int numStrings = 1000;
+  if(gl_texture_pile_height() < numStrings)
+    gl_texture_pile_height(numStrings);
+#endif
+#endif
 
   // handle themes and tooltip font size
   if(CTX::instance()->guiTheme.size())
diff --git a/Fltk/optionWindow.cpp b/Fltk/optionWindow.cpp
index 3a15257df3..2766d0db7b 100644
--- a/Fltk/optionWindow.cpp
+++ b/Fltk/optionWindow.cpp
@@ -1226,7 +1226,7 @@ optionWindow::optionWindow(int deltaFontSize)
   FL_NORMAL_SIZE -= deltaFontSize;
 
   int width = 34 * FL_NORMAL_SIZE + WB;
-  int height = 13 * BH + 4 * WB;
+  int height = 12 * BH + 4 * WB;
   int L = 7 * FL_NORMAL_SIZE;
 
   win = new paletteWindow
@@ -1323,17 +1323,15 @@ optionWindow::optionWindow(int deltaFontSize)
       general.value[10]->align(FL_ALIGN_RIGHT);
       general.value[10]->callback(general_options_ok_cb, (void*)"rotation_center_coord");
 
-      general.butt[17] = new Fl_Check_Button
-        (L + 2 * WB, 2 * WB + 12 * BH, BW, BH, "Enable stereo");
-      general.butt[17]->type(FL_TOGGLE_BUTTON);
-      general.butt[17]->callback(general_options_ok_cb);
-
       general.butt[18] = new Fl_Check_Button
-        (L + 2 * WB, 2 * WB + 11 * BH, BW, BH, "Enable camera");
+        (L + 2 * WB, 2 * WB + 11 * BH, width/2-WB, BH, "Enable camera");
       general.butt[18]->type(FL_TOGGLE_BUTTON);
       general.butt[18]->callback(general_options_ok_cb);
 
-
+      general.butt[17] = new Fl_Check_Button
+        (L + width / 2, 2 * WB + 11 * BH, width/2-WB, BH, "Enable stereo");
+      general.butt[17]->type(FL_TOGGLE_BUTTON);
+      general.butt[17]->callback(general_options_ok_cb);
 
       o->end();
     }
diff --git a/Geo/GModelIO_Mesh.cpp b/Geo/GModelIO_Mesh.cpp
index ac22b7bd05..6fb7578452 100644
--- a/Geo/GModelIO_Mesh.cpp
+++ b/Geo/GModelIO_Mesh.cpp
@@ -62,13 +62,14 @@ void GModel::_storePhysicalTagsInEntities(int dim,
   }
 }
 
- static void replaceCommaByDot(const std::string name){
-   char myCommand[1000], myCommand2[1000];
-   sprintf(myCommand, "sed 's/,/./g' %s > temp.txt", name.c_str());
-   SystemCall(myCommand);
-   sprintf(myCommand2, "mv temp.txt %s ", name.c_str());
-   SystemCall(myCommand2);
- }
+static void replaceCommaByDot(const std::string name)
+{
+  char myCommand[1000], myCommand2[1000];
+  sprintf(myCommand, "sed 's/,/./g' %s > temp.txt", name.c_str());
+  SystemCall(myCommand);
+  sprintf(myCommand2, "mv temp.txt %s ", name.c_str());
+  SystemCall(myCommand2);
+}
 
 static bool getVertices(int num, int *indices, std::map<int, MVertex*> &map,
                         std::vector<MVertex*> &vertices)
@@ -1663,7 +1664,8 @@ int GModel::readUNV(const std::string &name)
 	  if(!CTX::instance()->mesh.switchElementTags) {
             if(sscanf(buffer, "%d %d %d %d %d %d", &num, &type, &elementary, &physical,
                       &color, &numNodes) != 6) break;
-	  } else {
+	  } 
+          else {
             if(sscanf(buffer, "%d %d %d %d %d %d", &num, &type, &physical, &elementary,
                       &color, &numNodes) != 6) break;
 	  }
diff --git a/Geo/discreteEdge.cpp b/Geo/discreteEdge.cpp
index 4e4f57723e..3a6c1cc85c 100644
--- a/Geo/discreteEdge.cpp
+++ b/Geo/discreteEdge.cpp
@@ -40,6 +40,9 @@ void discreteEdge::createTopo()
 
 void discreteEdge::orderMLines()
 {
+  //printf("ordering line %d\n", tag());
+  //if(lines.size() <= 1) return;
+
   std::vector<MLine*> _m;
   std::list<MLine*> segments;
 
diff --git a/Plugin/Bubbles.cpp b/Plugin/Bubbles.cpp
new file mode 100644
index 0000000000..a2decbeb52
--- /dev/null
+++ b/Plugin/Bubbles.cpp
@@ -0,0 +1,175 @@
+// Gmsh - Copyright (C) 1997-2010 C. Geuzaine, J.-F. Remacle
+//
+// See the LICENSE.txt file for license information. Please report all
+// bugs and problems to <gmsh@geuz.org>.
+
+#include <stdlib.h>
+#include "Gmsh.h"
+#include "GModel.h"
+#include "MTriangle.h"
+#include "Numeric.h"
+#include "Bubbles.h"
+
+StringXNumber BubblesOptions_Number[] = {
+  {GMSH_FULLRC, "ShrinkFactor", NULL, 0.},
+};
+
+StringXString BubblesOptions_String[] = {
+  {GMSH_FULLRC, "OutputFile", NULL, "bubbles.geo"}
+};
+
+extern "C"
+{
+  GMSH_Plugin *GMSH_RegisterBubblesPlugin()
+  {
+    return new GMSH_BubblesPlugin();
+  }
+}
+
+std::string GMSH_BubblesPlugin::getHelp() const
+{
+  return "Plugin(Bubbles) constructs a geometry consisting of "
+    "`bubbles' inscribed in the Voronoi of an input triangulation. "
+    "`ShrinkFactor' allows to change the size of the bubbles. "
+    "The plugin expects a triangulation in the `z = 0' plane to exist "
+    "in the current model.\n\n"
+    "Plugin(Bubbles) creates one `.geo' file.";
+}
+
+int GMSH_BubblesPlugin::getNbOptions() const
+{
+  return sizeof(BubblesOptions_Number) / sizeof(StringXNumber);
+}
+
+StringXNumber *GMSH_BubblesPlugin::getOption(int iopt)
+{
+  return &BubblesOptions_Number[iopt];
+}
+
+int GMSH_BubblesPlugin::getNbOptionsStr() const
+{
+  return sizeof(BubblesOptions_String) / sizeof(StringXString);
+}
+
+StringXString *GMSH_BubblesPlugin::getOptionStr(int iopt)
+{
+  return &BubblesOptions_String[iopt];
+}
+
+static double myangle(double c[3], double p[3])
+{
+  double v[3] = {1, 0, 0};
+  double n[3] = {0, 0, 1};
+  if(fabs(c[0] - p[0]) < 1e-12 && 
+     fabs(c[1] - p[1]) < 1e-12 && 
+     fabs(c[2] - p[2]) < 1e-12)
+    return 0.;
+  return angle_plan(c, v, p, n);
+}
+
+class compareAngle{
+ private:
+  SPoint3 v;
+ public:
+  compareAngle(SPoint3 vv) : v(vv) {}
+  bool operator()(const SPoint3 &b1, const SPoint3 &b2)
+  {
+    double p1[3] = {b1.x(), b1.y(), b1.z()};
+    double p2[3] = {b2.x(), b2.y(), b2.z()};
+    double c[3] = {v.x(), v.y(), v.z()};
+    double a1 = myangle(c, p1);
+    double a2 = myangle(c, p2);
+    return a1 < a2;
+  }
+};
+
+PView *GMSH_BubblesPlugin::execute(PView *v)
+{
+  double shrink = (double)BubblesOptions_Number[0].def;
+  std::string fileName = BubblesOptions_String[0].def;
+
+  FILE *fp = fopen(fileName.c_str(), "w");
+  if(!fp){
+    Msg::Error("Could not open output file '%s'", fileName.c_str());
+    return v;
+  }
+  
+  GModel *m = GModel::current();
+
+  int p = m->getMaxElementaryNumber(0) + 1;
+  int l = m->getMaxElementaryNumber(1) + 1;
+  int s = m->getMaxElementaryNumber(2) + 1;
+  int ll = s, ps = 1;
+
+  for(GModel::viter vit = m->firstVertex(); vit != m->lastVertex(); vit++)
+    (*vit)->writeGEO(fp);
+
+  for(GModel::eiter eit = m->firstEdge(); eit != m->lastEdge(); eit++)
+    (*eit)->writeGEO(fp);
+
+  for(GModel::fiter fit = m->firstFace(); fit != m->lastFace(); fit++){
+    (*fit)->writeGEO(fp);
+    fprintf(fp, "Delete { Surface {%d}; }\n", (*fit)->tag());
+
+    int sbeg = s;
+    int llbeg = ll;
+
+    // compute vertex-to-triangle_barycenter map
+    std::map<MVertex*, std::vector<SPoint3> > v2t;
+    for(unsigned int i = 0; i < (*fit)->triangles.size(); i++)
+      for(int j = 0; j < 3; j++)
+        v2t[(*fit)->triangles[i]->getVertex(j)].push_back((*fit)->triangles[i]->barycenter());
+
+    // add boundary vertices in map to get cells "closer" to the boundary
+    for(std::map<MVertex*, std::vector<SPoint3> >::iterator it = v2t.begin();
+        it != v2t.end(); it++){
+      MVertex *v = it->first;
+      if(v->onWhat() && v->onWhat()->dim() < 2)
+        it->second.push_back(SPoint3(it->first->x(), it->first->y(), it->first->z()));
+    }
+ 
+    for(std::map<MVertex*, std::vector<SPoint3> >::iterator it = v2t.begin();
+        it != v2t.end(); it++){
+      if(it->second.size() > 2){
+        // get barycenter of cell boundary points and order them
+        SPoint3 bc;
+        for(unsigned int i = 0; i < it->second.size(); i++)
+          bc += it->second[i];
+        bc *= 1. / (double)it->second.size();
+        compareAngle comp(bc);
+        std::sort(it->second.begin(), it->second.end(), comp);
+        // shrink cells
+        if(shrink){
+          for(unsigned int i = 0; i < it->second.size(); i++){
+            double dir[3] = {it->second[i].x() - bc.x(), 
+                             it->second[i].y() - bc.y(), 
+                             it->second[i].z() - bc.z()};
+            it->second[i][0] -= shrink * dir[0];
+            it->second[i][1] -= shrink * dir[1];
+            it->second[i][2] -= shrink * dir[2];
+          }
+        }
+        // create b-spline bounded surface for each cell
+        int nump = it->second.size();
+        for(unsigned int i = 0; i < nump; i++){
+          SPoint3 &b(it->second[i]);
+          fprintf(fp, "Point(%d) = {%.16g, %.16g, %.16g};\n", p++, b.x(), b.y(), b.z());
+        }
+        fprintf(fp, "BSpline(%d) = {", l++);
+        for(int i = nump - 1; i >= 0; i--)
+          fprintf(fp, "%d,", p - i - 1);
+        fprintf(fp, "%d};\n", p - nump);
+        fprintf(fp, "Line Loop(%d) = {%d};\n", ll++, l - 1);
+        fprintf(fp, "Plane Surface(%d) = {%d};\n", s++, ll - 1);
+      }
+    }
+    fprintf(fp, "Physical Surface(%d) = {%d:%d};\n", ps++, sbeg, s - 1);
+
+    fprintf(fp, "Plane Surface(%d) = {%d, %d:%d};\n", s++, (*fit)->tag(), llbeg, ll - 1);
+    fprintf(fp, "Physical Surface(%d) = {%d};\n", ps++, s - 1);
+  }
+
+  fclose(fp);
+
+  return v;
+}
diff --git a/Plugin/Bubbles.h b/Plugin/Bubbles.h
new file mode 100644
index 0000000000..f6ba36852f
--- /dev/null
+++ b/Plugin/Bubbles.h
@@ -0,0 +1,33 @@
+// Gmsh - Copyright (C) 1997-2010 C. Geuzaine, J.-F. Remacle
+//
+// See the LICENSE.txt file for license information. Please report all
+// bugs and problems to <gmsh@geuz.org>.
+
+#ifndef _BUBBLES_H_
+#define _BUBBLES_H_
+
+#include "Plugin.h"
+
+extern "C"
+{
+  GMSH_Plugin *GMSH_RegisterBubblesPlugin();
+}
+
+class GMSH_BubblesPlugin : public GMSH_PostPlugin
+{
+ public:
+  GMSH_BubblesPlugin(){}
+  std::string getName() const { return "Bubbles"; }
+  std::string getShortHelp() const
+  {
+    return "Create bubbles from triangulation";
+  }
+  std::string getHelp() const;
+  int getNbOptions() const;
+  StringXNumber *getOption(int iopt);
+  int getNbOptionsStr() const;
+  StringXString* getOptionStr(int iopt);  
+  PView *execute(PView *);
+};
+
+#endif
diff --git a/Plugin/CMakeLists.txt b/Plugin/CMakeLists.txt
index ceef4362e0..c2c0a550a2 100644
--- a/Plugin/CMakeLists.txt
+++ b/Plugin/CMakeLists.txt
@@ -25,6 +25,7 @@ set(SRC
   HomologyComputation.cpp
   Distance.cpp ExtractEdges.cpp NearestNeighbor.cpp
   AnalyseCurvedMesh.cpp FieldFromAmplitudePhase.cpp
+  Bubbles.cpp
 )
 
 file(GLOB HDR RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} *.h) 
diff --git a/Plugin/PluginManager.cpp b/Plugin/PluginManager.cpp
index a733df1bb5..36caecaf91 100644
--- a/Plugin/PluginManager.cpp
+++ b/Plugin/PluginManager.cpp
@@ -49,6 +49,7 @@
 #include "HomologyComputation.h"
 #include "ExtractEdges.h"
 #include "FieldFromAmplitudePhase.h"
+#include "Bubbles.h"
 
 // for testing purposes only :-)
 #undef HAVE_DLOPEN
@@ -222,6 +223,8 @@ void PluginManager::registerDefaultPlugins()
                       ("ExtractEdges", GMSH_RegisterExtractEdgesPlugin()));
     allPlugins.insert(std::pair<std::string, GMSH_Plugin*>
                       ("FieldFromAmplitudePhase", GMSH_RegisterFieldFromAmplitudePhasePlugin()));
+    allPlugins.insert(std::pair<std::string, GMSH_Plugin*>
+                      ("Bubbles", GMSH_RegisterBubblesPlugin()));
 #if defined(HAVE_TETGEN)
     allPlugins.insert(std::pair<std::string, GMSH_Plugin*>
                       ("Tetrahedralize", GMSH_RegisterTetrahedralizePlugin()));
-- 
GitLab