mirror of https://github.com/vitalif/openscad
122 lines
4.3 KiB
C++
122 lines
4.3 KiB
C++
#include "polyset-utils.h"
|
|
#include "polyset.h"
|
|
#include "Polygon2d.h"
|
|
#include "printutils.h"
|
|
#include "cgal.h"
|
|
|
|
#ifdef NDEBUG
|
|
#define PREV_NDEBUG NDEBUG
|
|
#undef NDEBUG
|
|
#endif
|
|
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
|
|
#include <CGAL/Constrained_Delaunay_triangulation_2.h>
|
|
#include <CGAL/Triangulation_2_filtered_projection_traits_3.h>
|
|
#ifdef PREV_NDEBUG
|
|
#define NDEBUG PREV_NDEBUG
|
|
#endif
|
|
|
|
typedef CGAL::Exact_predicates_inexact_constructions_kernel K;
|
|
typedef CGAL::Triangulation_2_filtered_projection_traits_3<K> Projection;
|
|
typedef CGAL::Triangulation_data_structure_2 <
|
|
CGAL::Triangulation_vertex_base_2<Projection>,
|
|
CGAL::Constrained_triangulation_face_base_2<Projection> > Tds;
|
|
typedef CGAL::Constrained_Delaunay_triangulation_2<Projection, Tds, CGAL::Exact_predicates_tag> CDT;
|
|
|
|
#include <boost/foreach.hpp>
|
|
|
|
namespace PolysetUtils {
|
|
|
|
// Project all polygons (also back-facing) into a Polygon2d instance.
|
|
// It's important to select all faces, since filtering by normal vector here
|
|
// will trigger floating point incertainties and cause problems later.
|
|
Polygon2d *project(const PolySet &ps) {
|
|
Polygon2d *poly = new Polygon2d;
|
|
|
|
BOOST_FOREACH(const PolySet::Polygon &p, ps.polygons) {
|
|
Outline2d outline;
|
|
BOOST_FOREACH(const Vector3d &v, p) {
|
|
outline.vertices.push_back(Vector2d(v[0], v[1]));
|
|
}
|
|
poly->addOutline(outline);
|
|
}
|
|
return poly;
|
|
}
|
|
|
|
/* Tessellation of 3d PolySet faces
|
|
|
|
This code is for tessellating the faces of a 3d PolySet, assuming that
|
|
the faces are near-planar polygons.
|
|
|
|
The purpose of this code is originally to fix github issue 349. Our CGAL
|
|
kernel does not accept polygons for Nef_Polyhedron_3 if each of the
|
|
points is not exactly coplanar. "Near-planar" or "Almost planar" polygons
|
|
often occur due to rounding issues on, for example, polyhedron() input.
|
|
By tessellating the 3d polygon into individual smaller tiles that
|
|
are perfectly coplanar (triangles, for example), we can get CGAL to accept
|
|
the polyhedron() input.
|
|
*/
|
|
|
|
/* Given a 3D PolySet with near planar polygonal faces, tessellate the
|
|
faces. As of writing, our only tessellation method is triangulation
|
|
using CGAL's Constrained Delaunay algorithm. This code assumes the input
|
|
polyset has simple polygon faces with no holes.
|
|
The tessellation will be robust wrt. degenerate and self-intersecting
|
|
*/
|
|
void tessellate_faces(const PolySet &inps, PolySet &outps) {
|
|
int degeneratePolygons = 0;
|
|
for (size_t i = 0; i < inps.polygons.size(); i++) {
|
|
const PolySet::Polygon pgon = inps.polygons[i];
|
|
if (pgon.size() < 3) {
|
|
degeneratePolygons++;
|
|
continue;
|
|
}
|
|
std::vector<PolySet::Polygon> triangles;
|
|
if (pgon.size() == 3) {
|
|
triangles.push_back(pgon);
|
|
}
|
|
else {
|
|
// Build a data structure that CGAL accepts
|
|
std::vector<K::Point_3> cgalpoints;
|
|
BOOST_FOREACH(const Vector3d &v, pgon) {
|
|
cgalpoints.push_back(K::Point_3(v[0], v[1], v[2]));
|
|
}
|
|
// Calculate best guess at face normal using Newell's method
|
|
K::Vector_3 normal;
|
|
CGAL::normal_vector_newell_3(cgalpoints.begin(), cgalpoints.end(), normal);
|
|
|
|
// Pass the normal vector to the (undocumented)
|
|
// CGAL::Triangulation_2_filtered_projection_traits_3. This
|
|
// trait deals with projection from 3D to 2D using the normal
|
|
// vector as a hint, and allows for near-planar polygons to be passed to
|
|
// the Constrained Delaunay Triangulator.
|
|
Projection actualProjection(normal);
|
|
CDT cdt(actualProjection);
|
|
for (size_t i=0;i<cgalpoints.size(); i++) {
|
|
cdt.insert_constraint(cgalpoints[i], cgalpoints[(i+1)%cgalpoints.size()]);
|
|
}
|
|
|
|
// Iterate over the resulting faces
|
|
CDT::Finite_faces_iterator fit;
|
|
for (fit=cdt.finite_faces_begin(); fit!=cdt.finite_faces_end(); fit++) {
|
|
PolySet::Polygon pgon;
|
|
for (int i=0;i<3;i++) {
|
|
K::Point_3 v = cdt.triangle(fit)[i];
|
|
pgon.push_back(Vector3d(v.x(), v.y(), v.z()));
|
|
}
|
|
triangles.push_back(pgon);
|
|
}
|
|
}
|
|
|
|
// ..and pass to the output polyhedron
|
|
for (size_t j=0;j<triangles.size();j++) {
|
|
PolySet::Polygon t = triangles[j];
|
|
outps.append_poly();
|
|
outps.append_vertex(t[0].x(),t[0].y(),t[0].z());
|
|
outps.append_vertex(t[1].x(),t[1].y(),t[1].z());
|
|
outps.append_vertex(t[2].x(),t[2].y(),t[2].z());
|
|
}
|
|
}
|
|
if (degeneratePolygons > 0) PRINT("WARNING: PolySet has degenerate polygons");
|
|
}
|
|
}
|