From c0abc2389193d6870d06d6b762bcb3c7e3be7b1f Mon Sep 17 00:00:00 2001 From: carlosgonzalez-elemwave Date: Tue, 21 Jul 2026 15:29:24 +0000 Subject: [PATCH 1/9] WIP | DRAFT | Fix tests after rebase --- .vscode/settings.dev.json | 8 +++++++- test/app/launcherTest.cpp | 2 ++ .../cases/multiObject/basic.tessellator.json | 16 ++++++++++++++++ testData/cases/multiObject/cone.stl | Bin 0 -> 205184 bytes .../multiObject/mixedMesher.tessellator.json | 16 ++++++++++++++++ .../sameFileMultipleGroups.tessellator.json | 16 ++++++++++++++++ .../multiObject/singleObject.tessellator.json | 15 +++++++++++++++ testData/cases/multiObject/sphere.stl | Bin 0 -> 25984 bytes 8 files changed, 72 insertions(+), 1 deletion(-) create mode 100644 testData/cases/multiObject/basic.tessellator.json create mode 100644 testData/cases/multiObject/cone.stl create mode 100644 testData/cases/multiObject/mixedMesher.tessellator.json create mode 100644 testData/cases/multiObject/sameFileMultipleGroups.tessellator.json create mode 100644 testData/cases/multiObject/singleObject.tessellator.json create mode 100644 testData/cases/multiObject/sphere.stl diff --git a/.vscode/settings.dev.json b/.vscode/settings.dev.json index b90d07d..9d190ee 100644 --- a/.vscode/settings.dev.json +++ b/.vscode/settings.dev.json @@ -5,5 +5,11 @@ "testMate.cpp.test.executables": "{build,build-dbg,Build,BUILD,out,Out,OUT}/**/*{test,Test,TEST}*", "testMate.cpp.test.workingDirectory": "${workspaceFolder}", "extensions.ignoreRecommendations": true, - "C_Cpp.intelliSenseEngine": "disabled" + "C_Cpp.intelliSenseEngine": "disabled", + "clangd.arguments": ["-log=verbose", + "-pretty", + "--background-index", + "--query-driver=/usr/bin/g++" + ], + "clangd.fallbackFlags": ["-std=c++17"] } diff --git a/test/app/launcherTest.cpp b/test/app/launcherTest.cpp index c459e88..0d656d9 100644 --- a/test/app/launcherTest.cpp +++ b/test/app/launcherTest.cpp @@ -61,6 +61,8 @@ TEST_F(LauncherTest, launches_alhambra_case) TEST_F(LauncherTest, parses_staircased_without_compression) { int ac = 3; + // ObjectDefinition definition{"longPolyline.vtu", "Cable"}; + // auto mesh = meshlib::app::readMesh("testData/cases/longPolyline/longPolyline.tessellator.json", definition); const char* av[] = { NULL, "-i", "testData/cases/longPolyline/longPolyline.tessellator.json" }; int exitCode; diff --git a/testData/cases/multiObject/basic.tessellator.json b/testData/cases/multiObject/basic.tessellator.json new file mode 100644 index 0000000..7923268 --- /dev/null +++ b/testData/cases/multiObject/basic.tessellator.json @@ -0,0 +1,16 @@ +{ + "grid": { + "numberOfCells": [50, 50, 50], + "boundingBox": [ + [-100.0, -100.0, -100.0], + [ 100.0, 100.0, 100.0] + ] + }, + "mesher": { + "type": "staircase" + }, + "objects": [ + {"filename": "sphere.stl", "group": "sphere_group"}, + {"filename": "cone.stl", "group": "cone_group"} + ] +} diff --git a/testData/cases/multiObject/cone.stl b/testData/cases/multiObject/cone.stl new file mode 100644 index 0000000000000000000000000000000000000000..b855cac399afee6aaee522a1e8efc93cd5dc32e4 GIT binary patch literal 205184 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

Qj6w?%hYIL1}#FE*+Sdky5ss+vBd znyO+>E6kFWIGoRj&0>_q)^1c5!Vi;S>A=vt*oxSkKCeen!R(^{`hL{KQ^2UoFLYerqD?8ig4!n|4;MTVZaj$dMhTe-hl^TJ!&*St>qx|&I5 zCRU6yDohjC42212_2H0}xMD3M(TeqRCxZ_Me0EsN3U5yBc$EEJaM0{CMGpt|k+F(} zpJ;_CYV|mAvFdk!G8Go&+*(EBr07b{J&qy=0MXzSQISbzCJw*7DxIt_;jA8&nRWW! zuYAuB#EW@2-qNR+$jeL|aa4_VE6kFWAH=j&e=_R7pS$`qX|iv3KEjXFUXWiFYC<9*bc zSBN$g=9Se|(O4^ry`pj3Yaq=f6Rxs@wQ7l_PEKzqOgO8nT7=PF>w_U#?O=v#=8~B> zav2xA3@c1HtH-RZRm*-NesRepGeyUpFO?Ct!i2MWoHSM0!G88eQO#UhnMg#$9uuM> zD@-`6=Wam8Zm*3fs+m_SqsXe!5B`-EZe{K^;*e+H4R%gsevGK7A}>>PWCzc1&@6?yS=W6w`}RAd5} zK1G9CiNlsyVKP|V<_8?aFR{X(yM9BeMacExWl#;z3Jm-(OIChxM=pI(2VjL;nO9?t zrS^Z2gTwMe98<;-S&P;18F=Sbm_AmIm7h8f;B~*_Pp2;9RuRW6S&2g|NUR3Wt#B(7 zuF)WOZJo)Hr5gT7B^wzpv*acXa(Cu_pVv@0KFi*(<7Zy;^|_V(hqc;w-G@Kd>cObm zsrSlXzqdW{S3g+9F=b5AS(!b7T; zSdAUhsm0i+&LfU8eO5+g2lg?eQ3@ zudHtQlK5~%wcqoP|L2_j`F|CoOrMoeMDxUh#0FNlm6YJh2+{qt~$)qf8kSt|?f*!Pp!Zp^i6PsV28vUU# zudHr6Mz~`gMkUXeaobKqG_O`hQRPIX1(nNIxRsf>-olq^iu1nv%!FI1#`$wt>X)Yv zE8NNyt?XdMYW(sr z>akWt^#id6j52*zMo}+83_``06>epU4!^zbsDe*Whp5PuG2v#;k~wIFTlpJ~XrgY~ zu)>6Mf9)KW#8EX8KlYZ_=gHzV%=?+5O_8&Zia1u7C04iSF84cEMM~-=j)@$K zA11@f5B9k09QvU!Yt7&m-LV1-Io*dp*XouRWtEIf>UVwp_irEjUtf_d`e&C|%}i|M zr2d15#R|9bQ)ugIRM27DtwM_rYwVZuLy(4?#OwpB`torP%wZg5;_HiTI1)sW~(p#)GU(NP*#xGt5 zmca^h*Xo+*6+sdY_MOnhTJzPU9u*M9S}V+5t4C#jeXCxJQLHsBP3rQg>)s{SCaf@b zt!^A-HG?R%(<}3Q#DkS1HQPs)^UNHy!lYhV+gIXhcfS?>Tz|v1J_yf=qq58U{C$7( zWx1iE?)@AQtNR;@I)B#+Gu-||^D@{`UMsxn!)&*@?J;3R5idNoC1>jMi8)PVdjp3< zR{Abk@!MGKv%+k*x+(!Lcp2D4EBv|shBFd=EQ-+oOid-VoMyN^j^XvyZgMN^4Ol%r z8GHsS{JH*3RbRr**0skZt9s|XUex}>>N8~T`58Ks3%A0pRK)>9R%P6WeH8aM zcAwSh-(+f`J${M(ht`2Z$XOx-*HJY?ozb|;*vMTBam&q^GkR8HmMStW~_U)|1uG#OSJaK10Q2dMW+ z#m1QmK-Sf4&rP3p1_XPBp@9`9gVp2dmff|&pX+bf)*!L%?0t=WJ?$%TX30u}zZ*{p zOU*pD!mUiWNdta)cC_U#h@U8a*v_vsU@sYQEqzeML0pDyx7A)PHB}sZD||RB+{*op zXrj(Z#jazmi6CvA&626$cUWONZ*{YxoC(NIi@*DU?~bQ*$iJV>i5JtNu2z(Hk&#&I zAF0XM5;>GjG!1OQTU?9Z0Hdt1<+Qr(QRHkY*43=AMYX!E55Bp_od3=vuiO6Aiyw=B z^119cn9-f>d{c1320nuo?9BbfFMmVsg82NR9=BRmB}c6E7HeIW+E#nD)UiBAtPg9D zR=AbP(5i~@#F@3p8*bFHW7}7IKBK!OgJrz3PAS6*`AvW zW!uRV)vkCeZ0D_R#DP&gx*L7?bNvm+Eipy4e)@0Gz;=G6LBGSTRz|Hbar_P2$;0dz z`CWNdR`_$R9;b5E8f4!IUHmXJOrNm}`_gad<~++%m_Am|c#!=#y(*;mVP=>mAeE8NQ5 zZBJKc_K!Z?%G915WDbV2XDTFNt(mxTklHIEFZhvM=y|W@G~rgNrTVj?yI%b{6mDfw zH?NP#tH)8NJ&rQ_%-yMgIHA{2n4(sXQyr`Bm5O^-#2}}cxN?wNvshKcha-y$kKmx$ zXYL*jeFdvL87s_0t6P4T3LbpwA9&F#W7}U=95gwtZjl%3oSc+yg(|r~8-K~e8=D#wHaOPdyE7qE#D>*r% zh{%h)s1yFyX8o_NrDb``)f% zlv!ci|iF!NLMD+O054RvK_#zgFT-``Vp}V|9zwUYfa%r=PfHD9jA2*UB0z z%o3~T-ag((_FUyIh$o95X35HrpNYSaky6jf3b!(^4hQmZ{BnK=bsuDfAdV?xUX4n? zVw4r8kJamnBD8@BL|!JtN*v-%Vl}MKuYK~5Z^_A&-L>U3eOh$Kc``()R@eqxJtHqN zkg4KGR_zO^5KIdi3xXs^i zQ0+GRKdf-9X7zZw6-~shZ`285+ijiAl5ws<*>)@3%D-tu+hr52Fd5umyJLjtj{TfO zrc@b10~27S4`&Edz0>mTQ{h(budViF?DqTarXn%UppW~D^~wCeE|PB#W$HvL_L7-0 z=GDlT6r-##udHr2{Uviy@nAnYxEN(FnQ-Hln4+2$=9Sem@*;NbyOfJjri=+U&cCW% zDOQ+QR*$nLD_d)YKiA)I>;<@d2~W4}J}avA6{Y&~O11qYHs*&FX05;B*r8B6My&AX zT0K^D%<7UA=C0K%a8XOG!PJ0|>!mU)xzKK%sKCEynbGOB6 z#0RVbP;pN^1{^f|%-y*X#~QsAW}?+?XBd778A!5^jbNX>WA>S%Z7=e^N@j&yxxcZ- zQWZj0n7i(8?4qma?#;jbTehG3U%oE9{oEZ>bY(5SVXu3&!mZ5q7Tw|P^FDI=Z|Y>) z?K5jccQ}62E50H(RInn%u9tn4%nHYbtD3h**&YlU06 zzi~Fzh4|x_pZ)3gP+!Ro1@qOUp6B9_jk3brwL0;6VguH_7$yBItzxa2xN>lh2YDZT zmszpTyjrQ&&o3#eS>aahuYA!g6(*e3E$fWEqCzOS z#Js(B)}#ryQjNKRg1zBZxRtrvVoox5yz|sno~e+eF2f`<6I)zM-7B8H6=tHiQ}pO!Whi91)}mw{ozbv2{b?R_<@aQMJwq10fq6G|5cS5fv3{tuPa< zZWX4SR7lnRCx6S9JoqPyA11>}1Kys_?B5d46b;NMvuj4F4G+MCf&!7qN88K%!<6i?R*xAHfv+q2n=T(z85m{;y^R(}$0_g&G&DAQ*p4zW2| zXZ(_0XII2AGfcQ)uj+kR;a2Xia^V;D1HdRN%q#cTDjL~Wtre^yjyY&`;|DjgT`SBb zt4Ch6Doi_Bst+^K>Kg4Ac4D)dkv-ez-}*;ikUK3tddIw4sRlXO?M;QL6>hb1cVD|B zw{zstXOF|)h)Wq{c3f0jTL6%s^%nJ?irzvZ+z(X z_Qkipr8sE9tsI2sM37h?E8NQ7z&KO3Vzp#Z=E7Eay7k=)9JY#)KPGt6dOQm{(TM zSdDyps^QOB-zr9#OJ-s_k(BxpESweQmDMe}!(Oq+B)P=Qjdn((xn#mMFN5fgcW#9V zXZ3g=<>{xI;!I^Bl^mvw)gwb+6%baKS5^-{v8wJ9rJVMaICIHNob{sY|F9yI8HBUC ztte8lP9&VYW4WQ?UNOpCGT~aRw&z~0FyX8onWAD8^%7*dT7BXq+)A~08FgbJaV@lg z2oT3)Sox8arDGzN8YO@K75_$Z)E_FlYs+btw93q6KQ?MutgsEXdPYUWYN=&N?Z->Y z?%Hyi46P65AZy!J*alnOqN2YTua95;N519{Z2!Z*{KZ>8`}&9OIw~?tT3(c>g;{F_ zHP2uCz^hZsxvHmvT%Q$WL0&xtelYC{vi^r;oBiU)bRR+uGLH%37Ncpj@?yS?Fu8D;f6 z$+O35Lt)BTJ+dFgDEDDrSv_u|DRTC6^NUfY&&nura73xZwN|*5`oEm+qHsGMyXNtB8I##jpeXKA=t!{OF#B0QA@FB=7ui*F-kkYn~IaR3vPLNo{q_ z^CPhu7A{r&*#+@*=6T(PJu;q-;TmgoTiQ@KYRvD*4*7?2Z^nBQS+WL61PN>1hdiiPdU$+o!m zGg17rD}_A^_c!sxmBAm~boA@U0lTtk|nBwdEf2iNK?jyYYMVatUFs_URm9uJF+bNj#N{e_x)<-k_lIJ z`gO_;-Z!>XkfCYjY$k+SU-DOQ+?R@Z&owQ30mIa!SA zMfSv*i7N*aL6)LNkVD~Cj$X&jau@a+^mC7koTjC@JMPD+8euC;QL7J!ezV+-KK!}< zP2)VWQuO`V#XEC%WovOlqsA z&%pbz!k_Ek)FNT_hhwjLd{#VnB<$#Qu@lYmZe_NQHI~Y3v)6!C zCCF)tuH^ioPG{_{6>jBkICfzd2g$oamsY(o<4^ZjwFblngR9o z`09UJ95nl^t~q$*3&=~a++`tWK`9|S>dQLV|n|3f9DrM zpZ6t#CmR8rE z2JQg8kh8SHOtiW(sTDzDMMIX2+DABOCazTbRGzHX2ZzF~%-va&O5W!``i-~eZjPTT zPMB~jqx!w!wa3H?xAHd}HwjdRzMn_cqqxZ>Q?%JDGK6>;R+w;(Slj9nznu4Bg+JH7 zY1rM$Q}>$x;-Go8G71{7cbj$2o@tCP`tdvFl9||YuXwNU!wU1t>cOZwfu?im1f$F) z6K--)F=r{<%Kgng6xWu*Omu&Z&eQ=Ae`G(ynR-V!Xu_==*1fV4XGJPG21Tu& zaV^g(QQT=?Q8fF^-FAK=nN(sJm4o{_Csc!IaEe@?d9_lF&);+U|L{#O$mhSiXka_PTI!6Fx*L!@LnD1HDvon9XzATTMG3;`FGf#~-IXVzI5Y)gIN+WvOGGhr(9d>dH)PRO(@=t5Q9y zBUox%?bT94W-KW_oE2_mGE|l?I$~M^r=~R=Abv)1spM`dU{@HGFogzb!N88EzRHELr(Mj6#>B97^^5{H%7eW#ukZe=EpNVs-Lr#2#4=N~UdnLaC{xQ#70 zTkku=hQh7P#1`Xl@)x@dn)GT|x@S1}HC8CIBZR=cybDWB5=G98Ip2exkn!YEh7-cS*a5G||ddCX$%IapRIW0uh zhVidHb;n#X;YO83)tB@) zxmLK9xm#!B9@#65znjX0Es;a=#zzBi0{`Km&;I<}H?gY6`=~no5H0mSf3c`$!mW(Tnp&-^ zF{7+-D|7d7$X;jm%2afhXT9z9iRRTxwH}}M`zEHst<2pP-BA_aZ?>*8u&A3i$;`w? zPU;8Y2d8CGGl;^i{0&Dvb#c%NGtvExbEB%3v!Cl*95nk((Q!Xcty)@Pidx<5ZoHYg z6|wI*Zscs|ZPlMwauN#?LH66TIdSnuE0elqqlgEIih4CganQ6hcLzTzKDWXYwR&XL zY6njzS&`Eebu`%&?0MFDR4Lrbq;7O3=7gNwV)ntm`u87eCuYs~)BO#_?Q4bkYPOGj zV!u6N_F+<6-MkDe9KR!#BIiV0Ib-CrcT8k^1I=e(m-My5t=wPh1Dl8k;6BWD_cx2Of#H#r&tQewZgo4Sh$^~X(RkWxAkE#C&e&W>QwvYlU0+8?I3*xkLyC zYt2`C17l}cWrVFTsjZ&XpG01{S3S2*-7nUfyDK^I`0x|&UMbwlY;RF&R%U8PZqC`= zsB0%XXeO@YYGiM?74`<)Uz<_H zIar_Yu1+|H&gSk)=j_>L*4E7qf9aJ!l)E7Q+hVOLy0Vs&rtlfqw{3-6neB7!j&IdZ zh%9oNmL_$}!^I!1o%dFl)K(8GS{^{(Wmc>;Umfp0x5C`Dx>cA`tB)_*bCdCR>xOQ2 z5>#YrQjbcGsz|ZIq+VIeyI|JxbUTYqu-4pNS(_PkVf~!^ZmRmvR2ITP^J=9Ueh=1% z_mK=|{I_=2q`73m4NsR=0XlvPDE*srWln0Z3g&?8o5M$|yV~6s?|mD%{FU zY;O?18lT#IxRt-*sMxCCk@)PiuV9&1D+jYuT~w>}^AEiG(X22Oqf8mATU<*W{k6h` zv%2k@peB_H2&x~5xL}kyX!YShEd%k|wZbH``fy-P4FylveVA}o4|{ba9)xI+jSM9J zyAy6@6uEZl2jQR8&TZ@oG>DF3N z*s)P-2S04*R~qc`ApBqyQZ0vVU+wvf`sQh&tW;ZJGOTJ&@W29LeK?nj+BfRvZRc0p z&IumSfPA|ZZe{umuWyg;MjvkFZ#bhn-5FN=Ff+`nu}`=-*iRTPg;`>C%?~nOeXnJm z(OCR2ajYJz`WLKEKZm0frq4>8ZsBh9;a2Xi*Xr-VJQ*U$)MT8K(adV{=kA!6=5F%?V^m1>-1#r|;mNS^Yv*C(1}=I-IZsd!jYMu`<>qSb8;5~^WE!=n3p ztddD5z~`9SW+MJ}cE&>0`eE{2*V69fvq(hI!RuH59~AR+v6kH@l0^ z0C95qbLw&4UBoffte(*wG_b;yvAWg`D!PLPR+v{-x9Bc+PM(R1QmLNP#eOKG`seSM zS2r2OUN15%Ryd-|o6SjMpUi&upHFm0zr1&mHTTvr*av~ zkgP0cn1nnpxNDyKDYOU@?-ia~VZK^D)@$oTIV;Rvt7k1Ixr}5{H>&<%t@&zipz?6s z=23LE!lbr(#%eE5AE_hRs8WZ{j@8WemYHDxAUwCiq_(;^SgV$dlF+N|#15U!-IbjE zw#~}De*I_PnENK)T^uwm&Gxoxi3h+gY%5Grs~c-`FF<8E`>9pMLDSOeaUOE5@LFMt zT0OFA7wX}9g?e$&yjmFr%Zb%^Ry{)(udiT!eQzIJ!o zSNlvg)2`O;s8!q7?uNo7vwEye6rB@c4TYI#b)Ce9hg=-Y=_8NE!z&J&eWqwTyX`>= zw{m}NjfJQM*18XK*Zs9~w($TUC$%NSPpv+2a(87daT*HZK`Y$KY#%XBRqw!ASV(rz zOk7z@R-4m8p>tOF;#WUW{IH#0`GMcV`XDussr%Rx&y;PqJvT)U+z5SS2lJy>3fp5W75R%F_|Q}QnOQa?y;z3he;3d4)|JAo%&YONYQJ;Xk^LY4 ze(}T1Fn!7<*8PL*skXu_v3g_;_NvK^T128dGh?L>r|(m-jwh3R+fbN3R*#Wb)t{Vw zMFlza?PkeJ98L*=I6b->3b!)h#vH6#&VEX75yvc9iPO(dys(bb_Z$~L%nZ|KQ6trg~#)gzjy_}mJA zu7A^5SF82GPMM;bIq3dcJjki?aL|g*rBbgPWCfX>*jkCZ(aQa`SS@Q4w|O6&2l%6Z z#hx;&h*Z!dMzM7G$nsZ{N*sRarG0&}TUIvzw ztbr9KvelKpx?mH@q*`H8TRq-+WjRCUGxe3w+1y=O%h`#l%slP2oTjDK?F>_Bz_&`? za45_~tA}T~$AkCxc+k9BsYV1z)bg!)AJosYMg*fw853@tW>t)`!o0G&vN%UpJ#2gGNH%IJVXgUUQr8XzMq<&~3Uk-$!y$W}D;^}8NW4@#m|?AHX;N3L zMoy-7b68KYr z-0NOhW9ilWMNSj$Ce>nh&#L8&pEy>@iasmV_zmn}CIi`R_zB{e3@blad1dTk+f&tl zrWO$z*lMpdU?q;Kc-~;LcJY+jsgAbJw(~W1OF>513fp+uLu$nVBg7+nEN!6$_Vd^eyp$zUrS}k1yck!+I99lodDXIm z%t5SZJ~tE%!H zXVy9QxNRzUR#p&urgrJ6pMCuc7j;v7Abl`DtnlZWKI46q7akI9R2D)5+xe9StfgY* zuy9tmm3cKr$zGqh?e&S~hxG+iw?dJs-aZEL zFtx(1+~15j$yvtF(EU#e+STDRP>kD>-|X^UTw=!mS)xwpb1O-d10qIDg^&A9xJbn!77&JJ0J}HBNKb z6>ep=w^)suYUrFwk#jPE8TE&|=I%<)JOD3p z3yIZC(Us0TC+s3)H_WKhhasm4w~~|h&6*VN<8?p$y4(fvWKqrZS*gYf89b+te(GTn z$CNSQ#>vA~XK97$V|BA|?8L^l_d51shACrSt*d=jm_AmII{nHg_FPyI$ILLVMqNgY zb}LLDs~d6HyUiL)r_2fBm>DMAU{v2HT=8HjOdqR9WzEJLOpX3ID@jEhQ^tfl9FoPk za32e^)(Z2=>UMS;JEWOW$@9e7tIq3doluE6E75-eSXUvHr z6&ojN2B{V?m(0XT&hn6@a4Y|&xpNZB;6BVm_cy%0iaEoH)4ob(CaxSz&hkvo5*k?H zRwng0VWZ-~>^92{73?y7{$2BGr5g5-+$&a;3JW5xR$(|}Kof4B7K)c)g$cJ(jhc*| zoL2aA{SDilNK}8`&M$|7kc|k^>{}^HYyKsIw8BiZy3v5@PkwpUIap`O{P>!?w(~0u zI&o@MKc^&ZiB2*H>$c4iO|I<98qSe(g!`@2Lt!$kww-s8cT%T#xDU57MO(HO@0ItF z`OGyY;v$f0m6GjZ(4xuI|?f5W!ozH>0_BStR{nti6|n1i)y zX@x0jb+ZiQ`>4O_X9pKKO-plkWV|leT`NpctH()GwQ3owz*$j6PSeudEiZ#JmuuD1 z3RBeTcINWFYH5W(*Wa*3kb6yPuL?=_eAGbJH#DhReF?R%M37dPyDOdXd)QIVXz%nY za+u)%Ce*1Ukcd!fT`~GH1>k>Q5>;@&itL zouzrTGK%MfmAh8Bl_@&T%P6Xa6ZiM%&V*a3mgtUsMU@e@!mZ5R7D2LJgcYSmpH&_> zXkM+1+AB;U4jE2Z0i(>Nm5Ib@>?h-wTVY;V-RebIyJMbbm7RLEVw8EcGK!T~`e7~A z3b!&7TkQu`tavh3xRt+QiwD`cg@2W*{*4+wIB52*v0CyaM|Ov?7r+X)%5Jgk74N*{ z4yKRZX^~g%sjicBtS}SZ-;CAzJm=(y;-E=pw$JrJB9>T&p)eDzZt*$xipVP&&Mn+1 z4w{K82m9{K%A{K1R`v!aIr$x_3fZWghlA#l`)lzamKy70g(+(F;lN3V*hO-$;RLbm zp0gZTRdaV_SSlX0!h~BnxUb#u&foZ%CvtnlTZ(Gt)k?LW$q0e9aL@|3GDYVZ*!+%u zN>(w$lyQIK1XMG|9}w;bk1j;#grOTHU-Q<~lWysopqI z#UH;XXXwwYswp~aQi<-YFh#9y=W-Gc@^lkBo%R()lg!+$oMo-0l4r2OOtkuNxE~LL zY%L=(HHa^z$`B5kedg{wdk214VTxMaR(^<#sLP-xgZvxhG!s{Ha*_^fgk(9ba4VC# z?Wu-^a1d%jHljO|%>50!Tff{2Gtuf9K|<%u+W1zVDB_qhCiOV`vvw$0Vft9zsHSu1 zx5PFlA}@2$d~Dx}Gk&>4Atz%Eh3T^rXI~k`!dc-~?yr&albM6O^W^iXsCt|@)8zDT znk%C`D=QqOT0P!JH^t8A?mX5y;m^NH>lCB5<_+4H(RJAM7eiP+E-ZI+rn zip+B>OwpB`J0HYaKZA&yzH5{Jw=wEz&u3b!)h#<}MeeQiR@o>oPcy$sR8AxjMS>=IIri=+U&daF$t`+8$)op!{XGO*fI|wUalzo?> zp)gCVZha6Rkjt>bpBvvNH|+jQ?t=Juj|W+a!!jguPCh(096iDrrh`5!aj3JzW)T%7 zmzb)v7ZpFul9eAkU93+(opLDL$`l{j9%NYg!D!F@oE4?MQT{HYj=GWsc2vV zm?h0pv*(!%q!ng~)g#6^DJr@TKg{nNpZOR<~X8tg}!7k!twd72j?E zGiA)Hrl2YwyK9B%V|CkOLe>j@B+qw>RW9O~8Rpeg5EWTr`dB^VC~SLb5>HeXvVvtY zto$HTyJzlVUr+lAmdUW%_PxTCUv7n4nQ(K50(RGmwbkU~;qbm5N5PNOEW9+CuHuK8 zVP1`OgNkviFiWg%#KHFRtzrRg5jnsQ6UXZ2?~;4PmRMoRSUvW6-RQ%-vU>RKrGQUR z2S%AoCfsu?%qy!~97Vk-za!%+BzUrDU;>zMGgiX`u)-{{x>og%x!?JG$Q#VLN?Sbq z6Y+;xveJMj%jw2^D=XZ}Ol%zdA2U*zQOPvkidX;mlP`PmvUX>}4RYSzZYZ9-m73Id zU8`s0#VBzfnNd&YF03z4j7d%EP|w|fR9WP?Z+TlqHS=m^6j2dAFBUF7=(blInoDM4 z)qa$MZ)JrEXLXBlc1Ce774>{n-J4e{)i|Fq^P_T>R+yabug$@ooL2aAt)6idRTlkx z;^Ls$XNr#7RVt3M!c4S!)^akgl4CeE9*t8xiis-+@nYEjK~!XgTdmyX^Z<5l!Sh~O zk#|1o6N`x}2jjU`tVTR&grp3V2dfVgwXMYEOyqOFdwD2KOLKQ* z2Wz#j)2sS0MXheE<>Vo9WaRU2|E)LYMAEu5Y(#hFZsU2p*Bc5`bY(4bgY!kY|o-8MHPVOxu zu~jsh&Q{O!Mcde&3RBeTx!U)5o>l0T(_vcVWhSoV%ZxFUSYbM^)aIR2VM>-Wk^7c0RCG3x?W2qwK*gL^nC(`N%tTeO zrp9BVLXw!%47WE>y9bV}LE=+eVYXY{PAX`Khtwc8t>o4YHWiHPt3h@GGL%UjMD zeSNXke6=@Fw`bS>LE>5~%w4NT#;an^eri?m)wDFJ$Er;EK32Gu`x`c~=xl{a?f!

