feat: GMP 精确算术后端 + 可插拔谓词接口
新增: - include/vde/foundation/exact_predicates_gmp.h: GMP 精确谓词 - orient_2d/3d, in_circle 完全精确(mpq_class 有理数) - 零舍入误差,任意精度 - include/vde/foundation/predicates.h: 统一谓词接口 - Predicates<T> 编译期选择后端 - VDE_USE_GMP=ON → GMP, OFF → 自适应 double - 便捷函数: orient(), in_circle() - cmake/FindGMP.cmake: GMP 查找模块 - CMake: -DVDE_USE_GMP=ON 启用 GMP 精确模式 - AdaptiveDouble 包装器: 统一后端接口 使用: cmake -DVDE_USE_GMP=ON .. # 完全精确模式
This commit is contained in:
@@ -7,6 +7,7 @@ set(CMAKE_CXX_EXTENSIONS OFF)
|
|||||||
option(BUILD_TESTS "Build tests" ON)
|
option(BUILD_TESTS "Build tests" ON)
|
||||||
option(BUILD_EXAMPLES "Build examples" ON)
|
option(BUILD_EXAMPLES "Build examples" ON)
|
||||||
option(ENABLE_SANITIZERS "Enable ASan" OFF)
|
option(ENABLE_SANITIZERS "Enable ASan" OFF)
|
||||||
|
option(VDE_USE_GMP "GMP exact arithmetic" OFF)
|
||||||
option(VDE_BUILD_PYTHON "Python bindings" OFF)
|
option(VDE_BUILD_PYTHON "Python bindings" OFF)
|
||||||
|
|
||||||
add_library(vde_compile_options INTERFACE)
|
add_library(vde_compile_options INTERFACE)
|
||||||
|
|||||||
@@ -0,0 +1,16 @@
|
|||||||
|
find_path(GMP_INCLUDE_DIR gmp.h
|
||||||
|
PATHS /usr/include /usr/include/x86_64-linux-gnu
|
||||||
|
)
|
||||||
|
find_library(GMP_LIBRARY NAMES gmp libgmp
|
||||||
|
PATHS /usr/lib /usr/lib/x86_64-linux-gnu
|
||||||
|
)
|
||||||
|
find_library(GMPXX_LIBRARY NAMES gmpxx libgmpxx
|
||||||
|
PATHS /usr/lib /usr/lib/x86_64-linux-gnu
|
||||||
|
)
|
||||||
|
include(FindPackageHandleStandardArgs)
|
||||||
|
find_package_handle_standard_args(GMP DEFAULT_MSG GMP_LIBRARY GMP_INCLUDE_DIR)
|
||||||
|
if(GMP_FOUND)
|
||||||
|
set(GMP_LIBRARIES ${GMPXX_LIBRARY} ${GMP_LIBRARY})
|
||||||
|
set(GMP_INCLUDE_DIRS ${GMP_INCLUDE_DIR})
|
||||||
|
mark_as_advanced(GMP_INCLUDE_DIR GMP_LIBRARY GMPXX_LIBRARY)
|
||||||
|
endif()
|
||||||
@@ -18,3 +18,20 @@ CircleTest in_circle(const Point2D& a, const Point2D& b,
|
|||||||
const Point2D& c, const Point2D& d);
|
const Point2D& c, const Point2D& d);
|
||||||
|
|
||||||
} // namespace vde::foundation::exact
|
} // namespace vde::foundation::exact
|
||||||
|
|
||||||
|
/// Adaptive double-precision predicates (default backend)
|
||||||
|
namespace vde::foundation {
|
||||||
|
struct AdaptiveDouble {
|
||||||
|
static exact::Orientation orient_2d(const Point2D& a, const Point2D& b, const Point2D& c) {
|
||||||
|
return exact::orient_2d(a, b, c);
|
||||||
|
}
|
||||||
|
static exact::Orientation orient_3d(const Point3D& a, const Point3D& b,
|
||||||
|
const Point3D& c, const Point3D& d) {
|
||||||
|
return exact::orient_3d(a, b, c, d);
|
||||||
|
}
|
||||||
|
static exact::CircleTest in_circle(const Point2D& a, const Point2D& b,
|
||||||
|
const Point2D& c, const Point2D& d) {
|
||||||
|
return exact::in_circle(a, b, c, d);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} // namespace vde::foundation
|
||||||
|
|||||||
@@ -0,0 +1,73 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "vde/foundation/exact_predicates.h"
|
||||||
|
|
||||||
|
#ifdef VDE_USE_GMP
|
||||||
|
#include <gmpxx.h>
|
||||||
|
|
||||||
|
namespace vde::foundation::exact {
|
||||||
|
|
||||||
|
/// GMP-based exact predicates — zero rounding error, arbitrary precision
|
||||||
|
struct GmpExact {
|
||||||
|
using Rational = mpq_class;
|
||||||
|
|
||||||
|
/// orient_2d with exact rational arithmetic
|
||||||
|
static Orientation orient_2d(const Point2D& a, const Point2D& b, const Point2D& c) {
|
||||||
|
Rational ax(a.x()), ay(a.y());
|
||||||
|
Rational bx(b.x()), by(b.y());
|
||||||
|
Rational cx(c.x()), cy(c.y());
|
||||||
|
Rational det = (bx - ax) * (cy - ay) - (by - ay) * (cx - ax);
|
||||||
|
int s = mpq_sgn(det.get_mpq_t());
|
||||||
|
return s > 0 ? Orientation::CounterClockwise :
|
||||||
|
s < 0 ? Orientation::Clockwise : Orientation::Collinear;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// orient_3d with exact rational arithmetic
|
||||||
|
static Orientation orient_3d(const Point3D& a, const Point3D& b,
|
||||||
|
const Point3D& c, const Point3D& d) {
|
||||||
|
Rational ax(a.x()), ay(a.y()), az(a.z());
|
||||||
|
Rational bx(b.x()), by(b.y()), bz(b.z());
|
||||||
|
Rational cx(c.x()), cy(c.y()), cz(c.z());
|
||||||
|
Rational dx(d.x()), dy(d.y()), dz(d.z());
|
||||||
|
|
||||||
|
Rational adx = ax - dx, ady = ay - dy, adz = az - dz;
|
||||||
|
Rational bdx = bx - dx, bdy = by - dy, bdz = bz - dz;
|
||||||
|
Rational cdx = cx - dx, cdy = cy - dy, cdz = cz - dz;
|
||||||
|
|
||||||
|
Rational det = adx * (bdy * cdz - bdz * cdy)
|
||||||
|
+ bdx * (cdy * adz - cdz * ady)
|
||||||
|
+ cdx * (ady * bdz - adz * bdy);
|
||||||
|
|
||||||
|
int s = mpq_sgn(det.get_mpq_t());
|
||||||
|
return s > 0 ? Orientation::CounterClockwise :
|
||||||
|
s < 0 ? Orientation::Clockwise : Orientation::Collinear;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// in_circle with exact rational arithmetic
|
||||||
|
static CircleTest in_circle(const Point2D& a, const Point2D& b,
|
||||||
|
const Point2D& c, const Point2D& d) {
|
||||||
|
Rational ax(a.x()), ay(a.y());
|
||||||
|
Rational bx(b.x()), by(b.y());
|
||||||
|
Rational cx(c.x()), cy(c.y());
|
||||||
|
Rational dx(d.x()), dy(d.y());
|
||||||
|
|
||||||
|
Rational adx = ax - dx, ady = ay - dy;
|
||||||
|
Rational bdx = bx - dx, bdy = by - dy;
|
||||||
|
Rational cdx = cx - dx, cdy = cy - dy;
|
||||||
|
|
||||||
|
Rational ab = adx * bdy - ady * bdx;
|
||||||
|
Rational bc = bdx * cdy - bdy * cdx;
|
||||||
|
Rational ca = cdx * ady - cdy * adx;
|
||||||
|
Rational al = adx * adx + ady * ady;
|
||||||
|
Rational bl = bdx * bdx + bdy * bdy;
|
||||||
|
Rational cl = cdx * cdx + cdy * cdy;
|
||||||
|
|
||||||
|
Rational det = al * bc + bl * ca + cl * ab;
|
||||||
|
int s = mpq_sgn(det.get_mpq_t());
|
||||||
|
return s > 0 ? CircleTest::Inside :
|
||||||
|
s < 0 ? CircleTest::Outside : CircleTest::On;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace vde::foundation::exact
|
||||||
|
|
||||||
|
#endif // VDE_USE_GMP
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "vde/foundation/exact_predicates.h"
|
||||||
|
#ifdef VDE_USE_GMP
|
||||||
|
#include "vde/foundation/exact_predicates_gmp.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace vde::foundation {
|
||||||
|
|
||||||
|
/// Unified predicate interface — selects GMP or adaptive double at compile time
|
||||||
|
struct Predicates {
|
||||||
|
#ifdef VDE_USE_GMP
|
||||||
|
using Backend = exact::GmpExact;
|
||||||
|
#else
|
||||||
|
using Backend = exact::AdaptiveDouble;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static exact::Orientation orient_2d(const Point2D& a, const Point2D& b, const Point2D& c) {
|
||||||
|
return Backend::orient_2d(a, b, c);
|
||||||
|
}
|
||||||
|
static exact::Orientation orient_3d(const Point3D& a, const Point3D& b,
|
||||||
|
const Point3D& c, const Point3D& d) {
|
||||||
|
return Backend::orient_3d(a, b, c, d);
|
||||||
|
}
|
||||||
|
static exact::CircleTest in_circle(const Point2D& a, const Point2D& b,
|
||||||
|
const Point2D& c, const Point2D& d) {
|
||||||
|
return Backend::in_circle(a, b, c, d);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// ── Convenience wrappers (preferred API) ──
|
||||||
|
inline exact::Orientation orient(const Point2D& a, const Point2D& b, const Point2D& c) {
|
||||||
|
return Predicates::orient_2d(a, b, c);
|
||||||
|
}
|
||||||
|
inline exact::Orientation orient(const Point3D& a, const Point3D& b, const Point3D& c, const Point3D& d) {
|
||||||
|
return Predicates::orient_3d(a, b, c, d);
|
||||||
|
}
|
||||||
|
inline exact::CircleTest in_circle(const Point2D& a, const Point2D& b, const Point2D& c, const Point2D& d) {
|
||||||
|
return Predicates::in_circle(a, b, c, d);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace vde::foundation
|
||||||
@@ -174,3 +174,15 @@ target_link_libraries(vde_capi
|
|||||||
PUBLIC vde_brep vde_sketch vde_collision vde_boolean vde_spatial
|
PUBLIC vde_brep vde_sketch vde_collision vde_boolean vde_spatial
|
||||||
PUBLIC vde_mesh vde_curves vde_core vde_foundation vde_compile_options
|
PUBLIC vde_mesh vde_curves vde_core vde_foundation vde_compile_options
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# ── GMP exact predicates (optional) ──
|
||||||
|
if(VDE_USE_GMP)
|
||||||
|
list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake")
|
||||||
|
find_package(GMP REQUIRED)
|
||||||
|
target_compile_definitions(vde_foundation PUBLIC VDE_USE_GMP)
|
||||||
|
target_include_directories(vde_foundation PUBLIC ${GMP_INCLUDE_DIRS})
|
||||||
|
target_link_libraries(vde_foundation PUBLIC ${GMP_LIBRARIES})
|
||||||
|
message(STATUS "GMP exact arithmetic: ${GMP_INCLUDE_DIRS}")
|
||||||
|
else()
|
||||||
|
message(STATUS "GMP disabled (-DVDE_USE_GMP=ON to enable)")
|
||||||
|
endif()
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
#include "vde/foundation/exact_predicates_gmp.h"
|
||||||
|
// GMP exact predicates — header-only with mpq_class
|
||||||
|
// This .cpp exists purely for CMake build verification
|
||||||
|
namespace vde::foundation::exact {
|
||||||
|
// Explicit instantiation marker
|
||||||
|
void __vde_gmp_marker() {}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user