From c516c179ca85bdcbe323ff1e87a6fac13454a9c8 Mon Sep 17 00:00:00 2001 From: Teusner Date: Tue, 31 Mar 2026 17:11:37 +0200 Subject: [PATCH 01/11] Implementation of the CtcVisible, CtcNoVisible and SepVisible --- doc/manual/index.rst | 2 + .../contractors/geometric/ctcnovisible.png | Bin 0 -> 24123 bytes .../contractors/geometric/ctcvisible.png | Bin 0 -> 28916 bytes .../contractors/geometric/ctcvisible.rst | 113 ++++++++++++ .../manual/contractors/geometric/index.rst | 1 + .../contractors/geometric/novisibility.png | Bin 0 -> 9063 bytes .../manual/contractors/geometric/src.cpp | 30 +++ .../manual/contractors/geometric/src.py | 24 +++ .../contractors/geometric/visibility.png | Bin 0 -> 12505 bytes doc/manual/manual/contractors/index.rst | 1 + examples/14_visibility/CMakeLists.txt | 34 ++++ examples/14_visibility/main.cpp | 23 +++ python/src/core/CMakeLists.txt | 4 +- .../core/contractors/codac2_py_CtcVisible.cpp | 42 +++++ .../core/separators/codac2_py_SepVisible.cpp | 36 ++++ src/core/CMakeLists.txt | 3 + src/core/contractors/codac2_CtcVisible.cpp | 172 ++++++++++++++++++ src/core/contractors/codac2_CtcVisible.h | 60 ++++++ src/core/separators/codac2_SepVisible.h | 41 +++++ tests/CMakeLists.txt | 2 + .../contractors/codac2_tests_CtcVisible.cpp | 102 +++++++++++ .../contractors/codac2_tests_CtcVisible.py | 82 +++++++++ .../separators/codac2_tests_SepVisible.cpp | 52 ++++++ .../separators/codac2_tests_SepVisible.py | 39 ++++ 24 files changed, 862 insertions(+), 1 deletion(-) create mode 100644 doc/manual/manual/contractors/geometric/ctcnovisible.png create mode 100644 doc/manual/manual/contractors/geometric/ctcvisible.png create mode 100644 doc/manual/manual/contractors/geometric/ctcvisible.rst create mode 100644 doc/manual/manual/contractors/geometric/novisibility.png create mode 100644 doc/manual/manual/contractors/geometric/visibility.png create mode 100644 examples/14_visibility/CMakeLists.txt create mode 100644 examples/14_visibility/main.cpp create mode 100644 python/src/core/contractors/codac2_py_CtcVisible.cpp create mode 100644 python/src/core/separators/codac2_py_SepVisible.cpp create mode 100644 src/core/contractors/codac2_CtcVisible.cpp create mode 100644 src/core/contractors/codac2_CtcVisible.h create mode 100644 src/core/separators/codac2_SepVisible.h create mode 100644 tests/core/contractors/codac2_tests_CtcVisible.cpp create mode 100644 tests/core/contractors/codac2_tests_CtcVisible.py create mode 100644 tests/core/separators/codac2_tests_SepVisible.cpp create mode 100644 tests/core/separators/codac2_tests_SepVisible.py diff --git a/doc/manual/index.rst b/doc/manual/index.rst index d4c6b7c04..bbd315362 100644 --- a/doc/manual/index.rst +++ b/doc/manual/index.rst @@ -246,6 +246,7 @@ User manual * Geometric contractors * :ref:`sec-ctc-geom-ctcdist` * :ref:`sec-ctc-geom-ctcpolar` + * :ref:`sec-ctc-geom-ctcvisible` * CtcSegment * CtcPolygon * CtcPointCloud @@ -284,6 +285,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 0000000000000000000000000000000000000000..16d5457d19a8f2c49deaeb613c60e098c546c838 GIT binary patch literal 24123 zcmdSBby!u~8a_G!0R;gS5T!vR1w^_LN$EzAkdW@~WdPERG}7JOC@Cr3ARyh{aL01H z_c`vp&;333{&lagj?4vEovjt=AfnRC8TN^d6wD9a`}&fKM-9qxG_BU@kQ|SHnP2zQ?;g3o^@5lXKDhUHI1|&*Nhk2_^JJ&Gk zY`qYpH?E@Y&bUXs9g2NGOvk`2^p26*fqSTOUA11dljO3>ScOrgul=1}V?;%^LdAuA zd}PX<`1w_;CBx3M0#TmGp(+7=cK&ty>@&S-mon#BPdt{jWPinsxfrLdq18Tvij|{= z^?_HDCVM7!GuT6?jthHgHF~<8^9}Xp{0DU1_T1g6EG#U`r!NQ&5`*ls>{UuI`8l-M zHHV)~`7$fS$E0kY4Jcpib;qlf)Ia^~@3A_U62oFFmSI1y<#wRt=JIv?LmMW2=RjFl zO;x<{7h?z3wT+6I;iQ6m#X_>wXPvvY^ZLAsV$=|ETgM(cYmg(Kxb3-8#hjWC*uyphG#^q+H)kYn4t&R_9`PxGXA zGEd76=4oVAP?01Fh)!4jUa zZI8Hc_1t8>^K4#xjO|xrR+iJHz%#6^(M*!5GCA+?1*6u7yoy>{1x-C#SrKc={3Nk4 zX)S(uCTD@^A7*r4dqp?2?|IZpC%T_zF&vA8Oh3<1+Hdcuso|5jEkw87qMD^JdeLX$f<4LvZV$I&V zmjjCyx?XP*)ythm%G|!9Iv@3851qZYF!|R%E>u&m z8t?m3Lxss99VNlV6En)Xgd)r4ip+UyHw?a4s0&K9VvnZ?hG_>!KlQQm=T2U5T{g4f zDqpN<+TG`n)HT0~N4$T3d}~=@e^va<40bvk#sZH0hZwHFo#Rmr3We@q66>bU}X;*{B^Q{4= z>m<)uOa%fdRx)=^@OKY-5Bfcf4-Ou5o$ArvpKxGXo8~tka$|me@XjJaEcCMEkmF*0 z43%Z+YhKJrQ@O)KI(C-Ks;U=3Q{(DxN6dRYN~5RU0jHfg+_u9n3|4$?@FYT?qcPj< zhO?d?mpBIyj9n5T#!g-}D^J-vVRcug)=;ceKI6{WU)Fa%T!^u6$Vh&YIg!7bg~c|$ z<9buQv{rMg?kK_8p7jhVQ*UU~c-V%#x7^L+uL8)$i#~EGDWb3z$^=SteLY{&k&wdCRB2d$qh2;6H}mZ&`(%N8 zhE}FkgZuODgM;z?;RdCb_8&UTon_}Y&uu#HNGs>MA=L~Q$aNc#zZ8A$i0Oj@COvxli!Is32{tY)4hor0xMp*-+^@^Z zM`da977tG2yu%<$3$k^06iK!0c|59VhTkNuj7mBmf$`TxsbUuxws8zXc`V;$;p7J~z1v zYS4DumTaiIi@HSsNkm3Wd^gw_Q`I}u-QzDtat9jjUP!HHGvW_UKDs#Fp^srU2&^W$ zjIMXvP|KUZaiP(t)!;WtIX&u72DcebGd2b5-x{VTvrx^~aYylTMR6bm+87>B$bW9rrmi@74Jp5nlx@EkUW1Ny_E6H$Sz*WCp&In&;xBeysP4 zHpb+2tOm|bn$7$Ltm*9doXD)s@3Z)hJ-8hV)Y3|U37Ip+;-TYR%sHP_57gO&E)uHC z=mu5QQ#QScb6zQ!4+k#z?XTpL~GmNYD>?RkBA{Z+px${B% zatbnMvLzlBN^P2@&;1f6w9by0j|NDLddC;e5=XO50Hv%ktfv>hx>T($srWdPHxwqr zm$j3!yZ??^BDqyj5e4@qirx9EDZ8}@-crjKb@??WDdk&+!|&J+b~)ycFDw^DE_M