X0xRpuWc+QNXW+F2uH~oFM?mjaUR}M0!h-=w>W`$ds)T0WiIG7CQDRZyLX(q1Z z#P`{=YF4imv43W-cP57Hz-v7E#t-pGf8rwJAC)OzDFv zj$L6UuH@`lwPGy{gi~Y(&8wABd?VfmedJrD^}`ugFDB10qN3FvqritF^0LCMRvNIz z$*;eunE6X{x9yyyjvg;OpPQSS+UZiZ=jK(5Qkk`!6k>(#yw!u}W!q!dH){B>?Y7#h zZQpa2)caWBR%S^%!HoC=MkS}W?bU{+f%|KFU-#^w6(++<1K!2H5@&^5xxcnjy+@F- z$h8xj6(uvn^eKMSc`^{k3bVxO@)MbZ#gBeMeeuI~ex*S_FQaUGKjorqyRG(W+fl>| zC~{iiR%XelfVli--e9j=DBEs3zuI;z9Q`mqtZ*x{WaJa;Y}{^ZlOcTGUEBH9wiBC^ zi^C(c!mUi78F{g9J2N&n7XG=%@{U;r7-U#&JMTMw!)<6_gYHtn`T*J}kAZ_G+nj1?05CtxTVmJ;$E#J3`CTUTtVQzgp`3B+p)- zI23MWmQ++!Rg*m`%D%f671^GfC2iecr$OSVp)eV&E^mp-v&s%qsm_`|G%x`ik&X59 zdb(Da3|6mk6q&7NV;{-;nAu3PWca&9H7m>#t6LmJoh7kBY+oX;Tg4CCbJOQK@|p@0 z$6RR&^6fYKn7nd-Z5JJtnPj8#_Ht9~x1XqhaJ+LfDq?pr6^&MyGFH!XSn#Q>@aN_k zZW#l)3*yr~^1{==7q!B?a(^>oVSXf+$k`0VC{xCSYo}~-nicV&73P)IvuYCCo+^Zm zioAB>;^fuJDArf-F8Lkt?rV23{4g`jt2Wx90nf?`v&8CF8%6G?ud9td*3X^aa59Q$ z`lG=R*x0;qMG|K;jC`wC#GVn7}ZZbEUKAHX5u(UxGMXt zFyXA8F%FrDzEdl|{Gq$%k}3M!3KP!i;kOr~dhV^LW-ghcO|h>6SYg6hT{Egy`xLR< zhic}MDLOcKx>lHQR?j$!^}*E7pQy+qJ7|)bqK)U=#{$)?FcYnAaTN9nek3y(52Ky9 zXp)(t?OCxyfj4M{Te-hc?^86e!W4CXqY9~3EmOaD+V_%~eI|9)4qm9!Cyuhh+_ieH z58^X~MfYRbT2!Pc%<6XTpw{k&!hAK`n`a3*dAds<;;z+)1E)qpXWn2=A4x6e6UAEd z)uf)aoYx9-*XnkT@V@#=O$HGVk(cRg^;}VG&uS{n-IdN%f9`iwxevE;f8$1-A}3^q z&_s9Ux%(SY(UDlq3bTFX`TOGWJ(GWLDrQ#I{mnl3%0YY*c#gN!&)F?TnM)?zxbvl`W`%iW z^*G(QPV4FWfs0Y*k_mS>aK8wO>TC0 z{t^$GiH)_XkE*j?tT5qL4)QKIGm{yW+LBY^r;6@OpOsP6S+b)Fzdf`$Q8nIohK($q zc~$e{vhQBD!t}YxC{B*RmgEt)~^X(qtHo*}aT7A6|Hx(v>)#U-yDVw~H{PM(u zKhS9~$*|IZ+%A=w{pk*cE%j=_lV>;*c~OC#^JzD#<*?MY&TCWz{Tc1o3fo|-=PKE? z!k?R6R@)a}^<&vg`fMVvy#|sPCpox{5s#&|4PGrZtGk>|`NAuOTbT^)>5`429xn9- z8})TWMYh`McjN$D zGnPtWJ8$)jaft4EWp_s6^X}SeueQBsquTj!?evRMxRqJb{C0N!vMQ4dL|Q*tmfCiH zwbaD4?EJk}xRqJbY&-jS;d!^t_p$A^=iO2lONMQ)I!h}U^*b4?ZuKRM6l{B{$WPSQ zk-4*-Uu`=%%fAx^9JIo%%#zCDP$yH>3CY@>Vq=R2CV*Lz_&jri+!D{q3X{R=wpN6_ z!e@wGI>n1D8raUSH0ZuhS?bJ>`}-P;?YYS?GTRkRSdq~_$Y6CIdfpzkooAJu*I6ri z_%^oP_T1`L5=lVB|)xPrE6VW9Pz&#_MzhkSt+IF(@ z*b_#(6>hcChjSBHt^OVP<*~1|o0aM|TkX|Svu1#z-#|PRZk4e+STg*QJ(?H_xAJeA zcMsrS_48@Vw%g9HG~lG^czxyR_r0OXi`EUq$!k3S1{p^7qw=Cwm<+3Jhs?Y|YSj|) zo)W{BZMStcONNEJ;I~_0GFaVWgZ)g7ZaFhQhTmTH+$%zyC38yRG(WsaY#x|7#ytL$S7+ENQp6a?&m9=UHt(Q9F&NZ>zo9c5 zvBQQHlhhz@)DL0XZO>OPoY;V>7ghnRSX$&rZU(L$swZdd@e{IEt_#E4w z{f-;;L)dm(?bYrQl& zySBlrO{B&v9>9e>^_RZ=;rGN_DywGuxZ1>it8RI(R=Abz{5bWren&rbrmU##qy6g< zJ70*Mt#B(_?J;XF_;6TI>>}9@+sD<4;)OGF8SPfMm92L3!toh+A3bAUR@C-!wW3gy znTx+`gVr?odzam{MP02Zu^@Xo$pKj5RwhHsk>Q2&bdxbijML7Cv*om%ZyaP+@vN+{4Ys>iPzS?|p zi9{^K23EM0`)k>APFrBTHm7}U@j=V(+Sb}?H%}i0mf8wiZL6mjj-QzKy~XyHrM3-T z?Jn6z>rnz=DVySJlS4++Qt9^yn3b!hPP8!7J)3bG%pB_p6t$&COX~m6H&r4PLD%3id)`cdc-%l??zbc!U(VqINwktmWJFMMm6Q4`rR<_!!70nyGpC?%rrnXV;Z}<#l6Rohdv$`zY z3G37S-B`}%@7k)_qBfs;w^y-IQcU}3_4LAdx>ndG+VZt}A9#*0no|!q>OAl)?a4;{ z0PmG`H7jffS2e$Z`*FxqTj9@5wg2{KUi8MC{{6Xl$nc2>5-VzjEvoyQHAQeR@y*7l z!{4=iTx}w`aAqzZvK4M+tKIheu#*lNWW{ZxmINzm`yUL#TJoV_qtxSe-&uC?FzW%ch<#yng zmEErEVUK3!B)@H-XKno`^)ay2CsIP-$0c&eOTdEw(~O=j-~F_`97A~HkcW( zXGNNVopiUZ6mDgIeys48rH-vS#h#Rul?6J3l>SZ2PL1 zR@>^Sz+PElOTAifsyLL{9^cB}#J{O|ugnqr#H;{p@!iTM+N#;2juXskmyi{16{AopskW{cVsyT5CN?W5IgPn^8h(T6|R->`YF*mf!bGk&rb|CMDEZPjd1$G9q+ zXoc;g)#FT#%C~3qai&w*L|ePnCT9MXzsuOQ!mVtzr}v6q($}`iCfcgmqKDG48Hh2)H_B2>w`)KvFyS$Is!~1!2WfN^htsWdKOKpWM z>S{%IFC3~_;a2W%tY%bvo}5Lz@K(uTtLFZO&rm+K6}G5WPrtprkJ*RquGP&KWt8wf zQXQ}*(@|E`_HnhM_`CSjtkGLxA2-#STh=;r9`fgVM)(uy1M6djKi7V6s}|86Bja1S zmHTUVnX=~xOPzf_sk;AAb`Kn=ryf>&wbW#yP~e4I;Z`O?)v6s?!NLQ`x>M~f!cyA? zua=sqOLZPTswA`?BMHu_}QoP-peN1+O0N`cY$rkidx}Tw%Y5i z-+l^E*+g4W_t)<9#*br^Sizj%GYD4Cy~zA>D{N7%o|c+f+v~;3irP|JJ!Wmi23FYC zuIfH(>vvdT8*KGhB|GzUduB5cWZehJidk9I)ryi=V*e7ps1eoRv^+I?Y~{UHA8uu?j0{Uz>de~Im$bV%Y=c)z4a>O&oHuBN zTbT?q>&!3DIJ%!-T$b7vb+yzz8?{$Q$KP$$(YBAP741B)TzGOMLt&d}b)Bnr)_PRsUau9lcJ6O_mfh>SwR?TGU8}u{g}c35xY(tk za4TEXadve1yZvOevT(LUtA!&%#lrD)`~HZsKDIbkkE~kJzzW+Ht4D3r1s|@jrIanP zl~^qUE0&CSA}=f4%C>97wN-WB=UvTnTZwg^lcQ$01-6~vk^3ND#;!)sR9m94N_Iox zIdAniJ*oD}B=d0!D{7v5ZriRjKXKpVZiTIc)y-aU;t6li3V*J@VNUBwu7sTsteC`( z#7}JdAZZxFUxDQ*O)he+Tg-1pPGN(OlwH6%fV~dmDF?^!tE#U=F z?_-5qtsdEa=RFqA3fnIC*ZN2$(te))_*VYhRGn@=_UqrBos);Gv2ubr{uOa;s_Qq{ zgoU$>vVE*o%Oh1nJS!_~AFXZ~NahGTC$k<=)%{pF+pg7K;gP|qmdp6Is_rKbXZvB> zRWWBN@W?W5hr*V~>Xsd3R#E-=j$0pjniXRzgluta?Z&xLWv{HT?Xr5V6)|i3uIRE? zwoz7(I+^l)tg!8}dQ|pb@ML=ZP1!43qSg9v;wg0i(tJOZTPli=}{E{BUg|%P9 zecQvovV9zu;fBK2&gx;Ws zPL;PM>Yr5S?2lEl@?`SM(gma)@N1B7}xgk+P|88*!sA?mLtH82trb47Dy_vP1!RxHueV=QETiHsqGdcF`dEOe{ zWvz31Zre(XYTU{#S>ZWvbvtp9k-{ipPbxJZ%yZA+b)KiHm|cx!8LV(C+m&g3SQECw zt^5sJj*Q$79PDS|lx6UoUoFGF!pl7GyJ%{jd#2h-j9sSntgP^yw|b1kvh5jhR07sK z_ncqnIcrg=VW}*q6>eoqH0I!bTN=5gn&+OWwh|-8>8r?AJFDkfDt<{kNOFp0uWX4{dzCupBlXU#C|coGwy2fGsaUONaq5H^?3L|@ z`y2O$R|FZ~U?^;ftgbtFDjS9M`Q>MS`e|0+sLQa$v3;C7k(g0d*mhYx&u1Wx>if*g zUfD+3+Km%vF66?kue%W0sY`a!_wR=naax2`*{f${$ zRg*pXDSMUqn3(DpE7G$?9oh4;a8~rF=!Uvkxa@4Ks>yCMK8S_0RkM8@7Ov;7%Bq#Z z*3Rl-sVk=+`+7fXfo0)reQaZ}*Us&Ne zZ*|+1LQauyl^M%CtXb=M=Q%%pA6E6P@C>%P@(k?fEOO@g^x9YUl1;D1>goG1uB`A3 zwz_#TWSzUuklm|)uV$@hs^@&GZbLyI>^$2Qp21evtt5527>w#?J=CoAO!b^^dra_T zh`g-u47R$he-Kgdtr&gG1!k>hs?}rtyeym*o{v_K)umGO6`sVkb&pBL*TdDH=lopl z!vnCw^YP}a#S2$1Bk{o_rm%=l6owYn!Y5}hMa4YxMECW@h_$3)X z_mi7`lqy!w$8`=eLiRJ_SH<*wY$b**xlIn)3R?#E*Y>Ni7a6K$T{}0pd^*4Ud3Qa7 z*Lj|AReKun+pWlZ9@I|_P;PMf=Ilj&UOa%T;nbc6yl^Z0x%MSmvVznil8^sD%&ogAr_Lp{^ zk9fy+r9~`z{E^u?6t)tpE#VX$Vm02m73=3t%U3s4l$U`m`O>#P`82sDR%L8IY>8%u z1)sqR+ZC(FDL3`4U@MHpme_t+Jyu_DC~Q}(o<2ic^Qwx^vL&`3wnWdZuwAiw`V74D z?w^-su$5SCNuRYRvG#+^bI(-Ul`(5i{Ej|5OW|3&&Sv^xeqc+ia4YxM=**r`Mti<{ z&XD=Gnjaqd>-@-=I#MA7YprlATZu+zyfQ3x)&Z*~j``s^;n_8Ljuo}Sv&-taXOuqT z6;(|f9Yo{lXnINLi807}AnjfAMo{uw^!ThkobH(bGXCRZx zw~GJ9Ng6djJXhBF!HOl>k86cnxxcYvr2q2T+gJ0$bHcN0&cXI{r^2oL4d*(}wW3#Y z+~{xh05Te=FR2;jS>*Y6o&A^!&qu2#&-qr}$GtJ5Ja49gS!;z`t+RIb0F*VTKHSPy zqWY+FKUljd8TVdCys8 z?A|yvn`~R3X`IkZT`G*S!gInixb*>}ZYnB^SD$NjquPGvSt_EdRtDmDEP3QN#lCaW z3Xe*w+pJ~30sgKP{#?(rNj2Vi)^4gEhq3E%ypG*HLr))8xRqyDJ0*=L%&d(kT(vUz zaPg4FI9|u@zH6)JsfWU?Ji8K~_k4RNPAUf)qddYryV`yO{M~DXTlpKddWL;QQANJ@ z;o0T>W`>@p+jkb$EFxb`#MRFf9vyKRoi+(T;)){Y^zx^3er$H!)||IzRHhYgarzgB5P&Ip3l? zygo)^M&GRw6=~$J^MlF+ynUWkP9RA}_=y_f9>?q0W#yF_b**qK&#tlSw|JhofIYk? zh~pXM{#HNH3eS~wevpsZ&o;Eet=wOt)I<{0m_P$&6SE59cusgePO9Bh%(-IqR4~uo zhi8;$b6eqs={(&;^Hn{7ZTF~L$JL(wxK_B8XWBfi2TPr7cGUtf5pccomZZLIcUW)jzyoQP(E?=;SuisX5@wS$%x||z#8oy zucpMzq+TmLSFEmc2Wv**EyV+8mwJtMk8rE6W3{R9IJSCJtJm1gv!YtP#;(V!=SovR z1m57Pm?PinYevmJJa*SHP3BG~I*@s;oxbbnvu%%eT-8)OtK_JM!XtlG(+4(-QDQ}^ zcm^{v=W{CYzj%syGxKna_N-3h&6MBn@#@)Cm4HXcNsMEKN50i9n&9a+5BU~^uF>vs zypDEa11vS;Dt3vRXlf*S^m*hr+fFpWJh#H**y>s7)S`)JlP!I?MxsaMIuiRnuNqhB zoqb+4t~{1JD#tE}2Po!w1HJYkvRJ3+J6%5#@OQR!T&96jl{g?$9L=F_v{gK?IBM^s$Ry3v3lAo{F00~_J-Fe@yPHP`~OqSF=q8t@T}a2 zN69*RuqD(Q^pjg^O^Q+C5wngG6gj!&j7>}qZYbPpwJ~_F+Q~W;ZnavU-QQ*Gx(~Oa zemi%WJjJ(d^$b`aetGOV*1ATbN1w-WJNH9XdPCt>ehSSdk}qNGx(|wZe17 z{WU9!hm6-3(q~RQS)<*favkkiQ!5XE`LQY{+daFcrN(c!!lT{&wfJLSKaZ8hlJ4~d zpJB3UxA!+_g@6{+C-0-7Nu&>58rIx!85GHI?wk^D!(JY zVJQ4I*EdKcOoosdWraUCPo49Ho?7%}xcg z)(W>;-+AKABe5EN#DBvpt7qk>us*B(o#XXc;dkyg+vYi5S&QywAO2jc=d5jhN!=K+ z=XZzI_Pd#i=KD;A-_80ysDC6oh)s+Q&pdDOhu?wU=B(enshDrm>S-Bxy6(eo(C?}0 zEGwE|MkTVVTpWz@lU|?h&U4;}6>hc8s6GC;R=5@Une4B9ioVzR5qo$mE$e;w9eBjF z8~}S1`Q=u)m4DOZ`L!Z5>lRTRv&;Rp_bv_XtiPOlQpn^Ko!f0`Wf}b4{S@YCzp0qt(CVq+>E=1* ziMHIUe}|t)8;PHcXUU9;2Ohio;i|dwclUT}s{rgP;+I?Dr(ktkS5qb6=)<48j>MH8 z^IN$U9$8l6o?^tbu}jvOXO%ggvHNg6T|ZI3n;C60N>;^uZdPB9-Pwnq?)ua@iDX~- ziS6U|>{`3=H}P|8R&-zaSrxNCtEYd(8??gT%D->ELHH5-aVy;DD@yYluHR~>!A->+ z`5p`Ho#S0zE8J>*gL@y0U4JY8j`jCp;qWqe=T`W0ndk8`-qlv<_HorcneoflR`d4T z=xc>aNANqGM|H1eEy;Adc`#<;Uzw)>I zmz+a>%qipb=ltJe|Fpev{cnB$@i$h`7k#eH`Bv_o&qqb8oGy0Bx$f(?x`)DlV}+l> z=%*C^wN|+Ap~xTBUmnFV-|9h6VTkV4_vW+tPgeZqzx2(={QH1k&S%mRMV`+k-?IKp zYgGLA@y|WJmH);H`rV(~s_@@f@xW&lY5K^wpe3J^=X1=+IP>ScZ@y9ecm99zUo%h6 znJ@KcTB91R`NKUF{u?X&n}(_s{u?Wv`XQm!AQ&ZJ(IPmTGgz@?P5BF+>?4vK= z@|aVs*{#V89`DJj_32vSzE{QiH?t3a!&TwW?caT)MORGgb1(lf|K2NGwZ8*w)&6d} zg~{{e`#2sKi=TBzuUZEr6NhT`gz z^sW3&@=-%^^+~fUEF7b{eruYDSH}v7&!cSt6_MUHmszWDYW;&u-11hhI{EV?`ee4^a4TtjKsPn|!km|BV%y zANVW}=)-?wMdrsPpU=O`Kf_z9zp)~t5YOoW3jd828CTHx0Sf<(75Q_qe)&rIKdWD2 zMIXBl=)<371>cDNZ}#E8v7%G$0Sf<(75VH-f5Nkyd;AWMYAgCD9cT0QC&uplEGs&L zAMmaGH&%4cuZsFs?%fL8F@Lk+Gt_5U5vzyyvGx(^@Jnte`V)D8!hhpFy2W^a!hd6h zqm1=;q>o|Atmuq-Kp*}aE8rIM)1S|u>sgJzv7+K9^H~a8Ba3B7p UQr+V#@vZW|gA-QRf*%h5AC+uZBLDyZ literal 0 HcmV?d00001 diff --git a/testData/cases/multiObject/mixedMesher.tessellator.json b/testData/cases/multiObject/mixedMesher.tessellator.json new file mode 100644 index 0000000..23c3d55 --- /dev/null +++ b/testData/cases/multiObject/mixedMesher.tessellator.json @@ -0,0 +1,16 @@ +{ + "grid": { + "numberOfCells": [50, 50, 50], + "boundingBox": [ + [-100.0, -100.0, -100.0], + [ 100.0, 100.0, 100.0] + ] + }, + "mesher": { + "type": "staircase" + }, + "objects": [ + {"filename": "sphere.stl", "group": "sphere_group"}, + {"filename": "cone.stl", "group": "cone_group", "mesher": {"type": "conformal"}} + ] +} diff --git a/testData/cases/multiObject/sameFileMultipleGroups.tessellator.json b/testData/cases/multiObject/sameFileMultipleGroups.tessellator.json new file mode 100644 index 0000000..b1bcd39 --- /dev/null +++ b/testData/cases/multiObject/sameFileMultipleGroups.tessellator.json @@ -0,0 +1,16 @@ +{ + "grid": { + "numberOfCells": [50, 50, 50], + "boundingBox": [ + [-100.0, -100.0, -100.0], + [ 100.0, 100.0, 100.0] + ] + }, + "mesher": { + "type": "staircase" + }, + "objects": [ + {"filename": "sphere.stl", "group": "group_a"}, + {"filename": "sphere.stl", "group": "group_b"} + ] +} diff --git a/testData/cases/multiObject/singleObject.tessellator.json b/testData/cases/multiObject/singleObject.tessellator.json new file mode 100644 index 0000000..9216505 --- /dev/null +++ b/testData/cases/multiObject/singleObject.tessellator.json @@ -0,0 +1,15 @@ +{ + "grid": { + "numberOfCells": [50, 50, 50], + "boundingBox": [ + [-100.0, -100.0, -100.0], + [ 100.0, 100.0, 100.0] + ] + }, + "mesher": { + "type": "staircase" + }, + "objects": [ + {"filename": "sphere.stl", "group": "default_group"} + ] +} diff --git a/testData/cases/multiObject/sphere.stl b/testData/cases/multiObject/sphere.stl new file mode 100644 index 0000000000000000000000000000000000000000..29885a13380c6e8b354e081e448be9bd5d23a7ee GIT binary patch literal 25984 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 literal 0 HcmV?d00001 From 386970ab8a1d3d7b77732b438fef61bad859b114 Mon Sep 17 00:00:00 2001 From: Alberto-o Date: Wed, 22 Jul 2026 14:51:26 +0200 Subject: [PATCH 2/9] Adds test to check manifolder behavior --- test/MeshFixtures.h | 33 +++++++++++++++++++++++++++++++++ test/cgal/ManifolderTest.cpp | 14 ++++++++++++++ 2 files changed, 47 insertions(+) diff --git a/test/MeshFixtures.h b/test/MeshFixtures.h index 65c77ad..d2f93d3 100644 --- a/test/MeshFixtures.h +++ b/test/MeshFixtures.h @@ -38,6 +38,39 @@ static Mesh buildNonManifoldPatchMesh(double stepSize) return m; } + +static Mesh buildTetCubeMesh1x1(double stepSize) +{ + Mesh m; + const double boxMin = 0.0; + const double boxMax = 1.0; + const std::size_t gridLinesNum = (std::size_t)((boxMax - boxMin) / (double)stepSize) + 1; + m.grid = utils::GridTools::buildCartesianGrid(boxMin, boxMax, gridLinesNum); + + m.coordinates = { + Coordinate({ 0.0, 0.0, 0.0 }), + Coordinate({ 1.0, 0.0, 0.0 }), + Coordinate({ 0.0, 1.0, 0.0 }), + Coordinate({ 0.0, 0.0, 1.0 }), + Coordinate({ 1.0, 1.0, 0.0 }), + Coordinate({ 1.0, 1.0, 1.0 }), + Coordinate({ 1.0, 0.0, 1.0 }), + Coordinate({ 0.0, 1.0, 1.0 }) + }; + + m.groups = { Group() }; + m.groups[0].elements = { + Element({0, 1, 2, 3}, Element::Type::Volume), + Element({1, 2, 4, 5}, Element::Type::Volume), + Element({1, 2, 3, 5}, Element::Type::Volume), + Element({1, 3, 5, 6}, Element::Type::Volume), + Element({2, 3, 5, 7}, Element::Type::Volume) + }; + + return m; +} + + static Mesh buildTetAndTriMesh(double stepSize) { Mesh m; diff --git a/test/cgal/ManifolderTest.cpp b/test/cgal/ManifolderTest.cpp index cedd1c3..7908aea 100644 --- a/test/cgal/ManifolderTest.cpp +++ b/test/cgal/ManifolderTest.cpp @@ -87,6 +87,20 @@ TEST_F(ManifolderTest, volume_and_surface) ASSERT_EQ(1, r.countElems()); } +TEST_F(ManifolderTest, cube_volume) +{ + Mesh m = buildTetCubeMesh1x1(1.0); + + Manifolder mani(m); + + ASSERT_EQ(12, mani.getClosedSurfacesMesh().countElems()); + // EXPECT_EQ(4, usedDifferentCoords(mani.getClosedSurfacesMesh()).size()); + + Mesh r = mani.getOpenSurfacesMesh(); + ASSERT_EQ(0, r.groups.size()); + ASSERT_EQ(0, r.countElems()); +} + TEST_F(ManifolderTest, closed_surface) { Mesh m = buildCubeSurfaceMesh(1.0); From e9a186c4e2d95344d4d650a7a28668b2e74b6e36 Mon Sep 17 00:00:00 2001 From: Alberto-o Date: Wed, 22 Jul 2026 16:25:23 +0200 Subject: [PATCH 3/9] Enabling tetrahedron reading and processing --- src/app/vtkIO.cpp | 14 ++++++++++++ src/meshers/StaircaseMesher.cpp | 7 ++++++ test/MeshFixtures.h | 32 ---------------------------- test/cgal/ManifolderTest.cpp | 13 ----------- test/meshers/StaircaseMesherTest.cpp | 13 +++++++++++ 5 files changed, 34 insertions(+), 45 deletions(-) diff --git a/src/app/vtkIO.cpp b/src/app/vtkIO.cpp index d55a561..094e8cc 100644 --- a/src/app/vtkIO.cpp +++ b/src/app/vtkIO.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #include @@ -76,6 +77,7 @@ Element vtkCellToElement(vtkCell* cell) vtkVertex* vertex = nullptr; vtkLine* line = nullptr; vtkTriangle* triangle = nullptr; + vtkTetra* tetra = nullptr; switch (cell->GetCellType()) { case VTK_VERTEX: @@ -102,6 +104,17 @@ Element vtkCellToElement(vtkCell* cell) }; elem.type = meshlib::Element::Type::Surface; break; + + case VTK_TETRA: + tetra = vtkTetra::SafeDownCast(cell); + elem.vertices = { + CoordinateId(tetra->GetPointIds()->GetId(0)), + CoordinateId(tetra->GetPointIds()->GetId(1)), + CoordinateId(tetra->GetPointIds()->GetId(2)), + CoordinateId(tetra->GetPointIds()->GetId(3)) + }; + elem.type = meshlib::Element::Type::Volume; + break; } return elem; @@ -131,6 +144,7 @@ Mesh vtuToMesh(vtkUnstructuredGrid* vtu) } } else { mesh.groups.resize(1); + auto k = vtu->GetNumberOfCells(); mesh.groups[0].elements.reserve(vtu->GetNumberOfCells()); for (vtkIdType i = 0; i < vtu->GetNumberOfCells(); i++) { mesh.groups[0].elements.push_back( diff --git a/src/meshers/StaircaseMesher.cpp b/src/meshers/StaircaseMesher.cpp index 6e9034d..aab9f75 100644 --- a/src/meshers/StaircaseMesher.cpp +++ b/src/meshers/StaircaseMesher.cpp @@ -9,6 +9,7 @@ #include "core/Compressor.h" #include "cgal/filler/Filler.h" +#include "cgal/Manifolder.h" #include "utils/RedundancyCleaner.h" #include "utils/MeshTools.h" @@ -26,6 +27,7 @@ StaircaseMesher::StaircaseMesher(const Mesh& inputMesh, int decimalPlacesInColla opts_(opts) { log("Preparing surfaces."); + //here, convert tetra intro hull tris surfaceMesh_ = buildMeshFilteringElements(inputMesh, isNotTetrahedron); log("Processing surface mesh."); @@ -53,8 +55,13 @@ void StaircaseMesher::process(Mesh& mesh) const auto dimensions = getHighestDimensionByGroup(mesh); + //mani has open_ and closed_ for every group id if (opts_.isVolume){ if (meshTools::isAClosedTopology(mesh.groups[0].elements)){ + + auto mani = meshlib::cgal::Manifolder(mesh); + mesh.groups[0].elements = mani.getClosedSurfacesMesh().groups[0].elements; + meshlib::cgal::filler::Filler f{ mesh }; auto filling = f.getMeshFilling(); mergeMesh(mesh, filling); diff --git a/test/MeshFixtures.h b/test/MeshFixtures.h index d2f93d3..7eda7c0 100644 --- a/test/MeshFixtures.h +++ b/test/MeshFixtures.h @@ -39,38 +39,6 @@ static Mesh buildNonManifoldPatchMesh(double stepSize) } -static Mesh buildTetCubeMesh1x1(double stepSize) -{ - Mesh m; - const double boxMin = 0.0; - const double boxMax = 1.0; - const std::size_t gridLinesNum = (std::size_t)((boxMax - boxMin) / (double)stepSize) + 1; - m.grid = utils::GridTools::buildCartesianGrid(boxMin, boxMax, gridLinesNum); - - m.coordinates = { - Coordinate({ 0.0, 0.0, 0.0 }), - Coordinate({ 1.0, 0.0, 0.0 }), - Coordinate({ 0.0, 1.0, 0.0 }), - Coordinate({ 0.0, 0.0, 1.0 }), - Coordinate({ 1.0, 1.0, 0.0 }), - Coordinate({ 1.0, 1.0, 1.0 }), - Coordinate({ 1.0, 0.0, 1.0 }), - Coordinate({ 0.0, 1.0, 1.0 }) - }; - - m.groups = { Group() }; - m.groups[0].elements = { - Element({0, 1, 2, 3}, Element::Type::Volume), - Element({1, 2, 4, 5}, Element::Type::Volume), - Element({1, 2, 3, 5}, Element::Type::Volume), - Element({1, 3, 5, 6}, Element::Type::Volume), - Element({2, 3, 5, 7}, Element::Type::Volume) - }; - - return m; -} - - static Mesh buildTetAndTriMesh(double stepSize) { Mesh m; diff --git a/test/cgal/ManifolderTest.cpp b/test/cgal/ManifolderTest.cpp index 7908aea..c05f1c5 100644 --- a/test/cgal/ManifolderTest.cpp +++ b/test/cgal/ManifolderTest.cpp @@ -87,19 +87,6 @@ TEST_F(ManifolderTest, volume_and_surface) ASSERT_EQ(1, r.countElems()); } -TEST_F(ManifolderTest, cube_volume) -{ - Mesh m = buildTetCubeMesh1x1(1.0); - - Manifolder mani(m); - - ASSERT_EQ(12, mani.getClosedSurfacesMesh().countElems()); - // EXPECT_EQ(4, usedDifferentCoords(mani.getClosedSurfacesMesh()).size()); - - Mesh r = mani.getOpenSurfacesMesh(); - ASSERT_EQ(0, r.groups.size()); - ASSERT_EQ(0, r.countElems()); -} TEST_F(ManifolderTest, closed_surface) { diff --git a/test/meshers/StaircaseMesherTest.cpp b/test/meshers/StaircaseMesherTest.cpp index 6d1dfdb..eb2af21 100644 --- a/test/meshers/StaircaseMesherTest.cpp +++ b/test/meshers/StaircaseMesherTest.cpp @@ -369,6 +369,19 @@ TEST_F(StaircaseMesherTest, testStaircaseWithCompression) EXPECT_EQ(0, countMeshElementsIf(compressedMesh, isNode)); } +TEST_F(StaircaseMesherTest, mesh_tetrahedron_volume){ + + Mesh m = buildCubeVolumeMesh(1.0); + meshlib::meshers::StaircaseMesherOptions opts; + opts.isVolume = true; + auto staircasedMesh = StaircaseMesher{m, 4, opts }.mesh(); + + EXPECT_EQ(0, countMeshElementsIf(staircasedMesh, isTriangle)); + EXPECT_EQ(12, countMeshElementsIf(staircasedMesh, isQuad)); + EXPECT_EQ(0, countMeshElementsIf(staircasedMesh, isTetrahedron)); + +} + #if APP_LOADED TEST_F(StaircaseMesherTest, fills_closed_volume_with_quads) From 896cf42ee74df5e28fda8e40ce65792a6d1f29a7 Mon Sep 17 00:00:00 2001 From: Alberto-o Date: Thu, 23 Jul 2026 09:57:07 +0200 Subject: [PATCH 4/9] adds test before code implementation --- test/meshers/StaircaseMesherTest.cpp | 36 ++++++++++++++++++++++++---- 1 file changed, 32 insertions(+), 4 deletions(-) diff --git a/test/meshers/StaircaseMesherTest.cpp b/test/meshers/StaircaseMesherTest.cpp index eb2af21..c8a1dab 100644 --- a/test/meshers/StaircaseMesherTest.cpp +++ b/test/meshers/StaircaseMesherTest.cpp @@ -369,15 +369,43 @@ TEST_F(StaircaseMesherTest, testStaircaseWithCompression) EXPECT_EQ(0, countMeshElementsIf(compressedMesh, isNode)); } -TEST_F(StaircaseMesherTest, mesh_tetrahedron_volume){ +TEST_F(StaircaseMesherTest, mesh_tetrahedron_volume_2x2){ - Mesh m = buildCubeVolumeMesh(1.0); + Mesh m = buildCubeVolumeMesh(0.5); meshlib::meshers::StaircaseMesherOptions opts; - opts.isVolume = true; + opts.volumeGroups.insert(0); + // opts.isVolume = true; + auto staircasedMesh = StaircaseMesher{m, 4, opts }.mesh(); + + EXPECT_EQ(0, countMeshElementsIf(staircasedMesh, isTriangle)); + EXPECT_EQ(36, countMeshElementsIf(staircasedMesh, isQuad)); + EXPECT_EQ(0, countMeshElementsIf(staircasedMesh, isTetrahedron)); + +} + +TEST_F(StaircaseMesherTest, mesh_surface_volume_2x2){ + + Mesh m = buildCubeSurfaceMesh(0.5); + meshlib::meshers::StaircaseMesherOptions opts; + opts.volumeGroups.insert(0); + // opts.isVolume = true; + auto staircasedMesh = StaircaseMesher{m, 4, opts }.mesh(); + + EXPECT_EQ(0, countMeshElementsIf(staircasedMesh, isTriangle)); + EXPECT_EQ(36, countMeshElementsIf(staircasedMesh, isQuad)); + EXPECT_EQ(0, countMeshElementsIf(staircasedMesh, isTetrahedron)); + +} + +TEST_F(StaircaseMesherTest, mesh_surface_not_volume_2x2){ + + Mesh m = buildCubeSurfaceMesh(0.5); + meshlib::meshers::StaircaseMesherOptions opts; + // opts.isVolume = true; auto staircasedMesh = StaircaseMesher{m, 4, opts }.mesh(); EXPECT_EQ(0, countMeshElementsIf(staircasedMesh, isTriangle)); - EXPECT_EQ(12, countMeshElementsIf(staircasedMesh, isQuad)); + EXPECT_EQ(24, countMeshElementsIf(staircasedMesh, isQuad)); EXPECT_EQ(0, countMeshElementsIf(staircasedMesh, isTetrahedron)); } From c60c5a6b48e5930be19e4c13c9ef580e9fbba720 Mon Sep 17 00:00:00 2001 From: Alberto-o Date: Mon, 27 Jul 2026 09:42:31 +0200 Subject: [PATCH 5/9] Commit before merging with dev --- src/cgal/filler/Filler.h | 2 +- src/core/CMakeLists.txt | 6 ++- src/core/Slicer.cpp | 25 +++++++++ src/core/Slicer.h | 7 ++- src/core/VolumeFiller.cpp | 90 +++++++++++++++++++++++++++++++ src/core/VolumeFiller.h | 18 +++++++ src/meshers/StaircaseMesher.cpp | 58 ++++++++++++-------- src/meshers/StaircaseMesher.h | 2 + src/utils/Geometry.cpp | 23 ++++++++ src/utils/Geometry.h | 2 + src/utils/MeshTools.cpp | 22 +++++--- test/CMakeLists.txt | 1 + test/core/SlicerTest.cpp | 16 ++++++ test/core/VolumeFillerTest.cpp | 96 +++++++++++++++++++++++++++++++++ 14 files changed, 338 insertions(+), 30 deletions(-) create mode 100644 src/core/VolumeFiller.cpp create mode 100644 src/core/VolumeFiller.h create mode 100644 test/core/VolumeFillerTest.cpp diff --git a/src/cgal/filler/Filler.h b/src/cgal/filler/Filler.h index 2b2d5af..8f2d4dc 100644 --- a/src/cgal/filler/Filler.h +++ b/src/cgal/filler/Filler.h @@ -32,7 +32,7 @@ class Filler { FillingState getFillingState(const CellIndex&) const; Mesh getMeshFilling() const; - + GridSlices getSlices() const {return slices_;}; private: GridSlices slices_; GridSegmentsArray segmentsArray_; diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index faab34a..fe92b76 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -8,6 +8,10 @@ add_library(tessellator-core "Smoother.cpp" "SmootherTools.cpp" "Staircaser.cpp" + "VolumeFiller.cpp" ) -target_link_libraries(tessellator-core tessellator-utils) \ No newline at end of file +target_link_libraries(tessellator-core + tessellator-utils + tessellator-cgal + CGAL::CGAL) \ No newline at end of file diff --git a/src/core/Slicer.cpp b/src/core/Slicer.cpp index fb5d7a2..a33defa 100644 --- a/src/core/Slicer.cpp +++ b/src/core/Slicer.cpp @@ -38,6 +38,31 @@ void orient(const Coordinates& coords, } } +// Mesh Slicer::fill(const Mesh& input){ +// //input mesh should be structured + +// Coordinates sCoords; +// sCoords.reserve(input.coordinates.size() * 100); + +// for (const auto& g : input.groups){ + +// for (auto const& itCell : +// buildCellCoordIdMap(sCoords, buildGroupIntersectionsWithGridPlanes(sCoords, g.elements))) { +// const IdSet& vIds = itCell.second; +// if (vIds.size() < 3) { +// continue; +// } +// } + +// } +// } + +// IdSet Slicer::buildGroupIntersectionsWithGridPlanes( +// Coordinates& sCoords, +// const std::vector& elements) +// { + +// } Slicer::Slicer(const Mesh& input, const std::vector& dimensionPolicy, const SlicerOptions& opts) : GridTools(input.grid), diff --git a/src/core/Slicer.h b/src/core/Slicer.h index e0bae73..7f418a4 100644 --- a/src/core/Slicer.h +++ b/src/core/Slicer.h @@ -24,7 +24,7 @@ class Slicer : public utils::GridTools { Slicer(const Mesh&, const std::vector& dimensionPolicy = {}, const SlicerOptions& opts = SlicerOptions()); Mesh getMesh() const { return mesh_; }; - + // Mesh fill(const Mesh&); static Elements buildTrianglesFromPath(const std::vector&, const std::vector&); @@ -53,6 +53,11 @@ class Slicer : public utils::GridTools { const Cell&, const Coordinate&, const Cell&, const Coordinate&) const; + // IdSet buildGroupIntersectionsWithGridPlanes( + // Coordinates& sCoords, + // const std::vector& elements); + + }; } diff --git a/src/core/VolumeFiller.cpp b/src/core/VolumeFiller.cpp new file mode 100644 index 0000000..0786e1d --- /dev/null +++ b/src/core/VolumeFiller.cpp @@ -0,0 +1,90 @@ +#include "VolumeFiller.h" +#include "cgal/filler/Filler.h" +#include "cgal/filler/FillerTools.h" +#ifdef TESSELLATOR_EXECUTION_POLICIES +#include +#endif + + +namespace meshlib { +namespace core { +using namespace utils; + + +Elements buildTriangleElements(Coordinates& cs, const TriVs tris) +{ + Elements r; + for (const auto& p : tris) { + Element e; + e.type = Element::Type::Surface; + e.vertices.reserve(3); + for (const auto& v : p) { + e.vertices.push_back(cs.size()); + cs.push_back(v); + } + r.push_back(e); + } + return r; +} +Elements buildLineElements(Coordinates& cs, const LinVs lins) +{ + Elements r; + for (const auto& p : lins) { + Element e; + e.type = Element::Type::Line; + e.vertices.reserve(2); + for (const auto& v : p) { + e.vertices.push_back(cs.size()); + cs.push_back(v); + } + r.push_back(e); + } + return r; +} + +VolumeFiller::VolumeFiller(const Mesh& input) +{ + Mesh m; + mesh_.grid = input.grid; + mesh_.groups.resize(input.groups.size()); + + meshlib::cgal::filler::Filler f{input}; + mesh_ = f.getMeshFilling(); + // auto slices = f.getSlices(); + // for (auto gId{0}; gId < mesh_.groups.size(); ++gId) { + + // for (const auto& x : { X, Y, Z }) { + // for (const auto& [i, slice]: slices[x]) { + // // const Priority pr{ getGroupPriority(gId) }; + // const Priority pr{ 0 }; + // auto trivs = slice.buildAllTriVs(pr, x, (meshlib::cgal::filler::Height)i); + // auto triEls = buildTriangleElements( + // m.coordinates, + // slice.buildAllTriVs(pr, x, (meshlib::cgal::filler::Height)i) + // ); + + // auto linvs = slice.buildAllLinVs(pr, x, (meshlib::cgal::filler::Height)i); + // auto lineEls = buildLineElements( + // mesh_.coordinates, + // slice.buildAllLinVs(pr, x, (meshlib::cgal::filler::Height)i) + // ); + + // } + // } + // } + //take structured mesh + //use filler to generate the slices: intersections of each grid plane with the mesh + //convert the slice intersection into a ElemV with dimension n, where n is the number of points of the slice intersection + //build a coordIdMap similar to the one created in Slicer::sliceTriangle, where instead of the intersection of a triangle with the grid planes, we have the intersection of the n-dimensional ElemV + //Create a triangulation of the intersection of the slice with the grid planes, following also slicer::sliceTriangle +} + +Mesh VolumeFiller::getMesh() +{ + return mesh_; +} + + + +} +} \ No newline at end of file diff --git a/src/core/VolumeFiller.h b/src/core/VolumeFiller.h new file mode 100644 index 0000000..439c9fc --- /dev/null +++ b/src/core/VolumeFiller.h @@ -0,0 +1,18 @@ +#pragma once + +#include "utils/GridTools.h" + +namespace meshlib{ +namespace core{ + +class VolumeFiller : public utils::GridTools{ +public: + + VolumeFiller(const Mesh&); + Mesh getMesh(); +private: + Mesh mesh_; +}; + +} +} \ No newline at end of file diff --git a/src/meshers/StaircaseMesher.cpp b/src/meshers/StaircaseMesher.cpp index aab9f75..c424ab8 100644 --- a/src/meshers/StaircaseMesher.cpp +++ b/src/meshers/StaircaseMesher.cpp @@ -9,7 +9,7 @@ #include "core/Compressor.h" #include "cgal/filler/Filler.h" -#include "cgal/Manifolder.h" +// #include "cgal/Manifolder.h" #include "utils/RedundancyCleaner.h" #include "utils/MeshTools.h" @@ -27,15 +27,41 @@ StaircaseMesher::StaircaseMesher(const Mesh& inputMesh, int decimalPlacesInColla opts_(opts) { log("Preparing surfaces."); - //here, convert tetra intro hull tris - surfaceMesh_ = buildMeshFilteringElements(inputMesh, isNotTetrahedron); - + surfaceMesh_ = MesherBase::buildSurfaceMesh(inputMesh, opts_.volumeGroups); log("Processing surface mesh."); process(surfaceMesh_); + + log("Preparing volumes"); + volumeMesh_ = MesherBase::buildVolumeMesh(inputMesh, opts_.volumeGroups); + fillMesh(volumeMesh_, opts_.volumeGroups); + log("Processing volume mesh."); + process(volumeMesh_); + + mergeMesh(surfaceMesh_, volumeMesh_); + + log("Mesh built succesfully.", 1); +} + +void StaircaseMesher::fillMesh(Mesh& m, const std::set& volumeGroups){ + if (m.countElems() == 0) return; + // auto mani = meshlib::cgal::Manifolder(m); + // for (const auto& gId : volumeGroups){ + // m.groups[gId].elements = mani.getClosedSurfacesMesh().groups[gId].elements; + // } + m.coordinates = utils::GridTools{m.grid}.absoluteToRelative(m.coordinates); + m = meshlib::cgal::filler::Filler(m).getMeshFilling(); + m.coordinates = utils::GridTools{m.grid}.relativeToAbsolute(m.coordinates); - log("Surface mesh built succesfully.", 1); + // m = f.getMeshFilling(); + + // auto filling = f.getMeshFilling(); + // mergeMesh(m, filling); + // // auto dimensions = getHighestDimensionByGroup(m); + // // RedundancyCleaner::removeOverlappedElementsByDimension(m, dimensions); + } + Mesh StaircaseMesher::buildSurfaceMesh(const Mesh& inputMesh, const Mesh & volumeSurface) { auto resultMesh = buildMeshFilteringElements(inputMesh, isNotTetrahedron); @@ -49,33 +75,23 @@ void StaircaseMesher::process(Mesh& mesh) const const auto slicingGrid{ buildSlicingGrid(originalGrid_, enlargedGrid_) }; if (mesh.countElems() == 0) { - mesh.grid = slicingGrid; + // mesh.grid = slicingGrid; return; } auto dimensions = getHighestDimensionByGroup(mesh); - //mani has open_ and closed_ for every group id - if (opts_.isVolume){ - if (meshTools::isAClosedTopology(mesh.groups[0].elements)){ - - auto mani = meshlib::cgal::Manifolder(mesh); - mesh.groups[0].elements = mani.getClosedSurfacesMesh().groups[0].elements; - - meshlib::cgal::filler::Filler f{ mesh }; - auto filling = f.getMeshFilling(); - mergeMesh(mesh, filling); - } else { - throw std::runtime_error("Input object marked to be meshed as a volume, but surface is not closed"); - } - } - log("Slicing.", 1); mesh.grid = slicingGrid; mesh = Slicer{ mesh, dimensions }.getMesh(); logNumberOfTriangles(countMeshElementsIf(mesh, isTriangle)); + // meshlib::cgal::filler::Filler f{mesh}; + // auto filling = f.getMeshFilling(); + // mergeMesh(mesh, filling); + + log("Collapsing.", 1); mesh = Collapser(mesh, decimalPlacesInCollapser_, dimensions).getMesh(); diff --git a/src/meshers/StaircaseMesher.h b/src/meshers/StaircaseMesher.h index df0b9bb..92fee31 100644 --- a/src/meshers/StaircaseMesher.h +++ b/src/meshers/StaircaseMesher.h @@ -16,9 +16,11 @@ class StaircaseMesher : public MesherBase { int decimalPlacesInCollapser_; Mesh surfaceMesh_; + Mesh volumeMesh_; StaircaseMesherOptions opts_; virtual Mesh buildSurfaceMesh(const Mesh& inputMesh, const Mesh& volumeSurface); + static void fillMesh(Mesh& inputMesh, const std::set& volumeGroups); void process(Mesh&) const; }; diff --git a/src/utils/Geometry.cpp b/src/utils/Geometry.cpp index fe2fc4e..2015763 100644 --- a/src/utils/Geometry.cpp +++ b/src/utils/Geometry.cpp @@ -70,6 +70,17 @@ std::vector Geometry::buildDisjointSmoothSets( } +QuaV Geometry::asQuaV(const Element& el, const std::vector& co) { + if (el.vertices.size() != 4) { + throw std::logic_error("Invalid conversion from element to QuaV"); + } + QuaV res; + for (std::size_t i = 0; i < el.vertices.size(); i++) { + res[i] = co[el.vertices[i]]; + } + return res; +} + TriV Geometry::asTriV(const Element& el, const std::vector& co) { if (el.vertices.size() != 3) { throw std::logic_error("Invalid conversion from element to TriV"); @@ -229,6 +240,18 @@ double Geometry::area(const TriV& tri) { return ((tri[0] - tri[1]) ^ (tri[1] - tri[2])).norm() / 2.0; } +double Geometry::area(const QuaV& qua) { + const Coordinates cs{ qua.begin(), qua.end() }; + + VecD crossSum; + for (std::size_t i = 0; i < qua.size(); ++i) { + const auto& p = qua[i]; + const auto& q = qua[(i + 1) % qua.size()]; + crossSum += (p ^ q); + } + return 0.5*crossSum.norm(); +} + } } diff --git a/src/utils/Geometry.h b/src/utils/Geometry.h index 113593b..e185f0c 100644 --- a/src/utils/Geometry.h +++ b/src/utils/Geometry.h @@ -28,6 +28,7 @@ class Geometry { static bool areAdjacentLines(const Element&, const Element&); + static QuaV asQuaV(const Element&, const Coordinates&); static TriV asTriV(const Element&, const Coordinates&); static LinV asLinV(const Element&, const Coordinates&); @@ -38,6 +39,7 @@ class Geometry { static VecD getCentroid(const Element&, const std::vector&); static VecD getCentroid(const TriV&); static double area(const TriV& tri); + static double area(const QuaV& qua); static bool isDegenerate(const TriV& tri, const double& areaTolerance = NORM_TOLERANCE); static bool areCollinear(const Coordinates&); template diff --git a/src/utils/MeshTools.cpp b/src/utils/MeshTools.cpp index d271f39..48e405d 100644 --- a/src/utils/MeshTools.cpp +++ b/src/utils/MeshTools.cpp @@ -273,12 +273,22 @@ void checkNoNullAreasExist(const Mesh& m) msg << info(e, m) << std::endl; } } - else if (Geometry::area(Geometry::asTriV(e, m.coordinates)) == 0.0) { - nullAreas = true; - msg << std::endl; - msg << "Group: " << &g - &m.groups.front() - << ", Element: " << &e - &g.elements.front() << std::endl; - msg << info(e, m) << std::endl; + else if (e.isTriangle()){ + if (Geometry::area(Geometry::asTriV(e, m.coordinates)) == 0.0) { + nullAreas = true; + msg << std::endl; + msg << "Group: " << &g - &m.groups.front() + << ", Element: " << &e - &g.elements.front() << std::endl; + msg << info(e, m) << std::endl; + } + } else if (e.isQuad()){ + if (Geometry::area(Geometry::asQuaV(e, m.coordinates)) == 0.0) { + nullAreas = true; + msg << std::endl; + msg << "Group: " << &g - &m.groups.front() + << ", Element: " << &e - &g.elements.front() << std::endl; + msg << info(e, m) << std::endl; + } } } } diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 638a03a..b4fbb61 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -17,6 +17,7 @@ add_executable(tessellator_tests "core/CollapserTest.cpp" "core/CompressorTest.cpp" "core/SlicerTest.cpp" + "core/VolumeFillerTest.cpp" "core/SmootherTest.cpp" "core/SmootherToolsTest.cpp" "core/SnapperTest.cpp" diff --git a/test/core/SlicerTest.cpp b/test/core/SlicerTest.cpp index 2367b93..70e18b4 100644 --- a/test/core/SlicerTest.cpp +++ b/test/core/SlicerTest.cpp @@ -1090,4 +1090,20 @@ TEST_F(SlicerTest, sphere_case_patch_contour_check_2) #endif } +// TEST_F(SlicerTest, cube1x1x1_size05_grid_fill) +// { +// Mesh m = buildCubeSurfaceMesh(0.5); + +// Mesh out; +// ASSERT_NO_THROW(out = Slicer{m}.getMesh()); +// EXPECT_EQ(48, countMeshElementsIf(out, isTriangle)); +// EXPECT_FALSE(containsDegenerateTriangles(out)); +// EXPECT_EQ(countContours(m), countContours(out)); + +// Mesh filled; +// ASSERT_NO_THROW(filled = Slicer::fill(out)); +// EXPECT_EQ(72, countMeshElementsIf(out, isTriangle)); +// } + + } diff --git a/test/core/VolumeFillerTest.cpp b/test/core/VolumeFillerTest.cpp new file mode 100644 index 0000000..e2a475e --- /dev/null +++ b/test/core/VolumeFillerTest.cpp @@ -0,0 +1,96 @@ +#include "MeshFixtures.h" +#include "gtest/gtest.h" + +#include "VolumeFiller.h" +#include "Slicer.h" +#include "Geometry.h" +#include "MeshTools.h" +#include "GridTools.h" +// #include "utils/RedundancyCleaner.h" +// #include "utils/CoordGraph.h" + +#if APP_LOADED + #include "app/vtkIO.h" +#endif + +namespace meshlib::core { + +using namespace meshFixtures; +using namespace utils; +using namespace meshTools; + +class VolumeFillerTest : public ::testing::Test { +public: +protected: +}; + +static Mesh toRelative(const Mesh& m) +{ + auto r{ m }; + r.coordinates = + utils::GridTools{ m.grid }.absoluteToRelative(m.coordinates); + return r; +} + +TEST_F(VolumeFillerTest, fill_cube1x1x1_size1_grid) +{ + Mesh m = buildCubeSurfaceMesh(01.0); + + Mesh out; + ASSERT_NO_THROW(out = Slicer{m}.getMesh()); + EXPECT_EQ(12, countMeshElementsIf(out, isTriangle)); + + Mesh filled = VolumeFiller{out}.getMesh(); + EXPECT_EQ(12, countMeshElementsIf(filled, isTriangle)); +} + +TEST_F(VolumeFillerTest, fill_cube1x1x1_size05_grid) +{ + //filling with unstruc. triangles + Mesh m = buildCubeSurfaceMesh(0.5); + Mesh filled = VolumeFiller{Slicer{buildCubeSurfaceMesh(0.5) }.getMesh()}.getMesh(); + Mesh filled_no_slicing = VolumeFiller{toRelative(buildCubeSurfaceMesh(0.5))}.getMesh(); + EXPECT_EQ(18, countMeshElementsIf(filled, isTriangle)); + EXPECT_EQ(18, countMeshElementsIf(filled_no_slicing, isTriangle)); + + //slicing hull + Mesh out_1; + ASSERT_NO_THROW(out_1 = Slicer{buildCubeSurfaceMesh(0.5) }.getMesh()); + EXPECT_EQ(48, countMeshElementsIf(out_1, isTriangle)); + + //filling and slicing + Mesh out_2; + GridTools gT{ filled.grid }; + filled.coordinates = gT.relativeToAbsolute(filled.coordinates); + ASSERT_NO_THROW(out_2 = Slicer{filled }.getMesh()); + EXPECT_EQ(72, countMeshElementsIf(out_2, isTriangle)); + + GridTools gT_no{ filled_no_slicing.grid }; + filled_no_slicing.coordinates = gT_no.relativeToAbsolute(filled_no_slicing.coordinates); + ASSERT_NO_THROW(out_2 = Slicer{filled_no_slicing}.getMesh()); + EXPECT_EQ(72, countMeshElementsIf(out_2, isTriangle)); + +} + +TEST_F(VolumeFillerTest, fill_cube1x1x1_size025_grid) +{ + Mesh m = buildCubeSurfaceMesh(0.25); + Mesh filled = VolumeFiller{Slicer{buildCubeSurfaceMesh(0.25) }.getMesh()}.getMesh(); + EXPECT_EQ(30, countMeshElementsIf(filled, isTriangle)); + + Mesh out_1; + ASSERT_NO_THROW(out_1 = Slicer{buildCubeSurfaceMesh(0.25) }.getMesh()); + EXPECT_EQ(192, countMeshElementsIf(out_1, isTriangle)); + + Mesh out_2; + GridTools gT{ filled.grid }; + filled.coordinates = gT.relativeToAbsolute(filled.coordinates); + + ASSERT_NO_THROW(out_2 = Slicer{filled }.getMesh()); + EXPECT_EQ(480, countMeshElementsIf(out_2, isTriangle)); + + // Mesh filled = VolumeFiller{out}.getMesh(); + // ASSERT_NO_THROW(out = Slicer{filled}.getMesh()); + // EXPECT_EQ(72, countMeshElementsIf(out, isTriangle)); +} +} \ No newline at end of file From eb955fb87e776f8d3d4269a0079c087d41fb8219 Mon Sep 17 00:00:00 2001 From: Alberto-o Date: Tue, 28 Jul 2026 09:25:52 +0200 Subject: [PATCH 6/9] Changes to propagate group names to vtk output. Property isVolume not needed, use volumeGroups instead --- src/app/launcher.cpp | 12 ++++++++---- src/app/vtkIO.cpp | 9 +++++++-- src/meshers/MesherBaseOptions.h | 1 - src/meshers/StaircaseMesher.cpp | 19 ++++++++++++++++++- src/utils/MeshTools.cpp | 1 + test/app/launcherTest.cpp | 8 ++++---- test/meshers/StaircaseMesherTest.cpp | 4 ++-- 7 files changed, 40 insertions(+), 14 deletions(-) diff --git a/src/app/launcher.cpp b/src/app/launcher.cpp index 6ce1de5..c9bb54c 100644 --- a/src/app/launcher.cpp +++ b/src/app/launcher.cpp @@ -150,8 +150,9 @@ meshlib::meshers::StaircaseMesherOptions readStaircaseMesherOptions(const nlohma } meshlib::meshers::StaircaseMesherOptions res; - - res.isVolume = isVolume; + if (isVolume){ + res.volumeGroups.insert(0); + } if (mesherConfig.contains("options") && mesherConfig["options"].contains("compress")) { res.compress = mesherConfig["options"]["compress"]; @@ -160,7 +161,7 @@ meshlib::meshers::StaircaseMesherOptions readStaircaseMesherOptions(const nlohma return res; } -meshlib::meshers::ConformalMesherOptions readConformalMesherOptions(const nlohmann::json& fileData, const std::optional& override) +meshlib::meshers::ConformalMesherOptions readConformalMesherOptions(const nlohmann::json& fileData, bool isVolume, const std::optional& override) { nlohmann::json mesherConfig; if (override.has_value()) { @@ -170,6 +171,9 @@ meshlib::meshers::ConformalMesherOptions readConformalMesherOptions(const nlohma } meshlib::meshers::ConformalMesherOptions res; + if (isVolume){ + res.volumeGroups.insert(0); + } if (mesherConfig.contains("options")) { res.snapperOptions.edgePoints = mesherConfig["options"]["edgePoints"]; res.snapperOptions.forbiddenLength = mesherConfig["options"]["forbiddenLength"]; @@ -204,7 +208,7 @@ std::unique_ptr buildMesher(const Mesh& in, const readStaircaseMesherOptions(fileData, objDef.isVolume, objDef.mesherOverride) }); } else if (mesherType == meshlib::app::conformal_mesher) { - return std::make_unique(meshlib::meshers::ConformalMesher{in, readConformalMesherOptions(fileData, objDef.mesherOverride)}); + return std::make_unique(meshlib::meshers::ConformalMesher{in, readConformalMesherOptions(fileData, objDef.isVolume, objDef.mesherOverride)}); } else { throw std::runtime_error("Unsupported mesher type"); } diff --git a/src/app/vtkIO.cpp b/src/app/vtkIO.cpp index 094e8cc..70a105c 100644 --- a/src/app/vtkIO.cpp +++ b/src/app/vtkIO.cpp @@ -183,8 +183,13 @@ vtkSmartPointer toVTKGroupNamesArray(const Mesh& mesh) groupNamesArray->SetName("groupNames"); groupNamesArray->SetNumberOfComponents(1); - for (const auto& group : mesh.groups) { - groupNamesArray->InsertNextValue(group.name.c_str()); + // for (const auto& group : mesh.groups) { + // groupNamesArray->InsertNextValue(group.name.c_str()); + // } + for (auto g = 0; g < mesh.groups.size(); g++) { + for (auto e = 0; e < mesh.groups[g].elements.size(); e++) { + groupNamesArray->InsertNextValue( mesh.groups[g].name.c_str() ); + } } return groupNamesArray; diff --git a/src/meshers/MesherBaseOptions.h b/src/meshers/MesherBaseOptions.h index 7403619..55138c1 100644 --- a/src/meshers/MesherBaseOptions.h +++ b/src/meshers/MesherBaseOptions.h @@ -7,7 +7,6 @@ namespace meshlib::meshers { class MesherBaseOptions { public: - bool isVolume = false; std::set volumeGroups{}; }; diff --git a/src/meshers/StaircaseMesher.cpp b/src/meshers/StaircaseMesher.cpp index c424ab8..35bd6b5 100644 --- a/src/meshers/StaircaseMesher.cpp +++ b/src/meshers/StaircaseMesher.cpp @@ -69,9 +69,24 @@ Mesh StaircaseMesher::buildSurfaceMesh(const Mesh& inputMesh, const Mesh & volum return resultMesh; } +std::vector getGroupNames(const Groups& groups){ + std::vector names; + names.reserve(groups.size()); + for (auto gId{0}; gId < groups.size(); ++gId) { + names.push_back(groups[gId].name); + } + return names; +} + +void copyGroupNames(Mesh& m, const std::vector& names){ + for (auto gId{0}; gId < m.groups.size(); ++gId) { + m.groups[gId].name = names[gId]; + } +} + void StaircaseMesher::process(Mesh& mesh) const { - + const auto groupNames = getGroupNames(mesh.groups); const auto slicingGrid{ buildSlicingGrid(originalGrid_, enlargedGrid_) }; if (mesh.countElems() == 0) { @@ -136,6 +151,8 @@ void StaircaseMesher::process(Mesh& mesh) const logNumberOfQuads(countMeshElementsIf(mesh, isQuad)); logNumberOfLines(countMeshElementsIf(mesh, isLine)); + copyGroupNames(mesh, groupNames); + } diff --git a/src/utils/MeshTools.cpp b/src/utils/MeshTools.cpp index cfd7253..a86479e 100644 --- a/src/utils/MeshTools.cpp +++ b/src/utils/MeshTools.cpp @@ -329,6 +329,7 @@ Mesh buildMeshFilteringElements( inElems.begin(), inElems.end(), std::back_inserter(r.groups[gId].elements), filter); + r.groups[gId].name = in.groups[gId].name; } return r; } diff --git a/test/app/launcherTest.cpp b/test/app/launcherTest.cpp index 5b3f3e4..056f5ee 100644 --- a/test/app/launcherTest.cpp +++ b/test/app/launcherTest.cpp @@ -71,7 +71,7 @@ TEST_F(LauncherTest, builds_staircased_mesher_default) EXPECT_NO_THROW(auto staircaseMesher = dynamic_cast(*mesher)); const auto & options = dynamic_cast(*mesher).getOptions(); - EXPECT_EQ(options.isVolume, false); + EXPECT_EQ(options.volumeGroups.size(), 0); EXPECT_EQ(options.compress, false); } @@ -95,7 +95,7 @@ TEST_F(LauncherTest, builds_staircased_mesher_without_compression) EXPECT_NO_THROW(auto staircaseMesher = dynamic_cast(*mesher)); const auto & options = dynamic_cast(*mesher).getOptions(); - EXPECT_EQ(options.isVolume, false); + EXPECT_EQ(options.volumeGroups.size(), 0); EXPECT_EQ(options.compress, false); } @@ -119,7 +119,7 @@ TEST_F(LauncherTest, builds_staircased_mesher_with_compression) EXPECT_NO_THROW(auto staircaseMesher = dynamic_cast(*mesher)); const auto & options = dynamic_cast(*mesher).getOptions(); - EXPECT_EQ(options.isVolume, false); + EXPECT_EQ(options.volumeGroups.size(), 0); EXPECT_EQ(options.compress, true); } @@ -315,7 +315,7 @@ TEST_F(LauncherTest, builds_staircased_mesher_with_override) EXPECT_NO_THROW(auto staircaseMesher = dynamic_cast(*mesher)); const auto & options = dynamic_cast(*mesher).getOptions(); - EXPECT_EQ(options.isVolume, false); + EXPECT_EQ(options.volumeGroups.size(), 0); EXPECT_EQ(options.compress, true); } diff --git a/test/meshers/StaircaseMesherTest.cpp b/test/meshers/StaircaseMesherTest.cpp index c8a1dab..334cacd 100644 --- a/test/meshers/StaircaseMesherTest.cpp +++ b/test/meshers/StaircaseMesherTest.cpp @@ -421,10 +421,10 @@ TEST_F(StaircaseMesherTest, fills_closed_volume_with_quads) mesh.grid[Z] = utils::GridTools::linspace(-100.0, 100.0, 51); meshlib::meshers::StaircaseMesherOptions opts; - opts.isVolume = false; + // opts.isVolume = false; auto staircasedMesh = StaircaseMesher{mesh, 4, opts }.mesh(); - opts.isVolume = true; + opts.volumeGroups.insert(0); auto staircasedMeshVolume = StaircaseMesher{mesh, 4, opts }.mesh(); EXPECT_EQ(0, countMeshElementsIf(staircasedMesh, isTriangle)); From 2642125713794e507e2b478367d41ee66060e8bd Mon Sep 17 00:00:00 2001 From: Alberto-o Date: Tue, 28 Jul 2026 13:25:31 +0200 Subject: [PATCH 7/9] Modifications to filler to selectively return grid-aligned exterior --- src/cgal/filler/Filler.cpp | 32 ++++++++++- src/cgal/filler/Filler.h | 16 ++++-- src/cgal/filler/Slice.cpp | 27 ++++++++++ src/cgal/filler/Slice.h | 2 + src/core/CMakeLists.txt | 1 - src/core/VolumeFiller.cpp | 90 ------------------------------- src/core/VolumeFiller.h | 18 ------- src/meshers/StaircaseMesher.cpp | 45 ++++++++-------- src/meshers/StaircaseMesher.h | 2 +- test/CMakeLists.txt | 1 - test/cgal/filler/FillerTest.cpp | 61 +++++++++++++++++++++ test/core/VolumeFillerTest.cpp | 96 --------------------------------- 12 files changed, 157 insertions(+), 234 deletions(-) delete mode 100644 src/core/VolumeFiller.cpp delete mode 100644 src/core/VolumeFiller.h delete mode 100644 test/core/VolumeFillerTest.cpp diff --git a/src/cgal/filler/Filler.cpp b/src/cgal/filler/Filler.cpp index ae72d0a..60c23bf 100644 --- a/src/cgal/filler/Filler.cpp +++ b/src/cgal/filler/Filler.cpp @@ -253,6 +253,29 @@ void sliceAlignedByGrid( ); } +void sliceAlignedByGridAndRemove( + Filler::GridSlices& slices, + const Polyhedron& m, + const Grid& g, + const Priority& priority) +{ + + auto polygons{ buildGridPlanesPolygons(makeFacesCCWOriented(m), g)}; + const std::array axis{ X, Y, Z }; + + std::for_each( +#ifdef TESSELLATOR_EXECUTION_POLICIES + std::execution::par, +#endif + axis.begin(), axis.end(), + [&](const auto& x) { + for (const auto& [i, polygon] : polygons[x]) { + slices[x][i].remove(polygon, priority); + } + } + ); +} + Priority Filler::getGroupPriority(const GroupId& gId) const { if (gId < groupPriorities_.size()) { @@ -508,7 +531,8 @@ FillerPolyhedrons buildFillerPolyhedrons( Filler::Filler( const Mesh& volumeMesh, const Mesh& surfaceMesh, - const std::vector& groupPriorities) + const std::vector& groupPriorities, + const FillerMode& fillerMode) { utils::meshTools::checkNoNullAreasExist(volumeMesh); utils::meshTools::checkNoNullAreasExist(surfaceMesh); @@ -547,7 +571,11 @@ Filler::Filler( log("Slicing surfaces", 2); sliceNonAlignedByGrid(slices_, fP.surfaces, grid_, pr, SlicingMode::Surface); log("Slicing aligned", 2); - sliceAlignedByGrid(slices_, fP.aligned, grid_, pr); + if (fillerMode == FillerMode::insideAndOutside) { + sliceAlignedByGrid(slices_, fP.aligned, grid_, pr); + } else if (fillerMode == FillerMode::onlyInside){ + sliceAlignedByGridAndRemove(slices_, fP.aligned, grid_, pr); + } log("Building segments arrays", 2); buildSegmentsArray(segmentsArray_, fP.aligned, grid_, pr); buildSegmentsArray(segmentsArray_, fP.volumes, grid_, pr); diff --git a/src/cgal/filler/Filler.h b/src/cgal/filler/Filler.h index 8f2d4dc..ed87558 100644 --- a/src/cgal/filler/Filler.h +++ b/src/cgal/filler/Filler.h @@ -9,17 +9,26 @@ namespace meshlib::cgal::filler { +enum class FillerMode{ + insideAndOutside, + onlyInside +}; + + class Filler { -public: + public: using Slices = std::map; using GridSlices = std::array; using SegmentsArray = std::map; using GridSegmentsArray = std::array; - + + FillerMode mode = FillerMode::insideAndOutside; + Filler( const Mesh& volumeMesh, const Mesh& surfaceMesh = Mesh(), - const std::vector& groupPriorities = std::vector()); + const std::vector& groupPriorities = std::vector(), + const FillerMode& mode = FillerMode::insideAndOutside); Filler(const Filler&) = delete; Filler(Filler&&) = default; Filler& operator=(const Filler&) = delete; @@ -42,6 +51,7 @@ class Filler { void mergeGroupsWithSamePriority(Groups& vGroups, Groups& sGroups); + }; diff --git a/src/cgal/filler/Slice.cpp b/src/cgal/filler/Slice.cpp index 9a674d7..5133ba6 100644 --- a/src/cgal/filler/Slice.cpp +++ b/src/cgal/filler/Slice.cpp @@ -183,6 +183,21 @@ void Slice::add(const Polylines2& polylines, const Priority& pr) } } +void Slice::remove(const Polylines2& polylines, const Priority& pr) +{ + SliceData& sd = data_[pr]; + + for (const auto& p : polylines) { + if (p.size() == 1) { + continue; + } + auto r{ removeSegmentsContainedInAnyAxis(p) }; + for (const auto& rr : r){ + sd.lines.erase(std::find(sd.lines.begin(), sd.lines.end(), rr)); + } + } +} + FillingState::FillingState(const FillingType& t) : type{ t }, priority_{ 0 } @@ -261,6 +276,18 @@ void Slice::add(const HPolygonSet& polygons, const Priority& pr) removeInSuperiorPriorities(pr); } +void Slice::remove(const HPolygonSet& polygons, const Priority& pr) +{ + if (polygons.isEmpty()) { + return; + } + + SliceData& sd = data_[pr]; + sd.surfaces.difference(polygons); + + removeInSuperiorPriorities(pr); +} + void Slice::mergeLines(const Slice& lhs) { for (const auto& [pr, sd] : lhs.data_) { diff --git a/src/cgal/filler/Slice.h b/src/cgal/filler/Slice.h index b61883c..b04bf76 100644 --- a/src/cgal/filler/Slice.h +++ b/src/cgal/filler/Slice.h @@ -85,8 +85,10 @@ class Slice { FillingState getFillingState(const ArrayIndex&) const; void add(const Polylines2&, const Priority&); + void remove(const Polylines2&, const Priority&); void addAsPolygon(const Polylines2&, const Priority&); void add(const HPolygonSet&, const Priority&); + void remove(const HPolygonSet&, const Priority&); void mergeLines(const Slice& lhs); void buildSearchMap(); void buildTriangulations(); diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index fe92b76..205c3a6 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -8,7 +8,6 @@ add_library(tessellator-core "Smoother.cpp" "SmootherTools.cpp" "Staircaser.cpp" - "VolumeFiller.cpp" ) target_link_libraries(tessellator-core diff --git a/src/core/VolumeFiller.cpp b/src/core/VolumeFiller.cpp deleted file mode 100644 index 0786e1d..0000000 --- a/src/core/VolumeFiller.cpp +++ /dev/null @@ -1,90 +0,0 @@ -#include "VolumeFiller.h" -#include "cgal/filler/Filler.h" -#include "cgal/filler/FillerTools.h" -#ifdef TESSELLATOR_EXECUTION_POLICIES -#include -#endif - - -namespace meshlib { -namespace core { -using namespace utils; - - -Elements buildTriangleElements(Coordinates& cs, const TriVs tris) -{ - Elements r; - for (const auto& p : tris) { - Element e; - e.type = Element::Type::Surface; - e.vertices.reserve(3); - for (const auto& v : p) { - e.vertices.push_back(cs.size()); - cs.push_back(v); - } - r.push_back(e); - } - return r; -} -Elements buildLineElements(Coordinates& cs, const LinVs lins) -{ - Elements r; - for (const auto& p : lins) { - Element e; - e.type = Element::Type::Line; - e.vertices.reserve(2); - for (const auto& v : p) { - e.vertices.push_back(cs.size()); - cs.push_back(v); - } - r.push_back(e); - } - return r; -} - -VolumeFiller::VolumeFiller(const Mesh& input) -{ - Mesh m; - mesh_.grid = input.grid; - mesh_.groups.resize(input.groups.size()); - - meshlib::cgal::filler::Filler f{input}; - mesh_ = f.getMeshFilling(); - // auto slices = f.getSlices(); - // for (auto gId{0}; gId < mesh_.groups.size(); ++gId) { - - // for (const auto& x : { X, Y, Z }) { - // for (const auto& [i, slice]: slices[x]) { - // // const Priority pr{ getGroupPriority(gId) }; - // const Priority pr{ 0 }; - // auto trivs = slice.buildAllTriVs(pr, x, (meshlib::cgal::filler::Height)i); - // auto triEls = buildTriangleElements( - // m.coordinates, - // slice.buildAllTriVs(pr, x, (meshlib::cgal::filler::Height)i) - // ); - - // auto linvs = slice.buildAllLinVs(pr, x, (meshlib::cgal::filler::Height)i); - // auto lineEls = buildLineElements( - // mesh_.coordinates, - // slice.buildAllLinVs(pr, x, (meshlib::cgal::filler::Height)i) - // ); - - // } - // } - // } - //take structured mesh - //use filler to generate the slices: intersections of each grid plane with the mesh - //convert the slice intersection into a ElemV with dimension n, where n is the number of points of the slice intersection - //build a coordIdMap similar to the one created in Slicer::sliceTriangle, where instead of the intersection of a triangle with the grid planes, we have the intersection of the n-dimensional ElemV - //Create a triangulation of the intersection of the slice with the grid planes, following also slicer::sliceTriangle -} - -Mesh VolumeFiller::getMesh() -{ - return mesh_; -} - - - -} -} \ No newline at end of file diff --git a/src/core/VolumeFiller.h b/src/core/VolumeFiller.h deleted file mode 100644 index 439c9fc..0000000 --- a/src/core/VolumeFiller.h +++ /dev/null @@ -1,18 +0,0 @@ -#pragma once - -#include "utils/GridTools.h" - -namespace meshlib{ -namespace core{ - -class VolumeFiller : public utils::GridTools{ -public: - - VolumeFiller(const Mesh&); - Mesh getMesh(); -private: - Mesh mesh_; -}; - -} -} \ No newline at end of file diff --git a/src/meshers/StaircaseMesher.cpp b/src/meshers/StaircaseMesher.cpp index 35bd6b5..37b92db 100644 --- a/src/meshers/StaircaseMesher.cpp +++ b/src/meshers/StaircaseMesher.cpp @@ -33,7 +33,7 @@ StaircaseMesher::StaircaseMesher(const Mesh& inputMesh, int decimalPlacesInColla log("Preparing volumes"); volumeMesh_ = MesherBase::buildVolumeMesh(inputMesh, opts_.volumeGroups); - fillMesh(volumeMesh_, opts_.volumeGroups); + fillMesh(volumeMesh_); log("Processing volume mesh."); process(volumeMesh_); @@ -42,22 +42,21 @@ StaircaseMesher::StaircaseMesher(const Mesh& inputMesh, int decimalPlacesInColla log("Mesh built succesfully.", 1); } -void StaircaseMesher::fillMesh(Mesh& m, const std::set& volumeGroups){ +void StaircaseMesher::fillMesh(Mesh& m){ if (m.countElems() == 0) return; - // auto mani = meshlib::cgal::Manifolder(m); - // for (const auto& gId : volumeGroups){ - // m.groups[gId].elements = mani.getClosedSurfacesMesh().groups[gId].elements; - // } - m.coordinates = utils::GridTools{m.grid}.absoluteToRelative(m.coordinates); - m = meshlib::cgal::filler::Filler(m).getMeshFilling(); - m.coordinates = utils::GridTools{m.grid}.relativeToAbsolute(m.coordinates); - - // m = f.getMeshFilling(); - - // auto filling = f.getMeshFilling(); - // mergeMesh(m, filling); - // // auto dimensions = getHighestDimensionByGroup(m); - // // RedundancyCleaner::removeOverlappedElementsByDimension(m, dimensions); + meshlib::cgal::filler::FillerMode mode = meshlib::cgal::filler::FillerMode::onlyInside; + if (m.groups[0].elements[0].isTetrahedron()) { + mode = meshlib::cgal::filler::FillerMode::insideAndOutside; + } + auto filling = m; + filling.coordinates = utils::GridTools{filling.grid}.absoluteToRelative(filling.coordinates); + filling = meshlib::cgal::filler::Filler(filling, Mesh(), std::vector(), mode).getMeshFilling(); + filling.coordinates = utils::GridTools{filling.grid}.relativeToAbsolute(filling.coordinates); + if (mode == meshlib::cgal::filler::FillerMode::insideAndOutside){ + m = filling; + } else if (mode == meshlib::cgal::filler::FillerMode::onlyInside){ + mergeMesh(m, filling); + } } @@ -84,11 +83,18 @@ void copyGroupNames(Mesh& m, const std::vector& names){ } } +static Mesh toAbsolute(const Mesh& m) +{ + auto r{ m }; + r.coordinates = + utils::GridTools{ m.grid }.relativeToAbsolute(m.coordinates); + return r; +} + void StaircaseMesher::process(Mesh& mesh) const { const auto groupNames = getGroupNames(mesh.groups); const auto slicingGrid{ buildSlicingGrid(originalGrid_, enlargedGrid_) }; - if (mesh.countElems() == 0) { // mesh.grid = slicingGrid; return; @@ -102,11 +108,6 @@ void StaircaseMesher::process(Mesh& mesh) const logNumberOfTriangles(countMeshElementsIf(mesh, isTriangle)); - // meshlib::cgal::filler::Filler f{mesh}; - // auto filling = f.getMeshFilling(); - // mergeMesh(mesh, filling); - - log("Collapsing.", 1); mesh = Collapser(mesh, decimalPlacesInCollapser_, dimensions).getMesh(); diff --git a/src/meshers/StaircaseMesher.h b/src/meshers/StaircaseMesher.h index 037d4e8..c6be343 100644 --- a/src/meshers/StaircaseMesher.h +++ b/src/meshers/StaircaseMesher.h @@ -21,7 +21,7 @@ class StaircaseMesher : public MesherBase { StaircaseMesherOptions opts_; virtual Mesh buildSurfaceMesh(const Mesh& inputMesh, const Mesh& volumeSurface); - static void fillMesh(Mesh& inputMesh, const std::set& volumeGroups); + static void fillMesh(Mesh& inputMesh); void process(Mesh&) const; }; diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index b4fbb61..638a03a 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -17,7 +17,6 @@ add_executable(tessellator_tests "core/CollapserTest.cpp" "core/CompressorTest.cpp" "core/SlicerTest.cpp" - "core/VolumeFillerTest.cpp" "core/SmootherTest.cpp" "core/SmootherToolsTest.cpp" "core/SnapperTest.cpp" diff --git a/test/cgal/filler/FillerTest.cpp b/test/cgal/filler/FillerTest.cpp index b2230e6..e8fbfa7 100644 --- a/test/cgal/filler/FillerTest.cpp +++ b/test/cgal/filler/FillerTest.cpp @@ -155,6 +155,14 @@ class FillerTest : public ::testing::Test { return r; } + static Mesh toAbsolute(const Mesh& m) + { + auto r{ m }; + r.coordinates = + utils::GridTools{ m.grid }.relativeToAbsolute(m.coordinates); + return r; + } + static bool allAreSimple(const FaceFilling& ff) { for (const auto [pr, ss] : ff.tris) { @@ -337,6 +345,59 @@ TEST_F(FillerTest, parallelogram_as_surface) EXPECT_EQ(0, countPWHs(f.getFaceFilling({ Cell({0, 0, 1}), Z }))); } +TEST_F(FillerTest, fill_cube1x1x1_size1_grid) +{ + Mesh m = buildCubeSurfaceMesh(01.0); + + Mesh out; + ASSERT_NO_THROW(out = Slicer{m}.getMesh()); + EXPECT_EQ(12, countMeshElementsIf(out, isTriangle)); + + Mesh filled = Filler{out}.getMeshFilling(); + EXPECT_EQ(12, countMeshElementsIf(filled, isTriangle)); +} + +TEST_F(FillerTest, fill_cube1x1x1_size05_grid) +{ + //filling with unstruc. triangles + Mesh m = buildCubeSurfaceMesh(0.5); + Mesh filled = Filler{Slicer{buildCubeSurfaceMesh(0.5) }.getMesh()}.getMeshFilling(); + Mesh filled_no_slicing = Filler{toRelative(buildCubeSurfaceMesh(0.5))}.getMeshFilling(); + EXPECT_EQ(18, countMeshElementsIf(filled, isTriangle)); + EXPECT_EQ(18, countMeshElementsIf(filled_no_slicing, isTriangle)); + + //slicing hull + Mesh out_1; + ASSERT_NO_THROW(out_1 = Slicer{buildCubeSurfaceMesh(0.5) }.getMesh()); + EXPECT_EQ(48, countMeshElementsIf(out_1, isTriangle)); + + //filling and slicing + Mesh out_2; + ASSERT_NO_THROW(out_2 = Slicer{toAbsolute(filled) }.getMesh()); + EXPECT_EQ(72, countMeshElementsIf(out_2, isTriangle)); + + ASSERT_NO_THROW(out_2 = Slicer{toAbsolute(filled_no_slicing)}.getMesh()); + EXPECT_EQ(72, countMeshElementsIf(out_2, isTriangle)); + +} + +TEST_F(FillerTest, fill_cube1x1x1_size025_grid) +{ + Mesh m = buildCubeSurfaceMesh(0.25); + Mesh filled = Filler{Slicer{buildCubeSurfaceMesh(0.25) }.getMesh()}.getMeshFilling(); + EXPECT_EQ(30, countMeshElementsIf(filled, isTriangle)); + + Mesh out_1; + ASSERT_NO_THROW(out_1 = Slicer{buildCubeSurfaceMesh(0.25) }.getMesh()); + EXPECT_EQ(192, countMeshElementsIf(out_1, isTriangle)); + + Mesh out_2; + ASSERT_NO_THROW(out_2 = Slicer{toAbsolute(filled) }.getMesh()); + EXPECT_EQ(480, countMeshElementsIf(out_2, isTriangle)); + +} + + TEST_F(FillerTest, planeXY_mesh_filling) { Filler f{ Slicer{ buildPlaneXYMesh(1.0) }.getMesh() }; diff --git a/test/core/VolumeFillerTest.cpp b/test/core/VolumeFillerTest.cpp deleted file mode 100644 index e2a475e..0000000 --- a/test/core/VolumeFillerTest.cpp +++ /dev/null @@ -1,96 +0,0 @@ -#include "MeshFixtures.h" -#include "gtest/gtest.h" - -#include "VolumeFiller.h" -#include "Slicer.h" -#include "Geometry.h" -#include "MeshTools.h" -#include "GridTools.h" -// #include "utils/RedundancyCleaner.h" -// #include "utils/CoordGraph.h" - -#if APP_LOADED - #include "app/vtkIO.h" -#endif - -namespace meshlib::core { - -using namespace meshFixtures; -using namespace utils; -using namespace meshTools; - -class VolumeFillerTest : public ::testing::Test { -public: -protected: -}; - -static Mesh toRelative(const Mesh& m) -{ - auto r{ m }; - r.coordinates = - utils::GridTools{ m.grid }.absoluteToRelative(m.coordinates); - return r; -} - -TEST_F(VolumeFillerTest, fill_cube1x1x1_size1_grid) -{ - Mesh m = buildCubeSurfaceMesh(01.0); - - Mesh out; - ASSERT_NO_THROW(out = Slicer{m}.getMesh()); - EXPECT_EQ(12, countMeshElementsIf(out, isTriangle)); - - Mesh filled = VolumeFiller{out}.getMesh(); - EXPECT_EQ(12, countMeshElementsIf(filled, isTriangle)); -} - -TEST_F(VolumeFillerTest, fill_cube1x1x1_size05_grid) -{ - //filling with unstruc. triangles - Mesh m = buildCubeSurfaceMesh(0.5); - Mesh filled = VolumeFiller{Slicer{buildCubeSurfaceMesh(0.5) }.getMesh()}.getMesh(); - Mesh filled_no_slicing = VolumeFiller{toRelative(buildCubeSurfaceMesh(0.5))}.getMesh(); - EXPECT_EQ(18, countMeshElementsIf(filled, isTriangle)); - EXPECT_EQ(18, countMeshElementsIf(filled_no_slicing, isTriangle)); - - //slicing hull - Mesh out_1; - ASSERT_NO_THROW(out_1 = Slicer{buildCubeSurfaceMesh(0.5) }.getMesh()); - EXPECT_EQ(48, countMeshElementsIf(out_1, isTriangle)); - - //filling and slicing - Mesh out_2; - GridTools gT{ filled.grid }; - filled.coordinates = gT.relativeToAbsolute(filled.coordinates); - ASSERT_NO_THROW(out_2 = Slicer{filled }.getMesh()); - EXPECT_EQ(72, countMeshElementsIf(out_2, isTriangle)); - - GridTools gT_no{ filled_no_slicing.grid }; - filled_no_slicing.coordinates = gT_no.relativeToAbsolute(filled_no_slicing.coordinates); - ASSERT_NO_THROW(out_2 = Slicer{filled_no_slicing}.getMesh()); - EXPECT_EQ(72, countMeshElementsIf(out_2, isTriangle)); - -} - -TEST_F(VolumeFillerTest, fill_cube1x1x1_size025_grid) -{ - Mesh m = buildCubeSurfaceMesh(0.25); - Mesh filled = VolumeFiller{Slicer{buildCubeSurfaceMesh(0.25) }.getMesh()}.getMesh(); - EXPECT_EQ(30, countMeshElementsIf(filled, isTriangle)); - - Mesh out_1; - ASSERT_NO_THROW(out_1 = Slicer{buildCubeSurfaceMesh(0.25) }.getMesh()); - EXPECT_EQ(192, countMeshElementsIf(out_1, isTriangle)); - - Mesh out_2; - GridTools gT{ filled.grid }; - filled.coordinates = gT.relativeToAbsolute(filled.coordinates); - - ASSERT_NO_THROW(out_2 = Slicer{filled }.getMesh()); - EXPECT_EQ(480, countMeshElementsIf(out_2, isTriangle)); - - // Mesh filled = VolumeFiller{out}.getMesh(); - // ASSERT_NO_THROW(out = Slicer{filled}.getMesh()); - // EXPECT_EQ(72, countMeshElementsIf(out, isTriangle)); -} -} \ No newline at end of file From 1d5e5ef6f2271453f9c0b22f709a21b038b6fd19 Mon Sep 17 00:00:00 2001 From: Alberto-o Date: Tue, 28 Jul 2026 13:49:19 +0200 Subject: [PATCH 8/9] Minor to avoid copying coordinates from unused mesh in mergeMesh. Adds function to convert to relativeCoordinates --- src/meshers/StaircaseMesher.cpp | 5 ++--- src/utils/MeshTools.cpp | 13 ++++++++++++- src/utils/MeshTools.h | 1 + 3 files changed, 15 insertions(+), 4 deletions(-) diff --git a/src/meshers/StaircaseMesher.cpp b/src/meshers/StaircaseMesher.cpp index 37b92db..d2d9006 100644 --- a/src/meshers/StaircaseMesher.cpp +++ b/src/meshers/StaircaseMesher.cpp @@ -49,15 +49,14 @@ void StaircaseMesher::fillMesh(Mesh& m){ mode = meshlib::cgal::filler::FillerMode::insideAndOutside; } auto filling = m; - filling.coordinates = utils::GridTools{filling.grid}.absoluteToRelative(filling.coordinates); + utils::meshTools::convertToRelativeCoordinates(filling); filling = meshlib::cgal::filler::Filler(filling, Mesh(), std::vector(), mode).getMeshFilling(); - filling.coordinates = utils::GridTools{filling.grid}.relativeToAbsolute(filling.coordinates); + utils::meshTools::convertToAbsoluteCoordinates(filling); if (mode == meshlib::cgal::filler::FillerMode::insideAndOutside){ m = filling; } else if (mode == meshlib::cgal::filler::FillerMode::onlyInside){ mergeMesh(m, filling); } - } diff --git a/src/utils/MeshTools.cpp b/src/utils/MeshTools.cpp index a86479e..00e0f1b 100644 --- a/src/utils/MeshTools.cpp +++ b/src/utils/MeshTools.cpp @@ -309,6 +309,17 @@ void convertToAbsoluteCoordinates(Mesh& m) ); } +void convertToRelativeCoordinates(Mesh& m) +{ + GridTools gT{ m.grid }; + + std::transform( + m.coordinates.begin(), m.coordinates.end(), + m.coordinates.begin(), + [&](const auto& v) { return gT.getRelative(v); } + ); +} + void checkSlicedMeshInvariants(const Mesh& m) { checkNoCellsAreCrossed(m); @@ -389,7 +400,7 @@ void mergeMesh(Mesh& lMesh, const Mesh& iMesh) assert(lMesh.groups.size() == iMesh.groups.size()); auto coordCount{ lMesh.coordinates.size() }; - + if (iMesh.countElems() == 0) return; lMesh.coordinates.insert(lMesh.coordinates.end(), iMesh.coordinates.begin(), iMesh.coordinates.end()); diff --git a/src/utils/MeshTools.h b/src/utils/MeshTools.h index 5fc4546..9bfed81 100644 --- a/src/utils/MeshTools.h +++ b/src/utils/MeshTools.h @@ -35,6 +35,7 @@ void reduceGrid(Mesh&, const Grid&); Mesh reduceGrid(const Mesh& m, const Grid& g); void convertToAbsoluteCoordinates(Mesh&); +void convertToRelativeCoordinates(Mesh&); void checkSlicedMeshInvariants(Mesh& m); From 0572cb3054a6a16e0da7ba55520130fb94eb55f6 Mon Sep 17 00:00:00 2001 From: Alberto-o Date: Thu, 30 Jul 2026 12:28:39 +0200 Subject: [PATCH 9/9] Minors answering PR comments --- .gitignore | 1 + src/core/Slicer.cpp | 26 -------------------------- src/core/Slicer.h | 2 -- 3 files changed, 1 insertion(+), 28 deletions(-) diff --git a/.gitignore b/.gitignore index 1fd2b13..130eeec 100644 --- a/.gitignore +++ b/.gitignore @@ -14,6 +14,7 @@ src/*.json .vs/ .vscode/settings.json +.vscode/settings.dev.json .vscode/launch.json .vscode/tasks.json diff --git a/src/core/Slicer.cpp b/src/core/Slicer.cpp index a33defa..8246fff 100644 --- a/src/core/Slicer.cpp +++ b/src/core/Slicer.cpp @@ -38,32 +38,6 @@ void orient(const Coordinates& coords, } } -// Mesh Slicer::fill(const Mesh& input){ -// //input mesh should be structured - -// Coordinates sCoords; -// sCoords.reserve(input.coordinates.size() * 100); - -// for (const auto& g : input.groups){ - -// for (auto const& itCell : -// buildCellCoordIdMap(sCoords, buildGroupIntersectionsWithGridPlanes(sCoords, g.elements))) { -// const IdSet& vIds = itCell.second; -// if (vIds.size() < 3) { -// continue; -// } -// } - -// } -// } - -// IdSet Slicer::buildGroupIntersectionsWithGridPlanes( -// Coordinates& sCoords, -// const std::vector& elements) -// { - -// } - Slicer::Slicer(const Mesh& input, const std::vector& dimensionPolicy, const SlicerOptions& opts) : GridTools(input.grid), opts_(opts) diff --git a/src/core/Slicer.h b/src/core/Slicer.h index 7f418a4..b51ae15 100644 --- a/src/core/Slicer.h +++ b/src/core/Slicer.h @@ -24,8 +24,6 @@ class Slicer : public utils::GridTools { Slicer(const Mesh&, const std::vector& dimensionPolicy = {}, const SlicerOptions& opts = SlicerOptions()); Mesh getMesh() const { return mesh_; }; - // Mesh fill(const Mesh&); - static Elements buildTrianglesFromPath(const std::vector&, const std::vector&); private: