Skip to content

Commit

Permalink
alpha shape 3D
Browse files Browse the repository at this point in the history
  • Loading branch information
ptitjano committed Oct 23, 2024
1 parent e919d0d commit 7f8d485
Show file tree
Hide file tree
Showing 6 changed files with 257 additions and 1 deletion.
2 changes: 2 additions & 0 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ file( GLOB_RECURSE SFCGAL_HEADERS "*.h" )
if (${CMAKE_CXX_COMPILER_ID} STREQUAL MSVC )
list(REMOVE_ITEM SFCGAL_SOURCES ${CMAKE_SOURCE_DIR}/src/algorithm/alphaShapes.cpp)
list(REMOVE_ITEM SFCGAL_HEADERS ${CMAKE_SOURCE_DIR}/src/algorithm/alphaShapes.h)
list(REMOVE_ITEM SFCGAL_SOURCES ${CMAKE_SOURCE_DIR}/src/algorithm/alphaShapes3D.cpp)
list(REMOVE_ITEM SFCGAL_HEADERS ${CMAKE_SOURCE_DIR}/src/algorithm/alphaShapes3D.h)
endif()

file( GLOB_RECURSE SFCGAL_HEADERS_COPIED RELATIVE ${CMAKE_SOURCE_DIR}/src "*.h" )
Expand Down
118 changes: 118 additions & 0 deletions src/algorithm/alphaShapes3D.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
// Copyright (c) 2024-2024, SFCGAL Contributors and Oslandia
// SPDX-License-Identifier: LGPL-2.0-or-later

#include "SFCGAL/algorithm/alphaShapes3D.h"
#include "SFCGAL/detail/GetPointsVisitor.h"
#include "SFCGAL/detail/transform/ForceOrderPoints.h"

#include <CGAL/Alpha_shape_3.h>
#include <CGAL/Alpha_shape_cell_base_3.h>
#include <CGAL/Alpha_shape_vertex_base_3.h>
#include <CGAL/Delaunay_triangulation_3.h>

namespace SFCGAL::algorithm {

using Vb3 = CGAL::Alpha_shape_vertex_base_3<Kernel>;
using Fb3 = CGAL::Alpha_shape_cell_base_3<Kernel>;
using Tds3 = CGAL::Triangulation_data_structure_3<Vb3, Fb3>;
using Delaunay3 =
CGAL::Delaunay_triangulation_3<Kernel, Tds3, CGAL::Fast_location>;
using Alpha_shape_3 = CGAL::Alpha_shape_3<Delaunay3>;
using Point_3 = Kernel::Point_3;

//! Reads the geometry and compute alphaShape
auto
buildAlphaShape(const Geometry &geom, AlphaShape3DMode mode)
-> std::unique_ptr<Alpha_shape_3>
{
if (geom.isEmpty()) {
return nullptr;
}

// Collect points from geometry
SFCGAL::detail::GetPointsVisitor getPointVisitor;
const_cast<Geometry &>(geom).accept(getPointVisitor);

// Need at least 4 points for 3D alpha shapes
if (getPointVisitor.points.size() < 4) {
return nullptr;
}

// Create points vector
std::vector<Point_3> points;
points.reserve(getPointVisitor.points.size());
for (const auto &point : getPointVisitor.points) {
points.push_back(point->toPoint_3());
}

// Create and configure alpha shape
auto alphaShape =
std::make_unique<Alpha_shape_3>(points.begin(), points.end());
alphaShape->set_mode(mode == AlphaShape3DMode::REGULARIZED
? Alpha_shape_3::REGULARIZED
: Alpha_shape_3::GENERAL);

// Set optimal alpha value for one solid component
auto optimalAlphaValue = alphaShape->find_optimal_alpha(1);
alphaShape->set_alpha(*optimalAlphaValue);

return alphaShape;
}

//! Converts alphaShape to a PolyhedralSurface
auto
alphaShape3D_to_polyhedralSurface(const Alpha_shape_3 &alphaShape)
-> std::unique_ptr<PolyhedralSurface>
{
auto resultSurface = std::make_unique<PolyhedralSurface>();

// Iterate through all facets of the alpha shape
for (auto facetIterator = alphaShape.alpha_shape_facets_begin();
facetIterator != alphaShape.alpha_shape_facets_end(); ++facetIterator) {

if (alphaShape.classify(*facetIterator) == Alpha_shape_3::REGULAR) {
// Get facet vertices
auto cell = facetIterator->first;
int idx = facetIterator->second;

const Point_3 pt1 = cell->vertex((idx + 1) & 3)->point();
const Point_3 pt2 = cell->vertex((idx + 2) & 3)->point();
const Point_3 pt3 = cell->vertex((idx + 3) & 3)->point();

// Create triangle polygon
auto poly = std::make_unique<Polygon>();
auto ring = std::make_unique<LineString>();

// create a closed ring
ring->addPoint(Point(pt1));
ring->addPoint(Point(pt2));
ring->addPoint(Point(pt3));
ring->addPoint(Point(pt1));

poly->setExteriorRing(ring.release());

// SFCGAL::transform::ForceOrderPoints force(false);
// poly->accept(force);

resultSurface->addPolygon(poly.release());
}
}

// SFCGAL::transform::ForceOrderPoints force(true);
// resultSurface->accept(force);

return resultSurface;
}

auto
alphaShapes3D(const Geometry &geom, AlphaShape3DMode mode)
-> std::unique_ptr<PolyhedralSurface>
{
std::unique_ptr<Alpha_shape_3> alphaShape = buildAlphaShape(geom, mode);
if (!alphaShape) {
return std::make_unique<PolyhedralSurface>();
}
return alphaShape3D_to_polyhedralSurface(*alphaShape);
}

} // namespace SFCGAL::algorithm
53 changes: 53 additions & 0 deletions src/algorithm/alphaShapes3D.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/**
* SFCGAL
*
* Copyright (C) 2024 SFCGAL Contributors and Oslandia <[email protected]>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library 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
* Library General Public License for more details.
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, see
<http://www.gnu.org/licenses/>.
*/

