PrusaSlicer-NonPlainar/src/admesh/stl.h
2022-03-15 19:53:43 +01:00

343 lines
14 KiB
C++

/* 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 <vector>
#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;
typedef Eigen::Matrix<int, 3, 1, Eigen::DontAlign> stl_triangle_vertex_indices;
static_assert(sizeof(stl_vertex) == 12, "size of stl_vertex incorrect");
static_assert(sizeof(stl_normal) == 12, "size of stl_normal incorrect");
struct stl_facet {
stl_normal normal;
stl_vertex vertex[3];
char extra[2];
stl_facet rotated(const Eigen::Quaternion<float, Eigen::DontAlign> &rot) const {
stl_facet out;
out.normal = rot * this->normal;
out.vertex[0] = rot * this->vertex[0];
out.vertex[1] = rot * this->vertex[1];
out.vertex[2] = rot * this->vertex[2];
return out;
}
};
#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;
struct stl_neighbors {
stl_neighbors() { reset(); }
void reset() {
neighbor[0] = -1;
neighbor[1] = -1;
neighbor[2] = -1;
which_vertex_not[0] = -1;
which_vertex_not[1] = -1;
which_vertex_not[2] = -1;
}
int num_neighbors() const { return 3 - ((this->neighbor[0] == -1) + (this->neighbor[1] == -1) + (this->neighbor[2] == -1)); }
// Index of a neighbor facet.
int neighbor[3];
// Index of an opposite vertex at the neighbor face.
char which_vertex_not[3];
};
struct stl_stats {
stl_stats() { memset(&header, 0, 81); }
char header[81];
stl_type type = (stl_type)0;
// Should always match the number of facets stored inside stl_file::facet_start.
uint32_t number_of_facets = 0;
// Bounding box.
stl_vertex max = stl_vertex::Zero();
stl_vertex min = stl_vertex::Zero();
stl_vertex size = stl_vertex::Zero();
float bounding_diameter = 0.f;
float shortest_edge = 0.f;
// After repair, the volume shall always be positive.
float volume = -1.f;
// Number of face edges connected to another face.
// Don't use this statistics after repair, use the connected_facets_1/2/3_edge instead!
int connected_edges = 0;
// Faces with >=1, >=2 and 3 edges connected to another face.
int connected_facets_1_edge = 0;
int connected_facets_2_edge = 0;
int connected_facets_3_edge = 0;
// Faces with 1, 2 and 3 open edges after exact chaining, but before repair.
int facets_w_1_bad_edge = 0;
int facets_w_2_bad_edge = 0;
int facets_w_3_bad_edge = 0;
// Number of faces read form an STL file.
int original_num_facets = 0;
// Number of edges connected one to another by snapping their end vertices.
int edges_fixed = 0;
// Number of faces removed because they were degenerated.
int degenerate_facets = 0;
// Total number of facets removed: Degenerate faces and unconnected faces.
int facets_removed = 0;
// Number of faces added by hole filling.
int facets_added = 0;
// Number of faces reversed because of negative volume or because one patch was connected to another patch with incompatible normals.
int facets_reversed = 0;
// Number of incompatible edges remaining after the patches were connected together and possibly their normals flipped.
int backwards_edges = 0;
// Number of triangles, which were flipped during the fixing process.
int normals_fixed = 0;
// Number of connected triangle patches.
int number_of_parts = 0;
void clear() { *this = stl_stats(); }
};
struct stl_file {
stl_file() {}
void clear() {
this->facet_start.clear();
this->neighbors_start.clear();
this->stats.clear();
}
size_t memsize() const {
return sizeof(*this) + sizeof(stl_facet) * facet_start.size() + sizeof(stl_neighbors) * neighbors_start.size();
}
std::vector<stl_facet> facet_start;
std::vector<stl_neighbors> neighbors_start;
// Statistics
stl_stats stats;
};
struct indexed_triangle_set
{
void clear() { indices.clear(); vertices.clear(); }
size_t memsize() const {
return sizeof(*this) + sizeof(stl_triangle_vertex_indices) * indices.size() + sizeof(stl_vertex) * vertices.size();
}
std::vector<stl_triangle_vertex_indices> indices;
std::vector<stl_vertex> vertices;
bool empty() const { return indices.empty() || vertices.empty(); }
};
extern bool stl_open(stl_file *stl, const char *file);
extern void stl_stats_out(stl_file *stl, FILE *file, char *input_file);
extern bool stl_print_neighbors(stl_file *stl, char *file);
extern bool stl_write_ascii(stl_file *stl, const char *file, const char *label);
extern bool stl_write_binary(stl_file *stl, const char *file, const char *label);
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_neighbor(stl_file *stl, int facet);
extern bool 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_get_size(stl_file *stl);
// the following function is not used
/*
template<typename T>
extern void stl_transform(stl_file *stl, T *trafo3x4)
{
Eigen::Matrix<T, 3, 3, Eigen::DontAlign> trafo3x3;
for (int i = 0; i < 3; ++i)
{
for (int j = 0; j < 3; ++j)
{
trafo3x3(i, j) = (i * 4) + j;
}
}
Eigen::Matrix<T, 3, 3, Eigen::DontAlign> r = trafo3x3.inverse().transpose();
for (uint32_t i_face = 0; i_face < stl->stats.number_of_facets; ++ i_face) {
stl_facet &face = stl->facet_start[i_face];
for (int i_vertex = 0; i_vertex < 3; ++ i_vertex) {
stl_vertex &v_dst = face.vertex[i_vertex];
stl_vertex v_src = v_dst;
v_dst(0) = T(trafo3x4[0] * v_src(0) + trafo3x4[1] * v_src(1) + trafo3x4[2] * v_src(2) + trafo3x4[3]);
v_dst(1) = T(trafo3x4[4] * v_src(0) + trafo3x4[5] * v_src(1) + trafo3x4[6] * v_src(2) + trafo3x4[7]);
v_dst(2) = T(trafo3x4[8] * v_src(0) + trafo3x4[9] * v_src(1) + trafo3x4[10] * v_src(2) + trafo3x4[11]);
}
face.normal = (r * face.normal.template cast<T>()).template cast<float>().eval();
}
stl_get_size(stl);
}
*/
template<typename T>
inline void stl_transform(stl_file *stl, const Eigen::Transform<T, 3, Eigen::Affine, Eigen::DontAlign>& t)
{
const Eigen::Matrix<T, 3, 3, Eigen::DontAlign> r = t.matrix().template block<3, 3>(0, 0).inverse().transpose();
for (size_t i = 0; i < stl->stats.number_of_facets; ++ i) {
stl_facet &f = stl->facet_start[i];
for (size_t j = 0; j < 3; ++j)
f.vertex[j] = (t * f.vertex[j].template cast<T>()).template cast<float>().eval();
f.normal = (r * f.normal.template cast<T>()).template cast<float>().eval();
}
stl_get_size(stl);
}
template<typename T>
inline void stl_transform(stl_file *stl, const Eigen::Matrix<T, 3, 3, Eigen::DontAlign>& m)
{
const Eigen::Matrix<T, 3, 3, Eigen::DontAlign> r = m.inverse().transpose();
for (size_t i = 0; i < stl->stats.number_of_facets; ++ i) {
stl_facet &f = stl->facet_start[i];
for (size_t j = 0; j < 3; ++j)
f.vertex[j] = (m * f.vertex[j].template cast<T>()).template cast<float>().eval();
f.normal = (r * f.normal.template cast<T>()).template cast<float>().eval();
}
stl_get_size(stl);
}
template<typename V>
inline void its_translate(indexed_triangle_set &its, const V v)
{
for (stl_vertex &v_dst : its.vertices)
v_dst += v;
}
template<typename T>
inline void its_transform(indexed_triangle_set &its, T *trafo3x4)
{
for (stl_vertex &v_dst : its.vertices) {
stl_vertex v_src = v_dst;
v_dst(0) = T(trafo3x4[0] * v_src(0) + trafo3x4[1] * v_src(1) + trafo3x4[2] * v_src(2) + trafo3x4[3]);
v_dst(1) = T(trafo3x4[4] * v_src(0) + trafo3x4[5] * v_src(1) + trafo3x4[6] * v_src(2) + trafo3x4[7]);
v_dst(2) = T(trafo3x4[8] * v_src(0) + trafo3x4[9] * v_src(1) + trafo3x4[10] * v_src(2) + trafo3x4[11]);
}
}
template<typename T>
inline void its_transform(indexed_triangle_set &its, const Eigen::Transform<T, 3, Eigen::Affine, Eigen::DontAlign>& t, bool fix_left_handed = false)
{
//const Eigen::Matrix<double, 3, 3, Eigen::DontAlign> r = t.matrix().template block<3, 3>(0, 0);
for (stl_vertex &v : its.vertices)
v = (t * v.template cast<T>()).template cast<float>().eval();
if (fix_left_handed && t.matrix().block(0, 0, 3, 3).determinant() < 0.)
for (stl_triangle_vertex_indices &i : its.indices)
std::swap(i[0], i[1]);
}
template<typename T>
inline void its_transform(indexed_triangle_set &its, const Eigen::Matrix<T, 3, 3, Eigen::DontAlign>& m, bool fix_left_handed = false)
{
for (stl_vertex &v : its.vertices)
v = (m * v.template cast<T>()).template cast<float>().eval();
if (fix_left_handed && m.determinant() < 0.)
for (stl_triangle_vertex_indices &i : its.indices)
std::swap(i[0], i[1]);
}
extern void its_rotate_x(indexed_triangle_set &its, float angle);
extern void its_rotate_y(indexed_triangle_set &its, float angle);
extern void its_rotate_z(indexed_triangle_set &its, float angle);
extern void stl_generate_shared_vertices(stl_file *stl, indexed_triangle_set &its);
extern bool its_write_obj(const indexed_triangle_set &its, const char *file);
extern bool its_write_off(const indexed_triangle_set &its, const char *file);
extern bool its_write_vrml(const indexed_triangle_set &its, const char *file);
typedef Eigen::Matrix<float, 3, 1, Eigen::DontAlign> obj_color; // Vec3f
/// <summary>
/// write idexed triangle set into obj file with color
/// </summary>
/// <param name="its">input model</param>
/// <param name="color">color of stored model</param>
/// <param name="file">define place to store</param>
/// <returns>True on success otherwise FALSE</returns>
extern bool its_write_obj(const indexed_triangle_set& its, const std::vector<obj_color> &color, const char* file);
extern bool stl_write_dxf(stl_file *stl, const char *file, char *label);
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 *= float(1.0 / length);
}
extern void stl_calculate_volume(stl_file *stl);
extern void stl_repair(stl_file *stl, bool fixall_flag, bool exact_flag, bool tolerance_flag, float tolerance, bool increment_flag, float increment, bool nearby_flag, int iterations, bool remove_unconnected_flag, bool fill_holes_flag, bool normal_directions_flag, bool normal_values_flag, bool reverse_all_flag, bool verbose_flag);
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, const stl_facet *new_facet);
// Validate the mesh, assert on error.
extern bool stl_validate(const stl_file *stl);
extern bool stl_validate(const stl_file *stl, const indexed_triangle_set &its);
#endif