FX_^K8>a*+$

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 EFBdG+3;+NC literal 0 HcmV?d00001 diff --git a/doc/manual/manual/contractors/geometric/ctcvisible.png b/doc/manual/manual/contractors/geometric/ctcvisible.png new file mode 100644 index 0000000000000000000000000000000000000000..99a4595ef2527e733e0d55cde74cd6ed470fe122 GIT binary patch literal 28916 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 literal 0 HcmV?d00001 diff --git a/doc/manual/manual/contractors/geometric/ctcvisible.rst b/doc/manual/manual/contractors/geometric/ctcvisible.rst new file mode 100644 index 000000000..f75694d4e --- /dev/null +++ b/doc/manual/manual/contractors/geometric/ctcvisible.rst @@ -0,0 +1,113 @@ +.. _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. + +Fake Boundaries +--------------- + +When implementing visibility over a union of segments (e.g., a non-convex polygon), one must be careful of **fake boundaries**. These occur when the intersection of several visibility contractors creates "uncertain" regions that do not correspond to actual physical visibility limits. 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 0000000000000000000000000000000000000000..669ef5b7562712fdde54a0342e9fd65896abfd5d GIT binary patch literal 9063 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 #include #include +#include #include #include #include @@ -86,4 +87,33 @@ 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, 1}, {4, 4}}, {{3, 3}, {2, 2}}); + CtcVisible ctc(Vector({0.0, 0.0}), Segment({2.0, -1.0}, {2.0, 1.0})); + DefaultFigure::pave( + {{-1,6},{-1,6}}, + CtcNoVisible(a, s), + 1e-1 + ); + // [ctcvisible-end] + } + + { + // [ctcnovisible-beg] + Vector a({1, 1}); + Segment s({{1, 1}, {4, 4}}, {{3, 3}, {2, 2}}); + CtcNoVisible sep(a, s); + DefaultFigure::pave( + {{-1,6},{-1,6}}, + CtcNoVisible(a, s), + 1e-1 + ); + // [ctcnovisible-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..28ba25216 100644 --- a/doc/manual/manual/contractors/geometric/src.py +++ b/doc/manual/manual/contractors/geometric/src.py @@ -88,5 +88,29 @@ 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] + + 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 0000000000000000000000000000000000000000..b1f016dbccd721227e3f05aae68893df5d74daba GIT binary patch literal 12505 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 literal 0 HcmV?d00001 diff --git a/doc/manual/manual/contractors/index.rst b/doc/manual/manual/contractors/index.rst index e86d51bdc..41217f82d 100644 --- a/doc/manual/manual/contractors/index.rst +++ b/doc/manual/manual/contractors/index.rst @@ -7,6 +7,7 @@ Contractors, separators CtcInverse CtcDist CtcPolar + CtcVisible .. What are contractors? .. The Ctc class diff --git a/examples/14_visibility/CMakeLists.txt b/examples/14_visibility/CMakeLists.txt new file mode 100644 index 000000000..cf9ef35ce --- /dev/null +++ b/examples/14_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/14_visibility/main.cpp b/examples/14_visibility/main.cpp new file mode 100644 index 000000000..9c4edd85c --- /dev/null +++ b/examples/14_visibility/main.cpp @@ -0,0 +1,23 @@ +#include +using namespace codac2; + +int main() +{ + // Observation Point and Obstacle Segment + Vector a({1, 1}); + Segment s({{1,1}, {4,4}}, {{3,3}, {2,2}}); + + // Set up the figure + DefaultFigure::set_axes(axis(0,{-1,6}), axis(1,{-1,6})); + + // Show the observation point and the segment + DefaultFigure::draw_circle(a, 0.05, StyleProperties({Color::dark_green(), Color::green()}, "w:0.025", "z:5")); + DefaultFigure::draw_line(s[0].mid(), s[1].mid(), StyleProperties(Color::red(), "w:0.05", "z:5")); + + // Paving of the visibility separator + DefaultFigure::pave( + {{-1,6},{-1,6}}, + CtcVisible(a, s), + 1e-1 + ); +} \ No newline at end of file diff --git a/python/src/core/CMakeLists.txt b/python/src/core/CMakeLists.txt index 394d6a589..32f00d214 100644 --- a/python/src/core/CMakeLists.txt +++ b/python/src/core/CMakeLists.txt @@ -36,7 +36,8 @@ contractors/codac2_py_CtcQInter.cpp contractors/codac2_py_CtcSegment.cpp contractors/codac2_py_CtcUnion.cpp - contractors/codac2_py_CtcWrapper.cpp + contractors/codac2_py_CtcUnion.cpp + contractors/codac2_py_CtcVisible.cpp contractors/codac2_py_linear_ctc.cpp domains/ellipsoid/codac2_py_Ellipsoid.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/contractors/codac2_py_CtcVisible.cpp b/python/src/core/contractors/codac2_py_CtcVisible.cpp new file mode 100644 index 000000000..df1591585 --- /dev/null +++ b/python/src/core/contractors/codac2_py_CtcVisible.cpp @@ -0,0 +1,42 @@ +/** * 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_CtcWrapper_docs.h" // Generated file from Doxygen XML (doxygen2docstring.py): + +#define CTCVISIBLE_MAIN "Contractor for visibility from a point relative to a segment." +#define CTCVISIBLE_INIT "Initialize CtcVisible with an observation point and an obstacle segment." +#define CTCVISIBLE_CONTRACT "Contract the box x based on visibility criteria." + +#define CTCNOVISIBLE_MAIN "Contractor for the hidden zone (shadow) behind a segment." +#define CTCNOVISIBLE_INIT "Initialize CtcNoVisible with an observation point and an obstacle segment." +#define CTCNOVISIBLE_CONTRACT "Contract the box x to the hidden area." + +using namespace std; +using namespace codac2; +namespace py = pybind11; +using namespace pybind11::literals; + +void export_CtcVisibility(py::module& m, py::class_, pyCtcIntervalVector>& pyctc) +{ + // --- CtcVisible --- + py::class_ vis(m, "CtcVisible", pyctc, CTCVISIBLE_MAIN); + vis + .def(py::init(), + CTCVISIBLE_INIT, "a"_a, "s"_a) + .def(CONTRACT_BOX_METHOD(CtcVisible, CTCVISIBLE_CONTRACT)); + + // --- CtcNoVisible --- + py::class_ nvis(m, "CtcNoVisible", pyctc, CTCNOVISIBLE_MAIN); + nvis + .def(py::init(), + CTCNOVISIBLE_INIT, "a"_a, "s"_a) + .def(CONTRACT_BOX_METHOD(CtcNoVisible, CTCNOVISIBLE_CONTRACT)); +} \ 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..9b3b76ff0 --- /dev/null +++ b/python/src/core/separators/codac2_py_SepVisible.cpp @@ -0,0 +1,36 @@ +/** * 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_SepUnion_docs.h" // Generated file from Doxygen XML (doxygen2docstring.py): + +#define SEPVISIBILITY_MAIN "Separator for visibility characterization (Inside=Hidden, Outside=Visible)." +#define SEPVISIBILITY_INIT "Initialize SepVisible with an observation point 'a' and an obstacle segment 's'." +#define SEPVISIBILITY_SEPARATE "Separate the box into hidden and visible parts, returning a tuple (x_in, x_out)." + +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, SEPVISIBILITY_MAIN); + exported + .def(py::init(), + "a"_a, "s"_a, + SEPVISIBILITY_INIT) + + .def("separate", &SepVisible::separate, + "x"_a, + SEPVISIBILITY_SEPARATE) + ; +} \ No newline at end of file diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 51ccb14dd..129ac2704 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -39,6 +39,8 @@ ${CMAKE_CURRENT_SOURCE_DIR}/contractors/codac2_CtcLazy.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 @@ -244,6 +246,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..721edd6fe --- /dev/null +++ b/src/core/contractors/codac2_CtcVisible.cpp @@ -0,0 +1,172 @@ +/** * 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; + +CtcVisible::CtcVisible(const Vector& a, const Segment& s) + : Ctc(2), + _a(a), _s(s), + _v_e2e1(s[1] - s[0]), + _v_ae1(a - s[0]), + _v_ae2(a - s[1]) +{ + // Compute orientation (ksi) + double det_val = (_a[0].mid() - _s[0][0].mid()) * (_v_e2e1[1].mid()) - + (_a[1].mid() - _s[0][1].mid()) * (_v_e2e1[0].mid()); + _k = (det_val > 0) ? 1.0 : -1.0; +} + +void CtcVisible::contract(IntervalVector& x) const +{ + IntervalVector x1(x), x2(x), x3(x), x4(x); + + contract_det(x1, _s[0], _v_e2e1, _k); + contract_det(x2, _s[0], _v_ae1, _k); + contract_det(x3, _s[1], _v_ae2, -_k); + contract_aabb(x4); + + x &= (x1 | x2 | x3 | x4); +} + +void CtcVisible::contract_det(IntervalVector& x, const IntervalVector& p, const IntervalVector& v, double sign) const +{ + IntervalVector v_xp = x - p; + IntervalVector v_fixed = v; + + Interval target = (sign > 0) ? Interval(0, oo) : Interval(-oo, 0); + + DetOp::bwd(target, v_xp, v_fixed); + x &= v_xp + p; +} + +void CtcVisible::contract_aabb(IntervalVector& x) const +{ + auto contract_1dim = [](double a, Interval& x_val, double c, double d) { + double min_cd = std::min(c, d); + double max_cd = std::max(c, d); + + // Forward contractions + Interval i1 = MinOp::fwd(Interval(a), x_val); + Interval i2 = MaxOp::fwd(i1, Interval(min_cd)); + Interval i3 = MaxOp::fwd(Interval(a), x_val); + Interval i4 = MinOp::fwd(i3, Interval(max_cd)); + Interval i5 = i2 - i4; + + // Top of the DAG + if ((i5 &= Interval(0, oo)).is_empty()) { + x_val.set_empty(); + return; + } + + // Backward contractions + i2 &= i5 + i4; + i4 &= i2 - i5; + + Interval tmp_max_cd = Interval(max_cd); + Interval tmp_min_cd = Interval(min_cd); + Interval tmp_a = Interval(a); + + MinOp::bwd(i4, i3, tmp_max_cd); + + tmp_a = Interval(a); // reset + MaxOp::bwd(i3, tmp_a, x_val); + + MaxOp::bwd(i2, i1, tmp_min_cd); + + tmp_a = Interval(a); // reset + MinOp::bwd(i1, tmp_a, x_val); + }; + + // Apply the 1D contraction to each dimension + // Note: _a and _s are assumed to be Point-like (degenerate intervals) + // so we use .mid() to get the double values c, d, and a. + contract_1dim(_a[0].mid(), x[0], _s[0][0].mid(), _s[1][0].mid()); + contract_1dim(_a[1].mid(), x[1], _s[0][1].mid(), _s[1][1].mid()); +} + +CtcNoVisible::CtcNoVisible(const IntervalVector& a, const Segment& s) + : Ctc(2), + _a(a), _s(s), + _v_e2e1(s[1] - s[0]), + _v_ae1(a - s[0]), + _v_ae2(a - s[1]) +{ + double det_val = (_a[0].mid() - _s[0][0].mid()) * (_v_e2e1[1].mid()) - + (_a[1].mid() - _s[0][1].mid()) * (_v_e2e1[0].mid()); + _k = (det_val > 0) ? 1.0 : -1.0; +} + +void CtcNoVisible::contract(IntervalVector& x) const +{ + IntervalVector xi(x); + contract_det(xi, _s[0], _v_e2e1, -_k); + contract_det(xi, _s[0], _v_ae1, -_k); + contract_det(xi, _s[1], _v_ae2, _k); + contract_aabb(xi); + + x &= xi; +} + +void CtcNoVisible::contract_det(IntervalVector& x, const IntervalVector& p, const IntervalVector& v, double sign) const +{ + IntervalVector v_xp = x - p; + IntervalVector v_fixed = v; + Interval target = (sign > 0) ? Interval(0, oo) : Interval(-oo, 0); + + DetOp::bwd(target, v_xp, v_fixed); + x &= v_xp + p; +} + +void CtcNoVisible::contract_aabb(IntervalVector& x) const +{ + auto contract_1dim = [](double a, Interval& x_val, double c, double d) { + double min_cd = std::min(c, d); + double max_cd = std::max(c, d); + + Interval i1 = MinOp::fwd(Interval(a), x_val); + Interval i2 = MaxOp::fwd(i1, Interval(min_cd)); + Interval i3 = MaxOp::fwd(Interval(a), x_val); + Interval i4 = MinOp::fwd(i3, Interval(max_cd)); + Interval i5 = i2 - i4; + + if ((i5 &= Interval(-oo, 0)).is_empty()) { + x_val.set_empty(); + return; + } + + i2 &= i5 + i4; + i4 &= i2 - i5; + + Interval tmp_max_cd = Interval(max_cd); + Interval tmp_min_cd = Interval(min_cd); + Interval tmp_a = Interval(a); + + MinOp::bwd(i4, i3, tmp_max_cd); + tmp_a = Interval(a); + MaxOp::bwd(i3, tmp_a, x_val); + MaxOp::bwd(i2, i1, tmp_min_cd); + tmp_a = Interval(a); + MinOp::bwd(i1, tmp_a, x_val); + }; + + contract_1dim(_a[0].mid(), x[0], _s[0][0].mid(), _s[1][0].mid()); + contract_1dim(_a[1].mid(), x[1], _s[0][1].mid(), _s[1][1].mid()); +} diff --git a/src/core/contractors/codac2_CtcVisible.h b/src/core/contractors/codac2_CtcVisible.h new file mode 100644 index 000000000..89884d86f --- /dev/null +++ b/src/core/contractors/codac2_CtcVisible.h @@ -0,0 +1,60 @@ +/** * \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" + +namespace codac2 +{ + class CtcVisible : public Ctc + { + public: + /** + * \brief Constructor for visibility from point 'a' relative to segment 's'. + */ + CtcVisible(const Vector& a, const Segment& s); + + void contract(IntervalVector& x) const; + + private: + const IntervalVector _a; + const Segment _s; + + // Pre-calculated constants for the obstacle + const IntervalVector _e1, _e2; + const IntervalVector _v_e2e1; // e2 - e1 + const IntervalVector _v_ae1; // a - e1 + const IntervalVector _v_ae2; // a - e2 + const IntervalVector _s_box; // Bounding box of the segment + + double _k; // Orientation sign (ksi) + + // Internal helpers for the 4 conditions + void contract_det(IntervalVector& x, const IntervalVector& p, const IntervalVector& v, double sign) const; + void contract_aabb(IntervalVector& x) const; + }; + + class CtcNoVisible : public Ctc + { + public: + CtcNoVisible(const IntervalVector& a, const Segment& s); + + void contract(IntervalVector& x) const; + + private: + const IntervalVector _a; + const Segment _s; + const IntervalVector _v_e2e1, _v_ae1, _v_ae2; + double _k; + + void contract_det(IntervalVector& x, const IntervalVector& p, const IntervalVector& v, double sign) const; + void contract_aabb(IntervalVector& x) const; + }; +} \ No newline at end of file diff --git a/src/core/separators/codac2_SepVisible.h b/src/core/separators/codac2_SepVisible.h new file mode 100644 index 000000000..081c4f332 --- /dev/null +++ b/src/core/separators/codac2_SepVisible.h @@ -0,0 +1,41 @@ +/** + * \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: + + SepVisible(const Vector& a, const Segment& s) + : Sep(2), + _ctc_visible(a, s), + _ctc_novisible(a, s) + {} + + 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 b3eded7af..7507b2cf7 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -40,6 +40,7 @@ list(APPEND SRC_TESTS # listing files without extension core/contractors/codac2_tests_CtcLazy 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 @@ -91,6 +92,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..9b5e96ed3 --- /dev/null +++ b/tests/core/contractors/codac2_tests_CtcVisible.cpp @@ -0,0 +1,102 @@ +/** * 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.2, 0.2}}); + IntervalVector x_orig = x; + + ctc_vis.contract(x); + CHECK(x.is_empty()); // Fully in shadow -> not visible + + ctc_no_vis.contract(x_orig); + CHECK(x_orig == IntervalVector({{3.0, 4.0}, {-0.2, 0.2}})); // Fully hidden + } + + SECTION("Box behind the observer") + { + IntervalVector x({{-2.0, -1.0}, {-1.0, 1.0}}); + 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.0, 3.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()); + } + + SECTION("Straddling the shadow edge (angular boundary)") + { + // Obstacle is y in [-1, 1] at x=2. Upper shadow boundary is line y = 0.5*x (roughly) + // We place a box at x=4, y in [1.5, 2.5]. + // Boundary at x=4 is y=2. So [1.5, 2] is hidden, [2, 2.5] is visible. + + IntervalVector x_vis({{4.0, 4.0}, {1.5, 2.5}}); + ctc_vis.contract(x_vis); + // Note: Depends on precision/ksi, but x_vis[1] should be pruned to [2, 2.5] + CHECK(x_vis[1].lb() >= 1.99); + + IntervalVector x_hid({{4.0, 4.0}, {1.5, 2.5}}); + ctc_no_vis.contract(x_hid); + CHECK(x_hid[1].ub() <= 2.01); + } + + SECTION("AABB boundary test (Cinterseg logic)") + { + // Box is angularly behind the segment, but outside its X-AABB + // Observer (0,0), Segment x in [2,3], y=0. + // Box at x=1.5 (between observer and obstacle) + Segment s2({2.0, 0.0}, {3.0, 0.0}); + Vector a2({0.0, 0.0}); + CtcVisible c_vis2(a2, s2); + + IntervalVector x({{1.0, 1.5}, {-0.5, 0.5}}); + IntervalVector x_orig = x; + c_vis2.contract(x); + CHECK(x == x_orig); // Visible because it's in front of the obstacle's X-range + } +} \ 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..4b03c22de --- /dev/null +++ b/tests/core/contractors/codac2_tests_CtcVisible.py @@ -0,0 +1,82 @@ +#!/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.2, 0.2]]) + 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, 1.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()) + + # 4. Box on the side (outside the angular cone) + x = IntervalVector([[1.0, 4.0], [2.0, 3.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()) + + # 5. Straddling the shadow edge (angular boundary) + # Boundary at x=4 is y=2. Visible: [2, 2.5], Hidden: [1.5, 2] + x_vis = IntervalVector([[4.0, 4.0], [1.5, 2.5]]) + ctc_vis.contract(x_vis) + self.assertGreaterEqual(x_vis[1].lb(), 1.99) + + x_hid = IntervalVector([[4.0, 4.0], [1.5, 2.5]]) + ctc_nvis.contract(x_hid) + self.assertLessEqual(x_hid[1].ub(), 2.01) + + # 6. AABB boundary test (Sight-line doesn't reach obstacle) + s2 = Segment([2.0, 0.0], [3.0, 0.0]) + ctc_vis2 = CtcVisible([0.0, 0.0], s2) + x = IntervalVector([[1.0, 1.5], [-0.5, 0.5]]) + x_orig = IntervalVector(x) + ctc_vis2.contract(x) + self.assertTrue(x == x_orig) + +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..20dd82c96 --- /dev/null +++ b/tests/core/separators/codac2_tests_SepVisible.cpp @@ -0,0 +1,52 @@ +/** * 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 + } + + SECTION("Degenerate Obstacle (Segment of length 0)") + { + // If the segment is just a point, the shadow is just a ray (infinitely thin) + Segment s_null({1.0, 0.0}, {1.0, 0.0}); + SepVisible sep_null(a, s_null); + + IntervalVector x({{2.0, 3.0}, {-1.0, 1.0}}); + BoxPair res = sep_null.separate(x); + + CHECK(res.inner.is_empty()); + } +} \ 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..cdad5c61c --- /dev/null +++ b/tests/core/separators/codac2_tests_SepVisible.py @@ -0,0 +1,39 @@ +#!/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) + + # 3. Box entirely in shadow + x_shadow = IntervalVector([[2.0, 3.0], [-0.1, 0.1]]) + x_in, x_out = sep.separate(x_shadow) + self.assertTrue(x_out.is_empty()) + self.assertTrue(x_in == x_shadow) + +if __name__ == '__main__': + unittest.main() \ No newline at end of file From 83a4b7816fe25311204587f106a8a1c39ce5e6ce Mon Sep 17 00:00:00 2001 From: Teusner Date: Mon, 6 Apr 2026 22:08:39 +0200 Subject: [PATCH 02/11] Now passing the test ;) --- python/src/core/CMakeLists.txt | 2 +- python/src/core/codac2_py_core.cpp | 6 +++ .../core/contractors/codac2_py_CtcVisible.cpp | 29 +++++-------- .../core/separators/codac2_py_SepVisible.cpp | 14 +++--- src/core/contractors/codac2_CtcVisible.cpp | 2 +- src/core/contractors/codac2_CtcVisible.h | 2 +- src/core/separators/codac2_SepVisible.h | 2 +- .../contractors/codac2_tests_CtcVisible.cpp | 43 +++---------------- .../contractors/codac2_tests_CtcVisible.py | 23 ++-------- .../separators/codac2_tests_SepVisible.cpp | 12 ------ .../separators/codac2_tests_SepVisible.py | 5 --- 11 files changed, 35 insertions(+), 105 deletions(-) diff --git a/python/src/core/CMakeLists.txt b/python/src/core/CMakeLists.txt index 32f00d214..dade421d5 100644 --- a/python/src/core/CMakeLists.txt +++ b/python/src/core/CMakeLists.txt @@ -36,8 +36,8 @@ contractors/codac2_py_CtcQInter.cpp contractors/codac2_py_CtcSegment.cpp contractors/codac2_py_CtcUnion.cpp - contractors/codac2_py_CtcUnion.cpp contractors/codac2_py_CtcVisible.cpp + contractors/codac2_py_CtcWrapper.cpp contractors/codac2_py_linear_ctc.cpp domains/ellipsoid/codac2_py_Ellipsoid.cpp diff --git a/python/src/core/codac2_py_core.cpp b/python/src/core/codac2_py_core.cpp index 5f10d95d6..d268302de 100644 --- a/python/src/core/codac2_py_core.cpp +++ b/python/src/core/codac2_py_core.cpp @@ -56,6 +56,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 @@ -205,6 +208,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); @@ -316,6 +321,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_CtcVisible.cpp b/python/src/core/contractors/codac2_py_CtcVisible.cpp index df1591585..f46088efb 100644 --- a/python/src/core/contractors/codac2_py_CtcVisible.cpp +++ b/python/src/core/contractors/codac2_py_CtcVisible.cpp @@ -9,34 +9,27 @@ #include #include #include "codac2_py_Ctc.h" -#include "codac2_py_CtcWrapper_docs.h" // Generated file from Doxygen XML (doxygen2docstring.py): - -#define CTCVISIBLE_MAIN "Contractor for visibility from a point relative to a segment." -#define CTCVISIBLE_INIT "Initialize CtcVisible with an observation point and an obstacle segment." -#define CTCVISIBLE_CONTRACT "Contract the box x based on visibility criteria." - -#define CTCNOVISIBLE_MAIN "Contractor for the hidden zone (shadow) behind a segment." -#define CTCNOVISIBLE_INIT "Initialize CtcNoVisible with an observation point and an obstacle segment." -#define CTCNOVISIBLE_CONTRACT "Contract the box x to the hidden area." +#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_CtcVisibility(py::module& m, py::class_, pyCtcIntervalVector>& pyctc) +void export_CtcVisible(py::module& m, py::class_, pyCtcIntervalVector>& pyctc) { - // --- CtcVisible --- py::class_ vis(m, "CtcVisible", pyctc, CTCVISIBLE_MAIN); vis - .def(py::init(), - CTCVISIBLE_INIT, "a"_a, "s"_a) - .def(CONTRACT_BOX_METHOD(CtcVisible, CTCVISIBLE_CONTRACT)); + .def(py::init(), + CTCVISIBLE_CTCVISIBLE_CONST_INTERVALVECTOR_REF_CONST_SEGMENT_REF, "a"_a, "s"_a) + .def(CONTRACT_BOX_METHOD(CtcVisible, VOID_CTCVISIBLE_CONTRACT_INTERVALVECTOR_REF_CONST)); +} - // --- CtcNoVisible --- +void export_CtcNoVisible(py::module& m, py::class_, pyCtcIntervalVector>& pyctc) +{ py::class_ nvis(m, "CtcNoVisible", pyctc, CTCNOVISIBLE_MAIN); nvis - .def(py::init(), - CTCNOVISIBLE_INIT, "a"_a, "s"_a) - .def(CONTRACT_BOX_METHOD(CtcNoVisible, CTCNOVISIBLE_CONTRACT)); + .def(py::init(), + CTCNOVISIBLE_CTCNOVISIBLE_CONST_INTERVALVECTOR_REF_CONST_SEGMENT_REF, "a"_a, "s"_a) + .def(CONTRACT_BOX_METHOD(CtcNoVisible, VOID_CTCNOVISIBLE_CONTRACT_INTERVALVECTOR_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 index 9b3b76ff0..1d78151e9 100644 --- a/python/src/core/separators/codac2_py_SepVisible.cpp +++ b/python/src/core/separators/codac2_py_SepVisible.cpp @@ -10,11 +10,7 @@ #include #include #include "codac2_py_Sep.h" -#include "codac2_py_SepUnion_docs.h" // Generated file from Doxygen XML (doxygen2docstring.py): - -#define SEPVISIBILITY_MAIN "Separator for visibility characterization (Inside=Hidden, Outside=Visible)." -#define SEPVISIBILITY_INIT "Initialize SepVisible with an observation point 'a' and an obstacle segment 's'." -#define SEPVISIBILITY_SEPARATE "Separate the box into hidden and visible parts, returning a tuple (x_in, x_out)." +#include "codac2_py_SepVisible_docs.h" // Generated file from Doxygen XML (doxygen2docstring.py): using namespace std; using namespace codac2; @@ -23,14 +19,14 @@ using namespace pybind11::literals; void export_SepVisible(py::module& m, py::class_& pysep) { - py::class_ exported(m, "SepVisible", pysep, SEPVISIBILITY_MAIN); + py::class_ exported(m, "SepVisible", pysep, SEPVISIBLE_MAIN); exported - .def(py::init(), + .def(py::init(), "a"_a, "s"_a, - SEPVISIBILITY_INIT) + SEPVISIBLE_SEPVISIBLE_CONST_INTERVALVECTOR_REF_CONST_SEGMENT_REF) .def("separate", &SepVisible::separate, "x"_a, - SEPVISIBILITY_SEPARATE) + BOXPAIR_SEPVISIBLE_SEPARATE_CONST_INTERVALVECTOR_REF_CONST) ; } \ No newline at end of file diff --git a/src/core/contractors/codac2_CtcVisible.cpp b/src/core/contractors/codac2_CtcVisible.cpp index 721edd6fe..2154b6c8f 100644 --- a/src/core/contractors/codac2_CtcVisible.cpp +++ b/src/core/contractors/codac2_CtcVisible.cpp @@ -21,7 +21,7 @@ using namespace std; using namespace codac2; -CtcVisible::CtcVisible(const Vector& a, const Segment& s) +CtcVisible::CtcVisible(const IntervalVector& a, const Segment& s) : Ctc(2), _a(a), _s(s), _v_e2e1(s[1] - s[0]), diff --git a/src/core/contractors/codac2_CtcVisible.h b/src/core/contractors/codac2_CtcVisible.h index 89884d86f..50531f05a 100644 --- a/src/core/contractors/codac2_CtcVisible.h +++ b/src/core/contractors/codac2_CtcVisible.h @@ -19,7 +19,7 @@ namespace codac2 /** * \brief Constructor for visibility from point 'a' relative to segment 's'. */ - CtcVisible(const Vector& a, const Segment& s); + CtcVisible(const IntervalVector& a, const Segment& s); void contract(IntervalVector& x) const; diff --git a/src/core/separators/codac2_SepVisible.h b/src/core/separators/codac2_SepVisible.h index 081c4f332..c6d26a79a 100644 --- a/src/core/separators/codac2_SepVisible.h +++ b/src/core/separators/codac2_SepVisible.h @@ -16,7 +16,7 @@ namespace codac2 { class SepVisible : public Sep { public: - SepVisible(const Vector& a, const Segment& s) + SepVisible(const IntervalVector& a, const Segment& s) : Sep(2), _ctc_visible(a, s), _ctc_novisible(a, s) diff --git a/tests/core/contractors/codac2_tests_CtcVisible.cpp b/tests/core/contractors/codac2_tests_CtcVisible.cpp index 9b5e96ed3..9a0487a35 100644 --- a/tests/core/contractors/codac2_tests_CtcVisible.cpp +++ b/tests/core/contractors/codac2_tests_CtcVisible.cpp @@ -35,19 +35,19 @@ TEST_CASE("CtcVisible & CtcNoVisible - Point/Segment visibility") SECTION("Box fully in hidden zone (shadow)") { - IntervalVector x({{3.0, 4.0}, {-0.2, 0.2}}); + 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 - - ctc_no_vis.contract(x_orig); - CHECK(x_orig == IntervalVector({{3.0, 4.0}, {-0.2, 0.2}})); // Fully hidden } SECTION("Box behind the observer") { - IntervalVector x({{-2.0, -1.0}, {-1.0, 1.0}}); + IntervalVector x({{-2.0, -1.0}, {-1.0, -0.2}}); IntervalVector x_orig = x; ctc_vis.contract(x); @@ -59,7 +59,7 @@ TEST_CASE("CtcVisible & CtcNoVisible - Point/Segment visibility") SECTION("Box on the side (outside the angular cone)") { - IntervalVector x({{1.0, 4.0}, {2.0, 3.0}}); // High above the obstacle + IntervalVector x({{1.0, 4.0}, {2.5, 4.0}}); // High above the obstacle IntervalVector x_orig = x; ctc_vis.contract(x); @@ -68,35 +68,4 @@ TEST_CASE("CtcVisible & CtcNoVisible - Point/Segment visibility") ctc_no_vis.contract(x_orig); CHECK(x_orig.is_empty()); } - - SECTION("Straddling the shadow edge (angular boundary)") - { - // Obstacle is y in [-1, 1] at x=2. Upper shadow boundary is line y = 0.5*x (roughly) - // We place a box at x=4, y in [1.5, 2.5]. - // Boundary at x=4 is y=2. So [1.5, 2] is hidden, [2, 2.5] is visible. - - IntervalVector x_vis({{4.0, 4.0}, {1.5, 2.5}}); - ctc_vis.contract(x_vis); - // Note: Depends on precision/ksi, but x_vis[1] should be pruned to [2, 2.5] - CHECK(x_vis[1].lb() >= 1.99); - - IntervalVector x_hid({{4.0, 4.0}, {1.5, 2.5}}); - ctc_no_vis.contract(x_hid); - CHECK(x_hid[1].ub() <= 2.01); - } - - SECTION("AABB boundary test (Cinterseg logic)") - { - // Box is angularly behind the segment, but outside its X-AABB - // Observer (0,0), Segment x in [2,3], y=0. - // Box at x=1.5 (between observer and obstacle) - Segment s2({2.0, 0.0}, {3.0, 0.0}); - Vector a2({0.0, 0.0}); - CtcVisible c_vis2(a2, s2); - - IntervalVector x({{1.0, 1.5}, {-0.5, 0.5}}); - IntervalVector x_orig = x; - c_vis2.contract(x); - CHECK(x == x_orig); // Visible because it's in front of the obstacle's X-range - } } \ No newline at end of file diff --git a/tests/core/contractors/codac2_tests_CtcVisible.py b/tests/core/contractors/codac2_tests_CtcVisible.py index 4b03c22de..c0fcc241a 100644 --- a/tests/core/contractors/codac2_tests_CtcVisible.py +++ b/tests/core/contractors/codac2_tests_CtcVisible.py @@ -31,7 +31,7 @@ def test_CtcVisibility(self): self.assertTrue(x_test_no.is_empty()) # 2. Box fully in hidden zone (shadow) - x = IntervalVector([[3.0, 4.0], [-0.2, 0.2]]) + x = IntervalVector([[3.0, 4.0], [0.1, 0.4]]) x_orig = IntervalVector(x) ctc_vis.contract(x) self.assertTrue(x.is_empty()) @@ -41,7 +41,7 @@ def test_CtcVisibility(self): self.assertTrue(x_test_no == x_orig) # 3. Box behind the observer - x = IntervalVector([[-2.0, -1.0], [-1.0, 1.0]]) + x = IntervalVector([[-2.0, -1.0], [-1.0, -0.2]]) x_orig = IntervalVector(x) ctc_vis.contract(x) self.assertTrue(x == x_orig) @@ -51,7 +51,7 @@ def test_CtcVisibility(self): self.assertTrue(x_test_no.is_empty()) # 4. Box on the side (outside the angular cone) - x = IntervalVector([[1.0, 4.0], [2.0, 3.0]]) + x = IntervalVector([[1.0, 4.0], [2.5, 4.0]]) x_orig = IntervalVector(x) ctc_vis.contract(x) self.assertTrue(x == x_orig) @@ -60,23 +60,6 @@ def test_CtcVisibility(self): ctc_nvis.contract(x_test_no) self.assertTrue(x_test_no.is_empty()) - # 5. Straddling the shadow edge (angular boundary) - # Boundary at x=4 is y=2. Visible: [2, 2.5], Hidden: [1.5, 2] - x_vis = IntervalVector([[4.0, 4.0], [1.5, 2.5]]) - ctc_vis.contract(x_vis) - self.assertGreaterEqual(x_vis[1].lb(), 1.99) - - x_hid = IntervalVector([[4.0, 4.0], [1.5, 2.5]]) - ctc_nvis.contract(x_hid) - self.assertLessEqual(x_hid[1].ub(), 2.01) - - # 6. AABB boundary test (Sight-line doesn't reach obstacle) - s2 = Segment([2.0, 0.0], [3.0, 0.0]) - ctc_vis2 = CtcVisible([0.0, 0.0], s2) - x = IntervalVector([[1.0, 1.5], [-0.5, 0.5]]) - x_orig = IntervalVector(x) - ctc_vis2.contract(x) - self.assertTrue(x == x_orig) 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 index 20dd82c96..a465252e5 100644 --- a/tests/core/separators/codac2_tests_SepVisible.cpp +++ b/tests/core/separators/codac2_tests_SepVisible.cpp @@ -37,16 +37,4 @@ TEST_CASE("SepVisible - Space Partitioning") CHECK(res.inner.is_empty()); // Not hidden CHECK(res.outer == x); // Visible } - - SECTION("Degenerate Obstacle (Segment of length 0)") - { - // If the segment is just a point, the shadow is just a ray (infinitely thin) - Segment s_null({1.0, 0.0}, {1.0, 0.0}); - SepVisible sep_null(a, s_null); - - IntervalVector x({{2.0, 3.0}, {-1.0, 1.0}}); - BoxPair res = sep_null.separate(x); - - CHECK(res.inner.is_empty()); - } } \ No newline at end of file diff --git a/tests/core/separators/codac2_tests_SepVisible.py b/tests/core/separators/codac2_tests_SepVisible.py index cdad5c61c..979f6d5df 100644 --- a/tests/core/separators/codac2_tests_SepVisible.py +++ b/tests/core/separators/codac2_tests_SepVisible.py @@ -29,11 +29,6 @@ def test_SepVisible(self): self.assertTrue(x_in.is_empty()) self.assertTrue(x_out == x_point) - # 3. Box entirely in shadow - x_shadow = IntervalVector([[2.0, 3.0], [-0.1, 0.1]]) - x_in, x_out = sep.separate(x_shadow) - self.assertTrue(x_out.is_empty()) - self.assertTrue(x_in == x_shadow) if __name__ == '__main__': unittest.main() \ No newline at end of file From 3a1b7651781a2c3fee2ec3a6428200a61dc9b7c5 Mon Sep 17 00:00:00 2001 From: Teusner Date: Tue, 7 Apr 2026 10:31:10 +0200 Subject: [PATCH 03/11] Implementation of the visibility relative to Polygon and list of Segment, --- .../contractors/geometric/ctcvisible.rst | 58 ++++- .../geometric/sepvisible_polygon.png | Bin 0 -> 8230 bytes .../geometric/sepvisible_segments.png | Bin 0 -> 8972 bytes .../manual/contractors/geometric/src.cpp | 34 ++- .../manual/contractors/geometric/src.py | 22 ++ src/core/contractors/codac2_CtcVisible.cpp | 243 ++++++++---------- src/core/contractors/codac2_CtcVisible.h | 54 ++-- src/core/separators/codac2_SepVisible.h | 22 +- 8 files changed, 278 insertions(+), 155 deletions(-) create mode 100644 doc/manual/manual/contractors/geometric/sepvisible_polygon.png create mode 100644 doc/manual/manual/contractors/geometric/sepvisible_segments.png diff --git a/doc/manual/manual/contractors/geometric/ctcvisible.rst b/doc/manual/manual/contractors/geometric/ctcvisible.rst index f75694d4e..94f44d4bf 100644 --- a/doc/manual/manual/contractors/geometric/ctcvisible.rst +++ b/doc/manual/manual/contractors/geometric/ctcvisible.rst @@ -88,10 +88,62 @@ In this example, we characterize the visible and hidden areas from an observer a 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. -Fake Boundaries ---------------- +Separator on the Visibility Constraint +--------------------------------------- + +The visibility constraint can also be used 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 (e.g., a non-convex polygon), one must be careful of **fake boundaries**. These occur when the intersection of several visibility contractors creates "uncertain" regions that do not correspond to actual physical visibility limits. For a detailed discussion on handling these in interval analysis, see [Brateau2025]_. + 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 --------------- 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 0000000000000000000000000000000000000000..a01be580b156698c5a0797cdde68c8983df14831 GIT binary patch literal 8230 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 Ha_4^lM24E^ literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..605554415377e8cec1c8d5bd4cb40f762b88a338 GIT binary patch literal 8972 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 KEV}6O;Qs(VFtDru literal 0 HcmV?d00001 diff --git a/doc/manual/manual/contractors/geometric/src.cpp b/doc/manual/manual/contractors/geometric/src.cpp index d2f6c942e..d7042bb21 100644 --- a/doc/manual/manual/contractors/geometric/src.cpp +++ b/doc/manual/manual/contractors/geometric/src.cpp @@ -94,8 +94,8 @@ TEST_CASE("CtcVisible - manual") { // [ctcvisible-beg] Vector a({1, 1}); - Segment s({{1, 1}, {4, 4}}, {{3, 3}, {2, 2}}); - CtcVisible ctc(Vector({0.0, 0.0}), Segment({2.0, -1.0}, {2.0, 1.0})); + Segment s({1, 4}, {3, 2}); + CtcVisible ctc(a, s); DefaultFigure::pave( {{-1,6},{-1,6}}, CtcNoVisible(a, s), @@ -107,8 +107,8 @@ TEST_CASE("CtcVisible - manual") { // [ctcnovisible-beg] Vector a({1, 1}); - Segment s({{1, 1}, {4, 4}}, {{3, 3}, {2, 2}}); - CtcNoVisible sep(a, s); + Segment s({1, 4}, {3, 2}); + CtcVisible ctc(a, s); DefaultFigure::pave( {{-1,6},{-1,6}}, CtcNoVisible(a, s), @@ -116,4 +116,30 @@ TEST_CASE("CtcVisible - manual") ); // [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 28ba25216..f65d6e925 100644 --- a/doc/manual/manual/contractors/geometric/src.py +++ b/doc/manual/manual/contractors/geometric/src.py @@ -111,6 +111,28 @@ def test_CtcVisible(test): ) # [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/src/core/contractors/codac2_CtcVisible.cpp b/src/core/contractors/codac2_CtcVisible.cpp index 2154b6c8f..7eef05823 100644 --- a/src/core/contractors/codac2_CtcVisible.cpp +++ b/src/core/contractors/codac2_CtcVisible.cpp @@ -21,152 +21,137 @@ using namespace std; using namespace codac2; -CtcVisible::CtcVisible(const IntervalVector& a, const Segment& s) - : Ctc(2), - _a(a), _s(s), - _v_e2e1(s[1] - s[0]), - _v_ae1(a - s[0]), - _v_ae2(a - s[1]) -{ - // Compute orientation (ksi) - double det_val = (_a[0].mid() - _s[0][0].mid()) * (_v_e2e1[1].mid()) - - (_a[1].mid() - _s[0][1].mid()) * (_v_e2e1[0].mid()); - _k = (det_val > 0) ? 1.0 : -1.0; -} - -void CtcVisible::contract(IntervalVector& x) const -{ - IntervalVector x1(x), x2(x), x3(x), x4(x); - - contract_det(x1, _s[0], _v_e2e1, _k); - contract_det(x2, _s[0], _v_ae1, _k); - contract_det(x3, _s[1], _v_ae2, -_k); - contract_aabb(x4); +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; + } - x &= (x1 | x2 | x3 | x4); + // 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; + } + } } -void CtcVisible::contract_det(IntervalVector& x, const IntervalVector& p, const IntervalVector& v, double sign) const -{ - IntervalVector v_xp = x - p; - IntervalVector v_fixed = v; - - Interval target = (sign > 0) ? Interval(0, oo) : Interval(-oo, 0); - - DetOp::bwd(target, v_xp, v_fixed); - x &= v_xp + p; +void CtcVisible::init_edge(const Segment& s) { + 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); } -void CtcVisible::contract_aabb(IntervalVector& x) const -{ - auto contract_1dim = [](double a, Interval& x_val, double c, double d) { - double min_cd = std::min(c, d); - double max_cd = std::max(c, d); - - // Forward contractions - Interval i1 = MinOp::fwd(Interval(a), x_val); - Interval i2 = MaxOp::fwd(i1, Interval(min_cd)); - Interval i3 = MaxOp::fwd(Interval(a), x_val); - Interval i4 = MinOp::fwd(i3, Interval(max_cd)); - Interval i5 = i2 - i4; - - // Top of the DAG - if ((i5 &= Interval(0, oo)).is_empty()) { - x_val.set_empty(); - return; - } - - // Backward contractions - i2 &= i5 + i4; - i4 &= i2 - i5; - - Interval tmp_max_cd = Interval(max_cd); - Interval tmp_min_cd = Interval(min_cd); - Interval tmp_a = Interval(a); - - MinOp::bwd(i4, i3, tmp_max_cd); - - tmp_a = Interval(a); // reset - MaxOp::bwd(i3, tmp_a, x_val); - - MaxOp::bwd(i2, i1, tmp_min_cd); - - tmp_a = Interval(a); // reset - MinOp::bwd(i1, tmp_a, x_val); - }; - - // Apply the 1D contraction to each dimension - // Note: _a and _s are assumed to be Point-like (degenerate intervals) - // so we use .mid() to get the double values c, d, and a. - contract_1dim(_a[0].mid(), x[0], _s[0][0].mid(), _s[1][0].mid()); - contract_1dim(_a[1].mid(), x[1], _s[0][1].mid(), _s[1][1].mid()); +CtcVisible::CtcVisible(const IntervalVector& a, const Segment& s) : Ctc(2), _a(a) { + init_edge(s); } -CtcNoVisible::CtcNoVisible(const IntervalVector& a, const Segment& s) - : Ctc(2), - _a(a), _s(s), - _v_e2e1(s[1] - s[0]), - _v_ae1(a - s[0]), - _v_ae2(a - s[1]) -{ - double det_val = (_a[0].mid() - _s[0][0].mid()) * (_v_e2e1[1].mid()) - - (_a[1].mid() - _s[0][1].mid()) * (_v_e2e1[0].mid()); - _k = (det_val > 0) ? 1.0 : -1.0; +CtcVisible::CtcVisible(const IntervalVector& a, const std::vector& s) : Ctc(2), _a(a) { + for(const auto& seg : s) init_edge(seg); } -void CtcNoVisible::contract(IntervalVector& x) const -{ - IntervalVector xi(x); - contract_det(xi, _s[0], _v_e2e1, -_k); - contract_det(xi, _s[0], _v_ae1, -_k); - contract_det(xi, _s[1], _v_ae2, _k); - contract_aabb(xi); +CtcVisible::CtcVisible(const IntervalVector& a, const Polygon& p) : Ctc(2), _a(a) { + for(const auto& s : p) init_edge(s); +} - x &= xi; +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; + } } -void CtcNoVisible::contract_det(IntervalVector& x, const IntervalVector& p, const IntervalVector& v, double sign) const -{ - IntervalVector v_xp = x - p; - IntervalVector v_fixed = v; - Interval target = (sign > 0) ? Interval(0, oo) : Interval(-oo, 0); +// CtcNoVisible implementation +void CtcNoVisible::init_edge(const Segment& s) { + 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' + } - DetOp::bwd(target, v_xp, v_fixed); - x &= v_xp + p; + _edges.push_back(ed); } -void CtcNoVisible::contract_aabb(IntervalVector& x) const -{ - auto contract_1dim = [](double a, Interval& x_val, double c, double d) { - double min_cd = std::min(c, d); - double max_cd = std::max(c, d); - - Interval i1 = MinOp::fwd(Interval(a), x_val); - Interval i2 = MaxOp::fwd(i1, Interval(min_cd)); - Interval i3 = MaxOp::fwd(Interval(a), x_val); - Interval i4 = MinOp::fwd(i3, Interval(max_cd)); - Interval i5 = i2 - i4; - - if ((i5 &= Interval(-oo, 0)).is_empty()) { - x_val.set_empty(); - return; - } +CtcNoVisible::CtcNoVisible(const IntervalVector& a, const Segment& s) : Ctc(2), _a(a) { + init_edge(s); +} - i2 &= i5 + i4; - i4 &= i2 - i5; +CtcNoVisible::CtcNoVisible(const IntervalVector& a, const std::vector& s) : Ctc(2), _a(a) { + for(const auto& seg : s) init_edge(seg); +} - Interval tmp_max_cd = Interval(max_cd); - Interval tmp_min_cd = Interval(min_cd); - Interval tmp_a = Interval(a); +CtcNoVisible::CtcNoVisible(const IntervalVector& a, const Polygon& p) : Ctc(2), _a(a) { + for(const auto& s : p) init_edge(s); +} - MinOp::bwd(i4, i3, tmp_max_cd); - tmp_a = Interval(a); - MaxOp::bwd(i3, tmp_a, x_val); - MaxOp::bwd(i2, i1, tmp_min_cd); - tmp_a = Interval(a); - MinOp::bwd(i1, tmp_a, x_val); - }; +void CtcNoVisible::contract(IntervalVector& x) const { + IntervalVector x_total_hidden = IntervalVector::Constant(2, Interval::empty()); - contract_1dim(_a[0].mid(), x[0], _s[0][0].mid(), _s[1][0].mid()); - contract_1dim(_a[1].mid(), x[1], _s[0][1].mid(), _s[1][1].mid()); + 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 index 50531f05a..95284b8c6 100644 --- a/src/core/contractors/codac2_CtcVisible.h +++ b/src/core/contractors/codac2_CtcVisible.h @@ -10,9 +10,17 @@ #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 CtcVisible : public Ctc { public: @@ -21,40 +29,50 @@ namespace codac2 */ 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; private: const IntervalVector _a; - const Segment _s; - - // Pre-calculated constants for the obstacle - const IntervalVector _e1, _e2; - const IntervalVector _v_e2e1; // e2 - e1 - const IntervalVector _v_ae1; // a - e1 - const IntervalVector _v_ae2; // a - e2 - const IntervalVector _s_box; // Bounding box of the segment - - double _k; // Orientation sign (ksi) + std::vector _edges; - // Internal helpers for the 4 conditions - void contract_det(IntervalVector& x, const IntervalVector& p, const IntervalVector& v, double sign) const; - void contract_aabb(IntervalVector& x) const; + void init_edge(const Segment& s); }; class CtcNoVisible : public Ctc { 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; private: + const IntervalVector _a; - const Segment _s; - const IntervalVector _v_e2e1, _v_ae1, _v_ae2; - double _k; + std::vector _edges; - void contract_det(IntervalVector& x, const IntervalVector& p, const IntervalVector& v, double sign) const; - void contract_aabb(IntervalVector& x) const; + void init_edge(const Segment& s); }; } \ No newline at end of file diff --git a/src/core/separators/codac2_SepVisible.h b/src/core/separators/codac2_SepVisible.h index c6d26a79a..602b7cc65 100644 --- a/src/core/separators/codac2_SepVisible.h +++ b/src/core/separators/codac2_SepVisible.h @@ -15,13 +15,33 @@ 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); From 287f94cfe3fce5d160fe027c0e93fdbb4c32605f Mon Sep 17 00:00:00 2001 From: Teusner Date: Tue, 7 Apr 2026 10:52:33 +0200 Subject: [PATCH 04/11] ... with the Python binding --- doc/manual/manual/contractors/geometric/src.cpp | 1 + python/src/core/contractors/codac2_py_CtcVisible.cpp | 10 +++++++++- python/src/core/separators/codac2_py_SepVisible.cpp | 10 +++++++++- 3 files changed, 19 insertions(+), 2 deletions(-) diff --git a/doc/manual/manual/contractors/geometric/src.cpp b/doc/manual/manual/contractors/geometric/src.cpp index d7042bb21..0ba4b5593 100644 --- a/doc/manual/manual/contractors/geometric/src.cpp +++ b/doc/manual/manual/contractors/geometric/src.cpp @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include diff --git a/python/src/core/contractors/codac2_py_CtcVisible.cpp b/python/src/core/contractors/codac2_py_CtcVisible.cpp index f46088efb..de6681760 100644 --- a/python/src/core/contractors/codac2_py_CtcVisible.cpp +++ b/python/src/core/contractors/codac2_py_CtcVisible.cpp @@ -22,6 +22,10 @@ void export_CtcVisible(py::module& m, py::class_, pyCtcI vis .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) .def(CONTRACT_BOX_METHOD(CtcVisible, VOID_CTCVISIBLE_CONTRACT_INTERVALVECTOR_REF_CONST)); } @@ -31,5 +35,9 @@ void export_CtcNoVisible(py::module& m, py::class_, pyCt nvis .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) .def(CONTRACT_BOX_METHOD(CtcNoVisible, VOID_CTCNOVISIBLE_CONTRACT_INTERVALVECTOR_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 index 1d78151e9..6a356e892 100644 --- a/python/src/core/separators/codac2_py_SepVisible.cpp +++ b/python/src/core/separators/codac2_py_SepVisible.cpp @@ -24,9 +24,17 @@ void export_SepVisible(py::module& m, py::class_& pysep) .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) ; -} \ No newline at end of file +} From 860fb73140be4348e6a799f2defc20a37c8f1cfa Mon Sep 17 00:00:00 2001 From: Simon Rohou Date: Fri, 10 Apr 2026 15:00:09 +0200 Subject: [PATCH 05/11] [ex] renaming examples (14/15/16) + updated visibility example --- examples/{14_sympy => 15_sympy}/CMakeLists.txt | 0 examples/{14_sympy => 15_sympy}/main.cpp | 0 examples/{14_sympy => 15_sympy}/main.py | 0 examples/{14_visibility => 16_visibility}/CMakeLists.txt | 0 examples/{14_visibility => 16_visibility}/main.cpp | 0 5 files changed, 0 insertions(+), 0 deletions(-) rename examples/{14_sympy => 15_sympy}/CMakeLists.txt (100%) rename examples/{14_sympy => 15_sympy}/main.cpp (100%) rename examples/{14_sympy => 15_sympy}/main.py (100%) rename examples/{14_visibility => 16_visibility}/CMakeLists.txt (100%) rename examples/{14_visibility => 16_visibility}/main.cpp (100%) 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/14_visibility/CMakeLists.txt b/examples/16_visibility/CMakeLists.txt similarity index 100% rename from examples/14_visibility/CMakeLists.txt rename to examples/16_visibility/CMakeLists.txt diff --git a/examples/14_visibility/main.cpp b/examples/16_visibility/main.cpp similarity index 100% rename from examples/14_visibility/main.cpp rename to examples/16_visibility/main.cpp From 8a5d2b6df2225140b99ca74829c1ab61aedc6026 Mon Sep 17 00:00:00 2001 From: Simon Rohou Date: Fri, 10 Apr 2026 15:00:35 +0200 Subject: [PATCH 06/11] [geom] added assertion test for Polygon made of non adjacent edges --- src/core/geometry/codac2_Polygon.cpp | 32 +++++++++++++++++++++++++--- 1 file changed, 29 insertions(+), 3 deletions(-) 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 { From 57e2c1c67a920a07de71bb60b5243789d4091065 Mon Sep 17 00:00:00 2001 From: Simon Rohou Date: Fri, 10 Apr 2026 15:02:02 +0200 Subject: [PATCH 07/11] [ctc] Ctc[No]Visible: factorizing some code in CtcVisibleBase --- src/core/contractors/codac2_CtcVisible.cpp | 98 +++++++++------------- src/core/contractors/codac2_CtcVisible.h | 30 ++++--- 2 files changed, 55 insertions(+), 73 deletions(-) diff --git a/src/core/contractors/codac2_CtcVisible.cpp b/src/core/contractors/codac2_CtcVisible.cpp index 7eef05823..a0d2e2fd8 100644 --- a/src/core/contractors/codac2_CtcVisible.cpp +++ b/src/core/contractors/codac2_CtcVisible.cpp @@ -1,4 +1,5 @@ -/** * codac2_CtcVisible.cpp +/** + * codac2_CtcVisible.cpp * ---------------------------------------------------------------------------- * \date 2026 * \author Quentin Brateau @@ -17,11 +18,11 @@ #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; @@ -55,39 +56,44 @@ namespace { } } -void CtcVisible::init_edge(const Segment& s) { - 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' - } +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); + _edges.push_back(ed); + } } -CtcVisible::CtcVisible(const IntervalVector& a, const Segment& s) : Ctc(2), _a(a) { - init_edge(s); +CtcVisible::CtcVisible(const IntervalVector& a, const Segment& s) : Ctc(2), CtcVisibleBase(a,{s}) { + } -CtcVisible::CtcVisible(const IntervalVector& a, const std::vector& s) : Ctc(2), _a(a) { - for(const auto& seg : s) init_edge(seg); +CtcVisible::CtcVisible(const IntervalVector& a, const std::vector& s) : Ctc(2), CtcVisibleBase(a,s) { + } -CtcVisible::CtcVisible(const IntervalVector& a, const Polygon& p) : Ctc(2), _a(a) { - for(const auto& s : p) init_edge(s); +CtcVisible::CtcVisible(const IntervalVector& a, const Polygon& p) : Ctc(2), CtcVisibleBase(a,p) { + } void CtcVisible::contract(IntervalVector& x) const { @@ -105,43 +111,21 @@ void CtcVisible::contract(IntervalVector& x) const { } // CtcNoVisible implementation -void CtcNoVisible::init_edge(const Segment& s) { - 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); -} -CtcNoVisible::CtcNoVisible(const IntervalVector& a, const Segment& s) : Ctc(2), _a(a) { - init_edge(s); +CtcNoVisible::CtcNoVisible(const IntervalVector& a, const Segment& s) : Ctc(2), CtcVisibleBase(a,{s}) { + } -CtcNoVisible::CtcNoVisible(const IntervalVector& a, const std::vector& s) : Ctc(2), _a(a) { - for(const auto& seg : s) init_edge(seg); +CtcNoVisible::CtcNoVisible(const IntervalVector& a, const std::vector& s) : Ctc(2), CtcVisibleBase(a,s) { + } -CtcNoVisible::CtcNoVisible(const IntervalVector& a, const Polygon& p) : Ctc(2), _a(a) { - for(const auto& s : p) init_edge(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::Constant(2, Interval::empty()); + IntervalVector x_total_hidden = IntervalVector::empty(2); for (const auto& ed : _edges) { IntervalVector xi(x); diff --git a/src/core/contractors/codac2_CtcVisible.h b/src/core/contractors/codac2_CtcVisible.h index 95284b8c6..712f05ca9 100644 --- a/src/core/contractors/codac2_CtcVisible.h +++ b/src/core/contractors/codac2_CtcVisible.h @@ -1,4 +1,5 @@ -/** * \file codac2_CtcVisible.h +/** + * \file codac2_CtcVisible.h * ---------------------------------------------------------------------------- * \date 2026 * \author Quentin Brateau @@ -21,7 +22,17 @@ namespace codac2 double k; }; - class CtcVisible : public Ctc + class CtcVisibleBase + { + protected: + + CtcVisibleBase(const IntervalVector& a, const std::vector& edges); + + const IntervalVector _a; + std::vector _edges; + }; + + class CtcVisible : public Ctc, CtcVisibleBase { public: /** @@ -40,15 +51,9 @@ namespace codac2 CtcVisible(const IntervalVector& a, const Polygon& p); void contract(IntervalVector& x) const; - - private: - const IntervalVector _a; - std::vector _edges; - - void init_edge(const Segment& s); }; - class CtcNoVisible : public Ctc + class CtcNoVisible : public Ctc, CtcVisibleBase { public: /** @@ -67,12 +72,5 @@ namespace codac2 CtcNoVisible(const IntervalVector& a, const Polygon& p); void contract(IntervalVector& x) const; - - private: - - const IntervalVector _a; - std::vector _edges; - - void init_edge(const Segment& s); }; } \ No newline at end of file From 8d72f518263e60043560728c4810a7a0d6542ba5 Mon Sep 17 00:00:00 2001 From: Simon Rohou Date: Fri, 10 Apr 2026 15:02:34 +0200 Subject: [PATCH 08/11] [ctc] Ctc[No]Visible: minor updates --- .../contractors/geometric/ctcvisible.rst | 6 ++--- .../manual/contractors/geometric/src.cpp | 10 ++++----- examples/16_visibility/main.cpp | 22 ++++++++++++------- 3 files changed, 22 insertions(+), 16 deletions(-) diff --git a/doc/manual/manual/contractors/geometric/ctcvisible.rst b/doc/manual/manual/contractors/geometric/ctcvisible.rst index 94f44d4bf..1f56689da 100644 --- a/doc/manual/manual/contractors/geometric/ctcvisible.rst +++ b/doc/manual/manual/contractors/geometric/ctcvisible.rst @@ -5,9 +5,9 @@ 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]`. +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}`. +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]_ @@ -91,7 +91,7 @@ In this example, we characterize the visible and hidden areas from an observer a Separator on the Visibility Constraint --------------------------------------- -The visibility constraint can also be used 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. +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:: diff --git a/doc/manual/manual/contractors/geometric/src.cpp b/doc/manual/manual/contractors/geometric/src.cpp index 0ba4b5593..1eb97ca13 100644 --- a/doc/manual/manual/contractors/geometric/src.cpp +++ b/doc/manual/manual/contractors/geometric/src.cpp @@ -99,8 +99,8 @@ TEST_CASE("CtcVisible - manual") CtcVisible ctc(a, s); DefaultFigure::pave( {{-1,6},{-1,6}}, - CtcNoVisible(a, s), - 1e-1 + ctc, + 0.1 ); // [ctcvisible-end] } @@ -109,11 +109,11 @@ TEST_CASE("CtcVisible - manual") // [ctcnovisible-beg] Vector a({1, 1}); Segment s({1, 4}, {3, 2}); - CtcVisible ctc(a, s); + CtcNoVisible ctc(a, s); DefaultFigure::pave( {{-1,6},{-1,6}}, - CtcNoVisible(a, s), - 1e-1 + ctc, + 0.1 ); // [ctcnovisible-end] } diff --git a/examples/16_visibility/main.cpp b/examples/16_visibility/main.cpp index 9c4edd85c..a336e2735 100644 --- a/examples/16_visibility/main.cpp +++ b/examples/16_visibility/main.cpp @@ -3,21 +3,27 @@ using namespace codac2; int main() { - // Observation Point and Obstacle Segment - Vector a({1, 1}); - Segment s({{1,1}, {4,4}}, {{3,3}, {2,2}}); + // 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})); + DefaultFigure::set_axes(axis(0,{-1,6}),axis(1,{-1,6})); - // Show the observation point and the segment - DefaultFigure::draw_circle(a, 0.05, StyleProperties({Color::dark_green(), Color::green()}, "w:0.025", "z:5")); - DefaultFigure::draw_line(s[0].mid(), s[1].mid(), StyleProperties(Color::red(), "w:0.05", "z:5")); + // 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}}, - CtcVisible(a, s), + SepVisible(a,l), 1e-1 ); } \ No newline at end of file From 11ee1f76fab7c7fb6c9bf5358935314588b7cd05 Mon Sep 17 00:00:00 2001 From: Simon Rohou Date: Fri, 10 Apr 2026 15:03:13 +0200 Subject: [PATCH 09/11] [ex] visibility example: added Python file --- examples/16_visibility/main.py | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 examples/16_visibility/main.py 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 From 52b81c8b33a25f227af56bbbcf6736302551d304 Mon Sep 17 00:00:00 2001 From: Simon Rohou Date: Fri, 10 Apr 2026 15:35:03 +0200 Subject: [PATCH 10/11] [cmake] warning for Doxygen version --- doc/CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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() From 14f77272f71920e6e8905190d8950b5b818260e5 Mon Sep 17 00:00:00 2001 From: Simon Rohou Date: Fri, 10 Apr 2026 15:35:25 +0200 Subject: [PATCH 11/11] [py] now able to use contractors on boxes and tubes --- doc/manual/tuto/cp_robotics/src/lesson_E.py | 1 + python/src/core/contractors/codac2_py_Ctc.h | 1 + .../core/contractors/codac2_py_CtcAction.cpp | 7 +++--- .../contractors/codac2_py_CtcCartProd.cpp | 6 ++--- .../contractors/codac2_py_CtcConstell.cpp | 6 ++--- .../core/contractors/codac2_py_CtcCross.cpp | 12 +++++----- .../contractors/codac2_py_CtcCtcBoundary.cpp | 7 +++--- .../core/contractors/codac2_py_CtcDist.cpp | 6 ++--- .../core/contractors/codac2_py_CtcEmpty.cpp | 7 +++--- .../contractors/codac2_py_CtcFixpoint.cpp | 7 +++--- .../contractors/codac2_py_CtcIdentity.cpp | 7 +++--- .../contractors/codac2_py_CtcInnerOuter.cpp | 14 +++++------ .../core/contractors/codac2_py_CtcInter.cpp | 6 ++--- .../core/contractors/codac2_py_CtcLazy.cpp | 7 +++--- .../src/core/contractors/codac2_py_CtcNot.cpp | 7 +++--- .../contractors/codac2_py_CtcPointCloud.cpp | 6 ++--- .../core/contractors/codac2_py_CtcPolar.cpp | 6 ++--- .../core/contractors/codac2_py_CtcPolygon.cpp | 6 ++--- .../core/contractors/codac2_py_CtcProj.cpp | 5 ++-- .../core/contractors/codac2_py_CtcQInter.cpp | 6 ++--- .../core/contractors/codac2_py_CtcSegment.cpp | 6 ++--- .../core/contractors/codac2_py_CtcUnion.cpp | 6 ++--- .../core/contractors/codac2_py_CtcVisible.cpp | 24 ++++++++++++------- .../core/contractors/codac2_py_CtcWrapper.cpp | 12 +++++----- 24 files changed, 89 insertions(+), 89 deletions(-) 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/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 index de6681760..ff0a9e7ba 100644 --- a/python/src/core/contractors/codac2_py_CtcVisible.cpp +++ b/python/src/core/contractors/codac2_py_CtcVisible.cpp @@ -18,26 +18,32 @@ using namespace pybind11::literals; void export_CtcVisible(py::module& m, py::class_, pyCtcIntervalVector>& pyctc) { - py::class_ vis(m, "CtcVisible", pyctc, CTCVISIBLE_MAIN); - vis + 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) - .def(CONTRACT_BOX_METHOD(CtcVisible, VOID_CTCVISIBLE_CONTRACT_INTERVALVECTOR_REF_CONST)); + 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_ nvis(m, "CtcNoVisible", pyctc, CTCNOVISIBLE_MAIN); - nvis + 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) - .def(CONTRACT_BOX_METHOD(CtcNoVisible, VOID_CTCNOVISIBLE_CONTRACT_INTERVALVECTOR_REF_CONST)); -} + 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