From c516c179ca85bdcbe323ff1e87a6fac13454a9c8 Mon Sep 17 00:00:00 2001 From: Teusner Date: Tue, 31 Mar 2026 17:11:37 +0200 Subject: [PATCH] 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 zcmd43by!qw*ET+&2!fP|AV^3mDI%b>(o#x?4BeeWgQ7@C3y73-GjxYYNp}v7#L(UG zUBeyE{k+fjzQ5x;et&$Q$1#rJp1rTVuC>l}u5+zD{;%aEaIwj-ArJ_zl%%Kv1agfU z^}@OVeuJ>NVFKQ6+DNL~Lm+rfs25r!GafkvLJg4;eWBzWw>IhGerNx@b^DMZHt>VZ z^`C@zgj-ncNvU&Mr3$NkuFB@Mo_93gm`a!ID3**TmJXT`n<;MzXO}{i!!+{JoWCoX zZ_m6|di-`4j{uAAH}%*w)l<``T9~nM9jA)`6+RVy%vj7O%!#M^H0IcIdLHLGdN^HT z4+++FbU~LdBO&s0q_sa*TN!OXLuYr7(Zhc)%&$F!)3z>KL4^eU_4fh}T9>WQZ<}iM zom@sR$l4`an<_)0lz!(AVe_PuJ%4<1ZhwC7WLxTf3W@Xi5nXVfx%)d|;e~~TcgTSZ z=7QX(CWgnkh2OY6p`*C#Z==^P#^Hw)mHf*`{e71PAr0Bw>(5#o_<8(urGJy=nGJMX zdPuO1q)J7U?6ghEtywBX<=w!*zSk z635xq@oJZNUVOHpP^ziqZXan{!Ha<`Ar*Z`<=&$F#*n^-=;(~7M^mBIbd6h(3rc>EIg#=WzfmNBD ze!_Vlb-c@Ge#A~G3$s=v!CFosug}L7@WU>ItJbXjd zfV~Uem#v&L*t?v^EY?#CO$qcWgVwt4_v3f3W{xfYNobAC$hAvV;6e9 zoW&@Ji~By|>?HkJZa%_B;f6FbG>}|~hK)px@7SC`sZraXD_ zWE{#+%N4-nf4X8s<_S9+hg-2Hm_L{j}m0Og$=Vp+G=k#UvYM3FJ-B6d=c&+rNmP_&q zcka#8BVp;{;VzfAvTkPYwOc>SxbabDG?%~4!i5W_c^YeF`>?Z*1cyuy)x!le-z|E` z!MdL0B)SMEUYszlcnM+6O)eeWW2~H&Gnwn zRZeWD<=@B;vRgSao=?V?NbZrYGO1=Aec*h0cJRq^S!<>2+`P_-?NU-ZaW`uR6EBjE zI7m>+I^OaYbb^+~?wSbi+~O(M%-ME$s$fQZMtKk4OY_UK0=&e% z)lkb79=NKGc7*uZ+?$01ro!ruo2H{>QnAw~fx)roWmQ4^a^YW;N0kFT&nSj+)w0<> zQ7zcu=M^3t$MF7M(d7D_9ZN4I5d2(ux4ut1b~=t|yVFj~@FQOB23};`z6W3Rx1vdA zp`rFx&%CvbW)SPF-wub(w!bjXRlT1r>rvwIa7v}=tIw<2&-pR}(;3XsN;hHl*JvHO zoJ(TzIQ`P?dUPO2#%=L+@hrc7gJ&IM&S-I=!UQjF+U|QPd2Gp1 zpsUta(9bf%j4um0(n~VujtvO~2qh(MCZ5)e@VVZ(rkj|Q`eI@qyZQ27-@9~)taj=i z%+l@{iQ{qN>*X^~b^RP9i>7R4)gOvBhTJey_*pCe3F?s}J}o3sTjv>kO`r%`!IAGz zsXuw5+)nj)bR}PX@rQmcd^|N@;!7zpRrr^Y>iQ=|_N6fUw8_chCIHAcD2&gCgW4Z4 z;Z=2R?iyz)PQS>Wu3=jfG#gQUNXw^M*=&U8W#Q$1YId0UJ9jf@tC!HSgO}>{&DilKnko$-@?>P={#yb|HSP}YE+A>$CCv+3f^tJg@d(u zVR$Z)>C_M7!-?-d8^yCn+6skKCl6xGPK+g<;PJN@T$GuOz!fgMAE*-{qs^_!zVqL%Pf1<=kUxrI)u|PpQ)H-Rpnh zQC#ApdDO8AT$gG2OOmx#v0S{kSN=PF)s;!uw6cfKJ#F#Q($WV)C76dj!Or|vAR*+B zjiyH3MbPt*C z+;?5G#C{A@;{tImw1tfy$S?HMQhb6#TI*HFaNRf=%KeG)&lA6`nhmXf+5F&fHWg$} zY+~A%EhINm6A{3*xmvrw9W@Ggh;W zoi|ta1O#A9w63``Ks81}jM5M!} zTqLx~VSaLatPZ47Mdva`2w&eDTs-U}_M1V&nB@3onimc2_(VUP&bpe3Rjc$v4@YiO$-kae5}hS`RD2tJ}Fjj3>~K zWJ_xu$UuggdnHXWMXa zi<)*z;l5Z`KYnt~dos1O(DB&X-7%ZH?xH5@e1-QBLGO%hL?qShX10~bN4iHj(y+*L z&-`yr>%AoP^~#rZ2Qu+TYc$7aMxO~x;48H*<41=}j{Un-&nF{GOaM}LY}BZvc%G*g zVOKZ1*N_V-PM(Z~`GGiF8HOt~9h4L*Hb;>WK{L&sTkc5@ypJG z#Bh5iKs}5@5?sVx&8Ld7ucua}qg!Ua&EjGvLGG&hW=c0);E%2I#d6uF??n_}=FLku zVvl9{bJVP}o;bkS5@>MA3uMC`5FaSB1#8sdH8hhhNcZ!(O3v}J<3j`jurf( z)#daK)-4ry@_KlgbhsS5&>QO-4(-hzGX+oRR){*Ez7}~b`Zo@dlSDuICNo|P2S`6bilaVy@vHMm2|Ec>b^ zB<$n5hi*=1B4RJKrP?jzw|$*edR*)O8cl|IQ-$le=!u1n>%wGi=ixhRn-BQu%~ zI6xnENr}l={7CkmuiKXx(ascq}=YaXOHXhvO?f{O=c8EXfXv)_~;>}#3Fmai+Nmv8)J zaF*>7H*4kjCzL;pPr(vyW0|yBs%HJ}l%7lATyE)6D))4vqlu}{OWE)gSO2KB&@(L* z{~atZTeJRE&ZR@WErw*TX>3%~xyYxBrHTA1eZ9i=_I8JnRH)emN$)lJO`B97WN-B+ zZ4uvFeU%wb!{DW|N$I)2QCNQ$p+dob$N03{V`YHTeNNaG<|R2{o+#*X2TL-C=NaD{ zA-;}`xVZb4<|KqH>+F=Ldrvn%D?JH+-qL-1k2lkLt4idCQI15pjIxp|pBIAJJ;u{< z1!mJbMwKWet@SBL#nSMjk3gVRqj}GAEwj8*-*N=lwM4g~qfxEGw>WI~(>xsA9CoCo z9Y^Ioi3^WHQ$ZGgAcS5W^h^c&dU873^Q6KmrseZU0G~c}usmLT%Je)OZCHWvuxUPg zU?X4wyJYQBX8*WYX|GgK$?m{#{G{7lw|sjEhIjIgZk;MWYO?f`8MA6yZI6F8^sJ67 zC#PgC39;V|lR(CA2KOfnX`6NJ;;({#D@1e*6dVio27Egic>? zy2HKzLge|2+v-flallxjDEt`FzRLMM@5p4xqp2{PbIY0|6JmN!D0p#ZMdszHic8Kd zGosZm3zFMNID0K;e!kZx3+k8iJ)7BJb;_4h>SRx-H8J9!>|KP{T81F%iq)A%xXhZ8 zDs>O-2TJl>x2YQZ6+n{PIZl12t5CeA^7d`+Ve}vdtD9FFH0asW9enh1sXbET`Wi(= zs>!=0xpfb|$8)igDd{aXIb6?y?$imM4~cT|tl?@v>n)3UBX8Mds`d@#X{XVbYp2nA z35ulgOp)(k%woKYy6d6j`7S{IEUNBcMth#DY`giq++>&B{(Tge1%mF_9o#FmmX>UL zI?v*AVl)+15&w5@blItAE`@-KOjX*XI;pbYLkY1m>3?#lbbGwi?y6!oQ3omTo71LJ zMz~M^;J|NUO72&U)?R9;ut0b{pLEMU_2b|@3RsPdncr}WHC%M+_v14%{iV!PPN5y$ zpYmlSClRl1&UFEwpU!~%p9Rmxczzm8rgsA_h;wyoJGag>Z!rnqIP6hzijvnX!_>wY zRjGyNJ=@NftGov&_{+(|-%jg@^2;Ny-6QUOJXc+45c)gs11?O5`C84vj0|mUb{q;hEY94^jtGt8 zWjLMGbAgkql5nm8fv9`TQ+E-EqU5jY_5~QL@AaJk$BU_q|J}nr7*(!)^Tc{{abFJB zbDK}lQ?)dbc*X80GgLqKSa18H)qO{v^>V#uP)U(h$}D#(-LSq!#ZASl@N&Ak%)I$C z-{9#N>yE_fu|oZu8LcC7Lv;dN8s~$X<58C14&OU@?Z>#@hgJCq6w6(BC#@*ih1R*Q zjhLBET-KHu?~{xll^YN3emF-O;UajL)Z3pdyesG- zxXu9hVP0|{BcBsX*zf&~49}&vlzZAL(Hl(s4j)e9?Og_$ovdbdjX&PkZfN+#oFQQi zAbF>ZYDrtrxfIT@>K-K>_=3WYQ>S}EORykZf_ zAkFQcbfUGN$+H&z?r` zmBMy1=DM&bnA}Qb{`!zD#`R*#^M$>WPE_7zU3z?nP=7Cw66EMHk;~J9pi~VW7#9+v z@$xiY&q1WnT6?_y_$kPtDo1xHUo&g5w)Dh#hyKB;@8ulfKHupROjpCa@lxi6jyp@ifRqOXm24W%L|)4Z6YDdt3cJA5>_XSrDuAgHGRL*o zBnY$sK__z@QPkjH!1mxer8G1!%C;TBqd17r&RSlMh}%e@cfWU!$!&%h(5AwZ)+~?6 z$x=x-9_8#qp6-E}%dyKU?v070A+IX!kDr2HkLac-+bz;(=jHacMzFteil!-j%Jnd( zAU3$_^q&0Z#=~q!cK+LzHs86X_eDW@nd(g^Xlg(%O{&=7d$7- zp8E%tNQE+U<#@Q2gPMm5o(5)%f4toPTXsUkL7@yWnM22@L!o@L?em(uRO_xu8dNx?jn4+U`7sxK?5?--bD zoS&DvibO=IK3)D^r|xRe@O)*mF>Yxg3~{==nB%(KF@(#T{ZzGivKB_qJj|g|Yj1+M zJazA9&tK2-k9HUt8u(Kl zFUO_L_Qt*u>iFD3>8&Zd+&!*+si1EssRNUeIcOwr)4+>(pW_;P9m8^9uXe9kN=AKZ z6|Q-S@sQoZzJO#rz94`8=xfHkZy?eD6EpNcon`y&gSL{*%m6CEm#tSN5@9;o0#=5Qr5|iW>2?xcmHj*zBrs zOqEZHr+r!Y&hus-s+N1CSFIP*KC(Ttq&S*Xd%XNOe(xvacikkbuCzp)4X1MF?MB&5l(@wKtyCGEsyJ6*`24bI-r+ z8@J^*d0uMYLt&(!KGy?Z9tsbcGy%r(61Vv1ahavEyj3&^q$c3SCJ<^N79wcq;Oz(Z z9qRwxk3`(QB;Lh-LLUeGW-%aY{-je+_c$UC;!FmZ!z!A#z(*@^L<&%!9Q8%N4&J;i z9}555kCF}Ow?t0};>5AC9$$EkbNae05MCEWpLULY)Wk+8g!u_m83PF-ZOP z9y|-W!vgV+lx%n3`Y9zlk2);y>)-3B>ErROEn<>h-8d-aabSawr$^ucO!P?Cc1aJa~Zmg$~A_%MQBy zfnm*_J>=7)A|3v>T005TXW!q9Cw5{SnW~gh9_q5ZgchAB@9&N*`!Wai_Zg$k!A5+$ z@w5Sx$^_Y;wlV_O25gppTa@_Doeyi&1X#<5^_R6L`>Po>H8o`mwQh&QnQv11M;(7u z|Lq=+%x-O7=1yhyTN@z@FFW|D+4gF`$Cu=578gp@pK1kO96JY}?6=?2U{oiFSimZK;_g zhEwF&gInvc=2v5b$Vo1GgW`92=EKN%eDk5=YHfkj#jJ}WZ##Q;Qz6t1LN*8;FjDjD z#A34#vaGBFjVAk4m*QawuEfWV<40WCnHIOIeFECWht^E{kgbd64ODpjmPD>A1M)&c z@|)up9jJvsY(l=H(*dFmhmt4r7~u|rBXd$yMSG?cljmsaDP59y*YK<=iX>MrIFx|0 zo?1k^^Yr2@pLe^7Xn#3yD0>p2*YV=&M4v+Moxy3Fig zuYW5hCc2I8Pve0lM++iFC(ZAX+9qrM0G0Mw+9|`K3N#qbnrGbi9+%fiG-1OudSakz zBi&R}*-12??OAPssFQ6N*on`PyC(SrZQ>Oe?;E-9Ea z*Y|Lict?eIzPN3LB|fNwYJ8-wu4_T03PRHDZ;ViABAvPxb9;W}KmGN8Mw|y2IFAXC zA~s}?)F2S%u=kO^g6aJUM3%1fVF3ULVAUQ{=cA?P9Y41-T&`E?~#X~ws3g5JSdLTR8~a2P-{`vtAy#L_7xJFV_o2h~?sL3j&0pKuCyG|_JVLGUGupSmwTYC*0H1p_zs_@i# zh1}&?s@#RlBU)OK3z@dWN5}4R4)~~(MSp18>W<}+7fh7TNc60VI7jDwIwwE-6yzC* zN&s1-+Z>;{!>|-amFXG3zTVr(+izkmu8c4F7Ig?@g5Cg2Ma(c|3aXTWmH{8UhR^?< zC|@n*FnY}~*&18{!q1Qi$;z?rOV9SmMj(>aVS7U^OYs>{G0%8z&maJp@hh~b<3g~Q zgo}#ZPgY7FxatoJ$9r1Ty?XU(Z%u%niEU>!j3oQ-;{s;)jZdCdcscc6D>@U5qzKCh zXlKymLp5-JxV_K!t|_mO8#%L!IN-kip0Xs%Eb&I94gOVD&AJ&do0pZAS`*={dN=al# zMIg2EB{peZ+)3f`;{1^y>ep%v7FTQrf)xIf)w}?)*D3rr{dl8TFwRLY*2a4COxYr@Fc z`?c=j(79FP7xKUDb!*9^GFAn2kePaSsb{30ct6|vGrvS_rI_nlbNqN|wfa?z8yXlq zWhk70;(i#3)yi)+j6?{5J)B!BLZkqp4W%?Eu4J!q9xL zW3|d`e<~q7)!*17GeUwLe_YD^%Q7vyl6gZcnn&F-NA0d|o~zSgj`8%Y#*t0Do&C#C zx`d6fqfSV(f3b7vU-v^1RJjVC3G5p;e*^xMJvNX$5YjdBUg&-*f?;nEfL3=XvrMM8o{xl%~oGwnm!jkGiV za8Hym*GQDSYk(lY$_kjwB(GF|3&km1?{l@IkNEv>InV1?sfv8&Hj;KnPxp{1!onK0 zNm3HG+N31vuXf_~>;0{B&T5n2NtKqDo;zKQdyBEwplelS(&@K#h*oJjt^!ah2S<+G zLQ_RklQ~hIY?P5%b`phHGvd061sYsWpPGO^6o_|n9;?CjNM!kz~0YZe+vZBNLVOk%@`$;vPZ7un$_z`@h+Ia=K0_Y+Zy=8e6Wxbt{I#_2Re- z)lR0Ya(d^aH30z30NKWvs3cXW4Jb6zhH^5Ds_XS8J`~DF7!KF`i1Y})UdKDqF&Wzfn8%cOfDw+kifEtN+u?Q&bXH3E;!%_;QE7oKBP; z@qa=x{;wknD4M8Qu5Q7f4}&>QlqmWqYq=hkl?ms#UHRTX)=~qKJ69vIQ?T7Q2={o_ zSd}hYu`fro?wc_vdgC|HAwN?74=P9u9JzTeG*l%Ey%trSmdGIf+vx)!RuBkW?|)`v zz);el_~YCD20^i?D722ret9CIY~GjHDiv2}VAuJNGw6h-5k;9#Bm=DiunB5ZaV)|T zUr@7tnIYHdRKN(Xiv$%e>35Iqz5p6fKs$%YD~w`INkuS>O%Q+A58(Q3<}`LuFQ(_L z%LpX`{O`S<^R;*0@R}Na0y3g+b-$GKoF@n5iD>NvNE&mb-UNo)2Bw!2KL97TY9(O# z0ckXj64&Z!asVDge23cmfgbUL&YK|Upai9mF_p<&LXzBmOzFe%A*$~pDOjlgASs1Z zt{enuR3$_AgLId&u1Q`C0Lg#GuuhozJ199rvGvD_3^htj!pzLf9A-a~WNX)Z%I)DV z7tG1F76G;!>MRR^3}e4)Fw+_pOfzmC7Y}lAFTL2QzBoClAd}@^6^Z9}z4XAI_$Ij! zwZ}McVygEta!H|pCXY&+?BIeruZ8tn{w$}ovY>e`ojLZWBzYM*7gX8X_u*E9**HmW z+GX_%9_=9!TKo>jWO>L1?XXR*2?jR6UjLWaf5M$SD7}6I26*4w5A5y?{R@m=)l?&< zpek{lQ>AaG{!a2(JolLL4zSb@*7xn7qZdaq)9Ak!!!fLW1n`1Lti0a{CXYx{N=3qe|6V3bX2OHg@RyO6XDs!xJu;Lm@Et6~HFV)>Glsnzw^erFC7hnY~JD>#U zR%!y1h+2K}pA9Ld}vZvq{{Hbdv*{IDofd8$y%Fv`0n8?fu4bEs?oa0lfMzb{`paO8+L-VjPrLV!my z#QTEOkckvWQkB@E!Z({=ySuQ_Al^j4(L=EKB-a4LjFd};pz%^)`RW~Pg6}@bD0h~l#=u#g(d#-b zST*`c#}@PPPu=E;&Xtv4M*3T-Z9_|nCPfb@db+SP_X207RkWEqjinYe+TFgF-#7&73SMr8q-!RP9xuUKtAL%b zUh;w}Pm&Qp+y~-SGl^4wq(5E*HqXuYCi>#5-TwH5Hu z!S6tE_7e;aa2xFm4f>DCFkyoEO0b&*!zJ6hPc6^*8BjYS4X)(pk3tZ&kLcHh-%7TE z%>f2NBPe%XL^*({$Srp7g5)$SG3nOuJ6sfjq)^z*KamjX_p3Aom%U{xs)vjml>IH@ z@rN9adQP1=^r-57r_~e7JNThd;S<3VkCz}qDGy#@71lx^4i=qgXrWJ0tMC>DtB|}& zG=K&XrU4$1`k5Ix4mA`|Q!M6~w)MjPLJfFS=zBxKD=3p}xY`8d#}7<=<6C;zDo=cS zJ_~TfJAmStmmBC=1xmob`{vw$84kgj3+~&aj7oQh8q)tBbr1m_29|fp>?u2c8CP6sx#2yd?_?hiGuX7ddI)|Gwk8_J8e!ahc_<|X=C=AppyhYK_ ze~4cpDX1a`H!S<&@vR3m1I>m;@AQ71b*Zv^DZjr#xYNsx65Mu$uBIkUHMzmC6VQep z3e?`1(I~W1dFH5Bmd@X6q8|coj7A}zkyJtXt<%5_)Hr}uUG}G{yGAP`A(jL8h=!4k zOE=mQkvL;7>;qAf%}|E5@iFIRH9sfqULl0KS@nZWD?s_qJ3ag%CV!5jqDf`vO6gf1%bt5%qR)<@@dAHw?`U;;=S z+3v%mnMA+C<8zmOr;72&e030^LSx)2*QJElGFW~6kYMsv4pB9HCwKVcKuga+Kxxo; zbylF!g+krxD4A&m#nUqU@_^2g>=V=%W_ z>ZR(l#uvP03f!~+P%arqRJi&=qyX>U*F2$%D$(!~!&AY$sovD-&hhj&pO#iK8+U(h zeE&oAdurT$Uw~gf9?;=cFze*l>e$@grAvC(dl}#aO)?3@d+#O;gKp~Cq=8tRy65?% z=H8xp_3xfNWV7k{7s3bZDDjZXO~c`c@Y>!|^^+0@F(0cMSfJCoRbONIp;V<0q{bSA zd1hnb*ROaG@8`+H54P_-B*Y07I!_l3IZF;P0)62n+3dkndEEYy;9IrWc8!r@RTlv? zllwb;OniFP?ZLkkrKN>crwgS3Zzf*Sg)A?lg7gPPc>J|~CQ#yMTMg$=c9A{N@w)5f z3H4=w_G^XI6)3`Ms~IiO6ea{pe4kqxFk+zhui(mVQ{D8&3qggu+j9;~x`!C%K(lq- ztUY|H`}4zhOo9DDMY5+Rp}|5IN1+rfZ!2D-^xDt|BI8kcn&oorwR_?DwFer8#`gLg zAU{P0JL-tCKT;dE|HuA1W$36Aa}$kJ**sEX9^uY*c;R!yKp$rYo8HOlF zessfj*jaX|I*OLawC;AF4p791-UmA~P zuU2tc$7Xcdrp2&Uge&Mp#nVYCD=WKR*CT7G|uH=vZ$)lKT=%@S_(eeDh@=zg~=fOp5d4#3r8Y4;%v$TL3_ ziY40*6tH3wK=Oq_LKQY>!z&Ib5gd!fZQ;yieh69taks5ss+7=(DoluBan$rxCWUwt z9U&fvj6fp{f*FVTLrNG_8H(86-m>POAoE|1s!%{NDvKO{OM=GO6K>l?*+=B$j#I55 zd2lQRfmE;rIuzc7x0y`lD&&8y6-mA6p{_K|WdR_+eFD1!5}RrO7o!0nt>5O7q<26H zqV1E^DxdtqV*%vGiWj*_DyTd-2&7d4c982HzhsJ-43GKRO}Do^(JS%e=9%T!R&%&i z?yRbl44LumszAi&vI*Anr?&k?{+5q#3hY}dybuLl59#pZIvFmq5jl`RW^QiZd5{Vc zUJcV&&TTo6xMuEc%g9g(7Au9j82dmVZM&{!O1#f)XSU`3`fwmN;UvB?>Omk+^Ro}9+Har`!UJfG-$iDCi+C#$9-8B#7A+|; z<(y1di#f$eH?B6V)N+3p;|65oiwtm`@Z+gsF#mb#4cV%3kbQ|yC?U#@qDs%&kW%x= zI(#g^*FNiLqU}(D911AWt5srx0Y!(u&E0(`S2FJ6o%PAZ_;A?!g*8;=)HFy(gaF98 zZ@I=@ZK2RPtEPAqh6XWVj1kQ-@O|b&0qB&2n&=)nq`wQ7abZmy4gHT2;E;BrQ46GR z2WP;JXRO*LlK5jpnSB-2eE_rgM7;NrkTyB`6qub<&hYBS3Kj&ZF|m$+`VscI&7g>n zBqq@YBCPw=8)auKr`9cTO)BPO*imQyS01L%Ew~&Be;GKU(|pb$H6K}6*|b$v(PeT~ zROxHVTKJGo4VH6A{!R_;(evA}3j;AL6ELM_zwtvE4f?6Xy4o}@y-wbF^Y*v31q<@)J0Fi30wR$4j z91%RE0gSOW5C&(cAOuFGM*G94`7RHgHsAta>m_?pVYc+)v*uGRMbcz6=ez#Wk~|p_ z_fpLbUNWN&Ku4+xw=R5qPVLlbD0P6hpu(V0nqRK>JQvmfltM9C&Qv!m!&P3jlst&k zZ>iGc@RDpYu3BI*+C0v@L^%O`EAz&H!(_$tLOYitM1?ZH7#3kIgh0r*!XB$ z3@!^!;_taDc8`H#_lglR7Cv`!2g{{e4a(IpLJSct01zs~27LMpYt;PEyp=1^XFV>2 zsb`qk+3}mmLB|w1BYF#-h6qO4uqOBo+OQ5D7DVj>a0c)4F)pSbtH5o3yqsXX4!HJ-a!gp6^wGe8%ryX?(W$oys|uTQ zNc-J$fgs$_W6an@gayy`tS63uE}1ISMNG47FelgIbJfD{<>TYPy`Ql|Rlmcfzl4Pm z7Wqi>1(!~w{(dmf|6XP_!#_)kp-i{gtuO0Bt&)Gtvc?zGfCV7PH~Lk)Wypo(LI2P6 zWDlFweTSMt$SjZ!Sqj^A94=jTXx{c3>PxFMl|{3oarsq(VkoA1X5m*g!qdFOHWqn9 za(1vAy?vkxnY|vAGp;p~+|j*}9QJ9UjFp)pj#di$`p@)zbZ`28j^U>&x)J%7KEthk z%L@PrYFPSjW|=6&YeRN1h^ar_>Kvx>(xQS(ji30^wGj z{>b||@U5nRWlOKbQ1Bc=DR?@F3pDt3Tjdy;`_65LZ+k0-AD?}!7X1%SvzLgZf_T#b zbo%i$&cs`h<}S6@iL5{6u^3gCr%Oh6tWwrtB3UY&qD>h%-(BD+GU3b0eFnGlu;^br z`?JvZrdyw^Rt1GC2q5!bp9I&HN4WQ%i0Zd|woQ$I)byk#sYKy7@XpxwEq;*f|AI*t zMEc&)@YGw?dBM6dyXS)%hM!hgq0J{#Fu9a^O`*U@P&-d^V>){s2OrmUiyXc&{7Aw* zEo*VA;GY2zOW%hBP-I57;Bcz$yh zA8X>yb!PuI++z@xQG}~U@?d@Q>lBK!S0q1`m4lq1qP3HTtK^BEH^cCt4yd`VCm|$$8Zb0PY`SxpW zZZlq$idbUt2@*(Z3=tvxF%_URq)c-YHQNA~ww|!#W)zwKEQxIU=rqaMxI)vpcU~$1 zP?oI;NEe&082XrooNdZ!F*?@HOY_ae0OuiPN^tn8Mp&CCCJCnT!6eFE8zx^K6*N7w zmyuaQQSodHGR}Bw6WRz~J?D6>e=vmc>L5fg`&etU50FhHfwp0T4f4Z}g$jM%8CK}G zYv>mPgLSXmq$>S!Dz*&oQG?aCAq7#U2Qg9REXSnPfEL86LzYxm6z=avkMPJQ@FfT&GOovJfd zjpAV2nDPFWq3|BZ}VGRQ|&shABt^+{-=53N1>J zEHR8Fg~?EXCHnTTC?Dtcx_x-ubiE)r%S%4p1DakWRDlCb>{l-% z!zbCdc0|>|4`Q5XEQucVz1kUG`f4hoqBur1@sSkT*b*K)EYA~^1uMd3o(fFg=Zsm% zvbbh!3q&WKtH~s~W*EHzAX1?V3&kxh{JR$W>RW23yFVkZ`IAqLJ&0NQ%G@Jt_JgJXm8M973Otcv-uz&@q4BO$^AM?J-*_pmyhJPBlr;ks zgvqWNKj}~$yRqoc#rT2ddOGr#zY2wxM1|S-l!i!5ZAY!n+OvW8Ths+57%{utNG*$0*ll>LUW{eSL#ddP1ch}l$)}vtYDh$#+ zl3-tM2Kl?kD1P|ifD9fCD(eoi-<%kPtDX8(hrVB!*^yiYv>?{Aw{qcgq~=LXC-627 zB+g_oyCshW(ZPNIKyegp_oKJK9`@;3>owop=&O>ICF7=(5UQ`r08=7icP?;N$N6|n zFIokj>h(sI8S48kA;Iv}Pr)gFDtxQs2l^U9lY2hhtj4(${eM7mCy}j*)#uX>AFB0v z;kK>f{A}I_FMV~iyn60-Ia_@~2|R2<895h^VHv@-7Nt=&&(wTg6}@!oouCmon3LN_ zBGfNBFWGuJx6(@ILET2s7byB6ur%p{15yi%M@p|>6F32F ztr}oifAeo$O=yf8<}>cDjx8s18x|Lq=NKF&AHsDx4o?Bm6s<{wNg1pRMO^#dR zq5r5dpxJ}S5Y{Q!a6MiwTL`J^)dCWZ40zP0KTkeUSF}Gu(U{@3z`tGZDhU3*UOTrx z(9ivve$Og_E@}At{V`Qs-UUo^2PM;ls2+;khV$Z-jqeq1%eA*c$j7AK{kWLb1U0u0 z&Gp$(6GH|fIB7-^-UgwPKuL+@(nX1aar~fq@#Y%&2%goqGG#-F-#1thC6o9Nf1^)eBKIs&^ zo}rc?a%Im$qd&zPGeoyX)k&}{?<#_iP6`3zUOm9faoEU;A9^m-SVxNnA*D^A`M{Un zXqJvAFM8tm#l_8PO4|)w0O)@^#wQLyU3*FC++1F5GECtCG@7)f>G33~XyPnQ-Smyx z!t(6w*!|xT@+(2ib=>bj{dV=e03+w%iS%JxT0SHg_inPd57EhY_A(K3e>*msNmv`U z@s+YicJ-^`3>(y~wuT^hmWx7nDk1i8q$*$eHQG)JPBl{EvJy>~j#m70Z|vb^xiIp- z7jJAzCADeR#ZHUD=z^AvK0fAC;JIgo-)f=-{MYQ3HQg+wrrN_UZI`+dh5q-WA+R}? zcbeU5UqQvxGx2rwuY_e+U1mG5{rvnbeRYsB2m;9W+5#ZYsJ$uVvyJ+Z#YyC!Q2016 zks+ayqwsGmce0#MpSM4$d}T<^Y}>l`+r{$eHspiRGCCym#sr7J^X*Q5sbP>M7)vfq zLPGB3XbJ{8FhLpsoI}b0g`iYI_Tt&7=<+nMokRpST1>zM-IiSSQ7`Z0Q0@mqWFjx76a$RQoT}YsB zEOC{@6ICKoGK}x2irJg+;mbH((82T-M>7cloK@%6##gI)NufP7YON=68$iZdN97SM zdNjya)R#Lb0de1^iWUMXRujOa>uYUhdYUY@hx+P-(Y7Hg{qe1bx92K&uczvbmzWBI z?(h5@zbr@zda{$ZcUPhSvzW-YoN#cL6H?8xn18xKRHRMwQ7QE&vqm=Y?WKbrdY*%h ze8Fdvb*dEUpF3txY2xs8=`=scUa6* zR61q+!F=hhT}??UR^%&sCtIRF)=`_PJ0>vwXt8b{_zz05fP>mTHMv}`CR15oPS%=p z{Ex(?*yDp3|BF)mZF`Wy-hloHP)$d+V$$;Y@=ewxf1*S9fd13_XXo6~GIck%AT|n7 z8WbDcgIr>Yw3H$4p%_p$vbrR4)X7#(6=iN)8^MKWfN~Zx!@K?VX3X5XQ*XLPX4Pt$ z#gELzR%_8(9yV1T@IQnMZ|1OIVra7J^4gK@B9~SDlhu}gIv7ygAQi(v>);qDoPd5v zb;I+c5bB2fv7BI!mT+qEu9tvB9BchC8PsG~(PRk#$#LK~gSAeRes9CD7 zk>p;W-IM-<+1`18)sdxZC8vFETcz!^-+(-*t*Srx>l;zNhFpg@JhWmLp3|bt!wNg- zA-7G2hYeTSVnwy7a!w?S8?V@^)dv0LWtNkF#;4$8l#)aBnxQPLo668i<8QxLDnmX* z)uG#I+y_{Y8kFt|igzW^k6M)Tf+)2R|6Xup?}g&%4Mvx{8}I`V<0|cE4l1B^?J{17WE=g^Woe#va&$|0Rax39Ics|nUb@V2q;ZQ ze1+)RpdnI}T@|o;Ms`GX7Ah0`7w?V@&fR~7(&4``h+}~XkCzGs`Sr$uQ(o3r3=uTI zGK4xOGo!UnBK~BE{ol}ki|&M)M&|$s8;D+?>_;V~0l;hjPD*J-vJyP*Mp;igkWgoz zwvS~ba$iSFg9grva`5HKH-`XS{PMokTt1M}+4U1;O!Qqf#PCbwzjKG1fFqbmzE_%OX=7MIP7hC@P3MS--at*&s zOVjL&!eKPwoJ<~?xJ$T(mgnw`Aj+r=Drj$4z91ST^a&gCy%7h>4=nTisd!t?x<(BzAahBFOihj)(;k-rYLdM6g)y-sfDOO z9>sa)D~#QMcspQ`y&vkQi9a291;)vZyXuPJiXP}7JI%cLA1GI8sXaI(efwwc>wn}G z=xekhJCY9==M6>H%ClWF;GKCXfB35uVv>ew>|y0&-{b`kc2y7KnfOH z4U&bM#DlG9G zlXl#lD3np@ZQ=&M8I|q$o4>Acg9eF+}u2h zVv%1qRbU_iRLXcDDS|&`y0>{jcN@LtF;7>}zx)pcppZ~Vd5^5Ue*M~t&%nByBTC-` z>FdvW&EHs3#^Ntc`|pc{gQ-7n2WpIt0?xar-Q}eF-=h6B&WZ&(-+|L2dvZt?YS5mV zg)#rYcAw=7xt!j!TK4L4v`{jzKw&i}?e+N;^&?Bj4|CK*P9VhpdW2~E&!?~AHn1Q+ zSZ*~u(Z{-LFWiZ^A#9f}8@m_*1{gLP$4fk`kkTDZ?1trM&Nc{qOdg8FfgWWh1Oy9+ z*V6w;ppIHs&nWsNNYgT)?`49u`%~m_Sc-6WP9oL zIMHZUq28ZbZ|-Kj$55U5hM|R%#1yEUDaQK&ztOiqOh;YSU*{c$-tcXl`X4Xpu%D0; zzK{42BjqdQ9b;g$altVm2RoTc*g$LwEIo&L)-y@lcYAcZOttdF3!c|4P3XDfV?pS+ z&4jw`+#Fi=Wvgos)adoSOKrh;9^LTI7k5ir+wVsd9dnZaqy; zqpEJaR73g}M8-!I3=nzi#Q`1BzjTMpc)jpd3&dO>Omawa9q^3_l_=rBZHFmy96EC+ zH1ihzx>Fo=AeMw%zwFtT<~r_duN_i=rrBi*T;>MztdZMct27_jdW`LK9+8Cq_Y3nX z{1s)~QXgvYUoBxA6ZjOHjpH~VDBg}jB%#_X! zJTu&olH^sU!VBapD9OB>z#t+GW5v(XhpJGGN<19%?;~IC!=|VA{-MNsVK~))-s9vk% zxZ$z7vDA)Af3IlUBN~MCXNu+rYitAbWUbZnqrfH`E@PHFe<8SkZW|@A+bfC|%3~C~ z_RRlPjw(41*Lz02U*5+&?7|kN1}q#&Bgs<5gFch!=^zQy zo^n*~Gf~)sw)vaAtLL6BvegBg?r7!5V5k=rGS#yX+QHg(7q~hoK9^-!n`KdI$LbPg z+$RmlSd(mLCc!Sn4c0U3hHEa0qNtVhI0f-L(Gd|E)o<>V%|B{J^`9@T+ezw!!}WaZ zQ;G}VNKA7}%S{0Jed@DYa<-4<#y!1Lne@aya3q02)ELy@lln_@qR9ByZRpm4b}BK= z-%y)Di7DV1xfqhx;=tV;r@;c5=5W7%X8?m)If(x_hdVLy6NYzYJF*(-l?N|qQo~VC zGNs$xN=h%JxRhug;Dt448p2s;OSBc;RJOFY&2;9S&bqa4!jIL`aCU#!{aMQl0J{ao zprF5VZYF{wwkbvgoi$mVU@rZaai{b;ShF>In%>Z`zrSDKzml&n;@fCiSdO#VX?R?r zyY@rRk$5+7Titujg4^Z>S9mVnw)4Rv@|>7`e$YB~E27m|I5ycWV$<5~Ve#%buko3x zgc+wC!xh+B9icj=6tI={GbX1^t)|a14D4wAbFZFlV~RzVtUF0Bo2m?u;@Ugvd?A2u zv0n&U6=iUbk#;9c)-A!p-xVYBLvm-P$EtTRdwhdi1A!*ur@ugRGXa|KJqls^%4^W& zr#t4kyu1GLV20k_PEMvB*Ic7C=ytew%JcOv>Ead9O1sG*g1c;NQ^#rf_cbxv@-PxjVmtJ$$MpAir%UdG`3Cc|leD?zq3Jy6$~-eX|~HD49C`o0fffqh;JDSpU8jQZAh?5Iog?paNE~6KaFX zx09DlLuNbps5%$wnzZ02Tw9EPx6wcDwOm}kN!9XpC0mOYAE$Q=tEO}MiEVqLKKk1hIQ!d_LdkcPj`@zqmZuAg01@UgVuy;Po( zS2RG@9rF$9cifdst2hnTR?>SS(W|!)z%^ye$B4_cjZ+1s^7r?@1dw<)sCFdN2 z(NmqEtt^uQ3E-AZh-b8~a|++(zUpnuI+mMK*n#(>-@6}>)GxZ3G5*7JLO#Z*(yqdXIAj8~T7{QVVcLUS`>;iee1Q)S@i{RmA*oCj%Q?!I4=^=}5jwEh>Y zoVSOJU|~&(+9Gpz^t3l8*)p#{ET<;j@#ESSS*5^7!}VfdV_M?gqp2ZvT8vnlN^Jkw zzN~oh6jY+MgJm#?)|`1;a)_zu3)ADR5oKCl^DkJn99=)gz3NW6G0jI+QEFDVe98rC zDCcx|*QjM<6*?q3l6&&^k5PxY{yK5OQqPxdFc9oh+nC0TgaDfP&NgM&Pt*qy<|et& z$~W7;zooUV>X!0{)e4Qt7T~9H^ z2`ejTR9OJ3V7{*pCcn@_O3r`X4^8j^szRS|9p@k!RA~4RQhE{UR9V}BsNSiR2djus zx>D>MF_7lyok`yT{&K}ifwd^JP9jyM4e(?{hY7%wTedzvK2;G;CQoHDJ0-KMv`#5n zrz+ps)6uZoIQNy7(K0@ssPjeTR|c3%-7Jv%M2A1C{w5y;KCn6ncUNDVkjL!H)L!`# zu>ObM*+#einbKGsM*~Ejd-#buL_)&Yz~B8i;EU87@u=8+6Lp?}CT*)$mZv7y5T+^F ziS8SF-TOIg(%GSvh87UTe&V?t><1A5H2yBIQTeVkfG}{)bU5va=uT25Mbje$3{+sQ zYg@A9Bi!4d>CSZ6iE%jqIEpN>$YLwVv(xG<&^9>e9P-yd$%88u(y5OM$o0JRC8ql5 z0geH45&a5t^uokky>RR_d%UMaFi^{iUFb7$qM(uDUHMjcF+5(o{UgdCK)d*Uq1LPs zlg`Oeanx~-OVHZo_|Cd1`IHZ^w&3WtIS1En}idK?`U>dCa zT+c%@Z#C!JyFBv({Wyvjt5w4}^;_2$WjLO))*3BN_@H5+JKi-mt_CMir1n(xl%Q?T zoD{+U8#z}$eJa$lg%W@UFm5>srb)}j?e;;yi@!p+9Q{Umpd54VC_FjP-Lu2psofHc zzSqARKbBLAFR#AgtII>~v5=v^^=|;P(pd!BRXeh^aAGAm9PqYil<%|E`!e^0jV6l8fTn0UmtqSLYt3LW4#HN zr=+t(LyHyi0vTfz1>|8lU{h?STu0JadYh3!?e#sC0my-=YkKyo(-$Myu8MKJm9EIu zg7K53CWVz4PMX!*oW={k&lqq)CL=i)b;by@xiqRi0^|A$7BI;5_B>n(2Oqlp-RmK+ z@k}Y@qq7K2ISC2VA-}Hs2unDlHcEp~B^DPaD{xrK$=!7v(CY137)*;&e9JwCo4?AT zj2Fb)tAGWa@>J^lF+BZ6)^S3hb+^j=i@^RaVpTXneGTOBX=_;!t(J%&6fyDn`| z0&+fQNh9IlXRNmV=4Es}n9Ys()!x^#L{(26+GOJ$_Eb35r4Ko%Mugz?A|f==lMc8s z9eO0Zdh$vEIBPK{UPZH|UmL6pAg1bIOpZ7h)TZl)QwHfF0Sir}g_Cc4hQG55lY$s3 zeA78dQGbL}!Q=rO6^dG>1C}~o&#s~8M!E2^;|R<&A9s-pkdu4;P>f;`GC#o{rehrH zYw+5v&K{q+hirCI=-Fy+EPf#fJdgyEJ7-Gp2<7yBt*BExC8_{$6iRFyZ{z=6{N5o7 zRMXzR->L)KE6_v5{gkWyo0^gG{Fwl`=sR%F2#-8nzlKg8mvf)LjYSYOG%+x!efo?@ z-Pui+WRXKd4N|Ng<0f*&D28d}he}c_UD06hH5X6~K?$41Z%G21@L|*# zn=n{SS0xU9Wr?Y|_6f`z)E+Q6$7dB-6L~ZJ?jTN z@NblSb^~dGD;bFvqh}d$vWLkLpgS6IOnJr{x3; zy~xuavGrfm1pIw4rIGbXFt`|ZEm}%oB47%g=)o5>j1u#m4d~te^Ahxb1ki*znk&HJ zN!PQ&UImZr6T+NVvHDAYbeT-vtF%vm5}3hWg?7(C=U{_m;UWjvhmYq0Iw9~spl-TI zec<>}Aq?2eYHps9Pq~yZA@&2L9zYyGJQ%v$V|AkP7&^%*#B@Rm0#?(ZwI=%lMk%nw z_t8&EnR|4D@!{`$~2poOU$Opv=qC8mp=bSBJ5YjLJ_TW(nr?@QV5 zrxN1TZp%`BYkb+r>@yU<+oZPG0vXv|J$TLqPBf#K*P~UNN zXTAfM948w-SO>NYuBXN5I8F*c9&5#MOG2r<|AU4#DV~4TNa>c0dkog5Rf#)l>o`oJ5(yPqKT8eH-_NYETSP`LUhf|x-K`%VHETVb@GS)hJ zLJSg6HCDdsx@-dmw@BDW+gDT$qR(JmjPe=Dn- zIu1{JGj>2<_$_|4ETQ+D+fG%Z9bmq@D}HwZ{stQxMwW@`69=9%xQe! z;FSVaoqeovYL!Y$e}(R*?Ya_(@<59m`>BL0z))qkFJ%_%w~1Z;URE2e8Y`zG1Gn}4 z*>hlgJ=o3|R^JS*XgQyCU9M@?)26wpKXx|!Z?mR?bssHz=MeGd?$tB?11=WrLv_;1 zP31P6vTD!3u1ESFLc1W3FP$2X0xdPl!G7!BgNeW$BzOFUi(a&id*|ej!6N=(R-|{c z%)63=xQ)gB2SBvB23z5)awV!zrH7WIzQ63$NDqBkr)DwLlLd!}mKrpL)j}8+>Li85 zf3D8|Gff)xbRa>5*zA=Va9Hml4UO(75cO5G`xcHYM?oh(ZH+mp315>_&{qVPnlg)- z!BT@}Vb}DPZTcoIUt9{Rp&Rne9ygSf-uTNXWhsLChB!HDdbnJO62t9W@)l2K~EAh3o}5fPd8 zC=(mjrR^Rh85qp>bHa^YIN#b8! zMl+Kke|W(Nd{;<|`3^!M8^6xegLc5^UmqF(cH!(($?K?X5(@IGeBixM+5w6MuiX~a z9Hj?G1hz9E4l>v~)Wjxn5Rn>Hug@*?9wEhQgp7wvMPql!>)v*!B1@WcKw|D@hEz&U zv9A9RRVQ3~O|c80=j`1jT!>{3ioRzxZB}ppXn%+Usk{J7n`gfu%E@f?=^hj(J}km8 z-o=s86~h2R4?K5+e;_eh>s>zyQykiH05i#ceyegKM6<-x4z4%drb4tHno30fIiymX zjVx1%m9w5br2-oEgggF{^Vo0`<^2G{Lw@G0a)=?57*Y%~fE$kPGSijH529ij#bTy} ze*4m*%TK_~rIfA0f~V-!Tf51@-n=w>j{Uxf=46vQzJ71xzxO=MX*J6S3{v~B=!djR zeuL}%YgKq}W6QjWSI5agzv9M;q^O&UVo=8{a+@7?_+TCY3D=s1zdZ=04WTEWicx#c z*r(EiIom5KJq3;ujMQ;Hi5>$!k4*}l^|24Km~P>=jFT%ArDD}i@Vlx|<1f;sHw$SWLFXX+uV*xoQ&-+;Kkfmr2a`K!|^lON2qh8UOBEci{O#U8AdO#LUnc7 ziMEVqX|Lg!|BI24t-ZXRVfqmNvz-dwNW``H*)fE~nM zoSUsSz3)8g+4*paV#s8NktLqja=CzcX(Ot+GgAtI;CEjuoFZq`%T<#3u*T%_DPn@@AQa zR3vW{NqMeMmWb;w(G~d08d1fwthj+K)+IQFq1mo{;(^o(t)W?&ZJ@dR@?^=qHP2TW z%NacOG>(?U({0fiYgOE;$48LjLKg_8b&~sZiz&U;PPPlYQ%M9keb1BIe6o9ef8> zJzCrR%MvhIBPbIsx_4?EmJ+zCbN5ls@IDW%z95YL-v`W^YqHM@#K(K%^hrhte9hZc z{l-t?qg7G?9Ip9-q*i(;1rs7X!e7x>RQXoWCj3PBKizFv&pQjnq@mx`3(pEYj{(`Z zdiTZJs>N3=d6HF{aH?fx8V|aaVQ%A#L9J-=i$;iFjgy0{dtVK#pn1sXzQ`yqFSq-R zE+XO({r_XM!M+lQ2QsD{ZJo~rpRvQubG1ss!F;9YSy2Fn2L++GP*->)8S0V5x(auN zFCpW$9R5R%oPWh|O=iV&g$Lq=FcF7ufg$uEnF@!4mG!jJ&=#e4(>BV>d*M(h;>(u7 tLsNhvoh^lQU%;jqAoEWwI_^=vs7`I4k+I~T-r$!oqvIw9dB+?s{TD0(gO&gQ 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 zcmd6NWl&r}x9t!J5^P8au7Lmv?gV!mJh;0BcP9k*06_u)0t9z=_uvk}g1ZIx+q|#p z)%|h*-l{uQQ-_)9K6GzcYwg`9R6$Mx{RP1b2n2#IB`K-|fxtZjZ#ZN`@J-x&;RCoJ zI0{QCBZHS0vT+FbjPE3-?)1^t)XCMr!31JvV{2_f?`Y&;Vq)WHZtHXe-^vdL(L4;{4EiGNbc(fC|}AU!0+?nkL>fd4t6=LG#ag#0T~{<*bd7af>3-d&^jh6jgdw ziyrZm@39U<^eGGP@rU3l5v9?jp>|?Ib!~x-sg5H@m=e-r2?O=0H|`Hvy2IB?m{)bS zEnA+$m$&TZ$vNZ!T&4K%I-j9$jwz7?pf3@Gkx`zJBRpN7K1)K8$-k08-{@nDk*kxe z#ehE{$|@!sH|W@h0#IxNG9fZi$o}9^R^Mc(FG*gGkRrTJ6oMc0Vbh`B?S8Sa<<32MXPAmoNOaZ8CN zfA>#%P>~rA4H2>*7A$g(!Ox@oy6g{F(($X{&UV>2ThslB@9Ktz^4H1pSut9QFdH!) zLCZKYXa^=(-)U`k#KY8moiM#D$=Zdv6}LjaPfF*=loS+s#W6YOMCw9rbMcb40i7VC zee|yP&_C8__H}f60gn(OYT$7>JQ7CPUAWhqm$9RnhMK(m3s#ivFMmh0NcrR)B0Mcp zgds7!mj93FVGZ5nah__bks2>iWnV2K&H2@F}3O{o3;C6-OFQ5>+laSHAQ zr1(^64+P;WNpEkyV40Y%o}L=7V+}8LCBM~^`y)$WcQ3&Sww?vv_-)jNv7OZ=-E%~U zvaFG}Md0vMs?--j#$@VZ(Q zQu1YiYs3`484`!r=@hv(X|-vnrRRG_?xbHk|4vC{XEHx-1cyM8m*BMv?ycnk_z{E) z57>ha=3w^KDAvnTys-A|#_0I@tdsuswT4>Sr{53WE%kQC`O1wdb#@7bb67KXG$g8S(Ar`um=DCC10w zwz9nqIL#>>Z6cfR$!*V7RQl(&1`DPN$p%)3UNA$&hD{_Hl#1c zq-|SnNrD-+#YDFlhDgkYd=mM3Ur8R$rwr?GXG{{W&$=!v5qqGkMeb&Q;#@FhVyg7z zeAyu~KlPNB^rLL(!4v(?A9L-s{(Kx=knSq+Ci<|H`s)`Je^QONa-ByEG*{2sG|MkN zm(oE|`If{q76wnQsi3IXK6o8%cS)MSM*0kr(VUC{_c`w(u)8j~`G5YxIvn~SMg#nlF-HB}}8*`f5~sfnhG{nlY_5`B^f zHcWorsv&0@2I@i*&#xz#cR@)d0#*r@OoDkk`>i9dUcJ(&GRK<8ku|zJTvVr%y(9u4 zz=bM1FLZtLmtxKt6h{Ow@N%h5sN+a zR@=%NC=Q9#<6h4XRf+)iR|pUa@dz?@_V|bhw9TQEnRR9d3$iYaZ;6Q?eHHbBmjAI5 z{HYubDgJ!a${n!rFN1RSVr8hQUFXyC+V6e=zBT9Q9IWXu{OHRCM@-x1^J$G%1`xfk z08rjK|*zzaJ@Pgm`rT_GN>1Av{DA= z)Ous@@@&|5apd!`6dE31e3h(({0HBz5UlmBEu(4S;Z{8?q z65yIEF{CfCNBORZ=JSNy7i6>rYCGHpTHE9wx;-b)Fc9s1EVIkMu4w!Y_+))3wa@eR z#XEX`5>=$o3093-E6Q9<3vn^An53k>c7LRl1v@bi%|Cip?8=Nc^z2pCRxdoT#%ZrW z!nhgVQ{QdY&xi2OU0iC>d>F4u*ext%3T9_tyb=y-zT#%3-%I*nfH%Ra)UWPkiu}X4 zTs^O-$m-uG5c+o^6FciOtr~50?iQlDMZGNu8R`w$v6J*@taipjg`fbz~2Yt@#+ zw@Z&Sty>^XI%`SuKxHCklcH#l#;jvsQJpo1_0~UYh{TqIlaBz<6aux)YaARLmYwN& zJkB+Sg)4p44En?)SmVS8k{7kU;ueRA3v5Ts4EXGa-#T}Tr}ni^ZPr>}eY{b0tjgaD z<&NTgY&DkE>kl*eUi1UwJ8SN#sm6q@@9|%u2G^sd^%>(b1>~P4B~hK5$UZE4oK~sh_Wa^P&9T@UBOXqYR{MTbh)mM0^B@@8dc!&-HR!?y(R9p<06za|KDs20X&!xAeDI)f@nD2xzRn>OvZ zuJ!~tKUSGx+7NTnTle$X-o@9U#_jMKC+Y$(C~bgVtRGoBW? z^IrMVGsA|SDXi(!WAJcyz2JG&tnD&qV#X)#0q&ZR@PMt@X-oFdacJIxN5&gIyx1^N zKmQ%xHL5{#m9e3&-!bTZKe5Y6Hgic+7C3@8S^QW1HCdTvvCI#yn0cGV;<+_SuxqIa zw%xtG4);a$!ip= z21Vlc{v@=8gNgneXeYuEw=(3s!DvnUR#<91^?_Xcz5{kh5ONw-P+*LUQ^{TWwd)~q z=lD!tUo=UUmy1awvF4Wq*)T0hbg=K%I@9gL?T)ilFFM>;z{R?>bacjh4=;#P8~&o5 zkK|;xjI;B8(?d7Ewd%={j28UCsm*{(=;%^zF@UpgYHRiy5i?w)HM$hRkIRrO!)cw_SGtJHLhzJD^5*M``2IiQ7IV+vP-`TEB zZ*)13%{*1#Up;-8XbPehlfoO#F9r`6zth>1&~lrS9CwjEC-3 zbLVVI?_1g*D%dVP0)uz|DfuIAx>&0>Dyy1O&ynxEpobbi{OU{|79zVJRDCx%C4!C! zuj6@cuyjiZuTbn7sT+1Yv>+-G zh+G7^}}PU628;x5Ggf=~B1d3?D4CG(>v7{9dqDCH4qSw1=%l-&`Hb98Rtd{aS7viJu` zyDhZZ`Jr>8-YmRGE&_rwVp|Wj^vy{%(q0o=GB)1hPuNvm`MbCXfW?MUK&bSlc_ggG z$8+uO<#X397rI1Hy^7+ty`a4i$d&ja7$RO!P+>L4qU*z*ssO`N3C3_+OlUXn+Atq2 z=U%p#0;-49>;8P^?jY3_IWi=(p(&ClA`J-kRTnO&g27tsXz^ybS9z4kqQM|#Ln0#9 z-AvvN-(0xvutx`%g-8;=Kjo_&6AbLkme*>idLs%kPws&+Hj6qqu&Ai0G*s;UsjYwj|Jv$w~i&cM=FPu%hf3Jiym*^`D$(GcNDq9tlg`m-9Hrm~5B zQJC@Pv?d;6%N5z>Evac|BtTSw}m5oF_)BBtA zvxE8Ce!upst1FjQ`MkG3`qaHu?@nsrIgl)Qa0uf_{E$6TIIQ2$(?>GsH2gSEl1t^T zzV&X9UvS;))97*#HfMF=G0N0sn6X@))x^!IPGoJT zw5Ak-Vtw1I>4ETuCVw|{r`o6|g8WV9#?+SvFr$0*o?|O)qCXIx;qvBAA8N_waW4Y^ z0ht>VjGmV^n}gpwXUmPW&dU_Mym(humPR^I!^5Z=V7H8n&-yn`%5r}<@=(7-c(Xk@ zIXPNsCa>$Z63A-vI!#tvTe~rhVSoDQW$6eym5a)TLUZNNmJ~GPXJ)~BLQWgwxeAl6 z?(S+23?VBktMGyN^vDl%5k3^+?#?!4V{Eg|ML9!T{rVZm@8{;`B8Wc3ra#_oStmvMTRMztx~w%nP5b0xyX2g) z(b2Z*8C{r?Qmoo1)Fc_IQU-Jxa`H1`{)(Zk=d3CdG?jDnPERr4rlP9K;=HTe6M?^W z-q%{2_#Dwc1||`C3062g{dN#!v|xTg*uaM2RDnv;(2}dde5LeQV4~ALQfRUvOIyax zbTm+BA)ikG+%MF}<)-^2CnY7h&~t8DLn(94PH*`9za#EjcXIiG82|i^hK7uRA&PU! zEo<=;#`N^`%gyfHPVXSS<&sNu)i8;yDs)<0fHnvLi#EBxeg!mpUV3;xKK6!%h5g}e zfxetRp(bF@?O3|e`3iLZvPH7ErY3<-ql?M-FR2DPUQKQ7h0Xckg4NAcKed|MX8(Ak zNN(3XAF7uK9Y;|_A3BO_mprDYf|?YSl_xi|f3G%B@BLe9|82#jZ~%x-NK!?mCz@1# zB#kdLG<2+Z5v2H+pr9H)(TOotIvZN_JgW_=d=_<@yPma^GV6Jcii(Pm=;)bZ&f|XS zbp6V?KTdLzNrC(#rBfUC{_(40G`8kCP(Zs^fxT-Pd3hm;x*qw9wJ+prEvMcKi--Uh z6x2!WPw3SWe3`PB*dw6>(Qq6NHXWOiGH`Rg=R6;eENA8M@38S`)vn4qHgfX>y14PB z$Cs3TX&|Qg`L`o_QRl`JnipdzI-y5tGQuO@Fm9WKB4J+y5m0g`q@11Eot>Q<7T({L z1C#rAb%{#DVDnCCW5%0n-6KlQ<^o8UUj850B96D^Xlyvc214PO0s{=02<&F9oTRsZ zXB$!3UC$Ek`1A|vv?A#hs7Or2`MrXrrbG@gUQ_u8c9CXM##z(3LPkm1Wp>~>psOV!BFm0{c^BdD*#aiDg~D~>M4yNdukzaO&oTqT=&G|{ zbMn>w>olX|sv~%aR~58*qR-Tp{%A(ki}?+)3ddgvEGVX+fESdsR`WHHe(*>)#-ERS zgU|_aj(HG=?B@=E+*N13v>_;(f-BBuzh^;tmJvQ8@c85G%qd4UWwTbO$t=x12=Hxd zQz9puY^SbPC#kPDPjX@*-JXyKLI>jyIqGm6`pjRye(7k%)lN0JpW^X*amB>M_;`-g zSmu>0IhRFDq2eMG1u4WK_#HFLPPlEvtEpF-Myl~(SI-2K`t*H!_wmcF>gvvd9Cc&u zUEJLW$IX2+AAx9+*25eq8%y&Gevf5ISeToSdaD{}-5uEaQgCu6O2tvZjplzm=DRJe z)4p4~d<^eY-I}>PH%|uthFErj1^^XcKI(L@3Z0a=?UjE6jAno{zkU10^l6%|q~&I} zhZ<9>U`Vf)T;~Guq0%Q(Z)uM*u6=ZMP~>YpVh0SAGC@Iz7l(@j&NDh&r}KeJ+%Efg z*VosxRvcCwgh&`fZ8o*9UcT(;#?_P2)+Pe9S)^Y61#mL_)tQZ`p|k*rhpqp}9|0ZX zkkIAoW}@E(8a>?yu~|8BL^^xdd8%7Bf)zHz=L zTHjHa4#%f;O>6Pbr~c1 zB2!XCHsludkby0+OvkAfd$49 zf>kjC8*f-yAABay>^}s&I`EnCsD63>=WWb3jZci5zvYHN( zfzhB5tDvCJR$(%b#O?eZz$6tN9a>!f_~UOmHK)$H@bD zK-!xrr3PO*?@eRj<4eiOVVs|zW8>m}R8>;r5_5%Ywl7dO(lM>xyY-X9skS z96{zNDJlZhLI?WQeR(u!RIK*6lw-EX3MC{wesz6zEsrzQXU%zp%hros+bc82-2$ri zx9g!+4-|19$?kbsHhwO>V18EJ^g|t(MYlrOy6Zd=8a~l$<|$guRRoqB z_5K4sV6vKJ;;>yBEHmh&p`)XG_b&g0(@{nS4Tvyp&x7iw$A{a&RG!2@RNM-u9i_vj zQ@I32yz`z*jIc3ebfhl?rJ^EJ#yJ!ngwM||Vs z<8C)Qxlawq%Oy|7($)v=*4xD+x9yDJtK&}0`kkB<8hZM-I3cG`hUM^pVSZ7C1dMdEa!5GnunP(cc_xvV)Pc-X{Vw3kACAk2_!q3;?fduA z{GYd=D9^?$H8eB;h>wDt6ij^ML?uKP@O3`;{~sQ)IdBmH)@0RhgMhLMbdFq8YPF6) zxfpO&3b+EOJM~5v`sdG|i;9V*GDp8*WQ@+t%!H_H^E|Z-)j&z)kBI(;nmRZyk0yam z!=Wb(#*s2qTF7)gP|s>D6XK$P{k(AMHxjAW=Q%6>J?n8S3UP#=+{*y(o?UHcMN7)a z7z6zpS^6$)+|5EkUA^zib5uNDHzs-hYvZ54&+tJZ`RwXuuT&BHIq=<-@?$-@mI#THv3RaZCP4>u)fj*ijFU*(t56`UZF}4)k+l-@=~hM2+s2OM8wsAB*MPFBq=KcjBGw7sc%s8 zZ%IjRa={P~1L7wCy{QV1Pso)Hc=l&d>Zw^AyZ2k#1d|E}ZsXw}!zm_yC9tb-gm(85 z(~krnCG9|?1FC79G^pZG@=Am<|0!sQpQg|CM3 zh3q=fIgdg7K2uOu?n38WXgk}P=pPz-{i6^AZhG#&I1 zit`x(*s~hHYiPNBJb4ClI<@#ckCGH5D+{3BXCmUSd}^k~KRn z^)h`pP|X^51fcjZY9eQpRz_j{010H&{^j{#GJE*sq_R!Zi8zPV?BBwh0H}{$51tV0 z3AJmi=VxoI2_elM=i|*Z>$53vT*1xNuh0157; z69?@W(PS6@8Xw=ybJR3(b*Y#oeqBLD9*|zWJFb7eJEg3w{B*RnJ5}iYpTTHfii(Op z^4}Gzl~U2t4kWSz{}y$Vam^8ONqIaM{^6NSOh?Dam}@qYMiV9s+IB#g^_o^@(7XUx z2F!U?sY)IY%{EWr_Hex=B`f=uhbQHO<0g~KzPiiN(hx|0_s#EScPd%Xj^1Gc1keU3 zENKV>T#txJvS8u+wj2U^R{!)Yz?Fxz^b0}`tG~Gd_wNAoF^at)_u4C}#iYj>c=sw& zj?-eCG98;T;8;MTTOFDF34Z@~vWS=r8Cwj+2}StF=@))v2M32FKnNQf8-5I8A&#n} zNbMn56a<_`@q*9}PA~vsI+7-|(&e`~|8ZQMn=!SjMKNKv{Qyie$FQofVeI zz_;P>xA#3QE!@@FggjyNJ9_%;vNGsr5ZKctIxs`SOaW=!OcCT8vMW$uK)#)nhhI+95*Ajg|2#Go$)=>+n&h9 z9N|4hgMeNYT#0x{8xNx=f^f*k=l44B #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