From db929950ed5d0099bfb1bb93765310f2ac48e19a Mon Sep 17 00:00:00 2001
From: Christophe Geuzaine <cgeuzaine@ulg.ac.be>
Date: Mon, 13 Oct 2008 13:59:55 +0000
Subject: [PATCH] make STL reader accept multiple solids per file

---
 Common/VertexArray.cpp |   8 ++++
 Common/VertexArray.h   |   2 +
 Geo/GModelIO_Mesh.cpp  | 103 +++++++++++++++++++++++------------------
 3 files changed, 68 insertions(+), 45 deletions(-)

diff --git a/Common/VertexArray.cpp b/Common/VertexArray.cpp
index ee86f6e162..1a254f5f9f 100644
--- a/Common/VertexArray.cpp
+++ b/Common/VertexArray.cpp
@@ -123,6 +123,7 @@ void VertexArray::finalize()
     _data3.clear();
   }
   _barycenters.clear();
+  //printf("vert array : %d Mb\n", getMemoryUsage());
 }
 
 class AlphaElement {
@@ -208,3 +209,10 @@ void VertexArray::sort(double x, double y, double z)
   _normals = sortedNormals;
   _colors = sortedColors;
 }
+
+int VertexArray::getMemoryUsage()
+{
+  int bytes = _vertices.size() * sizeof(float) + _normals.size() * sizeof(char) +
+    _colors.size() * sizeof(unsigned char);
+  return bytes / 1024 / 1024;
+}
diff --git a/Common/VertexArray.h b/Common/VertexArray.h
index dc67e6fa4a..094c201b79 100644
--- a/Common/VertexArray.h
+++ b/Common/VertexArray.h
@@ -158,6 +158,8 @@ class VertexArray{
   void finalize();
   // sort the arrays with elements back to front wrt the eye position
   void sort(double x, double y, double z);
+  // estimate the size of the vertex array in megabytes
+  int getMemoryUsage();
 };
 
 #endif
