diff --git a/doc/CMakeLists.txt b/doc/CMakeLists.txt index d81683072..9dd03539c 100644 --- a/doc/CMakeLists.txt +++ b/doc/CMakeLists.txt @@ -6,11 +6,11 @@ if(WITH_PYTHON) - find_package(Doxygen) + find_package(Doxygen 1.16.1 QUIET) if(NOT DOXYGEN_FOUND) - message(STATUS "Doxygen not found, not able to generate/install the API documentation.") + message(STATUS "The required version of Doxygen has not been found, the API documentation will not be generated.") else() diff --git a/doc/manual/index.rst b/doc/manual/index.rst index 4820ca06b..3ad3e0a2c 100644 --- a/doc/manual/index.rst +++ b/doc/manual/index.rst @@ -248,6 +248,7 @@ User manual * Geometric contractors * :ref:`sec-ctc-geom-ctcdist` * :ref:`sec-ctc-geom-ctcpolar` + * :ref:`sec-ctc-geom-ctcvisible` * CtcSegment * CtcPolygon * CtcPointCloud @@ -286,6 +287,7 @@ User manual * SepInverse * SepTransform * Geometrical separators + * SepVisible * SepPolarCart or SepCartPolar * SepPolygon * SepEllipse diff --git a/doc/manual/manual/contractors/geometric/ctcnovisible.png b/doc/manual/manual/contractors/geometric/ctcnovisible.png new file mode 100644 index 000000000..16d5457d1 Binary files /dev/null and b/doc/manual/manual/contractors/geometric/ctcnovisible.png differ diff --git a/doc/manual/manual/contractors/geometric/ctcvisible.png b/doc/manual/manual/contractors/geometric/ctcvisible.png new file mode 100644 index 000000000..99a4595ef Binary files /dev/null and b/doc/manual/manual/contractors/geometric/ctcvisible.png differ diff --git a/doc/manual/manual/contractors/geometric/ctcvisible.rst b/doc/manual/manual/contractors/geometric/ctcvisible.rst new file mode 100644 index 000000000..1f56689da --- /dev/null +++ b/doc/manual/manual/contractors/geometric/ctcvisible.rst @@ -0,0 +1,165 @@ +.. _sec-ctc-geom-ctcvisible: + +The CtcVisible and CtcNoVisible contractors +=========================================== + + Main authors: `Quentin Brateau `_ + +The visibility constraint characterizes the set of points :math:`\mathbf{x} \in \mathbb{R}^2` that are visible from an observation point :math:`\mathbf{a}` given an obstacle segment :math:`[\mathbf{e}_1\mathbf{e}_2]`. + +This constraint is based on the work of **Rémy Guyonneau** (see [Guyonneau2013]_). A point :math:`\mathbf{x}` is considered visible if the segment :math:`[\mathbf{a}\mathbf{x}]` does not intersect the obstacle segment :math:`[\mathbf{e}_1\mathbf{e}_2]`. This creates a "shadow cone" originating from :math:`\mathbf{a}`. + +.. image:: visibility.png + :alt: Illustration of the visibility constraint from an observation point [Guyonneau2013]_ + :width: 250px + +.. image:: novisibility.png + :alt: Illustration of the non-visibility constraint from an observation point [Guyonneau2013]_ + :width: 250px + +.. doxygenclass:: codac2::CtcVisible + :project: codac + +.. doxygenclass:: codac2::CtcNoVisible + :project: codac + +.. note:: + Current implementations assume a thin (degenerated) observation point :math:`\mathbf{a}`. Future versions will support visibility from **observation lines**, set defined by **convex polygons**, and from any **set** defined by a Separator. + +Methods +------- + +.. doxygenfunction:: codac2::CtcVisible::contract(IntervalVector&) const + :project: codac + +.. doxygenfunction:: codac2::CtcNoVisible::contract(IntervalVector&) const + :project: codac + + + +Example of use: Characterizing the set of visible and non-visible points from an observation point +-------------------------------------------------------------------------------------------------- + +In this example, we characterize the visible and hidden areas from an observer at the origin :math:`\mathbf{a}=(0,0)^\intercal` facing a wall represented by a segment from :math:`(2, -1)` to :math:`(2, 1)`. + +.. tabs:: + + .. group-tab:: Python + + .. literalinclude:: src.py + :language: py + :start-after: [ctcvisible-beg] + :end-before: [ctcvisible-end] + :dedent: 4 + + .. group-tab:: C++ + + .. literalinclude:: src.cpp + :language: c++ + :start-after: [ctcvisible-beg] + :end-before: [ctcvisible-end] + :dedent: 4 + +.. figure:: ctcvisible.png + :width: 400px + +.. tabs:: + + .. group-tab:: Python + + .. literalinclude:: src.py + :language: py + :start-after: [ctcnovisible-beg] + :end-before: [ctcnovisible-end] + :dedent: 4 + + .. group-tab:: C++ + + .. literalinclude:: src.cpp + :language: c++ + :start-after: [ctcnovisible-beg] + :end-before: [ctcnovisible-end] + :dedent: 4 + + Characterization of the visibility area. The points that are not visible are out of the visibility region and belongs to the blue area. The uncertain region is represented in yellow. + +.. figure:: ctcnovisible.png + :width: 400px + + Characterization of the non-visibility area. The points that are visible are out of the non-visibility region and belongs to the blue area. The uncertain region is represented in yellow. + +Separator on the Visibility Constraint +--------------------------------------- + +The visibility constraint can also be applied as a separator, which allows us to characterize the set of points that are visible or not visible from an observation point. This is particularly useful for applications such as path planning, where one needs to determine the regions that are accessible or hidden from a certain viewpoint. + +.. tabs:: + + .. group-tab:: Python + + .. literalinclude:: src.py + :language: py + :start-after: [sepvisible_list-begin] + :end-before: [sepvisible_list-end] + :dedent: 4 + + .. group-tab:: C++ + + .. literalinclude:: src.cpp + :language: c++ + :start-after: [sepvisible_list-begin] + :end-before: [sepvisible_list-end] + :dedent: 4 + + +.. figure:: sepvisible_segments.png + :width: 400px + + Characterization of the visibility area relative to a list of obstacle segments. The green area is visible from the observation point, while blue area is not visible. The uncertain region is represented in yellow. + +.. tabs:: + + .. group-tab:: Python + + .. literalinclude:: src.py + :language: py + :start-after: [sepvisible_polygon-begin] + :end-before: [sepvisible_polygon-end] + :dedent: 4 + + .. group-tab:: C++ + + .. literalinclude:: src.cpp + :language: c++ + :start-after: [sepvisible_polygon-begin] + :end-before: [sepvisible_polygon-end] + :dedent: 4 + + +.. figure:: sepvisible_polygon.png + :width: 400px + + Characterization of the visibility area relative to a polygon obstacle. The green area is visible from the observation point, while blue area is not visible. The uncertain region is represented in yellow. + +.. note:: + + When implementing visibility over a union of segments, **fake boundaries** are appearing. These occur when the intersection of several visibility contractors creates "uncertain" regions that do not correspond to actual physical visibility limits. This is particularly visible on the two Figures that show a line of yellow boxes in the blue area. For a detailed discussion on handling these in interval analysis, see [Brateau2025]_. + +Related content +--------------- + +.. |visibility-pdf| replace:: **Download the manuscript** +.. _visibility-pdf: https://theses.hal.science/tel-00961501/file/these_remy_guyonneau.pdf + +.. |fake_boundaries-pdf| replace:: **Download the paper** +.. _fake_boundaries-pdf: https://cyber.bibl.u-szeged.hu/index.php/actcybern/article/view/4560 + +.. admonition:: References + + .. [Guyonneau2013] \ R. Guyonneau. *Modélisation et exploitation d'incertitudes pour la robotique mobile à l'aide de l'analyse par intervalles*. PhD Thesis, 2013. |visibility-pdf|_ + + .. [Brateau2025] \ Q. Brateau, et al. *Considering Adjacent Sets for Computing the Visibility Region*. Acta Cybernetica, 2025. |fake_boundaries-pdf|_ + +.. admonition:: Technical documentation + + See the `C++ API documentation of CtcVisible <../../api/html/classcodac2_1_1_ctc_visible.html>`_. \ No newline at end of file diff --git a/doc/manual/manual/contractors/geometric/index.rst b/doc/manual/manual/contractors/geometric/index.rst index d7e815fd8..938c40df6 100644 --- a/doc/manual/manual/contractors/geometric/index.rst +++ b/doc/manual/manual/contractors/geometric/index.rst @@ -5,6 +5,7 @@ Geometric contractors ctcdist.rst ctcpolar.rst + ctcvisible.rst CtcSegment CtcPolygon CtcEllipse diff --git a/doc/manual/manual/contractors/geometric/novisibility.png b/doc/manual/manual/contractors/geometric/novisibility.png new file mode 100644 index 000000000..669ef5b75 Binary files /dev/null and b/doc/manual/manual/contractors/geometric/novisibility.png differ diff --git a/doc/manual/manual/contractors/geometric/sepvisible_polygon.png b/doc/manual/manual/contractors/geometric/sepvisible_polygon.png new file mode 100644 index 000000000..a01be580b Binary files /dev/null and b/doc/manual/manual/contractors/geometric/sepvisible_polygon.png differ diff --git a/doc/manual/manual/contractors/geometric/sepvisible_segments.png b/doc/manual/manual/contractors/geometric/sepvisible_segments.png new file mode 100644 index 000000000..605554415 Binary files /dev/null and b/doc/manual/manual/contractors/geometric/sepvisible_segments.png differ diff --git a/doc/manual/manual/contractors/geometric/src.cpp b/doc/manual/manual/contractors/geometric/src.cpp index ce07ceb43..1eb97ca13 100644 --- a/doc/manual/manual/contractors/geometric/src.cpp +++ b/doc/manual/manual/contractors/geometric/src.cpp @@ -11,6 +11,8 @@ #include #include #include +#include +#include #include #include #include @@ -86,4 +88,59 @@ TEST_CASE("CtcPolar - manual") // x = [1.5, 2.5] ; y = [6.53834, 7.85812] ; rho = [7, 8] ; theta = [1.20558, 1.38218] // [ctcpolar-2-end] } +} + +TEST_CASE("CtcVisible - manual") +{ + { + // [ctcvisible-beg] + Vector a({1, 1}); + Segment s({1, 4}, {3, 2}); + CtcVisible ctc(a, s); + DefaultFigure::pave( + {{-1,6},{-1,6}}, + ctc, + 0.1 + ); + // [ctcvisible-end] + } + + { + // [ctcnovisible-beg] + Vector a({1, 1}); + Segment s({1, 4}, {3, 2}); + CtcNoVisible ctc(a, s); + DefaultFigure::pave( + {{-1,6},{-1,6}}, + ctc, + 0.1 + ); + // [ctcnovisible-end] + } + + { + // [sepvisible_list-begin] + Vector a({1, 1}); + std::vector l = {{{1,4}, {2, 3}}, {{2, 3}, {2.5,1}}, {{4, 0.5}, {3.5, -0.5}}}; + SepVisible sep(a, l); + DefaultFigure::pave( + {{-1,6},{-1,6}}, + sep, + 1e-1 + ); + // [sepvisible_list-end] + } + + { + // [sepvisible_polygon-begin] + Vector a({1, 1}); + Polygon p({{2.5,3}, {2, 2}, {3,1}, {4, 1.5}, {4, 3}}); + SepVisible sep(a, p); + DefaultFigure::pave( + {{-1,6},{-1,6}}, + sep, + 1e-1 + ); + // [sepvisible_polygon-end] + } } \ No newline at end of file diff --git a/doc/manual/manual/contractors/geometric/src.py b/doc/manual/manual/contractors/geometric/src.py index c99ba1bb1..f65d6e925 100644 --- a/doc/manual/manual/contractors/geometric/src.py +++ b/doc/manual/manual/contractors/geometric/src.py @@ -88,5 +88,51 @@ def tests_CtcPolar_manual(test): test.assertTrue(Approx(rho,1e-5) == Interval([7,8])) test.assertTrue(Approx(theta,1e-5) == Interval([1.20558,1.38218])) + def test_CtcVisible(test): + # [ctcvisible-beg] + a = [1, 1] + s = Segment([1, 4], [3, 2]) + ctc = CtcVisible(a, s) + DefaultFigure.pave( + [[-1,6],[-1,6]], + ctc, + 0.1 + ) + # [ctcvisible-end] + + # [ctcnovisible-beg] + a = [1, 1] + s = Segment([1, 4], [3, 2]) + ctc = CtcNoVisible(a, s) + DefaultFigure.pave( + [[-1,6],[-1,6]], + ctc, + 0.1 + ) + # [ctcnovisible-end] + + # [sepvisible_list-begin] + a = [1, 1] + l = [Segment([1,4], [2, 3]), Segment([2, 3], [2.5,1]), Segment([4, 0.5], [3.5, -0.5])] + sep = SepVisible(a, l) + DefaultFigure.pave( + [[-1,6],[-1,6]], + sep, + 0.1 + ) + # [sepvisible_list-end] + + # [sepvisible_polygon-begin] + a = [1, 1] + p = Polygon([[2.5,3], [2, 2], [3,1], [4, 1.5], [4, 3]]) + sep = SepVisible(a, p) + DefaultFigure.pave( + [[-1,6],[-1,6]], + sep, + 0.1 + ) + # [sepvisible_polygon-end] + + if __name__ == '__main__': unittest.main() \ No newline at end of file diff --git a/doc/manual/manual/contractors/geometric/visibility.png b/doc/manual/manual/contractors/geometric/visibility.png new file mode 100644 index 000000000..b1f016dbc Binary files /dev/null and b/doc/manual/manual/contractors/geometric/visibility.png differ diff --git a/doc/manual/manual/contractors/index.rst b/doc/manual/manual/contractors/index.rst index 703b6995c..0267838da 100644 --- a/doc/manual/manual/contractors/index.rst +++ b/doc/manual/manual/contractors/index.rst @@ -9,6 +9,7 @@ Contractors, separators CtcLohner CtcDist CtcPolar + CtcVisible .. What are contractors? .. The Ctc class diff --git a/doc/manual/tuto/cp_robotics/src/lesson_E.py b/doc/manual/tuto/cp_robotics/src/lesson_E.py index 43ff25caa..d1b0e7efb 100644 --- a/doc/manual/tuto/cp_robotics/src/lesson_E.py +++ b/doc/manual/tuto/cp_robotics/src/lesson_E.py @@ -153,6 +153,7 @@ def f(i): # [E-q13-beg] def contractors_list(tube_x12): + global tube_v12 for ti in T: tj = ti - 0.01 pi = tube_x12(tj) diff --git a/examples/14_sympy/CMakeLists.txt b/examples/15_sympy/CMakeLists.txt similarity index 100% rename from examples/14_sympy/CMakeLists.txt rename to examples/15_sympy/CMakeLists.txt diff --git a/examples/14_sympy/main.cpp b/examples/15_sympy/main.cpp similarity index 100% rename from examples/14_sympy/main.cpp rename to examples/15_sympy/main.cpp diff --git a/examples/14_sympy/main.py b/examples/15_sympy/main.py similarity index 100% rename from examples/14_sympy/main.py rename to examples/15_sympy/main.py diff --git a/examples/16_visibility/CMakeLists.txt b/examples/16_visibility/CMakeLists.txt new file mode 100644 index 000000000..cf9ef35ce --- /dev/null +++ b/examples/16_visibility/CMakeLists.txt @@ -0,0 +1,34 @@ +# ================================================================== +# codac / basics example - cmake configuration file +# ================================================================== + + cmake_minimum_required(VERSION 3.5) + project(codac_example LANGUAGES CXX) + + set(CMAKE_CXX_STANDARD 20) + set(CMAKE_CXX_STANDARD_REQUIRED ON) + +# Adding Codac + + # In case you installed Codac in a local directory, you need + # to specify its path with the CMAKE_PREFIX_PATH option. + # set(CMAKE_PREFIX_PATH "~/codac/build_install") + + find_package(CODAC REQUIRED) + message(STATUS "Found Codac version ${CODAC_VERSION}") + +# Initializating Ibex + + ibex_init_common() + +# Compilation + + if(FAST_RELEASE) + add_compile_definitions(FAST_RELEASE) + message(STATUS "You are running Codac in fast release mode. (option -DCMAKE_BUILD_TYPE=Release is required)") + endif() + + add_executable(${PROJECT_NAME} main.cpp) + target_compile_options(${PROJECT_NAME} PUBLIC ${CODAC_CXX_FLAGS}) + target_include_directories(${PROJECT_NAME} SYSTEM PUBLIC ${CODAC_INCLUDE_DIRS}) + target_link_libraries(${PROJECT_NAME} PUBLIC ${CODAC_LIBRARIES}) \ No newline at end of file diff --git a/examples/16_visibility/main.cpp b/examples/16_visibility/main.cpp new file mode 100644 index 000000000..a336e2735 --- /dev/null +++ b/examples/16_visibility/main.cpp @@ -0,0 +1,29 @@ +#include +using namespace codac2; + +int main() +{ + // Observation point and obstacle segments + Vector a({1,1}); + std::vector l = { + {{1,4},{2,3}}, + {{2,3},{2.5,1}}, + {{4,0.5},{3.5,-0.5}} + }; + + // Set up the figure + DefaultFigure::set_axes(axis(0,{-1,6}),axis(1,{-1,6})); + + // Show the observation point and the segments + DefaultFigure::draw_circle(a, 0.05, {{Color::dark_green(),Color::green()},"w:0.025","z:5"}); + + for(const auto& s : l) + DefaultFigure::draw_line(s, {Color::red(),"w:0.05","z:5"}); + + // Paving of the visibility separator + DefaultFigure::pave( + {{-1,6},{-1,6}}, + SepVisible(a,l), + 1e-1 + ); +} \ No newline at end of file diff --git a/examples/16_visibility/main.py b/examples/16_visibility/main.py new file mode 100644 index 000000000..037802665 --- /dev/null +++ b/examples/16_visibility/main.py @@ -0,0 +1,24 @@ +from codac import * + +# Observation point and obstacle segments +a = Vector([1,1]) +l = [ + Segment([[1,4],[2,3]]), + Segment([[2,3],[2.5,1]]), + Segment([[4,0.5],[3.5,-0.5]]) +] + +# Set up the figure +DefaultFigure.set_axes(axis(0,[-1,6]), axis(1,[-1,6])) + +# Show the observation point and the segments +DefaultFigure.draw_circle(a, 0.05, StyleProperties([Color.dark_green(),Color.green()],"w:0.025","z:5")) +for s in l: + DefaultFigure.draw_line(s, StyleProperties(Color.red(),"w:0.05","z:5")) + +# Paving of the visibility separator +DefaultFigure.pave( + [[-1,6],[-1,6]], + SepVisible(a,l), + 1e-1 +) \ No newline at end of file diff --git a/python/src/core/CMakeLists.txt b/python/src/core/CMakeLists.txt index 46181886a..65fe7acf2 100644 --- a/python/src/core/CMakeLists.txt +++ b/python/src/core/CMakeLists.txt @@ -36,6 +36,7 @@ contractors/codac2_py_CtcQInter.cpp contractors/codac2_py_CtcSegment.cpp contractors/codac2_py_CtcUnion.cpp + contractors/codac2_py_CtcVisible.cpp contractors/codac2_py_CtcWrapper.cpp contractors/codac2_py_linear_ctc.cpp @@ -113,6 +114,7 @@ separators/codac2_py_SepQInter.cpp separators/codac2_py_SepTransform.cpp separators/codac2_py_SepUnion.cpp + separators/codac2_py_SepVisible.cpp separators/codac2_py_SepWrapper.cpp tools/codac2_py_Approx.cpp diff --git a/python/src/core/codac2_py_core.cpp b/python/src/core/codac2_py_core.cpp index 8b0cf704f..05f160098 100644 --- a/python/src/core/codac2_py_core.cpp +++ b/python/src/core/codac2_py_core.cpp @@ -55,6 +55,8 @@ void export_CtcProj(py::module& m, py::class_,pyCtcInter void export_CtcQInter(py::module& m, py::class_,pyCtcIntervalVector>& ctc); void export_CtcSegment(py::module& m, py::class_,pyCtcIntervalVector>& ctc); void export_CtcUnion(py::module& m, py::class_,pyCtcIntervalVector>& ctc); +void export_CtcVisible(py::module& m, py::class_,pyCtcIntervalVector>& ctc); +void export_CtcNoVisible(py::module& m, py::class_,pyCtcIntervalVector>& ctc); void export_CtcWrapper(py::module& m, py::class_,pyCtcIntervalVector>& ctc); void export_linear_ctc(py::module& m); @@ -146,6 +148,7 @@ void export_SepProj(py::module& m, py::class_& sep); void export_SepQInter(py::module& m, py::class_& sep); void export_SepTransform(py::module& m, py::class_& sep); void export_SepUnion(py::module& m, py::class_& sep); +void export_SepVisible(py::module& m, py::class_& sep); void export_SepWrapper(py::module& m, py::class_& sep); // tools @@ -209,6 +212,8 @@ PYBIND11_MODULE(_core, m) export_CtcQInter(m, py_ctc_iv); export_CtcSegment(m, py_ctc_iv); export_CtcUnion(m, py_ctc_iv); + export_CtcVisible(m, py_ctc_iv); + export_CtcNoVisible(m, py_ctc_iv); export_CtcWrapper(m, py_ctc_iv); export_linear_ctc(m); @@ -318,6 +323,7 @@ PYBIND11_MODULE(_core, m) export_SepQInter(m,py_sep); export_SepTransform(m,py_sep); export_SepUnion(m,py_sep); + export_SepVisible(m,py_sep); export_SepWrapper(m,py_sep); // tools diff --git a/python/src/core/contractors/codac2_py_Ctc.h b/python/src/core/contractors/codac2_py_Ctc.h index f557eb009..efbdb12f7 100644 --- a/python/src/core/contractors/codac2_py_Ctc.h +++ b/python/src/core/contractors/codac2_py_Ctc.h @@ -14,6 +14,7 @@ #include #include #include +#include #include #include "codac2_py_matlab.h" diff --git a/python/src/core/contractors/codac2_py_CtcAction.cpp b/python/src/core/contractors/codac2_py_CtcAction.cpp index f6ca6dff9..fc71e564e 100644 --- a/python/src/core/contractors/codac2_py_CtcAction.cpp +++ b/python/src/core/contractors/codac2_py_CtcAction.cpp @@ -32,9 +32,8 @@ void export_CtcAction(py::module& m, py::class_,pyCtcInt }), CTCACTION_CTCACTION_CONST_C_REF_CONST_OCTASYM_REF, "c"_a, "a"_a) - - .def(CONTRACT_BOX_METHOD(CtcAction, - VOID_CTCACTION_CONTRACT_INTERVALVECTOR_REF_CONST)) - ; + + CONTRACT_METHODS(exported, CtcAction, + VOID_CTCACTION_CONTRACT_INTERVALVECTOR_REF_CONST) } \ No newline at end of file diff --git a/python/src/core/contractors/codac2_py_CtcCartProd.cpp b/python/src/core/contractors/codac2_py_CtcCartProd.cpp index 909841069..b44663ffd 100644 --- a/python/src/core/contractors/codac2_py_CtcCartProd.cpp +++ b/python/src/core/contractors/codac2_py_CtcCartProd.cpp @@ -84,12 +84,12 @@ void export_CtcCartProd(py::module& m, py::class_,pyCtcI }), CTCCARTPROD_CTCCARTPROD_CONST_C_REF_VARIADIC, "c1"_a, "c2"_a, "c3"_a, "c4"_a, "c5"_a, "c6"_a, "c7"_a, "c8"_a, "c9"_a, "c10"_a) - - .def(CONTRACT_BOX_METHOD(CtcCartProd, - VOID_CTCCARTPROD_CONTRACT_INTERVALVECTOR_REF_CONST)) ; + CONTRACT_METHODS(exported, CtcCartProd, + VOID_CTCCARTPROD_CONTRACT_INTERVALVECTOR_REF_CONST) + m.def("cart_prod_ctc", [](const std::list>>& l) { Collection> c; diff --git a/python/src/core/contractors/codac2_py_CtcConstell.cpp b/python/src/core/contractors/codac2_py_CtcConstell.cpp index ec7757335..816fd2d51 100644 --- a/python/src/core/contractors/codac2_py_CtcConstell.cpp +++ b/python/src/core/contractors/codac2_py_CtcConstell.cpp @@ -28,8 +28,8 @@ void export_CtcConstell(py::module& m, py::class_,pyCtcI .def(py::init&>(), CTCCONSTELL_CTCCONSTELL_CONST_VECTOR_INTERVALVECTOR_REF, "M"_a) - - .def(CONTRACT_BOX_METHOD(CtcConstell, - VOID_CTCCONSTELL_CONTRACT_INTERVALVECTOR_REF_CONST)) ; + + CONTRACT_METHODS(exported, CtcConstell, + VOID_CTCCONSTELL_CONTRACT_INTERVALVECTOR_REF_CONST) } \ No newline at end of file diff --git a/python/src/core/contractors/codac2_py_CtcCross.cpp b/python/src/core/contractors/codac2_py_CtcCross.cpp index 12e464cff..cacaf1b0c 100644 --- a/python/src/core/contractors/codac2_py_CtcCross.cpp +++ b/python/src/core/contractors/codac2_py_CtcCross.cpp @@ -27,19 +27,19 @@ void export_CtcCross(py::module& m, py::class_,pyCtcInte .def(py::init(), CTCCROSS_CTCCROSS_CONST_SEGMENT_REF_CONST_INTERVALVECTOR_REF, "e"_a, "r"_a) - - .def(CONTRACT_BOX_METHOD(CtcCross, - VOID_CTCCROSS_CONTRACT_INTERVALVECTOR_REF_CONST)) ; + CONTRACT_METHODS(exported_cross, CtcCross, + VOID_CTCCROSS_CONTRACT_INTERVALVECTOR_REF_CONST) + py::class_ exported_nocross(m, "CtcNoCross", pyctc, CTCNOCROSS_MAIN); exported_nocross .def(py::init(), CTCNOCROSS_CTCNOCROSS_CONST_SEGMENT_REF_CONST_INTERVALVECTOR_REF, "e"_a, "r"_a) - - .def(CONTRACT_BOX_METHOD(CtcNoCross, - VOID_CTCNOCROSS_CONTRACT_INTERVALVECTOR_REF_CONST)) ; + + CONTRACT_METHODS(exported_nocross, CtcNoCross, + VOID_CTCNOCROSS_CONTRACT_INTERVALVECTOR_REF_CONST) } \ No newline at end of file diff --git a/python/src/core/contractors/codac2_py_CtcCtcBoundary.cpp b/python/src/core/contractors/codac2_py_CtcCtcBoundary.cpp index fdd42b415..3a956312b 100644 --- a/python/src/core/contractors/codac2_py_CtcCtcBoundary.cpp +++ b/python/src/core/contractors/codac2_py_CtcCtcBoundary.cpp @@ -33,9 +33,8 @@ void export_CtcCtcBoundary(py::module& m, py::class_,pyC }), CTCCTCBOUNDARY_CTCCTCBOUNDARY_CONST_C_REF_CONST_FUNCTION_BOOLINTERVAL_CONST_VECTOR_REF__REF, "ctc_boundary"_a, "inside_test"_a) - - .def(CONTRACT_BOX_METHOD(CtcCtcBoundary, - VOID_CTCCTCBOUNDARY_CONTRACT_INTERVALVECTOR_REF_CONST)) - ; + + CONTRACT_METHODS(exported, CtcCtcBoundary, + VOID_CTCCTCBOUNDARY_CONTRACT_INTERVALVECTOR_REF_CONST) } \ No newline at end of file diff --git a/python/src/core/contractors/codac2_py_CtcDist.cpp b/python/src/core/contractors/codac2_py_CtcDist.cpp index 59f11af62..9eb6c890c 100644 --- a/python/src/core/contractors/codac2_py_CtcDist.cpp +++ b/python/src/core/contractors/codac2_py_CtcDist.cpp @@ -28,9 +28,6 @@ void export_CtcDist(py::module& m, py::class_,pyCtcInter .def(py::init<>(), CTCDIST_CTCDIST) - .def(CONTRACT_BOX_METHOD(CtcDist, - VOID_CTCDIST_CONTRACT_INTERVALVECTOR_REF_CONST)) - .def("contract", [](const CtcDist& c, Interval& a1, Interval& a2, Interval& b1, Interval& b2, Interval& d) -> py::tuple @@ -47,4 +44,7 @@ void export_CtcDist(py::module& m, py::class_,pyCtcInter VOID_CTCDIST_CONTRACT_INTERVAL_REF_INTERVAL_REF_INTERVAL_REF_INTERVAL_REF_INTERVAL_REF_CONST, "a1"_a, "a2"_a, "b1"_a, "b2"_a, "d"_a) ; + + CONTRACT_METHODS(exported, CtcDist, + VOID_CTCDIST_CONTRACT_INTERVALVECTOR_REF_CONST) } \ No newline at end of file diff --git a/python/src/core/contractors/codac2_py_CtcEmpty.cpp b/python/src/core/contractors/codac2_py_CtcEmpty.cpp index 832ddd86f..9a5a31c3f 100644 --- a/python/src/core/contractors/codac2_py_CtcEmpty.cpp +++ b/python/src/core/contractors/codac2_py_CtcEmpty.cpp @@ -27,9 +27,8 @@ void export_CtcEmpty(py::module& m, py::class_,pyCtcInte .def(py::init(), CTCEMPTY_CTCEMPTY_INDEX "n"_a) - - .def(CONTRACT_BOX_METHOD(CtcEmpty, - VOID_CTCEMPTY_CONTRACT_INTERVALVECTOR_REF_CONST)) - ; + + CONTRACT_METHODS(exported, CtcEmpty, + VOID_CTCEMPTY_CONTRACT_INTERVALVECTOR_REF_CONST) } \ No newline at end of file diff --git a/python/src/core/contractors/codac2_py_CtcFixpoint.cpp b/python/src/core/contractors/codac2_py_CtcFixpoint.cpp index f1e9551fc..989c4baa7 100644 --- a/python/src/core/contractors/codac2_py_CtcFixpoint.cpp +++ b/python/src/core/contractors/codac2_py_CtcFixpoint.cpp @@ -32,9 +32,8 @@ void export_CtcFixpoint(py::module& m, py::class_,pyCtcI }), CTCFIXPOINT_CTCFIXPOINT_CONST_C_REF_DOUBLE, "c"_a, "ratio"_a=0.1) - - .def(CONTRACT_BOX_METHOD(CtcFixpoint, - VOID_CTCFIXPOINT_CONTRACT_INTERVALVECTOR_REF_CONST)) - ; + + CONTRACT_METHODS(exported, CtcFixpoint, + VOID_CTCFIXPOINT_CONTRACT_INTERVALVECTOR_REF_CONST) } \ No newline at end of file diff --git a/python/src/core/contractors/codac2_py_CtcIdentity.cpp b/python/src/core/contractors/codac2_py_CtcIdentity.cpp index 57e2f327c..85880af9b 100644 --- a/python/src/core/contractors/codac2_py_CtcIdentity.cpp +++ b/python/src/core/contractors/codac2_py_CtcIdentity.cpp @@ -27,9 +27,8 @@ void export_CtcIdentity(py::module& m, py::class_,pyCtcI .def(py::init(), CTCIDENTITY_CTCIDENTITY_INDEX "n"_a) - - .def(CONTRACT_BOX_METHOD(CtcIdentity, - VOID_CTCIDENTITY_CONTRACT_INTERVALVECTOR_REF_CONST)) - ; + + CONTRACT_METHODS(exported, CtcIdentity, + VOID_CTCIDENTITY_CONTRACT_INTERVALVECTOR_REF_CONST) } \ No newline at end of file diff --git a/python/src/core/contractors/codac2_py_CtcInnerOuter.cpp b/python/src/core/contractors/codac2_py_CtcInnerOuter.cpp index d61c77aed..cea45fa07 100644 --- a/python/src/core/contractors/codac2_py_CtcInnerOuter.cpp +++ b/python/src/core/contractors/codac2_py_CtcInnerOuter.cpp @@ -32,12 +32,11 @@ void export_CtcInnerOuter(py::module& m, py::class_,pyCt }), CTCINNER_CTCINNER_CONST_S_REF, "s"_a) - - .def(CONTRACT_BOX_METHOD(CtcInner, - VOID_CTCINNER_CONTRACT_INTERVALVECTOR_REF_CONST)) - ; + CONTRACT_METHODS(exported_inner, CtcInner, + VOID_CTCINNER_CONTRACT_INTERVALVECTOR_REF_CONST) + py::class_ exported_outer(m, "CtcOuter", pyctc, CTCOUTER_MAIN); exported_outer @@ -48,9 +47,8 @@ void export_CtcInnerOuter(py::module& m, py::class_,pyCt }), CTCOUTER_CTCOUTER_CONST_S_REF, "s"_a) - - .def(CONTRACT_BOX_METHOD(CtcOuter, - VOID_CTCOUTER_CONTRACT_INTERVALVECTOR_REF_CONST)) - ; + + CONTRACT_METHODS(exported_outer, CtcOuter, + VOID_CTCOUTER_CONTRACT_INTERVALVECTOR_REF_CONST) } \ No newline at end of file diff --git a/python/src/core/contractors/codac2_py_CtcInter.cpp b/python/src/core/contractors/codac2_py_CtcInter.cpp index c27853069..438f4b45c 100644 --- a/python/src/core/contractors/codac2_py_CtcInter.cpp +++ b/python/src/core/contractors/codac2_py_CtcInter.cpp @@ -50,9 +50,6 @@ void export_CtcInter(py::module& m, py::class_,pyCtcInte .def("nb", &CtcInter::nb, SIZET_CTCINTER_X_NB_CONST) - .def(CONTRACT_BOX_METHOD(CtcInter, - VOID_CTCINTER_X_CONTRACT_X_REF_VARIADIC_CONST)) - .def("__iand__", [](CtcInter& c1, const CtcBase& c2) { c1 &= std::dynamic_pointer_cast>(c2.copy()); @@ -60,4 +57,7 @@ void export_CtcInter(py::module& m, py::class_,pyCtcInte }, CTCINTER_X_VARIADIC_REF_CTCINTER_X_OPERATORINTEREQ_CONST_C_REF) ; + + CONTRACT_METHODS(exported, CtcInter, + VOID_CTCINTER_X_CONTRACT_X_REF_VARIADIC_CONST) } \ No newline at end of file diff --git a/python/src/core/contractors/codac2_py_CtcLazy.cpp b/python/src/core/contractors/codac2_py_CtcLazy.cpp index 45d30f7db..10f25c61e 100644 --- a/python/src/core/contractors/codac2_py_CtcLazy.cpp +++ b/python/src/core/contractors/codac2_py_CtcLazy.cpp @@ -32,9 +32,8 @@ void export_CtcLazy(py::module& m, py::class_,pyCtcInter }), CTCLAZY_CTCLAZY_CONST_C_REF, "c"_a) - - .def(CONTRACT_BOX_METHOD(CtcLazy, - VOID_CTCLAZY_CONTRACT_INTERVALVECTOR_REF_CONST)) - ; + + CONTRACT_METHODS(exported, CtcLazy, + VOID_CTCLAZY_CONTRACT_INTERVALVECTOR_REF_CONST) } \ No newline at end of file diff --git a/python/src/core/contractors/codac2_py_CtcNot.cpp b/python/src/core/contractors/codac2_py_CtcNot.cpp index 162243d90..f7e3ac89e 100644 --- a/python/src/core/contractors/codac2_py_CtcNot.cpp +++ b/python/src/core/contractors/codac2_py_CtcNot.cpp @@ -32,9 +32,8 @@ void export_CtcNot(py::module& m, py::class_,pyCtcInterv }), CTCNOT_CTCNOT_CONST_C_REF, "c"_a) - - .def(CONTRACT_BOX_METHOD(CtcNot, - VOID_CTCNOT_CONTRACT_INTERVALVECTOR_REF_CONST)) - ; + + CONTRACT_METHODS(exported, CtcNot, + VOID_CTCNOT_CONTRACT_INTERVALVECTOR_REF_CONST) } \ No newline at end of file diff --git a/python/src/core/contractors/codac2_py_CtcPointCloud.cpp b/python/src/core/contractors/codac2_py_CtcPointCloud.cpp index 2fb8a5d4d..7aca6d7b1 100644 --- a/python/src/core/contractors/codac2_py_CtcPointCloud.cpp +++ b/python/src/core/contractors/codac2_py_CtcPointCloud.cpp @@ -29,8 +29,8 @@ void export_CtcPointCloud(py::module& m, py::class_,pyCt CTCPOINTCLOUD_CTCPOINTCLOUD_CONST_VECTOR_INTERVALVECTOR_REF, "p"_a) - .def(CONTRACT_BOX_METHOD(CtcPointCloud, - VOID_CTCPOINTCLOUD_CONTRACT_INTERVALVECTOR_REF_CONST)) - ; + + CONTRACT_METHODS(exported, CtcPointCloud, + VOID_CTCPOINTCLOUD_CONTRACT_INTERVALVECTOR_REF_CONST) } \ No newline at end of file diff --git a/python/src/core/contractors/codac2_py_CtcPolar.cpp b/python/src/core/contractors/codac2_py_CtcPolar.cpp index d8755890b..27d432407 100644 --- a/python/src/core/contractors/codac2_py_CtcPolar.cpp +++ b/python/src/core/contractors/codac2_py_CtcPolar.cpp @@ -28,9 +28,6 @@ void export_CtcPolar(py::module& m, py::class_,pyCtcInte .def(py::init<>(), CTCPOLAR_CTCPOLAR) - .def(CONTRACT_BOX_METHOD(CtcPolar, - VOID_CTCPOLAR_CONTRACT_INTERVALVECTOR_REF_CONST)) - .def("contract", [](const CtcPolar& c, Interval& x, Interval& y, Interval& rho, Interval& theta) -> py::tuple @@ -47,4 +44,7 @@ void export_CtcPolar(py::module& m, py::class_,pyCtcInte "x"_a, "y"_a, "rho"_a, "theta"_a) ; + + CONTRACT_METHODS(exported, CtcPolar, + VOID_CTCPOLAR_CONTRACT_INTERVALVECTOR_REF_CONST) } \ No newline at end of file diff --git a/python/src/core/contractors/codac2_py_CtcPolygon.cpp b/python/src/core/contractors/codac2_py_CtcPolygon.cpp index 8ce08d8b2..8f2b5605d 100644 --- a/python/src/core/contractors/codac2_py_CtcPolygon.cpp +++ b/python/src/core/contractors/codac2_py_CtcPolygon.cpp @@ -29,10 +29,10 @@ void export_CtcPolygon(py::module& m, py::class_,pyCtcIn CTCPOLYGON_CTCPOLYGON_CONST_POLYGON_REF, "p"_a) - .def(CONTRACT_BOX_METHOD(CtcPolygon, - VOID_CTCPOLYGON_CONTRACT_INTERVALVECTOR_REF_CONST)) - ; + CONTRACT_METHODS(exported, CtcPolygon, + VOID_CTCPOLYGON_CONTRACT_INTERVALVECTOR_REF_CONST) + py::implicitly_convertible(); } \ No newline at end of file diff --git a/python/src/core/contractors/codac2_py_CtcProj.cpp b/python/src/core/contractors/codac2_py_CtcProj.cpp index 2862ee761..09211c944 100644 --- a/python/src/core/contractors/codac2_py_CtcProj.cpp +++ b/python/src/core/contractors/codac2_py_CtcProj.cpp @@ -45,7 +45,8 @@ void export_CtcProj(py::module& m, py::class_,pyCtcInter VOID_CTCPROJ_CONTRACT_INTERVALVECTOR_REF_DOUBLE_CONST, "x"_a, "eps"_a) - .def(CONTRACT_BOX_METHOD(CtcProj, - VOID_CTCPROJ_CONTRACT_INTERVALVECTOR_REF_CONST)) ; + + CONTRACT_METHODS(exported, CtcProj, + VOID_CTCPROJ_CONTRACT_INTERVALVECTOR_REF_CONST) } \ No newline at end of file diff --git a/python/src/core/contractors/codac2_py_CtcQInter.cpp b/python/src/core/contractors/codac2_py_CtcQInter.cpp index 273e307c7..34f3913d5 100644 --- a/python/src/core/contractors/codac2_py_CtcQInter.cpp +++ b/python/src/core/contractors/codac2_py_CtcQInter.cpp @@ -57,8 +57,8 @@ void export_CtcQInter(py::module& m, py::class_,pyCtcInt .def("nb", &CtcQInter::nb, SIZET_CTCQINTER_NB_CONST) - .def(CONTRACT_BOX_METHOD(CtcQInter, - VOID_CTCQINTER_CONTRACT_INTERVALVECTOR_REF_CONST)) - ; + + CONTRACT_METHODS(exported, CtcQInter, + VOID_CTCQINTER_CONTRACT_INTERVALVECTOR_REF_CONST) } \ No newline at end of file diff --git a/python/src/core/contractors/codac2_py_CtcSegment.cpp b/python/src/core/contractors/codac2_py_CtcSegment.cpp index 605bfd8a5..7136aa224 100644 --- a/python/src/core/contractors/codac2_py_CtcSegment.cpp +++ b/python/src/core/contractors/codac2_py_CtcSegment.cpp @@ -33,8 +33,8 @@ void export_CtcSegment(py::module& m, py::class_,pyCtcIn CTCSEGMENT_CTCSEGMENT_CONST_SEGMENT_REF, "ab"_a) - .def(CONTRACT_BOX_METHOD(CtcSegment, - VOID_CTCSEGMENT_CONTRACT_INTERVALVECTOR_REF_CONST)) - ; + + CONTRACT_METHODS(exported, CtcSegment, + VOID_CTCSEGMENT_CONTRACT_INTERVALVECTOR_REF_CONST) } \ No newline at end of file diff --git a/python/src/core/contractors/codac2_py_CtcUnion.cpp b/python/src/core/contractors/codac2_py_CtcUnion.cpp index 0af26087c..d8b84dfe1 100644 --- a/python/src/core/contractors/codac2_py_CtcUnion.cpp +++ b/python/src/core/contractors/codac2_py_CtcUnion.cpp @@ -50,9 +50,6 @@ void export_CtcUnion(py::module& m, py::class_,pyCtcInte .def("nb", &CtcUnion::nb, SIZET_CTCUNION_X_NB_CONST) - .def(CONTRACT_BOX_METHOD(CtcUnion, - VOID_CTCUNION_X_CONTRACT_X_REF_VARIADIC_CONST)) - .def("__ior__", [](CtcUnion& c1, const CtcBase& c2) { c1 |= std::dynamic_pointer_cast>(c2.copy()); @@ -60,4 +57,7 @@ void export_CtcUnion(py::module& m, py::class_,pyCtcInte }, CTCUNION_X_VARIADIC_REF_CTCUNION_X_OPERATORUNIONEQ_CONST_C_REF) ; + + CONTRACT_METHODS(exported, CtcUnion, + VOID_CTCUNION_X_CONTRACT_X_REF_VARIADIC_CONST) } \ No newline at end of file diff --git a/python/src/core/contractors/codac2_py_CtcVisible.cpp b/python/src/core/contractors/codac2_py_CtcVisible.cpp new file mode 100644 index 000000000..ff0a9e7ba --- /dev/null +++ b/python/src/core/contractors/codac2_py_CtcVisible.cpp @@ -0,0 +1,49 @@ +/** * Codac binding (core) + * ---------------------------------------------------------------------------- + * \date 2026 + * \author Quentin Brateau + * \copyright Copyright 2026 Codac Team + * \license GNU Lesser General Public License (LGPL) + */ +#include +#include +#include +#include "codac2_py_Ctc.h" +#include "codac2_py_CtcVisible_docs.h" // Generated file from Doxygen XML (doxygen2docstring.py): + +using namespace std; +using namespace codac2; +namespace py = pybind11; +using namespace pybind11::literals; + +void export_CtcVisible(py::module& m, py::class_, pyCtcIntervalVector>& pyctc) +{ + py::class_ exported(m, "CtcVisible", pyctc, CTCVISIBLE_MAIN); + exported + .def(py::init(), + CTCVISIBLE_CTCVISIBLE_CONST_INTERVALVECTOR_REF_CONST_SEGMENT_REF, "a"_a, "s"_a) + .def(py::init&>(), + CTCVISIBLE_CTCVISIBLE_CONST_INTERVALVECTOR_REF_CONST_VECTOR_SEGMENT_REF, "a"_a, "l"_a) + .def(py::init(), + CTCVISIBLE_CTCVISIBLE_CONST_INTERVALVECTOR_REF_CONST_POLYGON_REF, "a"_a, "p"_a); + ; + + CONTRACT_METHODS(exported, CtcVisible, + VOID_CTCVISIBLE_CONTRACT_INTERVALVECTOR_REF_CONST) +} + +void export_CtcNoVisible(py::module& m, py::class_, pyCtcIntervalVector>& pyctc) +{ + py::class_ exported(m, "CtcNoVisible", pyctc, CTCNOVISIBLE_MAIN); + exported + .def(py::init(), + CTCNOVISIBLE_CTCNOVISIBLE_CONST_INTERVALVECTOR_REF_CONST_SEGMENT_REF, "a"_a, "s"_a) + .def(py::init&>(), + CTCNOVISIBLE_CTCNOVISIBLE_CONST_INTERVALVECTOR_REF_CONST_VECTOR_SEGMENT_REF, "a"_a, "l"_a) + .def(py::init(), + CTCNOVISIBLE_CTCNOVISIBLE_CONST_INTERVALVECTOR_REF_CONST_POLYGON_REF, "a"_a, "p"_a); + ; + + CONTRACT_METHODS(exported, CtcNoVisible, + VOID_CTCNOVISIBLE_CONTRACT_INTERVALVECTOR_REF_CONST) +} \ No newline at end of file diff --git a/python/src/core/contractors/codac2_py_CtcWrapper.cpp b/python/src/core/contractors/codac2_py_CtcWrapper.cpp index 6d43fe64c..d07306bad 100644 --- a/python/src/core/contractors/codac2_py_CtcWrapper.cpp +++ b/python/src/core/contractors/codac2_py_CtcWrapper.cpp @@ -28,21 +28,21 @@ void export_CtcWrapper(py::module& m, py::class_,pyCtcIn .def(py::init(), CTCWRAPPER_YX_CTCWRAPPER_CONST_Y_REF, "y"_a) - - .def(CONTRACT_BOX_METHOD(CtcWrapper, - VOID_CTCWRAPPER_YX_CONTRACT_X_REF_CONST)) ; + CONTRACT_METHODS(exported_ctcwrapper_intervalvector, CtcWrapper, + VOID_CTCWRAPPER_YX_CONTRACT_X_REF_CONST) + py::class_> exported_ctcwrapper_pavingout(m, "CtcWrapper_PavingOut", pyctc, CTCWRAPPER_MAIN); exported_ctcwrapper_pavingout .def(py::init(), CTCWRAPPER_YX_CTCWRAPPER_CONST_Y_REF, "y"_a) - - .def(CONTRACT_BOX_METHOD(CtcWrapper, - VOID_CTCWRAPPER_YX_CONTRACT_X_REF_CONST)) ; + + CONTRACT_METHODS(exported_ctcwrapper_pavingout, CtcWrapper, + VOID_CTCWRAPPER_YX_CONTRACT_X_REF_CONST) } \ No newline at end of file diff --git a/python/src/core/separators/codac2_py_SepVisible.cpp b/python/src/core/separators/codac2_py_SepVisible.cpp new file mode 100644 index 000000000..6a356e892 --- /dev/null +++ b/python/src/core/separators/codac2_py_SepVisible.cpp @@ -0,0 +1,40 @@ +/** * Codac binding (core) + * ---------------------------------------------------------------------------- + * \date 2026 + * \author Quentin Brateau + * \copyright Copyright 2026 Codac Team + * \license GNU Lesser General Public License (LGPL) + */ + +#include +#include +#include +#include "codac2_py_Sep.h" +#include "codac2_py_SepVisible_docs.h" // Generated file from Doxygen XML (doxygen2docstring.py): + +using namespace std; +using namespace codac2; +namespace py = pybind11; +using namespace pybind11::literals; + +void export_SepVisible(py::module& m, py::class_& pysep) +{ + py::class_ exported(m, "SepVisible", pysep, SEPVISIBLE_MAIN); + exported + .def(py::init(), + "a"_a, "s"_a, + SEPVISIBLE_SEPVISIBLE_CONST_INTERVALVECTOR_REF_CONST_SEGMENT_REF) + + .def(py::init&>(), + "a"_a, "s"_a, + SEPVISIBLE_SEPVISIBLE_CONST_INTERVALVECTOR_REF_CONST_VECTOR_SEGMENT_REF) + + .def(py::init(), + "a"_a, "p"_a, + SEPVISIBLE_SEPVISIBLE_CONST_INTERVALVECTOR_REF_CONST_POLYGON_REF) + + .def("separate", &SepVisible::separate, + "x"_a, + BOXPAIR_SEPVISIBLE_SEPARATE_CONST_INTERVALVECTOR_REF_CONST) + ; +} diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 93516ee29..4f6a00ed6 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -41,6 +41,8 @@ ${CMAKE_CURRENT_SOURCE_DIR}/contractors/codac2_CtcLohner.h ${CMAKE_CURRENT_SOURCE_DIR}/contractors/codac2_CtcNot.h ${CMAKE_CURRENT_SOURCE_DIR}/contractors/codac2_CtcUnion.h + ${CMAKE_CURRENT_SOURCE_DIR}/contractors/codac2_CtcVisible.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/contractors/codac2_CtcVisible.h ${CMAKE_CURRENT_SOURCE_DIR}/contractors/codac2_CtcPointCloud.cpp ${CMAKE_CURRENT_SOURCE_DIR}/contractors/codac2_CtcPointCloud.h ${CMAKE_CURRENT_SOURCE_DIR}/contractors/codac2_CtcPolar.cpp @@ -248,6 +250,7 @@ ${CMAKE_CURRENT_SOURCE_DIR}/separators/codac2_SepTransform.h ${CMAKE_CURRENT_SOURCE_DIR}/separators/codac2_SepUnion.cpp ${CMAKE_CURRENT_SOURCE_DIR}/separators/codac2_SepUnion.h + ${CMAKE_CURRENT_SOURCE_DIR}/separators/codac2_SepVisible.h ${CMAKE_CURRENT_SOURCE_DIR}/separators/codac2_SepWrapper.cpp ${CMAKE_CURRENT_SOURCE_DIR}/separators/codac2_SepWrapper.h diff --git a/src/core/contractors/codac2_CtcVisible.cpp b/src/core/contractors/codac2_CtcVisible.cpp new file mode 100644 index 000000000..a0d2e2fd8 --- /dev/null +++ b/src/core/contractors/codac2_CtcVisible.cpp @@ -0,0 +1,141 @@ +/** + * codac2_CtcVisible.cpp + * ---------------------------------------------------------------------------- + * \date 2026 + * \author Quentin Brateau + * \copyright Copyright 2026 Codac Team + * \license GNU Lesser General Public License (LGPL) + */ + + + /** * codac2_CtcVisible.cpp + */ + +#include "codac2_CtcVisible.h" +#include "codac2_det.h" +#include "codac2_arith_sub.h" +#include "codac2_max.h" +#include "codac2_min.h" +#include "codac2_IntervalVector.h" + +using namespace std; +using namespace codac2; + +namespace { + + // 1. Determinant contraction + void core_det(IntervalVector& x, const IntervalVector& p, const IntervalVector& v, const double sign) { + IntervalVector v_xp = x - p; + IntervalVector v_fixed = v; + DetOp::bwd(sign * Interval(0, oo), v_xp, v_fixed); + x &= v_xp + p; + } + + // 2. AABB Contraction + // require_overlap = true -> CtcNoVisible (Intersection) + // require_overlap = false -> CtcVisible (Union) + void core_aabb(IntervalVector& x, const IntervalVector& a, const IntervalVector& e1, const IntervalVector& e2, bool require_overlap) { + for(int i = 0; i < 2; ++i) { + Interval xi = x[i], ai = Interval(a[i]); + Interval obs_min = min(e1[i], e2[i]), obs_max = max(e1[i], e2[i]); + + Interval i1 = min(ai, xi), i2 = max(i1, obs_min); + Interval i3 = max(ai, xi), i4 = min(i3, obs_max); + Interval i5 = i2 - i4; + + Interval target = require_overlap ? Interval(-oo, 0) : Interval(0, oo); + + if ((i5 &= target).is_empty()) { x.set_empty(); return; } + + i2 &= i5 + i4; i4 &= i2 - i5; + Interval t_max = obs_max, t_min = obs_min, t_a = ai; + MinOp::bwd(i4, i3, t_max); MaxOp::bwd(i3, t_a, xi); + t_a = ai; MaxOp::bwd(i2, i1, t_min); MinOp::bwd(i1, t_a, xi); + x[i] &= xi; + } + } +} + +CtcVisibleBase::CtcVisibleBase(const IntervalVector& a, const std::vector& edges) + : _a(a) +{ + for(const auto& s : edges) + { + VisibilityEdgeData ed; + ed.e1 = IntervalVector(s[0]); + ed.e2 = IntervalVector(s[1]); + ed.v_e2e1 = ed.e2 - ed.e1; + ed.v_ae1 = IntervalVector(_a) - ed.e1; + ed.v_ae2 = IntervalVector(_a) - ed.e2; + ed.s_box = s.box(); + + Interval det_val = (_a[0] - ed.e1[0]) * (ed.v_e2e1[1]) - + (_a[1] - ed.e1[1]) * (ed.v_e2e1[0]); + + if (det_val.lb() > 0) { + ed.k = 1.0; + } else if (det_val.ub() < 0) { + ed.k = -1.0; + } else { + ed.k = 0.0; // Edge is collinear with point 'a' + } + + _edges.push_back(ed); + } +} + +CtcVisible::CtcVisible(const IntervalVector& a, const Segment& s) : Ctc(2), CtcVisibleBase(a,{s}) { + +} + +CtcVisible::CtcVisible(const IntervalVector& a, const std::vector& s) : Ctc(2), CtcVisibleBase(a,s) { + +} + +CtcVisible::CtcVisible(const IntervalVector& a, const Polygon& p) : Ctc(2), CtcVisibleBase(a,p) { + +} + +void CtcVisible::contract(IntervalVector& x) const { + for (const auto& ed : _edges) { + IntervalVector x1(x), x2(x), x3(x), x4(x); + + core_det(x1, ed.e1, ed.v_e2e1, ed.k); + core_det(x2, ed.e1, ed.v_ae1, ed.k); + core_det(x3, ed.e2, ed.v_ae2, -ed.k); + core_aabb(x4, _a, ed.e1, ed.e2, false); // Visible = Disjoint check + + x &= (x1 | x2 | x3 | x4); + if (x.is_empty()) return; + } +} + +// CtcNoVisible implementation + +CtcNoVisible::CtcNoVisible(const IntervalVector& a, const Segment& s) : Ctc(2), CtcVisibleBase(a,{s}) { + +} + +CtcNoVisible::CtcNoVisible(const IntervalVector& a, const std::vector& s) : Ctc(2), CtcVisibleBase(a,s) { + +} + +CtcNoVisible::CtcNoVisible(const IntervalVector& a, const Polygon& p) : Ctc(2), CtcVisibleBase(a,p) { + +} + +void CtcNoVisible::contract(IntervalVector& x) const { + IntervalVector x_total_hidden = IntervalVector::empty(2); + + for (const auto& ed : _edges) { + IntervalVector xi(x); + + core_det(xi, ed.e1, ed.v_e2e1, -ed.k); + core_det(xi, ed.e1, ed.v_ae1, -ed.k); + core_det(xi, ed.e2, ed.v_ae2, ed.k); + core_aabb(xi, _a, ed.e1, ed.e2, true); // NoVisible = Overlap check + + x_total_hidden |= xi; + } + x &= x_total_hidden; +} diff --git a/src/core/contractors/codac2_CtcVisible.h b/src/core/contractors/codac2_CtcVisible.h new file mode 100644 index 000000000..712f05ca9 --- /dev/null +++ b/src/core/contractors/codac2_CtcVisible.h @@ -0,0 +1,76 @@ +/** + * \file codac2_CtcVisible.h + * ---------------------------------------------------------------------------- + * \date 2026 + * \author Quentin Brateau + * \copyright Copyright 2026 Codac Team + * \license GNU Lesser General Public License (LGPL) + */ + +#pragma once + +#include "codac2_Ctc.h" +#include "codac2_Segment.h" +#include "codac2_Polygon.h" + +namespace codac2 +{ + struct VisibilityEdgeData { + IntervalVector e1, e2; + IntervalVector v_e2e1, v_ae1, v_ae2; + IntervalVector s_box; + double k; + }; + + class CtcVisibleBase + { + protected: + + CtcVisibleBase(const IntervalVector& a, const std::vector& edges); + + const IntervalVector _a; + std::vector _edges; + }; + + class CtcVisible : public Ctc, CtcVisibleBase + { + public: + /** + * \brief Constructor for visibility from point 'a' relative to segment 's'. + */ + CtcVisible(const IntervalVector& a, const Segment& s); + + /** + * \brief Constructor for visibility from point 'a' relative to a list of segments 's'. + */ + CtcVisible(const IntervalVector& a, const std::vector& s); + + /** + * \brief Constructor for visibility from point 'a' relative to polygon 'p'. + */ + CtcVisible(const IntervalVector& a, const Polygon& p); + + void contract(IntervalVector& x) const; + }; + + class CtcNoVisible : public Ctc, CtcVisibleBase + { + public: + /** + * \brief Constructor for non-visibility from point 'a' relative to segment 's'. + */ + CtcNoVisible(const IntervalVector& a, const Segment& s); + + /** + * \brief Constructor for non-visibility from point 'a' relative to a list of segments 's'. + */ + CtcNoVisible(const IntervalVector& a, const std::vector& s); + + /** + * \brief Constructor for non-visibility from point 'a' relative to polygon 'p'. + */ + CtcNoVisible(const IntervalVector& a, const Polygon& p); + + void contract(IntervalVector& x) const; + }; +} \ No newline at end of file diff --git a/src/core/geometry/codac2_Polygon.cpp b/src/core/geometry/codac2_Polygon.cpp index 18ed52161..6d44ddf54 100644 --- a/src/core/geometry/codac2_Polygon.cpp +++ b/src/core/geometry/codac2_Polygon.cpp @@ -95,13 +95,39 @@ namespace codac2 { } Polygon::Polygon(initializer_list edges) - : vector(edges) + : Polygon(std::vector(edges)) { } Polygon::Polygon(const vector& edges) - : vector(edges) + : std::vector( + [&edges]() + { + if(edges.size() <= 1) + return edges; + + size_t ring_begin = 0; + for(size_t i = 0; i+1 < edges.size(); ++i) + { + if(edges[i][1].intersects(edges[i+1][0])) + continue; + + assert_release(edges[i][1].intersects(edges[ring_begin][0]) && + "Polygon: a contour must be closed before starting a new one"); + assert_release(i + 1-ring_begin >= 3 && + "Polygon: each contour must have at least 3 segments"); + + ring_begin = i+1; + } + + assert_release(edges.back()[1].intersects(edges[ring_begin][0]) && + "Polygon: last contour must be closed"); + assert_release(edges.size() - ring_begin >= 3 && + "Polygon: each contour must have at least 3 segments"); + + return edges; + }()) { } - + Polygon::Polygon(const IntervalVector& x) : Polygon([&x]() -> vector { diff --git a/src/core/separators/codac2_SepVisible.h b/src/core/separators/codac2_SepVisible.h new file mode 100644 index 000000000..602b7cc65 --- /dev/null +++ b/src/core/separators/codac2_SepVisible.h @@ -0,0 +1,61 @@ +/** + * \file codac2_SepVisible.h + * ---------------------------------------------------------------------------- + * \date 2026 + * \author Quentin Brateau + * \copyright Copyright 2026 Codac Team + * \license GNU Lesser General Public License (LGPL) + */ + +#pragma once + +#include "codac2_Sep.h" +#include "codac2_CtcVisible.h" + +namespace codac2 { + class SepVisible : public Sep { + public: + /** + * \brief Constructor for visibility separation from point 'a' relative to segment 's'. + */ + SepVisible(const IntervalVector& a, const Segment& s) + : Sep(2), + _ctc_visible(a, s), + _ctc_novisible(a, s) + {} + + /** + * \brief Constructor for visibility separation from point 'a' relative to a list of segments 's'. + */ + SepVisible(const IntervalVector& a, const std::vector& s) + : Sep(2), + _ctc_visible(a, s), + _ctc_novisible(a, s) + {} + + /** + * \brief Constructor for visibility separation from point 'a' relative to polygon 'p'. + */ + SepVisible(const IntervalVector& a, const Polygon& p) + : Sep(2), + _ctc_visible(a, p), + _ctc_novisible(a, p) + {} + + BoxPair separate(const IntervalVector& x) const override { + IntervalVector x_in(x); + IntervalVector x_out(x); + + _ctc_novisible.contract(x_in); + _ctc_visible.contract(x_out); + + assert((x_in | x_out) == x); + + return {x_in, x_out}; + } + + private: + const CtcVisible _ctc_visible; + const CtcNoVisible _ctc_novisible; + }; +} \ No newline at end of file diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index bf4afdc4f..c9ef12a5b 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -41,6 +41,7 @@ list(APPEND SRC_TESTS # listing files without extension core/contractors/codac2_tests_CtcLohner core/contractors/codac2_tests_CtcPolygon core/contractors/codac2_tests_CtcSegment + core/contractors/codac2_tests_CtcVisible core/contractors/codac2_tests_linear_ctc ../doc/manual/manual/contractors/geometric/src ../doc/manual/manual/contractors/analytic/src @@ -93,6 +94,7 @@ list(APPEND SRC_TESTS # listing files without extension core/separators/codac2_tests_SepPolygon core/separators/codac2_tests_SepProj core/separators/codac2_tests_SepTransform + core/separators/codac2_tests_SepVisible core/tools/codac2_tests_Approx core/tools/codac2_tests_serialization diff --git a/tests/core/contractors/codac2_tests_CtcVisible.cpp b/tests/core/contractors/codac2_tests_CtcVisible.cpp new file mode 100644 index 000000000..9a0487a35 --- /dev/null +++ b/tests/core/contractors/codac2_tests_CtcVisible.cpp @@ -0,0 +1,71 @@ +/** * Codac tests - Visibility Contractors + * ---------------------------------------------------------------------------- + * \date 2026 + * \author Quentin Brateau + * \copyright Copyright 2026 Codac Team + * \license GNU Lesser General Public License (LGPL) + */ + +#include +#include +#include + +using namespace codac2; + +TEST_CASE("CtcVisible & CtcNoVisible - Point/Segment visibility") +{ + // Observer at origin, obstacle horizontal from (2, -1) to (2, 1) + Vector a({0.0, 0.0}); + Segment s({2.0, -1.0}, {2.0, 1.0}); + + CtcVisible ctc_vis(a, s); + CtcNoVisible ctc_no_vis(a, s); + + SECTION("Box fully in visible zone (in front of obstacle)") + { + IntervalVector x({{0.5, 1.5}, {-0.5, 0.5}}); + IntervalVector x_orig = x; + + ctc_vis.contract(x); + CHECK(x == x_orig); // Should not be contracted + + ctc_no_vis.contract(x_orig); + CHECK(x_orig.is_empty()); // Cannot be hidden if in front + } + + SECTION("Box fully in hidden zone (shadow)") + { + IntervalVector x({{3.0, 4.0}, {0.1, 0.4}}); + IntervalVector x_orig = x; + + ctc_no_vis.contract(x_orig); + CHECK(x == x_orig); // Fully hidden + + ctc_vis.contract(x); + CHECK(x.is_empty()); // Fully in shadow -> not visible + } + + SECTION("Box behind the observer") + { + IntervalVector x({{-2.0, -1.0}, {-1.0, -0.2}}); + IntervalVector x_orig = x; + + ctc_vis.contract(x); + CHECK(x == x_orig); // Visible (obstacle is far away in the other direction) + + ctc_no_vis.contract(x_orig); + CHECK(x_orig.is_empty()); + } + + SECTION("Box on the side (outside the angular cone)") + { + IntervalVector x({{1.0, 4.0}, {2.5, 4.0}}); // High above the obstacle + IntervalVector x_orig = x; + + ctc_vis.contract(x); + CHECK(x == x_orig); + + ctc_no_vis.contract(x_orig); + CHECK(x_orig.is_empty()); + } +} \ No newline at end of file diff --git a/tests/core/contractors/codac2_tests_CtcVisible.py b/tests/core/contractors/codac2_tests_CtcVisible.py new file mode 100644 index 000000000..c0fcc241a --- /dev/null +++ b/tests/core/contractors/codac2_tests_CtcVisible.py @@ -0,0 +1,65 @@ +#!/usr/bin/env python + +# Codac tests - Visibility +# ---------------------------------------------------------------------------- +# \date 2026 +# \author Quentin Brateau +# \copyright Copyright 2026 Codac Team +# \license GNU Lesser General Public License (LGPL) + +import unittest +from codac import * + +class TestVisibility(unittest.TestCase): + + def test_CtcVisibility(self): + # Observer at origin, obstacle vertical at x=2 from y=-1 to y=1 + a = [0.0, 0.0] + s = Segment([2.0, -1.0], [2.0, 1.0]) + + ctc_vis = CtcVisible(a, s) + ctc_nvis = CtcNoVisible(a, s) + + # 1. Box fully in visible zone (in front of obstacle) + x = IntervalVector([[0.5, 1.5], [-0.5, 0.5]]) + x_orig = IntervalVector(x) + ctc_vis.contract(x) + self.assertTrue(x == x_orig) + + x_test_no = IntervalVector(x_orig) + ctc_nvis.contract(x_test_no) + self.assertTrue(x_test_no.is_empty()) + + # 2. Box fully in hidden zone (shadow) + x = IntervalVector([[3.0, 4.0], [0.1, 0.4]]) + x_orig = IntervalVector(x) + ctc_vis.contract(x) + self.assertTrue(x.is_empty()) + + x_test_no = IntervalVector(x_orig) + ctc_nvis.contract(x_test_no) + self.assertTrue(x_test_no == x_orig) + + # 3. Box behind the observer + x = IntervalVector([[-2.0, -1.0], [-1.0, -0.2]]) + x_orig = IntervalVector(x) + ctc_vis.contract(x) + self.assertTrue(x == x_orig) + + x_test_no = IntervalVector(x_orig) + ctc_nvis.contract(x_test_no) + self.assertTrue(x_test_no.is_empty()) + + # 4. Box on the side (outside the angular cone) + x = IntervalVector([[1.0, 4.0], [2.5, 4.0]]) + x_orig = IntervalVector(x) + ctc_vis.contract(x) + self.assertTrue(x == x_orig) + + x_test_no = IntervalVector(x_orig) + ctc_nvis.contract(x_test_no) + self.assertTrue(x_test_no.is_empty()) + + +if __name__ == '__main__': + unittest.main() \ No newline at end of file diff --git a/tests/core/separators/codac2_tests_SepVisible.cpp b/tests/core/separators/codac2_tests_SepVisible.cpp new file mode 100644 index 000000000..a465252e5 --- /dev/null +++ b/tests/core/separators/codac2_tests_SepVisible.cpp @@ -0,0 +1,40 @@ +/** * Codac tests - Visibility Separator + * ---------------------------------------------------------------------------- + * \date 2026 + * \author Quentin Brateau + * \copyright Copyright 2024 Codac Team + * \license GNU Lesser General Public License (LGPL) + */ + +#include +#include + +using namespace codac2; + +TEST_CASE("SepVisible - Space Partitioning") +{ + Vector a({0.0, 0.0}); + Segment s({1.0, 1.0}, {1.0, -1.0}); // Vertical wall at x=1 + SepVisible sep(a, s); + + SECTION("Consistency check: in | out should cover boundary") + { + IntervalVector x({{0.0, 2.0}, {-2.0, 2.0}}); + BoxPair res = sep.separate(x); + + // The union of the contracted boxes should ideally cover the original box + // (minus the parts definitively removed by contractors) + CHECK(!res.inner.is_empty()); // Some parts are hidden (x > 1) + CHECK(!res.outer.is_empty()); // Some parts are visible (x < 1 or y > cone) + } + + SECTION("Corner case: Box exactly on the observation point") + { + // A point at the source is always visible (or at least not hidden by the obstacle) + IntervalVector x({{0.0, 0.0}, {0.0, 0.0}}); + BoxPair res = sep.separate(x); + + CHECK(res.inner.is_empty()); // Not hidden + CHECK(res.outer == x); // Visible + } +} \ No newline at end of file diff --git a/tests/core/separators/codac2_tests_SepVisible.py b/tests/core/separators/codac2_tests_SepVisible.py new file mode 100644 index 000000000..979f6d5df --- /dev/null +++ b/tests/core/separators/codac2_tests_SepVisible.py @@ -0,0 +1,34 @@ +#!/usr/bin/env python + +# Codac tests - Visibility +# ---------------------------------------------------------------------------- +# \date 2026 +# \author Quentin Brateau +# \copyright Copyright 2026 Codac Team +# \license GNU Lesser General Public License (LGPL) + +import unittest +from codac import * + +class TestVisibility(unittest.TestCase): + + def test_SepVisible(self): + a = [0.0, 0.0] + s = Segment([1.0, 1.0], [1.0, -1.0]) + sep = SepVisible(a, s) + + # 1. Space Partitioning + x = IntervalVector([[0.0, 2.0], [-2.0, 2.0]]) + x_in, x_out = sep.separate(x) + self.assertFalse(x_in.is_empty()) + self.assertFalse(x_out.is_empty()) + + # 2. Box exactly on observation point + x_point = IntervalVector([[0.0, 0.0], [0.0, 0.0]]) + x_in, x_out = sep.separate(x_point) + self.assertTrue(x_in.is_empty()) + self.assertTrue(x_out == x_point) + + +if __name__ == '__main__': + unittest.main() \ No newline at end of file