From a7d2d2391062ed5776d1a2368cc96cedd6de8e46 Mon Sep 17 00:00:00 2001 From: eap Date: Tue, 17 Oct 2017 17:38:39 +0300 Subject: [PATCH] GPUSPHGUI: create a 2D remesher algo and add Chordal Error parameter --- .../gui/NETGENPLUGIN/images/netgen2d3d.png | Bin 26499 -> 29486 bytes .../NETGENPLUGIN/images/netgen2d3d_only.png | Bin 29046 -> 29966 bytes .../NETGENPLUGIN/images/netgen2d_remesher.png | Bin 0 -> 17931 bytes doc/salome/gui/NETGENPLUGIN/input/index.doc | 4 + .../NETGENPLUGIN/input/netgen_2d_3d_hypo.doc | 31 +- idl/NETGENPlugin_Algorithm.idl | 25 + resources/NETGENPlugin.xml | 23 + src/GUI/NETGENPluginGUI.cxx | 9 +- src/GUI/NETGENPluginGUI_HypothesisCreator.cxx | 416 +++++++---- src/GUI/NETGENPluginGUI_HypothesisCreator.h | 18 +- src/GUI/NETGENPlugin_msg_en.ts | 8 + src/NETGENPlugin/CMakeLists.txt | 2 + src/NETGENPlugin/NETGENPluginBuilder.py | 24 +- src/NETGENPlugin/NETGENPlugin_Hypothesis.cxx | 80 +- src/NETGENPlugin/NETGENPlugin_Hypothesis.hxx | 14 +- .../NETGENPlugin_Hypothesis_2D.cxx | 123 ++-- .../NETGENPlugin_Hypothesis_2D.hxx | 23 + .../NETGENPlugin_Hypothesis_2D_i.cxx | 104 ++- .../NETGENPlugin_Hypothesis_2D_i.hxx | 23 + .../NETGENPlugin_Hypothesis_i.cxx | 30 + .../NETGENPlugin_Hypothesis_i.hxx | 33 +- src/NETGENPlugin/NETGENPlugin_Mesher.cxx | 268 +++++-- src/NETGENPlugin/NETGENPlugin_Mesher.hxx | 5 +- .../NETGENPlugin_NETGEN_2D_ONLY.cxx | 2 + src/NETGENPlugin/NETGENPlugin_NETGEN_2D_i.cxx | 41 +- src/NETGENPlugin/NETGENPlugin_NETGEN_2D_i.hxx | 21 +- src/NETGENPlugin/NETGENPlugin_Remesher_2D.cxx | 685 ++++++++++++++++++ src/NETGENPlugin/NETGENPlugin_Remesher_2D.hxx | 62 ++ src/NETGENPlugin/NETGENPlugin_i.cxx | 4 + 29 files changed, 1727 insertions(+), 351 deletions(-) create mode 100644 doc/salome/gui/NETGENPLUGIN/images/netgen2d_remesher.png create mode 100644 src/NETGENPlugin/NETGENPlugin_Remesher_2D.cxx create mode 100644 src/NETGENPlugin/NETGENPlugin_Remesher_2D.hxx diff --git a/doc/salome/gui/NETGENPLUGIN/images/netgen2d3d.png b/doc/salome/gui/NETGENPLUGIN/images/netgen2d3d.png index dbf97953f7e9b4726feae01078fa7d685564e411..17f9d55b910afb8170c2175ee1990f871d7d7076 100644 GIT binary patch literal 29486 zcmd43by!qy+b)a>28f`Pw1|YXbfcsK(%s$NJt`s%(%s$CGB9+fbm!3BJ@DQ9p67kv z{nWAd{{Gq=2gfkZT5Hz2*L|PYb;iik92?_nvM`1Z6qyfvpTa(QhhMDDy`On4m)8wl9#inKES(Y(j6d0G zgY!G)QYm(Ftl)9J>RI^PMuC_JI{Z*fLEh+*cz08U7|HV8nKBh7!N&)`<))SIABS&P zMmQ!X+eNA4NoN=s5|I8>P!37!m+ zgM2vwYK6CIFmW23Tt&6QO6Nb+_%n)=Vg5J)>GwqfD`|+?R1I=1Iu#~U$4q{ezGHY# zoZ!A=*G~4G8Wxa1MK{Y|?O~i<+?p=MBOQ{;?2 z4?K)EXLYkRV7!{MG#G&{G1Of3!n8?@;yA362OVo&xsO_Cleb^4cWm;{)h%o&q)!`n z*9Vj(m35}e;rCS4}%YCaURMScsZArF7rXsU3njBb~H| zGU}{OAf2Rw{9Z>V)hbD=ox>qEW&Sdplj3LXfL=&x>0!C(=+@$jFsbyT8|$l-8A-n^ z4HT-WbLWM#a>_YT`Q36NPyE^>L4%$os(>Me2Hpou)^l~dC*}5^oiv4ZuB;@6SMo;( z_&;V&dahcfOncjBb7dDPck{XKJ)#Jd7+IM?Cv9d(%#gY^>^5x0Ofu_0sSTO2P$6y0 zd*(mplv~)HmRl&BH9EONDxh9$OD|twwEIAAq*8?9Xsj^vv3b&l=2D3_A&8a*sO?XYEj7tBGZ zxTr{KOfzZb;JJ;zE>yb`b`iaD3{@7Xx~WN;mn#U@(vkm-+F&Lna)9%CXd_=SE|C@P}+s!9iv@{mKZ9w`Zj~ ziRv<^$fO~)^vmJmRNe@l4uhUzb(t%U>YM}p_jAH-6^lBWWvr1osP*wBauJGiQCn4t zzvJe9){ft_ZmJ=#5%Gj=jA3Ra^5rTl-`F)kza&M@Uv4{^1-CD74TBRp3}$5DPo7rB znQ~XqQ||pt0F7Qd9d#+zYAg1CY3O&5m^D^Fo zJk;-HAz7~VtcLd=w2@vl9=K{2)|?%D2gl}eO%w>d@d=O9{l%!T`MTq6ck#DFVci{> zJoG9?x-;w%FI6hUQSsNWU!P8eMU%@C^vhpp^$mQ^QqouM{}HcbL&b((G=*BYBb&$e zaC1vKWqvm9$Jw9=bi=9NF;&2m9b0U7!(B(`6-{OQ)>!|w8|Sy`Lj(pHdBI>3uL)S1@BE332jpI>j}nb;`V%l<-)wZpo&ZeT>YmxZ%VY zeFeAR!Ize7Gq#a}il!X0=Z={17EBDjN@YW?c=%ox8|**ryxmw}Dlt`MJ^;-%Q0NGE zxEhdAt%{7qPAvHS^Jir8-su{i{b_wSWBUx8ULZR1_B5&CL!mvM!r8dNS>6~TDfhsp z!f-c^Ffo4ye+MzVeQ{!FGLMU$LtSgP=5NhIQ2*t4R|+xwmF9u(#g``eM>R*1AK=E) z#S2>B`mC>K?1)wgZ?uJ7N9jEZGfWKddQ7(zx;6O)-CU`#bEa}x$1a4EIN&L!=>jLG z{Lqip{WM}cLbeu8uCjafIw7%V1|z9LGZ0?5EI!5~oOun}(EJ$~GpV*VgYbOocu#0% z<{JeXoKK%VowM$fpU!-~t|KiR=N>4Po%^w(IDcANxmaVd^*}$7 z-!s0qR}xzxR|8tQ8IE2#V3nF|?|ON<+jiDbvN83NrpIYr1vvweqIa5BkO=dNp;;+f z-&<=feDDP6p*}mdz88F@+VfU*9BCA$5FZ~e9=Lf*B=IrB93c*^FV=zuXt(?jPO14M zItGLPyuPF{nI{O4kJJ^3ja%)-t6_Y#5^3KYoltCbZ)dTo)pEa>6_-!n*yVZ#vE@ax z5S{6;?blx@ot{sVH#*!^sP~{s>gBvvK6>S)cyPbBAEg0K{(wtZhpQk!&7SGkSwck$P%YSGw;rb05W`zJ7bq)D>~4_e_(+ zGUz^Ci+}bZ!d!Fr)U~Q3W1J~#yknT_P}_qn!KzN(*4T8e0%rvjpArvkQPGDGU()7! zH5jiUBc;W%@^~^zak=6yb9GJpJzi9qz>_}C2%0u_Rbp({w-WDj?l%(g zGqpKFCMe@nujkjw3%lN&iU^3~FmWIc5|8W3#rDzGbSKy@$h8e9EBNi&JhW48YSDuQ zGsDFQ-^fC~Ypara@NDuG!rVwselfrP6BP8czdaFt8H{Z)Ylh=mr&%(-WEI_fdo$57 zz+sjSgWy_^Z&kXiuAzzSNeYofzjwL4mZ ztTN;ZN}cjAWo&yBI6~>7en1k{(k5~p@q}0V5`6R3N?xB)eW!LE3sJ0bf-fF@&zfFS zdX2#?JFV2%U-hl)lD0Q`6BbyZ^;p-HpcZvvlFuXgm&4V}u~jfM0b6WrmWOgn<1v{y zZE!F>d;h3I%8607s`K8j!VGVUr5A#J9o?x(948^`CnOH9zhgGLa2;jOtW$l*q3uqvM2%Gs~1yECQm{v=k59i2b4Lcg&vs6s^#-DjG4EqyT4S>GP^vL2qx zdJwR$j;CTdBy4v?r()G{PGY1eIXXBtJ0-W^@`UeYJ|{VI4||a(-O}#xY`qolXmztF zkk?D$tUN6iH2{C1q#^XzdICk_`I&ZES=s*S;d0SE>qCq8@4tEyVkukQo~^Ot(y6s( zN!YUPpBOHkmyVPuOIs5W5)$&7jV_(=!!{84ytCqd9jajwrl&On+rm?pW!lNDQx*Ee zFm`Y;?_wPekF14}JERj+(^9nSS1wMX>@~eU?e1$s-r4nXilSo}^cUhQQjF6?f zU3{d;%r;V8YP)jWkpf}fE1w)Gl=V(gE;4G(eP=QCOUgP?z1qt5y0t;_N%BJ)9GE*y z&ZI%mPb^~4_oAkxyWo#>E*Dn8{c8e;^u|+^3zL1F)&AR=i9bPyqvL@udCcC-LhHYj zm$R_5v$LAc_%W`nc@9*)U5Xc2W7!-p@GUB$XFT)p?|I@%T5oA>EnN^zAIoX=RG>u5 z(t!wkO{gr(@b=Zm^7&bg_29*GR@;GykM_55!#y`byJw2$7sDj8+RIm@w;j8D?xyp_ zmdS@RzVP;5H*#`v)6p`+v=h|L+F!q)UYL%WCGQaF{Hl{}`O(_iNqFzOOCr8!EM$#W zLrMyMX}E9YXq}WMe5n^A?55MgHN+s~@$`%BfVg78=u^VWCr1tE;Z>GuYi}95-+Cw7 z@xP=s9_pRc{@{s+tL!yqInh}BM<$vPQMG;#jf{j)lw$aU^{M`3CQ|`AH zrV~HxXBOvRISnc5x#KbKxGStX%Jv{S!zJoLv(fim-Q8`^OB!6N?2W(NXEceUDZVYz z=$djQJlDN&o|v8{EOg#Jx8>yxxY?y8Yt3j`k27_8p#f(5Z?C)azB}jLiTlnAhe=0n zJAr!VztWIHh!mHdF7U5!+=EhB@r#twL;`b48MTvR(J-7%EG0GOJ!&Jht1VWp_UhY8 z?N&UtX6ii?;8aqWyq=WS7#>?`dd~%U2?9LW1lH&jQK&EaK3X=NKGTrS?yMGF8OhZZ zA~?T%K?+f|MzkcdSPnzkhQDS1a_OoLN?>}fKyyuSUgoh8{`$<4x;U}^+zhnU-29x% zZp(k}b;mH45Oz#W#ZQrt)FH-@YQh;?^`*}8*Y;bntu%S%*Ucr$8eYc}VSMI+9+~$G zUruxF&fNP&PS1s2+x;{8X@gkz=PNK8^`C!LzFaSl25 zkbKi1#_)!I@)?i1iK69C4-W0eviUd{d9qyxn2=-W9P~N4C?;t{@5vQjLK((ROGE=4 zx=5}Iuz@YWp`Z0k5n*99%4>6$E2-A&?S{mN>;P^j+R4}xGGWtW8N5`xny5vXL}pL}G-5qymG z(}3o`;W1SOFO{M7rlUwb?+t|}iz2UKKzTXVGaRB^)$$)%LeB-UhlQwy(MoT1E%k+J zPeDJcK%E_w|5WB)K)TKT0-4_JRVbC&ScSt!K|ej0bpy98C7PY7Y9ZM?nF=-eb_Ggm zBr~=mwaVqQd9MerZtU)xnpS`M{JGM^BD4S%hel?u{}~4dhlKCz?o?l?>`C8Kai!?4 zZ*T3(%4G6J!5NW*T>{eEdZr&$s%71(A<1%zd?--#{ocU z6UgXJnw>Q}Mll51v%vP_-vHs7Gn|Oir9{=Zv1K3P>=DYKQisb1ia0`Uv$C_rU(;uU zEei_^ms$OP`RREjDSTMXX^Yao?Mm}J=;Fu5eSH>pH)`0laWnyPnG~!J95}Q*rTQKH zud6*HqXVLnB&X&xwm&hFO8CBN)o&_f8CEP%hQoLI@<+{kxh(RIbE#^?`1r{ri(*;i z;W5+I!{<9^0xGAoa$dJ^469ja5pBWpdha+I-r~-dm|+eLHEJS{bMiA{o<%JfWkil- zR(M9a79!DejUfRssgb;&D}YL(*_ zsa3CZ3h25adgJ+g<15YJJ6j#Mh?>vUB)f<>zTMS6b(6t-HpSWe!Bky+uk!$QwQPmM1PS= z;&fzQn)8ftgRhXNH_(xjCq+D`I^Ql=tvi~lH83=MVY~FQ+(^oJ=Jx>`ZnU+U_HkTVPIrr!@Pt%Ec6YZ$BHQRr^gNrQrCF#+^yX4PahEQzN`PiYX-<+2xr0?O`(u22s)qa7Y<58ORiH+*sbJ-tS+Fgx8q}?tSHg+UB zz8FtdLmFiD5jB>CnzfEO;8Wh=cV8;a$2U%55XQSG3saTm9!%##@vP=j)w_(g*XK_% zE^(G*4XawN6$_MxbEK`2E{`|*HZ^=No8)t3vI5F?kD7#hFU~VCY;A0G)~QJGObZ(u zbt3_yS!veVomp{z{o3-Yx;jUFyf~2uqNQxTRxI-na{jz{chsfw%si3Tm04Qg^z^iD zqKI&9pyMUcUT}R0i_^7Bdk}HQArmHgoZ&CNfsjq3@pVf~F(6WW$2lTiw}G z2|r?T@F2I2)C0W-4hnCm$fcOtR>`^-O4Se+qOzFcTUD@ z$DZ#8@by822vW9B??TNQxBdBomuT?I{&W9`fRPf+(D^c99qr zl{G%!<%i{_Ex%D6a5&!#J8wOJUusYc}*P(Z$;1 za^t>CK_Aq*t6d&sWZnYWtF4lT*mr!$JnHH&_A3k`o%Y_zL;*-LXjfSYf@Sq#hlf3q zDeodgg9#2T*80CoqOq$uuSg4&yPuKNt9tCA1YVu)8TX781(C2kdw=GvIO65wq7Z9n z1U1^>)`Apaj7Pk1l(_Y>{^d05=547~;Syhh$MBksEvf|9qeyvGwg9)mYO;H0y82?N zr-X#N_{$e5$@mwoqXnNdpbq}dbMEuGPS2h_)A)^9aK%e!!x^U1MJ5wGPqxVIFh@z}vZ02KSUM-^F zZrc|Y?jmm0WwB3po{aXXWxT$nw`&mO74NmXXLb_LcDykxW<6U_Qs2GFX6}HeV^3@6EjCEQYCRZU%;=Oe zjiwoF#$m9pGgYf856`SVTza)%23^(ANslTX=UurpK@?^dmW%5^#$XcjrOdYmMurEX^W*qzX2|ZR zyFK#8h#$dp2rK+~ukzT#m7cA!YU}an$&|iDu-IBVv?*=QJ71sBwq}T~44TIdZNWFb zQ)Sz1E#XHyIxyug&Us#K9I263P+Yg>9v?f@YeFN78W8=~>R$ANrF3AjmAj3a(J1D3 z4%4fL$A#%C)LvWhaTDWo0aOqTCF$`8sGgQWT5i;r8ehv}vv+?acTk`6a6kay%w~Ur z)%6OVQUB#rT3Xt>38C#(`Cj=nGbXf&_Q-s!(6DPL>&+XDMgMp<{L>AY`}eswDvu6L zf37(%gXQ-2?ORFoXSxH)e4Gk}ot@8HHV&`teNNS17TB$vZ~Y-7xV*qu)b{gbGgQufODs}8k;UzDKJvkAMh~ui`(g@4nubf>o$;H$YUaOo z8h8Ksd^gJ@xnVd&R{S_r2$dk1}qvu*R&0t%E~zN5^y_ zT$~w);`Qs-_oJo)s&-OJ9jMcva@$^WhJ}TlpU3Xq6AYIW@&70#l`S@)Oh^2KI%CRt zy)ip@tB*T7kp+iB@Y#+}R@{}d%-@LM|N8w#(%B5fyHAOZosu2X9pv*A>JGbUUB_0g zxz6-VJp1jw{P`op?YQ~f+xvl&lhgUtRk;`9Qt*nMvUp~!STnGsgb~1bh4p+g7>uD{6s;|WyvU!|ps8#yVIbaaJ{m(fapyxzV=+1V2d7BR{e3nhW4h+X4} z#m3pq7$@EmK)c2A-Dmt(p~9}@;SA%(BLZ4 ztc@NmP>zU=?F?kB?*z!1Y@A4|T+HOWtHFP@%@R$gDyIVh?HRls{qkh1gJ-W!U*W0Y zC*7+nS-mg=ZnNdb*$<^zRCbp(r4*~pSk33YX{Oblj145*a-Ad}x8{5hs1@P2q#D#45G!BQKqBM}i1@Vs=IzM;@*{QmWa z-_pXu;FRymb>Ih_5uWpMC0Xd2lq zCK|yuwsv;uZYP`9H&=U5sK39z!`@s{01oZYFI}OV%PmGt(n*NXtwnBe+Di_LiTB9j^d@Tcr_!#$^aUL zkfIxp1#Q&bHt7HeS$iS^5a<>8UUhOOiWA}6WF&)208|zdT@5UIgjnkCd(*tq>^YVQtm(x6vVo^ zy5lk006`-o%AOcTg(YHQLZ$wE z3GD|uVRiwB!@7)?>k7^bEUY*-bKJf9tK#ClZGmzKyTZ6k5O~XghR3iBgqiKha$h0e zXE;!6I~og!dVKfag|a8ol8a+)EOcAzItneYtJC3vvjPz>p1i;U9{(o?*Px#QO$R2_ zo(HrwpCtVry>d9SqWt{q_2ste5DW7k6IP9o+FWz@ckB;=XaG8Jn*t@FJ^PdRN(V;E-+(F~g19P=J6{YgBa9qV*XTwXSqnVtP4A;H4Q{-v^# z&CAP+POC09C)o}4i{*6HJCAc4sU&U@H#csA;2)sv*R0(dFOjdYGt|`f8I_ihXc-%e zE-a)2Y~YxGdSYUIQz=i*2gt*k4Q7?`g;%%j_SV|Ej`kg#daaK^Jr@bYrFXeli3z?; z(fM#y%OrtIzr3QX#qRdBQnUriW^Ov2T`7C=Xs6EKGx!ooxFp`Nrr=`0X;%^~W0i<4 zzEL3o;`Vb}eNH^jA4h}64iK(M3kwURKQj<|^Zo&@Et-I4fBnTbl&ul5n2i3%&j*Xt zIf#gfFAh3LKM!d~P=21MIcnNSy6*Rn=PqWfKmP`1SK>V67S;%v@O?(yJUm?9SdT5T zxI0@*#APQO^YI>l6XA~^AA?dh^V^nupus!wQ+MoTRaI4^H-v*rVC}})eq)GY)~k0` zdUpaXKY^#6%YOBj)hstJA732kT@jZjv^MhwEKXpuh*$^5v)bM-H-cnW?@CP(6VY(~ z3=3-tiYq6D?tx)`!O9j3#*U$_?cnB-5UTZ?UcIqGRmo)D$v2*#6FI}A`7geMKFH=| zQ*n0=*5i*wB~DlT^Cu=QqgLvf@$1Z`#nE~c@0&bJMGipoZ3Hrrh z52GwLinO%!RJFA)5-6cWtAQ^T;qcD}25%DB zi(_(j(aP-{?c2Hr@vTWqnrRb$f<5(mFPK@#=srLDt7ZA$GypRM6ylP?!pL1+Z{KLt zTWrV(<1cNl{>aE6=jWd@B32I1qkay4az~XaEa#hs(){FfW;YePkuSPxal%3gc4g=?)!422n0t?*zLkO|3S{grC z!&ipCfDZgXFEr-dewrQjTeypMolkPSSd;wKD4dIgnq!jrdf?+F>?%eu$(jX<;0E1 zu85Z$R`(g5w%Wl6#JwZ`9;5B5SRw^#tXfets6Z@wBh=5frzJWobhO+N=o%XNI);W| z6$_NQjY^9KbAfgR^k9Mq-> zH{6@^1QobxSCi+k0uN(vC`)1_Ul9!n0|NuB7=>+SSvP|L_WAAk6ppJi#){+`)i-ff z=F`!eZ23oXkG%6_%O8UNcaRoi7MAcS!rPDEJ^J{Lf}$e<+=eH#p>5pANwb=v6ONT+ zY1G(>24CE`?;UOyz(5PfBrBWqlE;Zx6UsXPnD=2(gT=*}#dN86)a})QmSkF*#l-Kh zPyu|@FKYDKBv{RfbWv}D4NHTV42Oho&f&MQyrz1|WoaqT^sj%vOAO?1xmafbGRj7G zu|`ev$yS%fXK_GSD?jRUb8!!?T&&&k6n5*WT<3n)oZBLmu7Luj((BqRs6i1mDpiON zC>+vYke?hdU%ji`94)Y4Y(`m_Lumnj;?+tRHqqIuGqG z$TWQgq}!-Bu}B?qb!s_xtMeEQ&qJg;7<ZuGgv zevN?^av8Tdn!l2Fb~I3+vVS62bpw6k?(RN&`S)^Q+9Y3Nf5sr6BXu{7N8sj^xS#Fz z#MGlAi%JAfzP)$h+nN}(xhGbqp7KHpL$#o zt6f$%4VIQ!AppgD$+Yk}*){tCpxEn`D(?mPXj&DNF#oH|G>Mt$j0Fe=j<2eeaC z&c<+xz@w2XRvXT>{-l=aacMvik2lDuQ7ewEKcA8CZCncCVOiNuh8U`Qy1?(^IPzN@Tq`=GNVB?oh5AZ&qH|bS?;9^^MioBPH=X!X}7GYjN)Aj#8~#-@4y{=|j6t zQvfUq9;ao9?4Iu3mmK}^Y~6oPDV5k7oWOA}cYUGJZ}BUX+i)q{lkB_8%bEfIhbW($ z#Pxlm+5^Cl7@j2pmkV3O>xtc1A^t_Q>XH@Vr|fj$-_ROWZe-c+mps!71*3omFUgH& zeJAf`D^?Z5GQ7iFcR?X~z?no?%_?OB(?{*L*WV>3<;Sw|#{u;GZ``-~*>UhBVghPd zAbX;buhcZzg+i+BI#f?h;o;#yNBj-bh}`$$e;D2Wt^yd-{8t6A8?I-fRpz*BZ4M5? z5>7?lVm^QVRn1al`jnWCIDi^utVpYk2iO!22w1JNJu?leHDk$(An}yDdKDfazdm?m zDB(*c9+u9lRN^>YbzS9X|3ow*=I-O=KU%SBvMI7|T>W(o-gjIx6on$ifZL@L6A71o z1VXAF=Q30ekdhE`A!0q-uI)AG?klgmXV0u8Df#rlBUo^Z^n?8I!nQy@ttFtcO7e#= z%>e}w(3VEfEPnZdRIJ@#weho6|MgkK@dIBBDssN0pks&oz8KZ#5vTP3i23zeu6KtX zOLd-W8y}BBxI5ePUq00r3mtQZmFKlTZ%^Q|rvymf+SazXxYz_120d}Oi{!FoWK&a9 zW@ct866(+Ja6uazn@Nf6G4?TwDUZG7{p`Mk($Z0gqW!Mw=8Wr6p89-*=;!z{&)Ne< zsahMdXh3V=+uK2)AHKOMI(JbSzG^JQZi@|UUxfJOT_K)1(X0lCBuNA z&q8kiZMy7(jlCVR}Rylb1%a+pT(O zI50MsV$iqVDi;&k>=Ew)n1)j_`mzA%{0pA}_4D)hPXlx|`q?L5N;)<;Yjj(9UUfBx zEuOxFLr^{9@@ViU8LRnNB+%o^4s%lSw2IV{-2`sy0#LLW4Yo=WfC4>Uv^SDR{z2Y5 z4wpBI$9a$0ag()PwT?;JUAGSK0T$zaBBO!izL^^Pjlm4xETI>nPei)zFUfJ>e7V;f%NEFelAL)lOg{lxK$3{2>zCgfZd?R@#WQPd z0T`OhaE@;(^`D^zwpsV%%q=ucaRZ+2@OS#{HaFLVvtYyIkKm zQl?0&K*I*odC^5`QL|_=Am568LD4x@q`sz}t&{*srtVURe5_paKrlA8wEQz5r+|4g zC~Asxrfb0C9a&|KIJvpYs8~mabp7OnGn5D`snzH^LP%OB^8!@i5pkej1)Zy~sA!of z{GlsR3X^}4dKH1$2O$GXA&u_8%p&e+5vTG3)mT z`{l=7U0tH%B^ou%eaUY5$mA~s9}hE`Gk=znTJ^yThJ0?bZm^TMBfzUDZ!tB+G?}| zJpnTiNOYkqT*Dvs>h83ztFzrzk7NAy5R$&bJ;*Ny6`v^_Xu{8p;W$2XO5itKSe()c z3TBtnI#g!~VFBY%KTh)BwwcPw{|r7F%D8xV(j`EXh8fYr@D35q2e0|u>Vw3R-0DV- za2T}Q_O+-%gTC6IAU%7T%xX3ixid{#*Fw$C9&MbmB?zprC&{>SIx|Or1>+Cr3)`;#YctOdy+ zji8j)9hif;t-#x*Gu5k$6S?u5mi@6_;&KWqvpdajdU$$rx^s)~)xjr9;jU0udnqEc z9M$sdAX0%3B?Es$JaSHIzXL3)jo_1B7UJ`>^FcxFC`?CY)LW&ZuHJmX32EWjEgRpUMFxZ|rkVK0sro})i zkOKpxwRQDSRu&_C^ya~%m!}&!6O)r0C$8)JWc-)=LO@qLKhFr|wAoM9y7(pSs|P|2 zt{^E~ZSm38;>CqMxR+5Xq@kVm2t6T3q%fScW`p9xR; zf&KYEYJ~N^V!w#4zW#$Rq8YC19Hefp?za0sy~6oz_FsW@?6w6YdJ~5fA4_xD%NK&Y zbsfxcr#s^*4<0?UJKNQile7Bu7i{e`aW(ymQs7`{saCz(Q&PVKbR9R(DvQBzpFrHO z`1o$%k*;zPaR5K^(GIQ$!ttx40Ad-dJvgDU9&uIUxP^OX8UYi}v~L(eSGP{z3BgT2I}&|kQjY_K5qEV8e6a){mxr}@{IkUVuXyqg;V4z>DLg|7z{$rWB5Lfo?npOt zW_|*#REX61xl4kh#$7LYeYMw+JEBxKfuQ)yuJ?35#l*+=4-E~SY>zac5f`=-M+H_Z zWc`sTnk>@5` zg<8=YdEMuL)aRv}NO&2kH8p)5N#KxhczW=X8N$E@VeY$_%d!9(_jiXn-!e!r4f3jGab zah~(o96QUDjmWl`uNP8|{$~WMo7lM%&8v2}?;tFm-Ml{_iZ+-A3uV}>(iIoU+?)`| z)nAkf3|oGnPXKvww83WaKBL2qXyqo``cx5NX=y3646uzOfEl(WN=aQ2IqQ|Rb}Oi! zTJA}kY-U5<4Qvonu5{W0g~>1!ZAfr+C}R|H((#zGlS8_0M!Us)sZjN^R5?R28bv3% z3Tstyr;FX%y>p%Y1&^4Od5HC|q&uz)E@g}UqN}rW;e3$koAn`v@h@P?H?a5PMvH) zoy;oLluv){P)a}1o4-M@nomU*Yig%@);dAmP@#@(jIRd z5ddT7E{>#F;5AnCC|A9NU-yT~iZR zsQD+dtF^uT5TrYR={cY~nm zx05wNI3&)CN2I{!dO{Y~08iWj72}v6!hDPc?wpA4=?<73sZz6UuI*h)D)Z^fpA&E; zbrx)92T*n)Xfp&Sorx@LN;y)*PvQwAfhQERwB&N@P1a_`;nDgLSR?r7W9rDClRNv9 zYJ+I7d}J!9YjBZ72A5V=y4r3T7B((2=iEdFhtk`I=`lg;jWdsI)CyP}-1^v!59GZ!UWUm-}7URuPAp9>h`@GD! zjBfMjnW;&6=e%p|GnRq8yHDM#A0wIb&pK|cCcHg(Ep~-s1=W)Nt38-lhg6cSV}X*%d>LX26E22`d5&ch9f{v)FI{m?e<1W5qy@`p5T(;ZD=+_>DxumrT zqOJOp1$|4c{t#QIV!bh2in~Lsjeo(S3b9}Ad>&p*sme_x`-4;2wtcb&6`kNb{x7s| zJ-=Y9aCj^j8%zXw@^6D<|IoYEu!%>;`uRNujQrzoI_XTjRrvq4qJN>Qt+??6Fqoh|ytnz5Dpi&&|U*u?>lV_g?X+7%{%bcWbM_TpG+Y zsdSCzb3grMJdn)0RoYmPOwY_R3&@K7T0a#XZz3>#fKUIWB)>vh0JL_?Pss8kR zl^9yz>QIi9zGRVz_FxHz-XR)#`e4BGUl0=ZU`g|S*qeg^H?yzW3KYm%r=8b8cFk8T zI0Tl0T!9ijix?dVPK|N4?=#Xd82d(JlXr#1prCR#4VpF8Y^^ov9!wy%-cx&RujP?H zzkL7*KdDrE!D@uwhPQrq=}!q=4X*dZ$K`AuTI8())`u9-8S(i&IHPEljppk;A8sXs z#|>@}S1iC}Rjab-0y<~!%RHBnWsf#z6_uA88yhq3&p9)5a%1^ zSxyB2Q!q~HnqymPNxo-vtwb4ijb8xsZ@(Bvx$XlPuPNR ziLwdo@BMUQ(jFhZJ7}7DXY06Vy{^9wfaL4}oNB-2tjBaJ^U3q)VPMy>Vs&ZY1a6I4 zr_K|4egfG6siY}jwyn8$CIKJoAVU|pIT4_Ve~$SluQ3)Fn<|xV=*h{_n?=%+eqsfq z;AsY3(IA4hub%El#P&1i{$NT)8P0q5%)zbzS@a8)di9^xf&O9=HPA?_kuU5 zg{0Wn$X}9@h(SqH`s7PRqB~jM2X)+(sg_C-+=@#O>5p6GO$oElj%7KJjN|nekD}S+ zC*}6=E5Y;dxog`Y$IturQb+;x-Gy9?GcqnV))a#Xd48mLX|>5qv$c$m=QP?=-ANvx z8Uo3qvDRm%C7RgS`(b_I`Ftq!B(2t&#ncXS(0Cv`Iqa)w#=8kI6B7VWR?C21+)s_B z`h#b7xZE`ZbC2@YnjK-{24MqY20;fBkHpz7F$oS_2S-G%G zb>rF0+c5;LF)WpEQ#`Jp$Km4_s+LEKqN90nmV)yDTF0~xLWF{{6$^v{qiB>~^KLgS zUi^{vedjgvvR@=+(c?Jo{ybL6U&{4X6&Kk}_~5jPnnIyU7%+B`!XqN`R7&?7jDe?o zjX?N^#ss<=s`ekHwbQK{of>ezwt1=FA+0l<-KSCO6byWHsbpSNliFx{wKAK**T6`; za9b3-R<9h2;yY20kEYj_0+KS)`g{Yaz>PIZFag`pT`_sZ&R(Tbsti;Dig~)%)GqP6 zb=yf5Je&$PuYl(WO3v2k0H4vnmcD>B+vSaouCEUseUy-ZH-3HK;^szBc-27)u>lJD zoxuq{%2g_gznjejB3**AWt{e>tiBke-Om0Y{O%1}_#d+YcWA*oT07{gmM|U3#0NnP zz24sk7Ag%@jZQ2^-)+|Sbv+OS2WRtQfYD~tf=C5EFsA2V+9;{O#{r=t0`}wKQT*$s zD~I)$GTT=Y1xiKT?t688orC&Fk5S{B8>{EjvLJ*+#I#$ZeSE9`Bz(u{wHaTcKL2Fz_~S29ISb9NR8?i z2wq%VMxc;;-Pc_T|1sYMsL!mYmf&)QF5=-Fp~k7vT)F|BYQ@i8#*;tl&R{V!Gqc>I zHZ~N=5XF$%GG!%TFP?AgDS3GtW%M6>bw#I!dlcVY>X!MbHrK!-RfyZ`YXX()#Js!6 z^?^z!;|F%L!BqU@BVP*yWKc)0^dIC};EG+ypG4VCW&R-7eUC4!$ zw8m6B^8UYZL*+XQeC($F%MJYxrBnap-xm+c}n#^$6@ZI0L9-8?~Kac%IPbH&rv);`^v?E~&s5HVl;%KWX) zp)K9pE4INOT49LX1Q0cdppvd~c)9d0l1281k55=~a>#w^_W-YTb#)CH<3b3yzU4Iy zKaGAQjh3dg1LeKah?&9aE)yy2XhxcGo{YPX)g5d|qxky!|jDL!YfuX~AJz1I%+ zbd}{m^-Onn03?(&%k#=9D21tO-AgQ(=mUuUxFUkRB;$E~_}tGnqe2Mn5oz`*Bq*~I_QIe71Om( z?vlClNz6UxU(wKrD?6R<-CUYW#!v(2 zJjxDsTa&~QS!^w}(R?lL*I=NK@vTlJkDk6B^-_?uTHvE{wt9ZY3vE)=X$T;379(u*>Lve7@iaDpT>I+V&9qL zI&SiPI?7T1b=!Ckr2GnLQ~3FiMZN;vc4IDsN>h71ofhp$a(C6v^udX_@Me)ZcyMQ{ z8^l~BQ^x1-l6xSI&;`PDs6j0<*C@f(bV6&AIljo{=E~II3lFcgYf>#)L?lIk0ZLsX zB91L`8dNT|Gc^z(`3yBA-vqAR@A-V{ki64;NoF#cpI7d_N!hGbn;u}2vMyR+3P zS}<*qgeb+b=kcm;q7NlvygIBKZu`OX449c~8%2b{4D)_ZD`69kSuenvn0s)tOAQ7E zp$#KK*0aEf>FVr7PECx$0VR1P>i-1(DYU8jy4pqor}oatp+c*_290#Ag6SN)Ec-J5 z(&3T$dzbTfvjbc`iZoHG`VTIw>Ph5#sYypwH!=ufFx$KL(&aKly}&c`^(cRc{^D1g zt414`!f*Araoqmju>$_D3&cOhW&T5fDERpA3j|E+e+tC*=$rI&1Jb~bu=LSY)zj_C zmSjHn!$L9HUXql_h`U%Ia7vSmlis$rwldlLnRdBgO?B&2tDAsR+{{$G^Ij2wxJG=Yw0%HOkilZJk7Npe(q6-DGU$$D9X$@tyAh{@4<2Ep_jL;VO57OGUtle9LHOf2E>8_r79K|& z8%u$5dAvCq2y(TD%MsFLUI@4H5JnPhE-b^p5hm&{_y5M1lupApa>K}D33&T-TtOuO z1>p|b$=KP)*1i7EoeL~1p|!Q-98ignrO^fxN-ZZZ3BfZtccZ{+IhKe+t4t8o)HXV+ z1D4$0Kk%=f5_94$yA_urY3u%Ez69g#e5+BDu{xAmLFcWp4j}Dp+iN;ZN7sJ#0`WYJ zNKcuc%G#b$fPBgoYjDN0nl=MUL6T1@dul+{C7&a0&%%at7a~r`%S#LjfZjR{G26sq z`~i=$6l>S^SKV6g^~2NiZ*IRf7*M|;iu)jId(XgD<_)%jQj~~9g`h+mn?UqR03E}V zr#*h&dib&CH#9pDHM-GRr#s}Yb6-V%HUEbCf=o|P@9RsJ-s>BgcKlczMwZk)<51V- z8T(z>9*8L?xbMxx+4E|e)|7((K1G{1bTC7V><4MiiIZ}%N=Rd4X)M1z?$Q#L4PbOW zhwHH_b*EFv*qX3!=PLEhVVbN0xc6>uetR5v1YYRBCXdneJmijyo@qRrhxppBjgTr9 z1ZdCR+)K?}Ctw~5g%5P2R!&=E`v0=Ny|?f306TgfW=|M}vocZf4(FXmeX|z=frza7 ztPVZ>%Io1meLhc2$e2d2SJBKNAwXtBnWl$MlFOC_Ei|9AU5$7Z>+fli&wG!h*@NT@o7? zx4ZlB(5rn(MEaE0`tsOxp`#64dEnBLUS_t1454rS`REQQ(u*bdACTp&oVJ1*UCx)( zH$xg`P7J11RbQ^)eeoKrdYpVrDK2i+-QB;sO4B+*08X9q;^KmuhNk(qEj-zbXZd3A zIzBhsw;q3c_*%I=1jScBbl$$4XS`P$)q)4Gj~d>9y*l-}9DI%d4E+qN29wB6Xaq|F5*KfQqthyB$Qjm6DW@ zmXceeJ2W`;8ggYWC}@&*x!qgp6#PLiy|5+sgq4t}(o=Gbi%9Ay9QABOHjU z-VOd1yIrs}ZGlmxae701PL8B~#uqpwzi&}uDybyKrBkcl>%cFL~fS%L|9 zh)@4wr}hvmw_&lzXR7lnBm~DW@+@pE=AUplFK*u5TUwl76e4VE`!KiGUyi2*L|h4N zZ6YvqfV`6cU%r8Xfjah~A|^@U0^8@Oa@Lxf^4aRHPJB-S3JZId@V*?SDGN`ihDWO6 zsz^4?T;#GghI8B*e_rT%uFS`vmU2gdRh4n-J8Beqn%9Y7_|+L*xTlZa+VNwL=Nnhf znJ4T0{6Txy&7+iV_XnrL?G5(Ze!d8>Bw;u7muEwT)V;97)*Xwtv}7a3n)7~(?$D5Z zrJEF{$3`I%85q?d_I$*Lafv$N7jaXV1bh1O z4GmEl0t& z)1@Bvk=l!=^K)~**JBH`#Ovk@&zn9ajUjvq@2`wiS=lCdusEHyk3 zx+{4UsCo#*Qlwbr|NGG?joF3=B#Jy$>G7(uNn6+MGp_%!@`mdr{!E!#DiZj~yQnBG z+3*t{C#K~Gj*d5Czmgjz5a&7HOC8Hg@R3tKPja%z+`h)#yy}Q~g~^*w^fXE9+(aRL z9Z>?#M;Ck_;g9MWhD`cWq*PSGMvnz4aHoTCpe@1j2I-m}&Adiyu>6AlUD9DFcVS*$ z)7hBc6J5`(alD$FLDBw-M(5(~iVc!e4AW0q3f!VztJshiS;fW82WyM%=@WL_aG8PS zWT~LYq;m{KMaBMFHEOm{^!73vnvZt1h0Mv_n{Enq$H&J&50TQ>e^5|Z$OYTfsyiA~ zQbb_hy))6cw6t_gL~E0iM4h<0y3z?(=@RrHPNkwM<(8r!!gYfVcm86dXTXd~VES@OBkjfCBf^V*^y&AU~dn2)0--iOiu_v{=r0-P_kE zCnbfP1<8VtkFrQKYr9#21K&dbxQWXJH%lxrCtCIrTJ&?U6fdjlghU+i}HA=LS^ za?&Cn-(=QNG{wyN7p5RHsJdgVRHptia+nyxoD!cw8cTn*K7be?9I&*sbX^&FIDS+q zw@79hs>kN>JYBAM){%PWT}-0B+6=)IE@SyR*WbSYX& zY0YT18fToF;ZaGJI2MuXyu#Lz=H~YAF^b1jCPB*Z09~wwjfK35aC!RX*2CZOgy{J9 z$r%l(h=}|MT7s59O|Po0rC|sxE-}^^cw&)7-JTXl1n~vk6znK19JC%PuEkY*tnsn1 zc73xkXE8gwBwoqaJ1KkSnj89bg4<|n!1(g&bZ*{}N=2q;!Eq0CS8@T`K@HwVnjXT! zu5RuuUIIb_{ZY(Aw9PFqtgHet?zd%@m1$Lo@N_sFhCKkBe?c6&%8hC5RO zixCx1P7am9B6;6qW!|~iiV5TSUdq|U0l6x$ClJ|CiH)5e77~Ny{O&PVuTUOy#vdH$ zOlyW7_E+L2D!ZwQEMI-jsR$1V!SIzkv_=emTU|9OFCr(VdY7=S&BWXgp4>V%aCT;P zHuQ|xkNtHyyrIEqds5rk!%#&9r^eYn?RE4?P_zwsFgBhXXwRiUO!e{-Ra8>?#w#NE z6(%Yw@(nh{H#|CO23ABt>cumIx$Z-SE6|phVnqvl(49&6q-hC&Dm=G&QWjXW{gT5~ zgzC+kH|i!P5uZO(?750a1xX8`=amsWv!N@Rs|u{EGt7=yTqKV`XmF2wXl$g;$k3q? zO2YGyqXN6S=`e2nTAkqOIuU`|s>OJd-lt54nHApAoCJ(&R?QQw<*g>d?;aw(ol8Me zb1-qNrbwCmvXf+EP?lN0cYG`ZUeoQcI4OzjtZRmv$%j>I=n!ssaKq4-1E*81y}lZl z7$$tiOh}cYqGAe#9-bjHT`>;fUcd&KW9FJ;I~|xGL%ngHUAr|lc)dQGl6qc<2Dz0) z=G4TyQri#AEpAeYxrxAHaaSsb32~mYLE&S~+c%|*1hCdNFw80}EBI}%?qon1#puJy z(;pH9c7`PBiIE}3oP2E(jHv~z#7xgHl7OyfCxi?6K$K4u{_+@q3KONToAU6(^DNFtSMlFA++ucvIpEu}%0 znA#m{{wdw_>(73NN?Ory52;|-!(TkpTMi7x>zV_kc7sA zVo+X7?Jjn*bVI`;L`FpPnDGw%vK#z#$dfO*@E|HW`u&d|64SLVcy@dSD|}iihXY9) z;z`9hHssma*$EfDwT@)Gq+aI}^T+ccXLI{YYLrq21BAsz05B(v#Qq9k2Kb zpU`YStyO?~oNPS>paNJU_nWae%52GdA~?lW6#`lyRH!)K59EL+PL5nGkggAdy-4cy zqO@%2@$yaa=1UO%(2akOn+Oz&7O=92?%=?&b>&_;XwRI?TMQr{m|j2i@sRs?fFxJ_v4|zvh?hLCi{+okpnDq{hMud)#kuU}Hc(Z)X?=4SHN5pG@jE_A z8c^rdSPRF>!fIV!(b=eE*c~+fjR}d-(#C(a_xl@zLZfzYI9eY-o}=`BBPzH({}hzz zbf25+!f@TVj0|Xpym+`cE34K?-;8jjhQ3C)3b`gh%Ubs*IT^W@2{-&#hOAn-#Y)tK z#*h7Tf^t;3zJBm&KEpWM)ivqtYPj6hP2e=WQ$-Qp<@}O|VR-v~|J4&l%9y7hL`o@s zesS@=QLQr)gqxe&@%Mq{t!aP2E$y~9*xG*BU-Ikk@AnPf<4j0QG`&1~I9;bdnQ!0g zb0fOEx;hU=dH~lDfY$Y-nLh5gal9G}WCQ(Z8zzKE0HI4VJ}_NPoLo7X>wZ8$fbD7J z%LU1j2|G6ZI#TPm)G4#xC1xmM*8<1;u@%pN^9GrK!1UwKDMLw53NQ(Umx}PPVr5?5 z>F=8)22ER9p))3P3BYe@|DfBJ`vI{&j;1Chc73{9@5kZ1bBq5#O9ep(d$V;eycJ6} zCm8FM^*H_3pMohG1Aq80|QaB;9^4d1~pF1nHNATMuL>+6%hA<*^@!N5f(ei z)1@{VS`q9y>T8{qCmqGkK4D-_(sXStt&`nIKKq$rjrv33!RogcpL#D&PfjFHcV_lm z@Z=mtL@5F5n#UpS&vy+20*KfC-a-p8A}QHLn_TH{RBJ5X1h#>kdLPeCd@YY{?2)&% zwe|SqiED5fK3p72XNWaQtr4Funi%r+N*zS^51fj zZe(QShk7Z&o13=N`MEzXQ9_5?zW<|t9)5bfcW@N_*reJ_kS{CC?C?`z@Wh zB;bBTN+sgztCC!q8zz$&bd1-u(|mivCt(PpE>ADdJpl9`-wS8=!hb*&o|{W*ks=gu ze(nyQw3!WX%x{w2c6~TJp7=(dE@+-*X2Wi~YXJKpHQ3l4AUQoRw2 zZ0w6K-$NxoDkwjwpIyj1oXic}%u?6{z+nc5Xx*tg$#dS|sk7%z3UVg8LErlo=;4JKwUcu-$SpUpwE z>uc}e56=VEUy5x}x+MtNqt?}@UIL(Y{pHK++=2pWOUp;^-?!O}gs+-Qj(KgKC&h&4 z#cVI5Z!GuI6HeDh)EpqrXeExXQVvwpI@^}>G#?HuIQ^D(7m~F46)j4Fepg;z9(?rY&Q*Zx(ZlJZ@#qN=G$xwUc3IwOV-SVT%PWeWkk)cZRzGF+P@dUiHdEK33hz{1pqZVO zwS~19wF$Jrdt>MZ1>diRC><#`1F7oWq0^th%d)Y5cw>1c< zR#0mn8f)w7nvZS?-f9XO-bfuBOp$Svg6`OVAO>;rs%pIUo#IBF8XU<~7$tWimFNkb zEH*U~BNZMM6`kYqI#(D%TBh~I&%K2zr*z29&a2>%1!TxHSYnT3ZG1`_^>BNh|F-Ql zIpC%j72yITRPr5B?BoGx3lW1eG6Zz0lvU}*eH;?IHxEV` zi=}KjaXfFXTm)R$O%Nyd57;&$JCDHRJweEl8}ec^wo_h7IQMnRbe-XCC$ZxrPgnZ% zaBWK;5>d2pQc>C*wrI9BQHwXA@wLQy7bHCqHBGoh_P*78!lzi4AD39_ax-)p_%`?DA=EW1JD-KT$l~JS z3TNT9wZaXT2dwgB{Ir7eZ5nxjarXZE}GbE8xbvoV=*0h^3OG!HGE|^S3}uN?1*e zkj2`vZ(l^~;3Fjq7%(Z_PKiZa_aMo94jOtlrdLuWwIww7LT(d#0=PN)7{9%m&8SM{ z-mJ*({f9JIDyH#>lN0iOBqX<}sIBk=<}Z^Hd7X;C>4e#M^Br%$f@hCxgGRt@`JuU# zIfik`H&FAc92(Hk)9v>?lod$B6#mUmO1WKJTu2{0Fx?!Mo5I0J!~`LJb(f`~UE3&k z&zAh~bU;2%T*+Hf-;kKuW;E)8pCA~+0(MWA(U+P8jbtpHlTW8ETlb;-k7<{+rlBbk zLw31zw!ehBk*KU1`#idTmjGi&l@#wuvY?36d+o{Y(JPZu)3cIuTcAtA`=kLVmS#72 z7aJQDSfkbnpe2F_;oPd9O7CALoyXbmW>#f zO1?$jgt~CEF=ij{*SXLg$cNKMh*NU)zS439$)31L8UV6ieAAoXcOAd%l!nJi+nHR2tlm{P9uM|3}C5U zlr$Kuo7!ByTLIU>!p64WtXJXfw=%!Emy~{R<^+RUO39AHuQQhdPCz;UuD0-aV|NT4 zoz0kAEebs*x@03J%#mD(5M`~8j+YmErq(5>aqeZx>CEKZQP$j@NB+Dv{L~Tv?xJaH zYop`fv|@48eW`e*Xv;Fn@W^zyBWisx8%y^5T$0JqFLqc zH8np{Q~Lu54j_EfuV2_p?Pv?DJAVH_vhD2lhesR2V0jIxV7e}Q`KUv1|jJH_8z=ESxx zFL@>`G4r%OD3M@V$Q8Q0nlqyZ+aDL^|0TXIQwm&Ail-Gyir|@;$o=|N3Ls81vomyu zPai8^O$AO)KDV1KMKMlQW5)ulX5xt!!`4hKUUH>f>uj+L>n~AiQc_6MrnHu>?vjoS z$|{H6&v7vfn(ff0qC_Crzwcf#1F;(r-@QG@!Z955y>>KK-4~z;IWFT!FP9RX&HKfn zqwneFGX5RFHaIqUo}qj+-5Q1ObyzYnGdD?ClLX*>iH`Px$N`r7< zZ17N^ihT4D)w8UYr+*K&F1FUr!mk|lOwwVlpF|6VhB+)@Wv<#Ax!dcxuPd$H#e1y3 zPq0vf)D(1V?4R`>r$0bNc_lS6+;x7?lP*On*oEhH{@lDOhy<8-i$~nm$gK@3HHS{X zT*P!ocz3f*RnB64-;(Ln)^?uvCFjoEje)5f(EJ5($Ub@qeb(l)nRV%VV^Wa}g#*2O zyunMDg_YIRGP}BEB}aF3&14+t$@0pCv|Jj#h%wBl3LlQrK3o37jiI&)s&M+=v_@Z( z!$TZO*<_yKGj4kJAU}R%9~=ze-530BcN6vVO`BJrNTY)-5M`HKOdir zj3s$SQ+M+klSeUgTUKJR`-)$VoG_|$G1d$hZlL;)W|4}Me zwxOkt_)oy;Uoy<|f}yN}0_@^Cvb)LGC!qdm1#1|2Xl+;8OeKZ?)B!W%kq~I?r#z?! zbV>WPW~0;xc2$T4+h%(FxOH5&y|ZiiA}WuMuRQxNx&3*$hSBuFDp=HV-Z{T_FXS%d zVU&Ey+p&1poBC7Uik8wGq6(iHdG0&hQ86JYdDu zY_`r#;Pv9&wvP||0U*RGcXn7Co{g zh|_Fxy7LD@&HGI60C9Ne-P;Xj*F|O1rVzZw>jSJ`J+&F|Rk1)1*Limh(@@vjp-No= zAp--$OFKL1-G%)3E>3R(TOg?g-~kb9oQ(3tY z_NIXPL!%O-!c%p;ED#`%>d&ib@-%<4Fh8X$7aCMghlkR5{l3U(swgZ`Uq3jMRvsME>3o4)I;|>I@X*F0ZJN1-##1lIC_Km2|$& z>t^%aZ5L)~Vd3A_-g&jq5`<~_-rx1M2MGv_)45&A=~cb_R-pXn41dBzUE6^hGB3Cv zdHQx%vvmTvZ9u6u`(}}&t=vDHnlrP@YTo|7g{%D?vW-1eam|>DuKmc|}HXDZu}FOl*FN+wJ1Y)}>8tE;QQ zA3sV1PHARt{u=Cw+(kiY>g?RetALuD`vFVNLX#ibc$xm|4=a}4x)N-}<~u&u$WJ}l z0B7#%y7TYyk=70!w+MxX23Lm=3=@>O+nafC4_&`HCy24)A8m4?hG^Ee1X7dcuq^^CDXmsJV4ipXrrHfk zkw4%|3UMpKL3f7ye;<8wu1A=FninDffQOVvzJ+^aI>re2cP13ZZ7+6%N8>Nj_dG>; z!l;Jx0UCfAFOQG_+Tiq!X8vy-B@hl0adGjPK>g5m`8vs+YAx&1z4Ia&_7v>wXpN?e zjfC-eDQacz!^w@>lXW_eKoG9PTCWv4#l@y3%4wb8+fBl2rp5qoMPuyEp;3*o9O)-y z4uzTBu0t$|V!0l^Ke&n|SfWH=j(zv3O-wEw&xErrhjR)ui_}7Fd1s%?#qYzlxeN32 z-%BfUNJ7EAj5N<|r0O*(lz$$ZE4x&9Tt?~e+=M4iF*{N*TkZbP+ZK2yh*}C{rTgvxVN6Iwi*{8x&`LtgImAFU$tNc0S6Kr-hj( zZPwooynmb)9qNwN#H8F7OeZ@tU)5)`u?$Xj=aiK854wxS_Xbc~S_@3)E40&gz%UuOpG!>?=_r0<0MbR07T6Nbph zhd)m24HkBG!Y?m9+4QQ=XKG!gTyI9DM7iwv1BL-`XI=SY(3-4RXe{>f~MS9apnYz6>a6kH9(e&eK z@7YGqO=nkEw*!rUE3Ny{7Rp;(-3ky&uuE$(>2$O)g=ah~cD6EIjA=jSa(i|}cSar$ zIZr3NtqP7%^VQxJ+dIYk(UY9Ly(!A3c45I1M|$)cMCZA>c`&mbp6s`QG*HVvq=y0m zq^vpxKYl9Q$D#TTS5uqHPZJRl9UjdqDgv>=vrTZ7CSDYi&)<-bU209AR_KC_diyN=Wii~Ol&&~uu@FcUH+8nr^hfb^3p6fA*ESvW` zgO9H39w!A6Tqt1oSBEradjUMRoFUJ=cX;*5?GJstvTy0#?XVN#BYwN_EGp}kbPX@p zPRpNqWn~3CfQycqRir@A2Mre!MhAvTW@hg(mfFo{>rO2!CnhG&EX-Mqxw$lQeGZlt zJP&(lK%V1W$O{XLtfMDp1@<3bZU4^LJ#`$J5hqG@sobKOYGhxs8fZpYH?s=te-yf~ zDyCd)-)~uGb7Rw>g(+p+?)YYrhLI5sEP=*6?(XdkjJdByQ?$|@{}hvX)|HMl2+s!% z3WZAfUj)O|4yV4Txz!;F09S*qiJOeg@4=F6kTDxLw?2R$B3<&`MLl>SfW+7rT#~@S z|Io%cY)m|SxdZb52%)nY)>=VpxWpxIe9N(O0g!I!oPt!`*0J|SQ>y!nennb#) zgaqX7`;jV-Q}(XzZZ4muJ8@G(a(~{7sLvZt4!X-I`5`4R1mCSSWW3Cc2jo6Zj(F&$ zs-7k)I*oG!%inqn80yN!OGFRJf3Yqg8{m)fZ$S zb~fiUp}e{;c0-I()Rs_8ulgRBrOC2E5>fIlHGNrCyy8 zjP~s+ez`~-wYW{bNa5oR_Q}RmU%Fip7Q%v(LV~;)C@#C1MVCSi=r1aBoq(0?J)^3$ zxjCJmsn~@cki2|ODS;$Y#*`^$PYDB8I3jncP)!^40-ca zkRdMjJ#3QO)4!_9ZWd>7&g3MW=ZT&~74aT3Yp9s)n!?-DY5Ia;4;+NR3X)mL)H836 z#{+sfqO;uQSu_mr3oT9Y@^-PoIJnrO{0)TYuVsPD`mmUR9o>ML&qfzAREdP=*Y zmxB>#X7LkDEm7mUHJ+<_M#}hT8`9q*C`1b5zl0#Qq2-MRyRW-rI z@Um`0b!;Dg{6y0L*(Z5jU)oFYD5T9`Dr3nD#Ct&=Y5v>Mw;4^njUi=URrVsmBLH7= zlE*xz_e0-GT7uq5K!|Y*jen#ya>^=*r9c(}?!qtB9E|>vIY&20tF`>atXKMX!3SPK N6l7GTizHvX|6fzi`PTpd literal 26499 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 diff --git a/doc/salome/gui/NETGENPLUGIN/images/netgen2d3d_only.png b/doc/salome/gui/NETGENPLUGIN/images/netgen2d3d_only.png index 4b574320fa7e1e134add952bec5014b656199511..f0abed3cb0807c40e234724d085a57158f343b20 100644 GIT binary patch literal 29966 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

%irYtA{} zIp6z!@AqHV|BqY_Ix~Cr-t+9W?sebyT2uc-cMS9EzyKN2@b%uZ%R@lmT&MO>dmYt2 z_QalR(=enhEN%u26x}^Ntx(_N77u6pV%bl9z_78g0VDDCbTPLViS5Kh$)O9ts@H|Dd<_$ z3DNrO_JWFXTzEK^V~?&ekV)z(Oogc^ z??A-_pPw`!Xj~WLqxU8HL_pAUa9>>PuZ!_SUcW#)44WhAQGTz0!G#?e9v)iA_>egj z-CJer`AmP1iw3)voJKS*M^bY7mPK2thvvo;O>~MuF_sb3)VJXe`DEsE!g)?Mznt#INu4x@V>wsf}hgB~q+U9ql{-U#`5?Gzue^Ai8-r(_Y`}Z1BDEciNp-E@!EziH}#>g*EjZYb-`A zMR`K3WE2-CjqT%kI6XzouacAMgIeu4EeVu3HMsIX->MkIG5g~CHHu6+TLW8LTRRi+ zaqCQd!=yn&bBkQPCrgoFc;I3OU4!C;;hdA1SxXre6Coso7N6j(xm~m9C8GY+XSeEu#U^t5l@iw07r_T15%}m0f}R&t!iZ{o zhS=8%=Pj>DNXW_-_uGzku5ofE)YXHDXf=#*!B9(J%8gZ*)eNEGqkc9bAUVgJ^cr*Dp8+I@^{i zxO3k-F)dyua_`k5z@DBlC1xlY9nu>O@Eg3F@FfXLHnhH zYweF$IXS4NzIznFs)Uv{9^$s9#%UZ8gNuB{uWn_$?1ri9` z*#>fFEk3GQ^ssxb{k1BK4HKU>q)j2#VrcnBSy@F;Ofg+7 zgZfL3d(MWHrSmV7lIG5c?q};W;6*zfG(SzJb-G28H?A7`dGMZT1%rhurZ#xY1edX}gG|wgH4C1T!$ApMm@wT(mV9@kTfXSyK(O z^S`c@mqYgdVuVsuQX;RbOM#H)x`a$M@G)&}ZN=L)Ev?S84)u48QXP4IzbZ2}Himpd z2L~2nDhh7bp~7s=b(w79mrtCZRfv-~FZRj8HlFaF2DDp4SrVI6v*G=XjVG$>#is<; zlisRn`Y=0BeUfrk+*>W#6U5AdM{}L1tp;Z1QfAM^|_wEtH7i?Vo zfXqz#&e**mjcYGfWxHbcT8r;t?8R2?eSUO!e)#y^9VNd;)86iYEPq7pfneKi%Ep$m zNW3VUS(0AT&4Vmwf+Xm`LP3Cig}uU3SkRu=eKeq%QBIz#^9zmC?2ni%4P)a;uFqGC z2qOoJ*cTSQHTrsg>i_+@r{^pD=5VVWT~!Uz$2A@vc^Mhh5E&+|^J>>$8ymdChk9ql z%d9#O37QN2cj~J@E=rc~9~|#L0_4=bULojnRK?3$rO{!*&tuvfpKdgDvA`pY&g`nx z9E64jDnLYU!c|R8&A^}_)I%)HPd7?l2eSw{jD>W?F<7>B#<=qQ9$uV3d~h^KK$9CP zQS5;m7>IzyXwYc!M$7WmuP1^kb9OcnKo{!h=rDqBCMhXtBIxZci^n(G=WI`RcU(dB z)VuHgv$Nm-h4uz|FL0ZH(63#)*8H4J#p!U<+{4qeMY&J-0_*q$!ar_)`3DUXlh}8w z_5drH%Jfk6o&6W)510JllU2;bu_LT=&u4ONlV6>xK?CRh^K`y;#F@RbzU-iWEpjFejrXtnsmcJlAnqG zs+v{vSm(z+Dla1=F3IC|tk}L1=u6UiNM*8rgFKQ)!P&{x_4eJnRC=W*F&{n%RR8dZ z(_S0CGw6eXVXE-Wt)cqEP@O*SujSvjxI%UEi}JCYoy(=@TlFsmWmm-%TW$3f7Ps_?y<7S@maT!(+)6=uO>Cx5IZ5ivll#+5azRbQL_oBYW zd1jrzaMXl_gpwAV6plGfl`(^UT>Shf7W^~6R)gFdu9{64@ItTy^QaPc5h!fWR1FxCP!k%!;-%=Gk`_O`E*PVW59u8lUo@0iY~gRjqKF!P57&HoII zx4A7AKlCkBu@W5`!_$YLI@2+ys+m$E-I{`sKIdJNN zbd7>|tq`a8@D!OXj(2SclJ1gHuvcbzG5rbL1P;gu)~>H`-kUDIjilzLDgtA&6phLF zm~D-5C=nwg_;PA}w?8eVwvEdG*12`-*00GrVN62Wk^4z0;X3I7DapxZ{Uvm`iC3BT z{)RhuOFcEQKJ0t@fBW9NS6@_}>0WvFz}rWOgM^GPWi=Gvy!~Dvk8zy<_HGX;-(9H1 zHFb5Ls%3~37~@w@voNQeNQ4I1UKbMT{1lCk8Dx^0l$U3eVZhqjF%k94?0ftzmK>qL z{s2unG|b&D=oJi?HDzc&o3p?SNnP!pWqFdgGFZbRn(WewSUCq|#gFp(V|oaI88KiM z6OySQ*1v(wUkE2T;2(qosO2Ao^B4AkvjMlt{6XQ_3#W7Oo}Qk09!D!H^pkAanj|C= z!ZFZ>%g%gNY-*3N^xeCVj~|7=Hjh9kG)WBR>8s`QOm5Dc^KuueDO?=!hA7C*Of{Gq z8ciZG6yBTZAaZQrAYnuZ44pD zspz>m3zwB26=){f38=R_PevXHy(=z`cjX|faySjyydAZ<@Uf}Mh0Y|gbGhiPnESff zTT(~7tnO|(A0O1YpFcMT_I#izkLK=!KBv!Y%y6#0n1g0~jh#IZqzOpg!?`wtjn6Bu zh+z1h+_h*1H>=P5HN9dJG&~i~k&&_4!>wJ>pF^kjrgh`StMK?Ajp?}j`ikOz^w|`8 zmO>`Q+`H5D)(8>cJ>NRVD*sv(haq3>JtL#07W|pKi3{a^!|}|Qs(K9rgK&sJH*2b8 z0BH4KKb1iu5dbu)M$^X@)ko#rWtB8Rea5B=aON3FBUrQSl2#`6Tt|qUn>+4V(Cpa#H?YI!#H;>yr;>v!Cyv;8!yL&v7GP`{+J ztw;b-Qd-^+vk|e??^_Qnr5n92GWq zcz7r{E)H3NXJK*XaLc7PMU2P~&nMn{7ArSDOIpW8P+US{*$BsOwWjE)dG_WmEx}3l ze%Y__%dh1z>z64N;j7ogA zT}v%zrf`;=_E$whc#OhV2l5e!SrU@6kMsd+rjLT56C<~tnZLXLbJ*)(%Y2sUzr?4wBSE7`PFND2%tYG#eU$)WNR#+tcp0N34Rf|5T?=2Q|+vAUV4`0PcSlhrfvNfo}{k|MmKMF{|z*FSHlQ^q{GE) zYrP%CKQ&GRcz>kc6R+{mLZy7L>f=cALrUt8rS2Z`_pjtw&N=;D%3^|xS1+bK2v!%s zhIr-1RMOy=n&s!^zLK5M2UA_BrlufQ`_w$5`R=x6?;>JyKeNZg#H$jM=%Y1p_}jfJ zX8!;I$TVKR+Gv0w zNSzP-N}(@bUWRf@rzw~M8?Hu!jm?w||A5ui>qs-(`9ah4d*@*X7yq+8(FL~+HQD9R zn2`o&EI^BFXK+anR|(R?M8j(D4f@62jA=99zc3ApYa1re^h(FP_;?-5^W?b>BjGcz zUR@a;2DcbAP<13+RWFlt4Q}1?GTb6ZMML4>#BhdOMyH9Sf5g zs{E9z>R`t~#lXPWB*M3Sa`wP~8%OeF)!BY@8{@pRb8S5|>9t#$hD&B-i8HdW-S0U( zI%*jiq2$z;lo|mrz#-W94r?N~iUfdIoAdYSnfugIK7|_36^bU!!F0?YR)r5Lr!{k6Sa1 z#b!io$%migxArKbV}&&jyGtHd1#pJRafk#Des4W*IN_TZx?Qr(kS`VFM}#+_s3N1i z?=2^HXV2mS4I%vfxy}KnA_6Q(U;}U92T!x;r^`d7P(y=(Tt<1D`idzsM?KRb;{)+y zb+l5paN4v&&`Dy0qh!fA@owhwTodAtgndQ0j!BmCG5l`3b0`CM$o%t9u6|2pa~)ky z?gPaoitPG&S~enON_-KQ)j<+eU$os{-!k}hEmTL(?=96LISkgeH8J;BMMXsY-yHcV zac}x=v#yg*-d3l=V(P3ovh3NP_8O)@rhTf+7gB*sZ-tpIMNr~nR9Dx|jmu8+m!K}D`fgp>e|u;7OZ8$)p4^bX~~VjS`@qd#7@3`0dd z5VnhG&uY5{o23H)(-+)E{bBfHs3^Qf;0J}N>@B&$uM~)Q#W#}>0{A@2=%QopAn)8Z zrnqlRG)prc`P98a1Y?d`>=h0b7CEzEt?E+@06l0nQYLI>@f}4-D7eh>#4V9a3h|~( zhi=UOi`#^T)SC_?oRvs@*z;vEm{vnvu*ozJF}c07(|ji{rmU)}>nTi{aG&yl`ClmD z(bUrC*xo5MpUYLk#v^D^VDQcT;AL0Hm_CABQph^Vh3@AjyvAsfNs%y0d^c9pAm@a zTEec#h0j~R3x}NC8Q9s~&%^MN`7JS%u3oX2YmeY4zRsxUr7C4yNu5<-J^}|74Yh1x zr{CP#CIT==Yj5wnjtc)oHZyB$Ys!<@s)>a-KX1iZPnYHI1Vrl>T1(D$d65emFGolk z22pNtY5nGH+1}osnO|kq#d>i0!66p9;@%FZAVA!7s#BI8o0dc*G7Qw$i{HE{DtL1e zOm^Vjf`Lz$n3$MudhbVDC6|351s`H3P%mXS#c4BNi=!aS@h-P|y}IGHI)P$rZ2W2W zau{!?*hYN}?BLvBK?2eZ`6(eq>+@U8qnF>uo8Cu6^om}icIJNc==AWXZf@?oj=4>z z)rp$dY>TEnO&ILN*s0c{SJg9Gb3bXncUPyDudOS{eY_5omEH3MO#=_CygUgKa>{TU z?flKRw{x%9N?Lx`TGrS}d3kxUKP?Q}8a$$1^tVSF=en|BQHJKFfuR%pR;NBcF#!uz zR8-{lU}9iOgNX98Gxlj*zx}5&5X;`(OA8uyamnq;y7?Fti;y;?bORL;06DeSb+BiL z_f6HsbF3z1-;I%ia;x*mcAxY58nEDpO{1FPzh@{MwAQdPt{i*NyuUUccdn-gT1}QR zX&EqDii)c3$F6q#?R|tIH8ln^OL1y+Vy$hKtZ_ziw$iMZigrgyIXP9paAnz>%>UdE zwAHc?A6^vdKYWPYPW*Gyz2Wh%ufo9e>l%NhKB1WB3@>~kdnUDshc+ZQjkI*$la0O5l|os)D$7K{Q~|V9`aiMee~$2W--uIDwmzV0cNIN zkQ0#NWXW6peoo_ssT!Z$WDv%C%<8s1AKT%Lr%>q&Wl~%W~2C`mBT>s>5_+@dr_1x5`=Fc0oo)#Oy3dH&iB`o+1uY z^+%^dYYc6i9GR`srVOBtv~5#x<`RD2qbs}3Ok$Lt@!oCHP~3A{t}q(fY3JsW=0Hfk z#=+5=UgNxykttwNNVsOIH(|Sqlqt!91&I&o?;9g(a<# zh8n0>9D7lu4cOZm$6IcCZnMr=Sl-h)DJ{^Q3If9e?^K!y8G_HX#|pv`4ajc}PU+=K zeHOCIUfAfUhuj7M=7fZZOsRx@uPTSbqfUa=v5e60&5n^uF#_4Jv9>X~rM3FMg5j;uc#L za$F`{&fc1?CyqlnBam_*Zx7m}cXx;MUwnyYMF;e!br4En?&uiTbm1!|H#7vJR|5bL zSZqxBmeC2moqGK4_}*;q==IIQEqnR>l>LkqMWsmTffi_^FL@WcqaBUx@qtX=D&1(5{obRis4^aWhemj_ z`_A%X4ArOo0egbi9V>b>#xb~g?o+3xD#zGZKDLeEw_Q*ExW7L=v7(XK|A^y5iJ>v- zM`(P77I+G~gki;FjIfY7IPJ4y{w57SMMQ5}{LH(vd2c3)fJRt(Qj%8G1-tbUkGy^F z&bTv!&!*&DQm5rvZRMUC@7HMtrfhR!Z1gf3amt#@yv!W7IM4OCUcTVJe!VT8MSM0XAKRGQgDWIWNhuzeZo$|1x6nb=y-?+164lL_l z($4O#^u%`$t|-&U{QgYy-`}kd?RR#z;k`C9q=*>K_DYd&xZ%$irSM3L#)6_Tbk42i zjtJp1?u{%qJ0Y$7#aK9UtuX&<#RWOm8hR&=eXbsLGkMCxHOwo8LlS5o0otpkF`pHN zu_s)$+-N)_h^_MRMD5EI!?L5fcY4sY6#uMDRiGEz5kWFYKJMaV&AXF^3;H2GwUze9 zuDH0o1vK1a;~0m-3@!815G8|(hfFZ7gN>c+ztrljE;%P4Q0F1 z4l^|H4aP@$5A4hPq*ei`9*YlZc4ql zd=^qnzul0(GWeTIHB%HLXW6z!|JRmf7vH1BlP52y%k8kl6=G~D#8x)L73UXC+NUJy z($Ps$u#{FdwLXU& zvr(fD2AIG2uCIMc=3vSzT`eRZ)-5kBx_rWbsE$u+zrYwZ0;pNhO<0ywnZM6FaKZ={}5UKBpPkyPbpYQD%Ubz#qvn1 z`L|0Gf8(4U<^uutI9M&8{;N4!^wUdRKBGL zd{1AO!-oAIv=D!mA}xQfjz?ZSR`4ZP|3k*@dUKhThb_h?}+3V8mr=w{}UY} zJ%K01`1r1PPR{F<7ykUk@U&ZVA3Ri3#BmVBBsJ)cO%jB|+a=8RmB@pFWJl+ar}GBR z$inBBf6W6zL3|$=Kz5(@!c+}#I(!kz`iBYYOP63d@VFI-0=}QNk@qH`2qSefm~R8R z+5eEsFp;cUbH5Z~(mU1sxcA^02(gzGU~v>GHG}0-)5s_~Bm^C3*WA;`D_^IlO-r_l zkM9jBz5URi9;oYIbiW|$zp_OiuwAw(KkZ>uXWhW}&4ua4XA?3MQ61Gj{0dK8SI~Iq0#9LTc=U_0~|eu=a=C2%D@Mcvfr9;%${M7%vaH;+hW3XA(xf#ij?jj@Ii^Yv7S0jotj`OMsM zq$81UTQUcG-g4-pkVhfmV;-n_U^R$O#tO^>&IiTpkL0mjoo>%!&muP%3g?$Nssnza zlTktbq10yU`g^>eJ0_4GSC?7=S^aVPDdOS^-jA<}qrX z@Z5pnxL9C5Rx}2xzbP~#;R;eBWboaM?^!M`2ug;*Ml7PDm^50$Z7nS*2OHB}`B^2c z;G@)W!%$#*fDeWUcF($m8@!%NytYhImu5Z_L?{-Q1VqSLB)Vml7{(+jDN#!Lpu>E+ z*=12$&LrNh{31P-9vE!Oz%!HKKHc#h<};@5=_d}~8|6Q98M5`MeZn>|F|jpfSBo^m z+u9cDP|31bQc|UvJzF!Q70cuql47@E^GZ$4zChUd985N`wM9m*w~l*@i@N{Dg{LZA znELw$*bF>)jzyE@uWn1D$6-YdQ_HZh@3PtV9b7OA_*!pp0^O^2v&XdnS2{1#bfQGH*`S8%wb`XLqbfH=T$Z8}Tw9?1D38 zIR1l0p~H)Y$wcu(-DU4CwG6f$eJkVsEaA(T$Hz!S&a^L^Sc_kOep}8qF(9JdB2n_E zxwSRYZ~*H<{yQ&>YkGm@aR@d_q}Sv>d~J28pHymcm5F@W;O`cxv6& z3}2@!E|KLIwjw1)?}fPet9dOmEfJ)i4PP zqGy}qX}T;CdhM=8l$B{z*VTLy$0NeUM@Oh8-^9V%-;f2SL)QsKRrWW(D-Azt)FmD{ zD;;uNhe??OdI@4mO2!m*RaHUBt&|wIv9ZIMHX`ZF6RhUe90rb}LZ=Of z_*{Q@S-P>2Ie2%wyTkRpFy3)DV!eA~graR_jK}$u(n^dHDu1(`ygSOqn2lJ`;f84c z6U+yRf`;E0ba1%!wxr~KJ2`SrzwES6@N5ZBbO9+WB||%!-%|tRP#%Bm7TSJ%%<}7? z41qu@1}*1S_wD3zNsH&G$};aS#w67^*S%V>uKJgvv5?04k^7fPHyzK)`r=9wX007` z)pxB~?$bQg>|KF{3lay}qly`+VtFwgN_2jirK6oD{IA}vm57Y@tp%lVJhDsmA;2Ih zP(i(KZy!=v$bq0Hj_~-bS`r+PcL_=rFoLxIYqY<@M?ACL@tN|WB>}g|)XOMEotV!r zOQjhf_W$ei?%%&388lwCmF2v&xoJ-I`U;k%B?}Y<=J&@E_;s~ZhX}^8$FWH&x6tts zy}GA*p04f5r0^e_TuF1r_H!=JBmjJfh;5I2=hML5y zwq#;RvJG1G7)r2#T&poenZGYO%Pr(TSTBg8$$1~vS;>I~0fA7uttM9@V;1y(0IgKf A;Q#;t literal 29046 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 D66+`x diff --git a/doc/salome/gui/NETGENPLUGIN/images/netgen2d_remesher.png b/doc/salome/gui/NETGENPLUGIN/images/netgen2d_remesher.png new file mode 100644 index 0000000000000000000000000000000000000000..67eef4f60bd29d7033584f32007f931a6d0c99a8 GIT binary patch literal 17931 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 literal 0 HcmV?d00001 diff --git a/doc/salome/gui/NETGENPLUGIN/input/index.doc b/doc/salome/gui/NETGENPLUGIN/input/index.doc index f2d23d4..c255103 100644 --- a/doc/salome/gui/NETGENPLUGIN/input/index.doc +++ b/doc/salome/gui/NETGENPLUGIN/input/index.doc @@ -9,6 +9,10 @@ - Solids are split into tetrahedral elements. Pyramids are constructed as a transition from quadrangles to tetrahedra. - Generating 3D meshes from 2D meshes, working without geometrical objects. +- Generating 2D meshes from 2D meshes, working without geometrical objects. + +When working without geometrical objects, Negten requires that the +input mesh to be a manifold shell. To manage parameters of the NETGENPLUGIN use \subpage netgen_2d_3d_hypo_page and \subpage additional_hypo_page. diff --git a/doc/salome/gui/NETGENPLUGIN/input/netgen_2d_3d_hypo.doc b/doc/salome/gui/NETGENPLUGIN/input/netgen_2d_3d_hypo.doc index 4ef8e06..9e34081 100644 --- a/doc/salome/gui/NETGENPLUGIN/input/netgen_2d_3d_hypo.doc +++ b/doc/salome/gui/NETGENPLUGIN/input/netgen_2d_3d_hypo.doc @@ -17,6 +17,10 @@ meshing 2D objects). \image html netgen2d3d_only.png

Dialog boxes of NETGEN 2D and NETGEN 3D algorithms
+
+\image html netgen2d_remesher.png +
Hypothesis dialog box of NETGEN 2D remesher algorithms
+ - Name - allows to define the name for the algorithm (NETGEN 2D (or 3D) Parameters by default). @@ -31,13 +35,19 @@ parameters below. You can select \a Custom to define them manually. - Growth rate - allows to define how much the linear dimensions of two adjacent cells can differ (e.g. 0.3 means 30%). - Nb. Segs per Edge - allows to define the minimum number of -mesh segments in which edges will be split. This parameter is used -only if Limit Size by Surface Curvature is checked. +mesh segments in which edges will be split. Size of elements computed using +this value is trimmed between Min Size and Max Size +bounds. This parameter is used only if Limit Size by Surface + Curvature is checked. - Nb Segs per Radius - allows to define the size of mesh segments and mesh faces in which curved edges and surfaces will -be split. This value divided by a radius of curvature gives an element -size at a given point. This parameter is used only if Limit Size by - Surface Curvature is checked. +be split. A radius of local curvature divided by this value gives an element +size at a given point. Element size computed this way is then trimmed +between Min Size and Max Size bounds. This parameter is +used only if Limit Size by Surface Curvature is checked. +- Chordal Error - allows to define the maximum distance between +the generated 2D element and the surface. Size of elements computed using +this criterion is trimmed between Min Size and Max Size bounds. - Limit Size by Surface Curvature - if this box is checked in, then size of mesh segments and mesh faces on curved edges and surfaces is defined using value of Nb Segs per Radius parameter, and @@ -55,6 +65,9 @@ process is rather time consuming comparing to creation of initial mesh. - Fuse Coincident Nodes on Edges and Vertices - allows merging mesh nodes on vertices and edges which are geometrically coincident but are topologically different. +- Ridge angle - allows to define minimum angle in degrees between +normals of adjacent triangles at which the remesher (Netgen 2D working +w/o geometry) considers the edge between these triangles as a feature edge. \image html netgen3d_local_size.png @@ -70,7 +83,9 @@ can be changed. - Mesh-size File - opens a dialog to select a file defining size of elements. The file includes two obligatory sections. The first section defines the size at points. The second section defines the -size along lines. Let's consider the following sample size file. +size along lines. Sizes defined in the file are trimmed between Min +Size and Max Size bounds. Let's consider the following +sample size file. \code 2 92.5 92.5 92.5 0.05 @@ -87,6 +102,7 @@ section.
"25 25 0 25 25 200 0.3" means that along the line between points (25, 25, 0) and (25, 25, 200) size of elements should be 0.3. + \image html netgen2d3d_simple.png NETGEN 2D simple parameters and NETGEN 3D simple @@ -124,6 +140,7 @@ close edges influence each other. - The local size of segments influences the size of close triangles. - The order of elements and their size in the 1D mesh generated by NETGEN differ from those in the 1D mesh generated by Regular_1D -algorithm, resulting in different 2D and 3D meshes. +algorithm, which results in different 2D and 3D meshes at the same 1D +input parameters. */ diff --git a/idl/NETGENPlugin_Algorithm.idl b/idl/NETGENPlugin_Algorithm.idl index 5a0b252..a69c4f9 100644 --- a/idl/NETGENPlugin_Algorithm.idl +++ b/idl/NETGENPlugin_Algorithm.idl @@ -66,6 +66,14 @@ module NETGENPlugin { }; + /*! + * NETGENPlugin_Remesher_2D: interface of "NETGEN Remesher" algorithm, + * generating 2D elements basing on an existing 2D mesh + */ + interface NETGENPlugin_Remesher_2D : SMESH::SMESH_2D_Algo + { + }; + /*! * NETGENPlugin_Hypothesis: interface of "NETGEN parameters" hypothesis */ @@ -92,6 +100,11 @@ module NETGENPlugin void SetNbSegPerEdge(in double value); double GetNbSegPerEdge(); + void SetChordalErrorEnabled(in boolean value); + boolean GetChordalErrorEnabled(); + void SetChordalError(in double value); + double GetChordalError(); + void SetNbSegPerRadius(in double value); double GetNbSegPerRadius(); @@ -136,6 +149,18 @@ module NETGENPlugin { }; + /*! + * interface of "NETGEN Remesher parameters" hypothesis used by NETGENPlugin_Remesher_2D algoritm + */ + interface NETGENPlugin_RemesherHypothesis_2D : NETGENPlugin_Hypothesis_2D + { + /*! + * \brief Set/get ridge angle + */ + void SetRidgeAngle(in double angle ); + double GetRidgeAngle(); + }; + /*! * NETGENPlugin_Hypothesis: interface of "NETGEN 2D simple parameters" hypothesis */ diff --git a/resources/NETGENPlugin.xml b/resources/NETGENPlugin.xml index d7d7651..3e173c1 100644 --- a/resources/NETGENPlugin.xml +++ b/resources/NETGENPlugin.xml @@ -45,6 +45,7 @@ + + + + + + + + + + NETGEN_Remesher_2D=Triangle(algo=smeshBuilder.NETGEN) + NETGEN_RemesherParameters_2D=Parameters() + + + diff --git a/src/GUI/NETGENPluginGUI.cxx b/src/GUI/NETGENPluginGUI.cxx index 51b876b..34f9ab8 100755 --- a/src/GUI/NETGENPluginGUI.cxx +++ b/src/GUI/NETGENPluginGUI.cxx @@ -41,10 +41,11 @@ extern "C" SMESHGUI_GenericHypothesisCreator* GetHypothesisCreator( const QString& aHypType ) { SMESHGUI_GenericHypothesisCreator* aCreator = NULL; - if( aHypType=="NETGEN_Parameters_2D" || // 1D-2D - aHypType=="NETGEN_Parameters" || // 1D-2D-3D - aHypType=="NETGEN_Parameters_2D_ONLY" || // 2D - aHypType=="NETGEN_Parameters_3D" ) // 3D + if( aHypType=="NETGEN_Parameters_2D" || // 1D-2D + aHypType=="NETGEN_Parameters" || // 1D-2D-3D + aHypType=="NETGEN_Parameters_2D_ONLY" || // 2D + aHypType=="NETGEN_Parameters_3D" || // 3D + aHypType=="NETGEN_RemesherParameters_2D" ) // 2D { aCreator = new NETGENPluginGUI_HypothesisCreator( aHypType ); } diff --git a/src/GUI/NETGENPluginGUI_HypothesisCreator.cxx b/src/GUI/NETGENPluginGUI_HypothesisCreator.cxx index 2db7d1e..bbb79bb 100644 --- a/src/GUI/NETGENPluginGUI_HypothesisCreator.cxx +++ b/src/GUI/NETGENPluginGUI_HypothesisCreator.cxx @@ -92,9 +92,11 @@ NETGENPluginGUI_HypothesisCreator::NETGENPluginGUI_HypothesisCreator( const QStr { myGeomSelectionTools = NULL; myLocalSizeMap.clear(); - myIs2D = ( theHypType.startsWith("NETGEN_Parameters_2D")); + myIs2D = ( theHypType.startsWith("NETGEN_Parameters_2D") || + theHypType == "NETGEN_RemesherParameters_2D"); myIsONLY = ( theHypType == "NETGEN_Parameters_2D_ONLY" || - theHypType == "NETGEN_Parameters_3D"); + theHypType == "NETGEN_Parameters_3D" || + theHypType == "NETGEN_RemesherParameters_2D"); } NETGENPluginGUI_HypothesisCreator::~NETGENPluginGUI_HypothesisCreator() @@ -116,6 +118,8 @@ bool NETGENPluginGUI_HypothesisCreator::checkParams(QString& msg) const res = myNbSegPerEdge->isValid(msg,true) && res; if ( myNbSegPerRadius ) res = myNbSegPerRadius->isValid(msg,true) && res; + if ( myRidgeAngle ) + res = myRidgeAngle->isValid(msg,true) && res; if ( !res ) // -- issue 0021364: Dump of netgen parameters has duplicate lines storeParamsToHypo( data_old ); @@ -125,6 +129,8 @@ bool NETGENPluginGUI_HypothesisCreator::checkParams(QString& msg) const QFrame* NETGENPluginGUI_HypothesisCreator::buildFrame() { + const bool isRemesher = ( hypType() == "NETGEN_RemesherParameters_2D" ); + QFrame* fr = new QFrame( 0 ); fr->setObjectName( "myframe" ); QVBoxLayout* lay = new QVBoxLayout( fr ); @@ -177,7 +183,7 @@ QFrame* NETGENPluginGUI_HypothesisCreator::buildFrame() myFineness = new QComboBox( GroupC1 ); QStringList types; types << tr( "NETGEN_VERYCOARSE" ) << tr( "NETGEN_COARSE" ) << tr( "NETGEN_MODERATE" ) << - tr( "NETGEN_FINE" ) << tr( "NETGEN_VERYFINE" ) << tr( "NETGEN_CUSTOM" ); + tr( "NETGEN_FINE" ) << tr( "NETGEN_VERYFINE" ) << tr( "NETGEN_CUSTOM" ); myFineness->addItems( types ); aGroupLayout->addWidget( myFineness, row, 1 ); connect( myFineness, SIGNAL( activated( int ) ), this, SLOT( onFinenessChanged() ) ); @@ -208,8 +214,31 @@ QFrame* NETGENPluginGUI_HypothesisCreator::buildFrame() row++; } + myChordalErrorEnabled = 0; + myChordalError = 0; + if (( myIs2D && !isRemesher ) || !myIsONLY ) + { + myChordalErrorEnabled = new QCheckBox( tr( "NETGEN_CHORDAL_ERROR" ), GroupC1 ); + aGroupLayout->addWidget( myChordalErrorEnabled, row, 0 ); + myChordalError = new SMESHGUI_SpinBox( GroupC1 ); + myChordalError->RangeStepAndValidator( COORD_MIN, COORD_MAX, .1, "length_precision" ); + aGroupLayout->addWidget( myChordalError, row, 1 ); + connect( myChordalErrorEnabled, SIGNAL( stateChanged(int)), SLOT( onChordalErrorEnabled())); + row++; + } + + myRidgeAngle = 0; + if ( isRemesher ) + { + aGroupLayout->addWidget( new QLabel( tr( "NETGEN_RIDGE_ANGLE" ), GroupC1 ), row, 0 ); + myRidgeAngle = new SMESHGUI_SpinBox( GroupC1 ); + myRidgeAngle->RangeStepAndValidator( 0, 90, 10, "angle_precision" ); + aGroupLayout->addWidget( myRidgeAngle, row, 1 ); + row++; + } + mySurfaceCurvature = 0; - if ( myIs2D || !myIsONLY ) + if (( myIs2D && !isRemesher ) || !myIsONLY ) { mySurfaceCurvature = new QCheckBox( tr( "NETGEN_SURFACE_CURVATURE" ), GroupC1 ); aGroupLayout->addWidget( mySurfaceCurvature, row, 0, 1, 2 ); @@ -225,12 +254,16 @@ QFrame* NETGENPluginGUI_HypothesisCreator::buildFrame() row++; } - myOptimize = new QCheckBox( tr( "NETGEN_OPTIMIZE" ), GroupC1 ); - aGroupLayout->addWidget( myOptimize, row, 0, 1, 2 ); - row++; + myOptimize = 0; + if ( !isRemesher ) + { + myOptimize = new QCheckBox( tr( "NETGEN_OPTIMIZE" ), GroupC1 ); + aGroupLayout->addWidget( myOptimize, row, 0, 1, 2 ); + row++; + } myFuseEdges = 0; - if (!myIsONLY) + if ( !myIsONLY ) { myFuseEdges = new QCheckBox( tr( "NETGEN_FUSE_EDGES" ), GroupC1 ); aGroupLayout->addWidget( myFuseEdges, row, 0, 1, 2 ); @@ -337,6 +370,22 @@ void NETGENPluginGUI_HypothesisCreator::retrieveParams() const else myNbSegPerRadius->setText( data.myNbSegPerRadiusVar ); } + if ( myChordalError ) + { + myChordalErrorEnabled->setChecked( data.myChordalErrorEnabled && data.myChordalError > 0 ); + if(data.myChordalErrorVar.isEmpty()) + myChordalError->setValue( data.myChordalError > 0 ? data.myChordalError : 0.1 ); + else + myChordalError->setText( data.myChordalErrorVar ); + myChordalError->setEnabled( myChordalErrorEnabled->isChecked() ); + } + if ( myRidgeAngle ) + { + if ( data.myRidgeAngleVar.isEmpty() ) + myRidgeAngle->setValue( data.myRidgeAngle ); + else + myRidgeAngle->setText( data.myRidgeAngleVar ); + } if (myAllowQuadrangles) myAllowQuadrangles->setChecked( data.myAllowQuadrangles ); @@ -381,26 +430,26 @@ QString NETGENPluginGUI_HypothesisCreator::storeParams() const readParamsFromWidgets( data ); storeParamsToHypo( data ); - QString valStr = tr("NETGEN_MAX_SIZE") + " = " + QString::number( data.myMaxSize ) + "; "; - valStr += tr("NETGEN_MIN_SIZE") + " = " + QString::number( data.myMinSize ) + "; "; - if ( data.mySecondOrder ) - valStr += tr("NETGEN_SECOND_ORDER") + "; "; - if ( data.myOptimize ) - valStr += tr("NETGEN_OPTIMIZE") + "; "; - valStr += myFineness->currentText() + "(" + QString::number( data.myGrowthRate ) + ", " + - QString::number( data.myNbSegPerEdge ) + ", " + - QString::number( data.myNbSegPerRadius ) + ")"; + // QString valStr = tr("NETGEN_MAX_SIZE") + " = " + QString::number( data.myMaxSize ) + "; "; + // valStr += tr("NETGEN_MIN_SIZE") + " = " + QString::number( data.myMinSize ) + "; "; + // if ( data.mySecondOrder ) + // valStr += tr("NETGEN_SECOND_ORDER") + "; "; + // if ( data.myOptimize ) + // valStr += tr("NETGEN_OPTIMIZE") + "; "; + // valStr += myFineness->currentText() + "(" + QString::number( data.myGrowthRate ) + ", " + + // QString::number( data.myNbSegPerEdge ) + ", " + + // QString::number( data.myNbSegPerRadius ) + ")"; - if ( myIs2D && data.myAllowQuadrangles ) - valStr += "; " + tr("NETGEN_ALLOW_QUADRANGLES"); + // if ( myIs2D && data.myAllowQuadrangles ) + // valStr += "; " + tr("NETGEN_ALLOW_QUADRANGLES"); - if ( data.mySurfaceCurvature ) - valStr += "; " + tr("NETGEN_SURFACE_CURVATURE"); + // if ( data.mySurfaceCurvature ) + // valStr += "; " + tr("NETGEN_SURFACE_CURVATURE"); - if ( data.myFuseEdges ) - valStr += "; " + tr("NETGEN_FUSE_EDGES"); + // if ( data.myFuseEdges ) + // valStr += "; " + tr("NETGEN_FUSE_EDGES"); - return valStr; + return QString(); } bool NETGENPluginGUI_HypothesisCreator::readParamsFromHypo( NetgenHypothesisData& h_data ) const @@ -408,35 +457,45 @@ bool NETGENPluginGUI_HypothesisCreator::readParamsFromHypo( NetgenHypothesisData NETGENPlugin::NETGENPlugin_Hypothesis_var h = NETGENPlugin::NETGENPlugin_Hypothesis::_narrow( initParamsHypothesis() ); - //HypothesisData* data = SMESH::GetHypothesisData( hypType() ); h_data.myName = isCreation() ? hypName() : ""; - h_data.myMaxSize = h->GetMaxSize(); - h_data.myMaxSizeVar = getVariableName("SetMaxSize"); + h_data.myMaxSize = h->GetMaxSize(); + h_data.myMaxSizeVar = getVariableName("SetMaxSize"); h_data.mySecondOrder = h->GetSecondOrder(); - h_data.myOptimize = h->GetOptimize(); - - h_data.myFineness = (int) h->GetFineness(); - h_data.myGrowthRate = h->GetGrowthRate(); - h_data.myGrowthRateVar = getVariableName("SetGrowthRate"); - h_data.myNbSegPerEdge = h->GetNbSegPerEdge(); - h_data.myNbSegPerEdgeVar = getVariableName("SetNbSegPerEdge"); - h_data.myNbSegPerRadius = h->GetNbSegPerRadius(); - h_data.myNbSegPerRadiusVar = getVariableName("SetNbSegPerRadius"); - h_data.myMinSize = h->GetMinSize(); - h_data.myMinSizeVar = getVariableName("SetMinSize"); - h_data.mySurfaceCurvature = h->GetUseSurfaceCurvature(); - h_data.myFuseEdges = h->GetFuseEdges(); - h_data.myMeshSizeFile = h->GetMeshSizeFile(); + h_data.myOptimize = h->GetOptimize(); + + h_data.myFineness = (int) h->GetFineness(); + h_data.myGrowthRate = h->GetGrowthRate(); + h_data.myGrowthRateVar = getVariableName("SetGrowthRate"); + h_data.myNbSegPerEdge = h->GetNbSegPerEdge(); + h_data.myNbSegPerEdgeVar = getVariableName("SetNbSegPerEdge"); + h_data.myNbSegPerRadius = h->GetNbSegPerRadius(); + h_data.myNbSegPerRadiusVar = getVariableName("SetNbSegPerRadius"); + h_data.myChordalError = h->GetChordalError(); + h_data.myChordalErrorVar = getVariableName("SetChordalError"); + h_data.myChordalErrorEnabled = h->GetChordalErrorEnabled(); + h_data.myMinSize = h->GetMinSize(); + h_data.myMinSizeVar = getVariableName("SetMinSize"); + h_data.mySurfaceCurvature = h->GetUseSurfaceCurvature(); + h_data.myFuseEdges = h->GetFuseEdges(); + h_data.myMeshSizeFile = h->GetMeshSizeFile(); //if ( myIs2D ) { - NETGENPlugin::NETGENPlugin_Hypothesis_var h = - NETGENPlugin::NETGENPlugin_Hypothesis::_narrow( initParamsHypothesis() ); + // NETGENPlugin::NETGENPlugin_Hypothesis_var h = + // NETGENPlugin::NETGENPlugin_Hypothesis::_narrow( initParamsHypothesis() ); if ( !h->_is_nil() ) h_data.myAllowQuadrangles = h->GetQuadAllowed(); } + if ( myIs2D ) + { + NETGENPlugin::NETGENPlugin_RemesherHypothesis_2D_var rh = + NETGENPlugin::NETGENPlugin_RemesherHypothesis_2D::_narrow( h ); + + if ( !rh->_is_nil() ) + h_data.myRidgeAngle = rh->GetRidgeAngle(); + } NETGENPluginGUI_HypothesisCreator* that = (NETGENPluginGUI_HypothesisCreator*)this; NETGENPlugin::string_array_var myEntries = h->GetLocalSizeEntries(); @@ -472,8 +531,10 @@ bool NETGENPluginGUI_HypothesisCreator::storeParamsToHypo( const NetgenHypothesi SMESH::SetName( SMESH::FindSObject( h ), h_data.myName.toLatin1().data() ); h->SetVarParameter( h_data.myMaxSizeVar.toLatin1().constData(), "SetMaxSize"); h->SetMaxSize ( h_data.myMaxSize ); - h->SetSecondOrder ( h_data.mySecondOrder ); - h->SetOptimize ( h_data.myOptimize ); + if ( mySecondOrder ) + h->SetSecondOrder ( h_data.mySecondOrder ); + if ( myOptimize ) + h->SetOptimize ( h_data.myOptimize ); int fineness = h_data.myFineness; h->SetFineness ( fineness ); @@ -481,31 +542,53 @@ bool NETGENPluginGUI_HypothesisCreator::storeParamsToHypo( const NetgenHypothesi { h->SetVarParameter ( h_data.myGrowthRateVar.toLatin1().constData(), "SetGrowthRate"); h->SetGrowthRate ( h_data.myGrowthRate ); - h->SetVarParameter ( h_data.myNbSegPerEdgeVar.toLatin1().constData(), "SetNbSegPerEdge"); - h->SetNbSegPerEdge ( h_data.myNbSegPerEdge ); - h->SetVarParameter ( h_data.myNbSegPerRadiusVar.toLatin1().constData(), "SetNbSegPerRadius"); - h->SetNbSegPerRadius( h_data.myNbSegPerRadius ); + if ( myNbSegPerEdge ) + { + h->SetVarParameter ( h_data.myNbSegPerEdgeVar.toLatin1().constData(), "SetNbSegPerEdge"); + h->SetNbSegPerEdge ( h_data.myNbSegPerEdge ); + } + if ( myNbSegPerRadius ) + { + h->SetVarParameter ( h_data.myNbSegPerRadiusVar.toLatin1().constData(), "SetNbSegPerRadius"); + h->SetNbSegPerRadius( h_data.myNbSegPerRadius ); + } + } + if ( myChordalError ) + { + h->SetVarParameter ( h_data.myChordalErrorVar.toLatin1().constData(), "SetChordalError"); + h->SetChordalError ( h_data.myChordalError ); + h->SetChordalErrorEnabled( h_data.myChordalErrorEnabled ); } h->SetVarParameter ( h_data.myMinSizeVar.toLatin1().constData(), "SetMinSize"); h->SetMinSize ( h_data.myMinSize ); - h->SetUseSurfaceCurvature( h_data.mySurfaceCurvature ); - h->SetFuseEdges ( h_data.myFuseEdges ); + if ( mySurfaceCurvature ) + h->SetUseSurfaceCurvature( h_data.mySurfaceCurvature ); + if ( myFuseEdges ) + h->SetFuseEdges ( h_data.myFuseEdges ); h->SetMeshSizeFile ( h_data.myMeshSizeFile.toUtf8().constData() ); //if ( myIs2D ) { // NETGENPlugin::NETGENPlugin_Hypothesis_2D_var h_2d = // NETGENPlugin::NETGENPlugin_Hypothesis_2D::_narrow( h ); - // if ( !h_2d->_is_nil() ) // h_2d->SetQuadAllowed( h_data.myAllowQuadrangles ); - h->SetQuadAllowed( h_data.myAllowQuadrangles ); + if ( myAllowQuadrangles ) + h->SetQuadAllowed( h_data.myAllowQuadrangles ); } - - QMapIterator i(myLocalSizeMap); - while (i.hasNext()) { - i.next(); - const QString entry = i.key(); + if ( myIs2D ) + { + NETGENPlugin::NETGENPlugin_RemesherHypothesis_2D_var rh = + NETGENPlugin::NETGENPlugin_RemesherHypothesis_2D::_narrow( h ); + if ( !rh->_is_nil() ) + { + rh->SetVarParameter( h_data.myRidgeAngleVar.toLatin1().constData(), "SetRidgeAngle"); + rh->SetRidgeAngle ( h_data.myRidgeAngle ); + } + } + for ( QMapIterator i(myLocalSizeMap); i.hasNext(); i.next() ) + { + const QString entry = i.key(); const QString localSize = i.value(); if (localSize == "__TO_DELETE__") { @@ -513,10 +596,7 @@ bool NETGENPluginGUI_HypothesisCreator::storeParamsToHypo( const NetgenHypothesi } else { - std::istringstream tmp(localSize.toLatin1().constData()); - double val; - tmp >> val; - h->SetLocalSizeOnEntry(entry.toLatin1().constData(), val); + h->SetLocalSizeOnEntry(entry.toLatin1().constData(), localSize.toDouble()); } } } @@ -551,8 +631,18 @@ bool NETGENPluginGUI_HypothesisCreator::readParamsFromWidgets( NetgenHypothesisD h_data.myNbSegPerEdgeVar = myNbSegPerEdge->text(); if ( myNbSegPerRadius ) h_data.myNbSegPerRadiusVar = myNbSegPerRadius->text(); + if ( myChordalError ) + { + h_data.myChordalErrorVar = myChordalError->text(); + h_data.myChordalError = myChordalError->value(); + h_data.myChordalErrorEnabled = myChordalError->isEnabled(); + } + if ( myRidgeAngle ) + { + h_data.myRidgeAngleVar = myRidgeAngle->text(); + h_data.myRidgeAngle = myRidgeAngle->value(); + } - if ( myAllowQuadrangles ) h_data.myAllowQuadrangles = myAllowQuadrangles->isChecked(); @@ -577,6 +667,11 @@ bool NETGENPluginGUI_HypothesisCreator::readParamsFromWidgets( NetgenHypothesisD return true; } +void NETGENPluginGUI_HypothesisCreator::onChordalErrorEnabled() +{ + myChordalError->setEnabled( myChordalErrorEnabled->isChecked() ); +} + void NETGENPluginGUI_HypothesisCreator::onSurfaceCurvatureChanged() { bool isSurfaceCurvature = (mySurfaceCurvature ? mySurfaceCurvature->isChecked() : true); @@ -587,12 +682,17 @@ void NETGENPluginGUI_HypothesisCreator::onSurfaceCurvatureChanged() myNbSegPerEdge->setEnabled(isCustom && isSurfaceCurvature); if ( myNbSegPerRadius ) myNbSegPerRadius->setEnabled(isCustom && isSurfaceCurvature); + // if ( myChordalError ) + // { + // myChordalError->setEnabled( isSurfaceCurvature ); + // myChordalErrorEnabled->setEnabled( isSurfaceCurvature ); + // } } void NETGENPluginGUI_HypothesisCreator::onFinenessChanged() { bool isCustom = (myFineness->currentIndex() == UserDefined); - + myGrowthRate->setEnabled(isCustom); if ( myNbSegPerEdge ) myNbSegPerEdge->setEnabled(isCustom); @@ -600,45 +700,45 @@ void NETGENPluginGUI_HypothesisCreator::onFinenessChanged() myNbSegPerRadius->setEnabled(isCustom); if (!isCustom) + { + double aGrowthRate, aNbSegPerEdge, aNbSegPerRadius; + + switch ( myFineness->currentIndex() ) { - double aGrowthRate, aNbSegPerEdge, aNbSegPerRadius; - - switch ( myFineness->currentIndex() ) - { - case VeryCoarse: - aGrowthRate = 0.7; - aNbSegPerEdge = 0.3; - aNbSegPerRadius = 1; - break; - case Coarse: - aGrowthRate = 0.5; - aNbSegPerEdge = 0.5; - aNbSegPerRadius = 1.5; - break; - case Fine: - aGrowthRate = 0.2; - aNbSegPerEdge = 2; - aNbSegPerRadius = 3; - break; - case VeryFine: - aGrowthRate = 0.1; - aNbSegPerEdge = 3; - aNbSegPerRadius = 5; - break; - case Moderate: - default: - aGrowthRate = 0.3; - aNbSegPerEdge = 1; - aNbSegPerRadius = 2; - break; - } - - myGrowthRate->setValue( aGrowthRate ); - if ( myNbSegPerEdge ) - myNbSegPerEdge->setValue( aNbSegPerEdge ); - if ( myNbSegPerRadius ) - myNbSegPerRadius->setValue( aNbSegPerRadius ); + case VeryCoarse: + aGrowthRate = 0.7; + aNbSegPerEdge = 0.3; + aNbSegPerRadius = 1; + break; + case Coarse: + aGrowthRate = 0.5; + aNbSegPerEdge = 0.5; + aNbSegPerRadius = 1.5; + break; + case Fine: + aGrowthRate = 0.2; + aNbSegPerEdge = 2; + aNbSegPerRadius = 3; + break; + case VeryFine: + aGrowthRate = 0.1; + aNbSegPerEdge = 3; + aNbSegPerRadius = 5; + break; + case Moderate: + default: + aGrowthRate = 0.3; + aNbSegPerEdge = 1; + aNbSegPerRadius = 2; + break; } + + myGrowthRate->setValue( aGrowthRate ); + if ( myNbSegPerEdge ) + myNbSegPerEdge->setValue( aNbSegPerEdge ); + if ( myNbSegPerRadius ) + myNbSegPerRadius->setValue( aNbSegPerRadius ); + } } void NETGENPluginGUI_HypothesisCreator::onAddLocalSizeOnVertex() @@ -670,54 +770,54 @@ void NETGENPluginGUI_HypothesisCreator::addLocalSizeOnShape(TopAbs_ShapeEnum typ mySel->selectedObjects(ListSelectedObjects, NULL, false ); SALOME_ListIteratorOfListIO Object_It(ListSelectedObjects); for ( ; Object_It.More() ; Object_It.Next()) + { + Handle(SALOME_InteractiveObject) anObject = Object_It.Value(); + std::string entry, shapeName; + entry = geomSelectionTools->getEntryOfObject(anObject); + shapeName = anObject->getName(); + TopAbs_ShapeEnum shapeType; + shapeType = geomSelectionTools->entryToShapeType(entry); + if (shapeType == TopAbs_SHAPE) { - Handle(SALOME_InteractiveObject) anObject = Object_It.Value(); - std::string entry, shapeName; - entry = geomSelectionTools->getEntryOfObject(anObject); - shapeName = anObject->getName(); - TopAbs_ShapeEnum shapeType; - shapeType = geomSelectionTools->entryToShapeType(entry); - if (shapeType == TopAbs_SHAPE) - { - // E.A. if shapeType == TopAbs_SHAPE, it is NOT a TopoDS_Shape !!! - continue; - } - // -- - if(shapeType != typeShapeAsked) - { - continue; - } - // -- - myLocalSizeTable->setFocus(); - QString shapeEntry; - shapeEntry = QString::fromStdString(entry); - if (myLocalSizeMap.contains(shapeEntry)) - { - if (myLocalSizeMap[shapeEntry] != "__TO_DELETE__") - { - continue; - } - } - double phySize = h->GetMaxSize(); - std::ostringstream oss; - oss << phySize; - QString localSize; - localSize = QString::fromStdString(oss.str()); - // -- - int row = myLocalSizeTable->rowCount() ; - myLocalSizeTable->setRowCount(row+1); - myLocalSizeTable->setItem(row, LSZ_ENTRY_COLUMN, new QTableWidgetItem(shapeEntry)); - myLocalSizeTable->item(row, LSZ_ENTRY_COLUMN )->setFlags(0); - myLocalSizeTable->setItem(row, LSZ_NAME_COLUMN, new QTableWidgetItem(QString::fromStdString(shapeName))); - myLocalSizeTable->item(row, LSZ_NAME_COLUMN )->setFlags(0); - myLocalSizeTable->setItem(row, LSZ_LOCALSIZE_COLUMN, new QTableWidgetItem(localSize)); - myLocalSizeTable->item(row, LSZ_LOCALSIZE_COLUMN )->setFlags(Qt::ItemIsSelectable|Qt::ItemIsEditable|Qt::ItemIsEnabled); - myLocalSizeTable->resizeColumnToContents(LSZ_NAME_COLUMN); - myLocalSizeTable->resizeColumnToContents(LSZ_LOCALSIZE_COLUMN); - myLocalSizeTable->clearSelection(); - myLocalSizeTable->scrollToItem( myLocalSizeTable->item( row, LSZ_LOCALSIZE_COLUMN ) ); - // -- + // E.A. if shapeType == TopAbs_SHAPE, it is NOT a TopoDS_Shape !!! + continue; + } + // -- + if(shapeType != typeShapeAsked) + { + continue; + } + // -- + myLocalSizeTable->setFocus(); + QString shapeEntry; + shapeEntry = QString::fromStdString(entry); + if (myLocalSizeMap.contains(shapeEntry)) + { + if (myLocalSizeMap[shapeEntry] != "__TO_DELETE__") + { + continue; + } } + double phySize = h->GetMaxSize(); + std::ostringstream oss; + oss << phySize; + QString localSize; + localSize = QString::fromStdString(oss.str()); + // -- + int row = myLocalSizeTable->rowCount() ; + myLocalSizeTable->setRowCount(row+1); + myLocalSizeTable->setItem(row, LSZ_ENTRY_COLUMN, new QTableWidgetItem(shapeEntry)); + myLocalSizeTable->item(row, LSZ_ENTRY_COLUMN )->setFlags(0); + myLocalSizeTable->setItem(row, LSZ_NAME_COLUMN, new QTableWidgetItem(QString::fromStdString(shapeName))); + myLocalSizeTable->item(row, LSZ_NAME_COLUMN )->setFlags(0); + myLocalSizeTable->setItem(row, LSZ_LOCALSIZE_COLUMN, new QTableWidgetItem(localSize)); + myLocalSizeTable->item(row, LSZ_LOCALSIZE_COLUMN )->setFlags(Qt::ItemIsSelectable|Qt::ItemIsEditable|Qt::ItemIsEnabled); + myLocalSizeTable->resizeColumnToContents(LSZ_NAME_COLUMN); + myLocalSizeTable->resizeColumnToContents(LSZ_LOCALSIZE_COLUMN); + myLocalSizeTable->clearSelection(); + myLocalSizeTable->scrollToItem( myLocalSizeTable->item( row, LSZ_LOCALSIZE_COLUMN ) ); + // -- + } } void NETGENPluginGUI_HypothesisCreator::onRemoveLocalSizeOnShape() @@ -735,15 +835,15 @@ void NETGENPluginGUI_HypothesisCreator::onRemoveLocalSizeOnShape() QListIterator it( selectedRows ); it.toBack(); while (it.hasPrevious()) + { + row = it.previous(); + QString entry = myLocalSizeTable->item(row,LSZ_ENTRY_COLUMN)->text(); + if (myLocalSizeMap.contains(entry)) { - row = it.previous(); - QString entry = myLocalSizeTable->item(row,LSZ_ENTRY_COLUMN)->text(); - if (myLocalSizeMap.contains(entry)) - { - myLocalSizeMap[entry] = "__TO_DELETE__"; - } - myLocalSizeTable->removeRow(row ); + myLocalSizeMap[entry] = "__TO_DELETE__"; } + myLocalSizeTable->removeRow(row ); + } myLocalSizeTable->resizeColumnToContents(LSZ_NAME_COLUMN); myLocalSizeTable->resizeColumnToContents(LSZ_LOCALSIZE_COLUMN); } @@ -777,18 +877,20 @@ GeomSelectionTools* NETGENPluginGUI_HypothesisCreator::getGeomSelectionTools() QString NETGENPluginGUI_HypothesisCreator::caption() const { - return tr( QString( "NETGEN_%1_TITLE" ).arg(myIs2D?QString("2D"):QString("3D")).toLatin1().data() ); + return tr( myIs2D ? "NETGEN_2D_TITLE" : "NETGEN_3D_TITLE"); } QPixmap NETGENPluginGUI_HypothesisCreator::icon() const { - QString hypIconName = tr( QString("ICON_DLG_NETGEN_PARAMETERS%1").arg(myIs2D?QString("_2D"):QString("")).toLatin1().data() ); + QString hypIconName = tr( myIs2D ? + "ICON_DLG_NETGEN_PARAMETERS_2D" : + "ICON_DLG_NETGEN_PARAMETERS"); return SUIT_Session::session()->resourceMgr()->loadPixmap( "NETGENPlugin", hypIconName ); } QString NETGENPluginGUI_HypothesisCreator::type() const { - return tr( QString( "NETGEN_%1_HYPOTHESIS" ).arg(myIs2D?QString("2D"):QString("3D")).toLatin1().data() ); + return tr( myIs2D ? "NETGEN_2D_HYPOTHESIS" : "NETGEN_3D_HYPOTHESIS"); } QString NETGENPluginGUI_HypothesisCreator::helpPage() const diff --git a/src/GUI/NETGENPluginGUI_HypothesisCreator.h b/src/GUI/NETGENPluginGUI_HypothesisCreator.h index 64a1616..8ca5992 100644 --- a/src/GUI/NETGENPluginGUI_HypothesisCreator.h +++ b/src/GUI/NETGENPluginGUI_HypothesisCreator.h @@ -44,11 +44,11 @@ class QTableWidget; typedef struct { - double myMaxSize, myMinSize, myGrowthRate, myNbSegPerEdge, myNbSegPerRadius; - int myFineness; - bool mySecondOrder, myAllowQuadrangles, myOptimize, mySurfaceCurvature, myFuseEdges; - QString myName, myMeshSizeFile; - QString myMaxSizeVar, myMinSizeVar, myGrowthRateVar, myNbSegPerEdgeVar, myNbSegPerRadiusVar; + double myMaxSize, myMinSize, myGrowthRate, myNbSegPerEdge, myNbSegPerRadius, myRidgeAngle, myChordalError; + int myFineness; + bool mySecondOrder, myAllowQuadrangles, myOptimize, mySurfaceCurvature, myFuseEdges, myChordalErrorEnabled; + QString myName, myMeshSizeFile; + QString myMaxSizeVar, myMinSizeVar, myGrowthRateVar, myNbSegPerEdgeVar, myNbSegPerRadiusVar, myRidgeAngleVar, myChordalErrorVar; } NetgenHypothesisData; /*! @@ -76,6 +76,7 @@ protected: protected slots: virtual void onFinenessChanged(); + virtual void onChordalErrorEnabled(); virtual void onSurfaceCurvatureChanged(); virtual void onAddLocalSizeOnVertex(); virtual void onAddLocalSizeOnEdge(); @@ -102,12 +103,15 @@ private: SMESHGUI_SpinBox* myGrowthRate; SMESHGUI_SpinBox* myNbSegPerEdge; SMESHGUI_SpinBox* myNbSegPerRadius; + SMESHGUI_SpinBox* myRidgeAngle; + QCheckBox* myChordalErrorEnabled; + SMESHGUI_SpinBox* myChordalError; QCheckBox* myAllowQuadrangles; QCheckBox* mySurfaceCurvature; QCheckBox* myFuseEdges; - bool myIs2D; - bool myIsONLY; + bool myIs2D; // 2D or 3D + bool myIsONLY; // one dim or several QLineEdit* myMeshSizeFile; QTableWidget* myLocalSizeTable; diff --git a/src/GUI/NETGENPlugin_msg_en.ts b/src/GUI/NETGENPlugin_msg_en.ts index 139fb85..9f1050b 100644 --- a/src/GUI/NETGENPlugin_msg_en.ts +++ b/src/GUI/NETGENPlugin_msg_en.ts @@ -91,6 +91,14 @@ NETGEN_SEG_PER_RADIUS Nb. Segs per Radius + + NETGEN_CHORDAL_ERROR + Chordal Error + + + NETGEN_RIDGE_ANGLE + Ridge Angle + NETGEN_SURFACE_CURVATURE Limit Size by Surface Curvature diff --git a/src/NETGENPlugin/CMakeLists.txt b/src/NETGENPlugin/CMakeLists.txt index 5a5b078..cf4120e 100644 --- a/src/NETGENPlugin/CMakeLists.txt +++ b/src/NETGENPlugin/CMakeLists.txt @@ -110,6 +110,7 @@ SET(NETGENEngine_HEADERS NETGENPlugin_SimpleHypothesis_2D_i.hxx NETGENPlugin_SimpleHypothesis_3D_i.hxx NETGENPlugin_Mesher.hxx + NETGENPlugin_Remesher_2D.hxx NETGENPlugin_Defs.hxx ) @@ -136,6 +137,7 @@ SET(NETGENEngine_SOURCES NETGENPlugin_SimpleHypothesis_3D.cxx NETGENPlugin_SimpleHypothesis_2D_i.cxx NETGENPlugin_SimpleHypothesis_3D_i.cxx + NETGENPlugin_Remesher_2D.cxx NETGENPlugin_i.cxx ) diff --git a/src/NETGENPlugin/NETGENPluginBuilder.py b/src/NETGENPlugin/NETGENPluginBuilder.py index d5f9460..7334ae3 100644 --- a/src/NETGENPlugin/NETGENPluginBuilder.py +++ b/src/NETGENPlugin/NETGENPluginBuilder.py @@ -106,7 +106,11 @@ class NETGEN_Algorithm(Mesh_Algorithm): def __init__(self, mesh, geom=0): Mesh_Algorithm.__init__(self) if noNETGENPlugin: print "Warning: NETGENPlugin module unavailable" - self.Create(mesh, geom, self.algoType, LIBRARY) + if not mesh.GetMesh().HasShapeToMesh() and \ + self.meshMethod == "Triangle": # create a 2D remesher + self.Create(mesh, geom, "NETGEN_Remesher_2D", LIBRARY) + else: + self.Create(mesh, geom, self.algoType, LIBRARY) self.params = None pass @@ -161,6 +165,9 @@ class NETGEN_Algorithm(Mesh_Algorithm): else: hypType = "NETGEN_Parameters_3D" + if self.algo.GetName() == "NETGEN_Remesher_2D": + hypType = "NETGEN_RemesherParameters_2D" + if self.params and self.params.GetName() != hypType: self.mesh.RemoveHypothesis( self.params, self.geom ) self.params = None @@ -221,6 +228,21 @@ class NETGEN_1D2D3D_Algorithm(NETGEN_Algorithm): if self.Parameters(): self.params.SetNbSegPerRadius(theVal) pass + ## Sets @c ChordalError parameter + # @param theVal new value of the @c ChordalError parameter + def SetChordalError(self, theVal): + if self.Parameters(): + self.params.SetChordalError(theVal) + self.params.SetChordalErrorEnabled( theVal > 0 ) + pass + + ## Sets @c RidgeAngle parameter + # @param theVal new value of the @c RidgeAngle parameter + def SetRidgeAngle(self, theVal): + if self.Parameters(): + self.params.SetRidgeAngle(theVal) + pass + ## Sets @c QuadAllowed flag # @param toAllow new value of the @c QuadAllowed parameter (@c True by default) def SetQuadAllowed(self, toAllow=True): diff --git a/src/NETGENPlugin/NETGENPlugin_Hypothesis.cxx b/src/NETGENPlugin/NETGENPlugin_Hypothesis.cxx index fe4c2ee..9bd9ec8 100644 --- a/src/NETGENPlugin/NETGENPlugin_Hypothesis.cxx +++ b/src/NETGENPlugin/NETGENPlugin_Hypothesis.cxx @@ -43,18 +43,20 @@ using namespace std; NETGENPlugin_Hypothesis::NETGENPlugin_Hypothesis (int hypId, int studyId, SMESH_Gen * gen) : SMESH_Hypothesis(hypId, studyId, gen), - _maxSize (GetDefaultMaxSize()), - _minSize (0), - _growthRate (GetDefaultGrowthRate()), - _nbSegPerEdge (GetDefaultNbSegPerEdge()), - _nbSegPerRadius (GetDefaultNbSegPerRadius()), - _fineness (GetDefaultFineness()), - _secondOrder (GetDefaultSecondOrder()), - _optimize (GetDefaultOptimize()), - _localSize (GetDefaultLocalSize()), - _quadAllowed (GetDefaultQuadAllowed()), - _surfaceCurvature(GetDefaultSurfaceCurvature()), - _fuseEdges (GetDefaultFuseEdges()) + _maxSize (GetDefaultMaxSize()), + _minSize (0), + _growthRate (GetDefaultGrowthRate()), + _nbSegPerEdge (GetDefaultNbSegPerEdge()), + _nbSegPerRadius (GetDefaultNbSegPerRadius()), + _fineness (GetDefaultFineness()), + _chordalErrorEnabled(GetDefaultChordalError() > 0), + _chordalError (GetDefaultChordalError() ), + _secondOrder (GetDefaultSecondOrder()), + _optimize (GetDefaultOptimize()), + _localSize (GetDefaultLocalSize()), + _quadAllowed (GetDefaultQuadAllowed()), + _surfaceCurvature (GetDefaultSurfaceCurvature()), + _fuseEdges (GetDefaultFuseEdges()) { _name = "NETGEN_Parameters"; _param_algo_dim = 3; @@ -208,6 +210,34 @@ void NETGENPlugin_Hypothesis::SetNbSegPerRadius(double theVal) } } +//============================================================================= +/*! + * + */ +//============================================================================= +void NETGENPlugin_Hypothesis::SetChordalErrorEnabled(bool theVal) +{ + if (theVal != _chordalErrorEnabled) + { + _chordalErrorEnabled = theVal; + NotifySubMeshesHypothesisModification(); + } +} + +//============================================================================= +/*! + * + */ +//============================================================================= +void NETGENPlugin_Hypothesis::SetChordalError(double theVal) +{ + if (theVal != _chordalError) + { + _chordalError = theVal; + NotifySubMeshesHypothesisModification(); + } +} + //============================================================================= /*! * @@ -351,8 +381,8 @@ ostream & NETGENPlugin_Hypothesis::SaveTo(ostream & save) if (it_sm != _localSize.end()) { save << " " << "__LOCALSIZE_BEGIN__"; for ( ; it_sm != _localSize.end(); ++it_sm ) { - save << " " << it_sm->first - << " " << it_sm->second << "%#"; // "%#" is a mark of value end + save << " " << it_sm->first + << " " << it_sm->second << "%#"; // "%#" is a mark of value end } save << " " << "__LOCALSIZE_END__"; } @@ -363,6 +393,8 @@ ostream & NETGENPlugin_Hypothesis::SaveTo(ostream & save) save << " " << _meshSizeFile.size() << " " << _meshSizeFile; + save << " " << ( _chordalErrorEnabled ? _chordalError : 0. ); + return save; } @@ -476,12 +508,19 @@ istream & NETGENPlugin_Hypothesis::LoadFrom(istream & load) load.get( &_meshSizeFile[0], is+1 ); } + isOK = static_cast(load >> val); + if (isOK) + _chordalError = val; + else + load.clear(ios::badbit | load.rdstate()); + _chordalErrorEnabled = ( _chordalError > 0 ); + return load; } //============================================================================= /*! - * + * */ //============================================================================= ostream & operator <<(ostream & save, NETGENPlugin_Hypothesis & hyp) @@ -491,7 +530,7 @@ ostream & operator <<(ostream & save, NETGENPlugin_Hypothesis & hyp) //============================================================================= /*! - * + * */ //============================================================================= istream & operator >>(istream & load, NETGENPlugin_Hypothesis & hyp) @@ -584,6 +623,15 @@ double NETGENPlugin_Hypothesis::GetDefaultNbSegPerRadius() { return 2; } +//============================================================================= +/*! + * + */ +//============================================================================= +double NETGENPlugin_Hypothesis::GetDefaultChordalError() +{ + return -1; // disabled by default +} //============================================================================= /*! diff --git a/src/NETGENPlugin/NETGENPlugin_Hypothesis.hxx b/src/NETGENPlugin/NETGENPlugin_Hypothesis.hxx index 7d6da32..44f9095 100644 --- a/src/NETGENPlugin/NETGENPlugin_Hypothesis.hxx +++ b/src/NETGENPlugin/NETGENPlugin_Hypothesis.hxx @@ -83,6 +83,11 @@ public: void SetNbSegPerRadius(double theVal); double GetNbSegPerRadius() const { return _nbSegPerRadius; } + void SetChordalErrorEnabled(bool value); + double GetChordalErrorEnabled() const { return _chordalErrorEnabled; } + void SetChordalError(double value); + double GetChordalError() const { return _chordalError; } + typedef std::map TLocalSize; static TLocalSize GetDefaultLocalSize() { return TLocalSize(); } void SetLocalSizeOnEntry(const std::string& entry, double localSize); @@ -109,6 +114,7 @@ public: static double GetDefaultGrowthRate(); static double GetDefaultNbSegPerEdge(); static double GetDefaultNbSegPerRadius(); + static double GetDefaultChordalError(); static bool GetDefaultSecondOrder(); static bool GetDefaultOptimize(); static bool GetDefaultQuadAllowed(); @@ -116,10 +122,8 @@ public: static bool GetDefaultFuseEdges(); // Persistence - virtual ostream & SaveTo(ostream & save); - virtual istream & LoadFrom(istream & load); - friend NETGENPLUGIN_EXPORT ostream & operator <<(ostream & save, NETGENPlugin_Hypothesis & hyp); - friend NETGENPLUGIN_EXPORT istream & operator >>(istream & load, NETGENPlugin_Hypothesis & hyp); + virtual std::ostream & SaveTo(std::ostream & save); + virtual std::istream & LoadFrom(std::istream & load); /*! * \brief Does nothing @@ -141,6 +145,8 @@ private: double _nbSegPerEdge; double _nbSegPerRadius; Fineness _fineness; + bool _chordalErrorEnabled; + double _chordalError; bool _secondOrder; bool _optimize; TLocalSize _localSize; diff --git a/src/NETGENPlugin/NETGENPlugin_Hypothesis_2D.cxx b/src/NETGENPlugin/NETGENPlugin_Hypothesis_2D.cxx index 056c592..5db5097 100644 --- a/src/NETGENPlugin/NETGENPlugin_Hypothesis_2D.cxx +++ b/src/NETGENPlugin/NETGENPlugin_Hypothesis_2D.cxx @@ -28,7 +28,6 @@ //============================================================================= // #include "NETGENPlugin_Hypothesis_2D.hxx" -#include using namespace std; @@ -51,56 +50,72 @@ NETGENPlugin_Hypothesis_2D::NETGENPlugin_Hypothesis_2D (int hypId, int studyId, * */ //============================================================================= -// void NETGENPlugin_Hypothesis_2D::SetQuadAllowed(bool theVal) -// { -// if (theVal != _quadAllowed) -// { -// _quadAllowed = theVal; -// NotifySubMeshesHypothesisModification(); -// } -// } - -// //============================================================================= -// /*! -// * -// */ -// //============================================================================= -// bool NETGENPlugin_Hypothesis_2D::GetDefaultQuadAllowed() -// { -// return false; -// } - -// //============================================================================= -// /*! -// * -// */ -// //============================================================================= -// ostream & NETGENPlugin_Hypothesis_2D::SaveTo(ostream & save) -// { -// NETGENPlugin_Hypothesis::SaveTo(save); - -// save << " " << (int)_quadAllowed; - -// return save; -// } - -// //============================================================================= -// /*! -// * -// */ -// //============================================================================= -// istream & NETGENPlugin_Hypothesis_2D::LoadFrom(istream & load) -// { -// NETGENPlugin_Hypothesis::LoadFrom(load); - -// bool isOK = true; -// int is; - -// isOK = (load >> is); -// if (isOK) -// _quadAllowed = (bool) is; -// else -// load.clear(ios::badbit | load.rdstate()); - -// return load; -// } +NETGENPlugin_RemesherHypothesis_2D:: +NETGENPlugin_RemesherHypothesis_2D (int hypId, int studyId, SMESH_Gen * gen) + : NETGENPlugin_Hypothesis(hypId, studyId, gen) +{ + _name = "NETGEN_RemesherParameters_2D"; + _param_algo_dim = 2; + + _ridgeAngle = DefaultRidgeAngle(); +} + +//============================================================================= +/*! + * + */ +//============================================================================= + +void NETGENPlugin_RemesherHypothesis_2D::SetRidgeAngle( double angle ) +{ + if ( _ridgeAngle != angle ) + { + _ridgeAngle = angle; + NotifySubMeshesHypothesisModification(); + } +} + +//============================================================================= +/*! + * + */ +//============================================================================= + +double NETGENPlugin_RemesherHypothesis_2D::GetRidgeAngle() const +{ + return _ridgeAngle; +} + +//============================================================================= +/*! + * + */ +//============================================================================= + +std::ostream & NETGENPlugin_RemesherHypothesis_2D::SaveTo(std::ostream & save) +{ + NETGENPlugin_Hypothesis::SaveTo( save ); + save << " " << _ridgeAngle; + + return save; +} + +//============================================================================= +/*! + * + */ +//============================================================================= + +std::istream & NETGENPlugin_RemesherHypothesis_2D::LoadFrom(std::istream & load) +{ + NETGENPlugin_Hypothesis::LoadFrom( load ); + if ( !load ) + load.clear(ios::badbit | load.rdstate()); + + load >> _ridgeAngle; + + if ( !load ) + _ridgeAngle = DefaultRidgeAngle(); + + return load; +} diff --git a/src/NETGENPlugin/NETGENPlugin_Hypothesis_2D.hxx b/src/NETGENPlugin/NETGENPlugin_Hypothesis_2D.hxx index b73a3ba..cd0fe5c 100644 --- a/src/NETGENPlugin/NETGENPlugin_Hypothesis_2D.hxx +++ b/src/NETGENPlugin/NETGENPlugin_Hypothesis_2D.hxx @@ -57,4 +57,27 @@ public: // bool _quadAllowed; }; + +// Parameters of NETGEN remesher +// + +class NETGENPLUGIN_EXPORT NETGENPlugin_RemesherHypothesis_2D: public NETGENPlugin_Hypothesis +{ + public: + + NETGENPlugin_RemesherHypothesis_2D(int hypId, int studyId, SMESH_Gen * gen); + + void SetRidgeAngle( double angle ); + double GetRidgeAngle() const; + + static double DefaultRidgeAngle() { return 30.; } + + virtual std::ostream & SaveTo(std::ostream & save); + virtual std::istream & LoadFrom(std::istream & load); + + private: + + double _ridgeAngle; // in degrees +}; + #endif diff --git a/src/NETGENPlugin/NETGENPlugin_Hypothesis_2D_i.cxx b/src/NETGENPlugin/NETGENPlugin_Hypothesis_2D_i.cxx index e889033..d9cbde4 100644 --- a/src/NETGENPlugin/NETGENPlugin_Hypothesis_2D_i.cxx +++ b/src/NETGENPlugin/NETGENPlugin_Hypothesis_2D_i.cxx @@ -31,11 +31,6 @@ #include "SMESH_Gen.hxx" #include "SMESH_PythonDump.hxx" -#include "Utils_CorbaException.hxx" -#include "utilities.h" - -using namespace std; - //============================================================================= /*! * NETGENPlugin_Hypothesis_2D_i::NETGENPlugin_Hypothesis_2D_i @@ -71,56 +66,97 @@ NETGENPlugin_Hypothesis_2D_i::~NETGENPlugin_Hypothesis_2D_i() //============================================================================= /*! - * NETGENPlugin_Hypothesis_2D_i::SetQuadAllowed + * NETGENPlugin_Hypothesis_2D_i::GetImpl * - * Set QuadAllowed flag + * Get implementation */ //============================================================================= -// void NETGENPlugin_Hypothesis_2D_i::SetQuadAllowed (CORBA::Boolean theValue) -// { -// if ( NETGENPlugin_Hypothesis_i::isToSetParameter( GetQuadAllowed(), -// theValue, -// METH_SetQuadAllowed )) -// { -// this->GetImpl()->SetQuadAllowed(theValue); -// SMESH::TPythonDump() << _this() << ".SetQuadAllowed( " << theValue << " )"; -// } -// } +::NETGENPlugin_Hypothesis_2D* NETGENPlugin_Hypothesis_2D_i::GetImpl() +{ + return (::NETGENPlugin_Hypothesis_2D*)myBaseImpl; +} + +//================================================================================ +/*! + * \brief Verify whether hypothesis supports given entity type + * \param type - dimension (see SMESH::Dimension enumeration) + * \retval CORBA::Boolean - TRUE if dimension is supported, FALSE otherwise + * + * Verify whether hypothesis supports given entity type (see SMESH::Dimension enumeration) + */ +//================================================================================ +CORBA::Boolean NETGENPlugin_Hypothesis_2D_i::IsDimSupported( SMESH::Dimension type ) +{ + return type == SMESH::DIM_2D; +} + //============================================================================= /*! - * NETGENPlugin_Hypothesis_2D_i::GetQuadAllowed + * NETGENPlugin_RemesherHypothesis_2D_i::NETGENPlugin_RemesherHypothesis_2D_i * - * Get QuadAllowed flag + * Constructor */ //============================================================================= -// CORBA::Boolean NETGENPlugin_Hypothesis_2D_i::GetQuadAllowed() -// { -// return this->GetImpl()->GetQuadAllowed(); -// } +NETGENPlugin_RemesherHypothesis_2D_i:: +NETGENPlugin_RemesherHypothesis_2D_i (PortableServer::POA_ptr thePOA, + int theStudyId, + ::SMESH_Gen* theGenImpl) + : SALOME::GenericObj_i( thePOA ), + SMESH_Hypothesis_i( thePOA ), + NETGENPlugin_Hypothesis_2D_i( thePOA, theStudyId, theGenImpl ) +{ + myBaseImpl = new ::NETGENPlugin_RemesherHypothesis_2D (theGenImpl->GetANewId(), + theStudyId, + theGenImpl); +} + +//================================================================================ +/*! + * \brief Verify whether hypothesis supports given entity type + * \param type - dimension (see SMESH::Dimension enumeration) + * \retval CORBA::Boolean - TRUE if dimension is supported, FALSE otherwise + * + * Verify whether hypothesis supports given entity type (see SMESH::Dimension enumeration) + */ +//================================================================================ +CORBA::Boolean NETGENPlugin_RemesherHypothesis_2D_i::IsDimSupported( SMESH::Dimension type ) +{ + return type == SMESH::DIM_2D; +} //============================================================================= /*! - * NETGENPlugin_Hypothesis_2D_i::GetImpl + * NETGENPlugin_RemesherHypothesis_2D_i::GetImpl * * Get implementation */ //============================================================================= -::NETGENPlugin_Hypothesis_2D* NETGENPlugin_Hypothesis_2D_i::GetImpl() +::NETGENPlugin_RemesherHypothesis_2D* NETGENPlugin_RemesherHypothesis_2D_i::GetImpl() { - return (::NETGENPlugin_Hypothesis_2D*)myBaseImpl; + return (::NETGENPlugin_RemesherHypothesis_2D*)myBaseImpl; } //================================================================================ /*! - * \brief Verify whether hypothesis supports given entity type - * \param type - dimension (see SMESH::Dimension enumeration) - * \retval CORBA::Boolean - TRUE if dimension is supported, FALSE otherwise - * - * Verify whether hypothesis supports given entity type (see SMESH::Dimension enumeration) + * \brief Set ridge angle */ -//================================================================================ -CORBA::Boolean NETGENPlugin_Hypothesis_2D_i::IsDimSupported( SMESH::Dimension type ) +//================================================================================ + +void NETGENPlugin_RemesherHypothesis_2D_i::SetRidgeAngle( CORBA::Double angle ) { - return type == SMESH::DIM_2D; + GetImpl()->SetRidgeAngle( angle ); + + SMESH::TPythonDump() << _this() << ".SetRidgeAngle( " << SMESH::TVar(angle) << " )"; +} + +//================================================================================ +/*! + * \brief Return ridge angle + */ +//================================================================================ + +CORBA::Double NETGENPlugin_RemesherHypothesis_2D_i::GetRidgeAngle() +{ + return GetImpl()->GetRidgeAngle(); } diff --git a/src/NETGENPlugin/NETGENPlugin_Hypothesis_2D_i.hxx b/src/NETGENPlugin/NETGENPlugin_Hypothesis_2D_i.hxx index 55b1c3f..876a361 100644 --- a/src/NETGENPlugin/NETGENPlugin_Hypothesis_2D_i.hxx +++ b/src/NETGENPlugin/NETGENPlugin_Hypothesis_2D_i.hxx @@ -70,4 +70,27 @@ class NETGENPLUGIN_EXPORT NETGENPlugin_Hypothesis_2D_i: // }; }; +// NETGENPlugin_Remesher_2D parameters hypothesis + +class NETGENPLUGIN_EXPORT NETGENPlugin_RemesherHypothesis_2D_i: + public virtual POA_NETGENPlugin::NETGENPlugin_RemesherHypothesis_2D, + public NETGENPlugin_Hypothesis_2D_i +{ + public: + // Constructor + NETGENPlugin_RemesherHypothesis_2D_i( PortableServer::POA_ptr thePOA, + int theStudyId, + ::SMESH_Gen* theGenImpl); + + void SetRidgeAngle( CORBA::Double angle ); + + CORBA::Double GetRidgeAngle(); + + // Get implementation + ::NETGENPlugin_RemesherHypothesis_2D* GetImpl(); + + // Verify whether hypothesis supports given entity type + CORBA::Boolean IsDimSupported( SMESH::Dimension type ); +}; + #endif diff --git a/src/NETGENPlugin/NETGENPlugin_Hypothesis_i.cxx b/src/NETGENPlugin/NETGENPlugin_Hypothesis_i.cxx index d60d666..11461d6 100644 --- a/src/NETGENPlugin/NETGENPlugin_Hypothesis_i.cxx +++ b/src/NETGENPlugin/NETGENPlugin_Hypothesis_i.cxx @@ -307,6 +307,36 @@ CORBA::Double NETGENPlugin_Hypothesis_i::GetNbSegPerRadius() //============================================================================= +void NETGENPlugin_Hypothesis_i::SetChordalErrorEnabled(CORBA::Boolean theValue) +{ + if ( isToSetParameter( GetChordalErrorEnabled(), theValue, METH_SetChordalErrorEnabled )) + { + this->GetImpl()->SetChordalErrorEnabled(theValue); + SMESH::TPythonDump() << _this() << ".SetChordalErrorEnabled( " << theValue << " )"; + } +} + +CORBA::Boolean NETGENPlugin_Hypothesis_i::GetChordalErrorEnabled() +{ + return GetImpl()->GetChordalErrorEnabled(); +} + +void NETGENPlugin_Hypothesis_i::SetChordalError(CORBA::Double theValue) +{ + if ( isToSetParameter( GetChordalError(), theValue, METH_SetChordalError )) + { + this->GetImpl()->SetChordalError(theValue); + SMESH::TPythonDump() << _this() << ".SetChordalError( " << SMESH::TVar(theValue) << " )"; + } +} + +CORBA::Double NETGENPlugin_Hypothesis_i::GetChordalError() +{ + return GetImpl()->GetChordalError(); +} + +//============================================================================= + void NETGENPlugin_Hypothesis_i::SetLocalSizeOnShape(GEOM::GEOM_Object_ptr GeomObj, CORBA::Double localSize) throw (SALOME::SALOME_Exception) diff --git a/src/NETGENPlugin/NETGENPlugin_Hypothesis_i.hxx b/src/NETGENPlugin/NETGENPlugin_Hypothesis_i.hxx index 12ffa7b..e9b908d 100644 --- a/src/NETGENPlugin/NETGENPlugin_Hypothesis_i.hxx +++ b/src/NETGENPlugin/NETGENPlugin_Hypothesis_i.hxx @@ -79,6 +79,11 @@ class NETGENPLUGIN_EXPORT NETGENPlugin_Hypothesis_i: void SetNbSegPerRadius(CORBA::Double theVal); CORBA::Double GetNbSegPerRadius(); + void SetChordalErrorEnabled(CORBA::Boolean value); + CORBA::Boolean GetChordalErrorEnabled(); + void SetChordalError(CORBA::Double value); + CORBA::Double GetChordalError(); + void SetLocalSizeOnShape(GEOM::GEOM_Object_ptr GeomObj, CORBA::Double localSize) throw (SALOME::SALOME_Exception); void SetLocalSizeOnEntry(const char* entry, CORBA::Double localSize); @@ -109,19 +114,21 @@ class NETGENPLUGIN_EXPORT NETGENPlugin_Hypothesis_i: // to remember whether a parameter is already set (issue 0021364) enum SettingMethod { - METH_SetMaxSize = 1, - METH_SetMinSize = 2, - METH_SetSecondOrder = 4, - METH_SetOptimize = 8, - METH_SetFineness = 16, - METH_SetGrowthRate = 32, - METH_SetNbSegPerEdge = 64, - METH_SetNbSegPerRadius = 128, - METH_SetLocalSizeOnEntry = 256, - METH_SetQuadAllowed = METH_SetLocalSizeOnEntry * 2, - METH_SetSurfaceCurvature = METH_SetQuadAllowed * 2, - METH_SetFuseEdges = METH_SetSurfaceCurvature * 2, - METH_LAST = METH_SetFuseEdges + METH_SetMaxSize = 1, + METH_SetMinSize = 2, + METH_SetSecondOrder = 4, + METH_SetOptimize = 8, + METH_SetFineness = 16, + METH_SetGrowthRate = 32, + METH_SetNbSegPerEdge = 64, + METH_SetNbSegPerRadius = 128, + METH_SetLocalSizeOnEntry = 256, + METH_SetQuadAllowed = METH_SetLocalSizeOnEntry * 2, + METH_SetSurfaceCurvature = METH_SetQuadAllowed * 2, + METH_SetFuseEdges = METH_SetSurfaceCurvature * 2, + METH_SetChordalErrorEnabled = METH_SetFuseEdges * 2, + METH_SetChordalError = METH_SetChordalErrorEnabled * 2, + METH_LAST = METH_SetFuseEdges }; int mySetMethodFlags; diff --git a/src/NETGENPlugin/NETGENPlugin_Mesher.cxx b/src/NETGENPlugin/NETGENPlugin_Mesher.cxx index dc3afe7..c343224 100644 --- a/src/NETGENPlugin/NETGENPlugin_Mesher.cxx +++ b/src/NETGENPlugin/NETGENPlugin_Mesher.cxx @@ -52,21 +52,29 @@ #include +#include #include +#include +#include +#include #include #include +#include #include +#include #include #include #include #include #include +#include #include #include #include #include #include #include +#include // Netgen include files #ifndef OCCGEOMETRY @@ -151,6 +159,7 @@ NETGENPlugin_Mesher::NETGENPlugin_Mesher (SMESH_Mesh* mesh, _optimize(true), _fineness(NETGENPlugin_Hypothesis::GetDefaultFineness()), _isViscousLayers2D(false), + _chordalError(-1), // means disabled _ngMesh(NULL), _occgeom(NULL), _curShapeIndex(-1), @@ -293,32 +302,36 @@ void NETGENPlugin_Mesher::SetParameters(const NETGENPlugin_Hypothesis* hyp) _fineness = hyp->GetFineness(); mparams.uselocalh = hyp->GetSurfaceCurvature(); netgen::merge_solids = hyp->GetFuseEdges(); + _chordalError = hyp->GetChordalErrorEnabled() ? hyp->GetChordalError() : -1.; _simpleHyp = NULL; // mesh size file mparams.meshsizefilename= hyp->GetMeshSizeFile().empty() ? 0 : hyp->GetMeshSizeFile().c_str(); - SMESH_Gen_i* smeshGen_i = SMESH_Gen_i::GetSMESHGen(); - CORBA::Object_var anObject = smeshGen_i->GetNS()->Resolve("/myStudyManager"); - SALOMEDS::StudyManager_var aStudyMgr = SALOMEDS::StudyManager::_narrow(anObject); - SALOMEDS::Study_var myStudy = aStudyMgr->GetStudyByID(hyp->GetStudyId()); - if ( !myStudy->_is_nil() ) + const NETGENPlugin_Hypothesis::TLocalSize& localSizes = hyp->GetLocalSizesAndEntries(); + if ( !localSizes.empty() ) { - const NETGENPlugin_Hypothesis::TLocalSize localSizes = hyp->GetLocalSizesAndEntries(); - NETGENPlugin_Hypothesis::TLocalSize::const_iterator it = localSizes.begin(); - for ( ; it != localSizes.end() ; it++) + SMESH_Gen_i* smeshGen_i = SMESH_Gen_i::GetSMESHGen(); + CORBA::Object_var anObject = smeshGen_i->GetNS()->Resolve("/myStudyManager"); + SALOMEDS::StudyManager_var aStudyMgr = SALOMEDS::StudyManager::_narrow(anObject); + SALOMEDS::Study_var myStudy = aStudyMgr->GetStudyByID(hyp->GetStudyId()); + if ( !myStudy->_is_nil() ) { - std::string entry = (*it).first; - double val = (*it).second; - // -- - GEOM::GEOM_Object_var aGeomObj; - SALOMEDS::SObject_var aSObj = myStudy->FindObjectID( entry.c_str() ); - if ( !aSObj->_is_nil() ) { - CORBA::Object_var obj = aSObj->GetObject(); - aGeomObj = GEOM::GEOM_Object::_narrow(obj); - aSObj->UnRegister(); + NETGENPlugin_Hypothesis::TLocalSize::const_iterator it = localSizes.begin(); + for ( ; it != localSizes.end() ; it++) + { + std::string entry = (*it).first; + double val = (*it).second; + // -- + GEOM::GEOM_Object_var aGeomObj; + SALOMEDS::SObject_var aSObj = myStudy->FindObjectID( entry.c_str() ); + if ( !aSObj->_is_nil() ) { + CORBA::Object_var obj = aSObj->GetObject(); + aGeomObj = GEOM::GEOM_Object::_narrow(obj); + aSObj->UnRegister(); + } + TopoDS_Shape S = smeshGen_i->GeomObjectToShape( aGeomObj.in() ); + ::SetLocalSize(S, val); } - TopoDS_Shape S = smeshGen_i->GeomObjectToShape( aGeomObj.in() ); - ::SetLocalSize(S, val); } } } @@ -597,7 +610,8 @@ namespace void setLocalSize(const TopoDS_Edge& edge, double size, - netgen::Mesh& mesh) + netgen::Mesh& mesh, + const bool overrideMinH = true) { if ( size <= std::numeric_limits::min() ) return; @@ -608,7 +622,7 @@ namespace TopoDS_Iterator vIt( edge ); if ( !vIt.More() ) return; gp_Pnt p = BRep_Tool::Pnt( TopoDS::Vertex( vIt.Value() )); - NETGENPlugin_Mesher::RestrictLocalSize( mesh, p.XYZ(), size ); + NETGENPlugin_Mesher::RestrictLocalSize( mesh, p.XYZ(), size, overrideMinH ); } else { @@ -618,15 +632,29 @@ namespace { Standard_Real u = u1 + delta*i; gp_Pnt p = curve->Value(u); - NETGENPlugin_Mesher::RestrictLocalSize( mesh, p.XYZ(), size ); + NETGENPlugin_Mesher::RestrictLocalSize( mesh, p.XYZ(), size, overrideMinH ); netgen::Point3d pi(p.X(), p.Y(), p.Z()); double resultSize = mesh.GetH(pi); if ( resultSize - size > 0.1*size ) // netgen does restriction iff oldH/newH > 1.2 (localh.cpp:136) - NETGENPlugin_Mesher::RestrictLocalSize( mesh, p.XYZ(), resultSize/1.201 ); + NETGENPlugin_Mesher::RestrictLocalSize( mesh, p.XYZ(), resultSize/1.201, overrideMinH ); } } } + + //================================================================================ + /*! + * \brief Return triangle size for a given chordalError and radius of curvature + */ + //================================================================================ + + double elemSizeForChordalError( double chordalError, double radius ) + { + if ( 2 * radius < chordalError ) + return 1.5 * radius; + return Sqrt( 3 ) * Sqrt( chordalError * ( 2 * radius - chordalError )); + } + } // namespace //================================================================================ @@ -636,16 +664,19 @@ namespace //================================================================================ void NETGENPlugin_Mesher::SetLocalSize( netgen::OCCGeometry& occgeo, - netgen::Mesh& ngMesh ) + netgen::Mesh& ngMesh) { - for(std::map::const_iterator it=EdgeId2LocalSize.begin(); it!=EdgeId2LocalSize.end(); it++) + // edges + std::map::const_iterator it; + for( it=EdgeId2LocalSize.begin(); it!=EdgeId2LocalSize.end(); it++) { int key = (*it).first; double hi = (*it).second; const TopoDS_Shape& shape = ShapesWithLocalSize.FindKey(key); setLocalSize( TopoDS::Edge(shape), hi, ngMesh ); } - for(std::map::const_iterator it=VertexId2LocalSize.begin(); it!=VertexId2LocalSize.end(); it++) + // vertices + for(it=VertexId2LocalSize.begin(); it!=VertexId2LocalSize.end(); it++) { int key = (*it).first; double hi = (*it).second; @@ -653,7 +684,8 @@ void NETGENPlugin_Mesher::SetLocalSize( netgen::OCCGeometry& occgeo, gp_Pnt p = BRep_Tool::Pnt( TopoDS::Vertex(shape) ); NETGENPlugin_Mesher::RestrictLocalSize( ngMesh, p.XYZ(), hi ); } - for(map::const_iterator it=FaceId2LocalSize.begin(); it!=FaceId2LocalSize.end(); it++) + // faces + for(it=FaceId2LocalSize.begin(); it!=FaceId2LocalSize.end(); it++) { int key = (*it).first; double val = (*it).second; @@ -671,7 +703,8 @@ void NETGENPlugin_Mesher::SetLocalSize( netgen::OCCGeometry& occgeo, ShapesWithControlPoints.insert( key ); } } - for(map::const_iterator it=SolidId2LocalSize.begin(); it!=SolidId2LocalSize.end(); it++) + //solids + for(it=SolidId2LocalSize.begin(); it!=SolidId2LocalSize.end(); it++) { int key = (*it).first; double val = (*it).second; @@ -688,6 +721,146 @@ void NETGENPlugin_Mesher::SetLocalSize( netgen::OCCGeometry& occgeo, for ( size_t i = 0; i < ControlPoints.size(); ++i ) NETGENPlugin_Mesher::RestrictLocalSize( ngMesh, ControlPoints[i].XYZ(), ControlPoints[i].Size() ); } + return; +} + +//================================================================================ +/*! + * \brief Restrict local size to achieve a required _chordalError + */ +//================================================================================ + +void NETGENPlugin_Mesher::SetLocalSizeForChordalError( netgen::OCCGeometry& occgeo, + netgen::Mesh& ngMesh) +{ + if ( _chordalError <= 0. ) + return; + + TopLoc_Location loc; + BRepLProp_SLProps surfProp( 2, 1e-6 ); + const double sizeCoef = 0.95; + + // find non-planar FACEs with non-constant curvature + std::vector fInd; + for ( int i = 1; i <= occgeo.fmap.Extent(); ++i ) + { + const TopoDS_Face& face = TopoDS::Face( occgeo.fmap( i )); + BRepAdaptor_Surface surfAd( face, false ); + switch ( surfAd.GetType() ) + { + case GeomAbs_Plane: + continue; + case GeomAbs_Cylinder: + case GeomAbs_Sphere: + case GeomAbs_Torus: // constant curvature + { + surfProp.SetSurface( surfAd ); + surfProp.SetParameters( 0, 0 ); + double maxCurv = Max( Abs( surfProp.MaxCurvature()), Abs( surfProp.MinCurvature() )); + double size = elemSizeForChordalError( _chordalError, 1 / maxCurv ); + occgeo.SetFaceMaxH( i, size * sizeCoef ); + // limit size one edges + TopTools_MapOfShape edgeMap; + for ( TopExp_Explorer eExp( face, TopAbs_EDGE ); eExp.More(); eExp.Next() ) + if ( edgeMap.Add( eExp.Current() )) + setLocalSize( TopoDS::Edge( eExp.Current() ), size, ngMesh, /*overrideMinH=*/false ); + break; + } + default: + Handle(Geom_Surface) surf = BRep_Tool::Surface( face, loc ); + if ( GeomLib_IsPlanarSurface( surf ).IsPlanar() ) + continue; + fInd.push_back( i ); + } + } + // set local size + if ( !fInd.empty() ) + { + BRep_Builder b; + TopoDS_Compound allFacesComp; + b.MakeCompound( allFacesComp ); + for ( size_t i = 0; i < fInd.size(); ++i ) + b.Add( allFacesComp, occgeo.fmap( fInd[i] )); + + // copy the shape to avoid spoiling its triangulation + TopoDS_Shape allFacesCompCopy = BRepBuilderAPI_Copy( allFacesComp ); + + // create triangulation with desired chordal error + BRepMesh_IncrementalMesh( allFacesCompCopy, + _chordalError, + /*isRelative = */Standard_False, + /*theAngDeflection = */ 0.5, + /*isInParallel = */Standard_True); + + // loop on FACEs + for ( TopExp_Explorer fExp( allFacesCompCopy, TopAbs_FACE ); fExp.More(); fExp.Next() ) + { + const TopoDS_Face& face = TopoDS::Face( fExp.Current() ); + Handle(Poly_Triangulation) triangulation = BRep_Tool::Triangulation ( face, loc ); + if ( triangulation.IsNull() ) continue; + + BRepAdaptor_Surface surf( face, false ); + surfProp.SetSurface( surf ); + + gp_XY uv[3]; + gp_XYZ p[3]; + double size[3]; + for ( int i = 1; i <= triangulation->NbTriangles(); ++i ) + { + Standard_Integer n1,n2,n3; + triangulation->Triangles()(i).Get( n1,n2,n3 ); + p [0] = triangulation->Nodes()(n1).Transformed(loc).XYZ(); + p [1] = triangulation->Nodes()(n2).Transformed(loc).XYZ(); + p [2] = triangulation->Nodes()(n3).Transformed(loc).XYZ(); + uv[0] = triangulation->UVNodes()(n1).XY(); + uv[1] = triangulation->UVNodes()(n2).XY(); + uv[2] = triangulation->UVNodes()(n3).XY(); + surfProp.SetParameters( uv[0].X(), uv[0].Y() ); + if ( !surfProp.IsCurvatureDefined() ) + break; + + for ( int n = 0; n < 3; ++n ) // get size at triangle nodes + { + surfProp.SetParameters( uv[n].X(), uv[n].Y() ); + double maxCurv = Max( Abs( surfProp.MaxCurvature()), Abs( surfProp.MinCurvature() )); + size[n] = elemSizeForChordalError( _chordalError, 1 / maxCurv ); + } + for ( int n1 = 0; n1 < 3; ++n1 ) // limit size along each triangle edge + { + int n2 = ( n1 + 1 ) % 3; + double minSize = size[n1], maxSize = size[n2]; + if ( size[n1] > size[n2] ) + minSize = size[n2], maxSize = size[n1]; + + if ( maxSize / minSize < 1.2 ) // netgen ignores size difference < 1.2 + { + ngMesh.RestrictLocalHLine ( netgen::Point3d( p[n1].X(), p[n1].Y(), p[n1].Z() ), + netgen::Point3d( p[n2].X(), p[n2].Y(), p[n2].Z() ), + sizeCoef * minSize ); + } + else + { + gp_XY uvVec( uv[n2] - uv[n1] ); + double len = ( p[n1] - p[n2] ).Modulus(); + int nb = int( len / minSize ) + 1; + for ( int j = 0; j <= nb; ++j ) + { + double r = double( j ) / nb; + gp_XY uvj = uv[n1] + r * uvVec; + + surfProp.SetParameters( uvj.X(), uvj.Y() ); + double maxCurv = Max( Abs( surfProp.MaxCurvature()), Abs( surfProp.MinCurvature() )); + double h = elemSizeForChordalError( _chordalError, 1 / maxCurv ); + + const gp_Pnt& pj = surfProp.Value(); + netgen::Point3d ngP( pj.X(), pj.Y(), pj.Z()); + ngMesh.RestrictLocalH( ngP, h * sizeCoef ); + } + } + } + } + } + } } //================================================================================ @@ -2724,6 +2897,7 @@ bool NETGENPlugin_Mesher::Compute() { // Local size on shapes SetLocalSize( occgeo, *_ngMesh ); + SetLocalSizeForChordalError( occgeo, *_ngMesh ); } // Precompute internal edges (issue 0020676) in order to @@ -3290,25 +3464,25 @@ bool NETGENPlugin_Mesher::Evaluate(MapShapeNbElems& aResMap) sm->GetComputeError().reset( new SMESH_ComputeError( COMPERR_ALGO_FAILED )); return false; } - if ( _simpleHyp ) - { - // Pass 1D simple parameters to NETGEN - // -------------------------------- - int nbSeg = _simpleHyp->GetNumberOfSegments(); - double segSize = _simpleHyp->GetLocalLength(); - for ( int iE = 1; iE <= occgeo.emap.Extent(); ++iE ) - { - const TopoDS_Edge& e = TopoDS::Edge( occgeo.emap(iE)); - if ( nbSeg ) - segSize = SMESH_Algo::EdgeLength( e ) / ( nbSeg - 0.4 ); - setLocalSize( e, segSize, *ngMesh ); - } - } - else // if ( ! _simpleHyp ) - { - // Local size on shapes - SetLocalSize( occgeo, *ngMesh ); - } + // if ( _simpleHyp ) + // { + // // Pass 1D simple parameters to NETGEN + // // -------------------------------- + // int nbSeg = _simpleHyp->GetNumberOfSegments(); + // double segSize = _simpleHyp->GetLocalLength(); + // for ( int iE = 1; iE <= occgeo.emap.Extent(); ++iE ) + // { + // const TopoDS_Edge& e = TopoDS::Edge( occgeo.emap(iE)); + // if ( nbSeg ) + // segSize = SMESH_Algo::EdgeLength( e ) / ( nbSeg - 0.4 ); + // setLocalSize( e, segSize, *ngMesh ); + // } + // } + // else // if ( ! _simpleHyp ) + // { + // // Local size on shapes + // SetLocalSize( occgeo, *ngMesh ); + // } // calculate total nb of segments and length of edges double fullLen = 0.0; int fullNbSeg = 0; diff --git a/src/NETGENPlugin/NETGENPlugin_Mesher.hxx b/src/NETGENPlugin/NETGENPlugin_Mesher.hxx index 88e093e..608de2a 100644 --- a/src/NETGENPlugin/NETGENPlugin_Mesher.hxx +++ b/src/NETGENPlugin/NETGENPlugin_Mesher.hxx @@ -124,6 +124,8 @@ class NETGENPLUGIN_EXPORT NETGENPlugin_Mesher void SetParameters(const NETGENPlugin_SimpleHypothesis_2D* hyp); void SetParameters(const StdMeshers_ViscousLayers* hyp ); void SetViscousLayers2DAssigned(bool isAssigned) { _isViscousLayers2D = isAssigned; } + + void SetLocalSizeForChordalError( netgen::OCCGeometry& occgeo, netgen::Mesh& ngMesh ); static void SetLocalSize( netgen::OCCGeometry& occgeo, netgen::Mesh& ngMesh ); bool Compute(); @@ -204,6 +206,7 @@ class NETGENPLUGIN_EXPORT NETGENPlugin_Mesher bool _optimize; int _fineness; bool _isViscousLayers2D; + double _chordalError; netgen::Mesh* _ngMesh; netgen::OCCGeometry* _occgeom; @@ -217,7 +220,7 @@ class NETGENPLUGIN_EXPORT NETGENPlugin_Mesher // a pointer to NETGENPlugin_Mesher* field of the holder, that will be // nullified at destruction of this - NETGENPlugin_Mesher ** _ptrToMe; + NETGENPlugin_Mesher ** _ptrToMe; }; //============================================================================= diff --git a/src/NETGENPlugin/NETGENPlugin_NETGEN_2D_ONLY.cxx b/src/NETGENPlugin/NETGENPlugin_NETGEN_2D_ONLY.cxx index 5cc5607..3e0a93f 100644 --- a/src/NETGENPlugin/NETGENPlugin_NETGEN_2D_ONLY.cxx +++ b/src/NETGENPlugin/NETGENPlugin_NETGEN_2D_ONLY.cxx @@ -319,6 +319,7 @@ bool NETGENPlugin_NETGEN_2D_ONLY::Compute(SMESH_Mesh& aMesh, // set local size defined on shapes aMesher.SetLocalSize( occgeoComm, *ngMeshes[0] ); + aMesher.SetLocalSizeForChordalError( occgeoComm, *ngMeshes[0] ); try { ngMeshes[0]->LoadLocalMeshSize( mparam.meshsizefilename ); } catch (NgException & ex) { @@ -460,6 +461,7 @@ bool NETGENPlugin_NETGEN_2D_ONLY::Compute(SMESH_Mesh& aMesh, bb.Increase (bb.Diam()/10); ngMesh->SetLocalH (bb.PMin(), bb.PMax(), mparam.grading); aMesher.SetLocalSize( occgeom, *ngMesh ); + aMesher.SetLocalSizeForChordalError( occgeoComm, *ngMesh ); try { ngMesh->LoadLocalMeshSize( mparam.meshsizefilename ); } catch (NgException & ex) { diff --git a/src/NETGENPlugin/NETGENPlugin_NETGEN_2D_i.cxx b/src/NETGENPlugin/NETGENPlugin_NETGEN_2D_i.cxx index 96d1f07..be39bd8 100644 --- a/src/NETGENPlugin/NETGENPlugin_NETGEN_2D_i.cxx +++ b/src/NETGENPlugin/NETGENPlugin_NETGEN_2D_i.cxx @@ -27,13 +27,9 @@ // $Header$ // #include "NETGENPlugin_NETGEN_2D_i.hxx" +#include "NETGENPlugin_Remesher_2D.hxx" #include "SMESH_Gen.hxx" -#include "Utils_CorbaException.hxx" -#include "utilities.h" - -using namespace std; - //============================================================================= /*! * NETGENPlugin_NETGEN_2D_i::NETGENPlugin_NETGEN_2D_i @@ -79,3 +75,38 @@ NETGENPlugin_NETGEN_2D_i::~NETGENPlugin_NETGEN_2D_i() { return ( ::NETGENPlugin_NETGEN_2D* )myBaseImpl; } + + + +//============================================================================= +/*! + * NETGENPlugin_Remesher_2D_i::NETGENPlugin_Remesher_2D_i + * + * Constructor + */ +//============================================================================= + +NETGENPlugin_Remesher_2D_i::NETGENPlugin_Remesher_2D_i( PortableServer::POA_ptr thePOA, + int theStudyId, + ::SMESH_Gen* theGenImpl ) + : SALOME::GenericObj_i( thePOA ), + SMESH_Hypothesis_i( thePOA ), + SMESH_Algo_i( thePOA ), + SMESH_2D_Algo_i( thePOA ) +{ + myBaseImpl = new ::NETGENPlugin_Remesher_2D( theGenImpl->GetANewId(), + theStudyId, + theGenImpl ); +} + +//============================================================================= +/*! + * NETGENPlugin_Remesher_2D_i::~NETGENPlugin_Remesher_2D_i + * + * Destructor + */ +//============================================================================= + +NETGENPlugin_Remesher_2D_i::~NETGENPlugin_Remesher_2D_i() +{ +} diff --git a/src/NETGENPlugin/NETGENPlugin_NETGEN_2D_i.hxx b/src/NETGENPlugin/NETGENPlugin_NETGEN_2D_i.hxx index 695ff80..ea9f210 100644 --- a/src/NETGENPlugin/NETGENPlugin_NETGEN_2D_i.hxx +++ b/src/NETGENPlugin/NETGENPlugin_NETGEN_2D_i.hxx @@ -38,7 +38,7 @@ #include "NETGENPlugin_NETGEN_2D.hxx" // ====================================================== -// NETGEN 3d algorithm +// NETGEN 1D2D algorithm // ====================================================== class NETGENPLUGIN_EXPORT NETGENPlugin_NETGEN_2D_i: public virtual POA_NETGENPlugin::NETGENPlugin_NETGEN_2D, @@ -56,4 +56,23 @@ public: ::NETGENPlugin_NETGEN_2D* GetImpl(); }; +// ====================================================== +// NETGEN 2D remesher algorithm +// ====================================================== +class NETGENPLUGIN_EXPORT NETGENPlugin_Remesher_2D_i: + public virtual POA_NETGENPlugin::NETGENPlugin_Remesher_2D, + public virtual SMESH_2D_Algo_i +{ +public: + // Constructor + NETGENPlugin_Remesher_2D_i( PortableServer::POA_ptr thePOA, + int theStudyId, + ::SMESH_Gen* theGenImpl ); + // Destructor + virtual ~NETGENPlugin_Remesher_2D_i(); + + // Get implementation + //::NETGENPlugin_NETGEN_2D* GetImpl(); +}; + #endif diff --git a/src/NETGENPlugin/NETGENPlugin_Remesher_2D.cxx b/src/NETGENPlugin/NETGENPlugin_Remesher_2D.cxx new file mode 100644 index 0000000..dd36c08 --- /dev/null +++ b/src/NETGENPlugin/NETGENPlugin_Remesher_2D.cxx @@ -0,0 +1,685 @@ +// Copyright (C) 2007-2016 CEA/DEN, EDF R&D, OPEN CASCADE +// +// Copyright (C) 2003-2007 OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN, +// CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com +// +// File : NETGENPlugin_Remesher_2D.cxx +// Created : Thu Sep 21 16:48:46 2017 +// Author : Edward AGAPOV (eap) +// + +#include "NETGENPlugin_Remesher_2D.hxx" + +#include "NETGENPlugin_Mesher.hxx" +#include "NETGENPlugin_Hypothesis_2D.hxx" + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +//#include + +#include + +using namespace nglib; + +// namespace netgen +// { +// #if defined(NETGEN_V5) && defined(WIN32) +// DLL_HEADER +// #endif +// extern STLParameters stlparam; +// } + +namespace +{ + //============================================================================= + /*! + * \brief Fill holes in the mesh, since netgen can remesh only a closed shell mesh. + * At destruction, remove triangles filling the holes + */ + class HoleFiller + { + public: + HoleFiller( SMESH_Mesh& meshDS ); + ~HoleFiller(); + void AddHoleBorders( Ng_STL_Geometry * ngStlGeo ); + void KeepHole() { myHole.clear(); } + + private: + SMESHDS_Mesh* myMeshDS; + std::vector< std::vector< gp_XYZ > > myHole; // initial border nodes + std::vector< gp_XYZ > myInHolePos; // position inside each hole + }; + + //================================================================================ + /*! + * \brief Fill holes in the mesh + */ + //================================================================================ + + HoleFiller::HoleFiller( SMESH_Mesh& theMesh ): + myMeshDS( theMesh.GetMeshDS() ) + { + SMESH_MeshEditor editor( &theMesh ); + { + // // merge nodes + // const double tol = Max( 0.1 * netgen::mparam.minh, Precision::Confusion() ); + // TIDSortedNodeSet allNodes; + // SMESH_MeshEditor::TListOfListOfNodes equalNodes; + // editor.FindCoincidentNodes( allNodes, tol, equalNodes, true ); + // editor.MergeNodes( equalNodes, /*noHoles=*/false ); + } + + // find holes + SMESH_MeshAlgos::TFreeBorderVec holes; + bool isManifold = true, isGoodOri = true; + SMESH_MeshAlgos::FindFreeBorders( *myMeshDS, holes, /*closedOnly=*/true, + &isManifold, &isGoodOri ); + + if ( !isManifold ) + { + // set bad faces into a compute error + SMESH_ComputeErrorPtr error = + SMESH_ComputeError::New( COMPERR_BAD_INPUT_MESH, + "Non-manifold mesh. Only manifold mesh can be re-meshed"); + SMESH::Controls::MultiConnection2D fun; + fun.SetMesh( myMeshDS ); + SMDS_ElemIteratorPtr fIt = myMeshDS->elementsIterator( SMDSAbs_Face ); + while ( fIt->more() ) + { + const SMDS_MeshElement* f = fIt->next(); + if ( fun.GetValue( f->GetID() ) > 2 ) + error->myBadElements.push_back( f ); + } + theMesh.GetSubMesh( theMesh.GetShapeToMesh() )->GetComputeError() = error; + + throw SALOME_Exception("Non-manifold mesh. Only manifold mesh can be re-meshed"); + } + + // fill holes + myHole.resize( holes.size() ); + myInHolePos.resize( holes.size() ); + std::vector newFaces; + for ( size_t i = 0; i < holes.size(); ++i ) + { + newFaces.clear(); + SMESH_MeshAlgos::FillHole( holes[i], *myMeshDS, newFaces ); + + // keep data to be able to remove hole filling faces after remeshing + if ( !newFaces.empty() ) + { + myHole[i].resize( holes[i].size() ); + for ( size_t iP = 0; iP < holes[i].size(); ++iP ) + myHole[i][iP] = SMESH_NodeXYZ( holes[i][iP] ); + + myInHolePos[i] = ( SMESH_NodeXYZ( newFaces[0]->GetNode(0)) + + SMESH_NodeXYZ( newFaces[0]->GetNode(1)) + + SMESH_NodeXYZ( newFaces[0]->GetNode(2)) ) / 3.; + // unmark to be able to remove them if meshing is canceled + for ( size_t iF = 0; iF < newFaces.size(); ++iF ) + newFaces[iF]->setIsMarked( false ); + } + } + // fix orientation + if ( !isGoodOri ) + { + SMDS_ElemIteratorPtr fIt = myMeshDS->elementsIterator( SMDSAbs_Face ); + while ( fIt->more() ) + { + const SMDS_MeshElement* f = fIt->next(); + gp_XYZ normal; + if ( SMESH_MeshAlgos::FaceNormal( f, normal )) + { + TIDSortedElemSet allFaces; + editor.Reorient2D( allFaces, normal, f ); + break; + } + } + } + } + //================================================================================ + /*! + * \brief Add hole borders to be kept in a new mesh + */ + //================================================================================ + + void HoleFiller::AddHoleBorders( Ng_STL_Geometry * ngStlGeo ) + { + for ( size_t i = 0; i < myHole.size(); ++i ) + for ( size_t iP = 1; iP < myHole[i].size(); ++iP ) + { + Ng_STL_AddEdge( ngStlGeo, + myHole[i][iP-1].ChangeData(), + myHole[i][iP-0].ChangeData() ); + } + } + //================================================================================ + /*! + * \brief Remove triangles filling the holes + */ + //================================================================================ + + HoleFiller::~HoleFiller() + { + if ( myMeshDS->NbNodes() < 3 ) + return; + + bool hasOrphanNodes = true; + + const double tol = Max( 1e-3 * netgen::mparam.minh, Precision::Confusion() ); + + for ( size_t i = 0; i < myHole.size(); ++i ) + { + std::vector< gp_XYZ >& borderPnt = myHole[i]; + const gp_XYZ& inHolePos = myInHolePos[i]; + if ( borderPnt.empty() ) continue; + borderPnt.pop_back(); // first point repeated at end + + // mark all nodes located on the hole border + + // new nodeSearcher for each hole, otherwise it contains removed nodes for i > 0 + SMESHUtils::Deleter< SMESH_NodeSearcher > nodeSearcher; + if ( hasOrphanNodes ) + { + std::vector< const SMDS_MeshNode* > sharedNodes; + sharedNodes.reserve( myMeshDS->NbNodes() ); + SMDS_NodeIteratorPtr nIt = myMeshDS->nodesIterator(); + while ( nIt->more() ) + { + const SMDS_MeshNode* n = nIt->next(); + if ( n->NbInverseElements() ) + sharedNodes.push_back( n ); + } + hasOrphanNodes = ((int) sharedNodes.size() < myMeshDS->NbNodes() ); + SMDS_ElemIteratorPtr elemIt( new SMDS_NodeVectorElemIterator( sharedNodes.begin(), + sharedNodes.end() )); + nodeSearcher._obj = SMESH_MeshAlgos::GetNodeSearcher( elemIt ); + } + else + { + nodeSearcher._obj = SMESH_MeshAlgos::GetNodeSearcher( *myMeshDS ); + } + + std::vector< const SMDS_MeshElement* > edgesToRemove; + edgesToRemove.reserve( borderPnt.size() ); + + // look for a border point coincident with a node + size_t iP = 0; + SMESH_NodeXYZ bordNode1; + for ( ; iP < borderPnt.size(); ++iP ) + { + bordNode1 = nodeSearcher->FindClosestTo( borderPnt[iP] ); + if (( bordNode1 - borderPnt[iP] ).SquareModulus() < tol * tol ) + break; + } + ++iP; + bordNode1._node->setIsMarked( true ); + + // find the rest nodes located on the hole border + boost::container::flat_set< const SMDS_MeshNode* > checkedNodes; + gp_XYZ p1 = bordNode1; + for ( size_t j = 0; j < borderPnt.size()+1; ++j, iP = ( iP+1 ) % borderPnt.size() ) + { + // among nodes surrounding bordNode1 find one most close to vec12 + gp_XYZ vec12 = borderPnt[iP] - p1; + bool pntReached = false; // last found node is at iP + while ( !pntReached ) + { + const SMDS_MeshNode* bordNode = bordNode1._node; + SMDS_ElemIteratorPtr fIt = bordNode->GetInverseElementIterator( SMDSAbs_Face ); + double minArea = 1e100; + checkedNodes.clear(); + checkedNodes.insert( bordNode ); + while ( fIt->more() ) + { + const SMDS_MeshElement* f = fIt->next(); + for ( int iN = 0, nbN = f->NbNodes(); iN < nbN; ++iN ) + { + const SMDS_MeshNode* n = f->GetNode( iN ); + if ( !checkedNodes.insert( n ).second ) + continue; + SMESH_NodeXYZ pn = n; + gp_XYZ vecPN = pn - bordNode1; + if ( vecPN * vec12 <= 0 ) + continue; + gp_XYZ vec1N = pn - p1; + double a = vec12.CrossSquareMagnitude( vec1N ); + if ( a < minArea ) + { + bordNode = n; + minArea = a; + } + } + if ( minArea < std::numeric_limits::min() ) + break; + } + if ( bordNode == bordNode1._node ) + return; // bug in the loop above + + SMESH_NodeXYZ bordNode2 = bordNode; + gp_XYZ vec1N = bordNode2 - p1; + double u = ( vec12 * vec1N ) / vec12.SquareModulus(); // param [0,1] of bordNode on vec12 + if ( u < 1 + tol ) + { + bordNode->setIsMarked( true ); + //cout << bordNode->GetID() << " "; + + if ( const SMDS_MeshElement* edge = myMeshDS->FindEdge( bordNode1._node, bordNode )) + edgesToRemove.push_back( edge ); + else + edgesToRemove.push_back( myMeshDS->AddEdge( bordNode1._node, bordNode )); + edgesToRemove.back()->setIsMarked( true ); + + if ( minArea > std::numeric_limits::min() && + minArea / vec12.SquareModulus() > tol * tol ) + { + // node is far from the border, move it + gp_XYZ p = p1 + u * vec12; + myMeshDS->MoveNode( bordNode, p.X(), p.Y(), p.Z() ); + } + bordNode1 = bordNode2; + } + //else -- there must be another border point between bordNode1 and bordNode + pntReached = ( u > 1 - tol ); + } + p1 = borderPnt[iP]; + + } + //cout << endl << endl; + + // remove all faces starting from inHolePos + + // get a starting face + std::vector< const SMDS_MeshNode* > nodesToRemove; + std::vector< const SMDS_MeshElement* > facesToRemove; + const SMDS_MeshNode* inHoleNode = nodeSearcher->FindClosestTo( inHolePos ); + if ( inHoleNode && ! inHoleNode->isMarked() ) + { + SMDS_ElemIteratorPtr fIt = inHoleNode->GetInverseElementIterator( SMDSAbs_Face ); + while ( fIt->more() ) + facesToRemove.push_back( fIt->next() ); + } + else + { + SMESHUtils::Deleter< SMESH_ElementSearcher > faceSearcher + ( SMESH_MeshAlgos::GetElementSearcher( *myMeshDS )); + if ( const SMDS_MeshElement* f = faceSearcher->FindClosestTo( inHolePos, SMDSAbs_Face )) + facesToRemove.push_back( f ); + else + continue; + } + for ( size_t iF = 0; iF < facesToRemove.size(); ++iF ) + facesToRemove[iF]->setIsMarked( true ); + + // remove faces and nodes + TIDSortedElemSet elemSet, avoidSet; + const SMDS_MeshElement* e; + while ( !facesToRemove.empty() ) + { + const SMDS_MeshElement* inHoleFace = facesToRemove.back(); + facesToRemove.pop_back(); + + // add adjacent faces into facesToRemove + for ( int iN = 0, nbN = inHoleFace->NbNodes(); iN < nbN; ++iN ) + { + const SMDS_MeshNode* n1 = inHoleFace->GetNode( iN ); + if ( !n1->isMarked() ) + { + SMDS_ElemIteratorPtr eIt = n1->GetInverseElementIterator(); + while ( eIt->more() ) + { + e = eIt->next(); + if ( e->GetType() == SMDSAbs_Face ) + { + if ( !e->isMarked() ) + facesToRemove.push_back( e ); + e->setIsMarked( true ); + } + else if ( e->GetType() == SMDSAbs_Edge ) + { + myMeshDS->RemoveFreeElement( e, 0, /*fromGroups=*/false ); + } + } + if ( n1->NbInverseElements() == 1 ) + nodesToRemove.push_back( n1 ); + } + else + { + const SMDS_MeshNode* n2 = inHoleFace->GetNodeWrap( iN+1 ); + if (( n2->isMarked() ) && + ( !(e = myMeshDS->FindEdge( n1, n2 )) || !e->isMarked() )) // n1-n2 not hole border + { + if ( e ) // remove edge + myMeshDS->RemoveFreeElement( e, 0, /*fromGroups=*/false ); + avoidSet.clear(); + avoidSet.insert( inHoleFace ); + if (( e = SMESH_MeshAlgos::FindFaceInSet( n1, n2, elemSet, avoidSet ))) + { + if ( !e->isMarked() ) + facesToRemove.push_back( e ); + e->setIsMarked( true ); + } + } + } + } + myMeshDS->RemoveFreeElement( inHoleFace, 0, /*fromGroups=*/false ); + + for ( size_t iN = 0; iN < nodesToRemove.size(); ++iN ) + myMeshDS->RemoveFreeNode( nodesToRemove[iN], 0, /*fromGroups=*/false ); + nodesToRemove.clear(); + } + + // remove edges from the hole border + // for ( size_t iE = 0; iE < edgesToRemove.size(); ++iE ) + // myMeshDS->RemoveFreeElement( edgesToRemove[iE], 0, /*fromGroups=*/false ); + + } // loop on holes + + return; + } // ~HoleFiller() + +} // namespace + +//============================================================================= +/*! + * Constructor + */ +//============================================================================= + +NETGENPlugin_Remesher_2D::NETGENPlugin_Remesher_2D(int hypId, int studyId, SMESH_Gen* gen) + : SMESH_2D_Algo(hypId, studyId, gen) +{ + _name = "NETGEN_Remesher_2D"; + _shapeType = (1 << TopAbs_FACE); // 1 bit /shape type + _compatibleHypothesis.push_back("NETGEN_RemesherParameters_2D"); + _requireShape = false; + + _hypothesis = 0; +} + +//============================================================================= +/*! + * Check assigned hypotheses + */ +//============================================================================= + +bool NETGENPlugin_Remesher_2D::CheckHypothesis (SMESH_Mesh& theMesh, + const TopoDS_Shape& theShape, + Hypothesis_Status& theStatus) +{ + _hypothesis = 0; + + // can work with no hypothesis + theStatus = SMESH_Hypothesis::HYP_OK; + + const list& hyps = + GetUsedHypothesis( theMesh, theShape, /*skipAux=*/true ); + + switch ( hyps.size() ) { + case 0: + break; + case 1: + _hypothesis = hyps.front(); + break; + default: + theStatus = SMESH_Hypothesis::HYP_INCOMPATIBLE; + } + + return theStatus == SMESH_Hypothesis::HYP_OK; +} + +//============================================================================= +/*! + * Compute mesh on an input mesh + */ +//============================================================================= + +bool NETGENPlugin_Remesher_2D::Compute(SMESH_Mesh& theMesh, + SMESH_MesherHelper* theHelper) +{ + if ( theMesh.NbFaces() == 0 ) + return !error( COMPERR_WARNING, "No faces in input mesh"); + + NETGENPlugin_Mesher mesher( &theMesh, theMesh.GetShapeToMesh(), /*isVol=*/false); + NETGENPlugin_NetgenLibWrapper ngLib; + netgen::Mesh * ngMesh = (netgen::Mesh*) ngLib._ngMesh; + Ng_STL_Geometry * ngStlGeo = Ng_STL_NewGeometry(); + netgen::STLTopology* stlTopo = (netgen::STLTopology*) ngStlGeo; + netgen::multithread.terminate = 0; + + const NETGENPlugin_RemesherHypothesis_2D* hyp = + dynamic_cast( _hypothesis ); + mesher.SetParameters( hyp );// for holeFiller + + SMESHDS_Mesh* meshDS = theMesh.GetMeshDS(); + HoleFiller holeFiller( theMesh ); + //theHelper->SetIsQuadratic( theMesh.NbFaces( ORDER_QUADRATIC )); + + // fill ngStlGeo with triangles + SMDS_ElemIteratorPtr fIt = meshDS->elementsIterator( SMDSAbs_Face ); + while ( fIt->more() ) + { + const SMDS_MeshElement* f = fIt->next(); + SMESH_NodeXYZ n1 = f->GetNode( 0 ); + SMESH_NodeXYZ n2 = f->GetNode( 1 ); + SMESH_NodeXYZ n3 = f->GetNode( 2 ); + Ng_STL_AddTriangle( ngStlGeo, + n1.ChangeData(), + n2.ChangeData(), + n3.ChangeData() ); + if ( f->NbNodes() > 3 ) + { + n2.Set( f->GetNode( 3 )); + Ng_STL_AddTriangle( ngStlGeo, + n1.ChangeData(), + n3.ChangeData(), + n2.ChangeData()); + } + } + // add edges + holeFiller.AddHoleBorders( ngStlGeo ); + + // init stl DS + Ng_Result ng_res = Ng_STL_InitSTLGeometry( ngStlGeo ); + if ( ng_res != NG_OK ) + { +#ifdef _DEBUG_ + holeFiller.KeepHole(); +#endif + std::string txt = "Error Initialising the STL Geometry"; + if ( !stlTopo->GetStatusText().empty() ) + txt += ". " + stlTopo->GetStatusText(); + return error( COMPERR_BAD_INPUT_MESH, txt ); + } + + Ng_Meshing_Parameters ngParams; + ng_res = Ng_STL_MakeEdges( ngStlGeo, ngLib._ngMesh, &ngParams ); + if ( ng_res != NG_OK ) + return error( "Error in Edge Meshing" ); + + // set parameters + if ( hyp ) + { + ngParams.maxh = hyp->GetMaxSize(); + ngParams.minh = hyp->GetMinSize(); + ngParams.meshsize_filename = (char*) hyp->GetMeshSizeFile().c_str(); + ngParams.quad_dominated = hyp->GetQuadAllowed(); + netgen::stlparam.yangle = hyp->GetRidgeAngle(); + mesher.SetParameters( hyp ); + } + else + { + double diagSize = Dist( stlTopo->GetBoundingBox().PMin(), stlTopo->GetBoundingBox().PMax()); + netgen::mparam.maxh = diagSize / GetGen()->GetBoundaryBoxSegmentation(); + netgen::mparam.minh = netgen::mparam.maxh; + } + + double h = netgen::mparam.maxh; + ngMesh->SetGlobalH( h ); + ngMesh->SetMinimalH( netgen::mparam.minh ); + ngMesh->SetLocalH( stlTopo->GetBoundingBox().PMin() - netgen::Vec3d(h, h, h), + stlTopo->GetBoundingBox().PMax() + netgen::Vec3d(h, h, h), + netgen::mparam.grading ); + ngMesh->LoadLocalMeshSize( ngParams.meshsize_filename ); + + netgen::OCCGeometry occgeo; + mesher.SetLocalSize( occgeo, *ngMesh ); + + // meshing + try + { + ng_res = Ng_STL_GenerateSurfaceMesh( ngStlGeo, ngLib._ngMesh, &ngParams ); + } + catch (netgen::NgException & ex) + { + if ( netgen::multithread.terminate ) + return false; + } + if ( ng_res != NG_OK ) + return error( "Error in Surface Meshing" ); + + int nbN = ngMesh->GetNP(); + int nbE = ngMesh->GetNSeg(); + int nbF = ngMesh->GetNSE(); + if ( nbF == 0 ) + return error( "Error in Surface Meshing" ); + + // remove existing mesh + SMDS_ElemIteratorPtr eIt = meshDS->elementsIterator(); + while ( eIt->more() ) + meshDS->RemoveFreeElement( eIt->next(), /*sm=*/0 ); + SMDS_NodeIteratorPtr nIt = meshDS->nodesIterator(); + while ( nIt->more() ) + meshDS->RemoveFreeNode( nIt->next(), /*sm=*/0 ); + + // retrieve new mesh + + // add nodes + std::vector< const SMDS_MeshNode* > newNodes( nbN+1 ); + for ( int i = 1; i <= nbN; ++i ) + { + const netgen::MeshPoint& p = ngMesh->Point(i); + newNodes[i] = meshDS->AddNode( p(0),p(1),p(2) ); + } + + // add edges + std::vector nodes(4); + for ( int i = 1; i <= nbE; ++i ) + { + const netgen::Segment& seg = ngMesh->LineSegment(i); + nodes.clear(); + for ( int j = 0; j < 2; ++j ) + { + size_t pind = seg.pnums[j]; + if ( pind > 0 && pind < newNodes.size() ) + nodes.push_back( newNodes[ pind ]); + else + break; + } + if ( nodes.size() == 2 && !meshDS->FindEdge( nodes[0], nodes[1] )) + meshDS->AddEdge( nodes[0], nodes[1] ); + } + + // add faces + for ( int i = 1; i <= nbF; ++i ) + { + const netgen::Element2d& elem = ngMesh->SurfaceElement(i); + nodes.clear(); + for ( int j = 1; j <= elem.GetNP(); ++j ) + { + size_t pind = elem.PNum(j); + if ( pind > 0 && pind < newNodes.size() ) + nodes.push_back( newNodes[ pind ]); + else + break; + } + switch( nodes.size() ) + { + case 3: meshDS->AddFace( nodes[0], nodes[1], nodes[2] ); break; + case 4: meshDS->AddFace( nodes[0], nodes[1], nodes[2], nodes[3] ); break; + } + } + + // as we don't assign the new triangles to a shape (the pseudo-shape), + // to avoid their removal at hypothesis modification, + // we mark the shape as always computed to avoid the error messages + // that no elements assigned to the shape + theMesh.GetSubMesh( theHelper->GetSubShape() )->SetIsAlwaysComputed( true ); + + return true; +} + +//============================================================================= +/*! + * Do not compute mesh on geometry + */ +//============================================================================= + +bool NETGENPlugin_Remesher_2D::Compute(SMESH_Mesh& theMesh, + const TopoDS_Shape& theShape) +{ + return false; +} + +//============================================================================= +/*! + * Terminate Compute() + */ +//============================================================================= + +void NETGENPlugin_Remesher_2D::CancelCompute() +{ + SMESH_Algo::CancelCompute(); + netgen::multithread.terminate = 1; +} + +//================================================================================ +/*! + * \brief Return progress of Compute() [0.,1] + */ +//================================================================================ + +double NETGENPlugin_Remesher_2D::GetProgress() const +{ + return netgen::multithread.percent / 100.; +} + +//============================================================================= +/*! + * + */ +//============================================================================= + +bool NETGENPlugin_Remesher_2D::Evaluate(SMESH_Mesh& aMesh, + const TopoDS_Shape& aShape, + MapShapeNbElems& aResMap) +{ + return false; +} diff --git a/src/NETGENPlugin/NETGENPlugin_Remesher_2D.hxx b/src/NETGENPlugin/NETGENPlugin_Remesher_2D.hxx new file mode 100644 index 0000000..be9a161 --- /dev/null +++ b/src/NETGENPlugin/NETGENPlugin_Remesher_2D.hxx @@ -0,0 +1,62 @@ +// Copyright (C) 2007-2016 CEA/DEN, EDF R&D, OPEN CASCADE +// +// Copyright (C) 2003-2007 OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN, +// CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com +// +// File : NETGENPlugin_Remesher_2D.hxx +// Created : Thu Sep 21 16:48:46 2017 +// Author : Edward AGAPOV (eap) + + +#ifndef __NETGENPlugin_Remesher_2D_HXX__ +#define __NETGENPlugin_Remesher_2D_HXX__ + +#include "NETGENPlugin_Defs.hxx" + +#include "SMESH_Algo.hxx" +#include "SMESH_Mesh.hxx" + +class NETGENPLUGIN_EXPORT NETGENPlugin_Remesher_2D: public SMESH_2D_Algo +{ + public: + NETGENPlugin_Remesher_2D(int hypId, int studyId, SMESH_Gen* gen); + + virtual bool CheckHypothesis(SMESH_Mesh& theMesh, + const TopoDS_Shape& theShape, + SMESH_Hypothesis::Hypothesis_Status& theStatus); + + virtual bool Compute(SMESH_Mesh & theMesh, SMESH_MesherHelper* theHelper); + + virtual bool Compute(SMESH_Mesh& theMesh, const TopoDS_Shape& theShape); + + virtual void CancelCompute(); + + virtual double GetProgress() const; + + + virtual bool Evaluate(SMESH_Mesh& theMesh, + const TopoDS_Shape& theShape, + MapShapeNbElems& theResMap); + + protected: + + const SMESHDS_Hypothesis* _hypothesis; +}; + +#endif diff --git a/src/NETGENPlugin/NETGENPlugin_i.cxx b/src/NETGENPlugin/NETGENPlugin_i.cxx index 7cf2c10..a74e633 100644 --- a/src/NETGENPlugin/NETGENPlugin_i.cxx +++ b/src/NETGENPlugin/NETGENPlugin_i.cxx @@ -69,6 +69,8 @@ extern "C" aCreator = new NETGENPlugin_Creator_i; else if (strcmp(aHypName, "NETGEN_2D3D") == 0) aCreator = new NETGENPlugin_Creator_i; + else if (strcmp(aHypName, "NETGEN_Remesher_2D") == 0) + aCreator = new NETGENPlugin_Creator_i; // Hypotheses else if (strcmp(aHypName, "NETGEN_Parameters") == 0) aCreator = new NETGENPlugin_Creator_i; @@ -82,6 +84,8 @@ extern "C" aCreator = new NETGENPlugin_Creator_i; else if (strcmp(aHypName, "NETGEN_SimpleParameters_3D") == 0) aCreator = new NETGENPlugin_Creator_i; + else if (strcmp(aHypName, "NETGEN_RemesherParameters_2D") == 0) + aCreator = new NETGENPlugin_Creator_i; else ; return aCreator; -- 2.39.2