File TriangulatedSurface.cpp
File List > src > TriangulatedSurface.cpp
Go to the documentation of this file
// Copyright (c) 2012-2013, IGN France.
// Copyright (c) 2012-2022, Oslandia.
// SPDX-License-Identifier: LGPL-2.0-or-later
#include "SFCGAL/TriangulatedSurface.h"
#include "SFCGAL/GeometryVisitor.h"
namespace SFCGAL {
TriangulatedSurface::TriangulatedSurface() = default;
TriangulatedSurface::TriangulatedSurface(const std::vector<Triangle> &triangles)
{
for (const auto &triangle : triangles) {
_triangles.push_back(triangle.clone());
}
}
TriangulatedSurface::TriangulatedSurface(const TriangulatedSurface &other)
= default;
auto
TriangulatedSurface::operator=(TriangulatedSurface other)
-> TriangulatedSurface &
{
swap(other);
return *this;
}
TriangulatedSurface::~TriangulatedSurface() = default;
auto
TriangulatedSurface::clone() const -> TriangulatedSurface *
{
return new TriangulatedSurface(*this);
}
auto
TriangulatedSurface::geometryType() const -> std::string
{
return "TriangulatedSurface";
}
auto
TriangulatedSurface::geometryTypeId() const -> GeometryType
{
return TYPE_TRIANGULATEDSURFACE;
}
auto
TriangulatedSurface::dimension() const -> int
{
// surface
return 2;
}
auto
TriangulatedSurface::coordinateDimension() const -> int
{
if (_triangles.empty()) {
return 0;
}
return _triangles[0].coordinateDimension();
}
auto
TriangulatedSurface::isEmpty() const -> bool
{
return _triangles.empty();
}
auto
TriangulatedSurface::is3D() const -> bool
{
return !_triangles.empty() && _triangles.front().is3D();
}
auto
TriangulatedSurface::isMeasured() const -> bool
{
return !_triangles.empty() && _triangles.front().isMeasured();
}
void
TriangulatedSurface::addTriangles(const TriangulatedSurface &other)
{
for (const auto &it : other) {
addTriangle(it);
}
}
auto
TriangulatedSurface::numGeometries() const -> size_t
{
return _triangles.size();
}
auto
TriangulatedSurface::geometryN(size_t const &n) const -> const Triangle &
{
BOOST_ASSERT(n < numGeometries());
return _triangles[n];
}
auto
TriangulatedSurface::geometryN(size_t const &n) -> Triangle &
{
BOOST_ASSERT(n < numGeometries());
return _triangles[n];
}
void
TriangulatedSurface::reserve(const size_t &n)
{
_triangles.reserve(n);
}
void
TriangulatedSurface::accept(GeometryVisitor &visitor)
{
return visitor.visit(*this);
}
void
TriangulatedSurface::accept(ConstGeometryVisitor &visitor) const
{
return visitor.visit(*this);
}
// Private class
// A modifier creating triangles from a TriangulatedSurface with the incremental
// builder.
template <class HDS>
class Triangulated2Polyhedron : public CGAL::Modifier_base<HDS> {
public:
Triangulated2Polyhedron(const TriangulatedSurface &surf) : surf(surf) {}
using Vertex = typename HDS::Vertex;
using Point = typename Vertex::Point;
using K = typename HDS::Traits;
using PointMap = std::map<Point, size_t>;
using HalfedgeSet = std::set<std::pair<Point, Point>>;
void
operator()(HDS &hds) override
{
// Postcondition: `hds' is a valid polyhedral surface.
CGAL::Polyhedron_incremental_builder_3<HDS> B(hds, true);
B.begin_surface(/* vertices */ surf.numGeometries() * 3,
/* facets */ surf.numGeometries(),
/* halfedges */ surf.numGeometries() * 3);
size_t vertex_idx = 0;
// first pass: insert vertices, only if they are not shared between faces
// thanks to a binary tree (PointMap)
for (size_t i = 0; i < surf.numGeometries(); i++) {
for (size_t j = 0; j < 3; j++) {
Point const p = surf.geometryN(i).vertex(j).toPoint_3();
if (points.find(p) == points.end()) {
B.add_vertex(p);
points[p] = vertex_idx++;
}
}
}
// second pass: adjacent triangles must be built with compliant orientations
// the two halfedges of a shared edge must be of opposite orientation
// Extract from CGAL's documentation
// "The convention is that the halfedges are oriented counterclockwise
// around facets as seen from the outside of the polyhedron"
for (size_t i = 0; i < surf.numGeometries(); i++) {
B.begin_facet();
CGAL::Triangle_3<K> const tri(surf.geometryN(i).toTriangle_3());
CGAL::Point_3<K> const pa(tri[0]);
CGAL::Point_3<K> const pb(tri[1]);
CGAL::Point_3<K> const pc(tri[2]);
if (edges.find(std::make_pair(pa, pb)) != edges.end() ||
edges.find(std::make_pair(pb, pc)) != edges.end() ||
edges.find(std::make_pair(pc, pa)) != edges.end()) {
BOOST_THROW_EXCEPTION(
Exception("When trying to build a CGAL::Polyhedron_3 from a "
"TriangulatedSurface: bad orientation for " +
surf.geometryN(i).asText() +
" consider using ConsistentOrientationBuilder first"));
}
B.add_vertex_to_facet(points[pa]);
B.add_vertex_to_facet(points[pb]);
B.add_vertex_to_facet(points[pc]);
edges.insert(std::make_pair(pa, pb));
edges.insert(std::make_pair(pb, pc));
edges.insert(std::make_pair(pc, pa));
B.end_facet();
}
B.end_surface();
}
private:
const TriangulatedSurface &surf;
PointMap points;
HalfedgeSet edges;
};
template <typename Polyhedron>
struct Plane_from_facet {
auto
operator()(typename Polyhedron::Facet &f) -> typename Polyhedron::Plane_3
{
typename Polyhedron::Halfedge_handle const h = f.halfedge();
return typename Polyhedron::Plane_3(h->vertex()->point(),
h->next()->vertex()->point(),
h->opposite()->vertex()->point());
}
};
template <typename K, typename Polyhedron>
auto
TriangulatedSurface::toPolyhedron_3() const -> std::unique_ptr<Polyhedron>
{
auto *poly = new Polyhedron();
Triangulated2Polyhedron<typename Polyhedron::HalfedgeDS> converter(*this);
poly->delegate(converter);
// compute planes
std::transform(poly->facets_begin(), poly->facets_end(), poly->planes_begin(),
Plane_from_facet<Polyhedron>());
return std::unique_ptr<Polyhedron>(poly);
}
template SFCGAL_API std::unique_ptr<detail::MarkedPolyhedron>
TriangulatedSurface::toPolyhedron_3<Kernel, detail::MarkedPolyhedron>() const;
template SFCGAL_API std::unique_ptr<CGAL::Polyhedron_3<Kernel>>
TriangulatedSurface::toPolyhedron_3<Kernel, CGAL::Polyhedron_3<Kernel>>() const;
} // namespace SFCGAL