diff --git a/Geo/GModelIO_Mesh.cpp b/Geo/GModelIO_Mesh.cpp
index 652ed99793..264254cdf3 100644
--- a/Geo/GModelIO_Mesh.cpp
+++ b/Geo/GModelIO_Mesh.cpp
@@ -602,40 +602,45 @@ int GModel::writePOS(const std::string &name, bool printElementary,
 
 int GModel::readSTL(const std::string &name, double tolerance)
 {
-  // Note: this routine only reads a single "solid" (not sure if the
-  // spec allows to read multiple solids in a single file)
-
   FILE *fp = fopen(name.c_str(), "rb");
   if(!fp){
     Msg::Error("Unable to open file '%s'", name.c_str());
     return 0;
   }
 
-  // read all facets and store triplets of points
-  std::vector<SPoint3> points;
+  // store triplets of points for each solid found in the file
+  std::vector<std::vector<SPoint3> > points;
   SBoundingBox3d bbox;
 
   // "solid", or binary data header
   char buffer[256];
   fgets(buffer, sizeof(buffer), fp);
-
-  if(!strncmp(buffer, "solid", 5)) { 
+  
+  if(!strncmp(buffer, "solid", 5)){
     // ASCII STL
+    points.resize(1);
     while(!feof(fp)) {
-      // "facet normal x y z", or "endsolid"
+      // "facet normal x y z" or "endsolid"
       if(!fgets(buffer, sizeof(buffer), fp)) break;
-      if(!strncmp(buffer, "endsolid", 8)) break;
-      char s1[256], s2[256];
-      float x, y, z;
-      sscanf(buffer, "%s %s %f %f %f", s1, s2, &x, &y, &z);
+      if(!strncmp(buffer, "endsolid", 8)){
+        // "solid"
+        if(!fgets(buffer, sizeof(buffer), fp)) break;
+        if(!strncmp(buffer, "solid", 5)){
+          points.resize(points.size() + 1);
+          // "facet normal x y z"
+          if(!fgets(buffer, sizeof(buffer), fp)) break;
+        }
+      }
       // "outer loop"
       if(!fgets(buffer, sizeof(buffer), fp)) break;
       // "vertex x y z"
       for(int i = 0; i < 3; i++){
         if(!fgets(buffer, sizeof(buffer), fp)) break;
-        sscanf(buffer, "%s %f %f %f", s1, &x, &y, &z);
+        char s1[256];
+        double x, y, z;
+        if(sscanf(buffer, "%s %lf %lf %lf", s1, &x, &y, &z) != 4) break;
         SPoint3 p(x, y, z);
-        points.push_back(p);
+        points.back().push_back(p);
         bbox += p;
       }
       // "endloop"
@@ -648,8 +653,9 @@ int GModel::readSTL(const std::string &name, double tolerance)
     // Binary STL
     Msg::Info("Mesh is in binary format");
     rewind(fp);
-    char header[80];
-    if(fread(header, sizeof(char), 80, fp)){
+    while(!feof(fp)) {
+      char header[80];
+      if(!fread(header, sizeof(char), 80, fp)) break;
       unsigned int nfacets = 0;
       size_t ret = fread(&nfacets, sizeof(unsigned int), 1, fp);
       bool swap = false;
@@ -659,6 +665,7 @@ int GModel::readSTL(const std::string &name, double tolerance)
         SwapBytes((char*)&nfacets, sizeof(unsigned int), 1);
       }
       if(ret && nfacets){
+        points.resize(points.size() + 1);
         char *data = new char[nfacets * 50 * sizeof(char)];
         ret = fread(data, sizeof(char), nfacets * 50, fp);
         if(ret == nfacets * 50){
@@ -667,7 +674,7 @@ int GModel::readSTL(const std::string &name, double tolerance)
             if(swap) SwapBytes((char*)xyz, sizeof(float), 12);
             for(int j = 0; j < 3; j++){
               SPoint3 p(xyz[3 + 3 * j], xyz[3 + 3 * j + 1], xyz[3 + 3 * j + 2]);
-              points.push_back(p);
+              points.back().push_back(p);
               bbox += p;
             }
           }
@@ -677,43 +684,49 @@ int GModel::readSTL(const std::string &name, double tolerance)
     }
   }
 
-  if(!points.size()){
-    Msg::Error("No facets found in STL file");
-    return 0;
-  }
-  
-  if(points.size() % 3){
-    Msg::Error("Wrong number of points in STL file");
-    return 0;
+  std::vector<GFace*> faces;
+  for(unsigned int i = 0; i < points.size(); i++){
+    if(points[i].empty()){
+      Msg::Error("No facets found in STL file for solid %d", i);
+      return 0;
+    }
+    if(points[i].size() % 3){
+      Msg::Error("Wrong number of points (%d) in STL file for solid %d",
+                 points[i].size(), i);
+      return 0;
+    }
+    Msg::Info("%d facets in solid %d", points[i].size() / 3, i);
+    // create face
+    GFace *face = new discreteFace(this, getNumFaces() + 1);
+    faces.push_back(face);
+    add(face);
   }
 
-  Msg::Info("%d facets", points.size() / 3);
-
-  // create face
-  GFace *face = new discreteFace(this, getNumFaces() + 1);
-  add(face);
-
   // create (unique) vertices and triangles
   double lc = norm(SVector3(bbox.max(), bbox.min()));
   double old_tol = MVertexLessThanLexicographic::tolerance;
   MVertexLessThanLexicographic::tolerance = lc * tolerance;
   std::set<MVertex*, MVertexLessThanLexicographic> vertices;
-  for(unsigned int i = 0; i < points.size(); i += 3){
-    MVertex *v[3];
-    for(int j = 0; j < 3; j++){
-      double x = points[i + j].x(), y = points[i + j].y(), z = points[i + j].z();
-      MVertex w(x, y, z);
-      std::set<MVertex*, MVertexLessThanLexicographic>::iterator it = vertices.find(&w);
-      if(it != vertices.end()) {
-        v[j] = *it;
-      }
-      else {
-        v[j] = new MVertex(x, y, z, face);
-        vertices.insert(v[j]);
-        face->mesh_vertices.push_back(v[j]);
+  for(unsigned int i = 0; i < points.size(); i ++){
+    for(unsigned int j = 0; j < points[i].size(); j += 3){
+      MVertex *v[3];
+      for(int k = 0; k < 3; k++){
+        double x = points[i][j + k].x();
+        double y = points[i][j + k].y();
+        double z = points[i][j + k].z();
+        MVertex w(x, y, z);
+        std::set<MVertex*, MVertexLessThanLexicographic>::iterator it = vertices.find(&w);
+        if(it != vertices.end()) {
+          v[k] = *it;
+        }
+        else {
+          v[k] = new MVertex(x, y, z, faces[i]);
+          vertices.insert(v[k]);
+          faces[i]->mesh_vertices.push_back(v[k]);
+        }
       }
+      faces[i]->triangles.push_back(new MTriangle(v[0], v[1], v[2]));
     }
-    face->triangles.push_back(new MTriangle(v[0], v[1], v[2]));
   }
 
   MVertexLessThanLexicographic::tolerance = old_tol;
-- 
GitLab