#ifndef _SFCGAL_ALGORITHM_ALPHASHAPES3D_H_
#define _SFCGAL_ALGORITHM_ALPHASHAPES3D_H_

#include "SFCGAL/Geometry.h"
#include "SFCGAL/PolyhedralSurface.h"
#include "SFCGAL/config.h"

namespace SFCGAL::algorithm {

/**
* Alpha shape calculation mode
*/
SFCGAL_API enum class AlphaShape3DMode {
GENERAL, ///< All faces from alpha complex (default)
REGULARIZED ///< Only faces from regular alpha complex
};

/**
* Compute the 3D alpha shape for a geometry with optimal alpha value
* https://doc.cgal.org/latest/Alpha_shapes_3/index.html
* @ingroup public_api
* @since 2.1
* @param geom input geometry
* @param mode GENERAL or REGULARIZED mode
* @return A PolyhedralSurface representing the optimal alpha shape
*/
SFCGAL_API std::unique_ptr<PolyhedralSurface>
alphaShapes3D(const Geometry &geom,
AlphaShape3DMode mode = AlphaShape3DMode::GENERAL);

} // namespace SFCGAL::algorithm

#endif
1 change: 1 addition & 0 deletions test/data/bunny1000Wkt.txt

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion test/unit/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ file( GLOB_RECURSE SFCGAL_UNIT_TEST_SOURCES *.cpp )

if (${CMAKE_CXX_COMPILER_ID} STREQUAL MSVC )
list(REMOVE_ITEM SFCGAL_UNIT_TEST_SOURCES ${CMAKE_SOURCE_DIR}/test/unit/SFCGAL/algorithm/AlphaShapesTest.cpp)
list(REMOVE_ITEM SFCGAL_UNIT_TEST_SOURCES ${CMAKE_SOURCE_DIR}/test/unit/SFCGAL/algorithm/AlphaShapes3DTest.cpp)
endif()

add_executable( unit-test-SFCGAL ${SFCGAL_UNIT_TEST_SOURCES} )
Expand All @@ -24,4 +25,3 @@ if (CMAKE_BUILD_TYPE STREQUAL "Debug" )
else()
add_test( unit-test ${CMAKE_CURRENT_BINARY_DIR}/unit-test-SFCGAL --auto_start_dbg=y --log_level=all)
endif()

82 changes: 82 additions & 0 deletions test/unit/SFCGAL/algorithm/AlphaShapes3DTest.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
/**
* SFCGAL
*
* Copyright (C) 2012-2013 Oslandia <[email protected]>
* Copyright (C) 2012-2013 IGN (http://www.ign.fr)
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library 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
* Library General Public License for more details.
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, see
<http://www.gnu.org/licenses/>.
*/
#include <boost/format/parsing.hpp>
#include <boost/test/tools/old/interface.hpp>
#include <boost/test/unit_test.hpp>

#include "SFCGAL/GeometryCollection.h"
#include "SFCGAL/Polygon.h"
#include "SFCGAL/algorithm/isValid.h"
#include "SFCGAL/algorithm/alphaShapes3D.h"
#include "SFCGAL/io/wkt.h"

using namespace SFCGAL;

#include "../../../test_config.h"
// always after CGAL
using namespace boost::unit_test;

BOOST_AUTO_TEST_SUITE(SFCGAL_algorithm_AlphaShapesTest)

// algorithm::alphaShapes3D


BOOST_AUTO_TEST_CASE(testAlphaShapes3D_Empty)
{
GeometryCollection emptyCollection;
emptyCollection.addGeometry(Polygon());
emptyCollection.addGeometry(Polygon());
std::unique_ptr<Geometry> emptyAlphaShape3D (algorithm::alphaShapes3D(emptyCollection));
BOOST_CHECK(emptyAlphaShape3D->isEmpty());
}

BOOST_AUTO_TEST_CASE(testAlphaShapes3D_MultiPoint)
{
std::string inputData(SFCGAL_TEST_DIRECTORY);
inputData += "/data/bunny1000Wkt.txt";
std::ifstream bunnyFS(inputData.c_str());
BOOST_REQUIRE(bunnyFS.good());

std::ostringstream inputWkt;
inputWkt << bunnyFS.rdbuf();

std::unique_ptr<Geometry> geomInput(io::readWkt(inputWkt.str()));

std::unique_ptr<Geometry> alphaShapesGeneral(algorithm::alphaShapes3D(geomInput->as<const SFCGAL::Geometry>()));

// check
try {
const SFCGAL::Validity validity = SFCGAL::algorithm::isValid(*alphaShapesGeneral);
bool is_valid = validity;
if (!is_valid) {
std::cout << validity.reason() << "\n";
exit(1);
}
} catch (SFCGAL::Exception &e) {
std::cout << e.what() << "\n";
exit(1);
}

exit(1);
}


BOOST_AUTO_TEST_SUITE_END()

0 comments on commit 7f8d485

Please sign in to comment.