From 40c4eb1477261184775c378c4994c6aef089dd92 Mon Sep 17 00:00:00 2001 From: hadware Date: Wed, 6 May 2020 16:43:16 +0200 Subject: [PATCH] Bootstraping repo, and setting up the RPDE notebook. --- numba_examples/pho_a.wav | Bin 0 -> 64334 bytes numba_examples/rpde.ipynb | 464 ++++++++++++++++++++++++++++++++++ requirements.txt | 8 + sly_examples/json_parser.py | 107 ++++++++ sly_examples/star.stl | 478 ++++++++++++++++++++++++++++++++++++ sly_examples/stl_parser.py | 115 +++++++++ 6 files changed, 1172 insertions(+) create mode 100755 numba_examples/pho_a.wav create mode 100644 numba_examples/rpde.ipynb create mode 100644 requirements.txt create mode 100644 sly_examples/json_parser.py create mode 100644 sly_examples/star.stl create mode 100644 sly_examples/stl_parser.py diff --git a/numba_examples/pho_a.wav b/numba_examples/pho_a.wav new file mode 100755 index 0000000000000000000000000000000000000000..14e2c911b734ba01fb60d8b15053ba64d0474544 GIT binary patch literal 64334 zcmWJsRaBHs7~bwKmR-8L8&Oa}u^YR)#ro||?C$RFKt)Bm8AP@vZ0xey%Wl_N3ngr-1_%;y7 zIm%&mym3q1XF)eWE{EJv?AU3e+b>$6=C9TSYmzI+H33`?ed%6@sE64hmykoDW86K? z$tJS_r}NetbsX(9;~aaAD+`81-awWj=#bs+t@dLkxN(Mls&1X(x4G0!M~E>Uq*U@N zqKzQMenKpPOm&j1XU$&LtIpXVGV~9^8>v9fLyHktVG1zat#z4QH^3oq1F{7(99NBd zi$$Vq;oHDL&Z{*$T>|4`cFx}D@N}(n z9{^Q=6Cl$-a<|qw%znTYZhc|4n!L@|&EG6T?FMI|I|wu$ycX2tJnLXuI}A5~Hq~2& zQ~qA@M|nfNLOVlWWV~#GSs$Bg%zz1Dd}FN9`|6Q~GGmrC+$n~Rz&3cS4L_HDW@L08 zWMoEWSX8NZHMYTgOEQ-;wvE`eXTYM!wG4-NVo#G7(^(!}wEH9y{tvna^8>$u1f!jy z!pJM|K1hSB)TmeN7shcHa7T(hDO*kdz)nmRIht{vF`ep#%Yclr?ABb7ZIv`hIP!cA z%<#|p+8GLJ1C_hD4yN7L9_L&MDua*1V#v+(h0K|rksj?75BzudIG4BCL$^&+J$RZ6 z`h(_J#}D@ih#snkUV%=5Fu{{R?VzWSZSXYAJz_iQ0L4MMfES={f?<|rz-?uPlqQ`b zAFEzznB>?ByN!jD=h7#8V%Q_tS6M?DD@icy71&`{vlU`&0j2=&fW?Nb<^bCc=R@}m z&<{ugL~2Dt*$i5HVO%oMhQb=q?VEf@a=5^9Q9B84k?o}&L038rh1 zdi(}vi{J5}LxF3(Us5ll_PX|&zv&FxYONCh^nZ+JO{0xrx(t zt;lEO?X0Q3*Zcx~=6XCM_`^ypXEhv2reFq7!E*>#%1Tt(I>7kE(rcgKng~7)WuXrd zV6+Wh<^WAlpZ`$rSJVj99LFy8ZT@HuzU^gOJZF*MyV_>sA_K@zSt{SqfbD@}d|!K3 zQXvE?IvonQMUJbM1?JV}J0^^|$dYP3VLRYB>8b_c!SBF$$bI)s7v5>K-ZvjNoi%+l zJvPUg7nsKx)&TLk1ZA3HnQF9hqyD`)-S)sW#d*QWb+lTDrV?O=YKUTrN?_3<{Mj$# zbSVSrPtti&UwugU5`(@!s0LTkQ8KygcGGO$XmyD_9=Qwuo3fwmhbusGz!-25!hyX_ zy~=9!zV1802g5o}-U=UUU9DLy?UH0EUHU%vV;spdC1_HlGTJxtR$u|^87T{;0S|Ez z9I1A@eVQ}awb41>HqAIyZInC`&K7bd1dXTV9)v*5RYO6)& zzB0~4&VwEz?}PZ2@(pm+m~C0+xC6Ql?M05p=hDuwp7=9^e*0_LL&^8x>#dP0GC!&7 zdh5H^pw0lUO!n5047z~!p|*G)@ZRI+Ym=305m?9D z(GH1I;=1a7>N@2}vxix>nU5!W0F6T4CzC8qo&Vd zbmoJeBG;h~!#5+RgI796IOA>iAT;`z0RQ;lp`JsIr9F#H^-9J>>7RG6spE zUab{c8SoRNGWNS5NCYGDQYg_ELM?}{w&;|{1Y3KPIMrQ+Jr}ucB45Q~&3HXv+Gm5g zdcYfy7qKw%45r&t!#>QaqlVyPKvF$IdaiqA%gXxc^{K7-eM=Q0%T?rBT8CF(P+r)T z(A;2+&lQhAawOUeo$OLuMw|5dfBGE53!}+gWtr``4q6AzLe&oLvKDz1`pSLEKGD3& zDAiroYjyJt!REKd2xB8KLo;8!Px}U_HIBC5btghDLa4|-_(V{XV~Vaz7A4klR|xau zzWUv;YhG`Xc8)HdW}bF!+{+=CLuv`%^g$d|S>;#O*ZUWN#gFNvHs6?1;2?8Rt?c z(d+$(($Z0K=Q7h@fTHPEx2WZ6rW&vOBl{v5CLOI%0d&V=R0-o&(9pP;w2K+^)YO3y}UX8p=f8WjpuSQ%&8?j`9@5rBZ9pkdfV#$am<{1Ge(@(y^-x$ngNKH7)MLP|QK=$Bp zq<&H%;T@_9yxT@F{sKa@vA{FK6)VLZ2P;EdL!yAidJVU9RQz9rV;u#}l&?FjoG=YNo`h*9|U#2KVO3Y+wksKFDk6{ttBe;~dS z;X2@Y<|=lNa5p<=I$P}f9Af7m=RD9e2nF#4JC4{vIl^Fg>|*K}3u!nq1&xM&vF90l zv@xm>wTE_=ev);cg9zCRKLrH_U&~`dmh^qkp61CFl+rnW*8Z9G_kQKJ#yn23lwb^n z9%cL&e`K8Zg7Yi$R^jF?9la)Q6TR5PYr_@4dJ*s#^>WD1uqLAbX$`?8ds)N!V&BFc zjk+I}8B!9U@;m0UpIJ`KMci}P0KDXIUsmUXj!nHGQj~Quj_+%Uvt>QaU7B}lm_DB2 zn~u3_Y!s~O9Nu`mF0TRBwy}4c$V=T}0Bo;78xZlhT@)vi=lwU}NJvm$DYCOBpkq#aK)GstS|06`Z{(hiU|yQ(2p%>bF%>x)g+!f0^dnL+b*Kn*4K^C(4FlX8Ej$BRJ5uGX zU9THztAjw1?U+=w8$8~n(DN0Ig0{|sZMiMjR(Rh>d9Tarvn8W3f8P}JM7!4jWpEfseY)pO%RrN&L)sykFMTX~t0+|U zsd|+ainH=-iusx;CIn~!DxVhTCyg1ObS2I^ZmNGUWdzD-d?P-}*-~F$?O9b?Z)^+c z4iO$#b{g-1He#~qbG<-*djjJFANdj3Rn#!ddUv}yPnV+6tN&__YiW9r85s!{jziu->q*bwFI1&Jb6ItHQd zvVM}LAXuQo&Kf5TJRLqBqbGQgDdZqRIl{wr#h9WAR-x4heVVNRl7+oNVR|<>Tnjd*5d}m;| z?>=fT@~9K1xxv5RyQpVb?;383NGb7CI5as1i**g?8L|x@NxQ;&=vx}#(WczE$c02~BLytn{gVP;KO)8hunO>h$ zhN>y7TiJ44@JsFOIzoKp0R&=Vnp4+}J3Y5->7r%9b0fxACzrB4V4jjG)#tvU-hckk zTMFy)R#!l8(`x-q;pLIqP?z^Y@)P7VP?#$MgaUVf5#U5urG1(?UAtMfNTA}Xgt_WT zws=%8?Wgys0JHyJuT$hSs7pUtbfkyh$>@;^YBdJO0rWarJNuXKZ|^?FOMEQ!r>)7D zW%yt$vpjYLgHOXIA}6EvBQfx6;9U1k*E@G7ga)rfM58*88OX)374C5BIGtX(TTW7a z*Ke|_p;{u}voLr^*g(ib|1dfV8w)Nq9|a8Z80km}Saw+Dqn)5TrF#W@(KQ>N*vCU2 zpvRN!)B?}{SYe(g8LJ8J;bd2}nW|T7U#Q2a#wr69B-trRwNS+u@UHM}(hTJt-DZ1% zYlgeo{l`w!U6L;A+t%DwC##-YzrAfOw^RdjJtB3nmxSumpXRgY&{kJ2$1T82dXy6G zdmgq*a-#Ov#~n}hJ>B+RTPEvm1>ErOj0XQb5!k3(A@RPs^jY|N^loG|Vk#T}9dkHL zp8=ujiY!f1BYmyhYFrKdLXP&k9m$J(9=|I(CTN1k0i>_xvRucV+0P#s5UyA5Ga#HY z*e>ig;sMeYLL+7~@(L^t+5>(LI_qk-w^_a#3-s>*l6J4ULDi>5>9Z^euDQ?^@Lh-! z1OP)ohq=RT2;&6Z4gChwWh)633{Su?X!&#>#xVLR+yvxC5Z%^l$uzDuq#4o1Q-k>S zt8<9^u49Lz%F*k52+<&f=7;$Gt z~DLe0dSl-GVcksk8a`cI$opXEF!f2b_i_vlnXAR8&u=UiAp)Vc6qfs5GT z3@5dNNXP=LMxsGO6Jp3#E6*ZO#WshWGJkQd>q!M%jc&%-}sYbuc z5N>>H;OY-)v(;ae52cCHa|)sQm~pdfF7i5|ih7AwOn3x6V)m2A_Izvhsy|=DXq?Hp zFC^)gyKX}5s7pu^{I2KWw8)9S=bv2hV|iDBY~<1SQPXtH>iuqWQ2YY6>ZXX2~1tm){>65+`~XJ;wST5 zNNn7mq>6ZO*f3@lyjwp@BIWBv+f>`ldC)jQx<`aJ$2)<2fj5tyjt^t5!Rt)SDHk^8sIoJDw zuf`|ad$vb9VJ}?aNU~6k{(6dmsuOEhXpw+WJ73LL#whnHFKK63YTYwXsYD7Tl{S~; zgUGZ4>b3lyZcW?0HhDXy`#)Jdu*yCPVzVEx_?jD4@8#+8Rr>b8g`<|w*u7P>!@4AY z+WX|C)H)UR*V;#do5yZKzZkkM8cz^v*tDQMp+@#KLLCT@$M9Z@@-$7*C}vR*EXokh z_1{bDbJfd6b)KskUlZ4|pUMSuc*Z*s&m=b&=|%#GzYKcEk%~4lqZf3vG7sq=EJhdb6-m zQ8&6}dDmE8ue#ha6}F2~FM&T-Da+niJxiJ>~7%&Q0yLeX9l@3XW*%^uFLm z=vjyrbkz6(*sMm&vehJKcF4L>`pMkYkaZaaiKEz2^KfewHDv|wPrTXxp{cT52(cbV zogg>SNVGSYA>a^mnj9{_X3By^Fzw+F;(o+?MrL_mz)mur=iYC|S1+i`?&^~9z^(K} z0ShC>Ma~Q<@HUc$!Cclp<6CPlgidVo{NNW5@YmaeDuAxiA^S%)(W^tN)7!D)Aln>5 znfJ_aZS?u*9T6V`N<4oP?m&HgPVlB0+fh#Xai8MA`oMZW5v!2A2z>?yz*2Dq z45^PK0PFw9vkV{MF4v41$Zl?^N~rwOAn3;$EXZn)+(2CTf{6UE=Dfx(oeP??q@ zsBJsi@Lx?$g}bq(^Wng5g|FV~IEVU0>kf*JNlENU+7wq9amH^y?JDY+<+O62ELnU} zvPm^TtJ3pLORUun+#sgfV97U7tV>;g!GV~Y*aO%g^kcl8LApk^iJE$v#`LV@ zUsfhTw&K>4#!x5Y^1vg^PHmxVt>73h$J7(epV+_Pzdioj7_%bt96>W+-rPl>zCYZ3 zZ^_dumA}M9`vJ5WLq#XUf0@fP8>9<*llj{WWYm;F71ZEvg;$gDa-jaO7~StvbZYyiR=;&Fj+91aIVK~urGpkcN*Ms8G{PE#O%f+!4FKgG%Yfiq(nMY@l%mO zJkWh;DC#Hr5cUpQ11knEwE?=t^7?@bougWsTi0|g=+Ijdol!gRsvKphW18JFM zp&1KROyD!{Sto?>X-7gCnV^xwbLJ$`{MKV<8((wJw*0ONts3178G!32A`!GxUh)3@ z{*!&zu(BC5C=ttADCKW{#r<}b-mL!KqU`$1BP-S$u7R)OIS)!VN-%SMk{8oScBa3WFC0b zu;{ti8?xXs%#-fh_wMRXW2<8jxq@~7KtA++VZn)+No%7< z`{Ic#d!FQW=k10yP4=FBs^w56z0cPtC@yf1R}qDRd}Cjs4+UtZec*DU(VH8pkH`r3 z^1Do-LO|;4eH&Un*2lDL?YpIX_!?v7`Cs&=XSIU&GbocVot@d#D!wB+%jA6OsIDBDQ!!tSExu&Oz= z;=0d`>pN0=_`-GCiSEVJ7eT7{A?fnW%W0EiV}p*8$GYvR-@Ls86s}49Mq6lnXeqQ7 znwf@t%_l{SWQ@F1z0foQ?7-}xoTS~QJVqY3L6q$7&GqlAN0+~^ThUV}US+I;dEjQ_ zU*ao~Kb#$=`C5s70=zMJ+w?!H{%*On_v{MvczjX^nklB0e!Mqu_w0*bB}hS~TT6V# zDr6-x3b9WtFy&h=hLb2-XMqrQgo2Xwsf!Y70^gDsJ3_?ioiUC6&6xv%y2q%s-cv)n zBd&&u*={_-8KdeJzhii@Ww?SOTLkekNNF;8k=r#_04I(S7NIR+TIHO)31fp z<8aj9v@wHc7)LbV^Kca0M8aJ1Ln@rog1PEeY8m_oZ5wJ*%Du~$)WAA2#eb|n2uFOc zr52BLWQXSUWxR`+?SG%}-3`@gg>U;q+Q+wQ+P1VT>%7Dz2 z^@wcebxpo-EN5!V&gSOUXMHEdOhD&WAfMxLI1wVjRi?cmpDa4Z^^goR*FY~ujGyM5 z^=sMK6$i&c)Bf}RWJ8NLRYE@(7I*zw-r|-vnEtvG-8*b^tyREv`B%j{#WcVIulFQG zr4Cs;>{dojEW`U1rrfkzXzP?VN49_BUehFl-;e^Bx7n%eB1R&4H@XvY9GnR;p*m=v zd~-r)hm{80VN8Qf(3u7%G)PM;N)D9@>wCCgjUv<~&lf?J5s>IPk%vQu`7UG>zrGJL=fv*}rU9tI~qX5c;Uq^(%Q`}Lu9l8wVA@OW3b^zYL zluMMoRv)mUAy?dbG#o2~d7)z+01z&}-jmIF+u6|zsHfT9lWSA|%YQum+QMAivxicC;h5-mus?2{g{4J` zia7m^@2mD!tgrEED-awppCEJvGEzEnvT}#z>`!hFwo`9|+cY=0s`kz%S7Tc1_^yf` zGjm?(OK4<;lmqAQFSZH|271@SzC+M<3W1b6(c(pG57_?0gj z>U*2DfO|fb5{Qhv9=|_HoU|i>7quru>NA`E1IL7Yx3}wh6$@kr$$sf~b(i6l)eQ1S z?Zd)xN(>LN0W`xJYdERRP~X*z0iFUN(`NGtGsp7VJjVFmu+&gsXfms8y^cK&rb}pv zH-ww8>L-$VzO{2p>(S0b+>2T;tkvt-FharBg@vo;EP6NUUi3Z8GiiGL_P2&do==T0 zAN_T8KT%hK?-C6j5btzvlQ-384|}Q4(!iXM$q~Bn68}j0MQFEXQ{RGmR@u0Jy)|{5 zB+X6q{2*b*i!sBeluhD}+Mm%D#$dpa1J)e?OG8p$QvXmlD9_8@iS)df{!M*1xO1i1 zhR0AhEkCF@t~8aF#*eQH-bN?Fl=@pDM$hH;y0*KW1HGPtaOr=_5t=mZS73#{#;C9+ zfnFiU5--ygo_h8y<|5K;c%X%%INz`9^58bhKWhu^uc7-OmyvGlS4L-j`;7NL?|X`sMeieQMO4_)fKA>eJ*98~Gb|IFo84oWe#GuH_gXe zf|_eMBdz@a55ufA;p{;>C^y$Y?sk zTkZLXeT|Wbi=*0@TDCig6nx6ZO&20|m>VRkJ7&~DDyP>R;ha*oz+(eur4GsKnwT(o z{n*Sba(F!b8Yo*EB+C<)^Y#kPiBn`%$_lMmo2m9yU=-6e=gisgKhzIC{Lm+n@`#$? zW$cT@MPRY6RLtt%(j)JiF1RK;t^TL~Xtr2RS$|tDmNs~HQ&K{v z?pyVFhsUPa1;#O(qj97S%4_ALK9FAYyvq6grMZi@RoS4OWt#^o1m8rFF!PW+;wP_9 zVWU%*4dtiDL}#$CA@&1X`^Pslm2UjApn6KTQa6kEDx@RjZuW)|tvR;zU(t{Ko|6mU z$KAIa5XUx0j%}RjindoiO;kKE&{N&>Y(OftSr*{j-ik=Cq*ci;Lt?O;$yM~B};u=eMg4;R9lQkoD8TIc?~;{tYNHV?epI5J;4)3d5l)M-KOUnrXpIN zs_4-CXHqzj&?M9_>^1CU^b*))x7a2&e>6ur7K5gMKSEnT3C<$xUc)ABmMl(@1>7+g z!QI4EhMSelzRrA4eTPFsq*@a1XH!l^dwEYOtMYi$oW7Z6yIWkoDUSU>ZzSGwE--?!{R-|o=f`{YokqV_}kA@gCu$VEtBXt*s} zdsw`&ucqTy>(utno?PirJBM(~Z)1!pF)Z;y+0_Np10cYs{~UAIni zO@2i5hPR0e<>>`IGP*$kE+8)mu*dz#{5j-AvM4gl>jv7(7B9cr`>n0D`FQJ+u0rl% zX{4szFxs-r{>5bk`yglHSfpmkRoZr12zfZ}6g<{3)v!`sq9Ch=YcCsqSsachu6wQt zu3@%5!!uyJ+M%w|rWzZZli(kTzKk^tBPkZK#s*blxDmZ%jz=Fv+^O0HPQw18WCreG ztBC{*-5?Ul2a0+}%Ti4jke1-w?2%KtW-p&PdCbb_MZ~ejvE6Sg`%6yzB2+AB(~2Xl zd}uMM5&09`WUDsx>W-K&&PHSn{bsLJt^TB?amIw>? zfCXhlT!^?F{KLzWG7(b`jRE;V5|J-(49W`HaZe=(M5-zknu4~u3<4{Q9bvynlx zmq9b)tCKvFS4Bm7@4-KCAT-|v8+t}?CUmbH5Q}l@UOn4FwH4caU3_;Ev#oCKNpYFUd$`WMRZ3;Io0CuRxDE7gnIijU4`%6#$iYtS(s-#oxzNkP#J@zs30A#Iwq4}NhxoMJXF}95* z3qP0Ooq!Hc^1P3TG3*gUb;LHVZA|TaD7tD=)lA_d>EozU<7E>sZtBl2#@LZ3I(ROCGS5Zw~R zQE7o}qGF2*ubH4-shy?u10Dgby6fg=t}JxDhb!184w<4#u8;i}bc!KHEHW001AC9O z#kV$eD*BY7vFg9ZwT=+*1B3)Sh4_=YoB5eN#%l<>(_<7l9laVNZ11*Gpbgs7q0@HMD)+yk0YH>>Wn63$93;M_3OkEwk6QcoQ`^w4)zXfHb zbp`wp=6=WvoExvjUBV{8CV?)vu3Od45Dbr&5s)4AIyxh4i`Ngr6VO&*yQrmiI7ike z7e6)JgcnmLvz~dEc@46U)B^kvR4XhFb^}>}Uqsb1-ZM^;uEUEgQ{~Zpu4Y@UZ(U?d z?f@8A3tdU0_-_cyi(DUu30%U4lIJ5H+TFS`)pGex=?<}7K;ysSint5;YeXe7B(TT! z3wg|AQt&dU!#xFgh%lDk z=M@=X3HlIp#XpC&iL@VqacnbyG~1QInp4_2y00dG^YK9~?VFab+NRhgpQKr+e`VHz z>S0gdVOSQ*0={66Ru>76_QSfjb?@&#By3hCn)NOZR3P#_$OHPrzS8j0utrJK7TD*2 z4hH9pvrMqfPMJp>elzwt^OW_R_%V!tT;Dql;0R#gM zfwp3XQ{k)*pNC#EXf?l?E5Kvq#R{EhrltsUS)tg%&eHh6IeVA(|dOUT&YiY>P2tw3^$g?3HekhN%xNqQdCaQ*~ zn5igIhHJL!PU_$2Zy1jlsK8APOFdIf)|c9jfj^=k_zn26*m{`MP5`P!54!KQt!lg9 z%@A3Yr_58pnb4i^w}?`3oQEEwoCWEV^TWx7$8g@ewZt(M`KQg2k8d&uRXk5k+sCnzT8fRx{{ zvrbo4)o`JIk6wZbXT1nihlB;6_9 zF0S5D_O`OQHAD=t7ZUuu2tn&Zr-e#`8hocR8N?0n*REssk+v|a+b~vrNDS%k=p5G8 z)N-k945yw;Qy#SsV3WPZhj%0((u>oyiOV88eJTj?&SuS2K~?AI)-{d08n(9-be-b~ z6rrZE&~L=q?4nRp3^egfqF2ndU~l#*{85P8vKE-5A**?6FmMJ4Gn_Z(7`et=eJwCX z_Xr3xjpR8b6mq$Hy{Si%%6ryxru|IE`|inHijrf`K+4e5aZ>b7#{m-) zSi{$G;GFLQz*QEIIf1!q#roFupXWyB91MQ~ujWTp_`K+S9RG1sncu(<;}iE*m;{0c z)jCI*je4{}Yw|^8u@A?t%2_|UaQOWMw)Z-?TXl>xrAGX>_TRClTf$fNFQh`hoUp%P z(?d%9e3>bvPnZ*^KbXgqTCZV&dwdR%2kbpc+-%+4!5o;TI@(n(sj;l0LIhp|fJo#($23 zL_q>idfdj8yY?F9sBX)`WOEg3RbYT=ylh-)iZkyvOaLOaRoY~2f`Mz#9lXDn@m{!g zbS1doa89{YxVd+A&tBeM$w_sqU5OZvSxeZ6>xTN+j;VGDi5zp=wa)G0#aLWCbEQ)IynddvY-8JsBgZzZbMAvs1*E^Af^Fiy= zg;d9ua)It?R(|^kVKEr(64bliM0vp=&ivtb^!4!l;Buk zZquWHc-4K$4&H&DpB>v;H7)ra%lkqlOj9H3JF6^mMaG_yXGX>j9g^H1G>JUaEm6$v zX>R;h<){d&lGWeq2p(vacIr{CVx*dK-+Om(OGH6bPk3Pw zrd%dnnEB$JzhoxWcqu>V-ICu9 z>7j_q3>G-f3m1DfGb=Z2~ffwQojn%RW zG9SChv{(IRWu$#FsmdmZa^2PEup83H(sN6or2Zkc9WM7E)pK1^v)*JYE_T) ziX>ITL4g7n4;?upX=e8Hr(<&Co4rTFE5y02F{R=kstSJFCP}1ahARO212WE~b}qDS zG8I@n9GeikJk+7$FM0ER+=E5x|~fecs9;|OW+GXxaTe$(a|I?<#^Qo_qVj&dxQ0T+JlsSd^A9jd&Y770ZVvTeFOPz$Imuo(JAf92d1DZ_b!$<1geLi|O#X zf;g`F*=MVD{7tD0YP!jd)O~ZjfZ^b;z#{N5SCoCUYqdKc_MB8Os7U^auZo98+-1MQ zK}ZLjC>eo3Lvn5kQX3-I!~BUn-3TB+e@&Swu~GVzxRjKI2e$pq~~ivYu3W^!l{Ct6$if+grk4A+J`Q zP)l?}t+C+i=y#-ck9?ouLEfRkVNC(cn4ht|t^wl=?H`RsI}@0zk1$dVm-KYtsA9Z$ zCC|oZ2^HyYh|l&bdmFe{WF{j7swd3!7{W4n2q~*@ z9*B8h8t5iC0y&eU^KABa1YZwI^BN#*cTdwS=3?5z8p`T5oRgvQD)UM5 z#<6zMr;sLN-@;SCmz|-uZqqs7i2RtKz8BRc=y<`oGLR_UVpt9yLHOlc6A=)yQg$4bede`;&^kaAjgi(^AiYDNe)f0M=aD|EV_YBPlhlKS7da=Vu zGWZOK(2%LSuY0PmHcU61)=kp%D-OzXB{Rg=WMfsIO;-0I!e(N`NHy#dEC&&bo<%HWto5D|csF=^;3Y35X%aNt zP$H`6&TU=ST-^SxZ=!6Z=_@1-f0Ne7bhGBL?Vj&FjMPRV9-E7J3qEfTF%VR#;w~CQi9%b=P)RDDTxR4Gt{m67UxeW+of^EGlY<{hb@as*$4 z?Ou7BPo=`}^etvIEw61`P6AQu7hbR*9I`%0DgKB>N2b408E!kja(e09lJn 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 literal 0 HcmV?d00001 diff --git a/numba_examples/rpde.ipynb b/numba_examples/rpde.ipynb new file mode 100644 index 0000000..1b87642 --- /dev/null +++ b/numba_examples/rpde.ipynb @@ -0,0 +1,464 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + " \n", + " " + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "import librosa\n", + "import numpy as np\n", + "import plotly.express as px\n", + "import plotly.graph_objs as go\n", + "from plotly.offline import init_notebook_mode, iplot\n", + "init_notebook_mode(connected=True)\n", + "from numpy.linalg import norm\n", + "\n", + "from tqdm import tqdm\n" + ] + }, + { + "cell_type": "markdown", + "source": [ + "### First things first\n", + "\n", + "_Loading the audio data, and embedding the audio time series_\n", + " Our algorithm operates on time series\n", + "(here, it's audio, what a coincidence!).\n" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%% md\n" + } + } + }, + { + "cell_type": "code", + "execution_count": null, + "outputs": [], + "source": [ + "# loads the sound data into a float32\n", + "data, rate = librosa.core.load(\"pho_a.wav\")\n", + "\n", + "px.line(data, x=range(len(data)), y=data)\n" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [], + "source": [ + "def embed_time_series(data: np.ndarray, dim: int, tau: int):\n", + " \"\"\"This creates an special embedding for the input data. The\n", + " output shape of this function is (N, D) with\n", + " N = len(data) - (dim - 1) * tau\n", + " D = dim\"\"\"\n", + " embed_points_nb = data.shape[0] - (dim - 1) * tau\n", + " embed_mask = np.arange(embed_points_nb)[:, np.newaxis] + np.arange(dim)\n", + " tau_offsets = np.arange(dim) * (tau - 1)\n", + " embed_mask = embed_mask + tau_offsets\n", + " return data[embed_mask]" + ] + }, + { + "cell_type": "code", + "execution_count": 51, + "metadata": {}, + "outputs": [], + "source": [ + "# this creates an embedding of a slice of the time series\n", + "ts = embed_time_series(data[:2000], dim=4, tau=22)\n", + "print(ts.shape)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "outputs": [], + "source": [ + "trace = go.Scatter3d(\n", + " x=ts[:,0],\n", + " y=ts[:,1],\n", + " z=ts[:,2],\n", + " name=\"Pho_a\",\n", + " mode='markers',\n", + " marker=dict(\n", + " color=\"blue\",\n", + " size=2,\n", + " symbol='circle',\n", + " line=dict(\n", + " color='rgb(204, 204, 204)',\n", + " width=1\n", + " ),\n", + " opacity=0.7\n", + " )\n", + " )\n", + "layout = go.Layout(margin=dict(l=0,r=0,b=0,t=0),\n", + " scene = dict(\n", + " xaxis={\"title\":\"f1\"},\n", + " yaxis={\"title\":\"f2\"},\n", + " zaxis={\"title\":\"f3\"})\n", + " )\n", + "fig = go.Figure(data=[trace], layout=layout)\n", + "iplot(fig, filename=\"/tmp/pho_a_trajectory.png\")" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, + { + "cell_type": "markdown", + "source": [ + "### This is where the fun begins\n", + "\n", + "Now, let's implement the RPDE algorithm.\n", + "I've provided 4 different implementations:\n", + "\n", + "1. The first one is a plain python implem of the recurrence diagram\n", + "2. The second one is numba implem without any parallelization\n", + "2. The third one is a naïve parallelized numba implem. It's \"almost\" working,\n", + "as it outputs a value close to the actual value, but some race conditions make it\n", + "undeterministic.\n", + "3. The third one is a fixed parallelized version of the previous numba implementation,\n", + "fully adapted to work in parallel" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%% md\n" + } + } + }, + { + "cell_type": "code", + "execution_count": null, + "outputs": [], + "source": [ + "def plain_python_recurrence_histogram(ts: np.ndarray, epsilon: float, t_max: int):\n", + " distances_histogram = np.zeros(len(ts))\n", + " epsilon = 0.25\n", + " for i in tqdm(np.arange(len(ts))):\n", + " # finding the first \"out of ball\" index\n", + " first_out = len(ts) # security\n", + " for j in np.arange(i + 1, len(ts)):\n", + " d = norm(ts[i] - ts[j])\n", + " if d > epsilon:\n", + " first_out = j\n", + " break\n", + "\n", + " # finding the first \"back to the ball\" index\n", + " for j in np.arange(first_out + 1, len(ts)):\n", + " d = norm(ts[i] - ts[j])\n", + " if d < epsilon:\n", + " distances_histogram[j - i] += 1\n", + " break\n", + " return distances_histogram" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, + { + "cell_type": "code", + "execution_count": 44, + "metadata": {}, + "outputs": [], + "source": [ + "from numba import jit, float32, prange, int32" + ] + }, + { + "cell_type": "code", + "execution_count": 42, + "metadata": {}, + "outputs": [], + "source": [ + "@jit(int32[:](float32[:,:], float32, int32), nopython=True)\n", + "def recurrence_histogram(ts: np.ndarray, epsilon: float, t_max: int):\n", + " distances_histogram = np.zeros(len(ts), dtype=np.int32)\n", + " for i in np.arange(len(ts)):\n", + " # finding the first \"out of ball\" index\n", + " first_out = len(ts) # security\n", + " for j in np.arange(i + 1, len(ts)):\n", + " if t_max > 0 and j - i > t_max:\n", + " break\n", + " d = norm(ts[i] - ts[j])\n", + " if d > epsilon:\n", + " first_out = j\n", + " break\n", + "\n", + " # finding the first \"back to the ball\" index\n", + " for j in np.arange(first_out + 1, len(ts)):\n", + " if 0 < t_max < j - i:\n", + " break\n", + " d = norm(ts[i] - ts[j])\n", + " if d < epsilon:\n", + " distances_histogram[j - i] += 1\n", + " break\n", + " return distances_histogram" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "outputs": [], + "source": [ + "# Here, we naively just switched out all \"np.arange\" occurences to numba.prange.\n", + "@jit(int32[:](float32[:,:], float32, int32), nopython=True)\n", + "def wrong_parallel_recurrence_histogram(ts: np.ndarray, epsilon: float, t_max: int):\n", + " distances_histogram = np.zeros(len(ts), dtype=np.int32)\n", + " for i in prange(len(ts)):\n", + " # finding the first \"out of ball\" index\n", + " first_out = len(ts) # security\n", + " for j in prange(i + 1, len(ts)):\n", + " if t_max > 0 and j - i > t_max:\n", + " break\n", + " d = norm(ts[i] - ts[j])\n", + " if d > epsilon:\n", + " first_out = j\n", + " break\n", + "\n", + " # finding the first \"back to the ball\" index\n", + " for j in prange(first_out + 1, len(ts)):\n", + " if 0 < t_max < j - i:\n", + " break\n", + " d = norm(ts[i] - ts[j])\n", + " if d < epsilon:\n", + " distances_histogram[j - i] += 1\n", + " break\n", + " return distances_histogram" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, + { + "cell_type": "code", + "execution_count": 46, + "metadata": {}, + "outputs": [], + "source": [ + "@jit(int32[:](float32[:,:], float32, int32), parallel=True, nopython=True)\n", + "def parallel_recurrence_histogram(ts: np.ndarray, epsilon: float, t_max: int):\n", + " return_distances = np.zeros(len(ts), dtype=np.int32)\n", + " for i in prange(len(ts)):\n", + " # finding the first \"out of ball\" index\n", + " first_out = len(ts) # security\n", + " for j in np.arange(i + 1, len(ts)):\n", + " if t_max > 0 and j - i > t_max:\n", + " break\n", + " d = norm(ts[i] - ts[j])\n", + " if d > epsilon:\n", + " first_out = j\n", + " break\n", + "\n", + " # finding the first \"back to the ball\" index\n", + " for j in np.arange(first_out + 1, len(ts)):\n", + " if t_max > 0 and j - i > t_max:\n", + " break\n", + " d = norm(ts[i] - ts[j])\n", + " if d < epsilon:\n", + " return_distances[i] = j - i\n", + " break\n", + " \n", + " # building histogram, can't be parallel\n", + " distance_histogram = np.zeros(len(ts), dtype=np.int32)\n", + " for i in np.arange(len(ts)):\n", + " if return_distances[i] != 0:\n", + " distance_histogram[return_distances[i]] += 1\n", + " return distance_histogram" + ] + }, + { + "cell_type": "markdown", + "source": [ + "### Running stuff\n", + "\n", + "Let's run the good ol' python implementation, just to take a look at what does\n", + "the algorithm's output look like.\n", + "\n", + "Also, let's check that the various numba implementations's outputs match the plain\n", + "python \"ground truth\"." + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%% md\n" + } + } + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "CPU times: user 1min 30s, sys: 67.9 ms, total: 1min 30s\n", + "Wall time: 55.6 s\n" + ] + }, + { + "data": { + "text/plain": [ + "array([0, 0, 0, ..., 0, 0, 0], dtype=int32)" + ] + }, + "execution_count": 18, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "distances_histogram = plain_python_recurrence_histogram(ts, 0.12, 10000)\n", + "px.line(distances_histogram, x=range(len(distances_histogram)), y=distances_histogram)" + ] + }, + { + "cell_type": "markdown", + "source": [ + "### The ~~hour~~ seconds of truth" + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "code", + "execution_count": null, + "outputs": [], + "source": [ + "%time plain_python_recurrence_histogram(ts, 0.12, 10000)" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, + { + "cell_type": "code", + "execution_count": null, + "outputs": [], + "source": [ + "%time numba_recurrence_histogram(ts, 0.12, 10000)" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, + { + "cell_type": "code", + "execution_count": null, + "outputs": [], + "source": [ + "%time parallel_recurrence_histogram(ts, 0.12, 10000)" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, + { + "cell_type": "code", + "execution_count": 52, + "metadata": { + "pycharm": { + "name": "#%%\n" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "CPU times: user 16min 10s, sys: 3.01 s, total: 16min 13s\n", + "Wall time: 6min 59s\n" + ] + }, + { + "data": { + "text/plain": [ + "array([0, 0, 0, ..., 0, 0, 0], dtype=int32)" + ] + }, + "execution_count": 52, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.6" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} \ No newline at end of file diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..3d0cba1 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,8 @@ +sly +numba +scipy +numpy +tqdm +librosa +jupyter +dataclasses >= 0.7; python_version <'3.7' \ No newline at end of file diff --git a/sly_examples/json_parser.py b/sly_examples/json_parser.py new file mode 100644 index 0000000..9ae07b6 --- /dev/null +++ b/sly_examples/json_parser.py @@ -0,0 +1,107 @@ +from sly import Lexer, Parser +import pprint + +""" +Implementation with SLY of a very simple JSON parser. +Little reminder: here's the grammar + + json := object | array + object := '{' members '}' + members := pair | pair ',' members + array := '[' elements ']' + elements := value | value "," elements + pair := STRING ':' value + value := STRING | INTEGER | FLOAT | object | array + +""" + + +class JSONLexer(Lexer): + tokens = {"FLOAT", "INTEGER", "STRING"} + + literals = {'{', '}', '[', ']', ',', ':'} + ignore = " \t\n" + + @_(r"\".*?\"") + def STRING(self, t): + t.value = t.value.strip("\"") + return t + + @_(r"\d+\.\d*") + def FLOAT(self, t): + t.value = float(t.value) + return t + + @_(r"\d+") + def INTEGER(self, t): + t.value = int(t.value) + return t + + +class JSONParser(Parser): + tokens = JSONLexer.tokens + start = "json" + + @_('object', + 'array') + def json(self, p): + return p[0] + + @_('"{" members "}"') + def object(self, p): + return {key: value for key, value in p.members} + + @_('pair') + def members(self, p): + return [p.pair] + + @_('pair "," members') + def members(self, p): + return [p.pair] + p.members + + @_('STRING ":" value') + def pair(self, p): + return p.STRING, p.value + + @_('"[" elements "]"') + def array(self, p): + return p.elements + + @_('value') + def elements(self, p): + return [p.value] + + @_('value "," elements') + def elements(self, p): + return [p.value] + p.elements + + @_('STRING', + 'INTEGER', + 'FLOAT', + 'object', + 'array') + def value(self, p): + return p[0] + + def error(self, p): + raise ValueError("Parsing error at token %s" % str(p)) + + +if __name__ == "__main__": + lexer = JSONLexer() + parser = JSONParser() + json_text = """{"menu": { + "id": "file", + "value": "File", + "popup": { + "menuitem": [ + {"value": "New", "onclick": "CreateNewDoc()"}, + {"value": "Open", "onclick": "OpenDoc()"}, + {"value": "Close", "onclick": "CloseDoc()"} + ], + "delay" : 1.5 + } + }} + """ + result = parser.parse(lexer.tokenize(json_text)) + pprint.pprint(result) diff --git a/sly_examples/star.stl b/sly_examples/star.stl new file mode 100644 index 0000000..0292428 --- /dev/null +++ b/sly_examples/star.stl @@ -0,0 +1,478 @@ +solid Star + facet normal 0 -1 0 + outer loop + vertex 0.83404 0 0.694596 + vertex 0.36904 0 1.5 + vertex 1.78814e-006 0 0.75 + endloop + endfacet + facet normal 0 -1 0 + outer loop + vertex 0.36904 0 1.5 + vertex 0.83404 0 2.3054 + vertex 1.78814e-006 0 2.25 + endloop + endfacet + facet normal 0 -1 0 + outer loop + vertex 1.29904 0 0 + vertex 1.76404 0 0.694596 + vertex 0.83404 0 0.694596 + endloop + endfacet + facet normal 0 -1 0 + outer loop + vertex 0.83404 0 2.3054 + vertex 1.76404 0 2.3054 + vertex 1.29904 0 3 + endloop + endfacet + facet normal 0 -1 0 + outer loop + vertex 1.76404 0 0.694596 + vertex 2.59808 0 0.75 + vertex 2.22904 0 1.5 + endloop + endfacet + facet normal 0 -1 0 + outer loop + vertex 2.22904 0 1.5 + vertex 2.59808 0 2.25 + vertex 1.76404 0 2.3054 + endloop + endfacet + facet normal 0 -1 0 + outer loop + vertex 0.83404 0 0.694596 + vertex 0.83404 0 2.3054 + vertex 0.36904 0 1.5 + endloop + endfacet + facet normal 0 -1 0 + outer loop + vertex 0.83404 0 0.694596 + vertex 1.76404 0 0.694596 + vertex 0.83404 0 2.3054 + endloop + endfacet + facet normal 0 -1 0 + outer loop + vertex 1.76404 0 0.694596 + vertex 2.22904 0 1.5 + vertex 0.83404 0 2.3054 + endloop + endfacet + facet normal 0 -1 0 + outer loop + vertex 2.22904 0 1.5 + vertex 1.76404 0 2.3054 + vertex 0.83404 0 2.3054 + endloop + endfacet + facet normal -0.897262 0 -0.441498 + outer loop + vertex 0.36904 0 1.5 + vertex 1.78814e-006 0 2.25 + vertex 0.36904 0.25 1.5 + endloop + endfacet + facet normal -0.897262 0 -0.441498 + outer loop + vertex 1.78814e-006 0 2.25 + vertex 1.78814e-006 0.25 2.25 + vertex 0.36904 0.25 1.5 + endloop + endfacet + facet normal -0.0662822 0 0.997801 + outer loop + vertex 1.78814e-006 0 2.25 + vertex 0.83404 0 2.3054 + vertex 1.78814e-006 0.25 2.25 + endloop + endfacet + facet normal -0.0662822 0 0.997801 + outer loop + vertex 0.83404 0 2.3054 + vertex 0.83404 0.25 2.3054 + vertex 1.78814e-006 0.25 2.25 + endloop + endfacet + facet normal -0.83098 0 0.556302 + outer loop + vertex 0.83404 0 2.3054 + vertex 1.29904 0 3 + vertex 0.83404 0.25 2.3054 + endloop + endfacet + facet normal -0.83098 0 0.556302 + outer loop + vertex 1.29904 0 3 + vertex 1.29904 0.25 3 + vertex 0.83404 0.25 2.3054 + endloop + endfacet + facet normal 0.83098 0 0.556303 + outer loop + vertex 1.76404 0 2.3054 + vertex 1.76404 0.25 2.3054 + vertex 1.29904 0 3 + endloop + endfacet + facet normal 0.83098 0 0.556303 + outer loop + vertex 1.29904 0 3 + vertex 1.76404 0.25 2.3054 + vertex 1.29904 0.25 3 + endloop + endfacet + facet normal 0.0662822 0 0.997801 + outer loop + vertex 2.59808 0 2.25 + vertex 2.59808 0.25 2.25 + vertex 1.76404 0 2.3054 + endloop + endfacet + facet normal 0.0662822 0 0.997801 + outer loop + vertex 1.76404 0 2.3054 + vertex 2.59808 0.25 2.25 + vertex 1.76404 0.25 2.3054 + endloop + endfacet + facet normal 0.897262 0 -0.441498 + outer loop + vertex 2.22904 0 1.5 + vertex 2.22904 0.25 1.5 + vertex 2.59808 0 2.25 + endloop + endfacet + facet normal 0.897262 0 -0.441498 + outer loop + vertex 2.59808 0 2.25 + vertex 2.22904 0.25 1.5 + vertex 2.59808 0.25 2.25 + endloop + endfacet + facet normal 0.897262 0 0.441498 + outer loop + vertex 2.59808 0 0.75 + vertex 2.59808 0.25 0.75 + vertex 2.22904 0 1.5 + endloop + endfacet + facet normal 0.897262 0 0.441498 + outer loop + vertex 2.22904 0 1.5 + vertex 2.59808 0.25 0.75 + vertex 2.22904 0.25 1.5 + endloop + endfacet + facet normal 0.0662821 0 -0.997801 + outer loop + vertex 1.76404 0 0.694596 + vertex 1.76404 0.25 0.694596 + vertex 2.59808 0 0.75 + endloop + endfacet + facet normal 0.0662821 0 -0.997801 + outer loop + vertex 2.59808 0 0.75 + vertex 1.76404 0.25 0.694596 + vertex 2.59808 0.25 0.75 + endloop + endfacet + facet normal 0.83098 0 -0.556302 + outer loop + vertex 1.29904 0 0 + vertex 1.29904 0.25 0 + vertex 1.76404 0 0.694596 + endloop + endfacet + facet normal 0.83098 0 -0.556302 + outer loop + vertex 1.76404 0 0.694596 + vertex 1.29904 0.25 0 + vertex 1.76404 0.25 0.694596 + endloop + endfacet + facet normal -0.83098 0 -0.556302 + outer loop + vertex 1.29904 0 0 + vertex 0.83404 0 0.694596 + vertex 1.29904 0.25 0 + endloop + endfacet + facet normal -0.83098 0 -0.556302 + outer loop + vertex 0.83404 0 0.694596 + vertex 0.83404 0.25 0.694596 + vertex 1.29904 0.25 0 + endloop + endfacet + facet normal -0.066282 0 -0.997801 + outer loop + vertex 0.83404 0 0.694596 + vertex 1.78814e-006 0 0.75 + vertex 0.83404 0.25 0.694596 + endloop + endfacet + facet normal -0.066282 0 -0.997801 + outer loop + vertex 1.78814e-006 0 0.75 + vertex 1.78814e-006 0.25 0.75 + vertex 0.83404 0.25 0.694596 + endloop + endfacet + facet normal -0.897262 0 0.441498 + outer loop + vertex 1.78814e-006 0 0.75 + vertex 0.36904 0 1.5 + vertex 1.78814e-006 0.25 0.75 + endloop + endfacet + facet normal -0.897262 0 0.441498 + outer loop + vertex 0.36904 0 1.5 + vertex 0.36904 0.25 1.5 + vertex 1.78814e-006 0.25 0.75 + endloop + endfacet + facet normal 0 1 0 + outer loop + vertex 0.48049 0.35 1.5 + vertex 0.155677 0.35 2.16012 + vertex 0.889765 0.35 2.20888 + endloop + endfacet + facet normal 0 1 0 + outer loop + vertex 0.889765 0.35 0.791115 + vertex 0.155677 0.35 0.839879 + vertex 0.48049 0.35 1.5 + endloop + endfacet + facet normal 0 1 0 + outer loop + vertex 0.889765 0.35 2.20888 + vertex 1.29904 0.35 2.82024 + vertex 1.70831 0.35 2.20888 + endloop + endfacet + facet normal 0 1 0 + outer loop + vertex 1.29904 0.35 0.179758 + vertex 0.889765 0.35 0.791115 + vertex 1.70831 0.35 0.791115 + endloop + endfacet + facet normal 0 1 0 + outer loop + vertex 2.11759 0.35 1.5 + vertex 1.70831 0.35 2.20888 + vertex 2.4424 0.35 2.16012 + endloop + endfacet + facet normal 0 1 0 + outer loop + vertex 1.70831 0.35 0.791115 + vertex 2.11759 0.35 1.5 + vertex 2.4424 0.35 0.839879 + endloop + endfacet + facet normal 0 1 0 + outer loop + vertex 0.889765 0.35 0.791115 + vertex 0.48049 0.35 1.5 + vertex 0.889765 0.35 2.20888 + endloop + endfacet + facet normal 0 1 0 + outer loop + vertex 0.889765 0.35 0.791115 + vertex 0.889765 0.35 2.20888 + vertex 1.70831 0.35 2.20888 + endloop + endfacet + facet normal 0 1 0 + outer loop + vertex 0.889765 0.35 0.791115 + vertex 1.70831 0.35 2.20888 + vertex 2.11759 0.35 1.5 + endloop + endfacet + facet normal 0 1 0 + outer loop + vertex 0.889765 0.35 0.791115 + vertex 2.11759 0.35 1.5 + vertex 1.70831 0.35 0.791115 + endloop + endfacet + facet normal -0.63446 0.707107 0.312186 + outer loop + vertex 1.78814e-006 0.25 0.75 + vertex 0.36904 0.25 1.5 + vertex 0.155677 0.35 0.839879 + endloop + endfacet + facet normal -0.63446 0.707107 0.312186 + outer loop + vertex 0.36904 0.25 1.5 + vertex 0.48049 0.35 1.5 + vertex 0.155677 0.35 0.839879 + endloop + endfacet + facet normal -0.63446 0.707107 -0.312186 + outer loop + vertex 0.36904 0.25 1.5 + vertex 0.155677 0.35 2.16012 + vertex 0.48049 0.35 1.5 + endloop + endfacet + facet normal -0.634459 0.707107 -0.312186 + outer loop + vertex 0.36904 0.25 1.5 + vertex 1.78814e-006 0.25 2.25 + vertex 0.155677 0.35 2.16012 + endloop + endfacet + facet normal -0.0468686 0.707106 0.705552 + outer loop + vertex 1.78814e-006 0.25 2.25 + vertex 0.83404 0.25 2.3054 + vertex 0.155677 0.35 2.16012 + endloop + endfacet + facet normal -0.0468684 0.707107 0.705552 + outer loop + vertex 0.83404 0.25 2.3054 + vertex 0.889765 0.35 2.20888 + vertex 0.155677 0.35 2.16012 + endloop + endfacet + facet normal -0.587592 0.707107 0.393365 + outer loop + vertex 0.83404 0.25 2.3054 + vertex 1.29904 0.25 3 + vertex 1.29904 0.35 2.82024 + endloop + endfacet + facet normal -0.587591 0.707107 0.393365 + outer loop + vertex 0.83404 0.25 2.3054 + vertex 1.29904 0.35 2.82024 + vertex 0.889765 0.35 2.20888 + endloop + endfacet + facet normal 0.587591 0.707107 0.393365 + outer loop + vertex 1.76404 0.25 2.3054 + vertex 1.29904 0.35 2.82024 + vertex 1.29904 0.25 3 + endloop + endfacet + facet normal 0.587591 0.707107 0.393365 + outer loop + vertex 1.76404 0.25 2.3054 + vertex 1.70831 0.35 2.20888 + vertex 1.29904 0.35 2.82024 + endloop + endfacet + facet normal 0.0468686 0.707106 0.705552 + outer loop + vertex 2.59808 0.25 2.25 + vertex 2.4424 0.35 2.16012 + vertex 1.76404 0.25 2.3054 + endloop + endfacet + facet normal 0.0468684 0.707107 0.705552 + outer loop + vertex 1.76404 0.25 2.3054 + vertex 2.4424 0.35 2.16012 + vertex 1.70831 0.35 2.20888 + endloop + endfacet + facet normal 0.63446 0.707107 -0.312187 + outer loop + vertex 2.22904 0.25 1.5 + vertex 2.4424 0.35 2.16012 + vertex 2.59808 0.25 2.25 + endloop + endfacet + facet normal 0.63446 0.707107 -0.312187 + outer loop + vertex 2.22904 0.25 1.5 + vertex 2.11759 0.35 1.5 + vertex 2.4424 0.35 2.16012 + endloop + endfacet + facet normal 0.63446 0.707106 0.312187 + outer loop + vertex 2.59808 0.25 0.75 + vertex 2.4424 0.35 0.839879 + vertex 2.22904 0.25 1.5 + endloop + endfacet + facet normal 0.63446 0.707107 0.312187 + outer loop + vertex 2.22904 0.25 1.5 + vertex 2.4424 0.35 0.839879 + vertex 2.11759 0.35 1.5 + endloop + endfacet + facet normal 0.0468685 0.707107 -0.705552 + outer loop + vertex 1.76404 0.25 0.694596 + vertex 2.4424 0.35 0.839879 + vertex 2.59808 0.25 0.75 + endloop + endfacet + facet normal 0.0468684 0.707107 -0.705552 + outer loop + vertex 1.76404 0.25 0.694596 + vertex 1.70831 0.35 0.791115 + vertex 2.4424 0.35 0.839879 + endloop + endfacet + facet normal 0.587591 0.707107 -0.393365 + outer loop + vertex 1.76404 0.25 0.694596 + vertex 1.29904 0.35 0.179758 + vertex 1.70831 0.35 0.791115 + endloop + endfacet + facet normal 0.587592 0.707107 -0.393365 + outer loop + vertex 1.29904 0.25 0 + vertex 1.29904 0.35 0.179758 + vertex 1.76404 0.25 0.694596 + endloop + endfacet + facet normal -0.587592 0.707107 -0.393365 + outer loop + vertex 0.83404 0.25 0.694596 + vertex 0.889765 0.35 0.791115 + vertex 1.29904 0.35 0.179758 + endloop + endfacet + facet normal -0.587592 0.707107 -0.393365 + outer loop + vertex 1.29904 0.25 0 + vertex 0.83404 0.25 0.694596 + vertex 1.29904 0.35 0.179758 + endloop + endfacet + facet normal -0.0468684 0.707107 -0.705552 + outer loop + vertex 0.83404 0.25 0.694596 + vertex 0.155677 0.35 0.839879 + vertex 0.889765 0.35 0.791115 + endloop + endfacet + facet normal -0.0468685 0.707107 -0.705552 + outer loop + vertex 0.83404 0.25 0.694596 + vertex 1.78814e-006 0.25 0.75 + vertex 0.155677 0.35 0.839879 + endloop + endfacet +endsolid Star \ No newline at end of file diff --git a/sly_examples/stl_parser.py b/sly_examples/stl_parser.py new file mode 100644 index 0000000..e84c6e3 --- /dev/null +++ b/sly_examples/stl_parser.py @@ -0,0 +1,115 @@ +import pprint +from dataclasses import dataclass +from typing import List, Tuple + +from sly import Lexer, Parser + + +@dataclass +class Vertex: + coords: Tuple[float, float, float] + + def __repr__(self): + return "VERTEX(%f,%f,%f)" % self.coords + + +@dataclass +class Facet: + vertices: Tuple[Vertex, Vertex, Vertex] + normal: Vertex + + def __repr__(self): + return "\t FACET({normal})\n\t\t{vertices}".format( + normal=repr(self.normal), + vertices="\n\t\t".join(repr(vertex) for vertex in self.vertices) + ) + + +@dataclass +class Solid: + name: str + facets: List[Facet] + + def __repr__(self): + return "SOLID({name})\n{facets}\n".format( + name=self.name, + facets="\n".join(repr(facet) for facet in self.facets) + ) + + +class STLLexer(Lexer): + tokens = {"SOLID_START", "SOLID_END", + "FACET_START", "FACET_END", "NAME_LITERAL", + "LOOP_START", "LOOP_END", + "NAME_LITERAL", "VERTEX_TAG", "FLOAT"} + + @_(r"[ \t\n]+") + def ignore_whitespace(self, t): + self.lineno += t.value.count("\n") + + SOLID_START = r"solid" + SOLID_END = r"endsolid" + FACET_START = r"facet normal" + FACET_END = r"endfacet" + LOOP_START = r"outer loop" + LOOP_END = r"endloop" + VERTEX_TAG = r"vertex" + + @_("[+-]?\d+(\.\d*(e[+-]?\d+)?)?") + def FLOAT(self, t): + t.value = float(t.value) + return t + + NAME_LITERAL = r"\w+" + + +class STLParser(Parser): + tokens = STLLexer.tokens + start = "stl" + + @_("solid") + def stl(self, p): + return p.solid + + @_("SOLID_START NAME_LITERAL facets_list SOLID_END") + @_("SOLID_START NAME_LITERAL facets_list SOLID_END NAME_LITERAL") + def solid(self, p): + return Solid(name=p[1], facets=p.facets_list) + + @_("facet") + def facets_list(self, p): + return [p.facet] + + @_("facet facets_list") + def facets_list(self, p): + return [p.facet] + p.facets_list + + @_("FACET_START triplet loop FACET_END") + def facet(self, p): + return Facet(vertices=p.loop, normal=Vertex(p.triplet)) + + @_("LOOP_START vertex vertex vertex LOOP_END") + def loop(self, p): + return p[1], p[2], p[3] + + @_("VERTEX_TAG triplet") + def vertex(self, p): + return Vertex(p.triplet) + + @_("FLOAT FLOAT FLOAT") + def triplet(self, p): + return p[0], p[1], p[2] + + def error(self, token): + pass + + +if __name__ == "__main__": + lexer = STLLexer() + parser = STLParser() + + with open("star.stl") as stl_file: + # pprint.pprint(list(lexer.tokenize(stl_file.read()))) + solid = parser.parse(lexer.tokenize(stl_file.read())) + + print(solid) \ No newline at end of file