Skip to content

Commit

Permalink
wip: feat: isSimple method for geometries
Browse files Browse the repository at this point in the history
  • Loading branch information
delhomer committed Oct 17, 2024
1 parent c92aa73 commit c1d2ac1
Show file tree
Hide file tree
Showing 5 changed files with 386 additions and 0 deletions.
117 changes: 117 additions & 0 deletions src/algorithm/isSimple.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
// Copyright (c) 2012-2013, IGN France.
// Copyright (c) 2012-2024, Oslandia.
// SPDX-License-Identifier: LGPL-2.0-or-later

#include "SFCGAL/algorithm/isSimple.h"

#include "SFCGAL/GeometryCollection.h"
#include "SFCGAL/LineString.h"
#include "SFCGAL/MultiLineString.h"
#include "SFCGAL/MultiPoint.h"
#include "SFCGAL/MultiPolygon.h"
#include "SFCGAL/MultiSolid.h"
#include "SFCGAL/Polygon.h"
#include "SFCGAL/PolyhedralSurface.h"
#include "SFCGAL/Triangle.h"
#include "SFCGAL/TriangulatedSurface.h"

#include "SFCGAL/Kernel.h"
#include "SFCGAL/algorithm/intersects.h"
#include "SFCGAL/algorithm/plane.h"
#include "SFCGAL/detail/algorithm/coversPoints.h"

using namespace SFCGAL::detail::algorithm;

namespace SFCGAL {

namespace algorithm {

auto
isSimple(const LineString &linestring) -> const Simplicity
{
if (linestring.is3D() ? selfIntersects3D(linestring) : selfIntersects(linestring)) {
return Simplicity::complex(boost::format("linestring self intersects").str());
}
return Simplicity::simple();
}

auto
isSimple(const Polygon &polygon, const double &toleranceAbs = 1e-9) -> const Simplicity
{
// Polygone must be planar (all points in the same plane)
if (polygon.is3D() && !isPlane3D<Kernel>(polygon, toleranceAbs)) {
return Simplicity::complex("Points don't lie in the same plane.");
}
return Simplicity::simple();
}

auto
isSimple(const MultiPoint &multipoint) -> const Simplicity
{
const size_t numPoint = multipoint.numGeometries();

for (size_t l = 0; l != numPoint - 1; ++l) {
for (size_t other_l = l + 1; other_l != numPoint; ++other_l) {
bool const duplicated_points = multipoint.pointN(l) == multipoint.pointN(other_l);
if (duplicated_points) {
return Simplicity::complex(
(boost::format("Points %d and %d are duplicated in the MultiPoint.") % l % other_l)
.str());
}
}
}

return Simplicity::simple();
}

auto
isSimple(const Geometry &g, const double &toleranceAbs) -> const Simplicity
{
switch (g.geometryTypeId()) {
case TYPE_POINT:
return Simplicity::simple();

case TYPE_LINESTRING: // no self-intersecting (excepted at ending point)
return isSimple(g.as<LineString>());

case TYPE_POLYGON: // are the rings simple? (3D) -> check the coplanarity
return isSimple(g.as<Polygon>(), toleranceAbs);

case TYPE_TRIANGLE:
return Simplicity::simple();

case TYPE_SOLID: // every phs is simple
return isSimple(g.as<Solid>());

case TYPE_MULTIPOINT: // no equal points
return isSimple(g.as<MultiPoint>());

case TYPE_MULTILINESTRING: // every ls is simple, and only intersections are at boundaries
return isSimple(g.as<MultiLineString>());

case TYPE_MULTIPOLYGON: // every polygon is simple
return isSimple(g.as<MultiPolygon>());

case TYPE_MULTISOLID: // every solid is simple
return isSimple(g.as<MultiSolid>());

case TYPE_GEOMETRYCOLLECTION: // every geometry is simple
return isSimple(g.as<GeometryCollection>());

case TYPE_TRIANGULATEDSURFACE: // every triangle is simple
return isSimple(g.as<TriangulatedSurface>());

case TYPE_POLYHEDRALSURFACE: // every polygon is simple
return isSimple(g.as<PolyhedralSurface>());
}

BOOST_THROW_EXCEPTION(Exception(
(boost::format("isSimple( %s ) is not defined") % g.geometryType())
.str()));
return Simplicity::complex(
(boost::format("isSimple( %s ) is not defined") % g.geometryType())
.str()); // to avoid warning
}

} // namespace algorithm
} // namespace SFCGAL
31 changes: 31 additions & 0 deletions src/algorithm/isSimple.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// Copyright (c) 2012-2013, IGN France.
// Copyright (c) 2012-2022, Oslandia.
// SPDX-License-Identifier: LGPL-2.0-or-later

