From 9a1a854bc9bdbd694ed8e7255b9e1a1d41c590b8 Mon Sep 17 00:00:00 2001 From: Adnane Belmadiaf Date: Tue, 5 Nov 2024 17:16:54 +0100 Subject: [PATCH] feat(IFCImporter): add vtkIFCImporter This PR adds support for IFC format, the importer allows to merge geometries to reduce the number of actors in the scene. fixes #3156 --- .../docs/gallery/IFCImporterWithIcon.jpg | Bin 0 -> 26778 bytes Documentation/content/examples/index.md | 6 +- .../IO/Geometry/IFCImporter/example/index.js | 49 +++ Sources/IO/Geometry/IFCImporter/index.d.ts | 163 +++++++++ Sources/IO/Geometry/IFCImporter/index.js | 321 ++++++++++++++++++ Sources/IO/Geometry/index.js | 2 + 6 files changed, 539 insertions(+), 2 deletions(-) create mode 100644 Documentation/content/docs/gallery/IFCImporterWithIcon.jpg create mode 100644 Sources/IO/Geometry/IFCImporter/example/index.js create mode 100644 Sources/IO/Geometry/IFCImporter/index.d.ts create mode 100644 Sources/IO/Geometry/IFCImporter/index.js diff --git a/Documentation/content/docs/gallery/IFCImporterWithIcon.jpg b/Documentation/content/docs/gallery/IFCImporterWithIcon.jpg new file mode 100644 index 0000000000000000000000000000000000000000..c5b2d1b2f7e01cced915eb761c25ea3ba980be07 GIT binary patch literal 26778 zcmeFYcU)6T_b(i~VnL81AaDdiFB*D3qJRlCp-DoKZh(OFCU}%09U-ANDItU^y~7ct z_ayWt9YT}d;o^DjeV*sN@9+HXeLwg2xqrQDXYYJ7YqDl$&z?22*IIKveEtJ)AEE?S z0$jRu3Gn*j0G!WU8U{aqZmbE@Py(weUN8WFOKpG)y7~kFK-oDv!ju($d!-Nm?Zywl zrHkV)GBI;{`w#RVUN3U(4gMo9QQ$w!@t-H&GPiItyU6nM;^c6=DEy*MG#8l0>K~Z< z7dHI|midLs`3T?yfB}>N3cuQa@%w`8(*Xd9T>#+9=YOS{#sdHq{s6$k(SN0Jd;tI` z{Q!XS-hZY2>z=$daWwf$+|`TgWlKu{U<(HT+|>sF9`peK6o|ig7ni@O+iw@FM;G<7 zzc{P_wg3yjZvZgB4qyflyg(qp6Tnk|*!dXX`LBNdKcZhk{*MIw`t$jp0IHjp_%9V+ zxkLrHOm*oB)ur?A0G3~m)78sY{zqFczHeQ*cH`!y>zA)mT<{I=0|3{qT)KMo#+~a| zDDK?2;9t6YOx zhlr@eb1hRxG?&;5=a0To6^#SaKb6d!TwK%3F2plkJn5IO{CeE~Nms62zIy%AjSD99 zqMny7U%qzj=3R>Gm#+OaC@x>2y88GU^+mA{93s>DuhXz;nS3bvMhg-aQFsp1c65$v z{7LuXqpu&jxas>eyQx!p31O21C@N-VK65?_xO3&AJgO^H0C_;j=H8Xmf0p|Hga7Cb zTxsW-Tul7}TSh%)W@>K~sj?n0T|md=nO!B+Ca6_#xzNOZbF<-F?Q5zYvV#|elyt1s zl_RRApo^7;i%WYH@*R1~-Q1^DiL$el41#{*w`)&X{yfR~{*Dka7Ps-xz1{ioUm0)A z>#{Fke8yYdn_5)T?lx}7D}R;eEn*eP*jtcpVF4N5w4?VplKpU~6L^Sat#~^5WHV~% z`P-V_{#DBVUPSojr_fjhCjUvFh}S+krgBK-wJx_%8T0D<E;?)ZbEpIaHCsuCPC?zYpPGWTWfeOEv%8Z2)hA{m#gz6T!yHyunK2W!%eysPv z6svqa3OvE2_fw-!&U1y;WZK7)iKV6R(OKg;pomrQ46-9fpg#*?o9!TCBr z)o#fD?!gXaN_Fe7yK%hl@W5>9u<_obnnONp#7CbG+e5F8DQ|B-@nWoXI=+3@6#E|N zT=Z{+P@3t5zc+b5?6@&k5F>Z%=ti%h*{Y0C*SA&c&E8d^<9miD(S+7-^1mOh@KL?m}HbhmwI^bGjm34{u`m;g!Mp167{MUw6Nw+OM71pb#R~8G3_E_i_ zUhj#zFYeKwe)_-E2qpD?DN*)kqLwp6J?n5ic9?!p+^+qpqX&_p9f=`qWzMQ#X7u^#yE${~KP;b!Hc>RUNNkrwV zH~D;e$~sdrGNbcI zh2AUcPDk%u9+dR!Z^>q-e#f2;eYty=d0p!q06hoX0rRaz2=7Gs@&8&7{5#!LS%!U0 z3UXg=!G0xZF_!&~wo-Svsu)j_B#`?C*QLfiffjpFWljjqsE@LPZ*`=U=#qHt97yjr zQ3aTz$dYGsv)`sNLszUc)@wQ<-E>h2T4jNIqCCN;oh+;?R)1EEe|F%|dv`x1a2J#a zgu-*=dXA=PmU^qFK4M~Lhd3ry3oYEbTPp&#yvBMX2_$_X+*kNgK98QVuAXBGqoYBY zK$Y!04=yT6*R@(pMOVX2Q!kCNe_A9ZcjiE)?WnxqUQlG@kJkP-de|T=1<4YpZoL_` zqK1MYfb^CrYP8g$X}!pRDb{X#{cIyCp=^8DDdqB>3ExR*M5P-|5I*J z9=8vbmZeQ)9}PNz7*BWt`uZp5<#$Ke-*DL_j*Q6cP zJk9}+yg3lgp2qq5Od2CUEg#1sr9M&MakQ4^QF;tZ`xItvew}MVyY`RC&7ipU@)>P^ zhbNiw=Hcn+(xF(vl*0mzvQ(DdCm})?PA;6iI_6a2?zBU&8ZriD$V=*&;;$qDV6SgVEtGa_PYrZ97D@q zcHl!5&gD9sd;5newRF{AJp;VeK*J^^Mx=18lr1m@cu1=6;2Qdl+ zV+lr8`ht3B&~uiOWzIu1hsi{;cnWgNbMdToc&!SNWmKMgeKs&)uD@W+H5L&}{64nY zpjxQ)Dyf4R9F^cvVcH4NRf3?tXScNx^6u`eqY(o~FU9UFSCoI-K<)&ml)RbXX2rPM(;!TbjmOp{+?9%%pDgBu(#-tRjW7HSv54` zx?G4t9lsj4Zic9olU`^n+{PK*j!W?PhYI8%BD79yy~3|%{r2y-|3AV1WnB!ov`txV z@j2uMQb0YUB^-+1rD)O<5H02C8+lRnxHT6qKovh z^~Lk5G7Id8ZXbP6V^u+iOQ-c{Z(Cf}ZO3P_(6ZOP#nyK>smogVEU|*un6Xsu?clNY zOo@*$m0P;Yj6u?^KrVOXc9*^_a*^La;?|)&i<8qMR0RT|o_4QvhGs0R5|dwXzgQ&y zwn88wlZO)t(^i78gFv9CkAnbS?zS}RNd>ui3Bq8+-UEURFS&##Pwpg9w4KkRoz@d^ zYlg=)O$voQ=%#z~$UK3OSK7w$dKp}|HobVV(nAj&JkeeJvg1&;CY5z#d2x(7>#sS@ ze(7)Kw6DMBv??_(eb3a$T@+&DMO)iXv!zl#!gqqdwXhaW)VF-W{txe-_ZsWzuAY2y%4H7@ zFb&l^m(nuoI^L7zr-OjrMGHPrn*JaDO~gN-f-)r9{|9|aB3ad$o?b2aIs_UhsYD0@aCvIwZ;9Dzvf_bHS5%k4EqF)(Dj>xP9loGwL zhalp#V9QxVw@$Z54Vxnz>60xDEHGn!yGVg(A@ky~gg@q!B?-?S@eM|5W<0Qgz3k@2 zmyEYE zoVQL&g_?0Q-bGd7`1bR>!rK;I8;@VX-nvs8%7!m++()f^bu3*T%_?~_ATY$wMXHMR zPQ4a(4(RhwOV|<1uG7XDx%BLu1{|!H8RhHWx}h zRd=9Hiu08W>+2%{ihpQ%%rYsw)Vv};Oteg7J;5U@!Qw^w^u^39Q6use`1JqPkN;7^ zH?55>`GJN+&5}Qllg?5nUaaouzKgKt+T?lfvUKISvKQtYKziRq4LLUJ zI^{^aefgil?%in%;WSsAwEB3|b!kOX>bB%eci^C;Pq~V~_WI_a!P$q9B)Snrr*{+eI3}otpx6wZKfMPJg-EZHb?QUBTy zEy3M`@^^sAntk6ft8rdaabXsxJCpEet`@I$YhsvArp`mt7nTbO-h<|%-9h%n$CSmZ z9(N^>4ka)IoGo98HVrR5x_|Lv671^Z!wE(ERxf$6O47&fD;j$HOP$4$4SCZi>B^Uf zzVm2TbUqX*)`2{NCkp0ux(78A+0s-*qM}CKVsy6q2}X-hc@CnX^_ib~ZvLiCM+tdJ zWba`3Ju>7jWqUG?dZl#X7DOr1|0ImiAeDW(vwyH(oausnE1-%d!^Hp6G51FP?Du zgzI*ojTTw4%o^9IHj<*t%mserAJEU=U+g{JO|e$~lsPaqGl!cPQq-Nx8r?xWO{n#a zTdT373CKTshkJ+Q$#@Yp+e5^!OULTt9&89xdZ@O**nwH_Z#Cts(fa}^_FQNOa;JAs zW_r7~?8hNRBs@W?z%7L_<6?bT7*HoN3PF0nqTt0|i~)7m;L;evPYbe#xV_TA{Qta& zPpCDoyL+)xVf4VEd4UWH>L6X@NMXys{Qu^g@cZF6!& z4vQn9-}xgeOXo^DWoK%XJDy@?kxKG2^0tJ7r!(y~oa45MosRY%z?G`}IV;f|>&-#c zE{koUFNDT%ko=cUc83{mrTttDL28Y@5emP_w$8Mep~4WCq)71R&Df)xuo&ebQU;P+ zu5gm7lj&1LQ$v4M9wbTu7e3aYaGyAVF`U=cJVqRX%SyuJWj><2y8{rMCQq{4m*pvDHz!*I7Dx5L`-f zHt%WTlXR{Z7BoP>a-U46+>mwZAD$%}e=21OF(@*5d$&!ud+J3j)CI(9C#4qHt<~N9 zw!6_`TndfKlqeCN`1WIS7n>?XlX_20NX)1v9{7Cz+1SZhDn(1aL4~6F?d~Vde|DP% zkH*Y?<%JnoWK4lL2eVf!%DCGqzNWk|jFU;h+IpL>=14kNqu}E%s2Z0oHoWeA8<@yE zx>phr&{fUcgIVj;cHXMEZ#9|jKs@rOU%~Zut}qt2=*Bj#pmxh&Y|=6oYZ~;g+3pPJ z!}RLp(U-_=~TtJC|L-xam{uzJ_H$;X0r>1D&8B)-A8Cg|&Nmxr6f zU~J5-?-E3$C}Zg7vHW}vB`iZcl}mi6hjoL%;r3373I zt>pE@@>H~$!JC1BU(ur2KF!r&KQ?!(+Khf#xirk8uJcGQn;?BKc>1VhC%%XZeH7Xd zBmAmXVFX%W8{y%ZIQnig-0#~t-g(~8e!H)cnV7J|EYJtZ= z#teciqX?;Y<3rn$9Sh7#|<&~gCS892JU_J5ZI18>qhprVurQ9(*5_M(J-$)bf7CF^> zDm$-7^xV-yR{Lg^;UfoUBOisiATW%HYW4ki!KXEn*@fbdeStAd2n~^R`9O0shGnTR zc>c}OEjVezN9<>%;hg*8L;XtKfUZ4{lJ@!c&nK;!$BG48^zc%1OqTmaIg}fWS@3&c zWHBtTok9^*2rneSIb0Ihf48{Z3TjU=+X&V=8HLs#*fhVd>vk5}5V007(y?(ZrLES| zTi#9h;JxEg07~*wEfZPk=26P}GD%l6INM!rfk|_b5T(e4F$)D_IpZ1180y78##!fcWi=ob`SCHV+mqX`X>)??`#nORb^zyEVq{!ajJ zM)pa4`Xg}{xs#?-#9`TO8Oo~OK)}`o*P73zN zT}+udbtpvfo$jN%%tSd$EYSq7x?mSNY9#N+~8a0NxI zKo6B8D8VO8PU55PKX>X?3!a{5d1A( z{CYBX$X><(I~8lfv#$*o=BALwUZ;>{ql}(id<$jIse@wL8UrcDDG|BoFxu7O6u)I^ zq|v6B`o(N=z+I>`)QBbLfag#J_`^R;d$2YKn2F ztf!pj`MC9f{X?^xe@icAaNSgBUhf_0Pc2~M3&Z1d@`%h6LPSfuv9YzZ>l2>NP9E{o z>QD>qXi$}yBBVkLW%{m)vJ)+c&sUcyW)tP^>Wypb+U@fb zL*%D%YdFHdLfL_Vrt`FA*hS$mDfHe~^j78?u_SMcs=5y$B#273{QktnA}Q&0rMO#P z@d`U-rZ*0ujut>k_(>lbpakB)VAEXVmvv`CFD6<`LTMB!o@bKH+-+H!l|T}%v|XBKJ~2g6s#6;P&nw_7`ICC^FJB) z|78805pIXI{|=Te$lB;m=NZj7Syh%g7M)lSgpJF|F<-4qNc4J#KdO1(Ur5N5MGYC{Z z;yn^cqx=DVq!_!#`sExDeJwLd>kTuNY^8)y2CP*6W4cVxe5}Kv%E*U= z7F~pav5a0$OX+cZKWv18bmotgpVg9_Ev%A<9xJIZ>Aib>(o5<_x^aE*8KMe~NI{>$ zC1+EuRaRQFTWpvGc7_tvJX1BGcWNRtQnJKyrt=}7WozgTdy1sb2JRg2EUHytXDY#X zcqrCURPMUe-Jj97_zwrbo(%J?%8vEvd!d73=rM*ukWUHRyTfd#w9qeACZ~DFQSE)@ zVrT@8%)oC!U$2rPG~i@p*tM)vOk=fUqxhaWsF${3a)#oK@anY>@86BOeJdS&!K3^=26Wg<6DZjKEf5^D*FHfc=V~`J8IOt!Zl!kRbFO4+{<^mEKDV$Vo;=CqSm}+U$SOzzP8pb_N-@0GGnmeUyq7HL~oS0?9nAlwx~nZGDoZJO??n3 zhw|f1_S;!_e&vEC4(_fphQJ+_cGTCVHx`%&CE0DK2sNz0m#^QrZh_w!xKL3#Uz?E8 zPOOQ3#W_~fLEq~$BAzl9?P=Qa$~ihJOxYr;yS00z7j%1kb^GWONg91}4%jm$tLw3D z8s{DRKOLt1@z3G%_u%g=EE!AE>8x`quya7Loj0Mp^>+CFOK-~BUaT8%Lq=~{2kyQzzZW<2KHn@#kOXP6u9>I$sz2xUylFTS{bS$Z`-SFos@>C5B56A(joJsR@P*9cAzZXNBs2KBIlqqn3T zb*L9=7R5~__HK<~{+uys)zw}o?3t%utcc5BKh3GN)3LrAKYPL%j*9e{RgbX>-_nq) zb-g(mObD@K@rynaqII(q!z)Fth)qxQ1jd%*LU6qh5TagnY_`S?(-sr`Nw*$P;4QED zNxnbRX+R%C&QyS#mv;)`i(h0fiJIW=xAFpTEDzoP_s{>aGUy z7ZYgGwZbJ{f*C^tcgP)CEa@4#0c^BJ8DT&*5?)jss@N!!tjsaN7@vELSS=CYUJj!V zZqjZheka5*Vg{t5v&tUds?5sZf-3b85boI8TrTyEH$kwJip8{>V7v<3vos#C6D%U= zBOAYYFSG9u94oy-Y=M>ZVj5@g*hS~4EpadNUiR?5xDf6cXBXWyi^5NdHCu)y^oC?t z(JDt^6a0~NgboS`P1!QwjwnZhdiD95K^qTX`QqFXBn&1A?%9o_PuVXjz<7oI`NM07 zN~`Y4Xnz_E8kM0Yvdg3RbQv^+6f8t?dLvS*bf z(j~+{fyUEQUwt;QKOVChU2ikF30rB`>BZ|yrAJQ{Igx`C#!X!LI=tOFg#g3|?n#1YTB={k zqo`L=oAdW%u-ONwC{3`y8adBUb@TJUM^a;Jqe|1e%>10WiM$F9bkF)By!8f?Lgsdl zRC&G8h~4;9t}B7d(^}iQX6JPTK8LyGXqR1Eg8 zYP$wmM4=Nn)j41bz3_9*-gx@H?V?pGNiKdob@3cfd=7v)UN~ZcPp!sz!v8G%dlm36 z&foeES)WbZKL@bEV^6f4)}zZM4BV=pFL^I`^Hm9X%+wb*%zWU_cuD^Am!zAGV-2>a zY9ryhMQfdOVQ;jj4Pr{G#}@@(ib^^%VVd<1ZoY7NmaEklpq53z0#TJ2`teb1wa|)y zG>U#S+PWKPXBJPaHlxk3>BA||STJIe=EzDgH!X)wU5AcNUyx5rBG_mQvQ(dCSkWyt znML9*3*deVuP{>!qIGy=$f$eVmMoP%GdU||fsq<@dpffvtV_$I6eF$NDDF9o+3uaO zu_|H9Wys83W?3xSwP{JRU@Tq6szG>1R7A#;H|C8pUhKt;wd(G;AqvC9%d>T5s)40z z*D680By!2_4GNr1KcB|I^P*hnLui%`ct^A;d~$^c#XAY*u_p7*;M6nIXqlASjqSeo zru2Oy=0a$JJd@xG%Y012^xPA@M(=`iz(BItS$m<6Db@a(5f$g2Ttr7IWR>;SUd@Hu z)_Lrhv+giZdav+igxS&wey5;mnKB&#U+;c55>w@4`1Q?O-nN07^pC3j591jb8gYnn z+W-#7wJhEOj_S>1xA-gGEFa8?@zEX1 zUh?K(a$Sz;+#PfM#3?x?73WNLf!_^L#(xiuqna7N&zslaM(WCJuy4Yb-39-lMzqq1 zc_|JLh-;lz?44a2NJ+>D3^s^i31>B|$wzX9c}9*;X;i)XN zj}%!w&L{808mL%vcJBp??Je^sDug!sZR%JtC{|{-OO(cth#KN99h*2%UJT-hr(wV5kO&UqS zK&LVpjo7se+>hH`s<9t4uF-11rGmI~C*Zv8(u*Y>E;iLN&e=Ww?u1=X0jX;m&CISo~tZ9|Hcn(ImjBL~GDAQ$e{)tf&)y`B?7Hz~gBNqux$CVBxDg`3P zywCG0OZkAI=|5IadYzPL$Vz$IU$%ycqp8=?u09#;cT~H+Z(_sleGHA{MeRSG5%hrT z<3R1R4cA9KtLUV+6LwS3@7Vpp+g0V&iNhK$lY3Kz@5T3bA3DFf54b*xd_axg>6Eg7 zeEkMnkTmj4zBM=!97tiOknkDX0guApQ}tl_4gqlzMqy#I!p%y85F6Hq&#GD;zUhE$ zISryRGwiQD}$O0g%Hdghx60Z3|GO zv@$e37FnMTxfsL-bMf(9Oj(vni0_uJ33T@u#RMTeuY!BJ^+&j|E*5{L5eTs6XPn8| z`QgXrr*6|k*J+-fUioZq$}0KZbAZKJ=HkS&hY3G$S zUT@%%gxrevzW-5e!(8Oo3ECFL61$@K_?X>IHI%G-N6*wst59$>6?MdolnfbDsi~8c zaN{$AkHx3__v+pAsI~E)&tq1>IIp**viejW74HxA$U`SHedmChV@-n@?@6508I4n% zyccXCcC!tAH(JcNk8jp$0#v=;4f>+H&e+Xj8=R~QCo8gz}h$`0$_8OwIWvwMzvc`{@ zoz5Gd1B9vf%P8rpEk>VBxkoSw7Bnh5`&cHd+7$WZkgw(15Z};shfy4o9(;N3s^UQ2 zr?EDpLjHWmrc(UqMeynYji<1s`nuR`H}_{W3{#unPvPhg3=MWH+pBlouVJTI49va8 zBdM;Yv_7JIY}p$TOmi1H#)o9o5yQdv?X_Kqnr2FghIkGlZH~XIsTP~ps^T+F0hXX^ zd@lE=O9M<`KREf7J`)AL;0Md#LE_Wux-~PM3)2c{B%Efp=&ez?w-a(mNvs2Y4cD)4 zrh&>O&wo(!n{ZrjK;_U5!3qX~vf|?9q{T&~#t}Xu?bmTssu>k5{H~;!ffl&#c0z)R zqMo+H!?lTdZa^ZDBc(^k!0j=K6C16>Y++UGiI>CaV^h*i)HsF#&#PJfH zk~Jv6Hb7C4gt1&^?a^^Xx{0ydNUXBDO?703s|b_sNqLH?7U?!9E*mpeo-kM1ioPye zqRK%u(}`F0O{6-~k&Rtoq6j!*6@RED6{q@QER>6jyK)U(6H+|JOiyeaP29fh`PKPHa24#f@0x|}77BzXqk|)$7 zU*@-GkCwG*XFHk!weX^nQ5?^n-+5$};Pb#WW#@yQO#lt?t&xEtK^r zQ{)hHttf&)y3x}d1tn?|2(IXT!>CnJ72zl2FS3thO-e{wHf4wI><8m4OfRtXrzVlC z{hir*gwoP*VBrTPj1RHi*nBv8;3i+Lq#`vtX7D1OK zqKtYy=3nIGSf|6Z%gP?q#gyl-7g}!6mU-N)S_XoTLox)pySm%=m*)=Squ^x^l2U~i zl#Fa|TSJE7KJYTnjBEXoTA}-mkP&Ebd8a#!JRQXr_Dj9EoVK66KNi{ripkG3=wF1x zeC7M|*U!$5>Q2J@fkKBUX=8$hyj;dtqMT!50;7g4EYv z(l1}CHulkRPCW329=G!AzxKvzWY*odF=1c-vq3NRBueadde>%&QGVbRAhVJ3CWb%4zQD%j*3ZVXJcb)5zT4lZgB+Ls<^%z*T*cGEOPNQ3ToJ( zruZul@3n5Uk2WmoUc{eVSUu*<1dXY-77w-`?w8XyH(~eCRHZ#cJ)m;NM>3IthHgC4 zSyQA#2TtBsYgydrsLk^nfNE%s{z1G)`|P0Sg$u_v7**9XO#uPlXb7~gL)eQmdiI8m z+Y>s*c?}Pu^e-HlQ60MM4>CZ$V$-AN0F7yawH<-42(K5Wzv7h@MKB>Em}TtWh%9TKi{`8Ia37DSC9cpsGLEMW=gAD(xK59eqvSpnT{Y;L8pV z(9-=e1l?fW+&Y%R;1Rl(6ev*h>cpIf%$U)M_AZW z2JSe&WF1R{3S`^Y;4ve2s~%VAHlnFNH-(^@pNtj$hQW%NXNuJ$7tpQT4x-F=eCVm# ztBW!i6Z%3k7>ppi0yZPEVWN5EYvVSrolz*Bw4#UB)VN!5j8YzXw^&> zg>As>?m-7U3)ZQVoE)l43oQ%K)!MXgMV)QuKNfOB@^AA%6747rd5cPr@-`Av{K zv>3TQQo=6QPGtT)&{BO=2m2$mYxafq_a8N2hr2u35qV*}Akc!6MVD=p%2Io79_rf8 z+;Y8*Hq>qFRUtmjxlG7DV^bvolU22G*L*12eE*>q!a04RcBlHNWTIk)YGF{|FiZ;# ziU&#EE2KedW?-9Z8^Jeq)HxL35eb@>=?gD7W4LY^CCzF)_>_n-$_cY}tkKWIB#y>x z@IDS;rTvtDi`1kSkQY;4#u!4e)A4WgJ(^*OWM;q5ZaoR<%?m{}n52_j|rd%`H}#fck+ z6t_YBc^e7Wj+Bc{vPds-^h3jNv|eLlBtNIgEH+VGHxMhI+m1debgs;CetR#bdOoLz_de|BE-a>WHJM~qjS$T% zbfhm=##_6l*%(%S&z;=P?icv}vMRTK(O{rm)6oBF)OYu-hdyRSlRTmfZ^wQ}`2$H0 zYQOq3+FDy@%S6?y086Kz^k5-HwF|RM?UQPGx-6zXU-w0Ea$v9U*uIbc91v*Gjkw`! zJeRnP_9+gisTZa?;!A&Ol+K>?%O0xnPa}x#>>1x~>f0>o%e8;$jQ_>uZ(WO*omG20 z<4Y!sw9U}Gk(5x~*=vtu=0v|6*1Y*@nU&105c+wmTavx;t3~g12^nV(RVBJaM@S|K zm_2SjYh8>KP1vijZ*#2{r`5ahr8Jz)Z}M4D+U%($?$ zgbPnMmp{NAIuWLAp@f+5rzoT<1_{1+$L|&ywqeRG`eWwl+jbF5wrI(Fo=au0egs`1Akk3O+%Ctr5n@Oc&USS z(L-@H9qcW~0*Fksp}kG6*Ggk@T`8*K{pBXg3AyX_6$kEt5~D|bzWP&^d2{>ris{#S zk)&}{T&yg2vd0@_`8?-4+Wh8tYM2rItxR*0d6eu4V-;2C;-{A9W3N{lb`E)y8Kcdo zL<`jJEsMNZpJa#f)sXzs%%jKL^j*e-SkPgKq7`~BtZF%)CMMd23EsTz$(J((r(z*d zByhoyd7GW^ICwQL1#w#i|LnJ37sXL`CU>wphS!nwRJ#_q9%rD?&Od6Ms3yoW##QyG z)mfs8^YeEToNu#9Zg#(eXJs%CUa}k=nuT>u=``p~W=vqp9aYBC=d;5^_U21-bZ!q# z+=N=crETD(Mm~KgDIf^S6v+yjwIjYCo;# z_nq!Lzm1MozF9Rm7;*49rc|3PQ5|GG)M-bM-kToLc$2$rEeoqzyUSjaJ2-$mR&%cY zE)hxtZud2X?~GW#o{S;}sH%VB zReRIr!w#Ow)odKdh1kX9{XmD7y@7?4)Gu>Z^*`}qzKDttr%U$W(M?KUPt>*X0WVW9dHg9_V~VkMh>YxD|rY>mh`fI z8%yvr#uZ0>*sRj#_dP+y;(~<*nmhW`9P-3XzqSG=21;kLt3+dWdG zE}eY|=iFq{5zAh@{v3DNI6x(_A>exsRC}KFvZS((mrmoDsP0_3ley!lS=D?<;`bj( zJcHJ|JSyLCI=MjZI?SR4lFzz*q)snyo>HsyiuEaPZf%#h@Su&SjbL@Vj(`+!aFwg8 zA|94LBoJfd54^|Dp+qWU9Mw0k(D9v`f72r%`K?8FFf^g0q7m@Ab) zdn(!4Y)?t{c%A@KS*{CGLG6Y4BP`H`L79JzE@g^MCg#SYAs)EDyJDRYtk&x4M4>sE zV?M^iAVxN|l=?%OSV`${1Y54-0ExIS(1_y6NLjhQ@$q|L2dPXkt5Kx~7*G+`nv5tm z7Dfa;hi@k){)lNln6%T(V0>EKqcNruakUb7+I94V^I}ISyC$VSPW-r1ra`kbo#UAD zc-?7*{T%RRG@>L$>hLb4YUXF~F?%4|IE|)Z0`jrLD5~PW#A}xBUCl?yB(n0J zm~9(*xE%A;jn$5_B+_M%^=?ov(}xR_yrH%O<-ojDwfw0K#M?R|E8P)vY`CPZKc$2pJcLo4%qDjxFATXgMIF*9&bdji6!H4hG@kH*&qQl^^Iv zCyxE7csQ}@<-R@RRX*iQfm8JrI8OM|uM|;PGjTIs`;VLfqHW)}h^TUmf4j9bY|2p@ zjHxhmk4fI8j!e;D)u0sM@ozFAYk!005DF!r?jDU#(t-9&+bl&_4_I zYPihz9=us!dODnBct^mSSvNSBi$zyBC6n3@>$DKcvG$*19LOygp{57TBATkA`0u((3*-|k5K&+ z0BB0+sfF@zpB|-0N9?i5=x^H}DK+0Mey3AsaWB=iso%gxy#JPK!gPJ|T4M)BF`D}2 zKqb0`rq=v3T9}Pp7i9U0iCzz~Ebe7Ex~JC}4q4=@04A6CsQcXbe6a!osZviP6}`t} zER8TPhkJ#&G1Y#)GOm%2KC;WX3kxbHg-Ke4r`M~q!u!Ib1Ea zI~jBRxzy4~pmFQ%s_~+zKBzIGNtM}mQa+n_^zw9BMSY&{_x|IrcE$2MN@XR>ZI#XW za0#8p1VgGI4Yf(E(!~0hJ!*q1d4K%V(689AJ~ytzbHHi?DPj1($Y1qfvH!ItLd3>S z+J*5cYoD@ukx$gxH`m==*I3FRz1G)G2kjTTr*;l-WT_nEQycK9-KVS&v1UW7dHDsY z2k{8{i#-*klep{GuIK8anFGa{1w=g^&6vX5C0Yelh3z1QNQ|<#prV74U*dcdLWLa! zjkHw=MoNmO%*U!vL9qzBX&wctZi~E$!EU?|qH8s#JJI~Yq`O8Q7*H=^uP3e-%;X5* zqQ>$>u%*O6A8j!($UvmjGFBe~evvULvK>kA)ddUj>hIKLl|I!mlSPV)TK%Z7NR8y< zbE5Y=f%kkOE%dFa^bFT=$66HZx|26e7lcEkKh`0 zyVojG`EB=6%cx>EX~rtCKB*#kWgTWRA@XkE5+_F;aP-{>`Sqw%VC53LZx#alEimkR zJohW2A547=1$?&_qw89QMNq*q25N%!LZB#{^={+ru_ayI5M4~yC{49WPw9%iFmI4^ z9S@I784#77s0Pt~KA{gpe%~;|qyh$mRga*UgQ9E6=?(H%D!<{MSDrW-Q)DI? zqwBvG)pwPIOx0wVma*}R@#luaOchnSiAFFMPDYh%P^(Dg%PH_Iu&u4@*UCiS4XX69 zZy28=QnvfGC^TAvN5EEx7L)ISF4yqT8t;Y>h_AS}N(SomxT`vKunGlgM!oIMC-K7( zqYie&AGm#!7QxAu`-YjyyOFGY3TXdIU|*EnkOr^w&>{N?_dcNO;!TSypSO$gwz);L zYFwUc3WEiQkHVyUo~`DAf#br|!HIAxapOcr#H||reobX)tg_LO5g8bce)-(IjANiO zN?=yYzx$QoyNFmf=xaZkM>u^+u~C>uO&J=|W5JQElvnJ!H3|0EvYYTvvcK2}?e(xq z8u(se(NZ`G*$PJiOOk)8*!U>Udr|dl&bo2cB+bf{gjzA{ud*{~Vjo(r&bN$7)d)B64RA7$8!fW{xiis1tcyFd zq#Rg2HK{GN-kN_50ikk@DK_u?R8lhH{aU)q9oVwKnUfC*fjSJ-GOr~z|kepCgT z&@*(bN$2ivumOLWeVvp4VfN)L_%AX0zBq8!Pg(Ce8fiNuzfhPTHo9cWuvjt^87oH= z9KXle(D0Qnl#EEND{@_VKRQy7axt^N^!B1W$#8lMd#uBTr*0?VI|kTp+lw5HYQ0IC z3z%S2+Wbn6uJ(Nw(?Pmj_wnhbT7MF*6O?pej?mX9TV+po#k?f%7 zx3u3542Jx~JyaU>=-AVc=d`!<0u?3k;8(cIeza)09X>5vi{3~mMmW7}qZ`Z_HBKM8 zYEysPxhB2jc|2%1{+S~d@}=z7;CvAqEj(@lNwe(4!byq)^9nbAhtc?zuHR*8^jxe1-$!Rs4M{?gq@^BT`b@ zaJ{_&x+kN~DIxP{6BLs4h7g)$N^smODz*n!d_azD!E&h6K8QHybUT;HeZ#1WOn4R)t5jw7xVk3HAf3 z3u9)|I1}Mf4t6X2Yfhqp-F<*>QXSJwgR_z{QP@h0lvkoHU{+TT)~7|vqutx)hl%Ha z-nd2Q?Vn#G$ELw!y|m|m2+2eiz2HR!VJF!zF zeWk-dYK6oUFZQ}UAJA|4f&R2s(`vcdg(_aLi19y0L$!6HDzMwdIavLHn9_;85nHH+ zqw%4=m+eo4Ul=J$yDT4(U;a^4pqUj0i(0Tk3+0(Bx#gPnyJ}^p!oXC&ZT+`)zB{Pt zZEctBRs`LkpnwELsi6fF2t|r}(?aN>_oCE5=paql7DOPR1PDDy2@qnWCsKln^eQCO z&^w_?FSalH+4u{M_7&{p4UQ4OwtryQts=S zT1(@-vktTJZOV4tqk4N=kB@aYimr)VBY2A})7p02ui6EsMk3Y)y8b#*o&HOL_P+@z zx^!*#fqE&;UGtJMZwp)#ahH~uJpU>kOG3p0vTX{WHUcerNuqj!A5)Tf8sfDV#DpmU zpC+L46H!=*2O^o4^`BVUrlO5SW~S3MwTUKTaT=u~uPwmm0zJUWYYaARPW0q#?RZrK z;tS?^TvG2FDuIeJ)s=G+aQY2zl&5Wpl}Ou~yNeLbLC#Nmm4@|0OVcqu%T9B_&pch= zv+FKi_bU&|%j*P`sZRrurb$?4g16{fOtiOdc?i&G`Sv@?{-F=Eqn(kec_Qj{wHXri z&PW7jR8VNJy-&A3D?_R6-gs0;BdBU4o-J~ukk=Ywh9+i zA!jiFmEAzjwh+2Xq$Smmdl;}v{@mWMO(<0*c%dU&?jq33G{*}kt12=w5-=`)Nj~tN zt!q6iEbG>b!3OJY@P4iE;?mg5>XD{M8 zS7C%4#sbA1T1*iNTifoZFCqI*MiLT=x8OM}0Ti(RdNAa58M=;lY5W{4CaU*@;5uZH zx)|#r7`EmU^@sw|aJ03~5IsJYD>$1+1ga3w6=$*Q($J72ZXn%igm+$N7Cj8varTmt+W8BTS5G-FI(T@D!2cVY{8iT zu@^gO#NJp{*GF!r6^RS{EXHigMN5c?bg@PtoGQ^WIzI^xoXNtO)f2!*KbcM?(mCJ9 zLfZ?yUp}D?`1WO?M>Z4bf0`Lu)_E28Jr>Lt=(-}aTse!K${UHm zb8a(Guf88Oz9Yi>=PRC0TAYhPh=ec*8qJONJA5!L#uBu&j*`cg~&1m4jDp0KY z!AjXq-#3rS0YORw10_y0OWLd`zRlt2qvf%hC=r=zxb{Ln`1Wy@x-YbYA{;=HeW(B# zsg2$&<+yj~U3>rFLesrwf&Kh1HS`8AJFN#i7w$M*iOcLD18y$HI{#!kYTVvVa&}8R z3J-!D!zv~j^~)=e?|KYCV&_c28z&}a@;ab%-CVTMjMe*|;8M1J2IXCh$rv_WxjDLQ zW+Vs@@{@_~kAEgZF?*TWmVWa~Fh@wh;3{HU(KC*v!qn*xTD~oBu>H!9i*q5-@rYHa zmXSQk3DfZg)$p%u7mn-rTF`-GE2;Xcn~gL~Tf^6 zV&yxC1#UpXYh&h~;7=wQ9#R}9ARQc-XiK}9`t@XY*6pkSs6b2uD)tAbX&RbUSo?{f z%xn9032_~9F{~@PHOZ3pFr27K(m!vRaN-xUgt*Wrvg3rM0}F`uJwuv7d6*cJrY+;W zpG@O1xLY0Htk%`%3_{O^{ZEP$OjliXUvgx)c0bA-9M{MVKVdIG+aN^JmDb;iiy>GA zb>04;rP>eTCv=6!fvfL0ZOm_V#F!^3kx?CQf@=e1%A;R}p=4U_Wsp+NTP# z8Lz|f%hHyUX?x@0hCEa?LWt?1{fpY{$2Rg1RLQq9e^whD5DQI%1m6);Ve6%nU_4h8-1B=_cm-u_k$JUdQ-E&@o-QU(c(RH*l%5Bl%7SF(GR0 z9#mM;vUuBwV2khxrW>&ZDRqIDRm?&9?{UXUBsVV=vb`5PBYEb;=3dV#{qg{tMk%St z$bQGMC=_4^)pS>AB1RUUIOM1e8*C!IN3bHIlk_I|C*aO_Ipr2qqw}=OOKcNWZI7ht zxZ4QH38}7=c^{J(G~{Oh2#R-BTS?PE+*yO#X)xZx8onNZe_ptqw=pSjJ|7wsWT%HW zN`=(FnJ_fUJay(294gTz@g)Qwd8iL|`2H&C#%i{=s7mar#p+vH3zn_{G^Qq^5@|NlB}GUlS762OR}P$i@d38&K7XRB2Avy9a}!_J zhS=!rrlvy0h*Kjz^bF==lIQplp{@87`fw^^aAFw#h@F;Xn`DOEqkN|LLKTwF!jZLR z6C&OAVyfYLc0Tj%5MHVnwry}Ilg>C_nv6KHfUs<(_eHy>=mtGCa@d9H({ z``xaTvAQt-B~DFr=H91>7WJ2bE|-&RY;gMegB(K- zE%EFlgwm%nM7tZ8t39Jt(`v4*f6hUq#s}3wbvCm3ECOw*=D0pN0jw-(!taBtrtN{G z=^|b4_DF!;#;)J&^hoGDS@ACDO0h^%iy7`kaDl@yNC&Gx7x6ZyA}frY7NMfq2$0nk z=T?!bw}208(P^fyidt-oA^ro6(NP=Uflqv1-ZYke08evW)3@+e_AW+`&SHCsn`5M0 zOn$CFLh*_S7k~rt9;Ao1KVj6!<~q3a(mQrItq~Gk9t*rFyslqaDm{AgGb#}-l?ici zKmrV}&S6E);6XJmR4(A>u`ngb$EUP*`|am`psxc4zJRzVoZm{L005=rpG>(PN)E6B+C!{+~IBIBMlb54HWYjJ8 z@_KTcr^BKq=ab-Z8{xT^J9zj9rLct9|Zkht% z!!PW4Qa5ZRm&gn#RjtH+Mq6}78Rq$TkPMWqKQ#Qi#@^qtsZamcO9NJ4BJUS4BSbfA z%6PD%UX`Wd2glQwKY2e$jIFlsx(K`LiuKJp#-G*_pVN^csJQrg58KZF^s7r zH#wRPlb&YR&2YlOdE667*C{e_*!NP%(cZQ8`WkdMN!Cy{I6}K}zF~2K$52n8_$0`{ zOKac!Xe(7G*?LM=u0zeN9$FFD-tMTEW*;;)r&E07H@wHEf1Nbs-CY`8%C&YU-PoC& zEn#B;_%R&mwsfFq<~viN={le!EDGj4ixp5s9xdS@PX%GFRRw1CWRZ*XadA78z1Bz;`O`#wPXouFhkVQdpT{4QW_n9{=9Pz2xBR=8;QNueCpasq%HY zg$#?Cu1j1$?7AzyMUnN%B>097koCn!s(F?C)ZJtWxHK$%o(3)D5%F%<_g%QpC185@ z^FyneF1%{qo1yvm$<=vEPRaIAQbo|$Ic4=hKth{Glj0F6=fo;>IYzw&z0J2OGe$U#jO-I}0bW46RQ z(nayi^Unb7xY|4Q1iIaPZ_r zjd(5Uu26KHx2%a*E#G32o-${Sf47``V}Wa(QvKToe>^h^4j1YUu=w@Yf(`w_*3f4S zzU7-2_1(T=&Zg@Un`DpScVzO~psHtM;8{5cY5M+GNi%F=>|m*CdeqPe}s*%4RGe zR{8%eIkRu8DUEMcf^-f9%0ZS|+>X?xrv2b6MjatDgLdK9wX47SDqy!5e3Yr_7Tuao zi`lXvM(w`%YIY8EIoq}>(t?y~nN~QQW2IF~Du2kko5ML%&ar!sy`Tk*(LaCMxt3gg z@8HF*PSo?B4=&SfVtdf{^Yu4D>XNSyIm*&T>&>O;@Gkqb@~G2iLLE|B3M4oAsG6Q& zlt*&!GOy=q6aeJ}1+LaL^rk;-mh7F_6n`%R!L!QZCqkIfb1rbDtAH9;_@YOauFIbA zAus<=CMkxBpC;&rMWjuO{pnzIymSrNQHA1gX>?#5;{bQe^1R`o;{!bcb*KoNt>5NM zmFt%?2sJlHz~HQ6z2v4`Q3Rp!TD5A>6!PvONNi7 zGeEC9D=ppAQy5nl);?2k>n9WP!}*q_THt{k2kPw-#8ya-PTm=FwV+$$8nT!u(6+) z>38{0M&$pi_7m*nk(QD#{n1I>DQOSR9da0X|E^%5L%aV% zE9~T9&Hf9_dGyY4iU2K1SQZt4PeQeqzg26PGr?YMyj%C z_?A46`&rq+&&OP%O(~6<+!5<$WJTmhCcij+-9lL z1iM-rqP+?KW3QB-BJHR$f0|F=0fvT;u{Uc?em`I3oL$fRBdH6U9jIlaxH|O8>-5qK zNRObnbHK&fb|)SEQIF$!SNtE_q^uuzefY*=T)ciV&3kL~V#T%R6)5{&5)8Tv2c$vJ z!u(B8w;{9J6SeRV8vZ>l*OWa1ETB znyX%_W`;Htvn^+8Ex4{W$;+X|&=a&@tAM6c#wg$gi$ji~uR1tGTyJG?-?2q$20%@A znmGByu#s8r>_Rda__D=qYChzXR_SkOEZWrC`tVNLV$s#foNE1@n#3`*j{*wOC{mZ# z-b%si8?9lMZHeFcAM^TxRvy6SnDE$CO@axF|SxtyQ-IJwxK_K_Q=D6UZ*eyFEY(jd)Ho*oQ1-m404{Kf@0fk`}h{0 zagq#3Br{Cmwe~zEl32@~)UJ1JqN5j!BNiuW5Co#{Po^a7JNN(mBjobh(8uAzX1kda zJB0jF}pMb28U_U6-n5!N$N-z1;Q~Ql> zid=eRIdo60;pHKw=(yl-%_&`<77PA;ZqxI?-Xeqx?>CYsQ`Aze!&>OcuKqC8uNXVV*cUpji19 zRGkrego&Q`f@JtUdcS0IF@m^Ovnrn4^!XpR#Xq*N>-ILhJR?CByDx-uOusv5Lr(6{$_D&b#wikjV?f2XSab`miy8{?I9@bL8F7=PR9 zc9zTg5cZEfBRi>I4gB!$KRbRnlz~L`uzf8HWp^YY^CuoWa`^G&@O)K-%CfcJlxAh% zL6hqb3g6$xRZAS4k^K}+zwtDKumC*FoUw{|O@6%Ln0$ifJ2Kh0PWI!ySKEHt`2F0* zrThC2dn;!e;+&V(!@9ngC=So8$~Z^<-LLa9cJ0QScbeZabYdcZ9V6e^lTzF&7*qZa zZ~upLJ8(r#y^5rlByrh%l@co^MHO$qowVCnYiigiNy?e-ToQ*)8+Pp=NF!(C{`|)% z`hTZIzwBM}+Yi6O0v { + importer.importActors(renderer); + resetCamera(); + render(); + }); +} + +// ---------------------------------------------------------------------------- +// Importer usage example +// ---------------------------------------------------------------------------- +vtkResourceLoader + .loadScript('https://cdn.jsdelivr.net/npm/web-ifc@0.0.64/web-ifc-api-iife.js') + .then(() => { + // Pass WebIFC api to vtkIFCImporter + vtkIFCImporter.setIFCAPI(window.WebIFC); + + // Trigger data download + importer + .setUrl( + 'https://raw.githubusercontent.com/ThatOpen/engine_web-ifc/refs/heads/main/tests/ifcfiles/public/duplex.ifc' + ) + .then(update); + }); diff --git a/Sources/IO/Geometry/IFCImporter/index.d.ts b/Sources/IO/Geometry/IFCImporter/index.d.ts new file mode 100644 index 00000000000..b43841527c0 --- /dev/null +++ b/Sources/IO/Geometry/IFCImporter/index.d.ts @@ -0,0 +1,163 @@ +import { vtkAlgorithm, vtkObject } from '../../../interfaces'; +import vtkRenderer from '../../../Rendering/Core/Renderer'; +import HtmlDataAccessHelper from '../../Core/DataAccessHelper/HtmlDataAccessHelper'; +import HttpDataAccessHelper from '../../Core/DataAccessHelper/HttpDataAccessHelper'; +import JSZipDataAccessHelper from '../../Core/DataAccessHelper/JSZipDataAccessHelper'; +import LiteHttpDataAccessHelper from '../../Core/DataAccessHelper/LiteHttpDataAccessHelper'; + +interface IIFCImporterOptions { + compression?: string; + progressCallback?: any; +} + +/** + * + */ +export interface IIFCImporterInitialValues { + mergeGeometries?: boolean; +} + +type vtkIFCImporterBase = vtkObject & + Omit< + vtkAlgorithm, + | 'getInputData' + | 'setInputData' + | 'setInputConnection' + | 'getInputConnection' + | 'addInputConnection' + | 'addInputData' + >; + +export interface vtkIFCImporter extends vtkIFCImporterBase { + /** + * + */ + getBaseURL(): string; + + /** + * + */ + getDataAccessHelper(): + | HtmlDataAccessHelper + | HttpDataAccessHelper + | JSZipDataAccessHelper + | LiteHttpDataAccessHelper; + + /** + * Get the url of the object to load. + */ + getUrl(): string; + + /** + * Import actors into the renderer. + * @param {vtkRenderer} renderer The vtkRenderer to import the actors into. + */ + importActors(renderer: vtkRenderer): void; + + /** + * Load the object data. + * @param {IIFCImporterOptions} [options] + */ + loadData(options?: IIFCImporterOptions): Promise; + + /** + * Parse data. + * @param {String | ArrayBuffer} content The content to parse. + */ + parse(content: string | ArrayBuffer): void; + + /** + * Parse data as ArrayBuffer. + * @param {ArrayBuffer} content The content to parse. + */ + parseAsArrayBuffer(content: ArrayBuffer): void; + + /** + * + * @param inData + * @param outData + */ + requestData(inData: any, outData: any): void; + + /** + * + * @param dataAccessHelper + */ + setDataAccessHelper( + dataAccessHelper: + | HtmlDataAccessHelper + | HttpDataAccessHelper + | JSZipDataAccessHelper + | LiteHttpDataAccessHelper + ): boolean; + + /** + * Set the url of the object to load. + * @param {String} url the url of the object to load. + * @param {IIFCImporterOptions} [option] The PLY reader options. + */ + setUrl(url: string, option?: IIFCImporterOptions): Promise; +} + +/** + * Set WebIFC api to be used by vtkIFCImporter + * @param {object} ifcApi + */ +export function setIFCAPI(ifcApi: any): void; + +/** + * Method used to decorate a given object (publicAPI+model) with vtkIFCImporter characteristics. + * + * @param publicAPI object on which methods will be bounds (public) + * @param model object on which data structure will be bounds (protected) + * @param {IIFCImporterInitialValues} [initialValues] (default: {}) + */ +export function extend( + publicAPI: object, + model: object, + initialValues?: IIFCImporterInitialValues +): void; + +/** + * Method used to create a new instance of vtkIFCImporter + * @param {IIFCImporterInitialValues} [initialValues] for pre-setting some of its content + */ +export function newInstance( + initialValues?: IIFCImporterInitialValues +): vtkIFCImporter; + +/** + * vtkIFCImporter is a source object that reads Industry Foundation Class(IFC) files. + * + * The vtkIFCImporter is using web-ifc library to parse the IFC file. + * + * @example + * ```js + * import vtkResourceLoader from '@kitware/vtk.js/IO/Core/ResourceLoader'; + * import vtkIFCImporter from '@kitware/vtk.js/IO/Geometry/IFCImporter'; + * + * function update() { + * importer.onReady(() => { + * importer.importActors(renderer); + * renderer.resetCamera(); + * renderWindow.render(); + * }); + * } + * + * vtkResourceLoader + * .loadScript('https://cdn.jsdelivr.net/npm/web-ifc@0.0.55/web-ifc-api-iife.js') + * .then(() => { + * // Pass WebIFC api to vtkIFCImporter + * vtkIFCImporter.setIFCAPI(window.WebIFC); + * + * // Trigger data download + * importer.setUrl(`${__BASE_PATH__}/data/ifc/house.ifc`).then(update); + * }); + * ``` + */ +export declare const vtkIFCImporter: { + newInstance: typeof newInstance; + extend: typeof extend; + setIFCAPI: typeof setIFCAPI; +}; +export default vtkIFCImporter; diff --git a/Sources/IO/Geometry/IFCImporter/index.js b/Sources/IO/Geometry/IFCImporter/index.js new file mode 100644 index 00000000000..b4969c198f0 --- /dev/null +++ b/Sources/IO/Geometry/IFCImporter/index.js @@ -0,0 +1,321 @@ +import macro from 'vtk.js/Sources/macros'; + +// Enable data soure for DataAccessHelper +import 'vtk.js/Sources/IO/Core/DataAccessHelper/LiteHttpDataAccessHelper'; // Just need HTTP +// import 'vtk.js/Sources/IO/Core/DataAccessHelper/HttpDataAccessHelper'; // HTTP + zip +// import 'vtk.js/Sources/IO/Core/DataAccessHelper/HtmlDataAccessHelper'; // html + base64 + zip +// import 'vtk.js/Sources/IO/Core/DataAccessHelper/JSZipDataAccessHelper'; // zip + +import DataAccessHelper from 'vtk.js/Sources/IO/Core/DataAccessHelper'; +import vtkActor from 'vtk.js/Sources/Rendering/Core/Actor'; +import vtkMapper from 'vtk.js/Sources/Rendering/Core/Mapper'; +import vtkDataArray from 'vtk.js/Sources/Common/Core/DataArray'; +import vtkPolyData from 'vtk.js/Sources/Common/DataModel/PolyData'; +import vtkCellArray from 'vtk.js/Sources/Common/Core/CellArray'; +import vtkAppendPolyData from 'vtk.js/Sources/Filters/General/AppendPolyData'; +import vtkMatrixBuilder from 'vtk.js/Sources/Common/Core/MatrixBuilder'; + +const { vtkErrorMacro } = macro; + +let WebIFC; + +/** + * Set WebIFC api to be used by vtkIFCImporter + * @param {object} ifcApi + */ +function setIFCAPI(ifcApi) { + WebIFC = ifcApi; +} + +function vtkIFCImporter(publicAPI, model) { + model.classHierarchy.push('vtkIFCImporter'); + const meshes = []; + + /** + * Create a vtkPolyData from an IFC mesh object + * @param {object} mesh the IFC web mesh object + * @returns vtkPolyData + */ + function createPolyDataFromIFCMesh(mesh) { + const { vertices, indices } = mesh; + const pd = vtkPolyData.newInstance(); + const cells = vtkCellArray.newInstance(); + + const pointValues = new Float32Array(vertices.length / 2); + const normalsArray = new Float32Array(vertices.length / 2); + + for (let i = 0; i < vertices.length; i += 6) { + pointValues[i / 2] = vertices[i]; + pointValues[i / 2 + 1] = vertices[i + 1]; + pointValues[i / 2 + 2] = vertices[i + 2]; + + normalsArray[i / 2] = vertices[i + 3]; + normalsArray[i / 2 + 1] = vertices[i + 4]; + normalsArray[i / 2 + 2] = vertices[i + 5]; + } + + const nCells = indices.length; + cells.resize((3 * nCells) / 3); + for (let cellId = 0; cellId < nCells; cellId += 3) { + const cell = indices.slice(cellId, cellId + 3); + cells.insertNextCell(cell); + } + + pd.getPoints().setData(pointValues, 3); + pd.setStrips(cells); + pd.getPointData().setNormals( + vtkDataArray.newInstance({ + name: 'Normals', + values: normalsArray, + numberOfComponents: 3, + }) + ); + + return pd; + } + + /** + * Create a colored vtkPolyData from an IFC mesh object + * @param {object} mesh the IFC mesh object + * @returns vtkPolyData + */ + function createColoredPolyDataFromIFCMesh(mesh) { + const { vertices, indices, color, userMatrix } = mesh; + + const pd = vtkPolyData.newInstance(); + const cells = vtkCellArray.newInstance(); + + const pointValues = new Float32Array(vertices.length / 2); + const normalsArray = new Float32Array(vertices.length / 2); + const colorArray = new Float32Array(vertices.length / 2); + + if (userMatrix) { + const transformMatrix = vtkMatrixBuilder + .buildFromRadian() + .setMatrix(userMatrix); + + for (let i = 0; i < vertices.length; i += 6) { + const point = [vertices[i], vertices[i + 1], vertices[i + 2]]; + const normal = [vertices[i + 3], vertices[i + 4], vertices[i + 5]]; + + transformMatrix.apply(point).apply(normal); + + pointValues[i / 2] = point[0]; + pointValues[i / 2 + 1] = point[1]; + pointValues[i / 2 + 2] = point[2]; + + normalsArray[i / 2] = normal[0]; + normalsArray[i / 2 + 1] = normal[1]; + normalsArray[i / 2 + 2] = normal[2]; + + const colorIndex = i / 2; + colorArray[colorIndex] = color.x; + colorArray[colorIndex + 1] = color.y; + colorArray[colorIndex + 2] = color.z; + } + } else { + for (let i = 0; i < vertices.length; i += 6) { + pointValues[i / 2] = vertices[i]; + pointValues[i / 2 + 1] = vertices[i + 1]; + pointValues[i / 2 + 2] = vertices[i + 2]; + + normalsArray[i / 2] = vertices[i + 3]; + normalsArray[i / 2 + 1] = vertices[i + 4]; + normalsArray[i / 2 + 2] = vertices[i + 5]; + + const colorIndex = i / 2; + colorArray[colorIndex] = color.x; + colorArray[colorIndex + 1] = color.y; + colorArray[colorIndex + 2] = color.z; + } + } + + const nCells = indices.length; + cells.resize((3 * nCells) / 3); + for (let cellId = 0; cellId < nCells; cellId += 3) { + const cell = indices.slice(cellId, cellId + 3); + cells.insertNextCell(cell); + } + + pd.getPoints().setData(pointValues, 3); + pd.setPolys(cells); + pd.getPointData().setNormals( + vtkDataArray.newInstance({ + name: 'Normals', + values: normalsArray, + numberOfComponents: 3, + }) + ); + + pd.getPointData().setScalars( + vtkDataArray.newInstance({ + name: 'Colors', + values: colorArray, + numberOfComponents: 3, + }) + ); + + return pd; + } + + function parseIfc(content) { + const modelID = model._ifcApi.OpenModel(new Uint8Array(content), { + COORDINATE_TO_ORIGIN: true, + USE_FAST_BOOLS: true, + }); + + model._ifcApi.StreamAllMeshes(modelID, (mesh) => { + const placedGeometries = mesh.geometries; + + for (let i = 0; i < placedGeometries.size(); i++) { + const placedGeometry = placedGeometries.get(i); + + const ifcGeometryData = model._ifcApi.GetGeometry( + modelID, + placedGeometry.geometryExpressID + ); + + const ifcVertices = model._ifcApi.GetVertexArray( + ifcGeometryData.GetVertexData(), + ifcGeometryData.GetVertexDataSize() + ); + + const ifcIndices = model._ifcApi.GetIndexArray( + ifcGeometryData.GetIndexData(), + ifcGeometryData.GetIndexDataSize() + ); + + meshes.push({ + vertices: ifcVertices, + indices: ifcIndices, + color: placedGeometry.color, + userMatrix: placedGeometry.flatTransformation, + }); + } + }); + + model._ifcApi.CloseModel(modelID); + } + + if (!model.dataAccessHelper) { + model.dataAccessHelper = DataAccessHelper.get('http'); + } + + function fetchData(url, options = {}) { + const { compression, progressCallback } = model; + return model.dataAccessHelper.fetchBinary(url, { + compression, + progressCallback, + }); + } + + publicAPI.setUrl = (url, options = { binary: true }) => { + model.url = url; + model.baseURL = url.split('/').slice(0, -1).join('/'); + model.compression = options.compression; + return publicAPI.loadData(options); + }; + + publicAPI.loadData = (options = {}) => + fetchData(model.url, options).then(publicAPI.parse); + + publicAPI.parse = (content) => { + publicAPI.parseAsArrayBuffer(content); + }; + + publicAPI.parseAsArrayBuffer = (content) => { + if (!content) { + vtkErrorMacro('No content to parse.'); + return; + } + + if (!WebIFC) { + vtkErrorMacro('vtkIFCImporter requires WebIFC API to be set.'); + return; + } + + model._ifcApi = new WebIFC.IfcAPI(); + model._ifcApi.Init().then(() => { + parseIfc(content); + publicAPI.invokeReady(); + }); + }; + + publicAPI.importActors = (renderer) => { + if (model.mergeGeometries) { + const opaqueMeshes = meshes.filter((mesh) => mesh.color.w === 1); + let apd = vtkAppendPolyData.newInstance(); + + opaqueMeshes.forEach((mesh) => { + const pd = createColoredPolyDataFromIFCMesh(mesh); + apd.addInputData(pd); + }); + + let mapper = vtkMapper.newInstance(); + mapper.setColorModeToDirectScalars(); + mapper.setInputConnection(apd.getOutputPort()); + + let actor = vtkActor.newInstance(); + actor.setMapper(mapper); + renderer.addActor(actor); + + const transparentMeshes = meshes.filter((mesh) => mesh.color.w < 1); + apd = vtkAppendPolyData.newInstance(); + + transparentMeshes.forEach((mesh) => { + const pd = createColoredPolyDataFromIFCMesh(mesh); + apd.addInputData(pd); + }); + + mapper = vtkMapper.newInstance(); + mapper.setColorModeToDirectScalars(); + mapper.setInputConnection(apd.getOutputPort()); + + actor = vtkActor.newInstance(); + actor.setMapper(mapper); + actor.getProperty().setOpacity(0.5); + renderer.addActor(actor); + } else { + meshes.forEach((mesh) => { + const pd = createPolyDataFromIFCMesh(mesh); + + const mapper = vtkMapper.newInstance(); + mapper.setInputData(pd); + + const actor = vtkActor.newInstance(); + actor.setMapper(mapper); + + const { x, y, z, w } = mesh.color; + actor.getProperty().setColor(x, y, z); + actor.getProperty().setOpacity(w); + + actor.setUserMatrix(mesh.userMatrix); + renderer.addActor(actor); + }); + } + }; +} + +const DEFAULT_VALUES = { + mergeGeometries: false, +}; + +export function extend(publicAPI, model, initialValues = {}) { + Object.assign(model, DEFAULT_VALUES, initialValues); + + macro.obj(publicAPI, model); + macro.get(publicAPI, model, ['url', 'baseURL']); + macro.setGet(publicAPI, model, ['dataAccessHelper', 'mergeGeometries']); + macro.event(publicAPI, model, 'ready'); + macro.algo(publicAPI, model, 0, 1); + + vtkIFCImporter(publicAPI, model); +} + +export const newInstance = macro.newInstance(extend, 'vtkIFCImporter'); + +export default { + newInstance, + extend, + setIFCAPI, +}; diff --git a/Sources/IO/Geometry/index.js b/Sources/IO/Geometry/index.js index d83543eb7ed..8f31be614be 100644 --- a/Sources/IO/Geometry/index.js +++ b/Sources/IO/Geometry/index.js @@ -4,6 +4,7 @@ import vtkDracoReader from './DracoReader'; import vtkSTLWriter from './STLWriter'; import vtkPLYWriter from './PLYWriter'; import vtkGLTFImporter from './GLTFImporter'; +import vtkIFCImporter from './IFCImporter'; export default { vtkSTLReader, @@ -12,4 +13,5 @@ export default { vtkSTLWriter, vtkPLYWriter, vtkGLTFImporter, + vtkIFCImporter, };