Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/prusa3d/PrusaSlicer.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'src/admesh')
-rw-r--r--src/admesh/CMakeLists.txt12
-rw-r--r--src/admesh/connect.cpp911
-rw-r--r--src/admesh/normals.cpp292
-rw-r--r--src/admesh/shared.cpp264
-rw-r--r--src/admesh/stl.h215
-rw-r--r--src/admesh/stl_io.cpp433
-rw-r--r--src/admesh/stlinit.cpp382
-rw-r--r--src/admesh/util.cpp518
8 files changed, 3027 insertions, 0 deletions
diff --git a/src/admesh/CMakeLists.txt b/src/admesh/CMakeLists.txt
new file mode 100644
index 000000000..44c97c3f1
--- /dev/null
+++ b/src/admesh/CMakeLists.txt
@@ -0,0 +1,12 @@
+project(admesh)
+cmake_minimum_required(VERSION 2.6)
+
+add_library(admesh STATIC
+ connect.cpp
+ normals.cpp
+ shared.cpp
+ stl.h
+ stl_io.cpp
+ stlinit.cpp
+ util.cpp
+)
diff --git a/src/admesh/connect.cpp b/src/admesh/connect.cpp
new file mode 100644
index 000000000..da5b66720
--- /dev/null
+++ b/src/admesh/connect.cpp
@@ -0,0 +1,911 @@
+/* ADMesh -- process triangulated solid meshes
+ * Copyright (C) 1995, 1996 Anthony D. Martin <amartin@engr.csulb.edu>
+ * Copyright (C) 2013, 2014 several contributors, see AUTHORS
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program 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 General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Questions, comments, suggestions, etc to
+ * https://github.com/admesh/admesh/issues
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+
+#include <boost/detail/endian.hpp>
+
+#include "stl.h"
+
+
+static void stl_match_neighbors_nearby(stl_file *stl,
+ stl_hash_edge *edge_a, stl_hash_edge *edge_b);
+static void stl_record_neighbors(stl_file *stl,
+ stl_hash_edge *edge_a, stl_hash_edge *edge_b);
+static void stl_initialize_facet_check_exact(stl_file *stl);
+static void stl_initialize_facet_check_nearby(stl_file *stl);
+static void stl_load_edge_exact(stl_file *stl, stl_hash_edge *edge,
+ stl_vertex *a, stl_vertex *b);
+static int stl_load_edge_nearby(stl_file *stl, stl_hash_edge *edge,
+ stl_vertex *a, stl_vertex *b, float tolerance);
+static void insert_hash_edge(stl_file *stl, stl_hash_edge edge,
+ void (*match_neighbors)(stl_file *stl,
+ stl_hash_edge *edge_a, stl_hash_edge *edge_b));
+static int stl_compare_function(stl_hash_edge *edge_a, stl_hash_edge *edge_b);
+static void stl_free_edges(stl_file *stl);
+static void stl_remove_facet(stl_file *stl, int facet_number);
+static void stl_change_vertices(stl_file *stl, int facet_num, int vnot,
+ stl_vertex new_vertex);
+static void stl_which_vertices_to_change(stl_file *stl, stl_hash_edge *edge_a,
+ stl_hash_edge *edge_b, int *facet1, int *vertex1,
+ int *facet2, int *vertex2,
+ stl_vertex *new_vertex1, stl_vertex *new_vertex2);
+static void stl_remove_degenerate(stl_file *stl, int facet);
+extern int stl_check_normal_vector(stl_file *stl,
+ int facet_num, int normal_fix_flag);
+static void stl_update_connects_remove_1(stl_file *stl, int facet_num);
+
+
+void
+stl_check_facets_exact(stl_file *stl) {
+ /* This function builds the neighbors list. No modifications are made
+ * to any of the facets. The edges are said to match only if all six
+ * floats of the first edge matches all six floats of the second edge.
+ */
+
+ stl_hash_edge edge;
+ stl_facet facet;
+ int i;
+ int j;
+
+ if (stl->error) return;
+
+ stl->stats.connected_edges = 0;
+ stl->stats.connected_facets_1_edge = 0;
+ stl->stats.connected_facets_2_edge = 0;
+ stl->stats.connected_facets_3_edge = 0;
+
+ stl_initialize_facet_check_exact(stl);
+
+ for(i = 0; i < stl->stats.number_of_facets; i++) {
+ facet = stl->facet_start[i];
+ // If any two of the three vertices are found to be exactally the same, call them degenerate and remove the facet.
+ if (facet.vertex[0] == facet.vertex[1] ||
+ facet.vertex[1] == facet.vertex[2] ||
+ facet.vertex[0] == facet.vertex[2]) {
+ stl->stats.degenerate_facets += 1;
+ stl_remove_facet(stl, i);
+ -- i;
+ continue;
+ }
+ for(j = 0; j < 3; j++) {
+ edge.facet_number = i;
+ edge.which_edge = j;
+ stl_load_edge_exact(stl, &edge, &facet.vertex[j], &facet.vertex[(j + 1) % 3]);
+ insert_hash_edge(stl, edge, stl_record_neighbors);
+ }
+ }
+ stl_free_edges(stl);
+
+#if 0
+ printf("Number of faces: %d, number of manifold edges: %d, number of connected edges: %d, number of unconnected edges: %d\r\n",
+ stl->stats.number_of_facets, stl->stats.number_of_facets * 3,
+ stl->stats.connected_edges, stl->stats.number_of_facets * 3 - stl->stats.connected_edges);
+#endif
+}
+
+static void
+stl_load_edge_exact(stl_file *stl, stl_hash_edge *edge,
+ stl_vertex *a, stl_vertex *b) {
+
+ if (stl->error) return;
+
+ {
+ stl_vertex diff = (*a - *b).cwiseAbs();
+ float max_diff = std::max(diff(0), std::max(diff(1), diff(2)));
+ stl->stats.shortest_edge = std::min(max_diff, stl->stats.shortest_edge);
+ }
+
+ // Ensure identical vertex ordering of equal edges.
+ // This method is numerically robust.
+ if (stl_vertex_lower(*a, *b)) {
+ } else {
+ std::swap(a, b);
+ edge->which_edge += 3; /* this edge is loaded backwards */
+ }
+ memcpy(&edge->key[0], a->data(), sizeof(stl_vertex));
+ memcpy(&edge->key[sizeof(stl_vertex)], b->data(), sizeof(stl_vertex));
+ // Switch negative zeros to positive zeros, so memcmp will consider them to be equal.
+ for (size_t i = 0; i < 6; ++ i) {
+ unsigned char *p = edge->key + i * 4;
+#ifdef BOOST_LITTLE_ENDIAN
+ if (p[0] == 0 && p[1] == 0 && p[2] == 0 && p[3] == 0x80)
+ // Negative zero, switch to positive zero.
+ p[3] = 0;
+#else /* BOOST_LITTLE_ENDIAN */
+ if (p[0] == 0x80 && p[1] == 0 && p[2] == 0 && p[3] == 0)
+ // Negative zero, switch to positive zero.
+ p[0] = 0;
+#endif /* BOOST_LITTLE_ENDIAN */
+ }
+}
+
+static void
+stl_initialize_facet_check_exact(stl_file *stl) {
+ int i;
+
+ if (stl->error) return;
+
+ stl->stats.malloced = 0;
+ stl->stats.freed = 0;
+ stl->stats.collisions = 0;
+
+
+ stl->M = 81397;
+
+ for(i = 0; i < stl->stats.number_of_facets ; i++) {
+ /* initialize neighbors list to -1 to mark unconnected edges */
+ stl->neighbors_start[i].neighbor[0] = -1;
+ stl->neighbors_start[i].neighbor[1] = -1;
+ stl->neighbors_start[i].neighbor[2] = -1;
+ }
+
+ stl->heads = (stl_hash_edge**)calloc(stl->M, sizeof(*stl->heads));
+ if(stl->heads == NULL) perror("stl_initialize_facet_check_exact");
+
+ stl->tail = (stl_hash_edge*)malloc(sizeof(stl_hash_edge));
+ if(stl->tail == NULL) perror("stl_initialize_facet_check_exact");
+
+ stl->tail->next = stl->tail;
+
+ for(i = 0; i < stl->M; i++) {
+ stl->heads[i] = stl->tail;
+ }
+}
+
+static void insert_hash_edge(stl_file *stl, stl_hash_edge edge,
+ void (*match_neighbors)(stl_file *stl,
+ stl_hash_edge *edge_a, stl_hash_edge *edge_b))
+{
+ if (stl->error) return;
+
+ int chain_number = edge.hash(stl->M);
+ stl_hash_edge *link = stl->heads[chain_number];
+
+ stl_hash_edge *new_edge;
+ stl_hash_edge *temp;
+ if(link == stl->tail) {
+ /* This list doesn't have any edges currently in it. Add this one. */
+ new_edge = (stl_hash_edge*)malloc(sizeof(stl_hash_edge));
+ if(new_edge == NULL) perror("insert_hash_edge");
+ stl->stats.malloced++;
+ *new_edge = edge;
+ new_edge->next = stl->tail;
+ stl->heads[chain_number] = new_edge;
+ return;
+ } else if(!stl_compare_function(&edge, link)) {
+ /* This is a match. Record result in neighbors list. */
+ match_neighbors(stl, &edge, link);
+ /* Delete the matched edge from the list. */
+ stl->heads[chain_number] = link->next;
+ free(link);
+ stl->stats.freed++;
+ return;
+ } else {
+ /* Continue through the rest of the list */
+ for(;;) {
+ if(link->next == stl->tail) {
+ /* This is the last item in the list. Insert a new edge. */
+ new_edge = (stl_hash_edge*)malloc(sizeof(stl_hash_edge));
+ if(new_edge == NULL) perror("insert_hash_edge");
+ stl->stats.malloced++;
+ *new_edge = edge;
+ new_edge->next = stl->tail;
+ link->next = new_edge;
+ stl->stats.collisions++;
+ return;
+ } else if(!stl_compare_function(&edge, link->next)) {
+ /* This is a match. Record result in neighbors list. */
+ match_neighbors(stl, &edge, link->next);
+
+ /* Delete the matched edge from the list. */
+ temp = link->next;
+ link->next = link->next->next;
+ free(temp);
+ stl->stats.freed++;
+ return;
+ } else {
+ /* This is not a match. Go to the next link */
+ link = link->next;
+ stl->stats.collisions++;
+ }
+ }
+ }
+}
+
+// Return 1 if the edges are not matched.
+static inline int stl_compare_function(stl_hash_edge *edge_a, stl_hash_edge *edge_b)
+{
+ // Don't match edges of the same facet
+ return (edge_a->facet_number == edge_b->facet_number) || (*edge_a != *edge_b);
+}
+
+void stl_check_facets_nearby(stl_file *stl, float tolerance)
+{
+ if (stl->error)
+ return;
+
+ if( (stl->stats.connected_facets_1_edge == stl->stats.number_of_facets)
+ && (stl->stats.connected_facets_2_edge == stl->stats.number_of_facets)
+ && (stl->stats.connected_facets_3_edge == stl->stats.number_of_facets)) {
+ /* No need to check any further. All facets are connected */
+ return;
+ }
+
+ stl_initialize_facet_check_nearby(stl);
+
+ for (int i = 0; i < stl->stats.number_of_facets; ++ i) {
+ //FIXME is the copy necessary?
+ stl_facet facet = stl->facet_start[i];
+ for (int j = 0; j < 3; j++) {
+ if(stl->neighbors_start[i].neighbor[j] == -1) {
+ stl_hash_edge edge;
+ edge.facet_number = i;
+ edge.which_edge = j;
+ if(stl_load_edge_nearby(stl, &edge, &facet.vertex[j],
+ &facet.vertex[(j + 1) % 3],
+ tolerance)) {
+ /* only insert edges that have different keys */
+ insert_hash_edge(stl, edge, stl_match_neighbors_nearby);
+ }
+ }
+ }
+ }
+
+ stl_free_edges(stl);
+}
+
+static int stl_load_edge_nearby(stl_file *stl, stl_hash_edge *edge, stl_vertex *a, stl_vertex *b, float tolerance)
+{
+ // Index of a grid cell spaced by tolerance.
+ typedef Eigen::Matrix<int32_t, 3, 1, Eigen::DontAlign> Vec3i;
+ Vec3i vertex1 = (*a / tolerance).cast<int32_t>();
+ Vec3i vertex2 = (*b / tolerance).cast<int32_t>();
+ static_assert(sizeof(Vec3i) == 12, "size of Vec3i incorrect");
+
+ if (vertex1 == vertex2)
+ // Both vertices hash to the same value
+ return 0;
+
+ // Ensure identical vertex ordering of edges, which vertices land into equal grid cells.
+ // This method is numerically robust.
+ if ((vertex1[0] != vertex2[0]) ?
+ (vertex1[0] < vertex2[0]) :
+ ((vertex1[1] != vertex2[1]) ?
+ (vertex1[1] < vertex2[1]) :
+ (vertex1[2] < vertex2[2]))) {
+ memcpy(&edge->key[0], vertex1.data(), sizeof(stl_vertex));
+ memcpy(&edge->key[sizeof(stl_vertex)], vertex2.data(), sizeof(stl_vertex));
+ } else {
+ memcpy(&edge->key[0], vertex2.data(), sizeof(stl_vertex));
+ memcpy(&edge->key[sizeof(stl_vertex)], vertex1.data(), sizeof(stl_vertex));
+ edge->which_edge += 3; /* this edge is loaded backwards */
+ }
+ return 1;
+}
+
+static void stl_free_edges(stl_file *stl)
+{
+ if (stl->error)
+ return;
+
+ if(stl->stats.malloced != stl->stats.freed) {
+ for (int i = 0; i < stl->M; i++) {
+ for (stl_hash_edge *temp = stl->heads[i]; stl->heads[i] != stl->tail; temp = stl->heads[i]) {
+ stl->heads[i] = stl->heads[i]->next;
+ free(temp);
+ ++ stl->stats.freed;
+ }
+ }
+ }
+ free(stl->heads);
+ free(stl->tail);
+}
+
+static void stl_initialize_facet_check_nearby(stl_file *stl)
+{
+ int i;
+
+ if (stl->error) return;
+
+ stl->stats.malloced = 0;
+ stl->stats.freed = 0;
+ stl->stats.collisions = 0;
+
+ /* tolerance = STL_MAX(stl->stats.shortest_edge, tolerance);*/
+ /* tolerance = STL_MAX((stl->stats.bounding_diameter / 500000.0), tolerance);*/
+ /* tolerance *= 0.5;*/
+
+ stl->M = 81397;
+
+ stl->heads = (stl_hash_edge**)calloc(stl->M, sizeof(*stl->heads));
+ if(stl->heads == NULL) perror("stl_initialize_facet_check_nearby");
+
+ stl->tail = (stl_hash_edge*)malloc(sizeof(stl_hash_edge));
+ if(stl->tail == NULL) perror("stl_initialize_facet_check_nearby");
+
+ stl->tail->next = stl->tail;
+
+ for(i = 0; i < stl->M; i++) {
+ stl->heads[i] = stl->tail;
+ }
+}
+
+
+
+static void
+stl_record_neighbors(stl_file *stl,
+ stl_hash_edge *edge_a, stl_hash_edge *edge_b) {
+ int i;
+ int j;
+
+ if (stl->error) return;
+
+ /* Facet a's neighbor is facet b */
+ stl->neighbors_start[edge_a->facet_number].neighbor[edge_a->which_edge % 3] =
+ edge_b->facet_number; /* sets the .neighbor part */
+
+ stl->neighbors_start[edge_a->facet_number].
+ which_vertex_not[edge_a->which_edge % 3] =
+ (edge_b->which_edge + 2) % 3; /* sets the .which_vertex_not part */
+
+ /* Facet b's neighbor is facet a */
+ stl->neighbors_start[edge_b->facet_number].neighbor[edge_b->which_edge % 3] =
+ edge_a->facet_number; /* sets the .neighbor part */
+
+ stl->neighbors_start[edge_b->facet_number].
+ which_vertex_not[edge_b->which_edge % 3] =
+ (edge_a->which_edge + 2) % 3; /* sets the .which_vertex_not part */
+
+ if( ((edge_a->which_edge < 3) && (edge_b->which_edge < 3))
+ || ((edge_a->which_edge > 2) && (edge_b->which_edge > 2))) {
+ /* these facets are oriented in opposite directions. */
+ /* their normals are probably messed up. */
+ stl->neighbors_start[edge_a->facet_number].
+ which_vertex_not[edge_a->which_edge % 3] += 3;
+ stl->neighbors_start[edge_b->facet_number].
+ which_vertex_not[edge_b->which_edge % 3] += 3;
+ }
+
+
+ /* Count successful connects */
+ /* Total connects */
+ stl->stats.connected_edges += 2;
+ /* Count individual connects */
+ i = ((stl->neighbors_start[edge_a->facet_number].neighbor[0] == -1) +
+ (stl->neighbors_start[edge_a->facet_number].neighbor[1] == -1) +
+ (stl->neighbors_start[edge_a->facet_number].neighbor[2] == -1));
+ j = ((stl->neighbors_start[edge_b->facet_number].neighbor[0] == -1) +
+ (stl->neighbors_start[edge_b->facet_number].neighbor[1] == -1) +
+ (stl->neighbors_start[edge_b->facet_number].neighbor[2] == -1));
+ if(i == 2) {
+ stl->stats.connected_facets_1_edge +=1;
+ } else if(i == 1) {
+ stl->stats.connected_facets_2_edge +=1;
+ } else {
+ stl->stats.connected_facets_3_edge +=1;
+ }
+ if(j == 2) {
+ stl->stats.connected_facets_1_edge +=1;
+ } else if(j == 1) {
+ stl->stats.connected_facets_2_edge +=1;
+ } else {
+ stl->stats.connected_facets_3_edge +=1;
+ }
+}
+
+static void stl_match_neighbors_nearby(stl_file *stl, stl_hash_edge *edge_a, stl_hash_edge *edge_b)
+{
+ int facet1;
+ int facet2;
+ int vertex1;
+ int vertex2;
+ int vnot1;
+ int vnot2;
+ stl_vertex new_vertex1;
+ stl_vertex new_vertex2;
+
+ if (stl->error) return;
+
+ stl_record_neighbors(stl, edge_a, edge_b);
+ stl_which_vertices_to_change(stl, edge_a, edge_b, &facet1, &vertex1,
+ &facet2, &vertex2, &new_vertex1, &new_vertex2);
+ if(facet1 != -1) {
+ if(facet1 == edge_a->facet_number) {
+ vnot1 = (edge_a->which_edge + 2) % 3;
+ } else {
+ vnot1 = (edge_b->which_edge + 2) % 3;
+ }
+ if(((vnot1 + 2) % 3) == vertex1) {
+ vnot1 += 3;
+ }
+ stl_change_vertices(stl, facet1, vnot1, new_vertex1);
+ }
+ if(facet2 != -1) {
+ if(facet2 == edge_a->facet_number) {
+ vnot2 = (edge_a->which_edge + 2) % 3;
+ } else {
+ vnot2 = (edge_b->which_edge + 2) % 3;
+ }
+ if(((vnot2 + 2) % 3) == vertex2) {
+ vnot2 += 3;
+ }
+ stl_change_vertices(stl, facet2, vnot2, new_vertex2);
+ }
+ stl->stats.edges_fixed += 2;
+}
+
+
+static void stl_change_vertices(stl_file *stl, int facet_num, int vnot, stl_vertex new_vertex) {
+ int first_facet;
+ int direction;
+ int next_edge;
+ int pivot_vertex;
+
+ if (stl->error) return;
+
+ first_facet = facet_num;
+ direction = 0;
+
+ for(;;) {
+ if(vnot > 2) {
+ if(direction == 0) {
+ pivot_vertex = (vnot + 2) % 3;
+ next_edge = pivot_vertex;
+ direction = 1;
+ } else {
+ pivot_vertex = (vnot + 1) % 3;
+ next_edge = vnot % 3;
+ direction = 0;
+ }
+ } else {
+ if(direction == 0) {
+ pivot_vertex = (vnot + 1) % 3;
+ next_edge = vnot;
+ } else {
+ pivot_vertex = (vnot + 2) % 3;
+ next_edge = pivot_vertex;
+ }
+ }
+#if 0
+ if (stl->facet_start[facet_num].vertex[pivot_vertex](0) == new_vertex(0) &&
+ stl->facet_start[facet_num].vertex[pivot_vertex](1) == new_vertex(1) &&
+ stl->facet_start[facet_num].vertex[pivot_vertex](2) == new_vertex(2))
+ printf("Changing vertex %f,%f,%f: Same !!!\r\n",
+ new_vertex(0), new_vertex(1), new_vertex(2));
+ else {
+ if (stl->facet_start[facet_num].vertex[pivot_vertex](0) != new_vertex(0))
+ printf("Changing coordinate x, vertex %e (0x%08x) to %e(0x%08x)\r\n",
+ stl->facet_start[facet_num].vertex[pivot_vertex](0),
+ *reinterpret_cast<const int*>(&stl->facet_start[facet_num].vertex[pivot_vertex](0)),
+ new_vertex(0),
+ *reinterpret_cast<const int*>(&new_vertex(0)));
+ if (stl->facet_start[facet_num].vertex[pivot_vertex](1) != new_vertex(1))
+ printf("Changing coordinate x, vertex %e (0x%08x) to %e(0x%08x)\r\n",
+ stl->facet_start[facet_num].vertex[pivot_vertex](1),
+ *reinterpret_cast<const int*>(&stl->facet_start[facet_num].vertex[pivot_vertex](1)),
+ new_vertex(1),
+ *reinterpret_cast<const int*>(&new_vertex(1)));
+ if (stl->facet_start[facet_num].vertex[pivot_vertex](2) != new_vertex(2))
+ printf("Changing coordinate x, vertex %e (0x%08x) to %e(0x%08x)\r\n",
+ stl->facet_start[facet_num].vertex[pivot_vertex](2),
+ *reinterpret_cast<const int*>(&stl->facet_start[facet_num].vertex[pivot_vertex](2)),
+ new_vertex(2),
+ *reinterpret_cast<const int*>(&new_vertex(2)));
+ }
+#endif
+ stl->facet_start[facet_num].vertex[pivot_vertex] = new_vertex;
+ vnot = stl->neighbors_start[facet_num].which_vertex_not[next_edge];
+ facet_num = stl->neighbors_start[facet_num].neighbor[next_edge];
+
+ if(facet_num == -1) {
+ break;
+ }
+
+ if(facet_num == first_facet) {
+ /* back to the beginning */
+ printf("\
+Back to the first facet changing vertices: probably a mobius part.\n\
+Try using a smaller tolerance or don't do a nearby check\n");
+ return;
+ }
+ }
+}
+
+static void
+stl_which_vertices_to_change(stl_file *stl, stl_hash_edge *edge_a,
+ stl_hash_edge *edge_b, int *facet1, int *vertex1,
+ int *facet2, int *vertex2,
+ stl_vertex *new_vertex1, stl_vertex *new_vertex2) {
+ int v1a; /* pair 1, facet a */
+ int v1b; /* pair 1, facet b */
+ int v2a; /* pair 2, facet a */
+ int v2b; /* pair 2, facet b */
+
+ /* Find first pair */
+ if(edge_a->which_edge < 3) {
+ v1a = edge_a->which_edge;
+ v2a = (edge_a->which_edge + 1) % 3;
+ } else {
+ v2a = edge_a->which_edge % 3;
+ v1a = (edge_a->which_edge + 1) % 3;
+ }
+ if(edge_b->which_edge < 3) {
+ v1b = edge_b->which_edge;
+ v2b = (edge_b->which_edge + 1) % 3;
+ } else {
+ v2b = edge_b->which_edge % 3;
+ v1b = (edge_b->which_edge + 1) % 3;
+ }
+
+ // Of the first pair, which vertex, if any, should be changed
+ if(stl->facet_start[edge_a->facet_number].vertex[v1a] ==
+ stl->facet_start[edge_b->facet_number].vertex[v1b]) {
+ // These facets are already equal. No need to change.
+ *facet1 = -1;
+ } else {
+ if( (stl->neighbors_start[edge_a->facet_number].neighbor[v1a] == -1)
+ && (stl->neighbors_start[edge_a->facet_number].
+ neighbor[(v1a + 2) % 3] == -1)) {
+ /* This vertex has no neighbors. This is a good one to change */
+ *facet1 = edge_a->facet_number;
+ *vertex1 = v1a;
+ *new_vertex1 = stl->facet_start[edge_b->facet_number].vertex[v1b];
+ } else {
+ *facet1 = edge_b->facet_number;
+ *vertex1 = v1b;
+ *new_vertex1 = stl->facet_start[edge_a->facet_number].vertex[v1a];
+ }
+ }
+
+ /* Of the second pair, which vertex, if any, should be changed */
+ if(stl->facet_start[edge_a->facet_number].vertex[v2a] ==
+ stl->facet_start[edge_b->facet_number].vertex[v2b]) {
+ // These facets are already equal. No need to change.
+ *facet2 = -1;
+ } else {
+ if( (stl->neighbors_start[edge_a->facet_number].neighbor[v2a] == -1)
+ && (stl->neighbors_start[edge_a->facet_number].
+ neighbor[(v2a + 2) % 3] == -1)) {
+ /* This vertex has no neighbors. This is a good one to change */
+ *facet2 = edge_a->facet_number;
+ *vertex2 = v2a;
+ *new_vertex2 = stl->facet_start[edge_b->facet_number].vertex[v2b];
+ } else {
+ *facet2 = edge_b->facet_number;
+ *vertex2 = v2b;
+ *new_vertex2 = stl->facet_start[edge_a->facet_number].vertex[v2a];
+ }
+ }
+}
+
+static void
+stl_remove_facet(stl_file *stl, int facet_number) {
+ int neighbor[3];
+ int vnot[3];
+ int i;
+ int j;
+
+ if (stl->error) return;
+
+ stl->stats.facets_removed += 1;
+ /* Update list of connected edges */
+ j = ((stl->neighbors_start[facet_number].neighbor[0] == -1) +
+ (stl->neighbors_start[facet_number].neighbor[1] == -1) +
+ (stl->neighbors_start[facet_number].neighbor[2] == -1));
+ if(j == 2) {
+ stl->stats.connected_facets_1_edge -= 1;
+ } else if(j == 1) {
+ stl->stats.connected_facets_2_edge -= 1;
+ stl->stats.connected_facets_1_edge -= 1;
+ } else if(j == 0) {
+ stl->stats.connected_facets_3_edge -= 1;
+ stl->stats.connected_facets_2_edge -= 1;
+ stl->stats.connected_facets_1_edge -= 1;
+ }
+
+ stl->facet_start[facet_number] =
+ stl->facet_start[stl->stats.number_of_facets - 1];
+ /* I could reallocate at this point, but it is not really necessary. */
+ stl->neighbors_start[facet_number] =
+ stl->neighbors_start[stl->stats.number_of_facets - 1];
+ stl->stats.number_of_facets -= 1;
+
+ for(i = 0; i < 3; i++) {
+ neighbor[i] = stl->neighbors_start[facet_number].neighbor[i];
+ vnot[i] = stl->neighbors_start[facet_number].which_vertex_not[i];
+ }
+
+ for(i = 0; i < 3; i++) {
+ if(neighbor[i] != -1) {
+ if(stl->neighbors_start[neighbor[i]].neighbor[(vnot[i] + 1)% 3] !=
+ stl->stats.number_of_facets) {
+ printf("\
+in stl_remove_facet: neighbor = %d numfacets = %d this is wrong\n",
+ stl->neighbors_start[neighbor[i]].neighbor[(vnot[i] + 1)% 3],
+ stl->stats.number_of_facets);
+ return;
+ }
+ stl->neighbors_start[neighbor[i]].neighbor[(vnot[i] + 1)% 3]
+ = facet_number;
+ }
+ }
+}
+
+void stl_remove_unconnected_facets(stl_file *stl)
+{
+ /* A couple of things need to be done here. One is to remove any */
+ /* completely unconnected facets (0 edges connected) since these are */
+ /* useless and could be completely wrong. The second thing that needs to */
+ /* be done is to remove any degenerate facets that were created during */
+ /* stl_check_facets_nearby(). */
+ if (stl->error)
+ return;
+
+ // remove degenerate facets
+ for (int i = 0; i < stl->stats.number_of_facets; ++ i) {
+ if(stl->facet_start[i].vertex[0] == stl->facet_start[i].vertex[1] ||
+ stl->facet_start[i].vertex[0] == stl->facet_start[i].vertex[2] ||
+ stl->facet_start[i].vertex[1] == stl->facet_start[i].vertex[2]) {
+ stl_remove_degenerate(stl, i);
+ i--;
+ }
+ }
+
+ if(stl->stats.connected_facets_1_edge < stl->stats.number_of_facets) {
+ // remove completely unconnected facets
+ for (int i = 0; i < stl->stats.number_of_facets; i++) {
+ if (stl->neighbors_start[i].neighbor[0] == -1 &&
+ stl->neighbors_start[i].neighbor[1] == -1 &&
+ stl->neighbors_start[i].neighbor[2] == -1) {
+ // This facet is completely unconnected. Remove it.
+ stl_remove_facet(stl, i);
+ -- i;
+ }
+ }
+ }
+}
+
+static void
+stl_remove_degenerate(stl_file *stl, int facet) {
+ int edge1;
+ int edge2;
+ int edge3;
+ int neighbor1;
+ int neighbor2;
+ int neighbor3;
+ int vnot1;
+ int vnot2;
+ int vnot3;
+
+ if (stl->error) return;
+
+ if (stl->facet_start[facet].vertex[0] == stl->facet_start[facet].vertex[1] &&
+ stl->facet_start[facet].vertex[1] == stl->facet_start[facet].vertex[2]) {
+ /* all 3 vertices are equal. Just remove the facet. I don't think*/
+ /* this is really possible, but just in case... */
+ printf("removing a facet in stl_remove_degenerate\n");
+ stl_remove_facet(stl, facet);
+ return;
+ }
+
+ if (stl->facet_start[facet].vertex[0] == stl->facet_start[facet].vertex[1]) {
+ edge1 = 1;
+ edge2 = 2;
+ edge3 = 0;
+ } else if (stl->facet_start[facet].vertex[1] == stl->facet_start[facet].vertex[2]) {
+ edge1 = 0;
+ edge2 = 2;
+ edge3 = 1;
+ } else if (stl->facet_start[facet].vertex[2] == stl->facet_start[facet].vertex[0]) {
+ edge1 = 0;
+ edge2 = 1;
+ edge3 = 2;
+ } else {
+ /* No degenerate. Function shouldn't have been called. */
+ return;
+ }
+ neighbor1 = stl->neighbors_start[facet].neighbor[edge1];
+ neighbor2 = stl->neighbors_start[facet].neighbor[edge2];
+
+ if(neighbor1 == -1) {
+ stl_update_connects_remove_1(stl, neighbor2);
+ }
+ if(neighbor2 == -1) {
+ stl_update_connects_remove_1(stl, neighbor1);
+ }
+
+
+ neighbor3 = stl->neighbors_start[facet].neighbor[edge3];
+ vnot1 = stl->neighbors_start[facet].which_vertex_not[edge1];
+ vnot2 = stl->neighbors_start[facet].which_vertex_not[edge2];
+ vnot3 = stl->neighbors_start[facet].which_vertex_not[edge3];
+
+ if(neighbor1 >= 0){
+ stl->neighbors_start[neighbor1].neighbor[(vnot1 + 1) % 3] = neighbor2;
+ stl->neighbors_start[neighbor1].which_vertex_not[(vnot1 + 1) % 3] = vnot2;
+ }
+ if(neighbor2 >= 0){
+ stl->neighbors_start[neighbor2].neighbor[(vnot2 + 1) % 3] = neighbor1;
+ stl->neighbors_start[neighbor2].which_vertex_not[(vnot2 + 1) % 3] = vnot1;
+ }
+
+ stl_remove_facet(stl, facet);
+
+ if(neighbor3 >= 0) {
+ stl_update_connects_remove_1(stl, neighbor3);
+ stl->neighbors_start[neighbor3].neighbor[(vnot3 + 1) % 3] = -1;
+ }
+}
+
+void
+stl_update_connects_remove_1(stl_file *stl, int facet_num) {
+ int j;
+
+ if (stl->error) return;
+ /* Update list of connected edges */
+ j = ((stl->neighbors_start[facet_num].neighbor[0] == -1) +
+ (stl->neighbors_start[facet_num].neighbor[1] == -1) +
+ (stl->neighbors_start[facet_num].neighbor[2] == -1));
+ if(j == 0) { /* Facet has 3 neighbors */
+ stl->stats.connected_facets_3_edge -= 1;
+ } else if(j == 1) { /* Facet has 2 neighbors */
+ stl->stats.connected_facets_2_edge -= 1;
+ } else if(j == 2) { /* Facet has 1 neighbor */
+ stl->stats.connected_facets_1_edge -= 1;
+ }
+}
+
+void
+stl_fill_holes(stl_file *stl) {
+ stl_facet facet;
+ stl_facet new_facet;
+ int neighbors_initial[3];
+ stl_hash_edge edge;
+ int first_facet;
+ int direction;
+ int facet_num;
+ int vnot;
+ int next_edge;
+ int pivot_vertex;
+ int next_facet;
+ int i;
+ int j;
+ int k;
+
+ if (stl->error) return;
+
+ /* Insert all unconnected edges into hash list */
+ stl_initialize_facet_check_nearby(stl);
+ for(i = 0; i < stl->stats.number_of_facets; i++) {
+ facet = stl->facet_start[i];
+ for(j = 0; j < 3; j++) {
+ if(stl->neighbors_start[i].neighbor[j] != -1) continue;
+ edge.facet_number = i;
+ edge.which_edge = j;
+ stl_load_edge_exact(stl, &edge, &facet.vertex[j],
+ &facet.vertex[(j + 1) % 3]);
+
+ insert_hash_edge(stl, edge, stl_record_neighbors);
+ }
+ }
+
+ for(i = 0; i < stl->stats.number_of_facets; i++) {
+ facet = stl->facet_start[i];
+ neighbors_initial[0] = stl->neighbors_start[i].neighbor[0];
+ neighbors_initial[1] = stl->neighbors_start[i].neighbor[1];
+ neighbors_initial[2] = stl->neighbors_start[i].neighbor[2];
+ first_facet = i;
+ for(j = 0; j < 3; j++) {
+ if(stl->neighbors_start[i].neighbor[j] != -1) continue;
+
+ new_facet.vertex[0] = facet.vertex[j];
+ new_facet.vertex[1] = facet.vertex[(j + 1) % 3];
+ if(neighbors_initial[(j + 2) % 3] == -1) {
+ direction = 1;
+ } else {
+ direction = 0;
+ }
+
+ facet_num = i;
+ vnot = (j + 2) % 3;
+
+ for(;;) {
+ if(vnot > 2) {
+ if(direction == 0) {
+ pivot_vertex = (vnot + 2) % 3;
+ next_edge = pivot_vertex;
+ direction = 1;
+ } else {
+ pivot_vertex = (vnot + 1) % 3;
+ next_edge = vnot % 3;
+ direction = 0;
+ }
+ } else {
+ if(direction == 0) {
+ pivot_vertex = (vnot + 1) % 3;
+ next_edge = vnot;
+ } else {
+ pivot_vertex = (vnot + 2) % 3;
+ next_edge = pivot_vertex;
+ }
+ }
+ next_facet = stl->neighbors_start[facet_num].neighbor[next_edge];
+
+ if(next_facet == -1) {
+ new_facet.vertex[2] = stl->facet_start[facet_num].
+ vertex[vnot % 3];
+ stl_add_facet(stl, &new_facet);
+ for(k = 0; k < 3; k++) {
+ edge.facet_number = stl->stats.number_of_facets - 1;
+ edge.which_edge = k;
+ stl_load_edge_exact(stl, &edge, &new_facet.vertex[k],
+ &new_facet.vertex[(k + 1) % 3]);
+
+ insert_hash_edge(stl, edge, stl_record_neighbors);
+ }
+ break;
+ } else {
+ vnot = stl->neighbors_start[facet_num].
+ which_vertex_not[next_edge];
+ facet_num = next_facet;
+ }
+
+ if(facet_num == first_facet) {
+ /* back to the beginning */
+ printf("\
+Back to the first facet filling holes: probably a mobius part.\n\
+Try using a smaller tolerance or don't do a nearby check\n");
+ return;
+ }
+ }
+ }
+ }
+}
+
+void
+stl_add_facet(stl_file *stl, stl_facet *new_facet) {
+ if (stl->error) return;
+
+ stl->stats.facets_added += 1;
+ if(stl->stats.facets_malloced < stl->stats.number_of_facets + 1) {
+ stl->facet_start = (stl_facet*)realloc(stl->facet_start,
+ (sizeof(stl_facet) * (stl->stats.facets_malloced + 256)));
+ if(stl->facet_start == NULL) perror("stl_add_facet");
+ stl->neighbors_start = (stl_neighbors*)realloc(stl->neighbors_start,
+ (sizeof(stl_neighbors) * (stl->stats.facets_malloced + 256)));
+ if(stl->neighbors_start == NULL) perror("stl_add_facet");
+ stl->stats.facets_malloced += 256;
+ }
+ stl->facet_start[stl->stats.number_of_facets] = *new_facet;
+
+ /* note that the normal vector is not set here, just initialized to 0 */
+ stl->facet_start[stl->stats.number_of_facets].normal = stl_normal::Zero();
+
+ stl->neighbors_start[stl->stats.number_of_facets].neighbor[0] = -1;
+ stl->neighbors_start[stl->stats.number_of_facets].neighbor[1] = -1;
+ stl->neighbors_start[stl->stats.number_of_facets].neighbor[2] = -1;
+ stl->stats.number_of_facets += 1;
+}
diff --git a/src/admesh/normals.cpp b/src/admesh/normals.cpp
new file mode 100644
index 000000000..a8faa44bd
--- /dev/null
+++ b/src/admesh/normals.cpp
@@ -0,0 +1,292 @@
+/* ADMesh -- process triangulated solid meshes
+ * Copyright (C) 1995, 1996 Anthony D. Martin <amartin@engr.csulb.edu>
+ * Copyright (C) 2013, 2014 several contributors, see AUTHORS
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program 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 General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Questions, comments, suggestions, etc to
+ * https://github.com/admesh/admesh/issues
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+
+#include "stl.h"
+
+static int stl_check_normal_vector(stl_file *stl, int facet_num, int normal_fix_flag);
+
+static void
+stl_reverse_facet(stl_file *stl, int facet_num) {
+ stl_vertex tmp_vertex;
+ /* int tmp_neighbor;*/
+ int neighbor[3];
+ int vnot[3];
+
+ stl->stats.facets_reversed += 1;
+
+ neighbor[0] = stl->neighbors_start[facet_num].neighbor[0];
+ neighbor[1] = stl->neighbors_start[facet_num].neighbor[1];
+ neighbor[2] = stl->neighbors_start[facet_num].neighbor[2];
+ vnot[0] = stl->neighbors_start[facet_num].which_vertex_not[0];
+ vnot[1] = stl->neighbors_start[facet_num].which_vertex_not[1];
+ vnot[2] = stl->neighbors_start[facet_num].which_vertex_not[2];
+
+ /* reverse the facet */
+ tmp_vertex = stl->facet_start[facet_num].vertex[0];
+ stl->facet_start[facet_num].vertex[0] =
+ stl->facet_start[facet_num].vertex[1];
+ stl->facet_start[facet_num].vertex[1] = tmp_vertex;
+
+ /* fix the vnots of the neighboring facets */
+ if(neighbor[0] != -1)
+ stl->neighbors_start[neighbor[0]].which_vertex_not[(vnot[0] + 1) % 3] =
+ (stl->neighbors_start[neighbor[0]].
+ which_vertex_not[(vnot[0] + 1) % 3] + 3) % 6;
+ if(neighbor[1] != -1)
+ stl->neighbors_start[neighbor[1]].which_vertex_not[(vnot[1] + 1) % 3] =
+ (stl->neighbors_start[neighbor[1]].
+ which_vertex_not[(vnot[1] + 1) % 3] + 4) % 6;
+ if(neighbor[2] != -1)
+ stl->neighbors_start[neighbor[2]].which_vertex_not[(vnot[2] + 1) % 3] =
+ (stl->neighbors_start[neighbor[2]].
+ which_vertex_not[(vnot[2] + 1) % 3] + 2) % 6;
+
+ /* swap the neighbors of the facet that is being reversed */
+ stl->neighbors_start[facet_num].neighbor[1] = neighbor[2];
+ stl->neighbors_start[facet_num].neighbor[2] = neighbor[1];
+
+ /* swap the vnots of the facet that is being reversed */
+ stl->neighbors_start[facet_num].which_vertex_not[1] = vnot[2];
+ stl->neighbors_start[facet_num].which_vertex_not[2] = vnot[1];
+
+ /* reverse the values of the vnots of the facet that is being reversed */
+ stl->neighbors_start[facet_num].which_vertex_not[0] =
+ (stl->neighbors_start[facet_num].which_vertex_not[0] + 3) % 6;
+ stl->neighbors_start[facet_num].which_vertex_not[1] =
+ (stl->neighbors_start[facet_num].which_vertex_not[1] + 3) % 6;
+ stl->neighbors_start[facet_num].which_vertex_not[2] =
+ (stl->neighbors_start[facet_num].which_vertex_not[2] + 3) % 6;
+}
+
+void
+stl_fix_normal_directions(stl_file *stl) {
+ char *norm_sw;
+ /* int edge_num;*/
+ /* int vnot;*/
+ int checked = 0;
+ int facet_num;
+ /* int next_facet;*/
+ int i;
+ int j;
+ struct stl_normal {
+ int facet_num;
+ struct stl_normal *next;
+ };
+ struct stl_normal *head;
+ struct stl_normal *tail;
+ struct stl_normal *newn;
+ struct stl_normal *temp;
+
+ int* reversed_ids;
+ int reversed_count = 0;
+ int id;
+ int force_exit = 0;
+
+ if (stl->error) return;
+
+ /* Initialize linked list. */
+ head = (struct stl_normal*)malloc(sizeof(struct stl_normal));
+ if(head == NULL) perror("stl_fix_normal_directions");
+ tail = (struct stl_normal*)malloc(sizeof(struct stl_normal));
+ if(tail == NULL) perror("stl_fix_normal_directions");
+ head->next = tail;
+ tail->next = tail;
+
+ /* Initialize list that keeps track of already fixed facets. */
+ norm_sw = (char*)calloc(stl->stats.number_of_facets, sizeof(char));
+ if(norm_sw == NULL) perror("stl_fix_normal_directions");
+
+ /* Initialize list that keeps track of reversed facets. */
+ reversed_ids = (int*)calloc(stl->stats.number_of_facets, sizeof(int));
+ if (reversed_ids == NULL) perror("stl_fix_normal_directions reversed_ids");
+
+ facet_num = 0;
+ /* If normal vector is not within tolerance and backwards:
+ Arbitrarily starts at face 0. If this one is wrong, we're screwed. Thankfully, the chances
+ of it being wrong randomly are low if most of the triangles are right: */
+ if (stl_check_normal_vector(stl, 0, 0) == 2) {
+ stl_reverse_facet(stl, 0);
+ reversed_ids[reversed_count++] = 0;
+ }
+
+ /* Say that we've fixed this facet: */
+ norm_sw[facet_num] = 1;
+ checked++;
+
+ for(;;) {
+ /* Add neighbors_to_list.
+ Add unconnected neighbors to the list:a */
+ for(j = 0; j < 3; j++) {
+ /* Reverse the neighboring facets if necessary. */
+ if(stl->neighbors_start[facet_num].which_vertex_not[j] > 2) {
+ /* If the facet has a neighbor that is -1, it means that edge isn't shared by another facet */
+ if(stl->neighbors_start[facet_num].neighbor[j] != -1) {
+ if (norm_sw[stl->neighbors_start[facet_num].neighbor[j]] == 1) {
+ /* trying to modify a facet already marked as fixed, revert all changes made until now and exit (fixes: #716, #574, #413, #269, #262, #259, #230, #228, #206) */
+ for (id = reversed_count - 1; id >= 0; --id) {
+ stl_reverse_facet(stl, reversed_ids[id]);
+ }
+ force_exit = 1;
+ break;
+ } else {
+ stl_reverse_facet(stl, stl->neighbors_start[facet_num].neighbor[j]);
+ reversed_ids[reversed_count++] = stl->neighbors_start[facet_num].neighbor[j];
+ }
+ }
+ }
+ /* If this edge of the facet is connected: */
+ if(stl->neighbors_start[facet_num].neighbor[j] != -1) {
+ /* If we haven't fixed this facet yet, add it to the list: */
+ if(norm_sw[stl->neighbors_start[facet_num].neighbor[j]] != 1) {
+ /* Add node to beginning of list. */
+ newn = (struct stl_normal*)malloc(sizeof(struct stl_normal));
+ if(newn == NULL) perror("stl_fix_normal_directions");
+ newn->facet_num = stl->neighbors_start[facet_num].neighbor[j];
+ newn->next = head->next;
+ head->next = newn;
+ }
+ }
+ }
+
+ /* an error occourred, quit the for loop and exit */
+ if (force_exit) break;
+
+ /* Get next facet to fix from top of list. */
+ if(head->next != tail) {
+ facet_num = head->next->facet_num;
+ if(norm_sw[facet_num] != 1) { /* If facet is in list mutiple times */
+ norm_sw[facet_num] = 1; /* Record this one as being fixed. */
+ checked++;
+ }
+ temp = head->next; /* Delete this facet from the list. */
+ head->next = head->next->next;
+ free(temp);
+ } else { /* if we ran out of facets to fix: */
+ /* All of the facets in this part have been fixed. */
+ stl->stats.number_of_parts += 1;
+ if(checked >= stl->stats.number_of_facets) {
+ /* All of the facets have been checked. Bail out. */
+ break;
+ } else {
+ /* There is another part here. Find it and continue. */
+ for(i = 0; i < stl->stats.number_of_facets; i++) {
+ if(norm_sw[i] == 0) {
+ /* This is the first facet of the next part. */
+ facet_num = i;
+ if(stl_check_normal_vector(stl, i, 0) == 2) {
+ stl_reverse_facet(stl, i);
+ reversed_ids[reversed_count++] = i;
+ }
+
+ norm_sw[facet_num] = 1;
+ checked++;
+ break;
+ }
+ }
+ }
+ }
+ }
+ free(head);
+ free(tail);
+ free(reversed_ids);
+ free(norm_sw);
+}
+
+static int stl_check_normal_vector(stl_file *stl, int facet_num, int normal_fix_flag) {
+ /* Returns 0 if the normal is within tolerance */
+ /* Returns 1 if the normal is not within tolerance, but direction is OK */
+ /* Returns 2 if the normal is not within tolerance and backwards */
+ /* Returns 4 if the status is unknown. */
+
+ stl_facet *facet;
+
+ facet = &stl->facet_start[facet_num];
+
+ stl_normal normal;
+ stl_calculate_normal(normal, facet);
+ stl_normalize_vector(normal);
+ stl_normal normal_dif = (normal - facet->normal).cwiseAbs();
+
+ const float eps = 0.001f;
+ if (normal_dif(0) < eps && normal_dif(1) < eps && normal_dif(2) < eps) {
+ /* It is not really necessary to change the values here */
+ /* but just for consistency, I will. */
+ facet->normal = normal;
+ return 0;
+ }
+
+ stl_normal test_norm = facet->normal;
+ stl_normalize_vector(test_norm);
+ normal_dif = (normal - test_norm).cwiseAbs();
+ if (normal_dif(0) < eps && normal_dif(1) < eps && normal_dif(2) < eps) {
+ if(normal_fix_flag) {
+ facet->normal = normal;
+ stl->stats.normals_fixed += 1;
+ }
+ return 1;
+ }
+
+ test_norm *= -1.f;
+ normal_dif = (normal - test_norm).cwiseAbs();
+ if (normal_dif(0) < eps && normal_dif(1) < eps && normal_dif(2) < eps) {
+ // Facet is backwards.
+ if(normal_fix_flag) {
+ facet->normal = normal;
+ stl->stats.normals_fixed += 1;
+ }
+ return 2;
+ }
+ if(normal_fix_flag) {
+ facet->normal = normal;
+ stl->stats.normals_fixed += 1;
+ }
+ return 4;
+}
+
+void stl_fix_normal_values(stl_file *stl) {
+ int i;
+
+ if (stl->error) return;
+
+ for(i = 0; i < stl->stats.number_of_facets; i++) {
+ stl_check_normal_vector(stl, i, 1);
+ }
+}
+
+void stl_reverse_all_facets(stl_file *stl)
+{
+ if (stl->error)
+ return;
+
+ stl_normal normal;
+ for(int i = 0; i < stl->stats.number_of_facets; i++) {
+ stl_reverse_facet(stl, i);
+ stl_calculate_normal(normal, &stl->facet_start[i]);
+ stl_normalize_vector(normal);
+ stl->facet_start[i].normal = normal;
+ }
+}
diff --git a/src/admesh/shared.cpp b/src/admesh/shared.cpp
new file mode 100644
index 000000000..91bb82e00
--- /dev/null
+++ b/src/admesh/shared.cpp
@@ -0,0 +1,264 @@
+/* ADMesh -- process triangulated solid meshes
+ * Copyright (C) 1995, 1996 Anthony D. Martin <amartin@engr.csulb.edu>
+ * Copyright (C) 2013, 2014 several contributors, see AUTHORS
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program 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 General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Questions, comments, suggestions, etc to
+ * https://github.com/admesh/admesh/issues
+ */
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <boost/nowide/cstdio.hpp>
+
+#include "stl.h"
+
+void
+stl_invalidate_shared_vertices(stl_file *stl) {
+ if (stl->error) return;
+
+ if (stl->v_indices != NULL) {
+ free(stl->v_indices);
+ stl->v_indices = NULL;
+ }
+ if (stl->v_shared != NULL) {
+ free(stl->v_shared);
+ stl->v_shared = NULL;
+ }
+}
+
+void
+stl_generate_shared_vertices(stl_file *stl) {
+ int i;
+ int j;
+ int first_facet;
+ int direction;
+ int facet_num;
+ int vnot;
+ int next_edge;
+ int pivot_vertex;
+ int next_facet;
+ int reversed;
+
+ if (stl->error) return;
+
+ /* make sure this function is idempotent and does not leak memory */
+ stl_invalidate_shared_vertices(stl);
+
+ stl->v_indices = (v_indices_struct*)
+ calloc(stl->stats.number_of_facets, sizeof(v_indices_struct));
+ if(stl->v_indices == NULL) perror("stl_generate_shared_vertices");
+ stl->v_shared = (stl_vertex*)
+ calloc((stl->stats.number_of_facets / 2), sizeof(stl_vertex));
+ if(stl->v_shared == NULL) perror("stl_generate_shared_vertices");
+ stl->stats.shared_malloced = stl->stats.number_of_facets / 2;
+ stl->stats.shared_vertices = 0;
+
+ for(i = 0; i < stl->stats.number_of_facets; i++) {
+ stl->v_indices[i].vertex[0] = -1;
+ stl->v_indices[i].vertex[1] = -1;
+ stl->v_indices[i].vertex[2] = -1;
+ }
+
+
+ for(i = 0; i < stl->stats.number_of_facets; i++) {
+ first_facet = i;
+ for(j = 0; j < 3; j++) {
+ if(stl->v_indices[i].vertex[j] != -1) {
+ continue;
+ }
+ if(stl->stats.shared_vertices == stl->stats.shared_malloced) {
+ stl->stats.shared_malloced += 1024;
+ stl->v_shared = (stl_vertex*)realloc(stl->v_shared,
+ stl->stats.shared_malloced * sizeof(stl_vertex));
+ if(stl->v_shared == NULL) perror("stl_generate_shared_vertices");
+ }
+
+ stl->v_shared[stl->stats.shared_vertices] =
+ stl->facet_start[i].vertex[j];
+
+ direction = 0;
+ reversed = 0;
+ facet_num = i;
+ vnot = (j + 2) % 3;
+
+ for(;;) {
+ if(vnot > 2) {
+ if(direction == 0) {
+ pivot_vertex = (vnot + 2) % 3;
+ next_edge = pivot_vertex;
+ direction = 1;
+ } else {
+ pivot_vertex = (vnot + 1) % 3;
+ next_edge = vnot % 3;
+ direction = 0;
+ }
+ } else {
+ if(direction == 0) {
+ pivot_vertex = (vnot + 1) % 3;
+ next_edge = vnot;
+ } else {
+ pivot_vertex = (vnot + 2) % 3;
+ next_edge = pivot_vertex;
+ }
+ }
+ stl->v_indices[facet_num].vertex[pivot_vertex] =
+ stl->stats.shared_vertices;
+
+ next_facet = stl->neighbors_start[facet_num].neighbor[next_edge];
+ if(next_facet == -1) {
+ if(reversed) {
+ break;
+ } else {
+ direction = 1;
+ vnot = (j + 1) % 3;
+ reversed = 1;
+ facet_num = first_facet;
+ }
+ } else if(next_facet != first_facet) {
+ vnot = stl->neighbors_start[facet_num].
+ which_vertex_not[next_edge];
+ facet_num = next_facet;
+ } else {
+ break;
+ }
+ }
+ stl->stats.shared_vertices += 1;
+ }
+ }
+}
+
+void
+stl_write_off(stl_file *stl, char *file) {
+ int i;
+ FILE *fp;
+ char *error_msg;
+
+ if (stl->error) return;
+
+ /* Open the file */
+ fp = boost::nowide::fopen(file, "w");
+ if(fp == NULL) {
+ error_msg = (char*)
+ malloc(81 + strlen(file)); /* Allow 80 chars+file size for message */
+ sprintf(error_msg, "stl_write_ascii: Couldn't open %s for writing",
+ file);
+ perror(error_msg);
+ free(error_msg);
+ stl->error = 1;
+ return;
+ }
+
+ fprintf(fp, "OFF\n");
+ fprintf(fp, "%d %d 0\n",
+ stl->stats.shared_vertices, stl->stats.number_of_facets);
+
+ for(i = 0; i < stl->stats.shared_vertices; i++) {
+ fprintf(fp, "\t%f %f %f\n",
+ stl->v_shared[i](0), stl->v_shared[i](1), stl->v_shared[i](2));
+ }
+ for(i = 0; i < stl->stats.number_of_facets; i++) {
+ fprintf(fp, "\t3 %d %d %d\n", stl->v_indices[i].vertex[0],
+ stl->v_indices[i].vertex[1], stl->v_indices[i].vertex[2]);
+ }
+ fclose(fp);
+}
+
+void
+stl_write_vrml(stl_file *stl, char *file) {
+ int i;
+ FILE *fp;
+ char *error_msg;
+
+ if (stl->error) return;
+
+ /* Open the file */
+ fp = boost::nowide::fopen(file, "w");
+ if(fp == NULL) {
+ error_msg = (char*)
+ malloc(81 + strlen(file)); /* Allow 80 chars+file size for message */
+ sprintf(error_msg, "stl_write_ascii: Couldn't open %s for writing",
+ file);
+ perror(error_msg);
+ free(error_msg);
+ stl->error = 1;
+ return;
+ }
+
+ fprintf(fp, "#VRML V1.0 ascii\n\n");
+ fprintf(fp, "Separator {\n");
+ fprintf(fp, "\tDEF STLShape ShapeHints {\n");
+ fprintf(fp, "\t\tvertexOrdering COUNTERCLOCKWISE\n");
+ fprintf(fp, "\t\tfaceType CONVEX\n");
+ fprintf(fp, "\t\tshapeType SOLID\n");
+ fprintf(fp, "\t\tcreaseAngle 0.0\n");
+ fprintf(fp, "\t}\n");
+ fprintf(fp, "\tDEF STLModel Separator {\n");
+ fprintf(fp, "\t\tDEF STLColor Material {\n");
+ fprintf(fp, "\t\t\temissiveColor 0.700000 0.700000 0.000000\n");
+ fprintf(fp, "\t\t}\n");
+ fprintf(fp, "\t\tDEF STLVertices Coordinate3 {\n");
+ fprintf(fp, "\t\t\tpoint [\n");
+
+ for(i = 0; i < (stl->stats.shared_vertices - 1); i++) {
+ fprintf(fp, "\t\t\t\t%f %f %f,\n",
+ stl->v_shared[i](0), stl->v_shared[i](1), stl->v_shared[i](2));
+ }
+ fprintf(fp, "\t\t\t\t%f %f %f]\n",
+ stl->v_shared[i](0), stl->v_shared[i](1), stl->v_shared[i](2));
+ fprintf(fp, "\t\t}\n");
+ fprintf(fp, "\t\tDEF STLTriangles IndexedFaceSet {\n");
+ fprintf(fp, "\t\t\tcoordIndex [\n");
+
+ for(i = 0; i < (stl->stats.number_of_facets - 1); i++) {
+ fprintf(fp, "\t\t\t\t%d, %d, %d, -1,\n", stl->v_indices[i].vertex[0],
+ stl->v_indices[i].vertex[1], stl->v_indices[i].vertex[2]);
+ }
+ fprintf(fp, "\t\t\t\t%d, %d, %d, -1]\n", stl->v_indices[i].vertex[0],
+ stl->v_indices[i].vertex[1], stl->v_indices[i].vertex[2]);
+ fprintf(fp, "\t\t}\n");
+ fprintf(fp, "\t}\n");
+ fprintf(fp, "}\n");
+ fclose(fp);
+}
+
+void stl_write_obj (stl_file *stl, char *file) {
+ int i;
+ FILE* fp;
+
+ if (stl->error) return;
+
+ /* Open the file */
+ fp = boost::nowide::fopen(file, "w");
+ if (fp == NULL) {
+ char* error_msg = (char*)malloc(81 + strlen(file)); /* Allow 80 chars+file size for message */
+ sprintf(error_msg, "stl_write_ascii: Couldn't open %s for writing", file);
+ perror(error_msg);
+ free(error_msg);
+ stl->error = 1;
+ return;
+ }
+
+ for (i = 0; i < stl->stats.shared_vertices; i++) {
+ fprintf(fp, "v %f %f %f\n", stl->v_shared[i](0), stl->v_shared[i](1), stl->v_shared[i](2));
+ }
+ for (i = 0; i < stl->stats.number_of_facets; i++) {
+ fprintf(fp, "f %d %d %d\n", stl->v_indices[i].vertex[0]+1, stl->v_indices[i].vertex[1]+1, stl->v_indices[i].vertex[2]+1);
+ }
+
+ fclose(fp);
+}
diff --git a/src/admesh/stl.h b/src/admesh/stl.h
new file mode 100644
index 000000000..096430d15
--- /dev/null
+++ b/src/admesh/stl.h
@@ -0,0 +1,215 @@
+/* ADMesh -- process triangulated solid meshes
+ * Copyright (C) 1995, 1996 Anthony D. Martin <amartin@engr.csulb.edu>
+ * Copyright (C) 2013, 2014 several contributors, see AUTHORS
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program 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 General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Questions, comments, suggestions, etc to
+ * https://github.com/admesh/admesh/issues
+ */
+
+#ifndef __admesh_stl__
+#define __admesh_stl__
+
+#include <stdio.h>
+#include <stdint.h>
+#include <stddef.h>
+
+#include <Eigen/Geometry>
+
+// Size of the binary STL header, free form.
+#define LABEL_SIZE 80
+// Binary STL, length of the "number of faces" counter.
+#define NUM_FACET_SIZE 4
+// Binary STL, sizeof header + number of faces.
+#define HEADER_SIZE 84
+#define STL_MIN_FILE_SIZE 284
+#define ASCII_LINES_PER_FACET 7
+
+typedef Eigen::Matrix<float, 3, 1, Eigen::DontAlign> stl_vertex;
+typedef Eigen::Matrix<float, 3, 1, Eigen::DontAlign> stl_normal;
+static_assert(sizeof(stl_vertex) == 12, "size of stl_vertex incorrect");
+static_assert(sizeof(stl_normal) == 12, "size of stl_normal incorrect");
+
+typedef struct {
+ stl_normal normal;
+ stl_vertex vertex[3];
+ char extra[2];
+} stl_facet;
+#define SIZEOF_STL_FACET 50
+
+static_assert(offsetof(stl_facet, normal) == 0, "stl_facet.normal has correct offset");
+static_assert(offsetof(stl_facet, vertex) == 12, "stl_facet.vertex has correct offset");
+static_assert(offsetof(stl_facet, extra ) == 48, "stl_facet.extra has correct offset");
+static_assert(sizeof(stl_facet) >= SIZEOF_STL_FACET, "size of stl_facet incorrect");
+
+typedef enum {binary, ascii, inmemory} stl_type;
+
+typedef struct {
+ stl_vertex p1;
+ stl_vertex p2;
+ int facet_number;
+} stl_edge;
+
+typedef struct stl_hash_edge {
+ // Key of a hash edge: sorted vertices of the edge.
+ unsigned char key[2 * sizeof(stl_vertex)];
+ // Compare two keys.
+ bool operator==(const stl_hash_edge &rhs) { return memcmp(key, rhs.key, sizeof(key)) == 0; }
+ bool operator!=(const stl_hash_edge &rhs) { return ! (*this == rhs); }
+ int hash(int M) const { return ((key[0] / 23 + key[1] / 19 + key[2] / 17 + key[3] /13 + key[4] / 11 + key[5] / 7 ) % M); }
+ // Index of a facet owning this edge.
+ int facet_number;
+ // Index of this edge inside the facet with an index of facet_number.
+ // If this edge is stored backwards, which_edge is increased by 3.
+ int which_edge;
+ struct stl_hash_edge *next;
+} stl_hash_edge;
+
+typedef struct {
+ // Index of a neighbor facet.
+ int neighbor[3];
+ // Index of an opposite vertex at the neighbor face.
+ char which_vertex_not[3];
+} stl_neighbors;
+
+typedef struct {
+ int vertex[3];
+} v_indices_struct;
+
+typedef struct {
+ char header[81];
+ stl_type type;
+ uint32_t number_of_facets;
+ stl_vertex max;
+ stl_vertex min;
+ stl_vertex size;
+ float bounding_diameter;
+ float shortest_edge;
+ float volume;
+ unsigned number_of_blocks;
+ int connected_edges;
+ int connected_facets_1_edge;
+ int connected_facets_2_edge;
+ int connected_facets_3_edge;
+ int facets_w_1_bad_edge;
+ int facets_w_2_bad_edge;
+ int facets_w_3_bad_edge;
+ int original_num_facets;
+ int edges_fixed;
+ int degenerate_facets;
+ int facets_removed;
+ int facets_added;
+ int facets_reversed;
+ int backwards_edges;
+ int normals_fixed;
+ int number_of_parts;
+ int malloced;
+ int freed;
+ int facets_malloced;
+ int collisions;
+ int shared_vertices;
+ int shared_malloced;
+} stl_stats;
+
+typedef struct {
+ FILE *fp;
+ stl_facet *facet_start;
+ stl_edge *edge_start;
+ stl_hash_edge **heads;
+ stl_hash_edge *tail;
+ int M;
+ stl_neighbors *neighbors_start;
+ v_indices_struct *v_indices;
+ stl_vertex *v_shared;
+ stl_stats stats;
+ char error;
+} stl_file;
+
+
+extern void stl_open(stl_file *stl, const char *file);
+extern void stl_close(stl_file *stl);
+extern void stl_stats_out(stl_file *stl, FILE *file, char *input_file);
+extern void stl_print_edges(stl_file *stl, FILE *file);
+extern void stl_print_neighbors(stl_file *stl, char *file);
+extern void stl_put_little_int(FILE *fp, int value_in);
+extern void stl_put_little_float(FILE *fp, float value_in);
+extern void stl_write_ascii(stl_file *stl, const char *file, const char *label);
+extern void stl_write_binary(stl_file *stl, const char *file, const char *label);
+extern void stl_write_binary_block(stl_file *stl, FILE *fp);
+extern void stl_check_facets_exact(stl_file *stl);
+extern void stl_check_facets_nearby(stl_file *stl, float tolerance);
+extern void stl_remove_unconnected_facets(stl_file *stl);
+extern void stl_write_vertex(stl_file *stl, int facet, int vertex);
+extern void stl_write_facet(stl_file *stl, char *label, int facet);
+extern void stl_write_edge(stl_file *stl, char *label, stl_hash_edge edge);
+extern void stl_write_neighbor(stl_file *stl, int facet);
+extern void stl_write_quad_object(stl_file *stl, char *file);
+extern void stl_verify_neighbors(stl_file *stl);
+extern void stl_fill_holes(stl_file *stl);
+extern void stl_fix_normal_directions(stl_file *stl);
+extern void stl_fix_normal_values(stl_file *stl);
+extern void stl_reverse_all_facets(stl_file *stl);
+extern void stl_translate(stl_file *stl, float x, float y, float z);
+extern void stl_translate_relative(stl_file *stl, float x, float y, float z);
+extern void stl_scale_versor(stl_file *stl, const stl_vertex &versor);
+inline void stl_scale(stl_file *stl, float factor) { stl_scale_versor(stl, stl_vertex(factor, factor, factor)); }
+extern void stl_rotate_x(stl_file *stl, float angle);
+extern void stl_rotate_y(stl_file *stl, float angle);
+extern void stl_rotate_z(stl_file *stl, float angle);
+extern void stl_mirror_xy(stl_file *stl);
+extern void stl_mirror_yz(stl_file *stl);
+extern void stl_mirror_xz(stl_file *stl);
+extern void stl_transform(stl_file *stl, float *trafo3x4);
+extern void stl_transform(stl_file *stl, const Eigen::Transform<float, 3, Eigen::Affine, Eigen::DontAlign>& t);
+extern void stl_open_merge(stl_file *stl, char *file);
+extern void stl_invalidate_shared_vertices(stl_file *stl);
+extern void stl_generate_shared_vertices(stl_file *stl);
+extern void stl_write_obj(stl_file *stl, char *file);
+extern void stl_write_off(stl_file *stl, char *file);
+extern void stl_write_dxf(stl_file *stl, char *file, char *label);
+extern void stl_write_vrml(stl_file *stl, char *file);
+inline void stl_calculate_normal(stl_normal &normal, stl_facet *facet) {
+ normal = (facet->vertex[1] - facet->vertex[0]).cross(facet->vertex[2] - facet->vertex[0]);
+}
+inline void stl_normalize_vector(stl_normal &normal) {
+ double length = normal.cast<double>().norm();
+ if (length < 0.000000000001)
+ normal = stl_normal::Zero();
+ else
+ normal *= (1.0 / length);
+}
+inline bool stl_vertex_lower(const stl_vertex &a, const stl_vertex &b) {
+ return (a(0) != b(0)) ? (a(0) < b(0)) :
+ ((a(1) != b(1)) ? (a(1) < b(1)) : (a(2) < b(2)));
+}
+extern void stl_calculate_volume(stl_file *stl);
+
+extern void stl_repair(stl_file *stl, int fixall_flag, int exact_flag, int tolerance_flag, float tolerance, int increment_flag, float increment, int nearby_flag, int iterations, int remove_unconnected_flag, int fill_holes_flag, int normal_directions_flag, int normal_values_flag, int reverse_all_flag, int verbose_flag);
+
+extern void stl_initialize(stl_file *stl);
+extern void stl_count_facets(stl_file *stl, const char *file);
+extern void stl_allocate(stl_file *stl);
+extern void stl_read(stl_file *stl, int first_facet, bool first);
+extern void stl_facet_stats(stl_file *stl, stl_facet facet, bool &first);
+extern void stl_reallocate(stl_file *stl);
+extern void stl_add_facet(stl_file *stl, stl_facet *new_facet);
+extern void stl_get_size(stl_file *stl);
+
+extern void stl_clear_error(stl_file *stl);
+extern int stl_get_error(stl_file *stl);
+extern void stl_exit_on_error(stl_file *stl);
+
+#endif
diff --git a/src/admesh/stl_io.cpp b/src/admesh/stl_io.cpp
new file mode 100644
index 000000000..81e29d286
--- /dev/null
+++ b/src/admesh/stl_io.cpp
@@ -0,0 +1,433 @@
+/* ADMesh -- process triangulated solid meshes
+ * Copyright (C) 1995, 1996 Anthony D. Martin <amartin@engr.csulb.edu>
+ * Copyright (C) 2013, 2014 several contributors, see AUTHORS
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program 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 General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Questions, comments, suggestions, etc to
+ * https://github.com/admesh/admesh/issues
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include "stl.h"
+
+#include <boost/nowide/cstdio.hpp>
+#include <boost/detail/endian.hpp>
+
+#if !defined(SEEK_SET)
+#define SEEK_SET 0
+#define SEEK_CUR 1
+#define SEEK_END 2
+#endif
+
+void
+stl_print_edges(stl_file *stl, FILE *file) {
+ int i;
+ int edges_allocated;
+
+ if (stl->error) return;
+
+ edges_allocated = stl->stats.number_of_facets * 3;
+ for(i = 0; i < edges_allocated; i++) {
+ fprintf(file, "%d, %f, %f, %f, %f, %f, %f\n",
+ stl->edge_start[i].facet_number,
+ stl->edge_start[i].p1(0), stl->edge_start[i].p1(1),
+ stl->edge_start[i].p1(2), stl->edge_start[i].p2(0),
+ stl->edge_start[i].p2(1), stl->edge_start[i].p2(2));
+ }
+}
+
+
+void
+stl_stats_out(stl_file *stl, FILE *file, char *input_file) {
+ if (stl->error) return;
+
+ /* this is here for Slic3r, without our config.h
+ it won't use this part of the code anyway */
+#ifndef VERSION
+#define VERSION "unknown"
+#endif
+ fprintf(file, "\n\
+================= Results produced by ADMesh version " VERSION " ================\n");
+ fprintf(file, "\
+Input file : %s\n", input_file);
+ if(stl->stats.type == binary) {
+ fprintf(file, "\
+File type : Binary STL file\n");
+ } else {
+ fprintf(file, "\
+File type : ASCII STL file\n");
+ }
+ fprintf(file, "\
+Header : %s\n", stl->stats.header);
+ fprintf(file, "============== Size ==============\n");
+ fprintf(file, "Min X = % f, Max X = % f\n",
+ stl->stats.min(0), stl->stats.max(0));
+ fprintf(file, "Min Y = % f, Max Y = % f\n",
+ stl->stats.min(1), stl->stats.max(1));
+ fprintf(file, "Min Z = % f, Max Z = % f\n",
+ stl->stats.min(2), stl->stats.max(2));
+
+ fprintf(file, "\
+========= Facet Status ========== Original ============ Final ====\n");
+ fprintf(file, "\
+Number of facets : %5d %5d\n",
+ stl->stats.original_num_facets, stl->stats.number_of_facets);
+ fprintf(file, "\
+Facets with 1 disconnected edge : %5d %5d\n",
+ stl->stats.facets_w_1_bad_edge, stl->stats.connected_facets_2_edge -
+ stl->stats.connected_facets_3_edge);
+ fprintf(file, "\
+Facets with 2 disconnected edges : %5d %5d\n",
+ stl->stats.facets_w_2_bad_edge, stl->stats.connected_facets_1_edge -
+ stl->stats.connected_facets_2_edge);
+ fprintf(file, "\
+Facets with 3 disconnected edges : %5d %5d\n",
+ stl->stats.facets_w_3_bad_edge, stl->stats.number_of_facets -
+ stl->stats.connected_facets_1_edge);
+ fprintf(file, "\
+Total disconnected facets : %5d %5d\n",
+ stl->stats.facets_w_1_bad_edge + stl->stats.facets_w_2_bad_edge +
+ stl->stats.facets_w_3_bad_edge, stl->stats.number_of_facets -
+ stl->stats.connected_facets_3_edge);
+
+ fprintf(file,
+ "=== Processing Statistics === ===== Other Statistics =====\n");
+ fprintf(file, "\
+Number of parts : %5d Volume : % f\n",
+ stl->stats.number_of_parts, stl->stats.volume);
+ fprintf(file, "\
+Degenerate facets : %5d\n", stl->stats.degenerate_facets);
+ fprintf(file, "\
+Edges fixed : %5d\n", stl->stats.edges_fixed);
+ fprintf(file, "\
+Facets removed : %5d\n", stl->stats.facets_removed);
+ fprintf(file, "\
+Facets added : %5d\n", stl->stats.facets_added);
+ fprintf(file, "\
+Facets reversed : %5d\n", stl->stats.facets_reversed);
+ fprintf(file, "\
+Backwards edges : %5d\n", stl->stats.backwards_edges);
+ fprintf(file, "\
+Normals fixed : %5d\n", stl->stats.normals_fixed);
+}
+
+void
+stl_write_ascii(stl_file *stl, const char *file, const char *label) {
+ int i;
+ char *error_msg;
+
+ if (stl->error) return;
+
+ /* Open the file */
+ FILE *fp = boost::nowide::fopen(file, "w");
+ if(fp == NULL) {
+ error_msg = (char*)
+ malloc(81 + strlen(file)); /* Allow 80 chars+file size for message */
+ sprintf(error_msg, "stl_write_ascii: Couldn't open %s for writing",
+ file);
+ perror(error_msg);
+ free(error_msg);
+ stl->error = 1;
+ return;
+ }
+
+ fprintf(fp, "solid %s\n", label);
+
+ for(i = 0; i < stl->stats.number_of_facets; i++) {
+ fprintf(fp, " facet normal % .8E % .8E % .8E\n",
+ stl->facet_start[i].normal(0), stl->facet_start[i].normal(1),
+ stl->facet_start[i].normal(2));
+ fprintf(fp, " outer loop\n");
+ fprintf(fp, " vertex % .8E % .8E % .8E\n",
+ stl->facet_start[i].vertex[0](0), stl->facet_start[i].vertex[0](1),
+ stl->facet_start[i].vertex[0](2));
+ fprintf(fp, " vertex % .8E % .8E % .8E\n",
+ stl->facet_start[i].vertex[1](0), stl->facet_start[i].vertex[1](1),
+ stl->facet_start[i].vertex[1](2));
+ fprintf(fp, " vertex % .8E % .8E % .8E\n",
+ stl->facet_start[i].vertex[2](0), stl->facet_start[i].vertex[2](1),
+ stl->facet_start[i].vertex[2](2));
+ fprintf(fp, " endloop\n");
+ fprintf(fp, " endfacet\n");
+ }
+
+ fprintf(fp, "endsolid %s\n", label);
+
+ fclose(fp);
+}
+
+void
+stl_print_neighbors(stl_file *stl, char *file) {
+ int i;
+ FILE *fp;
+ char *error_msg;
+
+ if (stl->error) return;
+
+ /* Open the file */
+ fp = boost::nowide::fopen(file, "w");
+ if(fp == NULL) {
+ error_msg = (char*)
+ malloc(81 + strlen(file)); /* Allow 80 chars+file size for message */
+ sprintf(error_msg, "stl_print_neighbors: Couldn't open %s for writing",
+ file);
+ perror(error_msg);
+ free(error_msg);
+ stl->error = 1;
+ return;
+ }
+
+ for(i = 0; i < stl->stats.number_of_facets; i++) {
+ fprintf(fp, "%d, %d,%d, %d,%d, %d,%d\n",
+ i,
+ stl->neighbors_start[i].neighbor[0],
+ (int)stl->neighbors_start[i].which_vertex_not[0],
+ stl->neighbors_start[i].neighbor[1],
+ (int)stl->neighbors_start[i].which_vertex_not[1],
+ stl->neighbors_start[i].neighbor[2],
+ (int)stl->neighbors_start[i].which_vertex_not[2]);
+ }
+ fclose(fp);
+}
+
+#ifndef BOOST_LITTLE_ENDIAN
+// Swap a buffer of 32bit data from little endian to big endian and vice versa.
+void stl_internal_reverse_quads(char *buf, size_t cnt)
+{
+ for (size_t i = 0; i < cnt; i += 4) {
+ std::swap(buf[i], buf[i+3]);
+ std::swap(buf[i+1], buf[i+2]);
+ }
+}
+#endif
+
+void
+stl_write_binary(stl_file *stl, const char *file, const char *label) {
+ FILE *fp;
+ int i;
+ char *error_msg;
+
+ if (stl->error) return;
+
+ /* Open the file */
+ fp = boost::nowide::fopen(file, "wb");
+ if(fp == NULL) {
+ error_msg = (char*)
+ malloc(81 + strlen(file)); /* Allow 80 chars+file size for message */
+ sprintf(error_msg, "stl_write_binary: Couldn't open %s for writing",
+ file);
+ perror(error_msg);
+ free(error_msg);
+ stl->error = 1;
+ return;
+ }
+
+ fprintf(fp, "%s", label);
+ for(i = strlen(label); i < LABEL_SIZE; i++) putc(0, fp);
+
+ fseek(fp, LABEL_SIZE, SEEK_SET);
+#ifdef BOOST_LITTLE_ENDIAN
+ fwrite(&stl->stats.number_of_facets, 4, 1, fp);
+ for (i = 0; i < stl->stats.number_of_facets; ++ i)
+ fwrite(stl->facet_start + i, SIZEOF_STL_FACET, 1, fp);
+#else /* BOOST_LITTLE_ENDIAN */
+ char buffer[50];
+ // Convert the number of facets to little endian.
+ memcpy(buffer, &stl->stats.number_of_facets, 4);
+ stl_internal_reverse_quads(buffer, 4);
+ fwrite(buffer, 4, 1, fp);
+ for (i = 0; i < stl->stats.number_of_facets; ++ i) {
+ memcpy(buffer, stl->facet_start + i, 50);
+ // Convert to little endian.
+ stl_internal_reverse_quads(buffer, 48);
+ fwrite(buffer, SIZEOF_STL_FACET, 1, fp);
+ }
+#endif /* BOOST_LITTLE_ENDIAN */
+ fclose(fp);
+}
+
+void
+stl_write_vertex(stl_file *stl, int facet, int vertex) {
+ if (stl->error) return;
+ printf(" vertex %d/%d % .8E % .8E % .8E\n", vertex, facet,
+ stl->facet_start[facet].vertex[vertex](0),
+ stl->facet_start[facet].vertex[vertex](1),
+ stl->facet_start[facet].vertex[vertex](2));
+}
+
+void
+stl_write_facet(stl_file *stl, char *label, int facet) {
+ if (stl->error) return;
+ printf("facet (%d)/ %s\n", facet, label);
+ stl_write_vertex(stl, facet, 0);
+ stl_write_vertex(stl, facet, 1);
+ stl_write_vertex(stl, facet, 2);
+}
+
+void
+stl_write_edge(stl_file *stl, char *label, stl_hash_edge edge) {
+ if (stl->error) return;
+ printf("edge (%d)/(%d) %s\n", edge.facet_number, edge.which_edge, label);
+ if(edge.which_edge < 3) {
+ stl_write_vertex(stl, edge.facet_number, edge.which_edge % 3);
+ stl_write_vertex(stl, edge.facet_number, (edge.which_edge + 1) % 3);
+ } else {
+ stl_write_vertex(stl, edge.facet_number, (edge.which_edge + 1) % 3);
+ stl_write_vertex(stl, edge.facet_number, edge.which_edge % 3);
+ }
+}
+
+void
+stl_write_neighbor(stl_file *stl, int facet) {
+ if (stl->error) return;
+ printf("Neighbors %d: %d, %d, %d ; %d, %d, %d\n", facet,
+ stl->neighbors_start[facet].neighbor[0],
+ stl->neighbors_start[facet].neighbor[1],
+ stl->neighbors_start[facet].neighbor[2],
+ stl->neighbors_start[facet].which_vertex_not[0],
+ stl->neighbors_start[facet].which_vertex_not[1],
+ stl->neighbors_start[facet].which_vertex_not[2]);
+}
+
+void
+stl_write_quad_object(stl_file *stl, char *file) {
+ FILE *fp;
+ int i;
+ int j;
+ char *error_msg;
+ stl_vertex connect_color = stl_vertex::Zero();
+ stl_vertex uncon_1_color = stl_vertex::Zero();
+ stl_vertex uncon_2_color = stl_vertex::Zero();
+ stl_vertex uncon_3_color = stl_vertex::Zero();
+ stl_vertex color;
+
+ if (stl->error) return;
+
+ /* Open the file */
+ fp = boost::nowide::fopen(file, "w");
+ if(fp == NULL) {
+ error_msg = (char*)
+ malloc(81 + strlen(file)); /* Allow 80 chars+file size for message */
+ sprintf(error_msg, "stl_write_quad_object: Couldn't open %s for writing",
+ file);
+ perror(error_msg);
+ free(error_msg);
+ stl->error = 1;
+ return;
+ }
+
+ fprintf(fp, "CQUAD\n");
+ for(i = 0; i < stl->stats.number_of_facets; i++) {
+ j = ((stl->neighbors_start[i].neighbor[0] == -1) +
+ (stl->neighbors_start[i].neighbor[1] == -1) +
+ (stl->neighbors_start[i].neighbor[2] == -1));
+ if(j == 0) {
+ color = connect_color;
+ } else if(j == 1) {
+ color = uncon_1_color;
+ } else if(j == 2) {
+ color = uncon_2_color;
+ } else {
+ color = uncon_3_color;
+ }
+ fprintf(fp, "%f %f %f %1.1f %1.1f %1.1f 1\n",
+ stl->facet_start[i].vertex[0](0),
+ stl->facet_start[i].vertex[0](1),
+ stl->facet_start[i].vertex[0](2), color(0), color(1), color(2));
+ fprintf(fp, "%f %f %f %1.1f %1.1f %1.1f 1\n",
+ stl->facet_start[i].vertex[1](0),
+ stl->facet_start[i].vertex[1](1),
+ stl->facet_start[i].vertex[1](2), color(0), color(1), color(2));
+ fprintf(fp, "%f %f %f %1.1f %1.1f %1.1f 1\n",
+ stl->facet_start[i].vertex[2](0),
+ stl->facet_start[i].vertex[2](1),
+ stl->facet_start[i].vertex[2](2), color(0), color(1), color(2));
+ fprintf(fp, "%f %f %f %1.1f %1.1f %1.1f 1\n",
+ stl->facet_start[i].vertex[2](0),
+ stl->facet_start[i].vertex[2](1),
+ stl->facet_start[i].vertex[2](2), color(0), color(1), color(2));
+ }
+ fclose(fp);
+}
+
+void
+stl_write_dxf(stl_file *stl, char *file, char *label) {
+ int i;
+ FILE *fp;
+ char *error_msg;
+
+ if (stl->error) return;
+
+ /* Open the file */
+ fp = boost::nowide::fopen(file, "w");
+ if(fp == NULL) {
+ error_msg = (char*)
+ malloc(81 + strlen(file)); /* Allow 80 chars+file size for message */
+ sprintf(error_msg, "stl_write_ascii: Couldn't open %s for writing",
+ file);
+ perror(error_msg);
+ free(error_msg);
+ stl->error = 1;
+ return;
+ }
+
+ fprintf(fp, "999\n%s\n", label);
+ fprintf(fp, "0\nSECTION\n2\nHEADER\n0\nENDSEC\n");
+ fprintf(fp, "0\nSECTION\n2\nTABLES\n0\nTABLE\n2\nLAYER\n70\n1\n\
+0\nLAYER\n2\n0\n70\n0\n62\n7\n6\nCONTINUOUS\n0\nENDTAB\n0\nENDSEC\n");
+ fprintf(fp, "0\nSECTION\n2\nBLOCKS\n0\nENDSEC\n");
+
+ fprintf(fp, "0\nSECTION\n2\nENTITIES\n");
+
+ for(i = 0; i < stl->stats.number_of_facets; i++) {
+ fprintf(fp, "0\n3DFACE\n8\n0\n");
+ fprintf(fp, "10\n%f\n20\n%f\n30\n%f\n",
+ stl->facet_start[i].vertex[0](0), stl->facet_start[i].vertex[0](1),
+ stl->facet_start[i].vertex[0](2));
+ fprintf(fp, "11\n%f\n21\n%f\n31\n%f\n",
+ stl->facet_start[i].vertex[1](0), stl->facet_start[i].vertex[1](1),
+ stl->facet_start[i].vertex[1](2));
+ fprintf(fp, "12\n%f\n22\n%f\n32\n%f\n",
+ stl->facet_start[i].vertex[2](0), stl->facet_start[i].vertex[2](1),
+ stl->facet_start[i].vertex[2](2));
+ fprintf(fp, "13\n%f\n23\n%f\n33\n%f\n",
+ stl->facet_start[i].vertex[2](0), stl->facet_start[i].vertex[2](1),
+ stl->facet_start[i].vertex[2](2));
+ }
+
+ fprintf(fp, "0\nENDSEC\n0\nEOF\n");
+
+ fclose(fp);
+}
+
+void
+stl_clear_error(stl_file *stl) {
+ stl->error = 0;
+}
+
+void
+stl_exit_on_error(stl_file *stl) {
+ if (!stl->error) return;
+ stl->error = 0;
+ stl_close(stl);
+ exit(1);
+}
+
+int
+stl_get_error(stl_file *stl) {
+ return stl->error;
+}
diff --git a/src/admesh/stlinit.cpp b/src/admesh/stlinit.cpp
new file mode 100644
index 000000000..e2939b8af
--- /dev/null
+++ b/src/admesh/stlinit.cpp
@@ -0,0 +1,382 @@
+/* ADMesh -- process triangulated solid meshes
+ * Copyright (C) 1995, 1996 Anthony D. Martin <amartin@engr.csulb.edu>
+ * Copyright (C) 2013, 2014 several contributors, see AUTHORS
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program 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 General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Questions, comments, suggestions, etc to
+ * https://github.com/admesh/admesh/issues
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+#include <assert.h>
+
+#include <boost/nowide/cstdio.hpp>
+#include <boost/detail/endian.hpp>
+
+#include "stl.h"
+
+#ifndef SEEK_SET
+#error "SEEK_SET not defined"
+#endif
+
+void
+stl_open(stl_file *stl, const char *file) {
+ stl_initialize(stl);
+ stl_count_facets(stl, file);
+ stl_allocate(stl);
+ stl_read(stl, 0, true);
+ if (!stl->error) fclose(stl->fp);
+}
+
+
+void
+stl_initialize(stl_file *stl) {
+ memset(stl, 0, sizeof(stl_file));
+ stl->stats.volume = -1.0;
+}
+
+#ifndef BOOST_LITTLE_ENDIAN
+extern void stl_internal_reverse_quads(char *buf, size_t cnt);
+#endif /* BOOST_LITTLE_ENDIAN */
+
+void
+stl_count_facets(stl_file *stl, const char *file) {
+ long file_size;
+ uint32_t header_num_facets;
+ uint32_t num_facets;
+ int i;
+ size_t s;
+ unsigned char chtest[128];
+ int num_lines = 1;
+ char *error_msg;
+
+ if (stl->error) return;
+
+ /* Open the file in binary mode first */
+ stl->fp = boost::nowide::fopen(file, "rb");
+ if(stl->fp == NULL) {
+ error_msg = (char*)
+ malloc(81 + strlen(file)); /* Allow 80 chars+file size for message */
+ sprintf(error_msg, "stl_initialize: Couldn't open %s for reading",
+ file);
+ perror(error_msg);
+ free(error_msg);
+ stl->error = 1;
+ return;
+ }
+ /* Find size of file */
+ fseek(stl->fp, 0, SEEK_END);
+ file_size = ftell(stl->fp);
+
+ /* Check for binary or ASCII file */
+ fseek(stl->fp, HEADER_SIZE, SEEK_SET);
+ if (!fread(chtest, sizeof(chtest), 1, stl->fp)) {
+ perror("The input is an empty file");
+ stl->error = 1;
+ return;
+ }
+ stl->stats.type = ascii;
+ for(s = 0; s < sizeof(chtest); s++) {
+ if(chtest[s] > 127) {
+ stl->stats.type = binary;
+ break;
+ }
+ }
+ rewind(stl->fp);
+
+ /* Get the header and the number of facets in the .STL file */
+ /* If the .STL file is binary, then do the following */
+ if(stl->stats.type == binary) {
+ /* Test if the STL file has the right size */
+ if(((file_size - HEADER_SIZE) % SIZEOF_STL_FACET != 0)
+ || (file_size < STL_MIN_FILE_SIZE)) {
+ fprintf(stderr, "The file %s has the wrong size.\n", file);
+ stl->error = 1;
+ return;
+ }
+ num_facets = (file_size - HEADER_SIZE) / SIZEOF_STL_FACET;
+
+ /* Read the header */
+ if (fread(stl->stats.header, LABEL_SIZE, 1, stl->fp) > 79) {
+ stl->stats.header[80] = '\0';
+ }
+
+ /* Read the int following the header. This should contain # of facets */
+ bool header_num_faces_read = fread(&header_num_facets, sizeof(uint32_t), 1, stl->fp);
+#ifndef BOOST_LITTLE_ENDIAN
+ // Convert from little endian to big endian.
+ stl_internal_reverse_quads((char*)&header_num_facets, 4);
+#endif /* BOOST_LITTLE_ENDIAN */
+ if (! header_num_faces_read || num_facets != header_num_facets) {
+ fprintf(stderr,
+ "Warning: File size doesn't match number of facets in the header\n");
+ }
+ }
+ /* Otherwise, if the .STL file is ASCII, then do the following */
+ else {
+ /* Reopen the file in text mode (for getting correct newlines on Windows) */
+ // fix to silence a warning about unused return value.
+ // obviously if it fails we have problems....
+ stl->fp = boost::nowide::freopen(file, "r", stl->fp);
+
+ // do another null check to be safe
+ if(stl->fp == NULL) {
+ error_msg = (char*)
+ malloc(81 + strlen(file)); /* Allow 80 chars+file size for message */
+ sprintf(error_msg, "stl_initialize: Couldn't open %s for reading",
+ file);
+ perror(error_msg);
+ free(error_msg);
+ stl->error = 1;
+ return;
+ }
+
+ /* Find the number of facets */
+ char linebuf[100];
+ while (fgets(linebuf, 100, stl->fp) != NULL) {
+ /* don't count short lines */
+ if (strlen(linebuf) <= 4) continue;
+
+ /* skip solid/endsolid lines as broken STL file generators may put several of them */
+ if (strncmp(linebuf, "solid", 5) == 0 || strncmp(linebuf, "endsolid", 8) == 0) continue;
+
+ ++num_lines;
+ }
+
+ rewind(stl->fp);
+
+ /* Get the header */
+ for(i = 0;
+ (i < 80) && (stl->stats.header[i] = getc(stl->fp)) != '\n'; i++);
+ stl->stats.header[i] = '\0'; /* Lose the '\n' */
+ stl->stats.header[80] = '\0';
+
+ num_facets = num_lines / ASCII_LINES_PER_FACET;
+ }
+ stl->stats.number_of_facets += num_facets;
+ stl->stats.original_num_facets = stl->stats.number_of_facets;
+}
+
+void
+stl_allocate(stl_file *stl) {
+ if (stl->error) return;
+
+ /* Allocate memory for the entire .STL file */
+ stl->facet_start = (stl_facet*)calloc(stl->stats.number_of_facets,
+ sizeof(stl_facet));
+ if(stl->facet_start == NULL) perror("stl_initialize");
+ stl->stats.facets_malloced = stl->stats.number_of_facets;
+
+ /* Allocate memory for the neighbors list */
+ stl->neighbors_start = (stl_neighbors*)
+ calloc(stl->stats.number_of_facets, sizeof(stl_neighbors));
+ if(stl->facet_start == NULL) perror("stl_initialize");
+}
+
+void
+stl_open_merge(stl_file *stl, char *file_to_merge) {
+ int num_facets_so_far;
+ stl_type origStlType;
+ FILE *origFp;
+ stl_file stl_to_merge;
+
+ if (stl->error) return;
+
+ /* Record how many facets we have so far from the first file. We will start putting
+ facets in the next position. Since we're 0-indexed, it'l be the same position. */
+ num_facets_so_far = stl->stats.number_of_facets;
+
+ /* Record the file type we started with: */
+ origStlType=stl->stats.type;
+ /* Record the file pointer too: */
+ origFp=stl->fp;
+
+ /* Initialize the sturucture with zero stats, header info and sizes: */
+ stl_initialize(&stl_to_merge);
+ stl_count_facets(&stl_to_merge, file_to_merge);
+
+ /* Copy what we need to into stl so that we can read the file_to_merge directly into it
+ using stl_read: Save the rest of the valuable info: */
+ stl->stats.type=stl_to_merge.stats.type;
+ stl->fp=stl_to_merge.fp;
+
+ /* Add the number of facets we already have in stl with what we we found in stl_to_merge but
+ haven't read yet. */
+ stl->stats.number_of_facets=num_facets_so_far+stl_to_merge.stats.number_of_facets;
+
+ /* Allocate enough room for stl->stats.number_of_facets facets and neighbors: */
+ stl_reallocate(stl);
+
+ /* Read the file to merge directly into stl, adding it to what we have already.
+ Start at num_facets_so_far, the index to the first unused facet. Also say
+ that this isn't our first time so we should augment stats like min and max
+ instead of erasing them. */
+ stl_read(stl, num_facets_so_far, false);
+
+ /* Restore the stl information we overwrote (for stl_read) so that it still accurately
+ reflects the subject part: */
+ stl->stats.type=origStlType;
+ stl->fp=origFp;
+}
+
+extern void
+stl_reallocate(stl_file *stl) {
+ if (stl->error) return;
+ /* Reallocate more memory for the .STL file(s) */
+ stl->facet_start = (stl_facet*)realloc(stl->facet_start, stl->stats.number_of_facets *
+ sizeof(stl_facet));
+ if(stl->facet_start == NULL) perror("stl_initialize");
+ stl->stats.facets_malloced = stl->stats.number_of_facets;
+
+ /* Reallocate more memory for the neighbors list */
+ stl->neighbors_start = (stl_neighbors*)
+ realloc(stl->neighbors_start, stl->stats.number_of_facets *
+ sizeof(stl_neighbors));
+ if(stl->facet_start == NULL) perror("stl_initialize");
+}
+
+
+/* Reads the contents of the file pointed to by stl->fp into the stl structure,
+ starting at facet first_facet. The second argument says if it's our first
+ time running this for the stl and therefore we should reset our max and min stats. */
+void stl_read(stl_file *stl, int first_facet, bool first) {
+ stl_facet facet;
+ int i;
+
+ if (stl->error) return;
+
+ if(stl->stats.type == binary) {
+ fseek(stl->fp, HEADER_SIZE, SEEK_SET);
+ } else {
+ rewind(stl->fp);
+ }
+
+ char normal_buf[3][32];
+ for(i = first_facet; i < stl->stats.number_of_facets; i++) {
+ if(stl->stats.type == binary)
+ /* Read a single facet from a binary .STL file */
+ {
+ /* we assume little-endian architecture! */
+ if (fread(&facet, 1, SIZEOF_STL_FACET, stl->fp) != SIZEOF_STL_FACET) {
+ stl->error = 1;
+ return;
+ }
+#ifndef BOOST_LITTLE_ENDIAN
+ // Convert the loaded little endian data to big endian.
+ stl_internal_reverse_quads((char*)&facet, 48);
+#endif /* BOOST_LITTLE_ENDIAN */
+ } else
+ /* Read a single facet from an ASCII .STL file */
+ {
+ // skip solid/endsolid
+ // (in this order, otherwise it won't work when they are paired in the middle of a file)
+ fscanf(stl->fp, "endsolid%*[^\n]\n");
+ fscanf(stl->fp, "solid%*[^\n]\n"); // name might contain spaces so %*s doesn't work and it also can be empty (just "solid")
+ // Leading space in the fscanf format skips all leading white spaces including numerous new lines and tabs.
+ int res_normal = fscanf(stl->fp, " facet normal %31s %31s %31s", normal_buf[0], normal_buf[1], normal_buf[2]);
+ assert(res_normal == 3);
+ int res_outer_loop = fscanf(stl->fp, " outer loop");
+ assert(res_outer_loop == 0);
+ int res_vertex1 = fscanf(stl->fp, " vertex %f %f %f", &facet.vertex[0](0), &facet.vertex[0](1), &facet.vertex[0](2));
+ assert(res_vertex1 == 3);
+ int res_vertex2 = fscanf(stl->fp, " vertex %f %f %f", &facet.vertex[1](0), &facet.vertex[1](1), &facet.vertex[1](2));
+ assert(res_vertex2 == 3);
+ int res_vertex3 = fscanf(stl->fp, " vertex %f %f %f", &facet.vertex[2](0), &facet.vertex[2](1), &facet.vertex[2](2));
+ assert(res_vertex3 == 3);
+ int res_endloop = fscanf(stl->fp, " endloop");
+ assert(res_endloop == 0);
+ // There is a leading and trailing white space around endfacet to eat up all leading and trailing white spaces including numerous tabs and new lines.
+ int res_endfacet = fscanf(stl->fp, " endfacet ");
+ if (res_normal != 3 || res_outer_loop != 0 || res_vertex1 != 3 || res_vertex2 != 3 || res_vertex3 != 3 || res_endloop != 0 || res_endfacet != 0) {
+ perror("Something is syntactically very wrong with this ASCII STL!");
+ stl->error = 1;
+ return;
+ }
+
+ // The facet normal has been parsed as a single string as to workaround for not a numbers in the normal definition.
+ if (sscanf(normal_buf[0], "%f", &facet.normal(0)) != 1 ||
+ sscanf(normal_buf[1], "%f", &facet.normal(1)) != 1 ||
+ sscanf(normal_buf[2], "%f", &facet.normal(2)) != 1) {
+ // Normal was mangled. Maybe denormals or "not a number" were stored?
+ // Just reset the normal and silently ignore it.
+ memset(&facet.normal, 0, sizeof(facet.normal));
+ }
+ }
+
+#if 0
+ // Report close to zero vertex coordinates. Due to the nature of the floating point numbers,
+ // close to zero values may be represented with singificantly higher precision than the rest of the vertices.
+ // It may be worth to round these numbers to zero during loading to reduce the number of errors reported
+ // during the STL import.
+ for (size_t j = 0; j < 3; ++ j) {
+ if (facet.vertex[j](0) > -1e-12f && facet.vertex[j](0) < 1e-12f)
+ printf("stl_read: facet %d(0) = %e\r\n", j, facet.vertex[j](0));
+ if (facet.vertex[j](1) > -1e-12f && facet.vertex[j](1) < 1e-12f)
+ printf("stl_read: facet %d(1) = %e\r\n", j, facet.vertex[j](1));
+ if (facet.vertex[j](2) > -1e-12f && facet.vertex[j](2) < 1e-12f)
+ printf("stl_read: facet %d(2) = %e\r\n", j, facet.vertex[j](2));
+ }
+#endif
+
+ /* Write the facet into memory. */
+ stl->facet_start[i] = facet;
+ stl_facet_stats(stl, facet, first);
+ }
+ stl->stats.size = stl->stats.max - stl->stats.min;
+ stl->stats.bounding_diameter = stl->stats.size.norm();
+}
+
+void stl_facet_stats(stl_file *stl, stl_facet facet, bool &first)
+{
+ if (stl->error)
+ return;
+
+ // While we are going through all of the facets, let's find the
+ // maximum and minimum values for x, y, and z
+
+ if (first) {
+ // Initialize the max and min values the first time through
+ stl->stats.min = facet.vertex[0];
+ stl->stats.max = facet.vertex[0];
+ stl_vertex diff = (facet.vertex[1] - facet.vertex[0]).cwiseAbs();
+ stl->stats.shortest_edge = std::max(diff(0), std::max(diff(1), diff(2)));
+ first = false;
+ }
+
+ // Now find the max and min values.
+ for (size_t i = 0; i < 3; ++ i) {
+ stl->stats.min = stl->stats.min.cwiseMin(facet.vertex[i]);
+ stl->stats.max = stl->stats.max.cwiseMax(facet.vertex[i]);
+ }
+}
+
+void
+stl_close(stl_file *stl) {
+ if (stl->error) return;
+
+ if(stl->neighbors_start != NULL)
+ free(stl->neighbors_start);
+ if(stl->facet_start != NULL)
+ free(stl->facet_start);
+ if(stl->v_indices != NULL)
+ free(stl->v_indices);
+ if(stl->v_shared != NULL)
+ free(stl->v_shared);
+}
+
diff --git a/src/admesh/util.cpp b/src/admesh/util.cpp
new file mode 100644
index 000000000..cc104fdd1
--- /dev/null
+++ b/src/admesh/util.cpp
@@ -0,0 +1,518 @@
+/* ADMesh -- process triangulated solid meshes
+ * Copyright (C) 1995, 1996 Anthony D. Martin <amartin@engr.csulb.edu>
+ * Copyright (C) 2013, 2014 several contributors, see AUTHORS
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program 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 General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Questions, comments, suggestions, etc to
+ * https://github.com/admesh/admesh/issues
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+
+#include "stl.h"
+
+static void stl_rotate(float *x, float *y, const double c, const double s);
+static float get_area(stl_facet *facet);
+static float get_volume(stl_file *stl);
+
+
+void
+stl_verify_neighbors(stl_file *stl) {
+ int i;
+ int j;
+ stl_edge edge_a;
+ stl_edge edge_b;
+ int neighbor;
+ int vnot;
+
+ if (stl->error) return;
+
+ stl->stats.backwards_edges = 0;
+
+ for(i = 0; i < stl->stats.number_of_facets; i++) {
+ for(j = 0; j < 3; j++) {
+ edge_a.p1 = stl->facet_start[i].vertex[j];
+ edge_a.p2 = stl->facet_start[i].vertex[(j + 1) % 3];
+ neighbor = stl->neighbors_start[i].neighbor[j];
+ vnot = stl->neighbors_start[i].which_vertex_not[j];
+
+ if(neighbor == -1)
+ continue; /* this edge has no neighbor... Continue. */
+ if(vnot < 3) {
+ edge_b.p1 = stl->facet_start[neighbor].vertex[(vnot + 2) % 3];
+ edge_b.p2 = stl->facet_start[neighbor].vertex[(vnot + 1) % 3];
+ } else {
+ stl->stats.backwards_edges += 1;
+ edge_b.p1 = stl->facet_start[neighbor].vertex[(vnot + 1) % 3];
+ edge_b.p2 = stl->facet_start[neighbor].vertex[(vnot + 2) % 3];
+ }
+ if (edge_a.p1 != edge_b.p1 || edge_a.p2 != edge_b.p2) {
+ /* These edges should match but they don't. Print results. */
+ printf("edge %d of facet %d doesn't match edge %d of facet %d\n",
+ j, i, vnot + 1, neighbor);
+ stl_write_facet(stl, (char*)"first facet", i);
+ stl_write_facet(stl, (char*)"second facet", neighbor);
+ }
+ }
+ }
+}
+
+void stl_translate(stl_file *stl, float x, float y, float z)
+{
+ if (stl->error)
+ return;
+
+ stl_vertex new_min(x, y, z);
+ stl_vertex shift = new_min - stl->stats.min;
+ for (int i = 0; i < stl->stats.number_of_facets; ++ i)
+ for (int j = 0; j < 3; ++ j)
+ stl->facet_start[i].vertex[j] += shift;
+ stl->stats.min = new_min;
+ stl->stats.max += shift;
+ stl_invalidate_shared_vertices(stl);
+}
+
+/* Translates the stl by x,y,z, relatively from wherever it is currently */
+void stl_translate_relative(stl_file *stl, float x, float y, float z)
+{
+ if (stl->error)
+ return;
+
+ stl_vertex shift(x, y, z);
+ for (int i = 0; i < stl->stats.number_of_facets; ++ i)
+ for (int j = 0; j < 3; ++ j)
+ stl->facet_start[i].vertex[j] += shift;
+ stl->stats.min += shift;
+ stl->stats.max += shift;
+ stl_invalidate_shared_vertices(stl);
+}
+
+void stl_scale_versor(stl_file *stl, const stl_vertex &versor)
+{
+ if (stl->error)
+ return;
+
+ // Scale extents.
+ auto s = versor.array();
+ stl->stats.min.array() *= s;
+ stl->stats.max.array() *= s;
+ // Scale size.
+ stl->stats.size.array() *= s;
+ // Scale volume.
+ if (stl->stats.volume > 0.0)
+ stl->stats.volume *= versor(0) * versor(1) * versor(2);
+ // Scale the mesh.
+ for (int i = 0; i < stl->stats.number_of_facets; ++ i)
+ for (int j = 0; j < 3; ++ j)
+ stl->facet_start[i].vertex[j].array() *= s;
+ stl_invalidate_shared_vertices(stl);
+}
+
+static void calculate_normals(stl_file *stl)
+{
+ if (stl->error)
+ return;
+
+ stl_normal normal;
+ for(uint32_t i = 0; i < stl->stats.number_of_facets; i++) {
+ stl_calculate_normal(normal, &stl->facet_start[i]);
+ stl_normalize_vector(normal);
+ stl->facet_start[i].normal = normal;
+ }
+}
+
+void stl_transform(stl_file *stl, float *trafo3x4) {
+ int i_face, i_vertex;
+ if (stl->error)
+ return;
+ for (i_face = 0; i_face < stl->stats.number_of_facets; ++ i_face) {
+ stl_vertex *vertices = stl->facet_start[i_face].vertex;
+ for (i_vertex = 0; i_vertex < 3; ++ i_vertex) {
+ stl_vertex &v_dst = vertices[i_vertex];
+ stl_vertex v_src = v_dst;
+ v_dst(0) = trafo3x4[0] * v_src(0) + trafo3x4[1] * v_src(1) + trafo3x4[2] * v_src(2) + trafo3x4[3];
+ v_dst(1) = trafo3x4[4] * v_src(0) + trafo3x4[5] * v_src(1) + trafo3x4[6] * v_src(2) + trafo3x4[7];
+ v_dst(2) = trafo3x4[8] * v_src(0) + trafo3x4[9] * v_src(1) + trafo3x4[10] * v_src(2) + trafo3x4[11];
+ }
+ }
+ stl_get_size(stl);
+ calculate_normals(stl);
+}
+
+void stl_transform(stl_file *stl, const Eigen::Transform<float, 3, Eigen::Affine, Eigen::DontAlign>& t)
+{
+ if (stl->error)
+ return;
+
+ unsigned int vertices_count = 3 * (unsigned int)stl->stats.number_of_facets;
+ if (vertices_count == 0)
+ return;
+
+ Eigen::MatrixXf src_vertices(3, vertices_count);
+ stl_facet* facet_ptr = stl->facet_start;
+ unsigned int v_id = 0;
+ while (facet_ptr < stl->facet_start + stl->stats.number_of_facets)
+ {
+ for (int i = 0; i < 3; ++i)
+ {
+ ::memcpy((void*)src_vertices.col(v_id).data(), (const void*)&facet_ptr->vertex[i], 3 * sizeof(float));
+ ++v_id;
+ }
+ facet_ptr += 1;
+ }
+
+ Eigen::MatrixXf dst_vertices(3, vertices_count);
+ dst_vertices = t * src_vertices.colwise().homogeneous();
+
+ facet_ptr = stl->facet_start;
+ v_id = 0;
+ while (facet_ptr < stl->facet_start + stl->stats.number_of_facets)
+ {
+ for (int i = 0; i < 3; ++i)
+ {
+ ::memcpy((void*)&facet_ptr->vertex[i], (const void*)dst_vertices.col(v_id).data(), 3 * sizeof(float));
+ ++v_id;
+ }
+ facet_ptr += 1;
+ }
+
+ stl_get_size(stl);
+ calculate_normals(stl);
+}
+
+void
+stl_rotate_x(stl_file *stl, float angle) {
+ int i;
+ int j;
+ double radian_angle = (angle / 180.0) * M_PI;
+ double c = cos(radian_angle);
+ double s = sin(radian_angle);
+
+ if (stl->error) return;
+
+ for(i = 0; i < stl->stats.number_of_facets; i++) {
+ for(j = 0; j < 3; j++) {
+ stl_rotate(&stl->facet_start[i].vertex[j](1),
+ &stl->facet_start[i].vertex[j](2), c, s);
+ }
+ }
+ stl_get_size(stl);
+ calculate_normals(stl);
+}
+
+void
+stl_rotate_y(stl_file *stl, float angle) {
+ int i;
+ int j;
+ double radian_angle = (angle / 180.0) * M_PI;
+ double c = cos(radian_angle);
+ double s = sin(radian_angle);
+
+ if (stl->error) return;
+
+ for(i = 0; i < stl->stats.number_of_facets; i++) {
+ for(j = 0; j < 3; j++) {
+ stl_rotate(&stl->facet_start[i].vertex[j](2),
+ &stl->facet_start[i].vertex[j](0), c, s);
+ }
+ }
+ stl_get_size(stl);
+ calculate_normals(stl);
+}
+
+void
+stl_rotate_z(stl_file *stl, float angle) {
+ int i;
+ int j;
+ double radian_angle = (angle / 180.0) * M_PI;
+ double c = cos(radian_angle);
+ double s = sin(radian_angle);
+
+ if (stl->error) return;
+
+ for(i = 0; i < stl->stats.number_of_facets; i++) {
+ for(j = 0; j < 3; j++) {
+ stl_rotate(&stl->facet_start[i].vertex[j](0),
+ &stl->facet_start[i].vertex[j](1), c, s);
+ }
+ }
+ stl_get_size(stl);
+ calculate_normals(stl);
+}
+
+
+
+static void
+stl_rotate(float *x, float *y, const double c, const double s) {
+ double xold = *x;
+ double yold = *y;
+ *x = float(c * xold - s * yold);
+ *y = float(s * xold + c * yold);
+}
+
+void stl_get_size(stl_file *stl)
+{
+ if (stl->error || stl->stats.number_of_facets == 0)
+ return;
+ stl->stats.min = stl->facet_start[0].vertex[0];
+ stl->stats.max = stl->stats.min;
+ for (int i = 0; i < stl->stats.number_of_facets; ++ i) {
+ const stl_facet &face = stl->facet_start[i];
+ for (int j = 0; j < 3; ++ j) {
+ stl->stats.min = stl->stats.min.cwiseMin(face.vertex[j]);
+ stl->stats.max = stl->stats.max.cwiseMax(face.vertex[j]);
+ }
+ }
+ stl->stats.size = stl->stats.max - stl->stats.min;
+ stl->stats.bounding_diameter = stl->stats.size.norm();
+}
+
+void stl_mirror_xy(stl_file *stl)
+{
+ if (stl->error)
+ return;
+
+ for(int i = 0; i < stl->stats.number_of_facets; i++) {
+ for(int j = 0; j < 3; j++) {
+ stl->facet_start[i].vertex[j](2) *= -1.0;
+ }
+ }
+ float temp_size = stl->stats.min(2);
+ stl->stats.min(2) = stl->stats.max(2);
+ stl->stats.max(2) = temp_size;
+ stl->stats.min(2) *= -1.0;
+ stl->stats.max(2) *= -1.0;
+ stl_reverse_all_facets(stl);
+ stl->stats.facets_reversed -= stl->stats.number_of_facets; /* for not altering stats */
+}
+
+void stl_mirror_yz(stl_file *stl)
+{
+ if (stl->error) return;
+
+ for (int i = 0; i < stl->stats.number_of_facets; i++) {
+ for (int j = 0; j < 3; j++) {
+ stl->facet_start[i].vertex[j](0) *= -1.0;
+ }
+ }
+ float temp_size = stl->stats.min(0);
+ stl->stats.min(0) = stl->stats.max(0);
+ stl->stats.max(0) = temp_size;
+ stl->stats.min(0) *= -1.0;
+ stl->stats.max(0) *= -1.0;
+ stl_reverse_all_facets(stl);
+ stl->stats.facets_reversed -= stl->stats.number_of_facets; /* for not altering stats */
+}
+
+void stl_mirror_xz(stl_file *stl)
+{
+ if (stl->error)
+ return;
+
+ for (int i = 0; i < stl->stats.number_of_facets; i++) {
+ for (int j = 0; j < 3; j++) {
+ stl->facet_start[i].vertex[j](1) *= -1.0;
+ }
+ }
+ float temp_size = stl->stats.min(1);
+ stl->stats.min(1) = stl->stats.max(1);
+ stl->stats.max(1) = temp_size;
+ stl->stats.min(1) *= -1.0;
+ stl->stats.max(1) *= -1.0;
+ stl_reverse_all_facets(stl);
+ stl->stats.facets_reversed -= stl->stats.number_of_facets; /* for not altering stats */
+}
+
+static float get_volume(stl_file *stl)
+{
+ if (stl->error)
+ return 0;
+
+ // Choose a point, any point as the reference.
+ stl_vertex p0 = stl->facet_start[0].vertex[0];
+ float volume = 0.f;
+ for(uint32_t i = 0; i < stl->stats.number_of_facets; ++ i) {
+ // Do dot product to get distance from point to plane.
+ float height = stl->facet_start[i].normal.dot(stl->facet_start[i].vertex[0] - p0);
+ float area = get_area(&stl->facet_start[i]);
+ volume += (area * height) / 3.0f;
+ }
+ return volume;
+}
+
+void stl_calculate_volume(stl_file *stl)
+{
+ if (stl->error) return;
+ stl->stats.volume = get_volume(stl);
+ if(stl->stats.volume < 0.0) {
+ stl_reverse_all_facets(stl);
+ stl->stats.volume = -stl->stats.volume;
+ }
+}
+
+static float get_area(stl_facet *facet)
+{
+ /* cast to double before calculating cross product because large coordinates
+ can result in overflowing product
+ (bad area is responsible for bad volume and bad facets reversal) */
+ double cross[3][3];
+ for (int i = 0; i < 3; i++) {
+ cross[i][0]=(((double)facet->vertex[i](1) * (double)facet->vertex[(i + 1) % 3](2)) -
+ ((double)facet->vertex[i](2) * (double)facet->vertex[(i + 1) % 3](1)));
+ cross[i][1]=(((double)facet->vertex[i](2) * (double)facet->vertex[(i + 1) % 3](0)) -
+ ((double)facet->vertex[i](0) * (double)facet->vertex[(i + 1) % 3](2)));
+ cross[i][2]=(((double)facet->vertex[i](0) * (double)facet->vertex[(i + 1) % 3](1)) -
+ ((double)facet->vertex[i](1) * (double)facet->vertex[(i + 1) % 3](0)));
+ }
+
+ stl_normal sum;
+ sum(0) = cross[0][0] + cross[1][0] + cross[2][0];
+ sum(1) = cross[0][1] + cross[1][1] + cross[2][1];
+ sum(2) = cross[0][2] + cross[1][2] + cross[2][2];
+
+ // This should already be done. But just in case, let's do it again.
+ //FIXME this is questionable. the "sum" normal should be accurate, while the normal "n" may be calculated with a low accuracy.
+ stl_normal n;
+ stl_calculate_normal(n, facet);
+ stl_normalize_vector(n);
+ return 0.5f * n.dot(sum);
+}
+
+void stl_repair(stl_file *stl,
+ int fixall_flag,
+ int exact_flag,
+ int tolerance_flag,
+ float tolerance,
+ int increment_flag,
+ float increment,
+ int nearby_flag,
+ int iterations,
+ int remove_unconnected_flag,
+ int fill_holes_flag,
+ int normal_directions_flag,
+ int normal_values_flag,
+ int reverse_all_flag,
+ int verbose_flag) {
+
+ int i;
+ int last_edges_fixed = 0;
+
+ if (stl->error) return;
+
+ if(exact_flag || fixall_flag || nearby_flag || remove_unconnected_flag
+ || fill_holes_flag || normal_directions_flag) {
+ if (verbose_flag)
+ printf("Checking exact...\n");
+ exact_flag = 1;
+ stl_check_facets_exact(stl);
+ stl->stats.facets_w_1_bad_edge =
+ (stl->stats.connected_facets_2_edge -
+ stl->stats.connected_facets_3_edge);
+ stl->stats.facets_w_2_bad_edge =
+ (stl->stats.connected_facets_1_edge -
+ stl->stats.connected_facets_2_edge);
+ stl->stats.facets_w_3_bad_edge =
+ (stl->stats.number_of_facets -
+ stl->stats.connected_facets_1_edge);
+ }
+
+ if(nearby_flag || fixall_flag) {
+ if(!tolerance_flag) {
+ tolerance = stl->stats.shortest_edge;
+ }
+ if(!increment_flag) {
+ increment = stl->stats.bounding_diameter / 10000.0;
+ }
+
+ if(stl->stats.connected_facets_3_edge < stl->stats.number_of_facets) {
+ for(i = 0; i < iterations; i++) {
+ if(stl->stats.connected_facets_3_edge <
+ stl->stats.number_of_facets) {
+ if (verbose_flag)
+ printf("\
+Checking nearby. Tolerance= %f Iteration=%d of %d...",
+ tolerance, i + 1, iterations);
+ stl_check_facets_nearby(stl, tolerance);
+ if (verbose_flag)
+ printf(" Fixed %d edges.\n",
+ stl->stats.edges_fixed - last_edges_fixed);
+ last_edges_fixed = stl->stats.edges_fixed;
+ tolerance += increment;
+ } else {
+ if (verbose_flag)
+ printf("\
+All facets connected. No further nearby check necessary.\n");
+ break;
+ }
+ }
+ } else {
+ if (verbose_flag)
+ printf("All facets connected. No nearby check necessary.\n");
+ }
+ }
+
+ if(remove_unconnected_flag || fixall_flag || fill_holes_flag) {
+ if(stl->stats.connected_facets_3_edge < stl->stats.number_of_facets) {
+ if (verbose_flag)
+ printf("Removing unconnected facets...\n");
+ stl_remove_unconnected_facets(stl);
+ } else
+ if (verbose_flag)
+ printf("No unconnected need to be removed.\n");
+ }
+
+ if(fill_holes_flag || fixall_flag) {
+ if(stl->stats.connected_facets_3_edge < stl->stats.number_of_facets) {
+ if (verbose_flag)
+ printf("Filling holes...\n");
+ stl_fill_holes(stl);
+ } else
+ if (verbose_flag)
+ printf("No holes need to be filled.\n");
+ }
+
+ if(reverse_all_flag) {
+ if (verbose_flag)
+ printf("Reversing all facets...\n");
+ stl_reverse_all_facets(stl);
+ }
+
+ if(normal_directions_flag || fixall_flag) {
+ if (verbose_flag)
+ printf("Checking normal directions...\n");
+ stl_fix_normal_directions(stl);
+ }
+
+ if(normal_values_flag || fixall_flag) {
+ if (verbose_flag)
+ printf("Checking normal values...\n");
+ stl_fix_normal_values(stl);
+ }
+
+ /* Always calculate the volume. It shouldn't take too long */
+ if (verbose_flag)
+ printf("Calculating volume...\n");
+ stl_calculate_volume(stl);
+
+ if(exact_flag) {
+ if (verbose_flag)
+ printf("Verifying neighbors...\n");
+ stl_verify_neighbors(stl);
+ }
+}