#ifndef SFCGAL_ALGORITHM_ISSIMPLE_H_
#define SFCGAL_ALGORITHM_ISSIMPLE_H_

#include "SFCGAL/Geometry.h"
#include "SFCGAL/Simplicity.h"

namespace SFCGAL {

/**
* Functions used to assert for geometry simplicity
*/
void SFCGAL_API
SFCGAL_ASSERT_GEOMETRY_SIMPLICITY(const Geometry &g);

namespace algorithm {

/**
* @brief Check simplicity of a geometry
* @ingroup public_api
*/
SFCGAL_API const Simplicity
isSimple(const Geometry &g, const double &toleranceAbs = 1e-9);

} // namespace algorithm
} // namespace SFCGAL

#endif
34 changes: 34 additions & 0 deletions src/capi/sfcgal_c.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
#include "SFCGAL/algorithm/extrude.h"
#include "SFCGAL/algorithm/intersection.h"
#include "SFCGAL/algorithm/intersects.h"
#include "SFCGAL/algorithm/isSimple.h"
#include "SFCGAL/algorithm/isValid.h"
#include "SFCGAL/algorithm/lineSubstring.h"
#include "SFCGAL/algorithm/minkowskiSum.h"
Expand Down Expand Up @@ -218,6 +219,39 @@ sfcgal_geometry_is_valid_detail(const sfcgal_geometry_t *geom,
return static_cast<int>(is_valid);
}

extern "C" auto
sfcgal_geometry_is_simple(const sfcgal_geometry_t *geom) -> int
{
SFCGAL_GEOMETRY_CONVERT_CATCH_TO_ERROR(
return (int)bool(SFCGAL::algorithm::isSimple(
*reinterpret_cast<const SFCGAL::Geometry *>(geom)));)
}

extern "C" auto
sfcgal_geometry_is_simple_detail(const sfcgal_geometry_t *geom,
char **invalidity_reason) -> int
{
// set to null for now
if (complexity_reason != nullptr) {
*complexity_reason = nullptr;
}

const auto *g = reinterpret_cast<const SFCGAL::Geometry *>(geom);
bool is_simple = false;
try {
SFCGAL::Simplicity const simplicity = SFCGAL::algorithm::isSimple(*g);
is_simple = simplicity;
if (!is_simple && (complexity_reason != nullptr)) {
*complexity_reason = strdup(simplicity.reason().c_str());
}
} catch (SFCGAL::Exception &e) {
if (complexity_reason != nullptr) {
*complexity_reason = strdup(e.what());
}
}
return static_cast<int>(is_simple);
}

extern "C" auto
sfcgal_geometry_is_3d(const sfcgal_geometry_t *geom) -> int
{
Expand Down
20 changes: 20 additions & 0 deletions src/capi/sfcgal_c.h
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,26 @@ sfcgal_geometry_is_valid_detail(const sfcgal_geometry_t *geom,
char **invalidity_reason,
sfcgal_geometry_t **invalidity_location);

/**
* Tests if the given geometry is simple or not
* @ingroup capi
*/
SFCGAL_API int
sfcgal_geometry_is_simple(const sfcgal_geometry_t *);

/**
* Tests if the given geometry is simple or not
* And return details in case of complexity
* @param geom the input geometry
* @param complexity_reason input/output parameter. If non null, a
* null-terminated string could be allocated and contain reason of the
* complexity
* @ingroup capi
*/
SFCGAL_API int
sfcgal_geometry_is_complexity_detail(const sfcgal_geometry_t *geom,
char **complexity_reason);

/**
* Tests if the given geometry is 3D or not
* @ingroup capi
Expand Down
Loading

0 comments on commit c1d2ac1

Please sign in to comment.