veusz-3.0.1/0000775000175000017500000000000013325026670012315 5ustar jssjss00000000000000veusz-3.0.1/MANIFEST.in0000664000175000017500000000143413161413406014050 0ustar jssjss00000000000000include VERSION AUTHORS ChangeLog COPYING INSTALL README include MANIFEST.in setup.py pyqtdistutils.py setup.cfg run_veusz_inplace include scripts/veusz recursive-include icons *.icns *.png *.ico *.svg LICENSE-* recursive-include support *.xml *.desktop recursive-include ui *.ui recursive-include examples *.vsz *.py *.csv *.dat recursive-include veusz/helpers *.c *.cpp *.h README LICENSE_* QtMml* # self tests recursive-include tests *.py *.sh *.vsz *.selftest *.csv *.dat *.npy *.npz *.qdp *.pco *.fits *.hdf5 # manual source and output recursive-include Documents/manual * recursive-include Documents/manual-source *.rst *.py Makefile make.bat *.png .gitignore include Documents/man-page/*.1 include Documents/man-page/*.pod include Documents/man-page/*.man.txt include Documents/Makefile veusz-3.0.1/Documents/0000775000175000017500000000000013325026667014264 5ustar jssjss00000000000000veusz-3.0.1/Documents/manual/0000775000175000017500000000000013325026667015541 5ustar jssjss00000000000000veusz-3.0.1/Documents/manual/.gitignore0000664000175000017500000000000013161413406017504 0ustar jssjss00000000000000veusz-3.0.1/Documents/manual/html/0000775000175000017500000000000013325026667016505 5ustar jssjss00000000000000veusz-3.0.1/Documents/manual/html/_images/0000775000175000017500000000000013325026667020111 5ustar jssjss00000000000000veusz-3.0.1/Documents/manual/html/_images/winwithgraph.png0000664000175000017500000015337013161413406023330 0ustar jssjss00000000000000PNG  IHDRUY(^sRGB pHYs!R!RsqtIME(:tEXtCommentCreated with GIMPW IDATxu|Ggvr\܁<(R(m RRhxB "šw_.;\.ݽߛOcwv|g"R$D"CDN#XB|\F9`Xf!>JcJq .9>Bi49Z"R%`<\1_![='tCL!` A)Bу_Nv}t"H+ tN+5Q޹kĢ+5bs!%I%ɈI.!bѠ& %1.P* 1(9aa\.ҲC&8…C79.t/ g\N}cKFTrM/ 1J"!& :u$AiMztXB-IBAle>  d~*޺?6:,VDa zPCIio^fTʜP(xz*BB$Ƅع!i8is˴,i;am6,agjdB.~ Ѕe*hXJ(q12Qio_X/JV6lX6'O6asg-g?؇{IzzFv$IX,GFTZHH""qdEнEj ҂"9:fX, !XН]W3+~*nfC%/JtHu)QBdBײ|:'$d;D!4m1F> Þxv5i<(0XJL|/֬Ybde1(NrG, &fыG Ҷ\*@z,99cA;n-f *M~ Uz\%g@Jư r?I8D~pWPxx5/==ˏ?mղUOoy{{{{{GEE?{z\ɱULE4~<(QZa/p.s6 tMX]¤C.ź['Iz?\{Yϟ'VDa T\AKL$LM'@BI ML$ً@=~aܗ]ԨU$ʕ+׻ϟ=ZD!(Ӧ.m^t,*.mf7ΰ/qɄYw%VUxQZR0DɵeT[P^Ac D%/dbVJR.)Xo .8p'1ŋV1y8)Lwd@P!qSM?omT7oܬPBDD$IڅPwÆ {V.,h2^4G&Egro@t N̵@KKf \hh4Ј+DOss5ÏaXȽJܘ/`wΟ eӜW> ț7u6@\b2b["Аs.D ~ҥ?.Yb̩~&>I 1VC7E|gyb,YidpeczdkLOfPiy"Fx@ vx' $u:X,YP3fX=&:ɛgML ,?K/ț?B)LXF j̓Uo?8n)/v:g%T!iOuȶe;%WD6w7V!fvd|4; aC!͛ _1}J~}b@=yd &_ܭ˗~nLls.|ޢɓV\ԙ >JyS % ?!>ݴG2BېDH"eH/$*'[*[ a^1T*E4-(>E˙F8xE˾ qx%߁xB…`qaFQKFDҠawgdQܾ}ZT&rKMYʮc-4_t5Um37'Ŧ nAa0oXޤlld62.B#FlW=2J]??!G DiDB)ӮNSj7~r^E=t\kJ-9&ʕ+Q$v1]o߾q4ŋ|}}(F:/O;=9'zbQvw zr "07B>n  Gv[ך4i$ j4׮]Tf*U8kAxi@n #+rm]RE,O؃Tr҈:t'B%L0HYRaYuA ctDc11+>:=9YI.lqdAT mU%?QAQ;wrlH##ɜ*?4O~~?,I IE$i A0 ;zxtjaa|4]M&OAM˗//F:I޴> H~s]d6tY Dli5VRWfQTJ oN~t(b)QJd8 ,XD:dĂE UO ,IU[S@C >P" ~n%?qry39,|ܣ" > 6  ^Ͻo=Z=lj|gaGa 裼` BYr6绅ga T%da^bcj1)@*U,3S K-7(YB2!2|*-,FXHsʫbR@J@YVĉ5[rBc9r˙I s۰mr@F¼3 CဂPGB :q,r/;sM!T] 0AN;>iy^‘[Nj8\ffc8ȏUEZu9X կ"ϰGvQv9M_3p| K;{;|\o+3Z+JRbm Ŕsz"֞D3_@`t DZj?mIy1'(жqpZJWg3u( K'';4P312_a(|78T:!h\8́gY"`A㠋DK`ca9TI{ |l0_)>8!*eVd98+7ġS+즈VO=ٞr%4Z7tW3G Ml/4ϕ 2yA*ݲ]QsoX?!ba>Pl?BD}Dg7šh h45Bw%b\خd,*iozOKa׆Kw:̚I  a/iv!Q)SzH}H"yu7@U˧R,f"[63HeB,/Z>N?+lWJ!QSMW=$vٜTX rM4ߵdTWEi=94gφG[AF̱1tpKFHlB%Es+ 7eAaN#ql%r⹙r bA$k:PlNzN,NN50_Iփ4+Gf/:g-Zd ThLL}Ro" J8cɰG/,!Z^f,_}f|r9.`wX;G31qA (]tD,@Ҏ-k.Fwݜ-!JqǩE`̊1RJ IQESX)fGl0l m44rɂAAo !q1|^ {F $6FnA8TWѭ $nE%H h9:Y,4v/ k۹I*vP.M Wi6F'WF%:pǩM0Q3\KSٽRI[ ҅t# @G4Ұ{BN#FȦkD4bw6}+Sɠ@JeVE֢p]zҶOE^8}rxcIZ?wp[)b1t*Qbk郭7-[V)SM;&rm/[N^[P9i&̈́Vk!hZga)dTp3'Bנ2GC w>?+n@EJ"q#7˩8;E`SSB%ksiT.Fc'H:gqǩxčr*Q] 2WCxiZY;G=*ܙvǩċ?N%w=8ƣl R(;Q(* honjN%d'N"#B`\',MYRKIl>>2׷r?NH"t̅B-eAzϡ{CB%l䀏%ĜJmIaNp%X 8 |z-149}$Z(qY_V),}0mK 8):smzͬ,%Bb_T*]}5+GΈqHd%ò^TtR>%<|x'z=JmVZZ+W2+JN~_^]//KV{=h[B'4jUQ`DBȠpGZ+a1ƧXꀁEFrjJ^|EPDbVc0Fh+G8qVX,ѨEEY ɳW&NV2k(/ZQ.1Ba< IҀHҵѣG޹}LML  mbRppŋףo߾V#0 <ΨdtiʼO=,NOg\zꭁ[N7Y ;rԨlNOIh2իx8w1yT_|EHHHVN>}aHԠADj}aĻwnݺSAL7<{rJVSSS+,]pE"B/Rv}m۶ڲe6m@`tE(pʅm9)_"_+mڶMxSHݻ$ԯiY~t钽0 IDATvR#"N?B@ѦgddffU\u  ku ]{?~j3#{tF%E?3ecb2 Ϟ?\t P^}?qݐ-y8Ĝs.[*)*FzDOăGreKUQ"6"'׎P'hyR*~=Me@o%)_vGy|q߾w/|W5lPK#IҠWiԠRTx 7;#ӰrCrߚ]\tqo[p5w߷>lքM"$̙UgmFqBq_uٲe6ܹsgZ^Ɖ j53sYݦ]zx=dӭՖ}8[7)nhxF;q60AyG 0`ML&ۼyU 17۔.F;{9~6;;>3f ʕ+dg CGegggffT* äR)B0LlŪSc|ZɌY̟_u7{}x?֫Wwi'4َa,f@wceþsTbc^_~'O͜c6;j ^AܹsO*V Tf3 h 6짟~ 8p˖-k׮X!RU1SUF*:&?F zJ Jr4)Okwiﳔݻ h11Μ9r 6P0y81^wRJ'OrBB }7 z |Ү\ѵ[-W֮iZa5Gw<@߰ΝM?kdʣG;ĭ{. ֩Yέ W z9 h ӝĄ/_]tO>AG޽ -qRX^ kU*zիC-Iwu`ql@̵l޸ŵM_S-: U('%m޸"ϟucǏ;vLDb0t]mV+ݠAyF;vZǵL4cX}^D"d2T*ȓX,y<ʳ0p[w̟wySO<[>y̿W42ά3;vXBY{Ap4UE7_<|g<<60Jzu?P͚~Njp x<-Ġgɒ%>RW_=mOjժgϞ˖-V:KC@4B-:A|Dd책NMl:W Z NB,3Kt1~}[B]b=}uF_df;{fbc;/il/`jqqbx]ZLj}J 555#3'+[T"9ݱ?ok.ߛ76j\~JĸP.<۶m{`۶m]t |Yю媄rW%צ-:^vIJ?Pyjf͛ϛ7H$"bСW\ڢF6u5={tC7[m1Jz.z=A^^^ S.(P*$i @Oƍ[uYK_ϭH"|ږ|xϦ{aQ>џ0q<#guѣB E gek_߹] ]8KٴsfBqQ7w\U++VCcO ;vT_RNP͹fYuv:sݻbnZ cɛQ| ޽[.~дI.=mժ}H]Fm6KOOY_CG!w$,ĉSL2&fRZzf=yƱ֭[O?n:zp|;ʶ?-'wWqfpk^|٣yo]Qc%OM(wWep`*1eFI<=#Uϥ*3ҍ='TP7{ ūVڼys !ӿk!c<믽WFl첦ԥI C!n (8yzDØǎ$b|U'_~ 5;I;woXiI:ɽ۔9CG~pOxpW:FLv=`wM1Bh8 iOq$W5!;.# rW9hiRT uJ塕C)[2;}J֐8czǩDB, Am/ Q^Zjog۞={7oaZO>z{OGC޳$?J gk&mئ6ϝ⫞iYc\<O" ̄FH[3CiӦufddtn0n?f+2м2+oݍ;qM[)AA8O&8+Gz+nȂ[.K|-#S:thX4_0hiJΝ{^V}@.I;w5npAPD@ p  4mtúu=zV(a7m>z3/\PFqM:NM+=r{qq˗@-:A5WuqRar-B'/ϲ]:ϾGv\;/tK  Դgĕ뗼߾}k1HbHVVqZvr!uُ3f]"N8J!?k?7CN;3ӏ̇δ7_5ӴЖ?tiCo?3Pkw~z/;o22GH ᕫ}?4 !$J˄t#+7S"fmZtz= ҵ+q/}&w>Onx+4VR%OOW^}U7 &|%lOܫÇdeeLoԨ` ϔ5i\X9@V풍O<`\0vUmZ!J3g4wlqjy_dڛ,1ڦ1+q>SݸycֿS[uZz܋$&^^^)V 5X(Q~ l5~G"zmƍڴisȑ_V;eiƍ/]ϟsիWlBVZӰ7 hѢ7}z!Й(ԻέX!^ju ƣ2 48Ѿ^~zzuLt/;)QebɼkxD|@Y\>lذy!!]tڹk ~8qMP(@(tLGr=ͻZc{θ#&>ӏ7xˉ](&f?U\VM*"^;]N=߿w屢?o?S>XӮL|ӏ9dЫ>UBF%F$0b( 5j=~'ٓGϟ0a @&SSXBȺ.YfK.UTWiЛo$W?]hu˭cn*3HIǎ3kҡxx>/ 5T"/Vf+^9b011A/4_I:AO~4ˮks5ZC!x6BZ@D3yөT9 32LU{ ! ުsĢA &Qzj޼yG۶1cY:9mJ㸸֭[/YC 01yh#,dޭА+zݭ׷f?ֲ|Embye?H ;H$+F^04Ԥ\3CJø/^>A l ?|lwpP+NDFF"hJxcǎN4%>!aOkm۶]`Ѡ8qjk?@O LJUdSav"IRaaXŷqR3 M2@"P1S'* ֯=  =wX(o^?  EB׋5[ƜE|j'-6_VQmkڳg;v\|%wkԬ9a~٘QPXsu>"h:V_DL!Ap#-[yKոagkБ_.\9Er/\:ٻX^ߤIͺGȑ#VRuٲeq1I:.~T'62.?(C.;3Q u"ܡ9?陪޽{cJK,7O6/[C?>{=3'|VؾlLnʟku/(8X>g|*V(۷3++߿ܹ5k6h`p&Oѿy `PUeVZ=+ԙYDv:]8NxX@< @nn\33d$ k>ai@9YZl{5ñ5j/WIMݓ_&I:@2omZ{A3~LMST<}be4˄4^tͷO:ҕM.1N뷂s}zUCfA`1Q2p"˾|S3qƩT*k>LJ .RJrr~5auTk-^ĉ+8a?@ǮO>e͏`4ڷoejʛ,Y֌/!x]'}YY _򋌔m۶:`45kFgff)ٖUiMOUe4&Xz5 ( L"O2k2"sĩx4S,>ktmQgh%VLsKIhF#ϣC ޱ{֦YN>} U?r`z0fp%u½{D"R)ͧ8p`r<-- trW~yns>D/+m_ax+ߺ}Ua!6*5%qVkvеLe# nS#Y2alqy3wς&@mh߀шv=kۮMpp I$}|}+V|u*񓨨r/!_ʕ+Za'\ѣK-|Ivm:mUp|޼y+W$t?q=%[- yp?*UxY 5[Ҫ.]L. -G+;4رc׬Ysڵm0]1m_,\xQFkZ{\(>^BC6nXݺmysg03BV9kvN<ǟ^>F$2k):u>{@FNڰp=z^ rL0W(ȴi[մ\Hr\!M8~٬9!2̘V{6h~'MHdsм9P*s]W_~  %?ΟϽիga~OΝ4ip ĉvڴiX, 9h?ѣj ?"h4'޻%3W;~H+1 Ůڴne;]T*ӦM_:w7+$tWg«IWw}y+Dh5v3q;`4C:osĩ&M>ܥ-3P̾rz.-C:ulwI 'p?A1bQll1cm۶ ڰaÐAmmΞP(ZlfSuoE`-AiYjVwI.\HHHq<22QFb!×-[:v2'[3gɓ̛7[8q/Ao盌U ֵʄ)MKOhuGQt[u>V +"uO'e||—_0\Tp?9=x1 s%%e&&t:qf Gaύ;w/^lqHTv-Qo 23g`^ǎ}sqZtʡCva̙ѽzr; 'Olx^= 4ϛM1V-~>_?R Kͫw}9(Ó_f^c-{]ݳϘѣ+Nر]jjٳĦМ\ޠ~6_P鴚ۤ|H F?A6jsr 68Vo>_ ӯ_$*Q9JO~zUg[tA6h^Νژ45j-pذ!w[rԤqF2ݻw\iN8q'eu4" bIcPPP\.3 !ꦩ)MD*C(<㼜,(>Vj{U_{P.o=QyE^r3T3L˩Z9Zu lׁ3A׮B`~< ڵ#g "A ZD@xo7e˖-[;|{21M6Y0㭿hYfvTڿoBUg2eg"~-F!Β_d\7s3zVCiA;ht11}||}8Y\.D̬Ǐ'$vDfGmmĝ. ԩ!6ۨ6ms>|ЩkS5xk|2àw4dsVi4YȘ d/qPtm[ |U.b˽]>m.>8%%ի|nVTRӧ-x񢯯ɦ.\[6+W9|l .]iղŖ_IRye5q:L8~aHBRif}HhӶoTX^tZ:T΀Fi4vԬk_'NJ%^^j233O8Uu@dV8\;wHOP*] !ٹsg۶mM$f͚<9VS M+ʖ+ "]Ǐpƍڥ%edHY7{sĩ zaSþ<\eIJe ,,ht|>/< ~B$ph:8b8X '!nEHS Z3;Ѩ?~Uh]A=>AԫWo~5lղ=7%usĈa_+ġCG+TP|i,2*/Yr̤&vډzB|μǎw~ k" N:}MdRIn_ϟ7ln`_nN{ꍸ3g/լQ"'=w~y B6[nNm 4G!B;[fu Ç+~^d!BPvƝLPyS5yf-(9ǜ=}.(ǯD/O~##GlyE  DƎUe6]A\y-A?tkoA- H%fTEB `|OнZ|Ӄ`Sj !:tG[ٿ gm'*3j_z^O=BV$j Y^E>N[qrkvJϛ-[:c33GھwlfcbbHMM{x u~{toJJ 9: !.\|OÆ6}ڷ5  Df/?Fon"rzN2uX bԩc)a2E݆ DbBCrC&񼤘Xt}r:Xtǎ zjZ 6l˗-/Xb萁ZM6TR-A* 5j`F^PWd2YPffV>W4U\,nOEd6sha }ۊa#֫̓g^}9ºtPϑtT]3};0o\K. I!ZB^*MfA ,R)TE,*U@z!үMr ss3;}3ofZٚkND̥\h^| [ǜGO~ٲKa䟣.-Sѵh\ޱmGt'/m_V$iڤ_jee|$@/&ӆ-[R +(Ta APERBHۚTZH[{vGXPȐ(7i2H]缼}iҥFueӼ{-: V V; .֯#aJŚ޾Xz9 q:ԛa;~æ9OW4RGB^|ϤRk^[?)W^ɘN._\Yr5W_.kT39gKAA2r pCmԏ=ϷũQi{ʕ+Ǎ{c(ڽ\QhXEB+J>v+ؽ(3\qB8{mj'0ދϜ;7}7O>]8_I[HfH)(UL@ g͌FE}.RxcJо;Ve&˯ m k :)#4D WW_퐮{#Gd駟vtbq kHX6-[%/vƑQ۶|ЧWF =is}n-դ  z'y SV.jDjgc._F_QN\ÆM7խZHHhPǎ;{֬wu6]^ax6k8pbiN߿Bb}5؂"ץXg+b6M@1Hg?`1K7g·#ߵ\(b#f>)2Bzh.ik3gt 9ڤq}- fRuwlʳZ (Eۓ״i+ 6kW^}SΥrrhdʕkl`uvnNnF ##k<3uj\||_~꟟ѷ:>zܴy?K>;c6cGQ&AD,&\l< bB R|$)pov*w&}f^NOBؗ-[۲wzџ篙S≨5SL6իIII?p}۔=Dw%?ly޸r6:.X6_~k<ύ|mLTpO?Vv@b(7 |<畗G Yn?##sjI={vmPq/\lܸai*W;n}C߼VO{ ۴zo?ʼn32`{+n_4 o#mX(dzES}VL-P͟&<Gͻ?TT fV2,åY~}7| EA?Jݤi+R-k:ٰlFQAXrɓ[4߿jG6>ڶkت\uPHv1e}@+ÞtLNI?P(Piw•3|2=K !(^'0g zI.B)E'S> \<#7PCz#xأe3 8ÏdvNDaأ}oRG(N=wÇggg\H[hiDM~<HKe/ޙfbVC|Fu* -EͷEC%eJ:2R,dX̷L!/GҋJftSʉ"{w ΙJ=KG`kK8/LE}?zHj̹-rJ._R/сQ~{ӺrʹƵ 4|֭[7k@liLz9Q-[.ս.]sǮ׿Bm%`!ڵԈ@"cN"$EZK}ؤZG7yP}?xvHughڶhՀBM*F`XXz1͠MRQ0F!\ {S{|2W-X" #aa/ԃ& Ļ{ȱD  *ٞ=A#d@-{w'٪lhԦqO+*]tئqxM+8cL6V4;Ê!Mǿo<%41qqΝoѢCR `04nxy/R;zhDb_0AБ#GFx#;zhzX}`1y4dl4 Gd-Z<ꭀ7'YO1ݞS{ }5#1_L- SNք}ر/_>pDVpy.@6Z}n&N`rBgM-D:͓0 vr~gbE#N;@:M\:r<7×M^(  d .4TdogkTSf1PXV\Ia{w3%zoEM(8{ҋ_C9Ze:-AM>>OI߸.q^ PU5 cC¸ʘHuBf'Jl&lV#HFJ `1Jh&f+ڿT5mJ)KգSFg~NS٢E 7Oq{68 n+(;[Lc7KA3rőhT~4GAٿx~S|~PLŢ`\􋁏['.)is \slO v SS_>Z~|x|:1I=?y{j5{ѸA ,V;y}r Μ9k_zС Az7Ro0;Bz$$xQTS7|bzop8XB B^H+V_J)oڴ)#9ޟ< wOFh(kyh}:vg^tC勿,׀w~Gn1m۶p!pF Vk&gN!_r85@VܙC~ $TZVgٻ?abfw{ݧg̝P>HHR%4EJAhe %>wleYAY_~Ltc[#mQb){وSJ'LP08SٴUsM;߽۔M[[ fٳEr7nA2,p!Fwܵj3wNի۱cǏ|Lً3$m;yrٶڵcTJeo4l۾۠~OɱlbBU>=^n/Z; 5\ޚ/^d9^U{Wy yōG1;:2Xn/9d!բڽaTieJ< Lf!$(S( CPB%Nu67[(]ַ!4Dw©̘T;a fy;oHuDGA~߲gSOm~C՞QO]9J/Nɞ8IZ)I#Ѱ'N tsTpwn߱[n<+%ߟg5ռ;~IӢW^?/yu~-k'/MY˒O7D7Y YDI0S;X1D^mdPE&&?}LttlusT*K/>ύT*u}: DڪemJlv>V(SDV PdG v_O[Cpy E-Ӷ ҹ6_*@WrHps((l?*|7&g ?мrW:u؏?{&~;K?1%ZacE 9Nܬ_簹n1 ERS$`pɕ+W%GU$qW-k\uIE s* LFR$@j*jiΗϼjI +% :ZY76==1o֨; z~ph SV}^^$ndvmVN[7Q+n5nzHj"1YsԬ80tbMSAZAVu5팼w_}aȢo6OO߱"8ݛ5iM_yǜOB>Zv?o4v͟0)Lۣc.g`N"-tfʓ ۹k'B'NxɘC?ڤq5"L&^߾cW]yj)frQwяNhմ{OB$J;۷yDү_1cܹ^[^:[9g[DMW0(|wA}2EѮ3Wp;۳etk׺O?L~i&4mwy_yZnт DzV+_OW't'NN8nX=8zpH͔XnKw[GGGM^fR8WRN7P~T侘 8ͬ;6Ovݟtp.XuDe}Z6!@R7nJTvU?lsD6ot?Ə~Ӳ}y~N'WV|󐹑:S#9N:Y+;hpevܽg{ 4!VMݼ7Z{ 9zfGFѾTؤɣǸg^ZuX>oE[U'K F׬_.6ks3-%3kڇ_ƭ`M83u ?K/>_,`}+o5^&D r?_{#u/wx^QNw*x3ٹӧNLM)/|aZ;w@b;-&ݜk׶C11M=ܟ+Om(9k׶1K2uzQ]>~zGʏyRD~ 5fg$vTKo[B}1K`:$ݾecQ %vީS_~E(XxUvҥ \v̙u_ksTLE]dcg'3}sh˻^?2q]~{XQCoB+~$L<~d| k,XA $0 F\en\<"&Z\:+'@/}qErBUZ*Frr|V.\N,ݪeܟQ g^Z6!2 =[Rj1덆l#A[XrN,-"d/XaDADAĄ{}'S6Rӷnݲn:r:`B-M}?fd2筞yx ر Y٘;iī)WCA5eRPUċDT=j0!CoDT.i]gh޾Q=YjQϟWfß1oڢ`ZvҥK7oުU ;[ ֭KII)1o߾}cKMMhm۶iF}Lu+)&d s۴i j[.Wo1H@ڂuƍ%4Wݿ}!?v-R`k>BRh`Rs;v6I5# JYV%?v5,8{7ӳ `G;2iכ&5\Óx > gfo^fK;ԉ)ڞjXsn`0ܼ& T]txSJ쯾\?mԦ=b; _=~9gOm:}c1=;zWqޝ֬GlEq/lMKB})b&w;Z-jU}Jfc|4Qb?[ IQZiSU)_!aXȿ⃔e͛7o޼, ٖ(Xr.c({%HF@HlYhC k.E-!>h\TEؽ?mc۹8o IDAT)ΩdHͨW C@ [{'#㹹yr_/ſ% ZEB?bS(._:PkŋUq&=#b/ BEhc9PN|w-̬CרQ=((Aܜm۴V+KոI+A0KTq #+GCv﮽EQ3re H)E+4Fԩ! .\['>`̹'G޵K9 jM_ll*))U /m> PɊgI3rzARkWQPEժ]L9 w2J> A{KNN;>Z tto!`PHCT(9MLpZn0fSZ1LIvU^ߦusm BͨRWת(۷2eg͚G"##ϟ{8aDS :?V(XlJU#{4k վUa R~⿳k֬=`#fyk T*wu豾}4m\fKU)ն;u)'t{v=u}4L{'3;"<OjѲٔk}G℠Geg͚U[}i߾kg͚ꫯ>/^o߾1~+VԴif͚>?^kۺKɉ l1`:!rS[&lyǐf,wFQ8`0<LO .]:תU…yF(|g|N`Æ(<O3fěZz>RIԨ׿rJuZ刑/wԥM_.YqܥKqqqZmTxLI49ctTg{^,_{֘ 慄T3fdz.)SnsSα8JvzRUxtRiJȒ{ 9c~_ULb3UFlƿnLv-3M6˻x)/,8.-Vnn^ݺu MWڷo'gֵy<`%<)m۶w} oF%xjsME}9Wvb7߷6RZT_3RZa Zf͵k׫fg\{ª}-Zeffi4&I `uVby)<@v#Ts#Ŗ7ibwғH| J%U];} VJJHHhРZ,:{2(J(Q b 2/ =劽rϑDl l߾]6%2 aFVso2Vp  H3RnQ(x jEFcU  ogeyW e dDamE_ _H8aFx 9s[٨Jj=r&$$` kts@+_"YjD"Hyp롤=zuVɩ8o< dJNNNNN,zC) '2Cb% ⛄=   H83  Hբ 1"7 Hl*HA_AP"fR,[W R7##åiG~Ӹυ&ž987*$<+ / HP6faqn ; EJjҤR45$luA!E=QUb, qk5S5Du T|^ycs)ÞJdpBӦMdkIm]! wh@,ٚv%x`pKBe"/ #(G7hk,KlW&I]! HcdolïڧչT&Aʄs~tp9ߓ0⧸-oT  (!iBA\A {A H1k N:zۨAAXGRܾ/AWرAʜQX ^sȑ A| Nw9hȷZD2X ^Ad2%''б-/60lA*Cb% ⛄ӧN~2nb1lJ$ԚlU_;?K>AA_A_uXA_AAE?&hR5!Ζ69!!eXا,j;rZ\ALߐUj`!6dwSѐo# ?)GdaJx.P! ?[涎6Imqٹ  H!GeS=qR䞀JOv8 3uTxydoS0Zpָ2 ù_A{~p vN̋C )/ HiD`!W UV:79}'a7bDA\}PR - Cs|>ΕçT*@Aܞ5 ~ K?#AAp|_?m_ a ˲ ;lBԫ))*jĈW+^aFȑ#Νz@tG9rHZZ/')ڰ}>}( 5E&s~H94o~ĉ!<<C Xu *jСX,~q_BOyޖRJd"d1E<)sd_A{SkB?xyF@rg d0{R)sAI{[Gv &@$\5+>#X ?*\qqnԆ˟J=s/aPAA2|)+xSM_!E׍;O.FQI9  +V!{uä,QT Ui~`x0/ %vr̹ 狙sU:GA߄WA&n?WJR*W(xfAJ' ʆ(I($þ=y}]I2U(<`7OQ88 H+ !rkzwJ=zwJHsNq*'A"m)K>H֛?7NRąVzVvVx&@q|Ep%̌;4ꑮq&Vl)A%E:GRύ" roOEQ ^PK{(g_G{4mjͶmwNPl˯CW@cN @A/}n`l EIRJ춰}䐇[O>J@ԛbd>bܹJeDD0 Յ Ƚ%77Ν;`6/ﰟ鿢ɲl='!Zn Omr?s÷zCSy,;ڵ+<3AAA͛7Bsau!ro~={d!x 7m{z@!}v?7lxPAV~AQF5G> !@Hif{d?<7mS@Q sӜ&l 7lxDPRz%BYjFŰfN(v&fNQhWf* 7?mx[DAP!jQ:r`w֭I a! MLJhʰ?m"'cZYO-re@_A\֮(t3>>^DHKKU)U!B άڰϋ}.We%Ducl J@183 HC_WrqbngUY)jr! ːؚA^f\AL "u sm@`Q˿A^ A'ӪT> @ >o[tcfl TeCచKvq ~iSG~?<A?ZFU޸U5 ]jM)Bb#Kd_A m[K3\n*91  W0JjOo޼df1] w/aP/8 Ayo/B=H"-g cա{cV^FTM㟌snQ"c/ T DsS/cG5nR.hccJ#?j[ءB\LWS2s POww@jqI"wR&3+++NZqՄ5 / +.+&J[^i׮_*0! !@ a#0a`Pw&P @R @)W[h@(b,7J $? %RD@7O)%@.HV KT"@)!$ 6WB S !r%oR aX  D(*D$ r J%J%"IMKD*Q (HTð:0 &W( +sH`6x^UvG>8 ݯ؞*1y mǭowBpJ\m.+R!͒Rfc:@ۏa)ͻ<ڦm oŹZcT&REAu5ZVϜ`X  38ş|e`"/RlذB0,X? * Ү :U$,gy|yCX`B2PBJ2 a+JGARRa@)y?΅ݰ]GB@(l3>&#Amxh&C:E@B ?% {1 qJX$&)QTX(1H FFF,k6%x R㕢(]t.HSW,pEܨMI8 -  @sAA:h6RI:B2؛7S__ He-0\Nnn(EoP!U]<)یJl1#"ty #qAsaX UY1\Τ7JeV8R 3)4%Uv8pAg괩sR{ g4Y/&߸pbvv`PT!!!uԩ_6P!IbfDA +B2 רoέ O5EeY71H*8yСCV56vݺY׮]z^߶} 5j٘uFeHQ/ H)b7O:d3v%u޽1kl6^vȐA bν mPb IDAT@r;Ԩ!={Ɉ  &>J!.hר/8XMeYt&6h2a5;Ѩ g0|)`ܹcZ/^0]Ν;veM(>'AL_2>2:~3g)QB[[[+w?k w[OeiPdjvp}lO ӵH)H$ٳm^׀ԓO\G 7!+)T0N=H'>ɕ+W|hC{>|_s\'O#/np"gUkG<22Z fӦM?յiӦMl4/[_"a+JJ]v;{f%G,?%KácoGt$gCC>7)}u*I6X9sfu_t!Q(߭_wY[4^UPG@CG֯(\9 `&̝3+ QPq<)>fmnn}G#.V|d2.{b )lysܒ:S?_M ?7N `e-_4Ebl|K 29v~(8HDDRȈ,7θW ){w\~"ԴqF @u۷D$ ޽[DV8Ws8O~*\~֏Ҍl"]P:86oT573mMdȻp_ pt,>1z_ԐsyFq ̏Bg|_81/0l>?BD"ڵ'8`:|>Wz?z=Z."p! vJᰈb1V^1֬7)]]]~;nN8sωȶGm0͛6mVWWgjf;^0;^G(; p ^P@@ PfJ_N_tJ_2c/P:p c944$"n;P!&Jai~8+Cuuu~T9qs='""P66mSWW7jf[' @@ PfJ_N_tJ_2c/P:p c/P2vH$ m޼`7;vk喛m;% =|jmƨmzp;'-466._(i" 4G` V2ap8n(T2555Qt:2ΎT*xfpjD0)jc26mB;DD1/,>t5\h!LPZ\á/^LFSww7j6[y/y;};<Թ~7d2{U\88q6wy037}C?x9V2sә:뷀>y+;ʉRsnt*FDt2I&"TS$Ƽ@a/_7p2<'@AWijj?Ap_(_%J#l}hƍ1 W2_PZNFMM-_G66X0J646S?.w,>tD"/^rsY]|1y_oۡ9WX?Q +T{^=_*L^x/:sfرcG}kWx=y\濙|4t艗^z o755cŲE ^G=α揌Rd:+[r"yWڜdgZ󾖼_pp^{D"z-[o#HZk#Ѵ: A'?ٙNЭ޾{BpXu˛Q`H"+N*Z&dTpp%ɓY oy}W9s&H+U`[[=z|?p] T6;7 ThМV>Mi2d2=g?~W O>okZ[ZbRFtm_=#跾v*_v+Whigr*''}AӺB|%\{'N+pûGG#́?wGVt: }\ȑ#?t(_rW_R 9ۄN'ҥK\|q;~z׻޽~xv>|;o6SÅ .?//>߳>_cw-@ XXxyC[0GԫaZ.W5k{vmƓ?ΛˍnPxwlp҉T6ww}G#.wSނ͛7wwwRf```ǎ"r5r3[SDC( ɝpͅV2גwzL:tx41?K_nǎ=hģCCAڕpu*)Sww7-[켟* [U&aQv4Ρ ?M>H4w$Idz|e˖yܑѷt|׶]r4</ݫǪU ^s=ߝkO|J؟ur4*5>|tٲř99oMt?L_* ٳׯZJJGF' rA*̇3T8t0\ ^eL3LD'\h L#,jpF 0Zt:H$gt'\.⺞b/xKRi5kvѝFt)(Ut:.[q6ShHDD{il,Z_.WfX @M߂/ wξHx,<;O'ή3h*:gu&WvcewU ,M~$YrVkRhp踈\c'kّC{2M(bÆ >UD>~GIOk3RImڬիLLRx\kSİZchxp7s[+Nzїi}|a"35j}Jn={.a/T" ;|XV룙NW&٭Rɭiv~h޳HO^V~(m80o+z"/TS;Pe=4f:m+Tz[^,[w7 P'}رH/=w$_^LtloJT`|5y<]j`%znifBmǍ=?Ifƿݹ%3͂=+VۿoML5Jz~Rn@^~z`[֟mlDO/l.T_ج _d4Mo^{NL閞Ezm>== t¾"*Q`ǿ(?`/T$l;ED@SVzGno3i?-2 _/"Hm]5]OZ[ϻi?+4F!ox{ K>_vn`'"W0hg(*=uww[ܲ9=40/ok`o.WF(C@$ͯ70J էNdb nl*(&LEZ/OyM kN8'JË0Nj/G^ookq9h+#k/'򟃅ֶ٦6c$b OwQ{"<=S T&N$$ɎY`:7[>'9IDATx_p8LSGcd2i$ N&X45U+o즾y[ ذ8ևn)^km_h$Lat2. /g)[|^p8}Goe0r&ȂGf_cR$#kWYq\hC<G\_氿ry?DիtM7Kwwwoo/nv!"۶I_w|t~̺Eo ?*Ql3mo&l!>/*򟃹 ѣGzrGq\vȳiRE[qSUކjnY^7nkdd-"à ^!%Ujkkmok޹lEŗطpVW%@s"uBeÇJ[Z]].\y:)?lO=m1 qh4V|a^4i@9J)z&l%RJWS)lPGq_ p0]Ŭ =>uOU?@)G_q_ vwО=#JRJk}uEkt1u{͝dRj]{]qi[[ g!U١CK/Y`6.C\wώ/(TyJOx !'piǭ+2Ӛ֕5wӦݽTرc~km_:>ؾ(ڢ]W$[v}}}T{Fmft_&eRLsh+T& <Qmm_h233Ggs0TN]mז9 ԧol۟Z?;95 ewZ[rZs7.ߺML&d33V`(t}D$Olx5GܖD÷fp ?3|V(Mg M׭ֻ֖[DgwwN9_fFfڼ#CCC/?p[_S7x3TR xߺ}1O~r?^xѪU?q׿嗯khhk^km#neY4 -Ð/?_SsaL52Z[oS0_y<^K=dd_Ks1RO_SUԅzqez+g}vddeߌV~pĉʗݵkܹ*ks?u+y'tˎTe~:ep2Z3gDQg%F8uuuuu-/a;oe R_J^_J-9A9g? w3o%KRU wڳKڈEC,^`|F Oh:+\cM{|9^_[x-o=o\?@U؁7Xk&Lu ˙i kLحϳ?zg^_ @q5 xSھ0T񂓜9*)nNyQ\]@k,$Y[-e ωV֜b}L9\[x2ܦ6'608eˬɗ\'݊}r9 oMXޕoz_$g[y︿9323ge7?>˴~w|sj/kM0IP|1UK徐2-s[f~pxpxfNE' Mzpgf `(t}D$Olx5GܖD÷KZaCDLT"nhF#Ch`|iW$B<5ɰ"V2%tjmiE|{| {oOD WêU(5ARJ WS ?ZgE< /;WTggǍ7GL3Q"#,رñh"m9P1kŪ"#(K]_v|}EO~r?^xѪU)?IGy͝Q*В')1 JD|` uZ:Rj2?ٯ>zs~9\g4MN L0ˏ>>9s?7kzkG#jZuQ_o _ozf$mffOOi% Nř"I^O}_|4T 'f1qy_I4N:gSt:'}?%nu|ƪ֋f>ՉT7@YM]=i>ʥkAOiH42{vgt221Z>0T8v70ju'L5|MMMTx<_9DDeGh#/C6te퓝mZ[[[guvD1j ( ^Rn85x"T2yϵ@^@%ScaC^km.CK JkdddxŋWh3JDcX[k[s0^כyyOD<ޖtZ8V$:jR2RRRJQJ  kk:;XQZZmZh-VS{z)JJkDim[֢Yψm)Z]%Ғ*D(e#RJD+kZVZ)R"f(eW(k}V)QjO,[Ț6ֳ9pb*-hѦe}DM끩Z+iԦ7u0\^ 5r#X,Oput))yjh64xcq ic)jg'>h,Y_"}e>Sg[U_V̂r.?8OΙzR>!֜o+O [@ّ;ʉRsnt*K&b0S;/ߺH2vj j%͙b93!WO83Bp֭Pjl"gfO<̞̈́"P/e4-tP//j?YE"]DQjj8Y|+XH$<tEXtCommentCreated with GIMPW IDATxyFy$Wosߗϙ5v Yc.6&!x׀mpƉ |6 8mc=xOO^jj:J%{O[T*U=ѣRNMM0JST:TbLgV@[%m6-|<ȓ-|ؘui(ҖJrBSL$+j*`Q/A BEKCT BFT(+![SP@#Թtd,Dd%~n'/6󯐐;^w o,qIZo4)\٨q,UKtBjB!ro''fft?;44jlv jOYG; jC +` /XCf ty r+ed RSgff.<0<J(;GP0Ģǰ 1 "ؕGI]ISO]ﶌ+c,q`>$#!JN]Wc\!T?ubnN&[VZȄye2~ݻ8bjZƴ&)mUNiذ?z #1!I2nJWk,<ڽ{ŋ,[btd0?y=g&&.se=nw8p B,(i>1b!UX7aa{fK "z ޿Χڽ{K˗-'Na.\8-^'R,._bdtPȟ_>YƏ^ŹVL=ԓ|^ sؖ-?{߽ZG-[;Lz!n,.DݔUM)f5by%|u7]uP.H7 $/?/^|qmWێ8bѱ-[.:p`w;F^b3gJgLt`ttltt˖wLFW,_eR+ٳO>Y 3dtldt˖,_7P6fkʖYl0HmH$֟80QJ4BtJr,X_^EUU}_xa[K.L5 7-J8Tfc-O9I>WFT_"1Zp"1V&sٿ-1"y'guc[,S9nB? o\(ehqxZUymom=Џ땯>vum ~{8ߞ9LTuZ&^:~z3٢K&ݻ.͑ ew-a:tHUkI:a[,9)omD䩜q*/iӧ]׽nrA''<= ѽ##c۷o5k֯Y]J BJsyB!ĥ' y!6! =hkW\a*_*媕DbٳIIU~;_xÎ5%NܓRPȧR!^gggk$IJSjEUUIuܚŞ69NgL!.!Ʋ)4BRri:0bc)^’,!R%$;,h2g`zĦ4[ mD2] 69L'jdشAÞp螯&8F/>pݼYku?#7o ru},)}Z+~/߯Jf^TdI՘N釆UUT2)Tg{:+ \6ӖK>Q&1MmG-W+w\~1L>"i]AR:r7Xcǡ9Dj]X$[$ _b.7ڵTUVr*ekl^(U+W|S_ a{%YRI3gZ;λn{׿Y[Ť2/&JpQ7G~׽○,%ŵIe|UV!ԔxP'r2KI_}b$ JTJ$L:d"a^]KRÿP* w7ǕJ:Ŷ(ӍNt3Fƅ:fOS뉦 ƅY0P < @RjG~E(1LjlJ\2tvrdRqelxdDLn gݒL&l1jJ%y` 'QI˦5:l8X Ll QκbNJTejgI)5l$*kM3Fg;97ݷ0Bc#NfgrO###^lvaYjl<$5f?099Ϳ?jSVj:`Q]3+ǖ,Yl?Et]/15 Plv={?{7Wʘ~G>d$ieQL&'??2V]?Z(Vgչo.`L+J!:WZ}[uŋrL87J !!Blx|g].b>\ݖpeFعL8)2U2R&}NK70O/Q,j6^K0ZegQg(5XTu 8t`֋sss?Tyut~W-oy%Tf~uWύ?K)}V/I.%UY d~kvznrjRX,>[  fZ%x^d~(E:3hHp739B]-dj_4s<l:U0~ųd~ceuXksVe1w!nd^F0_??g,| |d>æsz#T^V0gy7/73 ۷ɤKaLɚYk?\u`xduώ ,,OLJsb2Fuc7hvvŊϮ{ϻ2|C?]hX,>s-[E/ekֹؔ?:?ֱ>[o-=$%TJj40/FdIO=3;W -KO=y_|4h &vzlQFZ\1ƈƝܿ>0DJ\ e=J(.fqaHXm$%u 3e'qi}^ *i3W:[Z?JO>y_|3)E$Y73ٜK&RZ&.PIu[IJₘ$eD cXUYPd2/HLNS298y>.oAaND4(2)Qk\|$lhիSɔ,W?{ƍTBӪu7_i[eAF3f%Qyzqa*m3ҳ6V=o~?dnOũh׭%fH\|ɥ$lh˚իX*S]xS_&kK(mYzu*5B&3i{&Q%i$=/&:1I'kbjE7Bt]Լ=WtJѴ*'9S<W,;Tl^SEr}P%3̙ӏ>𱣇u]d%z˯Y&Z]̰qUػ{W)XҰVh>e5|]}ԝ4£#"^Gd%s3QdZ9KtV-leNa$i 'T&gp{v޷.-J߹x% 7QɔH~-ɊTUUjT,JL&J+,Iѫ:庐ZbKMchsF1m\F# WJ| TdjjZ߅YьOCEIRd9ᚉ$IN\ŜPI(D S\.ӒjƴfUaRCPVbY!DbIu]תVq܈(7 [dpFbC,BΨ,1]uZoΒHB(%:Ęoa$YR$IҙkG7lJrBP(5Mz\ $ .`4.ZLB}3b9MJxN;])/ۇP&a{$kO猹js-Q4Wbdz7kC v%bD6PN݌,)c,Jڲ&MCkR2By.Vzc,8lUn7p 1o6f5u=jtEj%QBUU[Q*u+(M&UjNeh!E4in!J%+ ~҅/Lա?p5NIcdVV-V˔X*U R~%PBX:}{1*6T;|^,kK6#e8`\Yn?:o`9ؿBފL{ f<״hDm|!tpSQgjD?U418TNˇy/{y1`eaEL!uu/ bHE$BN`Г#K.X7oUmx}Ny$c✆!"eQ]5iRYStwSv*x]\rA,FؾJkn1?Xt;5~tucGnW-s0rpY@Hm8vx2D BwvˉHֵH8qa9hazY<@~V Ïq"7_g^;4}oN,d Jcx[o 3F(Ǧ@QJ⣲(sn> mڮ]"CtjHvI)~?k7vbbkPq"a~$p<9j6 ҈T$b cɋvmftBo*HyW|;KiE1UUj̛%6[EC Nc0|}?[ގD"( 븄Tj:7WGzBp/H$ªNNV^)ϵh7Ft.s|㨦a-)}\O_____ї )J`Q …Ba 7ܰ)J4Yӈ3F QlNX$*e5  KAw4mj*?<`Z_8XX86۪K3m7(3ϕAKOKu0ڳ4j$In=g_Pgk3ZvJ+ה8_IAOfvXLӱ[A[^chlmuM+=m b~ш2Ѻ>3 z3@y#d pEz=^ulW{#UZRZF/K &9hW/<*sSFPgUυ .e%huA5Vd^/XV[yT: og2-h@\0h4sݵR+\0'R$,/r,* [wbOHm70F@j 7L`Bg{{F#"]m$+hV,/ϡmA=`6_ #|a f TTCvu+_C8tV &h"DQ۳v_;TG؉Z{;Dnٙ_^C pj n:ى 7Jh\mޟ(ׁsm&ql* ̾"; "LT*g+>|3 Bw ~y.Ʋ4 ͹ՙ7 IDATĿ*mA HʠE)9uw=A*xh-vD%D7k/l0zKnij r - }=DaΝZ社F{蠅lDGn]EQ[8|%.Vne[rfF!㮝!'FUViQmrw7^E}Dz(q3ng\;&UgMZT4fg18OC9\뤈oDLlAđ I|#".`}m|S:h v '-{:u+Qs~\2?hwǂ)WJ mNX@,4Npc@#8w=+r> zjVfZUW2M cnG$Jʳ;ܴiS" @K&nzY:Z@@* SU؜e?FN(ceӚ--acN#o!Q힚"gsޝ#xBfؤx8U)R{kOmGeklxk'W@ 1b# TB V*Pα6'~pjR|FSءSO/jP vNE.wQ>μt?'%RHOmCW9D½S9ST̰SXU@Fv/l{r& qഁ͹|Hvq1mi%1{ʨS wv"5*Æ`QM9m usS4(}&,p'س|amIZaSu6e&0,YBAy#Mgh>vٗ,5{3ّٙ G2⶯JU( mc>;KJg2&*/q.x\2ZHݮDʂ-$DcwJ{x xr56ЅDGgg&<]Qs9KJVs9h>֕ 6XZߕ|U/O6X@ dU<"^p-$l+=-n1cf囟LhڸV4vJhr$IRٝORA;"b`7Z3T tMh#Z^.Pa h 3V!$@6?[BGLe/ܡj(6yp낕(!dfzš\oώZ[]w '*gD7Sa@@}[6/l Js)UEV)WU05ΖW\ teQG\D[ݥ#y` &N;7esܦt@,ԵdmYq2wspZ,asBP3:b5!)-ifaNAŏNBpmB;b}_ ԗb%ud]"pS[Bng#;5Td!,[g V&N|-R0+m[DZ .%DUt;jUm׫EMWq7ʇkFr\INHrjӿشqc"X7Zhm8* * T8,ξb_ㇰ"Oߔ֢+LOD)M42ؚw ے;tJǕV\0-V{;H[vM3WWK4/7g//?yP ^%Iل__$]EGtp%.h>1Wvρrzs,37pD;^\ { ^9Cg뚹:+h0h•V)ߺ{DN]5LD"]@pI1u Z8hy3.2b'L5kd.|n8]%ľV$ą?ߩtݥ5RVu`0p3%*C4M+[Ty<څKT%ip~ӪVq>؏Sp0Uӹ@>4ӹ$և|DhgAٷT-V6*/> & ֍pW [̙!.[<]8m.6ӋOiŚu"+ Àĭ$$egί`&r=A|l])k[2`>$d1Mxf 6 움)̕ĂDBc 潌iK@ntW'߂ RE0L89xȭA+?bsDR5oǙ,U3Pk̓.ڼqh@N1e^afKt"*̨lQ9!˩xЙ- :6hmup`e@ASW\&0@* kT|; D1F|y$t@`.r(V6u+?C_)| g!d0>[bK6]Oϼs  }[VaT8^rKp5*<[2]XHE6C.,Jjd]>5t G6PN@m []3'N2T_@NyK8 :?lB6@5?(L m/ %Ͼ2 A1a* $§N9zt")/oU˥-]MUxSg D"]˕//vߒ851-ItvT*ryЭٿةU8ʸ(ù~$@3WdFtNQ$@( 7V:ozDf:WNLL^ǹ6-+pw.5HD|єT*+VVCMа48)F`JQX6ڼy\J}Wq+e+l4/빦di7*r$Y'v{N7oޜJr`벡]imeh^a"swc6Ũ}:g,&ۛmRʊ;e"vom tf[l\Xool[k֮k#"(8]PZ.Op8p+^V4j#GF6Kq\Wa+bmO=s5MO=qD?kpnpi6pzj]ߍVg\-qRp,_leNT|tg6dPqRԽhfOꕾ𶊘FXU1 qa vnKϞwI.&3[͛q#kb֌nXAߙ :xj \8;q'Dō| 4́%TipX۽o7"a}&xح S[4ZyBiU=7ǘ#J_/ Inn/ ,VNyK8NÇ`V@i"Y:p޺aׁb'h / #hv艽'qqpû%yͅaX#۫.YΣ^?]^2F0x Qjg5p*nvBTU;\OOU?}pov'Wun&XƁz]C"-} FI0GD /b|[l]&^ W!>||Hie4~uf9y:Yg%&VƷ֖vl&hBfKP@Aͅ}an Fl jz'.TM3w /VRE4+|fg]{1g/s;>k6g\sQeΔ6=#N,JЙ~92]vq& /%xmwqČobg&]`:/뙕Vo< w/`U-EJ&gHBd/vv*n\?Oz$#O$a^Oi5aiͭoPҿW3ltd\Ӊvf6 egmpPSa5(#}e^dZ-'pUU8tWW3VnznE#qIR?HU8b2/?^6x0:|u33]i$DWO=q"c`5wx x_ V/xF/5X75}'֕/MB4I{݁"KU*KA&+>kF|+h$ #:+g33ϩd"'}aB!bi#J^>;#],w*L&Myً7]' ,-0;po0xc0/ * Da34_gxcP/ * Pa h>#Vah74Ny & |k@"sR00@@Pꇷ9h 0/.\ 2`F</H@+ Okbҍe޾- TЫGt|ah/g3$766j*4_ 9h-ॐD00@* * PaPa  ga:j_ T ߙT `v򅽴0x$'x8-RqD$/ (@* PaH@a|ayJ8F<SHJ00@@* * PaPa  TT00@@* * PaPa TTFilw& u00@* * PaPa  TT00@@* * PaPa  TT00@@* * PaPa \6t1sfDEOwh?SB -U*ՕˆTsY*їξ<1>)elMU8U T^@ Ƙjj+H$ENDy6k UX -YxQB;oϤ#Ub$ w37\Ș^.;:Ç^P&ͳ)RkpW1===<1Tj:^<]8=<< Ubu8t4}j022V`Y45U4G&ͳɯ5|BRTVu]grB0ƪjTu]Nr3-|IMۚvk"/X۱M=* BGPOOPpOtExKW٫j"ZӞ&T#mo 4 D${|>i9cٚ'c5We3.+U6S<[rMd4f]:qn+#i0A[K OWؖ5YlUka-5HjA}UF[{]j֕:fy&''9f۹6L'0Y#aTX!:c1#(^]l^+]nŭ*Vd9&rQb/*F՚j 3! SB%IuF(a4um9kUbt5#$I) *QLg!"(#V^&ٳg>H$#G)DK"4ȈY딢F^kөԴ$%sq$2=ύ-ʤ3uzEٻg׺+ #.u1(r{_Cb;M$Ƙ$292(TyoYv}򥋆]f*wzlk#ɡ|aZ&Aٙ3nҪ%M4 ] 'Nyzzӫ30]dKsIz]lC .xaBA<"^!/ӗIwfJH&ͳɯ5c1I)%$]qcW.6QoM^a}a]' #24_BdQƻsPahsޝ30j2_Bą,˺TB(u]e6gN5J)%Lg F k/Lg$4Wb `IDATT: cx::) DOǤЛ0#I”bja q@ T[(diVt6Ʉ˅UZ|͡\$ / Z*#c+wɳgeҊ(TZL͡\$ /w!sν1]U;["B98دrԫ)@$lrvY"T |w2]]Z׵rAt*52rWwW-)|SUO1Uͬ\ꫳ+W}#t*t tN|0:\- ەsU n c 5/osk_cGH/߶~ѣjz\V3s\(Ze&Xet'hr\Tui<ȫf[YЌ"k&DŽ:{ٳs ի^,ٶ"u lg#|4;3ƾo*~'s䘉?~׻2/tGIrahD׈iFӷ>]\ā,iYWZӴ?~%Xg2cy]]oo{͕w}-O|bffe+?+/)J3Ki2S)LZ_J]x䓟 G 5謞Ր 3lr9[JcٙA⫗W|Wͥͫf 'O+9BM7)@3aַe)1TgOk^'6nLo ׹ Lekó&3+2{D"Zl)Ϳ] u٧MIt&̇1vwLNN>>[;;::cǎ|+jM̝=[|%i&u=+oe|cz>}Vd׽MZUXe4Yl *JՂ`UU﾿?޽{~߸a\x@POL1屾'ûgӹய}X\&~&'Z0 :CCCSSSm뇆̬;uo6wpMf])R6,g8:w~_8sŋ|S@D >d9IՈU#Sȥ׾زmoaŧ%"$i"+WqZF4F^ YS:*tn%1EI|ow_G$Pخ$bE\$)rxbZҗ~yK~4V %${t7WiI“ֺMNN{''']$Ó Mvmyrrҵ5u]>U&3j÷5֕^Moy*AK:T&3pU1?KOSsV7ҮC@K]rn:7xV-Paqޚعs/j~k*ڪqN."uBR∄(K\PJSkpJt2Oɿ^$F]}2o^>SOZ tٕWN=O}WeD͓DPJNfvY5+^IF!|&;@aTX#G^FU: E>r};^]H(|Sv JRk?#/ՙIRH"J2JHbǎ_Z_WV? S*}ٱEtSLQyfvv Ϯ[RkEQd[33SŸf7|W*7/g!J_Ի޵ off~ٹ#G:5q]#CyvұkWe^]8LO7'_u]u]/DU3 =[HssG~hK,Z<<& ^(̜8yj|La3^ d,[li. -ubjq47~}AX-;u&f34PWZLMG._ëٱ;et&Dgv֜0]\XM._C~軳wkj [__&Iw4 ^1^ + TTndnIΝ"u>XLXZvT*|_8I3L4,_b#*dvvZJ$33332KΝJR__[uUMtreF驩5U_j),1Q,^>3%+edmz=mIENDB`veusz-3.0.1/Documents/manual/html/_images/importdialog.png0000664000175000017500000006076213161413406023311 0ustar jssjss00000000000000PNG  IHDR}sRGB pHYsII1\jtIME StEXtCommentCreated with GIMPW IDATxy%U}N3ӳ/=,3 0DH\<1&jđH cG;Yڀ0Q5bl憩_(4Aw^Jkg٫%x 0(x&d2H7(J422\U(F5c-i3"L N` d? M+X2@ i(f*IjB/n$xf&/WJ($idxZh?lRlZ^*D*Br\!jщr"H3]]=K.dA[9BH9vYE̕kEl2h0q:jFW77AKZfPZ;2|rlLKWdyT.72b$UXly6&Lz/5$TNٖ,uT6ET0Z.h?~ܽ7_Aĭ+V̳ ?v^ؽ{ظT*Օ_z-^1)qʮ)21>E',l8:AɌm1Ww`>Ļ!! GmCx͙K`BLp;vdnLU&ǎc,Y1 ~ e;cLUNS#6a/xW!!DeL1v͏Rb+};^7oޛ/BOoixvx7se=ls8HpB)i>b.Õ[n)Moꩃiü̠7GDg;^7o'  R믽c.~:EwΩL-QOT*y^4dr:8¾{n:EBit5cM=͵~.glRtvSXF'VZkW2*RBˆH@)!??eθlszvo7wwݺ~ IJ>߿15 s 3؟_)W5nMj+QG٦ ʼ6|$xpӿ]{Oިm{-\X([w޽у^hqcΔ C'Lf@[([w={zHˤh"uF&5|陙0ML =uܻwCSbmglxD{T\OMeVX|RRQz۫ri2)ZUU՝vw]UUZ྽Y.OXT"O.Odl3:K,VX<c% Y"yOf nk|>x|;cpyz3su ^=$/]~瞧/~ˋti~UUYr>۳剉 Ld坓XU+L&Ȳs( \LvNe!\lWUXZrR,W6ElE#Gs)TLӓu?8ַZ_.?rʪUΑBO뷿cٲ˖;x\Z.D֓c"%Qk Mk (W3J+cDe*SʘTպ/RgMl׆|Ԫ!xSUs̬-4-rdt:9#]!n1cэVѮ'ajcQ3?8SG=٭~ӟ~ !л~!b*S* 2Yj z&Wd*ZWLJxMĤ1 dp8xs/O_k_tͷx O+۲eVlYw,6,ɽaoER(cG~eopm#rJӉTR(%R9mMeH!r/VRBH"3G !qW+ƃje.έ2Kkq¦DyRdz'MQ+kVїR֯!RetR"PA("C)!zpxcx ش.7k՝qx4jPZ,;v yj_޿MЇ>|B05) X9velRcy=JfM^JcG/d\k>n:SLVl/go◾2ɍ6o_>7WQ .ЕM/#6htА(UBGeeT(x7}3qǵ[ܱe7 "Oe(}_?ƝJg0ӻu7;ͨl2!O|1_'~oG>4,SG5/0xߢECJe2RR#'N\A7LUUkZ68Wc||q?q"1R˲*R<s+E@*%q7(Ě::OjNXc\sۿe7[Ӳ3czΊ.&tRL-_oݵu2O\mڔ(3͆K"Gx%L]g -1{kO2]z4auTNesy}[ww_ɿڤoդGUX._h~ry#~_"5+11u{%uo|w||wsWo̘`<"*LQLnr3!>2ڋ;ktT}剏xBq+BTK,ݷߙδ?׏6oA6c25fS\\4ۋ6@ֽ*`bA<ᮃLתjX<=+Hn]cEc)BTjL2f8szҠ3 VY p|61jCxgy~bb=W\ѩW-r̠0*S3f|_׮]c|t&3ў1}jj!}g-l.gxxڋ#1S~w|떛oЇ>>a6}|f(ES 6V{g_8Rv߼tOl!dF[J6hmȰjM\~mg;kljdi7gSeQ-!lgS+BQBHC.6"<е8*fc2*00)3{6:m S|kxwq])!fګAgQ+͍y!PAQ633]SGwgd\A٣guҥK㱸惬֪ǎ{d`` *4(uT)#uΆ#s5bRnFUL*t<=ezK٨j{"9yyzY-]'>RV;:5W1UMUHd*=zY.]O@zl:d>ȑHLMIbJL*3$ɇ4O)-(d4gr|rS {IB&TF4+XViD00őH\eɓ'^=|@U\.;11!FK8z{D(r1lʃ?ӫi~6S[BTKk+] ^La gNQ* …1"SVGHLͤ'"XcM"qUbH$VLg;l'l*#Lj6œ9MVirxuPw90O|=aX\ 1a7-؞7e㱺I(%lƦ5Z-yNWq*r#1B [@Gz6MΟQfc1:>j/8c:*2U2cMnez*JcѨ$K{@DJQ;i'ԷBcZV>$Y[+B)aZ6R^Hg&џOflF<,6ZUdjJK$u^oHPB0n 57ffg9&ZμR-@|(ucS[:ڢf+Ӗw_ެ`ݰd`/sYjuql:sb 65,oS&@HDHK08^Ar&ctv&Ek] zx-SNƚy4Su|!{X`Ywj(6: h&2o3ˏ l06l$9fI_#' _y כ.׽Ӹ}p9l?0V#LCbS&}{UAZ)2Gd,ӾX1z E\gԣYǸP&g_jrs%:f!Ƕ QdJx RAlHx 3 @ሯ1M|n-&Vo!ǔgD^-TZʱ.PY;8"/67|96m!!%l_'Jݟ i11~խ0ڋAEg?xPd8kISic]! cWG&4~۷E:w7 @SIEv6iÛ` ssmZs~ P|*oŷ¼nٝ}4gzq6:B Rߎ5E [md.]x؛dP~b6H!1T BED%'O>(т2BS(p_,˒$>FHzNH=6>B_BȈ-3GFj|aNmlrh:M; }MF,Hknۄۡk_svFl޲ /X~6\Z IDATM7 0_tuuij\&tu9cy$84R[+qtniʱ҉L&%OMH[:fibmA VU{ڴnrr{7VDV=qZY),^O?^zi".Z|f d~[بÍƘF6Mo{xxtf1aHY>yv=W6:U_r U$Iڻw)lv$yϞg}~V^Y b4}a۳WDW#M}zQkKgL*@<dr&ر҉6?Ⱥ HpHtPúCkð|-¡C.]*˲}ʞhCCCmޣC˓6w*kJ"eCO q h&޻ggЛL&d)цyd4۳{zٜH̛7Z*bQk"+f(Օc??ӗ-[>b]{v\+/9BHRT뙯.$d"O8TUX,Gyr\-s)LuBW:)+˥ѱDyd*fɤOs$jt3O΍ N0l틔T*L&ct;řj̀~Tuh':U@h*t<cHR8E"":X%IIJ>_J$;rqd, ˲<OiIY cAn1^T5~KFϏy~/jf+6|0 Qu{Ғ:4Ƃ Ta6Y6?O넯(y->U[x]Mژnʹl6%[cg5Ln `bzH0DƒIE&h? !d<>7>$f*[ݍʼn,Hϲkܡl\oqC 㬃 =7YM+#˲UTê|Wan1- 3Kl\316JezemAWe}AO{YcaΰVZw` 2-_{` Ɲ2#u v7`9֡ zF݁ƕuoSЬ @0*C\9Y˓W|Hws&3. FcLW4O5Lo٪,ӂ$FA4ƕ~{)W^g)msB=X.g\La|lpɌ Ln.w'& H&MA)~ u'>gB:S0ʝq LJ3LgtN9n1%jBաqeOy=l[<j 7J`:[06A=A]Jb1sվϯZ}k?WrOaNde\ToHf ,:<8@V2IcWZeVuFM @K۳meutji̳[i\72\uuyk ~Htl#eT)nzMacL[٘AWCAoĴeQFOt8Aq=v~0<rtI fzyŪu+brUqn=PWUq>8? S[6HUס0-@#A:LĒ!RBD*(M< jEͬ I"ن+RE&om$yϞ=gFE*+ԹM !X hi0Lr\qI{URBO*Jrpw+FLf9E r- Ґۏ+~6S)Y*ɯ8B3 T>+@39_xxqMhIւB,mWn'Վ`maN]mUrcӔ* F4<n2κ-Y9ژgi)MWj˥\=g}KkC׍}MXϲ l)KyL U~UO{&B`Zum kgc عkJ E#(F;ҸҸ4rĶ86l{Ckb<bHh 00|w?_*B(1_6Kfsv{~+VhsJ>{uwXS={Zbu8/ٻkm5 0#&LLL|#(O =BGr9$[T)XSk_]U3׭onmV۴UUkUJs@ Wڦ-y_vV ?dK[_sj.$vna7|O>!+wյNįVa;=- \8T#`6șt:7ҋ.4.}MaltIq/}6;z$ӹ[gJtn9phU?,]$ hDy"[򛊎U,@su.;l.ԇi0x<Hx8Hl.4웎ykCt%NYGMŅv Ե] YF#[GA #aU4V5q11,'qԪnSg1h ̚NHȖ!9e?'^%qxiڞn?yt< n*xYx\-{laa /(–Z}W@Z'z<^=Ub7Vj }a[V1`:519JEuDx G=1hC[!AZss"0m*Y#VlsP.@k+o{x9@n0BZ}4`t0 pͶ*jŵ pQ"®{(>ʥ”RKЗ1܂ȖQ"FC wG:#ё0t#e ۨpOz0H4>Ox=䏉&[*‹#?<} ! K0lr SHđ`Y;đh~LaH0kΔ@H0b !#-L 3K[漅8 !CЦ i!єj`l֨pM PY6*xY-gPy K9Lx$x=<T[O¥7}3 9o 3s[i9Sy5 |·FeiU8xxfIrr lfK[?a!WQ. w7ᬙ `.z1:K=Yw7ݶ)h¤)~VN@ё M@ f+ $b$Ѷp_`; }1*](MAU ʨÒa(%\0PU5t#%><#M]>]G0[eu֮=gZР0FJbcHT %pIZy2"DDQeSA4kJ}LR}FWt+#[6!$7o[U|.122>(L˔BbQ1Ig3IA ngf c^}aVֱ]@;8!J$ IVkRAGq"AP'h4 3 ez0@masyo_5H[sMx$!͚>''0M- * Pa#flxi0k ֿ1i 3(4 011AtZDm/H4 Ҡ]bJ%pYd0"GZ @ GZ <2HO7=l%3?ܸϕʜ+٘[~g}nz ?6y0;<᪰qُasePw!Oݭj/u<|:d2:ob'Tsph`6mTf<(f^&(hME:0cH;WVݘqHtGʓ߆5UvMԆ' -lMܒК+@s<bgjϕ{<ׇ/ _<29tA9GZT|G~V8#Hl\M[C 3 UL5,թp8(̣qm?lBM?A,ғDԶ.99Vgk~[٘f?9 <opma#qq4%㿽7aDNײ`|x 4ӻZ؉!4 h+cl1S@0?&JG DelP˼EVs(/ w!FDWZ ܠkU*NiY>k, u(>}ԇ[7`p*/C(%} <Ϛ 3ȧϚ4HmzV v5SB~aI hϚ<_ J!0Œ{ưT0I4LbM !h$KB 8[XYR{RTS b4r#hz L8e].?|x\ST@T.{ښ'N@ 0jqJRDDcccxg#W-Ia{x-(c ڙjuzbɅŽ캍l?lcsv<6p"%6tq>xcg1j[Π9lDd Uo3$!+h {LNlNw~S\Cs%V| Aw߁^wYh cǯrpƖ nB7}:"Lx8cN\q߿wvlF)xk cLՒqUz?*LL rP)z6\sc\Hn€l3Hc%]ElZ C,@Qԯ߱0v溵w3׭ewާ,BF\>V aٲ8`O)^=pǯ9W lҏ睤>iUDDA9??$.m姞=!+w/>?4X`R*3*#JtΏb q‚K@` BHecLVF;#H\2[Y ,VVȖXM/|f1<<$qYQDhtuDMA&_svTgLjﴕ_널^qy&[X~#Ѓdzw\'S07 XJ[;W|Ѧ\-+3n|>aáBD IDATB翸~Iƍ3L""Znr]Jٲۍ~K]So燇}[>k](\ve_8dN ^V?pPkh[*C[nع_WVYv.{$m{#K:T@ 2="| 8"@'ĵm_ /X~[oV""[G/4yK<cw1 f'ӫ?FCTԧ~_\r a ' YV։`HK'1c \*#J"IrTDc rZqJLJ UXVbsSZ?e2k~.OG*pXOW+5\PɃ!zGKcky"),:ݝY3&Ї_UQbT(3 Z$Y.%E ,p`f܉1 ghS+ժ$IEoE.+Jk:ՍI\VSbHV:E6 n)kkzch3lLɳJ>Qll=[#\>w+Fī?VJ0hjFuܾ*l4Жݸe7ha㕀g/x>I(!Rn$ԪkF[e|_l6-&E]se=Ҹ}\ьs:b<Xeb\HA? J>߰a!O?$|{$xƍ$E3qqP` gw[uG+ :[s؛vݾe>r 7>߼P6oMUÝpV'F!v%s#߳i_֮g. #L[?kw|Xb͚wqg fHBFFFW6.pz:#a:ҳQG?x62 [\x֯_ifA`mR.Εl Z[xV-ާzl` p?݅e#iL;=SevhG06t[|`d/]L*244 6n7ZϦR֊88(RΗ%KQm+/40!G"\dž=ѕN)V*ޔ" |.++V!ڻg,sٔ #&sxTX4/N&Sŋ޵gl粙 /HqTȦH Al,-˕6U2dd"Z0Rd2XTW Zg|00SVbQQR/tB0wUXJE] mX:j2 :! ~E)ʫO=JL:ɬݻ|>E N~-"$ LtB07 霢bP'˼_($W,L㇆M\h1V l $DWo/[1RJ};?rJI+*clwB.̾֝MO͢œ nذ?jW:{;nj*U2}ib N-͙?Vٸզ-fXGOn6EUM6>|[o]x[-cO?- Fm78n[TmwR[ѣGӟo|ݶm[&ٸqudP'3#ׁV]GM @gGmƫzrU1{Ǟx۷p կ~_f2//nY+bs ۷@p}\P0XDٷ)UJX%dtmo{… %I뮻z|0N_r%x;6c6èe&739]0?GkKC=J/_~UWyxg_DZw 9&0> 1{x@Suw'XǺ6.uqD,W*PI <șZNræ\Z(sxIW_JFcsȏ{?;=:!DcdX/u •W^O^|u Gڬ/]Le}%KJb$' ?U'_8:!WaJT:m}DG_ڶb"J-+hy'¢(\G~rXi~=;_޶`Ao>E N¾I% ݽk۳>i8,\طA*i* X,[)+D&N& @'PRd2F]:!*mr-F'q&0@С=Dm:DСrfſB!jchz3jۗ/[ w*,B>抏ggv8߽wyoFQUT}cNFa e3-_e7N /1AsXgV<D&ڬT*`@* * Pa{L5Ƙ(Feb(yTaY+w|`.S,l)}*݃: IVp+y* ;3e}  <6QMn<A | G}J=A軭qT*' }x[kЂY2*,J8bjj8Τ^E""N3>ٷ…d ,D&ٿsY*<::=1RrHH]FGOa0׈F" dhd ###9EQGF==$/zzzGF6nY¹sk,Cq=K" d2 VURH*c&|\r$IJEUUQ$+{ޒ1k;SO8s 3 *ʍ7X* {Z(un8W:cٴiSv]Ծg}lϨHںo*[XUM6>|[o]xq&a*{[`V7v悱k`#NT !jkVnr-"!W/t =z?~mwm۶e27[.`ƭV"n7A1m >}%w~{$xƍJ/f c=۷o_d2_~_%;gfe탶04ܴR=YOai]V[R5t#I֘UbuvF:Ch8h[6 ?#pïͻ .lT(J 1 Jt]w=S>`:Kw{ K9WnOiqtRlN+8š0w^WV.Vxy{$<}}}^{_*zT*|򫮺UgAђV~c}LW6cuÎ 뷠iټeW'Ơ7ӟ 4#h>я~$)H|EQsM=+!gm2*2:7Z (J&?^>GP(\yO>_J0gрnSjwWnESSh*,¡C,Y&/1/Y+4|ȡLߚ3u~`+"hX67m‚@; d2)ˊCqlllW\& fVOB*SF ¢(|Wi| vݵk…("%CEEI(Z> $I7`D*\x@{_zOe/ZhQ* ?H$`\9*,B>brl8Lfd2@Pf _0,Rx<*LRd2ƦBD"H沓 .00@ ^[0dY$ Mh41;@3$+y}4M&ϿRqdX2Fb4 KcPJ)c %d3\ݝ ȲR,\1ZA"Φ_E"- 82Rڶ XS,bq۶9g]> /vgLT ^D"=t…$k0D&s>* \(HqCr~eO.3pLӈF d8)d,PGFs$V*UIeYgcJs Ƙ$IJUUV+EVpx$l6x>ma!SX- 24 P3SvNylA!rRD,oX_l5(ƅUђ%'se+<Fi7ql>|F%Ֆsܘh%؏pmƋtqiLPc/Y*nBHZݸq;@yޗdCMCҥuHUk^{r˖;n !}l&қtXu&}}o:qaJ>/N$oذr7?$|$xƍIh<7}!!{&7n UIg9vJ?纛/|Lg_smڴixxG>|wU(\vev[Vl@TT߾eD_Mz7'OMjUA&:&h 2${O;w#Zf-ߡKQv4ACJ{'?yΝ/?䉾;–=u7~`*¸###s`|M\x֯_Mg?y޴y 4D)D*3Um~]SN8`Lи'>y@o =`ROSXvi_ɺ5+@e:pWA[}߹G1Q|[.T;YZ#hl٬:iW1.v:yD3SLDK/4MPUUUUH$JGףT>yYQ~uYɕQIBo`J{wk({uww3X,V(h>102i峖^:bLyRA"16ݻl'/l{jɒEP2/=F!*cE@o/)ͤSvۗL&ec50"qlllVX" a}sc~AJ DHHXbFJ/ߓGB aUQqb:Ji6R??N_s+&Akמ/ߗe( 5xXRDL MQUO)MO Ƙ x EE2"J-YP=_óEE6]h… ҩTGD:E#B,t2[4#ͱ) i8;|.F r d2dm$',b[gLԋO* c*c*sLd*Hq Q)M†6Lՠ8sn"3b<D6_~ 2F*zx4VUB) <M !OT;S"mλsJ;E DeHToSTEQUUBSBwmqǏ'@lǶ6M'MӪ=Ox郶~(Ҧm,8N YyD5/>9>RijYB+-!І+Z OTܝOީfZsw2J+X"w4־*lbX BH,yyب ^0{:@ ) i(@i47;;RDie"$I%`gn/Ƕ<**jQsKdi|Z$I+pmB}l{ޞeJSiُ{L) YI͗  Zh4ݶwҔJGۍ/Jػ;Es@U Ay'IIut 7/ |!Ds T*8VJ ; 7_kI4if-˖O kOcU/v5X"<ʾygZ"8[ OKu. ka`;NR\ ͼ5 M䀹3%wd,[Zf|&Ycw>nQne|EߕXj;+N 3e2o+F5ovҞ^M)?V+T) lÐw֡c;Iz7^2_}aHim߿EK,9/n!lJs]7DCt;IDATy٫/7ο|󓓖繦)E.@u){y\ 7矝B/j&g;;VOq=DZ>B/+2 ^w^ǝ(UU)TQ0YRHa) 0R@  IaX[o04RˏX0^Qtͱկá asD`5+J&Jp9:nFQT;}[7?n :iN_p`I07-G~ؒ#d,ʩNIENDB`veusz-3.0.1/Documents/manual/html/_images/createdataset.png0000664000175000017500000005706213161413406023427 0ustar jssjss00000000000000PNG  IHDR.q8sRGB pHYs2vltIME/SsǦtEXtCommentCreated with GIMPW IDATxy$U}}NU}Vgq .ɓc?5c4bA8 "yɏnĨl0].T9uoݺꪾ}?oMͩ9UַNC˕jM&d9NS*q8d񠋕%mhKB<8R?hl D)5R)Wk5uV$+Z3)5Nhi5RP tԹ)Pp꽃~З'ww^'_4?N* &lq*<@^$:~ (%%J'erzZIȲ$+juy5`9T w xFs3=΅G?*?!usZs]W|:鴮V*uؚRJ)UUu|lZRu1b5HbqIqyU4;B^]b'kKP©ZV+PNhjMV+R{OMf5gZJ-MSGVB3 V+bL9NrU]"a3ޙlZjgr >i4z VZ ιռa;S J8(_b;|Z޻姞|_|/^|ET8|?#ss9S`=ظn%3Y3gbחl s.V8~sd+A=*ؽ%rV}ܽ'x_z;޹|ʾbѸ_x+tizRiϞ]s \lE_qV.^xn>NRi|r.sžbit&fќj,J%rOqb6lK:gʔs"S"QJ_oٚ5oosK}߼ ]bMgww-[B^q1zMl&6:&v0+]KGQ谗#8.&^Ĩ'O~ݿ7}С˗W=/3+V:KT=yd\M6ٽ?42)._sO3=sgɤWܴi=/pNs iNȳMv=+]}˖-S6$J(%:H惉/pq&#{UUخ]9={w\?t;Ngٮp5b*SzMT:c$ӹHy"-$iG`:LSyѹ=[/nR7\/KHJl/],l8b7|օ{˭ڋ/> %F?OO[y7={w̌˕jڋf3̤<<.Mwz~6s<2Ya.]3͉ά }{m5ٷo߆gprSevDrH}35MrÔ;vl[/ʽ]E ;y=q)lp l̳ϯY~YbLg !9̔mV}ԍI7`_'SY[ Vke#OCZ&AlAܗ8:#1ι8c{;C]lֲD](fr'C,g|K8O⨕RGDܙ0ԭ>~6ɣOSO=ed):cG{9_w}k}b-o1u7Xo ұǍL6l'Ţu뭦(lM6_ɯ޲eEs]; Wsn+܈}rxxh^xzX7\wXҸ3ٚ9Nz^e%=JGí[uogXK).U*ujbWfJtRU󅁓R2jyfeW˓66jefe'LLs+ZuzN3Vi6&SY#WkW%DBH""hj$R#d6K D,QB乊mė o Axa>ςweDi#[菴+g$LMOcub8w?5>1ADn}֭dūfjkQf ȅ9Lh?+Ω9Rlro{۷rw/Ł~\` dk*֦2je&X?:*z$fo͚N'hku͚HT*MRPsw$)Ui&IDe"ʆF3]LW\2~LRLg O4G]I$J*ͥ/?ˇ~o'>6,sGk;eY[dD]|c|Tj6={䵚$r9Ua zڮZV(dIJG:ӭiLT2;S#U _B) ឳ\n= f$RZԊ5b=$ f'aeB9cV}+7/~cox CFVsTY~c߸K7|o.shN2Z^(<˷]ˇyG?W[,% 2SD^UUVuBqy[Ojdd̬$IB֪J"0sϤS??H&L_pմl6J&C q]viqr1n0h/6Z6%V6|#V !u5*9IRzg9gL$ϚSOۛO(5mg|Dg5m:m_ qϚ۬zO-ӎtNez_'O;vۿqbl6J-e\LȶO5+//}˿`:[I*E3ϹդZI(y7ل.kuޡyIgR2 `[oÿջyO~o}οv]S)>nb=˾xZiefŊ|q[mgF_J0N5IqO}}险L&%UU$ ]$1] U:̿xv߈į^-՝m6ת3ej,r3%D"m8}>YO5mgtFn74C0ܫ145̹,tuώ?}}}֋ݼeIbLO'Đ)3O]sU{2Qڼ\069S]+_ ?u'.y 3\&Tb E4[ӼNv !_Ʒ~۷xõλ[8g>#uݵ#y<]~uyX*׾K7,LK8!qwժ{ݼiq:rpp`5\g3ٞt23NIg{!B|l$=jbsJH*E)^e5 ^L*SsCGB9Y M9熏{2cI"](A;O[&+ }G喇.E0IYwwϪUۻyYfG?WSs"m#BWR͛68o _K/+;РEu6=sLզ?3G/e}aBLT`z!_x{gǘ7r'~æ}=>cs2P-z60ӝΘ}^)O IW6i1V& (|e;o4BI9&ԌTΪKHcV#N$$9!zmxmַlDsXjŃm#9;[xA!(l0ӦN朸 7ozW˸-Wz:k2t,%:[03駟*'LG4239.3 X7~cQ??/O=6m5t]z(y`O>е7=zIW?i!-QTӔ½ "I{O=[zQT+=i}g ;Chd{h\D-?a1<͞C_Rf}֜pϚ$8)⳦7v5[/5Y7=Y >yΟY{Tun\o{Ej裿=O? F%|K&JLL'r*ɌLjR5')ϏD)lhN$"Wp̼&kTK/;U.ɧ}7)1B$3D~.^tYQ>\IуzիSɔ5֪G}/L*F=ABmOGNnJgmJT_3K7Xs]\(L<%sw)+IҏgmZzu26ΈZz葹+}c6dge7iթԜ\TG3ɤg3(ICs~499ͩ,МtRj -1؄TGH$ U5U]x+$s+}AΒ>p34(JMsIQWJrBV k*S.S" ]hT"q%2ƘK!QT{1""V&|rZ5)I eB(ag1M\b3XIRq*IrUYR8$1ќzVrND9u]u8sBs].$; 6PLdvU;6DťZ8.7@V"45v4n.XFsN啙 'pGA3#2;lT*UT(;ܠ˯L[nY̾؝bqddngn[-(' 4'.ږ$u>D2]/`cOAFM9JY״|j& Zf}DE M(IUW14Q^sYV2TU4+,-8"J|6T*eG-s[#nyڑj*s;9/xnLVSrmEjhu5^pNkpgJ$T(V Ӯ,g1yݯ%|zY5[%L½fɳ\.SӲ 2ZG#7!aNŒh$%zK&$>]|C8Huʂz9F7+]2-X,*:/=%vF9 ޻'$ԶGtpi>R}l+FNnڬ4+*i&[@+$O*16E]@xB{#ꟚT8ܳnn]|y *^enސvz dir_-K }VQD(?6<4M}8zo<7ht,{lذ|L&L>߽gnY:^a6>1.fGæ)L ,ֹBݐnlOQbNX8qLO\)55UP5٪Fh+[rEtwuw-c9~]߹'M9!$WGUkW^ysm7|FBG?B!\i] !€3+r8 z>ri?ۺBX:twuo۶r͵w! ޾}BƲ6u*RGbĹ׼ :~ccc>g׿ۋž[޲fjшcipX涠oUm_`ej{+ܵdú38nkD༱q֮ΐF,XMlkju-ht0Dnٹ /ٰe˖"I2U:0BJm wnH'rGԍoK $ _z% @יt|))Z8F2^zyƍN @kB$ ]W9g]ٳ甍#!< Z9Ӵj|Oyt)q*XYH &QdҩpMSTm6diB׵_ Lj]&KCC!Ǽ:JK$mwBEъsq7;p<7T*$ĚNg :> MQh,Q4%4ЅÞ/t)it -ƕKL6!dh^`)X8u@|BLk a" "94]UkhDK"T]+X63S.tu.Sdx IDATRD6 7[F4OY3ZB@]L6Ww(J` #֥R9V BUR|T < F]'z{Tq4[u=vfW*MS)m pN4MT*,z1mB†ǔ簬,|_׽ċ D`sZ X(=<m)QV)& *A}fZiڒuKqͧ\ל^uvH 5X5XLOjqOOql^{Rr 蚳v).j%,kX-U&8/k +77|4"kN&A#Fk,JX,kC&1{5NrE^ VOqI\-`)@uMp{}k:тJ\B%'ݸqcY>X?@ (hrV9 "p`ThLq氬m,k+=U3B!5kXX: b.N# 8sX6SވfnxFfH6S2t]4sN)UEeI XMZKrB ]YYVt]+g' 3,]T\DQUujjf`py"TUZR#$|V=qp>M$1[lTMK˖T*ˆ@Ӵr,IU/ v'^fMJC+4Mj܍Z/OJ1t 4[ժd2OZdVνS'&s?5y0Ml۶W׽ƶ--'33C*A/9SS=EUUyߵZojr4NSMɶmN5s ?F#Gedjj[Ĺ4M\ 2S=(2,kXZ2Z֌1BhZe9$oCyXd&r*`j0'jGn6x $F7DVǎI!I#GdEW-Y 1Z54Nx!<08G&6Ɗz:EojԍP:JdY. :|А랕PJP&gη|/="W#D v&󹁁}ZÇ?ׯsV h-N$CCrwʕk7nܘN91J$IryݯxeCCӃ:4ڮ!Ś:Ej.[j%^ݷoO?44J#O8ͦ׭[r\. M"U.Eܱ'w,gsUW szmYut:U,̔ժ3YRT6d2`/"UiZE) |\$XZb=geS, Śa@`T @;X^*8@p{Ҁ 5@@b UP-‰'x򉉉!R9/~⿄=X,B>ۡukנ5h#Jң /kX8E*ᥗ_Z~ݲeÙL@Q.sK/|.!&N D$:44|~hhpTfL 2,v$ya$-7Ejkpt? '!?瘊W뮹blX]D .uF M vns5a;zt̰[]Y۶8;g^i%S\݊5bS̺9{;ʲmZ]Vg/1{IhuS(5N4y'T_jۿẗ́6sP@_0z^]fH# +]۶m#\M?B7z|,kW6Z{Z|rmkl,_XuK088X8 @ԜJ644wIM`HdTSx@{Z.K&H ̀4 t&21w55@ _0r#t3f²6b kQ`M7 ugƲv0ti9*JB%I ,eB?47rJ|'+ˊk噙 L&-2SUUZH$UUVk!djphEZ9v`>ńsE.M'&&W,_GT.5M34\.K|RiRtLRWfXq7ZXֱ+JCë4]WU5NVK&+N8.M^."h ZRje9PU,"Z8Y:N)m5\E"fO*4\6Xɽ@1ՠq $ґ{LgS+WjäWz{z\GXmhsV8ܭ)_rۆDus&SK)9xf+S1׿n4ZRPHl/2ƫ*ce#V+UƸ(ҸSAnS˹4~ t=*ūjve挱J"Z0ƂJV71W}F>5kU}D%`|B(ǎ$C#GȊB(u-ѫ&Ɔb!C0w }|*Zk1r[s<4k&Uu $-_0Ė\JI:>pա!!{ aFfxwh-uZB3C[l/iM565j{=j3^K/^ԡa]EQCnniA5$XvAJ"BP_03|>50PCzŨ5DB)W׫V߸qc:3($ݻwxm5]o5^2^hնcJ ѐeW\A)={_NRjȑ'NˤSk֮^by.#֒$uww);1>1 G5MW9ͮ\l`X岔bX$&PJ\:.Jt]t*d2`2kXl-I |>3|$ 4XV6`Ch˚77బ,kwUz xh0TU96e ),kh;˺8yr',& !Ry s=׋Dc.ĺ5/=cCC׭] =Q*~c_p~oo/De=ׯ[l8,^)˹\n\F4&s4&iL;u'|~hhpTBc1Nc.Y{9* U!\...d1Iٚ }Gt,<ϯ|ߒh[u)dt]Rp"`f4Oӑ:-kg1CG{1\wڪKC/jbP|z9̛G/bbPwϲCb˲YnB9{Cjwr;lL[K ט'vY5t-4 en.bk- Jɫ?w=!ZmR#oo&|}P!hmUkښZ^CclL )r:#^㓏`\#;E/ZyֿcVwwwm۶r 7I޿~߸oߞ荹NiLgmI w#o,pSqk_{m85ûo)Z7D2tmuqtM|8:b6-7>lO?o[a3555럏 6-yLEG+Cuo"B/t1~nTJ1ò>|p>9|itJ+*cL%q- j=9Vk`J:@g9F|P odV+e֭FXn]Vڀf՜Krbxu<6^iNЂnP~o(;vL$cYg70[͛7$I:r䰢(pkL/[ m/>6u^˲|gybU\)࿈"% ЄL}(RqzfϚ6n<%NscRI/իeYr׿59Cl;G"LO( 6$ScHq|vժ^۳géTZ>|ĉtb͚+W.峮| } pI!fc J!s!{Jš5'&&Ɵ{i䲙ž\.8hq(a3jnJ%IbJGH>MR}=rZ.r*dҙLFQdBDs6$)mƲ(qCKJȅD>_11\Kt/XKp4kpsaM|^BCu{,q8ᰋ ] !u#oK*AR±9C *lre 5p q!jB8%pBaU@ 8!1b$u\1I2Nf4.%yؘX*B!h}k6C 7#R>ʲ*1dYn攒10@\< '0 FNe !8%/C)TN^0@Q*1KR@&HR/槦ZP5 c*$s4`#9 `. @ sz.d"܇>DӴrE[>}u)M̤"l%0g1VE@]\.Oww%"d4W\:\M \itMLZujSDI?A8gJETI4MT*);ŚhHvfēϔe%͜wYLbďܦ ܍pI#ŀ`|U-SnjXKK66t"|}}U묞Q8kut}6M9cېr#йW<Кx!*\7"1ͣh)b`> <.)(qȊxKwM`QUJnr zIׁZ(6Wx7XH#QU;ppu}!L8: 0j d2iss¾>TA<k>y_" ڊd`Y/!_U+'hA[*ݙg#q/3սnk_ACU2v$4mϞjeRkhAdn{+G_G;FT"{7?Y'28khnnKqƒg)^|mAe(5kXXx{ЂpX~*=@#r`!e mXGnjjm|ÌxEGsBȧ'U熑e̚c>o`o+F[hvix֛b3ɝIwԚ?7W@0жUHAkk/Cի vhO)*DuM^67b2x?oXGܧH%E#߀zD-4ҕ[Ou(~5iVxZ1ˑ̃zU{#m%n5 ѶC}HAI^kGsIۑD&޾cFX;Xbroa23ZxXc[m{VN,g ex}+P4[7~5턬#5w;w 30Iz=:z}IDATg~y%|hzu je+ 4M۳wZֳ~Vp9qb2-EVRdld:E `+(E@+-:;Sq&r6b kk\$tXZ޾U?͍Vk9.m-5){P `!1!u꠱>.[ܫ3-k/Y \o~=m! OB@s8lƻWHS`҆@Fi`_.gW@fF"ۂ1]7\O)AAV B`k vkm/Oީ{u,5ŦI}f푠'aXցl'O+zFR U+H.$=ɳxFu?D#¡tqLXE'a x7:;s/[vZțK0ȏT}O$M8Ez.X8SFֳ o;[s,k+& Կ^{"8vhq\q Ww4bq&IӺ d"ZTx6x!A:mHz8h)Rev"vѻpb 9 ֋O#cutƎg(qO)x ~'Rh#X/xa:pA봁!&t}2L"8HsK F~,P BA^9k#!N@EyM3םkEgDlK"0`z1 $rN!hdSҬ5EM,j4?{}ti<eĞA5v )(^R0IkCۍ! *C`Y/\ :@ :E^N:M|k9S):DeT%&)XJ346%>eAљO s.RX[|ʠZ%~㋎78zHNKEaQN~mf;ALgNhdT|nPnYh; ڴv@k8N$WKB@b b b kkk4A''(R9~%s:b~z#t6O=9nX{:22|L&)g}{ΦkޖL1 E L<3ux^q㌜>!f|3 B>_fџyyMVu]7Ɔ *d B늵Ϫfu%b O׽j]֪kS;akwm twu7!K?I)5B8wY/ebɉJ6 e:K #m+V.kЂLJW]9BHZˍ;o 7B> Jw5ƍ WٲZ][DY4y@׃֡{۶mk~@wg(cU-kXAw^z ᷉@koݹcll87u{طu[vXs ǛZie3EVk{]W\q]/#֝qƙv[JgpZt+nA$[v GF6lٲe-HoBZru9E;朸L_Wk&H$JB3]3ͿAhk:N\>C_|q:%sEcPkM熗wq=Wa@$Q&h)|9sVw'HjziUWVU- q^/'&'C4/eJ :W&& XcPA6$rrxBt8eT*Nb LL:A;B5@@`g 眣@X7k&,kh 5V_06g{arrJ)_BI>ۼnu1>^z熇7lDkLLL߷s~C늵M[A( \gy+x>Q+f݅c0A6h0<_0Zmmc5wYE"!>^x柕dæ+ӆ"6!L% ]gǪ5nCm3gѓ&w-kȶHdAypS$9c@6@AӸ !]Is!w|GV.dEG)ֆbڔQYuUF}-P*ұNiH.Ծ|8eR骫>GUkW^ysm7|FBG?B!_A #4uc57TH.kC@սm6B5^}? |3z*5cD[P+ 8Zn&ߺs؃>٫9po~bo֭츙Zۈ{$&A Vڝ"Nj{+ܵdú38n[I|񩿳58#xhލJ 1"˭\Wp$kHeί|']{%;Z;xM7Z4ћI{YѲg﫧zZ"p,W;h":H' 7Du$Y&C46>C]|=94˻6_b+do~6a{kYJMI&dUlb7u!œǕSuR;CXoODUΙmj En]7P cdRDbHQveqsiUCj[W"f$X]~U8HJҡccMXu[DVVC}=t*id3it$$UjɱR97\w^6IrF$pDPD37{WY`'@5Y y݄.)ǜ;ȗ]xr2n%4X+bTBjڕW^ny _я|PȷX˄twuo۶;WZy%wg(cUjCt8'] ~ccc>g׿ۋž[޲fjh_0|}| 0MRk{]{lXٰ;wR7ɲvxuXw_t Dnٹ+_ĉッC^yIj5XkHUʝqlAe/ ʤW}=_NɜX&t!7?1HZCdオZ\ZëDeၮǦ=U0FfO\MnBKJe岞3]OS:|!8H)e|.)6i]\W>v _06g{arrJ)_BI>ۼnu1>^z熇7lDkLLL_y @ǓX97/#'_+}Ɵ5b 4fĶFEߧtg>Y9X:d2B[Zò׌|նm9zUkxuתnƝwMORJg-V;"_v'c-:zV$x 5m|,q&J!V]yFΝ !ȇ |;^[fx8=<{۶mwq5^jK.yw=CoRfLm] h~ccc>g׿ۋž[޲f];/FkLĥ\?ٰ~?a%w3nײy^i35M^@>t8$ܲsW|ӉěpG)֮*p  U#8 @'יt|))͌dYj8wO9TUK$8g)|֘j %zͺgt78>~LU+&6ךV]Ԝ3Uz f3DN<(6 GӴl6J;Gt&Π555@@,$b8皦-H(lybZR1[*Mͤ>&S79Vs|.Wؿ hXONN s*iZ:,AgQ`u6>^W5M0j}}%]w>4r_>{]! Zn0UЂo':J*cw/}K \UJ˜\1ͭuXɓ'm1 U lK8:C|uv\3 tvݜEZ x;h5^dZ>W*-[ .+i\Nb[ ]0^|mkqu t.^7gmVe`T&zOcǎ|k_[reLϿu/?=uDSoAGA_.iEg9reˌy7mS >&a8FRTU $ ;Q2&[ğg?3 :Қgr~bIfb8y|q`Ʌ?R78[4Ob-ū~W@A̽f[Ζo2T`x/B,je+ 4]?37'5`&UVt]NGUUe]f꾜H$~## fLO߇v@Tg_r"c&>RB%IbJlgΉ],?W袋lJmKA c\$Jqm J"Ko5UV}C"n W)D݇SX,Z;^`^9R7H$)a =8d/l Nkz6"OMMzukWnsg/D8fP/%9#^*3^ɏ?Mk׮祗vၞnL |sB˚RJD7Jf+W K~'VB~岁ˇ4%֜Q*& SIq8+,hWw!LVL&e2iJ3+4('ĐPNc =Κ(l&I׵)%X k9 7:HT&İ0iRRk9L kY3F(%]BfYBf_02i5甆:XY L&,ܻ0CdYf9>Q)3dY͙uRJ gpJx,k27E*/ Ts޴{RqF8 F0n0/%%5'K #֔RL:ZB@55@@@b b b kkXXX 555@4M@9uVSۥdBeJi'`ЃX4wmZ@p֦餩j5ugߛi_JiN:mfL%j7b.> tp;fu:q>;Hآӑ^7Tf$kp3!"O|*^!̼NB: r:+UF{x a*2.U!aE Qp35Dt.3W0} Dt R^p-gv4X =fJv:d˲)BuP&^a ^"TUI#(RG1E`,mRe]+D`7F0sj(`2"Z D":Wz6t/0>P\jQB0>a濄3P  W\3*HaT2 fm"J"ʂR K3&QR hQ|ѼT$r.k~̛LHYX^JfNhg׿OxLD&dN60Gw2x] eWD+iJ{6D1z6$Qgm$Drm:d<[?ە{ŘgQ[M6\s39L6"*P 7y !h3":DDQoM"T^"zzi=DD(ށ- #"#-'34i|"/V%ѷ'o`x."z[nD-t}CD?AVODh~ @ ۵3LD+x"CD E[-v"JuBm0W  :$mH?e#m*;g"-k1#M~|L5<&U4#mpۡlr8@>CE#r|` $/GMpE> RHo=GZH hiҼ&сGI[^_o5H V-W! ʖt `3 e!U%"ס=`x@ uQwNo)'elѦ~^6(}MmphqR-gEY}0_r]Ya8Tq(}'" ~T&.yP='VE~U/'ܭJFU}B0^ e UDX0"xFgDW(Go?#2_ ѯE B/g7_TaE%BP]-ʉxaTώa BUx_8w(٢y~*f1=*:a4?uW(niPxI@* {?(_Uc[ctxUǨڤKK4:jl.UPċhw6hMjD~qڲ5ܭ޲u_zhG2U[`p|_YF*w;:w*?naSEcjzQߋE^߉x? h^ @*;DbEN1fT-rwAt]jB9흼+Ue*"{Qwe  a}&!q^&J "D T|S4iL^ceU7"gɪu ="~UX )UC "D#[C}H@ab` @*n*kDݲmȏI\$%_-?SJM!oDׄ_!~5L ":I]0vIE, f(i6罓S*  ?k抰yF ާ1pz R*~`-GSkdw6Ro"YL`Tz=,&a<{|8~Q̮"Rj½@~0~Gk?x"sd:Fī_5$X;y{ޡii겷=Fda|{iH[߈SEc/Go=?&ES",՘v@؏i~Pn~@] :QcWn(X]F$ \n{gzՉ@NxoC9K4߳4$x&"fM"Fm Ṫ;?TC7ĽG'}~N5$8C ''DT"'DF( 8rD^)ߟ!Xg8]ĻN'^0WޚYc4ᯩU$x&"|iV''U_l!t].~Kt!8B]](MS|v!'OO{|+f1rQk4XM Q} 2oRgy>M~LC$ZVO*¿TmwX]C}NC.7"j5<1ME?Y;1K lk4ߵXajo =[:L1 [[AqZ~P?L#?8Z*]~Ga٪o^`5Dc>C+"\(p"M1rLF`9$^cۈZ8{W!oB9$Yՙktv@gLL+s(D4B aA}``I4ҶjP"x`a'H3{ڪܬ3(0>&YPl"keU > e a;4ƁFlN?kq0Zlj Bk +_ ~"ӄQ}᝝_ {NLFa"RU X1ئ ϼ+ZD4LD 6ɹ=@?S4z?HUVm8g߆AU;oRMDuӇ4Bql 9S܆Vb'v2D*3S(lN_+HAf8ʑ £t(*iz^{DlCk1EvIsLMCX+Ɵh漶&m(9rqG0[58#vWPՊ߮ V]`\NrS"Ɠۈh8'O ȕ뀒:wqb?&f3vZ=m2~z 1OF(ͩ9\"P4ʌYxn&"".:3'fpdwJt aPU/N%fޠ"PL3/\:aP*ҙoxW'g^U:wPFΓx B/Vۯn?"NЄBD럘3 ehݒUOQFZ `F3tzx OO}f(gZqΝӠ8#c {!CDX;תp'K⪨?kf r Hp U\R}6fߨ"[`=H' ;lÍ1r惢ߙsd+^Q=2 ن$ F DtBy!G C 1}DNkr` xif"נxԱN (U^J$(;bۜ7n>s+` r]geKŰJTt~DSo;9TiBYYj2a\5BUv[(~0Uř^7yf/B0] r GĊ XV]?tÀrx a5>`1h5 W!>+H]e)cƈliB4s- X{꽒DA1r%J|UWk h0K <*^*Z'J?9 UHBn "|!T^V e *MP;։^(kCZ5XJ{C{Zo P~&}*`O.{D_.(N.U7Ti~0 21= a)/h |3Z:[o2IsUXm^޻Ux->W!~B{݆Kyn>Eo5^?/p]Cgx"([ w8Uqzf.^W1~<w)͝hFj7yfQ^^Py@3cjX{=Ҥm[*$MWwE:UH{WHf(?6N޶6U-hx=Z#^8_U&kE>F3C\E`(Rѿ7I h * ?2(1աX͝N(;WU^5ޡs4W!~c RrkEXB_CQ-G6:kYt jNjr^x _D|8b[(~F%/3>M%Ĭi0&`RUZgPuxR+4~.:6Y~tP.—zWׄ '3gE<ÀeS"~[օy*mkP\ RJPzWz+uY-(7h0n~Qׇ:3-ŪV:->fbb;\BDDy ŹF&'9/!f^CD#E~bBl7ZA 1kwQ|)ȡwKn>bP^x}VCz[; L* eI3ss e2D4Ex_)-v5+zX%cJ#DZ07:i};{.u0 ."E{;AAwq#0'Hw1Y=16iE=)kN@ܠs7OfRn)Ƹb !vU'+v)tep^ȻA=f3uɎ2!;z&V^! 띢sz]c"*LDD]S_mhQ?{>ua觿FZvN;Q_unEM "|Fkx ט7"w!?5<-]KĎ+$@x1slAb&ABBBBBBBBBBBBBC $XBBBBBBBBBBBBBB` "$RKH7G GSǡ%؆(^jBB`Ό$ !!1^%$$}x 6AP$ I%:301EщBI%$BYPmP~q$̼rRr%XBB! ;1f I%pTDHmHHH,!a(j$X+;S$$:^$ !a D R-+Kt,P@DR, (=R-+Kt 0sR'HmHHH,!!ېD\ $XKA G(ڐ ]]BV I% ` c` ː+`'3*$$ *(0z, @D1v  <,!IDW[`ԆDC`IH6$!a7@$X B lC]r%XB`. y.XB"ADIrڐ$XBFȕ` I%y ڐZRAD һr%XB`. ,!ݐ+Xa\)U!!tSjCB`y.XBB` ن$$r,U!!IDW& %$$/! h yXB` f e+LԆDtdBْr"IB-K,+Uq/CRpL $'%f±keΜ9={㖊X$#h"/O=5_#K-%q}tp1tūH0%6c 7v٧HD8L}t{/IpW_$#w8bbltlc c9;yN6)¯H6IB6 2KgsC;Y+KnOKIV$^BQBmbw6l*~Z!C#>>l^pHeK*n/XlgKQI, IDATV ȡW1:d{ʁmdAyFXVJΖ"*K"Lp"!! qnv讀czgΰ`8(-]N 9tL N*Տ l!!H!>0 *Vp B['w6-U(+@nn=$veDsc 1$+ 9) 憈 H))5j$-['m ˉC&!lx_Hl ,.*FaaA8WlKۃsPFd8=5+ a1UEm(mSOEVhWkkP_;L:!#6rG퓝r6"11Yn r([#I& 1u6r455b_quu5o@bb"RRR#5%U w)"NKHmۆD3aYfaϭA!/}s2/?*LZ;#L%E858evMhnF D!moO-CEErK[Ό.#v(E.A3t t#NAMM>t>ƍ76Iw#z>yD\7&ۋ_~Y͛7 jkx@DHNNBFF7 C߾ҹVrE]ZX֥(d p:w6ؐ`]$8 UPHq;a!OUWWv裇FJl4JXT@D+++Spp f$]%rs ~Y+Q>$6{ 3{Q\6 }߿ݻw@Ŗr?='N +;5ص7:,Y y8rPE 12Ϝ$v>UT\‚SvJ"!vApHd27\ iFrǃ N*SR\ѣFAXyDS`[55LlBy`E N#ᖝ% P3h6mŮ]x<@l0mX1`0\3ݴbbbp `b\pBѪش 't2 Fvv62|p:薞^z\ҹxr^ 6"ApIq N FtG$€Uad_¬z4esoIqۧ䃡_$M\6ӪUE(Sfk W]3ڋ |NFBL#ŇhK _ A\ b.46z0]{M4 ,M_6s|x7#(UՖp3rTJ|&L/px@\ j3l 8p0Qomijr3Ρ wF`۪)yX}=aWZy Po%}Y7Bi#2FM7 .ѪJVK?eGSVVIJLMT`VF998X{Xld#i/] Ǎ:[O_nE^ޱ;0k[>ؓx'~jӴKB|-|Co9Sg/1-5Dl۞Dk_޽. G 캢mV5u`9>lR`Imk*+nA{rС66m1`@6ldi{q]⥗^_;Ͳ)wu;|a03n6|||>֣;i% 뗈AtnD|\*M1`mS&O‚ n຺z$ q 11Ng3\.7:,[Kaz7Vd\0M){ɰ'd!Ƥ$ ANJDRJ&tI]#vEXoX/ =B |!1Xei SEB'bRDsSMmY%ȦMvYJ80Yr% t0תm jh@?6jގ6obō9j: NBu!$lw`̘q睷kgD}≧OcիgpY'muZ/*N:i yY&e^k\,^#/^W\1%Y` @Áӓ1kV"|Ess ;?szp鍆;I&b"!{<>ښZ~P_7x ..P~ZRGD(VZBͼzN]w|LՒ ֋odS\=k~e]2%Ĭ!&nYY|)**AaAclPp{ #Qz FX#2vD}($АEEE(,,lZVa;io3Ş:=$;`{x$0}l~};nC=xO'݅=3d#륭Nƿ_M^VpfU8Xx N+fN;ȕ."&&8gcϡXHw.nK!4ԓ97NDsc#b} Pb"?GgBs H[}$ԬS)XihW"Vuz;t0rl9 ˟[KkW` fj1&FL0|[; 466G F#3=Equ3lknɃ{c;tJdB`Un466NVHͼ3VHӧ+GƲf=>k̓:"g\ЙHYYrssUCjеal;̈q --Ng<+?/ŏK{Œ^c+޾cxiy͝8;l#w(zyOǏ?.)w?`q,sI%Za8\>}'5 =]QVn@z\*z6f49Aúwرskvi||3AZZ:* DͤbDؠAf w;1J7Lavh3z&92;VCZ2&x56lzuϴm :`;-EZQlbKKq㐜ZX47G`۵$ắmLD#xmñq9gaÞwdfe66򬫯@*+0|l`6^}UKгgf]}=8lx<ߡ婧żŽ܁3.?sy̞u% Ipbw.?Mݝ8<% NcԻqˑ@%ˋ=nL<AŖ >ҰvBZz:؇$w oJX}$f zMlAt覥ݛV8Y~&ZV9|>wKw]%SZdUHNd uHJjy>I__̺[9g"m_qf߳Ϫ0Zxe՚S@ +/A~(47ִ@c-lЭ%9ȮdQ;^cE"iftFFF7?gm"1.d0wI'$b6NS40w&r[e`[;${-׵vM8cn*/e:U[ë;vbS➻oq㪫fcpgg&egy&XM(Uu{׀xu,ݹ}{!+Bpp{Gz v k׮x{3O| R, mVNZuĥS]n-9yoI%4AOG,c1 ctTи<~,z꣚A뜱\+ihv zN@2lqѪ")4pИzW= MPc)Z?j:aqGSĖ-4r].J6rZj*Ǝ;ޯ9vac7p&Qa;!- d L0[▛o1klsfߐm֑sAۗm rLI#Xaom " [kӞ={p≓QgKm( \D=pĂفUiu6r87"!{BR-ъ,TTUՈkapn!6 LOJѣP[Wc՗`a߯?Z;inj}{~8nD"1!nW,WnFdۀK#?-9Cic=b"".gB!{sg}.8pDa 0sRQTJ)2KK?ɉ ŦU/n٦LmF65-<8y=N@p8lÙvG֋E W;?o87t %aXCu;syuu;;,Y;~AzzpXdefHb&Ȱ9I}#|wxf矄.̌>S1QO{ʧsMp޵81AژN8%h;kL >_xvpϣDbδ;^J//>q8]1 )ޡppPp#q [0;ѳWOOtHJJBBB"bcc  % Cd:J\VdU@&'hTE&24̃3`oOcIqkIٚDf4 C m`\4'Oɭ֭Cnn$d3ۏJkFڲr zF_#p9ሉ δ;^Zp!lFyr0c Mn7l\TTˆ~﫛f{^7ȯ[0~X_D9iwdH,va8sv;v;b:6*)ȰBllu84B\\\YCϑm$ zĘlb [l&ʑ"-EE8mIhRx9v2kI+~\1e<87 `5i#wd;Kň hnvt6k8}V t ^<3~X|w8ԓ4 9ܖB@yUfݱxlv.>#JHX@uu5뇪*8v*8L ֭p)`~becr(:de{ `@9}6!6ׇ yV:J]M> I`Ap(//HD(({ErLax˖ /o$#+(lM++bԨqUadq{m-aQ 6lXATkDC;\rzS"b&8u,5s4?kU 7wP[ 61 HAKD 'NHet92gW'n 0Hʆ ѷO_Z6",x֖mM)@-fQoźSFgUTTYaBll,ɴeFO"ҌC Y @XW,1{k?Z*@-EӐo18^o6y_Fqq &j1a6RU`nE|6Ao3ٕ*NQޟ؛={lnF~ø,G@pr;?.U!U 'gɅsIz% r%fRҲ|Կ)' C~#=ymU^3 +p̈́{S|̠5~xgS3u^rJfي )$8:ʋ(z[tdRXxVrd kЏ j$ڢbX,0J_00 # c3I%7Ϩ}{ѿ?CC\N>$I Íb `=yaF^;u'arS]m%j+Q/>fp$CìT\RBC)Go"a%fI}u4vDH3E(,(h0|$.K"HzMfL֭Я_?8!%(N#_XşC*TQ*AFCFo-/ߢC/!I$L'JEHHblxMMM "$%%s+f ]#88-[g nq[_c-8`} P##*1n\@_\@2Ɓ^z 55% ݺ RSЫ]; AlltFD#&6" e+GItmGbbRSS2$:*3!@۸q/Dfill6mm 5$?8rՌX?~,,Xs_#z<.~&DQ[`<8tݺ|Сjdffb MU0>>!Šhn'#Km߃ofD=tٷo?һaժո+K/@՟|sn|N;z?ۇnPj%1#aKfp)o߾83pB|HJJرcѫ`͚u:(%o-ɠnS裏"7Xt9f;iHLLDx㍷[!7''z$Wlեصk'@}!?/gq <C dV5>b bcs ..Ś5u6TU).;; G #{p6v{Lk$5.>!33G ѣG!SRoSpmڵkp6G&&&#%%Mžҟ0p@du?O>(rfTp""B\|2kobɒxf͚N: v퐉bȶ)ɮ(9rc$ I6 :[Mcq7bV0֭ؖڌ[n}cz/X;bbt9|Q~~>Rl7cbc'XQqm wq2=Ȁoݶ#۷ć'466b9o6\.W @h$866>(vXNQVwng8n{\qTl FƯqx1vXLv  "P}'ߦm;P8=Q^QqO#v<Yٖ䋋OµᅬlS6m뮻 .DbBfN[vvڅ /8nס%8cq7Ǐ ㏸'O ef_^zF||<وEOÂDZZC*RPz nf,Z}rCHcm~3gL}#F R[U[N'amxQVV d#Am7~y~} z$.7Φf[ T 6m*k#Þ{a&އ8ɠȁ'|̌^z_9i+/@vV232d߻ c I\GL,@q'#a@!Uo[n7 ޹hjPGհ"K*+y` I%:[[McąW79q S߸oعvɓ&`u'/|x657ct6xr6@8I\㔄ɓZ]CC.[Cb -[ ÁXI'MFYY9x]D)d56.*^{5̛7/`5k`ܹhjv"!B[1"ySp-GVTc嚃Dw 3?5 3g@LL .47ֆ\_ׄA Eؼ;~^W~nzE|0sLCfIذafΜ[oհ*␞_xgq^x~sÍqERrTjkk1atM+W hS2*6p:5z}8jkkq!p -- Át?~})ȁTxd\:u שeU`R2d;j w߃;͍`X_0!!!qĀ8唩HK~a&4;81ؼ?(i8?Ҥ?֬Y4yDhMMm2]͘={6{9W\_/"2Muuojy"<[88 NoÁX8jHo㪻,fzT;f\~ݎ.Ae>TT_\t޵hljŢE1p6غ(WܨDE,!!ID FC{??ޚm2d0\nl Zfسg7n3_?o)#9V1c`ƌh?Vž={}sq% Aq̰cr"^{ \r ;＀q-ZO>P0"p6++9MM+:AW\jF.uk<^b \uULOs]r A|Ijyq]^^J3&SLۢzDϿ@||PSSݺu@ll,: } ,&b_}˟>Gxv*+159Pp <8?ql۾/ jf 4_'֬Ya&!--0?|8pӧOCSӲk2LM܀| vXd F[owqbcaߒR#V|4 n'nki"=Gs4éNEsS ӕx0i3n^cQSSϿz;ێf=W_{hjA{ <Wh@Yy9.<NTAׄzKiq>y.~-V(e 5ΤÂE(= TڳW^y%*pDEnoh@s1׭X|9.Ӊw}\r \ڈLdff{m~;gBP#jV̞=CNժU5k_7-- w9 ˅gn>w/~5Ì3pWs$$&MMMp:HLLDRRwJJ 0e'CyGQuawoz!4H)" Ҥ  JGzoR)' (i(Nz:$$!e><3sg̝{=t-7v~{o?7:ϩÆM?2u4ZN@벐GM6g:G{n8~[nGKf&b;{<.\z'ePg$IV & +m۽dbʔ)PF%YEv4BCtp d=>C|8kpYLbhxk?2#!-u,}Zg)ܹzuZO*47bL1ҥ*2eb +VN뇣7o.RqhU鳖A툌g+˚`/dM'hÃ;hٿoe%Gr)Qes>q<>!B\\BQaWQi]AaѢEP(Dl1sP*իWSjUJ|a׮]$&&Һu맚gݽܽ_/M дy $I"4  kˋweik$I\ Lwf׮] 緋 fmr]hJr|/~wW_} l2ztl})T2CRRC IQJ j5DT$+֭(dgZ-nnn으999R*mqyRL6.s$}w|f\6+Jyi;MUV*TgYtJׯu.^ggL ԓy.Ԩ ?oF)P0[*e2(TDx4I eYٔDN5k7eʕ2v(.]HƍСs!;&cfK\L Fʓ&?>!Р55ydel gұݯo1VBAtL<̦{ױ|\Xo1Wf}a3{/z3i"~Hvoȑ9xLQ?;Q2} IW^'00P /S`!f!)!D^M=i?K#SSި:vk:ޝX,V=vaDE'g$6ބNyiv֨QT9rN$oxbKQ</ߜů|~*4 `gPkU^ Zf =U7ߢ[jMZ?PL#X%ՂJbdʔ),Z0Ji6 Nc͚5*U[FR&sxP1Yj؎tT}Yn;kugK&UCʞs,Jqw{aԫ?;7b@q+SCFm8ߢywG*!_P(e)%Cre`}NM0Äˤ{( d*yϛxKPH[N>}c14f4WXѣ\z|jtt4?3[lSD #qa9w7EX<./W1YIgb6hdUd-ZJh4X`+Vb՜tz*?ooo-ZD'ҥCd5>d( *YΝ A[FRD₫k*"g>=KlB+hnۮuqoAZSӤO%j bQ]ܸO,FP(…K:8*~Aٳp\\ӝP|2l2\t9C'kaxR#Rgl3hժ%5kȑ#z&͚1aXv^z%;*U21>:vYHRݍ6S՚7 .;b9*!L:t9}Y,kNsYiJP,j2}gƤyNn]ӛÇ6X-O/_-Y@^S$hRT*%{Ga\J 3 h)\[Go.~xC~ q=9`r*Hb($q; yDDD>U˶}9f7GblJ#"ͨTJV}QT3*>ŠIw}Gƍy 6IQ(D6lLɏ-9t܅:tR[6ǻYC[W.xj5m2;q6*J\\\_8p ޞ,y9 IDATJkMozDEEa4>9>_Ec絗'Nѷ>2o8 .JJEpww'$$VC8If֭)MOTz>[eU/Di; ?/mF]2%ʫB`2R6TrY<<=  ONݺY"JU2fS"ӧO^z,_;~~,f;og >|J*1tPƌZcsZ^{oV$$Ń#͡%`ёU,sUlňz@ַhuO]) ,, O@Ξݿlb뎵y7z{nqh:f::}ʟ _>nݺeÙߘ ؼI|O @₌( =Cx) !_`'+$X$))ϑ$)Eȓxt*HXsvn:w'r3@T 8~ ?g EXʹ}ŋ߸[ڵ[N?ogo]jbb"^C͚ΫRoNI{.ez}DDA\\\jVk+IoڶkB<5Vq?g0Yy11cHàpϗ'z J*Ãk׮Ѿ(|9=ޞ k߿?]v%&&&C"tɗٖvژf4Z c 2ɖ;( &?=_ǐxˁC~gq|c^ 2r*VVˁHJJ5f 'OP Q|?損f foƢ(qi qqz+2F'%%r^^ kׯ斦}۷(_===v˚0 !‘QԬY-[2df4k2S6M6eǎ|h!#GI?M7:u;{߯iӦvZ-ZDF HJ}bGz;vs? ANm lG*39V1 UC(^RZͨTjZ4yk/qԨVQf3sHjՉiԲ,N.L+VkX̨Ybqsw_~,\Q[(m۶fO> 1ehvI„B$3`- Eg n@3} t+ Z.䧈[;.űۙ/6Xu6ձQgT*>2Ɍ>‘gro}6Goᗓ+[*U*٣7h٢+ֳ8ŅiHpOƎS~ڽ搒pdƍ0r?S|Eb!V\=BNkF(Q sE6.[HϿȖ|OFaԺu~X(K:y7={68Z;7; 7_˿zfՕZCvfӷdPk,=_(U$Xbcѽ ;w/+Bpv.>|FމgM oeHls CJPmdK˭[avyFU*DFFM.>>!@eɂ&_ EX-ʡwg]G T38o23uTvVѥk7 ),0mb\ I&̚5O}rˣgٟg@TiH]|>>n2rI&]8J|tJҙ H V% .>_y."**+W/`4.̌)R؟Sp1<`$D9sڵ#Fs3,_.&_>/5kƌS_}yHի[L_LtRu^vr@~ˀ@=``H+j@smWBB)rSdr{djV^P"<}ʦ1zG&HV) +o9n_ǙF*:B߼.Tz+2d@l6P|Y:t5k?o.gz"]ʕ>b:wIkϕR*%ɥe:T06eV=8hD\,*?* VNY-&5jV|Ku:j̠Y7C0}R'>!`2[S'?Ӝ%0` !!cNRLY̙#`\#ɐI !D1;+I#/J@!DIRmNj$IZd?Tq x$IS<8E#9ʗ%SSӰ 4 MT=ą:w'2F*(蚺qq~9O~aR9P"V899+ZhI/?5m

рNcӦ &&=wrʼ|TqIC5&o @ pL C*\=+Wmoh4Z* S;;wo245K=-ٻ6w·C=ΐkVH 6_L>ڏ~5ìefٻzfU$<=={^1gg'ʗ/60)ܒ$/o\\c-# r!'n:6m={\D4'G} $)1goaDƯœ@8QvѢCa6iڴ G Jz-xm7#vmh1 T$1$jڔ7')!;y`bϙ2e >@F ߆bV-|̐!]V@`}ش)1X}lS_VA6o|8cɒ%l}xz &PbE͛L63k,4 UJR`Aw tͫ9w_/[Jnj5SJU~a[a5K$U~k# Jڢe>a$qb UWj5纼Z ?r4"Ed)VJ+%Ng '2d<7K޽Yp!sfd] _~oql\N2=%J=H56lYٌ5M_FQ)J(O?0]uoWJ @?sx>I2e Zb>>!=2c )S J%c'ݺDRP| SЃ}ͥKjhYFZh<IE9oѫݨ^܈)ޕ(ފYfXT/+bRrc{/_6[>+bd1,TX.]rBs"Cf0=z4NNNۻ'l޼߻ ǎF+iqY>4b^5. ,㡣Thׅmؒ6dL䭷ZӫwO}3Nr~:u+fSvdQ|н1Z*a՚ԭ݂A3xZT^ߟVYqsѮm\yG^ƋKS,2 ~a(j4i !]=*l}pH9,g7<2'ӮA]3,C $N ZٹwbTZ,+PBvM-?Dp1˫RAw:+_| rw"l V|BqFz=899a0<.{7ofŊ#AѢE9r _[ТD:uO:KP(F  xfG`0rlG@/C^ aYo[וNHcPAce5oJ"2jZMɒ\x%.\|{ze#${{StteΩ g&>O gpEZ5oJfMt -FR1uT@ݻ@ q۷Kf0vRJpe>tLoHa|ԧ7 &o٢Ӝ<^`P""ݻf,Y;T9!1(Y- 2P9</}ã9JL?J% ~i@++i+dդy©!)^2}$_Nӵ[o$ -Ⱥ;s*&"P밞;Ë>|TlBUuH[$J4u*BNb8%ᔛk ʔpX"Y-J݅P?EY^tŊN[d(% U?~˗l2z=QQQᵢD6mbǎ,_*IIj92$Ofk yPǍ%f fLtއ+WRTInV]{ H2l1g̤zjTh_){^cC:JWO>SZùh_F6&ϗ tp36={tdZNĻPk2#2ϐ!CXx1ǎE7ύ;-3>ɐA8p jm~:V3ΞO-ѤYN$+h&NC5j4_|9 wwO+b۱J:tiiݺ Lڭ3b93f;v 1iՃˡ)UJ2 ~ €t6_#1~Ö́$]MNq@=!$H x^j$+/$q\b@z9#nr%>]v})L/CxݜuN3{fZƉQ#Jf]&#(nnnX°{3כ"\Ϋ7رcsĈ!=rW^upl5kIȗ>ݻСi:vu$z$߈ʗúuoprrl6R<6mÆ cΝTRTi,K=9{?b ̛?E6t@xt///֭]E&әe[Fʗ+ر=s>S.U+H8㯧#f,cʔ,QCRWB~jє{QkdJ~8ڶ`B # [5g׮O ɈV`Zpr3rHl֬ f݋߭aԘL4]EF1Qǁ*V3By/II1T\_>|0T*Ԯ]g' 9sf3dP  8 ]i=5j4SN E+j\2 ~2$!63@ k!D[vXN~z M%I)P %rmFTB((hWf8Wq$S>G$^/񑹖Ãrl]<-G9ˏ+SؿٛP*XBa:c[\MݻD߾hĩ ?}3gfzO^OժU0&侼vh6mӿ!\IH`/51o߾lْ &PB:v븓FJ9̞>݃ `ٸn~?Tqj4Qgeϸ~+Q_ޛ w5]@Jllz:tb޾ Ij՜~05F8eܿ?:ƫ5$Pk4$ѲES>x JŒ2d!}yfX|9*DV^Mn]XZ|}yݺuݻ&ItZ;.ߣȿ?[|v{=J*bBP!C&/B|JvX!I!D`p]q(M|pOA !6yaE#wc$Ѷpzxq&dPpaBB.P̜nԨ>~Th4jrZBJ$!rtX^ÐhU YRHܲFwy#pG}u@Y[KdHβ ];z&iMk (\!UǁR $0dBB.ԫW&RhfK$d >uxyy: $ 1,+0l<S @ѻiR]sJ72eg߬4+$AR];;??oކoQK":: .r36ڶJ&95{LxϗK.;L& |5lݺMI7Sb y5 uIZ0g`"IX/^HttS?M>)W:ڄK`l2  F> h"rfȐIv#?Ia0~$Iq$-B*I2lb4v!Dil׵4_DJ Jn#$ۄ/#?z%.hL`ە虯*UTs0^O||<{5W3%;>.'>l20~`Lr9/k(ͬc6etQt&/ ))I"Yr:jlO?AIF =NXs' Ƹ<?|kROR{ !N% WWW0g[;ZOL&]4CωIIؾKҸQR\]]=lZlφn|(솳I0pR ¢hڬ ߝLAUNԾ!2II7oJX'ه h:4hGe٣+ rHww7T*%OK!228ːiES7 ]f͚%O8M8QF( ^xX8cv0n])V,'$15kV3lpfO KT3rիeGK !;Kxa&Y5V\g#>#.1k]]ݘ1}f$̆=KcIH+h5Z_7~N; SjZ$ 01fջp&J@-eimpVM=]ukہ'-x080^ZǚӣG9R(|7n;LGvȑ[{ƴiӲUoCCPF]ԮE`?w<kNzv$J j7ob۶m?q[P()TUҼys)%.1Q*"9},׮ 2F<=<(V,`̓JV ˕ BpPj1}fׇ͚T^M&3CC)]*:C9b ZǏ믿&::_X #[غm'k_˚Vș!㥃S&U `H`TiP8yLyB@tph4'8<֜8 Ib4?S/Y-Dx=i$AZ'>HRcu:NZLƄo3۷d .ζFSRQd0kעUfԩZʳRAP%|4af13 RL-eOjb1[W$Vkۦ@FM&'Oqx)\"E ?ZҠRiQu0!#;B"""8a2d,%ɦ-2dȐ!_ĵk)OLJə/CFqAT v g)9 9KdȐ!C  xLVx||北!#z䌐B%g_ 2d3&gg'ZɐU|9ŋuFLe8B/eRےȐ!Ct:}m2d>d 2 X|-R0\WGd,C 2dȐ!#/!BːI<=v8[nBte L&2dȐ!C 2r+$XLe<;r oG$&ɾn GoZ&9seȐ!C 2d</_ !`/Av!ѾQB)^,ŋQ[pgS 2dȐ!C+ԯ_O/58B)@s$I7v$Il$I7^,b>+^Fh\Jdd+H?"E 2dȐHJJBb$ː]o،3Pje93dprzn5~@E@EiŤ!#x!H$IqVAi:DN>^ɕ6R|)~\+DEEGbkۆwi'2dȐ!#8 S+s2GkeBs&We:WFQB''$$z*W\ŧ@Ȑ!C ˕Vɔ$g Q!pww3CL5$I B3:/K{ !t!ޓΒ$Dn! H"||8i˕|r-nb0QT*YYYYJOjrxl@uF7Z- qHVMZFTȸ&,f3 JRB]+وj~&2TPgLZj=ᨆ1 7 2^|,lVI4&$Iz3 y< $!\-uI`*,!fPx[D!6l5XIVH[X㲛&6Q$%Ӭ/"(IR10 a.`$I{!xiVRǎ5cNx4nܹK;n-I۶D`J5+ͬSݫ{۶m'5jT#| C>2wds.'N}x.NNNx]Ç`En%" $dԭ]۵α:;Nc׮ٺfL> !G۷N`6O\]xٔSҠ:zm۶q nHTR-Zlt@^Ama֭8q[wBP/UTyY!*Ko0ψs...s2 v,@{`Lo"$Ieet׎oX!Y`8ހk5BZ$h~ x؂ml&@y;~4ӬӬ[9'5͢crtd ,?{<I~߱ #G egC$N8E*=g>;LH4%qn}+T%Xlc .Fh4]5N:Q#;dY[zi}!*W*7Jbꔩ|,oMb1իWMfsi-zN:͏7RrEey 2{T%)O4B:7N:ðC ϏO+ll߿ξ;8p gQ| IH)ÐamA O \|l];0` 3*Cf`Z!CHNǎ0|0~yc`JFj{ !$YI&Iծy,KIk'oǛ7ePkcd/B-iGBsi* $mXqm's$IVc'%IaG{@t$B@u!D$IמǏ+iVVͦU*YFIxCw,irW\tO [ 0jvlۘ>KL$>>??R* h61u}ݿK0A`kK#ٺp7{߯;7,ɞÇȑOZulڴʕ+Pp,OgMZ&NbiTj+kތȏQ:Ga$%lF1c0jd2^uFѠz->mB(uEb~ RtEE21݋E8ϩ\W5zJ5HVLƤtͿzWjz#Gݼdzʉ)ch5l AҚ4&И4&$9lEch ho3{>)'7= g)}xCn 1s_'hmכ[ݵɶ>6 >t8gWONK ~h6m̾_CRb4z"̻}BGغm'ϘNkhntޝ-;~)Z:ITo1w,߸Ʋ%KIJʑF ]wgkd3io\gWKҕ+ N&>~OMnXaIGhPy{Б~%_+W;ٷ?Eaذ%$RbyLj5ZI~!$x7`xDTl' )LUO:Φ!U쿧I{2~ om!DzZ$iCn@O(6 }.S "]&@Ro،v;o g_$81p1s$߯%2oF$>850v<{̙Y/Yuf ǎ X-R&ˎ>VZ3ŋN`MS$EI$Z1KX @@@-!y?X`<ӥ}";VEQ"LBf.qqO|M:ܔJmazKrP՜:u_tj74z5_wl+J:~w.fzHFn0HLJ3ުZfYjƬ'Oh@dmY!`y1E ?UM?o%n,k|P`E$ ˡWUuƍ[غm'4.WW:u|%K0yܸqRK;ku6֝<>OZz"\PzV6?A da$%\?yN< :g'#IB\x4* fUhJVt~VXh}0:c *^mUS4m4!6S述)(cS|iM9CGm,/cv“7jI„5.@[lT!ď@7I3 [a3J7 y JZG$;ٻ^=2ml6kñqqq̍Dd7}vʵ5"΢\dͪ/N?̙=&$ufw!HN̻תj*֮LڞmK6=#e?% h:u m)n_j+Q3 naWK8?Qn+[&OAdK1J|h+$ Zg%.J\ ̠)̣3I"(8]]扴hDɝxyX$RJ釱_>zBPdp_qe2_M&<,y[/r|_268;IO=@^mV/C& )S?`ie\8]njh>:MeKZGz<d1㳡}NTz)ŋ Eb.]reKTR%K,d0`Vi9y ӧMz;y1 F{vQ0qDZjErL!C9gjL6VZS"/Yw抓eM0khz'ex{ xƿ'`jr,4tZbM4亞5zNjc۶m?~re =@Km6nHd|!xH@5; > IYM&ILs 9642HRi Q<`6֟`s v6Ӭ)_jY)IpFi7><#:&6$9YpYn\2{l,胇3&L (I@P /2L ]|zV4F6mң߳|P b#Aycsf~vާ6 ɘӨUCϓ2,3$c-DE`0ZШ)`6**.(L#z=hcYYƉ5 <$rY?J|ڠ]~STQ&@3'Nر7|_+As9fVR ĭk! ^R?{O9VjF׈v6JVWE$x'@o fL_G]lތz$1!2[`+S Q"8(eKseʖ-o:3t*5 H) cfĤ%彩ПF g-$fk >JMjB6m[h ݻ}{X"cѷLXܳτB܉}w`- }S(>O3laVJ5=fl ?$u~4ZUH7MI%I,B`d]y/6Ӭhtg ߬`J֭[ILLb`6;w|߳Ws)@;bA ~;(E DOAQDlD@"%qɑJ.Ɂy8.;;7;;e?;<3٬_IB[tS&?ӪSWޜrܥ :1tȠ *uz'wA~_iDmD4w51y/^Z *zaƌO{7QB nr]IɃ`-<7.'MFh2,yIo1lL*#,z7Ety!9Sh𛭬ޠ?/"x# "Hff&ӧOgȑdffvH=Y:$ ibfMZ8y\&©e;˜;;wF6\TUkÙfvƁ9IN<&Í}zul܌vfĉ$:*\nW~C+RUUej<4*B>] G ըUt5%n$i;y,r.zJV3'T!^ _}ߙEL\U7c>NǎiӪ .Wxi³eoѼsEG3~ ~֯l\̠,!7sǓۇr^71\'i3>Ӆ3FIQ(=N'WzCkp<UeiykuF.; | 7rXx1fDEz|\xI!V<)mB"XPWbb0IQUQy6-޳W`Y8`O\flb65kuVn;`M/+^3wAS>8<(hJ&1羜=qLͲ?0d 4Zvgqj%$4H3(h J@ o>|:icԳxqlI>?N'O 9̜ȧoeqU +W^v a`Rzv;V""x$/<\Þ$$ӟ>o>9{?2b᧯}rtTFB58HjqEXeĿԩSmݴB޽0-%Q::ӧU~G(ၹT\Fdp~'.>jVXAT.ݑDoBՉAԬΊ+x>83]MУϓ;{W;$OA6)ܗ{z!n7dff+i`v ,:)]7 +GL !MB@9ˁAN%_X /Ӏ/;kOu,4Jqhg٣x&.`7W?-xg)QB@K<$/LfU.Ce"]-(5 ԫ[3)g8ra322d2g?9YתU=+S#ޝ4nȍ79i~bnw0nW\vp{kS+_ctk޻et /L 9 /SFPf'/ѵsg>(*&И@ zVp:mƠJQ IDAT*FB@)PAoh,zpDKX57 wzsDFV Ch8]EKW_MNN!!!HN}!z.n)gA*9{5\m?.rٻCWӷ/dgˤquvL%x#gj3f |^Lݺ 2cǎ%((Tƍ0yzGhxM{|o2fδt/iƣyox>z~ƨ~5 ) o~{DٯR(k׶$шl6Ӫes8߶MemD6]#k5 a֭<>>iٶm5S~0{֊OkU>۶2 KU5؏&8v[u؋Kc=ӃJ -]gD||< ,`ȐRK3%QbK-TU-n8L!Ẃϧ =pWm?"8PSBkOw S`?p~xa%hg98u222Xg{~"'N?$""Wq:U ӪEs?;G͏Rf]RӨy'VOn᧯ތW{7aFWeKa_Yɏ[Ncih .o6ʈۘ &ϾEDh_eTAMY7? II'i> 7p2P|NS'4֦¯ݖ]<{J4 3~[N;(fգOY_`?\z>i x7crJ3 dr."˥?Rc([Gm*$NqLLUx 3 ! ܢ.;_P/z\cUU%CESkNpQs ʌ_LK?\.|EcCnβoK3?Cګ߱c7/W&䗴կ_fDҥS3k&d~[ yýc"+voJfMyaE*B?0/}|z4 gGD]{^6xmSؖ !D 槑"m14D Xn. Ң(*6ՍͬⰀ"и~/TE` 1b PցzGw1C3fhRN=غu˹&no#Y1⮳7[z SطKc. *+`, MRpt؞pÔ8 6w9-̝xr*5EEpehޔoõFqd߄tat_|>Ν:5.< a$edKs 2sU. &2sM+M7mք5Vyn&4iħ}?!Po9pUv;-q4gx1 @6z+e…}z+K֭ٽ{CnC")t\d +Fq3!oYt+Ԓ;ztHVS:5믿ݷ{nMԩS1L ,@ h/ RUI Ig@/ tW+ PQЕ"2++u6r f5tk&&'"XFSzQ֜€NΏ 3d% xIش;k\#)Sw3`@ -M2{Kv3>&z֜+tNh0cڿ*BSAuP^ZMB-zxWgu DƳSynL.Z;x|rk&<:>Ĭ٤e*̔e@\׷V/6ƏR+M:nNWYl][ϊ]KYqfn`pjj2ٹg^>mzO:>=zӸ ^ǘ?IZxzD5hG.LOcv(ny݇]j^Fã'h Jygs0qNl<ģZVݨ9?Ý0k{^{/Yݻ VbĐu$`4yYOǯ`t^o\:$($'fq`n= :{?pq p:!:)$wdlβQv@;w:{vu"^{RnٲS&.ɕKXEp~g4^OxeWU5CCw)C׫WfEme)N~)޼#s1u*OOP$iqP"/:hB̒o,S_*hq2?,M6~4,m p!ju|=[գjTA2~nBAb4 B2X-.TtzA`]@&aMV}]xN&;Ѳn7t` vqcz;o01YTܷټVύC4j\g&|25ux@K)5 LMfK]iw8&LX72&e,A[vMH[ᰢcbE3i9<=yw ʇCQ[ŋÝ#϶?_'kO7烌'&ގf~w`ؠδnVBEnn3^}EQB0qLnlǑ(ہK-Ynfv`&皿N-N'uٹs7.;wS^]r3Z6fHV+yyy_C¾/r0`O<7Ё7ԫȺM 0ul]N;dܸqϡz`pi8913'2+|(m" C(awfYgYN'oMBuZ|w/ޙ?VvF._Va||%|?/7'8vt{? PV-F^5[~`fNf*BK׺CQw,/I(9-oEK%nV+-7cn2ODG&''ْ/$]_uvG7`}Nbcӳ ll_8Q0eT( =z^oJ,!\M7XQm5#gCFC{ f-G窳B9v$5܇+#ǧ0:/f忯R6lX ksNKLyzK"Eh e|,{CWtDRwm%.6ڶm͊Cc-øwCe=#FeQX[16$佨;< ߫ʟkC+GnO>e洩MH Кìಫ .@ (*_Z:Xmka)san`ct:/ۺi0tCѤ]Ԋ<~ʳ/0mG{,)>z\aQ=.Hao1ܾi֫`,?C' BPmA)v>eBt\Yl޽;_5NiZ5kpuy^?ξ}i%G%Q$W*oltelec]LUaLymVp[e'nW_[[ח&ԓyM_ui‹훱cn{S-hEC@4E k)R(^odqlc ϐ ֞W/k,)TG]C{ 3Cde^ddb>]plNm)EA՗OL( 1+媸KUU3mLq)_GoW[&Re,PӅrҬi֭4wʣ"77ӰQYmZ1?G[N֭[KYd$U{zډ)S^YF _ &pK]h &44mҰi'+P6^RK {,CIv$L!PjvlP ǜB3w}76k.sBnݨS7lFRT1E#+9|Pd"G|5*<\ّKy$nwYhUWxIGcҴysԉ.7l\KV*{_aBܼY'5fdmΝ \et1VaCK:NE <ë `.PQR$Lth,3[~1xv7*KۢI4_-@jz=SrnJogh žIzSv-yv\+"tx}ɮ".3}y"+GTСDN>5ihӦ{^,ټ=/!/v~k|1#eo੧c5WE͒̈́_豣rIia:iPLߏ0 8>U4ӅpژX˷_b[ލHŒ3vڌyVsf3w\ڶmKPkuyÎ7q3yD,f^ZH` f-_cǎUiwއK$^{M:wHZBa (NNV͚EYlVDEպR֭V-䊩;^Ǯy݌l"։#'NUM5'?8o"TɅ]?"`8o8w]K; kЭwZ?wT틢63Sf `q*%C·+/ +N&Q58xry6t+ bca$nwCfMر=fSN{ puA(8+%ZUU,L>p.1cyɴYS#/H9ĞG2|Yi˲4|<#O~6=ҐGr&N= ن-4jY*8S2qkp QN:Q5tTՍ͒UOv[-5fve.IoBJNi7^Ѱj*VZU_h4jһpWlذ!o:@Vǯj;Y]7113{&/N;o;¯O>`<̙=Ⱦ͛g>RL] eg[0[-O'W( bh i8q"~\Khk㦟IW;. VU23ٸq3t8~lHOp\m#Yn2|Πgrص tu~+*ddb^?-/F݇;QM6B0&ĤiKٵ(!&~^a;ӣ{l֨w`hu ۯSX\^:y  \Edpj5 !(3nAӴIC*O*H?͠Axg !pc~>M nߏ۾YFNǦ3{DD|Ki,a)yj?,Gv93.g2IUrTrSKtgM.2vz mmÏONok<,$>NlÖ~sOi9r2 2 r|B9tgRw(܍X2_ٟb!]yf@t:M?;tj?S荡6lpB0要<9q,yRNˎhӦ p8Ic0qD+V`붭 *쑤<';v{QQDhz( ps!΄\.v[u48vOWQ-ZEqv9p:mGąJVg@73*S ` $%D"H$Jr$D"H$D"\1H,H$D"H$)%D"H$D""X"H$D"I< IDATH$)%D"H$D",2p::u,NBQPfDQEA @ExCP4UE;~!@qPj3|o*wyU,Pn:&'|3sߞEA\7ī+=FAP蹪*8R͏Ǜ4z p@KGo#C?W7 yu,w =t᩟'y]Z/Xl).7R j=n*ۅ \nV]4TVh-U0D"H$9N.tS|$F[P-#jbD†jѿբ{JQK+d<]^NEۅc^(QROMT<˗/jJZ1U-%+Z2ł\#D[fBVvBDs:%I *[~o-|;mDՠVdr+T 9HtX\.nUu̒H$M(VϩcXf4hf..LRs¨ȌH$DR&V `"G|ԐAVp:m]N)ɕ*V/X\r9PUȚQ7B'3jd'oD"H$r "+Q(nJBv$G^@jԨ^Wkv;jteL'huȔ7N"H$Im.H$+[+8x2331FBCCiؠM6ZH`S%׹je%H$QuDqE.9JUYٙI IYs~_"$PYl۾͛7p8_5$==cǎsQrԱ-A2GqWD"H$?r`,<)@%Er1/ի7aX;z 5lܸpZlOq]ӽ5keڿ|K"H$< ,TШRG ]|_Yqv"9GK]_Wx_Y"\lg?02d0fn&N;F`6kS αYF;ݮ+~rmΎ;ʍK"H$D`PZL*Up}×_yZ%MiӺ5~pL&7o@UUF\:wHzz:V:̙cXt71c)7.D"H$󦇊i!$")S2oi}bWri`poo~{olݺ+Zt:Tr\Փd̘0-̛Zܸ$D"H.rN9F2J ኟS1_1jaQLgVg6+#ja8y_~ ~oH$3nU%0Dpp0Y t/_C=̻dGܹ{"7/@BBQUD/W-gMi޼9Gax:W{SH$D"Ig-b#G%YIOUέ), Drtn7n̿GEQUݯ# Frp"111vʍ+77wC@@,3˯D"H$ɅB`I(,/ W5yNкu+sr8t8NLJsf3qf>ZӧΡaÆJ AYx4hiTt׸++_OyUy3ϖH.6v #:t(}Ǐqݛ,?NNN.{&"" s\u ==S JppУGrH$˖-~+`0H&M۷/cWfɒ%5mVwQUr] ̜9}2h O<-[GH""Xr+. +/s]cRJ.UTՍÑGttmKBBHӦMiҤ sNg\#--DBBB0e/^oAV&11J͛y74iyfDܾ$&&. D"EDGcДHn5 ЩSGnE(iaAU4.Drq2zhzQ,Z 2e-ZFS) jq̘1㼉lD"ED3RJ$jBVӍ騰`g\@QN6l@bb"4nܸBUlEF/4&f͚3rʽ˅Vcbh4"\.N""X"H$V8֋..DriP~}9u9E޽{HHHnɵ^]w݅^`ʔ)ܹS7o^B_$559s(J'N`ҤI4h^~NM7İaxB0j(f͚Łp:2j(wGoa֭f6mѣK\ ֮]KFF4i҄zX3',2`ۼ|MN߾}eAH*}<H$D"Tƍ.3ƍyꩧ8y$z+>( 6dɒ%7@Νiذ!:twEnabcc9vd}"  ""NJJ $$$0~xRSS2d=z<쳜,f֬Y(ᅬniӦ  !!=yfիwΗtoEG~G~.H$ӦMo~ʣ>ʚ5kaeqݽ{7 <ի96|pL&{ԢE BBB۷I\\{tz#@nʍwE۵kxF3x׮]h+y%[v- 2hnFٳgB:wѣGIKKd\.v@JJ ǎz$+[ ED"H$4nܘ]z?={d<3ԭ[s?5s.h$::SNy(BNHHH5o߾VZaZ9xWGFFҨQsƩhy  x̚N:f+ׯ_eb!558 =yԥK fРA! D}y#tc, iil߱6SUU-.Lvm DrQ0?2ddd\@n7yyyTVBq￳}vbcc9s ڵiӦ<:66{ҿr% .6L y99ۣٺu+}amۖ0bccqK$)%ɖ-[Y312C$IdgguV:uHXD"ڵkp̙R9s!!!!ChZmf͚l߾t:f:+ cnרQFS浧xſ`]vر4=M7x̲.]JJJ ;wO>r%D`IeIH8D6%I9tAfD")%6nXb4$%%ѤI+pm۶l߾lbbb ~xEd2Qvmv܉f`0x>hkԭ[DRSSK̉.0k.fq\\6mbҥ\uUo?.K$U@ (Ԭ)3B"D͚( y ޽WfDrҺukڴiڵk_3229s&B"CyN:qqq$''{cv:_tܹL/ӕaddd0{l\T>aGngƌEdZl۶m}E̚;w~5jx?nVK||<<&Mp"c0hѢvsΥ:ڒH$ye.\横e|͇ȩSLj3#,DCb'###l63H:u ))oetFGrrk9M`̌,vڴm%<DDDЦM\אGdd$zNG~5jTA˖-by^RHPP͚5G`0HfիW( 006mPV-ƍj6h۶m;v䪫B'H$DOH䜤;[X(Wp^cŷˋGrebZIHHerZVe;#00#!ݳfnh)9D"H\B%&SW,D9ŒN$s芔_˳aq-[LOh2b2B(B"n3ilظG\X.3㆞=ܹ_?ӯ_?]?d%;vPV-jժfϞ#<ⷼ믿"tE`` ;vt9N233^:h4S[o L&u>fx莋^deer}%ݥYV+VB{VJKҍ7Gb'RK$@Q![{?T fw!`УaGQDGG?w<0z+&]q/rrrSUʻ=z@Q*v=zz]Ru6((.]-ݥ5HjԸʧD"MvN]vh @Q`0о}rG%9F)V%$$įrFѯyOs{^+R+n5c2*D\gEkl]흢(~o?WWȑ`y$54X p0a6lo w}򝬿(D"9vmrJ 5@%>>^fD")V-bK{D|3=_ ਨ(8ÑT_fEHMMz 999_r!00o]ȋhëI|>B4ZIv`*ĩdUU7\yEV-7D\vfK$r9j l ""OZOf͚U4l,Zߖ3T tԋk!?B K`#pXѭ e pjUJfeJ$UDќs>F|$IٿE3ge$̙3ԨQB F}Vuk0O?$&ʂ"&a},źN"vŜB%c^-<cJɧsq*H$yGQn3C - !FjB+X~`JHH "z^.rEEE EDB :H 5@=@yy63;3g'ssLo;֬.هeaz#J)3 MQQxeY6$)bƠ;X1_ob;X6k.Wė$+ڟ*$J^^ey,eY>*r4J!P>zmDWjGxoS{I n$$$pR˒?qxN@2ܹCznk)'z]郞%|xvIכX(X3f"+ HT*' {eY.Ͽp4&$)T!s$IzWa$"%IzM$Mv-I$Ia$-$i$I}Kk S6$i$I~2{ t21"8??_KP8<׮]+gϦ]vFRֳ,[AŢ>J6+WЬ񁊵HLR ᓿ`ˣ>\*8Q(Ӄ$i$IZD@Y%Ԋʃer0$`1_%Irhp$IE$; x_ռ O\f.wؼT2mcJ%jumW!ME1 c -jtR<<ϴrJJ-[t0h%##bU)Baw@n2tX|jfSo1A,?/-IH[%0u5jۦ,%Ir#@= km[ڦ=dY4@k 0S/o&Ȳ,ɲZ\f;""4;+E*)RK^__UU8m*`Au%))ٳgl20… J ac˒ ={q޽E}`vw@>CuiYd[|=&$IN"@/Yɲ|CO, hLԳ(D=+fYTH IDATl20U$0x[}UEi^+ҽM\fH[_]V0$8E,^ܿlzaebb OpzzBڵk4mjޣwz/rAU{RUʂgIqc Vw7gGvv^C#:ac({w&;ۢ틊Z5'NX\WXB ;;+V/l8\a' OPz|ՇX|)=kx(xa/>3_ YECKh r %Ij LFl[ Wu@WL۞EȴڣԬ׀@7=7ߞR̒U5ɕiF#a˷PPP%YWW\POz[o֭[C0c DVll,9998::ZMDjj*NNNXiӦ/6϶}D{D>5Vprr25k h߹~ӵtՃ1?FhPNč7hJG *HmQ>~S-ϫ>sdY]tk5,Kt!r(dA$-$I-rF ( \y/OpɤJ,j"lZqqvvɑOrs$ E+ $$P(+D ]vQFL>˗YYYL<sQBBB,>˗DXXJ_>}qe ԫW76%%{Ѳe wVZ0pBB-Z0Xf/b;QH~}{3Yc=Ŭ> cT*\D*$IeY~@6Sg`>0MI–(ÙPֱ,g I>V Ӄ$j뀾Es%Ijr (6,W&#%I@Y+j?e9[@_IVȲ_xņ/kӚӔLQ(ť'rsrp(jzÌy!lZQ!jOCV-Y0 o ?d̘1E-[ܹs)e.\9s>}:˖-3Igff_>۷رctլݲe ڵ*5СC+wkkk˪U`ĉBask wWe҈WyvY- _Pq.L-T˲,K _%jCT(ǁD%P(DYW}?2F !Jo[U(k/)Ѷ@` 1U5k>e2/w^eAgѦukz/JÆ ص7B)n9 k 4!ML2UV[lΝ;F ͛ ݻ9rH4DFF,Xz/Q5n@lqVtXjSLaҤIVwqǏsUz쉝E\!| 5jdѹHDZZj*N`Ae .ey,mdYJLβ,w"ev YȲ|Dպߖey,v%%bYe),*˲}-eYU,֏YAT `6[ `5F ro͔)S2n}˖-ܽ{'g֭ dY6IX.\vrr]UgժUYK.Yg}d!LQEx{ Um|wĥ%K F#1i$m]V|͙S`U [a@ BxڴimٲoZpp0۷ogFolٲ2Եk׮M||wܺu tR)G{{{x7nȈ#x7OM7PТ$',ʔ_N>[QBüRpsǘJ畐!\qrIItڵXP^xnܸQjN}8p^zY8wFoKڵK- ԩSe(qpp ++KȰH~H@uys²G8d~V! ~drތAEʏT6lXfYfͰ^zs2B5jٳgM5kZƍ4mڴ2aooOvvv `\c<=uOş2f֘2B8nm/uy8;;J Pe{L9 'gɐ}-V C={\۹sg:dZjŅ ,iӦ\zWe~2:d(W 23 {uşKnfn~w1ѵ@ K=طoUu֜;w*5mڔ+WX܏#g͚NNNY/4F'%%e5 7ɡejںueFƲ~^"Ox_$C,3gE5 1ÆBLNF } ꁓ-Zɓkj/k$Y͛7ʕ+4k> k`2dV_~ڵԲڵk3zh@߱OΟukm'HӥK0yQy{{~z.]ēݟ|׸932292EXX{&L&n @i-$<kY7ȲCӚ+5w9[<a~QM$zl>"Z XB&MV{V nٲUB9{lÙ};^ 뢰CР~}~,M&99uiǎf!TZ%IR , zeY>SIDqK5!)B).-lo1 Ql$ÆPjW_.5߻wR>>MibBX5#u%0ށ}CBI>%Xt֍뇍UjI7niܸ}xyy/lllx?<1W_ݻ'0˗[Ż((**"77`ض@P9W*@MUmW@?tey$djBw X%$IzXBNgG ١+ D@ ( wNll,={Z0x噀H21 |enjjjJEEEٳ!C`kkWh?\к5+DWL |]}w)4- 2[0z!xz $ TJO7]*;U[ܚ&}!ȟΝ;йsG&PplL2,]L%@ t-ZJlْsgs ܾ}&v>|(߸q2I҈>+^ϱcǸ{$^iiixxx|666Z[_Ѩ[?S4. ~M r aaaFѣ|A Uĭs͹s;T*e80@|l"U?~3gLO.>kޜ85kҲESs@l )CEo aͿuG7?dbѦMCzpuuJ-Z`۶mh;;; d׮]`'''rrre`X;;;j(]P舛?~nڴpAݻ4jcwqq!33ww*y]jkf֏Z⅌He#x-IK4(e[/[Y3J,N@?,'4o Tn@1oJ/3@,46P p%7ԱKM%I ece9 ]uۼSN٧7 jזOt'8 ]:%aTT$O@ *&!!!ڵOpC j޽{7k֌+Wмyswww륲UMxx8ƍvuu%))ɬQeE59s **Y%- ׾ͳ%UH8r;a`!p*q?=`F3(=c@$I_H T@H_$IiӚ9t`7?~_&='GG>]/u?u9~D`@ Ύ:paB 99ڵkɓ'-y\|"X^n=֖l 3]USu!UH@Ɂ=O 抲6Yter  "Go[S(M?2VɶF UH`,_(|#˲$E^tuuw̞=&zSy}85& ̍⧟޽{D[#@ 4[.ܺuj}v֍X܏- 5JVV5_߹su asquu"XWq'ЫW/vlΝ.PKc);w>d·|axAB[,j %EJ(?hH$g`*pR,J>ֲB0Rt=vrCeY,rjғ"Y3;Ư] O@ \p9s37 X;riBk};v̢>8sFɜNU-[ضg666{ZDw5\\\0 Yf*wnr#Ɨ?}=QSyѶ %442ߏw@q$&&2c f̘]9=V,-14n,ʱlˠk lѝ|~Բ W,rFo|{/J/$IA%' pPҿхa}3{?o_c˖oIN<Ũ{gϙO?m2b t7#K[^,Qżiժ%Fha+qIFϞ=ٻwkݺ5 +ORRRMHHۏΤ},uzԭ[HLRFoٲ$F]F+eի 4ݻ&#999f8j8B/$7oԎ.t Bpp Zdմ@ x|o>x (,,F׽{w"|~$-ZSh۶0?T0'{dYVv>(e9Q\^U-IFw@I 5 x&]R0AL6+'9w~;o}I:4&S|uٳx2dm5kP(޽;:lܸhڴ) ,(NHd 2EQXXh8Y{l/@\\dԩ:y뭷0aBmMZ53+/Y7uL=$D͚5Tl+q-.N`XcD?/ILf5补lYM(ѡ|WsՒ$.R5,_Q lI ^"nq~@#ߏQ(<S糳Rk[@PXt)))K6m әG4bE_()p }dذJͿ˥{@&F `͐fuA7??bn:|M֭[GÆ /;;;ڶmˡCڵ޶xH۴iàA6m+V(%&n7`cccV길8~VXALLټ ᅴ, vvvfǎeDDGGWy!\vm@}TPhm< IAA'O.%4i҄˗3i$n޼i1|233uvgĉϏѣG3m45ټy3>4N$$ڵkS߸qCBa1ĜXY (QXeƆkLaț ewӃ _||<:u28>g2֮峝;ōH Dc`5X |Vf`,@-("@_+Ͱgmۊ Ae更iF1cSLޞ+Vi&OnnVi&w_/[޽ĉfGGG25ח˗/MԫWmu`M!\Cmll=?JalTGcǎۭ3>͚߱PTXվ}>x0=NyKh$rX ^ Pbbbpvvf͚V«WfرԩSd TfY@@:ٿ?Bsj޼9yzz [.7o4Idffo~WذaC$ qqq1͑#9H5Ժ>}קƏ<-$DPT1@ xt}v郳yzz~z"II\wO}z9֭[aÆܼyӤrT ¬M /\´iغuYYyyydggnvOEMnJڈ]o @`< Jb.e_}i 64vu:Cd*XP1pppgϞܹYk Dž%I4EKe[nѠAGjj*ӦMcҥBtm7KڨQPTOl\Z`#WB 0|C2$:m'0qB^i(U^zIzwww:uy'E%Y~=www{=VZe6ܾ= 27o2, uwvvfҥl;)W*j.&n:,;;;бN}x8so#rbd,1|LIUXTDAai|0v7_=huΐ&Ϲ|&KޡgyH&13c+oº6g硄R"Q۠*QX$ #T1.\W_o &бcG\]]6.ٳgK`}X- LСCZ&ЎɰJRՕȉ&h쳥D/Ɲl?}QB̡34iZjyE"Ӭ]YChҔSyWѫWYz5³Z B 1%;taaarNv_mO;e pµVDu10c/00D}{z5 5iWlP!ԇn*% jjՒE q0уǤ/(ҥKԨQggg:vHjjQd޽{̝EEB^oׯd̘eiӐ7;;dڷooTӷNc< `'BUmݷ a$\$ѺMS[ۺv^CK^glG,2ڠS$B2kڵ`cc#b޽{YСCG,\h/fΜ)Y ^xxxxTDԡש,ZǏ_a͚4iX\XV@!L g=X,,,BN_F'Bm;okRN9ۚΘL}*I888Oga +аaC̙CDDYYY Ǐ]xn05χϙ[wӹ,^̏?F_1ae!,'XO\) йSN܁}d'P]DM 7u‚ѯ;iBWFT O@ www%$$DB5jĬY ::Zk$AyOg@d6#ovvQ]v(LLC.hݭ?oASݺuaTۣ%0tOHʸqLzZ@Uxl֭޽34ctb`޹]QoK=K (°m|BжgYML2UHC Çhܸ13g,Ž(Hpp0ʗ ƖR%F 999$%%^ˉ'֮tA~~~^r5Z7&=+ Zb㭷>`׮%@ٗ?EE=EZj!Y`&e(AINM??=-i-ޮruPG>bq# )%(Zb*%D8@`U+E a=,\ÇӢE -[O?͌3OxK]˛ׯKwui˝ӽ{wC-mFDDUҬnyG mkUnSSdH![Dh .*]NSdv;M[V|yo%Q/\eVs̬)/}KeAcaL~hr jg鄇oӲeK]vŞ>M 0qDar'WJtqqq1{G*ŋ&{bёK9|ÃB!0lk#r%lӎ?cGoĪߖH !ڎ{/6.ϸj!lhQB A]COOOa͔)S4iR! zLԫWwiHNήeݺьgРAfݻdeecyܺu{CZQav)))g ?;{|XF@ ЏFuILEIĒU딂WRR%jlvsև5}T>|H LN'3sju|};68"X 0)١aϞ?,LWPep{{aDcΎCnݨY}ԯ_{r x ~i֮]˪Ur]UG?,ܽ{(RSSuaVx6ڳg=z7npE_6MHH !!bkkˁ]Yt,ބfXZ;ի$$$зorCHII_~1b('O~#o/ؒ!e{}\rz݁Mt>]?C.?~⑜i>}W㨊H -WX$!)l 6nܨ wΝ;&c߾}ڵ ZިQ#fΜIDD%8W8e7Ҷm[CsOpp0vvE?~ ex"nݲ@E^^+Yrkr%c-+&Z%^©,oܸS#FsQE V3=-OZI'vvCE;;G(?aFݼyx9{,4jȨcccٹsgq̜9pV^RE;v5))UŅn]q?Kk׮Dݺu-: .дiSsHKKh!>|Hll,zjfddΡ ۻxt&DGWa\mqb'8-=Gʴ谤MDT\nt]͆yO/^ftr*0uO%Kn~'Y)&+ ͏?vH&H\TD6mQO6lк~ܹߟBCCIHHիFSiҤ 3fT ̛GQn.#Fҥ3P%K\ߗ.H_}(ٳgԩEv-޽KvN";;]M/_ѣG8p#rÆ;#\Z ,\O{wƣ7m z60Y)dgpӘ~um[٠__:t芇;6£wZlCցzXM^u.]2o>}ڿ͚5_RӤ |o|=^_|Qf3t7dsof.Ե{as=v, M --R-~:Qi]->Sr@sC 6N5I Ki%?u1G/ *=DI~^ْD]FxmHaA,I6HDVpptײ ,Xƒ8y4 oBxÆ BXV¡C/@PD-ΚEԩlȀ}?}~hx-sZV+..WWWņyyy޽XBzz:w7)K +5joFFʕ+tTIZ:Gx ؿ8$gISFmOn3O=- .f:7HKf=SF]Ro:yx*֯_Sڵ+ٜ:uJNPm"Jcؑqw} ٳPٽY-nԨQ!$$\4'X 0@IOOMF*_YG@2nQ!өSGap@ȨU}/vԩab"a!ފO~,+r"xK-tercۯ$S%+ }+|x/$x G)22(p˖-e޽SUխ2i IDATPY1}Net4D:YЪU+M;ub8m5-Z3u8ޞ3m>< MJJG={9<3_TNLܹ숎VzG`D,<7tS1!̊CYXD>!8;;=s[Յ{<#yy6:uj 3gN&**HpfͰe"Q (g<=T=`zjrHo.'-_|{x##C/\͛7%??1#\ccF?}֯_XU AU_vbPd{[N{ٽ8z<)}^V>KMMeTnrLp$!IAHMKE _ƿ9 6iHTnСU ///:wLLL /@zph7[KΝٴS^4n%:*HK˅ 3;FZZaaa"43b'}>|Xaog_*/+,Rdegq-_so؋l?ǠAa88 jբ{_< -[/~0R&ٸ:λq"ߨIRRR8pm۶u-BB ,0K  j(ϜDZ xȂ* :$;#7' ܜ z<{{{ *LܜUOOOzɯJ~ Y  .eTeQ&VB‚@$яc 87yԠ]@Uŋ¿%Ђ*sߐeΝ۩o鐸!Ef$'55=\ѧOywx1йK>]M7w/ߺ}TsHNNpWB@h`X";s&yGUMo$z (**RT+RM^^)A4;qΆf7$Ig?=3g윲y}_QsW?KӦKxNNٻw/۷F*mBQ]\f "}ÆjBg\xPWc0lXD&,">>'ФI|*^~[R/+Z5E9'='=%+\J+ spA ͆7DҾiШm0Eѫ(xyyѬY+<=HW"<ϮJF aٓ9{,u)0_BB&26#Fh"8#}nU 4T]7~GKϑPvUT/g63dϞͲ_d޽ 8>+_mhBHQʂ_~n `UWm `cG7ƍӥKq̚k^RoJLd״]7 gzDBQ5NIIw.D~¿ONS)f S^{M,~5;$&&+o+oʏr-YNŋ/Arr22228uo_v/Qu fUϤ%TȵZ( R$n$gЪJH<G/^NzNKr%S5 Ą Xb1Zj2tH3Glݺ ¸_':wɓ'ٴi{fѣGfӿ֭[Ǚ3gxw6mf"$$̷ǰaL0gl 림رhX}{́ZE' ̗ڤ gϖx}BBBX=5N~wgoW8q"g@/%%ǎ㩧)( @š3~<|ZZvAy?B |.~FYwzHɜ >>͛71Y\x"$$(fz|IOOF*XNMִiچx?8РA޽;+۶3f +V`ذalْ={ҳgO˅h7ciq|P]7d(\FN=~j֬i7oÆ yqӧa=t eP7;-[؍;YnDi |g BN>^/[LzJC+J4~U\(~gAbb"blڴ?ٳgYBg#G£jIQW! yM7o6Oy/PaCWHj8?:|/:mz9cjt`.gHA 13s[oqUVƋ/130{//O5k6] sa֭ѫW|zxx0p@Ǖ+WIKJJb̚5UrEwNNf,X9" )?Ϛ5Ol:+O| k32_ KYѲ8Mtm@ή 7o^1ƒs➯ޅȨgGO~_5O V]VEe&C}T ,K(?'B2?gd49)SyV*ӦMcϞ=l۶͡TZ7ERR&MbƌTZ=zM7i҄ &0i$\{.qgq(t jJQvz ItXi[FoES!;nG nݚ픶~!~zvTBj&X7Fcj;e&ח]$2yL&BD )%јCyM&#ޞ! #- I=kJ幮;S&g̘P0@s ߼y3fPZ56mʄ صk{v } G.\Xpތ xy-5(\h5 QǁWD-"۵K!N͛n,bcl.Zdgg믿Odd$~f4'fsw0|r<#%+ jDJ*pA 2gPjժtU5\a2IRRS eIvv)EzNMa%|KW2h ~RSS Nxx}({ a }FpLٸQ)'з\,xU?_},c6lפ jϒmo!xIh򇑎7 :MK)?9w:tcǎ >*~e?1ryVA$ }F#~~xyyPY_N||<|=C̞=)C3ߟpe:w\ڥ/ d,_Od$Ԫ aGh3óf-М8y>p@XgmfmѦ gƍ}IܜtNC˖-8p`/?ҡM(S1T ڕ8BB13vؼQP(W^" ɨDQ9sիWSn]*Ud79zh̖;vș3gظqK;;B?E~fk ˗/Ӭ uy3?^LƑۃWSre!ՊåK8tg ۋ P"XF9~/T ( H ߸I4hNVfjEҾ}{ڵk̤K.yfdKرc ]~ݺu bӦMuvQ" x,l3|xE4+Vkv &h&Vldc ]DD,-nܸ Z(X2Qfŝdga@AF܎eN^!nZH)B}gi=wԻ~xsyg-Abhy.zo,?甙N')o唫( $BOrG@hs2z~=o>hyvaYh0cO@ rErܪ4ߓ<#9C{W [ L4Ʉi2dF$&$$5~oA.0EBɍ7شim۶VZ?ƍ7WT(-ZD5LG6܂$tpUZ׮+WvDxA-c{9hf.G)))۷___z꥖1Q !d'Mfuuf*yDžz1ϕ[ +,s }m򓰰y/s.e(@J/-2X `MZQ/9-or}*H~AiUFlH {+ Ve˜AFzJpp0QQQc+Rz `{,fۇ/mF;5Y?ƕK˗ahؼYx_vLk)s9&>FӻKo(p+QBB;vP!}Vg(>}V'_IBp^j/ca4Yreά]wU! /E={BP0eS!ˊBy3c.CU4w6^A>aaaí;ԶݷJ`kKpEڵkGhhjD'+au*BIxxKP( 'b q"""h۶-7oٳͫL%9dy!lmXX*LV60p.Cπ쏊Hͅd/ gOJJ 7n$00~)\B ;l.1˄p3`05p  cpӼZ !aH)4;Ҏo~QDC*a< JJUa鞶:X9L+LKJ+_XHK9ƪhK V\IavevHe.֚vyϡgف▏+ᕰpx˵F>g=nzVJJ+pd8K@@u4?WG^sܓ:EgV'_VsC+K<#?[6msˑc+X޽dy9JZnCy~ sT"s/[H2Rq*'OҠA8^9}y$++ ѱ8зS[Ҿ}{زe -Z^z\E; `^w~җ3GŸ{Q֮%%p+⁽ ) 'yʳTk^d̠1L&oDRQbzH5AÇӧ@1f( E!7wOjԬt OS^c%+8ǎcԨQ6 L8D2h :֭[֭[w5TRfA5=p0ěhέСFi(uGb&~?TPU"1tW袱'Ҹ Mc.^3.))Il +V@J~xA8qBpZ#"h+tp0ڻ($''sE4oSSW^]7[˰{ ndggP(*5]Kԙk9̕kIdgKlc77z'K./ϟ'((VZߟ:uyf9b޹0h RRR&#dwww.\ŋ9zh9QE#UρWqR!Sh^| BL8űZR h c@*G?7/^,߰wLJ?Ϋ_7O\\}eƭ"<+V0h f!j*9qիǠAbÆ \xߟ(ټy3 |rbccu:x{[mh lJ NVL * 7jL IDAT'`moT U 1fz._Ύwupi%^{<ѾH;<)ի3g̙C>}G_H] [\;JjQu<<9w{FjL$ԩӓqcmݻ3g/pw{;;sQ0›:TVE, O{BѣX z(BYf 886mč7J컶jՊ۷(n===Y`˗/w{IUDm X RhMnXq Ap Wp z*vڌ1RRR0\?}=ϫx7m]'V] ( Mh&9,H-EHkK:WFAim[r;z?[ J+Jap#=_C &9%F>__OVӵk\ǩV?guY]v˝Xx!)kԯ ߲*· {{.^|9w(Y7ls`}FOZZ;_1p@Yr%yCsseΝի]Z9cim ܺ^ 瞃ٳ˷H;zTs~b ĉc0|8Bɠ>Xb'u(ŗgupvY&L"v_,mUXs;bQգ;l{T=,˗/ӪeKxQw|r?2|DDD{vL#~lceh|Oxf51~?zzeUT!(4iBJ/Xpa ѴiӢuvҥ ߿@dd$^%KDO:ܹsYdI8Q۵| V j1/hRW0YO? ɀ'q_ |4s1K!!غi5Σ2Vk/kQVVTAl!R]Ei#,Z5/İt"Yl16o!R%<<>4nܘӧҰA={wOs<]""+Y;'O^zbN(5ח5k>b=<,Z8|*2}c///ZjEV|2;vӓmRȑ#mٳgYp!ǎcʕ%R'N`/^\B| ݐ}!!!scHJJo߾~ڷH||>gP)0agϞUP&sΘL&bccټy3UVUVb*o>fϞMA*U} wwwϟ_S 4V YVh1_8YeirVXjpްA3zm)`r)wWCZZ4i҄Rp?www7ZH`MVS"c;KH;6M ^Eib2ev51fO;FVV4mڔ&MNj|9 ש0| 5kFf͸r ;wݝmX*\va49p;w.Je(BrR O=~A)zfd /ҥkW+f˗9|pγ0((nޱώuAÕ=ұ5T}^0fe" "")%!0ɤ$]FJS% x.gyzmpE76SjUz\?h36*WFQ,yk ߞotf* I1XzRR<pmHMM1yg@VcJuw;}xu<WuSn}M!R(eC gLzZ"),a›B, ӿصk;v̙3'߲?΂  vڼL*6MHpc{ Wh772DR߭> %0Sl QQFOqqqDGGo>Zh_j"h, md !,M}R=_@ĠY ) Ba[aJ޽ԩlٲSNihӦ sε+-ZĢEr4׮]_~)S27Ull.WJ i43k_~ox4M'lԽ;TVJN6L]E‰%2ڳ3@ߟ:5@eQQhoj !Z[]߬-'3d"0!: !"BBgq5lBB(!D!U"sMB({VrΝ\4i҄Pn]~G|||[. ,(P[ _|@sjqM&ѹN:ncӧ5:{c _}+t{{;ܠDpqRݠi]JJQ\@  [`5hC]S$ 6|Qka!y]|_B? B%Ro *3˖-cÆ |7lذ-[wԅ@ C/yCtVE{X#ROG0k`;&p5GBr\gkzW][ЦM *гZwe˖QfMx'>}:&Lf͚pM '88%ˋH7n̰a  y;u鮙Dge9{D &-~vM~Qc䦧ԩ|&\oHWڴ W _ʍ+Saaa,r9mϷ-vgΜnݺ }n DۄͺB !*I)frv#^G@0@Jin1Nw5jlo-RҟwK)[sp x['=rGY,T ^0u( $%&믿ѡ-+)gk׮{̙3www-Zs=DŽ ;HIIдiSVrGf[~^R,!zyEp)׫CP( ?si":QQD,g}-#,plQ~9111Rh-#""BsZ:tooo4iBI. ..%̋KЦ:lS/?&NT~pP;"8"dc+ >eO,C1ּ7gyb&/\ć }Ĉc޽+##Cֺ;e7 FJy6r Vkh3>d⢅`B[}Uò*ժVQO,BժV!1q'NѪxhpZhB4iڴ 3gLtvgBO8Fж6All,ӓFQvmu!w@a k/cItG3 x &EaM.YT͛wwWE61B]E/غi+aul[X ۶Vʿ~Q^z7E$BМb)gIp78͍hc}ceE3xF]f "޽ZIFHKAf#Ya wB ׯO޽ӧڵ-[p!U#8r.7$^hɫp~"gIRSf͠ysu}|Wx$PJ<|hW=wT دcۛaii+qb1?43곺h=gw#PMQb`=9*&!D8:ֺH>(F>>IB]$ߟ6mZT<ǨQAВtqzZ"]숧'/o/-H·3tA 0T& !Suh4rU;EwR $Yڠy F[S{j} qǤ$bbbחǞwGF; kW :!*E<ǰHO:a$a- )8)i(7خ-Tu)J v0իUQƪA&!!~;L\BXJZjJ॥sGD+4T,~]U 6Fɓ'r9NܹsTI]ZRRnck-/-z+ʗ+3e|B+\8yp轌v"Cx Đ?'L ;/OY:)*]c~|.EYĉ4j؈5k+PLKߟ'ҡ}fIKPmm#F~0a3rHNo2,`̖C)<[4F/6 #$+/B||LpP um\Dr޽@%(^^.dmI#3<B<"Ri+AX@,%!B+*Y%v0a/@'`wa4~& v 8gϞ gDvM*x Z)9DS|>`mJyP  ̢X[X ƒ(RI͛`ѢEN3?{m~6 ؍Qα^__$W$$$;w~tܨVaaazc4oޜ͛1"4h%K 7W=zq* %b[6Z|lJ;xE7 f !pkx>\"XώZOaD)rI mW;viu ۱vtFXQ60`̚9k׮Rj5^~U  PK[Dmw=tu~׮]+R@@@&K.q!Ra]ĉMY]K6fQH d;ipsSM t2E(:%''wfGI:thDp#Ԇ\6H)kv]OZ !vqkmp8e)-f\["2+0v~֎lcOTಂۃ'Oa o/7![娠$zZOOOɓ>>>T\Ǐ۱S+>BRavcX)'ƮSObxIB½k|,#GU VEMR^B)噼*QRKBV}{rA}!ۀo!Dg}B  !wlKKN`3pQ.t- X"XB!hgChBG'z6'S@ƣۯ?6!ŽO!xN}2^J)cRZ0,0  \EL1d)"G`!l4f({-enH>mƞ={`" IH{6nKuXij\j'5֭70fd .y( |eC6A[ 6;:K/G%#tV9U5k!DEjHӏBԷ*7X ||| 郁Qt&+fǡj"70Z7\ 2&<,|[J S8}Gv?oMBBӏ] Vpk?zy(o aE `y\t-[EyUwyZ5X1\ <"4 o@.d[*gљ6K)-WhBN޲:h)W:=׀ R:IJKq j3~B)V.׏R;z]wH)͏LZRϕR~b,&.b]Jl4fpW8y=?ƺu_%4oބ֭3|t|ꔖa1fP(2YYF<=.(*u 4Ӹ J7CyJsΑ@h޼9;ҩL]`gݻ_mۖ(X BhNs7}taJfģͺ6BWqm=5Mz0f!| EQB_K)I)be^*4lP6mK4hήXշ<=EnLWӃ޽zPJCUBIHGU=ԀPH)6fxUUZ[km-/XJɉ'"xFD]휧AZ+gذ,0_JUM>}~槥qy.^HVVիӢE lэ1cZ?"VNE1i= CJi)'ZbwƏa2S+K+'< ]'Fw!DUBcu}ݳ5n@CIBGb!bf?R'ˋ=Fj4lX)lݺoS^A݃}{1wQ~fwM/BI#WXz "EH]{"r-1 !Fʶ1 aly,w33ssKzMXN7X603sG$c7({rQ1hr:ob?ڀBHVhh׮%/t9v{s,7.ɩS8x 'N@Qf3IIIhɓ~Mzc0ƈIej Pu6uiKFrؽ$>n$sKIFulhphɴ>$v^=]^smG?2N CKxU3M⚿.wOyX>J/|}YTU}xSQh`Z;  1pq xr*Goֲxgig+IB5&Aؓ'XH\ PuBM@oX9SW\b"P}tc|FN5pбG,Xۆ ^cԨ*E*=-UQnPվC_%Et]j#EyNQDEQE ANr)?E:BW@#`(E1)r)Zy}T(%^>v1]FQpEQ^E ^Z|J(ʝ$((h/n'NG]ADp5Į'oy?3 `:ɏP*1OeY`4͚-.7EE5P-/hA^N-+4 .-%Oe/@UUurKv~so]g:/׍ m^B+1F&pkeq q}/ CQܖyL_DDDETT`?a4hDG*wbShEu4Z?Rgs..U*bm\:c]"w(]<[UUejE+7|{O]0KQw^hSU5\ yؿEIu.arEy TSKjv>+ m,)W , .Hr]g4iBll,QQQ5_Geرr8硅E::%jU:qqqr K\/o76}%;Z|Xh?@]ewC%UUs$ ""!.v=1M4)999 ,,җh,Uhމ`26^B5׈yJ||<ފgHj2fΜ[w:}ܶ=4[DLuD  T Hk!N֭iX,N8Ν;)^_F GFFJZE+2BRjzLr/5'Gh-fr[L|mfGQ , {sCl614k6Nvv6۷WvDRTT+5jk֬p[:uBf8m\فJ׃vدJ6@ϋ``V]yxtzO3 ]pjbm̀P%yV,Cц&%AEQ| /ևg6jԈFmph_=˗/g7N Dtyypت;b|][oW٪;֭0iS׵ZX,X,3NGtt4iӆݺ1do'Pm g-Bg}IYab=ѽ"Wq8:gϞ| wZu]ǧ~?~ AA ~Cݮym6_ ZxAxEO )'ooZȁX,dggp8JیFci"VZy[\rђf_/&FTz%ލ۸ql\'a0x{ ٤ML![(XyʞDڼy=m\}Te醻:7ޖs_SP?Y5=VUU-zAGՉp賑kITnBvc&~`ۿ'S\տ?C.YK4,w1D L*Izڶ/}TvI{ Ywi9 eL8_^XmΜyLzt*& L17 /?̈W^jj+`!`/򂴦>Cmo|Wf~vwhƌiqK O<4ƍ#2ҿc2NO` 1B`STcZ4́e0`)5e_V ^B\111|So vZoIrI#]`> NvUۮMM܌*/y#FǤG2+L68n6f8[1ի)a*ɘ`!پzŋ]ٟzmj!Bw} =ؐfs^g%;}4}a+k}S xqՋ:'OT/Bqڼ.f<ejח0|صIiŪUHmӚΫKtZkD'w!|yx5 30x MOG?k8CL]v;gxύdݲF:xdv]D ʊH_ԾնkxuϵNEsЧORSpEt5u&B aZn:z`C=Dv-ע ﴾K;N<-OYK d_b qbɴn11}4=)_K>yx ڦ,cuh}omĮ/mכe{oBؘHnzm>EZZ}ov"l66brrrcZl8Ng͛(3?44h$++ HRRL&q:۷t1䖥>p3ϸjbzCaFx}ܕ!E,Ⱦ_q]1A-**ncJ=}O[V qy݇vCq n6oFQD  I޶$ a%_C!!!`6گMۆ ns%(DGGcYfMz#F8+ɌP8y5/ (@ lpZI%)\9{'3o/e5§eDHHٌ` $$lo:K\qŧ&Z5?,9 IDAT`oS{BK/١}< 9k]\=rFQFXYGxdVϢSװO=~yܕa0c zvY?m8E`!4Ԁf'$$D!T͎XIFAL) X 4a ˿VS7ԞSΰK`قRoM[&{>ϠE B(S4Fo/Q!PUFn)'4V_CI*J'U%=nŭ45H飆ehLsBX<9lzmi1i|8C?)O\K;^ I E BfF f#&o#?D Ba21bnE Bp}7X -9Ul0мc?/T @Y7m0}[k]5%[Y  _ŭ`A߬]\="jZw]Tcڃ`$""g'<on[و'XA;K/.^Dpl*VʁyxD1ݮe9U7 <?8pڿݯXqpeZ^!Cfddopi6n4i`AApGUUDp0H*I\U2Vw^wqOCh޼\|~`~1֯wm<} A0e ̘Q#:0k֬Ϙ1# >C ~ %‚lspB1-yz x HpX B #< B`wﯷW0{=YgϞ-RP 7@+?hIN113ЭFD  STΨ!U7̒;o1 &VSw=큌 AvC`K'xB.J_`\pddhb#>% o=ٲEL-AOpx\"k[1B.pCP\ tn>(ha~&#Ȉ&|l`nw`B `Зfq¸!ѹsg+..ncZ}t~?Gn |>>`*5haӀbհt)$$K坅pWb2\ۆ2ӂ`A*l6;D$(Z "TlrOb6R ]Ѕ7| WɄ` $$tttB'Ÿm* s|vХ  եߝ)祠((((}`hٰla0PUQ:m%ߵti Û9yjUU)..&,,HAlL} fnwѝjzz^`d^׌.,6܀VS~wv}  'SOA?3\9Jt:ZXVl6[...&//,q]$"X'7/TIQ`jvLPwتy%}' .|̜9S ၏>ѣ!7-y 0}Vg,U]N9>RR@a2*)  ANvqqbZlvr?,ٚ_:SNw#FpkH_PC2H7#1 "XS&j*6¢"ΪO"\Kqqqb76mJ,[q#Z.b.׹ڃ#=PU$Z8'ѱ=ϱK%D6/3mԖm~*BX[ciK+BjL4IVRf<6U( \'jFkh!Vv۰~ݣ[< |&rU8lro_E!$ĀjlZ16IV@;v\u0t(LJ؈Gmg@ ݹsPy,O"\mDt7ؓ>OE^]U:[:d{OWԗPy5jT*K,`00j(lK?gRq\>;je[V"#}:III >O>T7T0f =b-,ZNj-j%d.ƍ]"#^l#c6Gaxx9),(d++ {]+<Zy彻,/"a*/"S…СC5k6ͯ5뵱:Nw&=$ja) }4_{رGF0{GBà%)0X dwZ)2SظaGfҵWO:vH< ,Tw"D!_$5&&37111gEGGY}?Om/6W_]„Ǔ'_~9mZ֑9/Z +kW 5_|NQQ8vƍ +l>}6lX<}=׌6kL.{wXn=~O sҖ cbI_^Y1ʡrΉjwo}g5111UZDBBUsxg8y7aECj;1֎;yhpr:dggcX"77Yqøq㈍%66A{D6nŸwr͸xFh\VSxڿ]i V -g@p82vSJOj _ޱe~*oѣ̟?uq ?`ڸ'rw6o}xKye<٥}]Ж.LIʷ A !L8kKEס/ڷodzzibX,X, QUn#&&XڶmKTTT~W=%" xV+!@B{4?+̷x|v1gqDf͚3n"DauVQ*_Xk$̪~֦q6tb)}%˗NEmBpcZKB=CR ӧ:Keggp8Zmݺ5f9hΛGx, m\.vkVS紒J=ݯer5//]Oʹ&"X!p:!p޵k+W,3ppafsKѦM暠=GG;xD܄#ZMǁ۵>=Tz]HLlڷoqe=?3YBMgY,IyO  o@"--49P\ Ӧyڿ ~@_Z)p Xf^AQ2 ۶m+)PD  N{IJjU&${bs w熂nc,w#Yܩ%TR?u00$!pFv}8\y%3gBhh4Qv2"¸K̳;$4oFFb߾}( K+d~`t aܱFfvB~ OsvڤD L֭[Lj#t?~|P N$<ïk5۵kxQ}t$'si߯o$11DqwENNNiEQ'668, ]41Q|KڵHV0Bٱ#vnyDbb\bX@ۢ|) ;pKu5o>DG|:NgYY 4MቱwcѲu.N@ݽ鈋#.sӧѣX,I("X03-` bDEEҢES7oFXY "vA4P ѨՊ T&S ݭ.S]JDD{3f`4OtD;糍?ުM6 '))ɫP,B鈉"h$Q<XD Bx8$1("XӼ ,ډIJ_z)/T!"XD0dYfСCFX #6ҺR%Olٲp6mJ.] mhтnfD<qJSC\v̟/ǽ@3f>%>F{x5MNʲu6rQJ%*A.g->Bb4i͊wbDAG~1ŧ1͵*c+&,,]ҿÖNQuJK-sy$xfTKw E+6Çgl`Xsؼe+MIMm+*MNN7oG󉉩 -.ΧD~.g/ ++)A "sϳ#"+x gsNlz0%Z6 7*,,d>|( vGjT5k&#W@DDG1HP‚Bh!_k"*z+$[sQXVfgF`A McU"""Dg5[$&6bk#GдiSMoYvIbbb0䖨!h::`jg[4^dq$-h!@PLddk֬ ..d5j󶢣?~/#`",T_È)50.]taȐ!e?~{n\̭w\=lx1 ޽"h4v)S8rQPPٳ&/ICKԻ}ˣM6t:8ڵkKE@R%^< ˕Wbs3ԢE믨*, '\˕*#K=+T.3*/y#FǤG2+L68n6f8\a!?7W^ᦄ^8Æ1L@~@¡J sيN-G¡\6x} =4?YjmZ֑yt_=:Q: s[^Ft:Pc_~!#FzEEEرǏc4i׮;w>ke˖1w\"##! ;e̙C>}U\C~%\wfȟ>Y{ 6uӦ',W\U-`9<3} N:dCtҥM2k#<"_ @"p딄Dluӿ'<7IVC,>v;b!v Fqn3{6˟~Z &"Xi1[^z躇KOI.j-ؿ7 fȚ&>2N  آ r@p`cLV &0i$ZlY:?//?lС=zҶ5j th!*T"K(NÇ1 V^?r$,a5fHN'> 44n: )((`֭Fݻ,O!\dZY/Њ"ぃII|~4mʆX~ݲ]ҳgO~iO@oQb!66V '"XPvzQPhlvFi-! םDFWJшfK.Dx9JrUD\"gIIr7ǯή'sh.[Ο}ƩPvu@VеkW222X,qyUk`A$ =EHHc27p.TUa{ կ[ڭwZ\TU|w60hP(..1_2\ U" Ȯ9Y!ѿ=7pÆMy7|Zo}m汭I<ڱ#>[n!w0~6t~)^}9"""իdddi&BCCi߾=M4#"X03:deoP%l6afBC-&77i? h~Ҷs3p߇8XŰje<,\cǒS`@ jH7I(t凟?ܡΫwp'Mbw~uNOڹxrat8S?/ެY*6UUq:#"XɌdCuJtt4cƌaѢExE7oʵ\,ƍ#"2"hp^uyZn CZE;37|۷&Pi"xݺAJ?Ac B6niOi}>ϦVm۶a0HMM%))ɧhז/ӉSpE˖S]BD WTg`X/yb,>ʫM||CgSxTP۩>x~˧`ݵݱczm{*+Vcǎjꬶ : Q@n% 5O`&yT󳾇z*fqsWy_F#;vcǎvvŚ5ktвeKtsM7_駞f8K߉Z'/"XASTŒ/3qddɚ5ߓڦ5ii=u-~KHJjw om83y2BbѢE̝;W.  Wr= NᝓP 6*fVf޴9 :tC8w}jՊ֭[yt;r$mkjF {/Eұ#5㤓SUA?*" ^E>}HMmE]Y3eR2220Ջ0@ˌj4@Aȕ*eɀoSNoߴ4B32(nт=))ӿs)"X yyyB;v0BCC~06t;F˖-iժo+]fĉL8ch![Ӂߎ@&IUpO ZغBϚיΥӱq׏t6oLrr2:t8+Ks]лGZ̟A8v-}LhAtXgdaFBʼnLjn/d2(oN8JdD$N=?g&;;-[`ܹ3ݻw'##EK/YvF O8p)e%3;{)[!!!vmU`ݺu`YvZRDEE1iS۲®]tr예`abмE+v'^*AmTdg璝}6 ⓗ"+f޽ر(z]Fp0wܳ@d޷o0Z_DPVG9NQGGkI}ӏ9snw؀ae޷lْ-[ˆ ltԉ:˴{a̙gn59b`aa2izGfrAEѡtM+Zf;EQ\ctNց꺙U3YV!{zjSJ9-2Z뽫UUQU6Tt=mZ9M6 ZêkZ9ޟ T9(%'y5J{Eծ%Tt-S"Rҩ3hJuQq\%m% M,LۙϨ\Ѐ' NŪ:ՒB:QQp*8UTT,杪NOLJJ[t ۊN!\֭[9v[fC;vX1A%VuBC}ip7 ΌI3`%46ԟXz%gv_.gA9Ⱟ3 ,XwP<3jOTTnJlk_}k׮4jȷEi,>UMzO8"=]&TETzP(W@*EHu`#i1F?Ҟg}ٯ&::VZŚ!X*viל8s >pW&""nݺy~N䑾3xRqu~\cM"}ZZ {Uoӂ xۘ`ٳ;wŅ^lEWUE(B7kPk'hĘǣ( ,[8݂2$f6-ZXi!%?;v6vUiҺV;99PiJNhڴ)Pkw>,A E;R\qZuUzڬVkisJJאgA nv+nht N j6l`ق<~8ؑw䋳hܸqB82*o-ZwQ=Tz۶mlٲܼ\VkZKWHQ,0ЉPyPD x=z4ڕPdee1yd,Xl&++-[p8ҥ ={C;T(Zzĕ4jUUy她GOĸx:$%#S▿3!mȦUNRUKIJNbƍlٲ>}Yus ]vk׮|hnnΒYv䤑rc2Ga;8vN%PtuZB3I^ѕWQѡt J׸?\cVK -16۩ ӡ%*IUv%9g.f8Nm_U#<"Ħz  PYf1qDz!RSS^xÇcbcc袋}(**_~ .0N_ӹsg6m*'`A1pٳȈS0#Pt*-{Kc X%1RTT@V BL4CtEٟ%3h䥗^bݥ, 7|3?mڴaȐ!,Cixuz$4!'MÆ^/ǥؖ9yΡ'xӏLpp+#/}ͳ1&~?@dd$={T`/%}4n€ضmpQDc$22[cRT: .Co0ҴY2ʑ:-> IF;"~GN']tqr Ncw&U^"_DӵU Qagn\חFsх~t GCRKbKs a3 Vdd$]v'N`͚5$&&ҥK7njw|wkCGsJ "/rp^'ŧۋ(JWTՉV:Ihܔ07}QDzz:;wc 2"l  ^*ǣ&IKK㝣 58S'8|0:7d jY&2%11w}k-̈́ Bff&Vm۶o߾Bhu#9 ?ޙEU}MqCQA5wD33[l~-󧶘ii߲2V݄Tw}cfpf^ddljGJٳgٶm* v1nTlH˖-3f rriZ!ueCZG A`D"򷴢Hp%}D?Q/'.y*,,,Ԗq3gҹs璲vڱl2N<)HrKh,F?%l6ƫdTaX' k[ӱw\8{NXXC=k׮qFRSSoYJ}"nhפQ^ v܄Wʮ^uu|S%z/-,).V8HLL m6\9{,6t-ے=IIgJ-bѢExxxذdf͚o]n`x"Fή S6^#FPt҅~CYʚ'N!lUV)Bpp0*MGǃKg,?2^IJ065W v<`Kjj*_~%ь3J`I!;)@%aie˙WƷUK3B~]-Ş={ C E&׿>7$[_݊Xm>}G֘6^43f̐jID ͊ (GgYR%ܚyO3><*I$((-[Z_>q)75kpppas^SgcٴiSf_aRK(bC!{6l[v"2ú˖Ud~=vEQvyW}ImaaaIA'N`]w&''ɓz+ 4m僃al?l>XȑKFں݄pyuBf2prrZ.LFFFɵlVI5cE(45V %iNƣ;H:ͺӱ$&&ҿ06:OO~GJP* a: oESVs- K )))t҅l|ٳ;O=˃MU_ڃqtBUrsse< ,~o_}5<Ӥ]&%U㧟~"55ロׯСCXj:2yZH&f`zMLvtp9uI׮]ph4ƗږL !##M6ѵkWZjU#}8q_geʴ)34N>MNwwo2d.Ko"X`ɭz]czԴ ؕԙV,(*.^zNgg'>h)ƍ 6 kkk QJmj̻ѩS} VVThK"1{ !G!))P<==KM&IR!cU&4,@ 6Bx888 QJjcccŁ܈СC$$$u5Cyw֕g?|_n5+_O{F9kS3hp"v9dJ,D٭~cӼbm)%8::LVV6c<ϲe3/гg(Æ %77GGG*cǻ)yZ :pvɰaC*%ɓ'IHH ((`vɠAHL0c ZJ;Ю 6 ]]7_|SlhJ網е38gvwNVV7osU_c P"_Χ~ ;G2d|Aܖ%_#ONM[%oEƥju1vδoߞs+>fS2pPBCC$) ~~~8;9QeTjy3o} X t.Y{ mI$qy=ZvĐ7J,13E08%/ŶڠX#--ٳ8{`Qg¹sop`8BDDGa۶macS,׮Ãa#G%K\q K M e8%[r{ KK5]t!;;dxWx2,JVvm۶Rmӫw [-lܸׯAHMU]% ƫFgg9ɂ9{vbNɏ?n4nPP={d֭$%%T7_yr#Kh rCK)o,4ܮiVSZU~ƢaK)Ûdz~z_H 6lΟHvNÇݙ4mJzz&ba+%"&&ggg^צ]vr$fɌma]T^]@6)@J$.Y5jǎc˖- 0[[[?K3a2-jS8̳Oa7*G)'[zsbU]צlTd:)x%uF ͛y3e8}4ѱcG:t耝yW+LgT$bbbP0*u?.Nb6cefcgW.g*5+ǻ1еkWٶm&3O/8}'{{:ct؟\ IP+D*U!9YqtjB޽ E`aPTCnXդ-z=ؿ?'''9(Z%"8˘WDgPpwX(**u?@ F=w232cʰQ51ޏ IDATKZȵRK,]z4i"ER't|sfciYΔ@}Gucŋ4o͠\-cqϽ\΢rg~>@nnM"X/bXRXDrۋa59gRT[-Z$SNa6mʈ#9c/KscK.OTE`.lڴFMߖ*CD"Hng.\  ""-ZA"jBkr Mq | &BCeI\΢K?H@@ B+/_مg%<9RK$D"i̞=T6mDzz:2hf>`W%o+BӧOkŴn;͚TX{w$-- {{{lll8qZ~߿5A·5D"4B˸f޽2t U~ & F^P ^i~vvP om,,K\]]Yj87B4eVXУs< 6mbȐ!XY/oM{ 7WWX8YZQ\!ED"H$}# XkBƆ%KK/*_f 999RK*"8;ӱܷ/4k&Ƿ.02;6l`9xlAZfmdœ>\2I"XBV)**EQIH$*QBxƌ6ͥgϞhر籠^O:U^ xQf7Vڇj/px~ʨQضm۷M6f?~XQ}֯ݵYKޕ-ERPk,lꇝíy'-V*MSH$IÇIKKGuPH,1 `Cv9^`E"A ++FIMMW^&@$h"rkVO|}&x\|k;Z:aUfXCXׇ~JD"i\h1w\vA@@vvvôi_cƌaҤI~zy$&3 "I r\ ={H ug|猽SLvV6MFhTdwƣIK2Ɣ>JEA&-rr㖝IZ䱮/$&f{~:qq8w@xdT膉 l߾m[G@@44hPPXJUhec"4ΛZc]PMnv-cAAN^7yS?D"iԇHz͛;Sgǻ|2ŋZl,XDRcw,A{B`kk[} ''gggʊÇsK޽KOFF\EQٳ'...$H,iȂX+8C_J';7X_MIBSOz~^:^~%kl---.H,*ݻwcccѣٴi#Gرcپ};EEE2MR ˄?@wu |k8ǸccB2Ϙ8t(oT $$IF%Kر5-RC۶_W_͔1X4P\vheM]֨Fe=Ի~G)%Ǝ~[W/ra[}ژ֣_\Vb„ IRF?v{+-{zvN>}xwoΜ:0DZǿ?xDx'M٪U+\]] }ӧ.1'/˝wn{Fʓ!EI2ײ]ˎQwƉ?,Ux}{+a|4utˤzTc@%>r96mD i0^fMm0.h>/ RyۣܺS^uĘJ#,V'$Tc$)ANh`Q4obŊX֬ -* QFؗѵ6fJyMgm H$ bpss#""XRe[nʕ+ٕ*2e ;|0ǏgҤI7%$$}v41|L@\Nhuu;ۍKV3h yC{ H]+ HysrKćI۬4n)%ׁSN8oP[}H$Brr2Ǐ',,rnRTܹFëa͚5pBBK.eҥfn7`*vW6(H$"mFff&& &22TKUeԩL6UO)umQ"Hn%:u0ϟ1kk*,grE{=rssKUҘcy"c[VӲ_K̫8Ԯ]o:+"SNbM eDK1=TyU0(2(zȡr_b{9x%?+WCB;9r(Aݺdӓ}sti?EE `?!E! IUbz{ݴ6gҾ"Ou+!s况~V6ڔ~߫ޛ^R}H$[Aaa!vۛQFq8Jb\fMͱ:u VZUc=FӦMMVI:Mډ̔Lrs*k9gɿ/徻ƣNDT g6EdImq$6ooMZ"~3x%?|իY:gNNN&^z6c͗_&7Wtڔ^{OO "xub GBD#m gH!ĆJCޯ"`݆~w7VdNTA|['2wpVO(7!%@BB ѱNOԶm[ڴiS#6y晒׻w&::{2u_K5,c9~J)4k;k )ŻB )..v!-^ڽ;w=OM61`bXXX0i&M#QkcV[BtW׶ nk[EVe1IЕoϛau]tFQF=u&ؔ\,H9QQQF/_NTT[l:YYYlܸPxʔ)&{͢E駟ig4^%ۖX|(}zɵJ>S"縒"Z;Dk^rH 666Mzz)kY_ilCxG97kQ='`n40?<ߝ Jv5 fe_%?4i\ DR7V\}{ոbȑٳ49 5Pb Ă lD8EQ\Y꽸t:X(OaJE\D SH,HҥK~:'O4nM~4M5Fbb"|F@O:լ3gΐ3ydĶN+kgg^˜9s~ KM.((`?q͙C&MB0h慇 Y`YdȑlٲKIyzy7D~1h3:voB_ u t;EqA#@b^u"!u}ފ!65l'b~VGD"-!+}_Rm6:t耟_ɓ3bĈR55PիWIJJbС޾sEF5 s!X)vvvwW_%y-,U۷x0y|zz[ɡhXXX0|pn݊JErP%D(gt@Xa!U]}s𫁨݉6xv xx؈@e/p܉609`AЮ+V6IB\ V]?\ j/ƱCڮ>J$̬ۛ,33k̮7ǎcȑu*3338pIoBll,禺l{YYY߿&#&ٵkaaa& Օ6?TkiT70l0v؁ZWJ9~8.]ݥ[ <(J{!i{H/pE9z7@[`?F. @;8A !.GzBD*rlB<а.Q<~ٻ@AA! j^Zehe%Kn=66XZZm&/3g˫˗/LJח]$''s)FUksFQBBBj^aa!;w$<<$ZhnL  ܿ?9uqcXOD;]Pr{pwk.T*r@ (**")IiFFFxOnx NV9xǠF࿊tBĢ 7b i0ⴢ(0~hm(A;ِ9 xTa>RQEQIcR3acG`;^OͻEza#.y]_iGVYfn!>>ތ?~ 111(rX`/FæM:th"6gffrelR$&.X_,]ѡkĉágCp[1`كJCr@tr=qqqRWmh|a*nx| h=XAr v=K̶?6BU%` dSx-r>)km@<%g,3;ׅSquuR ΪUJ `=z!Dpppm۶@֭kٳwww:vXc6oN޽qpp6Y/;ޏ%yr+T֖ l>o|Z$J>}ؿ?qqqu^E"UQ:Ng4 }E BU`Z?窑+e*h ڠWzѮSt0˩v(.ʓ 4ϵ{ <$y4p&Md4J^222صk 2ypUصk͛7m۶5fsUmq޳gOfSɒ+s8չ3~%9Kb[}U{E~syaKnKBCC9tG[nr@1/ ! r(@G BH=71RW6%M!tf>5 !B%#H$5C~^:M<}8{&S6Μ9ӧku/;hӦMz;mڴsaggGӦMMj?fHˉyaZ[^3ٖDr;н{wbcct}{% -[n9pRW*]Wh8v]O#uAeOy(t4 /V% TUYF4?6"wB^VDR9Bh"F/d޽XZZ_![nSN52E o߾ղSPP@|||Ǡ .pOH͵kRKFv*REQBuhn/׵hp\ѣqdeDU>g,Mypvr&((w7_Wb˗/w)UV\\\"LksFa˖-t [6puЧ*{O>x,I]g<=ߋP?ͬ,v͟Iw3_ivV/^}?%Kv-Aq_޷!CZfo!Ep*mcQEvRCw(Ю['6K'(r6hA?yVe3Z#ucC,Pl V?!g HOࡣx7muK"Tl( v BEQ&rsgƌ)ȿƁKx6BUT@zF&UQj͍ׯ抵M_gMV~$Db .$##T٦MPTܹTiVyfjT<>|0iҤjhO7$0]V;0ɓ'i޼yĴ>h4tO\\4RK7Hj8S0ի<؁17nX/"BT%mhaP4$W'k77Wos"// WWWl(Vpss $]3EEE8cc[+7coLlzI$Axx8Y@HLb֬YFohF*M4a̙,Z>x s￯NLLdq旛;ҥK <6_nnGh?p+W筷gϞCdd<'K/=̻~INm233/8st2i$t"5p̄?Tg*xv+VV3â矗"X%~R\C{D~n:wyW)./iwdmT:@ȿ{ү %H$O=J0}7o?ccg yޜn~RkkGaذaܹ}͛"A ~cժGӤeoߝ/V~xAihybg^{5,--K5Ǐ2g^3>#Hdd$flXz5'Fżyh޼9 IIL;//`)%D"H'66Z#(B;?:l#zo4_599890vXzw͌gPZ/kgvi۶I?>eCZ@>w&~~TM8}MemnL찣Τ,Oa^}—ۿ3Гں~qqrࣕ/aÆa bV ̸!CX lٲ?-^̩^#ތ"X`턫{ 2/\^vD"Jrr2+V(Uw^ܹsK}||6mZ62Qf΃? !B~G'fuN(qK"o&UT9sQFU6))>+X[PTdXKBB<2i{wꬪ,x~˭.c~4;hיb`Mxө}'ǭ3=9rp+ҒÇi&M^wl uh׎-#''G]B `D"RXpav||<8;;ceeł D< aŠ~&M>tKߌ7֜1KCYdY"xϞ=kZ䯿|yEEejp=%#S~mH :[E_~gf׮+.YV}JGKLŅP`HOFLX 6|6e=y TiD"Ԕ֯-,:a(UgGNMɉ.9 ڇ'9*+>]G;C|[gabiiIttju3\\#+kM"++s˥O/qp1~@ѯEt<ב?GGG>}???aw3uZ&N0dH<$/4V ?4 {09>MH9A?ޝӑ58nܸr'om6~'ÂywP,ݺt.*dsܦ }ʼJŻά6}[n%44u1cƗك^LaaTsMB|<z oG4M=)JMu+b>38 ?\ȬT.8?? #X9M.#щv%|lx]`ԩ6xc@ ˅ͅ{) Hc%D"zqssH 9sLj!|'L6[[Z xB;;==Ge}L M==ͶieeŅ ӓޚFϞZ(Jjs鐧?}ⓒWo',CWl.}ɶȾɎ5')+SLif׳/>Gqn1xstQ<zitu 8F#jKKKqwwvn&uϻn*$X(Hw1\\ H$VXnsg0g+]=pqqݓ'O4SS$OL~\^^}:waI$䕗c[<>.Re={;VfUsK}B>/alE(بG{^@A;CN{ EXrEQ !y#'E$v~'tF+6}t}8 8붝u@?]_@^]-5>6v#H$;t`kl,tBߘT^ll,#Fkb2Ag?GQ 4?|u8xjJgTG._\eqbРAl۶#Gʋc! ,m&C(JO`>Zng!=BpJQsmpz ! zr2`"@ |Ն?m7^ !,ҽ#B;$ KWVY{bV'')++{O%H$?yh۶TIk$9c+W- Aqq1>\UJ(5~mVս;KYϚE3>z%fif4ѣa̜E{-H'TYGM4mw.s"p>//M^r\X-_n"NgؤBl2h.ՉMK|VA?TݦO KP;V*;ZMӷ!+gY"H$F)((`ҫz ʬqVwȾK|&=rݺu|Dј399\z]C rhBV?0ֱ-J-\uW^>܁#GXܞ(ڥk]hѢm*;BFs6˔^|s?amuR-D(tWQEQ>։qNîIN]k_}=}NF./-D"EEEr$2L5}II8@YN6{Wo\5YMHr[i3^kgaUӦMr*gJU_MM{9 XXz D{V2'';F6mB= >e }y2-<_NyPne'A.c\ fL^cnȕ|фOD"u|<;"4r@$|ATz|f͢SL ;lbrpNJ RS5–66 ˋ˗/+l ZiWFxnˮ\յ899ae”lgggZumE|MǮ];jCMEe3O^ytPQGAڠ B?1u2uqI~r$IbccCǀ.P/EDRM6?- O3CZ|7\p_})Sd hӡ`Ǩ]xw֋iҤ ǎ?uDGG,---۶ s 99???i֣b̲u/^KF~!xp#^~.O>Igiy7Oe*c' TVQXMC lBҒH$#CK$wufO$iFRRח1}g]Xl{sknxu<Ν;h{ѬsR Dnnkliii ƶ '%1xc6/5[H@Ű^8(v!B(Q+r z@`?Ћ*NH$9Z"1oooa~O>&$)j/MrACP.D {a^!m}|}}K}J<栏ln';00'Ndnac݆aԻdz,N1ݡkǎ5jGч7?Uo{k'-n#''G&%epOK]7l>Կ7[Vpp=ކXڵ˯;#**Jyyytk{Iڳgbʹx MN֢eK'_}]ãt猫eҥQ}kg1ymW>?~VZ.k/BgNf;4c TcW"$wqM6SMar8 0(%]"CgOYYNŧ3O?rf/zU#{QۊR4N|ޔh`x 6ͫtvv]%iذaڹs&Nխ?Uǿ[o[7ԢO>R)^/*̚2e6lؠ.Co{[sѡC KSהutiU%L̼EM7BpqP?$]m2*ʁ3 #jjvJKM]c"^0Oyٺ(0OTeTg;w^\o^ܼ}p;ه30L&ӏTyKQaIѢ9N_!r.?xxppBB}EEb BmΑ#GջwvJJJS/XƍU||6oڤl\ب(rFcǎ@_ìbEfԇ/ BgVWVJJ_ MNւ%y+11/ k*>> 8;3U\a mFa# øܾ*"\Keer8n+++aH1*(,2mї*++KvH7Fm(iP3F={TLnw}w.JjԨQ`*-111j]ժ 8p[ֹޛԫܚk \wƍӶmv,N͛7rqb-IDT/9im޹t:> Ckn`,o5SOݭ^zU=6O3.dV\@ ZWIM)3DuU11뤰-7hoXX[]7۬re)fZruFgMsڸjnFe L.ԃO=is+v]|nBbg7LճgOʊaGu%$$ $P+vm;4-?2rMVϞOﺫTnCz .K<$i% B^mt6mhDaV v%p(Wjg*d48>0qcuIťQPή|xW!׮Ç_|s*[׮]/o.ҪUt+007 B0p{o@{lNog}6]˗E,х^v-LY=s5kgrД玈Fˆyu`ZT']}S'IOu-שGge ғOJ))M/t_ɲo_8Nɵ%s C\众^IGѠi52l6֦7\z杏t5&qXXLcƌ{(OkY{^队O|i^xAzmN چn»/hw2tMTWTThֵ;o5A-|t6VTPPp;v֭[AUVV&_)裷K.7B0&4EGfp:)%@EE:x M'l6Q#==]_|l6=9e=gF%T)%-}c*nҒK<* @zeilXZmۦ's[ Q]u (bW_g &MI'cSNՆ:#y=zT!!!5{.]UѣW_iڴi~ }߶yv|( #gY@@"##ukr\n;p֯[Q+ڲrYVnke/Sb9+Ue/s;KҾ}ڵ{N*+¤cgw qR} {>6@e>^s_OWM[صkk#9Y U#5G^rd6U\\LJJל3G(RCl+5e:Z߻Lf]^7<}Zz.aaңJw)%%ڽ{ &[.ZtB=vm ZKj#՗_jemP]w5?}9ѣGkǎ1ch?~_ p6nt/Aޛ٬ȈpuZ-&Zenk),,L6[L&B:9ҧ2غ(vzL&&8X_4V^"Z?Am_몁%IAVx}N^ XlĀAz m2_3tw+([mw)Q!!!r\*++SPPMz5_j[cbb&G)ܤ UȪ@J`c3L f G& pN:hq:zWZw͆5aRք'ܹu{r,?q^Q\.CVKrjBpiEl!m>snY; /~SR/5l05jvء'jܿ)==Nǫƍec+ue+$w:2 Z?0#ڻwRRR|*#??_lnb% -[Q<+Y7wrss[_'+G3g+(l!^H%-ܲǍIxjֻFxWA0SS*//תUkw$eeݯzVtnV@q :0ZPvMOO׀p_Oa\5t&?\6}_׿KU5+^=K.sر~$߽^PαK~19p%IB:M6hʴiJOO%\SY.˫[+~Y5UGfYӧNeonoM۾ԂWH?PvVs-gӭ3g:L58&&F{է+W곏VKM?:ԳجV],oMŋ%I[nՍ7^G9#R!Xnn}`^[{uqpzfGz鷯ĉr:YgN}F;I $5x?}OΟL˖ h,66V͓$͙3C![%Ws5oR=peddh̘1>Slөs+j{+9kǷ;Z/{д)Zz2W VdknTqY)a`T*,, ? @PE~NRCd6 ٗ/Rrr嗗K.>mcEEO >7mń q~9 è3n۲EXLY 5g?! ӥq}uခ5Kݫ=}ϲ"rۈ'S!ǯq'er۽G.[ GU[c>UTZh + {UnFFϣKv-..&!6M__iuJJZ믿rzwWew٫ڶoo˹ C&ݽ5]ph ?~\C 񩌓'O{>hu:_?7=--MG7-22\^{ [=m;[;{o?,{U?t%eݺɇx -7_wO=m驇7qaF7Bp?o'Iwcǎiɹ$]]oJoT|@ΤV7.a~W }g5Jg*M?ѫXS.v߯={hذan{)}yyynrYVUILm4j ̩^n*`4+DK4^$0 ^8;[?]T8~zݺj%Z}*@aaa>oݩqv<,'?"@.҅#Gz]G|:0IgL-k4*QPPKE \{-eiIL6ͧ=޽{'NP=oh0ueq`Жp1pdffꢋ.򹜓'Ojر>r<&84t i05Ocz;F !|~]QQ@())QHH!W\\ܦBgaa@]q:tH s+TL ON8aÆ\ɓ'խ[7)**h%&ҠAכLsB08ZV`Kouׯ{Mq B0h˚ -B=U~YNJrsssNsT0 {~؞ڴie/vॲ29Syl^mt3hIǎח_~%I:s$'+Wj~َ{Sgy%%%>#G(<}C5+"׭[7]u~+o xnNSw>y,BZwc~0گ-rm5lӰa)ԝ?4p"" P.V7`@n? ^z=zXfBBNSw>y,,_.::Mם? XIyy0{p,h 4i6WS  a ҌPm@Jr@B0h1ѣZ>|!N @ !6[$ق#k~/-ɣBqSk O>^ Cwh,>S꾣!: B0p ]B`ZVSVާ:!|v[;Z@pꝖs$nЁ@a=uߑi` @ !-8zRf[߶m>X$KK:TvwknǬ=K@]t\Zha=ʛjk>&B0 tc\>. [2jl:msmmX~ST9l;붴_CsǤ/5ZNw7cO8h ە~60՝qK- =ose5}q~5 lظO-v`t\@d]om_~y/ng r]@whxvmgðִ:Nwr αrRh Y R\WeoFnk1pw掕崗!LKACm w[= M=osyL::_uw]?w!=;4@' M H ji֦7wjk\8km@#7^eiCS_xn8A ;/˹3}v<˓uNo}"=;4 !B0`F7}E%Ў @ !B0` @ @ !B0`  @FapR|p8DEB911*)-Z DeDݺES+8&٤BY9Uxaw($8X]t2!m[PP]t\ 0ATsKP23*̟"p3  ־QhvpVYTiQ߄2s kJIIV#$'?S~KUB0  )WfjۿRUa롞q 1 ,r: W)(WDDVbJpVlA $ɤKrs&ɩRRTYB0*+J+ҿ'PXX̕QA! (/@yyզg`+%*p*.&OaPKQn޴ [NCGURZ,Ӑdld$L&L&&I&Ld\yudQad\[;J@C2 d2dz 2TUyejWaT.TΪz\50d&TQ^zFtdTZ1~7.o-Y@IDAT#IF9o2j.w|H2L5e2d2*_Kb2U=fd*U3j~֮Sz4Uͫ~}Vϫ^zkz՛WF ZK. Ub5\FB 2 e0I%yͻ l#Vfs R/W4KY1QV4qUTt!`ɤ@MA5֋oUE@Z`:S,j4jr͇}Sjay@̫}&N9˭R9̫ $fC£א)_[FRma;꾎=M_${{^kW~MMLk2rd(kwQ tğe(ipO !B0`hú hܾ~B0|mɬMB0Z qCj[2W̦~z>@-vpi'7^k-kԛzotfѯtZQ/dP,@M!A2ݡцx5,G;X9-CK0Q_ZcY_g#7%d9ƙ []_^^~博//xz<|e=M.R-k8#ݯGtwonow}H%&&<%:*:As̡:89$M?_-`Iz7rj\KeV:vTXfY[p$-AK-D++URtf 4 3ge7KKp'pZ-vuM%1p\ldM>|v;!30.'&&El!w.J!*.6R=cz\wxf OyTNn+00+t}MKpgtdw>&Q$%! ZT({C%2\""B$)ظ^b)P?TIENDB`veusz-3.0.1/Documents/manual/html/introduction.html0000664000175000017500000013222713325026534022114 0ustar jssjss00000000000000 Introduction — Veusz 3.0.1 documentation

Introduction

Veusz

Veusz is a 2D and 3D scientific plotting package. It is designed to be easy to use, easily extensible, but powerful. The program features a graphical user interface (GUI), which works under Unix/Linux, Windows or Mac OS. It can also be easily scripted (the saved file formats are similar to Python scripts) or used as module inside Python. Veusz reads data from a number of different types of data file, it can be manually entered, or constructed from other datasets.

In Veusz the document is built in an object-oriented fashion, where a document is built up by a number of widgets in a hierarchy. For example, multiple function or xy widgets can be placed inside a graph widget, and many graphs can be placed in a grid widget. The program also supports a variety of 3D plots, including 3D point and surface plots. The program produces vector rather than rastered 3D output.

Veusz can be extended by the user easily by adding plugins. Support for different data file types can be added with import plugins. Dataset plugins automate the manipulation of datasets. Tools plugins automate the manipulation of the document.

Installation

Please go to the website of Veusz to learn more about the program. Links to binaries, distribution packages and the source package can be found in downloads. For source installation, please see the package INSTALL.

Getting started

Veusz includes a built-in tutorial which starts the first time the program is run. You can rerun it later from the Help menu. It also includes many examples, to show how certain kinds of plots are produced. For more help and link to a video tutorial, see help.

Terminology

Here we define some terminology for future use.

Widget

A document and its graphs are built up from widgets. These widgets can often by placed within each other, depending on the type of the widget. A widget has children (those widgets placed within it) and its parent. The widgets have a number of different settings which modify their behaviour. These settings are divided into properties, which affect what is plotted and how it is plotted. These would include the dataset being plotted or whether an axis is logarithmic. There are also formatting settings, including the font to be used and the line thickness. In addition they have actions, which perform some sort of activity on the widget or its children, like “fit” for a fit widget.

As an aside, using the scripting interface, widgets are specified with a “path”, like a file in Unix or Windows. These can be relative to the current widget (do not start with a slash), or absolute (start with a slash). Examples of paths include, /page1/graph1/x, x and ..

The widget types include

  1. document - representing a complete document. A document can contain pages. In addition it contains a setting giving the page size for the document.

  2. page - representing a page in a document. One or more graphs can be placed on a page, or a grid.

  3. graph - defining an actual graph. A graph can be placed on a page or within a grid. Contained within the graph are its axes and plotters. A graph can be given a background fill and a border if required. It also has a margin, which specifies how far away from the edge of its parent widget to plot the body of the graph. A graph can contain several axes, at any position on the plot. In addition a graph can use axes defined in parent widgets, shared with other graphs. More than one graph can be placed within in a page. The margins can be adjusted so that they lie within or besides each other.

  4. grid - containing one or more graphs. A grid plots graphs in a gridlike fashion. You can specify the number of rows and columns, and the plots are automatically replotted in the chosen arrangement. A grid can contain graphs or axes. If an axis is placed in a grid, it can be shared by the graphs in the grid.

  5. axis - giving the scale for plotting data. An axis translates the coordinates of the data to the screen. An axis can be linear or logarithmic, it can have fixed endpoints, or can automatically get them from the plotted data. It also has settings for the axis labels and lines, tick labels, and major and minor tick marks. An axis may be “horizontal” or “vertical” and can appear anywhere on its parent graph or grid. If an axis appears within a grid, then it can be shared by all the graphs which are contained within the grid. The axis-broken widget is an axis sub-type. It is an axis type where there are jumps in the scale of the axis. The axis-function widget allows the user to create an axis where the values are scaled by a monotonic function, allowing non-linear and non-logarithmic axis scales. The widget can also be linked to a different axis via the function.

  6. plotters - types of widgets which plot data or add other things on a graph. There is no actual plotter widget which can be added, but several types of plotters listed below. Plotters typically take an axis as a setting, which is the axis used to plot the data on the graph (default x and y).

    1. function - a plotter which plots a function on the graph. Functions can be functions of x or y (parametric functions are not done yet!), and are defined in Python expression syntax, which is very close to most other languages. For example 3*x**2 + 2*x - 4. A number of functions are available (e.g. sin, cos, tan, exp, log…). Technically, Veusz imports the numpy package when evaluating, so numpy functions are available. As well as the function setting, also settable is the line type to plot the function, and the number of steps to evaluate the function when plotting. Filling is supported above/below/left/right of the function.
    2. xy - a plotter which plots scatter, line, or stepped plots. This versatile plotter takes an x and y dataset, and plots (optional) points, in a chosen marker and colour, connecting them with (optional) lines, and plotting (optional) error bars. An xy plotter can also plot a stepped line, allowing histograms to be plotted (note that it doesn’t yet do the binning of the data). The settings for the xy widget are the various attributes for the points, line and error bars, the datasets to plot, and the axes to plot on. The xy plotter can plot a label next to each dataset, which is either the same for each point or taken from a text dataset. If you wish to leave gaps in a plot, the input value nan can be specified in the numeric dataset.
    3. fit - fit a function to data. This plotter is a like the function plotter, but allows fitting of the function to data. This is achieved by clicking on a “fit” button, or using the “fit” action of the widget. The fitter takes a function to fit containing the unknowns, e.g. a*x**2 + b*x + c, and initial values for the variables (here a, b and c). It then fits the data (note that at the moment, the fit plotter fits all the data, not just the data that can be seen on the graph) by minimising the chi-squared. In order to fit properly, the y data (or x, if fitting as a function of x) must have a properly defined, preferably symmetric error. If there is none, Veusz assumes the same fractional error everywhere, or symmetrises asymmetric errors. Note that more work is required in this widget, as if a parameter is not well defined by the data, the matrix inversion in the fit will fail. In addition Veusz does not supply estimates for the errors or the final chi-squared in a machine readable way. If the fitting parameters vary significantly from 1, then it is worth “normalizing” them by adding in a factor in the fit equation to bring them to of the order of 1.
    4. bar - a bar chart which plots sets of data as horizontal or vertical bars. Multiple datasets are supported. In “grouped” mode the bars are placed side-by-side for each dataset. In “stacked” mode the bars are placed on top of each other (in the appropriate direction according to the sign of the dataset). Bars are placed on coordinates given, or in integer values from 1 upward if none are given. Error bars are plotted for each of the datasets. Different fill styles can be given for each dataset given. A separate key value can be given for each dataset.
    5. key - a box which describes the data plotted. If a key is added to a plot, the key looks for “key” settings of the other data plotted within a graph. If there any it builds up a box containing the symbol and line for the plotter, and the text in the “key” setting of the widget. This allows a key to be very easily added to a plot. The key may be placed in any of the corners of the plot, in the centre, or manually placed. Depending on the ordering of the widgets, the key will be placed behind or on top of the widget. The key can be filled and surrounded by a box, or not filled or surrounded.
    6. label - a text label places on a graph. The alignment can be adjusted and the font changed. The position of the label can be specified in fractional terms of the current graph, or using axis coordinates.
    7. rect, ellipse - these draw a rectangle or ellipse, respectively, of size and rotation given. These widgets can be placed directly on the page or on a graph. The centre can be given in axis coordinates or fractional coordinates.
    8. imagefile - draw an external graphs file on the graph or page, with size and rotation given. The centre can be given in axis coordinates or fractional coordinates.
    9. line - draw a line with optional arrowheads on the graph or page. One end can be given in axis coordinates or fractional coordinates.
    10. contour - plot contours of a 2D dataset on the graph. Contours are automatically calculated between the minimum and maximum values of the graph or chosen manually. The line style of the contours can be chosen individually and the region between contours can be filled with shading or color. 2D datasets currently consist of a regular grid of values between minimum and maximum positions in x and y. They can be constructed from three 1D datasets of x, y and z if they form a regular x, y grid.
    11. image - plot a 2D dataset as a colored image. Different color schemes can be chosen. The scaling between the values and the image can be specified as linear, logarithmic, square-root or square.
    12. polygon - plot x and y points from datasets as a polygon. The polygon can be placed directly on the page or within a graph. Coordinates are either plotted using the axis or as fractions of the width and height of the containing widget.
    13. boxplot - plot distribution of points in a dataset.
    14. polar - plot polar data or functions. This is a non-orthogonal plot and is placed directly on the page rather than in a graph.
    15. ternary - plot data of three variables which add up to 100 per cent.This is a non-orthogonal plot and is placed directly on the page rather than in a graph.
  7. 3D widgets - 3D graphs can be created by adding a 3D scene widget (scene3d) to a blank page, or by creating a new 3D document. The 3D scene has settings which control the angle the rotation angle of the plot, the position and color of lighting and the rendering method.

    To build up a 3D plot the following widgets can be placed inside it:

    1. graph3d - this is an analogous widget to the 2D graph widget, plotting a 3D plot with cartesian axes. It contains three or more axis3d widgets, and plotting widgets. The graph contains settings for the graph size (the default is 1 in each direction) and the 3D position of the graph in the same units. Multiple graph widgets can be added to a scene, though the position and sizes may need to be adjusted.
    2. axis3d - normally a 3D graph has three axes (X, Y and Z), but more axes can be added to plot multiple things on a single axis direction. This works in a similar way to the 2D axis widget. The widget has options for the axis label, tick labels, tick marks and grid lines (which appear on the outside of the 3D cube). An axis can be swiched between linear and logorithmic mode. Scalings can be applied to the data values plotted in that dimension or to the axis labels.
    3. point3d - for plotting points, and optionally connecting lines, in 3D. This, and the other plotting widgets are placed in a graph3d widget. The user provides three 1D datasets for the x, y and z values. The markers can be scaled in size by another optional dataset. The markers can also be colored according to another optional dataset, according to a color map, minimum and maximum. Error bars can be provided for each of the x, y and z datasets. The connecting line can also be colored if a color dataset is provided and a colormap chosen.
    4. function3d - for plotting either a functional line in 3D space or a functional surface. The type of plot is given by the mode parameter. In the case of the line, the x,y,z coordinates can be specified as a function of t, where t goes from 0 to 1, or by giving functions for two of the coordinates as a function of the other. For a surface, the value for x, y or z is given as a function of the other two. In addition, a function returning 0 to 1 can be provided for the color, which specifies the color map value for the surface at each position or the line color. For a 2D surface, the grid lines or surface fill can be hidden or shown. There are also settings giving the number of function evaluations to compute in each direction for a surface, or in one direction for a line.
    5. surface3d - for plotting a two dimensional surface from data values. The user should provide a 2D dataset for the height of a surface. The x, y or z axis for the height and other directions can be chosen. A second 2D dataset can be provided for the color of the surface at each point. Note that the coordinate of the 2D dataset lies at the center of each 2D grid point. The height of the grid at the edge is calculated by linear interpolation. Normally the grid is surrounded by four lines and the surface by two triangles. If a high resolution option is enabled, the each grid point is surrounded by eight lines and the surface drawn by eight triangles.
    6. volume3d - for plotting 3D volumes. In this widget, for a volume described by A×B×C values, then the user should provide four datasets, each containing up to A×B×C values (there can be holes in the representation). Three of the datasets give coordinates of the centers of the 3D cells and the fourth the color of the cell. An example set of datasets would be X=(0,0,0,0,1,1,1,1), Y=(0,0,1,1,0,0,1,1), Z=(0,1,0,1,0,1,0,1), color=(0.1,0.2,0.3,0.4,0.3,0.2,0.1,0). Additionally, the user can provide a transparency dataset, which can be useful for showing or hiding parts of the 3D space.

Settings: properties and formatting

The various settings of the widgets come in a number of types, including integers (e.g. 10), floats (e.g. 3.14), dataset names (mydata), expressions (x+y), text (hi there!), distances (see above), options (horizontal or vertical for axes).

Veusz performs type checks on these parameters. If they are in the wrong format the control to edit the setting will turn red. In the command line, a TypeError exception is thrown.

In the GUI, the current page is replotted if a setting is changed when enter is pressed or the user moves to another setting.

The settings are split up into formatting settings, controlling the appearance of the plot, or properties, controlling what is plotted and how it is plotted.

Default settings, including the default font and line style, and the default settings for any graph widget, can be modified in the “Default styles” dialog box under the “Edit” menu. Default settings are set on a per-document basis, but can be saved into a separate file and loaded. A default default settings file can be given to use for new documents (set in the preferences dialog).

Datasets

Data are imported into Veusz as a dataset. A dataset is imported from a file, entered manually, set via the command line, or linked to other datasets via an expression or dataset plugin. Each dataset has a unique name in the document. They can be seen in the dataset browser panel, or in the Data, Edit dialog box. To choose the data to be plotted, the user usually selects the dataset in the appropriate setting of a widget.

Veusz supports one-dimensional (1D) datasets, which are a list of values with optional error bars. Error bars can either be symmetric or asymmetric. Veusz also supports two-dimensional (2D) data. A 2D dataset is a grid of values, with either a fixed spacing in coordinates, or with arbitrary pixel sizes. An n-dimensional (nD) dataset is an arbitrary matrix of values. These cannot be plotted directly, but subsets can be plotted using python slice syntax to convert to 1D or 2D datasets.

In addition to simple numeric datasets, Veusz also supports date-time datasets. For details see the sections on reading data. Also supported are text datasets, which are lists of text strings.

Datasets can either be plain lists of values which are stored within the document, or they can be linked to a file, so that the values update if the file is reloaded, or they can be linked to other datasets via expressions or dataset plugins.

Text

Veusz understands a limited set of LaTeX-like formatting for text. There are some differences (for example, 10^23 puts the 2 and 3 into superscript), but it is fairly similar. You should also leave out the dollar signs. Veusz supports superscripts (^), subscripts (_), brackets for grouping attributes are { and }.

Supported LaTeX symbols include: \AA, \Alpha, \Beta, \Chi, \Delta, \Epsilon, \Eta, \Gamma, \Iota, \Kappa, \Lambda, \Mu, \Nu, \Omega, \Omicron, \Phi, \Pi, \Psi, \Rho, \Sigma, \Tau, \Theta, \Upsilon, \Xi, \Zeta, \alpha, \approx, \ast, \asymp, \beta, \bowtie, \bullet, \cap, \chi, \circ, \cup, \dagger, \dashv, \ddagger, \deg, \delta, \diamond, \divide, \doteq, \downarrow, \epsilon, \equiv, \eta, \gamma, \ge, \gg, \hat, \in, \infty, \int, \iota, \kappa, \lambda, \le, \leftarrow, \lhd, \ll, \models, \mp, \mu, \neq, \ni, \nu, \odot, \omega, \omicron, \ominus, \oplus, \oslash, \otimes, \parallel, \perp, \phi, \pi, \pm, \prec, \preceq, \propto, \psi, \rhd, \rho, \rightarrow, \sigma, \sim, \simeq, \sqrt, \sqsubset, \sqsubseteq, \sqsupset, \sqsupseteq, \star, \stigma, \subset, \subseteq, \succ, \succeq, \supset, \supseteq, \tau, \theta, \times, \umid, \unlhd, \unrhd, \uparrow, \uplus, \upsilon, \vdash, \vee, \wedge, \xi, \zeta. Please request additional characters if they are required (and exist in the unicode character set). Special symbols can be included directly from a character map.

Other LaTeX commands are supported. \ breaks a line. This can be used for simple tables. For example {a\b} {c\d} shows a c over b d. The command \frac{a}{b} shows a vertical fraction a/b.

Also supported are commands to change font. The command \font{name}{text} changes the font text is written in to name. This may be useful if a symbol is missing from the current font, e.g. \font{symbol}{g} should produce a gamma. You can increase, decrease, or set the size of the font with \size{+2}{text}, \size{-2}{text}, or \size{20}{text}. Numbers are in points.

Various font attributes can be changed: for example, \italic{some italic text} (or use \textit or \emph), \bold{some bold text} (or use \textbf) and \underline{some underlined text}.

Example text could include Area / \pi (10^{-23} cm^{-2}), or \pi\bold{g}.

Veusz plots these symbols with Qt’s unicode support. You can also include special characters directly, by copying and pasting from a character map application. If your current font does not contain these symbols then you may get a box character.

Veusz also supports the evaluation of a Python expression when text is written to the page. Python code is written inside the brackets %{{ }}%. Note that the Python evaluation happens before the LaTeX expansion is done. The return value of the expression is converted to text using the Python str() function. For example, the expression %{{2+2}}% would write 4. Custom functions and constants are supported when evaluation, in addition to the usual numpy functions. In addition, Veusz defines the following useful functions and values.

  1. ENVIRON is the os.environ dict of environment variables. %{{ENVIRON['USER']}}% would show the current user in unix.
  2. DATE([fmt]) returns the current date, by default in ISO format. fmt is an optional format specifier using datetime.date.strftime format specifiers.
  3. TIME([fmt]) returns the current date/time, by default in ISO format. fmt is an optional format specifier using datetime.datetime.strftime format specifiers.
  4. DATA(name[, part]) returns the Veusz dataset with given name. For numeric datasets this is a numpy array. For numeric datasets with errors, part specifies the dataset part to return, i.e. ‘data’, ‘serr’, ‘perr’, ‘nerr’. For example, the mean value of a dataset could be shown using %{{mean(DATA('x'))}}%.
  5. FILENAME() - returns the current document filename. This can include the directory/folder of the file. Note that the filename is escaped with ESCAPE() so that LaTeX symbols are not expanded when shown.
  6. BASENAME() - returns the current document filename, removing the directory or folder name Note that the filename is escaped with ESCAPE() so that LaTeX symbols are not expanded when shown.
  7. ESCAPE(x) - escapes any LaTeX symbols in x so that they are not interpreted as LaTeX.
  8. SETTING(path) - return the value of the Veusz setting given by the full path, e.g. %{{SETTING('/page1/width')}}%.
  9. LANG(mapping) - mapping is a dictionary which maps language names to strings. This returns the string corresponding to the current language. The keys come from the locale names which are the two-letter language codes (e.g. en or fr), or the full code (e.g. en_GB or de_AT). The default key is used if the language code is not found. An example is %{{ LANG({'de':'Druck','default':'Pressure'}) }}%.

Measurements

Distances, widths and lengths in Veusz can be specified in a number of different ways. These include absolute distances specified in physical units, e.g. 1cm, 0.05m, 10mm, 5in and 10pt, and relative units, which are relative to the largest dimension of the page, including 5%, 1/20, 0.05.

Color theme

From version 1.26, widgets are colored automatically using the color theme. This theme is specified in the main document widget settings. Widgets are given the colors in order given the order in a graph widget. The default theme can be specified in the preferences dialog box.

To override a theme, the user can manually specify the individual colors in the custom definitions dialog box. Color theme1 is used as the first theme color, then theme2, etc.

Axis numeric scales

The way in which numbers are formatted in axis scales is chosen automatically. For standard numerical axes, values are shown with the %Vg formatting (see below). For date axes, an appropriate date formatting is used so that the interval shown is correct. A format can be given for an axis in the axis number formatting panel can be given to explicitly choose a format. Some examples are given in the drop down axis menu. Hold the mouse over the example for detail.

C-style number formatting is used with a few Veusz specific extensions. Text can be mixed with format specifiers, which start with a % sign. Examples of C-style formatting include: %.2f (decimal number with two decimal places, e.g. 2.01), %.3e (scientific formatting with three decimal places, e.g. 2.123e-02), %g (general formatting, switching between %f and %e as appropriate). See http://opengroup.org/onlinepubs/007908799/xsh/fprintf.html for details.

Veusz extensions include %Ve, which is like %e except it displays scientific notation as written, e.g. 1.2x10^23, rather than 1.2e+23. %Vg switches between standard numbers and Veusz scientific notation for large and small numbers. %VE using engineering SI suffixes to represent large or small numbers (e.g. 1000 is 1k).

Veusz allows dates and times to be formatted using %VDX where X is one of the formatting characters for strftime (see http://opengroup.org/onlinepubs/007908799/xsh/strftime.html for details). These include a for an abbreviated weekday name, A for full weekday name, b for abbreviated month name, B for full month name, c date and time representation, d day of month 01..31, H hour as 00..23, I hour as 01..12, j as day of year 001..366, m as month 01..12, M minute as 00..59, p AM/PM, S second 00..61, U week number of year 00..53 (Sunday as first day of week), w weekday as decimal number 0..6, W week number of year (Monday as first day of week), x date representation, X time representation, y year without century 00..99 and Y year. %VDVS is a special Veusz addon format which shows seconds and fractions of seconds (e.g. 12.2).

Three dimensional (3D) plots

When drawing in three dimensions, Veusz builds up a 3D “scene” for the graph from the various plotting widgets, made up of triangles, line segments, points and text. Veusz does not use a standard (e.g. OpenGL) drawing method, but renders the scene itself. The advantage of this is that it can produce vector rather than bitmap or raster output. OpenGL, for example, is based around bitmaps.

Veusz applies lighting to the scene. The lighting depends on enabled light sources, which are set in the scene3d widget. Light sources have a color, intensity and position. Note that only the angle of the light to a surface affects its lighting, not its distance. The position of the light is relative to the viewer (camera), not the graph. Positive light coordinates are towards the graph (z), upwards (y) and rightwards (x). Normally each solid surface has an intrinsic color, which can be seen without any lighting. If a light source is enabled, the color of the light is added to the surface color, depending on the reflectivity of the surface. Each surface also has a transparency setting.

By default, Veusz uses a naive Painter’s Algorithm to draw the scene. It draws from the back of scene to the front. The main problem with this algorithm is that shapes and lines overlapping in depth can be confused as the depth of each object is calculated at only one point. In addition objects may intersect, which is not properly treated. In the scene3d object, the user can switch to a different rendering mode called BSP. In this accurate BSP mode, the objects are split so that they never overlap from any viewing angle. The disadvantage of this mode is that it is slow, uses a lot of memory and produces large output files. We plan in future to add another mode which handles overlaps better and does not unnecessarily split objects.

The plot is affected by the viewing angle, which is specified in the scene3d widget settings. The rotation is given be three rotations around lines in X, Y and Z directions (note that these are not the same directions as the X, Y and Z axes!). The X axis runs horizontally on the screen, the Y axis runs vertically, and the Z axis runs along the line of sight.

There is also a distance setting, which moves graphs closer to or away from the viewer. At larger distances the effect of perspective reduces, meaning that parts of the plot closer to the viewer are not larger than if they were at the farthest side. At large distances, a plot tends towards being isometric. At small distances, shapes are more distorted (note by default the size of the graph is 1 in these distance units). It is currently possible to place graphs inside the camera leading to strange output.

By default, Veusz enlarges the 3D rendered scene to fill the bounds of the 3D scene widget, so distance has no effect on the size of the plot. This scaling can be switched off by modifying the Size setting from “Auto” to a fixed number. A fixed size is useful if the user wants a graph to be the same size for any rotation. With this setting the size of the plot is affected by their distance.

By default, a 3D graph has dimensions of 1 along the X, Y and Z axes. The size can be adjusted using the size settings in the graph3d widget. Care should be taken that the graph size does not lead to points being at negative viewing distances. The default position of the plot is at the origin 0,0,0. If the user wants to plot multiple graph3d widgets, the positions should be adjusted to prevent overlap.

Normally in Veusz, sizes of objects (e.g. plot markers) are given in physical units. This makes less sense for a 3D plot as sizes can depend on distance. In a 3D graph sizes of plotting markers and line widths are given in 1/1000 of the graph bounding box maximum dimension.

The main window

You should see the main window when you run Veusz (you can just type the veusz command in Unix).

_images/mainwindow.png

The Veusz window is split into several sections. At the top is the menu bar and tool bar. These work in the usual way to other applications. Sometimes options are disabled (greyed out) if they do not make sense to be used. If you hold your mouse over a button for a few seconds, you will usually get an explanation for what it does called a “tool tip”.

Below the main toolbar is a second toolbar for constructing the graph by adding widgets (on the left), and some editing buttons. The add widget buttons add the request widget to the currently selected widget in the selection window. The widgets are arranged in a tree-like structure.

Below these toolbars and to the right is the plot window. This is where the current page of the current document is shown. You can adjust the size of the plot on the screen (the zoom factor) using the “View” menu or the zoom tool bar button (the magnifying glass). Initially you will not see a plot in the plot window, but you will see the Veusz logo. At the moment you cannot do much else with the window. In the future you will be able to click on items in the plot to modify them.

To the left of the plot window is the selection window, and the properties and formatting windows. The properties window lets you edit various aspects of the selected widget (such as the minimum and maximum values on an axis). Changing these values should update the plot. The formatting lets you modify the appearance of the selected widget. There are a series of tabs for choosing what aspect to modify.

The various windows can be “dragged” from the main window to “float” by themselves on the screen.

To the bottom of the window is the console. This window is not shown by default, but can be enabled in the View menu. The console is a Veusz and Python command line console. To read about the commands available see Commands. As this is a Python console, you can enter mathematical expressions (e.g. 1+2.0*cos(pi/4)) here and they will be evaluated when you press Enter. The usual special functions and the operators are supported. You can also assign results to variables (e.g. a=1+2) for use later. The console also supports command history like many Unix shells. Press the up and down cursor keys to browse through the history. Command line completion is not available yet!

There also exists a dataset browsing window, by default to the right of the screen. This window allows you to view the datasets currently loaded, their dimensions and type. Hovering a mouse over the size of the dataset will give you a preview of the data.

My first plot

After opening Veusz, on the left of the main window, you will see a Document, containing a Page, which contains a Graph with its axes. The Graph is selected in the selection window. The toolbar above adds a new widget to the selected widget. If a widget cannot be added to a selected widget it is disabled. On opening a new document Veusz automatically adds a new Page and Graph (with axes) to the document.

You will see something like this:

_images/winwithgraph.png

Select the x axis which has been added to the document (click on x in the selection window). In the properties window you will see a variety of different properties you can modify. For instance you can enter a label for the axis by writing Area (cm^{2}) in the box next to label and pressing enter. Veusz supports text in LaTeX-like form (without the dollar signs). Other important parameters is the log switch which switches between linear and logarithmic axes, and min and max which allow the user to specify the minimum and maximum values on the axes.

The formatting dialog lets you edit various aspects of the graph appearance. For instance the “Line” tab allows you to edit the line of the axis. Click on “Line”, then you can then modify its colour. Enter “green” instead of “black” and press enter. Try making the axis label bold.

Now you can try plotting a function on the graph. If the graph, or its children are selected, you will then be able to click the “function” button at the top (a red curve on a graph). You will see a straight line (y=x) added to the plot. If you select “function1”, you will be able to edit the functional form plotted and the style of its line. Change the function to x**2 (x-squared).

We will now try plotting data on the graph. Go to your favourite text editor and save the following data as test.dat:

1     0.1   -0.12   1.1    0.1
2.05  0.12  -0.14   4.08   0.12
2.98  0.08  -0.1    2.9    0.11
4.02  0.04  -0.1    15.3   1.0

The first three columns are the x data to plot plus its asymmetric errors. The final two columns are the y data plus its symmetric errors. In Veusz, go to the “Data” menu and select “Import”. Type the filename into the filename box, or use the “Browse…” button to search for the file. You will see a preview of the data pop up in the box below. Enter x,+,- y,+- into the descriptors edit box (note that commas and spaces in the descriptor are almost interchangeable in Veusz 1.6 or newer). This describes the format of the data which describes dataset “x” plus its asymmetric errors, and “y” with its symmetric errors. If you now click “Import”, you will see it has imported datasets x and y.

To plot the data you should now click on graph1 in the tree window. You are now able to click on the “xy” button (which looks like points plotted on a graph). You will see your data plotted on the graph. Veusz plots datasets x and y by default, but you can change these in the properties of the “xy” plotter.

You are able to choose from a variety of markers to plot. You can remove the plot line by choosing the “Plot Line” subsetting, and clicking on the “hide” option. You can change the colour of the marker by going to the “Marker Fill” subsetting, and entering a new colour (e.g. red), into the colour property.

veusz-3.0.1/Documents/manual/html/_static/0000775000175000017500000000000013325026667020133 5ustar jssjss00000000000000veusz-3.0.1/Documents/manual/html/_static/comment-close.png0000664000175000017500000000147513232117076023405 0ustar jssjss00000000000000PNG  IHDRaIDATxm8$km۶m۶m۶m۶AMfp:O'e$Qq aO[B3U9Og+ł-81dw=7q1CKa~ ʏ lϕ]O4l!A@@wny^xa*;1uSWݦO<*7g>b~yޞ mN\(t:+tU&>9Z}Ok=wԈ=ehjo OSd̳m#(2ڮ&!Q& .section { text-align: left; } div.footer { width: 940px; margin: 20px auto 30px auto; font-size: 14px; color: #888; text-align: right; } div.footer a { color: #888; } p.caption { font-family: ; font-size: inherit; } div.relations { display: none; } div.sphinxsidebar a { color: #444; text-decoration: none; border-bottom: 1px dotted #999; } div.sphinxsidebar a:hover { border-bottom: 1px solid #999; } div.sphinxsidebarwrapper { padding: 18px 10px; } div.sphinxsidebarwrapper p.logo { padding: 0; margin: -10px 0 0 0px; text-align: center; } div.sphinxsidebarwrapper h1.logo { margin-top: -10px; text-align: center; margin-bottom: 5px; text-align: left; } div.sphinxsidebarwrapper h1.logo-name { margin-top: 0px; } div.sphinxsidebarwrapper p.blurb { margin-top: 0; font-style: normal; } div.sphinxsidebar h3, div.sphinxsidebar h4 { font-family: 'Garamond', 'Georgia', serif; color: #444; font-size: 24px; font-weight: normal; margin: 0 0 5px 0; padding: 0; } div.sphinxsidebar h4 { font-size: 20px; } div.sphinxsidebar h3 a { color: #444; } div.sphinxsidebar p.logo a, div.sphinxsidebar h3 a, div.sphinxsidebar p.logo a:hover, div.sphinxsidebar h3 a:hover { border: none; } div.sphinxsidebar p { color: #555; margin: 10px 0; } div.sphinxsidebar ul { margin: 10px 0; padding: 0; color: #000; } div.sphinxsidebar ul li.toctree-l1 > a { font-size: 120%; } div.sphinxsidebar ul li.toctree-l2 > a { font-size: 110%; } div.sphinxsidebar input { border: 1px solid #CCC; font-family: 'goudy old style', 'minion pro', 'bell mt', Georgia, 'Hiragino Mincho Pro', serif; font-size: 1em; } div.sphinxsidebar hr { border: none; height: 1px; color: #AAA; background: #AAA; text-align: left; margin-left: 0; width: 50%; } /* -- body styles ----------------------------------------------------------- */ a { color: #004B6B; text-decoration: underline; } a:hover { color: #6D4100; text-decoration: underline; } div.body h1, div.body h2, div.body h3, div.body h4, div.body h5, div.body h6 { font-family: 'Garamond', 'Georgia', serif; font-weight: normal; margin: 30px 0px 10px 0px; padding: 0; } div.body h1 { margin-top: 0; padding-top: 0; font-size: 240%; } div.body h2 { font-size: 180%; } div.body h3 { font-size: 150%; } div.body h4 { font-size: 130%; } div.body h5 { font-size: 100%; } div.body h6 { font-size: 100%; } a.headerlink { color: #DDD; padding: 0 4px; text-decoration: none; } a.headerlink:hover { color: #444; background: #EAEAEA; } div.body p, div.body dd, div.body li { line-height: 1.4em; } div.admonition { margin: 20px 0px; padding: 10px 30px; background-color: #FCC; border: 1px solid #FAA; } div.admonition tt.xref, div.admonition a tt { border-bottom: 1px solid #fafafa; } dd div.admonition { margin-left: -60px; padding-left: 60px; } div.admonition p.admonition-title { font-family: 'Garamond', 'Georgia', serif; font-weight: normal; font-size: 24px; margin: 0 0 10px 0; padding: 0; line-height: 1; } div.admonition p.last { margin-bottom: 0; } div.highlight { background-color: white; } dt:target, .highlight { background: #FAF3E8; } div.note { background-color: #EEE; border: 1px solid #CCC; } div.seealso { background-color: #EEE; border: 1px solid #CCC; } div.topic { background-color: #eee; } p.admonition-title { display: inline; } p.admonition-title:after { content: ":"; } pre, tt, code { font-family: 'Consolas', 'Menlo', 'Deja Vu Sans Mono', 'Bitstream Vera Sans Mono', monospace; font-size: 0.9em; } .hll { background-color: #FFC; margin: 0 -12px; padding: 0 12px; display: block; } img.screenshot { } tt.descname, tt.descclassname, code.descname, code.descclassname { font-size: 0.95em; } tt.descname, code.descname { padding-right: 0.08em; } img.screenshot { -moz-box-shadow: 2px 2px 4px #eee; -webkit-box-shadow: 2px 2px 4px #eee; box-shadow: 2px 2px 4px #eee; } table.docutils { border: 1px solid #888; -moz-box-shadow: 2px 2px 4px #eee; -webkit-box-shadow: 2px 2px 4px #eee; box-shadow: 2px 2px 4px #eee; } table.docutils td, table.docutils th { border: 1px solid #888; padding: 0.25em 0.7em; } table.field-list, table.footnote { border: none; -moz-box-shadow: none; -webkit-box-shadow: none; box-shadow: none; } table.footnote { margin: 15px 0; width: 100%; border: 1px solid #EEE; background: #FDFDFD; font-size: 0.9em; } table.footnote + table.footnote { margin-top: -15px; border-top: none; } table.field-list th { padding: 0 0.8em 0 0; } table.field-list td { padding: 0; } table.field-list p { margin-bottom: 0.8em; } table.footnote td.label { width: .1px; padding: 0.3em 0 0.3em 0.5em; } table.footnote td { padding: 0.3em 0.5em; } dl { margin: 0; padding: 0; } dl dd { margin-left: 30px; } blockquote { margin: 0 0 0 30px; padding: 0; } ul, ol { /* Matches the 30px from the narrow-screen "li > ul" selector below */ margin: 10px 0 10px 30px; padding: 0; } pre { background: #EEE; padding: 7px 30px; margin: 15px 0px; line-height: 1.3em; } dl pre, blockquote pre, li pre { margin-left: 0; padding-left: 30px; } dl dl pre { margin-left: -90px; padding-left: 90px; } tt, code { background-color: #ecf0f3; color: #222; /* padding: 1px 2px; */ } tt.xref, code.xref, a tt { background-color: #FBFBFB; border-bottom: 1px solid white; } a.reference { text-decoration: none; border-bottom: 1px dotted #004B6B; } /* Don't put an underline on images */ a.image-reference, a.image-reference:hover { border-bottom: none; } a.reference:hover { border-bottom: 1px solid #6D4100; } a.footnote-reference { text-decoration: none; font-size: 0.7em; vertical-align: top; border-bottom: 1px dotted #004B6B; } a.footnote-reference:hover { border-bottom: 1px solid #6D4100; } a:hover tt, a:hover code { background: #EEE; } @media screen and (max-width: 870px) { div.sphinxsidebar { display: none; } div.document { width: 100%; } div.documentwrapper { margin-left: 0; margin-top: 0; margin-right: 0; margin-bottom: 0; } div.bodywrapper { margin-top: 0; margin-right: 0; margin-bottom: 0; margin-left: 0; } ul { margin-left: 0; } li > ul { /* Matches the 30px from the "ul, ol" selector above */ margin-left: 30px; } .document { width: auto; } .footer { width: auto; } .bodywrapper { margin: 0; } .footer { width: auto; } .github { display: none; } } @media screen and (max-width: 875px) { body { margin: 0; padding: 20px 30px; } div.documentwrapper { float: none; background: white; } div.sphinxsidebar { display: block; float: none; width: 102.5%; margin: 50px -30px -20px -30px; padding: 10px 20px; background: #333; color: #FFF; } div.sphinxsidebar h3, div.sphinxsidebar h4, div.sphinxsidebar p, div.sphinxsidebar h3 a { color: white; } div.sphinxsidebar a { color: #AAA; } div.sphinxsidebar p.logo { display: none; } div.document { width: 100%; margin: 0; } div.footer { display: none; } div.bodywrapper { margin: 0; } div.body { min-height: 0; padding: 0; } .rtd_doc_footer { display: none; } .document { width: auto; } .footer { width: auto; } .footer { width: auto; } .github { display: none; } } /* misc. */ .revsys-inline { display: none!important; } /* Make nested-list/multi-paragraph items look better in Releases changelog * pages. Without this, docutils' magical list fuckery causes inconsistent * formatting between different release sub-lists. */ div#changelog > div.section > ul > li > p:only-child { margin-bottom: 0; } /* Hide fugly table cell borders in ..bibliography:: directive output */ table.docutils.citation, table.docutils.citation td, table.docutils.citation th { border: none; /* Below needed in some edge cases; if not applied, bottom shadows appear */ -moz-box-shadow: none; -webkit-box-shadow: none; box-shadow: none; }veusz-3.0.1/Documents/manual/html/_static/minus.png0000664000175000017500000000013213232117076021760 0ustar jssjss00000000000000PNG  IHDR (!IDATxc8 g>@;(!&]f2nNIENDB`veusz-3.0.1/Documents/manual/html/_static/comment-bright.png0000664000175000017500000000136413232117076023554 0ustar jssjss00000000000000PNG  IHDRaIDATx<ߙm۶m۶qm۶m۶mM=D8tٍ\{56j>Qn~3sD{oS+ٻ؀=nnW?XumAHI%pHscYoo_{Z)48sڳۗ8YüYsj34s^#ǒtˋqkZܜwݿߵ>!8pVn{շ=n$p\^;=;wPIENDB`veusz-3.0.1/Documents/manual/html/_static/websupport.js0000664000175000017500000006140713235626226022710 0ustar jssjss00000000000000/* * websupport.js * ~~~~~~~~~~~~~ * * sphinx.websupport utilities for all documentation. * * :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS. * :license: BSD, see LICENSE for details. * */ (function($) { $.fn.autogrow = function() { return this.each(function() { var textarea = this; $.fn.autogrow.resize(textarea); $(textarea) .focus(function() { textarea.interval = setInterval(function() { $.fn.autogrow.resize(textarea); }, 500); }) .blur(function() { clearInterval(textarea.interval); }); }); }; $.fn.autogrow.resize = function(textarea) { var lineHeight = parseInt($(textarea).css('line-height'), 10); var lines = textarea.value.split('\n'); var columns = textarea.cols; var lineCount = 0; $.each(lines, function() { lineCount += Math.ceil(this.length / columns) || 1; }); var height = lineHeight * (lineCount + 1); $(textarea).css('height', height); }; })(jQuery); (function($) { var comp, by; function init() { initEvents(); initComparator(); } function initEvents() { $(document).on("click", 'a.comment-close', function(event) { event.preventDefault(); hide($(this).attr('id').substring(2)); }); $(document).on("click", 'a.vote', function(event) { event.preventDefault(); handleVote($(this)); }); $(document).on("click", 'a.reply', function(event) { event.preventDefault(); openReply($(this).attr('id').substring(2)); }); $(document).on("click", 'a.close-reply', function(event) { event.preventDefault(); closeReply($(this).attr('id').substring(2)); }); $(document).on("click", 'a.sort-option', function(event) { event.preventDefault(); handleReSort($(this)); }); $(document).on("click", 'a.show-proposal', function(event) { event.preventDefault(); showProposal($(this).attr('id').substring(2)); }); $(document).on("click", 'a.hide-proposal', function(event) { event.preventDefault(); hideProposal($(this).attr('id').substring(2)); }); $(document).on("click", 'a.show-propose-change', function(event) { event.preventDefault(); showProposeChange($(this).attr('id').substring(2)); }); $(document).on("click", 'a.hide-propose-change', function(event) { event.preventDefault(); hideProposeChange($(this).attr('id').substring(2)); }); $(document).on("click", 'a.accept-comment', function(event) { event.preventDefault(); acceptComment($(this).attr('id').substring(2)); }); $(document).on("click", 'a.delete-comment', function(event) { event.preventDefault(); deleteComment($(this).attr('id').substring(2)); }); $(document).on("click", 'a.comment-markup', function(event) { event.preventDefault(); toggleCommentMarkupBox($(this).attr('id').substring(2)); }); } /** * Set comp, which is a comparator function used for sorting and * inserting comments into the list. */ function setComparator() { // If the first three letters are "asc", sort in ascending order // and remove the prefix. if (by.substring(0,3) == 'asc') { var i = by.substring(3); comp = function(a, b) { return a[i] - b[i]; }; } else { // Otherwise sort in descending order. comp = function(a, b) { return b[by] - a[by]; }; } // Reset link styles and format the selected sort option. $('a.sel').attr('href', '#').removeClass('sel'); $('a.by' + by).removeAttr('href').addClass('sel'); } /** * Create a comp function. If the user has preferences stored in * the sortBy cookie, use those, otherwise use the default. */ function initComparator() { by = 'rating'; // Default to sort by rating. // If the sortBy cookie is set, use that instead. if (document.cookie.length > 0) { var start = document.cookie.indexOf('sortBy='); if (start != -1) { start = start + 7; var end = document.cookie.indexOf(";", start); if (end == -1) { end = document.cookie.length; by = unescape(document.cookie.substring(start, end)); } } } setComparator(); } /** * Show a comment div. */ function show(id) { $('#ao' + id).hide(); $('#ah' + id).show(); var context = $.extend({id: id}, opts); var popup = $(renderTemplate(popupTemplate, context)).hide(); popup.find('textarea[name="proposal"]').hide(); popup.find('a.by' + by).addClass('sel'); var form = popup.find('#cf' + id); form.submit(function(event) { event.preventDefault(); addComment(form); }); $('#s' + id).after(popup); popup.slideDown('fast', function() { getComments(id); }); } /** * Hide a comment div. */ function hide(id) { $('#ah' + id).hide(); $('#ao' + id).show(); var div = $('#sc' + id); div.slideUp('fast', function() { div.remove(); }); } /** * Perform an ajax request to get comments for a node * and insert the comments into the comments tree. */ function getComments(id) { $.ajax({ type: 'GET', url: opts.getCommentsURL, data: {node: id}, success: function(data, textStatus, request) { var ul = $('#cl' + id); var speed = 100; $('#cf' + id) .find('textarea[name="proposal"]') .data('source', data.source); if (data.comments.length === 0) { ul.html('
  • No comments yet.
  • '); ul.data('empty', true); } else { // If there are comments, sort them and put them in the list. var comments = sortComments(data.comments); speed = data.comments.length * 100; appendComments(comments, ul); ul.data('empty', false); } $('#cn' + id).slideUp(speed + 200); ul.slideDown(speed); }, error: function(request, textStatus, error) { showError('Oops, there was a problem retrieving the comments.'); }, dataType: 'json' }); } /** * Add a comment via ajax and insert the comment into the comment tree. */ function addComment(form) { var node_id = form.find('input[name="node"]').val(); var parent_id = form.find('input[name="parent"]').val(); var text = form.find('textarea[name="comment"]').val(); var proposal = form.find('textarea[name="proposal"]').val(); if (text == '') { showError('Please enter a comment.'); return; } // Disable the form that is being submitted. form.find('textarea,input').attr('disabled', 'disabled'); // Send the comment to the server. $.ajax({ type: "POST", url: opts.addCommentURL, dataType: 'json', data: { node: node_id, parent: parent_id, text: text, proposal: proposal }, success: function(data, textStatus, error) { // Reset the form. if (node_id) { hideProposeChange(node_id); } form.find('textarea') .val('') .add(form.find('input')) .removeAttr('disabled'); var ul = $('#cl' + (node_id || parent_id)); if (ul.data('empty')) { $(ul).empty(); ul.data('empty', false); } insertComment(data.comment); var ao = $('#ao' + node_id); ao.find('img').attr({'src': opts.commentBrightImage}); if (node_id) { // if this was a "root" comment, remove the commenting box // (the user can get it back by reopening the comment popup) $('#ca' + node_id).slideUp(); } }, error: function(request, textStatus, error) { form.find('textarea,input').removeAttr('disabled'); showError('Oops, there was a problem adding the comment.'); } }); } /** * Recursively append comments to the main comment list and children * lists, creating the comment tree. */ function appendComments(comments, ul) { $.each(comments, function() { var div = createCommentDiv(this); ul.append($(document.createElement('li')).html(div)); appendComments(this.children, div.find('ul.comment-children')); // To avoid stagnating data, don't store the comments children in data. this.children = null; div.data('comment', this); }); } /** * After adding a new comment, it must be inserted in the correct * location in the comment tree. */ function insertComment(comment) { var div = createCommentDiv(comment); // To avoid stagnating data, don't store the comments children in data. comment.children = null; div.data('comment', comment); var ul = $('#cl' + (comment.node || comment.parent)); var siblings = getChildren(ul); var li = $(document.createElement('li')); li.hide(); // Determine where in the parents children list to insert this comment. for(i=0; i < siblings.length; i++) { if (comp(comment, siblings[i]) <= 0) { $('#cd' + siblings[i].id) .parent() .before(li.html(div)); li.slideDown('fast'); return; } } // If we get here, this comment rates lower than all the others, // or it is the only comment in the list. ul.append(li.html(div)); li.slideDown('fast'); } function acceptComment(id) { $.ajax({ type: 'POST', url: opts.acceptCommentURL, data: {id: id}, success: function(data, textStatus, request) { $('#cm' + id).fadeOut('fast'); $('#cd' + id).removeClass('moderate'); }, error: function(request, textStatus, error) { showError('Oops, there was a problem accepting the comment.'); } }); } function deleteComment(id) { $.ajax({ type: 'POST', url: opts.deleteCommentURL, data: {id: id}, success: function(data, textStatus, request) { var div = $('#cd' + id); if (data == 'delete') { // Moderator mode: remove the comment and all children immediately div.slideUp('fast', function() { div.remove(); }); return; } // User mode: only mark the comment as deleted div .find('span.user-id:first') .text('[deleted]').end() .find('div.comment-text:first') .text('[deleted]').end() .find('#cm' + id + ', #dc' + id + ', #ac' + id + ', #rc' + id + ', #sp' + id + ', #hp' + id + ', #cr' + id + ', #rl' + id) .remove(); var comment = div.data('comment'); comment.username = '[deleted]'; comment.text = '[deleted]'; div.data('comment', comment); }, error: function(request, textStatus, error) { showError('Oops, there was a problem deleting the comment.'); } }); } function showProposal(id) { $('#sp' + id).hide(); $('#hp' + id).show(); $('#pr' + id).slideDown('fast'); } function hideProposal(id) { $('#hp' + id).hide(); $('#sp' + id).show(); $('#pr' + id).slideUp('fast'); } function showProposeChange(id) { $('#pc' + id).hide(); $('#hc' + id).show(); var textarea = $('#pt' + id); textarea.val(textarea.data('source')); $.fn.autogrow.resize(textarea[0]); textarea.slideDown('fast'); } function hideProposeChange(id) { $('#hc' + id).hide(); $('#pc' + id).show(); var textarea = $('#pt' + id); textarea.val('').removeAttr('disabled'); textarea.slideUp('fast'); } function toggleCommentMarkupBox(id) { $('#mb' + id).toggle(); } /** Handle when the user clicks on a sort by link. */ function handleReSort(link) { var classes = link.attr('class').split(/\s+/); for (var i=0; iThank you! Your comment will show up ' + 'once it is has been approved by a moderator.'); } // Prettify the comment rating. comment.pretty_rating = comment.rating + ' point' + (comment.rating == 1 ? '' : 's'); // Make a class (for displaying not yet moderated comments differently) comment.css_class = comment.displayed ? '' : ' moderate'; // Create a div for this comment. var context = $.extend({}, opts, comment); var div = $(renderTemplate(commentTemplate, context)); // If the user has voted on this comment, highlight the correct arrow. if (comment.vote) { var direction = (comment.vote == 1) ? 'u' : 'd'; div.find('#' + direction + 'v' + comment.id).hide(); div.find('#' + direction + 'u' + comment.id).show(); } if (opts.moderator || comment.text != '[deleted]') { div.find('a.reply').show(); if (comment.proposal_diff) div.find('#sp' + comment.id).show(); if (opts.moderator && !comment.displayed) div.find('#cm' + comment.id).show(); if (opts.moderator || (opts.username == comment.username)) div.find('#dc' + comment.id).show(); } return div; } /** * A simple template renderer. Placeholders such as <%id%> are replaced * by context['id'] with items being escaped. Placeholders such as <#id#> * are not escaped. */ function renderTemplate(template, context) { var esc = $(document.createElement('div')); function handle(ph, escape) { var cur = context; $.each(ph.split('.'), function() { cur = cur[this]; }); return escape ? esc.text(cur || "").html() : cur; } return template.replace(/<([%#])([\w\.]*)\1>/g, function() { return handle(arguments[2], arguments[1] == '%' ? true : false); }); } /** Flash an error message briefly. */ function showError(message) { $(document.createElement('div')).attr({'class': 'popup-error'}) .append($(document.createElement('div')) .attr({'class': 'error-message'}).text(message)) .appendTo('body') .fadeIn("slow") .delay(2000) .fadeOut("slow"); } /** Add a link the user uses to open the comments popup. */ $.fn.comment = function() { return this.each(function() { var id = $(this).attr('id').substring(1); var count = COMMENT_METADATA[id]; var title = count + ' comment' + (count == 1 ? '' : 's'); var image = count > 0 ? opts.commentBrightImage : opts.commentImage; var addcls = count == 0 ? ' nocomment' : ''; $(this) .append( $(document.createElement('a')).attr({ href: '#', 'class': 'sphinx-comment-open' + addcls, id: 'ao' + id }) .append($(document.createElement('img')).attr({ src: image, alt: 'comment', title: title })) .click(function(event) { event.preventDefault(); show($(this).attr('id').substring(2)); }) ) .append( $(document.createElement('a')).attr({ href: '#', 'class': 'sphinx-comment-close hidden', id: 'ah' + id }) .append($(document.createElement('img')).attr({ src: opts.closeCommentImage, alt: 'close', title: 'close' })) .click(function(event) { event.preventDefault(); hide($(this).attr('id').substring(2)); }) ); }); }; var opts = { processVoteURL: '/_process_vote', addCommentURL: '/_add_comment', getCommentsURL: '/_get_comments', acceptCommentURL: '/_accept_comment', deleteCommentURL: '/_delete_comment', commentImage: '/static/_static/comment.png', closeCommentImage: '/static/_static/comment-close.png', loadingImage: '/static/_static/ajax-loader.gif', commentBrightImage: '/static/_static/comment-bright.png', upArrow: '/static/_static/up.png', downArrow: '/static/_static/down.png', upArrowPressed: '/static/_static/up-pressed.png', downArrowPressed: '/static/_static/down-pressed.png', voting: false, moderator: false }; if (typeof COMMENT_OPTIONS != "undefined") { opts = jQuery.extend(opts, COMMENT_OPTIONS); } var popupTemplate = '\
    \

    \ Sort by:\ best rated\ newest\ oldest\

    \
    Comments
    \
    \ loading comments...
    \
      \
      \

      Add a comment\ (markup):

      \
      \ reStructured text markup: *emph*, **strong**, \ ``code``, \ code blocks: :: and an indented block after blank line
      \
      \ \

      \ \ Propose a change ▹\ \ \ Propose a change ▿\ \

      \ \ \ \ \
      \
      \
      '; var commentTemplate = '\
      \
      \
      \ \ \ \ \ \ \
      \
      \ \ \ \ \ \ \
      \
      \
      \

      \ <%username%>\ <%pretty_rating%>\ <%time.delta%>\

      \
      <#text#>
      \

      \ \ reply ▿\ proposal ▹\ proposal ▿\ \ \

      \
      \
      <#proposal_diff#>\
              
      \
        \
        \
        \
        \ '; var replyTemplate = '\
      • \
        \
        \ \ \ \ \ \
        \
        \
      • '; $(document).ready(function() { init(); }); })(jQuery); $(document).ready(function() { // add comment anchors for all paragraphs that are commentable $('.sphinx-has-comment').comment(); // highlight search words in search results $("div.context").each(function() { var params = $.getQueryParameters(); var terms = (params.q) ? params.q[0].split(/\s+/) : []; var result = $(this); $.each(terms, function() { result.highlightText(this.toLowerCase(), 'highlighted'); }); }); // directly open comment window if requested var anchor = document.location.hash; if (anchor.substring(0, 9) == '#comment-') { $('#ao' + anchor.substring(9)).click(); document.location.hash = '#s' + anchor.substring(9); } }); veusz-3.0.1/Documents/manual/html/_static/down-pressed.png0000664000175000017500000000033613232117076023245 0ustar jssjss00000000000000PNG  IHDRaIDATxc@J@lKf[^g%_  HK ĿD Ab3CGhr.x/`X Wʱ 2 eF+,.xEJ lAR $WT?0i)1maUIENDB`veusz-3.0.1/Documents/manual/html/_static/comment.png0000664000175000017500000000120113232117076022265 0ustar jssjss00000000000000PNG  IHDRaHIDATx_VTVܰQǵFT7m]$|~\>&nMK<+W 7zɫ ?w!8_O ާ4& MS'/қ=rּ`V0!?t'$#'P`iawP?Dãqف.`Ž lZ%9A {EҺ !;e`fT]P]ZCDX2e)ןryOZs߂Ј {1<*Bx `(B42|k@=PAȚe; HͭU`B@(IϚR F"a(. |R*wZB/bZ fMQ+d!!065.9Eq+@3ىVSËd8;&KpHh0f;hY,]|Lcne!fKcJFiySOhמ%ws vaJ{ڣ;/S3 ?qcC\qHxsemk2n&;4i$n3> ycdIENDB`veusz-3.0.1/Documents/manual/html/_static/up.png0000664000175000017500000000031313232117076021252 0ustar jssjss00000000000000PNG  IHDR7IDATx@ez $& 8:& :Kpwn}O<:!!{G@Dz?"̧ S{g<ݢ lMQwy|? 0 pq8q` pL-'SBNAwTń|U VIENDB`veusz-3.0.1/Documents/manual/html/_static/jquery.js0000664000175000017500000101337213152644511022007 0ustar jssjss00000000000000/*! * jQuery JavaScript Library v3.2.1 * https://jquery.com/ * * Includes Sizzle.js * https://sizzlejs.com/ * * Copyright JS Foundation and other contributors * Released under the MIT license * https://jquery.org/license * * Date: 2017-09-03T00:14Z */ ( function( global, factory ) { "use strict"; if ( typeof module === "object" && typeof module.exports === "object" ) { // For CommonJS and CommonJS-like environments where a proper `window` // is present, execute the factory and get jQuery. // For environments that do not have a `window` with a `document` // (such as Node.js), expose a factory as module.exports. // This accentuates the need for the creation of a real `window`. // e.g. var jQuery = require("jquery")(window); // See ticket #14549 for more info. module.exports = global.document ? factory( global, true ) : function( w ) { if ( !w.document ) { throw new Error( "jQuery requires a window with a document" ); } return factory( w ); }; } else { factory( global ); } // Pass this if window is not defined yet } )( typeof window !== "undefined" ? window : this, function( window, noGlobal ) { // Edge <= 12 - 13+, Firefox <=18 - 45+, IE 10 - 11, Safari 5.1 - 9+, iOS 6 - 9.1 // throw exceptions when non-strict code (e.g., ASP.NET 4.5) accesses strict mode // arguments.callee.caller (trac-13335). But as of jQuery 3.0 (2016), strict mode should be common // enough that all such attempts are guarded in a try block. var arr = []; var document = window.document; var getProto = Object.getPrototypeOf; var slice = arr.slice; var concat = arr.concat; var push = arr.push; var indexOf = arr.indexOf; var class2type = {}; var toString = class2type.toString; var hasOwn = class2type.hasOwnProperty; var fnToString = hasOwn.toString; var ObjectFunctionString = fnToString.call( Object ); var support = {}; function DOMEval( code, doc ) { doc = doc || document; var script = doc.createElement( "script" ); script.text = code; doc.head.appendChild( script ).parentNode.removeChild( script ); } /* global Symbol */ // Defining this global in .eslintrc.json would create a danger of using the global // unguarded in another place, it seems safer to define global only for this module var version = "3.2.1", // Define a local copy of jQuery jQuery = function( selector, context ) { // The jQuery object is actually just the init constructor 'enhanced' // Need init if jQuery is called (just allow error to be thrown if not included) return new jQuery.fn.init( selector, context ); }, // Support: Android <=4.0 only // Make sure we trim BOM and NBSP rtrim = /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, // Matches dashed string for camelizing rmsPrefix = /^-ms-/, rdashAlpha = /-([a-z])/g, // Used by jQuery.camelCase as callback to replace() fcamelCase = function( all, letter ) { return letter.toUpperCase(); }; jQuery.fn = jQuery.prototype = { // The current version of jQuery being used jquery: version, constructor: jQuery, // The default length of a jQuery object is 0 length: 0, toArray: function() { return slice.call( this ); }, // Get the Nth element in the matched element set OR // Get the whole matched element set as a clean array get: function( num ) { // Return all the elements in a clean array if ( num == null ) { return slice.call( this ); } // Return just the one element from the set return num < 0 ? this[ num + this.length ] : this[ num ]; }, // Take an array of elements and push it onto the stack // (returning the new matched element set) pushStack: function( elems ) { // Build a new jQuery matched element set var ret = jQuery.merge( this.constructor(), elems ); // Add the old object onto the stack (as a reference) ret.prevObject = this; // Return the newly-formed element set return ret; }, // Execute a callback for every element in the matched set. each: function( callback ) { return jQuery.each( this, callback ); }, map: function( callback ) { return this.pushStack( jQuery.map( this, function( elem, i ) { return callback.call( elem, i, elem ); } ) ); }, slice: function() { return this.pushStack( slice.apply( this, arguments ) ); }, first: function() { return this.eq( 0 ); }, last: function() { return this.eq( -1 ); }, eq: function( i ) { var len = this.length, j = +i + ( i < 0 ? len : 0 ); return this.pushStack( j >= 0 && j < len ? [ this[ j ] ] : [] ); }, end: function() { return this.prevObject || this.constructor(); }, // For internal use only. // Behaves like an Array's method, not like a jQuery method. push: push, sort: arr.sort, splice: arr.splice }; jQuery.extend = jQuery.fn.extend = function() { var options, name, src, copy, copyIsArray, clone, target = arguments[ 0 ] || {}, i = 1, length = arguments.length, deep = false; // Handle a deep copy situation if ( typeof target === "boolean" ) { deep = target; // Skip the boolean and the target target = arguments[ i ] || {}; i++; } // Handle case when target is a string or something (possible in deep copy) if ( typeof target !== "object" && !jQuery.isFunction( target ) ) { target = {}; } // Extend jQuery itself if only one argument is passed if ( i === length ) { target = this; i--; } for ( ; i < length; i++ ) { // Only deal with non-null/undefined values if ( ( options = arguments[ i ] ) != null ) { // Extend the base object for ( name in options ) { src = target[ name ]; copy = options[ name ]; // Prevent never-ending loop if ( target === copy ) { continue; } // Recurse if we're merging plain objects or arrays if ( deep && copy && ( jQuery.isPlainObject( copy ) || ( copyIsArray = Array.isArray( copy ) ) ) ) { if ( copyIsArray ) { copyIsArray = false; clone = src && Array.isArray( src ) ? src : []; } else { clone = src && jQuery.isPlainObject( src ) ? src : {}; } // Never move original objects, clone them target[ name ] = jQuery.extend( deep, clone, copy ); // Don't bring in undefined values } else if ( copy !== undefined ) { target[ name ] = copy; } } } } // Return the modified object return target; }; jQuery.extend( { // Unique for each copy of jQuery on the page expando: "jQuery" + ( version + Math.random() ).replace( /\D/g, "" ), // Assume jQuery is ready without the ready module isReady: true, error: function( msg ) { throw new Error( msg ); }, noop: function() {}, isFunction: function( obj ) { return jQuery.type( obj ) === "function"; }, isWindow: function( obj ) { return obj != null && obj === obj.window; }, isNumeric: function( obj ) { // As of jQuery 3.0, isNumeric is limited to // strings and numbers (primitives or objects) // that can be coerced to finite numbers (gh-2662) var type = jQuery.type( obj ); return ( type === "number" || type === "string" ) && // parseFloat NaNs numeric-cast false positives ("") // ...but misinterprets leading-number strings, particularly hex literals ("0x...") // subtraction forces infinities to NaN !isNaN( obj - parseFloat( obj ) ); }, isPlainObject: function( obj ) { var proto, Ctor; // Detect obvious negatives // Use toString instead of jQuery.type to catch host objects if ( !obj || toString.call( obj ) !== "[object Object]" ) { return false; } proto = getProto( obj ); // Objects with no prototype (e.g., `Object.create( null )`) are plain if ( !proto ) { return true; } // Objects with prototype are plain iff they were constructed by a global Object function Ctor = hasOwn.call( proto, "constructor" ) && proto.constructor; return typeof Ctor === "function" && fnToString.call( Ctor ) === ObjectFunctionString; }, isEmptyObject: function( obj ) { /* eslint-disable no-unused-vars */ // See https://github.com/eslint/eslint/issues/6125 var name; for ( name in obj ) { return false; } return true; }, type: function( obj ) { if ( obj == null ) { return obj + ""; } // Support: Android <=2.3 only (functionish RegExp) return typeof obj === "object" || typeof obj === "function" ? class2type[ toString.call( obj ) ] || "object" : typeof obj; }, // Evaluates a script in a global context globalEval: function( code ) { DOMEval( code ); }, // Convert dashed to camelCase; used by the css and data modules // Support: IE <=9 - 11, Edge 12 - 13 // Microsoft forgot to hump their vendor prefix (#9572) camelCase: function( string ) { return string.replace( rmsPrefix, "ms-" ).replace( rdashAlpha, fcamelCase ); }, each: function( obj, callback ) { var length, i = 0; if ( isArrayLike( obj ) ) { length = obj.length; for ( ; i < length; i++ ) { if ( callback.call( obj[ i ], i, obj[ i ] ) === false ) { break; } } } else { for ( i in obj ) { if ( callback.call( obj[ i ], i, obj[ i ] ) === false ) { break; } } } return obj; }, // Support: Android <=4.0 only trim: function( text ) { return text == null ? "" : ( text + "" ).replace( rtrim, "" ); }, // results is for internal usage only makeArray: function( arr, results ) { var ret = results || []; if ( arr != null ) { if ( isArrayLike( Object( arr ) ) ) { jQuery.merge( ret, typeof arr === "string" ? [ arr ] : arr ); } else { push.call( ret, arr ); } } return ret; }, inArray: function( elem, arr, i ) { return arr == null ? -1 : indexOf.call( arr, elem, i ); }, // Support: Android <=4.0 only, PhantomJS 1 only // push.apply(_, arraylike) throws on ancient WebKit merge: function( first, second ) { var len = +second.length, j = 0, i = first.length; for ( ; j < len; j++ ) { first[ i++ ] = second[ j ]; } first.length = i; return first; }, grep: function( elems, callback, invert ) { var callbackInverse, matches = [], i = 0, length = elems.length, callbackExpect = !invert; // Go through the array, only saving the items // that pass the validator function for ( ; i < length; i++ ) { callbackInverse = !callback( elems[ i ], i ); if ( callbackInverse !== callbackExpect ) { matches.push( elems[ i ] ); } } return matches; }, // arg is for internal usage only map: function( elems, callback, arg ) { var length, value, i = 0, ret = []; // Go through the array, translating each of the items to their new values if ( isArrayLike( elems ) ) { length = elems.length; for ( ; i < length; i++ ) { value = callback( elems[ i ], i, arg ); if ( value != null ) { ret.push( value ); } } // Go through every key on the object, } else { for ( i in elems ) { value = callback( elems[ i ], i, arg ); if ( value != null ) { ret.push( value ); } } } // Flatten any nested arrays return concat.apply( [], ret ); }, // A global GUID counter for objects guid: 1, // Bind a function to a context, optionally partially applying any // arguments. proxy: function( fn, context ) { var tmp, args, proxy; if ( typeof context === "string" ) { tmp = fn[ context ]; context = fn; fn = tmp; } // Quick check to determine if target is callable, in the spec // this throws a TypeError, but we will just return undefined. if ( !jQuery.isFunction( fn ) ) { return undefined; } // Simulated bind args = slice.call( arguments, 2 ); proxy = function() { return fn.apply( context || this, args.concat( slice.call( arguments ) ) ); }; // Set the guid of unique handler to the same of original handler, so it can be removed proxy.guid = fn.guid = fn.guid || jQuery.guid++; return proxy; }, now: Date.now, // jQuery.support is not used in Core but other projects attach their // properties to it so it needs to exist. support: support } ); if ( typeof Symbol === "function" ) { jQuery.fn[ Symbol.iterator ] = arr[ Symbol.iterator ]; } // Populate the class2type map jQuery.each( "Boolean Number String Function Array Date RegExp Object Error Symbol".split( " " ), function( i, name ) { class2type[ "[object " + name + "]" ] = name.toLowerCase(); } ); function isArrayLike( obj ) { // Support: real iOS 8.2 only (not reproducible in simulator) // `in` check used to prevent JIT error (gh-2145) // hasOwn isn't used here due to false negatives // regarding Nodelist length in IE var length = !!obj && "length" in obj && obj.length, type = jQuery.type( obj ); if ( type === "function" || jQuery.isWindow( obj ) ) { return false; } return type === "array" || length === 0 || typeof length === "number" && length > 0 && ( length - 1 ) in obj; } var Sizzle = /*! * Sizzle CSS Selector Engine v2.3.3 * https://sizzlejs.com/ * * Copyright jQuery Foundation and other contributors * Released under the MIT license * http://jquery.org/license * * Date: 2016-08-08 */ (function( window ) { var i, support, Expr, getText, isXML, tokenize, compile, select, outermostContext, sortInput, hasDuplicate, // Local document vars setDocument, document, docElem, documentIsHTML, rbuggyQSA, rbuggyMatches, matches, contains, // Instance-specific data expando = "sizzle" + 1 * new Date(), preferredDoc = window.document, dirruns = 0, done = 0, classCache = createCache(), tokenCache = createCache(), compilerCache = createCache(), sortOrder = function( a, b ) { if ( a === b ) { hasDuplicate = true; } return 0; }, // Instance methods hasOwn = ({}).hasOwnProperty, arr = [], pop = arr.pop, push_native = arr.push, push = arr.push, slice = arr.slice, // Use a stripped-down indexOf as it's faster than native // https://jsperf.com/thor-indexof-vs-for/5 indexOf = function( list, elem ) { var i = 0, len = list.length; for ( ; i < len; i++ ) { if ( list[i] === elem ) { return i; } } return -1; }, booleans = "checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped", // Regular expressions // http://www.w3.org/TR/css3-selectors/#whitespace whitespace = "[\\x20\\t\\r\\n\\f]", // http://www.w3.org/TR/CSS21/syndata.html#value-def-identifier identifier = "(?:\\\\.|[\\w-]|[^\0-\\xa0])+", // Attribute selectors: http://www.w3.org/TR/selectors/#attribute-selectors attributes = "\\[" + whitespace + "*(" + identifier + ")(?:" + whitespace + // Operator (capture 2) "*([*^$|!~]?=)" + whitespace + // "Attribute values must be CSS identifiers [capture 5] or strings [capture 3 or capture 4]" "*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|(" + identifier + "))|)" + whitespace + "*\\]", pseudos = ":(" + identifier + ")(?:\\((" + // To reduce the number of selectors needing tokenize in the preFilter, prefer arguments: // 1. quoted (capture 3; capture 4 or capture 5) "('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|" + // 2. simple (capture 6) "((?:\\\\.|[^\\\\()[\\]]|" + attributes + ")*)|" + // 3. anything else (capture 2) ".*" + ")\\)|)", // Leading and non-escaped trailing whitespace, capturing some non-whitespace characters preceding the latter rwhitespace = new RegExp( whitespace + "+", "g" ), rtrim = new RegExp( "^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" + whitespace + "+$", "g" ), rcomma = new RegExp( "^" + whitespace + "*," + whitespace + "*" ), rcombinators = new RegExp( "^" + whitespace + "*([>+~]|" + whitespace + ")" + whitespace + "*" ), rattributeQuotes = new RegExp( "=" + whitespace + "*([^\\]'\"]*?)" + whitespace + "*\\]", "g" ), rpseudo = new RegExp( pseudos ), ridentifier = new RegExp( "^" + identifier + "$" ), matchExpr = { "ID": new RegExp( "^#(" + identifier + ")" ), "CLASS": new RegExp( "^\\.(" + identifier + ")" ), "TAG": new RegExp( "^(" + identifier + "|[*])" ), "ATTR": new RegExp( "^" + attributes ), "PSEUDO": new RegExp( "^" + pseudos ), "CHILD": new RegExp( "^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\(" + whitespace + "*(even|odd|(([+-]|)(\\d*)n|)" + whitespace + "*(?:([+-]|)" + whitespace + "*(\\d+)|))" + whitespace + "*\\)|)", "i" ), "bool": new RegExp( "^(?:" + booleans + ")$", "i" ), // For use in libraries implementing .is() // We use this for POS matching in `select` "needsContext": new RegExp( "^" + whitespace + "*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\(" + whitespace + "*((?:-\\d)?\\d*)" + whitespace + "*\\)|)(?=[^-]|$)", "i" ) }, rinputs = /^(?:input|select|textarea|button)$/i, rheader = /^h\d$/i, rnative = /^[^{]+\{\s*\[native \w/, // Easily-parseable/retrievable ID or TAG or CLASS selectors rquickExpr = /^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/, rsibling = /[+~]/, // CSS escapes // http://www.w3.org/TR/CSS21/syndata.html#escaped-characters runescape = new RegExp( "\\\\([\\da-f]{1,6}" + whitespace + "?|(" + whitespace + ")|.)", "ig" ), funescape = function( _, escaped, escapedWhitespace ) { var high = "0x" + escaped - 0x10000; // NaN means non-codepoint // Support: Firefox<24 // Workaround erroneous numeric interpretation of +"0x" return high !== high || escapedWhitespace ? escaped : high < 0 ? // BMP codepoint String.fromCharCode( high + 0x10000 ) : // Supplemental Plane codepoint (surrogate pair) String.fromCharCode( high >> 10 | 0xD800, high & 0x3FF | 0xDC00 ); }, // CSS string/identifier serialization // https://drafts.csswg.org/cssom/#common-serializing-idioms rcssescape = /([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g, fcssescape = function( ch, asCodePoint ) { if ( asCodePoint ) { // U+0000 NULL becomes U+FFFD REPLACEMENT CHARACTER if ( ch === "\0" ) { return "\uFFFD"; } // Control characters and (dependent upon position) numbers get escaped as code points return ch.slice( 0, -1 ) + "\\" + ch.charCodeAt( ch.length - 1 ).toString( 16 ) + " "; } // Other potentially-special ASCII characters get backslash-escaped return "\\" + ch; }, // Used for iframes // See setDocument() // Removing the function wrapper causes a "Permission Denied" // error in IE unloadHandler = function() { setDocument(); }, disabledAncestor = addCombinator( function( elem ) { return elem.disabled === true && ("form" in elem || "label" in elem); }, { dir: "parentNode", next: "legend" } ); // Optimize for push.apply( _, NodeList ) try { push.apply( (arr = slice.call( preferredDoc.childNodes )), preferredDoc.childNodes ); // Support: Android<4.0 // Detect silently failing push.apply arr[ preferredDoc.childNodes.length ].nodeType; } catch ( e ) { push = { apply: arr.length ? // Leverage slice if possible function( target, els ) { push_native.apply( target, slice.call(els) ); } : // Support: IE<9 // Otherwise append directly function( target, els ) { var j = target.length, i = 0; // Can't trust NodeList.length while ( (target[j++] = els[i++]) ) {} target.length = j - 1; } }; } function Sizzle( selector, context, results, seed ) { var m, i, elem, nid, match, groups, newSelector, newContext = context && context.ownerDocument, // nodeType defaults to 9, since context defaults to document nodeType = context ? context.nodeType : 9; results = results || []; // Return early from calls with invalid selector or context if ( typeof selector !== "string" || !selector || nodeType !== 1 && nodeType !== 9 && nodeType !== 11 ) { return results; } // Try to shortcut find operations (as opposed to filters) in HTML documents if ( !seed ) { if ( ( context ? context.ownerDocument || context : preferredDoc ) !== document ) { setDocument( context ); } context = context || document; if ( documentIsHTML ) { // If the selector is sufficiently simple, try using a "get*By*" DOM method // (excepting DocumentFragment context, where the methods don't exist) if ( nodeType !== 11 && (match = rquickExpr.exec( selector )) ) { // ID selector if ( (m = match[1]) ) { // Document context if ( nodeType === 9 ) { if ( (elem = context.getElementById( m )) ) { // Support: IE, Opera, Webkit // TODO: identify versions // getElementById can match elements by name instead of ID if ( elem.id === m ) { results.push( elem ); return results; } } else { return results; } // Element context } else { // Support: IE, Opera, Webkit // TODO: identify versions // getElementById can match elements by name instead of ID if ( newContext && (elem = newContext.getElementById( m )) && contains( context, elem ) && elem.id === m ) { results.push( elem ); return results; } } // Type selector } else if ( match[2] ) { push.apply( results, context.getElementsByTagName( selector ) ); return results; // Class selector } else if ( (m = match[3]) && support.getElementsByClassName && context.getElementsByClassName ) { push.apply( results, context.getElementsByClassName( m ) ); return results; } } // Take advantage of querySelectorAll if ( support.qsa && !compilerCache[ selector + " " ] && (!rbuggyQSA || !rbuggyQSA.test( selector )) ) { if ( nodeType !== 1 ) { newContext = context; newSelector = selector; // qSA looks outside Element context, which is not what we want // Thanks to Andrew Dupont for this workaround technique // Support: IE <=8 // Exclude object elements } else if ( context.nodeName.toLowerCase() !== "object" ) { // Capture the context ID, setting it first if necessary if ( (nid = context.getAttribute( "id" )) ) { nid = nid.replace( rcssescape, fcssescape ); } else { context.setAttribute( "id", (nid = expando) ); } // Prefix every selector in the list groups = tokenize( selector ); i = groups.length; while ( i-- ) { groups[i] = "#" + nid + " " + toSelector( groups[i] ); } newSelector = groups.join( "," ); // Expand context for sibling selectors newContext = rsibling.test( selector ) && testContext( context.parentNode ) || context; } if ( newSelector ) { try { push.apply( results, newContext.querySelectorAll( newSelector ) ); return results; } catch ( qsaError ) { } finally { if ( nid === expando ) { context.removeAttribute( "id" ); } } } } } } // All others return select( selector.replace( rtrim, "$1" ), context, results, seed ); } /** * Create key-value caches of limited size * @returns {function(string, object)} Returns the Object data after storing it on itself with * property name the (space-suffixed) string and (if the cache is larger than Expr.cacheLength) * deleting the oldest entry */ function createCache() { var keys = []; function cache( key, value ) { // Use (key + " ") to avoid collision with native prototype properties (see Issue #157) if ( keys.push( key + " " ) > Expr.cacheLength ) { // Only keep the most recent entries delete cache[ keys.shift() ]; } return (cache[ key + " " ] = value); } return cache; } /** * Mark a function for special use by Sizzle * @param {Function} fn The function to mark */ function markFunction( fn ) { fn[ expando ] = true; return fn; } /** * Support testing using an element * @param {Function} fn Passed the created element and returns a boolean result */ function assert( fn ) { var el = document.createElement("fieldset"); try { return !!fn( el ); } catch (e) { return false; } finally { // Remove from its parent by default if ( el.parentNode ) { el.parentNode.removeChild( el ); } // release memory in IE el = null; } } /** * Adds the same handler for all of the specified attrs * @param {String} attrs Pipe-separated list of attributes * @param {Function} handler The method that will be applied */ function addHandle( attrs, handler ) { var arr = attrs.split("|"), i = arr.length; while ( i-- ) { Expr.attrHandle[ arr[i] ] = handler; } } /** * Checks document order of two siblings * @param {Element} a * @param {Element} b * @returns {Number} Returns less than 0 if a precedes b, greater than 0 if a follows b */ function siblingCheck( a, b ) { var cur = b && a, diff = cur && a.nodeType === 1 && b.nodeType === 1 && a.sourceIndex - b.sourceIndex; // Use IE sourceIndex if available on both nodes if ( diff ) { return diff; } // Check if b follows a if ( cur ) { while ( (cur = cur.nextSibling) ) { if ( cur === b ) { return -1; } } } return a ? 1 : -1; } /** * Returns a function to use in pseudos for input types * @param {String} type */ function createInputPseudo( type ) { return function( elem ) { var name = elem.nodeName.toLowerCase(); return name === "input" && elem.type === type; }; } /** * Returns a function to use in pseudos for buttons * @param {String} type */ function createButtonPseudo( type ) { return function( elem ) { var name = elem.nodeName.toLowerCase(); return (name === "input" || name === "button") && elem.type === type; }; } /** * Returns a function to use in pseudos for :enabled/:disabled * @param {Boolean} disabled true for :disabled; false for :enabled */ function createDisabledPseudo( disabled ) { // Known :disabled false positives: fieldset[disabled] > legend:nth-of-type(n+2) :can-disable return function( elem ) { // Only certain elements can match :enabled or :disabled // https://html.spec.whatwg.org/multipage/scripting.html#selector-enabled // https://html.spec.whatwg.org/multipage/scripting.html#selector-disabled if ( "form" in elem ) { // Check for inherited disabledness on relevant non-disabled elements: // * listed form-associated elements in a disabled fieldset // https://html.spec.whatwg.org/multipage/forms.html#category-listed // https://html.spec.whatwg.org/multipage/forms.html#concept-fe-disabled // * option elements in a disabled optgroup // https://html.spec.whatwg.org/multipage/forms.html#concept-option-disabled // All such elements have a "form" property. if ( elem.parentNode && elem.disabled === false ) { // Option elements defer to a parent optgroup if present if ( "label" in elem ) { if ( "label" in elem.parentNode ) { return elem.parentNode.disabled === disabled; } else { return elem.disabled === disabled; } } // Support: IE 6 - 11 // Use the isDisabled shortcut property to check for disabled fieldset ancestors return elem.isDisabled === disabled || // Where there is no isDisabled, check manually /* jshint -W018 */ elem.isDisabled !== !disabled && disabledAncestor( elem ) === disabled; } return elem.disabled === disabled; // Try to winnow out elements that can't be disabled before trusting the disabled property. // Some victims get caught in our net (label, legend, menu, track), but it shouldn't // even exist on them, let alone have a boolean value. } else if ( "label" in elem ) { return elem.disabled === disabled; } // Remaining elements are neither :enabled nor :disabled return false; }; } /** * Returns a function to use in pseudos for positionals * @param {Function} fn */ function createPositionalPseudo( fn ) { return markFunction(function( argument ) { argument = +argument; return markFunction(function( seed, matches ) { var j, matchIndexes = fn( [], seed.length, argument ), i = matchIndexes.length; // Match elements found at the specified indexes while ( i-- ) { if ( seed[ (j = matchIndexes[i]) ] ) { seed[j] = !(matches[j] = seed[j]); } } }); }); } /** * Checks a node for validity as a Sizzle context * @param {Element|Object=} context * @returns {Element|Object|Boolean} The input node if acceptable, otherwise a falsy value */ function testContext( context ) { return context && typeof context.getElementsByTagName !== "undefined" && context; } // Expose support vars for convenience support = Sizzle.support = {}; /** * Detects XML nodes * @param {Element|Object} elem An element or a document * @returns {Boolean} True iff elem is a non-HTML XML node */ isXML = Sizzle.isXML = function( elem ) { // documentElement is verified for cases where it doesn't yet exist // (such as loading iframes in IE - #4833) var documentElement = elem && (elem.ownerDocument || elem).documentElement; return documentElement ? documentElement.nodeName !== "HTML" : false; }; /** * Sets document-related variables once based on the current document * @param {Element|Object} [doc] An element or document object to use to set the document * @returns {Object} Returns the current document */ setDocument = Sizzle.setDocument = function( node ) { var hasCompare, subWindow, doc = node ? node.ownerDocument || node : preferredDoc; // Return early if doc is invalid or already selected if ( doc === document || doc.nodeType !== 9 || !doc.documentElement ) { return document; } // Update global variables document = doc; docElem = document.documentElement; documentIsHTML = !isXML( document ); // Support: IE 9-11, Edge // Accessing iframe documents after unload throws "permission denied" errors (jQuery #13936) if ( preferredDoc !== document && (subWindow = document.defaultView) && subWindow.top !== subWindow ) { // Support: IE 11, Edge if ( subWindow.addEventListener ) { subWindow.addEventListener( "unload", unloadHandler, false ); // Support: IE 9 - 10 only } else if ( subWindow.attachEvent ) { subWindow.attachEvent( "onunload", unloadHandler ); } } /* Attributes ---------------------------------------------------------------------- */ // Support: IE<8 // Verify that getAttribute really returns attributes and not properties // (excepting IE8 booleans) support.attributes = assert(function( el ) { el.className = "i"; return !el.getAttribute("className"); }); /* getElement(s)By* ---------------------------------------------------------------------- */ // Check if getElementsByTagName("*") returns only elements support.getElementsByTagName = assert(function( el ) { el.appendChild( document.createComment("") ); return !el.getElementsByTagName("*").length; }); // Support: IE<9 support.getElementsByClassName = rnative.test( document.getElementsByClassName ); // Support: IE<10 // Check if getElementById returns elements by name // The broken getElementById methods don't pick up programmatically-set names, // so use a roundabout getElementsByName test support.getById = assert(function( el ) { docElem.appendChild( el ).id = expando; return !document.getElementsByName || !document.getElementsByName( expando ).length; }); // ID filter and find if ( support.getById ) { Expr.filter["ID"] = function( id ) { var attrId = id.replace( runescape, funescape ); return function( elem ) { return elem.getAttribute("id") === attrId; }; }; Expr.find["ID"] = function( id, context ) { if ( typeof context.getElementById !== "undefined" && documentIsHTML ) { var elem = context.getElementById( id ); return elem ? [ elem ] : []; } }; } else { Expr.filter["ID"] = function( id ) { var attrId = id.replace( runescape, funescape ); return function( elem ) { var node = typeof elem.getAttributeNode !== "undefined" && elem.getAttributeNode("id"); return node && node.value === attrId; }; }; // Support: IE 6 - 7 only // getElementById is not reliable as a find shortcut Expr.find["ID"] = function( id, context ) { if ( typeof context.getElementById !== "undefined" && documentIsHTML ) { var node, i, elems, elem = context.getElementById( id ); if ( elem ) { // Verify the id attribute node = elem.getAttributeNode("id"); if ( node && node.value === id ) { return [ elem ]; } // Fall back on getElementsByName elems = context.getElementsByName( id ); i = 0; while ( (elem = elems[i++]) ) { node = elem.getAttributeNode("id"); if ( node && node.value === id ) { return [ elem ]; } } } return []; } }; } // Tag Expr.find["TAG"] = support.getElementsByTagName ? function( tag, context ) { if ( typeof context.getElementsByTagName !== "undefined" ) { return context.getElementsByTagName( tag ); // DocumentFragment nodes don't have gEBTN } else if ( support.qsa ) { return context.querySelectorAll( tag ); } } : function( tag, context ) { var elem, tmp = [], i = 0, // By happy coincidence, a (broken) gEBTN appears on DocumentFragment nodes too results = context.getElementsByTagName( tag ); // Filter out possible comments if ( tag === "*" ) { while ( (elem = results[i++]) ) { if ( elem.nodeType === 1 ) { tmp.push( elem ); } } return tmp; } return results; }; // Class Expr.find["CLASS"] = support.getElementsByClassName && function( className, context ) { if ( typeof context.getElementsByClassName !== "undefined" && documentIsHTML ) { return context.getElementsByClassName( className ); } }; /* QSA/matchesSelector ---------------------------------------------------------------------- */ // QSA and matchesSelector support // matchesSelector(:active) reports false when true (IE9/Opera 11.5) rbuggyMatches = []; // qSa(:focus) reports false when true (Chrome 21) // We allow this because of a bug in IE8/9 that throws an error // whenever `document.activeElement` is accessed on an iframe // So, we allow :focus to pass through QSA all the time to avoid the IE error // See https://bugs.jquery.com/ticket/13378 rbuggyQSA = []; if ( (support.qsa = rnative.test( document.querySelectorAll )) ) { // Build QSA regex // Regex strategy adopted from Diego Perini assert(function( el ) { // Select is set to empty string on purpose // This is to test IE's treatment of not explicitly // setting a boolean content attribute, // since its presence should be enough // https://bugs.jquery.com/ticket/12359 docElem.appendChild( el ).innerHTML = "" + ""; // Support: IE8, Opera 11-12.16 // Nothing should be selected when empty strings follow ^= or $= or *= // The test attribute must be unknown in Opera but "safe" for WinRT // https://msdn.microsoft.com/en-us/library/ie/hh465388.aspx#attribute_section if ( el.querySelectorAll("[msallowcapture^='']").length ) { rbuggyQSA.push( "[*^$]=" + whitespace + "*(?:''|\"\")" ); } // Support: IE8 // Boolean attributes and "value" are not treated correctly if ( !el.querySelectorAll("[selected]").length ) { rbuggyQSA.push( "\\[" + whitespace + "*(?:value|" + booleans + ")" ); } // Support: Chrome<29, Android<4.4, Safari<7.0+, iOS<7.0+, PhantomJS<1.9.8+ if ( !el.querySelectorAll( "[id~=" + expando + "-]" ).length ) { rbuggyQSA.push("~="); } // Webkit/Opera - :checked should return selected option elements // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked // IE8 throws error here and will not see later tests if ( !el.querySelectorAll(":checked").length ) { rbuggyQSA.push(":checked"); } // Support: Safari 8+, iOS 8+ // https://bugs.webkit.org/show_bug.cgi?id=136851 // In-page `selector#id sibling-combinator selector` fails if ( !el.querySelectorAll( "a#" + expando + "+*" ).length ) { rbuggyQSA.push(".#.+[+~]"); } }); assert(function( el ) { el.innerHTML = "" + ""; // Support: Windows 8 Native Apps // The type and name attributes are restricted during .innerHTML assignment var input = document.createElement("input"); input.setAttribute( "type", "hidden" ); el.appendChild( input ).setAttribute( "name", "D" ); // Support: IE8 // Enforce case-sensitivity of name attribute if ( el.querySelectorAll("[name=d]").length ) { rbuggyQSA.push( "name" + whitespace + "*[*^$|!~]?=" ); } // FF 3.5 - :enabled/:disabled and hidden elements (hidden elements are still enabled) // IE8 throws error here and will not see later tests if ( el.querySelectorAll(":enabled").length !== 2 ) { rbuggyQSA.push( ":enabled", ":disabled" ); } // Support: IE9-11+ // IE's :disabled selector does not pick up the children of disabled fieldsets docElem.appendChild( el ).disabled = true; if ( el.querySelectorAll(":disabled").length !== 2 ) { rbuggyQSA.push( ":enabled", ":disabled" ); } // Opera 10-11 does not throw on post-comma invalid pseudos el.querySelectorAll("*,:x"); rbuggyQSA.push(",.*:"); }); } if ( (support.matchesSelector = rnative.test( (matches = docElem.matches || docElem.webkitMatchesSelector || docElem.mozMatchesSelector || docElem.oMatchesSelector || docElem.msMatchesSelector) )) ) { assert(function( el ) { // Check to see if it's possible to do matchesSelector // on a disconnected node (IE 9) support.disconnectedMatch = matches.call( el, "*" ); // This should fail with an exception // Gecko does not error, returns false instead matches.call( el, "[s!='']:x" ); rbuggyMatches.push( "!=", pseudos ); }); } rbuggyQSA = rbuggyQSA.length && new RegExp( rbuggyQSA.join("|") ); rbuggyMatches = rbuggyMatches.length && new RegExp( rbuggyMatches.join("|") ); /* Contains ---------------------------------------------------------------------- */ hasCompare = rnative.test( docElem.compareDocumentPosition ); // Element contains another // Purposefully self-exclusive // As in, an element does not contain itself contains = hasCompare || rnative.test( docElem.contains ) ? function( a, b ) { var adown = a.nodeType === 9 ? a.documentElement : a, bup = b && b.parentNode; return a === bup || !!( bup && bup.nodeType === 1 && ( adown.contains ? adown.contains( bup ) : a.compareDocumentPosition && a.compareDocumentPosition( bup ) & 16 )); } : function( a, b ) { if ( b ) { while ( (b = b.parentNode) ) { if ( b === a ) { return true; } } } return false; }; /* Sorting ---------------------------------------------------------------------- */ // Document order sorting sortOrder = hasCompare ? function( a, b ) { // Flag for duplicate removal if ( a === b ) { hasDuplicate = true; return 0; } // Sort on method existence if only one input has compareDocumentPosition var compare = !a.compareDocumentPosition - !b.compareDocumentPosition; if ( compare ) { return compare; } // Calculate position if both inputs belong to the same document compare = ( a.ownerDocument || a ) === ( b.ownerDocument || b ) ? a.compareDocumentPosition( b ) : // Otherwise we know they are disconnected 1; // Disconnected nodes if ( compare & 1 || (!support.sortDetached && b.compareDocumentPosition( a ) === compare) ) { // Choose the first element that is related to our preferred document if ( a === document || a.ownerDocument === preferredDoc && contains(preferredDoc, a) ) { return -1; } if ( b === document || b.ownerDocument === preferredDoc && contains(preferredDoc, b) ) { return 1; } // Maintain original order return sortInput ? ( indexOf( sortInput, a ) - indexOf( sortInput, b ) ) : 0; } return compare & 4 ? -1 : 1; } : function( a, b ) { // Exit early if the nodes are identical if ( a === b ) { hasDuplicate = true; return 0; } var cur, i = 0, aup = a.parentNode, bup = b.parentNode, ap = [ a ], bp = [ b ]; // Parentless nodes are either documents or disconnected if ( !aup || !bup ) { return a === document ? -1 : b === document ? 1 : aup ? -1 : bup ? 1 : sortInput ? ( indexOf( sortInput, a ) - indexOf( sortInput, b ) ) : 0; // If the nodes are siblings, we can do a quick check } else if ( aup === bup ) { return siblingCheck( a, b ); } // Otherwise we need full lists of their ancestors for comparison cur = a; while ( (cur = cur.parentNode) ) { ap.unshift( cur ); } cur = b; while ( (cur = cur.parentNode) ) { bp.unshift( cur ); } // Walk down the tree looking for a discrepancy while ( ap[i] === bp[i] ) { i++; } return i ? // Do a sibling check if the nodes have a common ancestor siblingCheck( ap[i], bp[i] ) : // Otherwise nodes in our document sort first ap[i] === preferredDoc ? -1 : bp[i] === preferredDoc ? 1 : 0; }; return document; }; Sizzle.matches = function( expr, elements ) { return Sizzle( expr, null, null, elements ); }; Sizzle.matchesSelector = function( elem, expr ) { // Set document vars if needed if ( ( elem.ownerDocument || elem ) !== document ) { setDocument( elem ); } // Make sure that attribute selectors are quoted expr = expr.replace( rattributeQuotes, "='$1']" ); if ( support.matchesSelector && documentIsHTML && !compilerCache[ expr + " " ] && ( !rbuggyMatches || !rbuggyMatches.test( expr ) ) && ( !rbuggyQSA || !rbuggyQSA.test( expr ) ) ) { try { var ret = matches.call( elem, expr ); // IE 9's matchesSelector returns false on disconnected nodes if ( ret || support.disconnectedMatch || // As well, disconnected nodes are said to be in a document // fragment in IE 9 elem.document && elem.document.nodeType !== 11 ) { return ret; } } catch (e) {} } return Sizzle( expr, document, null, [ elem ] ).length > 0; }; Sizzle.contains = function( context, elem ) { // Set document vars if needed if ( ( context.ownerDocument || context ) !== document ) { setDocument( context ); } return contains( context, elem ); }; Sizzle.attr = function( elem, name ) { // Set document vars if needed if ( ( elem.ownerDocument || elem ) !== document ) { setDocument( elem ); } var fn = Expr.attrHandle[ name.toLowerCase() ], // Don't get fooled by Object.prototype properties (jQuery #13807) val = fn && hasOwn.call( Expr.attrHandle, name.toLowerCase() ) ? fn( elem, name, !documentIsHTML ) : undefined; return val !== undefined ? val : support.attributes || !documentIsHTML ? elem.getAttribute( name ) : (val = elem.getAttributeNode(name)) && val.specified ? val.value : null; }; Sizzle.escape = function( sel ) { return (sel + "").replace( rcssescape, fcssescape ); }; Sizzle.error = function( msg ) { throw new Error( "Syntax error, unrecognized expression: " + msg ); }; /** * Document sorting and removing duplicates * @param {ArrayLike} results */ Sizzle.uniqueSort = function( results ) { var elem, duplicates = [], j = 0, i = 0; // Unless we *know* we can detect duplicates, assume their presence hasDuplicate = !support.detectDuplicates; sortInput = !support.sortStable && results.slice( 0 ); results.sort( sortOrder ); if ( hasDuplicate ) { while ( (elem = results[i++]) ) { if ( elem === results[ i ] ) { j = duplicates.push( i ); } } while ( j-- ) { results.splice( duplicates[ j ], 1 ); } } // Clear input after sorting to release objects // See https://github.com/jquery/sizzle/pull/225 sortInput = null; return results; }; /** * Utility function for retrieving the text value of an array of DOM nodes * @param {Array|Element} elem */ getText = Sizzle.getText = function( elem ) { var node, ret = "", i = 0, nodeType = elem.nodeType; if ( !nodeType ) { // If no nodeType, this is expected to be an array while ( (node = elem[i++]) ) { // Do not traverse comment nodes ret += getText( node ); } } else if ( nodeType === 1 || nodeType === 9 || nodeType === 11 ) { // Use textContent for elements // innerText usage removed for consistency of new lines (jQuery #11153) if ( typeof elem.textContent === "string" ) { return elem.textContent; } else { // Traverse its children for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) { ret += getText( elem ); } } } else if ( nodeType === 3 || nodeType === 4 ) { return elem.nodeValue; } // Do not include comment or processing instruction nodes return ret; }; Expr = Sizzle.selectors = { // Can be adjusted by the user cacheLength: 50, createPseudo: markFunction, match: matchExpr, attrHandle: {}, find: {}, relative: { ">": { dir: "parentNode", first: true }, " ": { dir: "parentNode" }, "+": { dir: "previousSibling", first: true }, "~": { dir: "previousSibling" } }, preFilter: { "ATTR": function( match ) { match[1] = match[1].replace( runescape, funescape ); // Move the given value to match[3] whether quoted or unquoted match[3] = ( match[3] || match[4] || match[5] || "" ).replace( runescape, funescape ); if ( match[2] === "~=" ) { match[3] = " " + match[3] + " "; } return match.slice( 0, 4 ); }, "CHILD": function( match ) { /* matches from matchExpr["CHILD"] 1 type (only|nth|...) 2 what (child|of-type) 3 argument (even|odd|\d*|\d*n([+-]\d+)?|...) 4 xn-component of xn+y argument ([+-]?\d*n|) 5 sign of xn-component 6 x of xn-component 7 sign of y-component 8 y of y-component */ match[1] = match[1].toLowerCase(); if ( match[1].slice( 0, 3 ) === "nth" ) { // nth-* requires argument if ( !match[3] ) { Sizzle.error( match[0] ); } // numeric x and y parameters for Expr.filter.CHILD // remember that false/true cast respectively to 0/1 match[4] = +( match[4] ? match[5] + (match[6] || 1) : 2 * ( match[3] === "even" || match[3] === "odd" ) ); match[5] = +( ( match[7] + match[8] ) || match[3] === "odd" ); // other types prohibit arguments } else if ( match[3] ) { Sizzle.error( match[0] ); } return match; }, "PSEUDO": function( match ) { var excess, unquoted = !match[6] && match[2]; if ( matchExpr["CHILD"].test( match[0] ) ) { return null; } // Accept quoted arguments as-is if ( match[3] ) { match[2] = match[4] || match[5] || ""; // Strip excess characters from unquoted arguments } else if ( unquoted && rpseudo.test( unquoted ) && // Get excess from tokenize (recursively) (excess = tokenize( unquoted, true )) && // advance to the next closing parenthesis (excess = unquoted.indexOf( ")", unquoted.length - excess ) - unquoted.length) ) { // excess is a negative index match[0] = match[0].slice( 0, excess ); match[2] = unquoted.slice( 0, excess ); } // Return only captures needed by the pseudo filter method (type and argument) return match.slice( 0, 3 ); } }, filter: { "TAG": function( nodeNameSelector ) { var nodeName = nodeNameSelector.replace( runescape, funescape ).toLowerCase(); return nodeNameSelector === "*" ? function() { return true; } : function( elem ) { return elem.nodeName && elem.nodeName.toLowerCase() === nodeName; }; }, "CLASS": function( className ) { var pattern = classCache[ className + " " ]; return pattern || (pattern = new RegExp( "(^|" + whitespace + ")" + className + "(" + whitespace + "|$)" )) && classCache( className, function( elem ) { return pattern.test( typeof elem.className === "string" && elem.className || typeof elem.getAttribute !== "undefined" && elem.getAttribute("class") || "" ); }); }, "ATTR": function( name, operator, check ) { return function( elem ) { var result = Sizzle.attr( elem, name ); if ( result == null ) { return operator === "!="; } if ( !operator ) { return true; } result += ""; return operator === "=" ? result === check : operator === "!=" ? result !== check : operator === "^=" ? check && result.indexOf( check ) === 0 : operator === "*=" ? check && result.indexOf( check ) > -1 : operator === "$=" ? check && result.slice( -check.length ) === check : operator === "~=" ? ( " " + result.replace( rwhitespace, " " ) + " " ).indexOf( check ) > -1 : operator === "|=" ? result === check || result.slice( 0, check.length + 1 ) === check + "-" : false; }; }, "CHILD": function( type, what, argument, first, last ) { var simple = type.slice( 0, 3 ) !== "nth", forward = type.slice( -4 ) !== "last", ofType = what === "of-type"; return first === 1 && last === 0 ? // Shortcut for :nth-*(n) function( elem ) { return !!elem.parentNode; } : function( elem, context, xml ) { var cache, uniqueCache, outerCache, node, nodeIndex, start, dir = simple !== forward ? "nextSibling" : "previousSibling", parent = elem.parentNode, name = ofType && elem.nodeName.toLowerCase(), useCache = !xml && !ofType, diff = false; if ( parent ) { // :(first|last|only)-(child|of-type) if ( simple ) { while ( dir ) { node = elem; while ( (node = node[ dir ]) ) { if ( ofType ? node.nodeName.toLowerCase() === name : node.nodeType === 1 ) { return false; } } // Reverse direction for :only-* (if we haven't yet done so) start = dir = type === "only" && !start && "nextSibling"; } return true; } start = [ forward ? parent.firstChild : parent.lastChild ]; // non-xml :nth-child(...) stores cache data on `parent` if ( forward && useCache ) { // Seek `elem` from a previously-cached index // ...in a gzip-friendly way node = parent; outerCache = node[ expando ] || (node[ expando ] = {}); // Support: IE <9 only // Defend against cloned attroperties (jQuery gh-1709) uniqueCache = outerCache[ node.uniqueID ] || (outerCache[ node.uniqueID ] = {}); cache = uniqueCache[ type ] || []; nodeIndex = cache[ 0 ] === dirruns && cache[ 1 ]; diff = nodeIndex && cache[ 2 ]; node = nodeIndex && parent.childNodes[ nodeIndex ]; while ( (node = ++nodeIndex && node && node[ dir ] || // Fallback to seeking `elem` from the start (diff = nodeIndex = 0) || start.pop()) ) { // When found, cache indexes on `parent` and break if ( node.nodeType === 1 && ++diff && node === elem ) { uniqueCache[ type ] = [ dirruns, nodeIndex, diff ]; break; } } } else { // Use previously-cached element index if available if ( useCache ) { // ...in a gzip-friendly way node = elem; outerCache = node[ expando ] || (node[ expando ] = {}); // Support: IE <9 only // Defend against cloned attroperties (jQuery gh-1709) uniqueCache = outerCache[ node.uniqueID ] || (outerCache[ node.uniqueID ] = {}); cache = uniqueCache[ type ] || []; nodeIndex = cache[ 0 ] === dirruns && cache[ 1 ]; diff = nodeIndex; } // xml :nth-child(...) // or :nth-last-child(...) or :nth(-last)?-of-type(...) if ( diff === false ) { // Use the same loop as above to seek `elem` from the start while ( (node = ++nodeIndex && node && node[ dir ] || (diff = nodeIndex = 0) || start.pop()) ) { if ( ( ofType ? node.nodeName.toLowerCase() === name : node.nodeType === 1 ) && ++diff ) { // Cache the index of each encountered element if ( useCache ) { outerCache = node[ expando ] || (node[ expando ] = {}); // Support: IE <9 only // Defend against cloned attroperties (jQuery gh-1709) uniqueCache = outerCache[ node.uniqueID ] || (outerCache[ node.uniqueID ] = {}); uniqueCache[ type ] = [ dirruns, diff ]; } if ( node === elem ) { break; } } } } } // Incorporate the offset, then check against cycle size diff -= last; return diff === first || ( diff % first === 0 && diff / first >= 0 ); } }; }, "PSEUDO": function( pseudo, argument ) { // pseudo-class names are case-insensitive // http://www.w3.org/TR/selectors/#pseudo-classes // Prioritize by case sensitivity in case custom pseudos are added with uppercase letters // Remember that setFilters inherits from pseudos var args, fn = Expr.pseudos[ pseudo ] || Expr.setFilters[ pseudo.toLowerCase() ] || Sizzle.error( "unsupported pseudo: " + pseudo ); // The user may use createPseudo to indicate that // arguments are needed to create the filter function // just as Sizzle does if ( fn[ expando ] ) { return fn( argument ); } // But maintain support for old signatures if ( fn.length > 1 ) { args = [ pseudo, pseudo, "", argument ]; return Expr.setFilters.hasOwnProperty( pseudo.toLowerCase() ) ? markFunction(function( seed, matches ) { var idx, matched = fn( seed, argument ), i = matched.length; while ( i-- ) { idx = indexOf( seed, matched[i] ); seed[ idx ] = !( matches[ idx ] = matched[i] ); } }) : function( elem ) { return fn( elem, 0, args ); }; } return fn; } }, pseudos: { // Potentially complex pseudos "not": markFunction(function( selector ) { // Trim the selector passed to compile // to avoid treating leading and trailing // spaces as combinators var input = [], results = [], matcher = compile( selector.replace( rtrim, "$1" ) ); return matcher[ expando ] ? markFunction(function( seed, matches, context, xml ) { var elem, unmatched = matcher( seed, null, xml, [] ), i = seed.length; // Match elements unmatched by `matcher` while ( i-- ) { if ( (elem = unmatched[i]) ) { seed[i] = !(matches[i] = elem); } } }) : function( elem, context, xml ) { input[0] = elem; matcher( input, null, xml, results ); // Don't keep the element (issue #299) input[0] = null; return !results.pop(); }; }), "has": markFunction(function( selector ) { return function( elem ) { return Sizzle( selector, elem ).length > 0; }; }), "contains": markFunction(function( text ) { text = text.replace( runescape, funescape ); return function( elem ) { return ( elem.textContent || elem.innerText || getText( elem ) ).indexOf( text ) > -1; }; }), // "Whether an element is represented by a :lang() selector // is based solely on the element's language value // being equal to the identifier C, // or beginning with the identifier C immediately followed by "-". // The matching of C against the element's language value is performed case-insensitively. // The identifier C does not have to be a valid language name." // http://www.w3.org/TR/selectors/#lang-pseudo "lang": markFunction( function( lang ) { // lang value must be a valid identifier if ( !ridentifier.test(lang || "") ) { Sizzle.error( "unsupported lang: " + lang ); } lang = lang.replace( runescape, funescape ).toLowerCase(); return function( elem ) { var elemLang; do { if ( (elemLang = documentIsHTML ? elem.lang : elem.getAttribute("xml:lang") || elem.getAttribute("lang")) ) { elemLang = elemLang.toLowerCase(); return elemLang === lang || elemLang.indexOf( lang + "-" ) === 0; } } while ( (elem = elem.parentNode) && elem.nodeType === 1 ); return false; }; }), // Miscellaneous "target": function( elem ) { var hash = window.location && window.location.hash; return hash && hash.slice( 1 ) === elem.id; }, "root": function( elem ) { return elem === docElem; }, "focus": function( elem ) { return elem === document.activeElement && (!document.hasFocus || document.hasFocus()) && !!(elem.type || elem.href || ~elem.tabIndex); }, // Boolean properties "enabled": createDisabledPseudo( false ), "disabled": createDisabledPseudo( true ), "checked": function( elem ) { // In CSS3, :checked should return both checked and selected elements // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked var nodeName = elem.nodeName.toLowerCase(); return (nodeName === "input" && !!elem.checked) || (nodeName === "option" && !!elem.selected); }, "selected": function( elem ) { // Accessing this property makes selected-by-default // options in Safari work properly if ( elem.parentNode ) { elem.parentNode.selectedIndex; } return elem.selected === true; }, // Contents "empty": function( elem ) { // http://www.w3.org/TR/selectors/#empty-pseudo // :empty is negated by element (1) or content nodes (text: 3; cdata: 4; entity ref: 5), // but not by others (comment: 8; processing instruction: 7; etc.) // nodeType < 6 works because attributes (2) do not appear as children for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) { if ( elem.nodeType < 6 ) { return false; } } return true; }, "parent": function( elem ) { return !Expr.pseudos["empty"]( elem ); }, // Element/input types "header": function( elem ) { return rheader.test( elem.nodeName ); }, "input": function( elem ) { return rinputs.test( elem.nodeName ); }, "button": function( elem ) { var name = elem.nodeName.toLowerCase(); return name === "input" && elem.type === "button" || name === "button"; }, "text": function( elem ) { var attr; return elem.nodeName.toLowerCase() === "input" && elem.type === "text" && // Support: IE<8 // New HTML5 attribute values (e.g., "search") appear with elem.type === "text" ( (attr = elem.getAttribute("type")) == null || attr.toLowerCase() === "text" ); }, // Position-in-collection "first": createPositionalPseudo(function() { return [ 0 ]; }), "last": createPositionalPseudo(function( matchIndexes, length ) { return [ length - 1 ]; }), "eq": createPositionalPseudo(function( matchIndexes, length, argument ) { return [ argument < 0 ? argument + length : argument ]; }), "even": createPositionalPseudo(function( matchIndexes, length ) { var i = 0; for ( ; i < length; i += 2 ) { matchIndexes.push( i ); } return matchIndexes; }), "odd": createPositionalPseudo(function( matchIndexes, length ) { var i = 1; for ( ; i < length; i += 2 ) { matchIndexes.push( i ); } return matchIndexes; }), "lt": createPositionalPseudo(function( matchIndexes, length, argument ) { var i = argument < 0 ? argument + length : argument; for ( ; --i >= 0; ) { matchIndexes.push( i ); } return matchIndexes; }), "gt": createPositionalPseudo(function( matchIndexes, length, argument ) { var i = argument < 0 ? argument + length : argument; for ( ; ++i < length; ) { matchIndexes.push( i ); } return matchIndexes; }) } }; Expr.pseudos["nth"] = Expr.pseudos["eq"]; // Add button/input type pseudos for ( i in { radio: true, checkbox: true, file: true, password: true, image: true } ) { Expr.pseudos[ i ] = createInputPseudo( i ); } for ( i in { submit: true, reset: true } ) { Expr.pseudos[ i ] = createButtonPseudo( i ); } // Easy API for creating new setFilters function setFilters() {} setFilters.prototype = Expr.filters = Expr.pseudos; Expr.setFilters = new setFilters(); tokenize = Sizzle.tokenize = function( selector, parseOnly ) { var matched, match, tokens, type, soFar, groups, preFilters, cached = tokenCache[ selector + " " ]; if ( cached ) { return parseOnly ? 0 : cached.slice( 0 ); } soFar = selector; groups = []; preFilters = Expr.preFilter; while ( soFar ) { // Comma and first run if ( !matched || (match = rcomma.exec( soFar )) ) { if ( match ) { // Don't consume trailing commas as valid soFar = soFar.slice( match[0].length ) || soFar; } groups.push( (tokens = []) ); } matched = false; // Combinators if ( (match = rcombinators.exec( soFar )) ) { matched = match.shift(); tokens.push({ value: matched, // Cast descendant combinators to space type: match[0].replace( rtrim, " " ) }); soFar = soFar.slice( matched.length ); } // Filters for ( type in Expr.filter ) { if ( (match = matchExpr[ type ].exec( soFar )) && (!preFilters[ type ] || (match = preFilters[ type ]( match ))) ) { matched = match.shift(); tokens.push({ value: matched, type: type, matches: match }); soFar = soFar.slice( matched.length ); } } if ( !matched ) { break; } } // Return the length of the invalid excess // if we're just parsing // Otherwise, throw an error or return tokens return parseOnly ? soFar.length : soFar ? Sizzle.error( selector ) : // Cache the tokens tokenCache( selector, groups ).slice( 0 ); }; function toSelector( tokens ) { var i = 0, len = tokens.length, selector = ""; for ( ; i < len; i++ ) { selector += tokens[i].value; } return selector; } function addCombinator( matcher, combinator, base ) { var dir = combinator.dir, skip = combinator.next, key = skip || dir, checkNonElements = base && key === "parentNode", doneName = done++; return combinator.first ? // Check against closest ancestor/preceding element function( elem, context, xml ) { while ( (elem = elem[ dir ]) ) { if ( elem.nodeType === 1 || checkNonElements ) { return matcher( elem, context, xml ); } } return false; } : // Check against all ancestor/preceding elements function( elem, context, xml ) { var oldCache, uniqueCache, outerCache, newCache = [ dirruns, doneName ]; // We can't set arbitrary data on XML nodes, so they don't benefit from combinator caching if ( xml ) { while ( (elem = elem[ dir ]) ) { if ( elem.nodeType === 1 || checkNonElements ) { if ( matcher( elem, context, xml ) ) { return true; } } } } else { while ( (elem = elem[ dir ]) ) { if ( elem.nodeType === 1 || checkNonElements ) { outerCache = elem[ expando ] || (elem[ expando ] = {}); // Support: IE <9 only // Defend against cloned attroperties (jQuery gh-1709) uniqueCache = outerCache[ elem.uniqueID ] || (outerCache[ elem.uniqueID ] = {}); if ( skip && skip === elem.nodeName.toLowerCase() ) { elem = elem[ dir ] || elem; } else if ( (oldCache = uniqueCache[ key ]) && oldCache[ 0 ] === dirruns && oldCache[ 1 ] === doneName ) { // Assign to newCache so results back-propagate to previous elements return (newCache[ 2 ] = oldCache[ 2 ]); } else { // Reuse newcache so results back-propagate to previous elements uniqueCache[ key ] = newCache; // A match means we're done; a fail means we have to keep checking if ( (newCache[ 2 ] = matcher( elem, context, xml )) ) { return true; } } } } } return false; }; } function elementMatcher( matchers ) { return matchers.length > 1 ? function( elem, context, xml ) { var i = matchers.length; while ( i-- ) { if ( !matchers[i]( elem, context, xml ) ) { return false; } } return true; } : matchers[0]; } function multipleContexts( selector, contexts, results ) { var i = 0, len = contexts.length; for ( ; i < len; i++ ) { Sizzle( selector, contexts[i], results ); } return results; } function condense( unmatched, map, filter, context, xml ) { var elem, newUnmatched = [], i = 0, len = unmatched.length, mapped = map != null; for ( ; i < len; i++ ) { if ( (elem = unmatched[i]) ) { if ( !filter || filter( elem, context, xml ) ) { newUnmatched.push( elem ); if ( mapped ) { map.push( i ); } } } } return newUnmatched; } function setMatcher( preFilter, selector, matcher, postFilter, postFinder, postSelector ) { if ( postFilter && !postFilter[ expando ] ) { postFilter = setMatcher( postFilter ); } if ( postFinder && !postFinder[ expando ] ) { postFinder = setMatcher( postFinder, postSelector ); } return markFunction(function( seed, results, context, xml ) { var temp, i, elem, preMap = [], postMap = [], preexisting = results.length, // Get initial elements from seed or context elems = seed || multipleContexts( selector || "*", context.nodeType ? [ context ] : context, [] ), // Prefilter to get matcher input, preserving a map for seed-results synchronization matcherIn = preFilter && ( seed || !selector ) ? condense( elems, preMap, preFilter, context, xml ) : elems, matcherOut = matcher ? // If we have a postFinder, or filtered seed, or non-seed postFilter or preexisting results, postFinder || ( seed ? preFilter : preexisting || postFilter ) ? // ...intermediate processing is necessary [] : // ...otherwise use results directly results : matcherIn; // Find primary matches if ( matcher ) { matcher( matcherIn, matcherOut, context, xml ); } // Apply postFilter if ( postFilter ) { temp = condense( matcherOut, postMap ); postFilter( temp, [], context, xml ); // Un-match failing elements by moving them back to matcherIn i = temp.length; while ( i-- ) { if ( (elem = temp[i]) ) { matcherOut[ postMap[i] ] = !(matcherIn[ postMap[i] ] = elem); } } } if ( seed ) { if ( postFinder || preFilter ) { if ( postFinder ) { // Get the final matcherOut by condensing this intermediate into postFinder contexts temp = []; i = matcherOut.length; while ( i-- ) { if ( (elem = matcherOut[i]) ) { // Restore matcherIn since elem is not yet a final match temp.push( (matcherIn[i] = elem) ); } } postFinder( null, (matcherOut = []), temp, xml ); } // Move matched elements from seed to results to keep them synchronized i = matcherOut.length; while ( i-- ) { if ( (elem = matcherOut[i]) && (temp = postFinder ? indexOf( seed, elem ) : preMap[i]) > -1 ) { seed[temp] = !(results[temp] = elem); } } } // Add elements to results, through postFinder if defined } else { matcherOut = condense( matcherOut === results ? matcherOut.splice( preexisting, matcherOut.length ) : matcherOut ); if ( postFinder ) { postFinder( null, results, matcherOut, xml ); } else { push.apply( results, matcherOut ); } } }); } function matcherFromTokens( tokens ) { var checkContext, matcher, j, len = tokens.length, leadingRelative = Expr.relative[ tokens[0].type ], implicitRelative = leadingRelative || Expr.relative[" "], i = leadingRelative ? 1 : 0, // The foundational matcher ensures that elements are reachable from top-level context(s) matchContext = addCombinator( function( elem ) { return elem === checkContext; }, implicitRelative, true ), matchAnyContext = addCombinator( function( elem ) { return indexOf( checkContext, elem ) > -1; }, implicitRelative, true ), matchers = [ function( elem, context, xml ) { var ret = ( !leadingRelative && ( xml || context !== outermostContext ) ) || ( (checkContext = context).nodeType ? matchContext( elem, context, xml ) : matchAnyContext( elem, context, xml ) ); // Avoid hanging onto element (issue #299) checkContext = null; return ret; } ]; for ( ; i < len; i++ ) { if ( (matcher = Expr.relative[ tokens[i].type ]) ) { matchers = [ addCombinator(elementMatcher( matchers ), matcher) ]; } else { matcher = Expr.filter[ tokens[i].type ].apply( null, tokens[i].matches ); // Return special upon seeing a positional matcher if ( matcher[ expando ] ) { // Find the next relative operator (if any) for proper handling j = ++i; for ( ; j < len; j++ ) { if ( Expr.relative[ tokens[j].type ] ) { break; } } return setMatcher( i > 1 && elementMatcher( matchers ), i > 1 && toSelector( // If the preceding token was a descendant combinator, insert an implicit any-element `*` tokens.slice( 0, i - 1 ).concat({ value: tokens[ i - 2 ].type === " " ? "*" : "" }) ).replace( rtrim, "$1" ), matcher, i < j && matcherFromTokens( tokens.slice( i, j ) ), j < len && matcherFromTokens( (tokens = tokens.slice( j )) ), j < len && toSelector( tokens ) ); } matchers.push( matcher ); } } return elementMatcher( matchers ); } function matcherFromGroupMatchers( elementMatchers, setMatchers ) { var bySet = setMatchers.length > 0, byElement = elementMatchers.length > 0, superMatcher = function( seed, context, xml, results, outermost ) { var elem, j, matcher, matchedCount = 0, i = "0", unmatched = seed && [], setMatched = [], contextBackup = outermostContext, // We must always have either seed elements or outermost context elems = seed || byElement && Expr.find["TAG"]( "*", outermost ), // Use integer dirruns iff this is the outermost matcher dirrunsUnique = (dirruns += contextBackup == null ? 1 : Math.random() || 0.1), len = elems.length; if ( outermost ) { outermostContext = context === document || context || outermost; } // Add elements passing elementMatchers directly to results // Support: IE<9, Safari // Tolerate NodeList properties (IE: "length"; Safari: ) matching elements by id for ( ; i !== len && (elem = elems[i]) != null; i++ ) { if ( byElement && elem ) { j = 0; if ( !context && elem.ownerDocument !== document ) { setDocument( elem ); xml = !documentIsHTML; } while ( (matcher = elementMatchers[j++]) ) { if ( matcher( elem, context || document, xml) ) { results.push( elem ); break; } } if ( outermost ) { dirruns = dirrunsUnique; } } // Track unmatched elements for set filters if ( bySet ) { // They will have gone through all possible matchers if ( (elem = !matcher && elem) ) { matchedCount--; } // Lengthen the array for every element, matched or not if ( seed ) { unmatched.push( elem ); } } } // `i` is now the count of elements visited above, and adding it to `matchedCount` // makes the latter nonnegative. matchedCount += i; // Apply set filters to unmatched elements // NOTE: This can be skipped if there are no unmatched elements (i.e., `matchedCount` // equals `i`), unless we didn't visit _any_ elements in the above loop because we have // no element matchers and no seed. // Incrementing an initially-string "0" `i` allows `i` to remain a string only in that // case, which will result in a "00" `matchedCount` that differs from `i` but is also // numerically zero. if ( bySet && i !== matchedCount ) { j = 0; while ( (matcher = setMatchers[j++]) ) { matcher( unmatched, setMatched, context, xml ); } if ( seed ) { // Reintegrate element matches to eliminate the need for sorting if ( matchedCount > 0 ) { while ( i-- ) { if ( !(unmatched[i] || setMatched[i]) ) { setMatched[i] = pop.call( results ); } } } // Discard index placeholder values to get only actual matches setMatched = condense( setMatched ); } // Add matches to results push.apply( results, setMatched ); // Seedless set matches succeeding multiple successful matchers stipulate sorting if ( outermost && !seed && setMatched.length > 0 && ( matchedCount + setMatchers.length ) > 1 ) { Sizzle.uniqueSort( results ); } } // Override manipulation of globals by nested matchers if ( outermost ) { dirruns = dirrunsUnique; outermostContext = contextBackup; } return unmatched; }; return bySet ? markFunction( superMatcher ) : superMatcher; } compile = Sizzle.compile = function( selector, match /* Internal Use Only */ ) { var i, setMatchers = [], elementMatchers = [], cached = compilerCache[ selector + " " ]; if ( !cached ) { // Generate a function of recursive functions that can be used to check each element if ( !match ) { match = tokenize( selector ); } i = match.length; while ( i-- ) { cached = matcherFromTokens( match[i] ); if ( cached[ expando ] ) { setMatchers.push( cached ); } else { elementMatchers.push( cached ); } } // Cache the compiled function cached = compilerCache( selector, matcherFromGroupMatchers( elementMatchers, setMatchers ) ); // Save selector and tokenization cached.selector = selector; } return cached; }; /** * A low-level selection function that works with Sizzle's compiled * selector functions * @param {String|Function} selector A selector or a pre-compiled * selector function built with Sizzle.compile * @param {Element} context * @param {Array} [results] * @param {Array} [seed] A set of elements to match against */ select = Sizzle.select = function( selector, context, results, seed ) { var i, tokens, token, type, find, compiled = typeof selector === "function" && selector, match = !seed && tokenize( (selector = compiled.selector || selector) ); results = results || []; // Try to minimize operations if there is only one selector in the list and no seed // (the latter of which guarantees us context) if ( match.length === 1 ) { // Reduce context if the leading compound selector is an ID tokens = match[0] = match[0].slice( 0 ); if ( tokens.length > 2 && (token = tokens[0]).type === "ID" && context.nodeType === 9 && documentIsHTML && Expr.relative[ tokens[1].type ] ) { context = ( Expr.find["ID"]( token.matches[0].replace(runescape, funescape), context ) || [] )[0]; if ( !context ) { return results; // Precompiled matchers will still verify ancestry, so step up a level } else if ( compiled ) { context = context.parentNode; } selector = selector.slice( tokens.shift().value.length ); } // Fetch a seed set for right-to-left matching i = matchExpr["needsContext"].test( selector ) ? 0 : tokens.length; while ( i-- ) { token = tokens[i]; // Abort if we hit a combinator if ( Expr.relative[ (type = token.type) ] ) { break; } if ( (find = Expr.find[ type ]) ) { // Search, expanding context for leading sibling combinators if ( (seed = find( token.matches[0].replace( runescape, funescape ), rsibling.test( tokens[0].type ) && testContext( context.parentNode ) || context )) ) { // If seed is empty or no tokens remain, we can return early tokens.splice( i, 1 ); selector = seed.length && toSelector( tokens ); if ( !selector ) { push.apply( results, seed ); return results; } break; } } } } // Compile and execute a filtering function if one is not provided // Provide `match` to avoid retokenization if we modified the selector above ( compiled || compile( selector, match ) )( seed, context, !documentIsHTML, results, !context || rsibling.test( selector ) && testContext( context.parentNode ) || context ); return results; }; // One-time assignments // Sort stability support.sortStable = expando.split("").sort( sortOrder ).join("") === expando; // Support: Chrome 14-35+ // Always assume duplicates if they aren't passed to the comparison function support.detectDuplicates = !!hasDuplicate; // Initialize against the default document setDocument(); // Support: Webkit<537.32 - Safari 6.0.3/Chrome 25 (fixed in Chrome 27) // Detached nodes confoundingly follow *each other* support.sortDetached = assert(function( el ) { // Should return 1, but returns 4 (following) return el.compareDocumentPosition( document.createElement("fieldset") ) & 1; }); // Support: IE<8 // Prevent attribute/property "interpolation" // https://msdn.microsoft.com/en-us/library/ms536429%28VS.85%29.aspx if ( !assert(function( el ) { el.innerHTML = ""; return el.firstChild.getAttribute("href") === "#" ; }) ) { addHandle( "type|href|height|width", function( elem, name, isXML ) { if ( !isXML ) { return elem.getAttribute( name, name.toLowerCase() === "type" ? 1 : 2 ); } }); } // Support: IE<9 // Use defaultValue in place of getAttribute("value") if ( !support.attributes || !assert(function( el ) { el.innerHTML = ""; el.firstChild.setAttribute( "value", "" ); return el.firstChild.getAttribute( "value" ) === ""; }) ) { addHandle( "value", function( elem, name, isXML ) { if ( !isXML && elem.nodeName.toLowerCase() === "input" ) { return elem.defaultValue; } }); } // Support: IE<9 // Use getAttributeNode to fetch booleans when getAttribute lies if ( !assert(function( el ) { return el.getAttribute("disabled") == null; }) ) { addHandle( booleans, function( elem, name, isXML ) { var val; if ( !isXML ) { return elem[ name ] === true ? name.toLowerCase() : (val = elem.getAttributeNode( name )) && val.specified ? val.value : null; } }); } return Sizzle; })( window ); jQuery.find = Sizzle; jQuery.expr = Sizzle.selectors; // Deprecated jQuery.expr[ ":" ] = jQuery.expr.pseudos; jQuery.uniqueSort = jQuery.unique = Sizzle.uniqueSort; jQuery.text = Sizzle.getText; jQuery.isXMLDoc = Sizzle.isXML; jQuery.contains = Sizzle.contains; jQuery.escapeSelector = Sizzle.escape; var dir = function( elem, dir, until ) { var matched = [], truncate = until !== undefined; while ( ( elem = elem[ dir ] ) && elem.nodeType !== 9 ) { if ( elem.nodeType === 1 ) { if ( truncate && jQuery( elem ).is( until ) ) { break; } matched.push( elem ); } } return matched; }; var siblings = function( n, elem ) { var matched = []; for ( ; n; n = n.nextSibling ) { if ( n.nodeType === 1 && n !== elem ) { matched.push( n ); } } return matched; }; var rneedsContext = jQuery.expr.match.needsContext; function nodeName( elem, name ) { return elem.nodeName && elem.nodeName.toLowerCase() === name.toLowerCase(); }; var rsingleTag = ( /^<([a-z][^\/\0>:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i ); var risSimple = /^.[^:#\[\.,]*$/; // Implement the identical functionality for filter and not function winnow( elements, qualifier, not ) { if ( jQuery.isFunction( qualifier ) ) { return jQuery.grep( elements, function( elem, i ) { return !!qualifier.call( elem, i, elem ) !== not; } ); } // Single element if ( qualifier.nodeType ) { return jQuery.grep( elements, function( elem ) { return ( elem === qualifier ) !== not; } ); } // Arraylike of elements (jQuery, arguments, Array) if ( typeof qualifier !== "string" ) { return jQuery.grep( elements, function( elem ) { return ( indexOf.call( qualifier, elem ) > -1 ) !== not; } ); } // Simple selector that can be filtered directly, removing non-Elements if ( risSimple.test( qualifier ) ) { return jQuery.filter( qualifier, elements, not ); } // Complex selector, compare the two sets, removing non-Elements qualifier = jQuery.filter( qualifier, elements ); return jQuery.grep( elements, function( elem ) { return ( indexOf.call( qualifier, elem ) > -1 ) !== not && elem.nodeType === 1; } ); } jQuery.filter = function( expr, elems, not ) { var elem = elems[ 0 ]; if ( not ) { expr = ":not(" + expr + ")"; } if ( elems.length === 1 && elem.nodeType === 1 ) { return jQuery.find.matchesSelector( elem, expr ) ? [ elem ] : []; } return jQuery.find.matches( expr, jQuery.grep( elems, function( elem ) { return elem.nodeType === 1; } ) ); }; jQuery.fn.extend( { find: function( selector ) { var i, ret, len = this.length, self = this; if ( typeof selector !== "string" ) { return this.pushStack( jQuery( selector ).filter( function() { for ( i = 0; i < len; i++ ) { if ( jQuery.contains( self[ i ], this ) ) { return true; } } } ) ); } ret = this.pushStack( [] ); for ( i = 0; i < len; i++ ) { jQuery.find( selector, self[ i ], ret ); } return len > 1 ? jQuery.uniqueSort( ret ) : ret; }, filter: function( selector ) { return this.pushStack( winnow( this, selector || [], false ) ); }, not: function( selector ) { return this.pushStack( winnow( this, selector || [], true ) ); }, is: function( selector ) { return !!winnow( this, // If this is a positional/relative selector, check membership in the returned set // so $("p:first").is("p:last") won't return true for a doc with two "p". typeof selector === "string" && rneedsContext.test( selector ) ? jQuery( selector ) : selector || [], false ).length; } } ); // Initialize a jQuery object // A central reference to the root jQuery(document) var rootjQuery, // A simple way to check for HTML strings // Prioritize #id over to avoid XSS via location.hash (#9521) // Strict HTML recognition (#11290: must start with <) // Shortcut simple #id case for speed rquickExpr = /^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]+))$/, init = jQuery.fn.init = function( selector, context, root ) { var match, elem; // HANDLE: $(""), $(null), $(undefined), $(false) if ( !selector ) { return this; } // Method init() accepts an alternate rootjQuery // so migrate can support jQuery.sub (gh-2101) root = root || rootjQuery; // Handle HTML strings if ( typeof selector === "string" ) { if ( selector[ 0 ] === "<" && selector[ selector.length - 1 ] === ">" && selector.length >= 3 ) { // Assume that strings that start and end with <> are HTML and skip the regex check match = [ null, selector, null ]; } else { match = rquickExpr.exec( selector ); } // Match html or make sure no context is specified for #id if ( match && ( match[ 1 ] || !context ) ) { // HANDLE: $(html) -> $(array) if ( match[ 1 ] ) { context = context instanceof jQuery ? context[ 0 ] : context; // Option to run scripts is true for back-compat // Intentionally let the error be thrown if parseHTML is not present jQuery.merge( this, jQuery.parseHTML( match[ 1 ], context && context.nodeType ? context.ownerDocument || context : document, true ) ); // HANDLE: $(html, props) if ( rsingleTag.test( match[ 1 ] ) && jQuery.isPlainObject( context ) ) { for ( match in context ) { // Properties of context are called as methods if possible if ( jQuery.isFunction( this[ match ] ) ) { this[ match ]( context[ match ] ); // ...and otherwise set as attributes } else { this.attr( match, context[ match ] ); } } } return this; // HANDLE: $(#id) } else { elem = document.getElementById( match[ 2 ] ); if ( elem ) { // Inject the element directly into the jQuery object this[ 0 ] = elem; this.length = 1; } return this; } // HANDLE: $(expr, $(...)) } else if ( !context || context.jquery ) { return ( context || root ).find( selector ); // HANDLE: $(expr, context) // (which is just equivalent to: $(context).find(expr) } else { return this.constructor( context ).find( selector ); } // HANDLE: $(DOMElement) } else if ( selector.nodeType ) { this[ 0 ] = selector; this.length = 1; return this; // HANDLE: $(function) // Shortcut for document ready } else if ( jQuery.isFunction( selector ) ) { return root.ready !== undefined ? root.ready( selector ) : // Execute immediately if ready is not present selector( jQuery ); } return jQuery.makeArray( selector, this ); }; // Give the init function the jQuery prototype for later instantiation init.prototype = jQuery.fn; // Initialize central reference rootjQuery = jQuery( document ); var rparentsprev = /^(?:parents|prev(?:Until|All))/, // Methods guaranteed to produce a unique set when starting from a unique set guaranteedUnique = { children: true, contents: true, next: true, prev: true }; jQuery.fn.extend( { has: function( target ) { var targets = jQuery( target, this ), l = targets.length; return this.filter( function() { var i = 0; for ( ; i < l; i++ ) { if ( jQuery.contains( this, targets[ i ] ) ) { return true; } } } ); }, closest: function( selectors, context ) { var cur, i = 0, l = this.length, matched = [], targets = typeof selectors !== "string" && jQuery( selectors ); // Positional selectors never match, since there's no _selection_ context if ( !rneedsContext.test( selectors ) ) { for ( ; i < l; i++ ) { for ( cur = this[ i ]; cur && cur !== context; cur = cur.parentNode ) { // Always skip document fragments if ( cur.nodeType < 11 && ( targets ? targets.index( cur ) > -1 : // Don't pass non-elements to Sizzle cur.nodeType === 1 && jQuery.find.matchesSelector( cur, selectors ) ) ) { matched.push( cur ); break; } } } } return this.pushStack( matched.length > 1 ? jQuery.uniqueSort( matched ) : matched ); }, // Determine the position of an element within the set index: function( elem ) { // No argument, return index in parent if ( !elem ) { return ( this[ 0 ] && this[ 0 ].parentNode ) ? this.first().prevAll().length : -1; } // Index in selector if ( typeof elem === "string" ) { return indexOf.call( jQuery( elem ), this[ 0 ] ); } // Locate the position of the desired element return indexOf.call( this, // If it receives a jQuery object, the first element is used elem.jquery ? elem[ 0 ] : elem ); }, add: function( selector, context ) { return this.pushStack( jQuery.uniqueSort( jQuery.merge( this.get(), jQuery( selector, context ) ) ) ); }, addBack: function( selector ) { return this.add( selector == null ? this.prevObject : this.prevObject.filter( selector ) ); } } ); function sibling( cur, dir ) { while ( ( cur = cur[ dir ] ) && cur.nodeType !== 1 ) {} return cur; } jQuery.each( { parent: function( elem ) { var parent = elem.parentNode; return parent && parent.nodeType !== 11 ? parent : null; }, parents: function( elem ) { return dir( elem, "parentNode" ); }, parentsUntil: function( elem, i, until ) { return dir( elem, "parentNode", until ); }, next: function( elem ) { return sibling( elem, "nextSibling" ); }, prev: function( elem ) { return sibling( elem, "previousSibling" ); }, nextAll: function( elem ) { return dir( elem, "nextSibling" ); }, prevAll: function( elem ) { return dir( elem, "previousSibling" ); }, nextUntil: function( elem, i, until ) { return dir( elem, "nextSibling", until ); }, prevUntil: function( elem, i, until ) { return dir( elem, "previousSibling", until ); }, siblings: function( elem ) { return siblings( ( elem.parentNode || {} ).firstChild, elem ); }, children: function( elem ) { return siblings( elem.firstChild ); }, contents: function( elem ) { if ( nodeName( elem, "iframe" ) ) { return elem.contentDocument; } // Support: IE 9 - 11 only, iOS 7 only, Android Browser <=4.3 only // Treat the template element as a regular one in browsers that // don't support it. if ( nodeName( elem, "template" ) ) { elem = elem.content || elem; } return jQuery.merge( [], elem.childNodes ); } }, function( name, fn ) { jQuery.fn[ name ] = function( until, selector ) { var matched = jQuery.map( this, fn, until ); if ( name.slice( -5 ) !== "Until" ) { selector = until; } if ( selector && typeof selector === "string" ) { matched = jQuery.filter( selector, matched ); } if ( this.length > 1 ) { // Remove duplicates if ( !guaranteedUnique[ name ] ) { jQuery.uniqueSort( matched ); } // Reverse order for parents* and prev-derivatives if ( rparentsprev.test( name ) ) { matched.reverse(); } } return this.pushStack( matched ); }; } ); var rnothtmlwhite = ( /[^\x20\t\r\n\f]+/g ); // Convert String-formatted options into Object-formatted ones function createOptions( options ) { var object = {}; jQuery.each( options.match( rnothtmlwhite ) || [], function( _, flag ) { object[ flag ] = true; } ); return object; } /* * Create a callback list using the following parameters: * * options: an optional list of space-separated options that will change how * the callback list behaves or a more traditional option object * * By default a callback list will act like an event callback list and can be * "fired" multiple times. * * Possible options: * * once: will ensure the callback list can only be fired once (like a Deferred) * * memory: will keep track of previous values and will call any callback added * after the list has been fired right away with the latest "memorized" * values (like a Deferred) * * unique: will ensure a callback can only be added once (no duplicate in the list) * * stopOnFalse: interrupt callings when a callback returns false * */ jQuery.Callbacks = function( options ) { // Convert options from String-formatted to Object-formatted if needed // (we check in cache first) options = typeof options === "string" ? createOptions( options ) : jQuery.extend( {}, options ); var // Flag to know if list is currently firing firing, // Last fire value for non-forgettable lists memory, // Flag to know if list was already fired fired, // Flag to prevent firing locked, // Actual callback list list = [], // Queue of execution data for repeatable lists queue = [], // Index of currently firing callback (modified by add/remove as needed) firingIndex = -1, // Fire callbacks fire = function() { // Enforce single-firing locked = locked || options.once; // Execute callbacks for all pending executions, // respecting firingIndex overrides and runtime changes fired = firing = true; for ( ; queue.length; firingIndex = -1 ) { memory = queue.shift(); while ( ++firingIndex < list.length ) { // Run callback and check for early termination if ( list[ firingIndex ].apply( memory[ 0 ], memory[ 1 ] ) === false && options.stopOnFalse ) { // Jump to end and forget the data so .add doesn't re-fire firingIndex = list.length; memory = false; } } } // Forget the data if we're done with it if ( !options.memory ) { memory = false; } firing = false; // Clean up if we're done firing for good if ( locked ) { // Keep an empty list if we have data for future add calls if ( memory ) { list = []; // Otherwise, this object is spent } else { list = ""; } } }, // Actual Callbacks object self = { // Add a callback or a collection of callbacks to the list add: function() { if ( list ) { // If we have memory from a past run, we should fire after adding if ( memory && !firing ) { firingIndex = list.length - 1; queue.push( memory ); } ( function add( args ) { jQuery.each( args, function( _, arg ) { if ( jQuery.isFunction( arg ) ) { if ( !options.unique || !self.has( arg ) ) { list.push( arg ); } } else if ( arg && arg.length && jQuery.type( arg ) !== "string" ) { // Inspect recursively add( arg ); } } ); } )( arguments ); if ( memory && !firing ) { fire(); } } return this; }, // Remove a callback from the list remove: function() { jQuery.each( arguments, function( _, arg ) { var index; while ( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) { list.splice( index, 1 ); // Handle firing indexes if ( index <= firingIndex ) { firingIndex--; } } } ); return this; }, // Check if a given callback is in the list. // If no argument is given, return whether or not list has callbacks attached. has: function( fn ) { return fn ? jQuery.inArray( fn, list ) > -1 : list.length > 0; }, // Remove all callbacks from the list empty: function() { if ( list ) { list = []; } return this; }, // Disable .fire and .add // Abort any current/pending executions // Clear all callbacks and values disable: function() { locked = queue = []; list = memory = ""; return this; }, disabled: function() { return !list; }, // Disable .fire // Also disable .add unless we have memory (since it would have no effect) // Abort any pending executions lock: function() { locked = queue = []; if ( !memory && !firing ) { list = memory = ""; } return this; }, locked: function() { return !!locked; }, // Call all callbacks with the given context and arguments fireWith: function( context, args ) { if ( !locked ) { args = args || []; args = [ context, args.slice ? args.slice() : args ]; queue.push( args ); if ( !firing ) { fire(); } } return this; }, // Call all the callbacks with the given arguments fire: function() { self.fireWith( this, arguments ); return this; }, // To know if the callbacks have already been called at least once fired: function() { return !!fired; } }; return self; }; function Identity( v ) { return v; } function Thrower( ex ) { throw ex; } function adoptValue( value, resolve, reject, noValue ) { var method; try { // Check for promise aspect first to privilege synchronous behavior if ( value && jQuery.isFunction( ( method = value.promise ) ) ) { method.call( value ).done( resolve ).fail( reject ); // Other thenables } else if ( value && jQuery.isFunction( ( method = value.then ) ) ) { method.call( value, resolve, reject ); // Other non-thenables } else { // Control `resolve` arguments by letting Array#slice cast boolean `noValue` to integer: // * false: [ value ].slice( 0 ) => resolve( value ) // * true: [ value ].slice( 1 ) => resolve() resolve.apply( undefined, [ value ].slice( noValue ) ); } // For Promises/A+, convert exceptions into rejections // Since jQuery.when doesn't unwrap thenables, we can skip the extra checks appearing in // Deferred#then to conditionally suppress rejection. } catch ( value ) { // Support: Android 4.0 only // Strict mode functions invoked without .call/.apply get global-object context reject.apply( undefined, [ value ] ); } } jQuery.extend( { Deferred: function( func ) { var tuples = [ // action, add listener, callbacks, // ... .then handlers, argument index, [final state] [ "notify", "progress", jQuery.Callbacks( "memory" ), jQuery.Callbacks( "memory" ), 2 ], [ "resolve", "done", jQuery.Callbacks( "once memory" ), jQuery.Callbacks( "once memory" ), 0, "resolved" ], [ "reject", "fail", jQuery.Callbacks( "once memory" ), jQuery.Callbacks( "once memory" ), 1, "rejected" ] ], state = "pending", promise = { state: function() { return state; }, always: function() { deferred.done( arguments ).fail( arguments ); return this; }, "catch": function( fn ) { return promise.then( null, fn ); }, // Keep pipe for back-compat pipe: function( /* fnDone, fnFail, fnProgress */ ) { var fns = arguments; return jQuery.Deferred( function( newDefer ) { jQuery.each( tuples, function( i, tuple ) { // Map tuples (progress, done, fail) to arguments (done, fail, progress) var fn = jQuery.isFunction( fns[ tuple[ 4 ] ] ) && fns[ tuple[ 4 ] ]; // deferred.progress(function() { bind to newDefer or newDefer.notify }) // deferred.done(function() { bind to newDefer or newDefer.resolve }) // deferred.fail(function() { bind to newDefer or newDefer.reject }) deferred[ tuple[ 1 ] ]( function() { var returned = fn && fn.apply( this, arguments ); if ( returned && jQuery.isFunction( returned.promise ) ) { returned.promise() .progress( newDefer.notify ) .done( newDefer.resolve ) .fail( newDefer.reject ); } else { newDefer[ tuple[ 0 ] + "With" ]( this, fn ? [ returned ] : arguments ); } } ); } ); fns = null; } ).promise(); }, then: function( onFulfilled, onRejected, onProgress ) { var maxDepth = 0; function resolve( depth, deferred, handler, special ) { return function() { var that = this, args = arguments, mightThrow = function() { var returned, then; // Support: Promises/A+ section 2.3.3.3.3 // https://promisesaplus.com/#point-59 // Ignore double-resolution attempts if ( depth < maxDepth ) { return; } returned = handler.apply( that, args ); // Support: Promises/A+ section 2.3.1 // https://promisesaplus.com/#point-48 if ( returned === deferred.promise() ) { throw new TypeError( "Thenable self-resolution" ); } // Support: Promises/A+ sections 2.3.3.1, 3.5 // https://promisesaplus.com/#point-54 // https://promisesaplus.com/#point-75 // Retrieve `then` only once then = returned && // Support: Promises/A+ section 2.3.4 // https://promisesaplus.com/#point-64 // Only check objects and functions for thenability ( typeof returned === "object" || typeof returned === "function" ) && returned.then; // Handle a returned thenable if ( jQuery.isFunction( then ) ) { // Special processors (notify) just wait for resolution if ( special ) { then.call( returned, resolve( maxDepth, deferred, Identity, special ), resolve( maxDepth, deferred, Thrower, special ) ); // Normal processors (resolve) also hook into progress } else { // ...and disregard older resolution values maxDepth++; then.call( returned, resolve( maxDepth, deferred, Identity, special ), resolve( maxDepth, deferred, Thrower, special ), resolve( maxDepth, deferred, Identity, deferred.notifyWith ) ); } // Handle all other returned values } else { // Only substitute handlers pass on context // and multiple values (non-spec behavior) if ( handler !== Identity ) { that = undefined; args = [ returned ]; } // Process the value(s) // Default process is resolve ( special || deferred.resolveWith )( that, args ); } }, // Only normal processors (resolve) catch and reject exceptions process = special ? mightThrow : function() { try { mightThrow(); } catch ( e ) { if ( jQuery.Deferred.exceptionHook ) { jQuery.Deferred.exceptionHook( e, process.stackTrace ); } // Support: Promises/A+ section 2.3.3.3.4.1 // https://promisesaplus.com/#point-61 // Ignore post-resolution exceptions if ( depth + 1 >= maxDepth ) { // Only substitute handlers pass on context // and multiple values (non-spec behavior) if ( handler !== Thrower ) { that = undefined; args = [ e ]; } deferred.rejectWith( that, args ); } } }; // Support: Promises/A+ section 2.3.3.3.1 // https://promisesaplus.com/#point-57 // Re-resolve promises immediately to dodge false rejection from // subsequent errors if ( depth ) { process(); } else { // Call an optional hook to record the stack, in case of exception // since it's otherwise lost when execution goes async if ( jQuery.Deferred.getStackHook ) { process.stackTrace = jQuery.Deferred.getStackHook(); } window.setTimeout( process ); } }; } return jQuery.Deferred( function( newDefer ) { // progress_handlers.add( ... ) tuples[ 0 ][ 3 ].add( resolve( 0, newDefer, jQuery.isFunction( onProgress ) ? onProgress : Identity, newDefer.notifyWith ) ); // fulfilled_handlers.add( ... ) tuples[ 1 ][ 3 ].add( resolve( 0, newDefer, jQuery.isFunction( onFulfilled ) ? onFulfilled : Identity ) ); // rejected_handlers.add( ... ) tuples[ 2 ][ 3 ].add( resolve( 0, newDefer, jQuery.isFunction( onRejected ) ? onRejected : Thrower ) ); } ).promise(); }, // Get a promise for this deferred // If obj is provided, the promise aspect is added to the object promise: function( obj ) { return obj != null ? jQuery.extend( obj, promise ) : promise; } }, deferred = {}; // Add list-specific methods jQuery.each( tuples, function( i, tuple ) { var list = tuple[ 2 ], stateString = tuple[ 5 ]; // promise.progress = list.add // promise.done = list.add // promise.fail = list.add promise[ tuple[ 1 ] ] = list.add; // Handle state if ( stateString ) { list.add( function() { // state = "resolved" (i.e., fulfilled) // state = "rejected" state = stateString; }, // rejected_callbacks.disable // fulfilled_callbacks.disable tuples[ 3 - i ][ 2 ].disable, // progress_callbacks.lock tuples[ 0 ][ 2 ].lock ); } // progress_handlers.fire // fulfilled_handlers.fire // rejected_handlers.fire list.add( tuple[ 3 ].fire ); // deferred.notify = function() { deferred.notifyWith(...) } // deferred.resolve = function() { deferred.resolveWith(...) } // deferred.reject = function() { deferred.rejectWith(...) } deferred[ tuple[ 0 ] ] = function() { deferred[ tuple[ 0 ] + "With" ]( this === deferred ? undefined : this, arguments ); return this; }; // deferred.notifyWith = list.fireWith // deferred.resolveWith = list.fireWith // deferred.rejectWith = list.fireWith deferred[ tuple[ 0 ] + "With" ] = list.fireWith; } ); // Make the deferred a promise promise.promise( deferred ); // Call given func if any if ( func ) { func.call( deferred, deferred ); } // All done! return deferred; }, // Deferred helper when: function( singleValue ) { var // count of uncompleted subordinates remaining = arguments.length, // count of unprocessed arguments i = remaining, // subordinate fulfillment data resolveContexts = Array( i ), resolveValues = slice.call( arguments ), // the master Deferred master = jQuery.Deferred(), // subordinate callback factory updateFunc = function( i ) { return function( value ) { resolveContexts[ i ] = this; resolveValues[ i ] = arguments.length > 1 ? slice.call( arguments ) : value; if ( !( --remaining ) ) { master.resolveWith( resolveContexts, resolveValues ); } }; }; // Single- and empty arguments are adopted like Promise.resolve if ( remaining <= 1 ) { adoptValue( singleValue, master.done( updateFunc( i ) ).resolve, master.reject, !remaining ); // Use .then() to unwrap secondary thenables (cf. gh-3000) if ( master.state() === "pending" || jQuery.isFunction( resolveValues[ i ] && resolveValues[ i ].then ) ) { return master.then(); } } // Multiple arguments are aggregated like Promise.all array elements while ( i-- ) { adoptValue( resolveValues[ i ], updateFunc( i ), master.reject ); } return master.promise(); } } ); // These usually indicate a programmer mistake during development, // warn about them ASAP rather than swallowing them by default. var rerrorNames = /^(Eval|Internal|Range|Reference|Syntax|Type|URI)Error$/; jQuery.Deferred.exceptionHook = function( error, stack ) { // Support: IE 8 - 9 only // Console exists when dev tools are open, which can happen at any time if ( window.console && window.console.warn && error && rerrorNames.test( error.name ) ) { window.console.warn( "jQuery.Deferred exception: " + error.message, error.stack, stack ); } }; jQuery.readyException = function( error ) { window.setTimeout( function() { throw error; } ); }; // The deferred used on DOM ready var readyList = jQuery.Deferred(); jQuery.fn.ready = function( fn ) { readyList .then( fn ) // Wrap jQuery.readyException in a function so that the lookup // happens at the time of error handling instead of callback // registration. .catch( function( error ) { jQuery.readyException( error ); } ); return this; }; jQuery.extend( { // Is the DOM ready to be used? Set to true once it occurs. isReady: false, // A counter to track how many items to wait for before // the ready event fires. See #6781 readyWait: 1, // Handle when the DOM is ready ready: function( wait ) { // Abort if there are pending holds or we're already ready if ( wait === true ? --jQuery.readyWait : jQuery.isReady ) { return; } // Remember that the DOM is ready jQuery.isReady = true; // If a normal DOM Ready event fired, decrement, and wait if need be if ( wait !== true && --jQuery.readyWait > 0 ) { return; } // If there are functions bound, to execute readyList.resolveWith( document, [ jQuery ] ); } } ); jQuery.ready.then = readyList.then; // The ready event handler and self cleanup method function completed() { document.removeEventListener( "DOMContentLoaded", completed ); window.removeEventListener( "load", completed ); jQuery.ready(); } // Catch cases where $(document).ready() is called // after the browser event has already occurred. // Support: IE <=9 - 10 only // Older IE sometimes signals "interactive" too soon if ( document.readyState === "complete" || ( document.readyState !== "loading" && !document.documentElement.doScroll ) ) { // Handle it asynchronously to allow scripts the opportunity to delay ready window.setTimeout( jQuery.ready ); } else { // Use the handy event callback document.addEventListener( "DOMContentLoaded", completed ); // A fallback to window.onload, that will always work window.addEventListener( "load", completed ); } // Multifunctional method to get and set values of a collection // The value/s can optionally be executed if it's a function var access = function( elems, fn, key, value, chainable, emptyGet, raw ) { var i = 0, len = elems.length, bulk = key == null; // Sets many values if ( jQuery.type( key ) === "object" ) { chainable = true; for ( i in key ) { access( elems, fn, i, key[ i ], true, emptyGet, raw ); } // Sets one value } else if ( value !== undefined ) { chainable = true; if ( !jQuery.isFunction( value ) ) { raw = true; } if ( bulk ) { // Bulk operations run against the entire set if ( raw ) { fn.call( elems, value ); fn = null; // ...except when executing function values } else { bulk = fn; fn = function( elem, key, value ) { return bulk.call( jQuery( elem ), value ); }; } } if ( fn ) { for ( ; i < len; i++ ) { fn( elems[ i ], key, raw ? value : value.call( elems[ i ], i, fn( elems[ i ], key ) ) ); } } } if ( chainable ) { return elems; } // Gets if ( bulk ) { return fn.call( elems ); } return len ? fn( elems[ 0 ], key ) : emptyGet; }; var acceptData = function( owner ) { // Accepts only: // - Node // - Node.ELEMENT_NODE // - Node.DOCUMENT_NODE // - Object // - Any return owner.nodeType === 1 || owner.nodeType === 9 || !( +owner.nodeType ); }; function Data() { this.expando = jQuery.expando + Data.uid++; } Data.uid = 1; Data.prototype = { cache: function( owner ) { // Check if the owner object already has a cache var value = owner[ this.expando ]; // If not, create one if ( !value ) { value = {}; // We can accept data for non-element nodes in modern browsers, // but we should not, see #8335. // Always return an empty object. if ( acceptData( owner ) ) { // If it is a node unlikely to be stringify-ed or looped over // use plain assignment if ( owner.nodeType ) { owner[ this.expando ] = value; // Otherwise secure it in a non-enumerable property // configurable must be true to allow the property to be // deleted when data is removed } else { Object.defineProperty( owner, this.expando, { value: value, configurable: true } ); } } } return value; }, set: function( owner, data, value ) { var prop, cache = this.cache( owner ); // Handle: [ owner, key, value ] args // Always use camelCase key (gh-2257) if ( typeof data === "string" ) { cache[ jQuery.camelCase( data ) ] = value; // Handle: [ owner, { properties } ] args } else { // Copy the properties one-by-one to the cache object for ( prop in data ) { cache[ jQuery.camelCase( prop ) ] = data[ prop ]; } } return cache; }, get: function( owner, key ) { return key === undefined ? this.cache( owner ) : // Always use camelCase key (gh-2257) owner[ this.expando ] && owner[ this.expando ][ jQuery.camelCase( key ) ]; }, access: function( owner, key, value ) { // In cases where either: // // 1. No key was specified // 2. A string key was specified, but no value provided // // Take the "read" path and allow the get method to determine // which value to return, respectively either: // // 1. The entire cache object // 2. The data stored at the key // if ( key === undefined || ( ( key && typeof key === "string" ) && value === undefined ) ) { return this.get( owner, key ); } // When the key is not a string, or both a key and value // are specified, set or extend (existing objects) with either: // // 1. An object of properties // 2. A key and value // this.set( owner, key, value ); // Since the "set" path can have two possible entry points // return the expected data based on which path was taken[*] return value !== undefined ? value : key; }, remove: function( owner, key ) { var i, cache = owner[ this.expando ]; if ( cache === undefined ) { return; } if ( key !== undefined ) { // Support array or space separated string of keys if ( Array.isArray( key ) ) { // If key is an array of keys... // We always set camelCase keys, so remove that. key = key.map( jQuery.camelCase ); } else { key = jQuery.camelCase( key ); // If a key with the spaces exists, use it. // Otherwise, create an array by matching non-whitespace key = key in cache ? [ key ] : ( key.match( rnothtmlwhite ) || [] ); } i = key.length; while ( i-- ) { delete cache[ key[ i ] ]; } } // Remove the expando if there's no more data if ( key === undefined || jQuery.isEmptyObject( cache ) ) { // Support: Chrome <=35 - 45 // Webkit & Blink performance suffers when deleting properties // from DOM nodes, so set to undefined instead // https://bugs.chromium.org/p/chromium/issues/detail?id=378607 (bug restricted) if ( owner.nodeType ) { owner[ this.expando ] = undefined; } else { delete owner[ this.expando ]; } } }, hasData: function( owner ) { var cache = owner[ this.expando ]; return cache !== undefined && !jQuery.isEmptyObject( cache ); } }; var dataPriv = new Data(); var dataUser = new Data(); // Implementation Summary // // 1. Enforce API surface and semantic compatibility with 1.9.x branch // 2. Improve the module's maintainability by reducing the storage // paths to a single mechanism. // 3. Use the same single mechanism to support "private" and "user" data. // 4. _Never_ expose "private" data to user code (TODO: Drop _data, _removeData) // 5. Avoid exposing implementation details on user objects (eg. expando properties) // 6. Provide a clear path for implementation upgrade to WeakMap in 2014 var rbrace = /^(?:\{[\w\W]*\}|\[[\w\W]*\])$/, rmultiDash = /[A-Z]/g; function getData( data ) { if ( data === "true" ) { return true; } if ( data === "false" ) { return false; } if ( data === "null" ) { return null; } // Only convert to a number if it doesn't change the string if ( data === +data + "" ) { return +data; } if ( rbrace.test( data ) ) { return JSON.parse( data ); } return data; } function dataAttr( elem, key, data ) { var name; // If nothing was found internally, try to fetch any // data from the HTML5 data-* attribute if ( data === undefined && elem.nodeType === 1 ) { name = "data-" + key.replace( rmultiDash, "-$&" ).toLowerCase(); data = elem.getAttribute( name ); if ( typeof data === "string" ) { try { data = getData( data ); } catch ( e ) {} // Make sure we set the data so it isn't changed later dataUser.set( elem, key, data ); } else { data = undefined; } } return data; } jQuery.extend( { hasData: function( elem ) { return dataUser.hasData( elem ) || dataPriv.hasData( elem ); }, data: function( elem, name, data ) { return dataUser.access( elem, name, data ); }, removeData: function( elem, name ) { dataUser.remove( elem, name ); }, // TODO: Now that all calls to _data and _removeData have been replaced // with direct calls to dataPriv methods, these can be deprecated. _data: function( elem, name, data ) { return dataPriv.access( elem, name, data ); }, _removeData: function( elem, name ) { dataPriv.remove( elem, name ); } } ); jQuery.fn.extend( { data: function( key, value ) { var i, name, data, elem = this[ 0 ], attrs = elem && elem.attributes; // Gets all values if ( key === undefined ) { if ( this.length ) { data = dataUser.get( elem ); if ( elem.nodeType === 1 && !dataPriv.get( elem, "hasDataAttrs" ) ) { i = attrs.length; while ( i-- ) { // Support: IE 11 only // The attrs elements can be null (#14894) if ( attrs[ i ] ) { name = attrs[ i ].name; if ( name.indexOf( "data-" ) === 0 ) { name = jQuery.camelCase( name.slice( 5 ) ); dataAttr( elem, name, data[ name ] ); } } } dataPriv.set( elem, "hasDataAttrs", true ); } } return data; } // Sets multiple values if ( typeof key === "object" ) { return this.each( function() { dataUser.set( this, key ); } ); } return access( this, function( value ) { var data; // The calling jQuery object (element matches) is not empty // (and therefore has an element appears at this[ 0 ]) and the // `value` parameter was not undefined. An empty jQuery object // will result in `undefined` for elem = this[ 0 ] which will // throw an exception if an attempt to read a data cache is made. if ( elem && value === undefined ) { // Attempt to get data from the cache // The key will always be camelCased in Data data = dataUser.get( elem, key ); if ( data !== undefined ) { return data; } // Attempt to "discover" the data in // HTML5 custom data-* attrs data = dataAttr( elem, key ); if ( data !== undefined ) { return data; } // We tried really hard, but the data doesn't exist. return; } // Set the data... this.each( function() { // We always store the camelCased key dataUser.set( this, key, value ); } ); }, null, value, arguments.length > 1, null, true ); }, removeData: function( key ) { return this.each( function() { dataUser.remove( this, key ); } ); } } ); jQuery.extend( { queue: function( elem, type, data ) { var queue; if ( elem ) { type = ( type || "fx" ) + "queue"; queue = dataPriv.get( elem, type ); // Speed up dequeue by getting out quickly if this is just a lookup if ( data ) { if ( !queue || Array.isArray( data ) ) { queue = dataPriv.access( elem, type, jQuery.makeArray( data ) ); } else { queue.push( data ); } } return queue || []; } }, dequeue: function( elem, type ) { type = type || "fx"; var queue = jQuery.queue( elem, type ), startLength = queue.length, fn = queue.shift(), hooks = jQuery._queueHooks( elem, type ), next = function() { jQuery.dequeue( elem, type ); }; // If the fx queue is dequeued, always remove the progress sentinel if ( fn === "inprogress" ) { fn = queue.shift(); startLength--; } if ( fn ) { // Add a progress sentinel to prevent the fx queue from being // automatically dequeued if ( type === "fx" ) { queue.unshift( "inprogress" ); } // Clear up the last queue stop function delete hooks.stop; fn.call( elem, next, hooks ); } if ( !startLength && hooks ) { hooks.empty.fire(); } }, // Not public - generate a queueHooks object, or return the current one _queueHooks: function( elem, type ) { var key = type + "queueHooks"; return dataPriv.get( elem, key ) || dataPriv.access( elem, key, { empty: jQuery.Callbacks( "once memory" ).add( function() { dataPriv.remove( elem, [ type + "queue", key ] ); } ) } ); } } ); jQuery.fn.extend( { queue: function( type, data ) { var setter = 2; if ( typeof type !== "string" ) { data = type; type = "fx"; setter--; } if ( arguments.length < setter ) { return jQuery.queue( this[ 0 ], type ); } return data === undefined ? this : this.each( function() { var queue = jQuery.queue( this, type, data ); // Ensure a hooks for this queue jQuery._queueHooks( this, type ); if ( type === "fx" && queue[ 0 ] !== "inprogress" ) { jQuery.dequeue( this, type ); } } ); }, dequeue: function( type ) { return this.each( function() { jQuery.dequeue( this, type ); } ); }, clearQueue: function( type ) { return this.queue( type || "fx", [] ); }, // Get a promise resolved when queues of a certain type // are emptied (fx is the type by default) promise: function( type, obj ) { var tmp, count = 1, defer = jQuery.Deferred(), elements = this, i = this.length, resolve = function() { if ( !( --count ) ) { defer.resolveWith( elements, [ elements ] ); } }; if ( typeof type !== "string" ) { obj = type; type = undefined; } type = type || "fx"; while ( i-- ) { tmp = dataPriv.get( elements[ i ], type + "queueHooks" ); if ( tmp && tmp.empty ) { count++; tmp.empty.add( resolve ); } } resolve(); return defer.promise( obj ); } } ); var pnum = ( /[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/ ).source; var rcssNum = new RegExp( "^(?:([+-])=|)(" + pnum + ")([a-z%]*)$", "i" ); var cssExpand = [ "Top", "Right", "Bottom", "Left" ]; var isHiddenWithinTree = function( elem, el ) { // isHiddenWithinTree might be called from jQuery#filter function; // in that case, element will be second argument elem = el || elem; // Inline style trumps all return elem.style.display === "none" || elem.style.display === "" && // Otherwise, check computed style // Support: Firefox <=43 - 45 // Disconnected elements can have computed display: none, so first confirm that elem is // in the document. jQuery.contains( elem.ownerDocument, elem ) && jQuery.css( elem, "display" ) === "none"; }; var swap = function( elem, options, callback, args ) { var ret, name, old = {}; // Remember the old values, and insert the new ones for ( name in options ) { old[ name ] = elem.style[ name ]; elem.style[ name ] = options[ name ]; } ret = callback.apply( elem, args || [] ); // Revert the old values for ( name in options ) { elem.style[ name ] = old[ name ]; } return ret; }; function adjustCSS( elem, prop, valueParts, tween ) { var adjusted, scale = 1, maxIterations = 20, currentValue = tween ? function() { return tween.cur(); } : function() { return jQuery.css( elem, prop, "" ); }, initial = currentValue(), unit = valueParts && valueParts[ 3 ] || ( jQuery.cssNumber[ prop ] ? "" : "px" ), // Starting value computation is required for potential unit mismatches initialInUnit = ( jQuery.cssNumber[ prop ] || unit !== "px" && +initial ) && rcssNum.exec( jQuery.css( elem, prop ) ); if ( initialInUnit && initialInUnit[ 3 ] !== unit ) { // Trust units reported by jQuery.css unit = unit || initialInUnit[ 3 ]; // Make sure we update the tween properties later on valueParts = valueParts || []; // Iteratively approximate from a nonzero starting point initialInUnit = +initial || 1; do { // If previous iteration zeroed out, double until we get *something*. // Use string for doubling so we don't accidentally see scale as unchanged below scale = scale || ".5"; // Adjust and apply initialInUnit = initialInUnit / scale; jQuery.style( elem, prop, initialInUnit + unit ); // Update scale, tolerating zero or NaN from tween.cur() // Break the loop if scale is unchanged or perfect, or if we've just had enough. } while ( scale !== ( scale = currentValue() / initial ) && scale !== 1 && --maxIterations ); } if ( valueParts ) { initialInUnit = +initialInUnit || +initial || 0; // Apply relative offset (+=/-=) if specified adjusted = valueParts[ 1 ] ? initialInUnit + ( valueParts[ 1 ] + 1 ) * valueParts[ 2 ] : +valueParts[ 2 ]; if ( tween ) { tween.unit = unit; tween.start = initialInUnit; tween.end = adjusted; } } return adjusted; } var defaultDisplayMap = {}; function getDefaultDisplay( elem ) { var temp, doc = elem.ownerDocument, nodeName = elem.nodeName, display = defaultDisplayMap[ nodeName ]; if ( display ) { return display; } temp = doc.body.appendChild( doc.createElement( nodeName ) ); display = jQuery.css( temp, "display" ); temp.parentNode.removeChild( temp ); if ( display === "none" ) { display = "block"; } defaultDisplayMap[ nodeName ] = display; return display; } function showHide( elements, show ) { var display, elem, values = [], index = 0, length = elements.length; // Determine new display value for elements that need to change for ( ; index < length; index++ ) { elem = elements[ index ]; if ( !elem.style ) { continue; } display = elem.style.display; if ( show ) { // Since we force visibility upon cascade-hidden elements, an immediate (and slow) // check is required in this first loop unless we have a nonempty display value (either // inline or about-to-be-restored) if ( display === "none" ) { values[ index ] = dataPriv.get( elem, "display" ) || null; if ( !values[ index ] ) { elem.style.display = ""; } } if ( elem.style.display === "" && isHiddenWithinTree( elem ) ) { values[ index ] = getDefaultDisplay( elem ); } } else { if ( display !== "none" ) { values[ index ] = "none"; // Remember what we're overwriting dataPriv.set( elem, "display", display ); } } } // Set the display of the elements in a second loop to avoid constant reflow for ( index = 0; index < length; index++ ) { if ( values[ index ] != null ) { elements[ index ].style.display = values[ index ]; } } return elements; } jQuery.fn.extend( { show: function() { return showHide( this, true ); }, hide: function() { return showHide( this ); }, toggle: function( state ) { if ( typeof state === "boolean" ) { return state ? this.show() : this.hide(); } return this.each( function() { if ( isHiddenWithinTree( this ) ) { jQuery( this ).show(); } else { jQuery( this ).hide(); } } ); } } ); var rcheckableType = ( /^(?:checkbox|radio)$/i ); var rtagName = ( /<([a-z][^\/\0>\x20\t\r\n\f]+)/i ); var rscriptType = ( /^$|\/(?:java|ecma)script/i ); // We have to close these tags to support XHTML (#13200) var wrapMap = { // Support: IE <=9 only option: [ 1, "" ], // XHTML parsers do not magically insert elements in the // same way that tag soup parsers do. So we cannot shorten // this by omitting or other required elements. thead: [ 1, "", "
        " ], col: [ 2, "", "
        " ], tr: [ 2, "", "
        " ], td: [ 3, "", "
        " ], _default: [ 0, "", "" ] }; // Support: IE <=9 only wrapMap.optgroup = wrapMap.option; wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead; wrapMap.th = wrapMap.td; function getAll( context, tag ) { // Support: IE <=9 - 11 only // Use typeof to avoid zero-argument method invocation on host objects (#15151) var ret; if ( typeof context.getElementsByTagName !== "undefined" ) { ret = context.getElementsByTagName( tag || "*" ); } else if ( typeof context.querySelectorAll !== "undefined" ) { ret = context.querySelectorAll( tag || "*" ); } else { ret = []; } if ( tag === undefined || tag && nodeName( context, tag ) ) { return jQuery.merge( [ context ], ret ); } return ret; } // Mark scripts as having already been evaluated function setGlobalEval( elems, refElements ) { var i = 0, l = elems.length; for ( ; i < l; i++ ) { dataPriv.set( elems[ i ], "globalEval", !refElements || dataPriv.get( refElements[ i ], "globalEval" ) ); } } var rhtml = /<|&#?\w+;/; function buildFragment( elems, context, scripts, selection, ignored ) { var elem, tmp, tag, wrap, contains, j, fragment = context.createDocumentFragment(), nodes = [], i = 0, l = elems.length; for ( ; i < l; i++ ) { elem = elems[ i ]; if ( elem || elem === 0 ) { // Add nodes directly if ( jQuery.type( elem ) === "object" ) { // Support: Android <=4.0 only, PhantomJS 1 only // push.apply(_, arraylike) throws on ancient WebKit jQuery.merge( nodes, elem.nodeType ? [ elem ] : elem ); // Convert non-html into a text node } else if ( !rhtml.test( elem ) ) { nodes.push( context.createTextNode( elem ) ); // Convert html into DOM nodes } else { tmp = tmp || fragment.appendChild( context.createElement( "div" ) ); // Deserialize a standard representation tag = ( rtagName.exec( elem ) || [ "", "" ] )[ 1 ].toLowerCase(); wrap = wrapMap[ tag ] || wrapMap._default; tmp.innerHTML = wrap[ 1 ] + jQuery.htmlPrefilter( elem ) + wrap[ 2 ]; // Descend through wrappers to the right content j = wrap[ 0 ]; while ( j-- ) { tmp = tmp.lastChild; } // Support: Android <=4.0 only, PhantomJS 1 only // push.apply(_, arraylike) throws on ancient WebKit jQuery.merge( nodes, tmp.childNodes ); // Remember the top-level container tmp = fragment.firstChild; // Ensure the created nodes are orphaned (#12392) tmp.textContent = ""; } } } // Remove wrapper from fragment fragment.textContent = ""; i = 0; while ( ( elem = nodes[ i++ ] ) ) { // Skip elements already in the context collection (trac-4087) if ( selection && jQuery.inArray( elem, selection ) > -1 ) { if ( ignored ) { ignored.push( elem ); } continue; } contains = jQuery.contains( elem.ownerDocument, elem ); // Append to fragment tmp = getAll( fragment.appendChild( elem ), "script" ); // Preserve script evaluation history if ( contains ) { setGlobalEval( tmp ); } // Capture executables if ( scripts ) { j = 0; while ( ( elem = tmp[ j++ ] ) ) { if ( rscriptType.test( elem.type || "" ) ) { scripts.push( elem ); } } } } return fragment; } ( function() { var fragment = document.createDocumentFragment(), div = fragment.appendChild( document.createElement( "div" ) ), input = document.createElement( "input" ); // Support: Android 4.0 - 4.3 only // Check state lost if the name is set (#11217) // Support: Windows Web Apps (WWA) // `name` and `type` must use .setAttribute for WWA (#14901) input.setAttribute( "type", "radio" ); input.setAttribute( "checked", "checked" ); input.setAttribute( "name", "t" ); div.appendChild( input ); // Support: Android <=4.1 only // Older WebKit doesn't clone checked state correctly in fragments support.checkClone = div.cloneNode( true ).cloneNode( true ).lastChild.checked; // Support: IE <=11 only // Make sure textarea (and checkbox) defaultValue is properly cloned div.innerHTML = ""; support.noCloneChecked = !!div.cloneNode( true ).lastChild.defaultValue; } )(); var documentElement = document.documentElement; var rkeyEvent = /^key/, rmouseEvent = /^(?:mouse|pointer|contextmenu|drag|drop)|click/, rtypenamespace = /^([^.]*)(?:\.(.+)|)/; function returnTrue() { return true; } function returnFalse() { return false; } // Support: IE <=9 only // See #13393 for more info function safeActiveElement() { try { return document.activeElement; } catch ( err ) { } } function on( elem, types, selector, data, fn, one ) { var origFn, type; // Types can be a map of types/handlers if ( typeof types === "object" ) { // ( types-Object, selector, data ) if ( typeof selector !== "string" ) { // ( types-Object, data ) data = data || selector; selector = undefined; } for ( type in types ) { on( elem, type, selector, data, types[ type ], one ); } return elem; } if ( data == null && fn == null ) { // ( types, fn ) fn = selector; data = selector = undefined; } else if ( fn == null ) { if ( typeof selector === "string" ) { // ( types, selector, fn ) fn = data; data = undefined; } else { // ( types, data, fn ) fn = data; data = selector; selector = undefined; } } if ( fn === false ) { fn = returnFalse; } else if ( !fn ) { return elem; } if ( one === 1 ) { origFn = fn; fn = function( event ) { // Can use an empty set, since event contains the info jQuery().off( event ); return origFn.apply( this, arguments ); }; // Use same guid so caller can remove using origFn fn.guid = origFn.guid || ( origFn.guid = jQuery.guid++ ); } return elem.each( function() { jQuery.event.add( this, types, fn, data, selector ); } ); } /* * Helper functions for managing events -- not part of the public interface. * Props to Dean Edwards' addEvent library for many of the ideas. */ jQuery.event = { global: {}, add: function( elem, types, handler, data, selector ) { var handleObjIn, eventHandle, tmp, events, t, handleObj, special, handlers, type, namespaces, origType, elemData = dataPriv.get( elem ); // Don't attach events to noData or text/comment nodes (but allow plain objects) if ( !elemData ) { return; } // Caller can pass in an object of custom data in lieu of the handler if ( handler.handler ) { handleObjIn = handler; handler = handleObjIn.handler; selector = handleObjIn.selector; } // Ensure that invalid selectors throw exceptions at attach time // Evaluate against documentElement in case elem is a non-element node (e.g., document) if ( selector ) { jQuery.find.matchesSelector( documentElement, selector ); } // Make sure that the handler has a unique ID, used to find/remove it later if ( !handler.guid ) { handler.guid = jQuery.guid++; } // Init the element's event structure and main handler, if this is the first if ( !( events = elemData.events ) ) { events = elemData.events = {}; } if ( !( eventHandle = elemData.handle ) ) { eventHandle = elemData.handle = function( e ) { // Discard the second event of a jQuery.event.trigger() and // when an event is called after a page has unloaded return typeof jQuery !== "undefined" && jQuery.event.triggered !== e.type ? jQuery.event.dispatch.apply( elem, arguments ) : undefined; }; } // Handle multiple events separated by a space types = ( types || "" ).match( rnothtmlwhite ) || [ "" ]; t = types.length; while ( t-- ) { tmp = rtypenamespace.exec( types[ t ] ) || []; type = origType = tmp[ 1 ]; namespaces = ( tmp[ 2 ] || "" ).split( "." ).sort(); // There *must* be a type, no attaching namespace-only handlers if ( !type ) { continue; } // If event changes its type, use the special event handlers for the changed type special = jQuery.event.special[ type ] || {}; // If selector defined, determine special event api type, otherwise given type type = ( selector ? special.delegateType : special.bindType ) || type; // Update special based on newly reset type special = jQuery.event.special[ type ] || {}; // handleObj is passed to all event handlers handleObj = jQuery.extend( { type: type, origType: origType, data: data, handler: handler, guid: handler.guid, selector: selector, needsContext: selector && jQuery.expr.match.needsContext.test( selector ), namespace: namespaces.join( "." ) }, handleObjIn ); // Init the event handler queue if we're the first if ( !( handlers = events[ type ] ) ) { handlers = events[ type ] = []; handlers.delegateCount = 0; // Only use addEventListener if the special events handler returns false if ( !special.setup || special.setup.call( elem, data, namespaces, eventHandle ) === false ) { if ( elem.addEventListener ) { elem.addEventListener( type, eventHandle ); } } } if ( special.add ) { special.add.call( elem, handleObj ); if ( !handleObj.handler.guid ) { handleObj.handler.guid = handler.guid; } } // Add to the element's handler list, delegates in front if ( selector ) { handlers.splice( handlers.delegateCount++, 0, handleObj ); } else { handlers.push( handleObj ); } // Keep track of which events have ever been used, for event optimization jQuery.event.global[ type ] = true; } }, // Detach an event or set of events from an element remove: function( elem, types, handler, selector, mappedTypes ) { var j, origCount, tmp, events, t, handleObj, special, handlers, type, namespaces, origType, elemData = dataPriv.hasData( elem ) && dataPriv.get( elem ); if ( !elemData || !( events = elemData.events ) ) { return; } // Once for each type.namespace in types; type may be omitted types = ( types || "" ).match( rnothtmlwhite ) || [ "" ]; t = types.length; while ( t-- ) { tmp = rtypenamespace.exec( types[ t ] ) || []; type = origType = tmp[ 1 ]; namespaces = ( tmp[ 2 ] || "" ).split( "." ).sort(); // Unbind all events (on this namespace, if provided) for the element if ( !type ) { for ( type in events ) { jQuery.event.remove( elem, type + types[ t ], handler, selector, true ); } continue; } special = jQuery.event.special[ type ] || {}; type = ( selector ? special.delegateType : special.bindType ) || type; handlers = events[ type ] || []; tmp = tmp[ 2 ] && new RegExp( "(^|\\.)" + namespaces.join( "\\.(?:.*\\.|)" ) + "(\\.|$)" ); // Remove matching events origCount = j = handlers.length; while ( j-- ) { handleObj = handlers[ j ]; if ( ( mappedTypes || origType === handleObj.origType ) && ( !handler || handler.guid === handleObj.guid ) && ( !tmp || tmp.test( handleObj.namespace ) ) && ( !selector || selector === handleObj.selector || selector === "**" && handleObj.selector ) ) { handlers.splice( j, 1 ); if ( handleObj.selector ) { handlers.delegateCount--; } if ( special.remove ) { special.remove.call( elem, handleObj ); } } } // Remove generic event handler if we removed something and no more handlers exist // (avoids potential for endless recursion during removal of special event handlers) if ( origCount && !handlers.length ) { if ( !special.teardown || special.teardown.call( elem, namespaces, elemData.handle ) === false ) { jQuery.removeEvent( elem, type, elemData.handle ); } delete events[ type ]; } } // Remove data and the expando if it's no longer used if ( jQuery.isEmptyObject( events ) ) { dataPriv.remove( elem, "handle events" ); } }, dispatch: function( nativeEvent ) { // Make a writable jQuery.Event from the native event object var event = jQuery.event.fix( nativeEvent ); var i, j, ret, matched, handleObj, handlerQueue, args = new Array( arguments.length ), handlers = ( dataPriv.get( this, "events" ) || {} )[ event.type ] || [], special = jQuery.event.special[ event.type ] || {}; // Use the fix-ed jQuery.Event rather than the (read-only) native event args[ 0 ] = event; for ( i = 1; i < arguments.length; i++ ) { args[ i ] = arguments[ i ]; } event.delegateTarget = this; // Call the preDispatch hook for the mapped type, and let it bail if desired if ( special.preDispatch && special.preDispatch.call( this, event ) === false ) { return; } // Determine handlers handlerQueue = jQuery.event.handlers.call( this, event, handlers ); // Run delegates first; they may want to stop propagation beneath us i = 0; while ( ( matched = handlerQueue[ i++ ] ) && !event.isPropagationStopped() ) { event.currentTarget = matched.elem; j = 0; while ( ( handleObj = matched.handlers[ j++ ] ) && !event.isImmediatePropagationStopped() ) { // Triggered event must either 1) have no namespace, or 2) have namespace(s) // a subset or equal to those in the bound event (both can have no namespace). if ( !event.rnamespace || event.rnamespace.test( handleObj.namespace ) ) { event.handleObj = handleObj; event.data = handleObj.data; ret = ( ( jQuery.event.special[ handleObj.origType ] || {} ).handle || handleObj.handler ).apply( matched.elem, args ); if ( ret !== undefined ) { if ( ( event.result = ret ) === false ) { event.preventDefault(); event.stopPropagation(); } } } } } // Call the postDispatch hook for the mapped type if ( special.postDispatch ) { special.postDispatch.call( this, event ); } return event.result; }, handlers: function( event, handlers ) { var i, handleObj, sel, matchedHandlers, matchedSelectors, handlerQueue = [], delegateCount = handlers.delegateCount, cur = event.target; // Find delegate handlers if ( delegateCount && // Support: IE <=9 // Black-hole SVG instance trees (trac-13180) cur.nodeType && // Support: Firefox <=42 // Suppress spec-violating clicks indicating a non-primary pointer button (trac-3861) // https://www.w3.org/TR/DOM-Level-3-Events/#event-type-click // Support: IE 11 only // ...but not arrow key "clicks" of radio inputs, which can have `button` -1 (gh-2343) !( event.type === "click" && event.button >= 1 ) ) { for ( ; cur !== this; cur = cur.parentNode || this ) { // Don't check non-elements (#13208) // Don't process clicks on disabled elements (#6911, #8165, #11382, #11764) if ( cur.nodeType === 1 && !( event.type === "click" && cur.disabled === true ) ) { matchedHandlers = []; matchedSelectors = {}; for ( i = 0; i < delegateCount; i++ ) { handleObj = handlers[ i ]; // Don't conflict with Object.prototype properties (#13203) sel = handleObj.selector + " "; if ( matchedSelectors[ sel ] === undefined ) { matchedSelectors[ sel ] = handleObj.needsContext ? jQuery( sel, this ).index( cur ) > -1 : jQuery.find( sel, this, null, [ cur ] ).length; } if ( matchedSelectors[ sel ] ) { matchedHandlers.push( handleObj ); } } if ( matchedHandlers.length ) { handlerQueue.push( { elem: cur, handlers: matchedHandlers } ); } } } } // Add the remaining (directly-bound) handlers cur = this; if ( delegateCount < handlers.length ) { handlerQueue.push( { elem: cur, handlers: handlers.slice( delegateCount ) } ); } return handlerQueue; }, addProp: function( name, hook ) { Object.defineProperty( jQuery.Event.prototype, name, { enumerable: true, configurable: true, get: jQuery.isFunction( hook ) ? function() { if ( this.originalEvent ) { return hook( this.originalEvent ); } } : function() { if ( this.originalEvent ) { return this.originalEvent[ name ]; } }, set: function( value ) { Object.defineProperty( this, name, { enumerable: true, configurable: true, writable: true, value: value } ); } } ); }, fix: function( originalEvent ) { return originalEvent[ jQuery.expando ] ? originalEvent : new jQuery.Event( originalEvent ); }, special: { load: { // Prevent triggered image.load events from bubbling to window.load noBubble: true }, focus: { // Fire native event if possible so blur/focus sequence is correct trigger: function() { if ( this !== safeActiveElement() && this.focus ) { this.focus(); return false; } }, delegateType: "focusin" }, blur: { trigger: function() { if ( this === safeActiveElement() && this.blur ) { this.blur(); return false; } }, delegateType: "focusout" }, click: { // For checkbox, fire native event so checked state will be right trigger: function() { if ( this.type === "checkbox" && this.click && nodeName( this, "input" ) ) { this.click(); return false; } }, // For cross-browser consistency, don't fire native .click() on links _default: function( event ) { return nodeName( event.target, "a" ); } }, beforeunload: { postDispatch: function( event ) { // Support: Firefox 20+ // Firefox doesn't alert if the returnValue field is not set. if ( event.result !== undefined && event.originalEvent ) { event.originalEvent.returnValue = event.result; } } } } }; jQuery.removeEvent = function( elem, type, handle ) { // This "if" is needed for plain objects if ( elem.removeEventListener ) { elem.removeEventListener( type, handle ); } }; jQuery.Event = function( src, props ) { // Allow instantiation without the 'new' keyword if ( !( this instanceof jQuery.Event ) ) { return new jQuery.Event( src, props ); } // Event object if ( src && src.type ) { this.originalEvent = src; this.type = src.type; // Events bubbling up the document may have been marked as prevented // by a handler lower down the tree; reflect the correct value. this.isDefaultPrevented = src.defaultPrevented || src.defaultPrevented === undefined && // Support: Android <=2.3 only src.returnValue === false ? returnTrue : returnFalse; // Create target properties // Support: Safari <=6 - 7 only // Target should not be a text node (#504, #13143) this.target = ( src.target && src.target.nodeType === 3 ) ? src.target.parentNode : src.target; this.currentTarget = src.currentTarget; this.relatedTarget = src.relatedTarget; // Event type } else { this.type = src; } // Put explicitly provided properties onto the event object if ( props ) { jQuery.extend( this, props ); } // Create a timestamp if incoming event doesn't have one this.timeStamp = src && src.timeStamp || jQuery.now(); // Mark it as fixed this[ jQuery.expando ] = true; }; // jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding // https://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html jQuery.Event.prototype = { constructor: jQuery.Event, isDefaultPrevented: returnFalse, isPropagationStopped: returnFalse, isImmediatePropagationStopped: returnFalse, isSimulated: false, preventDefault: function() { var e = this.originalEvent; this.isDefaultPrevented = returnTrue; if ( e && !this.isSimulated ) { e.preventDefault(); } }, stopPropagation: function() { var e = this.originalEvent; this.isPropagationStopped = returnTrue; if ( e && !this.isSimulated ) { e.stopPropagation(); } }, stopImmediatePropagation: function() { var e = this.originalEvent; this.isImmediatePropagationStopped = returnTrue; if ( e && !this.isSimulated ) { e.stopImmediatePropagation(); } this.stopPropagation(); } }; // Includes all common event props including KeyEvent and MouseEvent specific props jQuery.each( { altKey: true, bubbles: true, cancelable: true, changedTouches: true, ctrlKey: true, detail: true, eventPhase: true, metaKey: true, pageX: true, pageY: true, shiftKey: true, view: true, "char": true, charCode: true, key: true, keyCode: true, button: true, buttons: true, clientX: true, clientY: true, offsetX: true, offsetY: true, pointerId: true, pointerType: true, screenX: true, screenY: true, targetTouches: true, toElement: true, touches: true, which: function( event ) { var button = event.button; // Add which for key events if ( event.which == null && rkeyEvent.test( event.type ) ) { return event.charCode != null ? event.charCode : event.keyCode; } // Add which for click: 1 === left; 2 === middle; 3 === right if ( !event.which && button !== undefined && rmouseEvent.test( event.type ) ) { if ( button & 1 ) { return 1; } if ( button & 2 ) { return 3; } if ( button & 4 ) { return 2; } return 0; } return event.which; } }, jQuery.event.addProp ); // Create mouseenter/leave events using mouseover/out and event-time checks // so that event delegation works in jQuery. // Do the same for pointerenter/pointerleave and pointerover/pointerout // // Support: Safari 7 only // Safari sends mouseenter too often; see: // https://bugs.chromium.org/p/chromium/issues/detail?id=470258 // for the description of the bug (it existed in older Chrome versions as well). jQuery.each( { mouseenter: "mouseover", mouseleave: "mouseout", pointerenter: "pointerover", pointerleave: "pointerout" }, function( orig, fix ) { jQuery.event.special[ orig ] = { delegateType: fix, bindType: fix, handle: function( event ) { var ret, target = this, related = event.relatedTarget, handleObj = event.handleObj; // For mouseenter/leave call the handler if related is outside the target. // NB: No relatedTarget if the mouse left/entered the browser window if ( !related || ( related !== target && !jQuery.contains( target, related ) ) ) { event.type = handleObj.origType; ret = handleObj.handler.apply( this, arguments ); event.type = fix; } return ret; } }; } ); jQuery.fn.extend( { on: function( types, selector, data, fn ) { return on( this, types, selector, data, fn ); }, one: function( types, selector, data, fn ) { return on( this, types, selector, data, fn, 1 ); }, off: function( types, selector, fn ) { var handleObj, type; if ( types && types.preventDefault && types.handleObj ) { // ( event ) dispatched jQuery.Event handleObj = types.handleObj; jQuery( types.delegateTarget ).off( handleObj.namespace ? handleObj.origType + "." + handleObj.namespace : handleObj.origType, handleObj.selector, handleObj.handler ); return this; } if ( typeof types === "object" ) { // ( types-object [, selector] ) for ( type in types ) { this.off( type, selector, types[ type ] ); } return this; } if ( selector === false || typeof selector === "function" ) { // ( types [, fn] ) fn = selector; selector = undefined; } if ( fn === false ) { fn = returnFalse; } return this.each( function() { jQuery.event.remove( this, types, fn, selector ); } ); } } ); var /* eslint-disable max-len */ // See https://github.com/eslint/eslint/issues/3229 rxhtmlTag = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([a-z][^\/\0>\x20\t\r\n\f]*)[^>]*)\/>/gi, /* eslint-enable */ // Support: IE <=10 - 11, Edge 12 - 13 // In IE/Edge using regex groups here causes severe slowdowns. // See https://connect.microsoft.com/IE/feedback/details/1736512/ rnoInnerhtml = /\s*$/g; // Prefer a tbody over its parent table for containing new rows function manipulationTarget( elem, content ) { if ( nodeName( elem, "table" ) && nodeName( content.nodeType !== 11 ? content : content.firstChild, "tr" ) ) { return jQuery( ">tbody", elem )[ 0 ] || elem; } return elem; } // Replace/restore the type attribute of script elements for safe DOM manipulation function disableScript( elem ) { elem.type = ( elem.getAttribute( "type" ) !== null ) + "/" + elem.type; return elem; } function restoreScript( elem ) { var match = rscriptTypeMasked.exec( elem.type ); if ( match ) { elem.type = match[ 1 ]; } else { elem.removeAttribute( "type" ); } return elem; } function cloneCopyEvent( src, dest ) { var i, l, type, pdataOld, pdataCur, udataOld, udataCur, events; if ( dest.nodeType !== 1 ) { return; } // 1. Copy private data: events, handlers, etc. if ( dataPriv.hasData( src ) ) { pdataOld = dataPriv.access( src ); pdataCur = dataPriv.set( dest, pdataOld ); events = pdataOld.events; if ( events ) { delete pdataCur.handle; pdataCur.events = {}; for ( type in events ) { for ( i = 0, l = events[ type ].length; i < l; i++ ) { jQuery.event.add( dest, type, events[ type ][ i ] ); } } } } // 2. Copy user data if ( dataUser.hasData( src ) ) { udataOld = dataUser.access( src ); udataCur = jQuery.extend( {}, udataOld ); dataUser.set( dest, udataCur ); } } // Fix IE bugs, see support tests function fixInput( src, dest ) { var nodeName = dest.nodeName.toLowerCase(); // Fails to persist the checked state of a cloned checkbox or radio button. if ( nodeName === "input" && rcheckableType.test( src.type ) ) { dest.checked = src.checked; // Fails to return the selected option to the default selected state when cloning options } else if ( nodeName === "input" || nodeName === "textarea" ) { dest.defaultValue = src.defaultValue; } } function domManip( collection, args, callback, ignored ) { // Flatten any nested arrays args = concat.apply( [], args ); var fragment, first, scripts, hasScripts, node, doc, i = 0, l = collection.length, iNoClone = l - 1, value = args[ 0 ], isFunction = jQuery.isFunction( value ); // We can't cloneNode fragments that contain checked, in WebKit if ( isFunction || ( l > 1 && typeof value === "string" && !support.checkClone && rchecked.test( value ) ) ) { return collection.each( function( index ) { var self = collection.eq( index ); if ( isFunction ) { args[ 0 ] = value.call( this, index, self.html() ); } domManip( self, args, callback, ignored ); } ); } if ( l ) { fragment = buildFragment( args, collection[ 0 ].ownerDocument, false, collection, ignored ); first = fragment.firstChild; if ( fragment.childNodes.length === 1 ) { fragment = first; } // Require either new content or an interest in ignored elements to invoke the callback if ( first || ignored ) { scripts = jQuery.map( getAll( fragment, "script" ), disableScript ); hasScripts = scripts.length; // Use the original fragment for the last item // instead of the first because it can end up // being emptied incorrectly in certain situations (#8070). for ( ; i < l; i++ ) { node = fragment; if ( i !== iNoClone ) { node = jQuery.clone( node, true, true ); // Keep references to cloned scripts for later restoration if ( hasScripts ) { // Support: Android <=4.0 only, PhantomJS 1 only // push.apply(_, arraylike) throws on ancient WebKit jQuery.merge( scripts, getAll( node, "script" ) ); } } callback.call( collection[ i ], node, i ); } if ( hasScripts ) { doc = scripts[ scripts.length - 1 ].ownerDocument; // Reenable scripts jQuery.map( scripts, restoreScript ); // Evaluate executable scripts on first document insertion for ( i = 0; i < hasScripts; i++ ) { node = scripts[ i ]; if ( rscriptType.test( node.type || "" ) && !dataPriv.access( node, "globalEval" ) && jQuery.contains( doc, node ) ) { if ( node.src ) { // Optional AJAX dependency, but won't run scripts if not present if ( jQuery._evalUrl ) { jQuery._evalUrl( node.src ); } } else { DOMEval( node.textContent.replace( rcleanScript, "" ), doc ); } } } } } } return collection; } function remove( elem, selector, keepData ) { var node, nodes = selector ? jQuery.filter( selector, elem ) : elem, i = 0; for ( ; ( node = nodes[ i ] ) != null; i++ ) { if ( !keepData && node.nodeType === 1 ) { jQuery.cleanData( getAll( node ) ); } if ( node.parentNode ) { if ( keepData && jQuery.contains( node.ownerDocument, node ) ) { setGlobalEval( getAll( node, "script" ) ); } node.parentNode.removeChild( node ); } } return elem; } jQuery.extend( { htmlPrefilter: function( html ) { return html.replace( rxhtmlTag, "<$1>" ); }, clone: function( elem, dataAndEvents, deepDataAndEvents ) { var i, l, srcElements, destElements, clone = elem.cloneNode( true ), inPage = jQuery.contains( elem.ownerDocument, elem ); // Fix IE cloning issues if ( !support.noCloneChecked && ( elem.nodeType === 1 || elem.nodeType === 11 ) && !jQuery.isXMLDoc( elem ) ) { // We eschew Sizzle here for performance reasons: https://jsperf.com/getall-vs-sizzle/2 destElements = getAll( clone ); srcElements = getAll( elem ); for ( i = 0, l = srcElements.length; i < l; i++ ) { fixInput( srcElements[ i ], destElements[ i ] ); } } // Copy the events from the original to the clone if ( dataAndEvents ) { if ( deepDataAndEvents ) { srcElements = srcElements || getAll( elem ); destElements = destElements || getAll( clone ); for ( i = 0, l = srcElements.length; i < l; i++ ) { cloneCopyEvent( srcElements[ i ], destElements[ i ] ); } } else { cloneCopyEvent( elem, clone ); } } // Preserve script evaluation history destElements = getAll( clone, "script" ); if ( destElements.length > 0 ) { setGlobalEval( destElements, !inPage && getAll( elem, "script" ) ); } // Return the cloned set return clone; }, cleanData: function( elems ) { var data, elem, type, special = jQuery.event.special, i = 0; for ( ; ( elem = elems[ i ] ) !== undefined; i++ ) { if ( acceptData( elem ) ) { if ( ( data = elem[ dataPriv.expando ] ) ) { if ( data.events ) { for ( type in data.events ) { if ( special[ type ] ) { jQuery.event.remove( elem, type ); // This is a shortcut to avoid jQuery.event.remove's overhead } else { jQuery.removeEvent( elem, type, data.handle ); } } } // Support: Chrome <=35 - 45+ // Assign undefined instead of using delete, see Data#remove elem[ dataPriv.expando ] = undefined; } if ( elem[ dataUser.expando ] ) { // Support: Chrome <=35 - 45+ // Assign undefined instead of using delete, see Data#remove elem[ dataUser.expando ] = undefined; } } } } } ); jQuery.fn.extend( { detach: function( selector ) { return remove( this, selector, true ); }, remove: function( selector ) { return remove( this, selector ); }, text: function( value ) { return access( this, function( value ) { return value === undefined ? jQuery.text( this ) : this.empty().each( function() { if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { this.textContent = value; } } ); }, null, value, arguments.length ); }, append: function() { return domManip( this, arguments, function( elem ) { if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { var target = manipulationTarget( this, elem ); target.appendChild( elem ); } } ); }, prepend: function() { return domManip( this, arguments, function( elem ) { if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { var target = manipulationTarget( this, elem ); target.insertBefore( elem, target.firstChild ); } } ); }, before: function() { return domManip( this, arguments, function( elem ) { if ( this.parentNode ) { this.parentNode.insertBefore( elem, this ); } } ); }, after: function() { return domManip( this, arguments, function( elem ) { if ( this.parentNode ) { this.parentNode.insertBefore( elem, this.nextSibling ); } } ); }, empty: function() { var elem, i = 0; for ( ; ( elem = this[ i ] ) != null; i++ ) { if ( elem.nodeType === 1 ) { // Prevent memory leaks jQuery.cleanData( getAll( elem, false ) ); // Remove any remaining nodes elem.textContent = ""; } } return this; }, clone: function( dataAndEvents, deepDataAndEvents ) { dataAndEvents = dataAndEvents == null ? false : dataAndEvents; deepDataAndEvents = deepDataAndEvents == null ? dataAndEvents : deepDataAndEvents; return this.map( function() { return jQuery.clone( this, dataAndEvents, deepDataAndEvents ); } ); }, html: function( value ) { return access( this, function( value ) { var elem = this[ 0 ] || {}, i = 0, l = this.length; if ( value === undefined && elem.nodeType === 1 ) { return elem.innerHTML; } // See if we can take a shortcut and just use innerHTML if ( typeof value === "string" && !rnoInnerhtml.test( value ) && !wrapMap[ ( rtagName.exec( value ) || [ "", "" ] )[ 1 ].toLowerCase() ] ) { value = jQuery.htmlPrefilter( value ); try { for ( ; i < l; i++ ) { elem = this[ i ] || {}; // Remove element nodes and prevent memory leaks if ( elem.nodeType === 1 ) { jQuery.cleanData( getAll( elem, false ) ); elem.innerHTML = value; } } elem = 0; // If using innerHTML throws an exception, use the fallback method } catch ( e ) {} } if ( elem ) { this.empty().append( value ); } }, null, value, arguments.length ); }, replaceWith: function() { var ignored = []; // Make the changes, replacing each non-ignored context element with the new content return domManip( this, arguments, function( elem ) { var parent = this.parentNode; if ( jQuery.inArray( this, ignored ) < 0 ) { jQuery.cleanData( getAll( this ) ); if ( parent ) { parent.replaceChild( elem, this ); } } // Force callback invocation }, ignored ); } } ); jQuery.each( { appendTo: "append", prependTo: "prepend", insertBefore: "before", insertAfter: "after", replaceAll: "replaceWith" }, function( name, original ) { jQuery.fn[ name ] = function( selector ) { var elems, ret = [], insert = jQuery( selector ), last = insert.length - 1, i = 0; for ( ; i <= last; i++ ) { elems = i === last ? this : this.clone( true ); jQuery( insert[ i ] )[ original ]( elems ); // Support: Android <=4.0 only, PhantomJS 1 only // .get() because push.apply(_, arraylike) throws on ancient WebKit push.apply( ret, elems.get() ); } return this.pushStack( ret ); }; } ); var rmargin = ( /^margin/ ); var rnumnonpx = new RegExp( "^(" + pnum + ")(?!px)[a-z%]+$", "i" ); var getStyles = function( elem ) { // Support: IE <=11 only, Firefox <=30 (#15098, #14150) // IE throws on elements created in popups // FF meanwhile throws on frame elements through "defaultView.getComputedStyle" var view = elem.ownerDocument.defaultView; if ( !view || !view.opener ) { view = window; } return view.getComputedStyle( elem ); }; ( function() { // Executing both pixelPosition & boxSizingReliable tests require only one layout // so they're executed at the same time to save the second computation. function computeStyleTests() { // This is a singleton, we need to execute it only once if ( !div ) { return; } div.style.cssText = "box-sizing:border-box;" + "position:relative;display:block;" + "margin:auto;border:1px;padding:1px;" + "top:1%;width:50%"; div.innerHTML = ""; documentElement.appendChild( container ); var divStyle = window.getComputedStyle( div ); pixelPositionVal = divStyle.top !== "1%"; // Support: Android 4.0 - 4.3 only, Firefox <=3 - 44 reliableMarginLeftVal = divStyle.marginLeft === "2px"; boxSizingReliableVal = divStyle.width === "4px"; // Support: Android 4.0 - 4.3 only // Some styles come back with percentage values, even though they shouldn't div.style.marginRight = "50%"; pixelMarginRightVal = divStyle.marginRight === "4px"; documentElement.removeChild( container ); // Nullify the div so it wouldn't be stored in the memory and // it will also be a sign that checks already performed div = null; } var pixelPositionVal, boxSizingReliableVal, pixelMarginRightVal, reliableMarginLeftVal, container = document.createElement( "div" ), div = document.createElement( "div" ); // Finish early in limited (non-browser) environments if ( !div.style ) { return; } // Support: IE <=9 - 11 only // Style of cloned element affects source element cloned (#8908) div.style.backgroundClip = "content-box"; div.cloneNode( true ).style.backgroundClip = ""; support.clearCloneStyle = div.style.backgroundClip === "content-box"; container.style.cssText = "border:0;width:8px;height:0;top:0;left:-9999px;" + "padding:0;margin-top:1px;position:absolute"; container.appendChild( div ); jQuery.extend( support, { pixelPosition: function() { computeStyleTests(); return pixelPositionVal; }, boxSizingReliable: function() { computeStyleTests(); return boxSizingReliableVal; }, pixelMarginRight: function() { computeStyleTests(); return pixelMarginRightVal; }, reliableMarginLeft: function() { computeStyleTests(); return reliableMarginLeftVal; } } ); } )(); function curCSS( elem, name, computed ) { var width, minWidth, maxWidth, ret, // Support: Firefox 51+ // Retrieving style before computed somehow // fixes an issue with getting wrong values // on detached elements style = elem.style; computed = computed || getStyles( elem ); // getPropertyValue is needed for: // .css('filter') (IE 9 only, #12537) // .css('--customProperty) (#3144) if ( computed ) { ret = computed.getPropertyValue( name ) || computed[ name ]; if ( ret === "" && !jQuery.contains( elem.ownerDocument, elem ) ) { ret = jQuery.style( elem, name ); } // A tribute to the "awesome hack by Dean Edwards" // Android Browser returns percentage for some values, // but width seems to be reliably pixels. // This is against the CSSOM draft spec: // https://drafts.csswg.org/cssom/#resolved-values if ( !support.pixelMarginRight() && rnumnonpx.test( ret ) && rmargin.test( name ) ) { // Remember the original values width = style.width; minWidth = style.minWidth; maxWidth = style.maxWidth; // Put in the new values to get a computed value out style.minWidth = style.maxWidth = style.width = ret; ret = computed.width; // Revert the changed values style.width = width; style.minWidth = minWidth; style.maxWidth = maxWidth; } } return ret !== undefined ? // Support: IE <=9 - 11 only // IE returns zIndex value as an integer. ret + "" : ret; } function addGetHookIf( conditionFn, hookFn ) { // Define the hook, we'll check on the first run if it's really needed. return { get: function() { if ( conditionFn() ) { // Hook not needed (or it's not possible to use it due // to missing dependency), remove it. delete this.get; return; } // Hook needed; redefine it so that the support test is not executed again. return ( this.get = hookFn ).apply( this, arguments ); } }; } var // Swappable if display is none or starts with table // except "table", "table-cell", or "table-caption" // See here for display values: https://developer.mozilla.org/en-US/docs/CSS/display rdisplayswap = /^(none|table(?!-c[ea]).+)/, rcustomProp = /^--/, cssShow = { position: "absolute", visibility: "hidden", display: "block" }, cssNormalTransform = { letterSpacing: "0", fontWeight: "400" }, cssPrefixes = [ "Webkit", "Moz", "ms" ], emptyStyle = document.createElement( "div" ).style; // Return a css property mapped to a potentially vendor prefixed property function vendorPropName( name ) { // Shortcut for names that are not vendor prefixed if ( name in emptyStyle ) { return name; } // Check for vendor prefixed names var capName = name[ 0 ].toUpperCase() + name.slice( 1 ), i = cssPrefixes.length; while ( i-- ) { name = cssPrefixes[ i ] + capName; if ( name in emptyStyle ) { return name; } } } // Return a property mapped along what jQuery.cssProps suggests or to // a vendor prefixed property. function finalPropName( name ) { var ret = jQuery.cssProps[ name ]; if ( !ret ) { ret = jQuery.cssProps[ name ] = vendorPropName( name ) || name; } return ret; } function setPositiveNumber( elem, value, subtract ) { // Any relative (+/-) values have already been // normalized at this point var matches = rcssNum.exec( value ); return matches ? // Guard against undefined "subtract", e.g., when used as in cssHooks Math.max( 0, matches[ 2 ] - ( subtract || 0 ) ) + ( matches[ 3 ] || "px" ) : value; } function augmentWidthOrHeight( elem, name, extra, isBorderBox, styles ) { var i, val = 0; // If we already have the right measurement, avoid augmentation if ( extra === ( isBorderBox ? "border" : "content" ) ) { i = 4; // Otherwise initialize for horizontal or vertical properties } else { i = name === "width" ? 1 : 0; } for ( ; i < 4; i += 2 ) { // Both box models exclude margin, so add it if we want it if ( extra === "margin" ) { val += jQuery.css( elem, extra + cssExpand[ i ], true, styles ); } if ( isBorderBox ) { // border-box includes padding, so remove it if we want content if ( extra === "content" ) { val -= jQuery.css( elem, "padding" + cssExpand[ i ], true, styles ); } // At this point, extra isn't border nor margin, so remove border if ( extra !== "margin" ) { val -= jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles ); } } else { // At this point, extra isn't content, so add padding val += jQuery.css( elem, "padding" + cssExpand[ i ], true, styles ); // At this point, extra isn't content nor padding, so add border if ( extra !== "padding" ) { val += jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles ); } } } return val; } function getWidthOrHeight( elem, name, extra ) { // Start with computed style var valueIsBorderBox, styles = getStyles( elem ), val = curCSS( elem, name, styles ), isBorderBox = jQuery.css( elem, "boxSizing", false, styles ) === "border-box"; // Computed unit is not pixels. Stop here and return. if ( rnumnonpx.test( val ) ) { return val; } // Check for style in case a browser which returns unreliable values // for getComputedStyle silently falls back to the reliable elem.style valueIsBorderBox = isBorderBox && ( support.boxSizingReliable() || val === elem.style[ name ] ); // Fall back to offsetWidth/Height when value is "auto" // This happens for inline elements with no explicit setting (gh-3571) if ( val === "auto" ) { val = elem[ "offset" + name[ 0 ].toUpperCase() + name.slice( 1 ) ]; } // Normalize "", auto, and prepare for extra val = parseFloat( val ) || 0; // Use the active box-sizing model to add/subtract irrelevant styles return ( val + augmentWidthOrHeight( elem, name, extra || ( isBorderBox ? "border" : "content" ), valueIsBorderBox, styles ) ) + "px"; } jQuery.extend( { // Add in style property hooks for overriding the default // behavior of getting and setting a style property cssHooks: { opacity: { get: function( elem, computed ) { if ( computed ) { // We should always get a number back from opacity var ret = curCSS( elem, "opacity" ); return ret === "" ? "1" : ret; } } } }, // Don't automatically add "px" to these possibly-unitless properties cssNumber: { "animationIterationCount": true, "columnCount": true, "fillOpacity": true, "flexGrow": true, "flexShrink": true, "fontWeight": true, "lineHeight": true, "opacity": true, "order": true, "orphans": true, "widows": true, "zIndex": true, "zoom": true }, // Add in properties whose names you wish to fix before // setting or getting the value cssProps: { "float": "cssFloat" }, // Get and set the style property on a DOM Node style: function( elem, name, value, extra ) { // Don't set styles on text and comment nodes if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 || !elem.style ) { return; } // Make sure that we're working with the right name var ret, type, hooks, origName = jQuery.camelCase( name ), isCustomProp = rcustomProp.test( name ), style = elem.style; // Make sure that we're working with the right name. We don't // want to query the value if it is a CSS custom property // since they are user-defined. if ( !isCustomProp ) { name = finalPropName( origName ); } // Gets hook for the prefixed version, then unprefixed version hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ]; // Check if we're setting a value if ( value !== undefined ) { type = typeof value; // Convert "+=" or "-=" to relative numbers (#7345) if ( type === "string" && ( ret = rcssNum.exec( value ) ) && ret[ 1 ] ) { value = adjustCSS( elem, name, ret ); // Fixes bug #9237 type = "number"; } // Make sure that null and NaN values aren't set (#7116) if ( value == null || value !== value ) { return; } // If a number was passed in, add the unit (except for certain CSS properties) if ( type === "number" ) { value += ret && ret[ 3 ] || ( jQuery.cssNumber[ origName ] ? "" : "px" ); } // background-* props affect original clone's values if ( !support.clearCloneStyle && value === "" && name.indexOf( "background" ) === 0 ) { style[ name ] = "inherit"; } // If a hook was provided, use that value, otherwise just set the specified value if ( !hooks || !( "set" in hooks ) || ( value = hooks.set( elem, value, extra ) ) !== undefined ) { if ( isCustomProp ) { style.setProperty( name, value ); } else { style[ name ] = value; } } } else { // If a hook was provided get the non-computed value from there if ( hooks && "get" in hooks && ( ret = hooks.get( elem, false, extra ) ) !== undefined ) { return ret; } // Otherwise just get the value from the style object return style[ name ]; } }, css: function( elem, name, extra, styles ) { var val, num, hooks, origName = jQuery.camelCase( name ), isCustomProp = rcustomProp.test( name ); // Make sure that we're working with the right name. We don't // want to modify the value if it is a CSS custom property // since they are user-defined. if ( !isCustomProp ) { name = finalPropName( origName ); } // Try prefixed name followed by the unprefixed name hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ]; // If a hook was provided get the computed value from there if ( hooks && "get" in hooks ) { val = hooks.get( elem, true, extra ); } // Otherwise, if a way to get the computed value exists, use that if ( val === undefined ) { val = curCSS( elem, name, styles ); } // Convert "normal" to computed value if ( val === "normal" && name in cssNormalTransform ) { val = cssNormalTransform[ name ]; } // Make numeric if forced or a qualifier was provided and val looks numeric if ( extra === "" || extra ) { num = parseFloat( val ); return extra === true || isFinite( num ) ? num || 0 : val; } return val; } } ); jQuery.each( [ "height", "width" ], function( i, name ) { jQuery.cssHooks[ name ] = { get: function( elem, computed, extra ) { if ( computed ) { // Certain elements can have dimension info if we invisibly show them // but it must have a current display style that would benefit return rdisplayswap.test( jQuery.css( elem, "display" ) ) && // Support: Safari 8+ // Table columns in Safari have non-zero offsetWidth & zero // getBoundingClientRect().width unless display is changed. // Support: IE <=11 only // Running getBoundingClientRect on a disconnected node // in IE throws an error. ( !elem.getClientRects().length || !elem.getBoundingClientRect().width ) ? swap( elem, cssShow, function() { return getWidthOrHeight( elem, name, extra ); } ) : getWidthOrHeight( elem, name, extra ); } }, set: function( elem, value, extra ) { var matches, styles = extra && getStyles( elem ), subtract = extra && augmentWidthOrHeight( elem, name, extra, jQuery.css( elem, "boxSizing", false, styles ) === "border-box", styles ); // Convert to pixels if value adjustment is needed if ( subtract && ( matches = rcssNum.exec( value ) ) && ( matches[ 3 ] || "px" ) !== "px" ) { elem.style[ name ] = value; value = jQuery.css( elem, name ); } return setPositiveNumber( elem, value, subtract ); } }; } ); jQuery.cssHooks.marginLeft = addGetHookIf( support.reliableMarginLeft, function( elem, computed ) { if ( computed ) { return ( parseFloat( curCSS( elem, "marginLeft" ) ) || elem.getBoundingClientRect().left - swap( elem, { marginLeft: 0 }, function() { return elem.getBoundingClientRect().left; } ) ) + "px"; } } ); // These hooks are used by animate to expand properties jQuery.each( { margin: "", padding: "", border: "Width" }, function( prefix, suffix ) { jQuery.cssHooks[ prefix + suffix ] = { expand: function( value ) { var i = 0, expanded = {}, // Assumes a single number if not a string parts = typeof value === "string" ? value.split( " " ) : [ value ]; for ( ; i < 4; i++ ) { expanded[ prefix + cssExpand[ i ] + suffix ] = parts[ i ] || parts[ i - 2 ] || parts[ 0 ]; } return expanded; } }; if ( !rmargin.test( prefix ) ) { jQuery.cssHooks[ prefix + suffix ].set = setPositiveNumber; } } ); jQuery.fn.extend( { css: function( name, value ) { return access( this, function( elem, name, value ) { var styles, len, map = {}, i = 0; if ( Array.isArray( name ) ) { styles = getStyles( elem ); len = name.length; for ( ; i < len; i++ ) { map[ name[ i ] ] = jQuery.css( elem, name[ i ], false, styles ); } return map; } return value !== undefined ? jQuery.style( elem, name, value ) : jQuery.css( elem, name ); }, name, value, arguments.length > 1 ); } } ); function Tween( elem, options, prop, end, easing ) { return new Tween.prototype.init( elem, options, prop, end, easing ); } jQuery.Tween = Tween; Tween.prototype = { constructor: Tween, init: function( elem, options, prop, end, easing, unit ) { this.elem = elem; this.prop = prop; this.easing = easing || jQuery.easing._default; this.options = options; this.start = this.now = this.cur(); this.end = end; this.unit = unit || ( jQuery.cssNumber[ prop ] ? "" : "px" ); }, cur: function() { var hooks = Tween.propHooks[ this.prop ]; return hooks && hooks.get ? hooks.get( this ) : Tween.propHooks._default.get( this ); }, run: function( percent ) { var eased, hooks = Tween.propHooks[ this.prop ]; if ( this.options.duration ) { this.pos = eased = jQuery.easing[ this.easing ]( percent, this.options.duration * percent, 0, 1, this.options.duration ); } else { this.pos = eased = percent; } this.now = ( this.end - this.start ) * eased + this.start; if ( this.options.step ) { this.options.step.call( this.elem, this.now, this ); } if ( hooks && hooks.set ) { hooks.set( this ); } else { Tween.propHooks._default.set( this ); } return this; } }; Tween.prototype.init.prototype = Tween.prototype; Tween.propHooks = { _default: { get: function( tween ) { var result; // Use a property on the element directly when it is not a DOM element, // or when there is no matching style property that exists. if ( tween.elem.nodeType !== 1 || tween.elem[ tween.prop ] != null && tween.elem.style[ tween.prop ] == null ) { return tween.elem[ tween.prop ]; } // Passing an empty string as a 3rd parameter to .css will automatically // attempt a parseFloat and fallback to a string if the parse fails. // Simple values such as "10px" are parsed to Float; // complex values such as "rotate(1rad)" are returned as-is. result = jQuery.css( tween.elem, tween.prop, "" ); // Empty strings, null, undefined and "auto" are converted to 0. return !result || result === "auto" ? 0 : result; }, set: function( tween ) { // Use step hook for back compat. // Use cssHook if its there. // Use .style if available and use plain properties where available. if ( jQuery.fx.step[ tween.prop ] ) { jQuery.fx.step[ tween.prop ]( tween ); } else if ( tween.elem.nodeType === 1 && ( tween.elem.style[ jQuery.cssProps[ tween.prop ] ] != null || jQuery.cssHooks[ tween.prop ] ) ) { jQuery.style( tween.elem, tween.prop, tween.now + tween.unit ); } else { tween.elem[ tween.prop ] = tween.now; } } } }; // Support: IE <=9 only // Panic based approach to setting things on disconnected nodes Tween.propHooks.scrollTop = Tween.propHooks.scrollLeft = { set: function( tween ) { if ( tween.elem.nodeType && tween.elem.parentNode ) { tween.elem[ tween.prop ] = tween.now; } } }; jQuery.easing = { linear: function( p ) { return p; }, swing: function( p ) { return 0.5 - Math.cos( p * Math.PI ) / 2; }, _default: "swing" }; jQuery.fx = Tween.prototype.init; // Back compat <1.8 extension point jQuery.fx.step = {}; var fxNow, inProgress, rfxtypes = /^(?:toggle|show|hide)$/, rrun = /queueHooks$/; function schedule() { if ( inProgress ) { if ( document.hidden === false && window.requestAnimationFrame ) { window.requestAnimationFrame( schedule ); } else { window.setTimeout( schedule, jQuery.fx.interval ); } jQuery.fx.tick(); } } // Animations created synchronously will run synchronously function createFxNow() { window.setTimeout( function() { fxNow = undefined; } ); return ( fxNow = jQuery.now() ); } // Generate parameters to create a standard animation function genFx( type, includeWidth ) { var which, i = 0, attrs = { height: type }; // If we include width, step value is 1 to do all cssExpand values, // otherwise step value is 2 to skip over Left and Right includeWidth = includeWidth ? 1 : 0; for ( ; i < 4; i += 2 - includeWidth ) { which = cssExpand[ i ]; attrs[ "margin" + which ] = attrs[ "padding" + which ] = type; } if ( includeWidth ) { attrs.opacity = attrs.width = type; } return attrs; } function createTween( value, prop, animation ) { var tween, collection = ( Animation.tweeners[ prop ] || [] ).concat( Animation.tweeners[ "*" ] ), index = 0, length = collection.length; for ( ; index < length; index++ ) { if ( ( tween = collection[ index ].call( animation, prop, value ) ) ) { // We're done with this property return tween; } } } function defaultPrefilter( elem, props, opts ) { var prop, value, toggle, hooks, oldfire, propTween, restoreDisplay, display, isBox = "width" in props || "height" in props, anim = this, orig = {}, style = elem.style, hidden = elem.nodeType && isHiddenWithinTree( elem ), dataShow = dataPriv.get( elem, "fxshow" ); // Queue-skipping animations hijack the fx hooks if ( !opts.queue ) { hooks = jQuery._queueHooks( elem, "fx" ); if ( hooks.unqueued == null ) { hooks.unqueued = 0; oldfire = hooks.empty.fire; hooks.empty.fire = function() { if ( !hooks.unqueued ) { oldfire(); } }; } hooks.unqueued++; anim.always( function() { // Ensure the complete handler is called before this completes anim.always( function() { hooks.unqueued--; if ( !jQuery.queue( elem, "fx" ).length ) { hooks.empty.fire(); } } ); } ); } // Detect show/hide animations for ( prop in props ) { value = props[ prop ]; if ( rfxtypes.test( value ) ) { delete props[ prop ]; toggle = toggle || value === "toggle"; if ( value === ( hidden ? "hide" : "show" ) ) { // Pretend to be hidden if this is a "show" and // there is still data from a stopped show/hide if ( value === "show" && dataShow && dataShow[ prop ] !== undefined ) { hidden = true; // Ignore all other no-op show/hide data } else { continue; } } orig[ prop ] = dataShow && dataShow[ prop ] || jQuery.style( elem, prop ); } } // Bail out if this is a no-op like .hide().hide() propTween = !jQuery.isEmptyObject( props ); if ( !propTween && jQuery.isEmptyObject( orig ) ) { return; } // Restrict "overflow" and "display" styles during box animations if ( isBox && elem.nodeType === 1 ) { // Support: IE <=9 - 11, Edge 12 - 13 // Record all 3 overflow attributes because IE does not infer the shorthand // from identically-valued overflowX and overflowY opts.overflow = [ style.overflow, style.overflowX, style.overflowY ]; // Identify a display type, preferring old show/hide data over the CSS cascade restoreDisplay = dataShow && dataShow.display; if ( restoreDisplay == null ) { restoreDisplay = dataPriv.get( elem, "display" ); } display = jQuery.css( elem, "display" ); if ( display === "none" ) { if ( restoreDisplay ) { display = restoreDisplay; } else { // Get nonempty value(s) by temporarily forcing visibility showHide( [ elem ], true ); restoreDisplay = elem.style.display || restoreDisplay; display = jQuery.css( elem, "display" ); showHide( [ elem ] ); } } // Animate inline elements as inline-block if ( display === "inline" || display === "inline-block" && restoreDisplay != null ) { if ( jQuery.css( elem, "float" ) === "none" ) { // Restore the original display value at the end of pure show/hide animations if ( !propTween ) { anim.done( function() { style.display = restoreDisplay; } ); if ( restoreDisplay == null ) { display = style.display; restoreDisplay = display === "none" ? "" : display; } } style.display = "inline-block"; } } } if ( opts.overflow ) { style.overflow = "hidden"; anim.always( function() { style.overflow = opts.overflow[ 0 ]; style.overflowX = opts.overflow[ 1 ]; style.overflowY = opts.overflow[ 2 ]; } ); } // Implement show/hide animations propTween = false; for ( prop in orig ) { // General show/hide setup for this element animation if ( !propTween ) { if ( dataShow ) { if ( "hidden" in dataShow ) { hidden = dataShow.hidden; } } else { dataShow = dataPriv.access( elem, "fxshow", { display: restoreDisplay } ); } // Store hidden/visible for toggle so `.stop().toggle()` "reverses" if ( toggle ) { dataShow.hidden = !hidden; } // Show elements before animating them if ( hidden ) { showHide( [ elem ], true ); } /* eslint-disable no-loop-func */ anim.done( function() { /* eslint-enable no-loop-func */ // The final step of a "hide" animation is actually hiding the element if ( !hidden ) { showHide( [ elem ] ); } dataPriv.remove( elem, "fxshow" ); for ( prop in orig ) { jQuery.style( elem, prop, orig[ prop ] ); } } ); } // Per-property setup propTween = createTween( hidden ? dataShow[ prop ] : 0, prop, anim ); if ( !( prop in dataShow ) ) { dataShow[ prop ] = propTween.start; if ( hidden ) { propTween.end = propTween.start; propTween.start = 0; } } } } function propFilter( props, specialEasing ) { var index, name, easing, value, hooks; // camelCase, specialEasing and expand cssHook pass for ( index in props ) { name = jQuery.camelCase( index ); easing = specialEasing[ name ]; value = props[ index ]; if ( Array.isArray( value ) ) { easing = value[ 1 ]; value = props[ index ] = value[ 0 ]; } if ( index !== name ) { props[ name ] = value; delete props[ index ]; } hooks = jQuery.cssHooks[ name ]; if ( hooks && "expand" in hooks ) { value = hooks.expand( value ); delete props[ name ]; // Not quite $.extend, this won't overwrite existing keys. // Reusing 'index' because we have the correct "name" for ( index in value ) { if ( !( index in props ) ) { props[ index ] = value[ index ]; specialEasing[ index ] = easing; } } } else { specialEasing[ name ] = easing; } } } function Animation( elem, properties, options ) { var result, stopped, index = 0, length = Animation.prefilters.length, deferred = jQuery.Deferred().always( function() { // Don't match elem in the :animated selector delete tick.elem; } ), tick = function() { if ( stopped ) { return false; } var currentTime = fxNow || createFxNow(), remaining = Math.max( 0, animation.startTime + animation.duration - currentTime ), // Support: Android 2.3 only // Archaic crash bug won't allow us to use `1 - ( 0.5 || 0 )` (#12497) temp = remaining / animation.duration || 0, percent = 1 - temp, index = 0, length = animation.tweens.length; for ( ; index < length; index++ ) { animation.tweens[ index ].run( percent ); } deferred.notifyWith( elem, [ animation, percent, remaining ] ); // If there's more to do, yield if ( percent < 1 && length ) { return remaining; } // If this was an empty animation, synthesize a final progress notification if ( !length ) { deferred.notifyWith( elem, [ animation, 1, 0 ] ); } // Resolve the animation and report its conclusion deferred.resolveWith( elem, [ animation ] ); return false; }, animation = deferred.promise( { elem: elem, props: jQuery.extend( {}, properties ), opts: jQuery.extend( true, { specialEasing: {}, easing: jQuery.easing._default }, options ), originalProperties: properties, originalOptions: options, startTime: fxNow || createFxNow(), duration: options.duration, tweens: [], createTween: function( prop, end ) { var tween = jQuery.Tween( elem, animation.opts, prop, end, animation.opts.specialEasing[ prop ] || animation.opts.easing ); animation.tweens.push( tween ); return tween; }, stop: function( gotoEnd ) { var index = 0, // If we are going to the end, we want to run all the tweens // otherwise we skip this part length = gotoEnd ? animation.tweens.length : 0; if ( stopped ) { return this; } stopped = true; for ( ; index < length; index++ ) { animation.tweens[ index ].run( 1 ); } // Resolve when we played the last frame; otherwise, reject if ( gotoEnd ) { deferred.notifyWith( elem, [ animation, 1, 0 ] ); deferred.resolveWith( elem, [ animation, gotoEnd ] ); } else { deferred.rejectWith( elem, [ animation, gotoEnd ] ); } return this; } } ), props = animation.props; propFilter( props, animation.opts.specialEasing ); for ( ; index < length; index++ ) { result = Animation.prefilters[ index ].call( animation, elem, props, animation.opts ); if ( result ) { if ( jQuery.isFunction( result.stop ) ) { jQuery._queueHooks( animation.elem, animation.opts.queue ).stop = jQuery.proxy( result.stop, result ); } return result; } } jQuery.map( props, createTween, animation ); if ( jQuery.isFunction( animation.opts.start ) ) { animation.opts.start.call( elem, animation ); } // Attach callbacks from options animation .progress( animation.opts.progress ) .done( animation.opts.done, animation.opts.complete ) .fail( animation.opts.fail ) .always( animation.opts.always ); jQuery.fx.timer( jQuery.extend( tick, { elem: elem, anim: animation, queue: animation.opts.queue } ) ); return animation; } jQuery.Animation = jQuery.extend( Animation, { tweeners: { "*": [ function( prop, value ) { var tween = this.createTween( prop, value ); adjustCSS( tween.elem, prop, rcssNum.exec( value ), tween ); return tween; } ] }, tweener: function( props, callback ) { if ( jQuery.isFunction( props ) ) { callback = props; props = [ "*" ]; } else { props = props.match( rnothtmlwhite ); } var prop, index = 0, length = props.length; for ( ; index < length; index++ ) { prop = props[ index ]; Animation.tweeners[ prop ] = Animation.tweeners[ prop ] || []; Animation.tweeners[ prop ].unshift( callback ); } }, prefilters: [ defaultPrefilter ], prefilter: function( callback, prepend ) { if ( prepend ) { Animation.prefilters.unshift( callback ); } else { Animation.prefilters.push( callback ); } } } ); jQuery.speed = function( speed, easing, fn ) { var opt = speed && typeof speed === "object" ? jQuery.extend( {}, speed ) : { complete: fn || !fn && easing || jQuery.isFunction( speed ) && speed, duration: speed, easing: fn && easing || easing && !jQuery.isFunction( easing ) && easing }; // Go to the end state if fx are off if ( jQuery.fx.off ) { opt.duration = 0; } else { if ( typeof opt.duration !== "number" ) { if ( opt.duration in jQuery.fx.speeds ) { opt.duration = jQuery.fx.speeds[ opt.duration ]; } else { opt.duration = jQuery.fx.speeds._default; } } } // Normalize opt.queue - true/undefined/null -> "fx" if ( opt.queue == null || opt.queue === true ) { opt.queue = "fx"; } // Queueing opt.old = opt.complete; opt.complete = function() { if ( jQuery.isFunction( opt.old ) ) { opt.old.call( this ); } if ( opt.queue ) { jQuery.dequeue( this, opt.queue ); } }; return opt; }; jQuery.fn.extend( { fadeTo: function( speed, to, easing, callback ) { // Show any hidden elements after setting opacity to 0 return this.filter( isHiddenWithinTree ).css( "opacity", 0 ).show() // Animate to the value specified .end().animate( { opacity: to }, speed, easing, callback ); }, animate: function( prop, speed, easing, callback ) { var empty = jQuery.isEmptyObject( prop ), optall = jQuery.speed( speed, easing, callback ), doAnimation = function() { // Operate on a copy of prop so per-property easing won't be lost var anim = Animation( this, jQuery.extend( {}, prop ), optall ); // Empty animations, or finishing resolves immediately if ( empty || dataPriv.get( this, "finish" ) ) { anim.stop( true ); } }; doAnimation.finish = doAnimation; return empty || optall.queue === false ? this.each( doAnimation ) : this.queue( optall.queue, doAnimation ); }, stop: function( type, clearQueue, gotoEnd ) { var stopQueue = function( hooks ) { var stop = hooks.stop; delete hooks.stop; stop( gotoEnd ); }; if ( typeof type !== "string" ) { gotoEnd = clearQueue; clearQueue = type; type = undefined; } if ( clearQueue && type !== false ) { this.queue( type || "fx", [] ); } return this.each( function() { var dequeue = true, index = type != null && type + "queueHooks", timers = jQuery.timers, data = dataPriv.get( this ); if ( index ) { if ( data[ index ] && data[ index ].stop ) { stopQueue( data[ index ] ); } } else { for ( index in data ) { if ( data[ index ] && data[ index ].stop && rrun.test( index ) ) { stopQueue( data[ index ] ); } } } for ( index = timers.length; index--; ) { if ( timers[ index ].elem === this && ( type == null || timers[ index ].queue === type ) ) { timers[ index ].anim.stop( gotoEnd ); dequeue = false; timers.splice( index, 1 ); } } // Start the next in the queue if the last step wasn't forced. // Timers currently will call their complete callbacks, which // will dequeue but only if they were gotoEnd. if ( dequeue || !gotoEnd ) { jQuery.dequeue( this, type ); } } ); }, finish: function( type ) { if ( type !== false ) { type = type || "fx"; } return this.each( function() { var index, data = dataPriv.get( this ), queue = data[ type + "queue" ], hooks = data[ type + "queueHooks" ], timers = jQuery.timers, length = queue ? queue.length : 0; // Enable finishing flag on private data data.finish = true; // Empty the queue first jQuery.queue( this, type, [] ); if ( hooks && hooks.stop ) { hooks.stop.call( this, true ); } // Look for any active animations, and finish them for ( index = timers.length; index--; ) { if ( timers[ index ].elem === this && timers[ index ].queue === type ) { timers[ index ].anim.stop( true ); timers.splice( index, 1 ); } } // Look for any animations in the old queue and finish them for ( index = 0; index < length; index++ ) { if ( queue[ index ] && queue[ index ].finish ) { queue[ index ].finish.call( this ); } } // Turn off finishing flag delete data.finish; } ); } } ); jQuery.each( [ "toggle", "show", "hide" ], function( i, name ) { var cssFn = jQuery.fn[ name ]; jQuery.fn[ name ] = function( speed, easing, callback ) { return speed == null || typeof speed === "boolean" ? cssFn.apply( this, arguments ) : this.animate( genFx( name, true ), speed, easing, callback ); }; } ); // Generate shortcuts for custom animations jQuery.each( { slideDown: genFx( "show" ), slideUp: genFx( "hide" ), slideToggle: genFx( "toggle" ), fadeIn: { opacity: "show" }, fadeOut: { opacity: "hide" }, fadeToggle: { opacity: "toggle" } }, function( name, props ) { jQuery.fn[ name ] = function( speed, easing, callback ) { return this.animate( props, speed, easing, callback ); }; } ); jQuery.timers = []; jQuery.fx.tick = function() { var timer, i = 0, timers = jQuery.timers; fxNow = jQuery.now(); for ( ; i < timers.length; i++ ) { timer = timers[ i ]; // Run the timer and safely remove it when done (allowing for external removal) if ( !timer() && timers[ i ] === timer ) { timers.splice( i--, 1 ); } } if ( !timers.length ) { jQuery.fx.stop(); } fxNow = undefined; }; jQuery.fx.timer = function( timer ) { jQuery.timers.push( timer ); jQuery.fx.start(); }; jQuery.fx.interval = 13; jQuery.fx.start = function() { if ( inProgress ) { return; } inProgress = true; schedule(); }; jQuery.fx.stop = function() { inProgress = null; }; jQuery.fx.speeds = { slow: 600, fast: 200, // Default speed _default: 400 }; // Based off of the plugin by Clint Helfers, with permission. // https://web.archive.org/web/20100324014747/http://blindsignals.com/index.php/2009/07/jquery-delay/ jQuery.fn.delay = function( time, type ) { time = jQuery.fx ? jQuery.fx.speeds[ time ] || time : time; type = type || "fx"; return this.queue( type, function( next, hooks ) { var timeout = window.setTimeout( next, time ); hooks.stop = function() { window.clearTimeout( timeout ); }; } ); }; ( function() { var input = document.createElement( "input" ), select = document.createElement( "select" ), opt = select.appendChild( document.createElement( "option" ) ); input.type = "checkbox"; // Support: Android <=4.3 only // Default value for a checkbox should be "on" support.checkOn = input.value !== ""; // Support: IE <=11 only // Must access selectedIndex to make default options select support.optSelected = opt.selected; // Support: IE <=11 only // An input loses its value after becoming a radio input = document.createElement( "input" ); input.value = "t"; input.type = "radio"; support.radioValue = input.value === "t"; } )(); var boolHook, attrHandle = jQuery.expr.attrHandle; jQuery.fn.extend( { attr: function( name, value ) { return access( this, jQuery.attr, name, value, arguments.length > 1 ); }, removeAttr: function( name ) { return this.each( function() { jQuery.removeAttr( this, name ); } ); } } ); jQuery.extend( { attr: function( elem, name, value ) { var ret, hooks, nType = elem.nodeType; // Don't get/set attributes on text, comment and attribute nodes if ( nType === 3 || nType === 8 || nType === 2 ) { return; } // Fallback to prop when attributes are not supported if ( typeof elem.getAttribute === "undefined" ) { return jQuery.prop( elem, name, value ); } // Attribute hooks are determined by the lowercase version // Grab necessary hook if one is defined if ( nType !== 1 || !jQuery.isXMLDoc( elem ) ) { hooks = jQuery.attrHooks[ name.toLowerCase() ] || ( jQuery.expr.match.bool.test( name ) ? boolHook : undefined ); } if ( value !== undefined ) { if ( value === null ) { jQuery.removeAttr( elem, name ); return; } if ( hooks && "set" in hooks && ( ret = hooks.set( elem, value, name ) ) !== undefined ) { return ret; } elem.setAttribute( name, value + "" ); return value; } if ( hooks && "get" in hooks && ( ret = hooks.get( elem, name ) ) !== null ) { return ret; } ret = jQuery.find.attr( elem, name ); // Non-existent attributes return null, we normalize to undefined return ret == null ? undefined : ret; }, attrHooks: { type: { set: function( elem, value ) { if ( !support.radioValue && value === "radio" && nodeName( elem, "input" ) ) { var val = elem.value; elem.setAttribute( "type", value ); if ( val ) { elem.value = val; } return value; } } } }, removeAttr: function( elem, value ) { var name, i = 0, // Attribute names can contain non-HTML whitespace characters // https://html.spec.whatwg.org/multipage/syntax.html#attributes-2 attrNames = value && value.match( rnothtmlwhite ); if ( attrNames && elem.nodeType === 1 ) { while ( ( name = attrNames[ i++ ] ) ) { elem.removeAttribute( name ); } } } } ); // Hooks for boolean attributes boolHook = { set: function( elem, value, name ) { if ( value === false ) { // Remove boolean attributes when set to false jQuery.removeAttr( elem, name ); } else { elem.setAttribute( name, name ); } return name; } }; jQuery.each( jQuery.expr.match.bool.source.match( /\w+/g ), function( i, name ) { var getter = attrHandle[ name ] || jQuery.find.attr; attrHandle[ name ] = function( elem, name, isXML ) { var ret, handle, lowercaseName = name.toLowerCase(); if ( !isXML ) { // Avoid an infinite loop by temporarily removing this function from the getter handle = attrHandle[ lowercaseName ]; attrHandle[ lowercaseName ] = ret; ret = getter( elem, name, isXML ) != null ? lowercaseName : null; attrHandle[ lowercaseName ] = handle; } return ret; }; } ); var rfocusable = /^(?:input|select|textarea|button)$/i, rclickable = /^(?:a|area)$/i; jQuery.fn.extend( { prop: function( name, value ) { return access( this, jQuery.prop, name, value, arguments.length > 1 ); }, removeProp: function( name ) { return this.each( function() { delete this[ jQuery.propFix[ name ] || name ]; } ); } } ); jQuery.extend( { prop: function( elem, name, value ) { var ret, hooks, nType = elem.nodeType; // Don't get/set properties on text, comment and attribute nodes if ( nType === 3 || nType === 8 || nType === 2 ) { return; } if ( nType !== 1 || !jQuery.isXMLDoc( elem ) ) { // Fix name and attach hooks name = jQuery.propFix[ name ] || name; hooks = jQuery.propHooks[ name ]; } if ( value !== undefined ) { if ( hooks && "set" in hooks && ( ret = hooks.set( elem, value, name ) ) !== undefined ) { return ret; } return ( elem[ name ] = value ); } if ( hooks && "get" in hooks && ( ret = hooks.get( elem, name ) ) !== null ) { return ret; } return elem[ name ]; }, propHooks: { tabIndex: { get: function( elem ) { // Support: IE <=9 - 11 only // elem.tabIndex doesn't always return the // correct value when it hasn't been explicitly set // https://web.archive.org/web/20141116233347/http://fluidproject.org/blog/2008/01/09/getting-setting-and-removing-tabindex-values-with-javascript/ // Use proper attribute retrieval(#12072) var tabindex = jQuery.find.attr( elem, "tabindex" ); if ( tabindex ) { return parseInt( tabindex, 10 ); } if ( rfocusable.test( elem.nodeName ) || rclickable.test( elem.nodeName ) && elem.href ) { return 0; } return -1; } } }, propFix: { "for": "htmlFor", "class": "className" } } ); // Support: IE <=11 only // Accessing the selectedIndex property // forces the browser to respect setting selected // on the option // The getter ensures a default option is selected // when in an optgroup // eslint rule "no-unused-expressions" is disabled for this code // since it considers such accessions noop if ( !support.optSelected ) { jQuery.propHooks.selected = { get: function( elem ) { /* eslint no-unused-expressions: "off" */ var parent = elem.parentNode; if ( parent && parent.parentNode ) { parent.parentNode.selectedIndex; } return null; }, set: function( elem ) { /* eslint no-unused-expressions: "off" */ var parent = elem.parentNode; if ( parent ) { parent.selectedIndex; if ( parent.parentNode ) { parent.parentNode.selectedIndex; } } } }; } jQuery.each( [ "tabIndex", "readOnly", "maxLength", "cellSpacing", "cellPadding", "rowSpan", "colSpan", "useMap", "frameBorder", "contentEditable" ], function() { jQuery.propFix[ this.toLowerCase() ] = this; } ); // Strip and collapse whitespace according to HTML spec // https://html.spec.whatwg.org/multipage/infrastructure.html#strip-and-collapse-whitespace function stripAndCollapse( value ) { var tokens = value.match( rnothtmlwhite ) || []; return tokens.join( " " ); } function getClass( elem ) { return elem.getAttribute && elem.getAttribute( "class" ) || ""; } jQuery.fn.extend( { addClass: function( value ) { var classes, elem, cur, curValue, clazz, j, finalValue, i = 0; if ( jQuery.isFunction( value ) ) { return this.each( function( j ) { jQuery( this ).addClass( value.call( this, j, getClass( this ) ) ); } ); } if ( typeof value === "string" && value ) { classes = value.match( rnothtmlwhite ) || []; while ( ( elem = this[ i++ ] ) ) { curValue = getClass( elem ); cur = elem.nodeType === 1 && ( " " + stripAndCollapse( curValue ) + " " ); if ( cur ) { j = 0; while ( ( clazz = classes[ j++ ] ) ) { if ( cur.indexOf( " " + clazz + " " ) < 0 ) { cur += clazz + " "; } } // Only assign if different to avoid unneeded rendering. finalValue = stripAndCollapse( cur ); if ( curValue !== finalValue ) { elem.setAttribute( "class", finalValue ); } } } } return this; }, removeClass: function( value ) { var classes, elem, cur, curValue, clazz, j, finalValue, i = 0; if ( jQuery.isFunction( value ) ) { return this.each( function( j ) { jQuery( this ).removeClass( value.call( this, j, getClass( this ) ) ); } ); } if ( !arguments.length ) { return this.attr( "class", "" ); } if ( typeof value === "string" && value ) { classes = value.match( rnothtmlwhite ) || []; while ( ( elem = this[ i++ ] ) ) { curValue = getClass( elem ); // This expression is here for better compressibility (see addClass) cur = elem.nodeType === 1 && ( " " + stripAndCollapse( curValue ) + " " ); if ( cur ) { j = 0; while ( ( clazz = classes[ j++ ] ) ) { // Remove *all* instances while ( cur.indexOf( " " + clazz + " " ) > -1 ) { cur = cur.replace( " " + clazz + " ", " " ); } } // Only assign if different to avoid unneeded rendering. finalValue = stripAndCollapse( cur ); if ( curValue !== finalValue ) { elem.setAttribute( "class", finalValue ); } } } } return this; }, toggleClass: function( value, stateVal ) { var type = typeof value; if ( typeof stateVal === "boolean" && type === "string" ) { return stateVal ? this.addClass( value ) : this.removeClass( value ); } if ( jQuery.isFunction( value ) ) { return this.each( function( i ) { jQuery( this ).toggleClass( value.call( this, i, getClass( this ), stateVal ), stateVal ); } ); } return this.each( function() { var className, i, self, classNames; if ( type === "string" ) { // Toggle individual class names i = 0; self = jQuery( this ); classNames = value.match( rnothtmlwhite ) || []; while ( ( className = classNames[ i++ ] ) ) { // Check each className given, space separated list if ( self.hasClass( className ) ) { self.removeClass( className ); } else { self.addClass( className ); } } // Toggle whole class name } else if ( value === undefined || type === "boolean" ) { className = getClass( this ); if ( className ) { // Store className if set dataPriv.set( this, "__className__", className ); } // If the element has a class name or if we're passed `false`, // then remove the whole classname (if there was one, the above saved it). // Otherwise bring back whatever was previously saved (if anything), // falling back to the empty string if nothing was stored. if ( this.setAttribute ) { this.setAttribute( "class", className || value === false ? "" : dataPriv.get( this, "__className__" ) || "" ); } } } ); }, hasClass: function( selector ) { var className, elem, i = 0; className = " " + selector + " "; while ( ( elem = this[ i++ ] ) ) { if ( elem.nodeType === 1 && ( " " + stripAndCollapse( getClass( elem ) ) + " " ).indexOf( className ) > -1 ) { return true; } } return false; } } ); var rreturn = /\r/g; jQuery.fn.extend( { val: function( value ) { var hooks, ret, isFunction, elem = this[ 0 ]; if ( !arguments.length ) { if ( elem ) { hooks = jQuery.valHooks[ elem.type ] || jQuery.valHooks[ elem.nodeName.toLowerCase() ]; if ( hooks && "get" in hooks && ( ret = hooks.get( elem, "value" ) ) !== undefined ) { return ret; } ret = elem.value; // Handle most common string cases if ( typeof ret === "string" ) { return ret.replace( rreturn, "" ); } // Handle cases where value is null/undef or number return ret == null ? "" : ret; } return; } isFunction = jQuery.isFunction( value ); return this.each( function( i ) { var val; if ( this.nodeType !== 1 ) { return; } if ( isFunction ) { val = value.call( this, i, jQuery( this ).val() ); } else { val = value; } // Treat null/undefined as ""; convert numbers to string if ( val == null ) { val = ""; } else if ( typeof val === "number" ) { val += ""; } else if ( Array.isArray( val ) ) { val = jQuery.map( val, function( value ) { return value == null ? "" : value + ""; } ); } hooks = jQuery.valHooks[ this.type ] || jQuery.valHooks[ this.nodeName.toLowerCase() ]; // If set returns undefined, fall back to normal setting if ( !hooks || !( "set" in hooks ) || hooks.set( this, val, "value" ) === undefined ) { this.value = val; } } ); } } ); jQuery.extend( { valHooks: { option: { get: function( elem ) { var val = jQuery.find.attr( elem, "value" ); return val != null ? val : // Support: IE <=10 - 11 only // option.text throws exceptions (#14686, #14858) // Strip and collapse whitespace // https://html.spec.whatwg.org/#strip-and-collapse-whitespace stripAndCollapse( jQuery.text( elem ) ); } }, select: { get: function( elem ) { var value, option, i, options = elem.options, index = elem.selectedIndex, one = elem.type === "select-one", values = one ? null : [], max = one ? index + 1 : options.length; if ( index < 0 ) { i = max; } else { i = one ? index : 0; } // Loop through all the selected options for ( ; i < max; i++ ) { option = options[ i ]; // Support: IE <=9 only // IE8-9 doesn't update selected after form reset (#2551) if ( ( option.selected || i === index ) && // Don't return options that are disabled or in a disabled optgroup !option.disabled && ( !option.parentNode.disabled || !nodeName( option.parentNode, "optgroup" ) ) ) { // Get the specific value for the option value = jQuery( option ).val(); // We don't need an array for one selects if ( one ) { return value; } // Multi-Selects return an array values.push( value ); } } return values; }, set: function( elem, value ) { var optionSet, option, options = elem.options, values = jQuery.makeArray( value ), i = options.length; while ( i-- ) { option = options[ i ]; /* eslint-disable no-cond-assign */ if ( option.selected = jQuery.inArray( jQuery.valHooks.option.get( option ), values ) > -1 ) { optionSet = true; } /* eslint-enable no-cond-assign */ } // Force browsers to behave consistently when non-matching value is set if ( !optionSet ) { elem.selectedIndex = -1; } return values; } } } } ); // Radios and checkboxes getter/setter jQuery.each( [ "radio", "checkbox" ], function() { jQuery.valHooks[ this ] = { set: function( elem, value ) { if ( Array.isArray( value ) ) { return ( elem.checked = jQuery.inArray( jQuery( elem ).val(), value ) > -1 ); } } }; if ( !support.checkOn ) { jQuery.valHooks[ this ].get = function( elem ) { return elem.getAttribute( "value" ) === null ? "on" : elem.value; }; } } ); // Return jQuery for attributes-only inclusion var rfocusMorph = /^(?:focusinfocus|focusoutblur)$/; jQuery.extend( jQuery.event, { trigger: function( event, data, elem, onlyHandlers ) { var i, cur, tmp, bubbleType, ontype, handle, special, eventPath = [ elem || document ], type = hasOwn.call( event, "type" ) ? event.type : event, namespaces = hasOwn.call( event, "namespace" ) ? event.namespace.split( "." ) : []; cur = tmp = elem = elem || document; // Don't do events on text and comment nodes if ( elem.nodeType === 3 || elem.nodeType === 8 ) { return; } // focus/blur morphs to focusin/out; ensure we're not firing them right now if ( rfocusMorph.test( type + jQuery.event.triggered ) ) { return; } if ( type.indexOf( "." ) > -1 ) { // Namespaced trigger; create a regexp to match event type in handle() namespaces = type.split( "." ); type = namespaces.shift(); namespaces.sort(); } ontype = type.indexOf( ":" ) < 0 && "on" + type; // Caller can pass in a jQuery.Event object, Object, or just an event type string event = event[ jQuery.expando ] ? event : new jQuery.Event( type, typeof event === "object" && event ); // Trigger bitmask: & 1 for native handlers; & 2 for jQuery (always true) event.isTrigger = onlyHandlers ? 2 : 3; event.namespace = namespaces.join( "." ); event.rnamespace = event.namespace ? new RegExp( "(^|\\.)" + namespaces.join( "\\.(?:.*\\.|)" ) + "(\\.|$)" ) : null; // Clean up the event in case it is being reused event.result = undefined; if ( !event.target ) { event.target = elem; } // Clone any incoming data and prepend the event, creating the handler arg list data = data == null ? [ event ] : jQuery.makeArray( data, [ event ] ); // Allow special events to draw outside the lines special = jQuery.event.special[ type ] || {}; if ( !onlyHandlers && special.trigger && special.trigger.apply( elem, data ) === false ) { return; } // Determine event propagation path in advance, per W3C events spec (#9951) // Bubble up to document, then to window; watch for a global ownerDocument var (#9724) if ( !onlyHandlers && !special.noBubble && !jQuery.isWindow( elem ) ) { bubbleType = special.delegateType || type; if ( !rfocusMorph.test( bubbleType + type ) ) { cur = cur.parentNode; } for ( ; cur; cur = cur.parentNode ) { eventPath.push( cur ); tmp = cur; } // Only add window if we got to document (e.g., not plain obj or detached DOM) if ( tmp === ( elem.ownerDocument || document ) ) { eventPath.push( tmp.defaultView || tmp.parentWindow || window ); } } // Fire handlers on the event path i = 0; while ( ( cur = eventPath[ i++ ] ) && !event.isPropagationStopped() ) { event.type = i > 1 ? bubbleType : special.bindType || type; // jQuery handler handle = ( dataPriv.get( cur, "events" ) || {} )[ event.type ] && dataPriv.get( cur, "handle" ); if ( handle ) { handle.apply( cur, data ); } // Native handler handle = ontype && cur[ ontype ]; if ( handle && handle.apply && acceptData( cur ) ) { event.result = handle.apply( cur, data ); if ( event.result === false ) { event.preventDefault(); } } } event.type = type; // If nobody prevented the default action, do it now if ( !onlyHandlers && !event.isDefaultPrevented() ) { if ( ( !special._default || special._default.apply( eventPath.pop(), data ) === false ) && acceptData( elem ) ) { // Call a native DOM method on the target with the same name as the event. // Don't do default actions on window, that's where global variables be (#6170) if ( ontype && jQuery.isFunction( elem[ type ] ) && !jQuery.isWindow( elem ) ) { // Don't re-trigger an onFOO event when we call its FOO() method tmp = elem[ ontype ]; if ( tmp ) { elem[ ontype ] = null; } // Prevent re-triggering of the same event, since we already bubbled it above jQuery.event.triggered = type; elem[ type ](); jQuery.event.triggered = undefined; if ( tmp ) { elem[ ontype ] = tmp; } } } } return event.result; }, // Piggyback on a donor event to simulate a different one // Used only for `focus(in | out)` events simulate: function( type, elem, event ) { var e = jQuery.extend( new jQuery.Event(), event, { type: type, isSimulated: true } ); jQuery.event.trigger( e, null, elem ); } } ); jQuery.fn.extend( { trigger: function( type, data ) { return this.each( function() { jQuery.event.trigger( type, data, this ); } ); }, triggerHandler: function( type, data ) { var elem = this[ 0 ]; if ( elem ) { return jQuery.event.trigger( type, data, elem, true ); } } } ); jQuery.each( ( "blur focus focusin focusout resize scroll click dblclick " + "mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave " + "change select submit keydown keypress keyup contextmenu" ).split( " " ), function( i, name ) { // Handle event binding jQuery.fn[ name ] = function( data, fn ) { return arguments.length > 0 ? this.on( name, null, data, fn ) : this.trigger( name ); }; } ); jQuery.fn.extend( { hover: function( fnOver, fnOut ) { return this.mouseenter( fnOver ).mouseleave( fnOut || fnOver ); } } ); support.focusin = "onfocusin" in window; // Support: Firefox <=44 // Firefox doesn't have focus(in | out) events // Related ticket - https://bugzilla.mozilla.org/show_bug.cgi?id=687787 // // Support: Chrome <=48 - 49, Safari <=9.0 - 9.1 // focus(in | out) events fire after focus & blur events, // which is spec violation - http://www.w3.org/TR/DOM-Level-3-Events/#events-focusevent-event-order // Related ticket - https://bugs.chromium.org/p/chromium/issues/detail?id=449857 if ( !support.focusin ) { jQuery.each( { focus: "focusin", blur: "focusout" }, function( orig, fix ) { // Attach a single capturing handler on the document while someone wants focusin/focusout var handler = function( event ) { jQuery.event.simulate( fix, event.target, jQuery.event.fix( event ) ); }; jQuery.event.special[ fix ] = { setup: function() { var doc = this.ownerDocument || this, attaches = dataPriv.access( doc, fix ); if ( !attaches ) { doc.addEventListener( orig, handler, true ); } dataPriv.access( doc, fix, ( attaches || 0 ) + 1 ); }, teardown: function() { var doc = this.ownerDocument || this, attaches = dataPriv.access( doc, fix ) - 1; if ( !attaches ) { doc.removeEventListener( orig, handler, true ); dataPriv.remove( doc, fix ); } else { dataPriv.access( doc, fix, attaches ); } } }; } ); } var location = window.location; var nonce = jQuery.now(); var rquery = ( /\?/ ); // Cross-browser xml parsing jQuery.parseXML = function( data ) { var xml; if ( !data || typeof data !== "string" ) { return null; } // Support: IE 9 - 11 only // IE throws on parseFromString with invalid input. try { xml = ( new window.DOMParser() ).parseFromString( data, "text/xml" ); } catch ( e ) { xml = undefined; } if ( !xml || xml.getElementsByTagName( "parsererror" ).length ) { jQuery.error( "Invalid XML: " + data ); } return xml; }; var rbracket = /\[\]$/, rCRLF = /\r?\n/g, rsubmitterTypes = /^(?:submit|button|image|reset|file)$/i, rsubmittable = /^(?:input|select|textarea|keygen)/i; function buildParams( prefix, obj, traditional, add ) { var name; if ( Array.isArray( obj ) ) { // Serialize array item. jQuery.each( obj, function( i, v ) { if ( traditional || rbracket.test( prefix ) ) { // Treat each array item as a scalar. add( prefix, v ); } else { // Item is non-scalar (array or object), encode its numeric index. buildParams( prefix + "[" + ( typeof v === "object" && v != null ? i : "" ) + "]", v, traditional, add ); } } ); } else if ( !traditional && jQuery.type( obj ) === "object" ) { // Serialize object item. for ( name in obj ) { buildParams( prefix + "[" + name + "]", obj[ name ], traditional, add ); } } else { // Serialize scalar item. add( prefix, obj ); } } // Serialize an array of form elements or a set of // key/values into a query string jQuery.param = function( a, traditional ) { var prefix, s = [], add = function( key, valueOrFunction ) { // If value is a function, invoke it and use its return value var value = jQuery.isFunction( valueOrFunction ) ? valueOrFunction() : valueOrFunction; s[ s.length ] = encodeURIComponent( key ) + "=" + encodeURIComponent( value == null ? "" : value ); }; // If an array was passed in, assume that it is an array of form elements. if ( Array.isArray( a ) || ( a.jquery && !jQuery.isPlainObject( a ) ) ) { // Serialize the form elements jQuery.each( a, function() { add( this.name, this.value ); } ); } else { // If traditional, encode the "old" way (the way 1.3.2 or older // did it), otherwise encode params recursively. for ( prefix in a ) { buildParams( prefix, a[ prefix ], traditional, add ); } } // Return the resulting serialization return s.join( "&" ); }; jQuery.fn.extend( { serialize: function() { return jQuery.param( this.serializeArray() ); }, serializeArray: function() { return this.map( function() { // Can add propHook for "elements" to filter or add form elements var elements = jQuery.prop( this, "elements" ); return elements ? jQuery.makeArray( elements ) : this; } ) .filter( function() { var type = this.type; // Use .is( ":disabled" ) so that fieldset[disabled] works return this.name && !jQuery( this ).is( ":disabled" ) && rsubmittable.test( this.nodeName ) && !rsubmitterTypes.test( type ) && ( this.checked || !rcheckableType.test( type ) ); } ) .map( function( i, elem ) { var val = jQuery( this ).val(); if ( val == null ) { return null; } if ( Array.isArray( val ) ) { return jQuery.map( val, function( val ) { return { name: elem.name, value: val.replace( rCRLF, "\r\n" ) }; } ); } return { name: elem.name, value: val.replace( rCRLF, "\r\n" ) }; } ).get(); } } ); var r20 = /%20/g, rhash = /#.*$/, rantiCache = /([?&])_=[^&]*/, rheaders = /^(.*?):[ \t]*([^\r\n]*)$/mg, // #7653, #8125, #8152: local protocol detection rlocalProtocol = /^(?:about|app|app-storage|.+-extension|file|res|widget):$/, rnoContent = /^(?:GET|HEAD)$/, rprotocol = /^\/\//, /* Prefilters * 1) They are useful to introduce custom dataTypes (see ajax/jsonp.js for an example) * 2) These are called: * - BEFORE asking for a transport * - AFTER param serialization (s.data is a string if s.processData is true) * 3) key is the dataType * 4) the catchall symbol "*" can be used * 5) execution will start with transport dataType and THEN continue down to "*" if needed */ prefilters = {}, /* Transports bindings * 1) key is the dataType * 2) the catchall symbol "*" can be used * 3) selection will start with transport dataType and THEN go to "*" if needed */ transports = {}, // Avoid comment-prolog char sequence (#10098); must appease lint and evade compression allTypes = "*/".concat( "*" ), // Anchor tag for parsing the document origin originAnchor = document.createElement( "a" ); originAnchor.href = location.href; // Base "constructor" for jQuery.ajaxPrefilter and jQuery.ajaxTransport function addToPrefiltersOrTransports( structure ) { // dataTypeExpression is optional and defaults to "*" return function( dataTypeExpression, func ) { if ( typeof dataTypeExpression !== "string" ) { func = dataTypeExpression; dataTypeExpression = "*"; } var dataType, i = 0, dataTypes = dataTypeExpression.toLowerCase().match( rnothtmlwhite ) || []; if ( jQuery.isFunction( func ) ) { // For each dataType in the dataTypeExpression while ( ( dataType = dataTypes[ i++ ] ) ) { // Prepend if requested if ( dataType[ 0 ] === "+" ) { dataType = dataType.slice( 1 ) || "*"; ( structure[ dataType ] = structure[ dataType ] || [] ).unshift( func ); // Otherwise append } else { ( structure[ dataType ] = structure[ dataType ] || [] ).push( func ); } } } }; } // Base inspection function for prefilters and transports function inspectPrefiltersOrTransports( structure, options, originalOptions, jqXHR ) { var inspected = {}, seekingTransport = ( structure === transports ); function inspect( dataType ) { var selected; inspected[ dataType ] = true; jQuery.each( structure[ dataType ] || [], function( _, prefilterOrFactory ) { var dataTypeOrTransport = prefilterOrFactory( options, originalOptions, jqXHR ); if ( typeof dataTypeOrTransport === "string" && !seekingTransport && !inspected[ dataTypeOrTransport ] ) { options.dataTypes.unshift( dataTypeOrTransport ); inspect( dataTypeOrTransport ); return false; } else if ( seekingTransport ) { return !( selected = dataTypeOrTransport ); } } ); return selected; } return inspect( options.dataTypes[ 0 ] ) || !inspected[ "*" ] && inspect( "*" ); } // A special extend for ajax options // that takes "flat" options (not to be deep extended) // Fixes #9887 function ajaxExtend( target, src ) { var key, deep, flatOptions = jQuery.ajaxSettings.flatOptions || {}; for ( key in src ) { if ( src[ key ] !== undefined ) { ( flatOptions[ key ] ? target : ( deep || ( deep = {} ) ) )[ key ] = src[ key ]; } } if ( deep ) { jQuery.extend( true, target, deep ); } return target; } /* Handles responses to an ajax request: * - finds the right dataType (mediates between content-type and expected dataType) * - returns the corresponding response */ function ajaxHandleResponses( s, jqXHR, responses ) { var ct, type, finalDataType, firstDataType, contents = s.contents, dataTypes = s.dataTypes; // Remove auto dataType and get content-type in the process while ( dataTypes[ 0 ] === "*" ) { dataTypes.shift(); if ( ct === undefined ) { ct = s.mimeType || jqXHR.getResponseHeader( "Content-Type" ); } } // Check if we're dealing with a known content-type if ( ct ) { for ( type in contents ) { if ( contents[ type ] && contents[ type ].test( ct ) ) { dataTypes.unshift( type ); break; } } } // Check to see if we have a response for the expected dataType if ( dataTypes[ 0 ] in responses ) { finalDataType = dataTypes[ 0 ]; } else { // Try convertible dataTypes for ( type in responses ) { if ( !dataTypes[ 0 ] || s.converters[ type + " " + dataTypes[ 0 ] ] ) { finalDataType = type; break; } if ( !firstDataType ) { firstDataType = type; } } // Or just use first one finalDataType = finalDataType || firstDataType; } // If we found a dataType // We add the dataType to the list if needed // and return the corresponding response if ( finalDataType ) { if ( finalDataType !== dataTypes[ 0 ] ) { dataTypes.unshift( finalDataType ); } return responses[ finalDataType ]; } } /* Chain conversions given the request and the original response * Also sets the responseXXX fields on the jqXHR instance */ function ajaxConvert( s, response, jqXHR, isSuccess ) { var conv2, current, conv, tmp, prev, converters = {}, // Work with a copy of dataTypes in case we need to modify it for conversion dataTypes = s.dataTypes.slice(); // Create converters map with lowercased keys if ( dataTypes[ 1 ] ) { for ( conv in s.converters ) { converters[ conv.toLowerCase() ] = s.converters[ conv ]; } } current = dataTypes.shift(); // Convert to each sequential dataType while ( current ) { if ( s.responseFields[ current ] ) { jqXHR[ s.responseFields[ current ] ] = response; } // Apply the dataFilter if provided if ( !prev && isSuccess && s.dataFilter ) { response = s.dataFilter( response, s.dataType ); } prev = current; current = dataTypes.shift(); if ( current ) { // There's only work to do if current dataType is non-auto if ( current === "*" ) { current = prev; // Convert response if prev dataType is non-auto and differs from current } else if ( prev !== "*" && prev !== current ) { // Seek a direct converter conv = converters[ prev + " " + current ] || converters[ "* " + current ]; // If none found, seek a pair if ( !conv ) { for ( conv2 in converters ) { // If conv2 outputs current tmp = conv2.split( " " ); if ( tmp[ 1 ] === current ) { // If prev can be converted to accepted input conv = converters[ prev + " " + tmp[ 0 ] ] || converters[ "* " + tmp[ 0 ] ]; if ( conv ) { // Condense equivalence converters if ( conv === true ) { conv = converters[ conv2 ]; // Otherwise, insert the intermediate dataType } else if ( converters[ conv2 ] !== true ) { current = tmp[ 0 ]; dataTypes.unshift( tmp[ 1 ] ); } break; } } } } // Apply converter (if not an equivalence) if ( conv !== true ) { // Unless errors are allowed to bubble, catch and return them if ( conv && s.throws ) { response = conv( response ); } else { try { response = conv( response ); } catch ( e ) { return { state: "parsererror", error: conv ? e : "No conversion from " + prev + " to " + current }; } } } } } } return { state: "success", data: response }; } jQuery.extend( { // Counter for holding the number of active queries active: 0, // Last-Modified header cache for next request lastModified: {}, etag: {}, ajaxSettings: { url: location.href, type: "GET", isLocal: rlocalProtocol.test( location.protocol ), global: true, processData: true, async: true, contentType: "application/x-www-form-urlencoded; charset=UTF-8", /* timeout: 0, data: null, dataType: null, username: null, password: null, cache: null, throws: false, traditional: false, headers: {}, */ accepts: { "*": allTypes, text: "text/plain", html: "text/html", xml: "application/xml, text/xml", json: "application/json, text/javascript" }, contents: { xml: /\bxml\b/, html: /\bhtml/, json: /\bjson\b/ }, responseFields: { xml: "responseXML", text: "responseText", json: "responseJSON" }, // Data converters // Keys separate source (or catchall "*") and destination types with a single space converters: { // Convert anything to text "* text": String, // Text to html (true = no transformation) "text html": true, // Evaluate text as a json expression "text json": JSON.parse, // Parse text as xml "text xml": jQuery.parseXML }, // For options that shouldn't be deep extended: // you can add your own custom options here if // and when you create one that shouldn't be // deep extended (see ajaxExtend) flatOptions: { url: true, context: true } }, // Creates a full fledged settings object into target // with both ajaxSettings and settings fields. // If target is omitted, writes into ajaxSettings. ajaxSetup: function( target, settings ) { return settings ? // Building a settings object ajaxExtend( ajaxExtend( target, jQuery.ajaxSettings ), settings ) : // Extending ajaxSettings ajaxExtend( jQuery.ajaxSettings, target ); }, ajaxPrefilter: addToPrefiltersOrTransports( prefilters ), ajaxTransport: addToPrefiltersOrTransports( transports ), // Main method ajax: function( url, options ) { // If url is an object, simulate pre-1.5 signature if ( typeof url === "object" ) { options = url; url = undefined; } // Force options to be an object options = options || {}; var transport, // URL without anti-cache param cacheURL, // Response headers responseHeadersString, responseHeaders, // timeout handle timeoutTimer, // Url cleanup var urlAnchor, // Request state (becomes false upon send and true upon completion) completed, // To know if global events are to be dispatched fireGlobals, // Loop variable i, // uncached part of the url uncached, // Create the final options object s = jQuery.ajaxSetup( {}, options ), // Callbacks context callbackContext = s.context || s, // Context for global events is callbackContext if it is a DOM node or jQuery collection globalEventContext = s.context && ( callbackContext.nodeType || callbackContext.jquery ) ? jQuery( callbackContext ) : jQuery.event, // Deferreds deferred = jQuery.Deferred(), completeDeferred = jQuery.Callbacks( "once memory" ), // Status-dependent callbacks statusCode = s.statusCode || {}, // Headers (they are sent all at once) requestHeaders = {}, requestHeadersNames = {}, // Default abort message strAbort = "canceled", // Fake xhr jqXHR = { readyState: 0, // Builds headers hashtable if needed getResponseHeader: function( key ) { var match; if ( completed ) { if ( !responseHeaders ) { responseHeaders = {}; while ( ( match = rheaders.exec( responseHeadersString ) ) ) { responseHeaders[ match[ 1 ].toLowerCase() ] = match[ 2 ]; } } match = responseHeaders[ key.toLowerCase() ]; } return match == null ? null : match; }, // Raw string getAllResponseHeaders: function() { return completed ? responseHeadersString : null; }, // Caches the header setRequestHeader: function( name, value ) { if ( completed == null ) { name = requestHeadersNames[ name.toLowerCase() ] = requestHeadersNames[ name.toLowerCase() ] || name; requestHeaders[ name ] = value; } return this; }, // Overrides response content-type header overrideMimeType: function( type ) { if ( completed == null ) { s.mimeType = type; } return this; }, // Status-dependent callbacks statusCode: function( map ) { var code; if ( map ) { if ( completed ) { // Execute the appropriate callbacks jqXHR.always( map[ jqXHR.status ] ); } else { // Lazy-add the new callbacks in a way that preserves old ones for ( code in map ) { statusCode[ code ] = [ statusCode[ code ], map[ code ] ]; } } } return this; }, // Cancel the request abort: function( statusText ) { var finalText = statusText || strAbort; if ( transport ) { transport.abort( finalText ); } done( 0, finalText ); return this; } }; // Attach deferreds deferred.promise( jqXHR ); // Add protocol if not provided (prefilters might expect it) // Handle falsy url in the settings object (#10093: consistency with old signature) // We also use the url parameter if available s.url = ( ( url || s.url || location.href ) + "" ) .replace( rprotocol, location.protocol + "//" ); // Alias method option to type as per ticket #12004 s.type = options.method || options.type || s.method || s.type; // Extract dataTypes list s.dataTypes = ( s.dataType || "*" ).toLowerCase().match( rnothtmlwhite ) || [ "" ]; // A cross-domain request is in order when the origin doesn't match the current origin. if ( s.crossDomain == null ) { urlAnchor = document.createElement( "a" ); // Support: IE <=8 - 11, Edge 12 - 13 // IE throws exception on accessing the href property if url is malformed, // e.g. http://example.com:80x/ try { urlAnchor.href = s.url; // Support: IE <=8 - 11 only // Anchor's host property isn't correctly set when s.url is relative urlAnchor.href = urlAnchor.href; s.crossDomain = originAnchor.protocol + "//" + originAnchor.host !== urlAnchor.protocol + "//" + urlAnchor.host; } catch ( e ) { // If there is an error parsing the URL, assume it is crossDomain, // it can be rejected by the transport if it is invalid s.crossDomain = true; } } // Convert data if not already a string if ( s.data && s.processData && typeof s.data !== "string" ) { s.data = jQuery.param( s.data, s.traditional ); } // Apply prefilters inspectPrefiltersOrTransports( prefilters, s, options, jqXHR ); // If request was aborted inside a prefilter, stop there if ( completed ) { return jqXHR; } // We can fire global events as of now if asked to // Don't fire events if jQuery.event is undefined in an AMD-usage scenario (#15118) fireGlobals = jQuery.event && s.global; // Watch for a new set of requests if ( fireGlobals && jQuery.active++ === 0 ) { jQuery.event.trigger( "ajaxStart" ); } // Uppercase the type s.type = s.type.toUpperCase(); // Determine if request has content s.hasContent = !rnoContent.test( s.type ); // Save the URL in case we're toying with the If-Modified-Since // and/or If-None-Match header later on // Remove hash to simplify url manipulation cacheURL = s.url.replace( rhash, "" ); // More options handling for requests with no content if ( !s.hasContent ) { // Remember the hash so we can put it back uncached = s.url.slice( cacheURL.length ); // If data is available, append data to url if ( s.data ) { cacheURL += ( rquery.test( cacheURL ) ? "&" : "?" ) + s.data; // #9682: remove data so that it's not used in an eventual retry delete s.data; } // Add or update anti-cache param if needed if ( s.cache === false ) { cacheURL = cacheURL.replace( rantiCache, "$1" ); uncached = ( rquery.test( cacheURL ) ? "&" : "?" ) + "_=" + ( nonce++ ) + uncached; } // Put hash and anti-cache on the URL that will be requested (gh-1732) s.url = cacheURL + uncached; // Change '%20' to '+' if this is encoded form body content (gh-2658) } else if ( s.data && s.processData && ( s.contentType || "" ).indexOf( "application/x-www-form-urlencoded" ) === 0 ) { s.data = s.data.replace( r20, "+" ); } // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode. if ( s.ifModified ) { if ( jQuery.lastModified[ cacheURL ] ) { jqXHR.setRequestHeader( "If-Modified-Since", jQuery.lastModified[ cacheURL ] ); } if ( jQuery.etag[ cacheURL ] ) { jqXHR.setRequestHeader( "If-None-Match", jQuery.etag[ cacheURL ] ); } } // Set the correct header, if data is being sent if ( s.data && s.hasContent && s.contentType !== false || options.contentType ) { jqXHR.setRequestHeader( "Content-Type", s.contentType ); } // Set the Accepts header for the server, depending on the dataType jqXHR.setRequestHeader( "Accept", s.dataTypes[ 0 ] && s.accepts[ s.dataTypes[ 0 ] ] ? s.accepts[ s.dataTypes[ 0 ] ] + ( s.dataTypes[ 0 ] !== "*" ? ", " + allTypes + "; q=0.01" : "" ) : s.accepts[ "*" ] ); // Check for headers option for ( i in s.headers ) { jqXHR.setRequestHeader( i, s.headers[ i ] ); } // Allow custom headers/mimetypes and early abort if ( s.beforeSend && ( s.beforeSend.call( callbackContext, jqXHR, s ) === false || completed ) ) { // Abort if not done already and return return jqXHR.abort(); } // Aborting is no longer a cancellation strAbort = "abort"; // Install callbacks on deferreds completeDeferred.add( s.complete ); jqXHR.done( s.success ); jqXHR.fail( s.error ); // Get transport transport = inspectPrefiltersOrTransports( transports, s, options, jqXHR ); // If no transport, we auto-abort if ( !transport ) { done( -1, "No Transport" ); } else { jqXHR.readyState = 1; // Send global event if ( fireGlobals ) { globalEventContext.trigger( "ajaxSend", [ jqXHR, s ] ); } // If request was aborted inside ajaxSend, stop there if ( completed ) { return jqXHR; } // Timeout if ( s.async && s.timeout > 0 ) { timeoutTimer = window.setTimeout( function() { jqXHR.abort( "timeout" ); }, s.timeout ); } try { completed = false; transport.send( requestHeaders, done ); } catch ( e ) { // Rethrow post-completion exceptions if ( completed ) { throw e; } // Propagate others as results done( -1, e ); } } // Callback for when everything is done function done( status, nativeStatusText, responses, headers ) { var isSuccess, success, error, response, modified, statusText = nativeStatusText; // Ignore repeat invocations if ( completed ) { return; } completed = true; // Clear timeout if it exists if ( timeoutTimer ) { window.clearTimeout( timeoutTimer ); } // Dereference transport for early garbage collection // (no matter how long the jqXHR object will be used) transport = undefined; // Cache response headers responseHeadersString = headers || ""; // Set readyState jqXHR.readyState = status > 0 ? 4 : 0; // Determine if successful isSuccess = status >= 200 && status < 300 || status === 304; // Get response data if ( responses ) { response = ajaxHandleResponses( s, jqXHR, responses ); } // Convert no matter what (that way responseXXX fields are always set) response = ajaxConvert( s, response, jqXHR, isSuccess ); // If successful, handle type chaining if ( isSuccess ) { // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode. if ( s.ifModified ) { modified = jqXHR.getResponseHeader( "Last-Modified" ); if ( modified ) { jQuery.lastModified[ cacheURL ] = modified; } modified = jqXHR.getResponseHeader( "etag" ); if ( modified ) { jQuery.etag[ cacheURL ] = modified; } } // if no content if ( status === 204 || s.type === "HEAD" ) { statusText = "nocontent"; // if not modified } else if ( status === 304 ) { statusText = "notmodified"; // If we have data, let's convert it } else { statusText = response.state; success = response.data; error = response.error; isSuccess = !error; } } else { // Extract error from statusText and normalize for non-aborts error = statusText; if ( status || !statusText ) { statusText = "error"; if ( status < 0 ) { status = 0; } } } // Set data for the fake xhr object jqXHR.status = status; jqXHR.statusText = ( nativeStatusText || statusText ) + ""; // Success/Error if ( isSuccess ) { deferred.resolveWith( callbackContext, [ success, statusText, jqXHR ] ); } else { deferred.rejectWith( callbackContext, [ jqXHR, statusText, error ] ); } // Status-dependent callbacks jqXHR.statusCode( statusCode ); statusCode = undefined; if ( fireGlobals ) { globalEventContext.trigger( isSuccess ? "ajaxSuccess" : "ajaxError", [ jqXHR, s, isSuccess ? success : error ] ); } // Complete completeDeferred.fireWith( callbackContext, [ jqXHR, statusText ] ); if ( fireGlobals ) { globalEventContext.trigger( "ajaxComplete", [ jqXHR, s ] ); // Handle the global AJAX counter if ( !( --jQuery.active ) ) { jQuery.event.trigger( "ajaxStop" ); } } } return jqXHR; }, getJSON: function( url, data, callback ) { return jQuery.get( url, data, callback, "json" ); }, getScript: function( url, callback ) { return jQuery.get( url, undefined, callback, "script" ); } } ); jQuery.each( [ "get", "post" ], function( i, method ) { jQuery[ method ] = function( url, data, callback, type ) { // Shift arguments if data argument was omitted if ( jQuery.isFunction( data ) ) { type = type || callback; callback = data; data = undefined; } // The url can be an options object (which then must have .url) return jQuery.ajax( jQuery.extend( { url: url, type: method, dataType: type, data: data, success: callback }, jQuery.isPlainObject( url ) && url ) ); }; } ); jQuery._evalUrl = function( url ) { return jQuery.ajax( { url: url, // Make this explicit, since user can override this through ajaxSetup (#11264) type: "GET", dataType: "script", cache: true, async: false, global: false, "throws": true } ); }; jQuery.fn.extend( { wrapAll: function( html ) { var wrap; if ( this[ 0 ] ) { if ( jQuery.isFunction( html ) ) { html = html.call( this[ 0 ] ); } // The elements to wrap the target around wrap = jQuery( html, this[ 0 ].ownerDocument ).eq( 0 ).clone( true ); if ( this[ 0 ].parentNode ) { wrap.insertBefore( this[ 0 ] ); } wrap.map( function() { var elem = this; while ( elem.firstElementChild ) { elem = elem.firstElementChild; } return elem; } ).append( this ); } return this; }, wrapInner: function( html ) { if ( jQuery.isFunction( html ) ) { return this.each( function( i ) { jQuery( this ).wrapInner( html.call( this, i ) ); } ); } return this.each( function() { var self = jQuery( this ), contents = self.contents(); if ( contents.length ) { contents.wrapAll( html ); } else { self.append( html ); } } ); }, wrap: function( html ) { var isFunction = jQuery.isFunction( html ); return this.each( function( i ) { jQuery( this ).wrapAll( isFunction ? html.call( this, i ) : html ); } ); }, unwrap: function( selector ) { this.parent( selector ).not( "body" ).each( function() { jQuery( this ).replaceWith( this.childNodes ); } ); return this; } } ); jQuery.expr.pseudos.hidden = function( elem ) { return !jQuery.expr.pseudos.visible( elem ); }; jQuery.expr.pseudos.visible = function( elem ) { return !!( elem.offsetWidth || elem.offsetHeight || elem.getClientRects().length ); }; jQuery.ajaxSettings.xhr = function() { try { return new window.XMLHttpRequest(); } catch ( e ) {} }; var xhrSuccessStatus = { // File protocol always yields status code 0, assume 200 0: 200, // Support: IE <=9 only // #1450: sometimes IE returns 1223 when it should be 204 1223: 204 }, xhrSupported = jQuery.ajaxSettings.xhr(); support.cors = !!xhrSupported && ( "withCredentials" in xhrSupported ); support.ajax = xhrSupported = !!xhrSupported; jQuery.ajaxTransport( function( options ) { var callback, errorCallback; // Cross domain only allowed if supported through XMLHttpRequest if ( support.cors || xhrSupported && !options.crossDomain ) { return { send: function( headers, complete ) { var i, xhr = options.xhr(); xhr.open( options.type, options.url, options.async, options.username, options.password ); // Apply custom fields if provided if ( options.xhrFields ) { for ( i in options.xhrFields ) { xhr[ i ] = options.xhrFields[ i ]; } } // Override mime type if needed if ( options.mimeType && xhr.overrideMimeType ) { xhr.overrideMimeType( options.mimeType ); } // X-Requested-With header // For cross-domain requests, seeing as conditions for a preflight are // akin to a jigsaw puzzle, we simply never set it to be sure. // (it can always be set on a per-request basis or even using ajaxSetup) // For same-domain requests, won't change header if already provided. if ( !options.crossDomain && !headers[ "X-Requested-With" ] ) { headers[ "X-Requested-With" ] = "XMLHttpRequest"; } // Set headers for ( i in headers ) { xhr.setRequestHeader( i, headers[ i ] ); } // Callback callback = function( type ) { return function() { if ( callback ) { callback = errorCallback = xhr.onload = xhr.onerror = xhr.onabort = xhr.onreadystatechange = null; if ( type === "abort" ) { xhr.abort(); } else if ( type === "error" ) { // Support: IE <=9 only // On a manual native abort, IE9 throws // errors on any property access that is not readyState if ( typeof xhr.status !== "number" ) { complete( 0, "error" ); } else { complete( // File: protocol always yields status 0; see #8605, #14207 xhr.status, xhr.statusText ); } } else { complete( xhrSuccessStatus[ xhr.status ] || xhr.status, xhr.statusText, // Support: IE <=9 only // IE9 has no XHR2 but throws on binary (trac-11426) // For XHR2 non-text, let the caller handle it (gh-2498) ( xhr.responseType || "text" ) !== "text" || typeof xhr.responseText !== "string" ? { binary: xhr.response } : { text: xhr.responseText }, xhr.getAllResponseHeaders() ); } } }; }; // Listen to events xhr.onload = callback(); errorCallback = xhr.onerror = callback( "error" ); // Support: IE 9 only // Use onreadystatechange to replace onabort // to handle uncaught aborts if ( xhr.onabort !== undefined ) { xhr.onabort = errorCallback; } else { xhr.onreadystatechange = function() { // Check readyState before timeout as it changes if ( xhr.readyState === 4 ) { // Allow onerror to be called first, // but that will not handle a native abort // Also, save errorCallback to a variable // as xhr.onerror cannot be accessed window.setTimeout( function() { if ( callback ) { errorCallback(); } } ); } }; } // Create the abort callback callback = callback( "abort" ); try { // Do send the request (this may raise an exception) xhr.send( options.hasContent && options.data || null ); } catch ( e ) { // #14683: Only rethrow if this hasn't been notified as an error yet if ( callback ) { throw e; } } }, abort: function() { if ( callback ) { callback(); } } }; } } ); // Prevent auto-execution of scripts when no explicit dataType was provided (See gh-2432) jQuery.ajaxPrefilter( function( s ) { if ( s.crossDomain ) { s.contents.script = false; } } ); // Install script dataType jQuery.ajaxSetup( { accepts: { script: "text/javascript, application/javascript, " + "application/ecmascript, application/x-ecmascript" }, contents: { script: /\b(?:java|ecma)script\b/ }, converters: { "text script": function( text ) { jQuery.globalEval( text ); return text; } } } ); // Handle cache's special case and crossDomain jQuery.ajaxPrefilter( "script", function( s ) { if ( s.cache === undefined ) { s.cache = false; } if ( s.crossDomain ) { s.type = "GET"; } } ); // Bind script tag hack transport jQuery.ajaxTransport( "script", function( s ) { // This transport only deals with cross domain requests if ( s.crossDomain ) { var script, callback; return { send: function( _, complete ) { script = jQuery( "

        Veusz command line and embedding interface (API)

        Introduction

        Veusz uses a common API, or set of commands, to control the program via its command line (from the Veusz console; click View, Windows, Console Window), the embedding interface (when Veusz is embedded in other Python programs), from within plugins, within documents (VSZ documents contain commands used to generate the document) or externally from the operating system command line (using veusz –listen).

        As Veusz is a a Python application it uses Python as its scripting language. You can therefore freely mix Veusz and Python commands on the Veusz command line (Click View, Windows, Console Window to get access to the command line). Veusz can also read in Python scripts from files on the command line (see the Load command).

        When commands are entered in the command prompt in the Veusz window, Veusz supports a simplified command syntax, whereq brackets following commands names, and commas, can replaced by spaces in Veusz commands (not Python commands). For example, Add('graph', name='foo'), may be entered as Add 'graph' name='foo'.

        The numpy package is already imported into the command line interface (as *), so you do not need to import it first.

        The command prompt supports history (use the up and down cursor keys to recall previous commands).

        Most of the commands listed below can be used in the in-program command line interface, using the embedding interface or using veusz –listen. Commands specific to particular modes are documented as such.

        Veusz also includes a new object-oriented version of the API, which is documented at new_api.

        Commands and API

        We list the allowed set of commands below

        Action

        Action('actionname', componentpath='.')

        Initiates the specified action on the widget (component) given the action name. Actions perform certain automated routines. These include “fit” on a fit widget, and “zeroMargins” on grids.

        Add

        Add('widgettype', name='nameforwidget', autoadd=True, optionalargs)

        The Add command adds a graph into the current widget (See the To command to change the current position).

        The first argument is the type of widget to add. These include “graph”, “page”, “axis”, “xy” and “grid”. name is the name of the new widget (if not given, it will be generated from the type of the widget plus a number). The autoadd parameter if set, constructs the default sub-widgets this widget has (for example, axes in a graph).

        Optionally, default values for the graph settings may be given, for example Add('axis', name='y', direction='vertical').

        Subsettings may be set by using double underscores, for example Add('xy', MarkerFill__color='red', ErrorBarLine__hide=True).

        Returns: Name of widget added.

        AddCustom

        AddCustom(type, name, value)

        Add a custom definition for evaluation of expressions. This can define a constant (can be in terms of other constants), a function of 1 or more variables, or a function imported from an external python module.

        ctype is “constant”, “function” or “import”.

        name is name of constant, or “function(x, y, …)” or module name.

        val is definition for constant or function (both are _strings_), or is a list of symbols for a module (comma separated items in a string).

        If mode is ‘appendalways’, the custom value is appended to the end of the list even if there is one with the same name. If mode is ‘replace’, it replaces any existing definition in the same place in the list or is appended otherwise. If mode is ‘append’, then an existing definition is deleted, and the new one appended to the end.

        AddImportPath

        AddImportPath(directory)

        Add a directory to the list of directories to try to import data from.

        CloneWidget

        CloneWidget(widget, newparent, newname=None)

        Clone the widget given, placing the copy in newparent and the name given. newname is an optional new name to give it Returns new widget path.

        Close

        Close()

        Closes the plotwindow. This is only supported in embedded mode.

        CreateHistogram

        CreateHistogram(inexpr, outbinsds, outvalsds, binparams=None, binmanual=None, method='counts', cumulative = 'none', errors=False)

        Histogram an input expression. inexpr is input expression. outbinds is the name of the dataset to create giving bin positions. outvalsds is name of dataset for bin values. binparams is None or (numbins, minval, maxval, islogbins). binmanual is None or a list of bin values. method is ‘counts’, ‘density’, or ‘fractions’. cumulative is to calculate cumulative distributions which is ‘none’, ‘smalltolarge’ or ‘largetosmall’. errors is to calculate Poisson error bars.

        DatasetPlugin

        DatasetPlugin(pluginname, fields, datasetnames={})>

        Use a dataset plugin. pluginname: name of plugin to use fields: dict of input values to plugin datasetnames: dict mapping old names to new names of datasets if they are renamed. The new name None means dataset is deleted

        EnableToolbar

        EnableToolbar(enable=True)

        Enable/disable the zooming toolbar in the plotwindow. This command is only supported in embedded mode or from veusz –listen.

        Export

        Export(filename, color=True, page=0, dpi=100, antialias=True, quality=85, backcolor='#ffffff00', pdfdpi=150, svgdpi=96, svgtextastext=False)

        Export the page given to the filename given. The filename must end with the correct extension to get the right sort of output file. Currrenly supported extensions are ‘.eps’, ‘.pdf’, ‘.ps’, ‘.svg’, ‘.jpg’, ‘.jpeg’, ‘.bmp’ and ‘.png’. If color is True, then the output is in colour, else greyscale. page is the page number of the document to export (starting from 0 for the first page!). A list of pages can be given for multipage formats (.pdf or .ps). dpi is the number of dots per inch for bitmap output files. antialias - antialiases output if True. quality is a quality parameter for jpeg output. backcolor is the background color for bitmap files, which is a name or a #RRGGBBAA value (red, green, blue, alpha). pdfdpi is the dpi to use when exporting EPS or PDF files. svgdpi is the dpi to use when exporting to SVG files. svgtextastext says whether to export SVG text as text, rather than curves.

        FilterDatasets

        FilterDatasets(filterexpr, datasets, prefix="", suffix="", invert=False, replaceblanks=False)

        Filter a list of datasets given. Creates new datasets for each with prefix and suffix added to input dataset names. filterexpr is an input numpy eexpression for filtering the datasets. If invert is set, the filter condition is inverted. If replaceblanks is set, filtered values are not removed, but replaced with a blank or NaN value. This command only works on 1D numeric, date or text datasets.

        ForceUpdate

        ForceUpdate()

        Force the window to be updated to reflect the current state of the document. Often used when periodic updates have been disabled (see SetUpdateInterval). This command is only supported in embedded mode or from veusz –listen.

        Get

        Get('settingpath')

        Returns: The value of the setting given by the path.

        >>> Get('/page1/graph1/x/min')
        'Auto'
        

        GetChildren

        GetChildren(where='.')

        Returns: The names of the widgets which are children of the path given

        GetClick

        GetClick()

        Waits for the user to click on a graph and returns the position of the click on appropriate axes. Command only works in embedded mode.

        Returns: A list containing tuples of the form (axispath, val) for each axis for which the click was in range. The value is the value on the axis for the click.

        GetColormap

        GetColormap(name, invert=False, nvals=256)

        Returns a colormap as a numpy array of red, green, blue, alpha values (ranging from 0 to 255) with the number of steps given.

        GetData

        GetData(name)

        Returns: For a 1D dataset, a tuple containing the dataset with the name given. The value is (data, symerr, negerr, poserr), with each a numpy array of the same size or None. data are the values of the dataset, symerr are the symmetric errors (if set), negerr and poserr and negative and positive asymmetric errors (if set). If a text dataset, return a list of text elements. If the dataset is a date-time dataset, return a list of Python datetime objects. If the dataset is a 2D dataset return the tuple (data, rangex, rangey), where data is a 2D numpy array and rangex/y are tuples giving the range of the x and y coordinates of the data. If it is an ND dataset, return an n-dimensional array.

        data = GetData('x')
        SetData('x', data[0]*0.1, \*data[1:])
        

        GetDataType

        GetDataType(name)

        Get type of dataset with name given. Returns ‘1d’ for a 1d dataset, ‘2d’ for a 2d dataset, ‘text’ for a text dataset and ‘datetime’ for a datetime dataset.

        GetDatasets

        GetDatasets()

        Returns: The names of the datasets in the current document.

        GPL

        GPL()

        Print out the GNU Public Licence, which Veusz is licenced under.

        ImportFile

        ImportFile('filename', 'descriptor', linked=False, prefix='', suffix='', encoding='utf_8', renames={})

        Imports data from a file. The arguments are the filename to load data from and the descriptor.

        The format of the descriptor is a list of variable names representing the columns of the data. For more information see Descriptors.

        If the linked parameter is set to True, if the document is saved, the data imported will not be saved with the document, but will be reread from the filename given the next time the document is opened. The linked parameter is optional.

        If prefix and/or suffix are set, then the prefix and suffix are added to each dataset name. If set, renames maps imported dataset names to final dataset names after import.

        Returns: A tuple containing a list of the imported datasets and the number of conversions which failed for a dataset.

        Changed in version 0.5: A tuple is returned rather than just the number of imported variables.

        ImportFile2D

        ImportFile2D('filename', datasets, xrange=None, yrange=None, invertrows=False, invertcols=False, transpose=False, prefix='', suffix='', linked=False, encoding='utf8', renames={})

        Imports two-dimensional data from a file. The required arguments are the filename to load data from and the dataset name, or a list of names to use.

        filename is a string which contains the filename to use. datasets is either a string (for a single dataset), or a list of strings (for multiple datasets).

        The xrange parameter is a tuple which contains the range of the X-axis along the two-dimensional dataset, for example (-1., 1.) represents an inclusive range of -1 to 1. The yrange parameter specifies the range of the Y-axis similarly. If they are not specified, (0, N) is the default, where N is the number of datapoints along a particular axis.

        invertrows and invertcols if set to True, invert the rows and columns respectively after they are read by Veusz. transpose swaps the rows and columns.

        If prefix and/or suffix are set, they are prepended or appended to imported dataset names. If set, renames maps imported dataset names to final dataset names after import.

        If the linked parameter is True, then the datasets are linked to the imported file, and are not saved within a saved document.

        The file format this command accepts is a two-dimensional matrix of numbers, with the columns separated by spaces or tabs, and the rows separated by new lines. The X-coordinate is taken to be in the direction of the columns. Comments are supported (use #, ! or %), as are continuation characters (\). Separate datasets are deliminated by using blank lines.

        In addition to the matrix of numbers, the various optional parameters this command takes can also be specified in the data file. These commands should be given on separate lines before the matrix of numbers. They are:

        1. xrange A B
        2. yrange C D
        3. invertrows
        4. invertcols
        5. transpose

        ImportFileCSV

        ImportFileCSV('filename', readrows=False, dsprefix='', dssuffix='', linked=False, encoding='utf_8', renames={})

        This command imports data from a CSV format file. Data are read from the file using the dataset names given at the top of the files in columns. Please see the reading data section of this manual for more information. dsprefix is prepended to each dataset name and dssuffix is added (the prefix option is deprecated and also addeds an underscore to the dataset name). linked specifies whether the data will be linked to the file. renames, if set, provides new names for datasets after import.

        ImportFileFITS

        ImportFileFits(filename, items, namemap={}, slices={}, twodranges={}, twod_as_oned=set([]), wcsmodes={}, prefix='', suffix='', renames={}, linked=False)

        Import data from a FITS file.

        items is a list of datasets to be imported. items are formatted like the following:

        '/':               import whole file
        '/hduname':        import whole HDU (image or table)
        '/hduname/column': import column from table HDU
        

        all values in items should be lower case.

        HDU names have to follow a Veusz-specific naming. If the HDU has a standard name (e.g. primary or events), then this is used. If the HDU has a EXTVER keyword then this number is appended to this name. An extra number is appended if this name is not unique. If the HDU has no name, then the name used should be ‘hduX’, where X is the HDU number (0 is the primary HDU).

        namemap maps an input dataset (using the scheme above for items) to a Veusz dataset name. Special suffixes can be used on the Veusz dataset name to indicate that the dataset should be imported specially.

        'foo (+)':  import as +ve error for dataset foo
        'foo (-)':  import as -ve error for dataset foo
        'foo (+-)': import as symmetric error for dataset foo
        

        slices is an optional dict specifying slices to be selected when importing. For each dataset to be sliced, provide a tuple of values, one for each dimension. The values should be a single integer to select that index, or a tuple (start, stop, step), where the entries are integers or None.

        twodranges is an optional dict giving data ranges for 2D datasets. It maps names to (minx, miny, maxx, maxy).

        twod_as_oned: optional set containing 2D datasets to attempt to read as 1D, treating extra columns as error bars

        wcsmodes is an optional dict specfying the WCS import mode for 2D datasets in HDUs. The keys are ‘/hduname’ and the values can be ‘pixel’: number pixel range from 0 to maximum (default) ‘pixel_wcs’: pixel number relative to WCS reference pixel ‘linear_wcs’: linear coordinate system from the WCS keywords ‘fraction’: fractional values from 0 to 1.

        renames is an optional dict mapping old to new dataset names, to be renamed after importing

        linked specifies that the dataset is linked to the file.

        Values under the VEUSZ header keyword can be used to override defaults:

        'name': override name for dataset
        'slice': slice on importing (use format "start:stop:step,...")
        'range': should be 4 item array to specify x and y ranges:
            [minx, miny, maxx, maxy]
        'xrange' / 'yrange': individual ranges for x and y
        'xcent' / 'ycent': arrays giving the centres of pixels
        'xedge' / 'yedge': arrays giving the edges of pixels
        'twod_as_oned': treat 2d dataset as 1d dataset with errors
        'wcsmode': use specific WCS mode for dataset (see values above)
        These are specified under the VEUSZ header keyword in the form
            KEY=VALUE
        or for column-specific values
        COLUMNNAME: KEY=VALUE
        

        Returns: list of imported datasets

        ImportFileHDF5

        ImportFileHDF5(filename, items, namemap={}, slices={}, twodranges={}, twod_as_oned=set([]), convert_datetime={}, prefix='', suffix='', renames={}, linked=False)

        Import data from a HDF5 file. items is a list of groups and datasets which can be imported. If a group is imported, all child datasets are imported. namemap maps an input dataset to a veusz dataset name. Special suffixes can be used on the veusz dataset name to indicate that the dataset should be imported specially.

        'foo (+)': import as +ve error for dataset foo
        'foo (-)': import as -ve error for dataset foo
        'foo (+-)': import as symmetric error for dataset foo
        

        slices is an optional dict specifying slices to be selected when importing. For each dataset to be sliced, provide a tuple of values, one for each dimension. The values should be a single integer to select that index, or a tuple (start, stop, step), where the entries are integers or None.

        twodranges is an optional dict giving data ranges for 2d datasets. It maps names to (minx, miny, maxx, maxy). twod_as_oned: optional set containing 2d datasets to attempt to read as 1d

        convert_datetime should be a dict mapping hdf name to specify date/time importing. For a 1d numeric dataset: if this is set to ‘veusz’, this is the number of seconds since 2009-01-01, if this is set to ‘unix’, this is the number of seconds since 1970-01-01. For a text dataset, this should give the format of the date/time, e.g. ‘YYYY-MM-DD|T|hh:mm:ss’ or ‘iso’ for iso format.

        renames is a dict mapping old to new dataset names, to be renamed after importing. linked specifies that the dataset is linked to the file.

        Attributes can be used in datasets to override defaults:

        'vsz_name': set to override name for dataset in veusz
        'vsz_slice': slice on importing (use format "start:stop:step,...")
        'vsz_range': should be 4 item array to specify x and y ranges:
            [minx, miny, maxx, maxy]
        'vsz_twod_as_oned': treat 2d dataset as 1d dataset with errors
        'vsz_convert_datetime': treat as date/time, set to one of the values
        above.
        

        For compound datasets these attributes can be given on a per-column basis using attribute names vsz_attributename_columnname.

        Returns: list of imported datasets

        ImportFileND

        def ImportFileND(comm, filename, dataset, shape=None, transpose=False, mode='text', csvdelimiter=',', csvtextdelimiter='"', csvlocale='en_US', prefix="", suffix="", encoding='utf_8', linked=False)

        Import an n-dimensional dataset from a file. The file should either be in CSV format (mode=’csv’) or whitespace-separated text (mode=’text’). A one-dimensional dataset is given as a list of numbers on a single line/row. A two-dimensional dataset is given by a set of rows. A three-dimensional dataset is given by a set of two-dimensional datasets, with blank lines between them. a four-dimensional dataset is given by a set of three-dimensional datasets with two blank lines between each. Each additional dataset increases the separating number of blank lines by one. Alternatively, the numbers can be given in any form (number of numbers on each row) and “shape” is included to reshape the data into the desired shape.

        In the file, or included as parameters above, the command “shape num1 num2…” can be included to reshape the output dataset to the shape given by the numbers in the row after “shape” (these should be in separate columns in CSV format). If one of these numbers is -1, then this dimension is inferred from the number of values and the other dimensions. Also supported is the “transpose” command or optional argument which reverses the order of the dimensions.

        ImportFilePlugin

        ImportFilePlugin('pluginname', 'filename', **pluginargs, linked=False, encoding='utf_8', prefix='', suffix='', renames={})

        Import data from file using import plugin ‘pluginname’. The arguments to the plugin are given, plus optionally a text encoding, and prefix and suffix to prepend or append to dataset names. renames, if set, provides new names for datasets after import.

        ImportFITSFile

        ImportFITSFile(datasetname, filename, hdu, datacol='A', symerrcol='B', poserrcol='C', negerrcol='D', linked=True/False, renames={})

        This command is deprecated. Please do not use in new code, but instead use ImportFileFITS.

        This command does a simple import from a FITS file. The FITS format is used within the astronomical community to transport binary data. For a more powerful FITS interface, you can use PyFITS within your scripts.

        The datasetname is the name of the dataset to import, the filename is the name of the FITS file to import from. The hdu parameter specifies the HDU to import data from (numerical or a name).

        If the HDU specified is a primary HDU or image extension, then a two-dimensional dataset is loaded from the file. The optional parameters (other than linked) are ignored. Any WCS information within the HDU are used to provide a suitable xrange and yrange.

        If the HDU is a table, then the datacol parameter must be specified (and optionally symerrcol, poserrcol and negerrcol). The dataset is read in from the named column in the table. Any errors are read in from the other specified columns.

        If linked is True, then the dataset is not saved with a saved document, but is reread from the data file each time the document is loaded. renames, if set, provides new names for datasets after import.

        ImportString

        ImportString('descriptor', 'data')

        Like, ImportFile, but loads the data from the specfied string rather than a file. This allows data to be easily embedded within a document. The data string is usually a multi-line Python string.

        Returns: A tuple containing a list of the imported datasets and the number of conversions which failed for a dataset.

        Changed in version 0.5: A tuple is returned rather than just the number of imported variables.

        ImportString('x y', '''
            1   2
            2   5
            3   10
        ''')
        

        ImportString2D

        ImportString2D(datasets, string, xrange=None, yrange=None, invertrows=None, invertcols=None, transpose=None)

        Imports a two-dimensional dataset from the string given. This is similar to the ImportFile2D command, with the same dataset format within the string. The optional values are also listed there. The various controlling parameters can be set within the string. See the ImportFile2D section for details.

        ImportStringND

        ImportStringND(dataset, string, shape=None, transpose=False)

        Imports a n-dimensional dataset from the string given. This is similar to the ImportFileND command. Please look there for more detail and the description of the optional parameters and in-stream allowed parameters.

        IsClosed

        IsClosed()

        Returns a boolean value telling the caller whether the plotting window has been closed.

        Note: this command is only supported in the embedding interface.

        List

        List(where='.')

        List the widgets which are contained within the widget with the path given, the type of widgets, and a brief description.

        Load

        Load('filename.vsz')

        Loads the veusz script file given. The script file can be any Python code. The code is executed using the Veusz interpreter.

        Note: this command is only supported at the command line and not in a script. Scripts may use the python execfile function instead.

        MoveToPage

        MoveToPage(pagenum)

        Updates window to show the page number given of the document.

        Note: this command is only supported in the embedding interface or veusz –listen.

        ReloadData

        ReloadData()

        Reload any datasets which have been linked to files.

        Returns: A tuple containing a list of the imported datasets and the number of conversions which failed for a dataset.

        Rename

        Remove('widgetpath', 'newname')

        Rename the widget at the path given to a new name. This command does not move widgets. See To for a description of the path syntax. ‘.’ can be used to select the current widget.

        Remove

        Remove('widgetpath')

        Remove the widget selected using the path. See To for a description of the path syntax.

        ResizeWindow

        ResizeWindow(width, height)

        Resizes window to be width by height pixels.

        Note: this command is only supported in the embedding interface or veusz –listen.

        Save

        Save('filename.vsz')

        Save the current document under the filename given.

        Set

        Set('settingpath', val)

        Set the setting given by the path to the value given. If the type of val is incorrect, an InvalidType exception is thrown. The path to the setting is the optional path to the widget the setting is contained within, an optional subsetting specifier, and the setting itself.

        Set('page1/graph1/x/min', -10.)
        

        SetAntiAliasing

        SetAntiAliasing(on)

        Enable or disable anti aliasing in the plot window, replotting the image.

        SetData

        SetData(name, val, symerr=None, negerr=None, poserr=None)

        Set the dataset name with the values given. If None is given for an item, it will be left blank. val is the actual data, symerr are the symmetric errors, negerr and poserr and the getative and positive asymmetric errors. The data can be given as lists or numpys.

        SetDataExpression

        SetDataExpression(name, val, symerr=None, negerr=None, poserr=None, linked=False, parametric=None)

        Create a new dataset based on the expressions given. The expressions are Python syntax expressions based on existing datasets.

        If linked is True, the dataset will change as the datasets in the expressions change.

        Parametric can be set to a tuple of (minval, maxval, numitems). t in the expression will iterate from minval to maxval in numitems values.

        SetDataND

        SetDataRange(name, val)

        Set a n-dimensional dataset to be the values given by val. val should be an n-dimensional numpy array of values, or a list of lists.

        SetDataRange

        SetDataRange(name, numsteps, val, symerr=None, negerr=None, poserr=None, linked=False)

        Set dataset to be a range of values with numsteps steps. val is tuple made up of (minimum value, maximum value). symerr, negerr and poserr are optional tuples for the error bars.

        If linked is True, the dataset can be saved in a document as a SetDataRange, otherwise it is expanded to the values which would make it up.

        SetData2D

        SetData2D('name', val, xrange=(A,B), yrange=(C,D), xgrid=[1,2,3...], ygrid=[4,5,6...])

        Creates a two-dimensional dataset with the name given. val is either a two-dimensional numpy array, or is a list of lists, with each list in the list representing a row. Do not give xrange if xgrid is set and do not give yrange if ygrid is set, and vice versa.

        xrange and yrange are optional tuples giving the inclusive range of the X and Y coordinates of the data. xgrid and ygrid are optional lists, tuples or arrays which give the coordinates of the edges of the pixels. There should be one more item in each array than pixels.

        SetData2DExpression

        SetData2DExpression('name', expr, linked=False)

        Create a 2D dataset based on expressions. name is the new dataset name expr is an expression which should return a 2D array linked specifies whether to permanently link the dataset to the expressions.

        SetData2DExpressionXYZ

        SetData2DExpressionXYZ('name', 'xexpr', 'yexpr', 'zexpr', linked=False)

        Create a 2D dataset based on three 1D expressions. The x, y expressions need to evaluate to a grid of x, y points, with the z expression as the 2D value at that point. Currently only linear fixed grids are supported. This function is intended to convert calculations or measurements at fixed points into a 2D dataset easily. Missing values are filled with NaN.

        SetData2DXYFunc

        SetData2DXYFunc('name', xstep, ystep, 'expr', linked=False)

        Construct a 2D dataset using a mathematical expression of “x” and “y”. The x values are specified as (min, max, step) in xstep as a tuple, the y values similarly. If linked remains as False, then a real 2D dataset is created, where values can be modified and the data are stored in the saved file.

        SetDataDateTime

        SetDataDateTime('name', vals)

        Creates a datetime dataset of name given. vals is a list of Python datetime objects.

        SetDataText

        SetDataText(name, val)

        Set the text dataset name with the values given. val must be a type that can be converted into a Python list.

        SetDataText('mylabel', ['oranges', 'apples', 'pears', 'spam'])
        

        SetToReference

        SetToReference(setting, refval)

        Link setting given to other setting refval.

        SetUpdateInterval

        SetUpdateInterval(interval)

        Tells window to update every interval milliseconds at most. The value 0 disables updates until this function is called with a non-zero. The value -1 tells Veusz to update the window every time the document has changed. This will make things slow if repeated changes are made to the document. Disabling updates and using the ForceUpdate command will allow the user to control updates directly.

        Note: this command is only supported in the embedding interface or veusz –listen.

        SetVerbose

        SetVerbose(v=True)

        If v is True, then extra information is printed out by commands.

        StartSecondView

        StartSecondView(name = 'window title')

        In the embedding interface, this method will open a new Embedding interface onto the same document, returning the object. This new window provides a second view onto the document. It can, for instance, show a different page to the primary view. name is a window title for the new window.

        Note: this command is only supported in the embedding interface.

        TagDatasets

        TagDatasets('tag', ['ds1', 'ds2'...])

        Adds the tag to the list of datasets given..

        To

        To('widgetpath')

        The To command takes a path to a widget and moves to that widget. For example, this may be “/”, the root widget, “graph1”, “/page1/graph1/x”, “../x”. The syntax is designed to mimic Unix paths for files. “/” represents the base widget (where the pages reside), and “..” represents the widget next up the tree.

        Quit

        Quit()

        Quits Veusz. This is only supported in veusz –listen.

        WaitForClose

        WaitForClose()

        Wait until the plotting window has been closed.

        Note: this command is only supported in the embedding interface.

        Zoom

        Zoom(factor)

        Sets the plot zoom factor, relative to a 1:1 scaling. factor can also be “width”, “height” or “page”, to zoom to the page width, height or page, respectively.

        This is only supported in embedded mode or veusz –listen.

        Security

        With the 1.0 release of Veusz, input scripts and expressions are checked for possible security risks. Only a limited subset of Python functionality is allowed, or a dialog box is opened allowing the user to cancel the operation. Specifically you cannot import modules, get attributes of Python objects, access globals() or locals() or do any sort of file reading or manipulation. Basically anything which might break in Veusz or modify a system is not supported. In addition internal Veusz functions which can modify a system are also warned against, specifically Print(), Save() and Export().

        If you are running your own scripts and do not want to be bothered by these dialogs, you can run veusz with the --unsafe-mode option.

        Using Veusz from other programs

        Non-Qt Python programs

        Veusz can be used as a Python module for plotting data. There are two ways to use the module: (1) with an older path-based Veusz commands, used in Veusz saved document files or (2) using an object-oriented interface. With the old style method the user uses a unix-path inspired API to navigate the widget tree and add or manipulate widgets. With the new style interface, the user navigates the tree with attributes of the Root object to access Nodes. The new interface is likely to be easier to use unless you are directly translating saved files.

        Older path-based interface

        """An example embedding program. Veusz needs to be installed into
        the Python path for this to work (use setup.py)
        
        This animates a sin plot, then finishes
        """
        
        import time
        import numpy
        import veusz.embed as veusz
        
        # construct a Veusz embedded window
        # many of these can be opened at any time
        g = veusz.Embedded('window title')
        g.EnableToolbar()
        
        # construct the plot
        g.To( g.Add('page') )
        g.To( g.Add('graph') )
        g.Add('xy', marker='tiehorz', MarkerFill__color='green')
        
        # this stops intelligent axis extending
        g.Set('x/autoExtend', False)
        g.Set('x/autoExtendZero', False)
        
        # zoom out
        g.Zoom(0.8)
        
        # loop, changing the values of the x and y datasets
        for i in range(10):
            x = numpy.arange(0+i/2., 7.+i/2., 0.05)
            y = numpy.sin(x)
            g.SetData('x', x)
            g.SetData('y', y)
        
            # wait to animate the graph
            time.sleep(2)
        
        # let the user see the final result
        print "Waiting for 10 seconds"
        time.sleep(10)
        print "Done!"
        
        # close the window (this is not strictly necessary)
        g.Close()
        

        The embed interface has the methods listed in the command line interface listed in the Veusz manual https://veusz.github.io/docs/manual.html

        Multiple Windows are supported by creating more than one Embedded object. Other useful methods include:

        • WaitForClose() - wait until window has closed
        • GetClick() - return a list of (axis, value) tuples where the user clicks on a graph
        • ResizeWndow(width, height) - resize window to be width x height pixels
        • SetUpdateInterval(interval) - set update interval in ms or 0 to disable
        • MoveToPage(page) - display page given (starting from 1)
        • IsClosed() - has the page been closed
        • Zoom(factor) - set zoom level (float) or ‘page’, ‘width’, ‘height’
        • Close() - close window
        • SetAntiAliasing(enable) - enable or disable antialiasing
        • EnableToolbar(enable=True) - enable plot toolbar
        • StartSecondView(name='Veusz') - start a second view onto the document of the current Embedded object. Returns a new Embedded object.
        • Wipe() - wipe the document of all widgets and datasets.

        New-style object interface

        In Veusz 1.9 or late a new style of object interface is present, which makes it easier to construct the widget tree. Each widget, group of settings or setting is stored as a Node object, or its subclass, in a tree. The root document widget can be accessed with the Root object. The dot operator “.” finds children inside other nodes. In Veusz some widgets can contain other widgets (Root, pages, graphs, grids). Widgets contain setting nodes, accessed as attributes. Widgets can also contain groups of settings, again accessed as attributes.

        An example tree for a document (not complete) might look like this

        Root
        \-- page1                     (page widget)
            \-- graph1                (graph widget)
                \--  x                (axis widget)
                \--  y                (axis widget)
                \-- function          (function widget)
            \-- grid1                 (grid widget)
                \-- graph2            (graph widget)
                    \-- xy1           (xy widget)
                        \-- xData     (setting)
                        \-- yData     (setting)
                        \-- PlotLine  (setting group)
                            \-- width (setting)
                            ...
                        ...
                    \-- x             (axis widget)
                    \-- y             (axis widget)
                \-- graph3            (graph widget)
                    \-- contour1      (contour widget)
                    \-- x             (axis widget)
                    \-- y             (axis widget)
        

        Here the user could access the xData setting node of the xy1 widget using Root.page1.graph2.xy1.xData. To actually read or modify the value of a setting, you should get or set the val property of the setting node. The line width could be changed like this

        graph = embed.Root.page1.graph2
        graph.xy1.PlotLine.width.val = '2pt'
        

        For instance, this constructs a simple x-squared plot which changes to x-cubed:

        import veusz.embed as veusz
        import time
        
        #  open a new window and return a new Embedded object
        embed = veusz.Embedded('window title')
        #  make a new page, but adding a page widget to the root widget
        page = embed.Root.Add('page')
        #  add a new graph widget to the page
        graph = page.Add('graph')
        #  add a function widget to the graph. The Add() method can take a list of settings
        #  to set after widget creation. Here, "function='x**2'" is equivalent to
        #  function.function.val = 'x**2'
        function = graph.Add('function', function='x**2')
        
        time.sleep(2)
        function.function.val = 'x**3'
        #  this is the same if the widgets have the default names
        Root.page1.graph1.function1.function.val = 'x**3'
        

        If the document contains a page called “page1” then Root.page1 is the object representing the page. Similarly, Root.page1.graph1 is a graph called graph1 in the page. You can also use dictionary-style indexing to get child widgets, e.g. Root[‘page1’][‘graph1’]. This style is easier to use if the names of widgets contain spaces or if widget names shadow methods or properties of the Node object, i.e. if you do not control the widget names.

        Widget nodes can contain as children other widgets, groups of settings, or settings. Groups of settings can contain child settings. Settings cannot contain other nodes. Here are the useful operations of Nodes:

        class Node(object):
          """properties:
            path - return path to object in document, e.g. /page1/graph1/function1
            type - type of node: "widget", "settinggroup" or "setting"
            name - name of this node, e.g. "graph1"
            children - a generator to return all the child Nodes of this Node, e.g.
              for c in Root.children:
                print c.path
            children_widgets - generator to return child widget Nodes of this Node
            children_settinggroups - generator for child setting groups of this Node
            children_settings - a generator to get the child settings
            childnames - return a list of the names of the children of this Node
            childnames_widgets - return a list of the names of the child widgets
            childnames_settinggroups - return a list of the names of the setting groups
            childnames_settings - return a list of the names of the settings
            parent - return the Node corresponding to the parent widget of this Node
        
            __getattr__ - get a child Node with name given, e.g. Root.page1
            __getitem__ - get a child Node with name given, e.g. Root['page1']
          """
        
          def fromPath(self, path):
             """Returns a new Node corresponding to the path given, e.g. '/page1/graph1'"""
        
        class SettingNode(Node):
            """A node which corresponds to a setting. Extra properties:
            val - get or set the setting value corresponding to this value, e.g.
             Root.page1.graph1.leftMargin.val = '2cm'
            """
        
        class SettingGroupNode(Node):
            """A node corresponding to a setting group. No extra properties."""
        
        class WidgetNode(Node):
            """A node corresponding to a widget.
        
               property:
                 widgettype - get Veusz type of widget
        
               Methods are below."""
        
            def WalkWidgets(self, widgettype=None):
                """Generator to walk widget tree and get widgets below this
                WidgetNode of type given.
        
                widgettype is a Veusz widget type name or None to get all
                widgets."""
        
            def Add(self, widgettype, *args, **args_opt):
                """Add a widget of the type given, returning the Node instance.
                """
        
            def Rename(self, newname):
                """Renames widget to name given.
                Existing Nodes corresponding to children are no longer valid."""
        
            def Action(self, action):
                """Applies action on widget."""
        
            def Remove(self):
                """Removes a widget and its children.
                Existing Nodes corresponding to children are no longer valid."""
        

        Note that Nodes are temporary objects which are created on the fly. A real widget in Veusz can have several different WidgetNode objects. The operators == and != can test whether a Node points to the same widget, setting or setting group.

        Here is an example to set all functions in the document to be x**2:

        for n in Root.WalkWidgets(widgettype='function'):
            n.function.val = 'x**2'
        

        Translating old to new style

        Here is an example how you might translate the old to new style interface (this is taken from the sin.vsz example).

        # old (from saved document file)
        Add('page', name='page1')
        To('page1')
        Add('graph', name='graph1', autoadd=False)
        To('graph1')
        Add('axis', name='x')
        To('x')
        Set('label', '\\\\italic{x}')
        To('..')
        Add('axis', name='y')
        To('y')
        Set('label', 'sin \\\\italic{x}')
        Set('direction', 'vertical')
        To('..')
        Add('xy', name='xy1')
        To('xy1')
        Set('MarkerFill/color', 'cyan')
        To('..')
        Add('function', name='function1')
        To('function1')
        Set('function', 'sin(x)')
        Set('Line/color', 'red')
        To('..')
        To('..')
        To('..')
        
        # new (in python)
        import veusz.embed
        embed = veusz.embed.Embedded('window title')
        
        page = embed.Root.Add('page')
        # note: autoAdd=False stops graph automatically adding own axes (used in saved files)
        graph = page.Add('graph', autoadd=False)
        x = graph.Add('axis', name='x')
        x.label.val = '\\\\italic{x}'
        y = graph.Add('axis', name='y')
        y.direction.val = 'vertical'
        xy = graph.Add('xy')
        xy.MarkerFill.color.val = 'cyan'
        func = graph.Add('function')
        func.function.val = 'sin(x)'
        func.Line.color.val = 'red'
        

        PyQt programs

        There is no direct PyQt interface. The standard embedding interface should work, however.

        Non Python programs

        Support for non Python programs is available in a limited form. External programs may execute Veusz using veusz --listen. Veusz will read its input from the standard input, and write output to standard output. This is a full Python execution environment, and supports all the scripting commands mentioned in Commands, a Quit() command, the EnableToolbar() and the Zoom(factor) command listed above. Only one window is supported at once, but many veusz --listen programs may be started.

        veusz --listen may be used from the shell command line by doing something like:

        veusz --listen < in.vsz
        

        where in.vsz contains:

        To(Add('page') )
        To(Add('graph') )
        SetData('x', arange(20))
        SetData('y', arange(20)**2)
        Add('xy')
        Zoom(0.5)
        Export("foo.pdf")
        Quit()
        

        A program may interface with Veusz in this way by using the popen C Unix function, which allows a program to be started having control of its standard input and output. Veusz can then be controlled by writing commands to an input pipe.

        veusz-3.0.1/Documents/manual/html/datasets.html0000664000175000017500000011737013325026534021205 0ustar jssjss00000000000000 Reading data — Veusz 3.0.1 documentation

        Reading data

        Currently Veusz supports reading data from files with text, CSV, HDF5, FITS, 2D text or CSV, QDP, binary and NPY/NPZ formats. Use the Data ‣ Import dialog to read data, or the importing commands in the API can be used. In addition, the user can load or write import plugins in Python which load data into Veusz in an arbitrary format. At the moment QDP, binary and NPY/NPZ files are supported with this method. The HDF5 file format is the most sophisticated, and is recommended for complex datasets.

        By default, data are “linked” to the file imported from. This means that the data are not stored in the Veusz saved file and are reloaded from the original data file when opening. In addition, the user can use the Data ‣ Reload menu option to reload data from linked files. Unselect the linked option when importing to remove the association with the data file and to store the data in the Veusz saved document.

        Note that a prefix and suffix can be given when importing. These are added to the front or back of each dataset name imported. They are convenient for grouping data together.

        _images/importdialog.png

        We list the various types of import below.

        Standard text import

        The default text import operates on simple text files. The data are assumed to be in columns separated by whitespace. Each column corresponds to dataset (or its error bars). Each row is an entry in the dataset.

        The way the data are read is goverened by a simple “descriptor”. This can simply be a list of dataset names separated by spaces. If no descriptor is given, the columns are treated as separate datasets and are given names col1, col2, etc. Veusz attempts to automatically determine the type of the data.

        When reading in data, Veusz treats any whitespace as separating columns. The columns do not actually need to be aligned. Furthermore a \ symbol can be placed at the end of a line to mark a continuation. Veusz will read the next line as if it were placed at the end of the current line. In addition comments and blank lines are ignored (unless in block mode). Comments start with a #, ;, ! or %, and continue until the end of the line. The special value nan can be used to specify a break in a dataset.

        If the option to read data in blocks is enabled, Veusz treats blank lines (or lines starting with the word no) as block separators. For each dataset in the descriptor, separate datasets are created for each block, using a numeric suffix giving the block number, e.g. _1, _2.

        Data types in text import

        Veusz supports reading in several types of data. The type of data can be added in round brackets after the name in the descriptor. Veusz will try to guess the type of data based on the first value, so you should specify it if there is any form of ambiguity (e.g. is 3 text or a number). Supported types are numbers (use numeric in brackets) and text (use text in brackets). An example descriptor would be x(numeric) +- y(numeric) + - label(text) for an x dataset followed by its symmetric errors, a y dataset followed by two columns of asymmetric errors, and a final column of text for the label dataset.

        A text column does not need quotation unless it contains space characters or escape characters. However make sure you deselect the “ignore text” option in the import dialog. This ignores lines of text to ease the import of data from other applications. Quotation marks are recommended around text if you wish to avoid ambiguity. Text is quoted according to the Python rules for text. Double or single quotation marks can be used, e.g. “A ‘test’”, ‘A second “test”’. Quotes can be escaped by prefixing them with a backslash, e.g. “A new "test"”. If the data are generated from a Python script, the repr function provides the text in a suitable form.

        Dates and times are also supported with the syntax dataset(date). Dates must be in ISO format YYYY-MM-DD. Times are in 24 hour format hh:mm:ss.ss. Dates with times are written YYYY-MM-DDThh:mm:ss.ss (this is a standard ISO format, see http://www.w3.org/TR/NOTE-datetime). Dates are stored within Veusz as a number which is the number of seconds since the start of January 1st 2009. Veusz also supports dates and times in the local format, though take note that the same file and data may not work on a system in a different location.

        Descriptors

        A list of datasets, or a “Descriptor”, is given in the Import dialog to describe how the data are formatted in the import file. The descriptor at its simplest is a space or comma-separated list of the names of the datasets to import. These are columns in the file.

        Following a dataset name the text +, -, or +- can be given to say that the following column is a positive error bar, negative error bar or symmetric error bar for the previous (non error bar) dataset. These symbols should be separated from the dataset name or previous symbol with a space or a comma symbol.

        In addition, if multiple numbered columns should be imported, the dataset name can be followed by square brackets containing a range in the form [a:b] to number columns a to b, or [:] to number remaining columns. See below for examples of this use.

        Dataset names can contain virtually any character, even unicode characters. If the name contains non alpha-numeric characters (characters outside of A-Z, a-z and 0-9), then the dataset name should be contained within back-tick characters. An example descriptor is `length data (m)`,+- `speed (mps)`,+,-, for two datasets with spaces and brackets in their names.

        Instead of specifying the descriptor in the Import dialog, the descriptor can be placed in the data file using a descriptor statement on a separate line, consisting of “descriptor” followed by the descriptor. Multiple descriptors can be placed in a single file, for example:

        # here is one section
        descriptor x,+- y,+,-
        1 0.5  2 0.1 -0.1
        2 0.3  4 0.2 -0.1
        # my next block
        descriptor alpha beta gamma
        1 2 3
        4 5 6
        7 8 9
        # etc...
        

        Descriptor examples

        1. x y two columns are present in the file, they will be read in as datasets x and y.

        2. x,+- y,+,- or x +- y + - two datasets are in the file. Dataset “x” consists of the first two columns. The first column are the values and the second are the symmetric errors. “y” consists of three columns (note the comma between + and -). The first column are the values, the second positive asymmetric errors, and the third negative asymmetric errors. Suppose the input file contains:

          1.0  0.3  2    0.1  -0.2
          1.5  0.2  2.3  2e-2 -0.3E0
          2.19 0.02 5    0.1  -0.1
          

          Then x will contain 1+-0.3, 1.5+-0.2, 2.19+-0.02. y will contain 2 +0.1 -0.2, 2.3 +0.02 -0.3, 5 +0.1 -0.1.

        3. x[1:2] y[:] the first column is the data x_1, the second x_2. Subsequent columns are read as y[1] to y[n].

        4. y[:]+- read each pair of columns as a dataset and its symmetric error, calling them y[1] to y[n].

        5. foo,,+- read the first column as the foo dataset, skip a column, and read the third column as its symmetric error.

        CSV files

        CVS (comma separated variable) files are often written from other programs, such as spreadsheets, including Excel and Gnumeric. Veusz supports reading from these files.

        In the import dialog choose “CSV”, then choose a filename to import from. In the CSV file the user should place the data in either rows or columns. Veusz will use a name above a column or to the left of a row to specify what the dataset name should be. The user can use new names further down in columns or right in rows to specify a different dataset name. Names do not have to be used, and Veusz will assign default col and row names if not given. You can also specify a prefix which is prepended to each dataset name read from the file.

        To specify symmetric errors for a column, put +- as the dataset name in the next column or row. Asymmetric errors can be stated with + and - in the columns.

        The data type in CSV files are automatically detected unless specified. The data type can be given in brackets after the column name, e.g. name (text), where the data type is date, numeric or text. Explicit data types are needed if the data look like a different data type (e.g. a text item of 1.23). The date format in CSV files can be specified in the import dialog box - see the examples given. In addition CSV files support numbers in European format (e.g. 2,34 rather than 2.34), depending on the setting in the dialog box.

        HDF5 files

        HDF5 is a flexible data format. Datasets and tables can be stored in a hierarchical arrangements of groups within a file. Veusz supports reading 1D numeric, text, date-time, 2D numeric or n-dimensional numeric data from HDF files. The h5py Python module must be installed to use HDF5 files (included in binary releases).

        In the import dialog box, choose which individual datasets to import, or selecting a group to import all the datasets within the group. If selecting a group, datasets in the group incompatible with Veusz are ignored.

        A name can be provided for each dataset imported by entering one under “Import as”. If one is not given, the dataset or column name is used. The name can also be specified by setting the HDF5 dataset attribute vsz_name to the name. Note that for compound datasets (tables), vsz_ attributes for columns are given by appending the suffix _columnname to the attribute.

        Error bars

        Error bars are supported in two ways. The first way is to combine 1D datasets. For the datasets which are error bars, use a name which is the same as the main dataset but with the suffix (+-), (+) or (-), for symmetric, postive or negative error bars, respectively. The second method is to use a 2D dataset with two or three columns, for symmetric or asymmetric error bars, respectively. Click on the dataset in the dialog and choose the option to import as a 1D dataset. This second method can also be enabled by adding an HDF5 attribute vsz_twod_as_oned set to a non-zero value for the dataset.

        Slices

        You may wish to reduce the dimensions of a dataset before importing by slicing. You can also give a slice to import a subset of a dataset. When importing, in the slice column you can give a slice expression. This should have the same number of entries as the dataset has dimensions, separated by commas. An entry can be a single number, to select a particular row or column. Alternatively it could be an expression like a:b:c or a:b, where a is the starting index, b is one beyond the stopping index and optionally c is the step size. A slice can also be specified by providing an HDF5 attribute vsz_slice for the dataset.

        2D data ranges

        2D data have an associated X and Y range. By default the number of pixels of the image are used to give this range. A range can be specified by clicking on the dataset and entering a minimum and maximum X and Y coordinates. Alternatively, provide the HDF5 attribute for the dataset vsz_range, which should be set to an array of four values (minimum x, minimum y, maximum x, maximum y).

        Dates

        Date/time datasets can be made from a 1D numeric dataset or from a text dataset. For the 1D dataset, use the number of seconds relative to the start of the year 2009 (this is Veusz format) or the year 1970 (this is Unix format). In the import dialog, click on the name of the dataset and choose the date option. To specify a date format in the HDF5 file, set the attribute vsz_convert_datetime to either veusz or unix.

        For text datasets, dates must be given in the right format, selected in the import dialog after clicking on the dataset name. As in other file formats, by default Veusz uses ISO 8601 format, which looks like 2013-12-22T21:08:07, where the date and time parts are optional. The T is also optional. You can also provide your own format when importing by giving a date expression using YYYY, MM, DD, hh, mm and ss (e.g. YYYY-MM-DD|T|hh:mm:ss), where vertical bars mark optional parts of the expression. To automate this, set the attribute vsz_convert_datetime to the format expression or iso to specify ISO format.

        2D text or CSV format

        Veusz can import 2D data from standard text or CSV files. In this case the data should consist of a matrix of data values, with the columns separated by one or more spaces or tabs and the rows on different lines.

        In addition to the data the file can contain lines at the top which affect the import. Such specifiers are used, for example, to change the coordinates of the pixels in the file. By default the first pixels coordinates is between 0 and 1, with the centre at 0.5. Subsequent pixels are 1 greater. Note that the lowest coordinate pixel is the bottom-left value in the table of imported values. When using specifiers in CSV files, put the different parts (separated by spaces) in separate columns. Below are listed the specifiers:

        1. xrange A B - make the 2D dataset span the coordinate range A to B in the x-axis (where A and B are numbers). Note that the range is inclusive, so a 1 pixel wide image with A=0 and B=1 would have the pixel centre at 0.5. The pixels are assumed to have the same spacing. Do not use this as the same time as the xedge or xcent options.
        2. yrange A B - make the 2D dataset span the coordinate range A to B in the y-axis (where A and B are numbers).
        3. xedge A B C... - rather than assume the pixels have the same spacing, give the coordinates of the edges of the pixels in the x-axis. The numbers should be space-separated and there should be one more number than pixels. Do not give xrange or xcent if this is given. If the values are increasing, the lowest coordinate value is at the left of the dataset, otherwise if they are decreasing, it is on the right (unless the rows/columns are inverted or transposed).
        4. yedge A B C... - rather than assume the pixels have the same spacing, give the coordinates of the edges of the pixels in the y-axis. If the values are increasing, the lowest coordinate value is at the bottom row. If they instead decrease, it is at the top.
        5. xcent A B C... - rather than give a total range or pixel edges, give the centres of the pixels. There should be the same number of values as pixels in the image. Do not give xrange or xedge if this is given. The order of the values specify whether the pixels are left to right or right to left.
        6. ycent A B C... - rather than give a total range or pixel edges, give the centres of the pixels. The value order specifies whether the pixels are bottom to top, or top to bottom.
        7. invertrows - invert the rows after reading the data.
        8. invertcols - invert the columns after reading the data.
        9. transpose - swap rows and columns after importing data.
        10. gridatedge - the first row and leftmost column give the positions of the centres of the pixels. This is also an option in the import dialog. The values should be increasing or decreasing.

        FITS files

        1D, 2D or n-dimensional data can be read from FITS files. 1D or 2D data can be read from image, primary or table HDUs. nD data can be read from from image or primary extensions. Note that pyfits or astropy must be installed to get FITS support.

        The import dialog box uses a tree to show the structure of the FITS file. The user can choose to import the whole file, by clicking the check box at the top. They can import data from a particular HDU by selecting that, or individual table columns can be selected.

        In the dialog box, a dataset can be given a name for the dataset. Otherwise the HDU or table column name is used. Note that a prefix and/or suffix can be specified to be added to all dataset names.

        If dataset y should have an error bar specified by column yerr, then in the name for yerr, enter ‘y (+-)’. Asymmetric error bars can be specified using (+) and (-) on inidividual columns.

        The slice column can be used to only import a subset of the dataset imported. This uses Python slicing syntax, which is comma-separated list of ranges and steps. A range is specified like 10:20, which selects the 11th to 20th items (the indices are numbered from 0, and the final index is one past the index you actually want). A stepped range can look like 10:20:2, which selects every other item in that range. Each of these numbers are optional, so : selects all items on that dimension. For example the slice :,10:14:2 selects all values on the first dimension, but only the 11th and 13th items on the next axis.

        When importing 2D data the user can specify whether to treat this as 1D plus error bars (dimensions should have 2 or 3 columns), or specify a range in 2D space the data covers. Veusz will also attempt to use WCS information in the file for the 2D range if not specified. The standard mode is to use the CDELT, CRVAL and CRPIX keywords to specify a linear range for the data. Alternatively the user can specify pixel numbering (numbering from 0 to N-1). There is a fraction option for using a range of 0 to 1. Finally there is a pixel numbering scheme which numbers in pixels from the CRPIX keyword items.

        Some of these options can be specified in the FITS file using the ‘VEUSZ’ header keyword. This header keyword can be added with the value ‘KEY=VALUE’ (applying to the whole HDU) or ‘COLUMN: KEY=VALUE’ (applying to a particular column in a table). Supported options for KEY are:

        name
        provide name for dataset in VALUE
        slice
        VALUE is slice to apply when importing dataset
        range
        range of data for 2D dataset in form [minx, miny, maxx, maxy]
        xrange/yrange
        range of dataset individually in x or y
        xcent/ycent
        set to list of values giving centers of pixels
        xedge/yedge
        set to list of values giving edges of pixels
        twod_as_oned
        treat as 1D data with error bars if VALUE=1
        wcsmode
        use specific WCS mode for 2D dataset (should be pixel/pixel_wcs/linear_wcs/fraction)

        Reading other data formats

        As mentioned above, a user may write some Python code to read a data file or set of data files. To write a plugin which is incorportated into Veusz, see https://github.com/veusz/veusz/wiki/ImportPlugins

        You can also include Python code in an input file to read data, which we describe here. Suppose an input file “in.dat” contains the following data:

        1   2
        2   4
        3   9
        4   16
        

        Of course this data could be read using the ImportFile command. However, you could also read it with the following Veusz script (which could be saved to a file and loaded with execfile or Load. The script also places symmetric errors of 0.1 on the x dataset.

        x = []
        y = []
        for line in open("in.dat"):
            parts = [float(i) for i in line.split()]
            x.append(parts[0])
            y.append(parts[1])
        SetData('x', x, symerr=0.1)
        SetData('y', y)
        

        Manipulating datasets

        Imported datasets can easily be modified in the Data Editor dialog box. This dialog box can also be used to create new datasets from scratch by typing them in. The Data Create dialog box is used to new datasets as a numerical sequence, parametrically or based on other datasets given expressions. If you want to plot a function of a dataset, you often do not have to create a new dataset. Veusz allows to enter expressions directly in many places.

        Using dataset plugins

        Dataset plugins can be used to perform arbitrary manipulation of datasets. Veusz includes several plugins for mathematical operation of data and other dataset manipulations, such as concatenation or splitting. If you wish to write your own plugins look at https://github.com/veusz/veusz/wiki/DatasetPlugins.

        Using expressions to create new datasets

        For instance, if the user has already imported dataset d, then they can create d2 which consists of d**2. Expressions are in Python numpy syntax and can include the usual mathematical functions.

        _images/createdataset.png

        Expressions for error bars can also be given. By appending _data, _serr, _perr or _nerr to the name of the dataset in the expression, the user can base their expression on particular parts of the given dataset (the main data, symmetric errors, positive errors or negative errors). Otherwise the program uses the same parts as is currently being specified.

        If a dataset name contains non alphanumeric characters, its name should be quoted in the expression in back-tick characters, e.g. `length (cm)`*2.

        The numpy functionality is particularly useful for doing more complicated expressions. For instance, a conditional expression can be written as where(x<y,x,y) or where(isfinite(x),a,b)).

        You often do not need to create a new dataset when. For example, with the xy point plotter widget, you can directly enter an expression as the X and Y dataset settings. When you give a direct dataset expression, you can define error bar expressions by separating them by commas, and optionally surrounding them by brackets. For example (a,0.1) plots dataset a as the data, with symmetric errors bars of 0.1. Asymmetric bars are given as (a,a*0.1,-a*0.1).

        Other useful functions in evaluation include those already mentioned in the LaTeX expansion description. DATA(name, [part]) returns the dataset with name given. The optional part, which can be ‘data’, ‘serr’, ‘perr’ or ‘nerr’, allows error bars to be returned for numerical data. SETTING(path) returns the value of the Veusz setting, which can include, for example, the best fitting parameters of a fit. ENVIRON is the Python environment variable dictionary, allowing values to be passed from the environment, e.g. float(ENVIRON['myvar']).

        Linking datasets to expressions

        A particularly useful feature is to be able to link a dataset to an expression, so if the expression changes the dataset changes with it, like in a spreadsheet.

        Splitting data

        Data can also be chopped in this method, for example using the expression x[10:20], which makes a dataset based on the 11th to 20th item in the x dataset (the ranges are Python syntax, and are zero-based). Negative indices count backwards from the end of the dataset. Data can be skipped using expressions such as data[::2], which skips every other element

        Defining new constants or functions

        User defined constants or functions can be defined in the “Custom definitions” dialog box under the edit menu. Functions can also be imported from external python modules.

        _images/customdefinition.png

        Custom definitions are defined on a per-document basis, but can be saved or loaded into a file. A default custom definitions file can be set in the preferences dialog box.

        Dataset plugins

        In addition to creating datasets based on expressions, a variety of dataset plugins exist, which make certain operations on datasets much more convenient. See the Data, Operations menu for a list of the default plugins. The user can easily create new plugins. See https://github.com/veusz/veusz/wiki/DatasetPlugins for details.

        Capturing data

        In addition to the standard data import, data can be captured as it is created from an external program, a network socket or a file or named pipe. When capturing from a file, the behaviour is like the Unix tail -f command, where new lines written to the file are captured. To use the capturing facility, the data must be written in the simple line based standard Veusz text format. Data are whitespace separated, with one value per dataset given on a single line.

        To capture data, use the dialog box Data ‣ Capture. A list of datasets should be given. This is the standard descriptor format. Choose the source of the data, which is either a a filename or named pipe, a network socket to connect to, or a command line for an external program. Capturing ends if the source of the data runs out (for external programs or network sockets) or the finish button is clicked. It can optionally end after a certain number of data lines or when a time period has expired. Normally the data are updated in Veusz when the capturing is finished. There is an option to update the document at intervals, which is useful for monitoring. A plot using the variables will update when the data are updated.

        Click the Capture button to start the capture. Click Finish or Cancel to stop. Cancelling destroys captured data.

        veusz-3.0.1/Documents/manual/html/searchindex.js0000664000175000017500000004071413325026534021337 0ustar jssjss00000000000000Search.setIndex({docnames:["api","datasets","index","introduction"],envversion:53,filenames:["api.rst","datasets.rst","index.rst","introduction.rst"],objects:{},objnames:{},objtypes:{},terms:{"05m":3,"10mm":3,"10pt":3,"11th":1,"123e":3,"13th":1,"1cm":3,"1st":1,"20th":1,"22t21":1,"2cm":0,"2pt":0,"2x10":3,"3e0":1,"5in":3,"boolean":0,"break":[0,1,3],"case":[0,1,3],"class":0,"default":[0,1,3],"export":2,"final":[0,1,3],"float":[0,1,3],"function":[0,2,3],"import":[0,2,3],"int":3,"new":[2,3],"public":[0,2],"return":[0,1,3],"switch":3,"true":0,"try":[0,1,3],CVS:1,EPS:0,For:[0,1,3],One:3,Such:1,The:[0,1,2],Then:1,There:[0,1,3],These:[0,1,3],Use:[0,1],Using:2,WCS:[0,1],With:[0,3],__getattr__:0,__getitem__:0,_columnnam:1,_data:1,_nerr:1,_perr:1,_serr:1,_strings_:0,abbrevi:3,abl:[1,3],about:3,abov:[0,1,3],absolut:3,accept:0,access:0,accord:[1,3],accur:3,achiev:3,action:[2,3],actionnam:0,activ:3,actual:[0,1,3],add:[2,3],addcustom:2,added:[0,1,3],addeds:0,addimportpath:2,adding:[0,1,3],addit:[0,1,3],addition:3,addon:3,adjust:3,advantag:3,affect:[1,3],after:[0,1,3],again:0,against:0,algorithm:3,alias:0,align:[1,3],all:[0,1,3],allow:[0,1,3],almost:3,along:[0,3],alpha:[0,1,3],alphanumer:1,alreadi:[0,1],also:[0,1,3],altern:[0,1],ambigu:1,analog:3,angl:3,ani:[0,1,3],anim:0,anoth:3,anti:0,antialia:0,antialias:0,anyth:0,anywher:3,api:[1,2],appear:3,append:[0,1],appendalwai:0,appl:0,appli:[0,1,3],applic:[0,1,3],appropri:[0,3],approx:3,arang:0,arbitrari:[1,3],area:3,arg:0,args_opt:0,argument:0,around:[1,3],arrai:[0,1,3],arrang:[1,3],arrowhead:3,asid:3,aspect:3,assign:[1,3],associ:1,assum:[1,3],ast:3,astronom:0,astropi:1,asymmetr:[0,1,3],asymp:3,attempt:[0,1],attribut:[0,1,3],auto:[0,3],autoadd:0,autoextend:0,autoextendzero:0,autom:[0,1,3],automat:[0,1,3],avail:[0,3],avoid:1,awai:3,axes:[0,3],axi:[0,1,2],axis3d:3,axispath:0,back:[1,3],backcolor:0,background:[0,3],backslash:1,backward:1,bar:[0,2,3],base:[1,2,3],basenam:3,basi:[0,1,3],basic:0,been:[0,3],befor:[0,1,3],behaviour:[1,3],behind:3,being:[1,3],below:[0,1,3],besid:3,best:1,beta:[1,3],better:3,between:[0,1,3],beyond:1,bin:[0,3],binari:[0,1,3],binmanu:0,binparam:0,bitmap:[0,3],black:3,blank:[0,1,3],block:1,blue:0,bmp:0,bodi:3,bold:3,border:3,both:0,bother:0,bottom:[1,3],bound:3,bowti:3,box:[0,1,3],boxplot:3,bracket:[0,1,3],brief:0,bring:3,broken:3,brows:3,browser:3,bsp:3,build:3,built:3,bullet:3,button:[1,3],calcul:[0,3],call:[0,1,3],caller:0,camera:3,can:[0,1,3],cancel:[0,1],cannot:[0,3],cap:3,captur:2,care:3,cartesian:3,cdelt:1,cell:3,cent:3,center:[1,3],centr:[0,1,3],centuri:3,certain:[0,1,3],chang:[0,1,3],charact:[0,1,3],chart:3,check:[0,1,3],chi:3,child:0,childnam:0,childnames_set:0,childnames_settinggroup:0,childnames_widget:0,children:[0,3],children_set:0,children_settinggroup:0,children_widget:0,choos:[1,3],chop:1,chosen:3,circ:3,click:[0,1,3],clone:0,clonewidget:2,close:[2,3],closer:3,code:[0,1,3],col1:1,col2:1,col:1,color:[0,2],colormap:[0,3],colour:[0,3],column:[0,1,3],columnnam:0,com:1,combin:1,come:3,comm:0,comma:[0,1,3],command:[1,2,3],comment:[0,1],common:0,commun:0,complet:[0,3],complex:1,complic:1,compon:0,componentpath:0,compound:[0,1],comput:3,concaten:1,condit:[0,1],confus:3,connect:[1,3],consist:[1,3],consol:[0,3],constant:[0,2,3],construct:[0,3],contain:[0,1,3],content:2,continu:[0,1],contour1:0,contour:[0,3],control:[0,3],conveni:1,convers:0,convert:[0,3],convert_datetim:0,coordin:[0,1,3],copi:[0,2,3],copyright:2,corner:3,correct:[0,3],correspond:[0,1,3],cos:3,could:[0,1,3],count:[0,1],cours:1,cover:1,creat:[0,2,3],createhistogram:2,creation:0,crpix:1,crval:1,csv:[0,2],csvdelimit:0,csvlocal:0,csvtextdelimit:0,ctype:0,cube:[0,3],cumul:0,cup:3,current:[0,1,3],currrenli:0,cursor:[0,3],curv:[0,3],custom:[0,1,3],cyan:0,dagger:3,dai:3,dashv:3,dat:[1,3],data:[0,2,3],datacol:0,datapoint:0,dataset:[0,2],datasetnam:0,datasetplugin:[1,2],date:[0,2,3],datetim:[0,1,3],ddagger:3,ddthh:1,de_at:3,decim:3,decreas:[1,3],def:0,defin:[0,2,3],definit:[0,1,3],deg:3,delet:0,delimin:0,delta:3,densiti:0,depend:[1,3],deprec:0,depth:3,describ:[1,3],descript:[0,1],descriptor:[0,2,3],deselect:1,design:[0,3],desir:0,destroi:1,detail:[0,1,2,3],detect:1,determin:1,dialog:[0,1,3],diamond:3,dict:[0,3],dictionari:[0,1,3],differ:[0,1,3],dimens:[0,1,3],dimension:[0,1,2],direct:[0,1,3],directli:[0,1,3],directori:[0,3],disabl:[0,3],disadvantag:3,displai:[0,3],distanc:3,distort:3,distribut:[0,3],divid:3,doc:0,document:[0,1,3],doe:[0,1,3],doesn:3,doing:[0,1],dollar:3,done:[0,3],dot:0,doteq:3,doubl:[0,1],down:[0,1,3],downarrow:3,download:3,dpi:0,drag:3,draw:3,drawn:3,drop:3,druck:3,ds1:0,ds2:0,dsprefix:0,dssuffix:0,each:[0,1,3],eas:1,easi:3,easier:0,easili:[0,1,3],edg:[0,1,3],edit:[1,3],editor:[1,3],eexpress:0,effect:3,eight:3,either:[0,1,3],element:[0,1],ellips:3,els:[0,3],emb:0,embed:2,emph:3,en_gb:3,en_u:0,enabl:[0,1,3],enabletoolbar:2,encod:0,end:[0,1,3],endpoint:3,engin:3,enlarg:3,enter:[0,1,3],entri:[0,1],environ:[0,1,3],eps:0,epsilon:3,equat:3,equiv:3,equival:0,error:[0,2,3],errorbarline__hid:0,escap:[1,3],estim:3,eta:3,etc:[1,3],european:1,evalu:[0,1,3],even:[0,1],event:0,everi:[0,1],everywher:3,exampl:[0,2,3],excel:1,except:[0,3],execfil:[0,1],execut:0,exist:[0,1,3],exp:3,expand:[0,3],expans:[1,3],expir:1,explan:3,explicit:1,explicitli:3,expr:0,express:[0,2,3],extend:[0,3],extens:[0,1,3],extern:[0,1,3],extra:0,extver:0,facil:1,factor:[0,3],fail:[0,3],fairli:3,fals:0,far:3,farthest:3,fashion:3,favourit:3,featur:[1,3],few:3,ffffff00:0,field:0,file:[0,2,3],filenam:[0,1,3],fill:[0,3],filter:0,filterdataset:2,filterexpr:0,find:0,finish:[0,1],first:[0,1,2],fit:[0,2,3],fitter:3,fix:[0,3],flexibl:1,fly:0,fmt:3,folder:3,follow:[0,1,3],font:3,foo:[0,1],forc:0,forceupd:2,form:[0,1,3],format:[0,2],found:3,four:[0,1,3],fourth:3,fprintf:3,frac:3,fraction:[0,1,3],freeli:0,from:[1,2,3],frompath:0,front:[1,3],full:[0,3],func:0,function1:[0,3],function3d:3,further:1,furthermor:1,futur:3,gamma:[1,3],gap:3,gener:[0,1,2,3],get:[1,2],getchildren:2,getclick:2,getcolormap:2,getdata:2,getdataset:2,getdatatyp:2,github:[0,1],give:[0,1,3],given:[0,1,3],glass:3,global:0,gnu:[0,2],gnumer:1,goe:3,going:3,goveren:1,gpl:2,graph1:[0,3],graph2:0,graph3:0,graph3d:3,graph:[0,3],graphic:[2,3],greater:[1,2],green:[0,3],grei:3,greyscal:0,grid1:0,grid:[0,3],gridatedg:1,gridlik:3,group:[0,1,3],guess:1,gui:3,h5py:1,handl:3,happen:3,has:[0,1,3],hat:3,have:[0,1,3],hdf5:[0,2],hdf:[0,1],hdu:[0,1],hdunam:0,hdux:0,header:[0,1],height:[0,3],help:3,here:[0,1,3],hidden:3,hide:3,hierarch:1,hierarchi:3,high:3,histogram:[0,3],histori:[0,3],hold:3,hole:3,horizont:3,hour:[1,3],hover:3,how:[0,1,3],howev:[0,1],html:[0,2,3],http:[0,1,2,3],ignor:[0,1],imag:[0,1,3],imagefil:3,importfil:[1,2],importfile2d:2,importfilecsv:2,importfilefit:2,importfilehdf5:2,importfilend:2,importfileplugin:2,importfitsfil:2,importplugin:1,importstr:2,importstring2d:2,importstringnd:2,inch:0,includ:[0,1,3],inclus:[0,1],incompat:1,incorport:1,incorrect:0,increas:[0,1,3],index:[0,1,2],indic:[0,1],individu:[0,1,3],inexpr:0,infer:0,inform:[0,1],infti:3,inidividu:1,initi:[0,3],input:[0,1,3],insid:[0,3],inspir:0,instal:[0,1,2],instanc:[0,1,3],instead:[0,1,3],integ:[0,3],intellig:0,intend:0,intens:3,interchang:3,interfac:[2,3],intern:0,interpol:3,interpret:[0,3],intersect:3,interv:[0,1,3],intrins:3,introduct:2,invalidtyp:0,invers:3,invert:[0,1],invertcol:[0,1],invertrow:[0,1],iota:3,isclos:2,isfinit:1,islogbin:0,iso:[0,1,3],isometr:3,ital:[0,3],item:[0,1,3],iter:0,its:[0,1,3],itself:[0,3],januari:1,jeremi:2,jpeg:0,jpg:0,jump:3,just:[0,3],kappa:3,kei:[0,1,3],keyword:[0,1],kind:3,label:[0,1,3],lambda:3,lang:3,languag:[0,3],larg:3,larger:3,largest:3,largetosmal:0,late:0,later:3,latex:[1,3],lead:3,learn:3,leav:3,left:[0,1,3],leftarrow:3,leftmargin:0,leftmost:1,length:[1,3],less:3,let:[0,3],letter:3,level:0,lhd:3,licenc:0,licens:2,lie:3,lies:3,light:3,like:[0,1,3],limit:[0,3],line:[1,2,3],linear:[0,1,3],linear_wc:[0,1],link:[0,2,3],linux:3,list:[1,2,3],listen:0,load:[1,2,3],local:[0,1,3],locat:1,log:3,logarithm:3,logo:3,logorithm:3,longer:0,look:[0,1,3],loop:0,lot:3,lower:0,lowest:1,mac:3,machin:3,made:[0,1,3],magnifi:3,mai:[0,1,3],main:[1,2],major:3,make:[0,1,3],mani:[0,1,3],manipul:[0,2,3],manual:[0,3],map:[0,3],margin:3,mark:[1,3],marker:[0,3],markerfil:0,markerfill__color:0,mathemat:[0,1,3],matrix:[0,1,3],max:[0,3],maxi:[0,1],maximum:[0,1,3],maxval:0,maxx:[0,1],mean:[0,1,3],measur:[0,2],memori:3,mention:[0,1],menu:[1,3],method:[0,1,3],might:0,millisecond:0,mimic:0,min:[0,3],mini:[0,1],minimis:3,minimum:[0,1,3],minor:3,minut:3,minval:0,minx:[0,1],miss:[0,3],mix:[0,3],mode:[0,1,3],model:3,modifi:[0,1,3],modul:[0,1,2,3],moment:[1,3],mondai:3,monitor:1,monoton:3,month:3,more:[0,1,3],most:[0,1,3],mous:3,move:[0,3],movetopag:2,mps:1,much:[1,3],multi:0,multipag:0,multipl:[0,1,3],multiplatform:2,must:[0,1,3],mydata:3,mylabel:0,myvar:1,naiv:3,name:[0,1,3],nameforwidget:0,namemap:0,nan:[0,1,3],navig:0,necessari:0,need:[0,1,3],neg:[0,1,3],negerr:0,negerrcol:0,neq:3,nerr:[1,3],network:1,never:3,new_api:0,newer:3,newnam:0,newpar:0,next:[0,1,3],node:0,non:[1,2,3],none:[0,3],normal:[1,3],notat:3,note:[0,1,3],now:3,npy:1,npz:1,num1:0,num2:0,number:[0,1,3],numbin:0,numer:[0,1,2],numitem:0,numpi:[0,1,3],numstep:0,nval:0,object:[2,3],odot:3,off:3,often:[0,1,3],old:2,older:2,omega:3,omicron:3,ominu:3,onc:0,one:[0,1,3],onli:[0,1,3],onlinepub:3,onto:0,open:[0,1,3],opengl:3,opengroup:3,oper:[0,1,3],oplu:3,option:[0,1,3],optionalarg:0,orang:0,order:[0,1,3],org:[1,2,3],orient:[0,3],origin:[1,3],orthogon:3,oslash:3,other:[2,3],otherwis:[0,1],otim:3,out:[0,1,3],outbind:0,outbinsd:0,output:[0,3],outsid:[1,3],outvalsd:0,over:3,overlap:3,overrid:[0,3],own:[0,1],packag:[0,2,3],page1:[0,3],page:[0,2,3],pagenum:0,painter:3,pair:1,panel:3,parallel:3,paramet:[0,1,3],parametr:[0,1,3],parent:[0,3],part:[1,3],particular:[0,1],particularli:1,pass:1,past:[1,3],path:[1,2,3],pdf:0,pdfdpi:0,pear:0,per:[0,1,3],perform:[0,1,3],period:[0,1],perman:0,perp:3,perr:[1,3],perspect:3,phi:3,physic:3,pipe:[0,1],pixel:[0,1,3],pixel_wc:[0,1],place:[0,1,3],plain:3,plan:3,pleas:[0,2,3],plot:[0,1,2],plotlin:0,plotter:[1,3],plotwindow:0,plu:[0,1,3],plugin:[0,2,3],pluginarg:0,pluginnam:0,png:0,point3d:3,point:[0,1,3],poisson:0,polar:3,polygon:3,pop:3,popen:0,poserr:0,poserrcol:0,posit:[0,1,3],possibl:[0,3],postiv:1,power:[0,3],prec:3,preceq:3,prefer:[1,3],prefix:[0,1],prepend:[0,1],present:[0,1],press:3,pressur:3,prevent:3,preview:3,previou:[0,1],primari:[0,1],print:0,problem:3,produc:3,program:[1,2,3],prompt:0,properli:3,properti:[0,2],propto:3,provid:[0,1,3],psi:3,put:[1,3],pyfit:[0,1],python:[1,2,3],qdp:1,qualiti:0,quit:2,quot:1,quotat:1,rang:[0,2],rangei:0,rangex:0,raster:3,rather:[0,1,3],read:[0,2,3],readabl:3,readrow:0,real:0,recal:0,recommend:1,rect:3,rectangl:3,red:[0,3],reduc:[1,3],refer:0,reflect:[0,3],refval:0,region:3,regular:3,rel:[0,1,3],releas:[0,1],reload:[0,1,3],reloaddata:2,remain:[0,1],remov:[1,2,3],renam:2,render:3,repeat:0,replac:0,replaceblank:0,replot:[0,3],repr:1,repres:[0,3],represent:3,request:3,requir:[0,3],reread:0,rerun:3,reshap:0,resid:0,resiz:0,resizewindow:2,resizewndow:0,resolut:3,respect:[0,1,3],result:[0,3],revers:0,rhd:3,rho:3,right:[0,1,3],rightarrow:3,rightward:3,risk:0,root:[0,3],rotat:3,round:1,routin:0,row:[0,1,3],rrggbbaa:0,rule:1,run:[0,1,3],sai:[0,1],same:[0,1,3],sander:2,save:[1,2,3],scale:[0,2],scatter:3,scene3d:3,scene:3,scheme:[0,1,3],scientif:[2,3],scratch:1,screen:3,script:[0,1,3],search:[2,3],second:[0,1,3],section:[0,1,3],secur:2,see:[0,1,2,3],seen:3,segment:3,select:[0,1,3],self:0,sens:3,separ:[0,1,3],sequenc:1,seri:3,serr:[1,3],set:[1,2],setantialias:2,setdata2d:2,setdata2dexpress:2,setdata2dexpressionxyz:2,setdata2dxyfunc:2,setdata:[1,2],setdatadatetim:2,setdataexpress:2,setdatand:2,setdatarang:2,setdatatext:2,settabl:3,settinggroup:0,settinggroupnod:0,settingnod:0,settingpath:0,settorefer:2,setup:0,setupdateinterv:2,setverbos:2,sever:[0,1,3],shade:3,shadow:0,shape:[0,3],share:3,shell:[0,3],should:[0,1,3],show:[0,1,3],shown:3,side:3,sight:3,sigma:3,sign:3,significantli:3,sim:3,simeq:3,similar:[0,3],similarli:0,simpl:[0,1,3],simplest:1,simpli:1,simplifi:0,sin:[0,3],sinc:[0,1],singl:[0,1,3],size:[0,1,3],skip:1,slash:3,sleep:0,slice:[0,2,3],slow:[0,3],small:3,smalltolarg:0,socket:1,solid:3,some:[0,1,3],someth:[0,3],sometim:3,sophist:1,sort:[0,3],sourc:[1,3],space:[0,1,3],spam:0,span:1,specfi:0,special:[0,1,3],specif:[0,1,3],specifi:[0,1,3],speed:1,split:[2,3],spreadsheet:1,sqrt:3,sqsubset:3,sqsubseteq:3,sqsupset:3,sqsupseteq:3,squar:[0,1,3],stack:3,standard:[0,2,3],star:3,start:[0,1,2],startsecondview:2,state:[0,1],statement:1,step:[0,1,3],stigma:3,stop:[0,1],store:[0,1,3],str:3,straight:3,strang:3,stream:0,strftime:3,strictli:0,string:[0,3],structur:[1,3],style:[2,3],sub:[0,3],subclass:0,subscript:3,subsequ:1,subset:[0,1,3],subseteq:3,succ:3,succeq:3,suffix:[0,1,3],suitabl:[0,1],sundai:3,superscript:3,suppli:3,support:[0,1,3],suppos:1,supset:3,supseteq:3,sure:1,surfac:3,surface3d:3,surround:[1,3],svg:0,svgdpi:0,svgtextastext:0,swap:[0,1],swich:3,symbol:[0,1,3],symerr:[0,1],symerrcol:0,symmetr:[0,1,3],symmetris:3,syntax:[0,1,3],system:[0,1],tab:[0,1,3],tabl:[0,1,3],tag:0,tagdataset:2,tail:1,take:[0,1,3],taken:[0,3],tan:3,tau:3,technic:3,tell:0,temporari:0,tend:3,term:[0,3],terminolog:2,ternari:3,test:[0,1,3],text:[0,2],textbf:3,textit:3,than:[0,1,3],thei:[0,1,3],them:[0,1,3],theme1:3,theme2:3,theme:2,themselv:3,therefor:0,theta:3,thi:[0,1,2,3],thick:3,thing:[0,3],third:1,those:[1,3],though:[1,3],three:[0,1,2],through:3,thrown:[0,3],tick:[1,3],tiehorz:0,time:[0,1,3],tip:3,titl:0,togeth:1,tool:3,toolbar:[0,3],top:[0,1,3],total:1,toward:3,translat:[2,3],transpar:3,transport:0,transpos:[0,1],treat:[0,1,3],tree:[0,1,3],triangl:3,tupl:0,turn:3,tutori:3,two:[0,1,3],twod_as_on:[0,1],twodrang:0,type:[0,2,3],typeerror:3,typic:3,umid:3,under:[0,1,2,3],underlin:3,underscor:0,understand:3,unicod:[1,3],uniqu:[0,3],unit:3,unix:[0,1,3],unknown:3,unless:[0,1],unlhd:3,unnecessarili:3,unrhd:3,unsaf:0,unselect:1,until:[0,1],uparrow:3,updat:[0,1,3],uplu:3,upsilon:3,upward:3,use:[0,1,3],used:[0,1,3],useful:[0,1,3],user:[0,1,2,3],uses:[0,1,3],using:[0,1,3],usual:[0,1,3],utf8:0,utf_8:0,val:0,valid:0,valu:[0,1,3],vari:3,variabl:[0,1,3],varieti:[1,3],variou:[0,1,3],vdash:3,vdv:3,vdx:3,vector:3,vee:3,veri:3,versa:0,versatil:3,version:[0,2,3],vertic:[0,1,3],veusz:1,via:[0,3],vice:0,video:3,view:[0,3],viewer:3,virtual:1,volum:3,volume3d:3,vsz:0,vsz_:1,vsz_attributename_columnnam:0,vsz_convert_datetim:[0,1],vsz_name:[0,1],vsz_rang:[0,1],vsz_slice:[0,1],vsz_twod_as_on:[0,1],wai:[0,1,3],wait:0,waitforclos:2,walk:0,walkwidget:0,want:[0,1,3],warn:0,wcsmode:[0,1],websit:3,wedg:3,week:3,weekdai:3,well:3,were:[1,3],what:[1,3],when:[0,1,3],where:[0,1,3],whereq:0,whether:[0,1,3],which:[0,1,3],whitespac:[0,1],whole:[0,1],wide:1,widget:[0,1,2],widgetnod:0,widgetpath:0,widgettyp:0,width:[0,3],wiki:1,window:[0,2],wipe:0,wish:[1,3],within:[0,1,3],without:3,word:1,work:[0,1,3],worth:3,would:[0,1,3],write:[0,1,3],written:[1,3],wrong:3,www:[1,2],x_1:1,x_2:1,xcent:[0,1],xdata:0,xedg:[0,1],xexpr:0,xgrid:0,xrang:[0,1],xsh:3,xstep:0,xy1:0,ycent:[0,1],ydata:0,year:[1,3],yedg:[0,1],yerr:1,yet:3,yexpr:0,ygrid:0,you:[0,1,3],your:[0,1,3],yrang:[0,1],ystep:0,yyyi:[0,1],zero:[0,1],zeromargin:0,zeta:3,zexpr:0,zoom:[2,3]},titles:["Veusz command line and embedding interface (API)","Reading data","Veusz documentation","Introduction"],titleterms:{"export":0,"function":1,"import":1,"new":[0,1],The:3,Using:[0,1],action:0,add:0,addcustom:0,addimportpath:0,api:0,axi:3,bar:1,base:0,captur:1,clonewidget:0,close:0,color:3,command:0,constant:1,creat:1,createhistogram:0,csv:1,data:1,dataset:[1,3],datasetplugin:0,date:1,defin:1,descriptor:1,dimension:3,document:2,embed:0,enabletoolbar:0,error:1,exampl:1,express:1,file:1,filterdataset:0,first:3,fit:1,forceupd:0,format:[1,3],from:0,get:[0,3],getchildren:0,getclick:0,getcolormap:0,getdata:0,getdataset:0,getdatatyp:0,gpl:0,hdf5:1,importfil:0,importfile2d:0,importfilecsv:0,importfilefit:0,importfilehdf5:0,importfilend:0,importfileplugin:0,importfitsfil:0,importstr:0,importstring2d:0,importstringnd:0,indic:2,instal:3,interfac:0,introduct:[0,3],isclos:0,line:0,link:1,list:0,load:0,main:3,manipul:1,measur:3,movetopag:0,non:0,numer:3,object:0,old:0,older:0,other:[0,1],path:0,plot:3,plugin:1,program:0,properti:3,pyqt:0,python:0,quit:0,rang:1,read:1,reloaddata:0,remov:0,renam:0,resizewindow:0,save:0,scale:3,secur:0,set:[0,3],setantialias:0,setdata2d:0,setdata2dexpress:0,setdata2dexpressionxyz:0,setdata2dxyfunc:0,setdata:0,setdatadatetim:0,setdataexpress:0,setdatand:0,setdatarang:0,setdatatext:0,settorefer:0,setupdateinterv:0,setverbos:0,slice:1,split:1,standard:1,start:3,startsecondview:0,style:0,tabl:2,tagdataset:0,terminolog:3,text:[1,3],theme:3,three:3,translat:0,type:1,veusz:[0,2,3],waitforclos:0,widget:3,window:3,zoom:0}})veusz-3.0.1/Documents/manual/html/index.html0000664000175000017500000003737613325026534020513 0ustar jssjss00000000000000 Veusz documentation — Veusz 3.0.1 documentation

        Veusz documentation

        Jeremy Sanders

        Copyright 2018

        This document is licensed under the GNU General Public License, version 2 or greater. Please see the file COPYING for details, or see http://www.gnu.org/licenses/gpl-2.0.html.

        This is the documentation for Veusz. Veusz is a multiplatform scientific plotting package with a graphical user interface.

        Contents:

        Indices and tables

        veusz-3.0.1/Documents/manual/html/search.html0000664000175000017500000000600713325026534020634 0ustar jssjss00000000000000 Search — Veusz 3.0.1 documentation

        Search

        Please activate JavaScript to enable the search functionality.

        From here you can search these documents. Enter your search words into the box below and click "search". Note that the search function will automatically search for all of the words. Pages containing fewer words won't appear in the result list.

        veusz-3.0.1/Documents/manual/html/.buildinfo0000664000175000017500000000034613325026534020455 0ustar jssjss00000000000000# Sphinx build info version 1 # This file hashes the configuration used when building these files. When it is not found, a full rebuild will be done. config: ee14a1d4fb737826e199e9a6fa6eb104 tags: 645f666f9bcd5a90fca523b33c5a78b7 veusz-3.0.1/Documents/manual/html/_sources/0000775000175000017500000000000013325026667020327 5ustar jssjss00000000000000veusz-3.0.1/Documents/manual/html/_sources/index.rst.txt0000664000175000017500000000141013242310603022762 0ustar jssjss00000000000000.. veusz documentation master file, created by sphinx-quickstart on Sun Feb 5 11:07:00 2017. You can adapt this file completely to your liking, but it should at least contain the root `toctree` directive. Veusz documentation =================== Jeremy Sanders Copyright 2018 This document is licensed under the GNU General Public License, version 2 or greater. Please see the file COPYING for details, or see ``_. This is the documentation for Veusz. Veusz is a multiplatform scientific plotting package with a graphical user interface. Contents: .. toctree:: :maxdepth: 3 introduction.rst datasets.rst api.rst Indices and tables ================== * :ref:`genindex` * :ref:`modindex` * :ref:`search` veusz-3.0.1/Documents/manual/html/_sources/api.rst.txt0000664000175000017500000013241613322660165022451 0ustar jssjss00000000000000================================================ Veusz command line and embedding interface (API) ================================================ .. _Commands: Introduction ############ Veusz uses a common API, or set of commands, to control the program via its command line (from the Veusz console; click View, Windows, Console Window), the embedding interface (when Veusz is embedded in other Python programs), from within plugins, within documents (VSZ documents contain commands used to generate the document) or externally from the operating system command line (using `veusz --listen`). As Veusz is a a Python application it uses Python as its scripting language. You can therefore freely mix Veusz and Python commands on the Veusz command line (Click View, Windows, Console Window to get access to the command line). Veusz can also read in Python scripts from files on the command line (see the :ref:`Load ` command). When commands are entered in the command prompt in the Veusz window, Veusz supports a simplified command syntax, whereq brackets following commands names, and commas, can replaced by spaces in Veusz commands (not Python commands). For example, :command:`Add('graph', name='foo')`, may be entered as :command:`Add 'graph' name='foo'`. The :command:`numpy` package is already imported into the command line interface (as `\*`), so you do not need to import it first. The command prompt supports history (use the up and down cursor keys to recall previous commands). Most of the commands listed below can be used in the in-program command line interface, using the embedding interface or using `veusz --listen`. Commands specific to particular modes are documented as such. Veusz also includes a new object-oriented version of the API, which is documented at new_api_. Commands and API ################ We list the allowed set of commands below Action ------ .. _Command.Action: :command:`Action('actionname', componentpath='.')` Initiates the specified action on the widget (component) given the action name. Actions perform certain automated routines. These include "fit" on a fit widget, and "zeroMargins" on grids. Add --- .. _Command.Add: :command:`Add('widgettype', name='nameforwidget', autoadd=True, optionalargs)` The Add command adds a graph into the current widget (See the :ref:`To ` command to change the current position). The first argument is the type of widget to add. These include "graph", "page", "axis", "xy" and "grid". :command:`name` is the name of the new widget (if not given, it will be generated from the type of the widget plus a number). The :command:`autoadd` parameter if set, constructs the default sub-widgets this widget has (for example, axes in a graph). Optionally, default values for the graph settings may be given, for example :command:`Add('axis', name='y', direction='vertical')`. Subsettings may be set by using double underscores, for example :command:`Add('xy', MarkerFill__color='red', ErrorBarLine__hide=True)`. Returns: Name of widget added. AddCustom --------- .. _Command.AddCustom: :command:`AddCustom(type, name, value)` Add a custom definition for evaluation of expressions. This can define a constant (can be in terms of other constants), a function of 1 or more variables, or a function imported from an external python module. ctype is "constant", "function" or "import". name is name of constant, or "function(x, y, ...)" or module name. val is definition for constant or function (both are _strings_), or is a list of symbols for a module (comma separated items in a string). If mode is 'appendalways', the custom value is appended to the end of the list even if there is one with the same name. If mode is 'replace', it replaces any existing definition in the same place in the list or is appended otherwise. If mode is 'append', then an existing definition is deleted, and the new one appended to the end. AddImportPath ------------- .. _Command.AddImportPath: :command:`AddImportPath(directory)` Add a directory to the list of directories to try to import data from. CloneWidget ----------- .. _Command.CloneWidget: :command:`CloneWidget(widget, newparent, newname=None)` Clone the widget given, placing the copy in newparent and the name given. newname is an optional new name to give it Returns new widget path. Close ----- .. _Command.Close: :command:`Close()` Closes the plotwindow. This is only supported in embedded mode. CreateHistogram --------------- .. _Command.CreateHistogram: :command:`CreateHistogram(inexpr, outbinsds, outvalsds, binparams=None, binmanual=None, method='counts', cumulative = 'none', errors=False)` Histogram an input expression. inexpr is input expression. outbinds is the name of the dataset to create giving bin positions. outvalsds is name of dataset for bin values. binparams is None or (numbins, minval, maxval, islogbins). binmanual is None or a list of bin values. method is 'counts', 'density', or 'fractions'. cumulative is to calculate cumulative distributions which is 'none', 'smalltolarge' or 'largetosmall'. errors is to calculate Poisson error bars. DatasetPlugin ------------- .. _Command.DatasetPlugin: :command:`DatasetPlugin(pluginname, fields, datasetnames={})>` Use a dataset plugin. pluginname: name of plugin to use fields: dict of input values to plugin datasetnames: dict mapping old names to new names of datasets if they are renamed. The new name None means dataset is deleted EnableToolbar ------------- .. _Command.EnableToolbar: :command:`EnableToolbar(enable=True)` Enable/disable the zooming toolbar in the plotwindow. This command is only supported in embedded mode or from `veusz --listen`. Export ------ .. _Command.Export: :command:`Export(filename, color=True, page=0, dpi=100, antialias=True, quality=85, backcolor='#ffffff00', pdfdpi=150, svgdpi=96, svgtextastext=False)` Export the page given to the filename given. The :command:`filename` must end with the correct extension to get the right sort of output file. Currrenly supported extensions are '.eps', '.pdf', '.ps', '.svg', '.jpg', '.jpeg', '.bmp' and '.png'. If :command:`color` is True, then the output is in colour, else greyscale. :command:`page` is the page number of the document to export (starting from 0 for the first page!). A list of pages can be given for multipage formats (.pdf or .ps). :command:`dpi` is the number of dots per inch for bitmap output files. :command:`antialias` - antialiases output if True. :command:`quality` is a quality parameter for jpeg output. :command:`backcolor` is the background color for bitmap files, which is a name or a #RRGGBBAA value (red, green, blue, alpha). :command:`pdfdpi` is the dpi to use when exporting EPS or PDF files. :command:`svgdpi` is the dpi to use when exporting to SVG files. :command:`svgtextastext` says whether to export SVG text as text, rather than curves. FilterDatasets -------------- .. _Command.FilterDatasets: :command:`FilterDatasets(filterexpr, datasets, prefix="", suffix="", invert=False, replaceblanks=False)` Filter a list of datasets given. Creates new datasets for each with prefix and suffix added to input dataset names. filterexpr is an input numpy eexpression for filtering the datasets. If invert is set, the filter condition is inverted. If replaceblanks is set, filtered values are not removed, but replaced with a blank or NaN value. This command only works on 1D numeric, date or text datasets. ForceUpdate ----------- .. _Command.ForceUpdate: :command:`ForceUpdate()` Force the window to be updated to reflect the current state of the document. Often used when periodic updates have been disabled (see SetUpdateInterval). This command is only supported in embedded mode or from `veusz --listen`. Get --- .. _Command.Get: :command:`Get('settingpath')` Returns: The value of the setting given by the path. .. code-block:: python >>> Get('/page1/graph1/x/min') 'Auto' GetChildren ----------- .. _Command.GetChildren: :command:`GetChildren(where='.')` Returns: The names of the widgets which are children of the path given GetClick -------- .. _Command.GetClick: :command:`GetClick()` Waits for the user to click on a graph and returns the position of the click on appropriate axes. Command only works in embedded mode. Returns: A list containing tuples of the form (axispath, val) for each axis for which the click was in range. The value is the value on the axis for the click. GetColormap ----------- .. _Command.GetColormap: :command:`GetColormap(name, invert=False, nvals=256)` Returns a colormap as a numpy array of red, green, blue, alpha values (ranging from 0 to 255) with the number of steps given. GetData ------- .. _Command.GetData: :command:`GetData(name)` Returns: For a 1D dataset, a tuple containing the dataset with the name given. The value is (data, symerr, negerr, poserr), with each a numpy array of the same size or None. data are the values of the dataset, symerr are the symmetric errors (if set), negerr and poserr and negative and positive asymmetric errors (if set). If a text dataset, return a list of text elements. If the dataset is a date-time dataset, return a list of Python datetime objects. If the dataset is a 2D dataset return the tuple (data, rangex, rangey), where data is a 2D numpy array and rangex/y are tuples giving the range of the x and y coordinates of the data. If it is an ND dataset, return an n-dimensional array. .. code-block:: python data = GetData('x') SetData('x', data[0]*0.1, \*data[1:]) GetDataType ----------- .. _Command.GetDataType: :command:`GetDataType(name)` Get type of dataset with name given. Returns '1d' for a 1d dataset, '2d' for a 2d dataset, 'text' for a text dataset and 'datetime' for a datetime dataset. GetDatasets ----------- .. _Command.GetDatasets: :command:`GetDatasets()` Returns: The names of the datasets in the current document. GPL --- .. _Command.GPL: :command:`GPL()` Print out the GNU Public Licence, which Veusz is licenced under. ImportFile ---------- .. _Command.ImportFile: :command:`ImportFile('filename', 'descriptor', linked=False, prefix='', suffix='', encoding='utf_8', renames={})` Imports data from a file. The arguments are the filename to load data from and the descriptor. The format of the descriptor is a list of variable names representing the columns of the data. For more information see :ref:`Descriptors `. If the linked parameter is set to True, if the document is saved, the data imported will not be saved with the document, but will be reread from the filename given the next time the document is opened. The linked parameter is optional. If prefix and/or suffix are set, then the prefix and suffix are added to each dataset name. If set, renames maps imported dataset names to final dataset names after import. Returns: A tuple containing a list of the imported datasets and the number of conversions which failed for a dataset. Changed in version 0.5: A tuple is returned rather than just the number of imported variables. ImportFile2D ------------ .. _Command.ImportFile2D: :command:`ImportFile2D('filename', datasets, xrange=None, yrange=None, invertrows=False, invertcols=False, transpose=False, prefix='', suffix='', linked=False, encoding='utf8', renames={})` Imports two-dimensional data from a file. The required arguments are the filename to load data from and the dataset name, or a list of names to use. filename is a string which contains the filename to use. datasets is either a string (for a single dataset), or a list of strings (for multiple datasets). The xrange parameter is a tuple which contains the range of the X-axis along the two-dimensional dataset, for example (-1., 1.) represents an inclusive range of -1 to 1. The yrange parameter specifies the range of the Y-axis similarly. If they are not specified, (0, N) is the default, where N is the number of datapoints along a particular axis. invertrows and invertcols if set to True, invert the rows and columns respectively after they are read by Veusz. transpose swaps the rows and columns. If prefix and/or suffix are set, they are prepended or appended to imported dataset names. If set, renames maps imported dataset names to final dataset names after import. If the linked parameter is True, then the datasets are linked to the imported file, and are not saved within a saved document. The file format this command accepts is a two-dimensional matrix of numbers, with the columns separated by spaces or tabs, and the rows separated by new lines. The X-coordinate is taken to be in the direction of the columns. Comments are supported (use `#`, `!` or `%`), as are continuation characters (`\\`). Separate datasets are deliminated by using blank lines. In addition to the matrix of numbers, the various optional parameters this command takes can also be specified in the data file. These commands should be given on separate lines before the matrix of numbers. They are: #. xrange A B #. yrange C D #. invertrows #. invertcols #. transpose ImportFileCSV ------------- .. _Command.ImportFileCSV: :command:`ImportFileCSV('filename', readrows=False, dsprefix='', dssuffix='', linked=False, encoding='utf_8', renames={})` This command imports data from a CSV format file. Data are read from the file using the dataset names given at the top of the files in columns. Please see the reading data section of this manual for more information. dsprefix is prepended to each dataset name and dssuffix is added (the prefix option is deprecated and also addeds an underscore to the dataset name). linked specifies whether the data will be linked to the file. renames, if set, provides new names for datasets after import. ImportFileFITS -------------- .. _Command.ImportFileFITS: :command:`ImportFileFits(filename, items, namemap={}, slices={}, twodranges={}, twod_as_oned=set(\[]), wcsmodes={}, prefix='', suffix='', renames={}, linked=False)` Import data from a FITS file. items is a list of datasets to be imported. items are formatted like the following: :: '/': import whole file '/hduname': import whole HDU (image or table) '/hduname/column': import column from table HDU all values in items should be lower case. HDU names have to follow a Veusz-specific naming. If the HDU has a standard name (e.g. primary or events), then this is used. If the HDU has a EXTVER keyword then this number is appended to this name. An extra number is appended if this name is not unique. If the HDU has no name, then the name used should be 'hduX', where X is the HDU number (0 is the primary HDU). namemap maps an input dataset (using the scheme above for items) to a Veusz dataset name. Special suffixes can be used on the Veusz dataset name to indicate that the dataset should be imported specially. :: 'foo (+)': import as +ve error for dataset foo 'foo (-)': import as -ve error for dataset foo 'foo (+-)': import as symmetric error for dataset foo slices is an optional dict specifying slices to be selected when importing. For each dataset to be sliced, provide a tuple of values, one for each dimension. The values should be a single integer to select that index, or a tuple (start, stop, step), where the entries are integers or None. twodranges is an optional dict giving data ranges for 2D datasets. It maps names to (minx, miny, maxx, maxy). twod_as_oned: optional set containing 2D datasets to attempt to read as 1D, treating extra columns as error bars wcsmodes is an optional dict specfying the WCS import mode for 2D datasets in HDUs. The keys are '/hduname' and the values can be 'pixel': number pixel range from 0 to maximum (default) 'pixel_wcs': pixel number relative to WCS reference pixel 'linear_wcs': linear coordinate system from the WCS keywords 'fraction': fractional values from 0 to 1. renames is an optional dict mapping old to new dataset names, to be renamed after importing linked specifies that the dataset is linked to the file. Values under the VEUSZ header keyword can be used to override defaults: :: 'name': override name for dataset 'slice': slice on importing (use format "start:stop:step,...") 'range': should be 4 item array to specify x and y ranges: [minx, miny, maxx, maxy] 'xrange' / 'yrange': individual ranges for x and y 'xcent' / 'ycent': arrays giving the centres of pixels 'xedge' / 'yedge': arrays giving the edges of pixels 'twod_as_oned': treat 2d dataset as 1d dataset with errors 'wcsmode': use specific WCS mode for dataset (see values above) These are specified under the VEUSZ header keyword in the form KEY=VALUE or for column-specific values COLUMNNAME: KEY=VALUE Returns: list of imported datasets ImportFileHDF5 -------------- .. _Command.ImportFileHDF5: :command:`ImportFileHDF5(filename, items, namemap={}, slices={}, twodranges={}, twod_as_oned=set(\[]), convert_datetime={}, prefix='', suffix='', renames={}, linked=False)` Import data from a HDF5 file. items is a list of groups and datasets which can be imported. If a group is imported, all child datasets are imported. namemap maps an input dataset to a veusz dataset name. Special suffixes can be used on the veusz dataset name to indicate that the dataset should be imported specially. :: 'foo (+)': import as +ve error for dataset foo 'foo (-)': import as -ve error for dataset foo 'foo (+-)': import as symmetric error for dataset foo slices is an optional dict specifying slices to be selected when importing. For each dataset to be sliced, provide a tuple of values, one for each dimension. The values should be a single integer to select that index, or a tuple (start, stop, step), where the entries are integers or None. twodranges is an optional dict giving data ranges for 2d datasets. It maps names to (minx, miny, maxx, maxy). twod_as_oned: optional set containing 2d datasets to attempt to read as 1d convert_datetime should be a dict mapping hdf name to specify date/time importing. For a 1d numeric dataset: if this is set to 'veusz', this is the number of seconds since 2009-01-01, if this is set to 'unix', this is the number of seconds since 1970-01-01. For a text dataset, this should give the format of the date/time, e.g. 'YYYY-MM-DD|T|hh:mm:ss' or 'iso' for iso format. renames is a dict mapping old to new dataset names, to be renamed after importing. linked specifies that the dataset is linked to the file. Attributes can be used in datasets to override defaults: :: 'vsz_name': set to override name for dataset in veusz 'vsz_slice': slice on importing (use format "start:stop:step,...") 'vsz_range': should be 4 item array to specify x and y ranges: [minx, miny, maxx, maxy] 'vsz_twod_as_oned': treat 2d dataset as 1d dataset with errors 'vsz_convert_datetime': treat as date/time, set to one of the values above. For compound datasets these attributes can be given on a per-column basis using attribute names vsz_attributename_columnname. Returns: list of imported datasets ImportFileND ------------ .. _Command.ImportFileND: :command:`def ImportFileND(comm, filename, dataset, shape=None, transpose=False, mode='text', csvdelimiter=',', csvtextdelimiter='"', csvlocale='en_US', prefix="", suffix="", encoding='utf_8', linked=False)` Import an n-dimensional dataset from a file. The file should either be in CSV format (mode='csv') or whitespace-separated text (mode='text'). A one-dimensional dataset is given as a list of numbers on a single line/row. A two-dimensional dataset is given by a set of rows. A three-dimensional dataset is given by a set of two-dimensional datasets, with blank lines between them. a four-dimensional dataset is given by a set of three-dimensional datasets with two blank lines between each. Each additional dataset increases the separating number of blank lines by one. Alternatively, the numbers can be given in any form (number of numbers on each row) and "shape" is included to reshape the data into the desired shape. In the file, or included as parameters above, the command "shape num1 num2..." can be included to reshape the output dataset to the shape given by the numbers in the row after "shape" (these should be in separate columns in CSV format). If one of these numbers is -1, then this dimension is inferred from the number of values and the other dimensions. Also supported is the "transpose" command or optional argument which reverses the order of the dimensions. ImportFilePlugin ---------------- .. _Command.ImportFilePlugin: :command:`ImportFilePlugin('pluginname', 'filename', \**pluginargs, linked=False, encoding='utf_8', prefix='', suffix='', renames={})` Import data from file using import plugin 'pluginname'. The arguments to the plugin are given, plus optionally a text encoding, and prefix and suffix to prepend or append to dataset names. renames, if set, provides new names for datasets after import. ImportFITSFile -------------- .. _Command.ImportFITSFile: :command:`ImportFITSFile(datasetname, filename, hdu, datacol='A', symerrcol='B', poserrcol='C', negerrcol='D', linked=True/False, renames={})` This command is deprecated. Please do not use in new code, but instead use ImportFileFITS. This command does a simple import from a FITS file. The FITS format is used within the astronomical community to transport binary data. For a more powerful FITS interface, you can use PyFITS within your scripts. The datasetname is the name of the dataset to import, the filename is the name of the FITS file to import from. The hdu parameter specifies the HDU to import data from (numerical or a name). If the HDU specified is a primary HDU or image extension, then a two-dimensional dataset is loaded from the file. The optional parameters (other than linked) are ignored. Any WCS information within the HDU are used to provide a suitable xrange and yrange. If the HDU is a table, then the datacol parameter must be specified (and optionally symerrcol, poserrcol and negerrcol). The dataset is read in from the named column in the table. Any errors are read in from the other specified columns. If linked is True, then the dataset is not saved with a saved document, but is reread from the data file each time the document is loaded. renames, if set, provides new names for datasets after import. ImportString ------------ .. _Command.ImportString: :command:`ImportString('descriptor', 'data')` Like, :ref:`ImportFile `, but loads the data from the specfied string rather than a file. This allows data to be easily embedded within a document. The data string is usually a multi-line Python string. Returns: A tuple containing a list of the imported datasets and the number of conversions which failed for a dataset. Changed in version 0.5: A tuple is returned rather than just the number of imported variables. .. code-block:: python ImportString('x y', ''' 1 2 2 5 3 10 ''') ImportString2D -------------- .. _Command.ImportString2D: :command:`ImportString2D(datasets, string, xrange=None, yrange=None, invertrows=None, invertcols=None, transpose=None)` Imports a two-dimensional dataset from the string given. This is similar to the :ref:`ImportFile2D ` command, with the same dataset format within the string. The optional values are also listed there. The various controlling parameters can be set within the string. See the :ref:`ImportFile2D ` section for details. ImportStringND -------------- .. _Command.ImportStringND: :command:`ImportStringND(dataset, string, shape=None, transpose=False)` Imports a n-dimensional dataset from the string given. This is similar to the :ref:`ImportFileND ` command. Please look there for more detail and the description of the optional parameters and in-stream allowed parameters. IsClosed -------- .. _Command.IsClosed: :command:`IsClosed()` Returns a boolean value telling the caller whether the plotting window has been closed. Note: this command is only supported in the embedding interface. List ---- .. _Command.List: :command:`List(where='.')` List the widgets which are contained within the widget with the path given, the type of widgets, and a brief description. Load ---- .. _Command.Load: :command:`Load('filename.vsz')` Loads the veusz script file given. The script file can be any Python code. The code is executed using the Veusz interpreter. Note: this command is only supported at the command line and not in a script. Scripts may use the python :command:`execfile` function instead. MoveToPage ---------- .. _Command.MoveToPage: :command:`MoveToPage(pagenum)` Updates window to show the page number given of the document. Note: this command is only supported in the embedding interface or `veusz --listen`. ReloadData ---------- .. _Command.ReloadData: :command:`ReloadData()` Reload any datasets which have been linked to files. Returns: A tuple containing a list of the imported datasets and the number of conversions which failed for a dataset. Rename ------ .. _Command.Rename: :command:`Remove('widgetpath', 'newname')` Rename the widget at the path given to a new name. This command does not move widgets. See :ref:`To ` for a description of the path syntax. '.' can be used to select the current widget. Remove ------ .. _Command.Remove: :command:`Remove('widgetpath')` Remove the widget selected using the path. See :ref:`To ` for a description of the path syntax. ResizeWindow ------------ .. _Command.ResizeWindow: :command:`ResizeWindow(width, height)` Resizes window to be width by height pixels. Note: this command is only supported in the embedding interface or `veusz --listen`. Save ---- .. _Command.Save: :command:`Save('filename.vsz')` Save the current document under the filename given. Set --- .. _Command.Set: :command:`Set('settingpath', val)` Set the setting given by the path to the value given. If the type of :command:`val` is incorrect, an :command:`InvalidType` exception is thrown. The path to the setting is the optional path to the widget the setting is contained within, an optional subsetting specifier, and the setting itself. .. code-block:: python Set('page1/graph1/x/min', -10.) SetAntiAliasing --------------- .. _Command.SetAntiAliasing: :command:`SetAntiAliasing(on)` Enable or disable anti aliasing in the plot window, replotting the image. SetData ------- .. _Command.SetData: :command:`SetData(name, val, symerr=None, negerr=None, poserr=None)` Set the dataset name with the values given. If None is given for an item, it will be left blank. val is the actual data, symerr are the symmetric errors, negerr and poserr and the getative and positive asymmetric errors. The data can be given as lists or numpys. SetDataExpression ----------------- .. _Command.SetDataExpression: :command:`SetDataExpression(name, val, symerr=None, negerr=None, poserr=None, linked=False, parametric=None)` Create a new dataset based on the expressions given. The expressions are Python syntax expressions based on existing datasets. If linked is True, the dataset will change as the datasets in the expressions change. Parametric can be set to a tuple of (minval, maxval, numitems). :command:`t` in the expression will iterate from minval to maxval in numitems values. SetDataND --------- .. _Command.SetDataND: :command:`SetDataRange(name, val)` Set a n-dimensional dataset to be the values given by val. val should be an n-dimensional numpy array of values, or a list of lists. SetDataRange ------------ .. _Command.SetDataRange: :command:`SetDataRange(name, numsteps, val, symerr=None, negerr=None, poserr=None, linked=False)` Set dataset to be a range of values with numsteps steps. val is tuple made up of (minimum value, maximum value). symerr, negerr and poserr are optional tuples for the error bars. If linked is True, the dataset can be saved in a document as a SetDataRange, otherwise it is expanded to the values which would make it up. SetData2D --------- .. _Command.SetData2D: :command:`SetData2D('name', val, xrange=(A,B), yrange=(C,D), xgrid=[1,2,3...], ygrid=[4,5,6...])` Creates a two-dimensional dataset with the name given. val is either a two-dimensional numpy array, or is a list of lists, with each list in the list representing a row. Do not give xrange if xgrid is set and do not give yrange if ygrid is set, and vice versa. xrange and yrange are optional tuples giving the inclusive range of the X and Y coordinates of the data. xgrid and ygrid are optional lists, tuples or arrays which give the coordinates of the edges of the pixels. There should be one more item in each array than pixels. SetData2DExpression ------------------- .. _Command.SetData2DExpression: :command:`SetData2DExpression('name', expr, linked=False)` Create a 2D dataset based on expressions. name is the new dataset name expr is an expression which should return a 2D array linked specifies whether to permanently link the dataset to the expressions. SetData2DExpressionXYZ ---------------------- .. _Command.SetData2DExpressionXYZ: :command:`SetData2DExpressionXYZ('name', 'xexpr', 'yexpr', 'zexpr', linked=False)` Create a 2D dataset based on three 1D expressions. The x, y expressions need to evaluate to a grid of x, y points, with the z expression as the 2D value at that point. Currently only linear fixed grids are supported. This function is intended to convert calculations or measurements at fixed points into a 2D dataset easily. Missing values are filled with NaN. SetData2DXYFunc --------------- .. _Command.SetData2DXYFunc: :command:`SetData2DXYFunc('name', xstep, ystep, 'expr', linked=False)` Construct a 2D dataset using a mathematical expression of "x" and "y". The x values are specified as (min, max, step) in xstep as a tuple, the y values similarly. If linked remains as False, then a real 2D dataset is created, where values can be modified and the data are stored in the saved file. SetDataDateTime --------------- .. _Command.SetDataDateTime: :command:`SetDataDateTime('name', vals)` Creates a datetime dataset of name given. vals is a list of Python datetime objects. SetDataText ----------- .. _Command.SetDataText: :command:`SetDataText(name, val)` Set the text dataset name with the values given. :command:`val` must be a type that can be converted into a Python list. .. code-block:: python SetDataText('mylabel', ['oranges', 'apples', 'pears', 'spam']) SetToReference -------------- .. _Command.SetToReference: :command:`SetToReference(setting, refval)` Link setting given to other setting refval. SetUpdateInterval ----------------- .. _Command.SetUpdateInterval: :command:`SetUpdateInterval(interval)` Tells window to update every interval milliseconds at most. The value 0 disables updates until this function is called with a non-zero. The value -1 tells Veusz to update the window every time the document has changed. This will make things slow if repeated changes are made to the document. Disabling updates and using the ForceUpdate command will allow the user to control updates directly. Note: this command is only supported in the embedding interface or `veusz --listen`. SetVerbose ---------- .. _Command.SetVerbose: :command:`SetVerbose(v=True)` If :command:`v` is :command:`True`, then extra information is printed out by commands. StartSecondView --------------- .. _Command.StartSecondView: :command:`StartSecondView(name = 'window title')` In the embedding interface, this method will open a new Embedding interface onto the same document, returning the object. This new window provides a second view onto the document. It can, for instance, show a different page to the primary view. name is a window title for the new window. Note: this command is only supported in the embedding interface. TagDatasets ----------- .. _Command.TagDatasets: :command:`TagDatasets('tag', ['ds1', 'ds2'...])` Adds the tag to the list of datasets given.. To -- .. _Command.To: :command:`To('widgetpath')` The To command takes a path to a widget and moves to that widget. For example, this may be "/", the root widget, "graph1", "/page1/graph1/x", "../x". The syntax is designed to mimic Unix paths for files. "/" represents the base widget (where the pages reside), and ".." represents the widget next up the tree. Quit ---- .. _Command.Quit: :command:`Quit()` Quits Veusz. This is only supported in `veusz --listen`. WaitForClose ------------ .. _Command.WaitForClose: :command:`WaitForClose()` Wait until the plotting window has been closed. Note: this command is only supported in the embedding interface. Zoom ---- .. _Command.Zoom: :command:`Zoom(factor)` Sets the plot zoom factor, relative to a 1:1 scaling. factor can also be "width", "height" or "page", to zoom to the page width, height or page, respectively. This is only supported in embedded mode or `veusz --listen`. Security ######## With the 1.0 release of Veusz, input scripts and expressions are checked for possible security risks. Only a limited subset of Python functionality is allowed, or a dialog box is opened allowing the user to cancel the operation. Specifically you cannot import modules, get attributes of Python objects, access globals() or locals() or do any sort of file reading or manipulation. Basically anything which might break in Veusz or modify a system is not supported. In addition internal Veusz functions which can modify a system are also warned against, specifically Print(), Save() and Export(). If you are running your own scripts and do not want to be bothered by these dialogs, you can run veusz with the :command:`--unsafe-mode` option. Using Veusz from other programs ############################### Non-Qt Python programs ---------------------- Veusz can be used as a Python module for plotting data. There are two ways to use the module: (1) with an older path-based Veusz commands, used in Veusz saved document files or (2) using an object-oriented interface. With the old style method the user uses a unix-path inspired API to navigate the widget tree and add or manipulate widgets. With the new style interface, the user navigates the tree with attributes of the ``Root`` object to access Nodes. The new interface is likely to be easier to use unless you are directly translating saved files. Older path-based interface -------------------------- .. code-block:: python """An example embedding program. Veusz needs to be installed into the Python path for this to work (use setup.py) This animates a sin plot, then finishes """ import time import numpy import veusz.embed as veusz # construct a Veusz embedded window # many of these can be opened at any time g = veusz.Embedded('window title') g.EnableToolbar() # construct the plot g.To( g.Add('page') ) g.To( g.Add('graph') ) g.Add('xy', marker='tiehorz', MarkerFill__color='green') # this stops intelligent axis extending g.Set('x/autoExtend', False) g.Set('x/autoExtendZero', False) # zoom out g.Zoom(0.8) # loop, changing the values of the x and y datasets for i in range(10): x = numpy.arange(0+i/2., 7.+i/2., 0.05) y = numpy.sin(x) g.SetData('x', x) g.SetData('y', y) # wait to animate the graph time.sleep(2) # let the user see the final result print "Waiting for 10 seconds" time.sleep(10) print "Done!" # close the window (this is not strictly necessary) g.Close() The embed interface has the methods listed in the command line interface listed in the Veusz manual https://veusz.github.io/docs/manual.html Multiple Windows are supported by creating more than one ``Embedded`` object. Other useful methods include: - ``WaitForClose()`` - wait until window has closed - ``GetClick()`` - return a list of ``(axis, value)`` tuples where the user clicks on a graph - ``ResizeWndow(width, height)`` - resize window to be ``width`` x ``height`` pixels - ``SetUpdateInterval(interval)`` - set update interval in ms or 0 to disable - ``MoveToPage(page)`` - display page given (starting from 1) - ``IsClosed()`` - has the page been closed - ``Zoom(factor)`` - set zoom level (float) or 'page', 'width', 'height' - ``Close()`` - close window - ``SetAntiAliasing(enable)`` - enable or disable antialiasing - ``EnableToolbar(enable=True)`` - enable plot toolbar - ``StartSecondView(name='Veusz')`` - start a second view onto the document of the current ``Embedded`` object. Returns a new ``Embedded`` object. - ``Wipe()`` - wipe the document of all widgets and datasets. .. _new_api: New-style object interface -------------------------- In Veusz 1.9 or late a new style of object interface is present, which makes it easier to construct the widget tree. Each widget, group of settings or setting is stored as a Node object, or its subclass, in a tree. The root document widget can be accessed with the ``Root`` object. The dot operator "." finds children inside other nodes. In Veusz some widgets can contain other widgets (Root, pages, graphs, grids). Widgets contain setting nodes, accessed as attributes. Widgets can also contain groups of settings, again accessed as attributes. An example tree for a document (not complete) might look like this :: Root \-- page1 (page widget) \-- graph1 (graph widget) \-- x (axis widget) \-- y (axis widget) \-- function (function widget) \-- grid1 (grid widget) \-- graph2 (graph widget) \-- xy1 (xy widget) \-- xData (setting) \-- yData (setting) \-- PlotLine (setting group) \-- width (setting) ... ... \-- x (axis widget) \-- y (axis widget) \-- graph3 (graph widget) \-- contour1 (contour widget) \-- x (axis widget) \-- y (axis widget) Here the user could access the xData setting node of the xy1 widget using ``Root.page1.graph2.xy1.xData``. To actually read or modify the value of a setting, you should get or set the ``val`` property of the setting node. The line width could be changed like this .. code-block:: python graph = embed.Root.page1.graph2 graph.xy1.PlotLine.width.val = '2pt' For instance, this constructs a simple x-squared plot which changes to x-cubed: .. code-block:: python import veusz.embed as veusz import time # open a new window and return a new Embedded object embed = veusz.Embedded('window title') # make a new page, but adding a page widget to the root widget page = embed.Root.Add('page') # add a new graph widget to the page graph = page.Add('graph') # add a function widget to the graph. The Add() method can take a list of settings # to set after widget creation. Here, "function='x**2'" is equivalent to # function.function.val = 'x**2' function = graph.Add('function', function='x**2') time.sleep(2) function.function.val = 'x**3' # this is the same if the widgets have the default names Root.page1.graph1.function1.function.val = 'x**3' If the document contains a page called "page1" then ``Root.page1`` is the object representing the page. Similarly, ``Root.page1.graph1`` is a graph called ``graph1`` in the page. You can also use dictionary-style indexing to get child widgets, e.g. Root['page1']['graph1']. This style is easier to use if the names of widgets contain spaces or if widget names shadow methods or properties of the Node object, i.e. if you do not control the widget names. Widget nodes can contain as children other widgets, groups of settings, or settings. Groups of settings can contain child settings. Settings cannot contain other nodes. Here are the useful operations of Nodes: .. code-block:: python class Node(object): """properties: path - return path to object in document, e.g. /page1/graph1/function1 type - type of node: "widget", "settinggroup" or "setting" name - name of this node, e.g. "graph1" children - a generator to return all the child Nodes of this Node, e.g. for c in Root.children: print c.path children_widgets - generator to return child widget Nodes of this Node children_settinggroups - generator for child setting groups of this Node children_settings - a generator to get the child settings childnames - return a list of the names of the children of this Node childnames_widgets - return a list of the names of the child widgets childnames_settinggroups - return a list of the names of the setting groups childnames_settings - return a list of the names of the settings parent - return the Node corresponding to the parent widget of this Node __getattr__ - get a child Node with name given, e.g. Root.page1 __getitem__ - get a child Node with name given, e.g. Root['page1'] """ def fromPath(self, path): """Returns a new Node corresponding to the path given, e.g. '/page1/graph1'""" class SettingNode(Node): """A node which corresponds to a setting. Extra properties: val - get or set the setting value corresponding to this value, e.g. Root.page1.graph1.leftMargin.val = '2cm' """ class SettingGroupNode(Node): """A node corresponding to a setting group. No extra properties.""" class WidgetNode(Node): """A node corresponding to a widget. property: widgettype - get Veusz type of widget Methods are below.""" def WalkWidgets(self, widgettype=None): """Generator to walk widget tree and get widgets below this WidgetNode of type given. widgettype is a Veusz widget type name or None to get all widgets.""" def Add(self, widgettype, *args, **args_opt): """Add a widget of the type given, returning the Node instance. """ def Rename(self, newname): """Renames widget to name given. Existing Nodes corresponding to children are no longer valid.""" def Action(self, action): """Applies action on widget.""" def Remove(self): """Removes a widget and its children. Existing Nodes corresponding to children are no longer valid.""" Note that Nodes are temporary objects which are created on the fly. A real widget in Veusz can have several different WidgetNode objects. The operators == and != can test whether a Node points to the same widget, setting or setting group. Here is an example to set all functions in the document to be ``x**2``: .. code-block:: python for n in Root.WalkWidgets(widgettype='function'): n.function.val = 'x**2' Translating old to new style ---------------------------- Here is an example how you might translate the old to new style interface (this is taken from the ``sin.vsz`` example). .. code-block:: python # old (from saved document file) Add('page', name='page1') To('page1') Add('graph', name='graph1', autoadd=False) To('graph1') Add('axis', name='x') To('x') Set('label', '\\\\italic{x}') To('..') Add('axis', name='y') To('y') Set('label', 'sin \\\\italic{x}') Set('direction', 'vertical') To('..') Add('xy', name='xy1') To('xy1') Set('MarkerFill/color', 'cyan') To('..') Add('function', name='function1') To('function1') Set('function', 'sin(x)') Set('Line/color', 'red') To('..') To('..') To('..') .. code-block:: python # new (in python) import veusz.embed embed = veusz.embed.Embedded('window title') page = embed.Root.Add('page') # note: autoAdd=False stops graph automatically adding own axes (used in saved files) graph = page.Add('graph', autoadd=False) x = graph.Add('axis', name='x') x.label.val = '\\\\italic{x}' y = graph.Add('axis', name='y') y.direction.val = 'vertical' xy = graph.Add('xy') xy.MarkerFill.color.val = 'cyan' func = graph.Add('function') func.function.val = 'sin(x)' func.Line.color.val = 'red' PyQt programs ============= There is no direct PyQt interface. The standard embedding interface should work, however. Non Python programs =================== Support for non Python programs is available in a limited form. External programs may execute Veusz using :command:`veusz --listen`. Veusz will read its input from the standard input, and write output to standard output. This is a full Python execution environment, and supports all the scripting commands mentioned in :ref:`Commands `, a :command:`Quit()` command, the :command:`EnableToolbar()` and the :command:`Zoom(factor)` command listed above. Only one window is supported at once, but many :command:`veusz --listen` programs may be started. :command:`veusz --listen` may be used from the shell command line by doing something like: .. code-block:: bash veusz --listen < in.vsz where :command:`in.vsz` contains: .. code-block:: python To(Add('page') ) To(Add('graph') ) SetData('x', arange(20)) SetData('y', arange(20)**2) Add('xy') Zoom(0.5) Export("foo.pdf") Quit() A program may interface with Veusz in this way by using the :command:`popen` C Unix function, which allows a program to be started having control of its standard input and output. Veusz can then be controlled by writing commands to an input pipe. veusz-3.0.1/Documents/manual/html/_sources/introduction.rst.txt0000664000175000017500000011023613305004723024406 0ustar jssjss00000000000000============ Introduction ============ Veusz ===== Veusz is a 2D and 3D scientific plotting package. It is designed to be easy to use, easily extensible, but powerful. The program features a graphical user interface (GUI), which works under Unix/Linux, Windows or Mac OS. It can also be easily scripted (the saved file formats are similar to Python scripts) or used as module inside Python. Veusz reads data from a number of different types of data file, it can be manually entered, or constructed from other datasets. In Veusz the document is built in an object-oriented fashion, where a document is built up by a number of widgets in a hierarchy. For example, multiple function or xy widgets can be placed inside a graph widget, and many graphs can be placed in a grid widget. The program also supports a variety of 3D plots, including 3D point and surface plots. The program produces vector rather than rastered 3D output. Veusz can be extended by the user easily by adding plugins. Support for different data file types can be added with import plugins. Dataset plugins automate the manipulation of datasets. Tools plugins automate the manipulation of the document. Installation ============ Please go to the website_ of Veusz to learn more about the program. Links to binaries, distribution packages and the source package can be found in downloads_. For source installation, please see the package INSTALL. .. _website: https://veusz.github.io/ .. _downloads: https://veusz.github.io/download/ Getting started =============== Veusz includes a built-in tutorial which starts the first time the program is run. You can rerun it later from the Help menu. It also includes many examples_, to show how certain kinds of plots are produced. For more help and link to a video tutorial, see help_. .. _examples: https://veusz.github.io/examples/ .. _help: https://veusz.github.io/help-support/ Terminology =========== Here we define some terminology for future use. Widget ------ A document and its graphs are built up from widgets. These widgets can often by placed within each other, depending on the type of the widget. A widget has children (those widgets placed within it) and its parent. The widgets have a number of different settings which modify their behaviour. These settings are divided into properties, which affect what is plotted and how it is plotted. These would include the dataset being plotted or whether an axis is logarithmic. There are also formatting settings, including the font to be used and the line thickness. In addition they have actions, which perform some sort of activity on the widget or its children, like "fit" for a fit widget. As an aside, using the scripting interface, widgets are specified with a "path", like a file in Unix or Windows. These can be relative to the current widget (do not start with a slash), or absolute (start with a slash). Examples of paths include, `/page1/graph1/x`, `x` and `.`. The widget types include #. :command:`document` - representing a complete document. A document can contain pages. In addition it contains a setting giving the page size for the document. #. :command:`page` - representing a page in a document. One or more graphs can be placed on a page, or a grid. #. :command:`graph` - defining an actual graph. A graph can be placed on a page or within a grid. Contained within the graph are its axes and plotters. A graph can be given a background fill and a border if required. It also has a margin, which specifies how far away from the edge of its parent widget to plot the body of the graph. A graph can contain several axes, at any position on the plot. In addition a graph can use axes defined in parent widgets, shared with other graphs. More than one graph can be placed within in a page. The margins can be adjusted so that they lie within or besides each other. #. :command:`grid` - containing one or more graphs. A grid plots graphs in a gridlike fashion. You can specify the number of rows and columns, and the plots are automatically replotted in the chosen arrangement. A grid can contain graphs or axes. If an axis is placed in a grid, it can be shared by the graphs in the grid. #. :command:`axis` - giving the scale for plotting data. An axis translates the coordinates of the data to the screen. An axis can be linear or logarithmic, it can have fixed endpoints, or can automatically get them from the plotted data. It also has settings for the axis labels and lines, tick labels, and major and minor tick marks. An axis may be "horizontal" or "vertical" and can appear anywhere on its parent graph or grid. If an axis appears within a grid, then it can be shared by all the graphs which are contained within the grid. The :command:`axis-broken` widget is an axis sub-type. It is an axis type where there are jumps in the scale of the axis. The :command:`axis-function` widget allows the user to create an axis where the values are scaled by a monotonic function, allowing non-linear and non-logarithmic axis scales. The widget can also be linked to a different axis via the function. #. plotters - types of widgets which plot data or add other things on a graph. There is no actual plotter widget which can be added, but several types of plotters listed below. Plotters typically take an axis as a setting, which is the axis used to plot the data on the graph (default x and y). #. :command:`function` - a plotter which plots a function on the graph. Functions can be functions of x or y (parametric functions are not done yet!), and are defined in Python expression syntax, which is very close to most other languages. For example `3*x**2 + 2*x - 4`. A number of functions are available (e.g. sin, cos, tan, exp, log...). Technically, Veusz imports the numpy package when evaluating, so numpy functions are available. As well as the function setting, also settable is the line type to plot the function, and the number of steps to evaluate the function when plotting. Filling is supported above/below/left/right of the function. #. :command:`xy` - a plotter which plots scatter, line, or stepped plots. This versatile plotter takes an x and y dataset, and plots (optional) points, in a chosen marker and colour, connecting them with (optional) lines, and plotting (optional) error bars. An xy plotter can also plot a stepped line, allowing histograms to be plotted (note that it doesn't yet do the binning of the data). The settings for the xy widget are the various attributes for the points, line and error bars, the datasets to plot, and the axes to plot on. The xy plotter can plot a label next to each dataset, which is either the same for each point or taken from a text dataset. If you wish to leave gaps in a plot, the input value `nan` can be specified in the numeric dataset. #. :command:`fit` - fit a function to data. This plotter is a like the function plotter, but allows fitting of the function to data. This is achieved by clicking on a "fit" button, or using the "fit" action of the widget. The fitter takes a function to fit containing the unknowns, e.g. `a*x**2 + b*x + c`, and initial values for the variables (here a, b and c). It then fits the data (note that at the moment, the fit plotter fits all the data, not just the data that can be seen on the graph) by minimising the chi-squared. In order to fit properly, the y data (or x, if fitting as a function of x) must have a properly defined, preferably symmetric error. If there is none, Veusz assumes the same fractional error everywhere, or symmetrises asymmetric errors. Note that more work is required in this widget, as if a parameter is not well defined by the data, the matrix inversion in the fit will fail. In addition Veusz does not supply estimates for the errors or the final chi-squared in a machine readable way. If the fitting parameters vary significantly from 1, then it is worth "normalizing" them by adding in a factor in the fit equation to bring them to of the order of 1. #. :command:`bar` - a bar chart which plots sets of data as horizontal or vertical bars. Multiple datasets are supported. In "grouped" mode the bars are placed side-by-side for each dataset. In "stacked" mode the bars are placed on top of each other (in the appropriate direction according to the sign of the dataset). Bars are placed on coordinates given, or in integer values from 1 upward if none are given. Error bars are plotted for each of the datasets. Different fill styles can be given for each dataset given. A separate key value can be given for each dataset. #. :command:`key` - a box which describes the data plotted. If a key is added to a plot, the key looks for "key" settings of the other data plotted within a graph. If there any it builds up a box containing the symbol and line for the plotter, and the text in the "key" setting of the widget. This allows a key to be very easily added to a plot. The key may be placed in any of the corners of the plot, in the centre, or manually placed. Depending on the ordering of the widgets, the key will be placed behind or on top of the widget. The key can be filled and surrounded by a box, or not filled or surrounded. #. :command:`label` - a text label places on a graph. The alignment can be adjusted and the font changed. The position of the label can be specified in fractional terms of the current graph, or using axis coordinates. #. :command:`rect, ellipse` - these draw a rectangle or ellipse, respectively, of size and rotation given. These widgets can be placed directly on the page or on a graph. The centre can be given in axis coordinates or fractional coordinates. #. :command:`imagefile` - draw an external graphs file on the graph or page, with size and rotation given. The centre can be given in axis coordinates or fractional coordinates. #. :command:`line` - draw a line with optional arrowheads on the graph or page. One end can be given in axis coordinates or fractional coordinates. #. :command:`contour` - plot contours of a 2D dataset on the graph. Contours are automatically calculated between the minimum and maximum values of the graph or chosen manually. The line style of the contours can be chosen individually and the region between contours can be filled with shading or color. 2D datasets currently consist of a regular grid of values between minimum and maximum positions in x and y. They can be constructed from three 1D datasets of x, y and z if they form a regular x, y grid. #. :command:`image` - plot a 2D dataset as a colored image. Different color schemes can be chosen. The scaling between the values and the image can be specified as linear, logarithmic, square-root or square. #. :command:`polygon` - plot x and y points from datasets as a polygon. The polygon can be placed directly on the page or within a graph. Coordinates are either plotted using the axis or as fractions of the width and height of the containing widget. #. :command:`boxplot` - plot distribution of points in a dataset. #. :command:`polar` - plot polar data or functions. This is a non-orthogonal plot and is placed directly on the page rather than in a graph. #. :command:`ternary` - plot data of three variables which add up to 100 per cent.This is a non-orthogonal plot and is placed directly on the page rather than in a graph. #. 3D widgets - 3D graphs can be created by adding a 3D scene widget (:command:`scene3d`) to a blank page, or by creating a new 3D document. The 3D scene has settings which control the angle the rotation angle of the plot, the position and color of lighting and the rendering method. To build up a 3D plot the following widgets can be placed inside it: #. :command:`graph3d` - this is an analogous widget to the 2D graph widget, plotting a 3D plot with cartesian axes. It contains three or more axis3d widgets, and plotting widgets. The graph contains settings for the graph size (the default is 1 in each direction) and the 3D position of the graph in the same units. Multiple graph widgets can be added to a scene, though the position and sizes may need to be adjusted. #. :command:`axis3d` - normally a 3D graph has three axes (X, Y and Z), but more axes can be added to plot multiple things on a single axis direction. This works in a similar way to the 2D axis widget. The widget has options for the axis label, tick labels, tick marks and grid lines (which appear on the outside of the 3D cube). An axis can be swiched between linear and logorithmic mode. Scalings can be applied to the data values plotted in that dimension or to the axis labels. #. :command:`point3d` - for plotting points, and optionally connecting lines, in 3D. This, and the other plotting widgets are placed in a graph3d widget. The user provides three 1D datasets for the x, y and z values. The markers can be scaled in size by another optional dataset. The markers can also be colored according to another optional dataset, according to a color map, minimum and maximum. Error bars can be provided for each of the x, y and z datasets. The connecting line can also be colored if a color dataset is provided and a colormap chosen. #. :command:`function3d` - for plotting either a functional line in 3D space or a functional surface. The type of plot is given by the mode parameter. In the case of the line, the x,y,z coordinates can be specified as a function of t, where t goes from 0 to 1, or by giving functions for two of the coordinates as a function of the other. For a surface, the value for x, y or z is given as a function of the other two. In addition, a function returning 0 to 1 can be provided for the color, which specifies the color map value for the surface at each position or the line color. For a 2D surface, the grid lines or surface fill can be hidden or shown. There are also settings giving the number of function evaluations to compute in each direction for a surface, or in one direction for a line. #. :command:`surface3d` - for plotting a two dimensional surface from data values. The user should provide a 2D dataset for the height of a surface. The x, y or z axis for the height and other directions can be chosen. A second 2D dataset can be provided for the color of the surface at each point. Note that the coordinate of the 2D dataset lies at the center of each 2D grid point. The height of the grid at the edge is calculated by linear interpolation. Normally the grid is surrounded by four lines and the surface by two triangles. If a high resolution option is enabled, the each grid point is surrounded by eight lines and the surface drawn by eight triangles. #. :command:`volume3d` - for plotting 3D volumes. In this widget, for a volume described by A×B×C values, then the user should provide four datasets, each containing up to A×B×C values (there can be holes in the representation). Three of the datasets give coordinates of the centers of the 3D cells and the fourth the color of the cell. An example set of datasets would be X=(0,0,0,0,1,1,1,1), Y=(0,0,1,1,0,0,1,1), Z=(0,1,0,1,0,1,0,1), color=(0.1,0.2,0.3,0.4,0.3,0.2,0.1,0). Additionally, the user can provide a transparency dataset, which can be useful for showing or hiding parts of the 3D space. Settings: properties and formatting ----------------------------------- The various settings of the widgets come in a number of types, including integers (e.g. 10), floats (e.g. 3.14), dataset names (`mydata`), expressions (`x+y`), text (`hi there!`), distances (see above), options (`horizontal` or `vertical` for axes). Veusz performs type checks on these parameters. If they are in the wrong format the control to edit the setting will turn red. In the command line, a TypeError exception is thrown. In the GUI, the current page is replotted if a setting is changed when enter is pressed or the user moves to another setting. The settings are split up into formatting settings, controlling the appearance of the plot, or properties, controlling what is plotted and how it is plotted. Default settings, including the default font and line style, and the default settings for any graph widget, can be modified in the "Default styles" dialog box under the "Edit" menu. Default settings are set on a per-document basis, but can be saved into a separate file and loaded. A default default settings file can be given to use for new documents (set in the preferences dialog). Datasets -------- Data are imported into Veusz as a dataset. A dataset is imported from a file, entered manually, set via the command line, or linked to other datasets via an expression or dataset plugin. Each dataset has a unique name in the document. They can be seen in the dataset browser panel, or in the Data, Edit dialog box. To choose the data to be plotted, the user usually selects the dataset in the appropriate setting of a widget. Veusz supports one-dimensional (1D) datasets, which are a list of values with optional error bars. Error bars can either be symmetric or asymmetric. Veusz also supports two-dimensional (2D) data. A 2D dataset is a grid of values, with either a fixed spacing in coordinates, or with arbitrary pixel sizes. An n-dimensional (nD) dataset is an arbitrary matrix of values. These cannot be plotted directly, but subsets can be plotted using python slice syntax to convert to 1D or 2D datasets. In addition to simple numeric datasets, Veusz also supports date-time datasets. For details see the sections on reading data. Also supported are text datasets, which are lists of text strings. Datasets can either be plain lists of values which are stored within the document, or they can be linked to a file, so that the values update if the file is reloaded, or they can be linked to other datasets via expressions or dataset plugins. .. _TextFonts: Text -------------------- Veusz understands a limited set of LaTeX-like formatting for text. There are some differences (for example, `10^23` puts the 2 and 3 into superscript), but it is fairly similar. You should also leave out the dollar signs. Veusz supports superscripts (`^`), subscripts (`_`), brackets for grouping attributes are `{` and `}`. Supported LaTeX symbols include: \\AA, \\Alpha, \\Beta, \\Chi, \\Delta, \\Epsilon, \\Eta, \\Gamma, \\Iota, \\Kappa, \\Lambda, \\Mu, \\Nu, \\Omega, \\Omicron, \\Phi, \\Pi, \\Psi, \\Rho, \\Sigma, \\Tau, \\Theta, \\Upsilon, \\Xi, \\Zeta, \\alpha, \\approx, \\ast, \\asymp, \\beta, \\bowtie, \\bullet, \\cap, \\chi, \\circ, \\cup, \\dagger, \\dashv, \\ddagger, \\deg, \\delta, \\diamond, \\divide, \\doteq, \\downarrow, \\epsilon, \\equiv, \\eta, \\gamma, \\ge, \\gg, \\hat, \\in, \\infty, \\int, \\iota, \\kappa, \\lambda, \\le, \\leftarrow, \\lhd, \\ll, \\models, \\mp, \\mu, \\neq, \\ni, \\nu, \\odot, \\omega, \\omicron, \\ominus, \\oplus, \\oslash, \\otimes, \\parallel, \\perp, \\phi, \\pi, \\pm, \\prec, \\preceq, \\propto, \\psi, \\rhd, \\rho, \\rightarrow, \\sigma, \\sim, \\simeq, \\sqrt, \\sqsubset, \\sqsubseteq, \\sqsupset, \\sqsupseteq, \\star, \\stigma, \\subset, \\subseteq, \\succ, \\succeq, \\supset, \\supseteq, \\tau, \\theta, \\times, \\umid, \\unlhd, \\unrhd, \\uparrow, \\uplus, \\upsilon, \\vdash, \\vee, \\wedge, \\xi, \\zeta. Please request additional characters if they are required (and exist in the unicode character set). Special symbols can be included directly from a character map. Other LaTeX commands are supported. `\\\\` breaks a line. This can be used for simple tables. For example `{a\\\\b} {c\\\\d}` shows `a c` over `b d`. The command `\\frac{a}{b}` shows a vertical fraction a/b. Also supported are commands to change font. The command `\\font{name}{text}` changes the font text is written in to name. This may be useful if a symbol is missing from the current font, e.g. `\\font{symbol}{g}` should produce a gamma. You can increase, decrease, or set the size of the font with `\\size{+2}{text}`, `\\size{-2}{text}`, or `\\size{20}{text}`. Numbers are in points. Various font attributes can be changed: for example, `\\italic{some italic text}` (or use `\\textit` or `\\emph`), `\\bold{some bold text}` (or use `\\textbf`) and `\\underline{some underlined text}`. Example text could include `Area / \\pi (10^{-23} cm^{-2})`, or `\\pi\\bold{g}`. Veusz plots these symbols with Qt's unicode support. You can also include special characters directly, by copying and pasting from a character map application. If your current font does not contain these symbols then you may get a box character. Veusz also supports the evaluation of a Python expression when text is written to the page. Python code is written inside the brackets :command:`%{{ }}%`. Note that the Python evaluation happens before the LaTeX expansion is done. The return value of the expression is converted to text using the Python :command:`str()` function. For example, the expression :command:`%{{2+2}}%` would write :command:`4`. Custom functions and constants are supported when evaluation, in addition to the usual numpy functions. In addition, Veusz defines the following useful functions and values. #. :command:`ENVIRON` is the :command:`os.environ` dict of environment variables. :command:`%{{ENVIRON['USER']}}%` would show the current user in unix. #. :command:`DATE([fmt])` returns the current date, by default in ISO format. fmt is an optional format specifier using :command:`datetime.date.strftime` format specifiers. #. :command:`TIME([fmt])` returns the current date/time, by default in ISO format. fmt is an optional format specifier using :command:`datetime.datetime.strftime` format specifiers. #. :command:`DATA(name[, part])` returns the Veusz dataset with given name. For numeric datasets this is a numpy array. For numeric datasets with errors, part specifies the dataset part to return, i.e. 'data', 'serr', 'perr', 'nerr'. For example, the mean value of a dataset could be shown using :command:`%{{mean(DATA('x'))}}%`. #. :command:`FILENAME()` - returns the current document filename. This can include the directory/folder of the file. Note that the filename is escaped with ESCAPE() so that LaTeX symbols are not expanded when shown. #. :command:`BASENAME()` - returns the current document filename, removing the directory or folder name Note that the filename is escaped with ESCAPE() so that LaTeX symbols are not expanded when shown. #. :command:`ESCAPE(x)` - escapes any LaTeX symbols in x so that they are not interpreted as LaTeX. #. :command:`SETTING(path)` - return the value of the Veusz setting given by the full path, e.g. :command:`%{{SETTING('/page1/width')}}%`. #. :command:`LANG(mapping)` - mapping is a dictionary which maps language names to strings. This returns the string corresponding to the current language. The keys come from the locale names which are the two-letter language codes (e.g. `en` or `fr`), or the full code (e.g. `en_GB` or `de_AT`). The `default` key is used if the language code is not found. An example is :command:`%{{ LANG({'de':'Druck','default':'Pressure'}) }}%`. Measurements ------------ Distances, widths and lengths in Veusz can be specified in a number of different ways. These include absolute distances specified in physical units, e.g. 1cm, 0.05m, 10mm, 5in and 10pt, and relative units, which are relative to the largest dimension of the page, including 5%, 1/20, 0.05. Color theme ----------- From version 1.26, widgets are colored automatically using the color theme. This theme is specified in the main document widget settings. Widgets are given the colors in order given the order in a graph widget. The default theme can be specified in the preferences dialog box. To override a theme, the user can manually specify the individual colors in the custom definitions dialog box. Color `theme1` is used as the first theme color, then `theme2`, etc. Axis numeric scales ------------------- The way in which numbers are formatted in axis scales is chosen automatically. For standard numerical axes, values are shown with the `%Vg` formatting (see below). For date axes, an appropriate date formatting is used so that the interval shown is correct. A format can be given for an axis in the axis number formatting panel can be given to explicitly choose a format. Some examples are given in the drop down axis menu. Hold the mouse over the example for detail. C-style number formatting is used with a few Veusz specific extensions. Text can be mixed with format specifiers, which start with a `%` sign. Examples of C-style formatting include: `%.2f` (decimal number with two decimal places, e.g. 2.01), `%.3e` (scientific formatting with three decimal places, e.g. 2.123e-02), `%g` (general formatting, switching between `%f` and `%e` as appropriate). See ``_ for details. Veusz extensions include `%Ve`, which is like `%e` except it displays scientific notation as written, e.g. 1.2x10^23, rather than 1.2e+23. `%Vg` switches between standard numbers and Veusz scientific notation for large and small numbers. `%VE` using engineering SI suffixes to represent large or small numbers (e.g. 1000 is 1k). Veusz allows dates and times to be formatted using `%VDX` where `X` is one of the formatting characters for strftime (see ``_ for details). These include `a` for an abbreviated weekday name, `A` for full weekday name, `b` for abbreviated month name, `B` for full month name, `c` date and time representation, `d` day of month 01..31, `H` hour as 00..23, `I` hour as 01..12, `j` as day of year 001..366, `m` as month 01..12, `M` minute as 00..59, `p` AM/PM, `S` second 00..61, `U` week number of year 00..53 (Sunday as first day of week), `w` weekday as decimal number 0..6, `W` week number of year (Monday as first day of week), `x` date representation, `X` time representation, `y` year without century 00..99 and `Y` year. `%VDVS` is a special Veusz addon format which shows seconds and fractions of seconds (e.g. 12.2). Three dimensional (3D) plots ---------------------------- When drawing in three dimensions, Veusz builds up a 3D "scene" for the graph from the various plotting widgets, made up of triangles, line segments, points and text. Veusz does not use a standard (e.g. OpenGL) drawing method, but renders the scene itself. The advantage of this is that it can produce vector rather than bitmap or raster output. OpenGL, for example, is based around bitmaps. Veusz applies lighting to the scene. The lighting depends on enabled light sources, which are set in the scene3d widget. Light sources have a color, intensity and position. Note that only the angle of the light to a surface affects its lighting, not its distance. The position of the light is relative to the viewer (camera), not the graph. Positive light coordinates are towards the graph (z), upwards (y) and rightwards (x). Normally each solid surface has an intrinsic color, which can be seen without any lighting. If a light source is enabled, the color of the light is added to the surface color, depending on the reflectivity of the surface. Each surface also has a transparency setting. By default, Veusz uses a naive Painter's Algorithm to draw the scene. It draws from the back of scene to the front. The main problem with this algorithm is that shapes and lines overlapping in depth can be confused as the depth of each object is calculated at only one point. In addition objects may intersect, which is not properly treated. In the scene3d object, the user can switch to a different rendering mode called BSP. In this accurate BSP mode, the objects are split so that they never overlap from any viewing angle. The disadvantage of this mode is that it is slow, uses a lot of memory and produces large output files. We plan in future to add another mode which handles overlaps better and does not unnecessarily split objects. The plot is affected by the viewing angle, which is specified in the scene3d widget settings. The rotation is given be three rotations around lines in X, Y and Z directions (note that these are not the same directions as the X, Y and Z axes!). The X axis runs horizontally on the screen, the Y axis runs vertically, and the Z axis runs along the line of sight. There is also a distance setting, which moves graphs closer to or away from the viewer. At larger distances the effect of perspective reduces, meaning that parts of the plot closer to the viewer are not larger than if they were at the farthest side. At large distances, a plot tends towards being isometric. At small distances, shapes are more distorted (note by default the size of the graph is 1 in these distance units). It is currently possible to place graphs inside the camera leading to strange output. By default, Veusz enlarges the 3D rendered scene to fill the bounds of the 3D scene widget, so distance has no effect on the size of the plot. This scaling can be switched off by modifying the Size setting from "Auto" to a fixed number. A fixed size is useful if the user wants a graph to be the same size for any rotation. With this setting the size of the plot is affected by their distance. By default, a 3D graph has dimensions of 1 along the X, Y and Z axes. The size can be adjusted using the size settings in the graph3d widget. Care should be taken that the graph size does not lead to points being at negative viewing distances. The default position of the plot is at the origin 0,0,0. If the user wants to plot multiple graph3d widgets, the positions should be adjusted to prevent overlap. Normally in Veusz, sizes of objects (e.g. plot markers) are given in physical units. This makes less sense for a 3D plot as sizes can depend on distance. In a 3D graph sizes of plotting markers and line widths are given in 1/1000 of the graph bounding box maximum dimension. The main window =============== You should see the main window when you run Veusz (you can just type the veusz command in Unix). .. image:: _images/mainwindow.png The Veusz window is split into several sections. At the top is the menu bar and tool bar. These work in the usual way to other applications. Sometimes options are disabled (greyed out) if they do not make sense to be used. If you hold your mouse over a button for a few seconds, you will usually get an explanation for what it does called a "tool tip". Below the main toolbar is a second toolbar for constructing the graph by adding widgets (on the left), and some editing buttons. The add widget buttons add the request widget to the currently selected widget in the selection window. The widgets are arranged in a tree-like structure. Below these toolbars and to the right is the plot window. This is where the current page of the current document is shown. You can adjust the size of the plot on the screen (the zoom factor) using the "View" menu or the zoom tool bar button (the magnifying glass). Initially you will not see a plot in the plot window, but you will see the Veusz logo. At the moment you cannot do much else with the window. In the future you will be able to click on items in the plot to modify them. To the left of the plot window is the selection window, and the properties and formatting windows. The properties window lets you edit various aspects of the selected widget (such as the minimum and maximum values on an axis). Changing these values should update the plot. The formatting lets you modify the appearance of the selected widget. There are a series of tabs for choosing what aspect to modify. The various windows can be "dragged" from the main window to "float" by themselves on the screen. To the bottom of the window is the console. This window is not shown by default, but can be enabled in the View menu. The console is a Veusz and Python command line console. To read about the commands available see :ref:`Commands `. As this is a Python console, you can enter mathematical expressions (e.g. `1+2.0*cos(pi/4)`) here and they will be evaluated when you press Enter. The usual special functions and the operators are supported. You can also assign results to variables (e.g. `a=1+2`) for use later. The console also supports command history like many Unix shells. Press the up and down cursor keys to browse through the history. Command line completion is not available yet! There also exists a dataset browsing window, by default to the right of the screen. This window allows you to view the datasets currently loaded, their dimensions and type. Hovering a mouse over the size of the dataset will give you a preview of the data. My first plot ============= After opening Veusz, on the left of the main window, you will see a Document, containing a Page, which contains a Graph with its axes. The Graph is selected in the selection window. The toolbar above adds a new widget to the selected widget. If a widget cannot be added to a selected widget it is disabled. On opening a new document Veusz automatically adds a new Page and Graph (with axes) to the document. You will see something like this: .. image:: _images/winwithgraph.png Select the x axis which has been added to the document (click on `x` in the selection window). In the properties window you will see a variety of different properties you can modify. For instance you can enter a label for the axis by writing `Area (cm^{2})` in the box next to label and pressing enter. Veusz supports text in LaTeX-like form (without the dollar signs). Other important parameters is the `log` switch which switches between linear and logarithmic axes, and `min` and `max` which allow the user to specify the minimum and maximum values on the axes. The formatting dialog lets you edit various aspects of the graph appearance. For instance the "Line" tab allows you to edit the line of the axis. Click on "Line", then you can then modify its colour. Enter "green" instead of "black" and press enter. Try making the axis label bold. Now you can try plotting a function on the graph. If the graph, or its children are selected, you will then be able to click the "function" button at the top (a red curve on a graph). You will see a straight line (y=x) added to the plot. If you select "function1", you will be able to edit the functional form plotted and the style of its line. Change the function to `x**2` (x-squared). We will now try plotting data on the graph. Go to your favourite text editor and save the following data as test.dat: :: 1 0.1 -0.12 1.1 0.1 2.05 0.12 -0.14 4.08 0.12 2.98 0.08 -0.1 2.9 0.11 4.02 0.04 -0.1 15.3 1.0 The first three columns are the x data to plot plus its asymmetric errors. The final two columns are the y data plus its symmetric errors. In Veusz, go to the "Data" menu and select "Import". Type the filename into the filename box, or use the "Browse..." button to search for the file. You will see a preview of the data pop up in the box below. Enter `x,+,- y,+-` into the descriptors edit box (note that commas and spaces in the descriptor are almost interchangeable in Veusz 1.6 or newer). This describes the format of the data which describes dataset "x" plus its asymmetric errors, and "y" with its symmetric errors. If you now click "Import", you will see it has imported datasets `x` and `y`. To plot the data you should now click on `graph1` in the tree window. You are now able to click on the "xy" button (which looks like points plotted on a graph). You will see your data plotted on the graph. Veusz plots datasets `x` and `y` by default, but you can change these in the properties of the "xy" plotter. You are able to choose from a variety of markers to plot. You can remove the plot line by choosing the "Plot Line" subsetting, and clicking on the "hide" option. You can change the colour of the marker by going to the "Marker Fill" subsetting, and entering a new colour (e.g. red), into the colour property. veusz-3.0.1/Documents/manual/html/_sources/datasets.rst.txt0000664000175000017500000006156013161413406023504 0ustar jssjss00000000000000Reading data ============ Currently Veusz supports reading data from files with text, CSV, HDF5, FITS, 2D text or CSV, QDP, binary and NPY/NPZ formats. Use the :menuselection:`Data --> Import` dialog to read data, or the importing commands in the API can be used. In addition, the user can load or write import plugins in Python which load data into Veusz in an arbitrary format. At the moment QDP, binary and NPY/NPZ files are supported with this method. The HDF5 file format is the most sophisticated, and is recommended for complex datasets. By default, data are "linked" to the file imported from. This means that the data are not stored in the Veusz saved file and are reloaded from the original data file when opening. In addition, the user can use the :menuselection:`Data --> Reload` menu option to reload data from linked files. Unselect the linked option when importing to remove the association with the data file and to store the data in the Veusz saved document. Note that a prefix and suffix can be given when importing. These are added to the front or back of each dataset name imported. They are convenient for grouping data together. .. image:: _images/importdialog.png We list the various types of import below. Standard text import -------------------- The default text import operates on simple text files. The data are assumed to be in columns separated by whitespace. Each column corresponds to dataset (or its error bars). Each row is an entry in the dataset. The way the data are read is goverened by a simple "descriptor". This can simply be a list of dataset names separated by spaces. If no descriptor is given, the columns are treated as separate datasets and are given names `col1`, `col2`, etc. Veusz attempts to automatically determine the type of the data. When reading in data, Veusz treats any whitespace as separating columns. The columns do not actually need to be aligned. Furthermore a `\\` symbol can be placed at the end of a line to mark a continuation. Veusz will read the next line as if it were placed at the end of the current line. In addition comments and blank lines are ignored (unless in block mode). Comments start with a `#`, `;`, `!` or `%`, and continue until the end of the line. The special value `nan` can be used to specify a break in a dataset. If the option to read data in blocks is enabled, Veusz treats blank lines (or lines starting with the word `no`) as block separators. For each dataset in the descriptor, separate datasets are created for each block, using a numeric suffix giving the block number, e.g. `_1`, `_2`. Data types in text import ````````````````````````` Veusz supports reading in several types of data. The type of data can be added in round brackets after the name in the descriptor. Veusz will try to guess the type of data based on the first value, so you should specify it if there is any form of ambiguity (e.g. is 3 text or a number). Supported types are numbers (use numeric in brackets) and text (use text in brackets). An example descriptor would be `x(numeric) +- y(numeric) + - label(text)` for an x dataset followed by its symmetric errors, a y dataset followed by two columns of asymmetric errors, and a final column of text for the label dataset. A text column does not need quotation unless it contains space characters or escape characters. However make sure you deselect the "ignore text" option in the import dialog. This ignores lines of text to ease the import of data from other applications. Quotation marks are recommended around text if you wish to avoid ambiguity. Text is quoted according to the Python rules for text. Double or single quotation marks can be used, e.g. `"A 'test'"`, `'A second "test"'`. Quotes can be escaped by prefixing them with a backslash, e.g. `"A new \\"test\\""`. If the data are generated from a Python script, the repr function provides the text in a suitable form. Dates and times are also supported with the syntax `dataset(date)`. Dates must be in ISO format `YYYY-MM-DD`. Times are in 24 hour format hh:mm:ss.ss. Dates with times are written YYYY-MM-DDThh:mm:ss.ss (this is a standard ISO format, see ``_). Dates are stored within Veusz as a number which is the number of seconds since the start of January 1st 2009. Veusz also supports dates and times in the local format, though take note that the same file and data may not work on a system in a different location. Descriptors ``````````` .. _Descriptors: A list of datasets, or a "Descriptor", is given in the Import dialog to describe how the data are formatted in the import file. The descriptor at its simplest is a space or comma-separated list of the names of the datasets to import. These are columns in the file. Following a dataset name the text `+`, `-`, or `+-` can be given to say that the following column is a positive error bar, negative error bar or symmetric error bar for the previous (non error bar) dataset. These symbols should be separated from the dataset name or previous symbol with a space or a comma symbol. In addition, if multiple numbered columns should be imported, the dataset name can be followed by square brackets containing a range in the form `[a:b]` to number columns a to b, or `[:]` to number remaining columns. See below for examples of this use. Dataset names can contain virtually any character, even unicode characters. If the name contains non alpha-numeric characters (characters outside of A-Z, a-z and 0-9), then the dataset name should be contained within back-tick characters. An example descriptor is :command:`\`length data (m)\`,+- \`speed (mps)\`,+,-`, for two datasets with spaces and brackets in their names. Instead of specifying the descriptor in the Import dialog, the descriptor can be placed in the data file using a descriptor statement on a separate line, consisting of "descriptor" followed by the descriptor. Multiple descriptors can be placed in a single file, for example: :: # here is one section descriptor x,+- y,+,- 1 0.5 2 0.1 -0.1 2 0.3 4 0.2 -0.1 # my next block descriptor alpha beta gamma 1 2 3 4 5 6 7 8 9 # etc... Descriptor examples ``````````````````` #. :command:`x y` two columns are present in the file, they will be read in as datasets `x` and `y`. #. :command:`x,+- y,+,-` or :command:`x +- y + -` two datasets are in the file. Dataset "x" consists of the first two columns. The first column are the values and the second are the symmetric errors. "y" consists of three columns (note the comma between + and -). The first column are the values, the second positive asymmetric errors, and the third negative asymmetric errors. Suppose the input file contains: :: 1.0 0.3 2 0.1 -0.2 1.5 0.2 2.3 2e-2 -0.3E0 2.19 0.02 5 0.1 -0.1 Then x will contain `1+-0.3`, `1.5+-0.2`, `2.19+-0.02`. y will contain `2 +0.1 -0.2`, `2.3 +0.02 -0.3`, `5 +0.1 -0.1`. #. :command:`x[1:2] y[:]` the first column is the data `x_1`, the second `x_2`. Subsequent columns are read as `y[1]` to `y[n]`. #. :command:`y[:]+-` read each pair of columns as a dataset and its symmetric error, calling them `y[1]` to `y[n]`. #. :command:`foo,,+-` read the first column as the foo dataset, skip a column, and read the third column as its symmetric error. CSV files --------- CVS (comma separated variable) files are often written from other programs, such as spreadsheets, including Excel and Gnumeric. Veusz supports reading from these files. In the import dialog choose "CSV", then choose a filename to import from. In the CSV file the user should place the data in either rows or columns. Veusz will use a name above a column or to the left of a row to specify what the dataset name should be. The user can use new names further down in columns or right in rows to specify a different dataset name. Names do not have to be used, and Veusz will assign default `col` and `row` names if not given. You can also specify a prefix which is prepended to each dataset name read from the file. To specify symmetric errors for a column, put `+-` as the dataset name in the next column or row. Asymmetric errors can be stated with `+` and `-` in the columns. The data type in CSV files are automatically detected unless specified. The data type can be given in brackets after the column name, e.g. `name (text)`, where the data type is `date`, `numeric` or `text`. Explicit data types are needed if the data look like a different data type (e.g. a text item of `1.23`). The date format in CSV files can be specified in the import dialog box - see the examples given. In addition CSV files support numbers in European format (e.g. 2,34 rather than 2.34), depending on the setting in the dialog box. HDF5 files ---------- HDF5 is a flexible data format. Datasets and tables can be stored in a hierarchical arrangements of groups within a file. Veusz supports reading 1D numeric, text, date-time, 2D numeric or n-dimensional numeric data from HDF files. The :command:`h5py` Python module must be installed to use HDF5 files (included in binary releases). In the import dialog box, choose which individual datasets to import, or selecting a group to import all the datasets within the group. If selecting a group, datasets in the group incompatible with Veusz are ignored. A name can be provided for each dataset imported by entering one under "Import as". If one is not given, the dataset or column name is used. The name can also be specified by setting the HDF5 dataset attribute ``vsz_name`` to the name. Note that for compound datasets (tables), ``vsz_`` attributes for columns are given by appending the suffix ``_columnname`` to the attribute. Error bars `````````` Error bars are supported in two ways. The first way is to combine 1D datasets. For the datasets which are error bars, use a name which is the same as the main dataset but with the suffix `(+-)`, `(+)` or `(-)`, for symmetric, postive or negative error bars, respectively. The second method is to use a 2D dataset with two or three columns, for symmetric or asymmetric error bars, respectively. Click on the dataset in the dialog and choose the option to import as a 1D dataset. This second method can also be enabled by adding an HDF5 attribute ``vsz_twod_as_oned`` set to a non-zero value for the dataset. Slices `````` You may wish to reduce the dimensions of a dataset before importing by slicing. You can also give a slice to import a subset of a dataset. When importing, in the slice column you can give a slice expression. This should have the same number of entries as the dataset has dimensions, separated by commas. An entry can be a single number, to select a particular row or column. Alternatively it could be an expression like ``a:b:c`` or ``a:b``, where ``a`` is the starting index, ``b`` is one beyond the stopping index and optionally ``c`` is the step size. A slice can also be specified by providing an HDF5 attribute ``vsz_slice`` for the dataset. 2D data ranges `````````````` 2D data have an associated X and Y range. By default the number of pixels of the image are used to give this range. A range can be specified by clicking on the dataset and entering a minimum and maximum X and Y coordinates. Alternatively, provide the HDF5 attribute for the dataset ``vsz_range``, which should be set to an array of four values (minimum x, minimum y, maximum x, maximum y). Dates ````` Date/time datasets can be made from a 1D numeric dataset or from a text dataset. For the 1D dataset, use the number of seconds relative to the start of the year 2009 (this is Veusz format) or the year 1970 (this is Unix format). In the import dialog, click on the name of the dataset and choose the date option. To specify a date format in the HDF5 file, set the attribute ``vsz_convert_datetime`` to either ``veusz`` or ``unix``. For text datasets, dates must be given in the right format, selected in the import dialog after clicking on the dataset name. As in other file formats, by default Veusz uses ISO 8601 format, which looks like `2013-12-22T21:08:07`, where the date and time parts are optional. The T is also optional. You can also provide your own format when importing by giving a date expression using YYYY, MM, DD, hh, mm and ss (e.g. `YYYY-MM-DD|T|hh:mm:ss`), where vertical bars mark optional parts of the expression. To automate this, set the attribute ``vsz_convert_datetime`` to the format expression or ``iso`` to specify ISO format. 2D text or CSV format --------------------- Veusz can import 2D data from standard text or CSV files. In this case the data should consist of a matrix of data values, with the columns separated by one or more spaces or tabs and the rows on different lines. In addition to the data the file can contain lines at the top which affect the import. Such specifiers are used, for example, to change the coordinates of the pixels in the file. By default the first pixels coordinates is between 0 and 1, with the centre at 0.5. Subsequent pixels are 1 greater. Note that the lowest coordinate pixel is the bottom-left value in the table of imported values. When using specifiers in CSV files, put the different parts (separated by spaces) in separate columns. Below are listed the specifiers: #. :command:`xrange A B` - make the 2D dataset span the coordinate range A to B in the x-axis (where A and B are numbers). Note that the range is inclusive, so a 1 pixel wide image with A=0 and B=1 would have the pixel centre at 0.5. The pixels are assumed to have the same spacing. Do not use this as the same time as the :command:`xedge` or :command:`xcent` options. #. :command:`yrange A B` - make the 2D dataset span the coordinate range A to B in the y-axis (where A and B are numbers). #. :command:`xedge A B C...` - rather than assume the pixels have the same spacing, give the coordinates of the edges of the pixels in the x-axis. The numbers should be space-separated and there should be one more number than pixels. Do not give :command:`xrange` or :command:`xcent` if this is given. If the values are increasing, the lowest coordinate value is at the left of the dataset, otherwise if they are decreasing, it is on the right (unless the rows/columns are inverted or transposed). #. :command:`yedge A B C...` - rather than assume the pixels have the same spacing, give the coordinates of the edges of the pixels in the y-axis. If the values are increasing, the lowest coordinate value is at the bottom row. If they instead decrease, it is at the top. #. :command:`xcent A B C...` - rather than give a total range or pixel edges, give the centres of the pixels. There should be the same number of values as pixels in the image. Do not give :command:`xrange` or :command:`xedge` if this is given. The order of the values specify whether the pixels are left to right or right to left. #. :command:`ycent A B C...` - rather than give a total range or pixel edges, give the centres of the pixels. The value order specifies whether the pixels are bottom to top, or top to bottom. #. :command:`invertrows` - invert the rows after reading the data. #. :command:`invertcols` - invert the columns after reading the data. #. :command:`transpose` - swap rows and columns after importing data. #. :command:`gridatedge` - the first row and leftmost column give the positions of the centres of the pixels. This is also an option in the import dialog. The values should be increasing or decreasing. FITS files ---------- 1D, 2D or n-dimensional data can be read from FITS files. 1D or 2D data can be read from image, primary or table HDUs. nD data can be read from from image or primary extensions. Note that pyfits or astropy must be installed to get FITS support. The import dialog box uses a tree to show the structure of the FITS file. The user can choose to import the whole file, by clicking the check box at the top. They can import data from a particular HDU by selecting that, or individual table columns can be selected. In the dialog box, a dataset can be given a name for the dataset. Otherwise the HDU or table column name is used. Note that a prefix and/or suffix can be specified to be added to all dataset names. If dataset y should have an error bar specified by column yerr, then in the name for yerr, enter 'y (+-)'. Asymmetric error bars can be specified using (+) and (-) on inidividual columns. The slice column can be used to only import a subset of the dataset imported. This uses Python slicing syntax, which is comma-separated list of ranges and steps. A range is specified like 10:20, which selects the 11th to 20th items (the indices are numbered from 0, and the final index is one past the index you actually want). A stepped range can look like 10:20:2, which selects every other item in that range. Each of these numbers are optional, so : selects all items on that dimension. For example the slice :,10:14:2 selects all values on the first dimension, but only the 11th and 13th items on the next axis. When importing 2D data the user can specify whether to treat this as 1D plus error bars (dimensions should have 2 or 3 columns), or specify a range in 2D space the data covers. Veusz will also attempt to use WCS information in the file for the 2D range if not specified. The standard mode is to use the CDELT, CRVAL and CRPIX keywords to specify a linear range for the data. Alternatively the user can specify pixel numbering (numbering from 0 to N-1). There is a fraction option for using a range of 0 to 1. Finally there is a pixel numbering scheme which numbers in pixels from the CRPIX keyword items. Some of these options can be specified in the FITS file using the 'VEUSZ' header keyword. This header keyword can be added with the value 'KEY=VALUE' (applying to the whole HDU) or 'COLUMN: KEY=VALUE' (applying to a particular column in a table). Supported options for KEY are: name provide name for dataset in VALUE slice VALUE is slice to apply when importing dataset range range of data for 2D dataset in form `[minx, miny, maxx, maxy]` xrange/yrange range of dataset individually in x or y xcent/ycent set to list of values giving centers of pixels xedge/yedge set to list of values giving edges of pixels twod_as_oned treat as 1D data with error bars if VALUE=1 wcsmode use specific WCS mode for 2D dataset (should be pixel/pixel_wcs/linear_wcs/fraction) Reading other data formats -------------------------- As mentioned above, a user may write some Python code to read a data file or set of data files. To write a plugin which is incorportated into Veusz, see ``_ You can also include Python code in an input file to read data, which we describe here. Suppose an input file "in.dat" contains the following data: :: 1 2 2 4 3 9 4 16 Of course this data could be read using the :ref:`ImportFile ` command. However, you could also read it with the following Veusz script (which could be saved to a file and loaded with :command:`execfile` or :ref:`Load `. The script also places symmetric errors of 0.1 on the x dataset. .. code-block:: python x = [] y = [] for line in open("in.dat"): parts = [float(i) for i in line.split()] x.append(parts[0]) y.append(parts[1]) SetData('x', x, symerr=0.1) SetData('y', y) Manipulating datasets ===================== Imported datasets can easily be modified in the Data Editor dialog box. This dialog box can also be used to create new datasets from scratch by typing them in. The Data Create dialog box is used to new datasets as a numerical sequence, parametrically or based on other datasets given expressions. If you want to plot a function of a dataset, you often do not have to create a new dataset. Veusz allows to enter expressions directly in many places. Using dataset plugins --------------------- Dataset plugins can be used to perform arbitrary manipulation of datasets. Veusz includes several plugins for mathematical operation of data and other dataset manipulations, such as concatenation or splitting. If you wish to write your own plugins look at ``_. Using expressions to create new datasets ---------------------------------------- For instance, if the user has already imported dataset d, then they can create d2 which consists of d**2. Expressions are in Python numpy syntax and can include the usual mathematical functions. .. image:: _images/createdataset.png Expressions for error bars can also be given. By appending :command:`_data`, :command:`_serr`, :command:`_perr` or :command:`_nerr` to the name of the dataset in the expression, the user can base their expression on particular parts of the given dataset (the main data, symmetric errors, positive errors or negative errors). Otherwise the program uses the same parts as is currently being specified. If a dataset name contains non alphanumeric characters, its name should be quoted in the expression in back-tick characters, e.g. :command:`\`length (cm)\`*2`. The numpy functionality is particularly useful for doing more complicated expressions. For instance, a conditional expression can be written as :command:`where(x`_ for details. Capturing data ============== In addition to the standard data import, data can be captured as it is created from an external program, a network socket or a file or named pipe. When capturing from a file, the behaviour is like the Unix :command:`tail -f` command, where new lines written to the file are captured. To use the capturing facility, the data must be written in the simple line based standard Veusz text format. Data are whitespace separated, with one value per dataset given on a single line. To capture data, use the dialog box :menuselection:`Data --> Capture`. A list of datasets should be given. This is the :ref:`standard descriptor format `. Choose the source of the data, which is either a a filename or named pipe, a network socket to connect to, or a command line for an external program. Capturing ends if the source of the data runs out (for external programs or network sockets) or the finish button is clicked. It can optionally end after a certain number of data lines or when a time period has expired. Normally the data are updated in Veusz when the capturing is finished. There is an option to update the document at intervals, which is useful for monitoring. A plot using the variables will update when the data are updated. Click the ``Capture`` button to start the capture. Click ``Finish`` or ``Cancel`` to stop. Cancelling destroys captured data. veusz-3.0.1/Documents/manual/html/genindex.html0000664000175000017500000000502313325026534021165 0ustar jssjss00000000000000 Index — Veusz 3.0.1 documentation

        Index

        veusz-3.0.1/Documents/manual/pdf/0000775000175000017500000000000013325026667016312 5ustar jssjss00000000000000veusz-3.0.1/Documents/manual/pdf/veusz.pdf0000664000175000017500000166207713325026572020177 0ustar jssjss00000000000000%PDF-1.5 % 1 0 obj << /Length 843 /Filter /FlateDecode >> stream xmUMo0WxNWH Z&T~3ڮzy87?nkNehܤ=77U\;?:׺v==onU;O^uu#½O ۍ=٘a?kLy6F/7}̽][H<Sicݾk^90jYVH^v}0<rL ͯ_/CkBnyWTHkuqö{s\녚"p]ϞќKյ u/A )`JbD>`2$`TY'`(ZqBJŌ )Ǩ%553<,(hlwB60aG+LgıcW c rn q9Mܗ8% CMq.5ShrAI皎\Sȩ ]8 `Y7ь1Oyezl,d mYĸSSJf-1i:C&e c4R$D& &+übLaj by+bYBg YJYYr֟bx(rGT̛`F+٭L ,C9?d+͊11ӊĊ׊T_~+Cg!o!_??/?㫄Y ?^B\jUP{xᇻL^U}9pQq0O}c}3tȢ}Ə!VOu˷ endstream endobj 3 0 obj << /Type /ObjStm /N 100 /First 825 /Length 1403 /Filter /FlateDecode >> stream xڽXMo7W1onhvۋ/ڒ$MD.,9Z⛙f\JȒȑH(EO&I򗆤$<~IԤjERf"Zd`lTA˕&֐Tת8@P]뉆+ #+ON(Yye)6WE^ZWO,dir "uM!He$xī'o+*|efƀS3%N!F86鍘 5@x}Q9C";fA`%o 8BUZz(hQl3dg@ Q3tjy Kd%"<805aj!6QrmBiLxVQr5q D(`FVE-XFVA;TCv ȜnN8hϩ d'3"y ȞQ@=!O<Ԝ,a%$PJm&^_^.-NWfRדWY>aÚ%sv<]]lnx)rf CnGS0}o}pu?pU*:.}×ae ^ njuHN<} w-#8[-Kty[JMX_9 7+R7-:5g&+ަY}3;(40}?Ftkh#MZھ_3JBRzi!>sVRW4Dž rݦ6]s囃z_~US7W;!3E{,X,/!۶ ƦZ eԟiqˑ\cH"\s1Vi 7}s+1mÛ+,8ޤe|G3Q3GهY,t؋phWɑЙ8?``Nr[ i:q#c}t9Ët|+έz6u?T}ER.{/߼ `伶e}My_R}0fQ4u;+=doLDLYLUz@۔_^,R"M΢M8#(m2){̌O`s(]᫴oC6N)+;ɯqބX/ZF'\ 'izdڥgS[4G^+zH;v>u9!^[.MKSN尜\)vc.v:LSIdm^K偡]>6 G?_nG ijqhG3$rf!gڦ??*Qu,0]\뼓YU5?6 endstream endobj 204 0 obj << /Type /ObjStm /N 100 /First 875 /Length 1137 /Filter /FlateDecode >> stream xڽXMoG W13C:-n:mKbP[kXNqQpJZ={$d6ƹbsƅoo'|w4>|11筡={6 x>xr&z`71bIXMJ2_Lv8y&S<),ɔs ,6m'cL08q>4td8v32k9r99/d)9W,iI@#VW$IK6$˒6Q NJd`I ͅ,I*1xeFH$lKVIlDiG ,K@Ќ $qȋsQ.ȭ %H,LS !KR HA)" ЏXP ($LCA IQ$ KPԙ CAP 8mab{DP%Js 2 aY*ᐄ5GPqr89A)u2". d$ $CeYq䏱/X(9͢1g/̓êYS܉yl-'ʹ1bbżNFs*A(ϕb(Nu{Y윭|%~ʮ5vߺ(Oqp:OlN3)f!̈")x.zow\yU??~B{xb|:-߹:#\6ӆc~خ:m<#L ??[-ICiOL9v.6*;.Ȓ]hӿ~74WfܫFo tƴй E0@i V-']ԔQvyVzpeFuSkZ:o\{`03E1U/1|-泶]d͸ $/F6;_h}@O4lupGOb~Kս`^GvoNWun7{kV'q/tq0;Rwvï=jdx }YI7Խ7>W:Ůib46KRkh9 lC:ʿ!iGpo4U_]|d^[ endstream endobj 439 0 obj << /Length 586 /Filter /FlateDecode >> stream xmTˎ0+$$0  a#A%߯jD岻fc;Z̫MfG} q]/ޭmޯo⣩0Z^x]fkn{E+{*ʧypg6;5PVpH8$hmڢ*߄zR:")󨺠3qXysO'H)-"}[˺s 3 4{pYdrK+ a }ѫW{ Fvm7344AGc ڤ_86 endstream endobj 440 0 obj << /Length 770 /Filter /FlateDecode >> stream xmUn0E"y$U6ɢ5h)8",c\Ws/.7?3oz(yѧ2zvAwG݌=yzVmMמMW\=j_I*Cn_f &1y+Sw$F5? S4!1!r3Ҵ>Za?ɻ=ñK}:j=w(]UU#5dkuѥy e*x12+Sx,099)5tJN'{fS 2R̼  KV iXBRs>^ .KCc2c4&Wo"q8^zl p5u%=cK(q/?xQcc/s/G|-mƯP/S8+8 4fRSYZ"?.01шŕ[KPKS60e;U}Z8~Sg; _gvi;Kc g̭oZ ' L^ ^$K{)p/EX{)^ (½ߎ> stream xmVMo8WhCj~H\HrhSbd IJ!ۇռâ؃޼!9_?7?UepPgww͡pcӷx6׏;[Rd񟇧}z eq<÷LUJM롯{Ni~l1>_\}~8ȳ&qq;RUl, g^Cs=~k*[4^͖OmTI:/nY㵞1Ls*J`#l neܢ8Wi+xA= pMn?SbZbh`-؁6+ҖtΘ 7 XB[M98h򯠛& jwJ7ɿq/1n^i 1z1MN F_ HĒ?K|M,愆f[ eR SxK¿ec QR+ey h_8khG_=soSs9S[<9^r%Z:k`N<'{>[AkZ&# 9%F-܂ϩ=WC'}k_KRV³ᯌQV $!6n/xzjgu endstream endobj 442 0 obj << /Length 1026 /Filter /FlateDecode >> stream xmKo0 ޡ@wbKE=îv;pCL2bzn>|ܘnxv%p[)OM5ף/ߝ\qh%-p< ~۷k'}r6?F<.oƓOVn<k~I1=9;[ˡy6Rw2)]~C2Dww<_ws1vn<ďqǝ{r?x),9|?\LR`йiߺq߿.I㻦\}𥹢9/85dNrf=KʳXxΈ9&^zz_/e%^I%Юskfy*x7`?J#+ ruAι.Ț낼 \duA\r \WyUb^卼:oy#yuȫF^7꼑Wl8/a9/Qr^8⼐Wyޅlf`;%[mp$[MyX[R+IL6`Yː 9HKvvI6)+Kk ㇹ7+/Qe\G$@if<`F[fĩW诉70O*Ƴx"ÜE)=+b~sN~v?SȆG?r#W?r#7?p>Sfcʥ~dFbw4ψ}}kfGl-?r\q# ?zSf fWKfUM}k5sBoh:0Ν4}{CUNzVcC6&9&jQ,^ktfj)B5&^SkP{MkMC"^+C*^kP{BEքkm V:^LZ"R[=nj lp\u[#CWCi8,ߙ~4?s endstream endobj 444 0 obj << /Length 244 /Filter /FlateDecode >> stream xڵ=k@ w wP+1]B_KvhS_slh!sWzDs~d x&v{6W@X>/]=.`=]nl`H>x$vzіTox8qJ'2 ucH\$ϱAB^ !9M{)ڐZm3٪WXZ+3(Y 0 .s_g?{e4 endstream endobj 451 0 obj << /Length 19 /Filter /FlateDecode >> stream x3PHW0Pp2Ac( endstream endobj 492 0 obj << /Length 1203 /Filter /FlateDecode >> stream xMs68" ZLL#5=9,s*,HM@Iqmʖc\ď=Ż$ gDCBc ׀(<1!Ljxt|C`H>)T97?#xc7C㛱G0F\u "eذguVz2cPo 5R$RG$ll!~ BCݷj!< Q ~(nwc"tLݵv5#%蹨,_#.[YywPxQRr$p(*ˋeu(R)LfOm#py7[vذZRu&*l|O JuF1}30/n6fqZ7G_wQCNe_kWgbuay)j-J'<ǀb= ޓݎ#af?}͌+!fRFcАv ٍBg*VYK}OA2VH_ˢ~EIRݝaE6g1\Vi/Y>/ t-ay4XD8֙ U$!CqEmy rJ*ֈi4<>t+*t<"B؎zz˃wn[Ze4|mU* &/EEkMlKmMp-mn A`I퐢=E5YYH -/Ҟ(]wd'%7D*`vtO&*a{䈝iϢ{&xXHXNqcL=7fwxc5?kx'ݜ4_ܡi ԞWEˆ%{NO7YתX0ӹ~ϕ'O[r?Nդ'>〯gFF=ԡ9NL2ͳr<[mwT1:Dŝ߫=ٳ^d K "i N;V^JQ5qSr }ITyA=P9ƾgfcKFf߮b{m^$hcS^CZI<)Y] }犍7x,MHެh^ʭ7zo^su4oځ9Y;~Q}wҗ{Z>)߸9 u   qjp3e,d[zֈ$!&P"Ywi} endstream endobj 553 0 obj << /Length 1195 /Filter /FlateDecode >> stream xMsF~At=8v\KM9`A|4 ,i\݃%kݗw` h"(41'w"_(O_O~\M~XP )b`pM9X9aJ!1%_p Oǧu/zau? w72ޯ^9x!"~x_Ivr1%!`fY8Ng߄i9uEa}I4f4;HBoGof/oPr~&_j.6QSB1o5 k4c\~sdO]FAIʘF/ZÈ#'`aѩ~TObԟ-~$K6W!H`%_کMl+㘰G;@\x~E(ڬ,Tb\Qv:Y¡ u \eK}vvHNĮZ&>/X%hj]Gq`o94ȴTR$'j^gCuE[H_KֵZlY]H%[ʲt?XJʲasJG7+3C T楩V7 uDj99#Pҫ2~xpSx~LY?TH Ub4P/@ FmU5e74g./=vHa ztօ(Y-Jjq3[gTX}Hx;R 6rĭk eۗRd]*VBg>ߊ{6\^ <qY `\x?zvCX(T:Iu6ׂ=yL N?7iRm՟)$K@X#Rx1. e1e.cvdyޅIKW&|bj*@]%$NweIҲYZFӨI2NչP-dw!xU{::!^;85T{zwD |ZՕXt6 ޾[8n$:g}+ө)ң3j~wDu0yx=:G|޿(e/oTM-Tjfɟd endstream endobj 405 0 obj << /Type /ObjStm /N 100 /First 902 /Length 2666 /Filter /FlateDecode >> stream xڵZn}!"c8$kAgNB!Pm%{& 5gzاլSN!R( ru@hّDQIhqW$fIM─Ӥh+qt )uO2$q\e$8Jqf%|( rTH$ΈIFlKH@.քd (8vB?fXpc$gFZ'guƕS9ů8cIq %C  T l wk:$-NMDs NݰӘ`L6] >l` |.g\0Z ^ \&aHpi )f>HakL:UPHUV>vM -.T`Jc`k@P>dW ԜOV"jj2#Fj Z`7 L l<8Jf_|drc8Q <>FL{M&{X?܃4Y?܃ fO`X;Pymo/n~GX3U{;ÿùpiv8ᗇ ]ȿj¸;; )XS8ۿ'^?N0;tzلuaK'8Ai)Ǐ1';|svJtW~)ӕ̓v3m]}tiT;xԉcx/N'bΟyus0vs>75qxaw|7xO7}Ӯ'dϦ8MwE/?ٻfⰻ8\!Vy>]/wWMSؽ}g2DƢ5ע_ɦ߾=M'3O,jBIm_qr|wp/'nbM102On>sܶ_uW{@lL`ئJoso[m/wr/wiӎO;vUX5QM0Z6Vc>eqT$>/}6қC?;sNҜ=xu>ChS$r9̙#Gmx&#YIz?є&.XC%uzfX).aHDZN"2}EJ9{jI9"ӌp&'e)Fj6bE&`rAJy ] qLHfZBq!8e o2bYd}`r7aއ"1Uk$E'0Q௶ukk\q ~ 2"Q"یmXTBKؤ ,#>ZK`a6cD-IS~H!#D1{9{5B<**7C+A"RXN y@N ʵ˘m-e =A ؤD*RM/J}=[tkoc~O[-VZ/ȕk6Eӝa٣;å־Zj= roSoێG:uZVe-7:#J7OLk,P endstream endobj 574 0 obj << /Length 695 /Filter /FlateDecode >> stream xIS0Bk0ph :S+Ǧ(;0Ɨx?%_$#0;hA*d;y~ g@A%`L`$AD%cprH 'B6"D0w&<]F׾} =ۻ)"]~* .z Mt78 G)#O2nv]Ai_)]ycW%Bx_ X0zg'*-T NI9֩YUiڔJj+XG˻V^dhC2X84pƓ,N&l8lSrmyO. ` L =}Z5Sࡾi )pΚW|4Xk4 #c҄Lg}m endstream endobj 578 0 obj << /Length 113 /Filter /FlateDecode >> stream x3PHW0Pp2@ Br.WtB PK@B(WH(sr9p[(XY)01344RIQ,ӌ [9 endstream endobj 583 0 obj << /Length 558 /Filter /FlateDecode >> stream xڍTn0}+R`ny6]uUmYr+CDU9s1!V%8 cE,,Q}^lEmse;Q >c`<[&~[rDQ-PV[r`?i cV`}ȹ΀\`GFV~\]LغJ^2й贔zbD3XNiT) }=Up\6LTW%l>Ա_A?ۼJqDž?j,գ$أ!̊XX8],W䄠hF 4ܻaUt븺TXRՕI5&kjˬ4>SJǺ}19͍٘Y]02 Rw63HWJɦ-Ŭ$UiyWl0aZGlx6j~չw0yқ]z,&3 endstream endobj 589 0 obj << /Length 196 /Filter /FlateDecode >> stream xڍ;A=gfS`&(w=ႋ.c^("d E-.g~}m|68oU<ַjy<56&&D 0dkȱæ励MLx[IX?5쿣4T ztV4 aQ|Z$Fm endstream endobj 597 0 obj << /Length 1933 /Filter /FlateDecode >> stream xڵX[8~?G\uL2`3ۙ&ę\޷L8uơ=z&Gy.ivWG6 $ƙ8)szʏjR8s3Jtovf^Vq2Ff#-D~v}-1TcC"Im[#^BDEvvשl[p: ^MjUL@NJM4~>(uOvYTiϸoD=UQ*pHCcUځSegZ#3<3[Я8yykr=MT7n2S&>}st{L'"XfBH:4xT<%_p,a_U_s@,v0I8lý 햳l1[As[_pw8q (U38< ]gxtO7Jzjۉ'CΟf,ֈpG"_/O%!͚*5`X@5>5PjSʦ֟b-=lxt?ʱ\n,N "Tq~ٕTƒ/G KBHF_H{3sgDS m= iwbZqѵ<ɷ(8R]>ksS,|B}iD58 ; 7P+*1`arhb uFlܬ}H`Ɉ OZVvQM sGu$ծdjkH}gfcv. t _Gtlpuc"i㩓"7 6"l6B+atHRnAbo3'#h6w8}MmL\2>['֙I60hH#cvW6B_^g@82N^A2|-Q773OdiZp_:\g| hm`;y]/+ow$؈WѹC {Agj^躽rzDrYpERR ~O@FL&pE)c`#<6]#Ɉ*%c=:sɰB2uͅuLs}W sf _+0޼{=Mk"-|lO:;;Gk J_ B1z8>X9NߚX\E+ȼU i,/5ۙ endstream endobj 601 0 obj << /Length 4107 /Filter /FlateDecode >> stream xڕr񮯘2(>quU*N8}_nt7 Fh 7Op󷛿<ܿOMYm7yU2<ԛ?pko8/tP# ͷ7DL 0TǛ75 ,6/QYe?oBT]ADt۝{3`]mGꫛ]n4hFx94Ձ=@_{:QAp.MNEjp0voPꁻԦz: ScFat|&( 4;4n$- SO.U="zaFiVAL Im^vQ-#i=<l!,ę/R``@U(}LcSk2-s󐹜v۱~8j{4"b4坸QڋfEўQG\ ͂t5sgyϩo<1J"= a3'ရRۤԆjVSG7]x1ܾ4i4R:4m 斚mr30'(z Ap_g><(._;T`9mW"Y c54'>ah…~ aidT Dw10TNz:,ߴHZ#Sy)`fWDPal+d@gVLYHԖi6ly&qTa]ǚVڍطC6&W}< КXƓy? $}:D4wa04H#%sװ,UAoqDYP+Hb0q}Q/o .'wSt2ZY1Q,`'gMlWVePda z@;Уz;ĪBw>;En9qىk S?Dh2`Yt ,_KO);p:;ux@}ثZ/!b$nnxTdpo$Wi*.5KbKODO /q +8[5wނP5޽AոFwl0K}>nu땫ط^7h%ƨP( 4Rsr+˨lRXHPn@I0$`3opq`Vv ZJBd&P]`<6n c7s]rH⣩h:3ɰL;!Ɋ$|E7H*VJ߅rR[G?̷.WO`ӽ|놬}bo.YhKHj-38 |I2yjwżEx"S%`e*yh-Hj1]}nG?,pkc): >(՜^b} %q.%Rz Z7۔XѮ\n{ԿD@6tҜsIQ_lTiC?4zh%:;Uv> _ /KS+XbRʩIX(>>)/9l0H0u+JԪYSFé$Y;k=}ͺ :UU|5hu:oUQsvJS䫧?n-QY&wŝ:G(av<0KP*{+[-rKҠ>9春xb,MxUVPJåO)>قObW8U+VQ!J2҂p]:Vrp0zQbT1ʱGpnφa@XTbsZ`tS5NoY dl;8_wÂ-ѷ4or kcs?˔bᏍ2q+0t3z2Iaզ9lD]uR(];gb)Ţ>ᨘ<`倿%82'ўÃ6Jʣ>j~yB^ وm#.s[GЀh}hiZ(FMe+љŊ_iQݿ3B>gͪTZMX@r͹x^eA^CwWZVXx[ 2DѱA\Vc)E?*98b~ k}9{6uO19`}Gޙy٣ը2 v(DAb Q{- H!N<1cjeԁpS~(_Nj;c<[nϘ灋JS1DM׹)P$yQ {Eiѹ#A~AU9?{: \EIh}Ǩgv<Fl#E~I9u䉈{!\zW@aM'5,dGuY~iv~'#m[+Bﮌ̲Kg7bqjxs!TsGgՍzm+PƝg9B$_q]E[Q?nIx|mF3\"nRx1Gʏ4)'kF_tBHχYN }dF?*=TqW)eo4 ioĖ/wYikg?WN endstream endobj 616 0 obj << /Length 3577 /Filter /FlateDecode >> stream xڵ˒>_#UA!28c$dʗIRLR Hq㙭 `h4R?|q*"鰊iEydT-9I7 v`g[+ap 8vrEw >WMu9j~5-&G+m'-~QJ Oخ~]giR++f0n^Z"e DQX)oV$ L /];_'*cZ22N⊍-$Q{ ]gtu4 @V? gP6V@r [6} b ӛMu*{^X1!8{GxT?:6Pwn;=#?-6]_+P%n<Tn.{I7g[Lm<< ],wPev6H׳^lG9 ~<(Sxt"w(LYV}- xs鯗 m?T9 =7H1žwP99hdﱳJ4+[[@ws<Ξ@5+ձpظkό!8U1vÉ:nl3 3@Mn!YT ev?auF~,-n|d|rzcQHy`= b 6,blgJ>7gw5]["5lO*-oyHH}K< n:o[  m" b+[tg ֒<]7)3 V(;ŷs&|&|<{42*Y6ךv p1jyY1a)v8,LkW-R Zx<<ʝDž\3pAZ];éB g|3 'Qd_`8#n즆/%JjGg#|:wv@$̘MNbJR#N lzmI( ( @_TtÂXӤ teI>|H,3cd^*!{354c\$Sp꺺vA-(LTEC얝Ax/2Dpc)#HifoQ~Vp(~$&.ځ)`y#ꌨ7i.7m})[^(]g"R0&CR_o=W蚲d4g7|GJ2PzugKO Q'8MhVE$]etN_j1\lgH7RA8$w76+RE@gh- O {GY[5:1"Cǜ&AHT1+'8"MI̼t̾ 7 6̢YΉ:+z"هU,g荋{"7Y%Z]4>H2oǠǔ+NC(yFu+[OypOJ8Ф{|8w6rֽ5cϿU#P`LPgoQ 0(LP] yZg*dU #?I$k霳m%~ endstream endobj 633 0 obj << /Length 3733 /Filter /FlateDecode >> stream xڕZYܶ~_1yTvi<Ȗ풫XCbvs xUn_7voNgϓ0wiK̏r/wy?c {6eڏ\~?|{;b#? ]q`W]<=R.J2ֻ7 D`j-TUAYKU:e}?q`?u$\LCЙIj(Pvw;ro8U=4kXvN-|:[&oK/âjxy H?qdEOO8H'6Lbig\pg Is T[."2bW{89>v%7 [;lj|I3 :2ϕE0ѱq&8hQDzd`yIdN=i?a0Um:QS4a sAnE +nÆThŰ&@#1?jA>( CWҩjpqO5\5h&aV\0!#20 b@!!h@Q9H NxsU0ܖF˼vF֕3uH f5gqףCr3!TV[/e(Y(n wFpFK[5v`;0]gqXC)&C@ekTREf'h\z-%CiS18 vNQa %g?)&T]D, Աϥk>A-vzdPE`8hʓO8a{\yC?:ɬo(m Y:v@KA 8yUgF+n3&˜'Rj7lVXm NC0Ev%ǂPUD}) `) |5moL9 w< 򟐊 b:yDP$Pda"zQ5#Ă1S4 /\wzʼn0^0lL8z-@>I82pU )24%_͕:6"l"ʃESeqlob[%X^@ m%`Rp lЭ3IK/2Lj?/; VA(Xr' )d i'dF~0>R &N(FD:$E!L[q((؜ڱ.8'B'*Xy$Ndp9)0+oC" gԌoyX-ǓL6(' |$O)fA I0 ;T>>5wuN 9M߉wxJOMvxvN\4V|7'& ȡDdn_fN7WL @bsf&.L# `Y2W\CJʆtIik# (aAa;1<{|Fu6rKDE8M7r3` zݑ雷S]dwoQ|9\osK/" gWS{}Xwn.O~~H(;qӍ$l%^G "7 Xٺ9[2o%4F2uydHɠ)NmV͕uٻm`M| @`2g&%5.?M`6pӵ\q&,U|W l(Ϭ0)<3=cW "&'$s;Hǟ?H_&>r*|uckCK?? |ta;%`aB6w 2$L_ n ?3urF$6{6c}_d΋a 1vk.g 8ᲆHX1XD[|v0>!=N=D໋? ~<z/hPབྷ > stream xڽZ]o\ }_II cnh In<861<&_?6;CQGEf)h΁$jnh/} *ZA%"!t(WY04q'P"  -bZj))+&X $j -7pO%d&!g-dqB6GhrPᖃ`BMZegGhD*4[ b V3,R6D`:<c ZL g i(0Sc"X%({ݶ+%7ZŰKa:;׋(ILjTBM#NCG@dPZwr9y/-TYFaQ5ghNNJ-.CR+m8%wͼ`6QiI`12 /Wv1rbi}iy>Xß_,=ǢԨpv kr/AކOw0 m>n0l9fy~qgc9Q$q.EJJ49McA˖> p#<*A=QDPSfS,YG4~p#M:S͍%vi3I+YF85= Y1X:B3 mED.8<b6‰s5Y'G-6g5X>T Ą.C>43d9&? B3C<&ѩ'5wLbܪ"2%p8cu! AّE@`N63dv0)DAj2x(SYEHz# UznhF^]*w^mG͇<|qH'xחK@Wax nZr1 vC|u<^nfOgG?:#aYK2i'7vY>s,^ڲomrFm޷ou^^_+;}yqPb `WNvm p pj^!箈3$>"f/Z3C8 9O>̐ -%%U9*f}֙!xy8o᝺*#!hLΝ@+J,&tg  ܇3C31ozumlg(Y H}5=c-RoJͥ[UT%??X!IZ-qԯ8X^{ZπFQzR9 /3 R6Y .̐.QFE-f [uxV YIɢbv̖ 2{`Af]2k"Ug@_#ܦ5*|D?w : ]/{=nxyp0N0ux;/v{0|9d8?;^6prq=(l~ ST1./\]\/í5Mr{dv&2(oɔO˥nr~lPDp7ҴGI ݳk7UG=%tOi9%i}rLBgJ.#ΠҘC&I'5#)C=f8)}|/;Gi/ݐcI'Pd:)'NO+(Zr(3I\jC)uZ2Q:LaEhRίj'6 twezw>}Sv[oX}Gw̩?fɓrhR.;ǹM " endstream endobj 644 0 obj << /Length 3760 /Filter /FlateDecode >> stream xڵْ}_RQWvdԙuvH%N7jIcgfDhvU"$.`.}}"]Hv7;d ]e~u~+f붜ΪQK}jU p/\x"Y.ܥF~ǟ]w/lwkλ(}8r։dJKw0THrO7cK_.+}1*9u[TW*uQ7ǁĸˢz/b^'*a40MG84U[V'j]1)ՍUS* tQG`LV`6PoDUp OmH@{D),$1#_Ya ^#8lv ]$!86zizܵ;@40s`dh|b@kdX7}{fC~"aW`Hf*~g4OG=a7 ]F$ J+z+EEB͈n)t;ax4<(O4']No'E8+jM{u~k'{T=L'kS)}k"n0lW46&ggnL̊Bqػg`^mub P81]ݎ\,Hh1fr)3 ;#?Ɋ]'9آ5%Ld97LE?:*:pKd0u&ԨJiѷH01=:(I ŷ wq ZԓI}#mqzq(8 M a ;e{{]0$p1/xQx{,-s{5 C 5 D1WD!^/2<5-hxuE_FX42QƦY&yw +Jr"$0}4#Bp~?/{Btք{E6 W̵t,RQ3;DIu77FQUYI8gv$=0#!D3@@8`I3a:p&nAĚ4Qx2y`Y=qwDi VR= 5HP~`u3|f lqv5 Opf/Sckg-!BJ\.{UTgrxu]Tl̺tȎL |ND7S % ^/>;S/"XW&JdE<>^'WەV al fiYڳx}ƾEx>E]SHkP. b YŸe[Ѯ?}sq@u~% nl_^`D{lLg/Hbk4%xum6w*Άco}b52²R? Y (C~5Yp+#* 34Dظ`~S !ArC4K3;V`|Pϲ(x&Id+\YEpBC 467`둹L(u7g( Ih2]ua ~$q :1NuEs)#VΝ@8/jkbF`96`C;6tK_U/j|DEy;qjgS@}QZ76 v\l=O.&?b+ac5G3I/$7)t^i!q:sO.JC(9a*W~ eKBAݝ&jt;iCkUoApr_CUrLDcˆKw٤XY|m#FE S댾dz] wF⟳f,.\8\a|V-xՁ=¤g*?jplCeV_ENhCR--6%Хf)tqnʥ܍U>t=78"aqiIw*/(ٗid|'?:(:8(܌t"oW5L_˨SN_nun4$Oak"UƸ6m8Ԧql` D"s\2㑦>}ܼ'"'~ؒa6mu,-.y[&,WGq;RؗQCpMv.umP /wf\OY^2wdՕC"MV.&樬+5e0Yˬ- Fd `_nH*L1@+:9ka ^$!ڗёWKͱf0[&铅(;_ܢ g"NTN}f2V_fLJF܏3Ǩ>R9H=[;$(Yjiȶ#|h.{<}ʬjh PQ./t[][: 秘KHXXUda7sB~l~mK_vs <ȍ"Fn=n1^zqVȟT"H&F2- @FOdBXԺ|"r rYVX 2dz` u?MWqi`Qns=e"[%SQ x h=OtUmp"Y>6eݭ)!VAn/'M7"\|6JΙKesPy2R6˥j_ GrѣQNėts l4]N^yFD؎.~g{,"G-gl3r%LFKF #OY34P*oKy٨+9'$gr}]uԐ aۉ9ja-=zJG)JXMçs12ifA |ոZsP;|-/828~(jуcA+;2_iX53đfyIڋEt˻X G\nЍnM93­g3#0 Crgx!}Ks,W|8\bd_4|v@q]Ǭ1اyxwnRܯI/W_\" endstream endobj 650 0 obj << /Length 3130 /Filter /FlateDecode >> stream xrί*{nD)tٲ#NT*pP,&={ 40dV[[ygl.*uHFnU$nM|Xs'zTe]QW/i*U*ͷgW [ :6pc/^mg?yƿ]y&{ g b@1بook:Y+;v{Pk:F- @cEK- KTө߭Sswonv/Dh4m Z<]EfHnw}Ʊap,pÏ䓨t q:otۋb0rs:(pszMa ͦ`X7cCۮ>Ɇ-uo]VufABǚ۫Z È Q ӂ[~}. %ēn$S*Vy@ _W4n{Ik!6'ϓjKpquYֈƽ@[m|W`[AbfT#0jjhc|&G\l`w[64D@Y=BО) )g0jsI0J -4F"{BsK--&-oxȭq'ܒ" X$ϱ44q,xj]"Ǫ>#sY$6irf7vOXGӲ3M":H8!k-[ n@)c?p(kq ^,;el݉NRMS74#M7Y;Hc7h__">/t W1`඿/`ss:K}5 'awTVܱ]v$jQWxBqd@Z> @4>@S-tѕO%d'3wW.ɺ=eؤA$tId79iJMX9-8լFQm>WCᜢQۮn/ ˵ɂQ7rXFC]ݱg'B c>LfGBW?D#Ѩ͙߼p[&@G2cPmŠUU͉BڿuCKm]0c^qxlcSqG4} H[\unk "w,Ą:b0!tOmx$sIÓ%.xhmh=5Z)^yCk*bFl;fb -8X.Ŷ贑scLFC3Kx45I8ΘUl"Ahl~BV7~@i*;[Τ겢ZO,؟⏳Ⅹ)j0tE> stream xڝ[[ܶ ~_1}ȩtW+ͮ9q[;מ835Td,q$A4l`/d< ~#̗AȈr6?yoq&L3EW^NkN5H?^]J`#6id#? My`SAWy׳N(l\*G39@$o2 rک?Xzz^UQ*xچwO/oB<gU?AXһڊ0ve &nfEKx?c!H3רPƍ~Ǵc]º00̀agnH\u)P>  ͌>(ӹQ;-K>w"̏J{0a觹0~蛳^{ߊe6?MQ*VM~&Щ]x`(kCRd CY1fJaD^)m![񣅴"&9LB?\?;>A*sUa= ΢8@oNZ&}1sQ=bޭt`:>3kݑ2K Ys4K\.kO2Vf. KT-W^C9+hD4Ih)4}q>`ߝJeh%SQqQ :1co=4)tEo3xjxaCE-̫:*Ƙ (}sY$OHcO3'Z:1FcphmŅXP챘-LkHܥ&R3ڒ-FC\sU)r$M%z<ښo9f&8e]MZ.itP}Lw2 Ő[cYQKS\݅7]'*Z>kJ*&}(fvn81QbU Mk~UiI [1矺E4C>ih(N<[<%WrE 04.>f^iu;b8K"0bw RFSN<"˷aKM<]pEѧtNlu{"DO]ơIj`\kGar*zcKտ=n>ͮ<˦(0ǚ0I#OLH,ҥ޳2Q;C4xY qT{]'US3puяY~(c<-\Xy}k/6L[g<9=3q%!$C]X IVw/~&O,,LG*t1Eh՝d~f2p0FЙt]On>MfJsvYځE.s0 W\ ۯl4J\ ~miqRۥGsҹW.szbmL0\J UTW@W>fp ~--MW$ZX'V:ЦM)WੱMZø6}\g( SȐ4pMG]T dNui)%;P-mJdalPH>ѡ$0`)VlFT6S*{qC??G ]m{wM Ou5݁h?pNW|-q endstream endobj 671 0 obj << /Length 2577 /Filter /FlateDecode >> stream xڕY_6OG.%1iz\h[$"ͧ?iI봽ٙ t9mwwo^#7xWfḩMUq7ca+HM>>L^)iu8/}p] jMJbJ͡Ki&]yt\(kv}M4"><uܦI$΢eyI4˙gixR 1#~(p=+j}b4S>mS)o4~A3A6N*3v3M6Z7cp};!-͋EJ+>IY0Kiǜ1˞iVTA$lCb!ڠyF25Q.!^"k@i>;wV!gX+f<4aiKiW,zlO-O^| q2j֣-މlKPփ}a\yCNU8~gB P3Q]]=X"ji%xsz{u\6MS5w:X&~JDsaD)G,)1iM= ;"LCk),*ᖁZzddeYyc0*xy"#Q8hLąڰ9{QXٵ"ē=a `xPs)i$K_Bf144\|jN.BtY#Jf#K8/ x5i,ro"I8E 5<.T 5)(J$ sϊGqbQOW"V|Y+ M>ᩌpy,vx%e,DU `PST>ݼӀ7=ϓNZZy, tsZKMYn]>fJL yD^ây_ rgicBD,bx \H-&o"#aPAv2,jWD?^Y8]8c̤$ H?r9@itL47YN#4l}:#)pz<8s!'(TdX=0|>dKb㬳͒hVsR>XAXq0;?ZmQ;"(|t +|te;BeyTf>eY f{Bqߪ.q2@kʧ>| ƎӁ f'{hYl>.[N]xa [b x~- d:q7_qE}R.3\+98B䂡 Z&\S-Ɗ@}U7r8U8R]$iTNX.YJ^IaeEM}Y P-5[XwQ\gQR_Y<1*&:7 x,b{fh&&"_D .rgC\I*"sa"0Ety9X=*g0c|BD5 0hi44Lsa47W`y3W{p}1T`{[; [Eȭ\ pB˚2\/_U4R:}Ҽ77} pk .lڕ[ %q::eɫo]\ \=!-r7FxZsЮI 7ed3*-ac:ܛU9x8kmZҞ/hx1.:W{<F!f'(/Q.DI$^%g9KwΰAX#q iL`Vwr.<B@]\"Iϯyh;,9 bSQWDJ{IʖȔ_Li^ïEW| (%s˽,5'.m;$4 1=2|v _%ȬŧLgEe|?x)S?z_"n7hxOI> nEYơ>)6]D5=Q"_Ged /`jyLEoY9 W+iE,DplAPdK>@&<.|0~m> stream xXIٱgog rv=*gE ;Wґ& 1lv7`l63v٬iE6BFQQzA-AT=Z< ji8bD #ШEEjcb j"C“e ޠjC\z "CP\m"E0CET>MjKܕE9vȯzK;D:hokZO\SAR`#Qh BWhF?uA~-Yk(-ڎG2V1[lQ\d k 9M'S͔@Pt& ~$Z[l:!p ԜTFo1G- }Ӫ\ )OڨD.$NɦPk Y6z~Bhoh!jSE$՜4 NP!4YZgxLZS ګјd ДZx)6ŵ j~S-m@o 5C`J "ʨ@W "^bgPDɆXkU mԫi̿UZwL\rC ~!Y=&.F !уF( ʁD9fEhv.j=/B!lElRJL}>`z,Z"4q#Z[ YfEM#B5.6E5= d  LoH qI8F/ 2|7,dj#X 6n"Kܯe,٬M\J,Bi. BSnQ)bBY M0| ,|MiMnJ1B[֐-|cԮE y2NȐ!CȐr9dȐG!C*/2dȁ!C y2d"C2rȐG!C U^d{#Cm&2QȐ!C2U2Ԏ!C _dȐ"C. (C u Ȑ!E\2QȐ`TP r9dȣ!Cj12t j*!B ?"aff|όtSD 2͢ 4fy<,ʢjF\&+WN"kD2(4 fhΜ9)tlB/HZ#r)D qBy]/[)($ťGbMt%5.I%Д}+ *kI#1A)2"ҳ%#,Rx%2DŠՄIȑÍ-ygUBBF7<#~CR;|?(ZªAЖ4e䌁ѭr9 -BKIw,޸(mOGVBf&:"MK+6M"󕑟iJŖ͛/&rf|.X,G 4q©/$CдU ;JQk W ii]ܭU?w.|γW._N5aV,{xJ1kf|}oDcMdp"^Ҙ){dsQ)4Ai,MQ29M1Fu%Lqؙ,t%ѕ [brkVZ_#"bqnIKD$LJ+} I(% 3PR"6*R?G/ВH[|CR5J?_%2.BMJR,ՖDqKS$.4`k:Z${|3>B?ށU+-Z4<, /A={Fp9,[ղ%iSVx^En[+/ĘM || UYMh5?# als VfSf"?iйI9Jlε AEg )ǑrFzry mȪ,gFX,7ߝ8qb馼f9fuKLLͦ6~NblPH rIEf\-RR=6.3$H 댐kHhHɥzG‰'%UZ`0 Ar&bSN(4"8QB D0͞=z%K3nnk`7WX˛6m 3}e7i"j\<ζ2W$B^E8erZk@;6 xb(M]O:YO<8m 9DE꯺պOzZ9knк,9-8G*yӾ}{&YS\:<ȅL A1,h%-эׇj gR f ޠ9 Lϟ׽{7Kop_zFЏ]J˗/-ZhhhZh%#$տ_ G vv>=<V{֬MGFf%'gͧ u?vzn߶!9)FS?›6mUk{0BZv6:Ld`.$zZkDZuu[KPZ_)j?kEtgRt6m޼~BѴXi\M+Z[BrCWk֬ްa=}͸l,&5k97w3ډyGHhѢ޽!'Il:' qXBm=?GX~rq8!h2i&sf;} NȰCwYCIcc?h4l6BApZoȇ&Bk\s-4bQ9zbS1~e׮]}h,41Ϩ`(&dQE/+*Udd9D_PMB_"[  ȳEf̜d䳱gN XcF]8gDnXh%ZDGGlڸV -uneBO"^jazQ>1uwfEp,׹U2|GhCɬwv"++SE-;o1G_]r8+4'%bEx/U"bK45W^Zq#Z{Pxm%dV,v6RO*$$C1$15C#J7$>ύ52# jg\P )?ggT(oHAZ#iIs(Q۶m_%֕a̳A XS>Gu' |Buk;j岢E}Ndgj O΀Ws(+T#YoڵfOАWQw|&RbQChM %EZ6zs1^%#xܱXZkQ6 ?-uB;+?>swJ,-bƉTդydgOg fช9^xT\93$x' |2[hAK"f:7oi RF]5JfǨ"dyw|sxL.Wߣ~x㓮?KcK<3I)Na\"пtCb\BjRhe@b\X   Eb/9鏑 mBL J'(۹ym.ŋSm8׸+w= Kcsm'5=)"$s8 כ Ӊ&>34e\F, ur̍VVM +gKK".p{F!\27q:OLA Zo Am=_"r, >b< ,rhxѫW7 xmw}|[ȏPP}yo zf3eRA>g ;lhRt>i /{I*+=`57E$clllT[Rƍp $J4 ?zk.T&s&7L"UғQϖD[&+M"gs>I){?{}I3aB0@ z Z͉"cN[\Gӯq,Wڵk`N9Oȹ 3.z*x@ZX3MǨXb|lr ,FrizClv{6Ka=k3L cf `3-Ϙ{赫 cE ZX3 B |q6m] Mà49VZ\ww)4D_H-OFS?v'e1M.a͂M1l0 yCLg6= BBVJI 6+1GI4Pb?YJ`1JS*>-e'3S)8Fj-'HMU+9T*IhAQɉQiqLRb9JIՔ2h'E)LG$~`JT{bx7PB|DB( W*>c D}V+:ZQ?0}E~W*G(Џ*BoԊ^wo_0c _g+<s`xކzFOןz#uB_>)SR APLU"?=(ӻgAOߩc |6[o |o޾ߛWz^իWZdVիWT6kk7Q6#$mQInۥ7~ .u# q V5wJ4P*4`~PJ~ZԐ6U k eV6ѡ/afth?SMNGKNO]w|zn[x냰 볰 ˠS5{̘0U7ݥ;Y_c"?cW_*a7&c$*;qT%|W*ӏ " Q%iJ`Ăf<4Or JTlDL\vJX"iى$(JW@Y K0dGz!\ M0=x#7?oNwn p A!tCh)z@N^@:mGR{pɡɌmM}7{z,[BiGeBfdfZ{vo_0g$yω##@FH:b0&CDzՒxj 1R 7ґ^^@M:!#)5;&h£#G\p"$(y^gRҩ#djK"Mn:z%$ϟ7g./5Zͻ.W BGG~ȽiA6zݼY3u>)f&jP-Q$4fZjEJx2 dfscY7ր|o_' ϨD,!4E/#Fy҇w2)wk~6;QMk-\$ ] LE9UZ5h'ef:93r۰A4쌄:Z3]T$Wj-hiyֻu":w`4/kF-M UF[rmLPT1Ҫ_z2a!Is5=hM`n:9(\zyȐd#L/-yy1Zl? 3B[?BSƵKvvJCKB[LDh,ƠQ6}mxhZD۶m?ij8M+QMf\M'ZVZ~;xLAjf06[9:BC5k !u 4!J_5RSʕ-iz^f)f mDr<C6R> Y*~WctW }.k~Sƨ_uiʕs7zN_/QS H }MVBӋ80o6AΕ0mGqZb ?~ɓGv< j@H I%?pLϚ>f0F_{/F$;RbŸۛM6,D=4Au9yhaC45 6'W[)>#_ȓwٱc[^6nc*-֮]ˌ/U65 M {cجY #'Sf6-M af'ݻu}YB*bcc#1#Tϝ߈[6iBktM?fӏAԔ ,JS-bf-UV5!;JB*hՑPB.KnXGmմҵɘm9zpֶEN<64]}bf h;91H9)^` nՑ,IԶ={<hjGk6}]P9\lXdMc*6UVtu2?ܩ#,4 r 5%9#]U#F})y 9~`̬<:a8; !&RSb˕-H|HVUzмYSqWbYDlIb.q jժ9 Ь'ݪz% )Jsu] HBb[,)ىZ;eDojJ2M q?nߺ| й'Mݾug|n.1Q_U<fZ6̂|Z?~SQWڪ{<.gm*% ['?[, 3gL޳pq5.i~yib<1! ^> LMs'7_LW.ߦM+)%m/_yH=}Vb*6/*i}Xbjr ꪐR\F:BDhR /=]. #=8F䉳E,NX¬,QfH"VWu^ 8}0SG!(R"JW i@qa\"s5SKc$+[N=~:%7Vy\&Ӆx:vЪU֭[uk';-2H\LŒ |2;'|rAFR_%b6.pr^,mڕYh)g  !AoېM%""b萁wWviY]/RȬY ^w!@]uoԸq^ԤiN]W9_D|,::Z.gϤR):'ܩ':7%?'w5jضmۮ]vԩsζ:thղ iv O8)vS22H"C; Xzb˗/0`@JJ \R7H]+5+V3>k \b=8ϟM2,1ɀАF,zwg( !5 4aH%5xkݥC)U!v:}]|x>|` ©I*ay6o3192s蕓#l}Pg2|j Nzz{F0;;;O6e7g̘ѾufNzuiNLu8}gdjUJBgn [grPTolP |fΝ[rئM-ZVd?z}t\6Ϲs<gϞ+)\EC";)Bx Ѿ}M6nܸE]t߿!C7oCp?,((w;(ZԠؠfêo?+2ؘ8,TGG~*˂ `| Kσ^{]N,AST)]ݻn+H5F9O/[(1;g9O˫W-s]}Z.&~'݌Jy]ߴe+|)`mŊW{RM3fpXhX#;g5{'=zY&GO z1ݻwl3t.7 ͡5B&`܌oDhuj;|"4X6͛RJT(蒏]6f/;9.9֍zvWnUZ+VBbNFl\#F p*$V8 sSq3n ^̏&Г'M޳8B}tƏ}+bSd~(h'22v˗/zO\.ZeR!lM&i"Zz}zBY W.p!@cǎՂX Q5+vcF;î2 q+U$1puϞ=TڐPaQݺukݺuڵ˕+WBuk׮o߾ƍ3gkǎ@q?/97DϠf=k5Uv*%Ji+X|z0P>dU|@/Ϙ4NJa[7/)D%fNY5Ah\&b/_~XAE\*Н;u|B ޡc̼ƺN(Yt(qjU%k]֪Yuʥ[!Ծu9z^报ߔ0~!=PZj !5BAh8 %A5qUr)՝- ݱڕWir`rqY@rƍ;vhDܹslٲB!#]V-?q)SՂjV&#:ӷ6UN MS ֨Y B_paڴid2DҵkW8΃X<w.J=̿u}]OD+W?~\ \.\&x<USn]{٪U+ 5jԫWpW^Ç>}e<==v"J(9wGZŦf9(j+5i=%44T5FL])#?;99uGkn4_bzF QF"Qh mWդ ғ&MR>f( ьIyRJ*[LgBoSt HzXx_g78%H 8o`ޖ6O{ubŲZ4kwkXMmZw4`bcr ,e > iL23rЎQ?mllT,zDhNAw PJ>LVty 9>"4t:8`⾙7H3t"Ə~]iiu-:J6ѡР LZZKkVo@&&&j!H$trrS?oםZX5LJJ2ӧ֮^b)>B9sF 9gnЧK.۷o2X~6lؔ)S-Z~zS f>v5%gc4ըS ( 4ns޳k}F` 7g]mXH7> Yl\&Bh,x5ȥ7oBT\çW(5*U:v1CBUާ"Y3R`=g_4(RIGZF=b*򮭧tlGL u4|郈:.Yv)::Z,☆F_?>CHn+mhFzԢE~Z4[ZB.BhPp9%X>8@sK -޾}[xqBg.]囂Т^Kf78NϊIpȌ:}ם_/~ZOʁZmڟ.}rdqeJ*P(j|8 ֮]s΁޼qwDNDDt+ \ƈ?3@ &&FJW'C"b%'F>? z# DBu)ZX_ WݺuY4"*y/Xҳgϰ0pK}B=pFhM{d۳{jU?9y-ϮT @KLqe;_m8yUb; :p܉Pd*򔉀ٳ*T;{^d;4ß`k箝[ݹaoBhAhFH6n ?+a`4Z9?cBM49A =VϯY޾RZϟA ?}|QfeLG 'O֨Q#===33@(.B_:֢c;vQ>ޞڌ{ղKY뷨zGV貒p8O>j6_XraM`Ol6OOԏpRRY4#l ]-~UN[T:t1cM~}vgg6c]vjTMnf/Sͦa `8QiCCʤ܇oBEg Zpc?_H2=YBh]BB dʔ)&=F"Z/QxX,DFz zBC۷{9 |$*SM<>%(qBާa3*U2l?yPRZU`SncX&M=ոնmpHsznʔD)y;g͚IG Zwc|MpF17CAs7Ukr̻Ls\)EUyѯկn#"Cq3ۮ\bT"T"xE64ߠoVjzu\`^h5B=|AQQq jӎp<Ч± )#tRRҊ+0Rr/o!=[I_h-k!!7B߿ID] ߊnؚw![jϭ3>/Ͽ]@SL5k'u߾xO21֓ +ce_M{m#up Sr|lp8˦(cBPs8Ø: ߓAJׯ]l۶w/{Ixx@?޾{ѶM{w)FSwkbs5$/>3+; pl 1送U| ]lK2!~vOHG|d4+YekKZsڍ͚asNP1H08( g9m<>f\L ?6{e*Y\nn! vb%5Ygr9ᫌB_6:C(F2B|`=mz6~?6333w bUe/|io( @eRiӦw*_ )T W'%K(fw<Ҭ5\ߡ ΝMcb B ޽lE|';MΥ  ?$LLZVI,iؠ=E3;+n|o!S۴>u4BwL["<(ە+q9iBIފI.\v* X'B뽓Ibn% n^O>|8u9sDGGS_P)///B2#4@߼y3f???3rjӶmlg;%?~s-[&MǔP&x{{٠ \Riu+QAcݺaoVZmÇC"d}@&e?syd =N]{hlZY &?gИaai}LOO)~AmQs9mhnp+߃Q{uOmy??kiKdef0\)$''^p>Z#_?5nR@h"8!BܻoR;<٣; t-'B_[xq-" ͤOv\TrJ~Vn6~dL)ƏP׉YaaYKGD"4S$͚5+=g B3nݺ%$$ܻwo C]/M,-q wwgϪӉ|+)~[_N?;O  x XW^.89xϠeKjRhY_hѸ868> AНqN MjbBiԛ^g,. ˔)w5/޹KV滺K¤/>i]CQ[} ]` 6*߬YӹsgϞQ^ݠ/Cŝvcl ./1{:mm*U5r\.#hwoG倔'\@t~ƙCSga}$xa2?2BA/Wֲ g,|? 62 Bgf RxrR y<SӜi:u2tfjG@Y+f+ɠQ6m:t(#%Kw ~}QFǧ6F=z߿'A^rY2''gw5|ҥKg+ϪA*zxxTRn!Z *_ѳѡo^0on&EWfsfπ uV8L$ )N.޵kVC qvV>{2WoZ#j RF:<>WO,DιA .]ڹs{Y:wޒ%K_JrWXPHuus:*gC c94)JG5kZD3+ "hv;uĹAh\,߱Ө`ӧR@ZM?Uv^5HAIDEx(暕W&):=BݻgL.l9~^j$c*ZT{ h(j:&&fԩ3g΄Lc>|V|kWmeFmΜ:HֲesR3it=}w97Up}3sӕgI Et ıƢg9OWfraOY`zǟdAo;::dɓ'+v+UQ:uazꙙDzk}m)-`Jx2$g_'3˗"P+bGPu~ t{ 9Ptd~4H/?Dh\`KP`/_6:^N݇olzJ~z hS܈ RvC:uoӯ_KYypN32ITk I}F} %3kBСDĥ?̈́ B zYd>} M!ܱXb۵0&2vӦM%Jo?`۾ `//tcW1#:qoݼaݚNjQڕr*֐gCYŬ?=sxD:{8Q\,blL:l0͖yÆ jG6idݷZ岱cǖ+Wa͊C@ѽz3gѹA,ϲeBB_mo ? bFl:5~ j١aC_lecǎ!lʁM9{@o͙=4y߼Eժ-WfUlժh< v l:S$ YAIY7of(̥J)F֭KIL zƌo(O>Fíؽ{e[nїݳ0pOLFSǻw  791T:0*44tu+[Ǹ6iPz2tϟa3r^\=Oh͞A_a%4\~*,,gJZ;@ZDXW|.ߥ[o޸DADChbŊޢy  R]P@P9  C"慇=o{>4|_J\#> `+%23DZ^Ο;.eRt4:PHޛh%1eݾ!!rifbbVLt& {e+V%?:uJ&1'19' y[T}gN?o۽Nh?g8{lgd}#Y.]<3|PTH ulQB[W#FBBBBBf^_TH[h>tŋqڗ*)p k{\o6kB )9{:*¦ʕm~G倄ښ|259ΞHHHHy"3RV|lD*Q9 Xh5"^"wSثUCkLQ( %GF(D LL.7*\"0)ϐI9 ~z XF{,DK2,WXIo2Y<}|kκ%b2B2zRD֯D慣SC߻I\:Ϟ<^kpcV-M )S_j! ǩF8vIOΎaaafhBz" ){0;R*UȡϾC_-6>lۺDD\׬(H#-gΘ.eA"CFZܓG||'1) +_"AoCP..KV!Dt|kظInw2[WZְqoӧOK*u֒8 *Vx|؉0y{&eKeeLWȥAAA}{lT`fêҡr uv}zaYX_+pXbu:22Dud۱ P4$BCgOL&a r!6r0->B0 p 'Nܭ{8pǎ;ySΞ={…˗/_vI&UT.^8ûW= BAn9ixhƽp,ϱ'hʋg2Y ;f&;bR.4&#=[VB, $ 6`ǎ+VT|9g,3*I(i?ScO0I[NbODq_ht6uS:uZr۵5`7jtɆӗmoOܫWVǨ5i~O8zmp]<4䵮ޞ>mbdR ssw+QxaoiiϠ6lHWBRYlܨ?1G9}\=X7ctPuP–|D$(R8vvÁ)K2nIffFnݴмacqHcoX?ʕ+>}ի@|cxxxDDDttt|||RRԩS޽ڷk[ RF$HdʂЫ9y?_Y}:LϜ0B&ʘxl.'ܼ뚟|cfNyH/5^jG猴Ln4C1KA=>M{b0ُx2Pk4b.=<<̇_B97%34tlE/I(.ZVV?]]|W/"h4|}}={g;&-]kݷ+`+XwboAH{4B:$H2 _;Lܜ9lBvv"#E^NvX?^?׉5Xç{.S~ņDw\P7Y驃<A9|e zM,\6F8~>l,ේMƄ߼vV-~[fpnx L.R^fx`1 F(xWZQ2/eiiL8AK#/\pϟ?ґ3WgG!tzЊxv=B3!φ& y ?Ҥn aܤ׮iJOes 92z枒RVVRRRH __Z' #}uC⣇`cVm\֭[\'ñS $w#h1Z  9JoBi3U+h@P ov;.޸DqN9Z )Ʀe NJZvBds8h1hnԩT8~Q"!ҷ\TZGG8rЏ @HcX5ݻ2?QY$Bl@KAhNZ7Bܱ@OwF"c?3?p&BsTӯ?owؓ˩&ldno`W+#4'JiT=IV㯮.D [Y/pF>QxK8;q<Q3.+pff2]v䌠:^, "&۷oUwDhV9c-x;.ӗʀE@׈\vyaJ_TRVX\&<5aBMx%kY5+_r%l/E.&B3jVϟ\}REꦥI<1+7TkcLtp)) Hg\"!sLٻNEdZ(ȃ;MnpmBU7WazEbCAxɟpﵻoMY|}ꯑ͛y=NU"~}F Z/# gck$׼?eHch%~@8nNQuћZ);74\5kuttVWN?*|MSlrn݆wa˯v{{C;?rry{?nf_М,b=%+"tf,yBr O8 ^0 w9yoUޟI$E>z{5m-LM=L7g o+2;z<w~YטŊ4za˖|}}>zM$6SqÚ 4!<<#~Cz"uV :ZgEH\޼пsH79] !CߛLQ N aР+W,Rv֊[xnJMhĈs޵DN)3p@-{RÎu5 '+?$k\ bT#=L{Fn9k޶V9hlw;O vnoOݡ~GgE{HTk+̯6w47>n%gjIͻ#JjܑC(X`׮]Dh6fV  o\~d;e]Ƞ<~?nsf-7nָ3iO;iة5fhk1oܸ9c5g0Mcdi=gܨg1ChSC*:tQ#F/f ={9|L4, Μ9k%]hYE IϿ~K7AEhQhb@$0YKΟ\8V/$|UBh^zcƌ!d(2.t -Њ$СCϱ4 (>n;uMn_ R2گ4]`O4T[nBS7y1 9yEt{Ѩ+X4wVĻÚW&fΝn~2?1T鋞&sFC ۭf/O/zyw'f&? aw稼u{>v8كk߹'^crիWyڵj"ѡ-8"06n6d#y\rvy) tљ@˥ӧ?3߽<=`碉3֯_OoݼBxg6m"Rz۷ntk>KXkvD=z;>Sͅ=Bg Cr\<(,/YegcK+/V-9ڙfUQ&()fw%E>㺬\Xŋ:^O MK4b M|Ņ>vtJsug #H !֭M6p[k2e p݄ZDM >8bo9?H #Gi3SCrmxpo-՟iQ 5s[c̨й Vѥ0&Ƶ~c/Mfˏd M<{h9U3!sk3LVpb$aײ\^E#@[v{xrkm7 ^=P5(9{`Bo,:}ɞ5iG2w%%}…5kT_X`+rX wv7?+-P;a"mIo~>T|S @]_p?| 믿%Ţ|Ϟ;M?Tc7ϿT =B% KРn"͖aKYiu'Ƈ?LѪ"4}&4tJDdJ:CU.gc&޽{eepݛHWB"4"z!u\9LLLRSSeH?? (>^FbOiS'KGuCKyebcWR,^tIQyj*R&Y+2U+Uw(,ҮڑU3 Uv#D}6x`"eшЈՍ8] eG8uTff ?Iê˳sAT%=9[)OIA[{ĩt#Phjl^5e}ƶӸgs6!-t Is#FjlEbVc{ K$ȎҚy'N)BmXAu7N :nϹ }w6H??؞z,52+ő dgf_QaT0qq WN@P+s#YQzAϨC1|Ԩ45x ;|(_/+.YO603 tL뙽.9Agf3f +d},jwn-CP~Ԏ]v)cdgɒu@ʃ?Ls 4L)KOj{%zzXzغT,q27TiO Z d99樇W,.ݿMBFɉya4gNIhϣ~=f~ ! w (/7Qs{s|VHwGE*!tiIܹshQ~E\iGzt]̙3yyy4!3=! 4fB\XC|^9-w gn,[2fTDhp HyaG 7g˫?S?#=ZqE8Xf;#AN0+/R?^̈>B3.Tt|ٴOafwta>/ezҮR%ZE6|ks> 4tv:՛]hY0/lCtYl縿X=%5vwOj\7rUT((LFrr—f.9J[@ȋ7]֞3d谉'?KoU˟z|mdy||YOg/֝tezѱ9#SW=$9zeBv62G 4rY Ex9yYi\v"A/Z(KHHdU-&MA4A&_VJ2BO>QڄLPB٠vw`lP;_< /q\.?W{).!B#B7C"4+ͥ&R.Aϫ`O&Z^̈́JqLqU,EթM /G\k09#٬̏nj "45ZEhF4`oTd2PW/j|Go`l&BM{Ʊ xF3)7nN赬 ˬ(줇@Hf~.C&Q޾_|/߼p=qOjQupҲO8Ləx>(֋P<6Є5oIw!f++%=B.ZѩSQ4 :yx8&&eO7eZzZjm;~:O6}iW>}4=33.:ࠛ6۵m|T Щɑv@ȉaB?zxy#ħvҾh(-~W5hs&L҆@ qc*7!C2 o׉&(mU Ԛ_՝]. ˦|Ԛ1dhw``8zlrܹ(ַo_bQǎK߃DQGHZ .'W,+F/x< v)(ؠ!Є/wߒf?Qzj4Q͏`1f]YÕB{aRwy ex2_0RsS0ҞI&19"(0W-o١SDA!y5uFI蚐Dwo+&.qz]ߟAjƴ=;tyNp cr o㋷g5wݿmhG_=[7I-oԺug5󲓣Hxx@9M<'_~#Ss,?/6~1໓dƒc c|6tP "4Yf:j(I4b1! _$+,`#B#B#BS;-!YAA}~;qtа&M9y+-KlfH~35BܸfRS՞gD7tꔲA&H-w#Ze|\LM7cwzu}jj-[X Ze6 ]I=w~(DB6Ғ╫V.?3Fi3횵%%P!Xrym4A"tI.Gh`,Z.wPt@x2gTb!/>>~GC/ 555)/+1bDnb:;Cn-C'Nw}sR7sAN:Ԙ#ǏKM<ꬦѣF1NeBkN~>x:wxrSmiE&StW٠*t.ޠ')l׭ܹl mQGGttN_<1kj){`'?NmdA?ky\| =󐳏̂ %ݛpJKs*TizqP {С֭[R2]7d0664022jժUMMtU%3XdY~~/qD 5d+’ [\$**,3K:9#Dժd*iS'mP޼ <&j9lP^rR j55eyW.ΞPZs(%) vlfƎ1nl^BN{)v=y&:M|99 ko+ToA'>ٯ׫s,zoBYEG]ѡ}  jW|&ddrxĄoӔ2y3<Sw&VW&񬬬~m-ZNVl2ABNR~SIG ֔/> ?wO>8iBDDtBCDHH M@℉*5QRR5b]:аL\RRXSgC8rFA><4)'ZYZ\"Z/c{vo&.]&T;Cd0[W=3 CVwleid*򘜳U]+>68$U@PPpQ1Q~qсؠĸऄФİԔȴԨtPJS#@)Iɉ aɉP01>MK+ (6: &?:ՈyGzxx y Úw s }ۗ>.>/|_~z덳'7O==Pu'^ 84C2@@7ə =Mhț`OY 'pFD-=c%'RDpvi&= &@8%pL O3YzJN4R3ҢNp&KNĄЄX9 AQ-[Xgp A<B,d6P( "P'౳"~X^/NB%!l >rI[E >HXNw*K>YB }YZLL@%*+/'% PE9uk}84Tc'9aq*Br'PH9$gZ3>[_///R_'"tAhxD%%-3 p$EFG`S&$DJn^oQ( ,xFyPy>U `!䠚r),aEQa53444444O w7lBB jD]|nN|\T`ؘHv^B"!E=}}逆I+}p9DhDh9qSN:tĉW\q}*3#Ywz!t\|4><)}BH:Op8@-*u)GDrm5[?9 ^YΆrѣׯ_#f?~*suT ?$"BFlD膌 lQ' / rnnVZZg23X\;;={xyy򹹊 FGLBP+!B2}*)>ES+Y45vTf2oݺu&3'99aA_ȑ#!ƍ)_ Ԃ11~7v& "4"t#5|n6!UZBxt\*B˃j؛333N8qbbܠAhh$1b8x8 |997o^o۶fD I "BUICn]eWbFUMik!3+))ѣ~oO##23BCڴicjz1>.… B~٥VZ+))fDFCCCCCn:/(L[RgL'L(M$g 4)jm#HR#7XElL@AA3pQ׮]KMM &GGE@m^zRMM|IG}| B#B+UezUf@~F$q\)]HNJ 6lh6mF )vvl6KAA(5pȕ+---DFCCCCCn]e߯-Y1E JQYb?33ԩS../bBFF%x{I{S lذ(0?s洂hhhhhMr$Y*PΊ@P͵#:P,LNN>s p@/766._,ָLWWֳgρ.\֭[d;w¢ǏӯΝ;_ .@zzzjaʔ)xѪu&$XTC.*s8OOׯBX,\͂hf͚7v޾֭[C"}B/^B۟={ۈhhͧjPNH³њB֭[7XtZEhyKa#d  _!B5ЀE{O:>y@ĢaÆmҿ;XTrN:SS )2_~Ν;gΜEV"}  +HFDٻw~'C_t/4?ۡC ?Ԯ^Jd?sº/"4o)cƌٸq#:?Çaٳga̙35իуLH![DC~rЈ("4>B<65 һwN9P"K͙32,]UqIXpBbv׮]Do0I\%BVX)@?Cm֟ |3fʕ+?_~uܙ@װnZi"ACnpPړHȕ"(E(|:5XҺtr" `zѢEz>|_vЁB;99k544 {yPŋƍdm.\mmm?ڜ)hiӦAG´L>_CCCHujB!B#B5|.ƍp;wR0MHTlْ@SB+'7}&rVFVZQo߾ Ǝ ׮]i``j[!| 2m6H$lӧOmll3f@aACn>a΍栛7.//urR$"4Z3wҨ T 2_ݻGKlڵ0dwbԩM6ԪHiuֶm[VF)ڵѣ>lԨQ0ݹs#F<'#E~う|:%9M7 / UYpuݿ,ҡCO>`KMpu $/zڿ?11TBh{{{X1cP3P9::jՊm uaekΝ;abGAͷnLB>F#`m޼AF *!;v 4DЮ3b\#p(@7WxqAFk ֧Of[4_ղeK2>N'S~Jسg@ƍ#D'& K;vHEh‡~&LuZ[[jOD 09=$mVSD44D覍оo_1Ӑ\#~n 9.6ox qh^ڥKbPݻg۷o'_pRn޼Yegd c`SLoG_|ŀ+*3g΄`d!s߾}ɡaǓ)Dk }[UfLP0!j#f7mڴ|rj%J'O#Gºȼ&͟?Ȑ!*w,9rl2t@CCFFCCSМtF 1f;meg B!B7nѢ ?TNAkJV [h"+/[bb?khhhhh e Yvv<ƝS徒9Y 0x,,`KQHd&#B!B7^h*ML06L±oXVVb6#K+y Wkח_ ydX{+>7 Z^i-!/@UnGW 9DBqEt@CCCCCnd ]%)jD!&lbGZ 9DzeZr䠏M*u@FCCCCCn>]X("4 [W#6"wb5dG5}Wp&y,a jz XCP(_>>>B+pEGQ֑BPЈ*!ab|̉+X B#B#B7|NLLLNNV}||TYRRV1"4"4"4Z]"4}1bccma~iAWHe|ze+rƵ9j(fY,VYYJ rZ(bz?9+Kc5 GVs]^ }ڥSdgeeeddE&'''$$EFGGSƷo#B+Eިkwb,T /&xZm~^fR~۷o/9c܍"4\ŪHڛ{J2_#"B׻/RyJX#gǭZS*L+ͬTCFBPB!B#B,&?WHkwMR4ƎVMNNVuho/r/Gg;3"t#J=<=vJ+ίti#4;PC|>r7bङXV7!$.C B#B42E#Baaa C"j8Ba0HEh#Ջ1"ZA WZRTfqy_ޚ BӌE|]H%1[n] YyBWBK6aHM@ѧN|HϿ^ʱ]}600طo_BholсI*++Te9.^]/-UEb:͊ >Z,x\g9oB/مO?V~&5}\+ɺ}EU wy;|En*?E/߸>E@:972N.;oǀH)w߼hYll,m3Dh,B̠/tE} t:\>tbV(mHY͓G6lݵ~wwMw?*B{xxجYTV(^z޼ydX< Н%&E'&&R]="..jPLA&۾S'm뛷wAmЬl*!R_P,M?SJ1k0q7J1M#/~7<=EB(ͥt#yӼ:46}P޽IMII^vm~~>}~̄}T{’LECfffiii,Pƍ8BWgU)Z lٶẋ@D~3!>|x"tjq^P;:Qz="t;9so3ԯ_߿OLs 2~ iO}}^W{YN'${/]DW~!4L<xXOOE^:++hᨴ?a9rE;884(+..ްa>>,KEuhhwIggg+Dŧr\^phsGlGhHK"4"tcn2ą]2)FFnMiu> +))qttTlrrA%%%N& 84w˅ lmmiR^sXyyH(z;~~ISw# !B;tUqzd29B70pH*"tP'+I: tYYzÝwUWM ~ݻV5gBGH: Zκ!jܼ4BZm FF=btwwWxIIzʪaD'cƏOOtuukêJAnx| f閖HM߃\3GZ֮УFߤgmeC#ޑ,::HIPV CBByʘH$R ԱSSSgΜI2ٳg1--t+k{7d<8c2J-[HUB"t"qH BWn뙳[ƍ7vqq7g/\F"tn+cL@">Y y"4A@QjxBR*x .,))!Sz7nYe]vЬϏ7|V脃"'e5dzt4]Ϋ; U RcU3l6tRC_tQifA۶ڤc/4 ]%\.W=o@j7׮]+*{>L߽jߋ_;"4"t]"4I*,#_hv߽ov2oޜ3gNx P(DhDڰ\yѱ7oި1vII}DBܹrW*A24+OMMۄh(ϱDxdZUnNۜ4BT~t Af/_ҳgm1bX#^h"4"tLʪ=^ @40P(޽_vvV BhGkM d2=|H.yVPPRjֿrDQaaaHMIk%COBVJ"rsҞ<߷o].]:c5 />{5C&:$Fjj:N>rssH&7??f7o_b%Iׯ]۴htjHKK3ԮV-''gԆcwh"JԗNzɠ9kfvaٲ=zt':nl{ߺavPc/4[WU)BE|{+_o}B.+FۛwyJ} 3֮]KP4Ѐܡϗ/_VZsdd$jpbA{@CawΚWO“&E߇LBsC,~:7 N%BW~޽ƍӠvP{{a5 ;~F2Ҵ'5^-(((//ů_9W (ZWW*'R OǸ 4U3f?^1sNWZ'XРG\tBEh~шJ;ߚ_tQmC뽃{k56"4"4"t `HSZ? @ --&*dzꬬ,7oo0>7^hKF$EH~.zne_M@hЪRt@sȡ*-Bx0rkQݞ=;ǎӮ]t;w&^n|(q"t[*"tSI!B#BT!< zþ@qqqnѢEtRdd/H42E0hSTTkA:7d/t#Lu@A}NG'T0wsM^g/BEEEݹsȈwE Gh2@4֭~eʺ}6kEfq Bߵ;5-3 hjd&ͽ"e[!C "}!CXbrrrT022rc: fbb2~x+njggGPpϞ=uUR ux<:9X,<,LrPci0AC)jd@^S'NbNڦR@{#7m4a--T+i"SvwɱgVWMM ._=mX+ᡔ Fs8I30zCgr}||ikLJN)D蚢hynްr,.NB谇m6BSOT}ȁMƍ###*sssԮ`X Lx۶m V (C\\E"WNGP,nnnb0p5go8L :|11hK#B7dV܏ݰ ]ݶ~/t3 Ӂ9**I&JKOO O焰&[l Nۻw/MGgr"@4uQtYYͭEarrڵkez!Q4쨭և\!yE 7X}r'VY6??_@Ws( ]O5F FFc%%%6l G&8vQ4U^zE6BU&z*`Ɉ4)#r@D8%@UzNfN+W)D"A'6mszD^A]ldݻ!týf#ȁ-c[n%Yu}}}Ν^gK zBV >ƅT)kAsBhoJ9Pdɒ鍑yguE(4mH[iFFCFC^h"4"tݘH$P˗/v\lYb~ aeeUeNŋĴ]! k.:9\.k4Hϟ?FGѪsAA--Ι3f.Шۿuvn:rV"߂+}RFY]VcPfWܹsݡZL߼H~ȿv횪$$$Y=4h=eʔ@9Yf5Uvc˳=c]rשּׁA#{CsN!("4"tm#tiiP(TB`fũS?eeeͣf$g|CӶrb,=݇+-0/h߁"t!H<&7O9 B)Dķ^/j!4C7c,B>y򤸸~g#5ܺu ~zjbl9u&LsNjJLLLhF_- »z ܊SM޻҄tl#Eh"tC@hܼTFDB!7(2k`J^yg \#o\+NS yR摕݄;9951ÿ*.'J/^ 㐨y<^S ש=`0Z}ix|K3sDhDhD m<8%WZ@k k.\]yN֚6lO?XLꯃMhDhD tlll3|dggGFF*g/^6a)sԹ}{-* R7otwhkdN9CO6ܠfk-7laHj~4%-"<|͂ih +<X999ײÇo M\ "4"tBeƱA^kw,I\===۶}Jse dPuWWWU="}_RIE޲ݬt׶'N Mn@n %@ LM'PC3=B1ƽ˲,˶$˲lY- BeT k:W Ϯ Lio{7p0Hzݎdγ f('ЈUKKK@@koo_xm}v=hEY2_ 2| Dh1p0Hzݎdγ f('ЈV\./4 *--0aAF6᥌Mq"X,^;.KNJb|[)J˽Wg}AF!Bۨ:+ O.`ŦcC|$fz3XNH_M "4"-TUUMT-怢ۗSTUZL| /c󦧧Yÿ>mfBǀ3F{逾/}Qoۏ#BlE1(%<8Od`}?x0B^OgcȲotCCCa˗/SXXs|mfSƢ8Ϝf!~; V?tBlF ނdZK?lo߆''1p IhilX{vi,A36wi vrdK.-((0ЫVp8@7nԞkIj7p`7 &ڼ %ɲISEq$13~3 BlqAU(hx|OOghOc@_M˼Yh7ʉ 5W-B"&nmmx  #K)J@_?dR\\lƆ,A`&gC39gBh'`P~߼44#΢]lꢬb7[JHd2LH"4"MT[[~KSDt_wŊ#"/iEX,W63 bccשT?ҟ=BC ݿkϴF;A+/aDhDhDh:%[p1*;<\qxb>8 dh1('9DhDhfM6͒TpjZZ=y| *MBnZ555A.ZƛneYT*tUzNAFFvl-tcc-pљAhQ ɀrXͬe˖( Yve˖)Sl߾}L=iG9okZk-NjCFvT-4/?܈6JZ^'Bv{&jjjL=** Ѱ'}(4rǏD&]/f>Nŵ%iJKKhϾG]4b[7%@F6AUX }3|yk̸5R+UCDcGv)䒆zB7iFv3k% y.\Hm:Q̙w:{ʅ ׸”,UeEFFZkD>l%ЈNZ x|Wꡧ2!>ʊ[EQv٦ Z҃";5 ȑ#&SVVVYTLL D+mP{OlRV:(cbե<Zo'Í˒vpNYk*S׭3kg󵉏 }q`U{p<@ 'OJ%Dh tkKEЧOmmiDvZ}*D ...##Ê-ZrЈΓ|wɷHP6m4}Asj?|C~~>6:v|֨4?k񿍴Cz')MI$Z$ e6BT=e}H>wMyhl.PM H|\TĄPEE\4kBMu]5 sYeh@$傒sgl15%B3hi'сg+\/npkk,.\hhh:nj1@"^^&Z-EFvZhHxm|[nEە>^Gcs'wIBv{~?~&ty? .dhPO% ؁cW4s8cL;jNr䥗_׮ݣχk}g,U__HgEum"?#B[`y\W[xp<]ZQ{|v߾ 9n7EUWW3^QQlur@!Bϙ[̀&23OQqG;xEF&[+222[[[5cRpOshIZh4]%Oy蕄hM ϝ 3RRRlW!qŕX 3ךE75֖ M홳m H|\_ۊ*HI\5FBZ'MdC[A~9to13A蚚[dvn.Dv}fCgٕIII111vkGxS!t"4"3Br`JZlL騤 = "B#B#B35 lZEEdXb/PVPF0y7…ޚ|MmxK/غ JpUVVs/9Rk\*76ȒEP jd9C(`VG芲Կ/WV.O蒲2/x|#{$y9sh8kU fo@߯~9A"Bu(ا>BO>M';զ c 8AxE-tMuB.fY A2ܹsK(2 4 q=Ͽ/<ʁ #>zt;Quu׺&䮠 +:;;UmMM̀QVfu0G B#Bc-4s-4x颹|]zCOw qVv,?c-bZ׌Z9<ZKMpSNDh'ߩWQ4Pjk#ov.֮uDyyQr6?+55p@m^Sߨ~FV#B#Bc,4BK+7[{ۥ~@g]ϠkY\}9"Z+k~~]-زq#VtYϟkF,4CF6S:6%BC;7[7_L䌁R╘˖gL}~'Aw 槟~zȐ!9e /@X{Qr=aΝk>`gޟGПpJ .FڳEZŧ9o|4l<2,9aÆ>yz=#F oP` BӪ=<|e˖W^yٺLҿmPBAF6ZyrDhs_0S }-:.6"+3ٔy]}zO{:V4r?B755%$$0LX\.ڰaÿAmmmuݜ;&c(bz2TJK#/`#B~jv쁣2EFBZ5B+\ +/Ϝ9$=5WUFZ5kQ# Y\?MV-4ZW 3ݣzҥ$Z__?nܸ*5kŋaM<;A ɴb@z{(klؠL$]o~HNNq!Krnw9M*K%l7JTPujhpu5RZcB!x@hB{T,)=((HB3<4,!{LU[=['R ͸R234;zo&O6rrrԯs_/7_/83BsT]d _˄9%Eh4ZpuZR hرlꐓ5'R.,H/,H+ ET_|5 |^jM.Oqh'r9+y0''!7V[ @4lyxJrxjRXJRXrRhRbh򕐤+!W|9!cb.)U\yІNPbd)K<}pCST45'NéOHLR eBI;6'+NYTJU@a".P@eJ/.4eyVip CU3 p` xiTܼd.\&$^r_ΌJHk* h7VV^/6"BÃ; j6'Oܾf͚hNSz!qOo ?lE*PhuX3P<=~NW5*`ΘGבVD6Dh!tuUaAQ SFei\M[Q/QJi[t?)WΨ\Bb(kj]GY\- pkKҭ6h7[TnFT .9N>#SH}]iT66]TYA(0b*?P(Kx ]F]U%Y.".UEshs \tnٜurrrTTǷlҗz ~?xhM6~ VbC֭#ު:8=<^nH2vAjmmK٦+BFhtE)Ep>wU'!ݟz`s ĸv-V`|Ax# @>8ASt!cg5_}~{deP Bj#^%xpiӦ_[MMMwV/i?mjB0N/r^5Y[~3) Ry"9P7=__w>8N:K'u3xuЩsrr%ݶʙ*!B#B[*Q mp@~Qmmm@@P(tчtUoFGQ ^=f"7бqxtO~.,, ,--&!B#Bk!tA}IJK KJINR5jeEeeD+[ief$e'(d&sX~~raA@+EpV M9F5W6BTۂT $rU-Mraxes¬8X9iXvF(zÔm 6'Tn愉!L c95'jN@7' Q6'L4' KS(ڜ05B՜0RٜPU6T 3愱"w9atA1S+PzE@Y\|-`Us4Ņf`^**ܤ|p sgZVXUYU6xLMVxMM MM$+Uj_`Dh[ tppp?kiv>l@uJ*( CW[['!39++ n_ADh#tmMiMV7W7*5ͬ*5ޕ*ԟ"UK+% ˿:UK&(ln֢IӢPjZB9EasVB愝؜q2֜C9a;ݜnKiNx-am [\mKjNبՖ-u%\SȯvRy`_Bn<۠x^]DOMK Um&X?Ӫ)..v+Uj?-E?Enl Yt{K,_Gu;~:q"4"|֔J9hOw_vgIK䵢F"cP;::X?AAAV*쮗>'oV^&>@nds}J(>k~{x:LW(8e`Tl3*(Vz#D=Z\d$_r%.[wF^ ӦMf f/]X㬬M<ϱϋ2Bcн "jS $k֐[cr7|Bݪ{|F$~`F!sAџ) $|s'LfFk\#)*/-ڽ{u.]f͚;wskG75!4u@v}_#VT%vI;@P>i1øYCё1&!jɘ{,PCZhڞi#BumMYJJo7lX[*TO<1o<`0yܜjI%mXCmS~֊P44?w(}eo{{{|||XXBm9$$dԩshСFrS 3b94NP[mqh RWzo7sBƙ3*2l K3i>nQf!4m0y346VIKJK׬Ys|AAN߾p8Y{.s֯__P_S]dlƊ}{wqmK(͈ Ӝ7ݻǧ6v466FFFFGGؒ튊&LP|`^,BBy[!q4pû׾}.VB-euBPdL6BN5T0B_f<Ά[ng1ڼ-Ā7(-,,XbEUV}]BAFzw?ss7mڔ%.16#L޲eM7ꫯ ૱ZCo[m]]]]HHHbbv;kN ZB?HT! Q4)0sJ.'b< CRp ?|o \OQ!ḌNX<}k78ll@gaki[v9ˤ"Q9npBC UgKH/a cH1l 7o_Py65׼۞?~~cC\3TӀ"#4E1b#4b/h_W'Ҏ; ܜ>x;>LspiaƜw6=ˎ[wjlFWAhjgWAhbqhhhttuc? uM0$҅Ϡ5P $:6@ΠNN|[in#H1ݫ .y d}6E#B,GGW 5T# N0,lfvЖss23RV\ύٳgOIII]m%Ì/LgokXPBFxgFh}`v9Ԕ ƒ̝;xUd"wҥ2 3gt~Z%C`B ]8|zKDP0̯3\.3󳆢߽]WDh "Zģm BKt&0xv9"tKf?RIaSc}QQц {"RD=zdǎ0^^suX6K"~\+"! n^t1 WWWͳv}"Uu:D䡇iz >b`g@1W@ i_&暐1҉Z rΆfrI[Ze2Yll޽{88cUgtZhOEhz¢,Ghm~֮LNHH VBjb9Me  4kOSi{U/jkkat%VmXXmOK͍umMMM-m- j13U DhB!~Mnp\\\l B:Ϝ9_~H$Ȱn,Ɖz i퓹%o^k~.%L&_p1RRRzMrCZdCnlb)Em}#?^ѿ nx=Rwn 1N~:t3T? {FԂo{Dh > w ,Fh +E+'޵s#x.Oı=y,UA 5] |i???TrryRRt|||MM;=AjXm4I7agϴ/ <i~adX`MBh { uη'B3p2UY3ZwP,%9#,tҕ`4o:QMB)BO>ٳpkoo)544KTUU5>(,N T&D=/zoű#? lxZi_)=B)IɈN,ˀQ}qVhck$0G!<ۈ33gΜ~W5/hVݵhZMMM"n[,v @ו̠]ȝw&P~gOnzDuY#Qcd#No0tOI{Ffhg9Bk/N΃A')Hh4+;fxD9AhGe |(嬬,`Ȳ2D\?DyE,*^5'l QR[KUO"!6xjOA}TXj׈Wyr"/\FR7l=r B[HiPYF3K W4';ZMStCCͭKيuP(Mcw[29 BǓ5տ# U~O5Pp2wKP(]HGG?wE&B'ջ:B5B0,F"B2gh(Zm]=???$$$,,L tZ eH!6۠*2f"˖>O=g^6o}?g_p愈h%-*ϛ;O?O$"Bk|~hh(4 W QMUUʼn_]B: lҥXتv~kP(D E4 i[= BѮ۷m5ѣ''')%IW+xЎBh:;;€\n{{;"f=6cbUЅÄ́E2sjV̜U~:PQ>c"B[,[*h (:Y9 GdAR;FhKP!ɐ=%rhWa6??N(G3u?RJgN^5IicILT"Iq#n"As}ÇSs܉"]-.5#0HmN!4 :1ioo2d'|]TM;},uuu!!! HF3٘ǏiM55":<,0+3ZR֠Ad)l^R;ZŞv@h)Sx#GH$ & E!vݳ^ O'XL ~Cw#{#ߒ? )kjjッx"Bv訐*9mN8DhZhQZ0L#4 mkgQb{h>5qi9Iao)//_4ޛDp^4UUU111@)))lRC#B:H;"Bi jR2+V;ۂmϓ'Oϧjq!E:B:U??@Nϴ$/oƿ֬[b+z `=7D]Df;vHHH(--5x(!B_Ü3C,gg+ Dqƺy9gO?uuD$6˹]km6BS-Z> ٝ bf\F6bq)}0V.ӌ\"zTVߺsǏ B奥߿oǶ!*DfS G/"""$$$<dk7g7#6_OtIG( @vHgpT,I\rjQ[[3kQ#:1k$4BWWWWVVEх@wtE4 ]#t*Lp)?oO ]̺Жop>]hvLEDh;85%VIK5 kkkbqee%- hZ] ?;D<#ԭ7b#c{`,43 sYqcrb!6K={z_zG{=2j K-Ah4Sc]SSLB躺:.--u'vZhgeЄj y,D n999!!!n>?>##ǓXq1sykv,wFr\Z޹뮻;ur^+:;L*B(u-HEY&!\.DtD40sqq1JAAq-6eQ?ߩٱkB&Է|]0Nm NU32fhFСsl1.:aϖ ]C$B'qw?q;6 [TQX\K{F;4ɂS&,4  D"tDtYYYII ckΝBc,UH;tܹl 4oV ֞ ***44^9N}}%XcsR'# ^*oMlIHLCڱйI7{7_5sJpyalW ŜKND,0v6ChlNrǎole ý;3g7K >@!!!qqqB%wttx攅 7zG\8օځ2#T"pz4W_}~7rRўlcUUUn9PF2ww.---co5w`szzzuu |9go1 r{ B ɰz 엩΃glRaAZ~rX VvHs ݿl>|{㭷vuuYl՛5'=ۍ- ][Sv 9  <ʱc9@MtG'>բ={nOq]pvC2>Ijg!Bш!T*愎EhB|kczl,Ǐ|m#FVVcmwـ3]k\km د* BѮMMM ]SSrIZ}ԩ͛7#Gy`!E~D?Hdu/~ n@ c3/F;=vv`Dh4L&sZhN5B9r^@jkkԯ.(V!QQ{zPِJ/0O_k(K> ^^(zB[H:$3 BQttqq13]r&֡рaaaiiia*%$$ŭN.?v}O!0]~bLNe˶zk/{X}޽vg̠B ǶnEFFF3rX/)999111ajEFFfff7B^!eꩋ_|_8pKr n]"@FF](h:˓守 j8ΉZJJJx:thq4;YI}@DzeX?ОNqLEVnhhp]%/ӧ1QJeZȍbrB=Bp礢 BTSI)N| 55ZȁBv?9s4>."7' u1a1T(Bg:#77!IΜ9m"mCܫ{d:;Lȅ S<:xYn1X,{i||}qDDDZZ&uO{{m={6@ @!B2%e KN&!t[[B777#B3|~˺yϞ=_Z[=G 4Ȕ)i*KQW?mmWB]4333***\-jVTTGPpzԒ%nHZ%* @FBBC328q&!t{{;ݐtGNNNKK "V9 ٝ bH E#B[b>~+iF.s=RRU͆ŬIO'k@s[-Bh>RVvآEnԈ( 4̕rrkͲLJ.*1N2Xh: rtGFFFGGKBc,429'yPy/MXyvkOSvn6"4%}T#]:CC;aÆ[WG`xĈ-Mf`@h7hNШn^S#_?B#~5jTtt4/..J`WN;bbSOB&T1B2 j:E澽jh&شcU}}{~"4ʺiwBDhkQ4y*y[ZZ(h:#77HMMmhh@6I&UUU x7Ɂ㙐\f 444\%)a`!bͰ|x b.J[drM*PM:UЋe / M{FЌ#FOo - p8pNIIo_뭭(Dh;5lURMCh=: ҃[7] 7p\m@ o۶m+VΆSrZ GA7r4^-fb7LmIe/a8L%;11^񊊊 o_ lO t.ڊfvJrkv@Ԕ8 6:s:\B# [bBUXF5о}]JZ9j6=~BGnκ"J]5RQvVI@GAQtGrrL&ÌnF2q۞$Ed`+n.~V,Y 6BvEE DGiN,8eB*褤$TͲiM^yE l1A>Vhc8aMگoܡW-njjr>d-&ODFFDbNey% 'K";Z.3 D"pf7="sjr*HPxmrBͳBhxv"G#_ҿn;fUT;}t3!B#B#BBݞb1"4"J*%}ƳW kc_$SR4[F跾|?%Y}6DFFF2+++]Q'iUk:ހ\T@._rDhDhDh4МptT̙GWGIA,P}ICHv_a)E?=e>G%__Uzh+"tMM CR2 myOw|L?TcGnq,^B v(Dh4 Z*2 P(tU8=w\|F8AOE_j>}vK5 ᝨ F;BWWW3t]RR6턵k֬A!%$XaU8e oF~BF!BNЅtD4+ݠwBKb9lж?s 䥗Ը5Hբ թIpX7IFBK$صVp +76b ȫN8z*2w ,(Dh4]UU輼h: 9XV#ۿl7FqHȀjzPHN@Ue oL҉urD((Dd)3h.K'Ȁnf'B3A&D2xӁ~S}H(!$ A+ߡFr|ӊMpWTTH$ꛚڭn+](\.S'$  c&Bk ӏWxmmm oTC:\D,GUIZ[[}+pRRR\\ "##5pgψO.wlpP(͈(Dh;o(,8cpp߆/ aLFhT>;gR;w )*"ァBuY'o,hx)O}Tnr\*9pZGFFF8Um K ND"黮1rnN m;eCţG=>/$T[HIVH oU"ro43=mJ]Kp:::Id%%%EEEtBGîTwr*B+dh52=KȟnczRw+xB!B}%!ԵN~D4B b:"}dggK\-4'{Bۮ+-v&ԧVhc8jm +LIXlkR2VTTT0 4|[y]C]]dzu ҦPu o[x BVOb3"4o?֭x.---))#|~~~>JNNNzzu溽ڧ <.//1UUV[gșn B G#BlGf^^5+|4 #QfhJr\3WZn:;wl>v`P(# y<JZZځvlصk5'tu^TDt#1'O_BFY%^b5iE$o \Zaz@iQ>˖-ӌ["==B9ı;o^nU+Wֶ kvrv!B#BGmm t u#UcE[Xp0^R(;!A5aZ+.BklFYzD߿ڵ:ed"E=s#{{ؾ>]xܙЈOyG@*a҃}Po[J.od8e B3ZTmX}݊ U,nx` 8lذ=x^˗/1bxSS%t:<\DchO9ߧ=~z ]]%#==f̘_~'&OB+G̖\.jf͜6j䯳Q[+1%谖33gΜ~W;;;kjj BBBBUJHHϯjsSRϫQgB"4C/}1eW$*Ek>gID;r޽-GhS3rBvֆgrlT68eAEt}eeѣEJ~FbZ(H+++9N\\@2MiiiEEE|K"k֐[nQ=چfL }-9igӚuG3g BF"4Z- ?)ZhjgWAVjjj࡙YZ)//ojjrHD+PHh%R43_1 ?cW.^BFFYWЎg"_u-Xg֬Y .\r%9uK/_&?N8X m AhPx[e>r]*nP+03.%T+$7;xs-7? B[<=50'%%NCuBB˭Zh"4"4"\QVzt 0v)ObR;Dh"\Nol1w;{ލ16&l[V{PXBR(Vh*u%M5uM&k'94rM^sIs;w~bb8>S=j͢4ƾtjٰ?:M ߱SMZXXBBB] 5i7o\8B]:u 4 EW&_cOiQ_6;R(t!zhhr!hу2WD@h@hAoli(/kQg.C'C'2 fLVx.Ex_e-bxoֶNQTy  zlkE3 4 "4j3jS[ f-B`ZBFCCC555.\8z2v1f`~V\'Zuf/0B` tWgTY)| 4Lm]Q\\PQQק(tA?FWG~n@ >:A`-x B_tլ)Qh'kUTTFhsssVVVtt4oF$)##Ldq_Y@N @h0Xlp(B_ GavBMA聁ʄ̱ Ce9Oti&iJKK}}ϛ};NB`u@~͠:!BkgP{@huF莎111)))555CCC7@?*kT.Vfqx1ݨQٹp3aX91MK{} O8B Sa!#4Zx+( *KV=Bקh46$;fQSZ8p?d5u_ DS(>d@h 4<͠7/TSCEfzdb\H\NK͉/,H` tffΝ;z{{3];M"r/N0W؛x1M@͛wO 0g ;ahɫ'B;^p3_{̝jdQHog7Z^EmPr-H䘄 A/(Ή@l&͛ijY\Ӈa6}L0owBܵ57o^NK/)kQgݺyL4 p֧%"ۯГ\މ@q>4R]]ϟv ֋TZ5(, z;aYBr8a R9fAŪBms9l A^zL=GG6`0sb @h@h@h8)1N=N u iV&y"Gss3J?hpQ~$FяtO zK9,JĐ 2椳4R2겼 @hQag@hooot.1 ~X*Ϗ=RG2M\r8Kex3:^XXG% 4 T.H B5wJ^|mAhN888(O!={DFFvwwdfhh(~T"a.G- B@_/,1.eӧǹZ:994j)BBKI ÀЀD _ųEiZ־DK@(p}ndq\b2KΘq30C%6?kDkmm%Hx4::  p]XEXxR!4?{V{zxKol|s^3 xΟ?Yb-qs B ̓g7 *r_:~&LB"3gxg?b9vtt8*(O._DMJJ):11񳅅Jh__POCύleA虉Ԃm8ᕻv3 tvvブ?vNzf& 4Zӝ SB Jk\.t]]`dd$55sVVɄ:kE?Ԯ =3A%;v }_h7ofegB5٤@hMDT@hQh ЀOw{xRRRܹ兖d b$K9ELaCL\Ś-~hV~>[9GB _uqINN @hvQaTRT t'$A63BxCd@So},oUU%,D"EEEfF7B-MU5Ԓnf;`5&YZE;]ۚ=gBOl"}}})Q@qb?\B8>Qs⍋BWV1F0XQU´nf ]]] ---ln:5:@&bC>vpg+ ][[UHyyxE)uu({ϜM7dV6WZ+r`帼 7;}W*rBSTtL%%%!@i R1~n@>RgdY?/JnW12>D*Icccwz BB3B͉Ί!%$&%e22B*˲+8Ω,"weNU%AU9]UZs%9mc m6R^ZuQjI!i rQArqA2[\TXy y9 yH8&IYٙ1̘謌̌H獵FMOHKHM8),Bs8ȣ.HKt5>B,+=::ӭt:r9N@'!IIE3?JR8'UQ*:i9)GNrJ&:-YQ9QI[9uԪ\jenue^uǕӻ,4]VDt>gg%oS^NRzùI奤zfW`kP"U@^ZZ߈Arf[ٹll B'7[7G ?o^^bQDѳߟj:b"H݁g[VTTĈF#?\]?svwW׳07 @p ]{X=ݴ{f#wں[Y-\7fjW~N{| j÷ uoO'r^.V&@?2{{Bx}L!ёQ#cc\ApA=èt0ǜND]SN. 2dVɽs{pO$::끓;ɹg8߹wJ7?9'9I.]EFF~bmSQ^wrce 4 4n+֬\hMO䨮s6q\oWm88v?%o_z%??Op;qss3zj`$&ڣ=9959V;@I[N( , ~B<ƞ"=?mnGHfz[nEf_0ݰME#~ş5\9f8???""bjZ,4Yv5B'iu,F`@h$r!Bؔ/400)=s6xzFk%@2rx ∏C~Occc9d4@b'f#3#m;*{z/h@f1 \RlVz4)3&ڊ M(O,gYie%霁̊ҬJJVe9<;+j8!z@rN,GsK8.sr=c9-',(䬮zCc8Cf477g*<<<==2ڣ~A~~E{xli3ED9A6/'g#?E @_D9Bظ"** )9E:%!l U2&pBV{G{mEY6]gF j .ߥcDαaaa r6Omlk 7GEE673#07ވ0]{bitZHxc===-&bEGG/ԤT|9݁: 4H͙Z0]u6 +'a3򜒢TZE *sEtD 9ZяѥAB8`sD&ϻD\pEuue+zRKyw8EK?ގ!B vڿc2 4r@h_wN!+vҨ&[C]mpQQ\/V1O}=njɫF&hS~Ӧ&|r{Rt7642^L?[N<> E~:qBЌf z 8E+IǥX`S[_uv o v6V,֠ ɤs5B6rpWW\QA6;1~SBq&`A_mf-gq$&&MߢP^ h@h@hZ J|,~ b|UZN$.hvFٷo_Hr7_5w;dYY=Z&ӟ8w\ԊК2;Btrbcc徾rD@ ^IHOg/z@ >N| r ~w|ݺv -hmb^u\}:Z ``WhV !ZfzGmUUŮ]22Rw_OUe=G*>|1Q{cG6mTYQZZ9x%.jbҨX~lbBEd |=xkKqy]'2$t< FBp@\1>ED;=MiiiDu\wCN/RC:sLMM@0jyIq>ZO:y-" :#tYY2gbb"\/_?˟qVHǕ+phyƞ^7w/XD" QEO8?B&eHPHZ(BV}9hFW_UI).}a64ZZ_}cOZZ=@)i(j?c ??(łC3(|?ԝ7d@ ?A^|%&2Ec3 4HZ!QhiGIZnV[cCOt2JqA~; sˊSSΜ9S[[ˠ7Y14$_" z!4>0ʏ_Z#D3=%Hq'_t?vJ$jݶfw}OltvOѶ3 4H9MJ¢*rو DgPCrTϜ9MHIN|钿?Zbv xHLfLLLff0ӧzMspPi"'QuMovnv᪵ AxM A 0xp~i@`dDg"><"+Adf9WxOJJJNNF&OC#3H===۷1Bs*,&z@Q  t0X ޮAT}}}C=ʷ" D"0^Z"b8~gc bpDt:x#3&[79dfD=ӹπ mBM`YA"<˺" 4^.**ڧ-, ЀЀ`0 d@膆Jϯʕ!(Ȅ? aX(~챸;wbNWw Ӿ>ޝ&}|ЀЀ`0Z-\{{{TTTQQju /Qô}S= 1މacz`IIəm-2ZZZz_VЀЀ`0 0 4Ŋ!H'} EMx)1W}d©Sp po;Μy555^uxxm6sr9.ޢYjZp ZhkdG V=?+ju))) \D_r[."Lb0+peTrY ABBk.B5SJjj #*0 4DDh|pVVV||bS4kE?Bdq<GQfRDS(Ff9'/ n&^j 4 !4"ۆ:J_7L뭯:0Zƫ(N!'&>>Axuc?"п| {SLfhhhWW|޽\kЀD!eDvuT!ӖRrc=NZ-u[vvW ݗ9rtt455u'NEkl !6{6A ŌjBBOni$''vw-*0B>S<$ dcܻАȡ򨹹944@3P+B#{_8r588800 ձ1%՜L>2L@h@hmBդtw4W4οk@J@h@hBdtGl).:k@,?to^^p䄋_<~\G_+!!,X)vA}}=]w1,Ϥw9a͚Z,B3 hZvChd|M Blοk@J@VZ"EO B766\# @%@B,BoBpDjŴpY===Wg2g-z7"mBآ@e@na]_[RSב'j !zxx8111;;[؞={.R"tfYY(ᘉSRFƳ.`l-,BM-/w"P'!!'BE!4Nt4#tWgP4%ckP.QkEw]RBBˉ򫡡!,,Lj6=7 +3 ki #,;^^a)bz\@h@hAV+gB3CluE~$h/^ՕR]I@NHHQdF袢"xꢷ&R@6 \~>0 JȘq"??sBЇYz!4!tx DAIk]YSFe/1Jʲ\i]RBBOBՅ+0n,3Bːݽ&avK\6~@2Lf.-ekˏ <b;jZBj/,fx-/%1ڝ]N!ɰwZx >>>//O?~ܑO_} "jK. teeɓ'Bb[t,~~޳z欣VS63v1>y?c|7)z}[χ/V_88HQhttҥy֑@[[fɀм}mmmaYrxx6mmYnbaxZLOOImE7U-XLLLcc# T g`[Zwu6J}M X6Q{5 % 4 4DU6>r䈎hx2$$D!TY\\#c}b&)*..N<2ox(5]x  'mb @[*!!N>1pR!z,QKۀ.&T]$IYUё7Zj*T^S׵`7WjkBw R9f/B#~ bˋ_555QQQ'?z<xi hP(2d>񟉛v;UQZ+99Z>Պ+&!BV/@h=B#vw Z쮮?:B+>ϼEQtVVDRȦrrrd.2::Yё/MeuW._#-.//D@hWL\ɆмptWgZ~5<[a`GyyyfVg&R"C--Bm~ddHV 񪢒ܡU򄲋v\KJJ16FW88$X(KVB+T\h0x<-BVZz+333;;[!ۗ^Zڵ-TaȽNM! h``Ν;vvz&zכ٘JUf}XX$}Cbb55wbbܐ"#4qS'!>/ڕ3e @hFh MgtLO3"J@/h퓒hݎbˬm wox/U.G`^K1'+.̵;;snߺUB+ V./ͺu#+# 4\dBk Bt&??ٱl%rss*ɋ$/k iUX\:2R%Zh##Ssrݶv˼;wH[Z Jbd֭[֢zW>ۉ򆗗@4-= Z"j%q"p 苃>Ј9e.P("~vlR6oRr pDRKKK)zͶZv s RԲo׸c( tJbXsC ,'5)Z13gHUK9v׺uW}j>L%qKxf@Yccc^\,[6w9Jrn>z =69 4 < %B698[ⶳ]ammfnv UT*mT%0Z#zϞ=|lll :??Ӽ`2A~'{+ke(TƕT$''wuuo>/q400]H{ZM Yz׮.M7yx8l1ڰs7Gޫh3ϛTHH]Ľ BktE͵)Z_Hϯ!=fom`-aX'TQ{vtt<ώ8yxz5lr#8u` _l\\T 8D|)'mi掎v}nllkSS@IК%y9PA o@Ƀ6B",X m|WkyUeB=\knݺ%TŌ"D}mo Y?_SS; alrœVsddYdlwOLdd$_(| mU..MMՋ^޻@ DBBBЀSJ W4be$OL5t1\}6ֶuVs{d/R( mORaqqpjzIu\ DAzh{ޣg`` $$DbFcl  5OO`@h@huF}ɳ:B177?yy2#7 G. gtΈ[]?ԷErZiJJQ66o6̇c?X]o^{^.1gsrr2<~8s*[A//gcgaxO= 7Q b23$@h@h@hi9ЀК<~fU䐁VZCh Bįw->->AQtwUrNs^bDbIwS?>3f<0㏭JKMQQh7BJUEв!ɯS?VTTBS6 =\s'_|}mEWWWK)8JCGhqunw6[N"2v=īn?Banf.JmWnn"EݓG:;RNX4$rBk^N@h0 4 "zbXj[\s}cc} -մiegj%[ڳfVK+^g-]zK~֫7Ldl#y[rS*j^'EY?Ibр DhQd8[Y'[-o/qr 4?<BUy_M=rui! ?w+<~Ɵ}7R з1Ĉzfll33{$) ?\_v#~FSxef8, lϙܒ%!4AVsЀ)'bEsow!P܊ ;[ ] Bh4x]3ދ%ϙ@w:vFv. ?>!!Aڍ0H@ LϺD"j -[G@h0$.mǣiee_WQmiiC444ab|K?DfXB_:vGffutDBz_UҲy[){3!WD[[[JJ #" ,!Z  BBRg_U[ !z"˅?xP6K ]?s2O CMQT,x Gh o zn+VYYz5j .*/\ʋp1{0Qh44ID3%%%D ĉlxٙ;熠OsgWx b?~GynrssX4 TЀJZFNkV_߳]T\%::Z'^:֞Qjaa!WUUQ%ez!h|ui]]]x# cݾ2BKˊEBO@h0Lд:ZIcgUTyDn71.t&'Otl%ޕTVVΘq@5Ax7&:^⩵uֿ6n5h 2kx@hN(mB9$R5.4 D@hS||ʃ\7%= $`A;<Lefffww7mmmO>1@' xf#,ҫZ+~fYjt7u!6:it' &&2A<͖2Z! `0$rBkz{{%f"9[^ ᧯8McccZph4AEM<7xȚMl^Ç/>H ?B. Xn9l*+/ݲdn"7&` 6h,q:B+'  D@hQRRBوZ9dgggiooųf9?? 8/--MK@cE AKihHHT9-K뼅y @h@hy 4^T%ި j D@IxB)!bVOս߃NOyGي'pN~ݵD64gӕkgdXY-4XvZIut"4op@h5A nF`PWdvIIG-Q{="4N=JKKC\$߇֬?ln`2>@ibyiVG@h[QQQO<s? Hۙ"xFP(َ0ZHzɀ3n zRI,F$6!jBBO67\ YYYMMMW._>#ѻa---999- O<.U.GYYYmm-@4.C:zdd?O$1D[=ɥ]C%hm hooEk'FGG_|qVss3bi@dc`d@=G& ]TTD<=#))x0"=22[Yե(A$iDE    -Bc&jD-TyGQK_BO Ivvȟ~1 TTD^ļݸ;fgwۖOFp ]]]$nXs3"~0̑?1cQhtgOfFD|,1*yB>`cc㫯}y}WoDԺ4MLkOYۂϼLGq/=S~~~SS  ->-`r"ly2{hhZsЀ *((9{Stu=n܈'%Kd<\~>~[ق(m_{' 'NY)lE9V?ZT"+篌_kϡ=M?^3=z-m:"M@d8eee$K as@h@h@h  fX.כ'MLLR_?D0v=<sgQQ>8 %ϸB#nG[g팵`@h@I}vFwYMEeyF ~g@.FF+{zzܺeiV׃ƇqvŦ7m6,?Ar?-?~Q~%}.Gh2s\Э[~Ome_@h@hDhRfRy9NLeYY)Z    zr[ovb6-"}0ou8BGDD ߂RTY[Zcּ{?ZPsp3Z|TLΞ=Bj\Tt%A,Oe[wZxЀZ zcKSUEY~a~x6%j SuǛ21]mJqZZ-N[$#4t?)ZpP<(h.Slp*GGܳúukͼee;רĵkzIIITצ\0Bh$rBBkHwu673$^A+&&+"vyz,X.&VA|~Qhy:鲋773:D?]`g q1̔6gJJW^Q[[G|A5x BBO.%&&J+< N07().^<~QKK GF&<\Km\࠻rj^BBBjE$;;kO?zb-)%m92 iɛ` Z[[mu%8)O;#*UӻrlS<[@tBBB[tYI@0\}ЀЀ`0 4DA|YfFb'imhh(#V,'ԾQH,[{ҡwnjjj҂LfA,] :&*pE 6['9HC BBj![m4Rrrĉ244DȈl/-rkͳA~ug"<ջ3~ 4 4 4 4   b~q6 +GsKu)4pvZ˯%%%DE0'&I"iz쫯ů,tMM 4 4 4 4  =i3Hf)-PMZnjj*--&ǚOsㆄjwnlYq<@?##Gڢ@h@h@h@h0zjddHwnS===V⡡dV)-9SǏ8<ѻ19) 4 4 4   R B#Rj?Ao\ۑ[DBb@ BB$" yyyv288(A]] /8aX7?} I& kA`0 4 4_L&forqbs&2/^GGG~~J2B߃cӤ\<-m AĵkGG +bXKڱDZh O  AJ>J^nTkɐ![Z"uBWuu ۀ @h0<%JI2Le(0W|0gkoe] BК}Bkjjj]AS1̛se @h 4 O)Z։Ү%HݐBZe-ZgB`@h@h'==]5,qvo@h 4 >\h-SQQQ[[aiF87 4 BB\eq455Q(i*((?7[cùsys=CA`0 z"Z$q@ajo4{@ @h0Zڍ_PJ.@͌ CǎK|ʰdVdd.\7W; 4 4 4 =E=c͚3gۯYJDz(Ϻ:: mٳ?d[^~aozӾ8%tX˩r}v9iZi&rR+-muSҔ-ڤGNƀ9b;>E  Q:`>nz_žOOyWƿxn&O7I2ᏦGZBnUiUyW׮]l\{-_–O˦Ϝ~ONoņt{[+jqV_ٳ_^t?/6ܲ-Yh -ABw -%ZBOvDmkK3N{c>\hͯ[b3mڼ{.: w}=ņ[Oϝ$t9s\6ݙ;WO̶&zSZBKhZ$&ABwoB'іwOJpEH롙pc>+% AIh -AB MB$4Hh % -$ЀZBKhHh +O}?5W#ǁIBKh =|dw=8rHm>$ =QC @ג$Gy#qIh OdB{wHhR\EZBKh╏U$MBꄮ]gtVg$%$4^5q/Hh - M&tKv>Ukv6u^gRݮ5?s3YԹ /~ι׹\:W{sDB -yBW i50u.vu6<՞y&ϽhOKh %4ݛ ?dduO_c whnrnAB -鐄rlu6dXwk~]F!-MBtiB7Y6$tI Xe׮O?owp wpff&O@Bom.ѵKw37fGcEH;T[;W͌M nm,U8&̬#ExНknKlrn~e1_~]ZͧFÝ.*7KDd<3=c@$2;Н뫅dt$q%]I\J&''&'ӓX6ebx(ϖeLL6Ɇ|)fTTEi2顩p.%pxo"~K%%Kx5_ AR].ţ⑋h7o#"C ^(9?4i^M0W!-nmnpE npp.tJ2qej8F>6z}<(9(=_<ɡ8=f" gn:(᠝)/`nfd.?2O;L'Sl&>=wL:ϑpzlhb|h|t dz"\٩ꭍBucΌF"GϥY?^ =|/? 6K<l?}ӧgE×<{< <{lEyɋk^W< 7P*dϊ7b)Ki 7q~o[`|\:N^3t #|t y#خ8TE# |y{ w:OpxX m@B@BKh$@BDB:u{^s~ ]ۜ@BwЕso杕'y|;kBIH'?|=ϟZ&Sv'|g[~ <#}T ]@,{'~ZNv^ -g/vVn͓|geY|%4;V|s+߳_?vl?Sm> `sx[BmZ­ -hB~mvv9>˯ Ce۶=}YhNL]N­'mvG)@w *e۶ܹ+8C9߽o ]1?uނm6 pF $@W$tdB& 7X(oܻ' endstream endobj 673 0 obj << /Type /XObject /Subtype /Image /Width 961 /Height 700 /BitsPerComponent 8 /ColorSpace /DeviceGray /Length 12169 /Filter /FlateDecode >> stream xxM77Ek,EkUtmjѡjEvQJі j3f"!y='7Khriν{{ν97:|S Tx*B`Mt#+ ,x?56NVH\Q:XANӣt8F'%"F$*uP_ήt?|'烂EW,g<(9׺^䍔/K{C +jP~yIM&*rt<ĕ8^=}y*]zIXj XGk2T婞3 ~y+~7'+B{cJo|܅VUd>6%(>>hTFU94PƾnޠKrS68Ϭ'T wƲA/t>8Y=;Wf})0뫇/U$gyr 5Q<;/GjV _7arbr7E9[p[TrBBf,\M6  , XY wgޯіs^0qӓU#Cfd7?mc7.k+W9f 4O},W8%6zmul4\`F?\=$s \S,7ȷu]n0%\^q)j}e[sK|sxX KJŎȱG=JE\I?r}qP5[m^٬Tn{ψ 0u I6ԍƜɪ_T_cU7䨎YJpvZɩWzEGz]8*1"zeC^CN0)X*RvEpTF6BVz#L\8ܨ?Ğ b!+em׽3 YfJ!*ڲWqcDCU>T\J =zpo˱?r2>LIfY?Kȯּ}Oמ@cC7w_;  \wKpxe^^yn]{U;x䤈c?-/;Ȏ~%pBs(Sv `ul~ru‘{&bH5a&>{|fn_zurs@ӧ˛jgx7K2cjU(UT(5DZ/&zTCמpyY_alȝOJȇB{ 5v:/scKFyY! fj zAv{i4Q>W3Ykxk Wh#~/4kS㲿UW%Vƅ4߫rM]l93cm$%T֏)u֦u_ijK/3TYrsm`FK9 l3-!f"|h[ḳM(Gnt>,[(gQ \ly\ 20nt l\YzU| 0sn!z-vRlXLy^/Ś|X6zD{#?^&Cgi6 b6HiaNz3cjn?-=UNa&F9|ƣ*0<νKjj}Ŝ~'P1SzMP\t3Ϳj2AGZfM7FG&'վ judtf먫uW4OM$5+O 2p:}fϚa"<Υ 湸Laa"ΝX;O#gwkcIlֳ_yO7SΜ}Uc6FY{JVd.!` qr}u_g)k~bVJp1 8\R*2+?t01EM@y&嬯vap-k{['Y3%;ˣEr^҅lQ &rRݤxmiU¹]=/_H}#t٭nbs0g3+`3g֧M?Ӭ/O^f~zpфAVnח}bYs爵OomΥ%?L'FҀKOf^kmӹL.OB\a2[M_ 4Se“RDy]@usvg$߳5=?Qܚr@:ϼp5{E&/Ӊ_?Ϙnӿlo>k$?f?ԫDϛe Onn^)p ykr#wom$ opw{O$K:vl4X6ٜ;p#=ײ%\CF{[1*n< 5]vҀݖ:P^t d^;TXY2Awle$KWxUqn:uϛ`ӻyQ']_/T#r㖀=SN 6{;lc'7OR΀tc7zdHLJ}(]UpLi5ETf]m̞Q6i^+C5d!(ˢ{ r,CkU'&7Rctuds^Zo}}fyߗ^ |TsWeOwDs^np[E#cuҀU=rdwOfj[ ~sVZ1 zhWN;wC<8vɱmQ1fUm*$`Uz:`*jgI13";Xi̐ 7{_.m'1Lzpxٷ!ji4`wq*1Fd9^Ho뷨΀ϋN16G }b#mYtL.1떀x9bUQrqw|}K2~i#W|ٽIi2jnzYo<cT¯<r|M?'KӾy.Yɪ.\~hi0u5',[ZJ?x{σQ/r }) ZOog>լ& ? /g}W73I{fґϨ~U?SJ-' vy'Iƶe)هrq{>ȭw$vk1vW [rs?3u>-WI~ֳMrűK'(P3wAg{JdJ{zU/Yqr={D~UϼYϸkh֨€#%)g^#r=om4w!Wya^sg*Rfkj6捖g-[E'[[!k_h }pg^-o|< ,۵txgxW˻I[_(tj^̛hZ4uW~Zڢ,XBS痢m>/-P0*>!)kI[ia)7";}뤱EZXv,Gx4D^J u:ce- -i`!J&ûx u,S;b2 _oub4@p* ouwZARmOweI}y%~ZXUun~>YК[dO qލ6&-Pɣia)]s#»qIOozsFZX2{i7Rx4@ch:}'9BTmK&A5@0@ 0@0@ 0&`0@ 0&`0@ 0 &`0@ 0 &`  `  `L0@ `L0@ `W wn~@\9u-L" r_ ʀ}n,Xm0!Ժ8u}r~xm|aFa-1m|g|\8p1*2^?gBB:~WB+ȩ'pOǷoժWF)Myu/o 07L%UVYaQ|5xV)#::5\I#5(Q c0buI9| IUkU/nmVi ].|!{xNmJuMW5x&pـ3usQ}Ѳ9/;  &` H+7mn ƀK75ѮNA7R:S=n8o)F@*TS6G_u q)+7d xP`!{܅d )wC6z_]pm`O{f ` 4:]ir<0] *[mzn6ǂÇVɩ1D.eQ8NMB@J~>D9zR~qе0벷kRկ-0ձKE$Rl =M +: )jcա.Gϒ\{^F_ a =T\,J@JSo ԁ@{/topuj y {kD w "m 8c\UOzD?;ْ\x}D:r`q- \^קX5ˮO3gXGù"Wy|㵯~u'*ҿ;u鈗 d|DATpI ̧<r'` * pXY jelHs`w0@ 0 &`  ` 0 &`  ` 0L `  ` 0L `  ` `@0@ 0@0@ 0&`0@ 0&`0@ 0Cg7A{ٲn`\$2Rt]_Gf0p0_9 `27lA-2 @/>L\g7SJ_*C+#j#ϛ$ U )f׋R,ji~nW\bȅ1uoR;\ރ3:[K)}B.j!>6r)e?K(ב Rzp|]ҹD~ 6VAT =ىbWȘjv[s&yB.𗋼H=G.mj[Zg pWȳSizkfA1{ }6|_f&9 }ABN?j9*6';칺,S>3Oa7[ʎמ88э:QJyѰZJD WXyt>E 9?ȑAȕ3ZYu&p{`  ` 0 c%ORg[M0InR:}SeOR,\MK9YnV7%sp=V=sCO/@r\iś"=[6_Rpg+HVHUZeߪBOIDMܚ6v⽦̑S)py^Wy[%c8 sߦ/COQ-@^^Uh2Fh*CZDL`f~g2pT]j&ͭ3Ǩ=:jx}ߢr0"u՗GMd\~+8Lq6p?y8S'uv>pkio.(/=+ئr$6 J礊o̤Jpf.(~L[WMeLrw\$`ujwDvks~]p.3,fτz؇xL<.<ΏRQ@XsRL=8;27hvY΀Dl̬pp ? vE(}ъ.wkcgbGdq holקz]Dx({YqF$V[*ܮ hjuRC<+1G7uWZLբ5,36^Uk[*O3Y[ )RWyVjILfT{^ .00瞢 lpU髞zOR sn!gtL7Ìm[ @`ѓdyHͅuK?g)_jDk Bj~fpbU &k耟AoS9%.pG? @ϢKz*"09cv}`5.)4Ѿ 62;vDþ&R]Q=Xd!slZj)Kv1׏?vqU2e(;Y"J[_%On6Ks(c=mO>[k*oKru&|x[K?U:#^ᣔ@lMwOKYk`j_|jtjJx=AثmMf#ٜmB!6%t^zA0,#:d3:6/o%s\ʇn x.Ӽ?Xq}E0,臜+Ǡ\?W)]"&͕*=_K'췑&ʜMN;U"B[*[~HCΛ= ](9ófN#Jc9#7ܭ};ea3_5?ui%Ota}D qUh>juTKkk粵? ,ςsYh_j[=^h ʮoX0@n Jj9\9` 0 &`  ` 0 &`  ` 0L `  ` 0L `  `L `  `L0@0@ 0&`0@ 0&`0@ 0 &`0@ 0 &`  ` 0 &`  ` 0]0@ 0 &`  ` 0 &`  ` 0L `  ` 0L `  `L `  `L `  `L0@ `L0@1kQ[.yqQMHـ.ExDzׂU=༸[z Bϒjˢ3י;UGpI@ NSk͉ͬI/%`.&iyyA;rӚ.+X 8[9Fd\wQL91>)>]D&)Kَů^lum$ni@F)$yP;:|ZJ2$!RGKWV7jp[H+_rԱ!K̅]unWw&Yx#`嘬ȓJ5wvs'Vqe]1brX) pUa5;K QS.]p[ig.˫n,=m[ Dٮ,2_c>qcHKPJG3L8ü3Fp6o7UT&`>!a9qR%:*y wSyw*\ˢ<0uroy}CBize]EZz')4&H}ȫOȫ JI'd^w=9S;I]x ` 0Lǀ۸/Q H ȹM_{OD~X \->#).fHISiz\mli5lS47f[5a YkJ N+9-$zZ7"*gʌٴ=t71x\p\"*9,8Jo6+ٕzby!r5sHT _.u#^|J@rLHS̭T:`(#Uk$R#RcJy~'2O}T GQ Z-"#Ws'Ͱ>VTdxp+`s4J&NjZ[G&M$)c Hƀ[QUy-y['y1&` \R֘bTVR]n âH@ Xvqy'sB 5 &`  ` 0 &`  ` 0L `  ` 0L `  ` `@0@ 0@0@ 0&`0@ 0&`0@ 0 &`0@ 0 &`&`0@ 0 &`0@ 0 &`  ` 0 &`  ` 0L `  ` 0L `  ` 0L `  `L `  `L0@ `L0@ `&`0@ 0 &`0@ 0 &`U3ɗ endstream endobj 677 0 obj << /Length 2605 /Filter /FlateDecode >> stream x]}&YEJ %^emNI] gIvчb59C= ^: re]M&4Ag"up ކ]%2խJfs>ں/?%h77_xY2TX2 6Ǜ?` o)tn1MU͟o$")NvVZeao~X$,ڲXW(ADZM4I%bLGgf7X[Zw(i/L|@-$d_FVUFZ,J`!Մ ۅrfG&c߉#6_ւ> =dwxboC97t30уU[pud@҅Φ:>J]BPq-Ck0@h謩DMSv뉯0[mHސv_n.9JC>adZ7bI!wqxSald\nt˞V(maH2 мxpjԃ"P0l ױ D Ej*7 SI!q( 9#Re}/>%4<`m_Zћ] ( )筘u겔Zi3vٖd Icۆ rh [}Qo:(3>{Ce]3OڱW?ܷeVXiW.Eih֏ ۈTG[3%D VP0S9Y/F/#I>1j)͍赌!f,04+;=w vgB޼ !Go EADjl,sE:hm&1G@c>$_}KMd`P'RAsg~3~fc 7E;(O8TO֝'Tŋ7{ѝ%sNrVB&Ob^IVϐudYBfO4-?v=CڏK W}a?߲A9d%B?#ޥHiY$D&Yc_O)uZDYpDd74{ BאNI,_@b(P o endstream endobj 674 0 obj << /Type /XObject /Subtype /Image /Width 853 /Height 601 /BitsPerComponent 8 /ColorSpace /DeviceRGB /Length 50554 /Filter /FlateDecode >> stream xXZUpuu[TPd=@{+*@E[{\ * ݖ2/ ґ&mE}i9I yy𚛛CAa"LB.aJ:?mI' S 7Pj V', 'I#/E։bᐃH& ƆAejH1D.Ayr FEXA袀T#(a ꢧ<ؿZ/K ' 9G-;uD.ߪP`YQ(1$`HZ 1}EX;*%*YՄZuU1QY?"dx(dob҅"̋)%OD_?(cOWsI Z2!~OF;PgV'@(jD"SHܯXE\-3-,Ta`qcB"(*ifdHZ kL$ںgB""'4$dAWW gu(bVc@Gۂ1Ӑ{^z߻/<%JI,qb$H`6M{ c3Yi;+ԱB͚ʗojUGEG}T7%[k{vmٰnՊe-|<;wlsoa~_OѿbS%T'*_1~% _T˯!2 Ĭ4Afn*CN '>A>s+_H2vtsg{^+`sHدQ FfB|OڽjłO싌x%: P@ PJv $&Zwy8rxwb| OJ~`h.h0 H (7٭H/ȋ ?~-+;]1+!qgaڻιŌbegw~^7> >chjࠛ,fRLj]_sM u'k58w2@ @@H rIne>}DϤA_ aF;%9G1Qć0@l;~,^#?rf Y۷}oq1|.l;vd/3;[7.ؾ)']JHgdpB>G((^q9+*\.eqycrx0ÆWtT]M.?g7v"oCB(ԆH*|cD6xGDܛc#ҎhL?Խ)VDDb@D ~Bi׵AsHE6BU8(T$S0;.!TxFn߾FTc=ނfx{{*w`Kij3_pF&{h/<m[F˖(>X|tz67y9\F+]3ZjǞ׵De? tpeK**bMrYaIɑ<ᐊEoV\O.ȥ(9.1S1I␆D #+M*( C).$E& ~IpuNF !X4 $4){wt)҃ol$d0b7nX%*<56qQYN]GhHvϰcz?*O&̠_Ƶ:ЗDlX4!IgdLoO?ў~ J'uw3 *bi g{@tBe#^ًr^!.YDzWZsg OW W~Ogfгև_he WHD&~;/&%`ëϧ%+Vek"Vu9 Udo\a#m*Dl$X`]b],3Xhvăr^Ehڅu!6XFXrt!3tG71DBCqP|.|xѤiS:J=[虰%Rk6b`ȽpC=Zto<ų3h#+.:v0߳}wa#?}6P8~2!>Z_߿wט(!/WzãDu>k.br$ AGWݏD!Wc=ׯCv]ggm6ˆjo|OLVbE0e+=}g/$s{߼~M̿(D!yb$/x9HG?>r>~h>|,zDhvn#c'4HNjAeµMŞ*kV4~Eh=-aS_YOG>] ;w;6Pc^t7APz")\FƐ3zR~ff@|& S<8lY>xu>5{l$Gb%uGtNRD CCLa;H/xG]rK|?]S]9+(#%i$>-|(ˇ!M}0O[|] TFTE N2etE^1+!kg_ X  f@и±BDrـo8|pg`ߞ7o\NOO,Iqla:b 42mF9%fMW hת+2. L«F.k\Si[{MӏUrDR Ĭ4ѫBAv][UkH6bL.a0b)H%c0ht>'#G(J( 4O YhJ껩`q ;Ɂ|s#v Y'2:;|?(]ď\1+1~gJ?R /<#3?,~Yi1;SGaa@@% RA2xD K:N #g3o3pD0OT/[Ga 2PXt fv:K #ʤMK2,xMdI4%@!!+#!dӒ(Y@VHV"Md; 3ҁ8 HQw B*|MMMM$ /@rR4B"^D%$}R# !{$J7 AD|A1;_p뻯b#D_LB|67џD!F>_+xIȗ'"^|,(|x)!;{CBއ=yx oAf> {S7}x/(^^_>Gxv_{KAAA=@K EQP@,DJNaAJzAQOYA(TPP**DKN}B(R*YE oa9HAP/ RG!. >!URA(j/ T+ (]A@Y=AUB3HtP}%UMjX FkdvF8| yMDP|jR9 %UZ`RS# . T9L{FH5*4&#H- HC(-6 +ఀLD?t Q/FhRz*t6¡Faf(AAn"=xl"_p@GH wްeȝ>zU:.jBNe)5Q5Q74 aCǤ~a?GXKK 0X OyˢEc ֌.z K3'3Qp(\B X iLVs_;#F߰uؐ[ n5gg?ٗ@G%IoŢFؠ6HL C4Fr94%{&=>J( ?E0l2pbCǂ6P|Ш(Z(?vOR“/5rV4CJ G?B/F[OiA[1EnIhDVk7?M&,K@2T6h څC!v3tʞ)}Wl'4'~T=H%%7h,4 B5C()7|^'34xeW Q o[Nʅj3PJcҺ)-t GDhqꅃiW:$zbkzȧdzPAm!^c$! i\4G5=|g)k-p%~9gٚ.m#K` j@Gq韊!ATT]/tB2X1ubV>(~AÅB t.TT%dmUnY-icUyGu}Ɠhޅj0I1-C&UlBŌ`-DF2pjk#!;ahYr61(W0:56(2!&1_\ xfazDd}x>QTAcFLU ަLd? )WyFgZ#`z f{QPGAALN,!d{|3,n?,)&i~'M9<+9E((Q&ڽR:L~Oр3OWAԍ lb5 GPv8ǘ|+*G)$gؠBh7C䐰%n(6 ~a9AseEJQ(壠􏂢Xۈ $i![lEKcgS?C~& g?OvBRhE,>W 0W 5W LEVY!B|1 e!iil ixJ`+ӣd? x t!R :93h6!,Z08ȓ"#i|`S'A\#C(ߣQ^nn99p%v>7'???//O,do]H^^@rrV` Y.o6\N,"3fpNI<ww׮]ڵkӾ}ݭ3 R%"L*Rr)sbp$w,~~n[E@Jl[$ W,fק2 <1Jj.cf7 {b\ʑ>2:ez*tE P"}i %qbL[Bu_"ϝ;Wn(=m5ZA>i]T:4Pj"Xf`o Zݼ}o5Zl޽{g83f@ gcoVkLw6͚7ջ-Z(T̙3M 2~HH~`# 4'4 d19B 0NfM;vأGkknݺuҥ]6#Èz3AJO ?J@@I\'Ok׮߿Gp[PHS`K 2tơl^9'8-3>$[$\>g;a/8l:u˗/ׯ_VV\Gfw/5$<#|C.0ÇO<}NܹsoܸDHJ*?w@<0(((a%bbAo0@쪟=1t T/^Va&(k7*_rVʖ^ڨW?,ɑf𭜜oof͚51 <[2ݽs[>ڵ&OuC4miLk԰k( r 5LL3h۴m }XVmt7ld7x.TijgϮ^87R.X6^|ٺܹs9rFPkA6" EGńQ= ܹs˖-7oަMݻw[HY-Lg98é׬VMzom7Ug&uք.}߿/KW\E+nty q76uA f- &%D|0)H+ Ǚ,:WsKF5ѣ׆nXп`<&ձXsm j׮ۻF [tػsU֭~xo#42= /Qi`ȝK}H'I)6U/˥efr4=mmGZ Q\f?>͚OsDaZBJ6noպM~Pb9~BoX-b-W^0@… '\7h.74qB.5Uˌs 4ؾw' ]LSNdb]q?Nτ4mika [˔)2888eeJ2(%$Ԧi k˗/ǸAsrϞ=׬Ym{8*#ٶMk;;N:5iҤvu֭quʥ܏%˼,--7ouyό}.x w` 6 nʕ]96y{%۷yY #XƵ2QLb;6&D( U*෈{rHпoCGTgG6,2hĿ2o_ms%+V3g9:ܶN@iv{eCr@ˑ[x:/&{tz {.09E›7oJĄct9:& ? Ms}vO:.Zϛ6_LWNz/Nk:{urWE/yvlQݿ^^.!{럮$W^gNܩ^fͬ(jժղeK$2eʢEt'u)jc E2hPl[S;B@?jTq0@>Z8+(u9=N~ B+egZ;/l9BBӐb_6 nnU{iv^rh΋[6eۧQY+VD9yh??fCPL^)ócg\e֯b4jԎ ~Ed5k;wN*ϸ vq#޺~ 6_LWUF[LTF&`L&8<==X)swJ5ΝЫZ[X!GtVCQ7o\6jMBeMTIGTFV:rhD4':>V4N)ٱ{;wn`o&MZ`u|}}]]b7(a:8[ni}^G3u8c݆6]7s/Β>+l.<7e b}:9O&uIVN"<9D&޸qL?||**XRZ#G(`0l] <7s-,P,cM/pMA~x׻'ˆzϒT"8tП3raS@޵t4n4l'bohvإMQGA+-\⁺_҇-5,EdZrL*"@c륳/h,b'q߼ycaa28N僃 R¤ދf6?5f3WCNq+XYw]M3WMx<={oݱEO>>ZJʕ,˽_<3Qg_kiР!F=yСp>͛v;v޸~ >BX8XU/ W\pae+XF= +$&&F0ƧC". q^c=TL;{3hb!VP# M4@e"mIh\- YxIl|"(io{HիWTTdK}?zDe[V͏ofp?R_Ao_ɤI{ nw=;.XUE_\fʖ-Wz-4qFuZgΜQuZ܎[>Eo ر7)(~!B`Y$5!JSQ/umwܺ{/.ԫ5w9R5{nzT‡:E9CWuE"%JxM׮bh˯96w/vnR'/^I켼'OKײؒkZ5 |bP73F)imvܹrw4hШQN s۶mÆїRs) e٥!jmY/9?lX}߾}Z Nv?,?QmȤZХ6#⇆™T~Kصg_Z`%H( ݳsY_pm-x_ح7:#9:: ^EUvϪQ0ʪ沅zy[J%B]񺌴xt;+_.HN KiΑ/sĴa7R2NKss9Ƴ+DY:| q΍-iղEYڞ-q|ٳ5ihܤQkծٰQTZz +teKFzI)ɩhta#=:~NUNECB ֿ+VVXF\ث- ,Nz_BB·ox|އNWG/tIX]d,qtVv<}ٙC;u8iҤ3f,^lʼnNK{lj&vDUf[EZoͭ+,g wtiZI[r2wnyYd{ձ#`#ǑՓ?kJ-Ih(SXs6+Ks9\sn&MF iӦAAAH9=|_hn3PKDGp4$m\C~3/,rhjDVo)5k9Z6] E_?}m"?'M!fy桲w1XrA… W?hkԿD 7#DoώT?.S^=Q{x -ĩFٵkqptJ3{t*v?0Bù  C>}FDy{qrLOl#h[w۪yuйq۠A}$.1o(K'!㾝d2@ P fZϋ׮^޻~ξ y9յW;߻;һ5!NǷV +V-WLiwN:k.9e?׾@uuKOi1]om~L?&G+NM"s63Sۃ`_BP\zeVYݱcZ`xsm&'*OK)ӜgM>=3w&byT[WΝ6t7f=ҫ0Sns'8~*U}FֹY7a:ש'O^ĩ}mی= O~D+sh*OGp̆:f-ӸrsU?{IXS6f_PPД)Sf͚ Z=4?HWWׯ_5*88_\[6;q1~^<9uf rX]jRWZyeTƲLNG{٠OSRa#DT c9Vu xT<4Vp-$0O?4ޜQ;8'3'',Q[ 6t1+j/-0 lpʕ*Z?/WcnE~>umj K3yyB.~Yx-(Gލ_^=mAun;q`w2K P<=~%2VJfqWй'\VGuڻ#5 8pҥK'''tD1HMˌ׼+ fcCkZO?̬TW7w|HSqda,qJǫyA++}{w +.ZUKvwl_FX0{\AVsi&,0LKdӧObb" 6bRk5R;6j(KM3ݷx>F$͛pEԴiVZΞ1s{F <ქN'&nuڍ+QQQ8GE̤(7'O0@5;q|%5X{t%K0OozO$`B6Qj3gw ,ӪCf_DBX-=p5];HC\KGG'H?5mݸQ ++Wn479 p%j5F !IT> JpT;?i K0XV=cN٭1F,s?#mXy8{=[V}ʿEs|pPA!,:ZQ֬Y_k>?˔)p<`Ĉͷ<3lٲ]]paĈ)))ٸ'f͚>|r'SV\ZR~}|psZ~Ο*__չ|S631DG* N @nĪ7D*[2ލklU5@BXmLwՒp?>ްthee5eʔӧCe" ?Q8}\;ty8YWsp߼i:Oh۶58mc=i6L37glţxOW+Oϕsz+8c2(bʸMZ@$GΉDO-=J(((~+YIɉ1$]aSA-:%T((((~yجxRMeӓJAAQ􏂂? J((((((((((􏂂dFb0G򙂂Ǡ%{NU>SPP*@ųÝsԑ˹"a6TgJE oa!IHK<ypCդoey|.<~PvkWc;zj¹_.)!TR;oֻ㿘4b8 *bNesALNTIiӵ_?(0qs#Αo`۳^,Y"JT3E,!78&u֯U#0`KjW41 `D;w4y#3߿`#&0qDGgHbɻv2, pO> 8QQQ&3@:W%:!RDr:.݆oa-?؈= yt^JVCN~МG2 :?gHu6]qxU+Qg&ɉ_6oŦo]ܺymG[F~x_g2Fȷc|s< 6`0t;F+ΉitSl==fj*$/M|>+VS.|)/-0(XiD0h)8ef'<$!13enkVnV"j9)ߚ5ohiv8898]rU_zi֢޽{%bqY< fyd+MnM:ƵI QR cxy{YZZXn>Ƽh+_>GNXf%a$rrdl{]"+PNEi>e,6| 6Bpה2P://F-k.Xr-3ֿB)rJ }۷oǏѱ )))t:ѡwN+U@-aeFzoz s~|$"޿cŬRF|n(p;;OpPxitWhIhwi)O:m%kTI&ye8>{4^^+ H$K?___M_"}[T̿rkQ֨Q߆-kХG}.;Ӻ5۱I۞:wmأ}-iRO)Ixj4բ˂:`cqK}O(6_ #-Vm=w 8[w(>b!tb Wf2)ʥsf{,?kLwp7.q`Nwd$ ax\:yʤjNierF7i =Jb$ܧLB]>^~,[n}\G5j԰fZ/By]kxA|VG{yhܵ3.o_Z߮ Kb̸wu{Ĥǽfh plovǦqioIi_/e~<q$#Xs 2 NhK|[- }H3=m 8]MtGI!۷CBBM{rݲR~jѿհOd=?7ybkNjƍ(slU7?6A?c;T_nH?Կv8G޻{ݩXT֭R]ֶ}+5{f99-ן9}49ˮ@<0=%ӦN < ,1 :e/o/[tvM{…mذA.Joљ/$3'lRֽ4yW s"Ǵ3VXnr<'q߼ySLT 1 /7E hC5+\ij.ի;]wަMCl[WJzuLӕJp-[bo 63M+O$)Ka5ْ/X>n7C~vpgpȃKgM Ąc}w>(; NQEd9?x } F&5&bޢَ}N㏂Ǥ!oW2N{-%7Pi12S1?4)' <Q3p8j^:IֹtÒ8UW/,0a¤IRnm%3Dx*?u}PZg'lzWݪmכhu(iM\w^ˠIǏēV `IXOBBssrE*xz2N?;P ݰ~ i捋q> TiQ#vBvXX^55+}4mEDDHқ0 q?Uv:W"##&?YZ}e6lh'JHbS$R6d5 :L(?)3 :TaN1\j2P Ϣ0O0AKT2hW/OOOMKNNVu?H*\3?V6^~;vtov/;b4=ګ ]wea(`4\o-$%+t3fJ}FYItNJF}d<ۑ|kMyߎ͈9=ۍq`O<1?xtG1 f6mAIҡS%f߬4z~;_l$_ItǏ/_ܤ7"O$G4(23h[^NM/9}a!篙/"mex+!0=6=k֬/_zxx0gr<ٸT.hQ1]ՂqRBF&QݴW&zwֲQ I wyݘ-x6>Ⱥt25rgoyaJ9]|Vݦm9G:}ɒ8 V.\L"޿4+?Gz l_pRjm'qÄ2e,޼y3_/[T>U}\012EO9'&&*:NsIЏk?~&6Me6\+H>1+O*l߾=%~ժUEn" zQQQGјLT<_uyРZ{*ݻ[Mʵ+SVsի= }&Yd韣u˗Qr.^tiCro!.͸n,V:/"EOD=zw@yDJ'\F6?ϴ}iG0R^zN&uOpA]޾e7o*Xcf W'/pZIo nnn>>>V2RtڬQ"1!.ߩ6G~٩VEBG1fDQ.}G'5t`ߏ61ΝulٳnJ=*XE j+mݻ ۷; UEE|>EW,UkTNgs.O $ ڵCǥKy 1ҿ_C WӁPbӐ cpw0' ^z yc}֘BIn0/&KT+e+hSv `&#-KEH91Igc|dd"# ^&NjwqG}UZm7M7VDžOKAy=Ğ#޺Ϻ\Q> KvFX*رcP?~H[!]R uer@f-㰲o8H 53|(V`wT^4|$ٻckm޼yhzGv…zg5^ek7gZurݪWu+Կoˆ`п%2d٨M w`;s&uO+O,dR6̟8v\&8ibM6]&a'1iҤήMP:$DS KX 3G剥!!O$dEDDVbE(࿏2dZ#5%:ݦ꟟_\\N e qQfrd2b5r\ԧ0NY4Flo'}C3^ă7GP>=>b0ctx<=h}!/K5 i[=qjvlhϺ9΁KٛT:8Y~;'I`k 41cЊ%[ҐۋpgϞYf/1;H)h<~oY P#?utHyi$ʷދ_EA߿25J;a„5W0:uɓ'E<&oN8 !rJCG|H/YcȥNǷ~sLKo\' G`3h\I@n KW`ص$_S+?%KTLF&$$o---tj#5F?[lQs?.+ u;8_13o2I̘h&[\PDp$&ŸF4F$",,`E44TKuWU{{W;U,ZT{nݯϹ^UČ9I0r4O>ܪϵ^3`8c1]o:ܒ J!gˢ?pek{ڙ3[\9mzQqH:6X}R}wMƌzSX '"?}ᴈ ^Vf\?Y/_|řgVmq*#|<7y/,J^rSϪ ߠDz8}ټzh47'=.xOwZ-9ZO_xꒋ{/]?ܦޟwb,\~duSIdjgyW=V:cS`iO٠#yG_c۱=~Hژ_aѯL>?.} EC%]rvx$bbO~cȇu2-^m_ѸWռY+D݁뛍{ k7\<9[WnqVk}%oym{Z[,[?r{'M1?p'N런+vjʾ{ko^M-$Կm?;=;>:~GMʐ;>9t߼6{]9~i_e7=Om|fƳo(UF}-!{>oΞ W>IҖUU#? ܯڿ91'{<˗zp.Iz'{8]Ow/3 ]S/[gώ7d</о|~t]_Sn{|߾}9o޼AiUW] '~wٲedx7ŌCtǘR6_xYn \U)C]3nf>ԓ|–m>пrӤ'eO`BR{/\p_}m9;:@硣r[j^|RL3p៧MǙkn3`B]Kz%g; SU \طӭN.sx_uw.7>vW|{ܡ*[>j2 SU:<+w~DŽ˯{w=W>o׌>O_r=⭋=u O[=5: 9{/x%y|PJi˾}e>^ڿzThՈ1b[[]:;*$BK??pw}f͚~xUa]2*"~xdt`|+_9mtw}Rlos)]blq@XQ 'HN3?;ksg6>[s~8ڗH؝mZ-oWi7pدup~CTȶڏ0Q_vuǦ[@Z`<8(ܯhUbkk!bgZ5wݍ=E.9+z?ZhgyOrfYmk=B6Zw~xOWۓ[k?Ȼ{ qܖkrӏ!| [qt-ͺY?zlʵSo;gջRdi KPPP~=QiL>+w}dw}_y0Ur0@R?PuApZ\o.4ИogfqȬo/)>1}yj,Bk>o-Y͕(o2 3tZfˈW!4_.8?x7^%9gw>kT%*fY g%gi|khC\U%a}| )/]*Ys{gd57ov.Z0,mT*=~ g_46km:yx>CoOڵk 6ݻYm/&f}Ɗގ7P~aw:MKs4Ri=C[~p}{7v,YM8D.yqې8m+/zǧ:O\y7hmk}s->??w}qeIo}/EOHq9j* -[&flm%u_C'M{y}JT mʭb@9>;{7.b><t.fv}7w&?>շ;^<_Owq'GP׏{:^w蟈޶uGߛ}iawŲc<0O;E{_<7nz1\. mU뿻C Il__xeQ4[fM[[['Xti$?IOGQ4?ޱ-9ly'//{1<ն1Ps<_"~rŵWZL+Nj*Z?GEjqjULwuɾM>cݩ+q׽QX?>/| |-?%9'Lu~7!]6f~y>?kKi]CRoSYFOujSdG^])~? ɻ;,X_l-{f/g-^=΍#V1˃x;G:}_̒wXVWII?C w\uyEt>rOtqmܸQh===_zwO @Hİ6 Jb3gVI֭[ ^{51'T^Z } {3sSD^[m[7*[^m3;~'ckTiwu톚2귎Y kL9{x^n6Ք{'oY nuɛn̩'M6eV2b_VIn^zip馛ČcMɫû{3o~7Θq O_].a3MߢKoC}s؎ W>Pe͈KO^=o̜>h+i߱WO~v'\V^8k T*׿ϯ^X)@/97 vdI.y}1c?9k+ح"Cb7PijHC1}v~A~CU… 3.yޙies /^@qbgq3bsowCn~1 3x/؄jlOxWx̧NN>KY D_9`Lxqw(Zc,\dޝs=̠?qPLs^_MNk ~(|̚oI_?yC.911b#,b3ƹo9/~Qw.;^x%)^| /XlI=З+nsg__~h}{ޢK>ώeg'Ͼ`h^yԥ۟S]ڹhU?V~g) ^$tnm?WC =nj1Mw{(yzl|.x\:3bAA1b|u؂Rv>|EUUY3AKoԟ7 _wVSS#unI2l\ 1gak̙ӮzcA^~]w9V%)5l&I'R&#Iwޖ/_~)t /^Ń" `È8}DĀs 8콕)_ Ӽߙ<}yFe1wuu> S]?un-]n_6Q,;p?}d Fe4~G}d[g6sk֬ihhoţqbbBEWw){oA{T*U߉ď#ngxU)+bIak:zdh"6`bLvw 2]m#@iFm.=="fvV^*ij##flo?6!BG!B?B!B!B!B!$ylʲc%E٥%9e+E*N+U5ZLQ[Re"Zu\kjVn5 Cmc^(bTƠ]QѪ+Ū ,SS%JEXHYS)@|j|+**NT\Qv\|ce%9%GKeg))*<\TxHa'ؗbo^.I\lbBk)<,ve%EGJN,))+=V^vXdV^qDuE2/PJEC՚B(5E` $eI] tb,lUH%PEC W ^n @I@1n^Ԡleۨf(Ɂ¯VieX]WV*V&>lՕye|斗TjU>#~!L}v.Iݎ֎vOg?x:=x\# :}.w_-y:(ڨ[ HSіT{5Q?ℒ}dOu{ݭR$\BR%{\&ZQVV{xqNU+nBRgF1x~wQQo8/}2V177wÆ ˖-[pҥKׯ_4;0EQ gF1X2ˎ9lMdwNȢWBGJN'1-viD"BiD.mlO29eϟ7oYM6J޽>p9)xft:+*,k ӿG1/T7i> 1榛n%ǜ{)o g/g~s%'|饗dտƠ@?2~쥥%Bز֙- 7X`?h̑>R!w8Rm}_aoEƼ/Wb<%L2%>ŋnwj}5k444g޶m\.OϜ) P^O8[/|c O9d&ɜ?u˖-b 5\}gO>MXz͛775NGKhF_{Z4ccQUc`Hҡ1QJ:Z9pw֬Y_?L&ټ%)"W8wф0Α"6aҥ{>ݭөzM7(tnڴD˕/_\ps̡MwtYg[FZB}FޠE&JG5O?r9dM/HzVkWWW?:nd ޞ&|/~,_ž0oku-*.-}mg][[PUU~g^JV[?A!q[B3E֬y3θz34^M"&?KEFSs*y 9ALJrdOh_H8/s%D:0 Z6\V+C$vOssӲe>QՕ/j Um>ۚB33JE$+k˥=;7Jk>@;[ 5vOKqdjo0VX!D񲲒|-++K<#37>to/Y/aH2\t:6m$m˖-EEEbLG?t_(JUόb21ѿa:!_:5o$=.OOwggG{{xhzȉ:Njg59mbƎgy1LL r􏐔k9.cP 7@VQJ5-M&sksKxK)&t?#$ut4Hпa@_CKGG/Կϧ!_Gro,XY#CƜd1<ӨsNd19ju5lԜ(B0-ir@oR"qp? 7W\qEY:N.|>g*E/VQi`<WY;rrr8@bvIUթqp\4@]??p4iҀSX`DfA 4 8sNht[)T~B0<CӇF*T :---Ǐ ,m __цcnokkef7v6KڿAOq:!}"A/@v/ZMfxw/?fX7ؕ#(3齭$s;[mCYQ;z H~Ÿv_c!OF5럃;dL(?Krnm??@z<]??@?4?KZ;7+++I+ $%R,@HJر#'' $!6mG/~^ooho Ah@RRW;ۓ8gfm'WPmΠyC0f1t%Eg~i}~G:edi >fD9ׄH_@?5~+ :c9#?CHXԿ;@HQ$%~t:GFп_dD?O}gGIڿL _OyAH\VuAsSV ?Hr4IAH2ВH?2DBׯRI!D_ ^l>!+ROTAs+2VT&y~X犥^Y55_c(A3c GG4nM*7 !GOz߷}~(wwaY _Mvw}w֭۷oߵk޽{:tرM6_9: Gп+V8qRPhFٜ{%hE߻ljg[t\h9¯|_|+;o%R8@ʂ<(o͚5544L&~mmmb@jժ'|o]G#xVE4d9]ve;p$fI?S4^֯__[[r?!ܯ;}p?;mlhQ;s9tH.{饗}_*һAr_fn>7}AkCYп? ;,?;dWGZ$Q׏(7HI7`x~WyaѪ+I|]- (_/{)S^c-76XZ͎ $hS G7ϕʵ MKG]sǚM_yt1@H2DKQ8?9R%Կv3,?bA?@qBd;ѿz)`Q[&"7xr4Vɖۧw*% &d\#(e5zgh~3JI]HŎ$WvBx/ ׿>As[]RRbG7 !qp/ C 3dGh eꟜ4!շAsCCC uI&,pY&?dt?j9lF)%A X_z2OY733~lfѿvOa 2k7 ƣePa;0 ?RQؑ| ϻq9-R֭ ߷TV)wA؍џߚƵ^4YŒhE/|CK>3Hc/%V|3/O׷pr$C$5H!03RbҠ/++kݜe 1LYA)?~@47#LCп. ?B?S}/? g$ֈVc7~w0);hiE?HaGkP@ԏOv]?߉?jпq<C8 ԿswcЕ: GCh/i3:N/87Tw7rZd[޿owuU4RVڵr/g?}(ASc3Y_땱_݁?қdqo h?/nKAп1oϗ_@o"/{G?@p?2g|Akaf~A3G/c8Ų2NӄGUX\$O׏a^'/..,SB0/*2jѿ^oiiIHGG>oT?PSZQ_TLÔU6:oxK޽ $!&IJ_Ϯ4UngSdd׈gG DR_E'lve%GJ9A2>1,FN^Qz2C?@W}o !y-NYRQv8XEYneynUe2_^]P+/TʔEJEZYZ,ԕie:u6 &mA^VB-itV-)ѨJԪbNYR)T BeLQ[)TWyU^u`r+rs++W,;&Ne9GJsǖgg)):R";\\tHvPQ!YAY?/y$q n@P$5b7vPpIN,:"vhس%%GŎ"PJURU~"W*Qpo]Ҫ".#폯SJ[ ]@g[doO 'EzEN:ٟS@"9%ѿ {$kH_w7{;WLKN_))X /P!;TCԟJd!+m}*blǠ.s @ D=[ e?biiRF )#QݯQb9A@/q}Cп!籸-uңŅK˂W.?!ʓW.WD׷ԊeRLWWӖ5vV1:u뇺L[WQwP]?k~*a]?º~Tu(Q,GAtHp׏~º~u( Q#P<~M_yGTz]?BCU tM tPKr:4@yc* "P qh*}*O|DA-+ |S,u;[|3G/ngig%q.[ۼv~]?|vQ.(q;[ݎ&oB/pgyT =?h#d<ߖ8tZyCt"~t}s9[\:wXyo1eoE./ihHxUٟdr՜bP)JKϑ ViΨymE?~}2 N[}K67 -[p¥K_>;;vڌc:#7׿ϧt:oGBuE.-߅V6հϛ7o}ֺ]vnܸaşwnj9ua-*_@BTy2,Z)1/4NGȐq;KKKe[kjm7jX`4c/gǞfm"ψ%HB7JP= r 1 5`x2d̦-[,ZԤ]sӮ'Ƭ^͛u/)^]?ҕd16ۭ:[dmSҞibO0)FK,/9\;I8 ƥK~5Փ뮛*˖/_9lh34o޴ᬳv Mma>^ C %?mDN G9,qf/bIl4-*,Yg\^ؠ(93~{e]][o%W;fӋ^gqi*g?~ɬqT}drHi}hҩdjvR>u"eW]yӅ•lܸh4Df44Ub~gw֯c͈ 7'Ovz\ ~пxe[Ƥ$mVGJVVN)gyjVюG1fs]u,Y\6ڌ,YuV2455- gJͅ|c~߸Y4HԴlٲÇTU_ u]mA7{\3tOϑfYvˢψq(1ӿAFCUmqN9@ixUa% њI W ^b l9%?`[VV8C =IƮe[tI@b9$,N{CgtmڴIۖ-[Ęva5a6>?wC kV8~?j힞Ύ6nя=߄_hZ}2tF/I5tHBȸEZ ԬDho,ObY#C? #C?4NQÍ֯[_EI,A?@M4>I!d #m{G[W _Re<BȘG\@?@JȠ-4MxD\C?HWRZJI' NӄGտI3X_tw ֿӍnR<h[RO:)8M2>?OaE:2\7|8Ia?Y4yZ?B'jx;ateG^g0A\.gp:;;%j<Ɖ?Z G<ny78SQQ!(⋷ {!_@<=fM}M!M}V _3tHG9LHG7};< #C$Uꤠ@_)tAAk hokS-iBGj9Btc#CA?4!0\G!]?2^_v}q4dH_?B?_ _Bп?z9@ 2HΞ4]|Klݺ $!MMM􏞿Qq4<C o#C4G G! ]?_CCA?CA?#C~B??12ydgg #cdɒ[r$iIq+t !G_#C?@?B?_@?B?CA?d#C4G G! ]?_CCA?CA?#$Aţs]v-'HBF 7p]?!O_}6K=G%Sg6x&@/GG4!4G@!_?_~!<CCA?CA?#C~B??eMg?@?BпT׿pa~H/!_4#C~Bпa_h$G;oJ3g /s G&I?JO=3E/! ro=i#C?@?Bпs> t <?㙿@/G]?!GhEA???@?B? HѮeٍjNӄGUV|CK*W)eUGVj0TtJğ*m]Q_AÈê:xС>V^UW:mFh8lZQ]XпΝvZN.ƹARHz8[0jmf-'hB'jIV8?j&DDVE(./)->ZVrxeynUE^ue*@QS)keu:EZUQhJ5ihRe:m`@:/UZ)RT D-T*kZVȫNTՕ'*r+W |ce*Js4첒%٥٥EGJ.*.,*<$+8́ ?/ܽy$ nD`G="v,?P؉%#bvk=.{NEYNe Da@ ɭ̕KTjyB%K@aENQ(:eZU$(:@AV(}YĠ+7h &mxkDRNY*"U(E5y28*r.U+K\Fe:)C6ﳋl}ZE|^#,6*#۱H˗%iq<""`|]"mtuwi|N5Ez{z{EEN<)ȩL)?) G iOvYO (vepv}%"N( ,'}e&X~)JTN+}%>rYnӠޤ-*/..<\RtD>'ɢXujZ<@!./- tPiZUV]S/iAҪ0hˤ¬. =ݣPQ( y,VvL򒜲b.GMп Ef6Ѡ;/~mF_@2MF;&__0THovőӪj^;3Ϝ>}JU~cѣG6s\W]y4Kֺg6#7&y1J;E2>iٲeSTU_꺚܍7׻-1f~JEdڷ/=Hoܹk׮Ihnȴmg!#L&A#=Ӹok0VXq9%?ض-++Kz}3@z=S%+;;N3//oӦMB޶lRTT$xo3tѿ(5odu~WOwWGGG{{{gGGwWg>3?#D gmr;Έ GHFCCA?R~B??hEA?п?KJU׈U!ChobOLԠhoSvcC3 ɓ=]yUbobv=?$E~EVIgO@z{,J?]-׸\vz@cb> %^-'{.גtz{JMʀ};;;|N>ym+ {L#WM?@蟩Yquql+¤GҤH@`C?@&Vڼ6%iCgjVJuVMSCMbnՄUCG/IVi@&A\#V?i-McuT7Qk_5:>eP{U12 F}EؠB?҃T.G>M3VB'Ԉw?z@/O/_~R՟\.Gh#!+)UUS)\_5??@?z毢tϧ;v?_46xou =W[]]ݍidB24iOCVT)b.6]O/_~R_UUUgg').GSQY6oʊ1RL/b%S?b}ͦR;b1l)]zίp?已gdP t9bKZ舡^?tҿvӇT*I-nCCI'kkk =WTW^^;&<7fSdv [;yȐPI-ܧv';68$Tv mlXb @"[[%۲dɲllIMjE-ZG}9-}:i#BoAֿD"Gb1宿s~_F?~A/PN7;;~ίRg2B*? @?rҿI宿s~_8::Adv\l<[]}TmiE֜xuVѨr_9~J_ww0ohhoqYHF\hҿH$^C@?Htf5NDSPt$#$;ZJᰊy<AAW#afmuddʪ/ B? ?X̝ҥ$;z,U_ P!foo}1~\`|S۲eK³ڴa4*Gzֿ_>jiGTpAz =trK*dEFW9Ldl޾6OcLo$HmpAL:E?ȑר*}D? ?_eWYq<K7e׭ãGQy`:?$F#Rt:v] PyAҿc߲)Gl߾}TIJˊk**gZ?|C@tk? ߊ,.ڵkևoVy`m^UUC n>1@С??Kڿ|~/r:S=O{^W'\WW;00x;ep(}Vu'>S{C@h-XV2ӿeC4L/[o6;;-)ߙOՋ}|8x0JG>_jOCOGe;՛g㯊FDoE[/ 8 jBl-}uˋu _ԝ9U_w\}VϾTW>G? ?ўyG P L8WZcK-QhkZABA)&?RdM}? 㙿؆iM̦vO<ِ_ ? ӓ!K;LM.c#CJP:hȠ(HHFHvB;VVV|=s~ߠH9CPt$#\9@*q_@?o?.+X>S%w̥xUUUDb}gPhݻwl.l]^r_@?MZj;JK|R u~Zjֲ>k0 [̙3v]#t QJOVG1+Gϟ(c~챿{K7g_˰#8s-RH!OA?o<*T?CC?@??;.6>YS}4ӢMkN]l<:?K74u8,Pt$#.4}N?ohnl'cs)(:-Wпh^)f#x-S[yV/,H?# !j9px^evyN9PKy.E@3@dGŀZ>/蟒 %[2ReU_eٴV3ӊ3SU,C,ճfV?m-iA$hWSYC~^!]o9ѿ%5%fVf,l dZ{_vտ\8aIQ8|pk~&"}Xyȇ_"<_;S{\G?//:}._߲d^eky&j/;=?c.ӳãGQy`:?$F#տŅx+拢ϝ?ڒ}m{Xw~ovzL.PW7luܼl1جw,%G':o~.v>Cᕽ{uHwwo/.$bH+Ȓ]ѿuVK>//oYwo]~^ʍvlo{п%M33So/<֛NKJWo m*b__hBp8+x5޲2@)2~ Cпҿeoq*M蟶찘?J37 ӚM\A{~|KS#,f qihɐUY~Q*?&D,$2( ᠧݹn}HﵢA6sLJHFDùrJEBye;T[[-Sp`@?EE>9v{D|k%qlĎ@9DxjKKS<N'&bdb4B?A!+1$k  Y;)Do+b5էjkN3-ڴs>sqユN߾$zC ͈g#qsv})?Jʱ|`gL.۬Ɖ\b ddGKUصk֭mmݻO'h@?ȤHϥ$;$SVxyK;ܡ=IIG?2; . c1w"<49yW_|ṷ|mj**)Կ-˅vJS?\I-( O00^WW OF QtET/S(ςc-$Tͮ~k4oh!gp:vkMz̭f9`K835 d_ ̂l\{4'&Q!$b a!0JT)ql-QKܔp+yaF5&,.ۋ\swnϧu现<;ro߽+=RA 63+IMdS,>I2%+Sy\T^_LHaTSAITA*JxJxF(e;IgBK̔_.:o˖-+ldd 6/g1՗=Me֢0P}7B31]r%{԰\y&_"ˮ \/SiE6_ѧ{?u[mo>Uk<?\ YC~e~ Zs q/>Ǧ߿χP﵊ݺs({F{e:?Dǣ>2>'dOogy`o15=&ޞf >%r﵊}'?e#7 endstream endobj 682 0 obj << /Length 1398 /Filter /FlateDecode >> stream xڍWK6 W(Ml[4fڙ><$9heDU=u}ʊbR ~?pNp~޼o^?y$褁F塳?8^,\5 x ܷՎXvK?TAG _z77$)9#0Ϝj(`?7ob J0%3'!~c<3Դɋ^Y<֡ ^4vlH4j쫒U~]ڢfO /@ew@]i2Iwve AIqLXʢX ǖ<)Up(.p@Q{SszOBPi6mi(ToRg!¿ !8ڄ6/6uCaK: UW F}z@.k|, -K#Dۀ2na܋ÖË; s?<H\".JB?NztTG p1#g3*ˢ)4h 6YDםJqJac`c ȿem-=B^=a="R㕬nSD5jqa &j4W)!JATW-/[U{Z`d~~3;U]#KrnID9J! }a俫\U:|" '1e:9_JA~36]>@~^Iq6B16lm<O{u 60]2ٯTZ5eCWU(䤐HǾLc[~:hB/<,Icb1a*0R endstream endobj 687 0 obj << /Length 1519 /Filter /FlateDecode >> stream xڕWKs8 WhoL16=tnۃl1_ْ}b z'!9zK0Q쭪ɧ/WY{NT{XL_EY,coL^@['四S*T,Q,LR_|'qN$R?@$(Pf~t&ԟOUL_ E( I}Ә ߦBۿ`~lӵ$mL^DE~jlEa(nCLwW2#E7W%̕s%m{f~EeÝDy]`2 3!Lsu~6U޵Zc,yP%3,8%1LAf/̀ҽ]iP( TjEo횂,EE!V ˓S"J7lUAfZCmghɒ}k%tXrHyQ]ikdKI2 U8Mٱ>Mv.mlM ϛr978u[0gH`:>Y]C1#-L@T~B:DGXt?ҰInB;Ӳ,@af*Ub\g&&NCtَC.;p֕@ij-Go ԅaávxmUi \, ("g/ I20fE3DT7 Jv ژS sgqq]qRvDzωk FV(HӔQ"XuY)ۮtn\aP4WCwWsn|(D6G:.v{?0$ u%뱂Z>u.]7v#5ZݚA$tR[1i wUmT*Y uB8W )ߦnvwHw&]^@z [quMBh׮׀^א( 2|}L<_3ŕ;póBtK JގX kzw˫]N{Wǘ{K}'qX[dG+W5{o.`x@{κqxGc= _/Kv0N&f(;4Myӂ>;jjGNξњ??v?WV@WFTH)`ߗY endstream endobj 684 0 obj << /Type /XObject /Subtype /Image /Width 471 /Height 541 /BitsPerComponent 8 /ColorSpace /DeviceRGB /Length 22951 /Filter /FlateDecode >> stream xy՝k?a=3v~L3x/6 ni1j#HEݯt7}վUwWUyrVY񉊬̓'OfԯN<92: •+}nnn+vТ3>3'q[8y毰YmkT*drJ^_ЪJ\ʔi=2(deTXT(3ȋTT F(ɨRa}GE-6T9X``]M)ߪzS24c'*ϬY8o>OC} @ Ỻ8U|{]0TYI+ЮжJY,ZNע 2,4_o%up;$6{xjn18t-.Jظp֥C֥TUWy lceP y+*vF¶U* .b!,ɵJQhx)ז |^+|. -f3F[ \2kL< dґJ#}{։0ؤttlُ}Ξpd'V$*I.ip>VpMvT<~MҔ~[j*mpJ^+. \8'aJSq(|6qyTɇCDhR2LdRRq+`b+- >_p?~xdomlv\FT/j{ 2 ~mn) Oz,3E7 \X^g˄j6#aWYZka.|cyВߞ?たa3?91/_~:wىDO/I7E8Ϟ$I*w~g1=,6Pxntd2ҧh݄{֯"lr $M LOkP &2eJ/I2T -3.aLNfKH2o_5ǖ${O|sO>&g($N'7}?]l!9{؞tqB&b~nwqTTQbW}rQvPPi9 9T,,laH\W:llMV:[iT^cmy%R1ߨƒ]t8w/kJw׹L:N,M UX̉d1-LRBVL&E86:|1JW"V+?Ʈ\d:lH?¿C;Sȱ,V}O?KNMf%MSXM8 wUk$ܜjN`]q^f]Ij]$^vMZ=,Vj5nO)_BRōhvsJFFG\n+wi768ipڀ'DM!FLNԪH<袚ކCKB^R}*md2IKqV&բ4RM R6*TeRkp[l{^i~w/9`l^IXiLn5,s^}R,dO e)~ߴl #?:]SEB&ayU̶Hx{R¼%񰚇Vu/s{Qqc6 ޖ &5ni޷ 0`H *PC@,[뿒.p&r@4|b]?мwƱF&ŬTtLdrQV_idI4Mbcf[OLWqhMJ ۺr{?{ςm2_X,,3p\8wGy$(7?g_|ٓL\ E$,]SJX#*擰:e+ӯ"ND xX9)mm|b!khٲaͤr-ר{oIy+\RnT𒌿?ᷥ _~ilr qf&?IWJwĵL2mL&o}4!S&vݚ!1[@Wo_^Qt,q,CuRDNц@0[,Ӌ3Xvj}մH:t 'eK{lp~ݶIiB, +(+zX[rkIX&$KԢbIZV.Uڄ3m’F65 4JJ5 JjԛrJR T)oo5%Tb4UjI_iVJhTlh5 2>6Goϟ=ZKpE~ZɌ'򆺵T2K:Tgt=oe=_ѼOe#Z28 u?S{zɿܭbRVPkIU  [<B!5۟KuWkHRg|SV&_x0' o8P;bF$tV F QWpJ}|'mm7:~ܝ(4BhUԛUX*c8F\+*<=1d<: ̈|SB^_+m,0?w$wmϤzpS¥,V"ybhGh*fGD~B.צ&T*y D bZѽR>N̝xs?sj?6]nSsd}/~Ki6='~եү:MnH5Y化ͷ|yۅ d13[TEj*q2]dxu& U&bhqP6HICӼ&Wб!7ΌJ U8$=qò%b&J-UkUI`͖L"aj`z{DxϝS0S`&dU3ٻ]fyklSd:]xygg&彣#0E3bVzW53fELmnP>!.\P2HV4,4\Hݫ,!9ٚ8'(SkJۯLI߆PF)ۙ km0 r#d+b&%n̚4ߔ3ӢL1l=d2 KDY] .HSx?>o<.v}ɳI/Ks34#$־ Ο~G{|LN }lz[W:XׯŒRz&tѸS]uq۝m*jkG~iڄHiS\GMx;FzlANNFqAqbBykpNm(P#mVթ4z+(7Vͼ:U5ժmk2 0Hoa\f`Eݑf2V&i՛m LǕljlYuR֭kF,s ]ŧ7/5ͼrD*obvi!Mww}}Qbr|̩}z`_*WQS K6R6r=lۨv71~9[,hb< ! n鷒>𻓗gjLJpHI3e2*d2-x5X֪6寐 dˤ}umhf Ցp!# dGWVX>mn3֩؍6.2 rZwU[Ӭ]Q.Ӻ%%J!=֞V)+p~[tEɴ2ݣ])7+ԔLC&>{TT+p]f٨l{Iu K=jQ6B~ӵ*nm'ۨe`s8gnT9nﱙ#7P\X e"_½xL6ꙨWSYoLd^ ԯL6KHe:p-nIXTqUZbk-61! 8ϕɇ9(l{y{`Zewc$\'x!'+r<#j7bQ Hv/^8 _X]՘.W瓱ՕebveY&Wuf90(tZ L|a΄o~mm7L*6/py(1?;Rgxn414;543uifKS&''&'/^&F{ƈQaktq<1|sCAŁ`)b@ߩĉ:{?s듞c`ХU+.˯D 8k']t* N ڵ=x.K}hvժK Վ.VYFjP LXx*x?1Y UzŜ:TsNó3CufGf7{%p X ֘BM\fDo"Xt1YGɄD"-'c+miH@}ڶuU. $:%Z)B&ۺuk4G^0s=&9Б]9I?g­=m]@5iBd f>ǠǍ&W&ѹkI|g1@Vk y_GK0(`Mf~R4LF$aUlK\]e؂mprN/`Ϯ2?3kĺ[2#^=ګd׭h1bؠdl,[{kN^uoޤtV 머==-Zlv.\V-okl }%l{EKmlUƝlRNCWzIZmE4sֵ.6+-^큱6n [jfZ8 +eu{0$z%gi9kg=?{ϴ(7kM#3Dz\fl8wwxWZy%҂d6MV%".%d]dRB.#%k;}fb|` T~+107\2W:qe3xKXgNoB> PTΞTLl?^i}}-ͦ#;^6>D4VTt[zh"'S;w=myiVХ\6*PcŖ&._,J,a:KLi > e3B*a*js[lh:֔;JQ.'OӘ>Y=JR(r09Lb\.tmnJ8ބK¦׺G-$EmoSuo;H %aHX-ke 9Sl+ab^ub>k[q32ݝjbhe+ 'tZFNKЗ=]ݟu^x$y#?'|Ԑ3Ƞfot+yx> SBl[iVf,4 CTW(J*-vCQu%le<8(%YŅx>aa,] E t$H81Qo]+%k617I$l$, A KبMgeB쫚E[ O,M5=. ˥.To%$;\5!IɨeۙW-zZœR doB&~#[g&CŒĜGYT8ծ*jgH}D*M7tW} P<|5o~j$̺2LHX+6-a۵l^ rq#LĴ"a]6- m=֜L&vWջ6KX~[+mBNSp>pLTշŒU+յ'::C s.4 mR fGhM'C^OŦxWhdR.} /!xQ™Tres6^+{*6iWlEˊ_(řD"F[U+B#t >f- KWdxb`BtIlZVemDfcj\.D U*ei42ITI*J9QbRb3!J(h-ehe1d; R֣TM }2ZNUVC#;KZγesmJ*abH2bŐmXgV$mJR+.Wg_+ZUiT$B!OdOڒf(.Y,yP5ώPkA¹r1-=ۭPVLJPusVJT%48)ѝ.YY)j{|k%a]R@9rTHI:(["[.M ]L^˳Va G@HJ8[,$PT}qz02ʥu dLD†'ԼVuF?%Pa7?*2Z"[%}++A3Wuߚ+gΌ}Iwzu 's̙<ۢ)u7ʀFeԬ7VMiم,j>wzxZWay ɬDZZJ5+);2f%[hlSu!ώPAŌWbN.Hx?_2Ő$٥1IӸ O~m/C+!vH8]=JvӜ) <~ i߼0e vg$U%-c)02`]¥B:ybAE±R+/F^VC#;pʻG],HCCytDJ8k*v2E: rc. H!aBxL0acfӫ+ F~53%,8Y_ll&ɠ5 JK8ɿtsY|@"q7t}vwrRkLy vHYƛwH smpú0V%}_}Uw_KXd|L]q٪#庄U\Nw{Á+u?eRZzд.3-T!a$嗞߸*@/%/Jx׮]llr{ eh$Fϛ'* GB r?=~TLђr1}Gx4`l:$z KVY |6|3gNQqa=uٳ-%,ZQ Rt+t4m.]@9oe4xj(U ,T+x9Br)-_Xs%[$VF593:^Sw/ڐs):՜kh1sdS$LljUdo3[i.s'9j a6JKVCPݪ`TºMB@.l+ov9]00[^Jh$PFN6GX1$pʼn˃G|_?̣>s>.x>8vohVg h Dݿ IX$r`1aB6 _xWS>K⑇"{dB4GêGi '\"\&/sou[XZZZ\\ 333SSSCCC{7ϼyEJ j{6I%_?Ëep |ssskU&#a0*}{nއ"C?^}օ6 h4 WVVB>Vw$4V(X, (& yX o9%|*$   LSaH㖉?'][t1Z_2;z 'p8 EGZ$h~Hx!a RH$B2L' 0{E#7hܘZcYqQ{"d@2}o]>Х2Cmފ9Dcv2ޚSœY;Qfc8Хd2h4 -$^ᮍ9rrGJz31yf5Lj9\>3bx gX,F2LaazFk-cթla.ӣ}j5v+awZ$lqwH$lk5 G%%0PkH#U޵ѽmnW09D0]+a5GGV$TZ([S#@{%cz0xKŒ.j0Q!.LjU@$am˶HX.$ f CAs%]9E.dcUkZHY=JА |>ϐp4$L%Ll  a{Q¶22xb[7bޒy:Z٣>pg#Bp<o0$ 0'a5XD"Û#xhq{#bȐp2!aNj  mT v&`q2_|vLxN¥R!L& 90$a5X/ ,E9#&^'x0-r,E0xtt4!u76-0ts! K8*[KcxGӢIJ J"E|>ߔpP|.VIʖ_0IZ2$\,!apT_r=d`-bxxT*Aք`q2 eH:C•J~ 'W8#aqjI䗪 trEv ex 0#𺄯\"EARu mJxYZۚΜh$g xd`!  E!ueNKJ)|6*[EcX][M%L5%LuuEM GdP΍X  {Om5VJ/u`.mJ/J66-$ w%LeHx}}Ҵk1%#HJxccǜECIb]F5f H0ź -C-@*cq8)ab_o9.a27^$ %K!LjEpXdH8N7%"u`.Ky_5 WӉI%fggHR)$ܴ.$  $\((Z)#L&.LDs!a#S2I. '+R2u w OKn!a Eex~~^:D<mL'O{Gd _ $B2Lt '$J, 8#X,f]¢u!a$,p.[]]]^^Z}>th4꜄u}=u dzOF!aw eڊB$D Sz !ɓRmJx $ s`peeE{K vHa]H +% (Z)#ᰄuz>K8,[}"Y zNK' gYiUSR6%)y_-0O4z[a-؁p7 -Ëq$%ao%u2$ %<[pCbĢA /Jؔ'I~@l(IDº' aHغ$h?s$^M|-]̵+_ uZe燚dCeB@F%K84Up6ݬ&ap'OPRf&C'  aH aH C$@-pú G8 aNb{6iKG4 aUHXph±We>f.al; -?n;>.J,p@L:X1K5%G88u)j]$l}[2 $:.J,7(x@L.ȓRmxH@nm-<$ $HJ$ !a$ܑm06&0 =HH=H)J":@-pú0vu\lUc4>Vw$<(doP~3պ?&OKUp<:/  HX.$ #a]D gR+U$0$ ODԁR K@0@0n# a$ 7  aHXt׹+UZ|Hxu'Ē1$>1e/զ@H)w?  H |7rv ^jJxYVg$2}cQbA Gܒ$T!a$ $  aHvg8"a7bNDJ{;ș^P<ӻfx~ 7 .SrwCoiKy|T1a!2AKxI Uu5ShQP]C[5csCN0!^r O%ܐX":oPRu HKi [l鵬1$`N s6Gۊ aIC,=f&BC$?2z$fn {BFBΆh jKepǜ? C0].a<&Ff3$a% #a}9 _¶?l܁;&l$Vi mkKnA [?F! SzH/l:/pJЛ/SzHp>}5^ yEJ ]sサOWgOO~W(=$ vIxm<=ydK~w~J ]ΤVȇNp{.tflgmB!aKµRJ9_.grZKi(́n#CCY[ G0;4^ChN s幃 Ǣ1CYr[r \ aW5GhU^$lqTFa6Zt&{6.=EvTMи0xk j%OW[풰]mN:*aQ kMCr3[Bo ySGv5x^e2Zy9gڄ$<> lrw%ln5'a-hzJ崄 a9 /FګJoKQ/JĘnnvh<ɲmZ<V!aE W]Woo8puz"iuQga ;7$gx0t'o7n|.6Ko~w{G'$cqLر+!apNܙ'Μ9F]:U[BhsbaX6;$ Y@0@<ݮvR>nV.[{seK3λ[itZpk `vQcNbX,n(jJz&4q']cGi9K^{aH]¿D4u?{>%aed\`eGvϘ0𘜃?[i ^(ԋ߷G\{燼81|xƍ44$#U-8o|(asIնg=<-*fo]2_~)Zq޽Yî_FǍ,[~4M8$agf1.s@ @no馫 _5|͛zp7[e9[L iIܜ-~v5Ъ?fwn"ޖ$hy=SDtA6xnh{xd.뇷|{3ђ'_'tU'vd0O,jxN.3~]хPpj":.jN-)i1_!OOx723vɊ~bn 30☍Vw` $ 0$ 0@0@ C 4&O;{Ιsg&'N44MY*Y'xEv8T:P഍~4N\o~T˱;930%v8T:뾠ɋ$)LTA*$gF*U~݊3;5֪yMkkHVv,T"tFu+a̩Jft+T0*~\ GyjM$857=xoKϺ*  FŃM44%PaHZH-0 y0$ 0@0@ C$ MSEJtΥΔ0%Ϗf҉+W6٤ y";S¡ɋkղDiק'i+||6";X³SXxmעPm+|&p+`RySJ Xh+Ȝ1ghڎ=r>]'.B{Z[=@\ꮒDBcNw/Z A[9/Bq2zZ[ a+%wu]/}8)ZσJe<(dK$9>OVԺZt/?ZΣJïdWIX X_e$e%HnOP^*$ {%΋Es=PM\˕3g nݻn7q\BWIXMX6Fi(ӗnk*U  >S333>}ᇆ{%E{P3f7GZZE s@hXs|&yn[ouzz=zkelc$l.7ƟIXy&΋hj -6G&o>H^ ~7|3?~[?3tڴ< {Nmm"a wٳ~z}_w_{Ghy?~yQ†/`1m1Fp.K7{g{o0Lo(%Ou`Zҋ=i{M_6ޯ aHt{.*)\.NO׶byڄh]N\nBm;bd%B!5XI[޶Qp,a¿0|у  t+!ϝC^~Y6RRz  \`'K8V̢odbb)6R2vp,aD:N!رHH a%s㹹BDJ(Q ;ß=z@:,Kөï! cT:Ѿl&R'W;ulЛ/cB}^~iC"?&$, u$I-@ $ H% MV&}#!մiL:~&do~ܳbI8:9=yqcccZYק'ȍT,SȇHF%^;AǙ"ml fNl g\.nbDwH a!a`]ͩ;m aH8*V:  1e0 ) tcri*-!a #d d$أ2+0$ tMkȜFkZŒ6 @0pUsĮnH)K^ 9@+o Fs{GP\ 3H.'3A'~`w#[%MVp9uU<gb00HH aH a!aHH aH(Tr9 WPJ-9_9>97 ZgԕƴJi(%G~:;L\卍uZo4V>XԩccJ!Bg\.a (57QX,?w"Y*2 vR)aJIa3mE*K4p>ƖL,$ϗcԛaKs;撩F8hMA$ pVcb_׿/''') +uP7+ u%SʯTc'x(ѽO>h'I8N޽kiE ^Zlt-7$ 7$lHdY8Aq/OXzT³O=̌(k?8!a_V ^y%8$aӭ]mºp ~tdwqk#\6J.KF=5 L& /x㍷z4=z^4'JIh[YnBZBsOuG%h}:[:E aTśnꫯxk>OJI&a3F](yrַhgI핰uζ~M^ C0ϥ.0>bH B;zu_wo+r?<$a{^~D"LVF&d(P5a(WkYʅ|g2jm%ao[HعFt:oL&EK<*\.nN sݻ`0>QJ!rVF\8qřL&u1m+i 0ȑ^woʁ7Ֆ7x| 00M,N:&47;Fgퟲ7tBg{ H÷p9b:mOxlt0[)^ҹ=}$ L-],JpUAgs{?ND#Ky!ܞ?)$ ̵ + ڵK:/}LN&:.yT"15%%l[JySh}$\(rX6NrLM ̮<H%V9CHXzh "U> E†]QlWk%͐g׌JYtsvR OY=O\3궝:=Z2cK謚ֵ' WjMBc}G=-a-m2BeՔ ʐ _*YBal ~aa04!a݋=þT asD"bՃ_3IWΨNvT,o馫 _k<7J%1 UM H4MAW+Nj&0gmȮ0M\;zu_woY-a6aDXB#{Gp6ZkH͵ sn2 '6)ͦRwocw)9H%:I7JxuxϦCx@X=e 慷B冲҅ʷʽ2dSǨu,|Wק$a ҘOdP(xp8D֕J3L}i]HkF!aˍpAG"J9EMgҡR)Gn$C@}r`2@#nޥ6diUJ0a: a[$ gɍR&\I4R9HxcDs.]쁄YT|:R,ɍ-pxujhtΦV1?\oəRdT.' yvJ68pvT.tr%Oɐ-3\2XF4bˣ=G',&&:x#ozd+ÖLjgX5N.s rck$<:|vG;GWΞGFЀ`4<|*[:ms3Ct~6B3ѹ]^P=@jqrck$<>J?ybR"^ 玽kk`t>ҹ3zuy7? jpeJ봧KdErc $ NOuњ C#ˁKg]JN&_f"F2d "l&y9bhO\T ɍiˤkND$kkɍ-i%c>|ҩ  .wj#jN[#aP:3 Y Lnl ébsH8^ԋ:zHlW/" rck$ZEP$< #+[ a0k z< yT a:SG72<䉟CxL%C?(H#> stream xڭkܶ6Er+K'7SH؇$]`˯H}8Cj8Λln7'y6'*7ڤQG6o}Vg[u+amiMky~W7O~y6&df~`s7yUMdЖ7OLIa:eoNwxwWa{t L# k+Zno0>?q8_JN)}).](Z@C7;k' ʲh;ޣ>2mmǃʜ`k4Ҁmy3,ڋ(:21 mqRLϕdBv%ʬG;:,PvBp/prQYDB^50C,^:I&XcX {ml0tZbΞ/ttu@>M58KYVd[*ϧ0RrMQd2z<9QĐMgD:k/v_){BR ttՒk0ܓڝշ$@@7$v8KB$^nv~K6Έ[5u U=Vܲb,-+d t˳E0[]ihٕ[v,;}M*J!`00b9 ea4}U  l?.X,8Am#֞5X Fh 8;C 41-`gK ¾e>TB7ŞmD9X%|T ,`Q;i{Cd?O#7F'[(!MpDcᦁ_s^w6a +?tcy aK<rZz#ht=_(ۘ$drDx@Q6%@Ȫ!+N 冎YR 6j=dOL,*'&Vdtsli2X&qsdѰ;5' W"藾jDDIV(J6E%PɧiƾcE1˃5(:)W1 lXIdY5lĖvH28kqzt` Vx [_T5 5Z(]Y v ٝ̓sͭ},& Ǧ>T5f,].eۣDku%&꭬`SHNg[b,wMYe{|/B2* $V ~AR[qQ;A7}I?%M`J-aTpʱB? #d *~Ysζ$+I7D'#XHς .1qW]!K>TAa"U]ԑbQrAॱ p$+P5BŮ`ؖ=QhЯO%NJc O#;EKýc_p8vYQìM3Rb`( Cpxs!<A`ʶvH+Nj7|\ \ͱ\еTO) TVq80՛Ûnܥ|~ŋu t6\+3?JB@\$w7zzv>?k[m k]St] 5Rӆgћ_Oq, W.(fh6Ay+wP,('m'H,l>b8H"9@.jdR:CMr:G{ R7M_\ it 6јNbPFǕȀ.KpeXWɰ&DM+^7 ,b &d ;- XZ޲crˮZ6nMKTșL8- Li=,v@|~?(ER*^~v FJmTv=S^NfJk8LL}D>{ft(\Nz]eLw8q8C@oPԐ= r0+!O%\ۮQ\IdoN)<cḄ;r-E×.NL%P*).zO hӌ4p%dXd"C™E6R,>*5,a+8wt$ !U6H{ \}2 ڥ^)KڒE endstream endobj 699 0 obj << /Length 3173 /Filter /FlateDecode >> stream xZmo6_!HU/vnn7X఻86kK$7rFf^93!ɂ?_껟ddQ82N# Qi2nODžf96[,E?VޔmUymX_nzw{^,A"է/,@d>Pq տXNFrZZEL&(/eӚ| -6.OEyg2l ~ؘf]^KdP-u.]ug)Is"]6/}9v̘Q!QUpu.G8L`%rщ :`mWA*iw.C)lV]:|p'HNh" yܵM ]Šz<uǪ 2 VY\9,A -qG< v(KҀs8 jlsOiu| K@3/v"N[ޓ',HcEqę@ m MmT4[И] OV !ͱ[ 788RVx WZNߧ BÛF/gRsYBr4ifZoӄsrcLvrCP'kʳ5g*b q|N$4-Vj92->%'J^g>w`|ɹsfʫWvEфog R.ƙs-LpZɈeD@'1̚e''ϴOhts d[ʸC0hӐ~l4qpe,6^3E BaPy4b>B=Ɏul28Li/JkJi>M|VK+\#~ɉE+it9Pajs#~JyPq&Iǧ5T$;ü"7K27  ?±9I%Y63rOac|п9m8Xh!:!@"@iB[cےKIMq*R e7ArK{BL5o{hfokvȧ6f,)}4+vPוMZ <Sbdla ԸU]8չ^Ki GuD.N;|-l튧HE*rtNB9l KF Ȍ #&wvÂTQ؁xWyQ6 gy(1~WHa=8q$1 ($P"w[;\l|:<:gbq9>fſS%&2?Dwr?U/bs b,~5@MGg4@ sP)dWtT .巏TOWi%EyDjHdoZ >g?~򐚦q} ~m]ו+f {L}Z։^=Wi 2xL |Id|ߋ/>}Ň`q^I^ b)ujfW+2AΦ)e/=@Z!>\5c^w@"U!*[yU \ tJ/$Z?!*I0TCW\!]{|}OyAW_l&h2J7˗/|bvtydQd"ұFv6. vV͍02Rɧ-  KזE&= 87vZhĘ oDɌX#{- v@4|fw O ؝ٳ3//3mۜ#cB ,KH>~J'Po]gB0.XDߍAv'P)qC]Tm]⡮|oQ%~GJtk -ZEњbrjJ 1!8wװaL"3ӆ?)Bfm HHzaC@УaIY/4k+ft '[7|0eB Ǻ5S;lPk]ݷ~cK1߼qS|5Mi^>&+]EC릢u<(؍9!^6uCi=͏ֻ('vِo ?5Ӛ5j(y"sH?٩:_3ÛM LKu,CE>02i8@Ǩ<;;KmB-@[წ} _:~JrANhd |+EBE8Zq]T#.`5gY'UWx+[t&Jd;;'Y* !AhKyn!԰,GBd6tgYAzwKť҅ *(hhUNǷʂQ<X; ,\h*IYu MbV}:ٌyrl;l 9i1pLM_ XW^[Zi@5L t$ jlܦ#<9jIȬ0yR } 7b!7PObNA endstream endobj 709 0 obj << /Length 3365 /Filter /FlateDecode >> stream xڵk۶ }'7'ΌHm2ICI#w_ AbX V`盯o4*XǫUWIafVO޿QzԻkvcQy_5-þ+E<0~/|s+dC? x/joWotDXUV&WaXZpnܔ }*H0LxW7_d& /w/#AE7!e: V0CL\D^a{9)*WC}дǼכ0;wE1R^Ϸ wy͝mi\OV.]Ylۼ~,̅恿<$kZPU n4MM+L]à|NŮ[GPP Y+du2H}[muo Mf0,̻b5z@^Џp*%$Ot U4G:L's5Ghn!)]؃b?McwKV2,HtW"4F t* S6?Pt~6 t*7GN!N})Lnd[4HbŸ,1WI?GAߴ-rLBmޢ? 7Ef@.8t$@C4`pP?oa,xt-:NI2P'0֪}v Gqqi ߂ݢ#  =$g5u:T .!͎dBZkpX eW1Ԣ.P0 '>NJGb0g@*S.r<=Gtc9IN7 5>!|cUV D[1\e Vg,˲>F4Ƞ(Lh(&c4{p`˶(xTb6Q!7VTDWc.Il@Lx_[O^޸{huUbPR@:@Y-#".Rrq$t0.r.;V!q4s/ i>0$яퟚ{Q~鶤2yX7C c~8Kpa ~( ţ_}X|0.2_Tu+M `)(S\>*X{eʪp@Xk6Ԇuѱ6kFt-d5lӇ+]bah*Ҙx")'v,*Hrm!<l3./$̅ʈY6Þe7%7r%\'MX%Hڊs24v<*%f6 fi58}Q3I48@@x;-ky#g2oaR_·wkPO,΂RʤvkWԮRoA2ipu+ :V cUWGpSc~0ۦi1-ᗽ$\w W$V6gE(=pks!թ{mY I.X>,2㊫nc|0T*Ƥ! xLѠx/ p'~G6v FN +25wT>:.I1F5{/=yU8HhWMil2Mq*UXuC<1[Kusz %xYڇcY2-.Ere=,b[ J&j UE5Qzi5XƼ'-.")\D#eᧇɧt삠3-f PwNE4+9`_oJ< 627ymzWdB! v^TjI w1ˡ}}23*ol(7ykЎWG֊`C=mP^3+iDzg|B6rKӆQd>/o'),=œ'%hKAAR:eFzs` cjBNʥ)$iʬY8G>@0^Aqm5 ( orcOGVQh(= UыB~#>dAV"4Zk7mRTjCQl#8P,{96r(ϽPJQ1-U4.S_;GM\ٌ{2Y endstream endobj 713 0 obj << /Length 2853 /Filter /FlateDecode >> stream x˒۸>_T5IT^ljT#B#)RK|} RF@0͛ͫw" H=R/s?._Ivemwϲʡ[,Y*I~u7݄+B/lgA7 ?y/{Xg/Ns6އ,C3T4zxy,}̏㌴XL$6[R{0<zjQs98l_Kbko~҇ak/ls([jKεʡֱRCVe_,""bA5R]Gj8ՊL.yS76ٵV,^w$ #Kp$MVCHR6J6e3JvVP'ji q*(y)ru۵wgѡc,ނ]ߡLfFKߪ>"({p 5uKiBFA)Fž A4hi- "6k2aAp.p'FbÂBTěWyQ 6=p`z%FV/u68Jgp*{Im]Wu ۩eɌqCa 6 Z0B+y܆c3,!zuĩ{')u@2X. bd xo.3hj K&Ɇ)&5'7nȪ]ކG@jAkiLZAiNf j_NS+v^85Q=a-0bhp}IeLF$ cTef'e |)BH gP%077 R SDprBrKG⡿;缣g0q{rkًd ?ͦI;С!B]/8 X'aZj1^Z $8Q;!h+\uqNgz sٱN&w )̍39 #rXn X)g`}BitWZHɹPWܹ[A:ڛeJAHGb%)eRhQٓA1INs GNPٴێm7QMSkT92Ps$m֎/ġ AVrwY+|)Ds|MXkzK?"׵+\ٵ4 ]h%S8Cyv%(9J:%nO  QnT?8kw[ hY1lx6p2뷽ɛFsJL\i z)G l؅ 9"J$Dl: ?y2 Dα)VCNٷCtª$$ҀB{$2@+4SMT,g)VІ{h (28 :dq֨yD='8 Θ/jNz1&hp iJzq4v%1&O.(^@aTɷ"XdN9=)B.oXGI(ݲn{0iE F  5#_`BFhOq\}e>T|m)J`἗`zM a9_UKE\7w̩AC@;u׀a|P,-ѾJg/DQk[+bXo PhK>b3'}Nk198W`LE76:xK"6>Z!~Xޠ }JqE|g(}T^V7ļ|I-ϝ[G> U2?e}_Wx[ CIh&E?tPH3b|'jr@C嗀ohpYY>h%B )_ؔ=5T7F~Мڊ]ڭK~E^9x3Ye0IxKѺAsb;[k-zwMDljݘXͳ. bҥI'1ڪsle J{8LQ~õFTiWZʪZALkeY,|Jb4/Y@a8/a~>b(h:R(H endstream endobj 730 0 obj << /Length 3088 /Filter /FlateDecode >> stream xڭisFTe-,GB&LY<`iV`}W`UVuwQ 'OxI膋"r~-γe|orz\ۼҮ {yv>yr~Q vz{Zdyl/Wj'jHbOHtI|ޗ/ӥOK'r?E].*BPrعHBe'@xP[mfv3H5r[s^|ױ6b2N%-%r~9-<@~Mzx-(ȊXlr xbN$ ڲXLl,&@SX]Ϻ*<*xv& 7Qٜg X 5'š^Qoc]]9mt滴I;ev<"2٤:pvEz8XC}XQ,sԙZ_]NPp< Ґ˷ ]e[\{V<ۋagMSoyD% qB*2*x9z ŻɖC*]ҪCF'S fP;M:6 ? ` $b韹a?Ej! ^Fxvi )IU%Z!5 ^pCyZ }m3E0֓qN`.Z8Y t+sO:<>H\1 SO(2BX2u/01;LcHa+E; HJQVv['g%a1>=c .GA= C.s@MNj"D;Z8+13مL҅$"P͈C>I*rdԀ+>o$*ZeJK¬ qG<`#8@ͻNL;|JS~KҘgp|8L, Eں&iX)!V9eagX$" YbYK*}WɭiN许<^GHg5TrTͯ/H/, c@bIrGbJ: <(͹t"mlP :d^e:U&NLmG0#)A1W5,3A$͖~^츠=q\]\W VW<`b% A.F'1:2 1Q"3Eq(5tI:ʋDOYYJY/`bsZ0`3G-M{LN ‹,^_&=5mxra{LxG1@qhIR?%#`3^r֧eR̎)FJ$!DKPq̚Yv^:z{ʢU8}Fq2 ha'׷*=k.jFUH*˘ d.cVuS* kuu|з 'ȟS庮գ]6uY֯F.$F(g,dE%LlD۾O_UG];vE/6'Ϡ,V1BЄtB^0^/_dm,}#XCro@;D]|3Ns2ձ`iuOi|=&@F`ev;g( j[8҉@o'o\G-PS$a/-CH_uFP1RP2HŸfӥXEy WE37sF|t ԗ]8W@ơytxM<3$p\=)JA:8$Q/?*fz=;lTV^~7"ikoخA&hןw ;$$]HQx~۹ > fvs-Ɛ1xޭl (ؖ7g7eOĠPdVxSXtL/ԛ"{g{ײ_%b"[TtF'K5~~k6L7}Vqb'3%t;" uwHTu*?q0 endstream endobj 736 0 obj << /Length 503 /Filter /FlateDecode >> stream xTn0+(5|^rk" D ]IA~}WN 0 Rcrf4..yt7@>ih*ǔ0Mg&ºbuXe[凈]P4!Hg*'?NZ Z,Z $ IWep|Kp8!q,W6!f;\Yɼu sZA`N> stream xڍ?o0wc;+FkuA,R$ħǍˀ޽^̋rPcm؀`C]+-"rkpyPH(/ēB72jda݉UC&vp:%X x՛J#GݥԌRrdm "ѝ!d.rblh&mz-/O endstream endobj 747 0 obj << /Length 1400 /Filter /FlateDecode >> stream xڵWK8WPcm` KB PQ&*BR_-8ĭ֧Vw_!cDW&$ќEsJQL>~& o"e<Ҽo=y\ET&ehҒ&EFd %Y\Eow7Nӄ9sIy4c<))Ea'vy6O-@}`3y1h܃MN6 8~HMm#DK#me[ɫKBFZo9/c(*X-:0S#kl<*)b"@ŏ^KcT甲ָxr7$E}@zVv uPe}Թry_[Kh=7bH9yr ~Fqڿ:R 5uy9*[]SOJ7#6ܾϺR3TCPl/ia ݫ'<Ëd(3rY~q0$PpG֎/9+NĕnqXdqо PKtLBq U 8`!}a[U]I<xj}zF@2.$RցփVV^: Y߮2$'#Glq l}1s ]L_+k}]N1a ;e4, GKVbVC$.Eu_CX Q^)/]x9c'Qu¤k|xS_u ! _ ~{=$/u #w'iSFxZ a*Tf@@&FD ʬBQiX"YQjn.@wp2aϞ1ߚbqh 45P;GavKF@l[6pYhFseY1Ty<)pkȓd I8671_!Ff+î78xM9 t|}/ەU?BNbiB_|y{:2)b@]]|b o_$D9:}A? ?#v.haBC/\B$ʋ3,@Y郕A; xR0U7d(}I p̔p֭ nyXylnt?P#I:P+8 \c,J!h،_b endstream endobj 641 0 obj << /Type /ObjStm /N 100 /First 859 /Length 1713 /Filter /FlateDecode >> stream xYn7}W1)P.Fĩm.6ȃll5d'Y;dUzy]g Ld" 6o|FMpބdNdR(hI\F91!!e|ɒ>m0k[L!meg pP@Q,NrTK6e2G'jhQB ~3䉪Ű`"d{bV,g.GsڌALr5JU ƳQ.0DE,-K0T_|^"@JHJhqaL( h$;@Qv2T`))0DJ`^}FV1k 3٨c JTt,j~>B' Fq(Fv$yN#o(j'Sx&L<\ϤـEC}2 p*CR naDhs](Qº.o$r .*9"9K rlbލFc2#Ɯ94Ջ_Sld&Wt0{{:7#|zHD ϦgG¼4:?,_ҏ?^U,:QuXϧY=oxy4=X Ӝ0V npIW.$+> `y{i%% &M6$MZ K"{a>Z^\h5GⒷɺMzqQkCh9m O-![~{ ^%>w}nW')Dl6cEbm[S7Qh:{]UCvW!YM:db#&?tՏ^3Cl1NçwX\}WUӫzr>.tv^M'I}YԺ=vFpgΈ#܋ έ=`q-rF> stream xyǝk?v ؝Ąg"6vf!y"}6b--RF[De>d{l")&$Jx7}_nG> @WQB"+++B._Hl㏷o߾M[{VmD<6ђ]s dMXr61AKI4?NT2%jNQ7;tŅ@ZoIbH$oPmTÄu?̍k'#MjRn]# [jl[&m"j ]]̒goH{Sш2)ʖJgH 85N4/'iB9E,6ٸVG?W.ȤO2]OOq[sK9>?z~=꘣zu݌vз{/+m]npzDմ*&ZnxV̕#*.1ֵjΖ L]>*}_N ٴ@hL:VVwZf=Ss;o󑟾"˹{_ce2n']ֵ1v,&L?m-m2FG^{z.;"o=}PU )D|)$ܸLsކBTЯ @ 6)Z\/3GI%rNw^}'>/"Q9q#7>7?sZwTˊ*3)$ʙ)6Q }>N!JΙ8GqtmՠZIv\*ZՊw/?JV[Fԧοs~Ut*~Gx,Ɗc;B,v>b^T^]f^JTf+wKmy|S^ YYu>LGzB8.Ͻg:gWWbOuydBBHZ!L:S9u4mP(x,(EL0(jMWO}/|7(+:*k1VR:)0S6֝/A9ْQ\se9HNyxBqT:DM溫_yIc~}K晿o>wo7Ğ0.^y) RZTo mWs8Y%ɉaO|۱_GP6ZW|sr<c bs%τO qƴjى#D8e}[COrRQK!#Iܝ>-HԽK]-uMv?-;) {w.3y\k--D4vu.XSROJ>Ͼ0kď S ԷFZQh]21C/=}qa25poZZ}K|ETWSv )d.9PnX5c7~[T z#?J?<%&@D-wu6kOgrN;֛L:ΘG6{y̻ ֥ )sZݿaq-Ut}].qںku`[b]n0spA c+Z:nՊҖwyR_P<A@i հӽ`pCٳ/__JŞ&wCUFRh?]x7i]Fӣj!;H>\-P9Jq="77ѧH8tTӷ_zח;9IFtύKߚŸ\K '=7aOM1γ]-MQ(|-Ռ]׵|ERsclD]Fd 7A3҅s՞4g?^84zz"&&oF~#SO_sfsʆO>A;*8K7_!Z!(dWDrX<<-17IօR(I)佘/e:ctגo)ŵ2ߐBF%tZwB"5VN+nl|lWˍ1`1ZRuX(˱r:1N4ƫ+aDZ:Hxu&)GSrKTc׻L-]!׀d)'d"uC0;J-tA xƏ:K˾Mo%2;=Go޸\O{z#IJ[\zo]|O꿉 w%i! =Z7|rlP|ZuNx 1e>*o>(\Vv51YY آ7~A<^1*R|Z>w6=6_ŅY:qttzWFSyw^C4j9Ju-iZW+O)յ$6Edu$,FhMūn!}S|WZm]1^}t\WL 7 bO/ѻC%GBuH]HUxuW\MՖ8gWS8ǒ4I8*Z:RpuXL#_sWU_8d\= -JSѵ@N]~ӛA w<K%jsEj+_[^W.*uWKm8}X) i YH$gwVi]a`uȶ5 RrTjbИ-Vb8r:*/JL~y;=}mħ?'KSD{7=|+Tb!ćoHR1KAZ{͎svRK &i`߭+y ߪKGA͊/]pM΍N^|k?? iT) ]?S.粙'jVv.o7DVԵt-mtۊ3F:[h޻s!?>uni5Hb?Dꖮ */Ak¤sٔ념.$[5P/$QB j,-֝Q D74xA}bJ]LbGQ+zϽ__8~O$ Uc](aoݼxj߹2#g~nı_ %BB%]ɫǜ" ȧakCwRi32ū/r|9ƫS$p-WF SKf_W)OÎ+̻(SaI*2*FuF$H9R]]vزPv|uvNL>/E2S|Z*$VV((Q~<;j\ӓY9 siOU{ Bw ~r/QqP]EM[},Ult}OO>ׯ:HLZ:^5cwdBzߜZY\]"g_}?}zdd0 +ߚ=5vRLV{2JܪD_y]Ii-~ kr$XwZBDhšu)L{[3+ӫ2 #RIwBʅL*$\TD-B˜C™&CYSԆ kz= EiLQQR WWu]!nq }'/gϼsMwƶzigH,rߢVۣyf n7E98B l!'J.H)2E'T̙uE&J,9veUTquT-C&?~oxxK7"xXL[S{jUbT2nlx"P*Os B$?ڴ-fvތ JfFmY4NrVИB^*`>'B9tQS"| 6(O&TvNVsYNE5EM:*1vn 2:NT,|{fl+٭ {1֊C3lf+lw, и:zviT(*[v;[BmrJ!LK+ڠP8gKtNɠs̉ZjТ J1,Y*av4o[b9Fp5ZliUg:dkGy(g.K˝B+":t* nz()S|땟vn"/(sOlʬwcŻ\'7^$V$X[Qhn5͊a]WGEԃ!&:bpfn-$TF&o5 q(u\\ _4Eu9ռV뺣~nӦn+[յIع ZK Y:-ns mRA{wՍ׺V '٤ يε9{{j۸Yo밷)[lvtdf ~6hXZݕL`͜(N_Rjr{i°7+,4)&˂=B/z[HQfV4v[I޶@n#vDVjY[Uth[-]a=fsHގeV̓ocL]N6$.97ϖ+t Ʈ<0b+iu7E1Bo iYah[;ljˇٖJJooUl{뛾Үmk%dB{gkm6vͥnKj[`:8/lzg({]>}mP{bo[~6nugcl]q;y͓J:y x#x睷] sg͎ q?vs}\}GS#F:A"}ݸ~\}`EW $aq!}MLp j^p5\ g4U9sud^_OT<բҥW^m(?Q1ּ3DV/ܳOMp6V)DuVj(Z.dٮtWu%N .'>bb(avl9nuQ09c9_\|qJ9~eCdꖺZm- hZyWKîV+DU2!8p;4޿~Y\NELZװ=,p\q63Ȥ_y{[>I'[5^][\Aa D7: 깙X43#npu(өH.+샐NF x a6[jکwÙLc %{7VI-su]ސ1 6 fvgUKZ^Ն;Wfp5\ W3V}~o֙s/yΞ_Yײ#^ WyhNPù_y jb`̚SG4p5 jl7YW\ W\ W@]W@*"\ vs68eW /\ p5jjjjh\ 꺜jy7ʮeT'LդP.su<$hC:=:jrn` 6SVHB*N"v6^Wycݙ綷|:rYz<WDž۵LBVZ(T*S}if=G&!tJ}!]RIʩpuS]m8WKRJkZ]Y-/m۰I^ lߐJ@vpu]JݮH$fZ Cq5[ѺvtέW8[D<8;3V,鴦)OXsB85 WGZ sW 1hT"'CVjB~W71jF5LV WՁT*jHi+W&zO\.3Nǵʳj3B#^ hx͛9]s3~è9!1<d50s6W\`ee%L K*Z^^ \Vbɑ bsoR9Co>ͳgOD"cY@oo')Դ\VWq_ ]L67C|w~߿ډcoy^oچg%ր+XE6-uޫW.6U9mµ7< y DBkWA:w\C7xzz6#T\}Moo 3,It#G)⢙9]ݠ6t5g{jE'pqruF gfS̩PliVբujg9JcZG?xvىƊd9#\ @k4\{ѧWO7W͸:؈dwA'R^y%C4:ņ1.]͈rap Ȥ;k>'if9*]&jږT2ECs4<6NA`2?_Hߵe~Hy Z<h#]'>ӧ~Rug_w{$$yj=W`0\ ]=Wc"p^173;q`G͌5u9mB(q/c^]m-upJW _%WgӱT2"l;~FP9yjR(Txjhp5p5WеΤcD'Mjj\QquU[[jmA@D@qlEN)z Bڬ Tx[j!y~z**ݾ}i[tz'Wﭫi8v>Z\6CRG\WE.VT(J JEkro'_Ufo׻a\ Wߚ{1I:S$T _7=fv 4)h^-nT{ueT*j`aАGq$/*T+j<ȹZm:Ds_y gtuM[&:H(&)?mjE9ŰV<0%ov{< F\֟a|Ws*NE!Ꝟ.ajMWSIi+yQjueP\g'jn_6p5? d'ڑ]  !m]wDK膈7GnJ%RqBkYz|'NS*塜8zO\Mw9==rʇo}_=vezuJtz<=tu} ꊣ<GZIzWcX+4nOW\ W@ D=5jhWW W ]M(\qu$$R]-T"*p5kr~ w`սI W\msۧg_w㹷r-1ј֘p5cH[^x쉛7>Hz5= W˹]}{c#7˱z7@GJ{E4&{6j~gK\} bBvtthL4f4fwJ"%ޚjGBtC7ИhiLZlY#ԖZUM/ڒmҘaî6)vz 7PM8sZjmKWqJ}ݑ[Ygc*^6 7I X[f3*t*Rq%.6ՒZҘM}jCh<;a :`H3fvD KO Fc11=MWת 4&somҼ*vuM5W 6*FW4qu2\~,f~gW:!4&{H'%𭺫 W{?jUsNntIw# hL4f4f#.vuW=yߦ# hL4f5fWN;W`#WGnu9jyW*/NRWBI W\ nr\ ꪜK<\ vs6P\]mcph[yW)WƲϻLƷJ۷o#nJWqp޺k\.3)%ЫW\rUi#a\T* iҋŢsurOI;f VUsw85597ձ9IhR_Hdr~W`9}*NM6~ʊcu-wy^xwD=}kk˱{t) bZΑ޵{akj~0- ?v׆osr^N8!Dy"姭zU(|1 e^ޛqg]Cr``WWlW_KuW͛7뮦_͌ [<4V⌁X!@ :|}~H32|\M"?@my)E|,S~x$^m X=Txh33=sJբ{ẨEW_~Vj]@; a8?D1Dp3D>'6b jשּׁ8ɤR%IʹCu69^XT*DBN]{ Cהp,iʩ44][ :u9puϥS@,ǽpߝ: D<bdiZN~īqlltvv/^|WΜ9ARS ؔeP.s\6|ՋF<ɉ˗}͓'NoEN)NRG@g|LW+73x2` \X/=:Pr_:%BI-pĭt*ycQ^p 6%.xө()5] QRQWr`*6b)U9W]\±m(2?7Jhd 'Bl3BI\Yjm~p2/ !N$Hy|j>cq4#p5qut=BIk!cuy<hHvf-!I$ڽ:Bģ ˉ]%i],5Xd-B[jk& W]tA$ʦg>D.]@*RJ"m/C.9'*D+aRhk\.F+H I 5|$Rwu5N|=TԡcruU\]yG \ ]A}۹@W\ `o] 8E{&$LҦ)WVpoH;u9gB㏷hBt9I:,]sh~VM4!aJ1RYWX B6X^R^\I<:tm{{+ ]omI$R>KWlޞ \:WRp>%H]qM\ss^T.¹ɄW@ۺXw`\۽j-۪NXZmh WwU״\ b jҔ6)<:#c[*1^[(7af4Tqj$^SmCvcѬ6@Wk:V׫jY ]mnsw'@Ύ AZZ9V옉fSptPoHW\ Wws `RWdk&cl8mlGuZpu ǚ{Նc oj͈W|tuUΥbD515C{1<D3y jhlb ]P`Y$́sC&]BŇh`'.g<6oU\J&NWBI[u \ ꚜj9m` W\ vrR U9`CWkۘsH"%^j}Qgm-ٌ^3*К @9-_b <ݼ5ElWwݤygqu{B&W﹫ #pŧ X& j;)\y^+V]CB8q4XSV8XxɓylWavP;ŊѼ>8/6H–gNە[ϖ{_ӛYmvNҵ y΅?4vzpڪv`c"\1b#yk[^sp9wj]CYyӀuL׳fYrnQ2l 'H{꺥I3dOܜv5'x{{hbv H+yF16<4] 2yzG'@0hW7:Ap\ ~cjMM;0s~j0[-Rqr5[&VyW+9g36ἓ*&9b'-& *u[hK};MQGʾ\R0~o¾unpw#'S;M;-V2 b5yNպ"U+zu7kրɛ鍙Ϥ&oxe{6 ]}hA>q\0t'^nynZTW znyHqKOc;ju]z]mp5\^TWk3wfU DW fb솿An<A_{pW9oi`wT]wtܖ5K ނirs \˩@ 펏`jqgtᵊK j++njjjjWW g_'_cx#;W/?rv?sΆzsg9v\H8NE t׮}ض;nf`v'2^O|]3oȠߪJSm@26X[Z.XSQj'PlEK\-C_OkPm/C\y4]@ ٌPO} R' -ǟlgW7Şl-+rr01vWg~hݡhM n 7{.Dit0 ˟ Q+M}-j6VC1րA[!ߢ\-OdKX-Q1|MT+]ZfNW[ ??9b\ Z /I'"c~V6< Ff9cMW% )[\  y_5l}gpjjjjW\ W\,-M_y~wڎ,/%':vv;?bb(iq5լj`~A*N^:tȾ1M1+=jḰO?I_!TWN#h*t+=x=w<Շ2餭[dЌh~hlgQz"!=#|G4Q\ j#O |\vs"#CcSS&D\-K"҉D!z:ws>\v/ OO9NI窱: lsWoZWۑ=n@@fϥ6sGJŨGiJ}&sOC";)c3./Q| $a\t 'm@lsWΩG ] 5\ @' ٌPO}+ ,J53t'@5z 11ܴQlCf~W˷R,9u8?СCvWǽ$W3 $&?.o:LOzDz}ǸzґXrNSse31\t0ǩ;W˝l|%kfc}POTW@W\ W\ p5\ p5jjhd-pnn,<Wd൵xֻf9]M9iN[ѶD]#'kbr/%[dֺ:p]t:\><;;drm֟ tn= ZWr 5 )??jsfjWL|i;]@:B <4j3-@cVdojml^{rW p~(Ť5-wC] [j VVHNW///'?YZZ+ŋŔfZ탭Y&5?˱fj.WrWRx<_ _ƒ>HO}/T^{-^[(g3&ǙƆƂ*f`ոW: rTBEn)~Jo梁WVVD;b@ m #i!ۈ\R>GFW2==_jvvV-i+ڶ41 hB.!JH$zbz*G+K l&ƷryJ>ާ}`qut=B[ꩉ;i8*%sLMsH$IZK&CΟhۆ m&aRhk\=;u+ O$l`5s3OLN Z'(?m%/ :XC$t_2,:@X]u7Ϟx_x.BĹ2E5uPp5%ѭv,TR8?B=bX<(ncya8ݖR $]\ @W\fmj3 ts<WЅ[=t:^ՖL!L&Q^ Вjj]v56tZz"}84&5wPp5BaTU3QUBujh=(ڼxٮo.r)nbWKAj1WR8BQK$ϦAqs΅j HtPp5p.Z`k\@%îN&RPߐȓGLK(nXGǓhjj-su\ \ W6tuL"kՒfg/;\Vj x;D"t:ϒIsdd  qN\S&'m;}U9տcjw8s7{-ՙjN?(.$[\sNM] T[3u\ @; hqjp5jjjWh֜p5/Z2жx\S$l6Wc& 7C54dzyB!kuγQh}yis\56҃VvQI˫ K?sS endstream endobj 753 0 obj << /Length 2288 /Filter /FlateDecode >> stream xڵk6 |X"gp_r)z]˵WʒGv_3e"r_LrfH=r[WX_Jb/E"Ia˕LRE{Su+`LitkhO럯^pMtl'AW7?/_eRaX._*8g1 ',TB,tMq♦&C]e)"Ϝмnh -#OO7c R~vy(ZÊN94][×0>Zwy,xGskE X gQ?l>3ź{EfOeFwey ,uśNWl+XD;3V*~HˇMrC[םaXQ VꏇƴmQ3`nu~~#n$gXYK,T4][ u{T,b=Ife~$C7,Ig`I׍wie%"  ~Xm+x۾ѹtYtG@@LnJƀm[d$h/C"פ @߂]G"^!F*7n[3'U8T\MLJ:@0K q9I>Tߏ׏G،3~^[ۢ*:O׷d7 aM4- RvlHȫhi'!D! yc@iӎ;8  )&ҋ+@}Lyzˆ' z<&TtHo YKјֹ]lΒr,L]ĄoD  l8 bݍC:^DřɄ9d8 { 0OyŭƷ2nl Qd{m#,ǒZH unA0Bǰc: \f(hQ2D(אp-0H>Âc=.Γ?&1h74VxɾV7|ݹURqDT~&$9O%6r)? q !bB-0##ؐHG>/!nWc]p]B u#w~/~ewFK]Qp@ҍi>'>9jkJ7B?=҃O5BdTq g#1lV 4$Շ]QZ`b% !mn].6#Q|ܮTR&|[0Nc!FMNݺ \ %A4#UG"v}r~5*砻SwJVMAS~ofq RT$rh9P"w,?&9  [U{rx7 &5”8hZ3*0^Tf^DŖ75wL;t7a\|˂j)WB=`lwtˎǀCqʙ7X $+aikÁ\]P ,ꍕjY&R8k}ˡE N=SPd@\~z2=;>ubq8WwDFǩ)P|RԷ+S~k2emH@SM/贷 ^9Ǚ܅ P7eIEV'5!Xzozӗg(B IpO;}Dڤ%GwYLy endstream endobj 758 0 obj << /Length 758 /Filter /FlateDecode >> stream xڕUMs0W(E/n@^6åJ??_ϮW&iSݷd&rS,V1[Se i,DU6k v(a7rwap}^~BK% hHdzvw/Y5Bg)Ojf~+v;>^'pd+RJ3a cĸ#v¹);C%PJ~h`# SJՔNs'1W6[ܹ.P _ l_Pd|r돯-0tmG+@M0l}d׹\;Sj7x$&nHbMCAxsI &V>H@"S_<_Rr]5nJ1@q0E1 ^!w&ZU="\ ġ }m2167= J7LmD6q4pa)RAnL8;|e&/^;IbeVS? !x!K1BB;3]A2#la׿_,6W\m{ڗ›T1Ji<#3O0JːN10 8Jﰕ endstream endobj 750 0 obj << /Type /XObject /Subtype /Image /Width 471 /Height 450 /BitsPerComponent 8 /ColorSpace /DeviceRGB /Length 16857 /Filter /FlateDecode >> stream x $Ga0vp$#-#Xa{m9lvՆ7]omlm,Ѕn FBBIFhB9ь4ͻͼk}>{#mVUw:2?22Ϊ|Fʯ~7 /\o ;V^ 7Xx@2! ՛:gaZA($VGREUY3rUTitP(6ѩʔx(直"[auj ӸPgϐ JW=u IS[@(&,ųtӭ5v1Uzj"\IjFZmZ*vi9S 9[Rs9ҤLLm~5nM<|`ns要/8cHl*a~-O%,AK Ia)|2]vHU*vGŽtk Aޕ*].eb>[$x ,n\*fvb\R\S)IWB:] 1`ldl{5W둘Wpt-J0ݙ:z쥟<^zpk/UOF$ x&b?;2&iIf`%,]ęne~i|t XOWE@:ݭXr Q&HDTE7fĦLJ6y]G4?53@Kgp3C]t/֛;^|)`w82욇ƇyT|[| ~nɖK&8ma:=)GobELHV$`j%O<}?#/JY-˥ IY?ྋ/RY{Ư\u}T*_Y=}_#@u4(ڸK*·qz 7l_m;_WF!7h L:&<(*XɿHLR;+r$ n0gf%y(ޖH?>qw#37;E$FGMxkx B>e"jI:V*d"K 60[JɁٖ9V0-7 u%ΦK_~#KI͗SǮg2 #ǟfvE0(Arrb(:T{ yW]bI{*6=!ۙLn{2'x^>z>P.֛po>$y}Z~ei em) HrلHلVR^L2q6\fG00[TºM_~ Gt6{/ȟ|fT],67yϝ?|;T{6?{nW[𰼋a[UP+G*^T[ݭPP(sR&_L I䛔3u2"%HN I@9+5"/"Kj J2NNUo\Q T LEJ#V[`j\mܭn#n[GTY@چc00`[+b~;>O|ɤwd :$z$tN3Z3,_\I3:^|ሑLaf& [n̤b2_w͛q|`6MgX`'˓|.#ˋ/gsܦ&GfGm:n83[ё-64+ˍ5E念FXw cܫXWU# >R~XeKz'^ ң/ j}=1DEX%2DM?]Ȏ|Ȯ%-F=͑%uq}e.deqZ3|_zf GؑpK-C I8F%aZۭTrE(|?&|@Fޘ4\ܠ U)cepQPTA TtD P ĕUiuoM&h8U lSW2D.&:Zm291rX)^qkXgY̤SXn= u\dŞѐ.5-ɰ/SC6T8tx>::Ćt:YoksMfgF_Wz=ZӌV;bGK[SF:G@&=a |/j4;↦ͮˤ[>VhP_&YGBj M: ћe\&V%Zv7+ae O//Mj:h9i;HF#>$4dqRKLt+i5[`c$*K'#_\?o}0>4APgzڭ^zr8az̩W_xf2I7j{}*.8bml,e'GؘDŽM׻a^ۿ`s.~>rN2ݽoIPh6᧦ϟYXZV44Xs̔LˑLDGuشIL2c=9jhfM$\l${eI'(F>Pmu{k㉟<{C~o}}s>I:b͒*1ƼBn$۠Jh'6[wxAT+JUK@ބxʽ֦g q0IK'hT艩J*$6ڠ\J/:וd2lN|}G(UJW,]rT*ɝT*Q,ȭ8sR0}'bo(]-7jd3sHR]ӯZ⸄hH&F% +=6PQ@VqIj&{0Gv!qZ| 53zϭk(^km}J>4 {{5Yԗۗ,^=})}Q6ve/dOfԯJR@|1 ɸu[Un8MW]Scl욐fd⏸(N6K̽{]`Y~oc$\.d)&S;ɝz >-=-=Q[%cxJ|#l-EEӉͅ :an6FmήfVdW+KSˋsҾH Ӛ%/NN,-$FDg #3#s3N973}vfzxzjx0yfzrh qQBHiѳ#gÄSgϼFzpfWO N ^8>ıޗ{&HE UK]rM#Unx !S:thP\ϼ*S;#aڏM{ijFGNc 61҂&'&߉Inr0-6C0i3RÜ[iFF$FH3wOH8O 2\YKdD&vd|H%6҉mItdǸotU]WRlNЄS"UVO Vpv񅙭Ϙd.3xYzGW)ozt {h==ݞ"e7`x)<\c.Wx4fh+=-2gpttpwA;80`mIDL*Zm\lH2OH[/j嗭]^> oMKe2-Z%ׄM]<_+2u_cߕ|T4R[[;Z4LmS&JoT\؊D06sn90m-o|gx]s`ӽ9#s(+dם)g[~ev\ vx[$LUwds%?"@a?eN6h ;!d-StPT{>n%$u!a쉓}af&]GZX6n~]pgFK8'as.τ9>FM?Tq, <0Q!rӫW*E%L BprLVF Oeupr율0fVBvx%ܴ}H#'_ %VGt`vHl:6<$IX(KY gHYЩ~r3ʧۛVWZFoP$ Cr(a#Z ECѲjS;=ayYQ[vra]HgNdk[( 9C-$ ifc9ϡ5-l[HP5 Bp`i-r8  a(ڮCnތ[,^Tpݺ0pն>*$*asiIr)з8b!Đ\FHH aH a!a#e}d4*NT1T掫!6`#jCuЖ2^H8 nW KXQ tya?6fTP{hm<(^8] ˇ\'4Bz T*찜}V@ %Io,gX>0)2Ʊl?.Fˌʾ\ ѫp$\geKv8e ɞ6Gȑ(0B B3tjGk޶&aϮ&vL-NG`9 慛ekUב'acJ{} k8؇#<0A0p ߛsvř\gBW}XnKX^ǂ"u0{OS%|(akMϽNH&tOt饐+00 ny[PƄ=1a t# [h ܽ1ap#G%aFbH.QFzOz43N%Yr8XVm߿2liA®ΎpfGP.jv͍^:Y 4x}@ C0@ C $%as}ؐYkC io jRyOFy'j_g 0oAAp3C ˬtߡgI,3,`.{jFٲ?`(/`,WloCս1}K&0wL}B+yJOl-=s͌v9e7“W>RuM3˱,4K%Ѕ0H a!a ;5=eNG:݇y"gW  [0 Ѐ(J9_*UȽDyA/Jx(6ө{UT1(U^;[/ʢ!ꬄUZP Sꮡ'RSc#t)<[ P5pEªvOFGQ o ׭끄mM[GB$lN:imL}Խ1aVW%laL 3s orY ӿܧt>}}8۔ 9Sб@@$+S/P HH aH a!a- a@uB' Nn $->E eTH aX ٨ $ Hi]H#aRAY¥l K˨0@ ۗp5eڅÕSނr1J{cɂ%"" gͩsmy_zsZn& :MWDrŴV smf&[b0ㅕ7Ft c,_wlƹk?.$ !aL avu0qv$:DeoCj'|ODjUgڽi6H6F#r\b z7cyY֯$Lm ʣ \>(gkXON>, )nĊS ˉ`ffx(>c CUJ@w] XJZ\l?zzEn/2 iWvzGe9%xf+؛X rl)˗W_`J8"Wb"y&\fK]Id. _jᶄM_".Ú"~dyo9[8,֪=oF¦J7 ሦu}"a6{ {v爄XfFGn 8ٹ\= [05',],$w_%Jb!dAltU+a,d.鴐5Ӓv)gKBYV;UB9U xT% 7CrY {|5?UmcO?umT4݅}kɒwdWqkEdtNMKbK`Ǵ!nYY)tP3,`wwF~B'a\fKAYA۵#}x HPJ>H T$l&<>s߯9(aG,2? 9ɝՁ (<^V|jn<$Đ\FX­>AH; @u-􄕻$+a-)@$ެc[#u|-ayf 35$P.I;I eTY $ Hi݆7d aDu$UوDC䠅!YDj,asH $eTH ak]=Ca{kaaʷ^\7|vҴ?%74F@@@o {g&qB切}+Y#b30$ # @¶%\˦8)^4 AqQʊRlTഄuU %DQ]KˊxK%@.K؜D|y$t!Wb*aeݖ*=nRQh.H)+5\+UUEJSЬ ؏P bH.ޫeSu!a=`Xw**J6Zib4*+:f@)B茄XZհ;snڴW$̵YVTw$@"<뺄]-6^K!(F\C G 2o%!5n/-G0e@ GAº< GH&#؋Fpw8ª3uH8f;eK8i \f c8‚F¦{_B# [!a<@0q#1$Q^kຄ u!a?I؜R+S|L$#PKxFp~˨nH K !9+0@p$`9m>[k@0$܁kdKz-kP `Iبkku P(F-0Y$ю0ѭ̺1N<6ʈ,gKx!aHy '&aze6]Ka'@F6tfl9+apú )F=H=aSSz! ׭pr͔ S\L0 C¦ ,c$jrQ!aK؜bq^ ۔'% u:P,#Y'HXqbH.-aZ[2 wz-rj0d S  ~BF $ Hi=H|#až"ṅ'@8J%:_I0q#1$Q!D{dx8pw옪x|V HAQ]i%#GەX`ImϽM&׊n@JLwhNC7ro7b%.f뎄WpHTHQ%Cs3>PHU=[mWYjGbVxyFos^Fۨb]DLˁRL JU- ŭ(yQ* )N ׭WuOF# *mɲ`9;jB\0ZݘR%+U1xFY oOk$lN<I\fepF0MH˺+U˔F)7-=ʖF`Zݹ "ӫIӒ[W,$^`t)1>^(d!63ՄYZDw=ew6庥, @S`t葀 2tP„˩kMH˺+~ja Цe** Gѹp1IrrYjc^]" ,9e_iT]fL(ggn G$V&a|Smj=ώ+s$/4h7]Kr bT+uD{ ˪JDo:&aI[$ 5 iFb./=N.?欄7צf ]$O2J a O7Q)(G*J(BdgN%j{C2Xg8@ xmh&p$ z>01P:(aBC 0^7 1In]H$as!a$Đ\F^-X 7 $B,:  a! $ 0$ 0t@֭Kxg$ Ha]͉Ez{IN2[J a@H~ #dmF§CrH aH a!aM WS; a@uB' ۞=}TN.D $bb!!I(1$Qe 'we a@MB Дp1l*fV~ $ 0$ p|$ Haݽ]H|#ab[ 7$V򒄻Cr!0x u!a͉m@H@3\FHH aH a!a o [wo @{%$mMv$I8Mo(#;_"diӺ0G,D{$ LzC $  'K˨u @օGf  a oNsH aH a< 5 ׭ $lNtsEH k$"1$Q!aHH aH a!a$ $  aH aH C  aH a!aHH aH a!aHH aH C CHM^W"Ixj OJn!$ 0$ !aHH aH a!aHH aH a!a$ $  aH aH C $ $ 0$ 0@0@ C $ $ 0$ 0@0_ƒCã#8GC,7xfП^];s ⷾYJ>sv,@8761U$ $ 0@0@ C $ tjm'9tH.H^:*!.<\C¡\.z=\_ r#ϦI!YY#')booC"̳I5$>&rދa $/Ds"̳I5$2Ցs.\!4HH:$!.<\C ;YVKR!D"5mrӑ|鎪6^Ed~ݏT/%א0$n4[h ˉ7ʂ$^$ [6wHw栊K}^JfFR_H”S3-[m9ۯ0$,M%WbXÔ [ LEr,e!."Lѳ\ٖiDZ\QA(7Cz 32*pxlOÅB!H2Z堻 e)h $_$wA2o,ʂ2Z[JOn3.۬\ΦčHIAӛ pxl{^^b=*<\g3r ǶfF_+f* ] IHex 3&%f!n$@£N \j]Vzƒ'f KTi/S"%lzXH7z#pɝ%hs>J%3R2xa/NȲETDR}kB>I荄'{ D6J,X[9} }C"̳Iu&NH聄۳S}(%HO'n! y!9",:lsIsqFbHzӓ\,\M,HWOO>tH.H^H:'!.<\gRĊč G 沢S;mr :$QgMzč G,å!Xf!=٬F2@pz ,d%?'Hl7z#եLj-%Ύ[ }7F聄wz;\;@XF$V!=y[b">@ nFIi,$ lKčHX*D$aUü`0@ s KOmL-T#W^Qo}_,Kteq#b+퍩{_/s_]w~[ez- )2;$T`JY,>{^ѷlR/?d?,{W) ངM[&_g|bϜY!.}ԇ~.Otlefն8nT<$̎U m柬UHHOO/_x],VH8dZZzMozg#}q7VY=q͑G#q@;4OU3JTQN}1q3S P,ow>!Ehz{]_(rSl70 ^}+"g-sÑ7ង{S>0i9@rj6I04S,+5ӤjWZ+UnT\}ϮlYc T#S?xd泑?s,R&_ğD&>,t$r#ckkTݨx~6?XV(n^\P(Dۏ?X,e10$D o?S9.ψ^|fɂK8wE䵋#D"G2GjikGڠK'5E^^PWvHYLŘ/Q)9UO(kd9q HKF2V6zQJ8H>yȹ{Bx̕o9W~;["/]h`BIpTZx,a*dƞOԑ7GM_?x_E)2/zKСCtkd ҍ C0hI=W]u"y͑'>#'?"rG0#"f*5 r&P"gg* wָ"PS$lll~[_D9㟍?y6Dbe~_HX~5HsGRRH(Kd3?&7zM%fT k/ -\~+N]rHtOm?t:ګ,WFO=26:ёs?Cl0+Ys(7oQv''1NOO :599I>5 ǷgfOG~@@JLM|;knۿ[yD١WI*I@._ΚKE> stream xڍ?o0wVBbh ) qħd@btNaűʀV @Kåհ.)a儴)І.?tO M}Cőrձ_&c%רa۲NWzՂT&i+VqԻR2$TޚD:FrǡI.ojq쯠c|7xP endstream endobj 767 0 obj << /Length 1460 /Filter /FlateDecode >> stream xڕWK6W>'z\<,4uE E' &i}vh4E^\qxG ΒLQ}X}̢o"TU=C%կ_2*yGiœ2Q^,6wןo:OIƊh#dRq/TW/H _pR,r._}ވS߭+o1gōLExJٔdӣ;ݪ`WcO `;>fY)C1t)7uIrmH-a{l@gz-1Dl#*) \gz x%+8'x)~LY (2KQ_YXO>86qyz ձpvTy=A^5BoZd,}9i g{;w%W! ~ǣi>uxFƵ8=|)R݉G=?j:xJ`Φ N'ERIN r'U8,\ oQ}@NeRLho|RАդqewu_̀3Cvv9tlkpeG^g޶5czx~``YM2'V.@7Ռ; -%\۫[e@J#Hёy߷ֆWXOCM-b(R_u PP͡\apI od& "߄4EgaSM;2/[j݁'$U$X#ʜB͈jfstalka4ɡ ѹUgs@G03"ª0y[<=CA  ?5[>uqAÂ;M*ڣ5tM*!nU53XkdqԄRr` ;B I~cME }(2_|p)3->n01d*GTҠ{EoCRg3DXx6?"L+8 <@g:m68dц)zKcrC WB |3\y6-Jm M~p€7$@Oqp4^ sƋ]R__-= ja6E+7X5J.*KWV*_&ʤ"_#g$ endstream endobj 773 0 obj << /Length 215 /Filter /FlateDecode >> stream xڍMO1sMdNK?pS/CV aݐ-[9x4'wއ`  1 I9 `vzTUOغ^!s*BBYR<)d~C KFK6X ̗@xՂ6.gA?oWT#Gi"Tf_ߌt}:/0W̆Ӿ>GP endstream endobj 780 0 obj << /Length 2086 /Filter /FlateDecode >> stream xڵYsܶ_G^ǐ Ƀ"ˮ2W{Z;ӡH `..;q_`o]yy׋F 9I{qȝxg){YƝut"X;o/qad^wvN^Ϙ+._l~ك #ΐ?SgB/ bqy/wKxxI}v4sxzoٲ$u~{{9-~yuMt}7^b&+я70 h߃A]CG7//mi5<n{+y_/6} ]YE_f!q4O&2p>9k+2wPB,ts"xzNxF3!OyS*^Z2(kZANHO9BU#qB[YJ :,6-e c}xwP5?(MM+I;,Wx'ʲj^twp-X]&5#J+F t4}}Ґs6ђe5.L(m=F/,h5ox۹gA+WI3 ] C=Huks4c v?cOD3Dx \` 3:AMA""ɠw1 7m{nBρ6`]Bc)p2xՎʱhXY*]+ 'vZ<.U`m4OmDDyOi5 f NQ$ L:B'U[Nfh9Q#"kÄSOLjh[D4CjТNd?( ˀw]X ,x`#-g&kuN4 N=Vy:df;_YmʌiWh,dw'b廡5L*2i:e;'J<'44SV]'?UrP eՔYRa~HϛA\FmQ I6DGUJhlǗ{F3i:j⦮L:Bk|8gW )tuKp5uCVZk󮯊΍bGYrklDnMԋTMQA6Tl(SDKriá*ȎOv&QSlt|<'o0x-xͯm`g6 fPJ? {V"lVrsۡK.rqVfIy8[}q> stream xڵYKܶ͜_3AV,G.G) ; _!HN~} v6YUNFoÝHí 'v^C|rľV;M6Te_45>RIhzl8|Wl;O@ps WD+bT |SRp{';N8ػ;dBLos˽斦oXP{mĸECᩓt"Jhh<I>zEz8#:,r&bA@_@@PJ)x.Lt9?:(y6}DmE(`P{PAs OrⲤQѲU'{k*ꍻMM-MTZQubf8ܩKXAZ7` n-Vv\:w ժ^Ժ4f}7W ˞ -hd-7w$ۆ7VRԐl%|8TC,pRxβpxF#"مhH_'M<];MƓ.05C0A-}p|&zaԼT/LG.F b/{  hciybM% u:5Ҝ`#&ȉye/սɚ gYQ ~캦Av+r )Ћ&m,vL]`QCW`<{X>'e@`KIP8L<"~.G:@!>ہ>F^[81:Ǡ 72WB$h Sɹc mQPz܌ V}y{`%xԠ+KxL:\T|.)ƹcUN~D oQYRSpK>WǦ4\b҆IYSU<"Ws\\ OА0$cOٶQp{Swh0*L`X "9&`d0$gOg;3X8vf sQGҏfPD"AgRfʀT< <:nx^[_ _B$'\o pE 0=C,wɕ؏]um})gpL#$-]|U,Ϟ > sZs/]Shs- ؄p4pgTxXy*ئ;?\cR?,,}pkBVU5 ޔ{*n#+L4.U=JU[{ݪc=,yt@2Ha?zGIUYCLV^?Ғ(HK_oyY}#چ_,lx~\dh B|CK^:ӊv!_)ڲ:o9(> stream xڵr6򮯘MTY4&J[e[98Z[N8$fkɀ$gk} >FtbjuF#VX]9yyūNB7\lW^۞W~n'u ,ٷ(.?ȪK^,eJxo7?9^b"v$Uv8Xi%l/Wza1|Ǔ?i9^D"nV}Z<hXJVIiZmGzK@Xi^kd9۬={Q`j+C&ۭJ3TS{9/Asn$DXX]M,-3(Q3DZ .q9Sbvkٞn+ J'!-ˮ.Svbk'O BWxPV2.ڶhhIm\6ܱCGWdyvvxN,V!~f2%؍v "> $ DGUz(o Y-Orڎ?ۑ5Dv{~m01 }}'CBn-44 B(1@Eƌ j'1z*LX})?MST;2Ɯ<0"j7!p%W WI7xЈ257\Or<鿭N^ʛu :Kp'. H 8QDk& /!eu} 0NͶ6eU^ͅ ^pח[i$g +ھijy.P̰fI>1AcN3,g 1CjA;C/l\>2#G<=4Zp!OlE)'q3Zi#b@0)afiiYtP,@>Kff_g(2~?!pb[&0lڻpw\_e Ed8h7摒vc&}-`6*І m˨}ۉbз,D}jdf68NAv)-Dv kIe.22RQ*r7)W㾉W֙T-momP{05c0q_lL CsJ,t!^M;v;rsv.̓ NLkk/73(C$~1(VW־_-%Ai/> nh'It, R=ږÅVs[{YWھRQ8ȱtx9%) CǤ#|!h:-"؆)>aw+H ?ђ@xƪ91+ ZijPkҭ@w U>cB+w|R*QNx`ZbA>6E k1vt '!P 5䁥`x3"= FY1s{xsF1Xk*ƝѫCC+'MRZ&aĄG$)Է g&( hԝqN DF/ B @; kީ(s&"$>\]~W Ґv,d߿>)YxS(H٧r.1"Q-ncۛ{Hే3!HMB8}|GZGg]_\ʟ+<X_%>I6خ5s㯸vťO@9 Yqc~@D:cher}`A鑉t m9Һz,VQ.-|px ]-C<2``k'G` Xjr bU70t,FBͲʋnAiꋁnnuZL'ǣr1knN~"A<(I3^$$D!=x4?&n9 g+\pG eUd/1qfML N_يL/ wP.QM J}(Z ፽}K O?g (A(R:Ÿ@`2P[?o;]eĺiĕC1^yM%jiivr%ٖ>ʎb{6/$k5ұ-7ir[.n\0&6]4>Җrǿ>wG: ct A{Czjg8 ^]ӗ#'+(79A+ endstream endobj 805 0 obj << /Length 1945 /Filter /FlateDecode >> stream x]o6ݿBo4IQU rH^j=$"h.Ҟ$vpH}:]'pf8/)= bqEf@/=43QT(R˗/#t)X,2[a.?X ѴSG;|q~仵88ߖװ[Ӑ?1 S(3R}u{o?SL 9B b` (HbT}1IҸOT<@ Թ.&:u\z]nT伤Os6yÎ{yʷ}"ݗŭ\7arSࡄ9c587/NYiSw4!':9Р8›r*]ku5+\@956L^kꎅ纫f6}hݮwMw'lukuEU[K7f{m4օa=;+Z`SV5UeEg4nWBEo}˱DRZcS+` M3ҤUEJ&nUaBC)rHi,K5wB$Yހ3Q1ӁWVMӽ7UaBɳHL)_$4xnCxtЦ]1S| \,fvzEpKp[B􁭌crl/1Ait 쀜טrp$;mgv클GQz2?Ȼy>{P: /Q=z,4,7,IN݄#$3x%F8( (y .f lq}ܚYT^G L0 )YTZm `J]2Z/of@~+K93339`)Sdn"O(vkʅjZ-o{Սc}Y:ICZvэ|BZΧ |NeAsj:+CGvݝ;PF$#HGy?g]I.&<>T`YN"_u]uI"}4,7,+ɋ)X q,!@].ӠJR`7k9.P-X1ZupWT,t+n8zt?Es ,b4c䫤1x9ǻ)o "RNjI& l ÇkF{\$b(W=AѽB^qHڙYJ:!{B'( 0] 6( -"|wDDoZR-GfÏ)|Yp/{j\`~"[(>J[Gu-V`룿ń3Xq8|w}K5!3) W{ 6e> fZAE +7çaξ endstream endobj 816 0 obj << /Length 1882 /Filter /FlateDecode >> stream xڽYKo6Q deQ u 0 Z$A!6[=-;!%J+nro\΍;/W_Nɼ, jI~lԋй*u컼oZom5uw-/yrǫ׫V; 6U}k,uԮʉǕ5}/HBJ#avc^^ g[U˔_~~wgytDyX&5& ,~?yMBme~ބwns0OWaog zsڸ'2GC/+~)`'7OR Hw3^^L;;^3݊- ߭c&z~m u:o;:sQ`ũvct]v a۝B)?xqz-פ{qmRACea7oԮZҩ= ݫF{T4҄ܛn.,uoTصgNG^h5ysb don} Sw*h |;h Rsa R3O "L΂`5e_cjP?q,vI̭wŊ-> (EF&s .-W: o"(b$78(gd1(^% X8븤BA׌ }a^%(tT%5'5Uю*'{'ʒu,yt'.{L.GCVr ?>7Ú#ޤ}QcJ́׼nnfϼ DIcJm`m{п!nK406.sUg `uU~;8Ԃ§Ʉ- 8iy6zu">?cj51뜷ԂYrԐ'麯FL =5i9T(]ۼ1GN#ŏb|Y*(õ`޷.=ͮ lo(g_*gi'D#^4(e'ִR F~iita Mӗmnwl Hu--zMLZNaLNB*ĭM8 ˤ2 }u^䘼 ϏY祅ٷ|1/ؚA ; ʝTiI:PgHWp7qZ> J"(#'}0ubZcPt@(2 Bl}NĐ9$H0I3A CYjTek~B3eͻ l :Wdj@o9fӆyȕ.Tn"x >*F* [3Cr8 v;whI9/l%ƒd <:"wp\U= @hE%\QW΃2650*fX1G|p0-YSǘCNz&.6 ZQ5ބeFɟ>c؜_oU*|TQ,_+~!jp62W?4tx hQEU3i*`G*_ڵ[yx3A?_ "cɡG}T\3(u_RFqeA`~_ \ endstream endobj 824 0 obj << /Length 2771 /Filter /FlateDecode >> stream xkoEQ-.hV|%CqA$HҬWVړqܢ|kq\3rFr,~8/; pqYD"cOExcZ+7uvK1\:m5w<۱O'/.N~=QY(&ۑ-ɛw"O xqEX[.^rE #c;^"^lJb㸥7uK;nwۢVVvis'2d1+)WKX*/`-5-y6'nnZT[WE5|a[vpj>mNp}-}iA ,K,Z6VS/j7X&$E[{@ &XWY]7yQdF+d~@J ^J htVpd\t"<j֝68u o9ǧ[oIthcbC xvǰݑxqp!0BzloeqDqРG)Cs}'.øƝUBpA`q39Vd[h^g#-S 3pT, @NU.k̹qvQ=ҿ QR,hzE r!k;{ʵn8oVXO)#d\&Βy?Ɉ4,%6]Po#߁ۉD:XT9p:6X :[>2yv >']|G՝~Մ.&wr~ .@9}7ŧ'8Bg'up^CyOD@'FʙhzN<NSЊe#6MP<" 8ĉL1czdnKKG!v Qr"{lH`FXG1<"pH ر~GZ'BZ!PQ!J1J;-P>7h b%L $ %^W9{ȁ鬊Kٖ!J*e0-"KE +Cr!!ρ`8Gx&PՀ<͑(D0v4mZ\i9h[쮜@tp2O[ ܺ*ʒ[k;&yC Y1#,6"@5"g9,g6'%fa O{Ӣk@MԖpYtzJtSmYdJ]9 q5x b6(9y'3]7ųnџ3 8*i}T)NEMS]E6NKF7"#!^w-nD Y LlG툨yvu1ы/3nxDnCIf[L rP%amvh ȩL=Ϥ&ZǠ+!0+Q ՗lGR9(ǎ$pL 5ͶuXo3à ra``WPC'A:s7إm||N H>j\?}p5ǜhavM_IfӁtOδZq< Ʀs ;>>wC*<dzzv ( 64nwM)s*ڧ{ѩ?w$g3H'Y7,KKmt ޔȐ0p?WK3!BnZgr6mGaI;iq 㟶!暧P (zTRNKPijfou5%W8ĥ+4HdD"ϋ^d[xO!SsS ҽ)I 14">S׈857 2<b*!uǀCUz*-]̍m$*B*@z I&& C еN}@Az*gkY^PLM>K :0+_OhTȲ?t T·7mլJ$ st1N;捂3fۼDM\g KYJfֲUlWD‘:EYmd\4E`eXW:J9໿y|ot6u= oi1$Zi{4'fN 7U~r 4&G>ᱹ=Mwd2AC̯#a/;K<ҭF^vkL#m'4[{׃BCj9voQ z0 DU;~{7=J菾MSγF4 %.u&_SW<ζ~s %0(/d;h#ߜ}O6<ʤþ)0 ,<;l~C`_HMpdHg-8)w1#/TTWZXQEH7QP^#Q}r"k%5uN0ۮϾ]ޛZ6pgC%`$ ǯ/\xqof?GT)yJQX϶ڢX{t𻗣gP [u.SLkm6i&ܧ?82`Y endstream endobj 749 0 obj << /Type /ObjStm /N 100 /First 884 /Length 1594 /Filter /FlateDecode >> stream xXQo7 ~_v:Hi -ذ nEhv;[_Ngg:;A9I"u NSqd8g<џ]xfv ?夎"\)NX;J(i'~] OJrU4  X6 S&e¨\0`%LTM0Eveq8$` %R`a*sN5;<1 6 U S؀{S* R  LUk).JnRjqEF?A 1$& cGp_柊 %a[*l-rBP^bUkNk?s_Lh-Z '`NxL\Q!E F"lc)` 091`f35 An1kr8b ZIk. V ,#2'd&c3@9j$ . $ L.8P3sb&Cl+j&: !no_yt/gxA<½ =yז?˺?e+^9d ?ebyX8+"8bꆗn-)6.Fpm*JwGSWe+y׬SF&2E@j$ުM\M&N "M8>oviۆQOvzUhVӮ4yVpɬn q9z\FN(^7 r07nTc8;[jFnp֎+!9WBܑsQk Vq!FP T[&i6.m. )5a1hmb%q(H4!Iƶ m7cTuZƴύc[Y)屜Jf˜}Xہ5X5VP- aYrB#pHig.52o[FrGiuf."6P)ҭ7Qf endstream endobj 836 0 obj << /Length 2964 /Filter /FlateDecode >> stream xk +sFoQcH)(ЭfoUޕ6=ݣ׉vo\`GLwO7uf9:9x̋D$Z's c9:ɬwզq9۬TѤM^ZV G'?<=9\%Ț}p %ug,?wi>ہӧ3{t",NjP:vUiqj (7-\#钾|P,?t FCl1vҷO _q4W5J+`mP J1N }z.7Ĵkψ[> %bR{߾fu*miV^WZ,T,_mx 3Ss',T=ԡbPyJ*ЧJOb_UθRLQ<~ZJ+C%6YYVY^U :Lc=jIݣUe5#W ̠3pA#wh8mqۋ u{e6DT($bp{lT1:8jNQ|L ;Aux "5BMPd82ԵR)"J)"3Է>}\f'Ԁo/F>zL[Hϫ* {Aj[hWYxFz13a")- GHؒh}Ϫ5y1~ }#4ÞWl0`C pY3 PvYw A ؐs!cH<cRJD;|JQDJL6MoӑG4Gɵ^= i_eA]'Mmf.vkVú)ZB滱8Qr{EASSkp=I@=ݞSJkS+j񡭙s']$93n Q 6cgC4Շ- &wSiL?AOv=y5>/<6u2:``vv>BQo'Bpar}]T+޸Uߣ5k?RLԓmviKI9: wDAwǒ9͎]Tƍ6߉ƽRͦ*j3-noh?7 m6 ue \$ qȏp9 D5,_`l 8W ΝC9Ƿ~J׏duNo"wx3YY|:JGhCf>kT{?.֒g8nn l$-}2:Hy=X`PJ*7k3:&)E>[P%V>$,N)τkPڊ}~.71i吳B8XoH8~(3AԐƥ^EL L6P&3Hv|&'d*#5̪ʌyG~fx!RŞ2^XQJed&n>,;][n @8?r0tѵnHЁ'1&5rkz]y3ŨtNr>b/xTIm"0& {3L,ڀ3v52TE*j 9Wc*ݩi>^0t%JDG`xqt|oa]O?KS^F%-]ŬE Ȗ {T" 2PADu&,JdM"F.E"FrPX$^5tKM1}Hm_|ᆷ{BJ{rJE1?A_k^ F7 endstream endobj 841 0 obj << /Length 2782 /Filter /FlateDecode >> stream xnF |1'h:8vuVr -g#ᐳ|H,߷/Q$XUօZtvꝌIBZg[+VǎH,>V]DDt;UiW1>B鸎zgG>kyw"76_]+$1< ?1?csWF붭Jvת7iIgרF9di \Ywgj\;튶Q, >G?`2I8/rX\GI؊&V-6&#[OG U߇ N!Y D,0zD|8Zwu^;heY1 D#8 ]ph0jՕ* >HL$EH&mzBw&_9lyO%탏=pk#L?vp$6"uJ2|6//7KlaO8)iڴnljqy11H{l(ɷJh(˪+2VAs"GH'A/´jl[V`0<|}L F *;OT3E[+71GƆK(dfvֿo!Y>LE!rhIoyaIsD|J( sjxk=;Ta&4 j?˯+jҹdUZteJ5n94.~ZN 8iqTe@&BoN %}SU-Et?sv`0H9_ $c{SAAoCoolqκOk5/W´cBC=]#XW츴ztoI$^6yUM 02PZh*h+H{ʳOwC+pn^,_5UMח fnɒPOT{/pqsIk4KF<9>.婔^5-=nVuCU9Z-mE$JYWuWa`OylZ”x՘ywLi%oux27 yfe3DA75X:7LpTfjQs*~keUG>Sl b$[YX܀~132a2J7s3 ٯ,oVnjelqr26 v'Crt ^UK@=8FYȇE"Қg窐',^0CC Ɇ7S1E⓽?p3F(ԉ >V6E)^k7 sCmnI)="X0" o:վS- ]8cb&q #zzHUzrCD)VK̡ d -}-˘ , .ݲx|d' Zlfc&M-YYrHmXc$HQafrD!AX@r$T_[ 0 r'S<vl1}l-tZ  3z_1rB&Pq%eS!`3C*n %G[%}AIzuu㚊T{KqIZcq y(^0rVQ:ĎB3û x\8'Q}>C]qbWE~aT#4C.qeBthAOXG7/ܖNxDvoΕ?BFPgZߜSYA1W6:4- ٗ:g~w3ţ2lQI@i#ą8Qq`2KV?"Z%r`@xZ%ⶅt2Mf&}{_09ɷLQ7"A(e7?⺀Co̮ .ntqYR;vOwuoFJu1y{U?;R`NrXVmS 5 GYܴ*f(Zb;%># pL W@J$RlB(V\(/=o*idi몬vFr++%*fa1q_jǮY{U Idcs,Z>1i So6uo;4ƕȻ BV\ISbuRA!==(Y/ k;,/)Oo~#kc3\om 6AU 8b*!>o|xc%:xC%K| 5|:Ad`bx py endstream endobj 852 0 obj << /Length 2025 /Filter /FlateDecode >> stream xڵYs6_GLVHQC7t:\>$}w^]%qKi]{lO"$A~, XO߽yPDEz4K,ɣ6w%Y߫5RojthUNQGD,W\z_1ł88Me @5`(j$[.}rd"'YDF"ݭ"fa+](AmOqS5'ڰJ7)o*^[kg^o&5oEubA{gtxݎ(u"$aQ!%Dл]w(M٨^dVU'J0%8mF(NiܜvMcMwUZd9}v&QFR;pw)`_L޺vnqob42' ^J7@Sw8e7&lk]O7npąQs= v2>Mw򰵆xKcx{vikTfLzVWp:7 v:6|0,"!m܎dO۬\(K=͎M}pZ+!CsT}w_ؘ_*qrYYSɜ":tbH3^bZ VmKan@+^);ww`\w8Ny#ZMo/qy,Ɓ_(j88Q:3x1=kXE ޤ|@s-C t1ա{ٔ! aE[Xo+'d@ѳON$ZF\$'Y%2j8LGY\u>Ѯֲ'ޭ3bly ~}ҶdJ1h9^>Lڈ|ܮZgFqv̥(Fm1( S<;$# [Ώfh8{Z|9n<{M}@ءFCVn?=.G1TAmf \% BnݾBԲ86\mQә_1/bd7"H>1<`k,ѫC4cw?8?P,eGE8HHO2Y, ;6*A}969x;y$#DtA$ؙR,Z[`}!|ړ=-R=] HLT)dAjl;΁3>;6'՜Z;jc;斟Gy̧PɼP!9<* Fcɣln&c/'N3'Q/;h/o|r9Cס+*G?Tgez:G=%(1xuXY4#LE_p|,Đԅ1Ʌ}[aT@=YoW5U]Gѕ/OJ©';S߆qL'br0| f<lcvtbD9&]vÓC>F`Fi ۨ9x`et] VqhpNĽ9[ 4rd|Pj{cH|A/gKS~ƪPA6ZwT~'8i(oeW/,Pvɺ}yP sGp>i|;q\W>#pܱ|+q'Ҽ8oWc,pNJ/we$_7ړȠ|?8I5"|gN9VnDlp}1.+C>սuGS-+gEpDw7> stream xXs6~_[aV$љ>\{Noz7i^)90Jϝ:/}iZI~Z΃C'?&WIHЙ-;Q YܹO~M0X: $&wCVk tn'Mb& vH }5+dN/7C1F!=JN|_o߯Meۍ,] QLys.褐B6{Qiخމ`ءzue4\*!VC-ѲETMF7mY=Bgwcv)PVl֍'KzVN~JX-ul$ >VwګtMk@ubMČ8>| MŦTRBmdfȉ˄܇h̋qמ0?gDT1wY-b-V~W-FRUh́=N9kwv>bϨe;Uj :6qmN3Q7lg)@p `,( ЗFFa$T>ߦ]zF)9d51L׿cQEE %si/K}K[Lєz#MSgoz)-t3&KՐcM^`ٴ^․h .fAkRdSEYF ٪0UƩc!UJn+)6K/AN[()uVmf .V:aLI@9],k @# Uԇ!SKҠH8Q BQ'fK[8f ㄋ#퉣a#oPŚ[EPU2A= ?Q"Pj} _oYw`;4 \7Sfė).%HqCm`J>5~,e '! b)MĘD9`9U$"N[>c.@pԭ/0)C}I|h+|,TF[KHӢߓ'z:W,1'LDyc;]D1P\?=1_E6D]ũ7׿X  endstream endobj 872 0 obj << /Length 1632 /Filter /FlateDecode >> stream xڵXK8WTM4>p`قBj/AąY˙#6TMZ--SoQ]zb{ˍ) h%aJ,{Q_ԿORu>TDW4}JA@(aw7?3gQy !Ih⭫ˁΣ$RA0N>Q0P+f^ g̏#Yq(AbnNBsϋRȿiw1?1-<}9_hDB$W~wQ80ԿǓngmCf[ѡe;ZRn40m1瑕\.6}lқ]򱵛캢E!s Ey$5 %``FZ=6H~׌q(rB/ nF{` '$|=')I)Wbj݁sm}[EDΏ1{{]"d(סdwAkjdb Q!iXEYjeercVcFڋuwpxA>16EiVˆHcdkÇM0|u¸hڥ;Qo0[{wj}ی fs:at10Sa5paa,cRx2 zwF3{u! _UjgUZLZ;Xdknڦ2⍝;^29]x9]ό;-*|Pvg!cxAݭyQZn&MbtF1h(`|VbQ_xԮ9;ڱ~m=C@--}x߉K9R_[.9a$A凷ZwhHƹ3`rW endstream endobj 883 0 obj << /Length 1897 /Filter /FlateDecode >> stream xڵYKsFWȩhƊg*eCliǤIZ3hIQ-F`;vnɼ, gw礡F[/ʄ+w ӭ{C^e\u{w^4m~ϾSgDuVD 8^;08*  Gwg8V狔Ћͯ_xe/~sfӾI‰{}*`1>ZWW_dTw֯߶ͨC۝9\AVe?UU{uG 18KNR{hɭ&]Y CRϐ*;%+ájY*Á8v5e]6e=ԋA`qEE>D17A۩/<3"=`Kg,舾kY߳.[v&)aq!WP}n#bWaFs#<;!▥܄hh8!Hn&2/xox0Lkw< cyؗ|~ho%F𶉘Ix^s 1?!`=&7_޿.g,xsy=uY\V*ė >Zh%{5 <y"F N}C Nc0,1L<E'VZ6I욀mjTe;ZG֦0M]%shE&,[JkWiUsM p$ iׂ )%&%bcM X׊G$ZYxIj'.I(Z'1jͦTR7vğ]R*k"[I 0t!  KQ+ Ο[=ڂIYJmCTG&.f~!H C{U̻}*wCK7A59Ys gr.|c #6k%-0'(5N`0e)&\\\V/ 9?)~ '@OiBʷc HPlBcBgU%$+sDGI(%Oa(/L5MR$ A 8R3 \\| L&qFz1/y#nY)2A&L zTg .=yfJ%ܮJ1".sC2ʲkޠ# 6]Pe+6^͝Q,T/vxє?/|2Ky;gox@3z3ϐҊd_Omno~Q7.|_ n8b^zS[8~p-\% bksFo!:'lͥdΪEןqM x endstream endobj 893 0 obj << /Length 1625 /Filter /FlateDecode >> stream xXݏ6 _G詖l΀>tp0#=b%j[5Gñuن"@$"IoޯWaȌ3͗^SKDЛgޭ3_n/ ZlKYUBFAHBwדWɟ kb#('wpzzVEwF)1í4 A]ńi߿w{^)EMin_qr]D㖟ۏATVrDWq4A2 NԮ8zL1o|SƌW9PA2 QR4FGಀD,tsmcUE8ka,D5P4 ,!UڵE޴ĚhOR@1Q| QY.,ܫ>ulΟxd=G Ih4'f;QOGpW q/a=Zh֭o  lOǬ7fS7R?f#sszFaDIIJY?|-=. G!id NC)Y:D]>0X8{sg{$&l-ϧiwr9kY-) w|̆c96V6>rY'㬌mSM r}=‚Oĩ94@ Cgl6F^W-r«'Ĩ< 4( U)TkC˘[Y(PʼEBYѺ/ K0Mڊ63 I,o}!>7ܮԮsK\n.DQ@{5{ctM/V/pއ j 7SJc'eNoHsJ#ܼ<Ō®Xcu-RWOT`O2(Z5pp/M[KȌHőY2ZN)2}vm 58/IůM.m +TըFFmtkpQ6.ʲ y-m'r(jO<ϔbgvQ,euM1PUMuJ1 q!`A/B8g ~_߫I0a|x1uF F!)1OzBތc7]DFeKs-,U] {^ѝ穭ۜ йgVQE&zx|J*{GaӮHױьۼ-$OKh}pѲKnZeoPi T: 2}Ty]K-m]uҐa$? Tb:8b~%@ g.j) Nג.^R }΂WM+*d >BƵ6b%#:Uzw["Xy3XP1{JrT O85H եyal3x+l;H°!ܝ9^fYs<*Vޚq9x<};@m'?w?ǃJt_l1͵( >c.ͅCWz8 endstream endobj 833 0 obj << /Type /ObjStm /N 100 /First 876 /Length 1413 /Filter /FlateDecode >> stream xXMo7 бA#~H"#@>@ qmdQ v qfwj6b(E=Q$g=dRUňq {s"Ǔ9ĸ&цgOQR dON^;+rcMNwI^c\{=OT o+3F\ 3M_ y9BLKwjh`⡉H؛V Ẁ@  liD 6S1˃ @ R4 !bC0}JD:T|ahL@fIjz Jz~%uNp7޿z6\5͊jqCsJ+Z&tJ''i:8v=G^pAQ?,7gtNr*?X֫)]o.fUVӋś圝fՏ^?~LFHRί EŎ~cBN3;80#&9b ǒtHKe"%c̦CeNC[ϝm+ G^?Z˝uzla|K}z^;u}lO#qEr'9Bn9803svN])n ꧄S4hLhѿeFQ ЭddFw{FQh6 %^F߀#Ls±дq,V%ҏq^$c{J+'k1 ?fT*C 1"}Y"5~;ae# C_p^:*GDC$?!u_vǴRpVw-p39_cxYn =m%q  q6؞?fm61u\Ce#؈È#F/X_0PIeȐ7 H7鰏>)Y6C8>ˊrnm9W[ǬGC=o;oȦ endstream endobj 903 0 obj << /Length 2155 /Filter /FlateDecode >> stream x˒>_#U5M7gwGVe$f)B! _~4r2NTtݍ~ zny"Xq-^{EZ"e:g(Sl{}TIA4u˻E&"&EXxG/ E=Ӫ%|;]("  ܕGWCEQȲ؝* 0)TYYIb|, }}F1': y®/0 0mQv_-J{xQ,1r^/kU}[}E`4ӊ?E BYᏣme;Rp>NsLf&nOWawo2:mmo\hřm%f])0j ppI[ ;%J5(`[5$T#3X<-VΙ5.G뮫?X}Ri"JG;'( Ȣ3zPt?hp.BG3iMSWX"6!HغyPuFh%1=Cօր Wf۪va#8VH<vA[Qp8UU=iW%7;N7jJr, grLsII'e#%M<0x*n"v ܸ>0 `B BזNzSCf3)` `'aఃЭa(!faêuƝfCk O ;\P-,IG=և+ +q49(T@Rr$밠iNo/ t^QH%NͮCA@j1V^k,5O^O.OvptxpeЮf1T@K$l#XWu Fg̦+Hkce#;]_R%~E#කǃ]Qw8: RwiYqBAF y[90 }e((b#SݴkG?9h' 0t)P$|'+((Ryń^Is؆r"J)F!rBR 1I{/`Y֕SUc H?FjQy{]¾dk2 P@ (y20ډlN6 ̬̽Wh-eV N'I8IJFIy`Oϔfh 7ZͩN.tPXFNW6gI(/u80#;pψP(ĵfe42Q,`̳0W* tT Px൅qq N 50i% rKᣙQQ(TUSp;J:"H"2AuרZ79>`F)/R$E5lF3ZBwQɋO0 cIp0=CVR-Jm+ ^ 2{$EPU(02 շ/s~9D> stream xZ[o~`/sxyHg49KrXr̍)z!ɴ0`C\sm,꯷W7nd(]Zn!Ћmf>}{F|-Kۂ<ᔕ{!E6ozw{+ l [d=ڡWwV?X6rȷ "ͭqek9_AL9+ UÃ{0F;B5vC'I-lOO]Yu-GٰT%icгUgp4.TFymP g$d/+T,g/WUM͞PMWAK$ fSٚȖRNΞLurN8`'w7IސPCewX;Z_Gk$w"b͑3Vԝrխt-$o͢J]l̈́IϠ)KxLpK,M90aQ)/P*x'U 6CY0<{XBQB76{ۆpc}0c:iԅDx |2=ӆ>|6E!giNK=d||O|L_kք!-,B2 2Eb'g7켱!"C!QѭTy3l?Ƹ0`7/t=jEQ uI0qK 4 2ڈ==g&}"#G: G~iT e=!ֹiRnTı=Xgo} endstream endobj 917 0 obj << /Length 2248 /Filter /FlateDecode >> stream xZ_6O%) !&wE.1RIZ\[WYr%ywO3Jv]6X`Mo?{+{?^mq R*ŵK/ȼY}ke?TƔn$;S31mū !y-7^<΂4n[/T ]pFj0{,8ALp?Iٻa߾VC`J}ՍYTo|ү?)Md7lRzOLU>3oJ"!u+\ՆZGWLq0%{|_Tp2)ɤRֺF6SL'򞀎9aת kl8dR ƴZ*UyP2Ī\m뾷6j~37 ?]\@Y%m^f y;Ipy}Y" XhJ}U@;&PiX`29cj xeg_TUq/Lۢr%SJ V{Cnnx7'pp cX1&CߡdOl+jC#sVד]]Sf$)Xjدd9 yFLƸcKTW1˖a]]:פ4j4eB=dz7t+Z_ {k:IZl Dh*Bi^3'^itаwZ`U^:Ͷ;ܹˤ`Ӆ.EYVȠ5\wiKwu|+ࡅ@QȧRƅ$#a*؇0 YL<(&Dƻ}^bA#k(xlV8uQ[! B1iD b8h>a5q*ѩ {dD]ni8ඖ %.n!Àu26p_ \u+X( 4>6{t-r<{[7E9A||} &LhYbkmQ@Enuv7$Yt7b <ㇱ,{ נ1)|A8j"!h0 @jWC :_H{zggՄ%BϪ(yrk( H%/ RЃ]i޺!dI> stream xko6{~Pz=vfsW Z;Yr%yw_×$RrbYm D4ysfHZ[w:'u?D["7rĺ{3~/HV,[q軥+*~8Fxp뻋_/0e[ uQ`r{q`[ oȉB#\?of–x|\[q`?vg?B6Ɩ rAQZv.Z}@-.O﹕,}vQvW5gW<,w{um{{ ^MIֶ`@nlW1_o#@`a_ Lt\H|rQ4>yRv'Qz_X (rf?tS JqE|)q괞3 "; PRٞ-qr NGzg;^Zu=Hos.#5+1tXv^}-$IlRdMĺߍ,֦J|shXk-b o/-4w2mШ}k{5tShj/Yv9iHTd-ǔwli)X­MܦXoE\]5>D¿Rū}:%aT^R1'ED [rǯCDHt[w)_8ah$X4HB5 n.ZjC덠9cF :OwWɎ`ëp:i)(&\jVM/@FiA{;UF鮣=`0h= 9Nkųac'd؉Pt`~9紁1G{ wڨA}!q5< eوEQHgrfL/N+ Rf٘sү~ G+lh\,]cmVI+9b%&7'$:.wfv@T!YD'OYm⤘oQ9dwW;Z֩Zb_3kP$%4JHšzlY6'FVYd֨ ґW6?]Bt g !RXa({Iy3+`R teZ%\&UIRz7 V:my`Ij3m`qƹON/L0v9żIm֤ئj .ۦz:r9+~%ef1ˌlPBeW!~0:I_}P]:ȣ-Eʹe*.PY#xԒp[x~QgӣǮ=~+gX] 3ho/T+x>x Y3bPL^:#u| IOGyrX6̆(!_ya^gT{~ !C\ kƙSU4zc9iM{VhIݞ^zbU EEՁ{;L0<4_r vD+tBt.(E<){)KId$&$ȣ1}&07rcʦ%k\JTo敀]TUb+XctM9s>R7^etU#.3ܞv_2l A8/d pUM[:|ɢjUnj,.&ᤋ@&)4꫽v\D㳒AxE!9 Bf鱷U9'`[K9`8[V > stream xn6_u/vDR>dv+`K-ŢmIJlؿ.qR[vX/slkf֫wgg/] H9u6c Ydq{(/FcGBeXYz}2a!%6g? P@c[2"laMm+זMVzbz3m`m=,lzĦrPWaWL$2D1+&8e ~R`yh Z`V^/ 3c4[4gzR6m;!㊒&crE\nP$[D372.XSszC;޽ Omu7*i4B&.n @ Ճ_,@Xtr'ʾaNd*@-%\Ө+#Zm̚y\쮢FS^eMK^*ٵǸhy9䲞ļlv,Li9~M84MJ}B6N\X0;p [:Yʍ{]~ϖ~z݌Q!v r.7Z٠\yZjvmqZa:dox$q*ϸ?HJa8HעE 0z=\ss?:VS|6$ejR3dL?}&qhDQiBrծ!=Vh,w]ދXHhq^'sqUvHvSi[h<j~@ ʁ `sCkvYrP~m=JUD>Ė\{Gba~rRIy*|p@,I/:k8Rp.mT4qxd#U-#zm-*\,3A5r33I1HuT0b=LE \`ku=UJI J3d?)e1 ̖r*x}=&n5sͼ6݆fr :qGTDҭ^+c 8 "kYVR!@ۨ}ARxWYWAp6S ȕvw>h1>_; !2 z6z%c[s"ȎPūo6Ki\LMc]s20 ͈wd{ZXsFo7~Bcl4\|<\HC;vl+'xI|s8S8㯋NXuUןoA ] endstream endobj 931 0 obj << /Length 2160 /Filter /FlateDecode >> stream xْ}UyR+7IW!w]r#A3HFRR4JuXGnv6F_h4Ac7= EJn1"ywCiXeU\%ynUR0"_~ywwsax8_~?x(f2x;:yWa Qq$3ԯC3 Y`6.Uz 㓩.a sڐy$Qm$0$SǶ$HTSTq ;_W2lO0\xq&UIU2u2"R@ypx)f,I#coǠZXTo;`n[\|V$Mi^:G7XW'"huy|WW|Eb,]uo;Ohk^>~L2J7B M?_yylMNk,fRiWJ /SL6!8C$ CH¼ HͭX6ph:08GǤ@4 9 v@nځ ˨!.`EXԬ|IfۓEg[k$$ jJƓH !mBVIk߷q+u՚`LVU^v%?8'S9KSShef!8)v&qI'i|*91MIӱnV`97eq 87L+7=BH}:MK1`$Vr"Be-oKwӤTf1I(F Ј?ӱ'8xhaIUZPM|lB508+ΧC(mlC[Oj"J9%xk+˫ `] 5Yq3%:ui OLk~P$ʮ6x1u5BNš^+v<+-M!D Zڳ\8G![ZO;#ѐ?Ls=ZX Ύg\Kf}* ֭]W]qg"&wL("X֝cy؁ y;YN}E0* z S{4n3 pgJa+:J.vF}aEĀ0=b y;  D$X `+ mFS@\)> P4lj+c|̊Jk&q XcU=.շnrwb='( _xӟ"zrN$<-"sp0oJj2;!4Xw Nakf&۲F%Kϓ ۢ?ʪ8ʮ 4ۖ}j+ Bw^傺\k-v6U]\Kv]/A5'LbRMP/DD,Ó220%% - @=(/^cbOV$ü/ڮv;GvEg7ptV D@}[IN}~Q5A>!CR*ZMbK> qf _ endstream endobj 937 0 obj << /Length 715 /Filter /FlateDecode >> stream xڥUMs0Whzgd}XJ7ؒqY{6NݧȊ0rŖ$4ByN FZ*̗& G4Ɔob[eM»W},d8)<_[! #i%5̐E20'zHm[OUY֬>^gG:hjw41M%VId$yؾPb&JP_2AjS+g<@|`C3΀"}v/P$%L p2S=˹}1Q7~Qv}UWq()8 ;kto@84H&X+8kp<Ư&&D fMq vEs>^R8ul{ρp2PHCc@}3fBb=Vg&ߺx)b+iYHpA$p%zC*iӦ͖% i$X} Ɨ0Isvpu˴| Wo[1m, Ryp :t&q1/h190^HC2X,Og<{EI>˕B*?HA9>ь+lЋ(E q U,> stream xڕQMOA ϯqe쑏E!9vE&,Syo{m5AÅ҇C xXmbjާQ玵cS_`n D#X! #iE6_6_N١f=}>kLp 7ܑd&dXCѾI?j6Ϯzcg-X"!(dx:`Q8u}VuN. ( l^_y}ANw,+6xls~:cé7GCk endstream endobj 953 0 obj << /Length1 1385 /Length2 5981 /Length3 0 /Length 6931 /Filter /FlateDecode >> stream xڍxTTk6 ]H HKt00 )%]! J H   qk}ߚ羯&N(G ez&V HM/;)9 G!e c650 C!8$) b@_@,@ wnP4)*dd~ݡp c\؊0`xȊ"(Og~!0P'ϑ`wDH.po {BX"/0x@B?ODp`0r#Hg  4tE0!FQx7;bZ4`CC<94mVG:ݡH gjpO(~ ANc8yy!wj0X6g( wP_~__f A( ;4bH`o( o?W  p$ٱf({p_ K?;[,ÜPH_G,lb&g8UTPa1$$RR1_H ]>ղ?QXB|P^@g gH `w8\/ Vz(Z@KWrWƪAe0o;:1߬m77 5D?0( _> nاK_.(VC~MLBbt"3@T`C0'σbP> #G~'Vl-P_(tap}y^#~pe;&j ZQ.Q]чSs,}< ]*9~ ewB1;OH=YlF`oÛj1wq_W|fe7zZf(=`+w>ZŌXݡJ26}O8Scn5GE:}5Lt9GSsoqxT$NU+ғ8J#y v${59뫆^5٫%1'߻|jcE5%E! wHZqDA>;{ Jw9^$C|v2H6]!=!cfxq+@Mk5zCHxTvΑިG6$ˮmW꒘zר{ę`||ÎnM9k%IDsqBSצ[8$VCf(h>V=Y|H>U>]:xoiчDF_=Dňt ]yocn&.UC TӖ߻SySrC5"&g:xK.gxӷ|:hqF %Uǘ u;OZ={_šOlnnzm7VyWE @C;_F|F/G&,Vo,*\6! XXcl\i[A ^Si0<}5o[cf,z^fy)ݨ,j(7 pg {r^z/~5 Q4&EBfMc|y;j]9w{ކrio"˪pV}iNϾ6eOm>_kBD1d/?0KFCl)kV6d,dӖ:ikA}%~013nW},$6Oɳr% =2nd~ MZ)~rjïLI9aз%>c%Ɔ\-m?^w%ߘ.q{"LB Ge}7Z "] g' nulVɫv7' ^Xu\!{w @1'd=ɝ<S:)ǧ odl_cֶJPCƎ[Rъ>석<7W{DN8"=5CM&5~yi> K*.`OSo$z7Z攍a3B `ڸqSS2G|&Pgqi[u|m泇+0g7MʛzRƾOLou#bԮ The6z<UMbiUy#y]?uYhc+bo# H,v-F1k}¸lC=441[҃=% N?\LC݇62-{:”|iY{Ѫ{e(62q Qf:"$Bc炎d}95OFxg͆HIQIB9E\ThčNQŨ[Jx$Q83ՠt|{ h)Db{tcqqNNr},uHꨰbg fxv:>BDN Oc{ZdZSz)(rz6 Vmݘ=p mEm?`c167n K*\`i)ZL= e`){Ka3[V`lY ;O'Nqd FO.*8Z.7/XTf['A~<0kڙFݗ;Bq{юҦ@}+MX/!ݲY_$#`Sfw C.ecJne6ާ :{( =tV8lyfQ,{TqGC+c9ɓNGe6o{;#l'LGKDHPa՗i-LtkeA곓k8].0U)B(Cc,Χ ۟ڥԧϣ0cKqisUL҃ܣ5zE]'!_lO>aѺ \C^C^˻!WG[,qnGĊbLյs|܋,TeF칢@o lKJz'O6Egx\_1 {$᧲dΠ؝e bc)ɗSuzI0Ts,РfꉆGa5yTd{~ϵM]_?5骞^;iSCAoڑ3Օ8ͺSCdtgDݹaAἚB5>rvGF VuN[uChU|p'lF9g7"4|VIq'?9om*Ok6>yjJmZ h 2f"}ĥQ*CKmȜYoa2|GXРJiμK=FV|USiɭ+7꠹ۼoI)ÈP鋹(RyTGޫ=':Kz3⊯+lZ0Q{_OOI`&Gr=$̒6QpU[Nô8 rvIxe)bC t0 zWbK-275"'_ZFrcˑ["#H6,Q̓9l3TD:hkl*bM !b6% B,UtZ?VʐKI{ˬk%i]58UY Ì) eT!qStQ{[hC %NLp# 2N_AyОdqdIi('] )e֎j8s;ō,~L8S3J4H#rU! فղ=HހLg+aA2F%!χw뚮S}p-u1(O[Yg3[t{R1^v9iޛ0qDϪǒ|9s!.H h])זs'SNGTfoS'Eɭ >$Kk.POU ls }~}f2Gh) ˩Ќ5~0/;+degOԉ3sHyD;^IuI˧T=j:DLϣ`9|J>R^{{q /;v$Ck9ӵbWH7m]: \xKx4MD,>T6+X}$y? ɵ[~J?VzЧ%7 8(UMl? Pz)XةkcN&gfǑ@z%ok}f1f $}2.m}G|=hGy2y#?u%"_>Sok磻1nzd-JJPg3}#1~n}mQgbM^s:t/31]}[w[FOOL4$&f"o&吵{":w c#.w֡bzPt¡!CeĦZAѯ_rRY? 7<|S[Vesf~~6îMcV]!e FlhجUr[0ZQ_`i{`J&X)4;m{GGNo}1|.Bjy9tޤc~; (|''<8/ܨO~>9L:%zKg_M!hg~%;7!`8@fnΈWwLXuS2H \\_1V) c #A4$ƦD!͘XѦB:ǁeYWӄ V  >LČ{mCĚϞ/zؑ>[Mۊ[9~CgeH{k@eTDIBmY/AY?om endstream endobj 955 0 obj << /Length1 1606 /Length2 19042 /Length3 0 /Length 19878 /Filter /FlateDecode >> stream xڬcg_-\6_ٶm˶mۮ.Wuٶm۶]]3scp"̽re;N!%TP434uecY8 YYaHIM -lE Mj&# lghaf FIMM_B@/Fe  ,!)' 75q4(Z[d,LlL(v/FvDK `p71 ; `h`v [#k` vNNF΀YDN;ӿvF./_^g ['? MNsw ' [b@p413p46qr Wz{{kWrpv26adon3 [Eo\M f/ c;[k) ߔ;DDyoĔ}[dG%j^72;`l G{Jx̎P7ϔĮ$OFqhH@?j /ȒNFzqAr`&rw|l0sewA8F1_8k1LG10I7->u`ռm-'39]LSó'ݠ\J~SRϸtPI!ruZJ63Ph#۝"Mh0ǣ#tBjTbeJHQ<و<֖Y @ C/ U'3 oQs~5.i玆L`q[pz AYL!d\9aױѷu@vnِ1Յ7^ǐxqFn,.e/Hk2RYć LNTh54W׎[~T tvJIWjͺd*j\d kr=/v|5/P`(GkQ!hg(N A&ҷ5瞷X ߗi3^C{7nLoswe j(:~-@|q%iQJ t4O{3DtܸRfMY =R<_ PW# A[ﷰxTsiwzd0_q ?^p#h1k@Y wGՂ؈+f`szˠ ڥ8S]= BU0껣 WXdVOG،+ner5;cr'&Xi}x4Pǖep!}aK+X?AYs]k*% _ӯff=*3nR%qTIfU:3po.{= %zB=aqcX ! Jm9CL kMf,g2Baa`9Q*H nm=G\eF嵍vI) [/=@#BG툟 w6#[gZj%z|$J]-L`m @:\Ak[ RqIɎʬ\m-s7H(1zAJkd?P HpIYֱ YkBR+7 _D[s3%H"L)K fcyyDm{)9LJ5! j͓5d̡2/p&bMC(  \bMr9#egCk>N]ុ {d1ziz=/JN19D< .ҝUH`wb(#,Tz'مJ])ҹ:[CO8_*Yn,S1SOdG".Ja!WT %~ }9Ptⶠwd؋h 08IbF2;Nfx#!Cas2;a,NNٶF %;:(gFl+',5n5 T׏bvgׇ50}T^n/~7z9O r q_C㢯xF|%Fбvr cNccsRᾖ:6WoWv1 @\VM}xlr4ltЛ gh;I38'k.gip ]ƪLMJ'P g'eQ;G/KP_YIDR̉s u{FbiwWaYw{#= `K [n:*a_V@2ד&.iHyY;OlsI-p0{+b*9(x-P#W{9/j Gc xͺz4KH}=ƺLGV>rIMXZOQ>'*f9"aTЛ(:[vλNY4 gZnО{g %JږLJgYFS>/el&K%H.6 9HZH"Ǎ'8o"S) .ʫ?u|) we=`b,@>-{˒O{L?jGbrZ L Ŧ+/cnEϭ^#(|5XK h[ ?=0 dW|\0؆YL2QBO~׃ǞHfu̩w3aShR1`6[$;Gp&u D$>U%e0n)#mOnCis|@RtXBۚl!Ÿ' >$NMec{(Xܫ# s!K9B 29jzȁ%GBrHDF"2Mffu%kl Y'nj8kǗ+J>x?z>>+*`2t%Wj5x}ݔs5e8r\i?oF \^#]4x) G"{ST1R |S d2bEO'xAI pxu}MgI5 Ėͪѯk;_H p2KĎmE]-6*Q"q7_Ը_yX%fÏ)7t6DZXVHUw}-W.mk;KHn%Li J_?@Z4*h<@]Lom6zKl79a^&\F 'ݷ&OIZc%I nph{GPhIĐE1~050b: &29`TIpLua(1 FasF^ ۄYa$"oվ$[#Ιe#o12)6 -ZbH-ug5¢s'7LHׇb[ʌӱ#rCpO1UorPv<:aJd͋dŅz%skߔW+|k釠. y;Sd*88+1dnx$I_!(_|Çaĝ0po@885 l<,aUVڪv*8xs׮8 p 7ʹʵ%~ 9㏐m Έ@ړx5bk9>YOY?4DkuYahd*&wOɑL6bsUTFRCb#<o>d׷G^tv[g[ck-YR^aJ)!Vx7]PK#,wc40`f~iyWN?_r.'Yoo&%ecnI+R& 1Ma;{44U?Y CޘNWݟڨIӢ`eG&A'1<&$ BS]D(p‰v+$ڒ^F$q:9:MMQp|fn|2&$HOԼA]B\F+&٭ /-uCSȝAa{NY's:)ѻ~471nLBo "Z#;5yn;@0iX@\ZqoE+#Z0dOuUaPvjUXTLV& ~i;:n@///{Vz'1=ОβW#u>يTBK泠9OgT` [s"*US(ݐ(Dqm kx-CHv鯼CeTUqE% @ʔ%i^ԹXӿ{dU)W4/0‡\ϣetw4=(}E/ y1kV//y(ǚ4UJ_}*k@=QaUj%LTj:8Zn>LǛ;({gϸ_ZT PXмT(H[᱓AMI>wĦD| !E,[LS! E&w-jЗ#% ﮤ+xqjRs`z2e݊D5Z!ʽIQݒRIZ!^%+o68S~_A%dPO  9 Т3wI\}h^ui4g,[uµSU9"U oFEjyMsDY`M9P'%q(ãRPו'IzSյqh}S0o|ί\`(vaP|c"FT3s+7xWvBebjS %] . B^뭬Lq1>vnL<zrKYNd2kFjTI\yLi P͢A5LZt4`R ĄFbe --%EW b./(y'h5`*nW{$5ճcO-~1ţQDC2%ᘳq0yQ}(rt"&f>us!?ET%}?/U;ȑfnjn(к#u=@@~.:M@N yg 3j|.$ ^MUڣ>yj.Vx%~#K,bJed2! 'Ťl0B]}Z1ߚ/P!޾rKIJ|O[+O$C(0( 1UZ෾H*fT0Ag0yfsPyeFJf++kfhk_'u`}0#w dw|EAy  MfkC-֟>(oQN/enC!uO=kZڛY^ 0Sds >;Xbŷ"Gqa>%ٻ5OM~_X NAu+Ϩre:DM&;p7&f#r!OTn fM)-$]/2cXXDѧdϖ17u/LzG  \:E**U}`C\d5mcDşS9>`ә$W=ix7j}u ILs8D-W.\(1wK]qqƋ0H)T&N8h<ϢVԯ7d BLi8i CR$_ŭh|=,MF?4>]{LsX|M'kT9%2UW6r0ZuZ%A?c[D}0$fyĖ!_e['̳6 jW-hoRdCU#awլ큃y!-5of!BoJhN1"e1) ;LRSE8Hf(M:/~*+-Z-Jx'4ݠoN;m ;tݫvճeRj-\Am+Ih`qf .M I5ڲRk5hJ䢍 j3^g#x 8|r#zS(㹖~l콶_ozYM_4Jߊ2Y mRWI :uF3NE4Zz|3NTi5m)\|FjɶL2(9h`z5)Q(ut#fr7Jp aR܈#韐$&49 .m\F<k˂%`2QKY [pĽ 2T@V?eI={?jw&j KS;:ð;_'Kb(j#98ݲ}~JYV# wn|0S"bgjkpCN4ţPFF|ʶ V~ \Vtq#E;<(1hSl ,3:f*I.QX>Л̵Jx!9zFvԼE ~҇_)BeM, H,ܸpgRm8ػOʧA?' ?{ xiy鬧!Œa5.J~Ex@|?wzL5<:o7*s 9XT+)kT3SBWvlvkI SESyziAcsp`B3eHTqLD*Dh.8KpOT:C~rhOfP>GDt]P6 }0gW~mR3>tvp^<:H80Dž˧35mdm_D͹G'"cqJ}.o9b.u0bh>3쎽A!"f"z Oj`TN _ >g`0Y3g#OcC w듓.Pgs(n19M(.y@|pZحwԼ1Q[H#_~h[G: Si.yU^Ka_`( ŻCӓQnin&5_Oq9m 1< hvXvjVz)V֕]E7\Cnj75Pu?ŤjэcgҸ`6WEl'ࡎCht.ꕔ(F~'q>4cz][AI9OGc=$9Q|{s&AQl&LYh9Cs/t'.td~I26!טuY}DCOVm٩:K+FָtOTi>4$QtP8 a,o?}ah0lx"Ardgn[aT' #jO˩)p}RDSƁzЮft w93gϪ ʌQs*hx@Y >Ѿy~Pd1/0S[:V3u>ӥ}t-Yߖ[N^$wo[Mx´:d&/ʱŚza"wG} ٌcqk\ΤWSdzu;-Q_6k$ 9/"/٢!YyAB`/Fߊ-ա%\<1ۼgÅ+v"D ,-ot~̇HpH^r/THہ-ZPVP\쏵*s@$`u=evZ%3h'XV3pNWO>8}*lBw KPR7mgxK2FCF[%n1z ->b{O;s qG(C"Iai` 7re d+>%J)"Ӏ9V+%Ƴ7j7 M;Qd y:hbI>a+g7fzJ,K~Q+UMlǖY_Ui.R>q|uVanto"+_"ѦVɞx^qt>o>svRztp6a,{o b zf$4=73hp1r`5 D[-a{App V/1O6i 씒ƣ2n I0BBGU!Coy "0'CiV^RɃidr*ֆW0Ж_z|Nohx)*ƤNt ֔[ =QfЁX:$kv)qHNtJ6ʫ׍e0>KT9jxVCCݯB"P06W_BI8ԦWԣ-(eDN]@@jce:6buj3;:x!5E)6IۗGf#dt|~U.輊p-(ǫ_UO9Y!"(K dqhȢA'vʛ@m@AHB 'pHq x8h3C*^U}@C'fNj,!8=y [4NnBBz+nelhǙ)&cw?r!o OЅcP+j!NME6B@-;qR|,e"qNzڬrख bhEi#| `m%= I"ʗ<:kb ^ )r1r"ILטK'&c,d` К3|龓rg@rHϘfׄ* ǁ7TӅ/lґԽK$ C#ڨ*78k>ڊA:$u飅ɾn V]gaLj~=(n{"`@dGĠ~j4_au^$03v?-kʿԧǪ߹QzK0ѕT녗\dVơ&f~ _2کux`?3C)]ietŸ[<%Cܓn3@ p] G=6ZNY~*#Mu6ĽrZ )ԕ$6p|pz> uNd7g7"o_cM͢2'3W >gt+jbF|w_g`y-f8ʂAra-ifR 77yE4%4[@{CZ1!~'?Ѥ(")4>DjɁrMz)kHօ!A?CNZwe(J0grSQ)i7}TPžC?f| MxӴv)uvkTnhtK7A?`?ۧNG?j:Y!RmJX[5ֵV?I8lnQA) (o$cPJx15M?Ϝ_yˮN, qDzC@6a͘6A0F7z^6c0t\VaiI;RqgWx <QW$4:a3̈́{԰??E=OG 借\︭6@Bk;rѾkq҆-MFkĿ eRBM*cҷNDGA-f В^n{א8zS? =#UmlP;I_3lU|9;ӞhG@ @eT]\|4@<8$'rH$bVu*Rw2g̚Ke7桋IH>/ߏ7X/s*t“bNJA4 l@g~y0RWj5в].I&(+x~V_P ^E%8@]~5NJY)F ghKP2iJuGdE^e[n&iCW%}':̶+7cxyxwbq~Eb(|l,I5!(YfH&msȌKB2X E}\EV~FTs)D2'Dd/ %s#i#a%6ə%ES9T %ڍ^>:R:GhG8ŗJZ#WT ׳ަ,nM+yKQ&6Y]/V2SG<UkzlFۺ; 40Gu=GGګ4t(x3a**.!l'P4p?>slaхB<S2aގMIWȪn CAۅp>^4KʬDHPQV%rf>5~Zy)?;\6C 1`œa ^x^c{o' ~¾XCby7'ѯFM(\b7E^:9 MQ@yj08 鹜/RcCpݼ3v-h`j&/27|MV͎(;L"ȭ&"5֧\46o;D`>s1hBWy֢k?*h[!s`]V:-TͰie>.C ċO7Ú_㉥˱7JaЙ'WD:Zs))a143R >8H=9nJ0 q =&DJ q4;yo(L%(u*|,4xm>cJ!ATn&+ 8K@3aҋ{%48z Xܻk,؁ M4֫,3slo2&J hIPgYOBPj3=&nl1a!_!B0ݬɖs8O|e!>H]j[ P>Yɕs.Cb8(:b;oӂĴjGj;[UL[0QXV{zAfX8N=78O Vkoi$WHN;3xn '-b|Zl#|*~:@rCn`MvFˆrOEd6[OX8풣֫5$Mo-e"B&xkO8\n$K>ՆъBo 7fPʽIg!dֳ;ɲ̵qub„mll'pp>"x@TRb1XH;X+R^؏ Q <9]m_]FAcʊqqNVETt$KlOϦ++[@gȌ:0O^5p6,W|m1h͆ccQ| 7?eN2糇||eskpr[|rX%`ؚﮂZU2$uIq (+Y;8Y~6SZ*cz2n ]T 0ƒ>ϋf R&7 ߪ \%*d:^o}8zv6j^ꎉstCjbwܴS!z񂱻"y>{sx= 4 S[`b;1M1aZNU5zJDg[~s5 {NoVXy*V`FatQim…:Jjcne߮mW5t7P&yĔ$v,,m34Jgނ)sgI;𕪷i4|Jw:WU?/"%|P×(@&Xt(4Wycl+6UxTD:Ȱ>M̎06Kp6?kBBӾdn҄Ӓ\P9mxց85:Z +KL )`ڜAez6jY{)j\1Ρ60qonu|ސEiIT~YF6=vi?nU-ItUƼ˼U_YZ3S.ҍ!U@2tj:q" endstream endobj 957 0 obj << /Length1 1612 /Length2 16708 /Length3 0 /Length 17553 /Filter /FlateDecode >> stream xڬctf].tl[wl;ض;tv:mc۶sy߽{cfUͫ\c+ \xƮrv@sW_#;,9ND&3777,9@@AMKK_Bƞwhz p,mE-)y @ht2(Xd-Mv@j ?93r&=L@'[KgKg,Ll\M!nf/BN#l);88Y:fU7O #r;[uFڛSҿ|az],.@r6Fssp WgK;b@p9;U'_8X8m`Y4qA303n?}n@5ꟙK` 4ew@2 "?qS= -jc#odw}0Y?w+?5&#bBvab`Yhhbb03۩LN6vzf&ZXXzvIH(.*"Ow꿢j(E-x=, }_k9#'KߒUxk0bv&̊_&NNU׉[\kЁ@ ʢ oUjFK VЄN_3PCIja~@}j6w{uCg©Ǿ4H ew22ߗ7e Q!L#j^v \I`gBIY` :-?$."r`txhw6;/8Ս}W),%wąJkP\h(43 {&?oA3/ݳ6cNH,%ĦN![Hk(M{!-% }> `2qȈu,4g޽‡[%:by2jTh/F4˃$~#U"*|@y@IJtΝ8!EAD&f$ s+T )WS")0lt!i: 0g;ˏ.^ArNXVg?FpbJKYб]u&Z̑yg%7To!݅Xi<ݍYqtn(4wa$ErmG{yYs@!9 Pn o=W 0by Nɓ7j]_𓍤\E6 ۊy$nHu) ,(y.Oޠ9pϸn mSBp DŪF _0fŠ! ipx7a Ց߱U&G0s6PUC P[HˀZrᰍ z\=lh.|zX 1wke]{7 %*615ҝ))Q݁(rͱ`w ; 8i*} 3hvȍ0AϠkb)8۫Љ!e[*;ڲ'AY9U7KãF@OȀy_2qON4-oc=o.-£*WS ܊Z˲3GĽAu嶑؁?S[:H=%N-ߺZ P~n'#}QaDFVJ/VShokre43C$CmR|ߟje1l⳥Y~Z%2o<pJ02ǟHzo{P~m (eu )![vF;vͤQ/|;sr'T,7)4G1/_ҍ7_%7ʪ砍 2O |nWϛ[]-s%*zmpsbQm7 @ikRoy͹ˏ](Wo+B==&% 7dp_Sɳׁr7sjJ֏{wAǂKZfȪOӍ9ޛuCi~h|V70oĜ)t`h$dR6D>W~`)Tg 51RMRпfKXI=\hCBDfHDW˗L vLJ*ͦ ʇN֚ 0i|aݑZ޴q뇰"y^*/ѹl'Yt:Ԏ96 ֈ BWou)5V6ݶ?nʓ# jT"̋MBjF X3ebk;M3r^S-1|Vw.Y=Q8dgRaHދϕX 8X" ?-= +SrB h(LN&iR֮azLAu<4:`buܟ3145ùL*E8 =i͈[*dcr莠Rnݬ4IǦl)^xfðNk_ >Č}^T5 YmMh/LVW'H)LI K=8s"XAOF(.l)F!KnLb d VPweИFqSG$d=f=f݄wI3 bnYqdz7)Hh f#hjD:ۀb=-lA 鱊 ~ ^0gtP!I~k.1ߩ16-N>ïY J{)Q!F-=6כuV c^Snl?o-۔O=!ؾw?lLK:SfBQ*:^-錅5`NgC1,Ӱ` |4uAaDÞG)qX8FmOι2ۺZ _:u= >rhNԼFx>GacTCvYxn ·-Rm"8! [Gl-}6Spߍ43!£I ox*F Dv nZ&捠DTbl83ࠗ$ߛ \hH\k)-w< obc~haE#O_w\YWlKUٖ0\ݞe)#JTa;Է#Gd:u)e>_GUt8Y(O(™tzyy̋ g3?0<ɓKu1x'Hy%O,[>o~6u΍&Y\ȟvcNL_8nt[bʭ]&00[,ZNvͺ S7:]rpN90)+\094NC{|)̒=Pf7amuS7tkeEMz S@V|ç˟0?j;KW_VvQ1v "}ha8c] ZljH⊿)nn kDnk?ŶdZVFpHYrZ*59E(h=чL [ p;cAa{A #4vǗ0qk45~ŵ+' b>xm0T)N=QRoTT]3 Y_ QX2mё|tBQ=~6!Ri+H6{'" Q/^zy>y OGQm'Z%D* k#n~t Z9Z+0;"JtؙlΤRծދ!iT.WA#bBbAǥWzɿCk_tq: a{&9wMp4#$’+KւfbWG6h 1сC =1˺?G+"e~kW9Y o~$AvS&D/KwE:T,%jFLv\œT}sT 065|wa&"0ޚh4J5ZxLp[ʛg$~jє Wut7%DZ+BBEX#IV8w?Pd *YKz&Vg&Q'֓Cź}f^۳9~wTA? 0?υ>P_/u<9Ǟ_h] 7"!,FïܛGbaOӠv[UBj\sm\|%ᄁԘgSrjocZM(`'r/y~uދ~ s{A&p;(KIk Rjq,/a Vt /Wb?s# MS[R~b%^8O 8VPrnrF؍٢́:Nm! >]$Wp[\?P 4Kyu6”E, Mf}qvvo+g+f6T`F. 2߹;4+62z. [/#S2OuC;huPchyjkA9S8Ò4ʎhGCHޡFP'‚vKV5O"vS΋[="h8U6Otp yPy7n}FN MvG>U}9Zd/ {,lI9I}}o(ڟȤ(2zs¿]gwTKaM6u:#3$_7i&)O+ r,);Wjs/O6{\CRsf7tk2Z! t/^WDI9M҅U^_*xZ/[{rDš=ǀowLCf3v-[leY]vu3yǪlp7gJdEZmԓ#iTe;~y4PE]s /i,TexTt #p .7uN^\ϏyA@-վ,ȕA 7G $?fg}Wsr$s bE (?I;}g^xw= ﹦=/But?COo`,~灯@{S\{)0Uݱ񪑣u[6ЏJ%m1 ÜKA4mse¨EwcׇLJgNnDqM$!+oZҴoG GdFӯ/E=QŸt6!//M$̡_!9l?(=FK_0(JVx/*kNʘqsk.&X- ͞%826 C>e3WPh)Kš(<=?uOo4խUvZ؜J)p[kbUfV'J6|M%J 튿ن,RcmE3AĴKqin!r$Hu*PNuY=0C<_$A smVڀgP`A{ABX\ qhdK沖8v{ MuhH,sGܴ QO)a$aT\!*%X'd}+WG/LM 9J-,yS3 J^S<1j=vtQEGtء%p=#j$ȕژ;(;Tr>>ٜ?Rvmگ:A.Aш2u$;g7JcUvW{;q`\_'u+qE'8gV!˿cN(Xv~IMG>|F g]5}u?O9mYḘ̇r!m@&3xImڡ!_sMQf|)tod¬+2NY%=Y3b4߹ig ؏h+H@0.^1qwnwk _F8*4{+٢Ju;gN.= s[Zq¾q0.hqCh՘^mc5SEp 1}*#) (3(]G~i~rMpu\ q6T}#<޼N ȇY%labz*zio-?>%cG(~k*Fin^g^ 8_gôIQ=" H|$^bBZ p YHIjS2(̯mo.lr\9ORqNzt)Jr!3M𝂂ko~̉Uy7ENItz{D9/dmGfCEKTШ~Rji︰ܠFRL7q:U,!,xMVsQõ_ 9pM^A\J}K1lf{|!ȀRJ=¥lw;cWV0Su*.#+iL5~+i<h>!4ӆqέ>3ޟI1$T{Ai]# ʻi2uu Z-׹ßIc6'îqy} pRs̍婞B5I@*DӮ^drV1{(k$,! &gevn fưm#*mPz6m@l)xA 36u`|[IrBz~ \bN&BE*=S{ ϷTҭX N2_HhVcwH;Gۂ|fh9 ,XEhCX(5q½i>Pg/(rB{~$IJYL+< l0<.F@E J QNUOE@[dFAiv,s'KS|xJUX!GyqH:Ð5xnO'41Nfc4[z/o|in>%[&..FBIq f6uLWB_7G07`}n$#aeTV%C ߹^9dF/}#} 6úl!gV%R"G/l:,k8] \NuT<L$|z%0޶fԞj%KZ4~fK& -)E !u9ݼ@]V+;P)TSد奓/!*rzmV/eZtˑ rBMeQ}!6IiYD2K"t46Ohͧ2!Bn2o CGªJoU4{0MTZphH.ftIC te<{5MZnG&Zr@:B{08n#1f+}㵻nv~nBF!5Sjk2k}1(N{:\ FJgh^:7D.<¶:-hʆxLL-nlw/ b @ V\Vdі@k%幽ыV:"RD>Nt eQV'S>H'%O[8VNVDUYn7U$e~ǚ3D58p;$~|{ $?WBD!:j7_L~ul[ (㛑.\dL&[=!PvoG\?WMABЬ{E7Z'6w*m;>;dye`O*Y9}iFhewU&1Ԁi(!cAJ 1,-'j_={ro"ĖBm4b_ߎڦEHV*y76kbF$L*Ds'ע0rfe& m1mIP\&V9fvcz-c0lvl)}W9 NOwI0 #`< Wv[RJǽ5 $=|'$09Tz}%Ii inq/[FyJs!AՇO6;UΓ6-I|B¯xQVwwr ]m.«ς5 rƹ8sr_j.BLiVEi< 8e#7) Nx۳/nddoPZ6jY4hg1v$hvGa̘&&ȕf&dngU#+F.XN֥)qD 2ë Z _$IBɱI_-]_2#@dH#gBg>lPb>?{=ٴ~Zߴ%Ó`w <%׻-YڀB%, &r${Qp0!,͓!䊊g5[W?>M+zJ3%9i<ٚHNGQgs/4B.`DjZgK1SX )=uw8Q:_kzD..Ta<`f\YRlm-sgu\XXM]x a ½zq6u֧b958*O# 7!6p\ntOAvI.kU:G:,![UdI{lb5+QZ5.?.!͈ ZH@vyv˱P(K@uXsQHghVaܿ54ӀDMxKڶ h,''j. \j鄗m+4LUiH z7Uߍ^Swk { ǭÿ |9{bЀup:Tr8yB}ێwL %-AVGh0 OfOX6 O*[g+fnІOedbq'ոX;#_+0|]4OQuؖA'v-*{',՟"јQR)i~Ie&*DRU-iW\ zޘm㐇0Xǩn^wO%{ZKo,u3^A8YGo[q ֑Qm6hT& d fg%`I p03ݜwg}MBK}Ij4J>؎uRNGV]A*~טk2vWضQN<e*mj,_cXZ"1~9EX[$\=O:QDv1acJ:_:a8EbƁDoaD}f컎UU~+mj]N-*k傶k>=&?k4U VӤlϥ>Qk~6J pDݍ]eIۺ*BBHoFM%A իwfpz$"_;!sKEUۙ*nZ 3 d,M|,";ꖆ %>BR֒ւuā v8@dKʨE^bG"~6RF%&,a k(@5oxŮ(>3*UhZ! yՇPic2͛{}i-ZmW伖.4 7CUbϪѣBr#!҃}JU/^q?Mep%K+$WaP!C5ɀ;e䫃9+6A}&xH= Ɛ.xW'%SA?'^tC`kU1n J2>)Pˢ ߠ4ΨŜQ@gqjj5u'kfG=Ce; l@rkۑ>>ofۇ/|cywB\UsjJ7(ʑyp)~cN4|L#|Lt돳 );%m_@ڳEKITx1Wg%v ThHuY@:e'Vm GJSs52 mFNBU8]3IqG6OYl&Z@|=?`2r(D0^-P tU[r|,[=Sq3wP Zyپ'2o+,Ua62?1=~ Gn  \TO%TIX^dpN'ܑKn%)8pT@̲|F/1Q׵\j6&LjmT*c+&XzUx Y2YFWE+vdvB.K_whH* 4L0Ǔ͍Nb\VE+ۆ/ Eԑ4#"` -ٯW ~l]dIeC1 OГY ;M ӲHAAUH2Ⱦ|\&#[مEup:nq !%r S9ם6<9KcUYCs fta~Mm%W$Ʀ0Pa SS=kO2j>H}d)c2a۲{`W>1ԮG(ETK|}_ >6Wi#%G{mk>eXXy|EQzb  ?8F[_>I4/Y.,ZGޓT]n`b$P$# J =I^;#FmQ.Y@RG坕רt#ŏkXռ~*ͨ;.eԹt>TcswTS֔M4:Z Ӧ&NϾ'ѤafwJEy葱ϣh/QX c\,4î#*8@dp! \гh;m\ Y4=h!:zwJl(zـA r2VJSde@P/ O6M"ca슫^М}|Osc7Y ddl xP?ѵȩkAi%P(!bHi2Ph*yK(wFy w') Ui ƑtSs)Y,0cѥW)#q` U&,~+%RD=Qփ둱RiϏGQue#?4ɴӧw;4El$-#HC~9 ^D &|YS >"f~y$Ř3n0 |=OsR{#k80':%%- 6P棫\Zi0؉De?E{.YZRf .}˩U]4scožQD\d\vHn=;_8| vt+#Y&E@5 Aii.dqKqZB ihO2FURk1uI 1?X֒uiCzތQ Ӌy5fXh^E+[xGT3M>MmYaga/d~ssYO2%=Y"Ȯ6ffb-Pbc#fݛsY= G$ϷԿ3R&M_FJ @ WrF#T[FxxKxp1(9P€oQvlIDİP~v$8bzsLɟ?㙢 {} yDGya43:A0;^]'@Yݻ ZDH?)KE{2[( iqW،O6BsаNh̤D f͔$+>Zh ,z ۦV j Y)K9m*o׉;`naԆ~ endstream endobj 959 0 obj << /Length1 1630 /Length2 11633 /Length3 0 /Length 12474 /Filter /FlateDecode >> stream xڭxUX\5h{pwwFwwn-@p3g<矫~[j&'VP2@`:&zFn,VN h$odmxE` 7C04|L\\\9@NMCC/_.#"fg5h ~*9`ja +hJʉT@[5@c1H 09qlM,j͑K`p[]vA;3``h ~5v2w)@6;h`agU&:r;Z黧 鯖iQ# t0p6t{Nf`wNf43t0::Ӽs5 /Y ?k;MssY"0, ?&NvĜꯝ~/dk0"0ȁ)T3D?DywF%߾N-dm-ghd  랱6tu;0C k&Ձ(?E]:&fz-,\& `csښ-l= FT-lT4UioOMF]d򟇿xA:v&3 d_gYC+@ ߿NF#jk 2ks&`c'w~ߛ]@<)joڽLЃAvu*y.Mr c]{C֔]I\/R<56=bs20Zj{[cJzEL8]Q:bڡx'FjG?>?98g&+;8fpSg ţVMkLt9 #4ϑh;H _q D;4YdP4q{:vE,+N'oT%zi VnZ^-+z†_gTOd7;pHHji'{:*iTQ_W$sʶ'jGxg.o tf 9fZ| acJo-gkk"Tá}[=DT*$JkgB[S|GǢrp35"6~,g\,ߎ7kuӿe3]<;F l ܚXe6PXt2*fÓs~cQ"F**^~vٔKO%exbO(/m/D>U^\* u{б9h,4kcӻKH #G\Y}]ĶQ|LcgmS1J+F:NC [mucFV+>w8d(*YL4MLtyD/2#gCEq a# Hq-Ap4꭪)C17mzbTFHG[ tߺ4Ty %H=&U柳~x=֠3p1 f\ŭ!I=zjŏ `$9ir48M;^n+;EɰT,͸n[]k3BANG!d 7w?T\s̖ ðḰ9|tPc ʑ}9em[ONS8zEnkjS?BS @M3~b\^>k! 3>'CVm7YcBռT 9SJnoowD领cALCcK$t1(n v{2+9bXQoH]3USz3Ct|k pk|Dˀ 0O0 #u'r/=On3k*U/,ٓLO|CuI%C+g"6^ym+u7Dwh.ަeh=YIDٔێhL|{Z1$D|35 7x$Zj3$ƂG|ryo+0jYW[Mc( *Eڤ)QRQ$!79k#6G,AhP2W( Ǵ-tkJv)[3~Ͼe# ,Їj(0i{FhdEMz 0mȒ=<4 jm\3^N)5}P_oX8) O$ji* ڈPc hQP ѢٽPucUbY[d跿CnO@C"QPDEikBl-i$nUPW{f0 ԈCAnT]=DڟEpVS_KxQyS8roW5h2`ԖLߦU~~Ǻ&l+NЄ*1RlwE1`\q ]#' rT4`En`+m!d% A_4ٮ̓w54zGU6,2?Q7|9(ξ(YF-Otmۇ.vNsfFOK}h;\+ - r2=\pRr] #h.ދfܫ2Q% ӷ?ͯ__E$.=ʏp?ј‰X&d^HNZ21mi]b4e)$6{c* kU&w>$lđ[OELJX2b." !k#oNǾ5CV/}kqkE.ɏTmFfIƔ#Y"Cu}LW/3;ԑF񇆒yOIpn,c{'/MU:oTJo7tDQfO^@WQ_m}}[-u aLAff )UCZw$ɑA;gԂ&R,jwS룋` ZXb dy$nő,~ -C"F$&Sj v}k:#e6c:3%Uko;Vd˝*N{l:.?"]~(z{u݂K*jz$^G?.?7T; a(_7LV,AuLnu$~%CTrz 4~ZgP'|w~-zJu#lJ=LyK IS^q+e)i% ʛs*zȜUYr셵h Q\ޱASCHѾ;Tшa:defW_v6ݾ)3N0SFݘAFoSƔ@,gʲ$ f9iҋA]zs#-LjE@e=1EڸymcA>+eWV1I~C98 [*ro+ ,2I\Gb Gaj$%IJ7N6N2SliùFY'Tn$JeYH³ Kt@77 ؞8YDovc| 4)xYb_w!{~K&]< ϸX&Yr^#AٛO,!I+[Gp7V̈nm wjz^id&}(pISz/A9 t)m[.R,,/9דJCء)aVy;p9eu ARmJ`Tט<$cW㞣 wҴhG#`e8qyJLUW"z,/3q^6u/8`x{" +BJfF)9{C6c+[he߭A%z5-9K7l[HS{pHҴr٭;Au>gujuiXz`؜{>36[yK- kdmҠ cySԹf\ϛأɲ~MB|}cړzBXYS9hnbORʨR-+jVBh~v*[[O#H ?E D8VH?3!xPڸ}Էiy?j=8 yZ4V+gafgLL#FA0Lv?U׸ SNl"1IQi,L( {=BXCClwCae =/D  Bpo. \ ֪ nbpW%=øνFDkLc7 2p>֑\Xk7c/[eJEbE뼨=Hx0b0tAܯyE%a~O6oaxp}1}¦LD 7yȏ:u00;2445B-w'o SByMs1 ~9GȹEm 2-[m⩮8Qԫ^}FtcΑzvEqE 2$gܵ)KyO(" b~>FhbχӠ`<}G zĖFA*kɾ!dv*hܹZ~tl"yG݃5v|2@K~tvꌩ3tBDGOQ-a=mY } &Wusxjj#zIN-#ԙ~ۈdJbWEnIx}FN=y՟j#0mb ;f}^|՝FڠXM@Z0o~Gܣtݵ FڎcxebIsBaTr9ZdĐNh yivXn7N]VBOy.E Y*[,̉4zljʃP|TBTIV3etǾԭ7:|1yDj$q $ ܯR("iUlR^iZ^"9-'7Jƹ{_xrS5f9 廸jx4 >a%CfP*tT62.pż2W,VIR3Aqg3+˹'+q1]D-~_UXD^'Y y q`mS\K9AtH`7NK]M׮\>z].ǯC,is%aRc*&+{>_bp[V~,CЮVSLTXu@F-HBE7?,`cg&tCܞ^V}M[D4ςe᫻ίG|VD/niQ-_kc fHc;WC aTFEv/[6*a dW:hahWeF5*\V-l,# F%epTxh ##un$Af"kwVv7Хri喔27O[H,QH9='YFcrZyCDXMϷ) 4m:^ysdށ\bߦ"l@W.}44q[7~]36q5Lu}< *zњ3hj3ǿ3)(9`XEf ǥ,=v($g/nmUJ-|(6DI)>bŴ$_*M fg0{ㅗflV>Yx('aO%upWZ?&$CeӂdQRچ:77exx|_',)ln7M9&.cd㉬ТSIԻ;:Բ{ZM- yA!]lTD+95`P1I{6=R3F?c&֜aR!vn':h(XǾ.UffrܱLPo229-$UdFѭ;5pd8>E+2j%Ynu4Kc͔BPt"9EgjÂ]Xbw[װ oHXH t?E%_Nk~,*',R1"j-N >}"$iILdl(v- jG@12p7,@{{@M:fZ/uliEY>#*f_ U D;saTIwt")n9UlE Aly;>EX㩋u휮tGKa RD?/+I|EEذ4&þ.1"y( fOwWl,E2+y07Qq&@iWZJ5U6Nב%[ [k]PUEJ =O3m3U*cTNJ`fR~λ =y+V7"zPj_n\Hv#4Qe3&B)vt\\}ĥ^bJӁcL؍L?G} >&.ПyovUvKǞ7$:LUbu^;mTuNdeI[POeQAb]1UW6fpu qͺ}^}hm /eL=0p7y3tj/SdSѣ_F.?eeWz>G1NRȭO]$AٕÛ5\̦/:A#9k Ilk@vl9+gK?rYkYmVò>2P ]qJX5 G3 +$;=Ԯ4&F8. #/]p=}iYl-_HS? {Y. .G&O$xIhG!ZNumZֶm Ӹ1y]tMFtPpǞEQ*3C]FVB/'SW<뛩7dQGfW6 Paݴv3ʽŤʷi?VOˢ>[ _"KqRug)D3pƗ֞ Djs0ErR *H<6̇9rʌt,(7Hd<Ǥolt63i)%}TK63 X}$f2]=~5Cu7B%,{0Cmbo/}#$Y;яj!_/Ps.ݕ\zv8S=9m!9pp?c',uvlj/&6C$мGP#jOkWIEYlJhm(=#܊}~e?1qH_cZD\8|A޵2ꕭF~dCb~qa-$@k:fh)3:蛪DP/C2~8lYL}y*$ S_Q1\rcJ+5xޛ*,{wY4t=.c G8|lY)5hJH릡ϣӲwrWn u^cNLW^ b2WrM?qVR~xUi!{YJ## $ψ(b7Ś K֪:pugSak" k0BOi{ۓNY_rjE۔ n!H3/6*/`|^=f=TRG(Mո}HOrJz<1q*6w6Os!oS!!W{"$6?KuBs WJ/V3pgڷNOn(`d ;-Pf!GgĈawzE_3f?Ow]m67cb=R3-z3\ fDUS] zYCN[!9*JIr->֒s\wf}q&AVj%&6QJM٭^Fju}`yIg22D= |qN\,vɳ(jA4${!Pɷ^g4\z1 0m.vnT^Er@IK2+LkdeB!Ad`2#1X˟콛!vmlhڶ|u(96o@5'HM/hƫf0nbC@$4hqT{p:w$gG/PZ/Is'<݅04;·[S>^6&pzB"У|M^5׮{k#06:NˋKdKTݰYr}lAx\vc/qB{Fֱ[x-w-!􇂡I :L}FIGqlRV5Ï~^'՗@ILwʄO1P𘇰!ƜoH ~m"+TcGj9SzF H0D8ԛж!;hTn;BXYڸnd:h?ni8k,Y$UsJ!z!Td]uc7dSUAK }OHZ|Y\̒w-H}Lx:v+}?>GDD*paS!3vī* Ն߹U<=FqW,5)JfP~3+M%wy\sa1XpMFOeX٧:ʐέ P*}/!!vQʹM4ĕB}f"Ǣ%Z9Q09U, jdOeOXA@~$q>˹zkX/K/}_qߍՌ($SLZ[Eյ +%,Ғd{Nlx_q`E$Iܺrb,B)_VQ(!wJ05^h}@ rbRW#82 , [1+|:zrC]tqv"3eWZܣ)7x=揙$%-kP;ZGϳqu>VW:> a-o,O Kn%GY|O3-'`e2H$WPMO&x|ud1*>dKY}x^s50GڰՄ)*~=)q6;o"ucJAh>#W'R Oe ͒Q0WP2qOFRm'H^岧%/<)!`V?c1\yntCn$-J6.S'[ǃ 9Fǟ#}陹F mĬaVUM_q,~l3\{,R+>'V)^`ú"4:X~m- ti&=Z `z,8lg%+1)?q$DŅR{UDz@zfZHSrzvOӜ SIH_U"$s I>rsG;c|*Zd*✂{ ^`S,:|nCc!> ;(/ITAO@vZ-'=nIZEΐX 2(8n]jV5n{$$ƒXᓃHX~te pws|(B{9i "W0G7N+"' J7VĈN B<^Y'o!}^#C̛^[#?06UImA6yS %ִnd`1^ͨpT[-Ǒ.34i:PfFw+Hj {x.u+$q{8uF+\9D l*7ҞKmE%vc|]M 9 olA>Q!:#5IˆTu\ıҺZ/>M @;s ŮK2ѳnrX(stY+ Äq8 UeDzzdy+~#$GM}Ok7Mg ^s};]kpN-[چ ZYddIMt-hZ B/_ ynK7U:Ҝ*([!gE#iu5m\mb`4| CvE1dfU9B;RPޮ {h!3#HK}YK $Z1_ǯN}?r)YTAOEK*BZXrVn\ZF n5wuSXMmiXZ?tWu|$+JY<)V᩾=Gc;+[6Q&^%"ds/X#oƑYƙ*]~ۺZRҼ:w󩣚0Jͤ&_7,(tvdF_>%~g~gZ*ij34hn2r50e3We e0g. F&}^?)i&.tܪ\߻cKAdAf檐:Atњw B ~uJ"5Բ[+bcPL=%h|@)T15._ڊ y-ƨmtI*kbF^b톽Ce+鵃]i[|ɠAM1'A;=Gا_ B.rE5i5Jד56&?@ȵqo^|,E3͐qopRP2rˎ Na`S&o>4Yx wĴCx=XV#bX6ɡ& ļQ0]A%i6 .Y,a oe@ؿrzΜiŝbSPmz].Y8(Kv{iiAGg7m e2Lsu4yO뱃Ta Mٮhu+(j_q6q ʛmW"qʘ$i>f6xcRz}i]sJ.wY]怸LwPrHdS0 Ʊ+Ff>F~T6+(tj@_<o(~2*E:Ђ@&#ʂ-vu-gRT|3aL92Ym-5~Ӧbם?#◜!X9ֲ0qKr =_I_kMOVRW2].Tg^1(2qni_!SõU$?1+> stream xڭxctݖuʉm۶Ol۶mVl۬Ul'~8c<{k͵CFL'dbgd*fgLD 8) Y>l_Ⱦ9:lE M&Sc33 #@NECCO_!#|t?\MmLm?!MM3)8R\N njkhh Pp1dƦNT3;G?c;[_9b 9 NmƦh6@'g `hhg;/v3 ;}F|>윜΀Ϭ "blaWn'`gibgWI>a>Ά@['_L&@'{kCܟ`i8mɀhjnhbm WwY'Uohoonڌ gNc@/  v: ʿf꓄ gJNe-[C= -bm-gh9`7@= T7 D BR03t(-f֟=ۮjkbh 56ŧb4\LmM<fR(O՝U#5at!7'?=R @b%;{87p$LA?גB X\lG.~;=b!uI@k m鴸) T1tre>% 4ڄ6A< GXn͂{8~g%Mb`!9~w5Wըy)m.{f>'G$chNjTob]@RQ>еa25qJ0}pU]\ytQ:}@]?$X +2?+?o^:lwW2+}잃>:Pn na;~cMe@ǻ'!M =9S ķt[ff0}{ n_{`x+`f Wơ^%zvc ^$%#u. ;aAsO6O2$y% F: S*x_~5="Q2z<~0[FڜEw`_@\fΝlb5_\5LH8r12U1g(bo!4[{涾m a.ݸ":n7_Tc  <܆9}ߨ*D-3#йTWye e%fwW,Ä|QAJ6[;;DM<6T")+z~Uh2 Ss rP7w0mʸ߉^Jv߁) _EgQʛ}݄[S^/ӧ\sw$ur'H"UM` Ӥ[U0QO0M7Z VXd|k:@rlBMe6%(Ү(ө㮷S=p,U }jh.fՕAQpB+w?)ɜ<$yc=A[[Fm7~ <$"dMU 9zA&i7M]`E jx%8{V r޷[A4t]f{%`\Tl)ň"NO k#eoRMUiZe5pS>M~kkhjzI<>ē Oh6j~ U;ih\vTd} 7);wR' 5 y$engbxzb*Hr>BN98->dfÏ]jZ$.J#+:A CpSJZg $o8(ա[L)*9:\0/Wg0/r/TSQ?@e{oFyGvw3~t:EE ULg0x+RORgcܻ`?ȢŅc\nѝxS'ٝ@2qFwƚ?! (?4rm-N1K$*ϦvzAIrXPzœp$uf@օ6}ޠ\vV7lZY)@;#lJ)t1@W"3^TͲ {*2prn !jhkh̗`Ru+eIGrA=O\РdyWE0-c ou0'lFMp:ؠц|U2F jpmq) ?\^!܉x- h*lCGP[g fK$CFcw/*gE4[5$1$r(z[rEL@{r hWhi?ޛKۣ]Rw|Oǜj)\ȓ|g1;=}$G>U ~N_-B$+ށob_npC[~mqnH}ap:_ɸ,Z°|;ivzgJ"̀4@ ڱPݝ+:Izt]1ƲmkKo/(~j~# >!4=_"3Ъ֝PChnm3 ;6Nl>tC,=zcYwJM[]ȹ;HeBV8J mo?ؕzDɫo|кӐv6l+(dؿWZG/@pAHj+/EdlRNeAⶀB|?V 3q ΓWÔ-3r`brE*d<ӹX46w+jٷQp>%Ц:}?K eW'A!U<%\C9yR0}6ϲa6jT&j-Wrcxx}ª2 K !/tIb{WkNWg ֟0 5keicQ]iײY'a E5=miV@3`=o&}1>(qݨwXf4%^(nX-T0 { Kj?_'IO"B*V5we4FhelE!wTG~c7e8fU{LU( llLkpSy-Bc;՜Í? B̑¸S-"OĚBlթapJR}Հ/TKh INA/=WɥJMHN)R#*Mɾ+°|Mg$s{f HYl7!wRh%)KK$|GpIQk)Ӷ"NkB3AɾDPbw_yzE2Q iAXՓ[8{@kp"҇RMd@,B6.g9lǎ$hsb$AtjX2m嘎Rj؎HOwY5rBWǭcfAlw~[(Gf("„!^?/7O'Nxz_z=.];;$НBv<ȷffK:MunKAnC{tn6Z}1ݶYo"~|OY̤caܸYb?%VΚHK:\ay+!v9%VҰ%0, _m?ޏ]!\|V×~WǏRُЯՎ}O#TX@@R|cz+R6˔T,ej:R/l"Ճt$!qV(Ճ#˾[CRE˅{Z &Ac& E$\O(Tk?֋t٥APBAk.G{YW8z dکa )PixMb̡:mDǯMY H\=@k+[%\)c; Hrw )/^16t`tlsX[yo#{Ɨ uxT

        kx UʅS9R}q߂F 9K*TC@]VkkG{8WsJ6V`4"QCJX3TNl9 (R0W:G'^O>_kIŗZ$mގr>B:~g)ٔ8h\3YwiW$VQq(Y'HІ)N1 v?JwPE\ kb(vlW "2?hnMՒ?iKZ?K8Nn2:N f(]8~V'a\ĸG{Wti2dDS_ 恮?$\8BjC9JAhD\5Xuc. zEY8ð_f6! P[* u6:)BZۚ++jȣаyy'5W>8H g54uw[Ш&$Al{ L8lԅ1)W|Z0[9}mìَq{zv~нę~!r`iz 変3)" Fv4,n<l&Nvf]Q*Edkŕγ\ɍIp714@1EfI'x@MO87 <E!z:~ SN; %],t )pZ5_ b}J3@JLqmQ^HFn-0=a=ҝآ?!Ead1Zj)⚓^Sq(<-0br:d]\jH-^uCO14T`Y,5:[Y{@[zv$Œ@̓hYf6bc%ȚE龔\KIﳾW'v}  #ΝgJs p.%8tJ)_r.`.ޕ.Q]tq̀AR }E;=ktXKx=Ei,Uǫ(@'v!{P:ykn GO euAwAMQ3 & mʁ'PDE ɥ9&ּH}woQJz`#Q0h oDAef־¬m+٫FXS:◜,Jwaxj~f̖N~28Kƨe Hk#t|]#!3(Gg;z~|IDٟSYU2ԭ>U_RgJ|B 17?>Ub(7ac8 Z@'8/ǜC  +q{bY0:1wx#I&m/8'aPgܐCϪF+m5z>;rO-D@>a): }vɔ\1xp7s ã"N#0ٻf+b=~ytM$`FD^zbE% wΰt&M>oE%(QF-Q9өyֲzJpt^]7m:0bw#HTmQPBvtQƚS{[ ~bw{S=S? &G;3bT68ZkɻY"̠? u2NB (j%QNM?#WX:sLGk*=4mjS8[z7ȏןxx9#8e4OQŵǠ= ?#n0Jgu~3qtv"3.dK@,R0; $>87hxt&mOi:gu J;oA`^ؖD/q_J-~aNmc4TH$>pRA%D, UyG,cjm$ѵf;o$]CNy|xu:suW)V*}t"m6[3T>-ڥ=Ã=#iTJ9VdLWvXmsǗuu%F :p6X~^?Jl1Mt]MUrs#-I`NI7\|(~Uס?0mJ177^`:BǠ0!2-RT.o1g:+-{tUSFVnq !I16Ne-ف:n"W Gm12} k2գ*|v+yvv6~~kzct2YaJ f(xVkTW#YF6LdM`SRW O3Ws^iC'bKX4_|omoɖb T 0UN[Ɠ{JGoQՂt=En5AN^G|Qoge+%覓7PP=~htz;ו9H{ݫ, idVBD 5EGlmP SkJ #=w̄;8ĹKkX`-h,B&xA:swzȞ:SC}ĕߍk4<Q4hVO.!cdo5R?ٙ_یPcْ#0=¿*F s#x%tԓnLm {)hBuu,hޛ zN+ R@ƮҋDžv!}Cdy-$FOHf+TR8&ޖ7%FA5|/Q'j8(R"4z甉i3hw敒.C&E*\؀ǫ17el=Y`0g3yzݾ/1.27(Cgۈoz+N֍ӥ]i=^ZSԙE_Vꝵ"ҍ4K㤘TIfSTE,.+&Is2(q(${]^Q9 w-X;TtO6=VvDWiF.hG{n N7HDZ.ڄ%VAL-uڢsfi;,IК2`pvռLwŋ&x7z?B "+~S Emfkq<,-~f%zzf'.5+v,1\-%7!y\ H^žڏE&Xăl:YV j~̏=[6)Z|a& e?p1Y"x ySpUj|V.#IZJk6ikbΤQ-aE&x鸂%n)2ohAwFh$jKZ ˴3/[m|Y3%PAa2"ZRH#IށT6P?/)lk;ڸ#;*1Cja\Mn :1 L&,ԋGe LIVXZ%c.6y =7Ye3aW4ob[BTG}^jTtS޹:Ȍw,6LZ,oxI,F66DЖԒZ(rp. EG"s~H:TP9]YȝVh,>l6o ˈ|dyn#ecb&ĺd),tphzZ<fqpNFࠠ@'J$1bP,E.~>&MH#;1u( +!'JT'~ AIAC,j~XgjG{Ct6Kcx:X1,4x1ZF,/512m|##MUE7:z6a65a)3LWoلah5eZ=\0 &ىp@ӽRT@w ~,w6;v<_6f'98etOƸԹ*7%I&Hߥ}lSo>HV!bʊ~GXu6;\&"r+)1Ŗn!:@M]QǪnͪn#m?g(js`TԯC'Pݬ*?^=i}vM* n.Ǵz[GfsT9(EfEOeW.5 "xM%\&{-h8#l&_OQr3ShEoU2J~Å5bc9aPcXno#hBb*YeFG[vn!,'~JiaD3pJour/^ݙc?Y\klhH܁J"oLi湗nx=&H#(! _ݴ?[]AsWI]@ 7_6%UMw{h. 0QDV˚G+;Z/vEFqQ,m~y IfKض0j-? 9-s; Z+Fˮ(8_gJd[UfT +")'z\sD~K EG-ױQ#6lu3rSiG O@iDmSI wv~M-KF  ^dPo}QefVG5t ?=ĐOf&ִ ;/ *{NZ4u,ɱޏ$Pe8ukk<4kw@m 7)(O)6eI'AftŅXTvh#(d1G="D:!彅岹0C"^q[91Z.'Du*W aGMڠJA ` shBUm3緰`F:ҪNe\BDa jZ66T\sg7`V&Jd~1LB Ȟ_VDb:=U5cXPB'ðQ%xeUFNl>ܴAQG!+,wjF1,:EY6< 7WEIw?1-z4J> stream xڭTeXKb@nFbf%$K:$iII )c<{;9>^^ag7W@0(_X@H vsB+ @-- U@0 jL@@* "&`\NCn^^Y~"`G(\]@PhN ij4t4@P}w;lA`K [dFA!>+ `8@wo( W7ap ꫪdFw] EB`(y!~`+E#:K hp wwU'To  O `q AGr;E  eiF o@ CRoS ?0Ȁ# 3{NںGzeG C_!.` /EuDM_XD@/3{'-լ?'P PCOT0c'sKH@PߵG>{ZxVa 40?ya^_~aII4@XXH -.or!- o4jP{!l@ԞwwsCG@0{P紌tD MNUa0:*X{`ZĒtuu@G)͚6z5=3lEwu1qIXBOחZaiu;8zDvJ`ZGJZF^SH:?~ݹv7;]֖& mvZg_%6bՏ#𫚍 k]"ӾU(s*"#(>~Z~4R&'z6ԗR~>j-oxOiQ?s3(>yB+i3Nj9M14V f*=9_ȡl~$禱ٟPl,>r_%c,8.kFnkS6ߧ;X*wlEZ$4еaKEg/g@`0ol9/m$9Dl% ԢDŽژc1&Rdhki\PJqL QOs~ ]ANä )l,Y*.r5&EWK>O _<Rҏʢ֨`,YMI9Q;]okn,.vt[f?HkĞci}Obg c`pm{^X !7> SQ›1q&BnDՅ5eTq#Civ'O * {*z8OE^P+~5(;SʹtY9պ#"U7' ݴ`tzM/d8{.7W-J$7ki3A^m\Njy4ڗUhR"mWdpfj!k5("k\l=II_P 9)M#dG_]- X2$/*Vsb;Ŵ|.UKl1`\Eaw6p߬Rv+G|*3+#\rM-fyBȍ_+x6hLbkpy΅G1MyړU)Ӗd^"H<)MQ @ NtI̒[Sxq4tGFŸWbh.3 / 'Vᐎ~10KU:{1;#R 

        &jm3o(zګv c$?ߓ-*_ؿgҽvPLWxTEٰ>6bADq`!;*u۸gxLu8GFuWBi7NE))ˍM܂ok)Ef3u+W>-k$ID+YSmPJ K6߳ڿCdX8ԁPb`3z9a;6^, )Txz֧I)t:D_vomvv JxhJly< F'd$UKlC4Mǿn#`gI#dw-!4clY08%}w~OaKY1u12! ̓\9+nr6Ii1g%+KzZ*vV5-tN&xQ\{X"N`3W}!n'3$2U^!Mψ5[ $d$1]TEsM8Mg s *.'ÑN7T߄pؾ3%=?֝~"[wb{4>v^U'8߻"?0 dSjN RO8 㹩xdER4B#3?n G/a 4'b^RI77tK8ҳ_b<5JnȕS0=󋝐ދ/y8Zf7ǫ^mFߗT{!jҋ٣jDŽG㗫o%gCOy޶pX\Okd"ϘrOm9XwH k {`/S!Uu)^@%^)Dl:F%.S_͸僡K*~Dže٧"pk_BLd1 {(T€pnR+_^\b../CˬN+dۜg6Znp[وͧy, sкKRL`|A.3iQBf.pQo6g똓*kZC˙fc >OUP5H#RC00D%ÝxU+5[ُY5Ij#PcN}˪ qgvEtC ;^*}teUX Bk6-H4~-s&HB.zmeNx%ѯ-Do8hOj.說n ;p5D̝ n@52/kteez _G_~?K]0-66z>&9 +V޶iVص0l7h%WYpr1ůu"%]9#Pj:=tgX!!S~tƾEd1i@b^#3}>6(݆9/[Jۼ\S"{y~ ,]u`bI4$Xb^uG-ܷV0?~ ;tu*.[&< _qϤh|-3b L}WE+?}6ǗD?;;~0zbN~88iMހRkITѸ3Q슃Ui7fٛRV2Y50ΫK79[mR#8Cz2d\r:tp5N1HEz>8j@2Y&`?*"XWзEAɃt2[ [AI.*> f-_=خshM %qhca.K,8eYg#=Xs)ڗfbr2% ''`1>W3eg?gW\ga9g~lp,ڦ׊z]#CV恕{*kSgME}w6a rk%^832Aኯ…IRJ2Uc<VSfJS`;UC,D CH;/=v$7u2vfrz+rC2̅9wa_飵CK^~ilύz4>@I8: u~gGN6dnY؍%ME|fJ8=7 H:׳Jc(?)\ӵ&Wժ)9 \0"h=r?&7U>rb\[7oӆvI|Q+Kyttn)ZFS+tEj_7jѵcT6ZvvxO"9pz"Њ]{H&_`5iGs.zu@iȵGidՌǬlΟRAO]O ]owi.X ;&KMʟ,D)w\%3ҝb s"ةpgT[4} Mgehc84u.1 rr5ȭёSn*ʰLg.) 2(-vUGh_G{jkz˯a>.lXft$.,O*$ Oe50L"fǹOI`.rV+%˪a/|Ѯ9&''x9*9q!]N> stream xڭweT\. 4Xyyy(bvn s 0FMY្Lnyt\v6@[{ [f k @LAQKZ^@#) ,h9qٚ*͉=d4]M@G30w4lٚX;]nf7 {Gw w{0E;'# xϪ(.` #_@jٻ_%{l`+\@)={0{G0@Dp9Zü;ߪ7vo;͘Xssli[3;+?s: fh,o~O ߱#@ +G -lm-od>1%cd x3Y_r1YZv֦D&AN W"lb03~r5[S5-0NdbePmM;UgVWQ߆CVuٙ0vFV.#;{?;?rF`G+@nֿ?Oz拭_c65}6qvt|'^y h0kg`lg /Ve[j\T36shnך-xCENۑBMY5H#lZv FE}gcXIYx֗%$*CeՏ[ޞ ؎mBhDJ~#i]L3nlԿE+9WOt!IQ-. <{4\ My;V#)L+G̀ gXocoNg!ԝp`u$h V_?1bfi䗚9gjL>lEw~dpqQ|xdyϫY|-ݐFA);e+oosLCD'bR7,O_KPx͗8:<~;ÍvufBnOH:p$Or  {KZzE㓫5 aǻVL<|)k,|ZCUO4'F O* {OWb&+c,n%L?G2KN:%=v/1ƒc2P;mۆmn!^ck=c*̸%}K{>W\[3' ڤ^W(^1|l{0)$ hg\Fv'8N+zl|WQ !qI18KBtw),28e~u3`PA]GL]I%UX=mMw-+jT@O=-uhP;+lkb"ߵM r|u8  10{SL'M9!&߉7|L+'{mL9.go🢦#*Htr # qqn6u,S8x3ik}4A9?.5-K0 @'_e.aT h`,} 17V %Ks] $&&{`+[W[^DF ez)f?}*:dy,ga֯|.Y;\,DS\[<8Y!8,\!ym`R.Ks .-rFyhWN۬cE|~G8 ,|slnvq {r)HKB7 ϟ6\_&MhʆE?'ˤz{ 1;ZxXb-;Gi(ƭ{d;:K/ l*ZX{b>gBO2$b4Hq_^/r&y9H~l}6!C À`$-ڵM`.N5fK!%-gpĀ9ؽ,>Wya&*(2TW-'9>nwHM^6> CJ>FH'igFID.)RXKEϚ˝tRQO:=9w/3 (_EjF|HO-ded01.ϗ| IEw;2u% Ar6͵ ʋZ¥!W8ɔ\$^fΥR>r"#gN*>}6xZIOSԓ:y.uZ&KE'0ւRmL7YO^80Oz_o~!9֑T;`$Krk!ƹ_io+:)d/u|iB" b>NPoݵ3Xy$DvV$`W * C}~\ wVHssEPτ뭀TpY}IQw00,פRWPVwL!R16ۣx㾲d9֔ d".IՊ~NodT$͎,I%c*w(pvsCj?/Pr E^F&PBK C{!$v=˘)f;6Q!0iriHQsN]h8V0,*cv/ E0sWn~ m#?vdO`7ܬMPm*mcZ+ J@d5XXf֎ 5 f‡k/dzYFM3m3c{ď+Pz>aGe4%&!&M-?>4^P<;M(.yOBy]-?#5xPEH}áS+umj5CTwsI1\؎F,.&sL@,@ SY*L]$i2Iـ.@bP~y>8>jm^8}-PWPV-|:zڕ#ph8Y 'l+"= B!^-G,K I~ 5^"6tJpT6Uybh؏bTWxjoEddϘ,yeG 8x*F 0yc~F1Fcy@**%_OCFYӣTfG5ɴV}#ΌxJ3ʁlio/=[u ^m/~ߝG"_0 PIa{ު,Vqwl 8[r@F˯p@D*ZLNf`_O~ƤCM%8pZtp "DYND.<^2uj5WPUZЖuBSؙOD`Ēc*;-SCc o[$EBkqF@Cwhk2*{93ֿs[f@b.Qɟ]ȇS>_!_/4KU9.h%1MK.=*.aݨbKcAqk3.o2!brA|$]u,m8G9yv{h4w(]~)-]W]lz=XںW: M,6P;F_f"RcJ >m͹,|rk!HKf{M<E:4"mf3YY!ע:Xw|$x0$ mt1ݏQ̎rH|Tc ^QAqm%ꐡX}<#$!W|6#oNja-&"'@8y[6KHkh}2ۤ0&RO_P8f"U.{؆om<]7QZߎCP"IMzx_,d~ ]1k&mO^E]>V2|EZB2mH\}[DF[xQr8j6Ln_5suBׂ7m%'Rۇ#U{.rUh$_5`{ a.yZ`;)t)hC&^ GN4.)'zVXDOotZ^RՇT[1Oo D7ʫ(O/įgoƨJѐV\˗E ϿhZ%5ZP42?YֵK-5k*I@]xM;kY92ӻZiEM<. q[rs#^cv34r'~ IXޫ.P9Uaٽw 6x$ٵehV@H n_.椯tf褼dMkMYWDnB;\/ -R*L7dOW&d5 M>;lA)VMmS۟|$3ŊNOgxus+5EzozxMeDCDF-i Z?P]w|aYoKSL29"z㺧MZz;BrHU2Rtz]ZjOI4,GS;Yr=GNu\?pHHãu@bDZnqջbTmN$I4p4FᙇOm=P ]34Ȣ{RFtN8yl^Fsg;)mq d}9̢ɵ3 `;1<ؿK 䅭vb?J1-؃D FIV5DPs}:Wȝw^hLUSXoǖ[-lh=8r}G#3I}ق%V<馿tmo`xp]{G.Ey:-n5ku QcDa)Tb&b{ .mqN:1qE{<3um>/o{Q4 IH 7- 6`P GWYr\V"LX|ȩV 7ęH~ؼWP:+$0Pw]gEL K\ȼ&}4`qxlPYhu`g8D<~UŹs߱ YYerML1ceH =HG<{ :#pΉva d m@C+Xz[].kH*P='W67W۹zH!QBP⧥(>s/1"˖o[)70m a2DB㌹+S83ϊN}eP⩳򦌐X8m{MH-HZZV?ܻ LO/+^ '=0憇in86WfhRq$!B'wV@B!GHh/D]+L8V5bi,BsRq>dfh} c«_ЀGVe]n* fR i]܅HoB }RM*/toĹQFw%z/hN'!Z&.fEx ~w̗N%\•KȻ7s/`Y ZP2_;Y@j].^gQ #M3 sJ.mSZOʆ \qz''u"[6z4)L{7e@ b|Bˀ즊[|7bqyZ^=ɕdBf ]P=2+]kKb.~RdI,N938_:dtwʹÙʉf-/%Q>B$icQD9"g}2!t{ޠ:N9tàL:rBNeQO/;hqu"UEiqAj‡GG9XZ@ot*4GCOًضׁ 1|w+Xx i_Cofwգؽ "},d Z4.1*/,z;&ů5aPɎn -ouQ{2U?%W|+A:m%2ʳ)B)dP/QLǗ?.&l?>[&dmvtt]̊'B*+-o <;P1jmi~{-Dܥ'wG}ISK|Xن Uv?x.h܉ʵO(nv›2F_Xiԕ+@-ڝidM}mzA塥\H`PP9f'?n[gKLO,f`3w*BL];v#",rKra[TE MNPpȾߐRF[9*TWx[h*EY9pL+ ?#/wX4P>`ҵ~WTJ(~k}Xڏ$m-.H@va-[G&GK&y܍l :"|e'IhdNu 7+M~!>Qx"xޱq!kdDW UbJAq9d@'eHg2=Afn&fs^?uX,Sr6 "d,MQ sElrx~/d "vZ4kcYWLܱg՛Nj PHZlV]jmVNMrљݟ'v" ±EnRM)D%jː% O1x(&UrD9!Iw6Xmeax1 TF[vYVRBIi(}3*i{6vHצ/kff"н]ϓe٧Yh?ڣGq Qi1ukt.EPn)? b8[Ol N-a C 54>+$U'E]7~/J%v7k7yHȪ=v~~ɑQQzfą:>jnGfrP8f"jؗ0V\劁G\*l̚#:6$pPt.-6CkY/Y0Yak!2%uXW/'  "hf3 ,:S,Fy g{cD TdR|A,;A0UI8 o5%G+ב6yhMdx1<5wDlxye7΋#S 3Th Z#i毎#Ϲ{hO|J+%]{N6eb^55amz(YAf.Dޝ0tE&ct:Z| yz2Xɱu>SPA3`2L]hh]Qo4vYH0tb+XeA44Eb ;찰uf@{`G)Yb|wɬ;P_nq6\b쐋GHH'ߦomXWdd#,p^6ȳ/PmBGʿׂ%_Pu_R-F-%&fYjڇasyWmvO?^6vRKMsb7!t}ؒ:n5.[^EU~[} sG^ӛ7o3juI@ޭ+h><%_S&xق:-['D9M 4E Gc.싞zu]XP[Wh`ӧb_Qgc!tg_!d@Cw@F?rEJV/u󸢡J4jHڣnxg= 2"݋iR)-'6M[EYUIJ9ҁJ ͵rpcvu/\qD(dձ4lfČO㜛^p&!,_aDq h63Ŷ13I,0 _7]smRp#ўz@.ZunVy*ze$Ei5Fa`xc[.s.;]X,3B ]3.1zJrq8F>.$ao$hҴ;Q|y2v#fQ"gjoRg9*Vpc*8ָF..\/le;& qvz[INW}uoOD })}ni$"#Yddې 3XJ BL*+ 6~1S9rB ,e;,õpT$0q wAђ^I O zTLP`Jt=l{|j_^Öߞc?-6^4:38h;FeOZfBbjN7)`3[? 7ͻN) Wڶ;%7wcBV4eMCK z\+wKio#H)WN"95Upq嫽LIXwa3ԪLƹmZ׼(AnG= ,5yidYCJex b`[LlShp0aΕ4#(x՝#lޒO+MZ킲9nsM9$Qu1GHON0cǘ>R~͠!~ BK dtFȶ vg]T΅2ļzj`MIR ^\רVzNUm7&!z@d{ĉƐ#zsEيFa Y\=h!ٛcuy-oI5qiVV1w%MR0iæTE{d'R1qVBF0e|lϫ;~Kdè}6,|Kv߆ڱ>k2b.A ~$sQm.:NSk .[ (z,$\,EGq Uw3AI0V/XVEB )/֍n9Fdi9I%F|]wN>2sj DĵIRu /C6 3:GB ܺ'/Σn]DE)# PN, Ym/Ddl1=QqJSy٧€8^lfse^WThoU}Wxc;-@Vo{[H4B?[Wš1W檅`Ŏ#͉LOI&,fſKmK$f@MYYD2rV3EqE~\^S^\ &; zblB,J"t8Wpx#kgU R+|'WȡTʼ2-ڻq[̹G-4tK20QCp<;LD)YyB]rpƶ֑p WƎ4xm&oz%BоcS.G֨C(ֻ8%Ko ([FQZqc+S[hx kI}9.Iy(\Xy~lfr;vR/RhTV$3٘>P&Aqd޹kOɺ.[9'MDHzAZEҼ5yQa9Eqg'{ +2t9*a&b*i}e0'ʇ>h o# Lۏ3[Uy IU>Jy++ rAo=N5-G:^;4-+W|wnA"Tmyšl SVi" SeQA ) w|kpuDl _sHmOkb.--]Lޝr dj.+wHo7dы|DZgu#c _6$!J՞s?,L.D\y @ m_G H*7Db]<4:VLJɦ>ԋ_tL7:ij;I!6(~D3I= :Q=./gS & 0cBm#|') zw}@j4G8e<Q3t"Nj.-9׊64s< )WUr}&$y) ("?78UnCQ MQl>-T(m@5l=e6{'S endstream endobj 967 0 obj << /Length1 1630 /Length2 19701 /Length3 0 /Length 20548 /Filter /FlateDecode >> stream xڬctm&۶m۶t|۶m۶ضm={fZY8 GU+ $lx,m]TlU.D0䢎#gK;[1#g&H `BB CN$jghinLDIMKKL=Cܖ+`Q rYZD$$ԉ$G#k"%ckK"9KΑ";[SJsb%DdDd0p7#8X:9't"2w4ug;"K[k+7WBv-l)99;8Z;$&<-dWMdg埒 WldiD pw'1o`J?3#r9Z;Y'Rǿe?stvX10i7- ?"mkfGo\j?3C7 #S;[k"S ߐDTw,3@ -_9_u+!{dl"9_5N&l-¶ab`I`dlbAdfdgۚ-mW[虙Nؚ eh7_JYony;FDΝȋM_@7rvt'[7~q[;FGO?jGǿ$?{`lg3=+ù3oxRLwt8ľIп֮/=|.ycPhÚ/p]CJy(Q >B3fQnLIhoRYEڟԵ$1"rpldx!.mn49o9qc+SRvf-r'eB]T+Voa&e8YPXD:q5eUFtUXKetb/d *!,S) &. em(NArq(z=.g(^ 9J1n|LBB?Ci.!U*bp@R1D*~VC ׹^c!Km4DK?x?/Μ 4˖Ay4 kK^B *\|", 57FVfwfEr'g=߰aS ÇGZ!,գȌ?03YjA7vDa5Dۀ}LmBIrﰏ ?vuZϫA'=!X voJԞ+ѳoʰߜqH(65LFUQnpsuW~VȑkFzHVi I5հ 78i =٣1?* )r?j!!>_k֐GZ`+ A+]='*׼^ٲ:غ[bE {m I -p\l@VxLG\nq~Ej΢B(BfF%o^n3SfM-mUm遠-Oz󩫀 Sj؀p P,%,/#Xo2_'.Z ђ2IR] ʧ OG,i;>vSUI5,̜BqnY-SԸN>r c?fqVXgi٭}׌8fhL8`0Ù8*gk;zcE3W Yxbwߞ!Н(Nd.*O?rW@6Am6ۚGu~߷4J0nC%lZM+}Ng2|{W\]qj*_~^*+߹X<^stX0tZRj‹_*|% 4J[=K lobueC]mbpER>.w%m+Y0:@ RdN@H ?zђ'uh}A6q9n{U+zYN+Dw6SG7-=w|s6^.hW6t[y#7S9'UCmί)N/)~`zyIe wʹ6OMzi{IA˄''_̪Tτ&I {Zܢ5{ VWxjv/p ٯglUtE;yS7XX_)dлBך uZ__JM߅%m`s)HwBSţq6L_)"+zSXnir@>swyԩQn1Q)ؽ6!c7 LP2!|y+>nSkFٯE&qmElɓwїDOm|'4ϷD&ԡϾ\0F0BcCə1 =z80w'Bh5H|-OCM4āgw-z:4!ZldȥjNtXҷ /bw=%Z, 2XжQjb'wa,?Kg5=˘p(g0T~= =0pPuѼ_{n܃ &ތyFtF쭎/U3>g ?駎ht{Og/hbq=>6GzĖe'__#bbf/YdD!-X#ꪮ\(7)wqNe`ŏa 4.[pyne[r&Nn qm5Q&h/4κflIZs(@[!~}Ŗ{5A5,bt73eKPw҄ZXea7Ix3Ѝ3arؾ]"uY@-a(BysAE`5؋;ƥ `L^p}QڇԦ.6Jב{ -tl7~-!xc]rvh"S'ݲtRA(S7 (hkwC\^M\a%[ݚisg+Y=~(@FZ8LX_K"P\6&*=jzMr!VObf(ԆϤ;{7u5낍t3@4>s^H֜߆?Gι7rbIQ*IXQāw`A's ,[ƇDB"):<>C9@UkxVtĴK 앭)qizp1j513gKhw@u f` y#u\`| *8.Lq4q&te*dRz  F_|шxdt')"*V+E\W޳] .r_~f!>Mq=!@`߉ r6+tbng+(p 95g @H-"tck3Ɨ%Q71-Pf nY֌yaNa@Qqh]HuC|&Jwb)ބX-07Yݿ;8nE3*lB'@v@gYp%@n׈{W@:K۱WBA|{kP^)fz#HCDnQ9%cfݲખ`ʰ /6),"J\uќWC;$H`# $;uK<[(f uq,N.By9']:V28>@us_UcX$XY==ųCEVid(cF-Ӫ )iIzBiD)`v817Θ\2tӍ(Tdqfʂe[fڳ{3Bfr}#o: Dc7{jZ$,-mEk>bXSD~&Gd:#GP"xr7^;洵{kNKʖB*XvOlj3w%%?MK_4q~-Y)B&e#mLpAjz4V*,#2* ܥ(}f 49 cE njqm/,wU'tA1?s C/(B=#+47Met,u҄dXy'pN¥(IsL':p#Sx=d /8ٹ5ptc_D !TGf(|NE>O߲WVh[ry6ſO17 !,D(Rgq9'"#Rx4 QY`jgqίIM Σ!zΐ8>Kt, Ed츯 Ny-kHT 40[@E}<ƐZ&mW{TĝC?ۢ<;j4Ԡ*Gĸ-q^b'k?Wq/vgn9)t˿LfNj:V n S!2;uXnWWfCxFc{ph=8F[vb.AҘG{f:3W?A G> 4nzhps#_T:"w-^V33j϶adm]8-zWлWż'oxoFIf WiBϿͷ1.?NB[:DOܲh*e L~r+VXSB*fP5f12wifpe.7V[^G,8hڷHkS"/U)Y6UNue6qpiq|k6'f~ilR=4B(m_~G֭HiҬq!Ńm\ttp%dܥKl>S1K麥FJT$Wk7í3:rIINj?`ҧe堨Şf-%UﯤAJ<3ט ؈9ҌҰ^'P,b DF!V~S@?N6g.^j!~#!2Ǭ.*|f(7 P X&ۮ%4 B#:* /J,|q{OjHWco_6bU 6[B4I6s8q|4~I iÖ4n׉d)5x| ,rިۈ*uG cŁ!sJi金hlb7+}rV4 1dG]$U_2v.TG'HM,¯7t$wf诘wLlLW;K!(ôw E$OVL= 0L2K DtT2F0S+NbbߪYCH TR _x|I"3Lw+&L+HKG iƒIJ`~^-qFR6 E[ƌ%?He|E[k%Q)J+h Ard0mI9!"<*ѳi}7P-0B 2*CdVPGSҰ&`rpo*®Kc" E5WpIw.uԜQ͊p*Əp/c ^ kDzFʷ9c?)ܱB5V4 jN Y(q9%B 6iUT'<3F4Si u؍ -'+4WM^ ~GFtN~MK:79@^@ yK \1l6h۲\̴ ͔ ַ0ԃ wPBD0m5CzDP`e +@whkXǀB& $߹~LitJK5:yvv |P[#.*i$=묨q->M'tN|[i/׷3^MNldr8\F8"v@hC@Y(y{qٮdK}m7Q֕$/$!z_h^\s>uqPdwd/[Ws~3}/i"~ł<?&j[[GjYnj7a70n_d#S% Mڢ$̺Ġ)q 0Ė\$*f9D$oomq")};[$س oE\˜TN䷋Lr4ɫ" b#Zᔍ,_QH9eĉ+ ,Cg.tHMСQڞ%r{d"y & kwj+ $uOֽPK1tt ! QͷadD) Y]n,4?1򷶥 |L HgPGg&y"yIbm Hen|֏K~LB]! 9πs9R5 >L'u`8>"uT :o/^+<4#DxJ 3 ;&g5 Ӽk ^_ʊ>n'Ya!:0_ETcވ/ib)j% y ~ rU ǂN0 t43 r2 8;y>CN߰ ؇P'K!! n =03e:-)f|wUObgCԛ j-]'U!ٻw'ṧ8#LuuMp1I3!j?Cj!h3ʑUc 1G|֏*5}^v 1Y*$RjfKs+C+Bˢ{ũv5 N٫WmA3R/gZGzÿ,*L>ߠw1nk܄JC̸ MG[< r%_j@~[&ˁq=Mkf1gs-&f\beTKy}wߎ.>B|3OBm`*YLV\&qVStՎJ&zp( s̙"M L)Pwqh:4A/&p&mj#ok),fQ/ "HIDxCƼuC$zD9z2[!v_c7 $ь5 .@tY%0?Cxl$,\!FYrcDA.ӣVi~cv*xTΒĿAOG<5U2GKjXz\Pw :٣^C3-32qsJԋt)&PWáp:CxQ>;?KËm5OMsz"$\+e3eu\Ҏjsq: wk2ܛ;%G9dC5ЖlP*i }D8=H)yr)sG]3IQekK%dN @L;qkrMaCm VBɤS&9CWME ~OViYuAʬarA~ i(v gB(H`1_5[a@:ޛodj㐅}~^E;Mѣ QdRV"aqk-8Q10D#B=ߨIO\VĭJNd"tٌ9_R7$?=Sq l~A nw[fB侾.n:%HwCǶ11tJ]G@4?Y1bl;EI"iB/nQnaU@,.ɵՑ *3eoYc,tR˲@L/ _XDl}4&b@}9|u{"X:ˠ7è.FV5X.#v逡9dHIրbJu* hDT7vdSL/^ڈ’oN"Dǧ:~%r GHm` ղL2r';V%֓8at@  )d۸ڌY}"G8LqCd1 6~h ޝ#|_KROQc8ln&C}r@-ѠDZNVXBR[V{!#UYVi_8&I<cQuǎ̆`9&*ZЯ;]RWDÎJkC$Ŋzq;m\^J( %-EL7jDQ|eN(6roi6bU`f߫Jeg^V Yt!`D9qk$Od+l`*"mŗ3Cut u EU@2xsF*a0tQ<~ A<̺ Ì0$E8M]#1*=R'#ĝ#*tf;q+~% eALn&_͡b8XǦ%S !)n4%IkA i!RAk1yDW"=K/eΞ/ڌZ]`O OjSaKjMbĝ8zѭ [)H90Gگzuxfs1:M-:o\]3.xy]o@cX)zŁX52l?ns%jW0pH0:ȝB;Yz>8E&8{ ׉B5i%B߆6x:ӄq!*I|}QO)yqN8]S@dnjELtv^7ꦍsӡ&95sA*(@\ DuQL|wM].+Ey<.x/;.Ѣ=qz#ۇV1Yzh'&,_b}0]bÇ802R5j/FWHF|uG|3>FTh?b y$9~HPc($ah&U-Ww6]mPX`Y=0cڷ&<6y id|H滫NnF&[ 6쒅_M4KETJ Gye2u=+cuO|Bj./7XN}YW.ݓ{XydkyQg4)R^ 0jnl=B1K! %sEK $@짋쌢 *OGt<䆺 ;-x~d%xEn٤ؘCI/'ٯvbv#J]03f` %WњAp'X(q(oLZa=V} Ij fXm(BT!E/a5^xtʞ$(^ WTpc6`{r;Qn~B"!Œ?̾+ 2*6PQqXDf^;/:S >QT؊0fz¹S$z] FĮ7$9Y'3k+\-TMKе2y#8e{ VYRrnb*X!<6f)ծE뵫7D̵+569X\t [Ԅ+.{t4,% ipjY?ۓ%@VT,jr耤aB>BvWh=ObDk%: txiD7,ZFڏLݖa>-r.N$Mc1*,7Ο|ỹa ^V|$`< 9n.P@_Р%{t2G} nw鰳JԬ*i@&#C_]^n:ΝgЊkI?mvL`6<9rʠԧQtmR$vHY;x(G#fr?[Fb9Eycv;d?T~8hkC.W+_5SG#Cqp4UcF M++`mft"x}LmlktBcY|3xpc2[Njqҷ]DPKQR﯀W![M)I=O3ǖmGh]8[;h%`v:mb$aݎIPSFR"V T0D8UW.VpV85~3D`sgHQ8wN}AOaJ圇s[9c_@%sM;ѫ8^6U'0ާaSr#fs$v#GNkJ4Z>`JjpuZQ̊Y"iƊ1[ }X"bZ4ûoS( Hj$φnRV\P`RyH NC 7ּh90f+>9X?Γ՚Hb?# dYq2dF:nv|L8 Dm NUYS+E4do)@AL^hD/ueV9 v-F9xY!9y4Fn: %:|WDCְFg>N.:1vE: W!LQW]֐ѽ6'Y=0e-w~Ov2}D#ݑZZJ_]&$Y[4vcZ`9čX3,$9bԞ1rSs&K o૚ZNu;[m1 ݑw / ҞL kCWުF4[aBZjWkoU,g uQgΔ l5*V":P=Gt~I`Qu&$"޹SMb2{ SSGCq9_ǷǡYJJGDg_K} b*n!A6ೱk rpL$Z$ۢ=SpfT~]Bt}SgwiRI{?C:x(:)BC%! ݛF)*oN0@O-S|(nэleFen=Nsw٨ 7}FASjo&ܔ?' FQ_mr%ө'FKwv$^ll#k4n&qk- (RRAm~JkDUBU K*SNsۇrXUc`޵q?F;yje▁L=?j׫, ommGc+O_r-X+ XF,.l0AĬa! ?|P]ʁ9-Ex_-U @a6cv3i150 Mqj$vM*& n\=Q9J3EwJ'c?WnT{{xLh` TH2y좶WRE$Nk;dr| h^Ҧ_>3yTxџ/$V8#ϲrXEqϗt軏/[f,@G;;3hڸ=|mR E AJFh11n32$\bqja CN_E5I.U{=_6Y}?ai9OKBڣ$KyZ̑ .^+VmϨ $ vM>ht^_WK %63>uSwDh]A<Չq@m!_@AreQ(zxI3=6ΉneFnf'fuW쾊KyD;8G" D<+^PV-,%-G..P&5 }IBmt}nyr2çWlf?@d'Mb⡨I/Qhj4`KcvS&{ڇzڡM?7p6O‘Ѹ{x}bvP`bw7H4 |K, ?jsi󣂡gmUiofTn=ȷF2pVB*!sW_6LRV:VCiwhB2:2y>;ZEpE#UL"*[ K{dp:-i`S\]+ w8_byTÚVidUn^@iu h {" OT65Hݞl&OQS$eBN22;f`cJ#~GqOQ'}c %W: m T1&#ͣ6]nZ6h"x'{Nɰ(Α *Z51 0\9W3t!>dUj+~4W[-+Ug^b(?Zkϛy_Aa 2Op&An+*ά ]h3/v:6=YǓlǽb[[XG~ i#t9l'4o>146$K?wWFJ(;TNHӓ&n V}Cm='`Hk{gfe]-ޞf!!}rHmmw~^BNCFݍIt'>[,Eʻ[u$Ju̪ -~J<vg_^B)Gʏ~!ժ%$>V᧘̌*XeglyA_,S/,>Ni#þȗ:8}eLm1`󾨔۸wN[<=Jhl_\Rh+&PME&\=X2^`>==7)>R5AQ:t"A˥wT1s@!ruٿ(0JH̜-Ά#RrK{aߞz?!xhc%hg[unque&k]f}[._ۄVf ]ŘQSnR1iКL+ƀOѳz&.AXԆm`/:4ꂇiەl$D̒^.3C/}Br%K8 $ ؽ/^xxu\KO;P(SW}MA\PEZ@Vu+$MaiM(1Y>Θ$ڧ~Wy; Ӳ;1夥u9W?K.'UW_X$L[NxQV٦'މ=%U7U@,rPJisdrDC1-r^S,PQ)6s.%*`;>녖 _4a#aSq҉c%|Y]iXXV\!Pi2t(j( Ik9ڛ9Q I^A1x %~r<?p,%tL[JpۧAC{"c=ȉyM6iW"2B,|GO*OݺtOK8w)*#h5`鯐IJMK+3r^6T R7`͊4wV"TJϜT-m#WIk%{RɔgD(@Z恕4X`ufv饌sN8'N}d+ !I#֎d0_B}F~xM:%^|K)Yn@BuG2NޘUȆ%Q.7@37X1#ʉ\I;l's@hwv%{uB/{AP>"V K6[9T-w-;Л1pZA)܉?(&|uY3pZE@fRq*O׆ QB;U'Klw\cY~t·~cåAŷ9kO撣(&Eb7Bk NG"^K&*R/'.Kkh(j_|t q\NSC[+ +ËX=9-t v"ղp).MncD7 ZZ (h f_nAZ`˙^9aZx|>C)SdwKH1Ek("f*W}t'ᵂT ҙfdu42 .~)A!s޹|=V7K={GWzYzLbXznw=qb@*Rvu׫on\S1}Tm3Qֵ,S@βm.A3gx>Tgm ||rD{սEv /Mg]+O9 rr ޴iIj6} w~o4F5_aozT&_ySTQYUvu4A `yAR#G>omy&6η~K1D>y 7^:8RS7ض;C]'2+JA4~̧TX#D59N endstream endobj 969 0 obj << /Length1 1647 /Length2 17320 /Length3 0 /Length 18182 /Filter /FlateDecode >> stream xڬcx_&vvǶmImN:NǶmNұ̙3y2s>}VUwUVSR6I]x VvƮ*@; -/GN.dfb3r1hL,,fnnn8r(@IMKK\ƞtPp3:ؙٻT53X̭lJ *Iu&\mLrV&ffs ֜r ;f&V$oE7qF#}ZV{.#{]ll,+O)zk_=3;VVfJV.&s#ۿ/_u fiebcMkuWZr⢴m/gS`4偦M gd>Gn6"bϳ;3_hM̑_`W'k?zfff&p+@L:켑)}#jŅ5^=JPvSñ>,[4_RBmNN FR3%]&)eP3N0WOndH~& ](M huEgOC#ý7F~R\< M>!_8]3?\II2^ /,Tw3"΍ML^W#p,N@:8kd&Zn7Ƶ4D F*֬si[|M#-6V^ *MlxGWc<.S#sĵ %┉oIPXR?}GզaXQmCѼ^OHϝDTjTN7 ~VZz7XrJͨ`/IW0>2) LzKU?<Ќ M΀}*&`\و8#WA`J#`g8/վFh W<]mN{%~ECIZ,O#1D~pܚnFz;i4"l,>Na GQj3vbC5}0 7%hx`KTC3WhͪkZIf ?ffJWeol)cO iD{=1{ abuBy aknf ɣdV=ղ*rX`&M9'I'bvBPyM_ #f ,H꯸qra:(w3̡!9Ƅ6R3)Ho=ݴ#ڂq^s:,ƒ^U/}o9dih:{gЃԑxu.8S:}w~E۽*k26;v4Z6A`mK C}QbkRxq+`7,!w=l7kKmw= 2hHUZuKҮ@?qؤ."iZI?l<.嵩nˣ`Y2Nέ-^\5˃)׽ׄt«􊈂ZV `Cg,nWl:wOᔉU뷍T.b[B XކOjA)N&D>|CmSzc(&6ДTɲ”PGYˌ} RxMήY/RBbtHy?x@z^Zt p 7W֮gY3! }P K4t^j?]T%W*0颦 F.^ZrRpn.l,tL^s1ޙSqO.j+W+#wNV7 ` 0f@ɒm>hn!~< >( e\ziG3.; JeN#bűy%[r]k? "Sy Ьmq (LXl W}r dԹP:`vaڔg1 Sf}&nq)nfQVn-;o0g>fGmA Zf W%&xzmjc+~J֗!bᴯs{W؅3Ԛ ۨ8JD=t%֤1 @\ȷJ˴ZӜ([AF#ZXP_DX au+EJ$ h~%_.6rX9iYhɂ()a|5u}'OJ B-#!7KCt>'Q"zfo Bl1J{rK׾LP(G+$ P9pƪE^X}^+}򉇝7:8\ή'^+;͙"$WkQ*Ƿ=Am*HG1)݊|Ul(W%쬨X ABg"SR \A~v _S\7kRv LpeP/:4l3ʲ=f 4αS{D[ud#w;."4֭mv^oOЄQ7*Gwn%XZ`(Ee6]1-lZ:ί- $Z`SX\P(joN3X'BUzvqYSȑuqJABE &G۲155xqy =ǔJ'G(c4gc9`5Sr>=Tv"5ooûݛѩ>3"M }Rۜ [ToQznLڀ˟n^U]|3,CU n" >YI[f$/ OE'qǡNFN~ |\Z \Q]`S\Yz/^_a v#b8,]ehKp!xYh_3L[%CHEӹCU.'["Nz7ļk7pey G|;YFRkh!́d']"mI첹Q`wQ8@zY[]P;ke%vKmWEFrU)(p IfїZ.;)a$>/dAp9e$4RTYlme k0P%CbCUl=,6WN8p1*d!|I.>[2oއ?Wo teMꀾj .졟!h .#vĘ;m;sݸ=s>lzSJIUo~ZI_JHRuWoB< :?I[D3 6wіQ#j~ v b)2&髏BNۜEi8\z~LCl'ڣ݄}TWzn Hh`XZ{(WRe)iv Q!}ư;r>:SiAfПcvƫ5& Tմw祜oif"rO]X#XK|?ËCʽZiF+]O&ފ'Dtacv@KD&fct ͽ0z.y_ I՚W;.K}>7, -a151 \3tN0^z_p([-2r,1t4imdWQwǥBc#Rf0~]V{c كt9('Z[aQʙ qGN'4)tɈBAKǹpil[} ;ZUZ.QFXr:#~TeJ4gy6=~z Bx_Ѥx+׊Z8zBv\bl^W u 殙31_0o~*%Ӡo&o[#gzalc=xxO#NLφAOqHgep%;wgzJgKCzkYe 3 u lD}XЖ&;PQsF1sI-=ބzBQ0+dTwx28zjF5 ^z$2/\g^j%~H4*B.@@ Rc nrpg37R.>#ߩlF,FEnHJ48*<5KQ{z],kn4]l<@4GhNU'`vSFW 4A%2`H@Ŭ5>E+PJw ME9?*ñ`ћ뚠Zؕ}'[5!PC݂!V2.[-7PUvEIP7c,% K>OBu1>N0Nvtj^b汐ÇwvF4YXإa?r;rg83vS fbullJ+t8pPImxb0/>s􇒢0qa0m=4ΗLr-bI삧҂OM?HySKos/χ:q }|.ϼԷ.Nz/oy:+E "|@kh1v53,eEғ.wZg5m<`?R0PY/ȲDl࿔∿6>G%)zw֑**9듯6]m݃/7 WxQ|Hňl\To Zjfw᭻ba=ֱF#S,!nGeN?B;b4`kQ" n>z5 FHOL %QLFʰ8+-z2+AhX9'(Uhj1M^ @Q9ng.RN%G'b,dzcu-6.?= zJw8!oe5N Ԙ>U/sE|QWbi*DBVkrq9)tV-au@+m8+ :,µ)5'[H7bu(SX  ǕOYjpAZ9`S‰iYgJ=#Ǯ,9R2N&Ϫ;4^.揓spxjG NshtYr\B`)q"д1U'8'RBap0C04[ƀ*rc'||}R3'']0bvI5]W4y~mf$"BO"K}3>.GFO! #2c3Ć%TN]">I%(6Ww$-o(.tc!THè qyWGGHDiƸ{j}nXD/gddψ?/dW`K +F HrԄM|ccQU,~߯JuDT0 *:o]1 a{Gx@>dUH¦-֩? 0 dBCH &p,[i$Y+&5Jbxtw+uTGH02ʻ_]eN4'ظ}Mv9["?#咕=9~] KW3Sԥ#nrf%˒_L.el.ݹ1z46 S2 RUId8\p"Zr:r&0}剔ƌR-LeM'RxNエ_j5a@Y5ؿGo<㯧%)KyvCSLe\"Q3|i|Jq2~$AMm@Fg̞ZcQEu _ωt ܄2F[] 8$ ׯr<<Vciҍݹ՛hncw~OڵfJҚLs!j8d#:/g`y!S4&Ӧ=yT%JbMG2R){b9'SX4hb?kQ~ѓ1OfU99eں QXDSѲ Q3@)/xmXSŬM0.(t#n_K\eD1\Jŷ35P^ =r]SfPisZѫHx: /`}$u(∸~sȐnM3.9.үNi7$>lrU Ϋ,G=x~T/&R C> *97CYyτĿ#N,ݶr]HEj.c禶*fZ"52U+Z6~%9ZA*#{V NCt(c鱙EQ.M?[=+sKQBlk]5Ǻi> -fF,c&y=ԇ[#xs4 IUu DCӗF}6k[N yp͌Q` BՐ2ij%?a! |ӧG->9g`Fq;zjmf~D/jjU 92g5=Ô(W񡣫_2'z; 'k!yU5 euEku6`\,S#8dr83TޖYS(婓#(@/.~XZeLv.l'0qѸ:2$ړ[ƚ|YO'RHʴq'B~eC6 H"ZZ!w$Q-(pRu}>\+T%{fN֝Pݔo(!۶T #M; EAHM#o&;uT*5UdCa=Yv]wl5[,n˛IU(RD 眖\bg3g1θ_>"&?ghNSXrȩ h2 pwؐKs/Ld67,t#U jH&=m(,BxKspϕk8Fn>K8enu=-UNmU$Sbl3'hwz+s.1 qm-4,_tA_ɎzC"qfN`}{kl څq;gۼu}nW B!]yWs&zV[27w~WM6X5>PoFl4}H2fa857Dbd 3U'x(O!*_0eڽ!SysaJYsE1;j9LD]70m| +`$E9]E ^U, RU ":Xt'>HȧL\nj46rxٶTb]kkv#a0L;BHO) !7 tEͶsP޿B{Fտ*1Ԡ5?gr6 k u $u($~Kt6B;~^R)%@F } DωWe-6.+Xn ttJ(Ss23~A_mBMQ3LPp'ԕc';iۖtŷB~MH[T\yŐW}imҏMM}x͆_C*knYRsPV6hqnc ٽ`.9 K=] P  {/̸tuGvz(HDwb)2?MMp>EH@V\t!~Qޙ3 X\ցxep 'sԫD ^gkb38{#D|Q݀%d(Jf  {mjBR4\ 2̆HnV'*}disXuwY If‚;)< tWB<p-z8[!G5lm@.GUͦ P^"٧jWxo.o %e&lAm MN% C;Qc)YTFr^X+^ ),ER?HV61;`8;g 5w,+oAl_U[Low&noJ"c詋~?Q(V Oo &<8L6Ss6 (ӫ;X,,'H1UZgN4q'3rCCl#" >i`zb8v !AL£{ 0_L$jۭ|Eo`478/._+Z2xE$7Eq"jla #B?U'[;ퟣ2"AݠQ&Bv.~uV$ >g9V:ȇl`Qʏ^l&͵lUu} ۹n.WrG ʱaf׽ DwD輆2ϒOb7"zFҴ6Ҥ\ƥ`h(C2O]L?оUBzrUdlxB J4x6wn1.P*7)ڌe^%ׁм뷕H gTNh$r20dxM.A6P2Httnƒ"`d [V3:WBKGi_nRfϐ (|4‚v[o[npݸ0R7d7L%:qa!"v.~]1ǙKLvlT 8p-6 jSBZ*)8V]! `oBiUI)7=nM虘+zi?b$iY4*0w6>/07TAbQ JHl-LVQɶsC5':FRG0#v`< 6i`X94(/Òs)&XƗ  2UwfCj?vƼR53$iO)7㹧J)ZOXDd(2&p¬{;=\D?ĩ5z3ϸ҉=z8Y9L)328Ǵ4Qow6(K;/{~˥S'.ik9m-i==wgu3Yʽf2\X 8GwKz |ʹEK ´x,9^l0 '0ղɋ N/j;fK ?rM1TmBmݜn1J k_5 #Nф{?`)gx4p!eevPϊMo(Nn8{5Zr׹C8%R[t)/?OӱZCR}y!*~z3&ulnx5*渾4_Ǎ]R% Rɲ8u(V 5|[_8"P?)3P\wegBb2fBa\Em /It?$LyIwbn_3dQnԍ:n jI؎ELlymn2MV ;ֵ-8iz}ek /$lTMWe#;:F^᝛s2b02?\,>V yq"+}FN6D@ߦG4Yl٭]jD;O~^ܲ209rXBR~JkͻFiϛ H}beYVzVmDz J嫀sA}s%t2rUMqxSiiixR;3D0鬱}X?rWJqrvα aYzC\*Bm}{SU./" !k~+XN&츂zFfeeELRނ,p{6|jCp"#IC@~@s]܄˞E"p:' Y{QpI{ ͏a< óy8 !60xk34]`rnM{w,TH"/c$K¦#bB ov"2@a @|an] ?́GW:z!,MP3vNŹq4i˘.&"Iea0j2mJc5){Q honH/sCM̒<^qCA#b}{wYRy)PU7fj#vSd+نvfRke>%c{q6!TTKuv8KiOI.ג бb1;+)Kxw<\3@օRW@1G;iVQd8*1ˤxSgwtP|7fG^gLreVxĹwltJZwXG=?aojs-YrҴ0|h}W,Đ'ΰcQ3APۨTDR&6͕!?c~e=F$ˢؾRj/g/(:18^#=Jo[Q݇<!.;ʵ}:./Ƃ0`)O3sɍ2/2Hpd>!_K7r|1MDLt)Ir,ͫz\ Tf#,+r1?^J+.2&nOm>7(w.U^ьp!5<]gFG3NĚsMʦJ zUn}`/Mcfka@M)!w8Zo( ݎ$3IʷE+ ?4=}5PT}^w0XEgG;.Nw-=r $)H'qd.&R % wXNO`BFԺEڃO/T(9}JuC T+~A+rXgbY g@R-XMX1eT]~9[qB> "?vT̀ĆTvCt]L: "G#8ճc0} f͑ cdҕVV Q\a3#t.}GSagl_wi_l r@Ք%\ LZҠM+Pl.zNRHr|c3<7?_3#F ,ޖxEɨP7k^xo=:-)=[:PDy#\GVツROZ#, 9sȎyr#x+2:\%Ю8E.sboUe$4^Σ FpѶ&S%q#H zO Qnh=`қo TC)*&s= 9b,w(F$B3-Y% /h}h0{5pJ:A:eB$@FK=R}6-To<%Wˮo:[DA;+Ŵ[  G,>$"O#*e{etNhsG~o/aHjj, -iMY~H*u~MsLKYX.35>]Dۉ_:psfdJ ,&,n1j=DйdY'&Sc9a7zi7qX}aH$"\[Zڄ耢y= $IJ6+fqD/+q^&eZ3ـ)]lE<870hdY9_5uNvPxTЩ;tQyRSݣ;| -}Ip񼤜q3:%)| ĉjUd>H1/DE4YE嵜)ϪuSQ3? p9Wbhl?ΰ R$XġtkU^ֈ칃L[GU/J2{4p`-Ʀ/OȰ<+M?S7B6rkjFo9E.+jRa`IEi=B8WKrf/| ˿b{kZT.=Q(v ]6%e dGQU~Sk}n.v(8ljܯ%kǟ]Hn_?jpZ" s'{ad)ɨJQLj=BLW&r|\%%[kh&;b>0Y1e2b2U^F^f|5;W%8=HyݬS"ѕ 0wv:tT2' 19*~h׍7BExٟP(̤*N;5>bfeF-ָ&( {"{Qͳn݃\_g+#c)m=LxtV?PMJ2P|x!d4*-q$k #:#::`$"=쪪 2bA`d)\]sCfGdo(lDFAjʖM 098ğ*G>軉XBF,4_j.5ppBrx<@P i>Fv۞FO>oYDX㉒ !EW'f̈́daK8052Mj&!+ՌbcilAo?2e|l;7[n̘n=3u;*Yd"Q :/6㼲mcr=M8] }1wӣAk$OBѕ%Pϧ=.6\ݷᕲp@!!`\YO1 I'QEVΊt.ߪ>fa@5OY2rjs|X:O>H]= endstream endobj 908 0 obj << /Type /ObjStm /N 100 /First 918 /Length 3544 /Filter /FlateDecode >> stream x[[sԸ~_bdݥSVBB,a21q [տ*f~Qr87|0_nF?ijX?3}9Ԋ)2RBߊMVQ2!^{,P(F/qfH~rl~|f;M߱ǹ^˽-Vy2}&Y,e.ݸddX$!9V;+1:yBǢ&sB+_@idW}3i/mF?|/CUjc?x:q}{'ԾQ q ,5R.Z䖜^@?u}gtm=2P7 vvw}MW2]/m e~R簦}cvhKXu;~jzEqxOm2^݄ @fexe)Wj 2" 7,W|pdla0đa2-IW ilCrϫ[xٍD%Je w,ؖ:?:< aD)XW)A=%P{(vGSLRBy X=jb4l ڝ&aŔ EGr;WԬڼd>D];U m71DM ΂jq "\)%.+dB}wVCYMd5jbM e|V_ n*;/UYLk0س5zL(P1 Yg0jUfe}ca pgI| Bمۛ{̥1~5$eZqqZ6dd3rݦ̅]d(#kdcKXYJo㼾)>n]lFqwOvVN}?{|xgoYF)U~Oz;rEIt;SH~>΋V3c< F`|5ʡgkȶ=&?dR9'?WZ0֩xUo~Iqs1>+IA!Ґ5la2N)ZuӺ,2RG?.g>=%}E_?:I1D/`i:Ld0GYUh|V7Cz0rT9|0KN? WT&u>DGtOt<'jLKZB-^_'ދގS~bTbH'tJWZY9Iٟ%f~үO&M.4yIJj&MD6n-kD5ibRH&2e )eI#OײHu8ܴ+豉 .eB'.|xum><9Y opf`Edrxs'oteWDG[en~{tAggRsY[S8O櫵8[,b%xw}cX{S5Y.yoN۾A Ǡ<$OPL:s@U2 H,[t3õaIJ2k.0~ {cpDD6ZR ڰ:D.5f%\=Pۋxt0:xa_WFH{%ۘ),̐tڨn}59IĀ;tQ+$ߊ3U&9f}L#;CQ10SzxT(.gS.w}Y.s_Bk5Kef 7AjG V{gnۚ%Z]hֹuli D;y'%5m: :mAT;8UЮkVn)dNױn+gSऻ 6Z31\i mWS<~5u( xZ\d"]uMU&ʈp>s(dj.VQ-s +C݄9LӲA ]e½:C[s:GL8f‰4ay0:4Af8g}U:L2O:Gll͎&\wju<2ϖC|al 'C@DbHlRDlDC*A!m¬kщqd[x^xv*ꇗ$h^Gh.Go–Gs:GNl,źdcze;0z&l(k"1UP,x^'ۓ,\$,|& 3BlL6eѕU8Gcp6$[p.Nwʒ Y|,;eeeKeY%,B5 YSoV Y؛eSY`o:7!o9T} endstream endobj 975 0 obj << /Type /ObjStm /N 100 /First 857 /Length 1909 /Filter /FlateDecode >> stream x}O$7)tl+e1/ 1oƇacøwُ/:++9Q:4=EBʗ]FM9-Ɵ\?ZN1 *Rό)[[R)øVvaq%ɝbRHe$f.IJvZoثf9}0ɍ2x0xC`$;BfUЪeeʬu22Tai`ie`y@G- Tuh*4Ѽ;Y 1PHX@dQ0fGRU-n;/XlYl ,PMF^03xJ; .fEFRVZEVxOR 7) nL}HsEn"lw0x>=̕%ԤO, TCo,.ZZ4鳴։+qFuM8۰XTabԖnW.quG^9rz61t;YKaܽö0Z1P>4Of]ٹ#q׿|x4n_<.|׷?}}[%ghC.rUL+6kݳZmhԪN oKͱof}u[ľd_ƾ.ǾF싱/Kűb˒}qKľ{,ñ/{{,ñ/{{,ًc{Y{1d/D%K>d{gcWIb{{gDZʹԚٟY^ں cUZpZ9*d?fg^kd?ӊ؛bp^ka^y-"y-^ 絈Xz-"Zbp^ka^y-"y-^ 絈Xz-"Zbp^ka^y-"y-^ 絈Xz-"Zbp^ka^y-"y-^ 絈Xz-"Zbp^ka^*hӊ؟v -R;iE^+V[j%V5-Yimufy*h5UӎU,fg-c?pZ-~,ُ"؏%؏0}ɾ;#bߍ}#h VLL>ji=?$~e˚ͺZ=|EGuC=-YΑyOg\KZX- E֮zi\kT|v}5QQ -\S¼5J pprDa1і}wJ}wiىٸ(=98JX&,&gz փT7ӽx1vyYNۂţn9h{P,bv\ dyM?sy2\G;/17͋mz➗.-@2rJ3JĈoǢ/ul|)5*|2|#kzܙ"# s|8-7.}_N~~^ߥzſ^_>~%R?^A#sDc#):oLeq]s;PUdӑ2tqql:]G5Ǫ81-5zP@AaUf@P@eZ볼΂ts*R:tK鴙&c\UNVNVNQ]͐isyE >|볼tGԓĔNYgtFv,vѼ f q^EyU3 aTb#3u$>? endstream endobj 1038 0 obj << /Author(\376\377\000J\000e\000r\000e\000m\000y\000\040\000S\000a\000n\000d\000e\000r\000s)/Title(\376\377\000V\000e\000u\000s\000z\000\040\000D\000o\000c\000u\000m\000e\000n\000t\000a\000t\000i\000o\000n)/Subject()/Creator(LaTeX with hyperref package)/Producer(pdfTeX-1.40.18)/Keywords() /CreationDate (D:20180722090841+02'00') /ModDate (D:20180722090841+02'00') /Trapped /False /PTEX.Fullbanner (This is pdfTeX, Version 3.14159265-2.6-1.40.18 (TeX Live 2017/Debian) kpathsea version 6.2.3) >> endobj 984 0 obj << /Type /ObjStm /N 54 /First 515 /Length 2189 /Filter /FlateDecode >> stream xڕZˎ\Wpk jO@ 6A;Xn8(@>uؗ=|UV9Ū,NJ2_X;7(''dL22:B"eEX|S `J^XiUQI$Ed^kȹQň%bcNFV1!96Fl%(6Z()KVVT#PR6fX9N:S݌W.QǾFc.V'(#|4 &c!V=Ȫ"( /^@*BX*$bc-퉧MI`yDW16(ׯN?<|8?_W6l7I\vI0$ SIܦ9 %OoN?~x|n6jq9r[ k$I%lYOa׫NvNvE/w~|+qMflZ) ʔIq)Yd6cc+ǡ(42ngsq@X^T4Zk C/XI~Ù^wj-y$ h#-J"]^9dkꄴp5.bHR- + ;`vj-=v=)ܓj0S'j_KdhW^渘ud0FsHa(dT W;Q52F)0F>0qO'Z5.+IQ$SIۋ$j6č>IT8AbӢ4"TY[+E(E<毹(JsHyyhv${==?~]7l"E*"`1fF:}Dk[G8{"u襈z=F05 48ht}hz6 Fe27Je4=\.ikoηۇgyo!nx# װ(a*lmo!]8 ?{i.ahsăY?@g}A1nkܨJ}1Qc٠P}Ka hGpeMC؟ {h b;l71sC{ܹH}" l4k*ʬxkW8nt=Q*wZ5nkq1H߸Mf e LZ:' GWE(M\d:Mb|(/!Tca^ORt}=ghӓf6dWͷ`ӱ"pս+Շ^wxoUgdcdyI WLZ xum;#$cl.o ]7[DO̗LDTF?A깹r9*ޯE] !)ڤs;s띾T~pF4% ;ss .s . . sa:3?tdM46d4)9M49d4ꐛDCnA.'m(/ ,,œ r3| ><6[y9O~m9O4S <9B11A1848DDB673D21317AADE8480E7C>] /Length 2476 /Filter /FlateDecode >> stream x%[h]Y:gs6M&i6m6=M\zI-M+ D82Spxy'_dAa/">Q|a8ODAӲE;KyސehF(hUٽ i.OSh!ʋ:B}b%N 1aOj.z-0aOhC {`U440aO)U4z<0aO);hG`Þ6NP2aOИ==D;Fɰ==Fc.doՄt`CR'zКtn3D;J/D_Y=t{zvSj!ѺմChh)e0q4, VJ]- +&ƅa</`7=ж sJ=жvY {m3J=FM94ߠ<f5}u2NO W@zu:Q @78N> 8 )0 p#`y0.q0&3`\pǼ3+/SZEtvh]X7-pw=ցeR&}E< BeY5~8x/͠ .=C&ҜxG1n@+z8mE $Wt{ZA+L6Mia&u qD*Κl:{q@*&%0f?:Vq\6/+$ #KdTPo@ *U5V7 ;]@)ę> [clOY$bK09L$,zAӮf|Uh=sU SxWGu}EN]^5D)(3/l Sz$Fo9RP4oIS:xwʔWU;cK).QF$d7nS'x'{ҰSm7 %S+}fpVLwh7 :w0axN;v8nӔTם-(q=1zy{f ew-Ny(3*19UM>ݡGFh:(MS {:As%XC!5_ߊ~IEx<$Fޒ,u{ub?`ftPF̖m3__c J Ƭ2kO%0 ؆10xM}BWVVU!UTy6G |&j6 t]t&ҡCbtVe4Ɗ{kԦu,o2XXijTm24u= ;MeS`=^SZ #z~TlMgo?d*}p!xg<=,ώN&LË:4hTiS\ь~s:5s:ROMm]%b.6+GL,DsģH`^22 !D∿ q0VķHexe#ʈQq01*bTĨqx-Њy3E|x(QģG"EJcJq .9>Bi49Z"R%`<\1_![='tCL!` A)Bу_Nv}t"H+ tN+5Q޹kĢ+5bs!%I%ɈI.!bѠ& %1.P* 1(9aa\.ҲC&8…C79.t/ g\N}cKFTrM/ 1J"!& :u$AiMztXB-IBAle>  d~*޺?6:,VDa zPCIio^fTʜP(xz*BB$Ƅع!i8is˴,i;am6,agjdB.~ Ѕe*hXJ(q12Qio_X/JV6lX6'O6asg-g?؇{IzzFv$IX,GFTZHH""qdEнEj ҂"9:fX, !XН]W3+~*nfC%/JtHu)QBdBײ|:'$d;D!4m1F> Þxv5i<(0XJL|/֬Ybde1(NrG, &fыG Ҷ\*@z,99cA;n-f *M~ Uz\%g@Jư r?I8D~pWPxx5/==ˏ?mղUOoy{{{{{GEE?{z\ɱULE4~<(QZa/p.s6 tMX]¤C.ź['Iz?\{Yϟ'VDa T\AKL$LM'@BI ML$ً@=~aܗ]ԨU$ʕ+׻ϟ=ZD!(Ӧ.m^t,*.mf7ΰ/qɄYw%VUxQZR0DɵeT[P^Ac D%/dbVJR.)Xo .8p'1ŋV1y8)Lwd@P!qSM?omT7oܬPBDD$IڅPwÆ {V.,h2^4G&Egro@t N̵@KKf \hh4Ј+DOss5ÏaXȽJܘ/`wΟ eӜW> ț7u6@\b2b["Аs.D ~ҥ?.Yb̩~&>I 1VC7E|gyb,YidpeczdkLOfPiy"Fx@ vx' $u:X,YP3fX=&:ɛgML ,?K/ț?B)LXF j̓Uo?8n)/v:g%T!iOuȶe;%WD6w7V!fvd|4; aC!͛ _1}J~}b@=yd &_ܭ˗~nLls.|ޢɓV\ԙ >JyS % ?!>ݴG2BېDH"eH/$*'[*[ a^1T*E4-(>E˙F8xE˾ qx%߁xB…`qaFQKFDҠawgdQܾ}ZT&rKMYʮc-4_t5Um37'Ŧ nAa0oXޤlld62.B#FlW=2J]??!G DiDB)ӮNSj7~r^E=t\kJ-9&ʕ+Q$v1]o߾q4ŋ|}}(F:/O;=9'zbQvw zr "07B>n  Gv[ך4i$ j4׮]Tf*U8kAxi@n #+rm]RE,O؃Tr҈:t'B%L0HYRaYuA ctDc11+>:=9YI.lqdAT mU%?QAQ;wrlH##ɜ*?4O~~?,I IE$i A0 ;zxtjaa|4]M&OAM˗//F:I޴> H~s]d6tY Dli5VRWfQTJ oN~t(b)QJd8 ,XD:dĂE UO ,IU[S@C >P" ~n%?qry39,|ܣ" > 6  ^Ͻo=Z=lj|gaGa 裼` BYr6绅ga T%da^bcj1)@*U,3S K-7(YB2!2|*-,FXHsʫbR@J@YVĉ5[rBc9r˙I s۰mr@F¼3 CဂPGB :q,r/;sM!T] 0AN;>iy^‘[Nj8\ffc8ȏUEZu9X կ"ϰGvQv9M_3p| K;{;|\o+3Z+JRbm Ŕsz"֞D3_@`t DZj?mIy1'(жqpZJWg3u( K'';4P312_a(|78T:!h\8́gY"`A㠋DK`ca9TI{ |l0_)>8!*eVd98+7ġS+즈VO=ٞr%4Z7tW3G Ml/4ϕ 2yA*ݲ]QsoX?!ba>Pl?BD}Dg7šh h45Bw%b\خd,*iozOKa׆Kw:̚I  a/iv!Q)SzH}H"yu7@U˧R,f"[63HeB,/Z>N?+lWJ!QSMW=$vٜTX rM4ߵdTWEi=94gφG[AF̱1tpKFHlB%Es+ 7eAaN#ql%r⹙r bA$k:PlNzN,NN50_Iփ4+Gf/:g-Zd ThLL}Ro" J8cɰG/,!Z^f,_}f|r9.`wX;G31qA (]tD,@Ҏ-k.Fwݜ-!JqǩE`̊1RJ IQESX)fGl0l m44rɂAAo !q1|^ {F $6FnA8TWѭ $nE%H h9:Y,4v/ k۹I*vP.M Wi6F'WF%:pǩM0Q3\KSٽRI[ ҅t# @G4Ұ{BN#FȦkD4bw6}+Sɠ@JeVE֢p]zҶOE^8}rxcIZ?wp[)b1t*Qbk郭7-[V)SM;&rm/[N^[P9i&̈́Vk!hZga)dTp3'Bנ2GC w>?+n@EJ"q#7˩8;E`SSB%ksiT.Fc'H:gqǩxčr*Q] 2WCxiZY;G=*ܙvǩċ?N%w=8ƣl R(;Q(* honjN%d'N"#B`\',MYRKIl>>2׷r?NH"t̅B-eAzϡ{CB%l䀏%ĜJmIaNp%X 8 |z-149}$Z(qY_V),}0mK 8):smzͬ,%Bb_T*]}5+GΈqHd%ò^TtR>%<|x'z=JmVZZ+W2+JN~_^]//KV{=h[B'4jUQ`DBȠpGZ+a1ƧXꀁEFrjJ^|EPDbVc0Fh+G8qVX,ѨEEY ɳW&NV2k(/ZQ.1Ba< IҀHҵѣG޹}LML  mbRppŋףo߾V#0 <ΨdtiʼO=,NOg\zꭁ[N7Y ;rԨlNOIh2իx8w1yT_|EHHHVN>}aHԠADj}aĻwnݺSAL7<{rJVSSS+,]pE"B/Rv}m۶ڲe6m@`tE(pʅm9)_"_+mڶMxSHݻ$ԯiY~t钽0 IDATvR#"N?B@ѦgddffU\u  ku ]{?~j3#{tF%E?3ecb2 Ϟ?\t P^}?qݐ-y8Ĝs.[*)*FzDOăGreKUQ"6"'׎P'hyR*~=Me@o%)_vGy|q߾w/|W5lPK#IҠWiԠRTx 7;#ӰrCrߚ]\tqo[p5w߷>lքM"$̙UgmFqBq_uٲe6ܹsgZ^Ɖ j53sYݦ]zx=dӭՖ}8[7)nhxF;q60AyG 0`ML&ۼyU 17۔.F;{9~6;;>3f ʕ+dg CGegggffT* äR)B0LlŪSc|ZɌY̟_u7{}x?֫Wwi'4َa,f@wceþsTbc^_~'O͜c6;j ^AܹsO*V Tf3 h 6짟~ 8p˖-k׮X!RU1SUF*:&?F zJ Jr4)Okwiﳔݻ h11Μ9r 6P0y81^wRJ'OrBB }7 z |Ү\ѵ[-W֮iZa5Gw<@߰ΝM?kdʣG;ĭ{. ֩Yέ W z9 h ӝĄ/_]tO>AG޽ -qRX^ kU*zիC-Iwu`ql@̵l޸ŵM_S-: U('%m޸"ϟucǏ;vLDb0t]mV+ݠAyF;vZǵL4cX}^D"d2T*ȓX,y<ʳ0p[w̟wySO<[>y̿W42ά3;vXBY{Ap4UE7_<|g<<60Jzu?P͚~Njp x<-Ġgɒ%>RW_=mOjժgϞ˖-V:KC@4B-:A|Dd책NMl:W Z NB,3Kt1~}[B]b=}uF_df;{fbc;/il/`jqqbx]ZLj}J 555#3'+[T"9ݱ?ok.ߛ76j\~JĸP.<۶m{`۶m]t |Yю媄rW%צ-:^vIJ?Pyjf͛ϛ7H$"bСW\ڢF6u5={tC7[m1Jz.z=A^^^ S.(P*$i @Oƍ[uYK_ϭH"|ږ|xϦ{aQ>џ0q<#guѣB E gek_߹] ]8KٴsfBqQ7w\U++VCcO ;vT_RNP͹fYuv:sݻbnZ cɛQ| ޽[.~дI.=mժ}H]Fm6KOOY_CG!w$,ĉSL2&fRZzf=yƱ֭[O?n:zp|;ʶ?-'wWqfpk^|٣yo]Qc%OM(wWep`*1eFI<=#Uϥ*3ҍ='TP7{ ūVڼys !ӿk!c<믽WFl첦ԥI C!n (8yzDØǎ$b|U'_~ 5;I;woXiI:ɽ۔9CG~pOxpW:FLv=`wM1Bh8 iOq$W5!;.# rW9hiRT uJ塕C)[2;}J֐8czǩDB, Am/ Q^Zjog۞={7oaZO>z{OGC޳$?J gk&mئ6ϝ⫞iYc\<O" ̄FH[3CiӦufddtn0n?f+2м2+oݍ;qM[)AA8O&8+Gz+nȂ[.K|-#S:thX4_0hiJΝ{^V}@.I;w5npAPD@ p  4mtúu=zV(a7m>z3/\PFqM:NM+=r{qq˗@-:A5WuqRar-B'/ϲ]:ϾGv\;/tK  Դgĕ뗼߾}k1HbHVVqZvr!uُ3f]"N8J!?k?7CN;3ӏ̇δ7_5ӴЖ?tiCo?3Pkw~z/;o22GH ᕫ}?4 !$J˄t#+7S"fmZtz= ҵ+q/}&w>Onx+4VR%OOW^}U7 &|%lOܫÇdeeLoԨ` ϔ5i\X9@V풍O<`\0vUmZ!J3g4wlqjy_dڛ,1ڦ1+q>SݸycֿS[uZz܋$&^^^)V 5X(Q~ l5~G"zmƍڴisȑ_V;eiƍ/]ϟsիWlBVZӰ7 hѢ7}z!Й(ԻέX!^ju ƣ2 48Ѿ^~zzuLt/;)QebɼkxD|@Y\>lذy!!]tڹk ~8qMP(@(tLGr=ͻZc{θ#&>ӏ7xˉ](&f?U\VM*"^;]N=߿w屢?o?S>XӮL|ӏ9dЫ>UBF%F$0b( 5j=~'ٓGϟ0a @&SSXBȺ.YfK.UTWiЛo$W?]hu˭cn*3HIǎ3kҡxx>/ 5T"/Vf+^9b011A/4_I:AO~4ˮks5ZC!x6BZ@D3yөT9 32LU{ ! ުsĢA &Qzj޼yG۶1cY:9mJ㸸֭[/YC 01yh#,dޭА+zݭ׷f?ֲ|Embye?H ;H$+F^04Ԥ\3CJø/^>A l ?|lwpP+NDFF"hJxcǎN4%>!aOkm۶]`Ѡ8qjk?@O LJUdSav"IRaaXŷqR3 M2@"P1S'* ֯=  =wX(o^?  EB׋5[ƜE|j'-6_VQmkڳg;v\|%wkԬ9a~٘QPXsu>"h:V_DL!Ap#-[yKոagkБ_.\9Er/\:ٻX^ߤIͺGȑ#VRuٲeq1I:.~T'62.?(C.;3Q u"ܡ9?陪޽{cJK,7O6/[C?>{=3'|VؾlLnʟku/(8X>g|*V(۷3++߿ܹ5k6h`p&Oѿy `PUeVZ=+ԙYDv:]8NxX@< @nn\33d$ k>ai@9YZl{5ñ5j/WIMݓ_&I:@2omZ{A3~LMST<}be4˄4^tͷO:ҕM.1N뷂s}zUCfA`1Q2p"˾|S3qƩT*k>LJ .RJrr~5auTk-^ĉ+8a?@ǮO>e͏`4ڷoejʛ,Y֌/!x]'}YY _򋌔m۶:`45kFgff)ٖUiMOUe4&Xz5 ( L"O2k2"sĩx4S,>ktmQgh%VLsKIhF#ϣC ޱ{֦YN>} U?r`z0fp%u½{D"R)ͧ8p`r<-- trW~yns>D/+m_ax+ߺ}Ua!6*5%qVkvеLe# nS#Y2alqy3wς&@mh߀шv=kۮMpp I$}|}+V|u*񓨨r/!_ʕ+Za'\ѣK-|Ivm:mUp|޼y+W$t?q=%[- yp?*UxY 5[Ҫ.]L. -G+;4رc׬Ysڵm0]1m_,\xQFkZ{\(>^BC6nXݺmysg03BV9kvN<ǟ^>F$2k):u>{@FNڰp=z^ rL0W(ȴi[մ\Hr\!M8~٬9!2̘V{6h~'MHdsм9P*s]W_~  %?ΟϽիga~OΝ4ip ĉvڴiX, 9h?ѣj ?"h4'޻%3W;~H+1 Ůڴne;]T*ӦM_:w7+$tWg«IWw}y+Dh5v3q;`4C:osĩ&M>ܥ-3P̾rz.-C:ulwI 'p?A1bQll1cm۶ ڰaÐAmmΞP(ZlfSuoE`-AiYjVwI.\HHHq<22QFb!×-[:v2'[3gɓ̛7[8q/Ao盌U ֵʄ)MKOhuGQt[u>V +"uO'e||—_0\Tp?9=x1 s%%e&&t:qf Gaύ;w/^lqHTv-Qo 23g`^ǎ}sqZtʡCva̙ѽzr; 'Olx^= 4ϛM1V-~>_?R Kͫw}9(Ó_f^c-{]ݳϘѣ+Nر]jjٳĦМ\ޠ~6_P鴚ۤ|H F?A6jsr 68Vo>_ ӯ_$*Q9JO~zUg[tA6h^Νژ45j-pذ!w[rԤqF2ݻw\iN8q'eu4" bIcPPP\.3 !ꦩ)MD*C(<㼜,(>Vj{U_{P.o=QyE^r3T3L˩Z9Zu lׁ3A׮B`~< ڵ#g "A ZD@xo7e˖-[;|{21M6Y0㭿hYfvTڿoBUg2eg"~-F!Β_d\7s3zVCiA;ht11}||}8Y\.D̬Ǐ'$vDfGmmĝ. ԩ!6ۨ6ms>|ЩkS5xk|2àw4dsVi4YȘ d/qPtm[ |U.b˽]>m.>8%%ի|nVTRӧ-x񢯯ɦ.\[6+W9|l .]iղŖ_IRye5q:L8~aHBRif}HhӶoTX^tZ:T΀Fi4vԬk_'NJ%^^j233O8Uu@dV8\;wHOP*] !ٹsg۶mM$f͚<9VS M+ʖ+ "]Ǐpƍڥ%edHY7{sĩ zaSþ<\eIJe ,,ht|>/< ~B$ph:8b8X '!nEHS Z3;Ѩ?~Uh]A=>AԫWo~5lղ=7%usĈa_+ġCG+TP|i,2*/Yr̤&vډzB|μǎw~ k" N:}MdRIn_ϟ7ln`_nN{ꍸ3g/լQ"'=w~y B6[nNm 4G!B;[fu Ç+~^d!BPvƝLPyS5yf-(9ǜ=}.(ǯD/O~##GlyE  DƎUe6]A\y-A?tkoA- H%fTEB `|OнZ|Ӄ`Sj !:tG[ٿ gm'*3j_z^O=BV$j Y^E>N[qrkvJϛ-[:c33GھwlfcbbHMM{x u~{toJJ 9: !.\|OÆ6}ڷ5  Df/?Fon"rzN2uX bԩc)a2E݆ DbBCrC&񼤘Xt}r:Xtǎ zjZ 6l˗-/Xb萁ZM6TR-A* 5j`F^PWd2YPffV>W4U\,nOEd6sha }ۊa#֫̓g^}9ºtPϑtT]3};0o\K. I!ZB^*MfA ,R)TE,*U@z!үMr ss3;}3ofZٚkND̥\h^| [ǜGO~ٲKa䟣.-Sѵh\ޱmGt'/m_V$iڤ_jee|$@/&ӆ-[R +(Ta APERBHۚTZH[{vGXPȐ(7i2H]缼}iҥFueӼ{-: V V; .֯#aJŚ޾Xz9 q:ԛa;~æ9OW4RGB^|ϤRk^[?)W^ɘN._\Yr5W_.kT39gKAA2r pCmԏ=ϷũQi{ʕ+Ǎ{c(ڽ\QhXEB+J>v+ؽ(3\qB8{mj'0ދϜ;7}7O>]8_I[HfH)(UL@ g͌FE}.RxcJо;Ve&˯ m k :)#4D WW_퐮{#Gd駟vtbq kHX6-[%/vƑQ۶|ЧWF =is}n-դ  z'y SV.jDjgc._F_QN\ÆM7խZHHhPǎ;{֬wu6]^ax6k8pbiN߿Bb}5؂"ץXg+b6M@1Hg?`1K7g·#ߵ\(b#f>)2Bzh.ik3gt 9ڤq}- fRuwlʳZ (Eۓ״i+ 6kW^}SΥrrhdʕkl`uvnNnF ##k<3uj\||_~꟟ѷ:>zܴy?K>;c6cGQ&AD,&\l< bB R|$)pov*w&}f^NOBؗ-[۲wzџ篙S≨5SL6իIII?p}۔=Dw%?ly޸r6:.X6_~k<ύ|mLTpO?Vv@b(7 |<畗G Yn?##sjI={vmPq/\lܸai*W;n}C߼VO{ ۴zo?ʼn32`{+n_4 o#mX(dzES}VL-P͟&<Gͻ?TT fV2,åY~}7| EA?Jݤi+R-k:ٰlFQAXrɓ[4߿jG6>ڶkت\uPHv1e}@+ÞtLNI?P(Piw•3|2=K !(^'0g zI.B)E'S> \<#7PCz#xأe3 8ÏdvNDaأ}oRG(N=wÇggg\H[hiDM~<HKe/ޙfbVC|Fu* -EͷEC%eJ:2R,dX̷L!/GҋJftSʉ"{w ΙJ=KG`kK8/LE}?zHj̹-rJ._R/сQ~{ӺrʹƵ 4|֭[7k@liLz9Q-[.ս.]sǮ׿Bm%`!ڵԈ@"cN"$EZK}ؤZG7yP}?xvHughڶhՀBM*F`XXz1͠MRQ0F!\ {S{|2W-X" #aa/ԃ& Ļ{ȱD  *ٞ=A#d@-{w'٪lhԦqO+*]tئqxM+8cL6V4;Ê!Mǿo<%41qqΝoѢCR `04nxy/R;zhDb_0AБ#GFx#;zhzX}`1y4dl4 Gd-Z<ꭀ7'YO1ݞS{ }5#1_L- SNք}ر/_>pDVpy.@6Z}n&N`rBgM-D:͓0 vr~gbE#N;@:M\:r<7×M^(  d .4TdogkTSf1PXV\Ia{w3%zoEM(8{ҋ_C9Ze:-AM>>OI߸.q^ PU5 cC¸ʘHuBf'Jl&lV#HFJ `1Jh&f+ڿT5mJ)KգSFg~NS٢E 7Oq{68 n+(;[Lc7KA3rőhT~4GAٿx~S|~PLŢ`\􋁏['.)is \slO v SS_>Z~|x|:1I=?y{j5{ѸA ,V;y}r Μ9k_zС Az7Ro0;Bz$$xQTS7|bzop8XB B^H+V_J)oڴ)#9ޟ< wOFh(kyh}:vg^tC勿,׀w~Gn1m۶p!pF Vk&gN!_r85@VܙC~ $TZVgٻ?abfw{ݧg̝P>HHR%4EJAhe %>wleYAY_~Ltc[#mQb){وSJ'LP08SٴUsM;߽۔M[[ fٳEr7nA2,p!Fwܵj3wNի۱cǏ|Lً3$m;yrٶڵcTJeo4l۾۠~OɱlbBU>=^n/Z; 5\ޚ/^d9^U{Wy yōG1;:2Xn/9d!բڽaTieJ< Lf!$(S( CPB%Nu67[(]ַ!4Dw©̘T;a fy;oHuDGA~߲gSOm~C՞QO]9J/Nɞ8IZ)I#Ѱ'N tsTpwn߱[n<+%ߟg5ռ;~IӢW^?/yu~-k'/MY˒O7D7Y YDI0S;X1D^mdPE&&?}LttlusT*K/>ύT*u}: DڪemJlv>V(SDV PdG v_O[Cpy E-Ӷ ҹ6_*@WrHps((l?*|7&g ?мrW:u؏?{&~;K?1%ZacE 9Nܬ_簹n1 ERS$`pɕ+W%GU$qW-k\uIE s* LFR$@j*jiΗϼjI +% :ZY76==1o֨; z~ph SV}^^$ndvmVN[7Q+n5nzHj"1YsԬ80tbMSAZAVu5팼w_}aȢo6OO߱"8ݛ5iM_yǜOB>Zv?o4v͟0)Lۣc.g`N"-tfʓ ۹k'B'NxɘC?ڤq5"L&^߾cW]yj)frQwяNhմ{OB$J;۷yDү_1cܹ^[^:[9g[DMW0(|wA}2EѮ3Wp;۳etk׺O?L~i&4mwy_yZnт DzV+_OW't'NN8nX=8zpH͔XnKw[GGGM^fR8WRN7P~T侘 8ͬ;6Ovݟtp.XuDe}Z6!@R7nJTvU?lsD6ot?Ə~Ӳ}y~N'WV|󐹑:S#9N:Y+;hpevܽg{ 4!VMݼ7Z{ 9zfGFѾTؤɣǸg^ZuX>oE[U'K F׬_.6ks3-%3kڇ_ƭ`M83u ?K/>_,`}+o5^&D r?_{#u/wx^QNw*x3ٹӧNLM)/|aZ;w@b;-&ݜk׶C11M=ܟ+Om(9k׶1K2uzQ]>~zGʏyRD~ 5fg$vTKo[B}1K`:$ݾecQ %vީS_~E(XxUvҥ \v̙u_ksTLE]dcg'3}sh˻^?2q]~{XQCoB+~$L<~d| k,XA $0 F\en\<"&Z\:+'@/}qErBUZ*Frr|V.\N,ݪeܟQ g^Z6!2 =[Rj1덆l#A[XrN,-"d/XaDADAĄ{}'S6Rӷnݲn:r:`B-M}?fd2筞yx ر Y٘;iī)WCA5eRPUċDT=j0!CoDT.i]gh޾Q=YjQϟWfß1oڢ`ZvҥK7oުU ;[ ֭KII)1o߾}cKMMhm۶iF}Lu+)&d s۴i j[.Wo1H@ڂuƍ%4Wݿ}!?v-R`k>BRh`Rs;v6I5# JYV%?v5,8{7ӳ `G;2iכ&5\Óx > gfo^fK;ԉ)ڞjXsn`0ܼ& T]txSJ쯾\?mԦ=b; _=~9gOm:}c1=;zWqޝ֬GlEq/lMKB})b&w;Z-jU}Jfc|4Qb?[ IQZiSU)_!aXȿ⃔e͛7o޼, ٖ(Xr.c({%HF@HlYhC k.E-!>h\TEؽ?mc۹8o IDAT)ΩdHͨW C@ [{'#㹹yr_/ſ% ZEB?bS(._:PkŋUq&=#b/ BEhc9PN|w-̬CרQ=((Aܜm۴V+KոI+A0KTq #+GCv﮽EQ3re H)E+4Fԩ! .\['>`̹'G޵K9 jM_ll*))U /m> PɊgI3rzARkWQPEժ]L9 w2J> A{KNN;>Z tto!`PHCT(9MLpZn0fSZ1LIvU^ߦusm BͨRWת(۷2eg͚G"##ϟ{8aDS :?V(XlJU#{4k վUa R~⿳k֬=`#fyk T*wu豾}4m\fKU)ն;u)'t{v=u}4L{'3;"<OjѲٔk}G℠Geg͚U[}i߾kg͚ꫯ>/^o߾1~+VԴif͚>?^kۺKɉ l1`:!rS[&lyǐf,wFQ8`0<LO .]:תU…yF(|g|N`Æ(<O3fěZz>RIԨ׿rJuZ刑/wԥM_.YqܥKqqqZmTxLI49ctTg{^,_{֘ 慄T3fdz.)SnsSα8JvzRUxtRiJȒ{ 9c~_ULb3UFlƿnLv-3M6˻x)/,8.-Vnn^ݺu MWڷo'gֵy<`%<)m۶w} oF%xjsME}9Wvb7߷6RZT_3RZa Zf͵k׫fg\{ª}-Zeffi4&I `uVby)<@v#Ts#Ŗ7ibwғH| J%U];} VJJHHhРZ,:{2(J(Q b 2/ =劽rϑDl l߾]6%2 aFVso2Vp  H3RnQ(x jEFcU  ogeyW e dDamE_ _H8aFx 9s[٨Jj=r&$$` kts@+_"YjD"Hyp롤=zuVɩ8o< dJNNNNN,zC) '2Cb% ⛄=   H83  Hբ 1"7 Hl*HA_AP"fR,[W R7##åiG~Ӹυ&ž987*$<+ / HP6faqn ; EJjҤR45$luA!E=QUb, qk5S5Du T|^ycs)ÞJdpBӦMdkIm]! wh@,ٚv%x`pKBe"/ #(G7hk,KlW&I]! HcdolïڧչT&Aʄs~tp9ߓ0⧸-oT  (!iBA\A {A H1k N:zۨAAXGRܾ/AWرAʜQX ^sȑ A| Nw9hȷZD2X ^Ad2%''б-/60lA*Cb% ⛄ӧN~2nb1lJ$ԚlU_;?K>AA_A_uXA_AAE?&hR5!Ζ69!!eXا,j;rZ\ALߐUj`!6dwSѐo# ?)GdaJx.P! ?[涎6Imqٹ  H!GeS=qR䞀JOv8 3uTxydoS0Zpָ2 ù_A{~p vN̋C )/ HiD`!W UV:79}'a7bDA\}PR - Cs|>ΕçT*@Aܞ5 ~ K?#AAp|_?m_ a ˲ ;lBԫ))*jĈW+^aFȑ#Νz@tG9rHZZ/')ڰ}>}( 5E&s~H94o~ĉ!<<C Xu *jСX,~q_BOyޖRJd"d1E<)sd_A{SkB?xyF@rg d0{R)sAI{[Gv &@$\5+>#X ?*\qqnԆ˟J=s/aPAA2|)+xSM_!E׍;O.FQI9  +V!{uä,QT Ui~`x0/ %vr̹ 狙sU:GA߄WA&n?WJR*W(xfAJ' ʆ(I($þ=y}]I2U(<`7OQ88 H+ !rkzwJ=zwJHsNq*'A"m)K>H֛?7NRąVzVvVx&@q|Ep%̌;4ꑮq&Vl)A%E:GRύ" roOEQ ^PK{(g_G{4mjͶmwNPl˯CW@cN @A/}n`l EIRJ춰}䐇[O>J@ԛbd>bܹJeDD0 Յ Ƚ%77Ν;`6/ﰟ鿢ɲl='!Zn Omr?s÷zCSy,;ڵ+<3AAA͛7Bsau!ro~={d!x 7m{z@!}v?7lxPAV~AQF5G> !@Hif{d?<7mS@Q sӜ&l 7lxDPRz%BYjFŰfN(v&fNQhWf* 7?mx[DAP!jQ:r`w֭I a! MLJhʰ?m"'cZYO-re@_A\֮(t3>>^DHKKU)U!B άڰϋ}.We%Ducl J@183 HC_WrqbngUY)jr! ːؚA^f\AL "u sm@`Q˿A^ A'ӪT> @ >o[tcfl TeCచKvq ~iSG~?<A?ZFU޸U5 ]jM)Bb#Kd_A m[K3\n*91  W0JjOo޼df1] w/aP/8 Ayo/B=H"-g cա{cV^FTM㟌snQ"c/ T DsS/cG5nR.hccJ#?j[ءB\LWS2s POww@jqI"wR&3+++NZqՄ5 / +.+&J[^i׮_*0! !@ a#0a`Pw&P @R @)W[h@(b,7J $? %RD@7O)%@.HV KT"@)!$ 6WB S !r%oR aX  D(*D$ r J%J%"IMKD*Q (HTð:0 &W( +sH`6x^UvG>8 ݯ؞*1y mǭowBpJ\m.+R!͒Rfc:@ۏa)ͻ<ڦm oŹZcT&REAu5ZVϜ`X  38ş|e`"/RlذB0,X? * Ү :U$,gy|yCX`B2PBJ2 a+JGARRa@)y?΅ݰ]GB@(l3>&#Amxh&C:E@B ?% {1 qJX$&)QTX(1H FFF,k6%x R㕢(]t.HSW,pEܨMI8 -  @sAA:h6RI:B2؛7S__ He-0\Nnn(EoP!U]<)یJl1#"ty #qAsaX UY1\Τ7JeV8R 3)4%Uv8pAg괩sR{ g4Y/&߸pbvv`PT!!!uԩ_6P!IbfDA +B2 רoέ O5EeY71H*8yСCV56vݺY׮]z^߶} 5j٘uFeHQ/ H)b7O:d3v%u޽1kl6^vȐA bν mPb IDAT@r;Ԩ!={Ɉ  &>J!.hר/8XMeYt&6h2a5;Ѩ g0|)`ܹcZ/^0]Ν;veM(>'AL_2>2:~3g)QB[[[+w?k w[OeiPdjvp}lO ӵH)H$ٳm^׀ԓO\G 7!+)T0N=H'>ɕ+W|hC{>|_s\'O#/np"gUkG<22Z fӦM?յiӦMl4/[_"a+JJ]v;{f%G,?%KácoGt$gCC>7)}u*I6X9sfu_t!Q(߭_wY[4^UPG@CG֯(\9 `&̝3+ QPq<)>fmnn}G#.V|d2.{b )lysܒ:S?_M ?7N `e-_4Ebl|K 29v~(8HDDRȈ,7θW ){w\~"ԴqF @u۷D$ ޽[DV8Ws8O~*\~֏Ҍl"]P:86oT573mMdȻp_ pt,>1z_ԐsyFq ̏Bg|_81/0l>?BD"ڵ'8`:|>Wz?z=Z."p! vJᰈb1V^1֬7)]]]~;nN8sωȶGm0͛6mVWWgjf;^0;^G(; p ^P@@ PfJ_N_tJ_2c/P:p c944$"n;P!&Jai~8+Cuuu~T9qs='""P66mSWW7jf[' @@ PfJ_N_tJ_2c/P:p c/P2vH$ m޼`7;vk喛m;% =|jmƨmzp;'-466._(i" 4G` V2ap8n(T2555Qt:2ΎT*xfpjD0)jc26mB;DD1/,>t5\h!LPZ\á/^LFSww7j6[y/y;};<Թ~7d2{U\88q6wy037}C?x9V2sә:뷀>y+;ʉRsnt*FDt2I&"TS$Ƽ@a/_7p2<'@AWijj?Ap_(_%J#l}hƍ1 W2_PZNFMM-_G66X0J646S?.w,>tD"/^rsY]|1y_oۡ9WX?Q +T{^=_*L^x/:sfرcG}kWx=y\濙|4t艗^z o755cŲE ^G=α揌Rd:+[r"yWڜdgZ󾖼_pp^{D"z-[o#HZk#Ѵ: A'?ٙNЭ޾{BpXu˛Q`H"+N*Z&dTpp%ɓY oy}W9s&H+U`[[=z|?p] T6;7 ThМV>Mi2d2=g?~W O>okZ[ZbRFtm_=#跾v*_v+Whigr*''}AӺB|%\{'N+pûGG#́?wGVt: }\ȑ#?t(_rW_R 9ۄN'ҥK\|q;~z׻޽~xv>|;o6SÅ .?//>߳>_cw-@ XXxyC[0GԫaZ.W5k{vmƓ?ΛˍnPxwlp҉T6ww}G#.wSނ͛7wwwRf```ǎ"r5r3[SDC( ɝpͅV2גwzL:tx41?K_nǎ=hģCCAڕpu*)Sww7-[켟* [U&aQv4Ρ ?M>H4w$Idz|e˖yܑѷt|׶]r4</ݫǪU ^s=ߝkO|J؟ur4*5>|tٲř99oMt?L_* ٳׯZJJGF' rA*̇3T8t0\ ^eL3LD'\h L#,jpF 0Zt:H$gt'\.⺞b/xKRi5kvѝFt)(Ut:.[q6ShHDD{il,Z_.WfX @M߂/ wξHx,<;O'ή3h*:gu&WvcewU ,M~$YrVkRhp踈\c'kّC{2M(bÆ >UD>~GIOk3RImڬիLLRx\kSİZchxp7s[+Nzїi}|a"35j}Jn={.a/T" ;|XV룙NW&٭Rɭiv~h޳HO^V~(m80o+z"/TS;Pe=4f:m+Tz[^,[w7 P'}رH/=w$_^LtloJT`|5y<]j`%znifBmǍ=?Ifƿݹ%3͂=+VۿoML5Jz~Rn@^~z`[֟mlDO/l.T_ج _d4Mo^{NL閞Ezm>== t¾"*Q`ǿ(?`/T$l;ED@SVzGno3i?-2 _/"Hm]5]OZ[ϻi?+4F!ox{ K>_vn`'"W0hg(*=uww[ܲ9=40/ok`o.WF(C@$ͯ70J էNdb nl*(&LEZ/OyM kN8'JË0Nj/G^ookq9h+#k/'򟃅ֶ٦6c$b OwQ{"<=S T&N$$ɎY`:7[>'9IDATx_p8LSGcd2i$ N&X45U+o즾y[ ذ8ևn)^km_h$Lat2. /g)[|^p8}Goe0r&ȂGf_cR$#kWYq\hC<G\_氿ry?DիtM7Kwwwoo/nv!"۶I_w|t~̺Eo ?*Ql3mo&l!>/*򟃹 ѣGzrGq\vȳiRE[qSUކjnY^7nkdd-"à ^!%Ujkkmok޹lEŗطpVW%@s"uBeÇJ[Z]].\y:)?lO=m1 qh4V|a^4i@9J)z&l%RJWS)lPGq_ p0]Ŭ =>uOU?@)G_q_ vwО=#JRJk}uEkt1u{͝dRj]{]qi[[ g!U١CK/Y`6.C\wώ/(TyJOx !'piǭ+2Ӛ֕5wӦݽTرc~km_:>ؾ(ڢ]W$[v}}}T{Fmft_&eRLsh+T& <Qmm_h233Ggs0TN]mז9 ԧol۟Z?;95 ewZ[rZs7.ߺML&d33V`(t}D$Olx5GܖD÷fp ?3|V(Mg M׭ֻ֖[DgwwN9_fFfڼ#CCC/?p[_S7x3TR xߺ}1O~r?^xѪU?q׿嗯khhk^km#neY4 -Ð/?_SsaL52Z[oS0_y<^K=dd_Ks1RO_SUԅzqez+g}vddeߌV~pĉʗݵkܹ*ks?u+y'tˎTe~:ep2Z3gDQg%F8uuuuu-/a;oe R_J^_J-9A9g? w3o%KRU wڳKڈEC,^`|F Oh:+\cM{|9^_[x-o=o\?@U؁7Xk&Lu ˙i kLحϳ?zg^_ @q5 xSھ0T񂓜9*)nNyQ\]@k,$Y[-e ωV֜b}L9\[x2ܦ6'608eˬɗ\'݊}r9 oMXޕoz_$g[y︿9323ge7?>˴~w|sj/kM0IP|1UK徐2-s[f~pxpxfNE' Mzpgf `(t}D$Olx5GܖD÷KZaCDLT"nhF#Ch`|iW$B<5ɰ"V2%tjmiE|{| {oOD WêU(5ARJ WS ?ZgE< /;WTggǍ7GL3Q"#,رñh"m9P1kŪ"#(K]_v|}EO~r?^xѪU)?IGy͝Q*В')1 JD|` uZ:Rj2?ٯ>zs~9\g4MN L0ˏ>>9s?7kzkG#jZuQ_o _ozf$mffOOi% Nř"I^O}_|4T 'f1qy_I4N:gSt:'}?%nu|ƪ֋f>ՉT7@YM]=i>ʥkAOiH42{vgt221Z>0T8v70ju'L5|MMMTx<_9DDeGh#/C6te퓝mZ[[[guvD1j ( ^Rn85x"T2yϵ@^@%ScaC^km.CK JkdddxŋWh3JDcX[k[s0^כyyOD<ޖtZ8V$:jR2RRRJQJ  kk:;XQZZmZh-VS{z)JJkDim[֢Yψm)Z]%Ғ*D(e#RJD+kZVZ)R"f(eW(k}V)QjO,[Ț6ֳ9pb*-hѦe}DM끩Z+iԦ7u0\^ 5r#X,Oput))yjh64xcq ic)jg'>h,Y_"}e>Sg[U_V̂r.?8OΙzR>!֜o+O [@ّ;ʉRsnt*K&b0S;/ߺH2vj j%͙b93!WO83Bp֭Pjl"gfO<̞̈́"P/e4-tP//j?YE"]DQjj8Y|+XH$<tEXtCommentCreated with GIMPW IDATxyFy$Wosߗϙ5v Yc.6&!x׀mpƉ |6 8mc=xOO^jj:J%{O[T*U=ѣRNMM0JST:TbLgV@[%m6-|<ȓ-|ؘui(ҖJrBSL$+j*`Q/A BEKCT BFT(+![SP@#Թtd,Dd%~n'/6󯐐;^w o,qIZo4)\٨q,UKtBjB!ro''fft?;44jlv jOYG; jC +` /XCf ty r+ed RSgff.<0<J(;GP0Ģǰ 1 "ؕGI]ISO]ﶌ+c,q`>$#!JN]Wc\!T?ubnN&[VZȄye2~ݻ8bjZƴ&)mUNiذ?z #1!I2nJWk,<ڽ{ŋ,[btd0?y=g&&.se=nw8p B,(i>1b!UX7aa{fK "z ޿Χڽ{K˗-'Na.\8-^'R,._bdtPȟ_>YƏ^ŹVL=ԓ|^ sؖ-?{߽ZG-[;Lz!n,.DݔUM)f5by%|u7]uP.H7 $/?/^|qmWێ8bѱ-[.:p`w;F^b3gJgLt`ttltt˖wLFW,_eR+ٳO>Y 3dtldt˖,_7P6fkʖYl0HmH$֟80QJ4BtJr,X_^EUU}_xa[K.L5 7-J8Tfc-O9I>WFT_"1Zp"1V&sٿ-1"y'guc[,S9nB? o\(ehqxZUymom=Џ땯>vum ~{8ߞ9LTuZ&^:~z3٢K&ݻ.͑ ew-a:tHUkI:a[,9)omD䩜q*/iӧ]׽nrA''<= ѽ##c۷o5k֯Y]J BJsyB!ĥ' y!6! =hkW\a*_*媕DbٳIIU~;_xÎ5%NܓRPȧR!^gggk$IJSjEUUIuܚŞ69NgL!.!Ʋ)4BRri:0bc)^’,!R%$;,h2g`zĦ4[ mD2] 69L'jdشAÞp螯&8F/>pݼYku?#7o ru},)}Z+~/߯Jf^TdI՘N釆UUT2)Tg{:+ \6ӖK>Q&1MmG-W+w\~1L>"i]AR:r7Xcǡ9Dj]X$[$ _b.7ڵTUVr*ekl^(U+W|S_ a{%YRI3gZ;λn{׿Y[Ť2/&JpQ7G~׽○,%ŵIe|UV!ԔxP'r2KI_}b$ JTJ$L:d"a^]KRÿP* w7ǕJ:Ŷ(ӍNt3Fƅ:fOS뉦 ƅY0P < @RjG~E(1LjlJ\2tvrdRqelxdDLn gݒL&l1jJ%y` 'QI˦5:l8X Ll QκbNJTejgI)5l$*kM3Fg;97ݷ0Bc#NfgrO###^lvaYjl<$5f?099Ϳ?jSVj:`Q]3+ǖ,Yl?Et]/15 Plv={?{7Wʘ~G>d$ieQL&'??2V]?Z(Vgչo.`L+J!:WZ}[uŋrL87J !!Blx|g].b>\ݖpeFعL8)2U2R&}NK70O/Q,j6^K0ZegQg(5XTu 8t`֋sss?Tyut~W-oy%Tf~uWύ?K)}V/I.%UY d~kvznrjRX,>[  fZ%x^d~(E:3hHp739B]-dj_4s<l:U0~ųd~ceuXksVe1w!nd^F0_??g,| |d>æsz#T^V0gy7/73 ۷ɤKaLɚYk?\u`xduώ ,,OLJsb2Fuc7hvvŊϮ{ϻ2|C?]hX,>s-[E/ekֹؔ?:?ֱ>[o-=$%TJj40/FdIO=3;W -KO=y_|4h &vzlQFZ\1ƈƝܿ>0DJ\ e=J(.fqaHXm$%u 3e'qi}^ *i3W:[Z?JO>y_|3)E$Y73ٜK&RZ&.PIu[IJₘ$eD cXUYPd2/HLNS298y>.oAaND4(2)Qk\|$lhիSɔ,W?{ƍTBӪu7_i[eAF3f%Qyzqa*m3ҳ6V=o~?dnOũh׭%fH\|ɥ$lh˚իX*S]xS_&kK(mYzu*5B&3i{&Q%i$=/&:1I'kbjE7Bt]Լ=WtJѴ*'9S<W,;Tl^SEr}P%3̙ӏ>𱣇u]d%z˯Y&Z]̰qUػ{W)XҰVh>e5|]}ԝ4£#"^Gd%s3QdZ9KtV-leNa$i 'T&gp{v޷.-J߹x% 7QɔH~-ɊTUUjT,JL&J+,Iѫ:庐ZbKMchsF1m\F# WJ| TdjjZ߅YьOCEIRd9ᚉ$IN\ŜPI(D S\.ӒjƴfUaRCPVbY!DbIu]תVq܈(7 [dpFbC,BΨ,1]uZoΒHB(%:Ęoa$YR$IҙkG7lJrBP(5Mz\ $ .`4.ZLB}3b9MJxN;])/ۇP&a{$kO猹js-Q4Wbdz7kC v%bD6PN݌,)c,Jڲ&MCkR2By.Vzc,8lUn7p 1o6f5u=jtEj%QBUU[Q*u+(M&UjNeh!E4in!J%+ ~҅/Lա?p5NIcdVV-V˔X*U R~%PBX:}{1*6T;|^,kK6#e8`\Yn?:o`9ؿBފL{ f<״hDm|!tpSQgjD?U418TNˇy/{y1`eaEL!uu/ bHE$BN`Г#K.X7oUmx}Ny$c✆!"eQ]5iRYStwSv*x]\rA,FؾJkn1?Xt;5~tucGnW-s0rpY@Hm8vx2D BwvˉHֵH8qa9hazY<@~V Ïq"7_g^;4}oN,d Jcx[o 3F(Ǧ@QJ⣲(sn> mڮ]"CtjHvI)~?k7vbbkPq"a~$p<9j6 ҈T$b cɋvmftBo*HyW|;KiE1UUj̛%6[EC Nc0|}?[ގD"( 븄Tj:7WGzBp/H$ªNNV^)ϵh7Ft.s|㨦a-)}\O_____ї )J`Q …Ba 7ܰ)J4Yӈ3F QlNX$*e5  KAw4mj*?<`Z_8XX86۪K3m7(3ϕAKOKu0ڳ4j$In=g_Pgk3ZvJ+ה8_IAOfvXLӱ[A[^chlmuM+=m b~ш2Ѻ>3 z3@y#d pEz=^ulW{#UZRZF/K &9hW/<*sSFPgUυ .e%huA5Vd^/XV[yT: og2-h@\0h4sݵR+\0'R$,/r,* [wbOHm70F@j 7L`Bg{{F#"]m$+hV,/ϡmA=`6_ #|a f TTCvu+_C8tV &h"DQ۳v_;TG؉Z{;Dnٙ_^C pj n:ى 7Jh\mޟ(ׁsm&ql* ̾"; "LT*g+>|3 Bw ~y.Ʋ4 ͹ՙ7 IDATĿ*mA HʠE)9uw=A*xh-vD%D7k/l0zKnij r - }=DaΝZ社F{蠅lDGn]EQ[8|%.Vne[rfF!㮝!'FUViQmrw7^E}Dz(q3ng\;&UgMZT4fg18OC9\뤈oDLlAđ I|#".`}m|S:h v '-{:u+Qs~\2?hwǂ)WJ mNX@,4Npc@#8w=+r> zjVfZUW2M cnG$Jʳ;ܴiS" @K&nzY:Z@@* SU؜e?FN(ceӚ--acN#o!Q힚"gsޝ#xBfؤx8U)R{kOmGeklxk'W@ 1b# TB V*Pα6'~pjR|FSءSO/jP vNE.wQ>μt?'%RHOmCW9D½S9ST̰SXU@Fv/l{r& qഁ͹|Hvq1mi%1{ʨS wv"5*Æ`QM9m usS4(}&,p'س|amIZaSu6e&0,YBAy#Mgh>vٗ,5{3ّٙ G2⶯JU( mc>;KJg2&*/q.x\2ZHݮDʂ-$DcwJ{x xr56ЅDGgg&<]Qs9KJVs9h>֕ 6XZߕ|U/O6X@ dU<"^p-$l+=-n1cf囟LhڸV4vJhr$IRٝORA;"b`7Z3T tMh#Z^.Pa h 3V!$@6?[BGLe/ܡj(6yp낕(!dfzš\oώZ[]w '*gD7Sa@@}[6/l Js)UEV)WU05ΖW\ teQG\D[ݥ#y` &N;7esܦt@,ԵdmYq2wspZ,asBP3:b5!)-ifaNAŏNBpmB;b}_ ԗb%ud]"pS[Bng#;5Td!,[g V&N|-R0+m[DZ .%DUt;jUm׫EMWq7ʇkFr\INHrjӿشqc"X7Zhm8* * T8,ξb_ㇰ"Oߔ֢+LOD)M42ؚw ے;tJǕV\0-V{;H[vM3WWK4/7g//?yP ^%Iل__$]EGtp%.h>1Wvρrzs,37pD;^\ { ^9Cg뚹:+h0h•V)ߺ{DN]5LD"]@pI1u Z8hy3.2b'L5kd.|n8]%ľV$ą?ߩtݥ5RVu`0p3%*C4M+[Ty<څKT%ip~ӪVq>؏Sp0Uӹ@>4ӹ$և|DhgAٷT-V6*/> & ֍pW [̙!.[<]8m.6ӋOiŚu"+ Àĭ$$egί`&r=A|l])k[2`>$d1Mxf 6 움)̕ĂDBc 潌iK@ntW'߂ RE0L89xȭA+?bsDR5oǙ,U3Pk̓.ڼqh@N1e^afKt"*̨lQ9!˩xЙ- :6hmup`e@ASW\&0@* kT|; D1F|y$t@`.r(V6u+?C_)| g!d0>[bK6]Oϼs  }[VaT8^rKp5*<[2]XHE6C.,Jjd]>5t G6PN@m []3'N2T_@NyK8 :?lB6@5?(L m/ %Ͼ2 A1a* $§N9zt")/oU˥-]MUxSg D"]˕//vߒ851-ItvT*ryЭٿةU8ʸ(ù~$@3WdFtNQ$@( 7V:ozDf:WNLL^ǹ6-+pw.5HD|єT*+VVCMа48)F`JQX6ڼy\J}Wq+e+l4/빦di7*r$Y'v{N7oޜJr`벡]imeh^a"swc6Ũ}:g,&ۛmRʊ;e"vom tf[l\Xool[k֮k#"(8]PZ.Op8p+^V4j#GF6Kq\Wa+bmO=s5MO=qD?kpnpi6pzj]ߍVg\-qRp,_leNT|tg6dPqRԽhfOꕾ𶊘FXU1 qa vnKϞwI.&3[͛q#kb֌nXAߙ :xj \8;q'Dō| 4́%TipX۽o7"a}&xح S[4ZyBiU=7ǘ#J_/ Inn/ ,VNyK8NÇ`V@i"Y:p޺aׁb'h / #hv艽'qqpû%yͅaX#۫.YΣ^?]^2F0x Qjg5p*nvBTU;\OOU?}pov'Wun&XƁz]C"-} FI0GD /b|[l]&^ W!>||Hie4~uf9y:Yg%&VƷ֖vl&hBfKP@Aͅ}an Fl jz'.TM3w /VRE4+|fg]{1g/s;>k6g\sQeΔ6=#N,JЙ~92]vq& /%xmwqČobg&]`:/뙕Vo< w/`U-EJ&gHBd/vv*n\?Oz$#O$a^Oi5aiͭoPҿW3ltd\Ӊvf6 egmpPSa5(#}e^dZ-'pUU8tWW3VnznE#qIR?HU8b2/?^6x0:|u33]i$DWO=q"c`5wx x_ V/xF/5X75}'֕/MB4I{݁"KU*KA&+>kF|+h$ #:+g33ϩd"'}aB!bi#J^>;#],w*L&Myً7]' ,-0;po0xc0/ * Da34_gxcP/ * Pa h>#Vah74Ny & |k@"sR00@@Pꇷ9h 0/.\ 2`F</H@+ Okbҍe޾- TЫGt|ah/g3$766j*4_ 9h-ॐD00@* * PaPa  ga:j_ T ߙT `v򅽴0x$'x8-RqD$/ (@* PaH@a|ayJ8F<SHJ00@@* * PaPa  TT00@@* * PaPa TTFilw& u00@* * PaPa  TT00@@* * PaPa  TT00@@* * PaPa \6t1sfDEOwh?SB -U*ՕˆTsY*їξ<1>)elMU8U T^@ Ƙjj+H$ENDy6k UX -YxQB;oϤ#Ub$ w37\Ș^.;:Ç^P&ͳ)RkpW1===<1Tj:^<]8=<< Ubu8t4}j022V`Y45U4G&ͳɯ5|BRTVu]grB0ƪjTu]Nr3-|IMۚvk"/X۱M=* BGPOOPpOtExKW٫j"ZӞ&T#mo 4 D${|>i9cٚ'c5We3.+U6S<[rMd4f]:qn+#i0A[K OWؖ5YlUka-5HjA}UF[{]j֕:fy&''9f۹6L'0Y#aTX!:c1#(^]l^+]nŭ*Vd9&rQb/*F՚j 3! SB%IuF(a4um9kUbt5#$I) *QLg!"(#V^&ٳg>H$#G)DK"4ȈY딢F^kөԴ$%sq$2=ύ-ʤ3uzEٻg׺+ #.u1(r{_Cb;M$Ƙ$292(TyoYv}򥋆]f*wzlk#ɡ|aZ&Aٙ3nҪ%M4 ] 'Nyzzӫ30]dKsIz]lC .xaBA<"^!/ӗIwfJH&ͳɯ5c1I)%$]qcW.6QoM^a}a]' #24_BdQƻsPahsޝ30j2_Bą,˺TB(u]e6gN5J)%Lg F k/Lg$4Wb `IDATT: cx::) DOǤЛ0#I”bja q@ T[(diVt6Ʉ˅UZ|͡\$ / Z*#c+wɳgeҊ(TZL͡\$ /w!sν1]U;["B98دrԫ)@$lrvY"T |w2]]Z׵rAt*52rWwW-)|SUO1Uͬ\ꫳ+W}#t*t tN|0:\- ەsU n c 5/osk_cGH/߶~ѣjz\V3s\(Ze&Xet'hr\Tui<ȫf[YЌ"k&DŽ:{ٳs ի^,ٶ"u lg#|4;3ƾo*~'s䘉?~׻2/tGIrahD׈iFӷ>]\ā,iYWZӴ?~%Xg2cy]]oo{͕w}-O|bffe+?+/)J3Ki2S)LZ_J]x䓟 G 5謞Ր 3lr9[JcٙA⫗W|Wͥͫf 'O+9BM7)@3aַe)1TgOk^'6nLo ׹ Lekó&3+2{D"Zl)Ϳ] u٧MIt&̇1vwLNN>>[;;::cǎ|+jM̝=[|%i&u=+oe|cz>}Vd׽MZUXe4Yl *JՂ`UU﾿?޽{~߸a\x@POL1屾'ûgӹய}X\&~&'Z0 :CCCSSSm뇆̬;uo6wpMf])R6,g8:w~_8sŋ|S@D >d9IՈU#Sȥ׾زmoaŧ%"$i"+WqZF4F^ YS:*tn%1EI|ow_G$Pخ$bE\$)rxbZҗ~yK~4V %${t7WiI“ֺMNN{''']$Ó Mvmyrrҵ5u]>U&3j÷5֕^Moy*AK:T&3pU1?KOSsV7ҮC@K]rn:7xV-Paqޚعs/j~k*ڪqN."uBR∄(K\PJSkpJt2Oɿ^$F]}2o^>SOZ tٕWN=O}WeD͓DPJNfvY5+^IF!|&;@aTX#G^FU: E>r};^]H(|Sv JRk?#/ՙIRH"J2JHbǎ_Z_WV? S*}ٱEtSLQyfvv Ϯ[RkEQd[33SŸf7|W*7/g!J_Ի޵ off~ٹ#G:5q]#CyvұkWe^]8LO7'_u]u]/DU3 =[HssG~hK,Z<<& ^(̜8yj|La3^ d,[li. -ubjq47~}AX-;u&f34PWZLMG._ëٱ;et&Dgv֜0]\XM._C~軳wkj [__&Iw4 ^1^ + TTndnIΝ"u>XLXZvT*|_8I3L4,_b#*dvvZJ$33332KΝJR__[uUMtreF驩5U_j),1Q,^>3%+edmz=mIENDB`veusz-3.0.1/Documents/manual-source/_images/defaultstyles.png0000664000175000017500000007246313161413406024042 0ustar jssjss00000000000000PNG  IHDRn-sRGB pHYs+tIMEqntEXtCommentCreated with GIMPW IDATx]g~Gemr{lc:I1 N $p(JB3 ئN NGMbv ؾƸ+wMC:j{wFi43fA]]!ǻ\.B0!TPJ borRW^KʷRB2bQAIT\ ˉE³Ȧrr" w@fuBAd*+ddBL9B#]$#s'TXgsZxRIINro+8B,9.^{b1 ~ bbm\˶o%%nF"&LL1|P]Y _r)CXג$`,N% 1 F!A:;8BHuc94*MľLJ "iσɕ2̼v߄#|5ٖIՈQiSĈFt jX01KL9ư}lrc9\=k9`lTs/رsg7 `B/~ QW,:ΐg֖uk?ˍuuu{U5О]U>:6s &isKq*ۚ[ŶDPnfѾjhw>w Q,rbmIr9NΊ*n޼qgkrc]ݐc#ݻ6|>OÓ&N}u-[6%3;o V_U]޵ U& BL떖k26:٥Y'Lfg5:ٝJ_'=!B8b57svI"0_m޾f͛Oַg:G9TSUSSU3m!K UÆe[t1Y[0`ps\͖Z/ra$WdSBmmV͛O8ۇ:Wߵ{w55ӧyK˫(y"ĴT2}\jkMeW_3^Ou&mW+zڴ[d-գ/-{_5l0a ; #b1w|](M_nذN9k6Rqli-ш<ǯ|V?uJ kOp*Ypz17peDxޥֈ&_Bd}YA҂ْmu.S/row`vDRBO" nņ N;YXzw?⨉~ׯ4vl㹽tc$Eōz39P>ql_&HTQ6m21$U&*ƈD#Ҧ֭[1uƱc|OZn޼)\=c{I "ɧ:vQfoh;٧M?q<ַeTWUϟ?_64m='b0lX+G-پ tSX ̶B"*g(3PHN`B0&& Lt$2G.%e. fP# H2ҋ:g'ZOOy:Do[Ɂ md\> (}g~6ad?ݳwY3oG}nzf#~:fl:sr&1ʫV2=flZ&ʕ46kg>#dѣ5,vRkW_͜57\yr޽t_~v!}ՠΉ3 JcL|Ct\eY#y(ڷow,^Ɯ֯og(4!E!X$f[ȍE26  YʈGi|衳++{0*T`&CNb,};._~ߺu°HUWP(r:`cI}'owފE_j}q1!r+WZr]t~Zw$ .0=^/  3 pQ[g`cF='\c0E,6&h(R7ώ*3l vDGJ8waa7iCp%̽NQ) ٴHm!<iaɫXHdeHTŒ%w_]{AQĂ ξLxow \~%on!urVLNCJ$ ov /d2]l}]U+W7S% Wk^ u] X:.QKdo'[z61uxxC2za B"<soec)E&q:ɉ{e[2V4Z-R Djykp BL6,$,~>Ϫl&LrMEiS lȲ躺з͒0M+.aQUviZ7Qn 3MX,}־qꮚH^Mؾ;eroZ>[_xO32Zxx%?'wK8C Qtcv%&}}W~7q;]I_Hƃ:zoum@k~wh|L `eD*t84 a}>?2vdc[~]fȳa+Pzi5Rܽ؞IAjf5\!N  T5*V?c=[(k 1{jجlΐc>!UHOP++x*uWU)0XeN%#=]J&^rQGG#ꫮ):;è߱*t}?]!uʉ^r駝R[[$ Mưgn Ӈ~w.#_v@z/1 $bRӵ닶MaxEu8XRxN۩E `"#Gj:}tڽwW]PO.ObΌ"!V2rVB.ŘFRU4/̐@pzeU JX]t/փH5(V%Re^ >IzIWjCR f{-blb+c8'#8"`nٺeJH$rY?k/ }쬫 A̤uC]w)S&_f"Tcc5ioԕ{dz4ڱc=>p?\ K_|!&ΥzCЗdкD'?|g[j#*U9@$$lشwtv`}Duk;uʠBadSy'6.w@mU^Ɂ7c0$g&(uɄptg?&bpy<۝ =n5z6}z6CͿl+[uӔlMٛ3ϻdU0JR>`'-ssnܵƱ̭+TIGwO4QWVU9T X\>c"+4Zj;?V&[~2}<$Ib\M7(Ek߻az!>_u>> "Ja !'GD ð0kg5G}xmYz&Oq:T3TMA@/|f\ͺDDvƔ`"Oj%Y jԦZ*&mȤ6=-)Vc@(՛1ڮgPM˴Eb$ c4|cի3qҌIJJ裼~LbؚL\k1,З֕>pTkKT:ҧ}1}Qdsv'Mծ֎֘;cp$1'\JXb9nƌY ü[:j()}ymjjr;yIr{WfL!QٳI4):W{q*dncr"z]N)sd[lMNEs0g.Mb墲E Y \ӥۈ\%1b"q~ޑ5^f݅ Ƚ@p:^rU ^ XNh͇BrI=-e;}ZW{WmPDs1q\}9jve1TRM?YVH5!b+XY Glb9u#KrZ#A%F W!\yY'_6?#fye KIVj^]ko!97G%Saޜ7'е\#[[YL6Zf5S+b1<LTm!^K4Ip[8ڵ¼yon%=^z-Rb@ Ԡm!\;fdWbI?# ^ f*kR,:tDjm'\; ; o/:nDSb)H{KާF(iԯ'{9ŃYİO%E &dOsm3޸R6dN`)8O96k*7ZL^UTkOy3E5 Y?(_"+|HeF'ۼxXl*26_ݷ<Ϲ~&f9o3J ˑ"bBN嗬DrkWGJay8Y"9S펽95m*d>o޾kߐ4[ == ytwv|1aJEUѥ/b{Gȑc!-Y[v!E# b׭UA˙9-j ;,8X߀^V0 H_Ba L)܃4IaD3XY)ʻ(>O)I˧r#K7m6y!kPcٵxeeuggHʶE-ֈ1Nr1+JFEQHy3nbN= i>RFeW֤{( Xc̲lQm4i#*YY3Wd/%e;<ʹOAѹʌ^ Z {Vvt!JHȆJJdRnR-b`[ʤ?mR09))~/ 5TqW=!Rm)u3xRa[f=%Rdp bv-UB5G s¼UXI7HwE0ө+ItTm"o)&x5iх -B@fXIFaud~BUj~g{_3϶iր(s-tlhORv6̀psIT]މLD)J˔X+|NQ\^1֎FQri%I?M4+DfX32bFbtS"*BDmsEՏ5C_1r޹VX&OR hE{K өpZ`*J^4>D/NsۍA] 4j&d1j2U"{a4|%PȉٖR-ҽ贬BC$1_/MawTįNGn/D\PA^tHlȃR.(W|IR0EmМI);~=u,[os)gXS'M#,S O& $ Ҝ\XP3>M{T]w}/!Qt˟H沶h7e-/GY7%ŀ4 Xl'N8]wu^ye}Cx#fbБ'O~gi*[, vPm{^L ]6BBwY5kfuuIf(c͚5C;v p8f͚sTVVR4663VB}EeܹsR7Сu(c,+7orտ^sJEE@1vWx>{ɧ??vjY DοHg^F FH\)t,$鷿 KiSӦ$ⱻ\15MsMV/1EX 5-]3OΥMM| Q&vMsGx\ǖgTDQ!kJ9'.}AϪ>sᴩ!6cԳ}Njjs~[dM/Vbc/]o rԪJ1 h dpA\1=?Bϓ8bp8X1Y{T5v)z^Mⅳ~Q`޽m&jV9R-] ]~萡MMhmdE(ZvYӃ>+Rgkx);F 0ztI:Rm.~4|0Ck#P2z[=+j]]`-!8GEԄٮ|LЊJk7]^)l7tٗ ^С3J jhF UU5wn },ژO $f (i3\rn/\wxM=>_/$ 1&@h,17RE/QILj)(((cD"eإĄ:vJR>$i 2/d /w}4 "B7GxuuuuM.R-Elæ5 {<%-LQ3KHzz===GM g-Zdgn yBy#I8swNfVvw.kƌnjM[;hִIܿ}ێN?6$I۾޽i[Q JԀ w/_[o^wyZ`wb}b՜ (r5Dd&r'vEۻ"~/|Ȕ q ᮈ@iA0ru_4Oip\lh^NA?<W\i_qS^3\Ζ sх2Lnu;l{kt'x+JlX2Ա_='ARBl3l;n_r!7xʖYvFTqvR.XQX$yGv$|*Ipw.Jm0=OB4 Ř?Ȫ⯾{w9فX*-rPIDQ앵ʓ  u8I37ՊwoJ#Ǩ#s7V5ߴCd<d(I2d%2qh&鴄ʠ3N$lJV4iUv.13'ͩcK(b Z䫭ODŽ4 (*Ү|U U jɉ1ljb#!9|I=9朓SPӁkq3 ֜*`&;ښh45|n+m$aW>Y| d/kUFiZ`F/)V1?=H\:-JG <239L,j(JWMR[ q%zf'V/+އTS#'PR6Dh%Pb+ T[(UX$,^ad[-1m@ALBXա0E{:E* jUՎ8+܎S Ek; s{v@\V(`L#цa~ |+@&7V!ga,baXO(t8;Mq2aتj>JBz#@!BNsx y[?iQ2MiX SsΡ|(̓Ib_蓣(#[D1Ǫ[vVN#DX8>3(UkLv!8a0a8a0qϹBsl؄<ּ%ꙉUj, :bqqUyFV;֯"Սwy|y22X\- Aԅ8pB{xG["kܞ :c@ǣ]]N9hFPZn!C(;BkϞ5=́m EP1wa '$fj &qE|PO*]ެ̀5@f8kg9Z[jjl>|QvF#^u׳g@n@6Z~>=3+)rp7tGŹni⫤,((T}Klyk헲UmYl"+2˵|Vf`LRZTk +ʳ9AMXxo] h@lĀx~q)i\)G L1#%/TBzQc/xl%I[,oAAvDvY@2 6jD#p x+/#/?}>o) _&lz=UG>N|s\>lh|.A. a/ =BEeYM xFF/x^6O|z[̤}-ȅ^ 4/\%>{Ҏz_]c`uuՂ -c+Y+Ov@wcZʃmE1dz1ُLdzY6tUٰy8/_zh[(>,JȪ6/Sn;pُ>\޸uI]{4M1H9TbOI E1tr z(_p;Xk7xK+^㏞#"Ai̜"}1hS^VI[DR*LiښPMOQHmu5%yy}jO׻n3Kpa"!߻{ڔ%;+ŏg)xjਹ^F, w jB.9RPPPy [(,R&ֺӦN6#E D:!( @<ټXT4oA<#\ Fq&@o@<JьLD, cHTQ=؀JZՖSs&P(C`cX!y.I: Qd>]|ڀe!x,2~XKXpRCA(lz-&tMߣ{WΓϼO[ z t_=nLJa{zĠ֭]C+FV_?qg J". E,TT'#5ը#˶ Bˆ~Q[Shns]f `b: l[/3}8hy֡p)u?nP+B&&pb_ އMռ^WٿXWSQ9j͟HmvX2>XXRZ|,a]/ӦJ'}LJ@h^IO:ױs[AE h(jk'IeNݼi}]BȤ3äv.| 6;{%tt X g/ϟuC&fZ2ݚBP3 l0֒2bɉxODo!vt)qRPP&D1jjaI$Z*bXb]rBL~~{@CԀ@1H  yWKAAAQpk@hmzsЪ(cC(J嵁 9-#RV^ƘjTT,KQ1U5tCWWgfB]A{}gwuݱ̥͂ Rgkx>irzl|&o Å%޷sϟϾgy`yK>~ڵc¼j*Gfj̔ pXr@Rj)y:BiNc= .TfBy.\եSQqy?;LHD !D;(\K2=zǺg~)׆%LV4- %kU @  )ܧX~("( Hia%<]Pp8|@,[hy=zZ1Bȼyge$!3ѿcaSzmxxQ#vr^5<e]/~`Ŋ2.ZHY%DD[HDMشC^ gT<|/P}yUA+\tiGG[oyܹZ`wY^ 7|i 'v#on:kZ *r ݥA)@32kNS裏\+7moj3ywߛH$ʫl:xw>u)t0@ s%ǎ3dHͪj3iL4{0X! (I{O  bs(RPX2  +lXSOJIsg)(j/8'd|;h-D< PZ;Φȓg[[pZ9gL-jݞsVO*id*YH ҄<ӕ3``={ZYxg7e8*>+ jey|Z[Ɖ)(((XBZlO$ yX\x<  2$-ys,8e:pܣ?GgއQX!Թ'$:eBas7UH{\|Uc?n_rKZ_gB1Pꘜ9 ={ ]Zn0rn)YZHIAQ4.+ARZ"H5 ]lkm)(Q={VZ 7I+ܢYpw?8y|f$-E?1$5@AAa[Yr=S&9n\pp\zMaQPP BK.sԨs<4e$Z`9V=!I`Z)jk1 B;H1\pՈA H=8)qo 2g!l)((lЪj $6DWIJrV&ST"@"Fڄ;Ѩ891Tr@Y)j.V&fF*))(((jŐnqc=a[&Lw0N7 4j N35dFyLIAAAQLCͪATQTxuKGqkXwmjѧ1B^͂Q!QRxq,BV85 ҄B|l])FhGp"L=]\N (J:] Ţyyv?2\ckpT!NQ, TóX3(ucNB"Zвnm[7VǖDivۦ]yC_}m lLgohJVKK ClnlidKvȮex5\\s(?mo\PTr Mu 4Y,͏bLfY]zHT7aB峴2_:n>*]lit|--(? 6Y2)LYV/+Y==`ZQ [Y=R'i~67m?K3`ډ )t|xkl\4EQmjR2r鯖>,VuewjxG[Z֍03-0H yG CĖY<=F; P}qԷO{e0?W 7 d}ɱ3`K˺@1kŋj5G~љnxf_xۤ{W;Eqew"ϱ]v-ѳWju:XK5ʫgU,.kE=Y `"jܿmbf¡=y=9~کغeES%mV͍jU0_-o+m`-Q= ulyRĉD.8?V1 cib팾齽;06OZl@`eeeަހm yϞ7ݴt*1D_t8~:~B]fRPP<񮪪];[ڣ-mx7*Lu>qǷl"Sb/%%W07} Q09c{1 Tg}>rD[[u wuBBw~9󐪪ʒ"mUT&vĈ;w)E᦭J~@UpAɧ R;fhZepWO?;ٕRVō7>Rb-,6o2vh=RZ6oٲf9v$Gq\yV囶a!u( CjCPx3mڔ~Ty!MFVegP nEyx^?ܹ?ym]mmT[_?۵77@ggZL6"\h4Z]]?gI]fT{g*ɿzЏ(J 'xbs.ci)?'H,_~78JB{l b(/jg(4n\%^>mqMMԱ8Q B~ ͛;;!9#0N %mO|z[̤}-Dy46PYo,E2wl D{BDhٳg}YRY8a^rʿńdF&BWVxՑt9Y:@A_@ٸac,. 6pOc%IhǣmZ'ODHL ?|~'N_B<÷'w5_*W0WDꥤH^T/ ?&ϊА$<̍U-Z\c BLÒH7ˑ6 1Ȇ}HH$dcs$<\ 0FWwWhۺFH3Sn_wUqƙ젹p8g:+v|zHxiLB!:,MuF~b O[UUBS:Me^dbTJɆ7qLqgeBou/jqfP!a !i?EsSh}_=b?l=|һEN@(RIau9z']Xj (I헿>3JvaG>,%cK:ZVm0P6\p*XЦPVVlq/-P@5|ԃqe<2gގEN mj$(曯ָŤ@*RɁ oMuj ߹bqQ,@bZR;kf8 kM9ٱMqRA94]UyfI8:TVj8|L+TU^Kcpp88<(&jI+Eep5l<6N"LPsJ n 5MGՙPLy~$O~Z2}(‹< > ,Oh'8'^_`83!#s/7!,Y+Sm3gN.;< fBk\ñ@P0جZ9 :Yv1!t9!g` Ǖ.Z_@eѡ3do{`Jji Zx,WUb㜫㢈W[5Wbry{_ג Nr.PRa4Μ7g:UbRDcDMn*up27hpxԛVjּRKErґ>~߿4 }L/r;JjW'!P+W ?}V~`O 0GmiP9q@W[ctzw6q}U:^XCmdo#z`P-uɅW^! _gc[75%'`ۨ%PW㵽~rUEWA׈`I @:UD\mUhhƌG1d 7 ۷f|f# (jo0xu Bl3NBݾYQa$1ݾeꔉ{8XZ9cR /?%Ilvf6ٳrʋ.쵿فS,ciE5fL4pwD̀Bƍ7eYퟓr~[W/Xp髯|8*8:@w3{;;;NUgB-[sI(vi͵lܷ%+-MXpfEqx<Eq`*Jm*\&ɓWX:!yӟ fslX:[Ɯ9!RSQǰ'+JdE[[ے%K!֭{ f5ndK07BF-MjuBEbMl6Rfcań. IXΦZ{Kl5@HZTaJT60:kUfXF6UӜ_{ (Ij-B:B1AD[@-Ү@a x2oZujz6lPe+==Jhj.>|'vϞ௠ԎNRsٶu텳}3# C?ikmꚆ0PhƞSKP A 5ɮ]{vut+ab޵{sOjU$Ijkku= v{X0Xma13cykd,2k0MvgdhpDF8=/~sۖZXhlŇ k!f:y6 k2(>Vy$:ƴo_ZFfxJRUd׬CC5Ba=+?%^\; ak^akjIbj3&H9Mmz~akB1f4 |IDATL/04h0c^p0dj K0tkB}VfڌQcᅺkA1aFEӴlyq& )cfxd0|:xe d33+3a9!\9s榖,pnټVDQ/Lؔ]@tшAU yEuC?{MӏPB&.[DӻZy_ 927erNkq_5M^ !;d{N8T͟H$)B:P@ %TQdAx"4._M/ fVES?:e{j47᰻DR*4}*dRM&pwbX*D<r"sŧ׊{& g3]T,V;4<=ػ/xw~Ěe0iRI1\`==J؏ϱ$xٳe;sRR9%'tpm:VU((Y͙'#|o 3Da`3Fޚ]gSY3g|5jRO喥UeR F&c"rKMZg?k/<%~ )wjDy=BW\9}x|pƌ/7!oN"ujM5Ml8MAtl/iuUWJvJ"!$C}nAQR @^ѡ,G'164in. $N!t6E">S9gduJ"L)Eu܀' ,r&CىF0ȲCQ &mE:nR ZZ ?_< orW?5=5:?5`x;!DW+.s]p0"|>_O‹g}!7t톫9@`ݺzz5MML$G7jd2#^|тS&/,P/={wuM=3]h1Lsi 3`$I,o4\7^{/Kiћ R[7P*I64c,8cBCVIQuZ,[}hAf  ?Sfˡ]j2ic,隖7qLga1MltH9sc3c7!㚛cѨl(%NMK,86B%e4k.\1#RDԐ:B!svܰ?oA $ki9:!Ps&r:R4BA`z$3q4 ;dفz֢" g{?֖0AP@mM;7bv N8pA o3F)ݷoGgD2ZfSA)x,s."B(pn|WF˹m˦&QU(P:kw%ѭ[6M4AcgEZPR*O |^׳n'M89O|-[nmok dN\j˗.]DnVdۯ~xN EWI$9C zqYn1w;%)}F$2BԆ }ș3BW|61\L^tʰe)-OPnj !zs=9}wBn&U74mc5W]wKdϔz3O 9I[1[}LRz! |>9ӟ5 %~LBO*t p@ n`~ϟw iсG:p["/xשl蚾m{VӘ>Q{59zO(kMFJr(,iڣ>Om[ŮO:iOV=:$s.ɄD3q*Wn۰/ܽpfHa?xpH;`Ǝ~?}@O#7,rorf3&SR- |>YyF;l`{ߏъ#G>7Z U`Lh'iDIPk寥|)3yo8[,Ռ[rFҺmoҜ͢,u˕+[@ι$ne__ .JgLH,H7λsߞ^jhʪ?QDU %} #S9 ~Nऱ Ʋ^@ АMrn8́@ YҜ)1[ꧦ~ưH1&sgK$c_̃(N]{irq9\;զ-q5wxArO.3as1ב ޫ1B_b6ƶhNP1S|08IZ,9+~mH'|*a$ן;DI˒%vYw|WU(TB UR>o^sh!bsJɧ*8s_s 2([rV{wO0Q4 l$ݻK-Z.ZUa W,O潄=W#2("A%$i"6~;],~RKt9mpXeRI#CC[|8yR'B͔"BD T,^ne>>,㏻yGHN8dyѢ /twM,B^|޽>?ٓ&ODس{׶5Ot嬙rvѪQR:;}:.ጱX,)--~rbXh4/O:NK܆H-clp0rι#ak Hzܭm^;_$ȊU*@K/eJmbHd(aEq.Eq^rU*@K/KmZ-FJIZpѪZ"%|21lصZ.t*##Z.UR ZZ PD1H-TWg7nb1v5 7i.JVQXDD uF Kу$ v+X*7qS9{4MXp0 ǵ`$bώEI54vT3t8BIENDB`veusz-3.0.1/Documents/manual-source/_images/importdialog.png0000664000175000017500000006076213161413406023643 0ustar jssjss00000000000000PNG  IHDR}sRGB pHYsII1\jtIME StEXtCommentCreated with GIMPW IDATxy%U}N3ӳ/=,3 0DH\<1&jđH cG;Yڀ0Q5bl憩_(4Aw^Jkg٫%x 0(x&d2H7(J422\U(F5c-i3"L N` d? M+X2@ i(f*IjB/n$xf&/WJ($idxZh?lRlZ^*D*Br\!jщr"H3]]=K.dA[9BH9vYE̕kEl2h0q:jFW77AKZfPZ;2|rlLKWdyT.72b$UXly6&Lz/5$TNٖ,uT6ET0Z.h?~ܽ7_Aĭ+V̳ ?v^ؽ{ظT*Օ_z-^1)qʮ)21>E',l8:AɌm1Ww`>Ļ!! GmCx͙K`BLp;vdnLU&ǎc,Y1 ~ e;cLUNS#6a/xW!!DeL1v͏Rb+};^7oޛ/BOoixvx7se=ls8HpB)i>b.Õ[n)Moꩃiü̠7GDg;^7o'  R믽c.~:EwΩL-QOT*y^4dr:8¾{n:EBit5cM=͵~.glRtvSXF'VZkW2*RBˆH@)!??eθlszvo7wwݺ~ IJ>߿15 s 3؟_)W5nMj+QG٦ ʼ6|$xpӿ]{Oިm{-\X([w޽у^hqcΔ C'Lf@[([w={zHˤh"uF&5|陙0ML =uܻwCSbmglxD{T\OMeVX|RRQz۫ri2)ZUU՝vw]UUZ྽Y.OXT"O.Odl3:K,VX<c% Y"yOf nk|>x|;cpyz3su ^=$/]~瞧/~ˋti~UUYr>۳剉 Ld坓XU+L&Ȳs( \LvNe!\lWUXZrR,W6ElE#Gs)TLӓu?8ַZ_.?rʪUΑBO뷿cٲ˖;x\Z.D֓c"%Qk Mk (W3J+cDe*SʘTպ/RgMl׆|Ԫ!xSUs̬-4-rdt:9#]!n1cэVѮ'ajcQ3?8SG=٭~ӟ~ !л~!b*S* 2Yj z&Wd*ZWLJxMĤ1 dp8xs/O_k_tͷx O+۲eVlYw,6,ɽaoER(cG~eopm#rJӉTR(%R9mMeH!r/VRBH"3G !qW+ƃje.έ2Kkq¦DyRdz'MQ+kVїR֯!RetR"PA("C)!zpxcx ش.7k՝qx4jPZ,;v yj_޿MЇ>|B05) X9velRcy=JfM^JcG/d\k>n:SLVl/go◾2ɍ6o_>7WQ .ЕM/#6htА(UBGeeT(x7}3qǵ[ܱe7 "Oe(}_?ƝJg0ӻu7;ͨl2!O|1_'~oG>4,SG5/0xߢECJe2RR#'N\A7LUUkZ68Wc||q?q"1R˲*R<s+E@*%q7(Ě::OjNXc\sۿe7[Ӳ3czΊ.&tRL-_oݵu2O\mڔ(3͆K"Gx%L]g -1{kO2]z4auTNesy}[ww_ɿڤoդGUX._h~ry#~_"5+11u{%uo|w||wsWo̘`<"*LQLnr3!>2ڋ;ktT}剏xBq+BTK,ݷߙδ?׏6oA6c25fS\\4ۋ6@ֽ*`bA<ᮃLתjX<=+Hn]cEc)BTjL2f8szҠ3 VY p|61jCxgy~bb=W\ѩW-r̠0*S3f|_׮]c|t&3ў1}jj!}g-l.gxxڋ#1S~w|떛oЇ>>a6}|f(ES 6V{g_8Rv߼tOl!dF[J6hmȰjM\~mg;kljdi7gSeQ-!lgS+BQBHC.6"<е8*fc2*00)3{6:m S|kxwq])!fګAgQ+͍y!PAQ633]SGwgd\A٣guҥK㱸惬֪ǎ{d`` *4(uT)#uΆ#s5bRnFUL*t<=ezK٨j{"9yyzY-]'>RV;:5W1UMUHd*=zY.]O@zl:d>ȑHLMIbJL*3$ɇ4O)-(d4gr|rS {IB&TF4+XViD00őH\eɓ'^=|@U\.;11!FK8z{D(r1lʃ?ӫi~6S[BTKk+] ^La gNQ* …1"SVGHLͤ'"XcM"qUbH$VLg;l'l*#Lj6œ9MVirxuPw90O|=aX\ 1a7-؞7e㱺I(%lƦ5Z-yNWq*r#1B [@Gz6MΟQfc1:>j/8c:*2U2cMnez*JcѨ$K{@DJQ;i'ԷBcZV>$Y[+B)aZ6R^Hg&џOflF<,6ZUdjJK$u^oHPB0n 57ffg9&ZμR-@|(ucS[:ڢf+Ӗw_ެ`ݰd`/sYjuql:sb 65,oS&@HDHK08^Ar&ctv&Ek] zx-SNƚy4Su|!{X`Ywj(6: h&2o3ˏ l06l$9fI_#' _y כ.׽Ӹ}p9l?0V#LCbS&}{UAZ)2Gd,ӾX1z E\gԣYǸP&g_jrs%:f!Ƕ QdJx RAlHx 3 @ሯ1M|n-&Vo!ǔgD^-TZʱ.PY;8"/67|96m!!%l_'Jݟ i11~խ0ڋAEg?xPd8kISic]! cWG&4~۷E:w7 @SIEv6iÛ` ssmZs~ P|*oŷ¼nٝ}4gzq6:B Rߎ5E [md.]x؛dP~b6H!1T BED%'O>(т2BS(p_,˒$>FHzNH=6>B_BȈ-3GFj|aNmlrh:M; }MF,Hknۄۡk_svFl޲ /X~6\Z IDATM7 0_tuuij\&tu9cy$84R[+qtniʱ҉L&%OMH[:fibmA VU{ڴnrr{7VDV=qZY),^O?^zi".Z|f d~[بÍƘF6Mo{xxtf1aHY>yv=W6:U_r U$Iڻw)lv$yϞg}~V^Y b4}a۳WDW#M}zQkKgL*@<dr&ر҉6?Ⱥ HpHtPúCkð|-¡C.]*˲}ʞhCCCmޣC˓6w*kJ"eCO q h&޻ggЛL&d)цyd4۳{zٜH̛7Z*bQk"+f(Օc??ӗ-[>b]{v\+/9BHRT뙯.$d"O8TUX,Gyr\-s)LuBW:)+˥ѱDyd*fɤOs$jt3O΍ N0l틔T*L&ct;řj̀~Tuh':U@h*t<cHR8E"":X%IIJ>_J$;rqd, ˲<OiIY cAn1^T5~KFϏy~/jf+6|0 Qu{Ғ:4Ƃ Ta6Y6?O넯(y->U[x]Mژnʹl6%[cg5Ln `bzH0DƒIE&h? !d<>7>$f*[ݍʼn,Hϲkܡl\oqC 㬃 =7YM+#˲UTê|Wan1- 3Kl\316JezemAWe}AO{YcaΰVZw` 2-_{` Ɲ2#u v7`9֡ zF݁ƕuoSЬ @0*C\9Y˓W|Hws&3. FcLW4O5Lo٪,ӂ$FA4ƕ~{)W^g)msB=X.g\La|lpɌ Ln.w'& H&MA)~ u'>gB:S0ʝq LJ3LgtN9n1%jBաqeOy=l[<j 7J`:[06A=A]Jb1sվϯZ}k?WrOaNde\ToHf ,:<8@V2IcWZeVuFM @K۳meutji̳[i\72\uuyk ~Htl#eT)nzMacL[٘AWCAoĴeQFOt8Aq=v~0<rtI fzyŪu+brUqn=PWUq>8? S[6HUס0-@#A:LĒ!RBD*(M< jEͬ I"ن+RE&om$yϞ=gFE*+ԹM !X hi0Lr\qI{URBO*Jrpw+FLf9E r- Ґۏ+~6S)Y*ɯ8B3 T>+@39_xxqMhIւB,mWn'Վ`maN]mUrcӔ* F4<n2κ-Y9ژgi)MWj˥\=g}KkC׍}MXϲ l)KyL U~UO{&B`Zum kgc عkJ E#(F;ҸҸ4rĶ86l{Ckb<bHh 00|w?_*B(1_6Kfsv{~+VhsJ>{uwXS={Zbu8/ٻkm5 0#&LLL|#(O =BGr9$[T)XSk_]U3׭onmV۴UUkUJs@ Wڦ-y_vV ?dK[_sj.$vna7|O>!+wյNįVa;=- \8T#`6șt:7ҋ.4.}MaltIq/}6;z$ӹ[gJtn9phU?,]$ hDy"[򛊎U,@su.;l.ԇi0x<Hx8Hl.4웎ykCt%NYGMŅv Ե] YF#[GA #aU4V5q11,'qԪnSg1h ̚NHȖ!9e?'^%qxiڞn?yt< n*xYx\-{laa /(–Z}W@Z'z<^=Ub7Vj }a[V1`:519JEuDx G=1hC[!AZss"0m*Y#VlsP.@k+o{x9@n0BZ}4`t0 pͶ*jŵ pQ"®{(>ʥ”RKЗ1܂ȖQ"FC wG:#ё0t#e ۨpOz0H4>Ox=䏉&[*‹#?<} ! K0lr SHđ`Y;đh~LaH0kΔ@H0b !#-L 3K[漅8 !CЦ i!єj`l֨pM PY6*xY-gPy K9Lx$x=<T[O¥7}3 9o 3s[i9Sy5 |·FeiU8xxfIrr lfK[?a!WQ. w7ᬙ `.z1:K=Yw7ݶ)h¤)~VN@ё M@ f+ $b$Ѷp_`; }1*](MAU ʨÒa(%\0PU5t#%><#M]>]G0[eu֮=gZР0FJbcHT %pIZy2"DDQeSA4kJ}LR}FWt+#[6!$7o[U|.122>(L˔BbQ1Ig3IA ngf c^}aVֱ]@;8!J$ IVkRAGq"AP'h4 3 ez0@masyo_5H[sMx$!͚>''0M- * Pa#flxi0k ֿ1i 3(4 011AtZDm/H4 Ҡ]bJ%pYd0"GZ @ GZ <2HO7=l%3?ܸϕʜ+٘[~g}nz ?6y0;<᪰qُasePw!Oݭj/u<|:d2:ob'Tsph`6mTf<(f^&(hME:0cH;WVݘqHtGʓ߆5UvMԆ' -lMܒК+@s<bgjϕ{<ׇ/ _<29tA9GZT|G~V8#Hl\M[C 3 UL5,թp8(̣qm?lBM?A,ғDԶ.99Vgk~[٘f?9 <opma#qq4%㿽7aDNײ`|x 4ӻZ؉!4 h+cl1S@0?&JG DelP˼EVs(/ w!FDWZ ܠkU*NiY>k, u(>}ԇ[7`p*/C(%} <Ϛ 3ȧϚ4HmzV v5SB~aI hϚ<_ J!0Œ{ưT0I4LbM !h$KB 8[XYR{RTS b4r#hz L8e].?|x\ST@T.{ښ'N@ 0jqJRDDcccxg#W-Ia{x-(c ڙjuzbɅŽ캍l?lcsv<6p"%6tq>xcg1j[Π9lDd Uo3$!+h {LNlNw~S\Cs%V| Aw߁^wYh cǯrpƖ nB7}:"Lx8cN\q߿wvlF)xk cLՒqUz?*LL rP)z6\sc\Hn€l3Hc%]ElZ C,@Qԯ߱0v溵w3׭ewާ,BF\>V aٲ8`O)^=pǯ9W lҏ睤>iUDDA9??$.m姞=!+w/>?4X`R*3*#JtΏb q‚K@` BHecLVF;#H\2[Y ,VVȖXM/|f1<<$qYQDhtuDMA&_svTgLjﴕ_널^qy&[X~#Ѓdzw\'S07 XJ[;W|Ѧ\-+3n|>aáBD IDATB翸~Iƍ3L""Znr]Jٲۍ~K]So燇}[>k](\ve_8dN ^V?pPkh[*C[nع_WVYv.{$m{#K:T@ 2="| 8"@'ĵm_ /X~[oV""[G/4yK<cw1 f'ӫ?FCTԧ~_\r a ' YV։`HK'1c \*#J"IrTDc rZqJLJ UXVbsSZ?e2k~.OG*pXOW+5\PɃ!zGKcky"),:ݝY3&Ї_UQbT(3 Z$Y.%E ,p`f܉1 ghS+ժ$IEoE.+Jk:ՍI\VSbHV:E6 n)kkzch3lLɳJ>Qll=[#\>w+Fī?VJ0hjFuܾ*l4Жݸe7ha㕀g/x>I(!Rn$ԪkF[e|_l6-&E]se=Ҹ}\ьs:b<Xeb\HA? J>߰a!O?$|{$xƍ$E3qqP` gw[uG+ :[s؛vݾe>r 7>߼P6oMUÝpV'F!v%s#߳i_֮g. #L[?kw|Xb͚wqg fHBFFFW6.pz:#a:ҳQG?x62 [\x֯_ifA`mR.Εl Z[xV-ާzl` p?݅e#iL;=SevhG06t[|`d/]L*244 6n7ZϦR֊88(RΗ%KQm+/40!G"\dž=ѕN)V*ޔ" |.++V!ڻg,sٔ #&sxTX4/N&Sŋ޵gl粙 /HqTȦH Al,-˕6U2dd"Z0Rd2XTW Zg|00SVbQQR/tB0wUXJE] mX:j2 :! ~E)ʫO=JL:ɬݻ|>E N~-"$ LtB07 霢bP'˼_($W,L㇆M\h1V l $DWo/[1RJ};?rJI+*clwB.̾֝MO͢œ nذ?jW:{;nj*U2}ib N-͙?Vٸզ-fXGOn6EUM6>|[o]x[-cO?- Fm78n[TmwR[ѣGӟo|ݶm[&ٸqudP'3#ׁV]GM @gGmƫzrU1{Ǟx۷p կ~_f2//nY+bs ۷@p}\P0XDٷ)UJX%dtmo{… %I뮻z|0N_r%x;6c6èe&739]0?GkKC=J/_~UWyxg_DZw 9&0> 1{x@Suw'XǺ6.uqD,W*PI <șZNræ\Z(sxIW_JFcsȏ{?;=:!DcdX/u •W^O^|u Gڬ/]Le}%KJb$' ?U'_8:!WaJT:m}DG_ڶb"J-+hy'¢(\G~rXi~=;_޶`Ao>E N¾I% ݽk۳>i8,\طA*i* X,[)+D&N& @'PRd2F]:!*mr-F'q&0@С=Dm:DСrfſB!jchz3jۗ/[ w*,B>抏ggv8߽wyoFQUT}cNFa e3-_e7N /1AsXgV<D&ڬT*`@* * Pa{L5Ƙ(Feb(yTaY+w|`.S,l)}*݃: IVp+y* ;3e}  <6QMn<A | G}J=A軭qT*' }x[kЂY2*,J8bjj8Τ^E""N3>ٷ…d ,D&ٿsY*<::=1RrHH]FGOa0׈F" dhd ###9EQGF==$/zzzGF6nY¹sk,Cq=K" d2 VURH*c&|\r$IJEUUQ$+{ޒ1k;SO8s 3 *ʍ7X* {Z(un8W:cٴiSv]Ծg}lϨHںo*[XUM6>|[o]xq&a*{[`V7v悱k`#NT !jkVnr-"!W/t =z?~mwm۶e27[.`ƭV"n7A1m >}%w~{$xƍJ/f c=۷o_d2_~_%;gfe탶04ܴR=YOai]V[R5t#I֘UbuvF:Ch8h[6 ?#pïͻ .lT(J 1 Jt]w=S>`:Kw{ K9WnOiqtRlN+8š0w^WV.Vxy{$<}}}^{_*zT*|򫮺UgAђV~c}LW6cuÎ 뷠iټeW'Ơ7ӟ 4#h>я~$)H|EQsM=+!gm2*2:7Z (J&?^>GP(\yO>_J0gрnSjwWnESSh*,¡C,Y&/1/Y+4|ȡLߚ3u~`+"hX67m‚@; d2)ˊCqlllW\& fVOB*SF ¢(|Wi| vݵk…("%CEEI(Z> $I7`D*\x@{_zOe/ZhQ* ?H$`\9*,B>brl8Lfd2@Pf _0,Rx<*LRd2ƦBD"H沓 .00@ ^[0dY$ Mh41;@3$+y}4M&ϿRqdX2Fb4 KcPJ)c %d3\ݝ ȲR,\1ZA"Φ_E"- 82Rڶ XS,bq۶9g]> /vgLT ^D"=t…$k0D&s>* \(HqCr~eO.3pLӈF d8)d,PGFs$V*UIeYgcJs Ƙ$IJUUV+EVpx$l6x>ma!SX- 24 P3SvNylA!rRD,oX_l5(ƅUђ%'se+<Fi7ql>|F%Ֆsܘh%؏pmƋtqiLPc/Y*nBHZݸq;@yޗdCMCҥuHUk^{r˖;n !}l&қtXu&}}o:qaJ>/N$oذr7?$|$xƍIh<7}!!{&7n UIg9vJ?纛/|Lg_smڴixxG>|wU(\vev[Vl@TT߾eD_Mz7'OMjUA&:&h 2${O;w#Zf-ߡKQv4ACJ{'?yΝ/?䉾;–=u7~`*¸###s`|M\x֯_Mg?y޴y 4D)D*3Um~]SN8`Lи'>y@o =`ROSXvi_ɺ5+@e:pWA[}߹G1Q|[.T;YZ#hl٬:iW1.v:yD3SLDK/4MPUUUUH$JGףT>yYQ~uYɕQIBo`J{wk({uww3X,V(h>102i峖^:bLyRA"16ݻl'/l{jɒEP2/=F!*cE@o/)ͤSvۗL&ec50"qlllVX" a}sc~AJ DHHXbFJ/ߓGB aUQqb:Ji6R??N_s+&Akמ/ߗe( 5xXRDL MQUO)MO Ƙ x EE2"J-YP=_óEE6]h… ҩTGD:E#B,t2[4#ͱ) i8;|.F r d2dm$',b[gLԋO* c*c*sLd*Hq Q)M†6Lՠ8sn"3b<D6_~ 2F*zx4VUB) <M !OT;S"mλsJ;E DeHToSTEQUUBSBwmqǏ'@lǶ6M'MӪ=Ox郶~(Ҧm,8N YyD5/>9>RijYB+-!І+Z OTܝOީfZsw2J+X"w4־*lbX BH,yyب ^0{:@ ) i(@i47;;RDie"$I%`gn/Ƕ<**jQsKdi|Z$I+pmB}l{ޞeJSiُ{L) YI͗  Zh4ݶwҔJGۍ/Jػ;Es@U Ay'IIut 7/ |!Ds T*8VJ ; 7_kI4if-˖O kOcU/v5X"<ʾygZ"8[ OKu. ka`;NR\ ͼ5 M䀹3%wd,[Zf|&Ycw>nQne|EߕXj;+N 3e2o+F5ovҞ^M)?V+T) lÐw֡c;Iz7^2_}aHim߿EK,9/n!lJs]7DCt;IDATy٫/7ο|󓓖繦)E.@u){y\ 7矝B/j&g;;VOq=DZ>B/+2 ^w^ǝ(UU)TQ0YRHa) 0R@  IaX[o04RˏX0^Qtͱկá asD`5+J&Jp9:nFQT;}[7?n :iN_p`I07-G~ؒ#d,ʩNIENDB`veusz-3.0.1/Documents/manual-source/_images/createdataset.png0000664000175000017500000005706213161413406023761 0ustar jssjss00000000000000PNG  IHDR.q8sRGB pHYs2vltIME/SsǦtEXtCommentCreated with GIMPW IDATxy$U}}NU}Vgq .ɓc?5c4bA8 "yɏnĨl0].T9uoݺꪾ}?oMͩ9UַNC˕jM&d9NS*q8d񠋕%mhKB<8R?hl D)5R)Wk5uV$+Z3)5Nhi5RP tԹ)Pp꽃~З'ww^'_4?N* &lq*<@^$:~ (%%J'erzZIȲ$+juy5`9T w xFs3=΅G?*?!usZs]W|:鴮V*uؚRJ)UUu|lZRu1b5HbqIqyU4;B^]b'kKP©ZV+PNhjMV+R{OMf5gZJ-MSGVB3 V+bL9NrU]"a3ޙlZjgr >i4z VZ ιռa;S J8(_b;|Z޻姞|_|/^|ET8|?#ss9S`=ظn%3Y3gbחl s.V8~sd+A=*ؽ%rV}ܽ'x_z;޹|ʾbѸ_x+tizRiϞ]s \lE_qV.^xn>NRi|r.sžbit&fќj,J%rOqb6lK:gʔs"S"QJ_oٚ5oosK}߼ ]bMgww-[B^q1zMl&6:&v0+]KGQ谗#8.&^Ĩ'O~ݿ7}С˗W=/3+V:KT=yd\M6ٽ?42)._sO3=sgɤWܴi=/pNs iNȳMv=+]}˖-S6$J(%:H惉/pq&#{UUخ]9={w\?t;Ngٮp5b*SzMT:c$ӹHy"-$iG`:LSyѹ=[/nR7\/KHJl/],l8b7|օ{˭ڋ/> %F?OO[y7={w̌˕jڋf3̤<<.Mwz~6s<2Ya.]3͉ά }{m5ٷo߆gprSevDrH}35MrÔ;vl[/ʽ]E ;y=q)lp l̳ϯY~YbLg !9̔mV}ԍI7`_'SY[ Vke#OCZ&AlAܗ8:#1ι8c{;C]lֲD](fr'C,g|K8O⨕RGDܙ0ԭ>~6ɣOSO=ed):cG{9_w}k}b-o1u7Xo ұǍL6l'Ţu뭦(lM6_ɯ޲eEs]; Wsn+܈}rxxh^xzX7\wXҸ3ٚ9Nz^e%=JGí[uogXK).U*ujbWfJtRU󅁓R2jyfeW˓66jefe'LLs+ZuzN3Vi6&SY#WkW%DBH""hj$R#d6K D,QB乊mė o Axa>ςweDi#[菴+g$LMOcub8w?5>1ADn}֭dūfjkQf ȅ9Lh?+Ω9Rlro{۷rw/Ł~\` dk*֦2je&X?:*z$fo͚N'hku͚HT*MRPsw$)Ui&IDe"ʆF3]LW\2~LRLg O4G]I$J*ͥ/?ˇ~o'>6,sGk;eY[dD]|c|Tj6={䵚$r9Ua zڮZV(dIJG:ӭiLT2;S#U _B) ឳ\n= f$RZԊ5b=$ f'aeB9cV}+7/~cox CFVsTY~c߸K7|o.shN2Z^(<˷]ˇyG?W[,% 2SD^UUVuBqy[Ojdd̬$IB֪J"0sϤS??H&L_pմl6J&C q]viqr1n0h/6Z6%V6|#V !u5*9IRzg9gL$ϚSOۛO(5mg|Dg5m:m_ qϚ۬zO-ӎtNez_'O;vۿqbl6J-e\LȶO5+//}˿`:[I*E3ϹդZI(y7ل.kuޡyIgR2 `[oÿջyO~o}οv]S)>nb=˾xZiefŊ|q[mgF_J0N5IqO}}险L&%UU$ ]$1] U:̿xv߈į^-՝m6ת3ej,r3%D"m8}>YO5mgtFn74C0ܫ145̹,tuώ?}}}֋ݼeIbLO'Đ)3O]sU{2Qڼ\069S]+_ ?u'.y 3\&Tb E4[ӼNv !_Ʒ~۷xõλ[8g>#uݵ#y<]~uyX*׾K7,LK8!qwժ{ݼiq:rpp`5\g3ٞt23NIg{!B|l$=jbsJH*E)^e5 ^L*SsCGB9Y M9熏{2cI"](A;O[&+ }G喇.E0IYwwϪUۻyYfG?WSs"m#BWR͛68o _K/+;РEu6=sLզ?3G/e}aBLT`z!_x{gǘ7r'~æ}=>cs2P-z60ӝΘ}^)O IW6i1V& (|e;o4BI9&ԌTΪKHcV#N$$9!zmxmַlDsXjŃm#9;[xA!(l0ӦN朸 7ozW˸-Wz:k2t,%:[03駟*'LG4239.3 X7~cQ??/O=6m5t]z(y`O>е7=zIW?i!-QTӔ½ "I{O=[zQT+=i}g ;Chd{h\D-?a1<͞C_Rf}֜pϚ$8)⳦7v5[/5Y7=Y >yΟY{Tun\o{Ej裿=O? F%|K&JLL'r*ɌLjR5')ϏD)lhN$"Wp̼&kTK/;U.ɧ}7)1B$3D~.^tYQ>\IуzիSɔ5֪G}/L*F=ABmOGNnJgmJT_3K7Xs]\(L<%sw)+IҏgmZzu26ΈZz葹+}c6dge7iթԜ\TG3ɤg3(ICs~499ͩ,МtRj -1؄TGH$ U5U]x+$s+}AΒ>p34(JMsIQWJrBV k*S.S" ]hT"q%2ƘK!QT{1""V&|rZ5)I eB(ag1M\b3XIRq*IrUYR8$1ќzVrND9u]u8sBs].$; 6PLdvU;6DťZ8.7@V"45v4n.XFsN啙 'pGA3#2;lT*UT(;ܠ˯L[nY̾؝bqddngn[-(' 4'.ږ$u>D2]/`cOAFM9JY״|j& Zf}DE M(IUW14Q^sYV2TU4+,-8"J|6T*eG-s[#nyڑj*s;9/xnLVSrmEjhu5^pNkpgJ$T(V Ӯ,g1yݯ%|zY5[%L½fɳ\.SӲ 2ZG#7!aNŒh$%zK&$>]|C8Huʂz9F7+]2-X,*:/=%vF9 ޻'$ԶGtpi>R}l+FNnڬ4+*i&[@+$O*16E]@xB{#ꟚT8ܳnn]|y *^enސvz dir_-K }VQD(?6<4M}8zo<7ht,{lذ|L&L>߽gnY:^a6>1.fGæ)L ,ֹBݐnlOQbNX8qLO\)55UP5٪Fh+[rEtwuw-c9~]߹'M9!$WGUkW^ysm7|FBG?B!\i] !€3+r8 z>ri?ۺBX:twuo۶r͵w! ޾}BƲ6u*RGbĹ׼ :~ccc>g׿ۋž[޲fjшcipX涠oUm_`ej{+ܵdú38nkD༱q֮ΐF,XMlkju-ht0Dnٹ /ٰe˖"I2U:0BJm wnH'rGԍoK $ _z% @יt|))Z8F2^zyƍN @kB$ ]W9g]ٳ甍#!< Z9Ӵj|Oyt)q*XYH &QdҩpMSTm6diB׵_ Lj]&KCC!Ǽ:JK$mwBEъsq7;p<7T*$ĚNg :> MQh,Q4%4ЅÞ/t)it -ƕKL6!dh^`)X8u@|BLk a" "94]UkhDK"T]+X63S.tu.Sdx IDATRD6 7[F4OY3ZB@]L6Ww(J` #֥R9V BUR|T < F]'z{Tq4[u=vfW*MS)m pN4MT*,z1mB†ǔ簬,|_׽ċ D`sZ X(=<m)QV)& *A}fZiڒuKqͧ\ל^uvH 5X5XLOjqOOql^{Rr 蚳v).j%,kX-U&8/k +77|4"kN&A#Fk,JX,kC&1{5NrE^ VOqI\-`)@uMp{}k:тJ\B%'ݸqcY>X?@ (hrV9 "p`ThLq氬m,k+=U3B!5kXX: b.N# 8sX6SވfnxFfH6S2t]4sN)UEeI XMZKrB ]YYVt]+g' 3,]T\DQUujjf`py"TUZR#$|V=qp>M$1[lTMK˖T*ˆ@Ӵr,IU/ v'^fMJC+4Mj܍Z/OJ1t 4[ժd2OZdVνS'&s?5y0Ml۶W׽ƶ--'33C*A/9SS=EUUyߵZojr4NSMɶmN5s ?F#Gedjj[Ĺ4M\ 2S=(2,kXZ2Z֌1BhZe9$oCyXd&r*`j0'jGn6x $F7DVǎI!I#GdEW-Y 1Z54Nx!<08G&6Ɗz:EojԍP:JdY. :|А랕PJP&gη|/="W#D v&󹁁}ZÇ?ׯsV h-N$CCrwʕk7nܘN91J$IryݯxeCCӃ:4ڮ!Ś:Ej.[j%^ݷoO?44J#O8ͦ׭[r\. M"U.Eܱ'w,gsUW szmYut:U,̔ժ3YRT6d2`/"UiZE) |\$XZb=geS, Śa@`T @;X^*8@p{Ҁ 5@@b UP-‰'x򉉉!R9/~⿄=X,B>ۡukנ5h#Jң /kX8E*ᥗ_Z~ݲeÙL@Q.sK/|.!&N D$:44|~hhpTfL 2,v$ya$-7Ejkpt? '!?瘊W뮹blX]D .uF M vns5a;zt̰[]Y۶8;g^i%S\݊5bS̺9{;ʲmZ]Vg/1{IhuS(5N4y'T_jۿẗ́6sP@_0z^]fH# +]۶m#\M?B7z|,kW6Z{Z|rmkl,_XuK088X8 @ԜJ644wIM`HdTSx@{Z.K&H ̀4 t&21w55@ _0r#t3f²6b kQ`M7 ugƲv0ti9*JB%I ,eB?47rJ|'+ˊk噙 L&-2SUUZH$UUVk!djphEZ9v`>ńsE.M'&&W,_GT.5M34\.K|RiRtLRWfXq7ZXֱ+JCë4]WU5NVK&+N8.M^."h ZRje9PU,"Z8Y:N)m5\E"fO*4\6Xɽ@1ՠq $ґ{LgS+WjäWz{z\GXmhsV8ܭ)_rۆDus&SK)9xf+S1׿n4ZRPHl/2ƫ*ce#V+UƸ(ҸSAnS˹4~ t=*ūjve挱J"Z0ƂJV71W}F>5kU}D%`|B(ǎ$C#GȊB(u-ѫ&Ɔb!C0w }|*Zk1r[s<4k&Uu $-_0Ė\JI:>pա!!{ aFfxwh-uZB3C[l/iM565j{=j3^K/^ԡa]EQCnniA5$XvAJ"BP_03|>50PCzŨ5DB)W׫V߸qc:3($ݻwxm5]o5^2^hնcJ ѐeW\A)={_NRjȑ'NˤSk֮^by.#֒$uww);1>1 G5MW9ͮ\l`X岔bX$&PJ\:.Jt]t*d2`2kXl-I |>3|$ 4XV6`Ch˚77బ,kwUz xh0TU96e ),kh;˺8yr',& !Ry s=׋Dc.ĺ5/=cCC׭] =Q*~c_p~oo/De=ׯ[l8,^)˹\n\F4&s4&iL;u'|~hhpTBc1Nc.Y{9* U!\...d1Iٚ }Gt,<ϯ|ߒh[u)dt]Rp"`f4Oӑ:-kg1CG{1\wڪKC/jbP|z9̛G/bbPwϲCb˲YnB9{Cjwr;lL[K ט'vY5t-4 en.bk- Jɫ?w=!ZmR#oo&|}P!hmUkښZ^CclL )r:#^㓏`\#;E/ZyֿcVwwwm۶r 7I޿~߸oߞ荹NiLgmI w#o,pSqk_{m85ûo)Z7D2tmuqtM|8:b6-7>lO?o[a3555럏 6-yLEG+Cuo"B/t1~nTJ1ò>|p>9|itJ+*cL%q- j=9Vk`J:@g9F|P odV+e֭FXn]Vڀf՜Krbxu<6^iNЂnP~o(;vL$cYg70[͛7$I:r䰢(pkL/[ m/>6u^˲|gybU\)࿈"% ЄL}(RqzfϚ6n<%NscRI/իeYr׿59Cl;G"LO( 6$ScHq|vժ^۳géTZ>|ĉtb͚+W.峮| } pI!fc J!s!{Jš5'&&Ɵ{i䲙ž\.8hq(a3jnJ%IbJGH>MR}=rZ.r*dҙLFQdBDs6$)mƲ(qCKJȅD>_11\Kt/XKp4kpsaM|^BCu{,q8ᰋ ] !u#oK*AR±9C *lre 5p q!jB8%pBaU@ 8!1b$u\1I2Nf4.%yؘX*B!h}k6C 7#R>ʲ*1dYn攒10@\< '0 FNe !8%/C)TN^0@Q*1KR@&HR/槦ZP5 c*$s4`#9 `. @ sz.d"܇>DӴrE[>}u)M̤"l%0g1VE@]\.Oww%"d4W\:\M \itMLZujSDI?A8gJETI4MT*);ŚhHvfēϔe%͜wYLbďܦ ܍pI#ŀ`|U-SnjXKK66t"|}}U묞Q8kut}6M9cېr#йW<Кx!*\7"1ͣh)b`> <.)(qȊxKwM`QUJnr zIׁZ(6Wx7XH#QU;ppu}!L8: 0j d2iss¾>TA<k>y_" ڊd`Y/!_U+'hA[*ݙg#q/3սnk_ACU2v$4mϞjeRkhAdn{+G_G;FT"{7?Y'28khnnKqƒg)^|mAe(5kXXx{ЂpX~*=@#r`!e mXGnjjm|ÌxEGsBȧ'U熑e̚c>o`o+F[hvix֛b3ɝIwԚ?7W@0жUHAkk/Cի vhO)*DuM^67b2x?oXGܧH%E#߀zD-4ҕ[Ou(~5iVxZ1ˑ̃zU{#m%n5 ѶC}HAI^kGsIۑD&޾cFX;Xbroa23ZxXc[m{VN,g ex}+P4[7~5턬#5w;w 30Iz=:z}IDATg~y%|hzu je+ 4M۳wZֳ~Vp9qb2-EVRdld:E `+(E@+-:;Sq&r6b kk\$tXZ޾U?͍Vk9.m-5){P `!1!u꠱>.[ܫ3-k/Y \o~=m! OB@s8lƻWHS`҆@Fi`_.gW@fF"ۂ1]7\O)AAV B`k vkm/Oީ{u,5ŦI}f푠'aXցl'O+zFR U+H.$=ɳxFu?D#¡tqLXE'a x7:;s/[vZțK0ȏT}O$M8Ez.X8SFֳ o;[s,k+& Կ^{"8vhq\q Ww4bq&IӺ d"ZTx6x!A:mHz8h)Rev"vѻpb 9 ֋O#cutƎg(qO)x ~'Rh#X/xa:pA봁!&t}2L"8HsK F~,P BA^9k#!N@EyM3םkEgDlK"0`z1 $rN!hdSҬ5EM,j4?{}ti<eĞA5v )(^R0IkCۍ! *C`Y/\ :@ :E^N:M|k9S):DeT%&)XJ346%>eAљO s.RX[|ʠZ%~㋎78zHNKEaQN~mf;ALgNhdT|nPnYh; ڴv@k8N$WKB@b b b kkk4A''(R9~%s:b~z#t6O=9nX{:22|L&)g}{ΦkޖL1 E L<3ux^q㌜>!f|3 B>_fџyyMVu]7Ɔ *d B늵Ϫfu%b O׽j]֪kS;akwm twu7!K?I)5B8wY/ebɉJ6 e:K #m+V.kЂLJW]9BHZˍ;o 7B> Jw5ƍ WٲZ][DY4y@׃֡{۶mk~@wg(cU-kXAw^z ᷉@koݹcll87u{طu[vXs ǛZie3EVk{]W\q]/#֝qƙv[JgpZt+nA$[v GF6lٲe-HoBZru9E;朸L_Wk&H$JB3]3ͿAhk:N\>C_|q:%sEcPkM熗wq=Wa@$Q&h)|9sVw'HjziUWVU- q^/'&'C4/eJ :W&& XcPA6$rrxBt8eT*Nb LL:A;B5@@`g 眣@X7k&,kh 5V_06g{arrJ)_BI>ۼnu1>^z熇7lDkLLL߷s~C늵M[A( \gy+x>Q+f݅c0A6h0<_0Zmmc5wYE"!>^x柕dæ+ӆ"6!L% ]gǪ5nCm3gѓ&w-kȶHdAypS$9c@6@AӸ !]Is!w|GV.dEG)ֆbڔQYuUF}-P*ұNiH.Ծ|8eR骫>GUkW^ysm7|FBG?B!_A #4uc57TH.kC@սm6B5^}? |3z*5cD[P+ 8Zn&ߺs؃>٫9po~bo֭츙Zۈ{$&A Vڝ"Nj{+ܵdú38n[I|񩿳58#xhލJ 1"˭\Wp$kHeί|']{%;Z;xM7Z4ћI{YѲg﫧zZ"p,W;h":H' 7Du$Y&C46>C]|=94˻6_b+do~6a{kYJMI&dUlb7u!œǕSuR;CXoODUΙmj En]7P cdRDbHQveqsiUCj[W"f$X]~U8HJҡccMXu[DVVC}=t*id3it$$UjɱR97\w^6IrF$pDPD37{WY`'@5Y y݄.)ǜ;ȗ]xr2n%4X+bTBjڕW^ny _я|PȷX˄twuo۶;WZy%wg(cUjCt8'] ~ccc>g׿ۋž[޲fjh_0|}| 0MRk{]{lXٰ;wR7ɲvxuXw_t Dnٹ+_ĉッC^yIj5XkHUʝqlAe/ ʤW}=_NɜX&t!7?1HZCdオZ\ZëDeၮǦ=U0FfO\MnBKJe岞3]OS:|!8H)e|.)6i]\W>v _06g{arrJ)_BI>ۼnu1>^z熇7lDkLLL_y @ǓX97/#'_+}Ɵ5b 4fĶFEߧtg>Y9X:d2B[Zò׌|նm9zUkxuתnƝwMORJg-V;"_v'c-:zV$x 5m|,q&J!V]yFΝ !ȇ |;^[fx8=<{۶mwq5^jK.yw=CoRfLm] h~ccc>g׿ۋž[޲f];/FkLĥ\?ٰ~?a%w3nײy^i35M^@>t8$ܲsW|ӉěpG)֮*p  U#8 @'יt|))͌dYj8wO9TUK$8g)|֘j %zͺgt78>~LU+&6ךV]Ԝ3Uz f3DN<(6 GӴl6J;Gt&Π555@@,$b8皦-H(lybZR1[*Mͤ>&S79Vs|.Wؿ hXONN s*iZ:,AgQ`u6>^W5M0j}}%]w>4r_>{]! Zn0UЂo':J*cw/}K \UJ˜\1ͭuXɓ'm1 U lK8:C|uv\3 tvݜEZ x;h5^dZ>W*-[ .+i\Nb[ ]0^|mkqu t.^7gmVe`T&zOcǎ|k_[reLϿu/?=uDSoAGA_.iEg9reˌy7mS >&a8FRTU $ ;Q2&[ğg?3 :Қgr~bIfb8y|q`Ʌ?R78[4Ob-ū~W@A̽f[Ζo2T`x/B,je+ 4]?37'5`&UVt]NGUUe]f꾜H$~## fLO߇v@Tg_r"c&>RB%IbJlgΉ],?W袋lJmKA c\$Jqm J"Ko5UV}C"n W)D݇SX,Z;^`^9R7H$)a =8d/l Nkz6"OMMzukWnsg/D8fP/%9#^*3^ɏ?Mk׮祗vၞnL |sB˚RJD7Jf+W K~'VB~岁ˇ4%֜Q*& SIq8+,hWw!LVL&e2iJ3+4('ĐPNc =Κ(l&I׵)%X k9 7:HT&İ0iRRk9L kY3F(%]BfYBf_02i5甆:XY L&,ܻ0CdYf9>Q)3dY͙uRJ gpJx,k27E*/ Ts޴{RqF8 F0n0/%%5'K #֔RL:ZB@55@@@b b b kkXXX 555@4M@9uVSۥdBeJi'`ЃX4wmZ@p֦餩j5ugߛi_JiN:mfL%j7b.> tp;fu:q>;Hآӑ^7Tf$kp3!"O|*^!̼NB: r:+UF{x a*2.U!aE Qp35Dt.3W0} Dt R^p-gv4X =fJv:d˲)BuP&^a ^"TUI#(RG1E`,mRe]+D`7F0sj(`2"Z D":Wz6t/0>P\jQB0>a濄3P  W\3*HaT2 fm"J"ʂR K3&QR hQ|ѼT$r.k~̛LHYX^JfNhg׿OxLD&dN60Gw2x] eWD+iJ{6D1z6$Qgm$Drm:d<[?ە{ŘgQ[M6\s39L6"*P 7y !h3":DDQoM"T^"zzi=DD(ށ- #"#-'34i|"/V%ѷ'o`x."z[nD-t}CD?AVODh~ @ ۵3LD+x"CD E[-v"JuBm0W  :$mH?e#m*;g"-k1#M~|L5<&U4#mpۡlr8@>CE#r|` $/GMpE> RHo=GZH hiҼ&сGI[^_o5H V-W! ʖt `3 e!U%"ס=`x@ uQwNo)'elѦ~^6(}MmphqR-gEY}0_r]Ya8Tq(}'" ~T&.yP='VE~U/'ܭJFU}B0^ e UDX0"xFgDW(Go?#2_ ѯE B/g7_TaE%BP]-ʉxaTώa BUx_8w(٢y~*f1=*:a4?uW(niPxI@* {?(_Uc[ctxUǨڤKK4:jl.UPċhw6hMjD~qڲ5ܭ޲u_zhG2U[`p|_YF*w;:w*?naSEcjzQߋE^߉x? h^ @*;DbEN1fT-rwAt]jB9흼+Ue*"{Qwe  a}&!q^&J "D T|S4iL^ceU7"gɪu ="~UX )UC "D#[C}H@ab` @*n*kDݲmȏI\$%_-?SJM!oDׄ_!~5L ":I]0vIE, f(i6罓S*  ?k抰yF ާ1pz R*~`-GSkdw6Ro"YL`Tz=,&a<{|8~Q̮"Rj½@~0~Gk?x"sd:Fī_5$X;y{ޡii겷=Fda|{iH[߈SEc/Go=?&ES",՘v@؏i~Pn~@] :QcWn(X]F$ \n{gzՉ@NxoC9K4߳4$x&"fM"Fm Ṫ;?TC7ĽG'}~N5$8C ''DT"'DF( 8rD^)ߟ!Xg8]ĻN'^0WޚYc4ᯩU$x&"|iV''U_l!t].~Kt!8B]](MS|v!'OO{|+f1rQk4XM Q} 2oRgy>M~LC$ZVO*¿TmwX]C}NC.7"j5<1ME?Y;1K lk4ߵXajo =[:L1 [[AqZ~P?L#?8Z*]~Ga٪o^`5Dc>C+"\(p"M1rLF`9$^cۈZ8{W!oB9$Yՙktv@gLL+s(D4B aA}``I4ҶjP"x`a'H3{ڪܬ3(0>&YPl"keU > e a;4ƁFlN?kq0Zlj Bk +_ ~"ӄQ}᝝_ {NLFa"RU X1ئ ϼ+ZD4LD 6ɹ=@?S4z?HUVm8g߆AU;oRMDuӇ4Bql 9S܆Vb'v2D*3S(lN_+HAf8ʑ £t(*iz^{DlCk1EvIsLMCX+Ɵh漶&m(9rqG0[58#vWPՊ߮ V]`\NrS"Ɠۈh8'O ȕ뀒:wqb?&f3vZ=m2~z 1OF(ͩ9\"P4ʌYxn&"".:3'fpdwJt aPU/N%fޠ"PL3/\:aP*ҙoxW'g^U:wPFΓx B/Vۯn?"NЄBD럘3 ehݒUOQFZ `F3tzx OO}f(gZqΝӠ8#c {!CDX;תp'K⪨?kf r Hp U\R}6fߨ"[`=H' ;lÍ1r惢ߙsd+^Q=2 ن$ F DtBy!G C 1}DNkr` xif"נxԱN (U^J$(;bۜ7n>s+` r]geKŰJTt~DSo;9TiBYYj2a\5BUv[(~0Uř^7yf/B0] r GĊ XV]?tÀrx a5>`1h5 W!>+H]e)cƈliB4s- X{꽒DA1r%J|UWk h0K <*^*Z'J?9 UHBn "|!T^V e *MP;։^(kCZ5XJ{C{Zo P~&}*`O.{D_.(N.U7Ti~0 21= a)/h |3Z:[o2IsUXm^޻Ux->W!~B{݆Kyn>Eo5^?/p]Cgx"([ w8Uqzf.^W1~<w)͝hFj7yfQ^^Py@3cjX{=Ҥm[*$MWwE:UH{WHf(?6N޶6U-hx=Z#^8_U&kE>F3C\E`(Rѿ7I h * ?2(1աX͝N(;WU^5ޡs4W!~c RrkEXB_CQ-G6:kYt jNjr^x _D|8b[(~F%/3>M%Ĭi0&`RUZgPuxR+4~.:6Y~tP.—zWׄ '3gE<ÀeS"~[օy*mkP\ RJPzWz+uY-(7h0n~Qׇ:3-ŪV:->fbb;\BDDy ŹF&'9/!f^CD#E~bBl7ZA 1kwQ|)ȡwKn>bP^x}VCz[; L* eI3ss e2D4Ex_)-v5+zX%cJ#DZ07:i};{.u0 ."E{;AAwq#0'Hw1Y=16iE=)kN@ܠs7OfRn)Ƹb !vU'+v)tep^ȻA=f3uɎ2!;z&V^! 띢sz]c"*LDD]S_mhQ?{>ua觿FZvN;Q_unEM "|Fkx ט7"w!?5<-]KĎ+$@x1slAb&ABBBBBBBBBBBBBC $XBBBBBBBBBBBBBB` "$RKH7G GSǡ%؆(^jBB`Ό$ !!1^%$$}x 6AP$ I%:301EщBI%$BYPmP~q$̼rRr%XBB! ;1f I%pTDHmHHH,!a(j$X+;S$$:^$ !a D R-+Kt,P@DR, (=R-+Kt 0sR'HmHHH,!!ېD\ $XKA G(ڐ ]]BV I% ` c` ː+`'3*$$ *(0z, @D1v  <,!IDW[`ԆDC`IH6$!a7@$X B lC]r%XB`. y.XB"ADIrڐ$XBFȕ` I%y ڐZRAD һr%XB`. ,!ݐ+Xa\)U!!tSjCB`y.XBB` ن$$r,U!!IDW& %$$/! h yXB` f e+LԆDtdBْr"IB-K,+Uq/CRpL $'%f±keΜ9={㖊X$#h"/O=5_#K-%q}tp1tūH0%6c 7v٧HD8L}t{/IpW_$#w8bbltlc c9;yN6)¯H6IB6 2KgsC;Y+KnOKIV$^BQBmbw6l*~Z!C#>>l^pHeK*n/XlgKQI, IDATV ȡW1:d{ʁmdAyFXVJΖ"*K"Lp"!! qnv讀czgΰ`8(-]N 9tL N*Տ l!!H!>0 *Vp B['w6-U(+@nn=$veDsc 1$+ 9) 憈 H))5j$-['m ˉC&!lx_Hl ,.*FaaA8WlKۃsPFd8=5+ a1UEm(mSOEVhWkkP_;L:!#6rG퓝r6"11Yn r([#I& 1u6r455b_quu5o@bb"RRR#5%U w)"NKHmۆD3aYfaϭA!/}s2/?*LZ;#L%E858evMhnF D!moO-CEErK[Ό.#v(E.A3t t#NAMM>t>ƍ76Iw#z>yD\7&ۋ_~Y͛7 jkx@DHNNBFF7 C߾ҹVrE]ZX֥(d p:w6ؐ`]$8 UPHq;a!OUWWv裇FJl4JXT@D+++Spp f$]%rs ~Y+Q>$6{ 3{Q\6 }߿ݻw@Ŗr?='N +;5ص7:,Y y8rPE 12Ϝ$v>UT\‚SvJ"!vApHd27\ iFrǃ N*SR\ѣFAXyDS`[55LlBy`E N#ᖝ% P3h6mŮ]x<@l0mX1`0\3ݴbbbp `b\pBѪش 't2 Fvv62|p:薞^z\ҹxr^ 6"ApIq N FtG$€Uad_¬z4esoIqۧ䃡_$M\6ӪUE(Sfk W]3ڋ |NFBL#ŇhK _ A\ b.46z0]{M4 ,M_6s|x7#(UՖp3rTJ|&L/px@\ j3l 8p0Qomijr3Ρ wF`۪)yX}=aWZy Po%}Y7Bi#2FM7 .ѪJVK?eGSVVIJLMT`VF998X{Xld#i/] Ǎ:[O_nE^ޱ;0k[>ؓx'~jӴKB|-|Co9Sg/1-5Dl۞Dk_޽. G 캢mV5u`9>lR`Imk*+nA{rС66m1`@6ldi{q]⥗^_;Ͳ)wu;|a03n6|||>֣;i% 뗈AtnD|\*M1`mS&O‚ n຺z$ q 11Ng3\.7:,[Kaz7Vd\0M){ɰ'd!Ƥ$ ANJDRJ&tI]#vEXoX/ =B |!1Xei SEB'bRDsSMmY%ȦMvYJ80Yr% t0תm jh@?6jގ6obō9j: NBu!$lw`̘q睷kgD}≧OcիgpY'muZ/*N:i yY&e^k\,^#/^W\1%Y` @Áӓ1kV"|Ess ;?szp鍆;I&b"!{<>ښZ~P_7x ..P~ZRGD(VZBͼzN]w|LՒ ֋odS\=k~e]2%Ĭ!&nYY|)**AaAclPp{ #Qz FX#2vD}($АEEE(,,lZVa;io3Ş:=$;`{x$0}l~};nC=xO'݅=3d#륭Nƿ_M^VpfU8Xx N+fN;ȕ."&&8gcϡXHw.nK!4ԓ97NDsc#b} Pb"?GgBs H[}$ԬS)XihW"Vuz;t0rl9 ˟[KkW` fj1&FL0|[; 466G F#3=Equ3lknɃ{c;tJdB`Un466NVHͼ3VHӧ+GƲf=>k̓:"g\ЙHYYrssUCjеal;̈q --Ng<+?/ŏK{Œ^c+޾cxiy͝8;l#w(zyOǏ?.)w?`q,sI%Za8\>}'5 =]QVn@z\*z6f49Aúwرskvi||3AZZ:* DͤbDؠAf w;1J7Lavh3z&92;VCZ2&x56lzuϴm :`;-EZQlbKKq㐜ZX47G`۵$ắmLD#xmñq9gaÞwdfe66򬫯@*+0|l`6^}UKгgf]}=8lx<ߡ婧żŽ܁3.?sy̞u% Ipbw.?Mݝ8<% NcԻqˑ@%ˋ=nL<AŖ >ҰvBZz:؇$w oJX}$f zMlAt覥ݛV8Y~&ZV9|>wKw]%SZdUHNd uHJjy>I__̺[9g"m_qf߳Ϫ0Zxe՚S@ +/A~(47ִ@c-lЭ%9ȮdQ;^cE"iftFFF7?gm"1.d0wI'$b6NS40w&r[e`[;${-׵vM8cn*/e:U[ë;vbS➻oq㪫fcpgg&egy&XM(Uu{׀xu,ݹ}{!+Bpp{Gz v k׮x{3O| R, mVNZuĥS]n-9yoI%4AOG,c1 ctTи<~,z꣚A뜱\+ihv zN@2lqѪ")4pИzW= MPc)Z?j:aqGSĖ-4r].J6rZj*Ǝ;ޯ9vac7p&Qa;!- d L0[▛o1klsfߐm֑sAۗm rLI#Xaom " [kӞ={p≓QgKm( \D=pĂفUiu6r87"!{BR-ъ,TTUՈkapn!6 LOJѣP[Wc՗`a߯?Z;inj}{~8nD"1!nW,WnFdۀK#?-9Cic=b"".gB!{sg}.8pDa 0sRQTJ)2KK?ɉ ŦU/n٦LmF65-<8y=N@p8lÙvG֋E W;?o87t %aXCu;syuu;;,Y;~AzzpXdefHb&Ȱ9I}#|wxf矄.̌>S1QO{ʧsMp޵81AژN8%h;kL >_xvpϣDbδ;^J//>q8]1 )ޡppPp#q [0;ѳWOOtHJJBBB"bcc  % Cd:J\VdU@&'hTE&24̃3`oOcIqkIٚDf4 C m`\4'Oɭ֭Cnn$d3ۏJkFڲr zF_#p9ሉ δ;^Zp!lFyr0c Mn7l\TTˆ~﫛f{^7ȯ[0~X_D9iwdH,va8sv;v;b:6*)ȰBllu84B\\\YCϑm$ zĘlb [l&ʑ"-EE8mIhRx9v2kI+~\1e<87 `5i#wd;Kň hnvt6k8}V t ^<3~X|w8ԓ4 9ܖB@yUfݱxlv.>#JHX@uu5뇪*8v*8L ֭p)`~becr(:de{ `@9}6!6ׇ yV:J]M> I`Ap(//HD(({ErLax˖ /o$#+(lM++bԨqUadq{m-aQ 6lXATkDC;\rzS"b&8u,5s4?kU 7wP[ 61 HAKD 'NHet92gW'n 0Hʆ ѷO_Z6",x֖mM)@-fQoźSFgUTTYaBll,ɴeFO"ҌC Y @XW,1{k?Z*@-EӐo18^o6y_Fqq &j1a6RU`nE|6Ao3ٕ*NQޟ؛={lnF~ø,G@pr;?.U!U 'gɅsIz% r%fRҲ|Կ)' C~#=ymU^3 +p̈́{S|̠5~xgS3u^rJfي )$8:ʋ(z[tdRXxVrd kЏ j$ڢbX,0J_00 # c3I%7Ϩ}{ѿ?CC\N>$I Íb `=yaF^;u'arS]m%j+Q/>fp$CìT\RBC)Go"a%fI}u4vDH3E(,(h0|$.K"HzMfL֭Я_?8!%(N#_XşC*TQ*AFCFo-/ߢC/!I$L'JEHHblxMMM "$%%s+f ]#88-[g nq[_c-8`} P##*1n\@_\@2Ɓ^z 55% ݺ RSЫ]; AlltFD#&6" e+GItmGbbRSS2$:*3!@۸q/Dfill6mm 5$?8rՌX?~,,Xs_#z<.~&DQ[`<8tݺ|Сjdffb MU0>>!Šhn'#Km߃ofD=tٷo?һaժո+K/@՟|sn|N;z?ۇnPj%1#aKfp)o߾83pB|HJJرcѫ`͚u:(%o-ɠnS裏"7Xt9f;iHLLDx㍷[!7''z$Wlեصk'@}!?/gq <C dV5>b bcs ..Ś5u6TU).;; G #{p6v{Lk$5.>!33G ѣG!SRoSpmڵkp6G&&&#%%Mžҟ0p@du?O>(rfTp""B\|2kobɒxf͚N: v퐉bȶ)ɮ(9rc$ I6 :[Mcq7bV0֭ؖڌ[n}cz/X;bbt9|Q~~>Rl7cbc'XQqm wq2=Ȁoݶ#۷ć'466b9o6\.W @h$866>(vXNQVwng8n{\qTl FƯqx1vXLv  "P}'ߦm;P8=Q^QqO#v<Yٖ䋋OµᅬlS6m뮻 .DbBfN[vvڅ /8nס%8cq7Ǐ ㏸'O ef_^zF||<وEOÂDZZC*RPz nf,Z}rCHcm~3gL}#F R[U[N'amxQVV d#Am7~y~} z$.7Φf[ T 6m*k#Þ{a&އ8ɠȁ'|̌^z_9i+/@vV232d߻ c I\GL,@q'#a@!Uo[n7 ޹hjPGհ"K*+y` I%:[[McąW79q S߸oعvɓ&`u'/|x657ct6xr6@8I\㔄ɓZ]CC.[Cb -[ ÁXI'MFYY9x]D)d56.*^{5̛7/`5k`ܹhjv"!B[1"ySp-GVTc嚃Dw 3?5 3g@LL .47ֆ\_ׄA Eؼ;~^W~nzE|0sLCfIذafΜ[oհ*␞_xgq^x~sÍqERrTjkk1atM+W hS2*6p:5z}8jkkq!p -- Át?~})ȁTxd\:u שeU`R2d;j w߃;͍`X_0!!!qĀ8唩HK~a&4;81ؼ?(i8?Ҥ?֬Y4yDhMMm2]͘={6{9W\_/"2Muuojy"<[88 NoÁX8jHo㪻,fzT;f\~ݎ.Ae>TT_\t޵hljŢE1p6غ(WܨDE,!!ID FC{??ޚm2d0\nl Zfسg7n3_?o)#9V1c`ƌh?Vž={}sq% Aq̰cr"^{ \r ;＀q-ZO>P0"p6++9MM+:AW\jF.uk<^b \uULOs]r A|Ijyq]^^J3&SLۢzDϿ@||PSSݺu@ll,: } ,&b_}˟>Gxv*+159Pp <8?ql۾/ jf 4_'֬Ya&!--0?|8pӧOCSӲk2LM܀| vXd F[owqbcaߒR#V|4 n'nki"=Gs4éNEsS ӕx0i3n^cQSSϿz;ێf=W_{hjA{ <Wh@Yy9.<NTAׄzKiq>y.~-V(e 5ΤÂE(= TڳW^y%*pDEnoh@s1׭X|9.Ӊw}\r \ڈLdff{m~;gBP#jV̞=CNժU5k_7-- w9 ˅gn>w/~5Ì3pWs$$&MMMp:HLLDRRwJJ 0e'CyGQuawoz!4H)" Ҥ  JGzoR)' (i(Nz:$$!e><3sg̝{=t-7v~{o?7:ϩÆM?2u4ZN@벐GM6g:G{n8~[nGKf&b;{<.\z'ePg$IV & +m۽dbʔ)PF%YEv4BCtp d=>C|8kpYLbhxk?2#!-u,}Zg)ܹzuZO*47bL1ҥ*2eb +VN뇣7o.RqhU鳖A툌g+˚`/dM'hÃ;hٿoe%Gr)Qes>q<>!B\\BQaWQi]AaѢEP(Dl1sP*իWSjUJ|a׮]$&&Һu맚gݽܽ_/M дy $I"4  kˋweik$I\ Lwf׮] 緋 fmr]hJr|/~wW_} l2ztl})T2CRRC IQJ j5DT$+֭(dgZ-nnn으999R*mqyRL6.s$}w|f\6+Jyi;MUV*TgYtJׯu.^ggL ԓy.Ԩ ?oF)P0[*e2(TDx4I eYٔDN5k7eʕ2v(.]HƍСs!;&cfK\L Fʓ&?>!Р55ydel gұݯo1VBAtL<̦{ױ|\Xo1Wf}a3{/z3i"~Hvoȑ9xLQ?;Q2} IW^'00P /S`!f!)!D^M=i?K#SSި:vk:ޝX,V=vaDE'g$6ބNyiv֨QT9rN$oxbKQ</ߜů|~*4 `gPkU^ Zf =U7ߢ[jMZ?PL#X%ՂJbdʔ),Z0Ji6 Nc͚5*U[FR&sxP1Yj؎tT}Yn;kugK&UCʞs,Jqw{aԫ?;7b@q+SCFm8ߢywG*!_P(e)%Cre`}NM0Äˤ{( d*yϛxKPH[N>}c14f4WXѣ\z|jtt4?3[lSD #qa9w7EX<./W1YIgb6hdUd-ZJh4X`+Vb՜tz*?ooo-ZD'ҥCd5>d( *YΝ A[FRD₫k*"g>=KlB+hnۮuqoAZSӤO%j bQ]ܸO,FP(…K:8*~Aٳp\\ӝP|2l2\t9C'kaxR#Rgl3hժ%5kȑ#z&͚1aXv^z%;*U21>:vYHRݍ6S՚7 .;b9*!L:t9}Y,kNsYiJP,j2}gƤyNn]ӛÇ6X-O/_-Y@^S$hRT*%{Ga\J 3 h)\[Go.~xC~ q=9`r*Hb($q; yDDD>U˶}9f7GblJ#"ͨTJV}QT3*>ŠIw}Gƍy 6IQ(D6lLɏ-9t܅:tR[6ǻYC[W.xj5m2;q6*J\\\_8p ޞ,y9 IDATJkMozDEEa4>9>_Ec絗'Nѷ>2o8 .JJEpww'$$VC8If֭)MOTz>[eU/Di; ?/mF]2%ʫB`2R6TrY<<=  ONݺY"JU2fS"ӧO^z,_;~~,f;og >|J*1tPƌZcsZ^{oV$$Ń#͡%`ёU,sUlňz@ַhuO]) ,, O@Ξݿlb뎵y7z{nqh:f::}ʟ _>nݺeÙߘ ؼI|O @₌( =Cx) !_`'+$X$))ϑ$)Eȓxt*HXsvn:w'r3@T 8~ ?g EXʹ}ŋ߸[ڵ[N?ogo]jbb"^C͚ΫRoNI{.ez}DDA\\\jVk+IoڶkB<5Vq?g0Yy11cHàpϗ'z J*Ãk׮Ѿ(|9=ޞ k߿?]v%&&&C"tɗٖvژf4Z c 2ɖ;( &?=_ǐxˁC~gq|c^ 2r*VVˁHJJ5f 'OP Q|?損f foƢ(qi qqz+2F'%%r^^ kׯ斦}۷(_===v˚0 !‘QԬY-[2df4k2S6M6eǎ|h!#GI?M7:u;{߯iӦvZ-ZDF HJ}bGz;vs? ANm lG*39V1 UC(^RZͨTjZ4yk/qԨVQf3sHjՉiԲ,N.L+VkX̨Ybqsw_~,\Q[(m۶fO> 1ehvI„B$3`- Eg n@3} t+ Z.䧈[;.űۙ/6Xu6ձQgT*>2Ɍ>‘gro}6Goᗓ+[*U*٣7h٢+ֳ8ŅiHpOƎS~ڽ搒pdƍ0r?S|Eb!V\=BNkF(Q sE6.[HϿȖ|OFaԺu~X(K:y7={68Z;7; 7_˿zfՕZCvfӷdPk,=_(U$Xbcѽ ;w/+Bpv.>|FމgM oeHls CJPmdK˭[avyFU*DFFM.>>!@eɂ&_ EX-ʡwg]G T38o23uTvVѥk7 ),0mb\ I&̚5O}rˣgٟg@TiH]|>>n2rI&]8J|tJҙ H V% .>_y."**+W/`4.̌)R؟Sp1<`$D9sڵ#Fs3,_.&_>/5kƌS_}yHի[L_LtRu^vr@~ˀ@=``H+j@smWBB)rSdr{djV^P"<}ʦ1zG&HV) +o9n_ǙF*:B߼.Tz+2d@l6P|Y:t5k?o.gz"]ʕ>b:wIkϕR*%ɥe:T06eV=8hD\,*?* VNY-&5jV|Ku:j̠Y7C0}R'>!`2[S'?Ӝ%0` !!cNRLY̙#`\#ɐI !D1;+I#/J@!DIRmNj$IZd?Tq x$IS<8E#9ʗ%SSӰ 4 MT=ą:w'2F*(蚺qq~9O~aR9P"V899+ZhI/?5m

        рNcӦ &&=wrʼ|TqIC5&o @ pL C*\=+Wmoh4Z* S;;wo245K=-ٻ6w·C=ΐkVH 6_L>ڏ~5ìefٻzfU$<=={^1gg'ʗ/60)ܒ$/o\\c-# r!'n:6m={\D4'G} $)1goaDƯœ@8QvѢCa6iڴ G Jz-xm7#vmh1 T$1$jڔ7')!;y`bϙ2e >@F ߆bV-|̐!]V@`}ش)1X}lS_VA6o|8cɒ%l}xz &PbE͛L63k,4 UJR`Aw tͫ9w_/[Jnj5SJU~a[a5K$U~k# Jڢe>a$qb UWj5纼Z ?r4"Ed)VJ+%Ng '2d<7K޽Yp!sfd] _~oql\N2=%J=H56lYٌ5M_FQ)J(O?0]uoWJ @?sx>I2e Zb>>!=2c )S J%c'ݺDRP| SЃ}ͥKjhYFZh<IE9oѫݨ^܈)ޕ(ފYfXT/+bRrc{/_6[>+bd1,TX.]rBs"Cf0=z4NNNۻ'l޼߻ ǎF+iqY>4b^5. ,㡣Thׅmؒ6dL䭷ZӫwO}3Nr~:u+fSvdQ|н1Z*a՚ԭ݂A3xZT^ߟVYqsѮm\yG^ƋKS,2 ~a(j4i !]=*l}pH9,g7<2'ӮA]3,C $N ZٹwbTZ,+PBvM-?Dp1˫RAw:+_| rw"l V|BqFz=899a0<.{7ofŊ#AѢE9r _[ТD:uO:KP(F  xfG`0rlG@/C^ aYo[וNHcPAce5oJ"2jZMɒ\x%.\|{ze#${{StteΩ g&>O gpEZ5oJfMt -FR1uT@ݻ@ q۷Kf0vRJpe>tLoHa|ԧ7 &o٢Ӝ<^`P""ݻf,Y;T9!1(Y- 2P9</}ã9JL?J% ~i@++i+dդy©!)^2}$_Nӵ[o$ -Ⱥ;s*&"P밞;Ë>|TlBUuH[$J4u*BNb8%ᔛk ʔpX"Y-J݅P?EY^tŊN[d(% U?~˗l2z=QQQᵢD6mbǎ,_*IIj92$Ofk yPǍ%f fLtއ+WRTInV]{ H2l1g̤zjTh_){^cC:JWO>SZùh_F6&ϗ tp36={tdZNĻPk2#2ϐ!CXx1ǎE7ύ;-3>ɐA8p jm~:V3ΞO-ѤYN$+h&NC5j4_|9 wwO+b۱J:tiiݺ Lڭ3b93f;v 1iՃˡ)UJ2 ~ €t6_#1~Ö́$]MNq@=!$H x^j$+/$q\b@z9#nr%>]v})L/CxݜuN3{fZƉQ#Jf]&#(nnnX°{3כ"\Ϋ7رcsĈ!=rW^upl5kIȗ>ݻСi:vu$z$߈ʗúuoprrl6R<6mÆ cΝTRTi,K=9{?b ̛?E6t@xt///֭]E&әe[Fʗ+ر=s>S.U+H8㯧#f,cʔ,QCRWB~jє{QkdJ~8ڶ`B # [5g׮O ɈV`Zpr3rHl֬ f݋߭aԘL4]EF1Qǁ*V3By/II1T\_>|0T*Ԯ]g' 9sf3dP  8 ]i=5j4SN E+j\2 ~2$!63@ k!D[vXN~z M%I)P %rmFTB((hWf8Wq$S>G$^/񑹖Ãrl]<-G9ˏ+SؿٛP*XBa:c[\MݻD߾hĩ ?}3gfzO^OժU0&侼vh6mӿ!\IH`/51o߾lْ &PB:v븓FJ9̞>݃ `ٸn~?Tqj4Qgeϸ~+Q_ޛ w5]@Jllz:tb޾ Ij՜~05F8eܿ?:ƫ5$Pk4$ѲES>x JŒ2d!}yfX|9*DV^Mn]XZ|}yݺuݻ&ItZ;.ߣȿ?[|v{=J*bBP!C&/B|JvX!I!D`p]q(M|pOA !6yaE#wc$Ѷpzxq&dPpaBB.P̜nԨ>~Th4jrZBJ$!rtX^ÐhU YRHܲFwy#pG}u@Y[KdHβ ];z&iMk (\!UǁR $0dBB.ԫW&RhfK$d >uxyy: $ 1,+0l<S @ѻiR]sJ72eg߬4+$AR];;??oކoQK":: .r36ڶJ&95{LxϗK.;L& |5lݺMI7Sb y5 uIZ0g`"IX/^HttS?M>)W:ڄK`l2  F> h"rfȐIv#?Ia0~$Iq$-B*I2lb4v!Dil׵4_DJ Jn#$ۄ/#?z%.hL`ە虯*UTs0^O||<{5W3%;>.'>l20~`Lr9/k(ͬc6etQt&/ ))I"Yr:jlO?AIF =NXs' Ƹ<?|kROR{ !N% WWW0g[;ZOL&]4CωIIؾKҸQR\]]=lZlφn|(솳I0pR ¢hڬ ߝLAUNԾ!2II7oJX'ه h:4hGe٣+ rHww7T*%OK!228ːiES7 ]f͚%O8M8QF( ^xX8cv0n])V,'$15kV3lpfO KT3rիeGK !;Kxa&Y5V\g#>#.1k]]ݘ1}f$̆=KcIH+h5Z_7~N; SjZ$ 01fջp&J@-eimpVM=]ukہ'-x080^ZǚӣG9R(|7n;LGvȑ[{ƴiӲUoCCPF]ԮE`?w<kNzv$J j7ob۶m?q[P()TUҼys)%.1Q*"9},׮ 2F<=<(V,`̓JV ˕ BpPj1}fׇ͚T^M&3CC)]*:C9b ZǏ믿&::_X #[غm'k_˚Vș!㥃S&U `H`TiP8yLyB@tph4'8<֜8 Ib4?S/Y-Dx=i$AZ'>HRcu:NZLƄo3۷d .ζFSRQd0kעUfԩZʳRAP%|4af13 RL-eOjb1[W$Vkۦ@FM&'Oqx)\"E ?ZҠRiQu0!#;B"""8a2d,%ɦ-2dȐ!_ĵk)OLJə/CFqAT v g)9 9KdȐ!C  xLVx||北!#z䌐B%g_ 2d3&gg'ZɐU|9ŋuFLe8B/eRےȐ!Ct:}m2d>d 2 X|-R0\WGd,C 2dȐ!#/!BːI<=v8[nBte L&2dȐ!C 2r+$XLe<;r oG$&ɾn GoZ&9seȐ!C 2d</_ !`/Av!ѾQB)^,ŋQ[pgS 2dȐ!C+ԯ_O/58B)@s$I7v$Il$I7^,b>+^Fh\Jdd+H?"E 2dȐHJJBb$ː]o،3Pje93dprzn5~@E@EiŤ!#x!H$IqVAi:DN>^ɕ6R|)~\+DEEGbkۆwi'2dȐ!#8 S+s2GkeBs&We:WFQB''$$z*W\ŧ@Ȑ!C ˕Vɔ$g Q!pww3CL5$I B3:/K{ !t!ޓΒ$Dn! H"||8i˕|r-nb0QT*YYYYJOjrxl@uF7Z- qHVMZFTȸ&,f3 JRB]+وj~&2TPgLZj=ᨆ1 7 2^|,lVI4&$Iz3 y< $!\-uI`*,!fPx[D!6l5XIVH[X㲛&6Q$%Ӭ/"(IR10 a.`$I{!xiVRǎ5cNx4nܹK;n-I۶D`J5+ͬSݫ{۶m'5jT#| C>2wds.'N}x.NNNx]Ç`En%" $dԭ]۵α:;Nc׮ٺfL> !G۷N`6O\]xٔSҠ:zm۶q nHTR-Zlt@^Ama֭8q[wBP/UTyY!*Ko0ψs...s2 v,@{`Lo"$Ieet׎oX!Y`8ހk5BZ$h~ x؂ml&@y;~4ӬӬ[9'5͢crtd ,?{<I~߱ #G egC$N8E*=g>;LH4%qn}+T%Xlc .Fh4]5N:Q#;dY[zi}!*W*7Jbꔩ|,oMb1իWMfsi-zN:͏7RrEey 2{T%)O4B:7N:ðC ϏO+ll߿ξ;8p gQ| IH)ÐamA O \|l];0` 3*Cf`Z!CHNǎ0|0~yc`JFj{ !$YI&Iծy,KIk'oǛ7ePkcd/B-iGBsi* $mXqm's$IVc'%IaG{@t$B@u!D$IמǏ+iVVͦU*YFIxCw,irW\tO [ 0jvlۘ>KL$>>??R* h61u}ݿK0A`kK#ٺp7{߯;7,ɞÇȑOZulڴʕ+Pp,OgMZ&NbiTj+kތȏQ:Ga$%lF1c0jd2^uFѠz->mB(uEb~ RtEE21݋E8ϩ\W5zJ5HVLƤtͿzWjz#Gݼdzʉ)ch5l AҚ4&И4&$9lEch ho3{>)'7= g)}xCn 1s_'hmכ[ݵɶ>6 >t8gWONK ~h6m̾_CRb4z"̻}BGغm'ϘNkhntޝ-;~)Z:ITo1w,߸Ʋ%KIJʑF ]wgkd3io\gWKҕ+ N&>~OMnXaIGhPy{Б~%_+W;ٷ?Eaذ%$RbyLj5ZI~!$x7`xDTl' )LUO:Φ!U쿧I{2~ om!DzZ$iCn@O(6 }.S "]&@Ro،v;o g_$81p1s$߯%2oF$>850v<{̙Y/Yuf ǎ X-R&ˎ>VZ3ŋN`MS$EI$Z1KX @@@-!y?X`<ӥ}";VEQ"LBf.qqO|M:ܔJmazKrP՜:u_tj74z5_wl+J:~w.fzHFn0HLJ3ުZfYjƬ'Oh@dmY!`y1E ?UM?o%n,k|P`E$ ˡWUuƍ[غm'4.WW:u|%K0yܸqRK;ku6֝<>OZz"\PzV6?A da$%\?yN< :g'#IB\x4* fUhJVt~VXh}0:c *^mUS4m4!6S述)(cS|iM9CGm,/cv“7jI„5.@[lT!ď@7I3 [a3J7 y JZG$;ٻ^=2ml6kñqqq̍Dd7}vʵ5"΢\dͪ/N?̙=&$ufw!HN̻תj*֮LڞmK6=#e?% h:u m)n_j+Q3 naWK8?Qn+[&OAdK1J|h+$ Zg%.J\ ̠)̣3I"(8]]扴hDɝxyX$RJ釱_>zBPdp_qe2_M&<,y[/r|_268;IO=@^mV/C& )S?`ie\8]njh>:MeKZGz<d1㳡}NTz)ŋ Eb.]reKTR%K,d0`Vi9y ӧMz;y1 F{vQ0qDZjErL!C9gjL6VZS"/Yw抓eM0khz'ex{ xƿ'`jr,4tZbM4亞5zNjc۶m?~re =@Km6nHd|!xH@5; > IYM&ILs 9642HRi Q<`6֟`s v6Ӭ)_jY)IpFi7><#:&6$9YpYn\2{l,胇3&L (I@P /2L ]|zV4F6mң߳|P b#Aycsf~vާ6 ɘӨUCϓ2,3$c-DE`0ZШ)`6**.(L#z=hcYYƉ5 <$rY?J|ڠ]~STQ&@3'Nر7|_+As9fVR ĭk! ^R?{O9VjF׈v6JVWE$x'@o fL_G]lތz$1!2[`+S Q"8(eKseʖ-o:3t*5 H) cfĤ%彩ПF g-$fk >JMjB6m[h ݻ}{X"cѷLXܳτB܉}w`- }S(>O3laVJ5=fl ?$u~4ZUH7MI%I,B`d]y/6Ӭhtg ߬`J֭[ILLb`6;w|߳Ws)@;bA ~;(E DOAQDlD@"%qɑJ.Ɂy8.;;7;;e?;<3٬_IB[tS&?ӪSWޜrܥ :1tȠ *uz'wA~_iDmD4w51y/^Z *zaƌO{7QB nr]IɃ`-<7.'MFh2,yIo1lL*#,z7Ety!9Sh𛭬ޠ?/"x# "Hff&ӧOgȑdffvH=Y:$ ibfMZ8y\&©e;˜;;wF6\TUkÙfvƁ9IN<&Í}zul܌vfĉ$:*\nW~C+RUUej<4*B>] G ըUt5%n$i;y,r.zJV3'T!^ _}ߙEL\U7c>NǎiӪ .Wxi³eoѼsEG3~ ~֯l\̠,!7sǓۇr^71\'i3>Ӆ3FIQ(=N'WzCkp<UeiykuF.; | 7rXx1fDEz|\xI!V<)mB"XPWbb0IQUQy6-޳W`Y8`O\flb65kuVn;`M/+^3wAS>8<(hJ&1羜=qLͲ?0d 4Zvgqj%$4H3(h J@ o>|:icԳxqlI>?N'O 9̜ȧoeqU +W^v a`Rzv;V""x$/<\Þ$$ӟ>o>9{?2b᧯}rtTFB58HjqEXeĿԩSmݴB޽0-%Q::ӧU~G(ၹT\Fdp~'.>jVXAT.ݑDoBՉAԬΊ+x>83]MУϓ;{W;$OA6)ܗ{z!n7dff+i`v ,:)]7 +GL !MB@9ˁAN%_X /Ӏ/;kOu,4Jqhg٣x&.`7W?-xg)QB@K<$/LfU.Ce"]-(5 ԫ[3)g8ra322d2g?9YתU=+S#ޝ4nȍ79i~bnw0nW\vp{kS+_ctk޻et /L 9 /SFPf'/ѵsg>(*&И@ zVp:mƠJQ IDAT*FB@)PAoh,zpDKX57 wzsDFV Ch8]EKW_MNN!!!HN}!z.n)gA*9{5\m?.rٻCWӷ/dgˤquvL%x#gj3f |^Lݺ 2cǎ%((Tƍ0yzGhxM{|o2fδt/iƣyox>z~ƨ~5 ) o~{DٯR(k׶$шl6Ӫes8߶MemD6]#k5 a֭<>>iٶm5S~0{֊OkU>۶2 KU5؏&8v[u؋Kc=ӃJ -]gD||< ,`ȐRK3%QbK-TU-n8L!Ẃϧ =pWm?"8PSBkOw S`?p~xa%hg98u222Xg{~"'N?$""Wq:U ӪEs?;G͏Rf]RӨy'VOn᧯ތW{7aFWeKa_Yɏ[Ncih .o6ʈۘ &ϾEDh_eTAMY7? II'i> 7p2P|NS'4֦¯ݖ]<{J4 3~[N;(fգOY_`?\z>i x7crJ3 dr."˥?Rc([Gm*$NqLLUx 3 ! ܢ.;_P/z\cUU%CESkNpQs ʌ_LK?\.|EcCnβoK3?Cګ߱c7/W&䗴կ_fDҥS3k&d~[ yýc"+voJfMyaE*B?0/}|z4 gGD]{^6xmSؖ !D 槑"m14D Xn. Ң(*6ՍͬⰀ"и~/TE` 1b PցzGw1C3fhRN=غu˹&no#Y1⮳7[z SطKc. *+`, MRpt؞pÔ8 6w9-̝xr*5EEpehޔoõFqd߄tat_|>Ν:5.< a$edKs 2sU. &2sM+M7mք5Vyn&4iħ}?!Po9pUv;-q4gx1 @6z+e…}z+K֭ٽ{CnC")t\d +Fq3!oYt+Ԓ;ztHVS:5믿ݷ{nMԩS1L ,@ h/ RUI Ig@/ tW+ PQЕ"2++u6r f5tk&&'"XFSzQ֜€NΏ 3d% xIش;k\#)Sw3`@ -M2{Kv3>&z֜+tNh0cڿ*BSAuP^ZMB-zxWgu DƳSynL.Z;x|rk&<:>Ĭ٤e*̔e@\׷V/6ƏR+M:nNWYl][ϊ]KYqfn`pjj2ٹg^>mzO:>=zӸ ^ǘ?IZxzD5hG.LOcv(ny݇]j^Fã'h Jygs0qNl<ģZVݨ9?Ý0k{^{/Yݻ VbĐu$`4yYOǯ`t^o\:$($'fq`n= :{?pq p:!:)$wdlβQv@;w:{vu"^{RnٲS&.ɕKXEp~g4^OxeWU5CCw)C׫WfEme)N~)޼#s1u*OOP$iqP"/:hB̒o,S_*hq2?,M6~4,m p!ju|=[գjTA2~nBAb4 B2X-.TtzA`]@&aMV}]xN&;Ѳn7t` vqcz;o01YTܷټVύC4j\g&|25ux@K)5 LMfK]iw8&LX72&e,A[vMH[ᰢcbE3i9<=yw ʇCQ[ŋÝ#϶?_'kO7烌'&ގf~w`ؠδnVBEnn3^}EQB0qLnlǑ(ہK-Ynfv`&皿N-N'uٹs7.;wS^]r3Z6fHV+yyy_C¾/r0`O<7Ё7ԫȺM 0ul]N;dܸqϡz`pi8913'2+|(m" C(awfYgYN'oMBuZ|w/ޙ?VvF._Va||%|?/7'8vt{? PV-F^5[~`fNf*BK׺CQw,/I(9-oEK%nV+-7cn2ODG&''ْ/$]_uvG7`}Nbcӳ ll_8Q0eT( =z^oJ,!\M7XQm5#gCFC{ f-G窳B9v$5܇+#ǧ0:/f忯R6lX ksNKLyzK"Eh e|,{CWtDRwm%.6ڶm͊Cc-øwCe=#FeQX[16$佨;< ߫ʟkC+GnO>e洩MH Кìಫ .@ (*_Z:Xmka)san`ct:/ۺi0tCѤ]Ԋ<~ʳ/0mG{,)>z\aQ=.Hao1ܾi֫`,?C' BPmA)v>eBt\Yl޽;_5NiZ5kpuy^?ξ}i%G%Q$W*oltelec]LUaLymVp[e'nW_[[ח&ԓyM_ui‹훱cn{S-hEC@4E k)R(^odqlc ϐ ֞W/k,)TG]C{ 3Cde^ddb>]plNm)EA՗OL( 1+媸KUU3mLq)_GoW[&Re,PӅrҬi֭4wʣ"77ӰQYmZ1?G[N֭[KYd$U{zډ)S^YF _ &pK]h &44mҰi'+P6^RK {,CIv$L!PjvlP ǜB3w}76k.sBnݨS7lFRT1E#+9|Pd"G|5*<\ّKy$nwYhUWxIGcҴysԉ.7l\KV*{_aBܼY'5fdmΝ \et1VaCK:NE <ë `.PQR$Lth,3[~1xv7*KۢI4_-@jz=SrnJogh žIzSv-yv\+"tx}ɮ".3}y"+GTСDN>5ihӦ{^,ټ=/!/v~k|1#eo੧c5WE͒̈́_豣rIia:iPLߏ0 8>U4ӅpژX˷_b[ލHŒ3vڌyVsf3w\ڶmKPkuyÎ7q3yD,f^ZH` f-_cǎUiwއK$^{M:wHZBa (NNV͚EYlVDEպR֭V-䊩;^Ǯy݌l"։#'NUM5'?8o"TɅ]?"`8o8w]K; kЭwZ?wT틢63Sf `q*%C·+/ +N&Q58xry6t+ bca$nwCfMر=fSN{ puA(8+%ZUU,L>p.1cyɴYS#/H9ĞG2|Yi˲4|<#O~6=ҐGr&N= ن-4jY*8S2qkp QN:Q5tTՍ͒UOv[-5fve.IoBJNi7^Ѱj*VZU_h4jһpWlذ!o:@Vǯj;Y]7113{&/N;o;¯O>`<̙=Ⱦ͛g>RL] eg[0[-O'W( bh i8q"~\Khk㦟IW;. VU23ٸq3t8~lHOp\m#Yn2|Πgrص tu~+*ddb^?-/F݇;QM6B0&ĤiKٵ(!&~^a;ӣ{l֨w`hu ۯSX\^:y  \Edpj5 !(3nAӴIC*O*H?͠Axg !pc~>M nߏ۾YFNǦ3{DD|Ki,a)yj?,Gv93.g2IUrTrSKtgM.2vz mmÏONok<,$>NlÖ~sOi9r2 2 r|B9tgRw(܍X2_ٟb!]yf@t:M?;tj?S荡6lpB0要<9q,yRNˎhӦ p8Ic0qD+V`붭 *쑤<';v{QQDhz( ps!΄\.v[u48vOWQ-ZEqv9p:mGąJVg@73*S ` $%D"H$Jr$D"H$D"\1H,H$D"H$)%D"H$D""X"H$D"I< IDATH$)%D"H$D",2p::u,NBQPfDQEA @ExCP4UE;~!@qPj3|o*wyU,Pn:&'|3sߞEA\7ī+=FAP蹪*8R͏Ǜ4z p@KGo#C?W7 yu,w =t᩟'y]Z/Xl).7R j=n*ۅ \nV]4TVh-U0D"H$9N.tS|$F[P-#jbD†jѿբ{JQK+d<]^NEۅc^(QROMT<˗/jJZ1U-%+Z2ł\#D[fBVvBDs:%I *[~o-|;mDՠVdr+T 9HtX\.nUu̒H$M(VϩcXf4hf..LRs¨ȌH$DR&V `"G|ԐAVp:m]N)ɕ*V/X\r9PUȚQ7B'3jd'oD"H$r "+Q(nJBv$G^@jԨ^Wkv;jteL'huȔ7N"H$Im.H$+[+8x2331FBCCiؠM6ZH`S%׹je%H$QuDqE.9JUYٙI IYs~_"$PYl۾͛7p8_5$==cǎsQrԱ-A2GqWD"H$?r`,<)@%Er1/ի7aX;z 5lܸpZlOq]ӽ5keڿ|K"H$< ,TШRG ]|_Yqv"9GK]_Wx_Y"\lg?02d0fn&N;F`6kS αYF;ݮ+~rmΎ;ʍK"H$D`PZL*Up}×_yZ%MiӺ5~pL&7o@UUF\:wHzz:V:̙cXt71c)7.D"H$󦇊i!$")S2oi}bWri`poo~{olݺ+Zt:Tr\Փd̘0-̛Zܸ$D"H.rN9F2J ኟS1_1jaQLgVg6+#ja8y_~ ~oH$3nU%0Dpp0Y t/_C=̻dGܹ{"7/@BBQUD/W-gMi޼9Gax:W{SH$D"Ig-b#G%YIOUέ), Drtn7n̿GEQUݯ# Frp"111vʍ+77wC@@,3˯D"H$ɅB`I(,/ W5yNкu+sr8t8NLJsf3qf>ZӧΡaÆJ AYx4hiTt׸++_OyUy3ϖH.6v #:t(}Ǐqݛ,?NNN.{&"" s\u ==S JppУGrH$˖-~+`0H&M۷/cWfɒ%5mVwQUr] ̜9}2h O<-[GH""Xr+. +/s]cRJ.UTՍÑGttmKBBHӦMiҤ sNg\#--DBBB0e/^oAV&11J͛y74iyfDܾ$&&. D"EDGcДHn5 ЩSGnE(iaAU4.Drq2zhzQ,Z 2e-ZFS) jq̘1㼉lD"ED3RJ$jBVӍ騰`g\@QN6l@bb"4nܸBUlEF/4&f͚3rʽ˅Vcbh4"\.N""X"H$V8֋..DriP~}9u9E޽{HHHnɵ^]w݅^`ʔ)ܹS7o^B_$559s(J'N`ҤI4h^~NM7İaxB0j(f͚Łp:2j(wGoa֭f6mѣK\ ֮]KFF4i҄zX3',2`ۼ|MN߾}eAH*}<H$D"Tƍ.3ƍyꩧ8y$z+>( 6dɒ%7@Νiذ!:twEnabcc9vd}"  ""NJJ $$$0~xRSS2d=z<쳜,f֬Y(ᅬniӦ  !!=yfիwΗtoEG~G~.H$ӦMo~ʣ>ʚ5kaeqݽ{7 <ի96|pL&{ԢE BBB۷I\\{tz#@nʍwE۵kxF3x׮]h+y%[v- 2hnFٳgB:wѣGIKKd\.v@JJ ǎz$+[ ED"H$4nܘ]z?={d<3ԭ[s?5s.h$::SNy(BNHHH5o߾VZaZ9xWGFFҨQsƩhy  x̚N:f+ׯ_eb!558 =yԥK fРA! D}y#tc, iil߱6SUU-.Lvm DrQ0?2ddd\@n7yyyTVBq￳}vbcc9s ڵiӦ<:66{ҿr% .6L y99ۣٺu+}amۖ0bccqK$)%ɖ-[Y312C$IdgguV:uHXD"ڵkp̙R9s!!!!ChZmf͚l߾t:f:+ cnרQFS浧xſ`]vر4=M7x̲.]JJJ ;wO>r%D`IeIH8D6%I9tAfD")%6nXb4$%%ѤI+pm۶l߾lbbb ~xEd2Qvmv܉f`0x>hkԭ[DRSSK̉.0k.fq\\6mbҥ\uUo?.K$U@ (Ԭ)3B"D͚( y ޽WfDrҺukڴiڵk_3229s&B"CyN:qqq$''{cv:_tܹL/ӕaddd0{l\T>aGngƌEdZl۶m}E̚;w~5jx?nVK||<<&Mp"c0hѢvsΥ:ڒH$ye.\横e|͇ȩSLj3#,DCb'###l63H:u ))oetFGrrk9M`̌,vڴm%<DDDЦM\אGdd$zNG~5jTA˖-by^RHPP͚5G`0HfիW( 006mPV-ƍj6h۶m;v䪫B'H$DOH䜤;[X(Wp^cŷˋGrebZIHHerZVe;#00#!ݳfnh)9D"H\B%&SW,D9ŒN$s芔_˳aq-[LOh2b2B(B"n3ilظG\X.3㆞=ܹ_?ӯ_?]?d%;vPV-jժfϞ#<ⷼ믿"tE`` ;vt9N233^:h4S[o L&u>fx莋^deer}%ݥYV+VB{VJKҍ7Gb'RK$@Q![{?T fw!`УaGQDGG?w<0z+&]q/rrrSUʻ=z@Q*v=zz]Ru6((.]-ݥ5HjԸʧD"MvN]vh @Q`0о}rG%9F)V%$$įrFѯyOs{^+R+n5c2*D\gEkl]흢(~o?WWȑ`y$54X p0a6lo w}򝬿(D"9vmrJ 5@%>>^fD")V-bK{D|3=_ ਨ(8ÑT_fEHMMz 999_r!00o]ȋhëI|>B4ZIv`*ĩdUU7\yEV-7D\vfK$r9j l ""OZOf͚U4l,Zߖ3T tԋk!?B K`#pXѭ e pjUJfeJ$UDќs>F|$IٿE3ge$̙3ԨQB F}Vuk0O?$&ʂ"&a},źN"vŜB%c^-<cJɧsq*H$yGQn3C - !FjB+X~`JHH "z^.rEEE EDB :H 5@=@yy63;3g'ssLo;֬.هeaz#J)3 MQQxeY6$)bƠ;X1_ob;X6k.Wė$+ڟ*$J^^ey,eY>*r4J!P>zmDWjGxoS{I n$$$pR˒?qxN@2ܹCznk)'z]郞%|xvIכX(X3f"+ HT*' {eY.Ͽp4&$)T!s$IzWa$"%IzM$Mv-I$Ia$-$i$I}Kk S6$i$I~2{ t21"8??_KP8<׮]+gϦ]vFRֳ,[AŢ>J6+WЬ񁊵HLR ᓿ`ˣ>\*8Q(Ӄ$i$IZD@Y%Ԋʃer0$`1_%Irhp$IE$; x_ռ O\f.wؼT2mcJ%jumW!ME1 c -jtR<<ϴrJJ-[t0h%##bU)Baw@n2tX|jfSo1A,?/-IH[%0u5jۦ,%Ir#@= km[ڦ=dY4@k 0S/o&Ȳ,ɲZ\f;""4;+E*)RK^__UU8m*`Au%))ٳgl20… J ac˒ ={q޽E}`vw@>CuiYd[|=&$IN"@/Yɲ|CO, hLԳ(D=+fYTH IDATl20U$0x[}UEi^+ҽM\fH[_]V0$8E,^ܿlzaebb OpzzBڵk4mjޣwz/rAU{RUʂgIqc Vw7gGvv^C#:ac({w&;ۢ틊Z5'NX\WXB ;;+V/l8\a' OPz|ՇX|)=kx(xa/>3_ YECKh r %Ij LFl[ Wu@WL۞EȴڣԬ׀@7=7ߞR̒U5ɕiF#a˷PPP%YWW\POz[o֭[C0c DVll,9998::ZMDjj*NNNXiӦ/6϶}D{D>5Vprr25k h߹~ӵtՃ1?FhPNč7hJG *HmQ>~S-ϫ>sdY]tk5,Kt!r(dA$-$I-rF ( \y/OpɤJ,j"lZqqvvɑOrs$ E+ $$P(+D ]vQFL>˗YYYL<sQBBB,>˗DXXJ_>}qe ԫW76%%{Ѳe wVZ0pBB-Z0Xf/b;QH~}{3Yc=Ŭ> cT*\D*$IeY~@6Sg`>0MI–(ÙPֱ,g I>V Ӄ$j뀾Es%Ijr (6,W&#%I@Y+j?e9[@_IVȲ_xņ/kӚӔLQ(ť'rsrp(jzÌy!lZQ!jOCV-Y0 o ?d̘1E-[ܹs)e.\9s>}:˖-3Igff_>۷رctլݲe ڵ*5СC+wkkk˪U`ĉBask wWe҈WyvY- _Pq.L-T˲,K _%jCT(ǁD%P(DYW}?2F !Jo[U(k/)Ѷ@` 1U5k>e2/w^eAgѦukz/JÆ ص7B)n9 k 4!ML2UV[lΝ;F ͛ ݻ9rH4DFF,Xz/Q5n@lqVtXjSLaҤIVwqǏsUz쉝E\!| 5jdѹHDZZj*N`Ae .ey,mdYJLβ,w"ev YȲ|Dպߖey,v%%bYe),*˲}-eYU,֏YAT `6[ `5F ro͔)S2n}˖-ܽ{'g֭ dY6IX.\vrr]UgժUYK.Yg}d!LQEx{ Um|wĥ%K F#1i$m]V|͙S`U [a@ BxڴimٲoZpp0۷ogFolٲ2Եk׮M||wܺu tR)G{{{x7nȈ#x7OM7PТ$',ʔ_N>[QBüRpsǘJ畐!\qrIItڵXP^xnܸQjN}8p^zY8wFoKڵK- ԩSe(qpp ++KȰH~H@uys²G8d~V! ~drތAEʏT6lXfYfͰ^zs2B5jٳgM5kZƍ4mڴ2aooOvvv `\c<=uOş2f֘2B8nm/uy8;;J Pe{L9 'gɐ}-V C={\۹sg:dZjŅ ,iӦ\zWe~2:d(W 23 {uşKnfn~w1ѵ@ K=طoUu֜;w*5mڔ+WX܏#g͚NNNY/4F'%%e5 7ɡejںueFƲ~^"Ox_$C,3gE5 1ÆBLNF } ꁓ-Zɓkj/k$Y͛7ʕ+4k> k`2dV_~ڵԲڵk3zh@߱OΟukm'HӥK0yQy{{~z.]ēݟ|׸932292EXX{&L&n @i-$<kY7ȲCӚ+5w9[<a~QM$zl>"Z XB&MV{V nٲUB9{lÙ};^ 뢰CР~}~,M&99uiǎf!TZ%IR , zeY>SIDqK5!)B).-lo1 Ql$ÆPjW_.5߻wR>>MibBX5#u%0ށ}CBI>%Xt֍뇍UjI7niܸ}xyy/lllx?<1W_ݻ'0˗[Ż((**"77`ض@P9W*@MUmW@?tey$djBw X%$IzXBNgG ١+ D@ ( wNll,={Z0x噀H21 |enjjjJEEEٳ!C`kkWh?\к5+DWL |]}w)4- 2[0z!xz $ TJO7]*;U[ܚ&}!ȟΝ;йsG&PplL2,]L%@ t-ZJlْsgs ܾ}&v>|(߸q2I҈>+^ϱcǸ{$^iiixxx|666Z[_Ѩ[?S4. ~M r aaaFѣ|A Uĭs͹s;T*e80@|l"U?~3gLO.>kޜ85kҲESs@l )CEo aͿuG7?dbѦMCzpuuJ-Z`۶mh;;; d׮]`'''rrre`X;;;j(]P舛?~nڴpAݻ4jcwqq!33ww*y]jkf֏Z⅌He#x-IK4(e[/[Y3J,N@?,'4o Tn@1oJ/3@,46P p%7ԱKM%I ece9 ]uۼSN٧7 jזOt'8 ]:%aTT$O@ *&!!!ڵOpC j޽{7k֌+Wмyswww륲UMxx8ƍvuu%))ɬQeE59s **Y%- ׾ͳ%UH8r;a`!p*q?=`F3(=c@$I_H T@H_$IiӚ9t`7?~_&='GG>]/u?u9~D`@ Ύ:paB 99ڵkɓ'-y\|"X^n=֖l 3]USu!UH@Ɂ=O 抲6Yter  "Go[S(M?2VɶF UH`,_(|#˲$E^tuuw̞=&zSy}85& ̍⧟޽{D[#@ 4[.ܺuj}v֍X܏- 5JVV5_߹su asquu"XWq'ЫW/vlΝ.PKc);w>d·|axAB[,j %EJ(?hH$g`*pR,J>ֲB0Rt=vrCeY,rjғ"Y3;Ư] O@ \p9s37 X;riBk};v̢>8sFɜNU-[ضg666{ZDw5\\\0 Yf*wnr#Ɨ?}=QSyѶ %442ߏw@q$&&2c f̘]9=V,-14n,ʱlˠk lѝ|~Բ W,rFo|{/J/$IA%' pPҿхa}3{?o_c˖oIN<Ũ{gϙO?m2b t7#K[^,Qżiժ%Fha+qIFϞ=ٻwkݺ5 +ORRRMHHۏΤ},uzԭ[HLRFoٲ$F]F+eի 4ݻ&#999f8j8B/$7oԎ.t Bpp Zdմ@ x|o>x (,,F׽{w"|~$-ZSh۶0?T0'{dYVv>(e9Q\^U-IFw@I 5 x&]R0AL6+'9w~;o}I:4&S|uٳx2dm5kP(޽;:lܸhڴ) ,(NHd 2EQXXh8Y{l/@\\dԩ:y뭷0aBmMZ53+/Y7uL=$D͚5Tl+q-.N`XcD?/ILf5补lYM(ѡ|WsՒ$.R5,_Q lI ^"nq~@#ߏQ(<S糳Rk[@PXt)))K6m әG4bE_()p }dذJͿ˥{@&F `͐fuA7??bn:|M֭[GÆ /;;;ڶmˡCڵ޶xH۴iàA6m+V(%&n7`cccV길8~VXALLټ ᅴ, vvvfǎeDDGGWy!\vm@}TPhm< IAA'O.%4i҄˗3i$n޼i1|233uvgĉϏѣG3m45ټy3>4N$$ڵkS߸qCBa1ĜXY (QXeƆkLaț ewӃ _||<:u28>g2֮峝;ōH Dc`5X |Vf`,@-("@_+Ͱgmۊ Ae更iF1cSLޞ+Vi&OnnVi&w_/[޽ĉfGGG25ח˗/MԫWmu`M!\Cmll=?JalTGcǎۭ3>͚߱PTXվ}>x0=NyKh$rX ^ Pbbbpvvf͚V«WfرԩSd TfY@@:ٿ?Bsj޼9yzz [.7o4Idffo~WذaC$ qqq1͑#9H5Ժ>}קƏ<-$DPT1@ xt}v郳yzz~z"II\wO}z9֭[aÆܼyӤrT ¬M /\´iغuYYyyydggnvOEMnJڈ]o @`< Jb.e_}i 64vu:Cd*XP1pppgϞܹYk Dž%I4EKe[nѠAGjj*ӦMcҥBtm7KڨQPTOl\Z`#WB 0|C2$:m'0qB^i(U^zIzwww:uy'E%Y~=www{=VZe6ܾ= 27o2, uwvvfҥl;)W*j.&n:,;;;бN}x8so#rbd,1|LIUXTDAai|0v7_=huΐ&Ϲ|&KޡgyH&13c+oº6g硄R"Q۠*QX$ #T1.\W_o &бcG\]]6.ٳgK`}X- LСCZ&ЎɰJRՕȉ&h쳥D/Ɲl?}QB̡34iZjyE"Ӭ]YChҔSyWѫWYz5³Z B 1%;taaarNv_mO;e pµVDu10c/00D}{z5 5iWlP!ԇn*% jjՒE q0уǤ/(ҥKԨQggg:vHjjQd޽{̝EEB^oׯd̘eiӐ7;;dڷooTӷNc< `'BUmݷ a$\$ѺMS[ۺv^CK^glG,2ڠS$B2kڵ`cc#b޽{YСCG,\h/fΜ)Y ^xxxxTDԡש,ZǏ_a͚4iX\XV@!L g=X,,,BN_F'Bm;okRN9ۚΘL}*I888Oga +аaC̙CDDYYY Ǐ]xn05χϙ[wӹ,^̏?F_1ae!,'XO\) йSN܁}d'P]DM 7u‚ѯ;iBWFT O@ www%$$DB5jĬY ::Zk$AyOg@d6#ovvQ]v(LLC.hݭ?oASݺuaTۣ%0tOHʸqLzZ@Uxl֭޽34ctb`޹]QoK=K (°m|BжgYML2UHC Çhܸ13g,Ž(Hpp0ʗ ƖR%F 999$%%^ˉ'֮tA~~~^r5Z7&=+ Zb㭷>`׮%@ٗ?EE=EZj!Y`&e(AINM??=-i-ޮruPG>bq# )%(Zb*%D8@`U+E a=,\ÇӢE -[O?͌3OxK]˛ׯKwui˝ӽ{wC-mFDDUҬnyG mkUnSSdH![Dh .*]NSdv;M[V|yo%Q/\eVs̬)/}KeAcaL~hr jg鄇oӲeK]vŞ>M 0qDar'WJtqqq1{G*ŋ&{bёK9|ÃB!0lk#r%lӎ?cGoĪߖH !ڎ{/6.ϸj!lhQB A]COOOa͔)S4iR! zLԫWwiHNήeݺьgРAfݻdeecyܺu{CZQav)))g ?;{|XF@ ЏFuILEIĒU딂WRR%jlvsև5}T>|H LN'3sju|};68"X 0)١aϞ?,LWPep{{aDcΎCnݨY}ԯ_{r x ~i֮]˪Ur]UG?,ܽ{(RSSuaVx6ڳg=z7npE_6MHH !!bkkˁ]Yt,ބfXZ;ի$$$зorCHII_~1b('O~#o/ؒ!e{}\rz݁Mt>]?C.?~⑜i>}W㨊H -WX$!)l 6nܨ wΝ;&c߾}ڵ ZިQ#fΜIDD%8W8e7Ҷm[CsOpp0vvE?~ ex"nݲ@E^^+Yrkr%c-+&Z%^©,oܸS#FsQE V3=-OZI'vvCE;;G(?aFݼyx9{,4jȨcccٹsgq̜9pV^RE;v5))UŅn]q?Kk׮Dݺu-: .дiSsHKKh!>|Hll,zjfddΡ ۻxt&DGWa\mqb'8-=Gʴ谤MDT\nt]͆yO/^ftr*0uO%Kn~'Y)&+ ͏?vH&H\TD6mQO6lк~ܹߟBCCIHHիFSiҤ 3fT ̛GQn.#Fҥ3P%K\ߗ.H_}(ٳgԩEv-޽KvN";;]M/_ѣG8p#rÆ;#\Z ,\O{wƣ7m z60Y)dgpӘ~um[٠__:t芇;6£wZlCցzXM^u.]2o>}ڿ͚5_RӤ |o|=^_|Qf3t7dsof.Ե{as=v, M --R-~:Qi]->Sr@sC 6N5I Ki%?u1G/ *=DI~^ْD]FxmHaA,I6HDVpptײ ,Xƒ8y4 oBxÆ BXV¡C/@PD-ΚEԩlȀ}?}~hx-sZV+..WWWņyyy޽XBzz:w7)K +5joFFʕ+tTIZ:Gx ؿ8$gISFmOn3O=- .f:7HKf=SF]Ro:yx*֯_Sڵ+ٜ:uJNPm"Jcؑqw} ٳPٽY-nԨQ!$$\4'X 0@IOOMF*_YG@2nQ!өSGap@ȨU}/vԩab"a!ފO~,+r"xK-tercۯ$S%+ }+|x/$x G)22(p˖-e޽SUխ2i IDATPY1}Net4D:YЪU+M;ub8m5-Z3u8ޞ3m>< MJJG={9<3_TNLܹ숎VzG`D,<7tS1!̊CYXD>!8;;=s[Յ{<#yy6:uj 3gN&**HpfͰe"Q (g<=T=`zjrHo.'-_|{x##C/\͛7%??1#\ccF?}֯_XU AU_vbPd{[N{ٽ8z<)}^V>KMMeTnrLp$!IAHMKE _ƿ9 6iHTnСU ///:wLLL /@zph7[KΝٴS^4n%:*HK˅ 3;FZZaaa"43b'}>|Xaog_*/+,Rdegq-_so؋l?ǠAa88 jբ{_< -[/~0R&ٸ:λq"ߨIRRR8pm۶u-BB ,0K  j(ϜDZ xȂ* :$;#7' ܜ z<{{{ *LܜUOOOzɯJ~ Y  .eTeQ&VB‚@$яc 87yԠ]@Uŋ¿%Ђ*sߐeΝ۩o鐸!Ef$'55=\ѧOywx1йK>]M7w/ߺ}TsHNNpWB@h`X";s&yGUMo$z (**RT+RM^^)A4;qΆf7$Ig?=3g윲y}_QsW?KӦKxNNٻw/۷F*mBQ]\f "}ÆjBg\xPWc0lXD&,">>'ФI|*^~[R/+Z5E9'='=%+\J+ spA ͆7DҾiШm0Eѫ(xyyѬY+<=HW"<ϮJF aٓ9{,u)0_BB&26#Fh"8#}nU 4T]7~GKϑPvUT/g63dϞͲ_d޽ 8>+_mhBHQʂ_~n `UWm `cG7ƍӥKq̚k^RoJLd״]7 gzDBQ5NIIw.D~¿ONS)f S^{M,~5;$&&+o+oʏr-YNŋ/Arr22228uo_v/Qu fUϤ%TȵZ( R$n$gЪJH<G/^NzNKr%S5 Ą Xb1Zj2tH3Glݺ ¸_':wɓ'ٴi{fѣGfӿ֭[Ǚ3gxw6mf"$$̷ǰaL0gl 림رhX}{́ZE' ̗ڤ gϖx}BBBX=5N~wgoW8q"g@/%%ǎ㩧)( @š3~<|ZZvAy?B |.~FYwzHɜ >>͛71Y\x"$$(fz|IOOF*XNMִiچx?8РA޽;+۶3f +V`ذalْ={ҳgO˅h7ciq|P]7d(\FN=~j֬i7oÆ yqӧa=t eP7;-[؍;YnDi |g BN>^/[LzJC+J4~U\(~gAbb"blڴ?ٳgYBg#G£jIQW! yM7o6Oy/PaCWHj8?:|/:mz9cjt`.gHA 13s[oqUVƋ/130{//O5k6] sa֭ѫW|zxx0p@Ǖ+WIKJJb̚5UrEwNNf,X9" )?Ϛ5Ol:+O| k32_ KYѲ8Mtm@ή 7o^1ƒs➯ޅȨgGO~_5O V]VEe&C}T ,K(?'B2?gd49)SyV*ӦMcϞ=l۶͡TZ7ERR&MbƌTZ=zM7i҄ &0i$\{.qgq(t jJQvz ItXi[FoES!;nG nݚ픶~!~zvTBj&X7Fcj;e&ח]$2yL&BD )%јCyM&#ޞ! #- I=kJ幮;S&g̘P0@s ߼y3fPZ56mʄ صk{v } G.\Xpތ xy-5(\h5 QǁWD-"۵K!N͛n,bcl.Zdgg믿Odd$~f4'fsw0|r<#%+ jDJ*pA 2gPjժtU5\a2IRRS eIvv)EzNMa%|KW2h ~RSS Nxx}({ a }FpLٸQ)'з\,xU?_},c6lפ jϒmo!xIh򇑎7 :MK)?9w:tcǎ >*~e?1ryVA$ }F#~~xyyPY_N||<|=C̞=)C3ߟpe:w\ڥ/ d,_Od$Ԫ aGh3óf-М8y>p@XgmfmѦ gƍ}IܜtNC˖-8p`/?ҡM(S1T ڕ8BB13vؼQP(W^" ɨDQ9sիWSn]*Ud79zh̖;vș3gظqK;;B?E~fk ˗/Ӭ uy3?^LƑۃWSre!ՊåK8tg ۋ P"XF9~/T ( H ߸I4hNVfjEҾ}{ڵk̤K.yfdKرc ]~ݺu bӦMuvQ" x,l3|xE4+Vkv &h&Vldc ]DD,-nܸ Z(X2Qfŝdga@AF܎eN^!nZH)B}gi=wԻ~xsyg-Abhy.zo,?甙N')o唫( $BOrG@hs2z~=o>hyvaYh0cO@ rErܪ4ߓ<#9C{W [ L4Ʉi2dF$&$$5~oA.0EBɍ7شim۶VZ?ƍ7WT(-ZD5LG6܂$tpUZ׮+WvDxA-c{9hf.G)))۷___z꥖1Q !d'Mfuuf*yDžz1ϕ[ +,s }m򓰰y/s.e(@J/-2X `MZQ/9-or}*H~AiUFlH {+ Ve˜AFzJpp0QQQc+Rz `{,fۇ/mF;5Y?ƕK˗ahؼYx_vLk)s9&>FӻKo(p+QBB;vP!}Vg(>}V'_IBp^j/ca4Yreά]wU! /E={BP0eS!ˊBy3c.CU4w6^A>aaaí;ԶݷJ`kKpEڵkGhhjD'+au*BIxxKP( 'b q"""h۶-7oٳͫL%9dy!lmXX*LV60p.Cπ쏊Hͅd/ gOJJ 7n$00~)\B ;l.1˄p3`05p  cpӼZ !aH)4;Ҏo~QDC*a< JJUa鞶:X9L+LKJ+_XHK9ƪhK V\IavevHe.֚vyϡgف▏+ᕰpx˵F>g=nzVJJ+pd8K@@u4?WG^sܓ:EgV'_VsC+K<#?[6msˑc+X޽dy9JZnCy~ sT"s/[H2Rq*'OҠA8^9}y$++ ѱ8зS[Ҿ}{زe -Z^z\E; `^w~җ3GŸ{Q֮%%p+⁽ ) 'yʳTk^d̠1L&oDRQbzH5AÇӧ@1f( E!7wOjԬt OS^c%+8ǎcԨQ6 L8D2h :֭[֭[w5TRfA5=p0ěhέСFi(uGb&~?TPU"1tW袱'Ҹ Mc.^3.))Il +V@J~xA8qBpZ#"h+tp0ڻ($''sE4oSSW^]7[˰{ ndggP(*5]Kԙk9̕kIdgKlc77z'K./ϟ'((VZߟ:uyf9b޹0h RRR&#dwww.\ŋ9zh9QE#UρWqR!Sh^| BL8űZR h c@*G?7/^,߰wLJ?Ϋ_7O\\}eƭ"<+V0h f!j*9qիǠAbÆ \xߟ(ټy3 |rbccu:x{[mh lJ NVL * 7jL IDAT'`moT U 1fz._Ύwupi%^{<ѾH;<)ի3g̙C>}G_H] [\;JjQu<<9w{FjL$ԩӓqcmݻ3g/pw{;;sQ0›:TVE, O{BѣX z(BYf 886mč7J컶jՊ۷(n===Y`˗/w{IUDm X RhMnXq Ap Wp z*vڌ1RRR0\?}=ϫx7m]'V] ( Mh&9,H-EHkK:WFAim[r;z?[ J+Jap#=_C &9%F>__OVӵk\ǩV?guY]v˝Xx!)kԯ ߲*· {{.^|9w(Y7ls`}FOZZ;_1p@Yr%yCsseΝի]Z9cim ܺ^ 瞃ٳ˷H;zTs~b ĉc0|8Bɠ>Xb'u(ŗgupvY&L"v_,mUXs;bQգ;l{T=,˗/ӪeKxQw|r?2|DDD{vL#~lceh|Oxf51~?zzeUT!(4iBJ/Xpa ѴiӢuvҥ ߿@dd$^%KDO:ܹsYdI8Q۵| V j1/hRW0YO? ɀ'q_ |4s1K!!غi5Σ2Vk/kQVVTAl!R]Ei#,Z5/İt"Yl16o!R%<<>4nܘӧҰA={wOs<]""+Y;'O^zbN(5ח5k>b=<,Z8|*2}c///ZjEV|2;vӓmRȑ#mٳgYp!ǎcʕ%R'N`/^\B| ݐ}!!!scHJJo߾~ڷH||>gP)0agϞUP&sΘL&bccټy3UVUVb*o>fϞMA*U} wwwϟ_S 4V YVh1_8YeirVXjpްA3zm)`r)wWCZZ4i҄Rp?www7ZH`MVS"c;KH;6M ^Eib2ev51fO;FVV4mڔ&MNj|9 ש0| 5kFf͸r ;wݝmX*\va49p;w.Je(BrR O=~A)zfd /ҥkW+f˗9|pγ0((nޱώuAÕ=ұ5T}^0fe" "")%!0ɤ$]FJS% x.gyzmpE76SjUz\?h36*WFQ,yk ߞotf* I1XzRR<pmHMM1yg@VcJuw;}xu<WuSn}M!R(eC gLzZ"),a›B, ӿصk;v̙3'߲?΂  vڼL*6MHpc{ Wh772DR߭> %0Sl QQFOqqqDGGo>Zh_j"h, md !,M}R=_@ĠY ) Ba[aJ޽ԩlٲSNihӦ sε+-ZĢEr4׮]_~)S27Ull.WJ i43k_~ox4M'lԽ;TVJN6L]E‰%2ڳ3@ߟ:5@eQQhoj !Z[]߬-'3d"0!: !"BBgq5lBB(!D!U"sMB({VrΝ\4i҄Pn]~G|||[. ,(P[ _|@sjqM&ѹN:ncӧ5:{c _}+t{{;ܠDpqRݠi]JJQ\@  [`5hC]S$ 6|Qka!y]|_B? B%Ro *3˖-cÆ |7lذ-[wԅ@ C/yCtVE{X#ROG0k`;&p5GBr\gkzW][ЦM *гZwe˖QfMx'>}:&Lf͚pM '88%ˋH7n̰a  y;u鮙Dge9{D &-~vM~Qc䦧ԩ|&\oHWڴ W _ʍ+Saaa,r9mϷ-vgΜnݺ }n DۄͺB !*I)frv#^G@0@Jin1Nw5jlo-RҟwK)[sp x['=rGY,T ^0u( $%&믿ѡ-+)gk׮{̙3www-Zs=DŽ ;HIIдiSVrGf[~^R,!zyEp)׫CP( ?si":QQD,g}-#,plQ~9111Rh-#""BsZ:tooo4iBI. ..%̋KЦ:lS/?&NT~pP;"8"dc+ >eO,C1ּ7gyb&/\ć }Ĉc޽+##Cֺ;e7 FJy6r Vkh3>d⢅`B[}Uò*ժVQO,BժV!1q'NѪxhpZhB4iڴ 3gLtvgBO8Fж6All,ӓFQvmu!w@a k/cItG3 x &EaM.YT͛wwWE61B]E/غi+aul[X ۶Vʿ~Q^z7E$BМb)gIp78͍hc}ceE3xF]f "޽ZIFHKAf#Ya wB ׯO޽ӧڵ-[p!U#8r.7$^hɫp~"gIRSf͠ysu}|Wx$PJ<|hW=wT دcۛaii+qb1?43곺h=gw#PMQb`=9*&!D8:ֺH>(F>>IB]$ߟ6mZT<ǨQAВtqzZ"]숧'/o/-H·3tA 0T& !Suh4rU;EwR $Yڠy F[S{j} qǤ$bbbחǞwGF; kW :!*E<ǰHO:a$a- )8)i(7خ-Tu)J v0իUQƪA&!!~;L\BXJZjJ॥sGD+4T,~]U 6Fɓ'r9NܹsTI]ZRRnck-/-z+ʗ+3e|B+\8yp轌v"Cx Đ?'L ;/OY:)*]c~|.EYĉ4j؈5k+PLKߟ'ҡ}fIKPmm#F~0a3rHNo2,`̖C)<[4F/6 #$+/B||LpP um\Dr޽@%(^^.dmI#3<B<"Ri+AX@,%!B+*Y%v0a/@'`wa4~& v 8gϞ gDvM*x Z)9DS|>`mJyP  ̢X[X ƒ(RI͛`ѢEN3?{m~6 ؍Qα^__$W$$$;w~tܨVaaazc4oޜ͛1"4h%K 7W=zq* %b[6Z|lJ;xE7 f !pkx>\"XώZOaD)rI mW;viu ۱vtFXQ60`̚9k׮Rj5^~U  PK[Dmw=tu~׮]+R@@@&K.q!Ra]ĉMY]K6fQH d;ipsSM t2E(:%''wfGI:thDp#Ԇ\6H)kv]OZ !vqkmp8e)-f\["2+0v~֎lcOTಂۃ'Oa o/7![娠$zZOOOɓ>>>T\Ǐ۱S+>BRavcX)'ƮSObxIB½k|,#GU VEMR^B)噼*QRKBV}{rA}!ۀo!Dg}B  !wlKKN`3pQ.t- X"XB!hgChBG'z6'S@ƣۯ?6!ŽO!xN}2^J)cRZ0,0  \EL1d)"G`!l4f({-enH>mƞ={`" IH{6nKuXij\j'5֭70fd .y( |eC6A[ 6;:K/G%#tV9U5k!DEjHӏBԷ*7X ||| 郁Qt&+fǡj"70Z7\ 2&<,|[J S8}Gv?oMBBӏ] Vpk?zy(o aE `y\t-[EyUwyZ5X1\ <"4 o@.d[*gљ6K)-WhBN޲:h)W:=׀ R:IJKq j3~B)V.׏R;z]wH)͏LZRϕR~b,&.b]Jl4fpW8y=?ƺu_%4oބ֭3|t|ꔖa1fP(2YYF<=.(*u 4Ӹ J7CyJsΑ@h޼9;ҩL]`gݻ_mۖ(X BhNs7}taJfģͺ6BWqm=5Mz0f!| EQB_K)I)be^*4lP6mK4hήXշ<=EnLWӃ޽zPJCUBIHGU=ԀPH)6fxUUZ[km-/XJɉ'"xFD]휧AZ+gذ,0_JUM>}~槥qy.^HVVիӢE lэ1cZ?"VNE1i= CJi)'ZbwƏa2S+K+'< ]'Fw!DUBcu}ݳ5n@CIBGb!bf?R'ˋ=Fj4lX)lݺoS^A݃}{1wQ~fwM/BI#WXz "EH]{"r-1 !Fʶ1 aly,w33ssKzMXN7X603sG$c7({rQ1hr:ob?ڀBHVhh׮%/t9v{s,7.ɩS8x 'N@Qf3IIIhɓ~Mzc0ƈIej Pu6uiKFrؽ$>n$sKIFulhphɴ>$v^=]^smG?2N CKxU3M⚿.wOyX>J/|}YTU}xSQh`Z;  1pq xr*Goֲxgig+IB5&Aؓ'XH\ PuBM@oX9SW\b"P}tc|FN5pбG,Xۆ ^cԨ*E*=-UQnPվC_%Et]j#EyNQDEQE ANr)?E:BW@#`(E1)r)Zy}T(%^>v1]FQpEQ^E ^Z|J(ʝ$((h/n'NG]ADp5Į'oy?3 `:ɏP*1OeY`4͚-.7EE5P-/hA^N-+4 .-%Oe/@UUurKv~so]g:/׍ m^B+1F&pkeq q}/ CQܖyL_DDDETT`?a4hDG*wbShEu4Z?Rgs..U*bm\:c]"w(]<[UUejE+7|{O]0KQw^hSU5\ yؿEIu.arEy TSKjv>+ m,)W , .Hr]g4iBll,QQQ5_Geرr8硅E::%jU:qqqr K\/o76}%;Z|Xh?@]ewC%UUs$ ""!.v=1M4)999 ,,җh,Uhމ`26^B5׈yJ||<ފgHj2fΜ[w:}ܶ=4[DLuD  T Hk!N֭iX,N8Ν;)^_F GFFJZE+2BRjzLr/5'Gh-fr[L|mfGQ , {sCl614k6Nvv6۷WvDRTT+5jk֬p[:uBf8m\فJ׃vدJ6@ϋ``V]yxtzO3 ]pjbm̀P%yV,Cц&%AEQ| /ևg6jԈFmph_=˗/g7N Dtyypت;b|][oW٪;֭0iS׵ZX,X,3NGtt4iӆݺ1do'Pm g-Bg}IYab=ѽ"Wq8:gϞ| wZu]ǧ~?~ AA ~Cݮym6_ ZxAxEO )'ooZȁX,dggp8JیFci"VZy[\rђf_/&FTz%ލ۸ql\'a0x{ ٤ML![(XyʞDڼy=m\}Te醻:7ޖs_SP?Y5=VUU-zAGՉp賑kITnBvc&~`ۿ'S\տ?C.YK4,w1D L*Izڶ/}TvI{ Ywi9 eL8_^XmΜyLzt*& L17 /?̈W^jj+`!`/򂴦>Cmo|Wf~vwhƌiqK O<4ƍ#2ҿc2NO` 1B`STcZ4́e0`)5e_V ^B\111|So vZoIrI#]`> NvUۮMM܌*/y#FǤG2+L68n6f8[1ի)a*ɘ`!پzŋ]ٟzmj!Bw} =ؐfs^g%;}4}a+k}S xqՋ:'OT/Bqڼ.f<ejח0|صIiŪUHmӚΫKtZkD'w!|yx5 30x MOG?k8CL]v;gxύdݲF:xdv]D ʊH_ԾնkxuϵNEsЧORSpEt5u&B aZn:z`C=Dv-ע ﴾K;N<-OYK d_b qbɴn11}4=)_K>yx ڦ,cuh}omĮ/mכe{oBؘHnzm>EZZ}ov"l66brrrcZl8Ng͛(3?44h$++ HRRL&q:۷t1䖥>p3ϸjbzCaFx}ܕ!E,Ⱦ_q]1A-**ncJ=}O[V qy݇vCq n6oFQD  I޶$ a%_C!!!`6گMۆ ns%(DGGcYfMz#F8+ɌP8y5/ (@ lpZI%)\9{'3o/e5§eDHHٌ` $$lo:K\qŧ&Z5?,9 IDAT`oS{BK/١}< 9k]\=rFQFXYGxdVϢSװO=~yܕa0c zvY?m8E`!4Ԁf'$$D!T͎XIFAL) X 4a ˿VS7ԞSΰK`قRoM[&{>ϠE B(S4Fo/Q!PUFn)'4V_CI*J'U%=nŭ45H飆ehLsBX<9lzmi1i|8C?)O\K;^ I E BfF f#&o#?D Ba21bnE Bp}7X -9Ul0мc?/T @Y7m0}[k]5%[Y  _ŭ`A߬]\="jZw]Tcڃ`$""g'<on[و'XA;K/.^Dpl*VʁyxD1ݮe9U7 <?8pڿݯXqpeZ^!Cfddopi6n4i`AApGUUDp0H*I\U2Vw^wqOCh޼\|~`~1֯wm<} A0e ̘Q#:0k֬Ϙ1# >C ~ %‚lspB1-yz x HpX B #< B`wﯷW0{=YgϞ-RP 7@+?hIN113ЭFD  STΨ!U7̒;o1 &VSw=큌 AvC`K'xB.J_`\pddhb#>% o=ٲEL-AOpx\"k[1B.pCP\ tn>(ha~&#Ȉ&|l`nw`B `Зfq¸!ѹsg+..ncZ}t~?Gn |>>`*5haӀbհt)$$K坅pWb2\ۆ2ӂ`A*l6;D$(Z "TlrOb6R ]Ѕ7| WɄ` $$tttB'Ÿm* s|vХ  եߝ)祠((((}`hٰla0PUQ:m%ߵti Û9yjUU)..&,,HAlL} fnwѝjzz^`d^׌.,6܀VS~wv}  'SOA?3\9Jt:ZXVl6[...&//,q]$"X'7/TIQ`jvLPwتy%}' .|̜9S ၏>ѣ!7-y 0}Vg,U]N9>RR@a2*)  ANvqqbZlvr?,ٚ_:SNw#FpkH_PC2H7#1 "XS&j*6¢"ΪO"\Kqqqb76mJ,[q#Z.b.׹ڃ#=PU$Z8'ѱ=ϱK%D6/3mԖm~*BX[ciK+BjL4IVRf<6U( \'jFkh!Vv۰~ݣ[< |&rU8lro_E!$ĀjlZ16IV@;v\u0t(LJ؈Gmg@ ݹsPy,O"\mDt7ؓ>OE^]U:[:d{OWԗPy5jT*K,`00j(lK?gRq\>;je[V"#}:III >O>T7T0f =b-,ZNj-j%d.ƍ]"#^l#c6Gaxx9),(d++ {]+<Zy彻,/"a*/"S…СC5k6ͯ5뵱:Nw&=$ja) }4_{رGF0{GBà%)0X dwZ)2SظaGfҵWO:vH< ,Tw"D!_$5&&37111gEGGY}?Om/6W_]„Ǔ'_~9mZ֑9/Z +kW 5_|NQQ8vƍ +l>}6lX<}=׌6kL.{wXn=~O sҖ cbI_^Y1ʡrΉjwo}g5111UZDBBUsxg8y7aECj;1֎;yhpr:dggcX"77Yqøq㈍%66A{D6nŸwr͸xFh\VSxڿ]i V -g@p82vSJOj _ޱe~*oѣ̟?uq ?`ڸ'rw6o}xKye<٥}]Ж.LIʷ A !L8kKEס/ڷodzzibX,X, QUn#&&XڶmKTTT~W=%" xV+!@B{4?+̷x|v1gqDf͚3n"DauVQ*_Xk$̪~֦q6tb)}%˗NEmBpcZKB=CR ӧ:Keggp8Zmݺ5f9hΛGx, m\.vkVS紒J=ݯer5//]Oʹ&"X!p:!p޵k+W,3ppafsKѦM暠=GG;xD܄#ZMǁ۵>=Tz]HLlڷoqe=?3YBMgY,IyO  o@"--49P\ Ӧyڿ ~@_Z)p Xf^AQ2 ۶m+)PD  N{IJjU&${bs w熂nc,w#Yܩ%TR?u00$!pFv}8\y%3gBhh4Qv2"¸K̳;$4oFFb߾}( K+d~`t aܱFfvB~ OsvڤD L֭[Lj#t?~|P N$<ïk5۵kxQ}t$'si߯o$11DqwENNNiEQ'668, ]41Q|KڵHV0Bٱ#vnyDbb\bX@ۢ|) ;pKu5o>DG|:NgYY 4MቱwcѲu.N@ݽ鈋#.sӧѣX,I("X03-` bDEEҢES7oFXY "vA4P ѨՊ T&S ݭ.S]JDD{3f`4OtD;糍?ުM6 '))ɫP,B鈉"h$Q<XD Bx8$1("XӼ ,ډIJ_z)/T!"XD0dYfСCFX #6ҺR%Olٲp6mJ.] mhтnfD<qJSC\v̟/ǽ@3f>%>F{x5MNʲu6rQJ%*A.g->Bb4i͊wbDAG~1ŧ1͵*c+&,,]ҿÖNQuJK-sy$xfTKw E+6Çgl`Xsؼe+MIMm+*MNN7oG󉉩 -.ΧD~.g/ ++)A "sϳ#"+x gsNlz0%Z6 7*,,d>|( vGjT5k&#W@DDG1HP‚Bh!_k"*z+$[sQXVfgF`A McU"""Dg5[$&6bk#GдiSMoYvIbbb0䖨!h::`jg[4^dq$-h!@PLddk֬ ..d5j󶢣?~/#`",T_È)50.]taȐ!e?~{n\̭w\=lx1 ޽"h4v)S8rQPPٳ&/ICKԻ}ˣM6t:8ڵkKE@R%^< ˕Wbs3ԢE믨*, '\˕*#K=+T.3*/y#FǤG2+L68n6f8\a!?7W^ᦄ^8Æ1L@~@¡J sيN-G¡\6x} =4?YjmZ֑yt_=:Q: s[^Ft:Pc_~!#FzEEEرǏc4i׮;w>ke˖1w\"##! ;e̙C>}U\C~%\wfȟ>Y{ 6uӦ',W\U-`9<3} N:dCtҥM2k#<"_ @"p딄Dluӿ'<7IVC,>v;b!v Fqn3{6˟~Z &"Xi1[^z躇KOI.j-ؿ7 fȚ&>2N  آ r@p`cLV &0i$ZlY:?//?lС=zҶ5j th!*T"K(NÇ1 V^?r$,a5fHN'> 44n: )((`֭Fݻ,O!\dZY/Њ"ぃII|~4mʆX~ݲ]ҳgO~iO@oQb!66V '"XPvzQPhlvFi-! םDFWJшfK.Dx9JrUD\"gIIr7ǯή'sh.[Ο}ƩPvu@VеkW222X,qyUk`A$ =EHHc27p.TUa{ կ[ڭwZ\TU|w60hP(..1_2\ U" Ȯ9Y!ѿ=7pÆMy7|Zo}m汭I<ڱ#>[n!w0~6t~)^}9"""իdddi&BCCi߾=M4#"X03:deoP%l6afBC-&77i? h~Ҷs3p߇8XŰje<,\cǒS`@ jH7I(t凟?ܡΫwp'Mbw~uNOڹxrat8S?/ެY*6UUq:#"XɌdCuJtt4cƌaѢExE7oʵ\,ƍ#"2"hp^uyZn CZE;37|۷&Pi"xݺAJ?Ac B6niOi}>ϦVm۶a0HMM%))ɧhז/ӉSpE˖S]BD WTg`X/yb,>ʫM||CgSxTP۩>x~˧`ݵݱczm{*+Vcǎjꬶ : Q@n% 5O`&yT󳾇z*fqsWy_F#;vcǎvvŚ5ktвeKtsM7_駞f8K߉Z'/"XASTŒ/3qddɚ5ߓڦ5ii=u-~KHJjw om83y2BbѢE̝;W.  Wr= NᝓP 6*fVf޴9 :tC8w}jՊ֭[yt;r$mkjF {/Eұ#5㤓SUA?*" ^E>}HMmE]Y3eR2220Ջ0@ˌj4@Aȕ*eɀoSNoߴ4B32(nт=))ӿs)"X yyyB;v0BCC~06t;F˖-iժo+]fĉL8ch![Ӂߎ@&IUpO ZغBϚיΥӱq׏t6oLrr2:t8+Ks]лGZ̟A8v-}LhAtXgdaFBʼnLjn/d2(oN8JdD$N=?g&;;-[`ܹ3ݻw'##EK/YvF O8p)e%3;{)[!!!vmU`ݺu`YvZRDEE1iS۲®]tr예`abмE+v'^*AmTdg璝}6 ⓗ"+f޽ر(z]Fp0wܳ@d޷o0Z_DPVG9NQGGkI}ӏ9snw؀ae޷lْ-[ˆ ltԉ:˴{a̙gn59b`aa2izGfrAEѡtM+Zf;EQ\ctNց꺙U3YV!{zjSJ9-2Z뽫UUQU6Tt=mZ9M6 ZêkZ9ޟ T9(%'y5J{Eծ%Tt-S"Rҩ3hJuQq\%m% M,LۙϨ\Ѐ' NŪ:ՒB:QQp*8UTT,杪NOLJJ[t ۊN!\֭[9v[fC;vX1A%VuBC}ip7 ΌI3`%46ԟXz%gv_.gA9Ⱟ3 ,XwP<3jOTTnJlk_}k׮4jȷEi,>UMzO8"=]&TETzP(W@*EHu`#i1F?Ҟg}ٯ&::VZŚ!X*viל8s >pW&""nݺy~N䑾3xRqu~\cM"}ZZ {Uoӂ xۘ`ٳ;wŅ^lEWUE(B7kPk'hĘǣ( ,[8݂2$f6-ZXi!%?;v6vUiҺV;99PiJNhڴ)Pkw>,A E;R\qZuUzڬVkisJJאgA nv+nht N j6l`ق<~8ؑw䋳hܸqB82*o-ZwQ=Tz۶mlٲܼ\VkZKWHQ,0ЉPyPD x=z4ڕPdee1yd,Xl&++-[p8ҥ ={C;T(Zzĕ4jUUy她GOĸx:$%#S▿3!mȦUNRUKIJNbƍlٲ>}Yus ]vk׮|hnnΒYv䤑rc2Ga;8vN%PtuZB3I^ѕWQѡt J׸?\cVK -16۩ ӡ%*IUv%9g.f8Nm_U#<"Ħz  PYf1qDz!RSS^xÇcbcc袋}(**_~ .0N_ӹsg6m*'`A1pٳȈS0#Pt*-{Kc X%1RTT@V BL4CtEٟ%3h䥗^bݥ, 7|3?mڴaȐ!,Cixuz$4!'MÆ^/ǥؖ9yΡ'xӏLpp+#/}ͳ1&~?@dd$={T`/%}4n€ضmpQDc$22[cRT: .Co0ҴY2ʑ:-> IF;"~GN']tqr Ncw&U^"_DӵU Qagn\חFsх~t GCRKbKs a3 Vdd$]v'N`͚5$&&ҥK7njw|wkCGsJ "/rp^'ŧۋ(JWTՉV:Ihܔ07}QDzz:;wc 2"l  ^*ǣ&IKK㝣 58S'8|0:7d jY&2%11w}k-̈́ Bff&Vm۶o߾Bhu#9 ?ޙEU}MqCQA5wD33[l~-󧶘ii߲2V݄Tw}cfpf^ddljGJٳgٶm* v1nTlH˖-3f rriZ!ueCZG A`D"򷴢Hp%}D?Q/'.y*,,,Ԗq3gҹs璲vڱl2N<)HrKh,F?%l6ƫdTaX' k[ӱw\8{NXXC=k׮qFRSSoYJ}"nhפQ^ v܄Wʮ^uu|S%z/-,).V8HLL m6\9{,6t-ے=IIgJ-bѢExxxذdf͚o]n`x"Fή S6^#FPt҅~CYʚ'N!lUV)Bpp0*MGǃKg,?2^IJ065W v<`Kjj*_~%ь3J`I!;)@%aie˙WƷUK3B~]-Ş={ C E&׿>7$[_݊Xm>}G֘6^43f̐jID ͊ (GgYR%ܚyO3><*I$((-[Z_>q)75kpppas^SgcٴiSf_aRK(bC!{6l[v"2ú˖Ud~=vEQvyW}ImaaaIA'N`]w&''ɓz+ 4m僃al?l>XȑKFں݄pyuBf2prrZ.LFFFɵlVI5cE(45V %iNƣ;H:ͺӱ$&&ҿ06:OO~GJP* a: oESVs- K )))t҅l|ٳ;O=˃MU_ڃqtBUrsse< ,~o_}5<Ӥ]&%U㧟~"55ロׯСCXj:2yZH&f`zMLvtp9uI׮]ph4ƗږL !##M6ѵkWZjU#}8q_geʴ)34N>MNwwo2d.Ko"X`ɭz]czԴ ؕԙV,(*.^zNgg'>h)ƍ 6 kkk QJmj̻ѩS} VVThK"1{ !G!))P<==KM&IR!cU&4,@ 6Bx888 QJjcccŁ܈СC$$$u5Cyw֕g?|_n5+_O{F9kS3hp"v9dJ,D٭~cӼbm)%8::LVV6c<ϲe3/гg(Æ %77GGG*cǻ)yZ :pvɰaC*%ɓ'IHH ((`vɠAHL0c ZJ;Ю 6 ]]7_|SlhJ網е38gvwNVV7osU_c P"_Χ~ ;G2d|Aܖ%_#ONM[%oEƥju1vδoߞs+>fS2pPBCC$) ~~~8;9QeTjy3o} X t.Y{ mI$qy=ZvĐ7J,13E08%/ŶڠX#--ٳ8{`Qg¹sop`8BDDGa۶macS,׮Ãa#G%K\q K M e8%[r{ KK5]t!;;dxWx2,JVvm۶Rmӫw [-lܸׯAHMU]% ƫFgg9ɂ9{vbNɏ?n4nPP={d֭$%%T7_yr#Kh rCK)o,4ܮiVSZU~ƢaK)Ûdz~z_H 6lΟHvNÇݙ4mJzz&ba+%"&&ggg^צ]vr$fɌma]T^]@6)@J$.Y5jǎc˖- 0[[[?K3a2-jS8̳Oa7*G)'[zsbU]צlTd:)x%uF ͛y3e8}4ѱcG:t耝yW+LgT$bbbP0*u?.Nb6cefcgW.g*5+ǻ1еkWٶm&3O/8}'{{:ct؟\ IP+D*U!9YqtjB޽ E`aPTCnXդ-z=ؿ?'''9(Z%"8˘WDgPpwX(**u?@ F=w232cʰQ51ޏ IDATKZȵRK,]z4i"ER't|sfciYΔ@}Gucŋ4o͠\-cqϽ\΢rg~>@nnM"X/bXRXDrۋa59gRT[-Z$SNa6mʈ#9c/KscK.OTE`.lڴFMߖ*CD"Hng.\  ""-ZA"jBkr Mq | &BCeI\΢K?H@@ B+/_مg%<9RK$D"i̞=T6mDzz:2hf>`W%o+BӧOkŴn;͚TX{w$-- {{{lll8qZ~߿5A·5D"4B˸f޽2t U~ & F^P ^i~vvP om,,K\]]Yj87B4eVXУs< 6mbȐ!XY/oM{ 7WWX8YZQ\!ED"H$}# XkBƆ%KK/*_f 999RK*"8;ӱܷ/4k&Ƿ.02;6l`9xlAZfmdœ>\2I"XBV)**EQIH$*QBxƌ6ͥgϞhر籠^O:U^ xQf7Vڇj/px~ʨQضm۷M6f?~XQ}֯ݵYKޕ-ERPk,lꇝíy'-V*MSH$IÇIKKGuPH,1 `Cv9^`E"A ++FIMMW^&@$h"rkVO|}&x\|k;Z:aUfXCXׇ~JD"i\h1w\vA@@vvvôi_cƌaҤI~zy$&3 "I r\ ={H ug|猽SLvV6MFhTdwƣIK2Ɣ>JEA&-rr㖝IZ䱮/$&f{~:qq8w@xdT膉 l߾m[G@@44hPPXJUhec"4ΛZc]PMnv-cAAN^7yS?D"iԇHz͛;Sgǻ|2ŋZl,XDRcw,A{B`kk[} ''gggʊÇsK޽KOFF\EQٳ'...$H,iȂX+8C_J';7X_MIBSOz~^:^~%kl---.H,*ݻwcccѣٴi#Gرcپ};EEE2MR ˄?@wu |k8ǸccB2Ϙ8t(oT $$IF%Kر5-RC۶_W_͔1X4P\vheM]֨Fe=Ի~G)%Ǝ~[W/ra[}ژ֣_\Vb„ IRF?v{+-{zvN>}xwoΜ:0DZǿ?xDx'M٪U+\]] }ӧ.1'/˝wn{Fʓ!EI2ײ]ˎQwƉ?,Ux}{+a|4utˤzTc@%>r96mD i0^fMm0.h>/ RyۣܺS^uĘJ#,V'$Tc$)ANh`Q4obŊX֬ -* QFؗѵ6fJyMgm H$ bpss#""XRe[nʕ+ٕ*2e ;|0ǏgҤI7%$$}v41|L@\Nhuu;ۍKV3h yC{ H]+ HysrKćI۬4n)%ׁSN8oP[}H$Brr2Ǐ',,rnRTܹFëa͚5pBBK.eҥfn7`*vW6(H$"mFff&& &22TKUeԩL6UO)umQ"Hn%:u0ϟ1kk*,grE{=rssKUҘcy"c[VӲ_K̫8Ԯ]o:+"SNbM eDK1=TyU0(2(zȡr_b{9x%?+WCB;9r(Aݺdӓ}sti?EE `?!E! IUbz{ݴ6gҾ"Ou+!s况~V6ڔ~߫ޛ^R}H$[Aaa!vۛQFq8Jb\fMͱ:u VZUc=FӦMMVI:Mډ̔Lrs*k9gɿ/徻ƣNDT g6EdImq$6ooMZ"~3x%?|իY:gNNN&^z6c͗_&7Wtڔ^{OO "xub GBD#m gH!ĆJCޯ"`݆~w7VdNTA|['2wpVO(7!%@BB ѱNOԶm[ڴiS#6y晒׻w&::{2u_K5,c9~J)4k;k )ŻB )..v!-^ڽ;w=OM61`bXXX0i&M#QkcV[BtW׶ nk[EVe1IЕoϛau]tFQF=u&ؔ\,H9QQQF/_NTT[l:YYYlܸPxʔ)&{͢E駟ig4^%ۖX|(}zɵJ>S"縒"Z;Dk^rH 666Mzz)kY_ilCxG97kQ='`n40?<ߝ Jv5 fe_%?4i\ DR7V\}{ոbȑٳ49 5Pb Ă lD8EQ\Y꽸t:X(OaJE\D SH,HҥK~:'O4nM~4M5Fbb"|F@O:լ3gΐ3ydĶN+kgg^˜9s~ KM.((`?q͙C&MB0h慇 Y`YdȑlٲKIyzy7D~1h3:voB_ u t;EqA#@b^u"!u}ފ!65l'b~VGD"-!+}_Rm6:t耟_ɓ3bĈR55PիWIJJbС޾sEF5 s!X)vvvwW_%y-,U۷x0y|zz[ɡhXXX0|pn݊JErP%D(gt@Xa!U]}s𫁨݉6xv xx؈@e/p܉609`AЮ+V6IB\ V]?\ j/ƱCڮ>J$̬ۛ,33k̮7ǎcȑu*3338pIoBll,禺l{YYY߿&#&ٵkaaa& Օ6?TkiT70l0v؁ZWJ9~8.]ݥ[ <(J{!i{H/pE9z7@[`?F. @;8A !.GzBD*rlB<а.Q<~ٻ@AA! j^Zehe%Kn=66XZZm&/3g˫˗/LJח]$''s)FUksFQBBBj^aa!;w$<<$ZhnL  ܿ?9uqcXOD;]Pr{pwk.T*r@ (**")IiFFFxOnx NV9xǠF࿊tBĢ 7b i0ⴢ(0~hm(A;ِ9 xTa>RQEQIcR3acG`;^OͻEza#.y]_iGVYfn!>>ތ?~ 111(rX`/FæM:th"6gffrelR$&.X_,]ѡkĉágCp[1`كJCr@tr=qqqRWmh|a*nx| h=XAr v=K̶?6BU%` dSx-r>)km@<%g,3;ׅSquuR ΪUJ `=z!Dpppm۶@֭kٳwww:vXc6oN޽qpp6Y/;ޏ%yr+T֖ l>o|Z$J>}ؿ?qqqu^E"UQ:Ng4 }E BU`Z?窑+e*h ڠWzѮSt0˩v(.ʓ 4ϵ{ <$y4p&Md4J^222صk 2ypUصk͛7m۶5fsUmq޳gOfSɒ+s8չ3~%9Kb[}U{E~syaKnKBCC9tG[nr@1/ ! r(@G BH=71RW6%M!tf>5 !B%#H$5C~^:M<}8{&S6Μ9ӧku/;hӦMz;mڴsaggGӦMMj?fHˉyaZ[^3ٖDr;н{wbcct}{% -[n9pRW*]Wh8v]O#uAeOy(t4 /V% TUYF4?6"wB^VDR9Bh"F/d޽XZZ_![nSN52E o߾ղSPP@|||Ǡ .pOH͵kRKFv*REQBuhn/׵hp\ѣqdeDU>g,Mypvr&((w7_Wb˗/w)UV\\\"LksFa˖-t [6puЧ*{O>x,I]g<=ߋP?ͬ,v͟Iw3_ivV/^}?%Kv-Aq_޷!CZfo!Ep*mcQEvRCw(Ю['6K'(r6hA?yVe3Z#ucC,Pl V?!g HOࡣx7muK"Tl( v BEQ&rsgƌ)ȿƁKx6BUT@zF&UQj͍ׯ抵M_gMV~$Db .$##T٦MPTܹTiVyfjT<>|0iҤjhO7$0]V;0ɓ'i޼yĴ>h4tO\\4RK7Hj8S0ի<؁17nX/"BT%mhaP4$W'k77Wos"// WWWl(Vpss $]3EEE8cc[+7coLlzI$Axx8Y@HLb֬YFohF*M4a̙,Z>x s￯NLLdq旛;ҥK <6_nnGh?p+W筷gϞCdd<'K/=̻~INm233/8st2i$t"5p̄?Tg*xv+VV3â矗"X%~R\C{D~n:wyW)./iwdmT:@ȿ{ү %H$O=J0}7o?ccg yޜn~RkkGaذaܹ}͛"A ~cժGӤeoߝ/V~xAihybg^{5,--K5Ǐ2g^3>#Hdd$flXz5'Fżyh޼9 IIL;//`)%D"H'66Z#(B;?:l#zo4_599890vXzw͌gPZ/kgvi۶I?>eCZ@>w&~~TM8}MemnL찣Τ,Oa^}—ۿ3Гں~qqrࣕ/aÆa bV ̸!CX lٲ?-^̩^#ތ"X`턫{ 2/\^vD"Jrr2+V(Uw^ܹsK}||6mZ62Qf΃? !B~G'fuN(qK"o&UT9sQFU6))>+X[PTdXKBB<2i{wꬪ,x~˭.c~4;hיb`Mxө}'ǭ3=9rp+ҒÇi&M^wl uh׎-#''G]B `D"RXpav||<8;;ceeł D< aŠ~&M>tKߌ7֜1KCYdY"xϞ=kZ䯿|yEEejp=%#S~mH :[E_~gf׮+.YV}JGKLŅP`HOFLX 6|6e=y TiD"Ԕ֯-,:a(UgGNMɉ.9 ڇ'9*+>]G;C|[gabiiIttju3\\#+kM"++s˥O/qp1~@ѯEt<ב?GGG>}???aw3uZ&N0dH<$/4V ?4 {09>MH9A?ޝӑ58nܸr'om6~'ÂywP,ݺt.*dsܦ }ʼJŻά6}[n%44u1cƗك^LaaTsMB|<z oG4M=)JMu+b>38 ?\ȬT.8?? #X9M.#щv%|lx]`ԩ6xc@ ˅ͅ{) Hc%D"zqssH 9sLj!|'L6[[Z xB;;==Ge}L M==ͶieeŅ ӓޚFϞZ(Jjs鐧?}ⓒWo',CWl.}ɶȾɎ5')+SLif׳/>Gqn1xstQ<zitu 8F#jKKKqwwvn&uϻn*$X(Hw1\\ H$VXnsg0g+]=pqqݓ'O4SS$OL~\^^}:waI$䕗c[<>.Re={;VfUsK}B>/alE(بG{^@A;CN{ EXrEQ !y#'E$v~'tF+6}t}8 8붝u@?]_@^]-5>6v#H$;t`kl,tBߘT^ll,#Fkb2Ag?GQ 4?|u8xjJgTG._\eqbРAl۶#Gʋc! ,m&C(JO`>Zng!=BpJQsmpz ! zr2`"@ |Ն?m7^ !,ҽ#B;$ KWVY{bV'')++{O%H$?yh۶TIk$9c+W- Aqq1>\UJ(5~mVս;KYϚE3>z%fif4ѣa̜E{-H'TYGM4mw.s"p>//M^r\X-_n"NgؤBl2h.ՉMK|VA?TݦO KP;V*;ZMӷ!+gY"H$F)((`ҫz ʬqVwȾK|&=rݺu|Dј399\z]C rhBV?0ֱ-J-\uW^>܁#GXܞ(ڥk]hѢm*;BFs6˔^|s?amuR-D(tWQEQ>։qNîIN]k_}=}NF./-D"EEEr$2L5}II8@YN6{Wo\5YMHr[i3^kgaUӦMr*gJU_MM{9 XXz D{V2'';F6mB= >e }y2-<_NyPne'A.c\ fL^cnȕ|фOD"u|<;"4r@$|ATz|f͢SL ;lbrpNJ RS5–66 ˋ˗/+l ZiWFxnˮ\յ899ae”lgggZumE|MǮ];jCMEe3O^ytPQGAڠ B?1u2uqI~r$IbccCǀ.P/EDRM6?- O3CZ|7\p_})Sd hӡ`Ǩ]xw֋iҤ ǎ?uDGG,---۶ s 99???i֣b̲u/^KF~!xp#^~.O>Igiy7Oe*c' TVQXMC lBҒH$#CK$wufO$iFRRח1}g]Xl{sknxu<Ν;h{ѬsR Dnnkliii ƶ '%1xc6/5[H@Ű^8(v!B(Q+r z@`?Ћ*NH$9Z"1oooa~O>&$)j/MrACP.D {a^!m}|}}K}J<栏ln';00'Ndnac݆aԻdz,N1ݡkǎ5jGч7?Uo{k'-n#''G&%epOK]7l>Կ7[Vpp=ކXڵ˯;#**Jyyytk{Iڳgbʹx MN֢eK'_}]ãt猫eҥQ}kg1ymW>?~VZ.k/BgNf;4c TcW"$wqM6SMar8 0(%]"CgOYYNŧ3O?rf/zU#{QۊR4N|ޔh`x 6ͫtvv]%iذaڹs&Nխ?Uǿ[o[7ԢO>R)^/*̚2e6lؠ.Co{[sѡC KSהutiU%L̼EM7BpqP?$]m2*ʁ3 #jjvJKM]c"^0Oyٺ(0OTeTg;w^\o^ܼ}p;ه30L&ӏTyKQaIѢ9N_!r.?xxppBB}EEb BmΑ#GջwvJJJS/XƍU||6oڤl\ب(rFcǎ@_ìbEfԇ/ BgVWVJJ_ MNւ%y+11/ k*>> 8;3U\a mFa# øܾ*"\Keer8n+++aH1*(,2mї*++KvH7Fm(iP3F={TLnw}w.JjԨQ`*-111j]ժ 8p[ֹޛԫܚk \wƍӶmv,N͛7rqb-IDT/9im޹t:> Ckn`,o5SOݭ^zU=6O3.dV\@ ZWIM)3DuU11뤰-7hoXX[]7۬re)fZruFgMsڸjnFe L.ԃO=is+v]|nBbg7LճgOʊaGu%$$ $P+vm;4-?2rMVϞOﺫTnCz .K<$i% B^mt6mhDaV v%p(Wjg*d48>0qcuIťQPή|xW!׮Ç_|s*[׮]/o.ҪUt+007 B0p{o@{lNog}6]˗E,х^v-LY=s5kgrД玈Fˆyu`ZT']}S'IOu-שGge ғOJ))M/t_ɲo_8Nɵ%s C\众^IGѠi52l6֦7\z杏t5&qXXLcƌ{(OkY{^队O|i^xAzmN چn»/hw2tMTWTThֵ;o5A-|t6VTPPp;v֭[AUVV&_)裷K.7B0&4EGfp:)%@EE:x M'l6Q#==]_|l6=9e=gF%T)%-}c*nҒK<* @zeilXZmۦ's[ Q]u (bW_g &MI'cSNՆ:#y=zT!!!5{.]UѣW_iڴi~ }߶yv|( #gY@@"##ukr\n;p֯[Q+ڲrYVnke/Sb9+Ue/s;KҾ}ڵ{N*+¤cgw qR} {>6@e>^s_OWM[صkk#9Y U#5G^rd6U\\LJJל3G(RCl+5e:Z߻Lf]^7<}Zz.aaңJw)%%ڽ{ &[.ZtB=vm ZKj#՗_jemP]w5?}9ѣGkǎ1ch?~_ p6nt/Aޛ٬ȈpuZ-&Zenk),,L6[L&B:9ҧ2غ(vzL&&8X_4V^"Z?Am_몁%IAVx}N^ XlĀAz m2_3tw+([mw)Q!!!r\*++SPPMz5_j[cbb&G)ܤ UȪ@J`c3L f G& pN:hq:zWZw͆5aRք'ܹu{r,?q^Q\.CVKrjBpiEl!m>snY; /~SR/5l05jvء'jܿ)==Nǫƍec+ue+$w:2 Z?0#ڻwRRR|*#??_lnb% -[Q<+Y7wrss[_'+G3g+(l!^H%-ܲǍIxjֻFxWA0SS*//תUkw$eeݯzVtnV@q :0ZPvMOO׀p_Oa\5t&?\6}_׿KU5+^=K.sر~$߽^PαK~19p%IB:M6hʴiJOO%\SY.˫[+~Y5UGfYӧNeonoM۾ԂWH?PvVs-gӭ3g:L58&&F{է+W곏VKM?:ԳجV],oMŋ%I[nՍ7^G9#R!Xnn}`^[{uqpzfGz鷯ĉr:YgN}F;I $5x?}OΟL˖ h,66V͓$͙3C![%Ws5oR=peddh̘1>Slөs+j{+9kǷ;Z/{д)Zz2W VdknTqY)a`T*,, ? @PE~NRCd6 ٗ/Rrr嗗K.>mcEEO >7mń q~9 è3n۲EXLY 5g?! ӥq}uခ5Kݫ=}ϲ"rۈ'S!ǯq'er۽G.[ GU[c>UTZh + {UnFFϣKv-..&!6M__iuJJZ믿rzwWew٫ڶoo˹ C&ݽ5]ph ?~\C 񩌓'O{>hu:_?7=--MG7-22\^{ [=m;[;{o?,{U?t%eݺɇx -7_wO=m驇7qaF7Bp?o'Iwcǎiɹ$]]oJoT|@ΤV7.a~W }g5Jg*M?ѫXS.v߯={hذan{)}yyynrYVUILm4j ̩^n*`4+DK4^$0 ^8;[?]T8~zݺj%Z}*@aaa>oݩqv<,'?"@.҅#Gz]G|:0IgL-k4*QPPKE \{-eiIL6ͧ=޽{'NP=oh0ueq`Жp1pdffꢋ.򹜓'Ojر>r<&84t i05Ocz;F !|~]QQ@())QHH!W\\ܦBgaa@]q:tH s+TL ON8aÆ\ɓ'խ[7)**h%&ҠAכLsB08ZV`Kouׯ{Mq B0h˚ -B=U~YNJrsssNsT0 {~؞ڴie/vॲ29Syl^mt3hIǎח_~%I:s$'+Wj~َ{Sgy%%%>#G(<}C5+"׭[7]u~+o xnNSw>y,BZwc~0گ-rm5lӰa)ԝ?4p"" P.V7`@n? ^z=zXfBBNSw>y,,_.::Mם? XIyy0{p,h 4i6WS  a ҌPm@Jr@B0h1ѣZ>|!N @ !6[$ق#k~/-ɣBqSk O>^ Cwh,>S꾣!: B0p ]B`ZVSVާ:!|v[;Z@pꝖs$nЁ@a=uߑi` @ !-8zRf[߶m>X$KK:TvwknǬ=K@]t\Zha=ʛjk>&B0 tc\>. [2jl:msmmX~ST9l;붴_CsǤ/5ZNw7cO8h ە~60՝qK- =ose5}q~5 lظO-v`t\@d]om_~y/ng r]@whxvmgðִ:Nwr αrRh Y R\WeoFnk1pw掕崗!LKACm w[= M=osyL::_uw]?w!=;4@' M H ji֦7wjk\8km@#7^eiCS_xn8A ;/˹3}v<˓uNo}"=;4 !B0`F7}E%Ў @ !B0` @ @ !B0`  @FapR|p8DEB911*)-Z DeDݺES+8&٤BY9Uxaw($8X]t2!m[PP]t\ 0ATsKP23*̟"p3  ־QhvpVYTiQ߄2s kJIIV#$'?S~KUB0  )WfjۿRUa롞q 1 ,r: W)(WDDVbJpVlA $ɤKrs&ɩRRTYB0*+J+ҿ'PXX̕QA! (/@yyզg`+%*p*.&OaPKQn޴ [NCGURZ,Ӑdld$L&L&&I&Ld\yudQad\[;J@C2 d2dz 2TUyejWaT.TΪz\50d&TQ^zFtdTZ1~7.o-Y@IDAT#IF9o2j.w|H2L5e2d2*_Kb2U=fd*U3j~֮Sz4Uͫ~}Vϫ^zkz՛WF ZK. Ub5\FB 2 e0I%yͻ l#Vfs R/W4KY1QV4qUTt!`ɤ@MA5֋oUE@Z`:S,j4jr͇}Sjay@̫}&N9˭R9̫ $fC£א)_[FRma;꾎=M_${{^kW~MMLk2rd(kwQ tğe(ipO !B0`hú hܾ~B0|mɬMB0Z qCj[2W̦~z>@-vpi'7^k-kԛzotfѯtZQ/dP,@M!A2ݡцx5,G;X9-CK0Q_ZcY_g#7%d9ƙ []_^^~博//xz<|e=M.R-k8#ݯGtwonow}H%&&<%:*:As̡:89$M?_-`Iz7rj\KeV:vTXfY[p$-AK-D++URtf 4 3ge7KKp'pZ-vuM%1p\ldM>|v;!30.'&&El!w.J!*.6R=cz\wxf OyTNn+00+t}MKpgtdw>&Q$%! ZT({C%2\""B$)ظ^b)P?TIENDB`veusz-3.0.1/Documents/manual-source/conf.py0000664000175000017500000002214413242310603020321 0ustar jssjss00000000000000# -*- coding: utf-8 -*- # # veusz documentation build configuration file, created by # sphinx-quickstart on Sun Feb 5 11:07:00 2017. # # This file is execfile()d with the current directory set to its # containing dir. # # Note that not all possible configuration values are present in this # autogenerated file. # # All configuration values have a default; values that are commented out # serve to show the default. import sys import os # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. sys.path.insert(0, os.path.abspath('..')) # -- General configuration ------------------------------------------------ # If your documentation needs a minimal Sphinx version, state it here. #needs_sphinx = '1.0' # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. extensions = [ 'sphinx.ext.autodoc', 'sphinx.ext.viewcode', ] # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] # The suffix(es) of source filenames. # You can specify multiple suffix as a list of string: # source_suffix = ['.rst', '.md'] source_suffix = '.rst' # The encoding of source files. #source_encoding = 'utf-8-sig' # The master toctree document. master_doc = 'index' # General information about the project. project = u'Veusz' copyright = u'2003-2018, Jeremy Sanders' author = u'Jeremy Sanders' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. # with open('../../VERSION') as f: version = release = f.read().strip() # The short X.Y version. #version = u'2.0' # The full version, including alpha/beta/rc tags. #release = u'2.0' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. # # This is also used if you do content translation via gettext catalogs. # Usually you set "language" from the command line for these cases. language = None # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: #today = '' # Else, today_fmt is used as the format for a strftime call. #today_fmt = '%B %d, %Y' # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. exclude_patterns = ['_build'] # The reST default role (used for this markup: `text`) to use for all # documents. #default_role = None # If true, '()' will be appended to :func: etc. cross-reference text. #add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). #add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. #show_authors = False # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' # A list of ignored prefixes for module index sorting. #modindex_common_prefix = [] # If true, keep warnings as "system message" paragraphs in the built documents. #keep_warnings = False # If true, `todo` and `todoList` produce output, else they produce nothing. todo_include_todos = False autoclass_content = 'both' # -- Options for HTML output ---------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. html_theme = 'alabaster' # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. #html_theme_options = {} # Add any paths that contain custom themes here, relative to this directory. #html_theme_path = [] # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". #html_title = None # A shorter title for the navigation bar. Default is the same as html_title. #html_short_title = None # The name of an image file (relative to this directory) to place at the top # of the sidebar. #html_logo = None # The name of an image file (relative to this directory) to use as a favicon of # the docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. #html_favicon = None # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = ['_static'] # Add any extra paths that contain custom files (such as robots.txt or # .htaccess) here, relative to this directory. These files are copied # directly to the root of the documentation. #html_extra_path = [] # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. #html_last_updated_fmt = '%b %d, %Y' # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. #html_use_smartypants = True # Custom sidebar templates, maps document names to template names. #html_sidebars = {} # Additional templates that should be rendered to pages, maps page names to # template names. #html_additional_pages = {} # If false, no module index is generated. #html_domain_indices = True # If false, no index is generated. #html_use_index = True # If true, the index is split into individual pages for each letter. #html_split_index = False # If true, links to the reST sources are added to the pages. #html_show_sourcelink = True # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. #html_show_sphinx = True # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. #html_show_copyright = True # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. #html_use_opensearch = '' # This is the file name suffix for HTML files (e.g. ".xhtml"). #html_file_suffix = None # Language to be used for generating the HTML full-text search index. # Sphinx supports the following languages: # 'da', 'de', 'en', 'es', 'fi', 'fr', 'hu', 'it', 'ja' # 'nl', 'no', 'pt', 'ro', 'ru', 'sv', 'tr' #html_search_language = 'en' # A dictionary with options for the search language support, empty by default. # Now only 'ja' uses this config value #html_search_options = {'type': 'default'} # The name of a javascript file (relative to the configuration directory) that # implements a search results scorer. If empty, the default will be used. #html_search_scorer = 'scorer.js' # Output file base name for HTML help builder. htmlhelp_basename = 'veuszdoc' # -- Options for LaTeX output --------------------------------------------- latex_elements = { # The paper size ('letterpaper' or 'a4paper'). #'papersize': 'letterpaper', # The font size ('10pt', '11pt' or '12pt'). #'pointsize': '10pt', # Additional stuff for the LaTeX preamble. #'preamble': '', # Latex figure (float) alignment #'figure_align': 'htbp', } # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, # author, documentclass [howto, manual, or own class]). latex_documents = [ (master_doc, 'veusz.tex', u'Veusz Documentation', u'Jeremy Sanders', 'manual'), ] # The name of an image file (relative to this directory) to place at the top of # the title page. #latex_logo = None # For "manual" documents, if this is true, then toplevel headings are parts, # not chapters. #latex_use_parts = False # If true, show page references after internal links. #latex_show_pagerefs = False # If true, show URL addresses after external links. #latex_show_urls = False # Documents to append as an appendix to all manuals. #latex_appendices = [] # If false, no module index is generated. #latex_domain_indices = True # -- Options for manual page output --------------------------------------- # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ (master_doc, 'veusz', u'Veusz Documentation', [author], 1) ] # If true, show URL addresses after external links. #man_show_urls = False # -- Options for Texinfo output ------------------------------------------- # Grouping the document tree into Texinfo files. List of tuples # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ (master_doc, 'veusz', u'Veusz Documentation', author, 'veusz', 'A scientific plotting package.', 'Miscellaneous'), ] # Documents to append as an appendix to all manuals. #texinfo_appendices = [] # If false, no module index is generated. #texinfo_domain_indices = True # How to display URL addresses: 'footnote', 'no', or 'inline'. #texinfo_show_urls = 'footnote' # If true, do not generate a @detailmenu in the "Top" node's menu. #texinfo_no_detailmenu = False veusz-3.0.1/Documents/manual-source/_static/0000775000175000017500000000000013325026667020465 5ustar jssjss00000000000000veusz-3.0.1/Documents/manual-source/_static/.gitignore0000664000175000017500000000000013161413406022430 0ustar jssjss00000000000000veusz-3.0.1/Documents/manual-source/_templates/0000775000175000017500000000000013325026667021174 5ustar jssjss00000000000000veusz-3.0.1/Documents/manual-source/_templates/.gitignore0000664000175000017500000000000013161413406023137 0ustar jssjss00000000000000veusz-3.0.1/Documents/manual-source/api.rst0000664000175000017500000013241613322660165020343 0ustar jssjss00000000000000================================================ Veusz command line and embedding interface (API) ================================================ .. _Commands: Introduction ############ Veusz uses a common API, or set of commands, to control the program via its command line (from the Veusz console; click View, Windows, Console Window), the embedding interface (when Veusz is embedded in other Python programs), from within plugins, within documents (VSZ documents contain commands used to generate the document) or externally from the operating system command line (using `veusz --listen`). As Veusz is a a Python application it uses Python as its scripting language. You can therefore freely mix Veusz and Python commands on the Veusz command line (Click View, Windows, Console Window to get access to the command line). Veusz can also read in Python scripts from files on the command line (see the :ref:`Load ` command). When commands are entered in the command prompt in the Veusz window, Veusz supports a simplified command syntax, whereq brackets following commands names, and commas, can replaced by spaces in Veusz commands (not Python commands). For example, :command:`Add('graph', name='foo')`, may be entered as :command:`Add 'graph' name='foo'`. The :command:`numpy` package is already imported into the command line interface (as `\*`), so you do not need to import it first. The command prompt supports history (use the up and down cursor keys to recall previous commands). Most of the commands listed below can be used in the in-program command line interface, using the embedding interface or using `veusz --listen`. Commands specific to particular modes are documented as such. Veusz also includes a new object-oriented version of the API, which is documented at new_api_. Commands and API ################ We list the allowed set of commands below Action ------ .. _Command.Action: :command:`Action('actionname', componentpath='.')` Initiates the specified action on the widget (component) given the action name. Actions perform certain automated routines. These include "fit" on a fit widget, and "zeroMargins" on grids. Add --- .. _Command.Add: :command:`Add('widgettype', name='nameforwidget', autoadd=True, optionalargs)` The Add command adds a graph into the current widget (See the :ref:`To ` command to change the current position). The first argument is the type of widget to add. These include "graph", "page", "axis", "xy" and "grid". :command:`name` is the name of the new widget (if not given, it will be generated from the type of the widget plus a number). The :command:`autoadd` parameter if set, constructs the default sub-widgets this widget has (for example, axes in a graph). Optionally, default values for the graph settings may be given, for example :command:`Add('axis', name='y', direction='vertical')`. Subsettings may be set by using double underscores, for example :command:`Add('xy', MarkerFill__color='red', ErrorBarLine__hide=True)`. Returns: Name of widget added. AddCustom --------- .. _Command.AddCustom: :command:`AddCustom(type, name, value)` Add a custom definition for evaluation of expressions. This can define a constant (can be in terms of other constants), a function of 1 or more variables, or a function imported from an external python module. ctype is "constant", "function" or "import". name is name of constant, or "function(x, y, ...)" or module name. val is definition for constant or function (both are _strings_), or is a list of symbols for a module (comma separated items in a string). If mode is 'appendalways', the custom value is appended to the end of the list even if there is one with the same name. If mode is 'replace', it replaces any existing definition in the same place in the list or is appended otherwise. If mode is 'append', then an existing definition is deleted, and the new one appended to the end. AddImportPath ------------- .. _Command.AddImportPath: :command:`AddImportPath(directory)` Add a directory to the list of directories to try to import data from. CloneWidget ----------- .. _Command.CloneWidget: :command:`CloneWidget(widget, newparent, newname=None)` Clone the widget given, placing the copy in newparent and the name given. newname is an optional new name to give it Returns new widget path. Close ----- .. _Command.Close: :command:`Close()` Closes the plotwindow. This is only supported in embedded mode. CreateHistogram --------------- .. _Command.CreateHistogram: :command:`CreateHistogram(inexpr, outbinsds, outvalsds, binparams=None, binmanual=None, method='counts', cumulative = 'none', errors=False)` Histogram an input expression. inexpr is input expression. outbinds is the name of the dataset to create giving bin positions. outvalsds is name of dataset for bin values. binparams is None or (numbins, minval, maxval, islogbins). binmanual is None or a list of bin values. method is 'counts', 'density', or 'fractions'. cumulative is to calculate cumulative distributions which is 'none', 'smalltolarge' or 'largetosmall'. errors is to calculate Poisson error bars. DatasetPlugin ------------- .. _Command.DatasetPlugin: :command:`DatasetPlugin(pluginname, fields, datasetnames={})>` Use a dataset plugin. pluginname: name of plugin to use fields: dict of input values to plugin datasetnames: dict mapping old names to new names of datasets if they are renamed. The new name None means dataset is deleted EnableToolbar ------------- .. _Command.EnableToolbar: :command:`EnableToolbar(enable=True)` Enable/disable the zooming toolbar in the plotwindow. This command is only supported in embedded mode or from `veusz --listen`. Export ------ .. _Command.Export: :command:`Export(filename, color=True, page=0, dpi=100, antialias=True, quality=85, backcolor='#ffffff00', pdfdpi=150, svgdpi=96, svgtextastext=False)` Export the page given to the filename given. The :command:`filename` must end with the correct extension to get the right sort of output file. Currrenly supported extensions are '.eps', '.pdf', '.ps', '.svg', '.jpg', '.jpeg', '.bmp' and '.png'. If :command:`color` is True, then the output is in colour, else greyscale. :command:`page` is the page number of the document to export (starting from 0 for the first page!). A list of pages can be given for multipage formats (.pdf or .ps). :command:`dpi` is the number of dots per inch for bitmap output files. :command:`antialias` - antialiases output if True. :command:`quality` is a quality parameter for jpeg output. :command:`backcolor` is the background color for bitmap files, which is a name or a #RRGGBBAA value (red, green, blue, alpha). :command:`pdfdpi` is the dpi to use when exporting EPS or PDF files. :command:`svgdpi` is the dpi to use when exporting to SVG files. :command:`svgtextastext` says whether to export SVG text as text, rather than curves. FilterDatasets -------------- .. _Command.FilterDatasets: :command:`FilterDatasets(filterexpr, datasets, prefix="", suffix="", invert=False, replaceblanks=False)` Filter a list of datasets given. Creates new datasets for each with prefix and suffix added to input dataset names. filterexpr is an input numpy eexpression for filtering the datasets. If invert is set, the filter condition is inverted. If replaceblanks is set, filtered values are not removed, but replaced with a blank or NaN value. This command only works on 1D numeric, date or text datasets. ForceUpdate ----------- .. _Command.ForceUpdate: :command:`ForceUpdate()` Force the window to be updated to reflect the current state of the document. Often used when periodic updates have been disabled (see SetUpdateInterval). This command is only supported in embedded mode or from `veusz --listen`. Get --- .. _Command.Get: :command:`Get('settingpath')` Returns: The value of the setting given by the path. .. code-block:: python >>> Get('/page1/graph1/x/min') 'Auto' GetChildren ----------- .. _Command.GetChildren: :command:`GetChildren(where='.')` Returns: The names of the widgets which are children of the path given GetClick -------- .. _Command.GetClick: :command:`GetClick()` Waits for the user to click on a graph and returns the position of the click on appropriate axes. Command only works in embedded mode. Returns: A list containing tuples of the form (axispath, val) for each axis for which the click was in range. The value is the value on the axis for the click. GetColormap ----------- .. _Command.GetColormap: :command:`GetColormap(name, invert=False, nvals=256)` Returns a colormap as a numpy array of red, green, blue, alpha values (ranging from 0 to 255) with the number of steps given. GetData ------- .. _Command.GetData: :command:`GetData(name)` Returns: For a 1D dataset, a tuple containing the dataset with the name given. The value is (data, symerr, negerr, poserr), with each a numpy array of the same size or None. data are the values of the dataset, symerr are the symmetric errors (if set), negerr and poserr and negative and positive asymmetric errors (if set). If a text dataset, return a list of text elements. If the dataset is a date-time dataset, return a list of Python datetime objects. If the dataset is a 2D dataset return the tuple (data, rangex, rangey), where data is a 2D numpy array and rangex/y are tuples giving the range of the x and y coordinates of the data. If it is an ND dataset, return an n-dimensional array. .. code-block:: python data = GetData('x') SetData('x', data[0]*0.1, \*data[1:]) GetDataType ----------- .. _Command.GetDataType: :command:`GetDataType(name)` Get type of dataset with name given. Returns '1d' for a 1d dataset, '2d' for a 2d dataset, 'text' for a text dataset and 'datetime' for a datetime dataset. GetDatasets ----------- .. _Command.GetDatasets: :command:`GetDatasets()` Returns: The names of the datasets in the current document. GPL --- .. _Command.GPL: :command:`GPL()` Print out the GNU Public Licence, which Veusz is licenced under. ImportFile ---------- .. _Command.ImportFile: :command:`ImportFile('filename', 'descriptor', linked=False, prefix='', suffix='', encoding='utf_8', renames={})` Imports data from a file. The arguments are the filename to load data from and the descriptor. The format of the descriptor is a list of variable names representing the columns of the data. For more information see :ref:`Descriptors `. If the linked parameter is set to True, if the document is saved, the data imported will not be saved with the document, but will be reread from the filename given the next time the document is opened. The linked parameter is optional. If prefix and/or suffix are set, then the prefix and suffix are added to each dataset name. If set, renames maps imported dataset names to final dataset names after import. Returns: A tuple containing a list of the imported datasets and the number of conversions which failed for a dataset. Changed in version 0.5: A tuple is returned rather than just the number of imported variables. ImportFile2D ------------ .. _Command.ImportFile2D: :command:`ImportFile2D('filename', datasets, xrange=None, yrange=None, invertrows=False, invertcols=False, transpose=False, prefix='', suffix='', linked=False, encoding='utf8', renames={})` Imports two-dimensional data from a file. The required arguments are the filename to load data from and the dataset name, or a list of names to use. filename is a string which contains the filename to use. datasets is either a string (for a single dataset), or a list of strings (for multiple datasets). The xrange parameter is a tuple which contains the range of the X-axis along the two-dimensional dataset, for example (-1., 1.) represents an inclusive range of -1 to 1. The yrange parameter specifies the range of the Y-axis similarly. If they are not specified, (0, N) is the default, where N is the number of datapoints along a particular axis. invertrows and invertcols if set to True, invert the rows and columns respectively after they are read by Veusz. transpose swaps the rows and columns. If prefix and/or suffix are set, they are prepended or appended to imported dataset names. If set, renames maps imported dataset names to final dataset names after import. If the linked parameter is True, then the datasets are linked to the imported file, and are not saved within a saved document. The file format this command accepts is a two-dimensional matrix of numbers, with the columns separated by spaces or tabs, and the rows separated by new lines. The X-coordinate is taken to be in the direction of the columns. Comments are supported (use `#`, `!` or `%`), as are continuation characters (`\\`). Separate datasets are deliminated by using blank lines. In addition to the matrix of numbers, the various optional parameters this command takes can also be specified in the data file. These commands should be given on separate lines before the matrix of numbers. They are: #. xrange A B #. yrange C D #. invertrows #. invertcols #. transpose ImportFileCSV ------------- .. _Command.ImportFileCSV: :command:`ImportFileCSV('filename', readrows=False, dsprefix='', dssuffix='', linked=False, encoding='utf_8', renames={})` This command imports data from a CSV format file. Data are read from the file using the dataset names given at the top of the files in columns. Please see the reading data section of this manual for more information. dsprefix is prepended to each dataset name and dssuffix is added (the prefix option is deprecated and also addeds an underscore to the dataset name). linked specifies whether the data will be linked to the file. renames, if set, provides new names for datasets after import. ImportFileFITS -------------- .. _Command.ImportFileFITS: :command:`ImportFileFits(filename, items, namemap={}, slices={}, twodranges={}, twod_as_oned=set(\[]), wcsmodes={}, prefix='', suffix='', renames={}, linked=False)` Import data from a FITS file. items is a list of datasets to be imported. items are formatted like the following: :: '/': import whole file '/hduname': import whole HDU (image or table) '/hduname/column': import column from table HDU all values in items should be lower case. HDU names have to follow a Veusz-specific naming. If the HDU has a standard name (e.g. primary or events), then this is used. If the HDU has a EXTVER keyword then this number is appended to this name. An extra number is appended if this name is not unique. If the HDU has no name, then the name used should be 'hduX', where X is the HDU number (0 is the primary HDU). namemap maps an input dataset (using the scheme above for items) to a Veusz dataset name. Special suffixes can be used on the Veusz dataset name to indicate that the dataset should be imported specially. :: 'foo (+)': import as +ve error for dataset foo 'foo (-)': import as -ve error for dataset foo 'foo (+-)': import as symmetric error for dataset foo slices is an optional dict specifying slices to be selected when importing. For each dataset to be sliced, provide a tuple of values, one for each dimension. The values should be a single integer to select that index, or a tuple (start, stop, step), where the entries are integers or None. twodranges is an optional dict giving data ranges for 2D datasets. It maps names to (minx, miny, maxx, maxy). twod_as_oned: optional set containing 2D datasets to attempt to read as 1D, treating extra columns as error bars wcsmodes is an optional dict specfying the WCS import mode for 2D datasets in HDUs. The keys are '/hduname' and the values can be 'pixel': number pixel range from 0 to maximum (default) 'pixel_wcs': pixel number relative to WCS reference pixel 'linear_wcs': linear coordinate system from the WCS keywords 'fraction': fractional values from 0 to 1. renames is an optional dict mapping old to new dataset names, to be renamed after importing linked specifies that the dataset is linked to the file. Values under the VEUSZ header keyword can be used to override defaults: :: 'name': override name for dataset 'slice': slice on importing (use format "start:stop:step,...") 'range': should be 4 item array to specify x and y ranges: [minx, miny, maxx, maxy] 'xrange' / 'yrange': individual ranges for x and y 'xcent' / 'ycent': arrays giving the centres of pixels 'xedge' / 'yedge': arrays giving the edges of pixels 'twod_as_oned': treat 2d dataset as 1d dataset with errors 'wcsmode': use specific WCS mode for dataset (see values above) These are specified under the VEUSZ header keyword in the form KEY=VALUE or for column-specific values COLUMNNAME: KEY=VALUE Returns: list of imported datasets ImportFileHDF5 -------------- .. _Command.ImportFileHDF5: :command:`ImportFileHDF5(filename, items, namemap={}, slices={}, twodranges={}, twod_as_oned=set(\[]), convert_datetime={}, prefix='', suffix='', renames={}, linked=False)` Import data from a HDF5 file. items is a list of groups and datasets which can be imported. If a group is imported, all child datasets are imported. namemap maps an input dataset to a veusz dataset name. Special suffixes can be used on the veusz dataset name to indicate that the dataset should be imported specially. :: 'foo (+)': import as +ve error for dataset foo 'foo (-)': import as -ve error for dataset foo 'foo (+-)': import as symmetric error for dataset foo slices is an optional dict specifying slices to be selected when importing. For each dataset to be sliced, provide a tuple of values, one for each dimension. The values should be a single integer to select that index, or a tuple (start, stop, step), where the entries are integers or None. twodranges is an optional dict giving data ranges for 2d datasets. It maps names to (minx, miny, maxx, maxy). twod_as_oned: optional set containing 2d datasets to attempt to read as 1d convert_datetime should be a dict mapping hdf name to specify date/time importing. For a 1d numeric dataset: if this is set to 'veusz', this is the number of seconds since 2009-01-01, if this is set to 'unix', this is the number of seconds since 1970-01-01. For a text dataset, this should give the format of the date/time, e.g. 'YYYY-MM-DD|T|hh:mm:ss' or 'iso' for iso format. renames is a dict mapping old to new dataset names, to be renamed after importing. linked specifies that the dataset is linked to the file. Attributes can be used in datasets to override defaults: :: 'vsz_name': set to override name for dataset in veusz 'vsz_slice': slice on importing (use format "start:stop:step,...") 'vsz_range': should be 4 item array to specify x and y ranges: [minx, miny, maxx, maxy] 'vsz_twod_as_oned': treat 2d dataset as 1d dataset with errors 'vsz_convert_datetime': treat as date/time, set to one of the values above. For compound datasets these attributes can be given on a per-column basis using attribute names vsz_attributename_columnname. Returns: list of imported datasets ImportFileND ------------ .. _Command.ImportFileND: :command:`def ImportFileND(comm, filename, dataset, shape=None, transpose=False, mode='text', csvdelimiter=',', csvtextdelimiter='"', csvlocale='en_US', prefix="", suffix="", encoding='utf_8', linked=False)` Import an n-dimensional dataset from a file. The file should either be in CSV format (mode='csv') or whitespace-separated text (mode='text'). A one-dimensional dataset is given as a list of numbers on a single line/row. A two-dimensional dataset is given by a set of rows. A three-dimensional dataset is given by a set of two-dimensional datasets, with blank lines between them. a four-dimensional dataset is given by a set of three-dimensional datasets with two blank lines between each. Each additional dataset increases the separating number of blank lines by one. Alternatively, the numbers can be given in any form (number of numbers on each row) and "shape" is included to reshape the data into the desired shape. In the file, or included as parameters above, the command "shape num1 num2..." can be included to reshape the output dataset to the shape given by the numbers in the row after "shape" (these should be in separate columns in CSV format). If one of these numbers is -1, then this dimension is inferred from the number of values and the other dimensions. Also supported is the "transpose" command or optional argument which reverses the order of the dimensions. ImportFilePlugin ---------------- .. _Command.ImportFilePlugin: :command:`ImportFilePlugin('pluginname', 'filename', \**pluginargs, linked=False, encoding='utf_8', prefix='', suffix='', renames={})` Import data from file using import plugin 'pluginname'. The arguments to the plugin are given, plus optionally a text encoding, and prefix and suffix to prepend or append to dataset names. renames, if set, provides new names for datasets after import. ImportFITSFile -------------- .. _Command.ImportFITSFile: :command:`ImportFITSFile(datasetname, filename, hdu, datacol='A', symerrcol='B', poserrcol='C', negerrcol='D', linked=True/False, renames={})` This command is deprecated. Please do not use in new code, but instead use ImportFileFITS. This command does a simple import from a FITS file. The FITS format is used within the astronomical community to transport binary data. For a more powerful FITS interface, you can use PyFITS within your scripts. The datasetname is the name of the dataset to import, the filename is the name of the FITS file to import from. The hdu parameter specifies the HDU to import data from (numerical or a name). If the HDU specified is a primary HDU or image extension, then a two-dimensional dataset is loaded from the file. The optional parameters (other than linked) are ignored. Any WCS information within the HDU are used to provide a suitable xrange and yrange. If the HDU is a table, then the datacol parameter must be specified (and optionally symerrcol, poserrcol and negerrcol). The dataset is read in from the named column in the table. Any errors are read in from the other specified columns. If linked is True, then the dataset is not saved with a saved document, but is reread from the data file each time the document is loaded. renames, if set, provides new names for datasets after import. ImportString ------------ .. _Command.ImportString: :command:`ImportString('descriptor', 'data')` Like, :ref:`ImportFile `, but loads the data from the specfied string rather than a file. This allows data to be easily embedded within a document. The data string is usually a multi-line Python string. Returns: A tuple containing a list of the imported datasets and the number of conversions which failed for a dataset. Changed in version 0.5: A tuple is returned rather than just the number of imported variables. .. code-block:: python ImportString('x y', ''' 1 2 2 5 3 10 ''') ImportString2D -------------- .. _Command.ImportString2D: :command:`ImportString2D(datasets, string, xrange=None, yrange=None, invertrows=None, invertcols=None, transpose=None)` Imports a two-dimensional dataset from the string given. This is similar to the :ref:`ImportFile2D ` command, with the same dataset format within the string. The optional values are also listed there. The various controlling parameters can be set within the string. See the :ref:`ImportFile2D ` section for details. ImportStringND -------------- .. _Command.ImportStringND: :command:`ImportStringND(dataset, string, shape=None, transpose=False)` Imports a n-dimensional dataset from the string given. This is similar to the :ref:`ImportFileND ` command. Please look there for more detail and the description of the optional parameters and in-stream allowed parameters. IsClosed -------- .. _Command.IsClosed: :command:`IsClosed()` Returns a boolean value telling the caller whether the plotting window has been closed. Note: this command is only supported in the embedding interface. List ---- .. _Command.List: :command:`List(where='.')` List the widgets which are contained within the widget with the path given, the type of widgets, and a brief description. Load ---- .. _Command.Load: :command:`Load('filename.vsz')` Loads the veusz script file given. The script file can be any Python code. The code is executed using the Veusz interpreter. Note: this command is only supported at the command line and not in a script. Scripts may use the python :command:`execfile` function instead. MoveToPage ---------- .. _Command.MoveToPage: :command:`MoveToPage(pagenum)` Updates window to show the page number given of the document. Note: this command is only supported in the embedding interface or `veusz --listen`. ReloadData ---------- .. _Command.ReloadData: :command:`ReloadData()` Reload any datasets which have been linked to files. Returns: A tuple containing a list of the imported datasets and the number of conversions which failed for a dataset. Rename ------ .. _Command.Rename: :command:`Remove('widgetpath', 'newname')` Rename the widget at the path given to a new name. This command does not move widgets. See :ref:`To ` for a description of the path syntax. '.' can be used to select the current widget. Remove ------ .. _Command.Remove: :command:`Remove('widgetpath')` Remove the widget selected using the path. See :ref:`To ` for a description of the path syntax. ResizeWindow ------------ .. _Command.ResizeWindow: :command:`ResizeWindow(width, height)` Resizes window to be width by height pixels. Note: this command is only supported in the embedding interface or `veusz --listen`. Save ---- .. _Command.Save: :command:`Save('filename.vsz')` Save the current document under the filename given. Set --- .. _Command.Set: :command:`Set('settingpath', val)` Set the setting given by the path to the value given. If the type of :command:`val` is incorrect, an :command:`InvalidType` exception is thrown. The path to the setting is the optional path to the widget the setting is contained within, an optional subsetting specifier, and the setting itself. .. code-block:: python Set('page1/graph1/x/min', -10.) SetAntiAliasing --------------- .. _Command.SetAntiAliasing: :command:`SetAntiAliasing(on)` Enable or disable anti aliasing in the plot window, replotting the image. SetData ------- .. _Command.SetData: :command:`SetData(name, val, symerr=None, negerr=None, poserr=None)` Set the dataset name with the values given. If None is given for an item, it will be left blank. val is the actual data, symerr are the symmetric errors, negerr and poserr and the getative and positive asymmetric errors. The data can be given as lists or numpys. SetDataExpression ----------------- .. _Command.SetDataExpression: :command:`SetDataExpression(name, val, symerr=None, negerr=None, poserr=None, linked=False, parametric=None)` Create a new dataset based on the expressions given. The expressions are Python syntax expressions based on existing datasets. If linked is True, the dataset will change as the datasets in the expressions change. Parametric can be set to a tuple of (minval, maxval, numitems). :command:`t` in the expression will iterate from minval to maxval in numitems values. SetDataND --------- .. _Command.SetDataND: :command:`SetDataRange(name, val)` Set a n-dimensional dataset to be the values given by val. val should be an n-dimensional numpy array of values, or a list of lists. SetDataRange ------------ .. _Command.SetDataRange: :command:`SetDataRange(name, numsteps, val, symerr=None, negerr=None, poserr=None, linked=False)` Set dataset to be a range of values with numsteps steps. val is tuple made up of (minimum value, maximum value). symerr, negerr and poserr are optional tuples for the error bars. If linked is True, the dataset can be saved in a document as a SetDataRange, otherwise it is expanded to the values which would make it up. SetData2D --------- .. _Command.SetData2D: :command:`SetData2D('name', val, xrange=(A,B), yrange=(C,D), xgrid=[1,2,3...], ygrid=[4,5,6...])` Creates a two-dimensional dataset with the name given. val is either a two-dimensional numpy array, or is a list of lists, with each list in the list representing a row. Do not give xrange if xgrid is set and do not give yrange if ygrid is set, and vice versa. xrange and yrange are optional tuples giving the inclusive range of the X and Y coordinates of the data. xgrid and ygrid are optional lists, tuples or arrays which give the coordinates of the edges of the pixels. There should be one more item in each array than pixels. SetData2DExpression ------------------- .. _Command.SetData2DExpression: :command:`SetData2DExpression('name', expr, linked=False)` Create a 2D dataset based on expressions. name is the new dataset name expr is an expression which should return a 2D array linked specifies whether to permanently link the dataset to the expressions. SetData2DExpressionXYZ ---------------------- .. _Command.SetData2DExpressionXYZ: :command:`SetData2DExpressionXYZ('name', 'xexpr', 'yexpr', 'zexpr', linked=False)` Create a 2D dataset based on three 1D expressions. The x, y expressions need to evaluate to a grid of x, y points, with the z expression as the 2D value at that point. Currently only linear fixed grids are supported. This function is intended to convert calculations or measurements at fixed points into a 2D dataset easily. Missing values are filled with NaN. SetData2DXYFunc --------------- .. _Command.SetData2DXYFunc: :command:`SetData2DXYFunc('name', xstep, ystep, 'expr', linked=False)` Construct a 2D dataset using a mathematical expression of "x" and "y". The x values are specified as (min, max, step) in xstep as a tuple, the y values similarly. If linked remains as False, then a real 2D dataset is created, where values can be modified and the data are stored in the saved file. SetDataDateTime --------------- .. _Command.SetDataDateTime: :command:`SetDataDateTime('name', vals)` Creates a datetime dataset of name given. vals is a list of Python datetime objects. SetDataText ----------- .. _Command.SetDataText: :command:`SetDataText(name, val)` Set the text dataset name with the values given. :command:`val` must be a type that can be converted into a Python list. .. code-block:: python SetDataText('mylabel', ['oranges', 'apples', 'pears', 'spam']) SetToReference -------------- .. _Command.SetToReference: :command:`SetToReference(setting, refval)` Link setting given to other setting refval. SetUpdateInterval ----------------- .. _Command.SetUpdateInterval: :command:`SetUpdateInterval(interval)` Tells window to update every interval milliseconds at most. The value 0 disables updates until this function is called with a non-zero. The value -1 tells Veusz to update the window every time the document has changed. This will make things slow if repeated changes are made to the document. Disabling updates and using the ForceUpdate command will allow the user to control updates directly. Note: this command is only supported in the embedding interface or `veusz --listen`. SetVerbose ---------- .. _Command.SetVerbose: :command:`SetVerbose(v=True)` If :command:`v` is :command:`True`, then extra information is printed out by commands. StartSecondView --------------- .. _Command.StartSecondView: :command:`StartSecondView(name = 'window title')` In the embedding interface, this method will open a new Embedding interface onto the same document, returning the object. This new window provides a second view onto the document. It can, for instance, show a different page to the primary view. name is a window title for the new window. Note: this command is only supported in the embedding interface. TagDatasets ----------- .. _Command.TagDatasets: :command:`TagDatasets('tag', ['ds1', 'ds2'...])` Adds the tag to the list of datasets given.. To -- .. _Command.To: :command:`To('widgetpath')` The To command takes a path to a widget and moves to that widget. For example, this may be "/", the root widget, "graph1", "/page1/graph1/x", "../x". The syntax is designed to mimic Unix paths for files. "/" represents the base widget (where the pages reside), and ".." represents the widget next up the tree. Quit ---- .. _Command.Quit: :command:`Quit()` Quits Veusz. This is only supported in `veusz --listen`. WaitForClose ------------ .. _Command.WaitForClose: :command:`WaitForClose()` Wait until the plotting window has been closed. Note: this command is only supported in the embedding interface. Zoom ---- .. _Command.Zoom: :command:`Zoom(factor)` Sets the plot zoom factor, relative to a 1:1 scaling. factor can also be "width", "height" or "page", to zoom to the page width, height or page, respectively. This is only supported in embedded mode or `veusz --listen`. Security ######## With the 1.0 release of Veusz, input scripts and expressions are checked for possible security risks. Only a limited subset of Python functionality is allowed, or a dialog box is opened allowing the user to cancel the operation. Specifically you cannot import modules, get attributes of Python objects, access globals() or locals() or do any sort of file reading or manipulation. Basically anything which might break in Veusz or modify a system is not supported. In addition internal Veusz functions which can modify a system are also warned against, specifically Print(), Save() and Export(). If you are running your own scripts and do not want to be bothered by these dialogs, you can run veusz with the :command:`--unsafe-mode` option. Using Veusz from other programs ############################### Non-Qt Python programs ---------------------- Veusz can be used as a Python module for plotting data. There are two ways to use the module: (1) with an older path-based Veusz commands, used in Veusz saved document files or (2) using an object-oriented interface. With the old style method the user uses a unix-path inspired API to navigate the widget tree and add or manipulate widgets. With the new style interface, the user navigates the tree with attributes of the ``Root`` object to access Nodes. The new interface is likely to be easier to use unless you are directly translating saved files. Older path-based interface -------------------------- .. code-block:: python """An example embedding program. Veusz needs to be installed into the Python path for this to work (use setup.py) This animates a sin plot, then finishes """ import time import numpy import veusz.embed as veusz # construct a Veusz embedded window # many of these can be opened at any time g = veusz.Embedded('window title') g.EnableToolbar() # construct the plot g.To( g.Add('page') ) g.To( g.Add('graph') ) g.Add('xy', marker='tiehorz', MarkerFill__color='green') # this stops intelligent axis extending g.Set('x/autoExtend', False) g.Set('x/autoExtendZero', False) # zoom out g.Zoom(0.8) # loop, changing the values of the x and y datasets for i in range(10): x = numpy.arange(0+i/2., 7.+i/2., 0.05) y = numpy.sin(x) g.SetData('x', x) g.SetData('y', y) # wait to animate the graph time.sleep(2) # let the user see the final result print "Waiting for 10 seconds" time.sleep(10) print "Done!" # close the window (this is not strictly necessary) g.Close() The embed interface has the methods listed in the command line interface listed in the Veusz manual https://veusz.github.io/docs/manual.html Multiple Windows are supported by creating more than one ``Embedded`` object. Other useful methods include: - ``WaitForClose()`` - wait until window has closed - ``GetClick()`` - return a list of ``(axis, value)`` tuples where the user clicks on a graph - ``ResizeWndow(width, height)`` - resize window to be ``width`` x ``height`` pixels - ``SetUpdateInterval(interval)`` - set update interval in ms or 0 to disable - ``MoveToPage(page)`` - display page given (starting from 1) - ``IsClosed()`` - has the page been closed - ``Zoom(factor)`` - set zoom level (float) or 'page', 'width', 'height' - ``Close()`` - close window - ``SetAntiAliasing(enable)`` - enable or disable antialiasing - ``EnableToolbar(enable=True)`` - enable plot toolbar - ``StartSecondView(name='Veusz')`` - start a second view onto the document of the current ``Embedded`` object. Returns a new ``Embedded`` object. - ``Wipe()`` - wipe the document of all widgets and datasets. .. _new_api: New-style object interface -------------------------- In Veusz 1.9 or late a new style of object interface is present, which makes it easier to construct the widget tree. Each widget, group of settings or setting is stored as a Node object, or its subclass, in a tree. The root document widget can be accessed with the ``Root`` object. The dot operator "." finds children inside other nodes. In Veusz some widgets can contain other widgets (Root, pages, graphs, grids). Widgets contain setting nodes, accessed as attributes. Widgets can also contain groups of settings, again accessed as attributes. An example tree for a document (not complete) might look like this :: Root \-- page1 (page widget) \-- graph1 (graph widget) \-- x (axis widget) \-- y (axis widget) \-- function (function widget) \-- grid1 (grid widget) \-- graph2 (graph widget) \-- xy1 (xy widget) \-- xData (setting) \-- yData (setting) \-- PlotLine (setting group) \-- width (setting) ... ... \-- x (axis widget) \-- y (axis widget) \-- graph3 (graph widget) \-- contour1 (contour widget) \-- x (axis widget) \-- y (axis widget) Here the user could access the xData setting node of the xy1 widget using ``Root.page1.graph2.xy1.xData``. To actually read or modify the value of a setting, you should get or set the ``val`` property of the setting node. The line width could be changed like this .. code-block:: python graph = embed.Root.page1.graph2 graph.xy1.PlotLine.width.val = '2pt' For instance, this constructs a simple x-squared plot which changes to x-cubed: .. code-block:: python import veusz.embed as veusz import time # open a new window and return a new Embedded object embed = veusz.Embedded('window title') # make a new page, but adding a page widget to the root widget page = embed.Root.Add('page') # add a new graph widget to the page graph = page.Add('graph') # add a function widget to the graph. The Add() method can take a list of settings # to set after widget creation. Here, "function='x**2'" is equivalent to # function.function.val = 'x**2' function = graph.Add('function', function='x**2') time.sleep(2) function.function.val = 'x**3' # this is the same if the widgets have the default names Root.page1.graph1.function1.function.val = 'x**3' If the document contains a page called "page1" then ``Root.page1`` is the object representing the page. Similarly, ``Root.page1.graph1`` is a graph called ``graph1`` in the page. You can also use dictionary-style indexing to get child widgets, e.g. Root['page1']['graph1']. This style is easier to use if the names of widgets contain spaces or if widget names shadow methods or properties of the Node object, i.e. if you do not control the widget names. Widget nodes can contain as children other widgets, groups of settings, or settings. Groups of settings can contain child settings. Settings cannot contain other nodes. Here are the useful operations of Nodes: .. code-block:: python class Node(object): """properties: path - return path to object in document, e.g. /page1/graph1/function1 type - type of node: "widget", "settinggroup" or "setting" name - name of this node, e.g. "graph1" children - a generator to return all the child Nodes of this Node, e.g. for c in Root.children: print c.path children_widgets - generator to return child widget Nodes of this Node children_settinggroups - generator for child setting groups of this Node children_settings - a generator to get the child settings childnames - return a list of the names of the children of this Node childnames_widgets - return a list of the names of the child widgets childnames_settinggroups - return a list of the names of the setting groups childnames_settings - return a list of the names of the settings parent - return the Node corresponding to the parent widget of this Node __getattr__ - get a child Node with name given, e.g. Root.page1 __getitem__ - get a child Node with name given, e.g. Root['page1'] """ def fromPath(self, path): """Returns a new Node corresponding to the path given, e.g. '/page1/graph1'""" class SettingNode(Node): """A node which corresponds to a setting. Extra properties: val - get or set the setting value corresponding to this value, e.g. Root.page1.graph1.leftMargin.val = '2cm' """ class SettingGroupNode(Node): """A node corresponding to a setting group. No extra properties.""" class WidgetNode(Node): """A node corresponding to a widget. property: widgettype - get Veusz type of widget Methods are below.""" def WalkWidgets(self, widgettype=None): """Generator to walk widget tree and get widgets below this WidgetNode of type given. widgettype is a Veusz widget type name or None to get all widgets.""" def Add(self, widgettype, *args, **args_opt): """Add a widget of the type given, returning the Node instance. """ def Rename(self, newname): """Renames widget to name given. Existing Nodes corresponding to children are no longer valid.""" def Action(self, action): """Applies action on widget.""" def Remove(self): """Removes a widget and its children. Existing Nodes corresponding to children are no longer valid.""" Note that Nodes are temporary objects which are created on the fly. A real widget in Veusz can have several different WidgetNode objects. The operators == and != can test whether a Node points to the same widget, setting or setting group. Here is an example to set all functions in the document to be ``x**2``: .. code-block:: python for n in Root.WalkWidgets(widgettype='function'): n.function.val = 'x**2' Translating old to new style ---------------------------- Here is an example how you might translate the old to new style interface (this is taken from the ``sin.vsz`` example). .. code-block:: python # old (from saved document file) Add('page', name='page1') To('page1') Add('graph', name='graph1', autoadd=False) To('graph1') Add('axis', name='x') To('x') Set('label', '\\\\italic{x}') To('..') Add('axis', name='y') To('y') Set('label', 'sin \\\\italic{x}') Set('direction', 'vertical') To('..') Add('xy', name='xy1') To('xy1') Set('MarkerFill/color', 'cyan') To('..') Add('function', name='function1') To('function1') Set('function', 'sin(x)') Set('Line/color', 'red') To('..') To('..') To('..') .. code-block:: python # new (in python) import veusz.embed embed = veusz.embed.Embedded('window title') page = embed.Root.Add('page') # note: autoAdd=False stops graph automatically adding own axes (used in saved files) graph = page.Add('graph', autoadd=False) x = graph.Add('axis', name='x') x.label.val = '\\\\italic{x}' y = graph.Add('axis', name='y') y.direction.val = 'vertical' xy = graph.Add('xy') xy.MarkerFill.color.val = 'cyan' func = graph.Add('function') func.function.val = 'sin(x)' func.Line.color.val = 'red' PyQt programs ============= There is no direct PyQt interface. The standard embedding interface should work, however. Non Python programs =================== Support for non Python programs is available in a limited form. External programs may execute Veusz using :command:`veusz --listen`. Veusz will read its input from the standard input, and write output to standard output. This is a full Python execution environment, and supports all the scripting commands mentioned in :ref:`Commands `, a :command:`Quit()` command, the :command:`EnableToolbar()` and the :command:`Zoom(factor)` command listed above. Only one window is supported at once, but many :command:`veusz --listen` programs may be started. :command:`veusz --listen` may be used from the shell command line by doing something like: .. code-block:: bash veusz --listen < in.vsz where :command:`in.vsz` contains: .. code-block:: python To(Add('page') ) To(Add('graph') ) SetData('x', arange(20)) SetData('y', arange(20)**2) Add('xy') Zoom(0.5) Export("foo.pdf") Quit() A program may interface with Veusz in this way by using the :command:`popen` C Unix function, which allows a program to be started having control of its standard input and output. Veusz can then be controlled by writing commands to an input pipe. veusz-3.0.1/Documents/manual-source/index.rst0000664000175000017500000000141013242310603020654 0ustar jssjss00000000000000.. veusz documentation master file, created by sphinx-quickstart on Sun Feb 5 11:07:00 2017. You can adapt this file completely to your liking, but it should at least contain the root `toctree` directive. Veusz documentation =================== Jeremy Sanders Copyright 2018 This document is licensed under the GNU General Public License, version 2 or greater. Please see the file COPYING for details, or see ``_. This is the documentation for Veusz. Veusz is a multiplatform scientific plotting package with a graphical user interface. Contents: .. toctree:: :maxdepth: 3 introduction.rst datasets.rst api.rst Indices and tables ================== * :ref:`genindex` * :ref:`modindex` * :ref:`search` veusz-3.0.1/Documents/manual-source/introduction.rst0000664000175000017500000011023613305004723022300 0ustar jssjss00000000000000============ Introduction ============ Veusz ===== Veusz is a 2D and 3D scientific plotting package. It is designed to be easy to use, easily extensible, but powerful. The program features a graphical user interface (GUI), which works under Unix/Linux, Windows or Mac OS. It can also be easily scripted (the saved file formats are similar to Python scripts) or used as module inside Python. Veusz reads data from a number of different types of data file, it can be manually entered, or constructed from other datasets. In Veusz the document is built in an object-oriented fashion, where a document is built up by a number of widgets in a hierarchy. For example, multiple function or xy widgets can be placed inside a graph widget, and many graphs can be placed in a grid widget. The program also supports a variety of 3D plots, including 3D point and surface plots. The program produces vector rather than rastered 3D output. Veusz can be extended by the user easily by adding plugins. Support for different data file types can be added with import plugins. Dataset plugins automate the manipulation of datasets. Tools plugins automate the manipulation of the document. Installation ============ Please go to the website_ of Veusz to learn more about the program. Links to binaries, distribution packages and the source package can be found in downloads_. For source installation, please see the package INSTALL. .. _website: https://veusz.github.io/ .. _downloads: https://veusz.github.io/download/ Getting started =============== Veusz includes a built-in tutorial which starts the first time the program is run. You can rerun it later from the Help menu. It also includes many examples_, to show how certain kinds of plots are produced. For more help and link to a video tutorial, see help_. .. _examples: https://veusz.github.io/examples/ .. _help: https://veusz.github.io/help-support/ Terminology =========== Here we define some terminology for future use. Widget ------ A document and its graphs are built up from widgets. These widgets can often by placed within each other, depending on the type of the widget. A widget has children (those widgets placed within it) and its parent. The widgets have a number of different settings which modify their behaviour. These settings are divided into properties, which affect what is plotted and how it is plotted. These would include the dataset being plotted or whether an axis is logarithmic. There are also formatting settings, including the font to be used and the line thickness. In addition they have actions, which perform some sort of activity on the widget or its children, like "fit" for a fit widget. As an aside, using the scripting interface, widgets are specified with a "path", like a file in Unix or Windows. These can be relative to the current widget (do not start with a slash), or absolute (start with a slash). Examples of paths include, `/page1/graph1/x`, `x` and `.`. The widget types include #. :command:`document` - representing a complete document. A document can contain pages. In addition it contains a setting giving the page size for the document. #. :command:`page` - representing a page in a document. One or more graphs can be placed on a page, or a grid. #. :command:`graph` - defining an actual graph. A graph can be placed on a page or within a grid. Contained within the graph are its axes and plotters. A graph can be given a background fill and a border if required. It also has a margin, which specifies how far away from the edge of its parent widget to plot the body of the graph. A graph can contain several axes, at any position on the plot. In addition a graph can use axes defined in parent widgets, shared with other graphs. More than one graph can be placed within in a page. The margins can be adjusted so that they lie within or besides each other. #. :command:`grid` - containing one or more graphs. A grid plots graphs in a gridlike fashion. You can specify the number of rows and columns, and the plots are automatically replotted in the chosen arrangement. A grid can contain graphs or axes. If an axis is placed in a grid, it can be shared by the graphs in the grid. #. :command:`axis` - giving the scale for plotting data. An axis translates the coordinates of the data to the screen. An axis can be linear or logarithmic, it can have fixed endpoints, or can automatically get them from the plotted data. It also has settings for the axis labels and lines, tick labels, and major and minor tick marks. An axis may be "horizontal" or "vertical" and can appear anywhere on its parent graph or grid. If an axis appears within a grid, then it can be shared by all the graphs which are contained within the grid. The :command:`axis-broken` widget is an axis sub-type. It is an axis type where there are jumps in the scale of the axis. The :command:`axis-function` widget allows the user to create an axis where the values are scaled by a monotonic function, allowing non-linear and non-logarithmic axis scales. The widget can also be linked to a different axis via the function. #. plotters - types of widgets which plot data or add other things on a graph. There is no actual plotter widget which can be added, but several types of plotters listed below. Plotters typically take an axis as a setting, which is the axis used to plot the data on the graph (default x and y). #. :command:`function` - a plotter which plots a function on the graph. Functions can be functions of x or y (parametric functions are not done yet!), and are defined in Python expression syntax, which is very close to most other languages. For example `3*x**2 + 2*x - 4`. A number of functions are available (e.g. sin, cos, tan, exp, log...). Technically, Veusz imports the numpy package when evaluating, so numpy functions are available. As well as the function setting, also settable is the line type to plot the function, and the number of steps to evaluate the function when plotting. Filling is supported above/below/left/right of the function. #. :command:`xy` - a plotter which plots scatter, line, or stepped plots. This versatile plotter takes an x and y dataset, and plots (optional) points, in a chosen marker and colour, connecting them with (optional) lines, and plotting (optional) error bars. An xy plotter can also plot a stepped line, allowing histograms to be plotted (note that it doesn't yet do the binning of the data). The settings for the xy widget are the various attributes for the points, line and error bars, the datasets to plot, and the axes to plot on. The xy plotter can plot a label next to each dataset, which is either the same for each point or taken from a text dataset. If you wish to leave gaps in a plot, the input value `nan` can be specified in the numeric dataset. #. :command:`fit` - fit a function to data. This plotter is a like the function plotter, but allows fitting of the function to data. This is achieved by clicking on a "fit" button, or using the "fit" action of the widget. The fitter takes a function to fit containing the unknowns, e.g. `a*x**2 + b*x + c`, and initial values for the variables (here a, b and c). It then fits the data (note that at the moment, the fit plotter fits all the data, not just the data that can be seen on the graph) by minimising the chi-squared. In order to fit properly, the y data (or x, if fitting as a function of x) must have a properly defined, preferably symmetric error. If there is none, Veusz assumes the same fractional error everywhere, or symmetrises asymmetric errors. Note that more work is required in this widget, as if a parameter is not well defined by the data, the matrix inversion in the fit will fail. In addition Veusz does not supply estimates for the errors or the final chi-squared in a machine readable way. If the fitting parameters vary significantly from 1, then it is worth "normalizing" them by adding in a factor in the fit equation to bring them to of the order of 1. #. :command:`bar` - a bar chart which plots sets of data as horizontal or vertical bars. Multiple datasets are supported. In "grouped" mode the bars are placed side-by-side for each dataset. In "stacked" mode the bars are placed on top of each other (in the appropriate direction according to the sign of the dataset). Bars are placed on coordinates given, or in integer values from 1 upward if none are given. Error bars are plotted for each of the datasets. Different fill styles can be given for each dataset given. A separate key value can be given for each dataset. #. :command:`key` - a box which describes the data plotted. If a key is added to a plot, the key looks for "key" settings of the other data plotted within a graph. If there any it builds up a box containing the symbol and line for the plotter, and the text in the "key" setting of the widget. This allows a key to be very easily added to a plot. The key may be placed in any of the corners of the plot, in the centre, or manually placed. Depending on the ordering of the widgets, the key will be placed behind or on top of the widget. The key can be filled and surrounded by a box, or not filled or surrounded. #. :command:`label` - a text label places on a graph. The alignment can be adjusted and the font changed. The position of the label can be specified in fractional terms of the current graph, or using axis coordinates. #. :command:`rect, ellipse` - these draw a rectangle or ellipse, respectively, of size and rotation given. These widgets can be placed directly on the page or on a graph. The centre can be given in axis coordinates or fractional coordinates. #. :command:`imagefile` - draw an external graphs file on the graph or page, with size and rotation given. The centre can be given in axis coordinates or fractional coordinates. #. :command:`line` - draw a line with optional arrowheads on the graph or page. One end can be given in axis coordinates or fractional coordinates. #. :command:`contour` - plot contours of a 2D dataset on the graph. Contours are automatically calculated between the minimum and maximum values of the graph or chosen manually. The line style of the contours can be chosen individually and the region between contours can be filled with shading or color. 2D datasets currently consist of a regular grid of values between minimum and maximum positions in x and y. They can be constructed from three 1D datasets of x, y and z if they form a regular x, y grid. #. :command:`image` - plot a 2D dataset as a colored image. Different color schemes can be chosen. The scaling between the values and the image can be specified as linear, logarithmic, square-root or square. #. :command:`polygon` - plot x and y points from datasets as a polygon. The polygon can be placed directly on the page or within a graph. Coordinates are either plotted using the axis or as fractions of the width and height of the containing widget. #. :command:`boxplot` - plot distribution of points in a dataset. #. :command:`polar` - plot polar data or functions. This is a non-orthogonal plot and is placed directly on the page rather than in a graph. #. :command:`ternary` - plot data of three variables which add up to 100 per cent.This is a non-orthogonal plot and is placed directly on the page rather than in a graph. #. 3D widgets - 3D graphs can be created by adding a 3D scene widget (:command:`scene3d`) to a blank page, or by creating a new 3D document. The 3D scene has settings which control the angle the rotation angle of the plot, the position and color of lighting and the rendering method. To build up a 3D plot the following widgets can be placed inside it: #. :command:`graph3d` - this is an analogous widget to the 2D graph widget, plotting a 3D plot with cartesian axes. It contains three or more axis3d widgets, and plotting widgets. The graph contains settings for the graph size (the default is 1 in each direction) and the 3D position of the graph in the same units. Multiple graph widgets can be added to a scene, though the position and sizes may need to be adjusted. #. :command:`axis3d` - normally a 3D graph has three axes (X, Y and Z), but more axes can be added to plot multiple things on a single axis direction. This works in a similar way to the 2D axis widget. The widget has options for the axis label, tick labels, tick marks and grid lines (which appear on the outside of the 3D cube). An axis can be swiched between linear and logorithmic mode. Scalings can be applied to the data values plotted in that dimension or to the axis labels. #. :command:`point3d` - for plotting points, and optionally connecting lines, in 3D. This, and the other plotting widgets are placed in a graph3d widget. The user provides three 1D datasets for the x, y and z values. The markers can be scaled in size by another optional dataset. The markers can also be colored according to another optional dataset, according to a color map, minimum and maximum. Error bars can be provided for each of the x, y and z datasets. The connecting line can also be colored if a color dataset is provided and a colormap chosen. #. :command:`function3d` - for plotting either a functional line in 3D space or a functional surface. The type of plot is given by the mode parameter. In the case of the line, the x,y,z coordinates can be specified as a function of t, where t goes from 0 to 1, or by giving functions for two of the coordinates as a function of the other. For a surface, the value for x, y or z is given as a function of the other two. In addition, a function returning 0 to 1 can be provided for the color, which specifies the color map value for the surface at each position or the line color. For a 2D surface, the grid lines or surface fill can be hidden or shown. There are also settings giving the number of function evaluations to compute in each direction for a surface, or in one direction for a line. #. :command:`surface3d` - for plotting a two dimensional surface from data values. The user should provide a 2D dataset for the height of a surface. The x, y or z axis for the height and other directions can be chosen. A second 2D dataset can be provided for the color of the surface at each point. Note that the coordinate of the 2D dataset lies at the center of each 2D grid point. The height of the grid at the edge is calculated by linear interpolation. Normally the grid is surrounded by four lines and the surface by two triangles. If a high resolution option is enabled, the each grid point is surrounded by eight lines and the surface drawn by eight triangles. #. :command:`volume3d` - for plotting 3D volumes. In this widget, for a volume described by A×B×C values, then the user should provide four datasets, each containing up to A×B×C values (there can be holes in the representation). Three of the datasets give coordinates of the centers of the 3D cells and the fourth the color of the cell. An example set of datasets would be X=(0,0,0,0,1,1,1,1), Y=(0,0,1,1,0,0,1,1), Z=(0,1,0,1,0,1,0,1), color=(0.1,0.2,0.3,0.4,0.3,0.2,0.1,0). Additionally, the user can provide a transparency dataset, which can be useful for showing or hiding parts of the 3D space. Settings: properties and formatting ----------------------------------- The various settings of the widgets come in a number of types, including integers (e.g. 10), floats (e.g. 3.14), dataset names (`mydata`), expressions (`x+y`), text (`hi there!`), distances (see above), options (`horizontal` or `vertical` for axes). Veusz performs type checks on these parameters. If they are in the wrong format the control to edit the setting will turn red. In the command line, a TypeError exception is thrown. In the GUI, the current page is replotted if a setting is changed when enter is pressed or the user moves to another setting. The settings are split up into formatting settings, controlling the appearance of the plot, or properties, controlling what is plotted and how it is plotted. Default settings, including the default font and line style, and the default settings for any graph widget, can be modified in the "Default styles" dialog box under the "Edit" menu. Default settings are set on a per-document basis, but can be saved into a separate file and loaded. A default default settings file can be given to use for new documents (set in the preferences dialog). Datasets -------- Data are imported into Veusz as a dataset. A dataset is imported from a file, entered manually, set via the command line, or linked to other datasets via an expression or dataset plugin. Each dataset has a unique name in the document. They can be seen in the dataset browser panel, or in the Data, Edit dialog box. To choose the data to be plotted, the user usually selects the dataset in the appropriate setting of a widget. Veusz supports one-dimensional (1D) datasets, which are a list of values with optional error bars. Error bars can either be symmetric or asymmetric. Veusz also supports two-dimensional (2D) data. A 2D dataset is a grid of values, with either a fixed spacing in coordinates, or with arbitrary pixel sizes. An n-dimensional (nD) dataset is an arbitrary matrix of values. These cannot be plotted directly, but subsets can be plotted using python slice syntax to convert to 1D or 2D datasets. In addition to simple numeric datasets, Veusz also supports date-time datasets. For details see the sections on reading data. Also supported are text datasets, which are lists of text strings. Datasets can either be plain lists of values which are stored within the document, or they can be linked to a file, so that the values update if the file is reloaded, or they can be linked to other datasets via expressions or dataset plugins. .. _TextFonts: Text -------------------- Veusz understands a limited set of LaTeX-like formatting for text. There are some differences (for example, `10^23` puts the 2 and 3 into superscript), but it is fairly similar. You should also leave out the dollar signs. Veusz supports superscripts (`^`), subscripts (`_`), brackets for grouping attributes are `{` and `}`. Supported LaTeX symbols include: \\AA, \\Alpha, \\Beta, \\Chi, \\Delta, \\Epsilon, \\Eta, \\Gamma, \\Iota, \\Kappa, \\Lambda, \\Mu, \\Nu, \\Omega, \\Omicron, \\Phi, \\Pi, \\Psi, \\Rho, \\Sigma, \\Tau, \\Theta, \\Upsilon, \\Xi, \\Zeta, \\alpha, \\approx, \\ast, \\asymp, \\beta, \\bowtie, \\bullet, \\cap, \\chi, \\circ, \\cup, \\dagger, \\dashv, \\ddagger, \\deg, \\delta, \\diamond, \\divide, \\doteq, \\downarrow, \\epsilon, \\equiv, \\eta, \\gamma, \\ge, \\gg, \\hat, \\in, \\infty, \\int, \\iota, \\kappa, \\lambda, \\le, \\leftarrow, \\lhd, \\ll, \\models, \\mp, \\mu, \\neq, \\ni, \\nu, \\odot, \\omega, \\omicron, \\ominus, \\oplus, \\oslash, \\otimes, \\parallel, \\perp, \\phi, \\pi, \\pm, \\prec, \\preceq, \\propto, \\psi, \\rhd, \\rho, \\rightarrow, \\sigma, \\sim, \\simeq, \\sqrt, \\sqsubset, \\sqsubseteq, \\sqsupset, \\sqsupseteq, \\star, \\stigma, \\subset, \\subseteq, \\succ, \\succeq, \\supset, \\supseteq, \\tau, \\theta, \\times, \\umid, \\unlhd, \\unrhd, \\uparrow, \\uplus, \\upsilon, \\vdash, \\vee, \\wedge, \\xi, \\zeta. Please request additional characters if they are required (and exist in the unicode character set). Special symbols can be included directly from a character map. Other LaTeX commands are supported. `\\\\` breaks a line. This can be used for simple tables. For example `{a\\\\b} {c\\\\d}` shows `a c` over `b d`. The command `\\frac{a}{b}` shows a vertical fraction a/b. Also supported are commands to change font. The command `\\font{name}{text}` changes the font text is written in to name. This may be useful if a symbol is missing from the current font, e.g. `\\font{symbol}{g}` should produce a gamma. You can increase, decrease, or set the size of the font with `\\size{+2}{text}`, `\\size{-2}{text}`, or `\\size{20}{text}`. Numbers are in points. Various font attributes can be changed: for example, `\\italic{some italic text}` (or use `\\textit` or `\\emph`), `\\bold{some bold text}` (or use `\\textbf`) and `\\underline{some underlined text}`. Example text could include `Area / \\pi (10^{-23} cm^{-2})`, or `\\pi\\bold{g}`. Veusz plots these symbols with Qt's unicode support. You can also include special characters directly, by copying and pasting from a character map application. If your current font does not contain these symbols then you may get a box character. Veusz also supports the evaluation of a Python expression when text is written to the page. Python code is written inside the brackets :command:`%{{ }}%`. Note that the Python evaluation happens before the LaTeX expansion is done. The return value of the expression is converted to text using the Python :command:`str()` function. For example, the expression :command:`%{{2+2}}%` would write :command:`4`. Custom functions and constants are supported when evaluation, in addition to the usual numpy functions. In addition, Veusz defines the following useful functions and values. #. :command:`ENVIRON` is the :command:`os.environ` dict of environment variables. :command:`%{{ENVIRON['USER']}}%` would show the current user in unix. #. :command:`DATE([fmt])` returns the current date, by default in ISO format. fmt is an optional format specifier using :command:`datetime.date.strftime` format specifiers. #. :command:`TIME([fmt])` returns the current date/time, by default in ISO format. fmt is an optional format specifier using :command:`datetime.datetime.strftime` format specifiers. #. :command:`DATA(name[, part])` returns the Veusz dataset with given name. For numeric datasets this is a numpy array. For numeric datasets with errors, part specifies the dataset part to return, i.e. 'data', 'serr', 'perr', 'nerr'. For example, the mean value of a dataset could be shown using :command:`%{{mean(DATA('x'))}}%`. #. :command:`FILENAME()` - returns the current document filename. This can include the directory/folder of the file. Note that the filename is escaped with ESCAPE() so that LaTeX symbols are not expanded when shown. #. :command:`BASENAME()` - returns the current document filename, removing the directory or folder name Note that the filename is escaped with ESCAPE() so that LaTeX symbols are not expanded when shown. #. :command:`ESCAPE(x)` - escapes any LaTeX symbols in x so that they are not interpreted as LaTeX. #. :command:`SETTING(path)` - return the value of the Veusz setting given by the full path, e.g. :command:`%{{SETTING('/page1/width')}}%`. #. :command:`LANG(mapping)` - mapping is a dictionary which maps language names to strings. This returns the string corresponding to the current language. The keys come from the locale names which are the two-letter language codes (e.g. `en` or `fr`), or the full code (e.g. `en_GB` or `de_AT`). The `default` key is used if the language code is not found. An example is :command:`%{{ LANG({'de':'Druck','default':'Pressure'}) }}%`. Measurements ------------ Distances, widths and lengths in Veusz can be specified in a number of different ways. These include absolute distances specified in physical units, e.g. 1cm, 0.05m, 10mm, 5in and 10pt, and relative units, which are relative to the largest dimension of the page, including 5%, 1/20, 0.05. Color theme ----------- From version 1.26, widgets are colored automatically using the color theme. This theme is specified in the main document widget settings. Widgets are given the colors in order given the order in a graph widget. The default theme can be specified in the preferences dialog box. To override a theme, the user can manually specify the individual colors in the custom definitions dialog box. Color `theme1` is used as the first theme color, then `theme2`, etc. Axis numeric scales ------------------- The way in which numbers are formatted in axis scales is chosen automatically. For standard numerical axes, values are shown with the `%Vg` formatting (see below). For date axes, an appropriate date formatting is used so that the interval shown is correct. A format can be given for an axis in the axis number formatting panel can be given to explicitly choose a format. Some examples are given in the drop down axis menu. Hold the mouse over the example for detail. C-style number formatting is used with a few Veusz specific extensions. Text can be mixed with format specifiers, which start with a `%` sign. Examples of C-style formatting include: `%.2f` (decimal number with two decimal places, e.g. 2.01), `%.3e` (scientific formatting with three decimal places, e.g. 2.123e-02), `%g` (general formatting, switching between `%f` and `%e` as appropriate). See ``_ for details. Veusz extensions include `%Ve`, which is like `%e` except it displays scientific notation as written, e.g. 1.2x10^23, rather than 1.2e+23. `%Vg` switches between standard numbers and Veusz scientific notation for large and small numbers. `%VE` using engineering SI suffixes to represent large or small numbers (e.g. 1000 is 1k). Veusz allows dates and times to be formatted using `%VDX` where `X` is one of the formatting characters for strftime (see ``_ for details). These include `a` for an abbreviated weekday name, `A` for full weekday name, `b` for abbreviated month name, `B` for full month name, `c` date and time representation, `d` day of month 01..31, `H` hour as 00..23, `I` hour as 01..12, `j` as day of year 001..366, `m` as month 01..12, `M` minute as 00..59, `p` AM/PM, `S` second 00..61, `U` week number of year 00..53 (Sunday as first day of week), `w` weekday as decimal number 0..6, `W` week number of year (Monday as first day of week), `x` date representation, `X` time representation, `y` year without century 00..99 and `Y` year. `%VDVS` is a special Veusz addon format which shows seconds and fractions of seconds (e.g. 12.2). Three dimensional (3D) plots ---------------------------- When drawing in three dimensions, Veusz builds up a 3D "scene" for the graph from the various plotting widgets, made up of triangles, line segments, points and text. Veusz does not use a standard (e.g. OpenGL) drawing method, but renders the scene itself. The advantage of this is that it can produce vector rather than bitmap or raster output. OpenGL, for example, is based around bitmaps. Veusz applies lighting to the scene. The lighting depends on enabled light sources, which are set in the scene3d widget. Light sources have a color, intensity and position. Note that only the angle of the light to a surface affects its lighting, not its distance. The position of the light is relative to the viewer (camera), not the graph. Positive light coordinates are towards the graph (z), upwards (y) and rightwards (x). Normally each solid surface has an intrinsic color, which can be seen without any lighting. If a light source is enabled, the color of the light is added to the surface color, depending on the reflectivity of the surface. Each surface also has a transparency setting. By default, Veusz uses a naive Painter's Algorithm to draw the scene. It draws from the back of scene to the front. The main problem with this algorithm is that shapes and lines overlapping in depth can be confused as the depth of each object is calculated at only one point. In addition objects may intersect, which is not properly treated. In the scene3d object, the user can switch to a different rendering mode called BSP. In this accurate BSP mode, the objects are split so that they never overlap from any viewing angle. The disadvantage of this mode is that it is slow, uses a lot of memory and produces large output files. We plan in future to add another mode which handles overlaps better and does not unnecessarily split objects. The plot is affected by the viewing angle, which is specified in the scene3d widget settings. The rotation is given be three rotations around lines in X, Y and Z directions (note that these are not the same directions as the X, Y and Z axes!). The X axis runs horizontally on the screen, the Y axis runs vertically, and the Z axis runs along the line of sight. There is also a distance setting, which moves graphs closer to or away from the viewer. At larger distances the effect of perspective reduces, meaning that parts of the plot closer to the viewer are not larger than if they were at the farthest side. At large distances, a plot tends towards being isometric. At small distances, shapes are more distorted (note by default the size of the graph is 1 in these distance units). It is currently possible to place graphs inside the camera leading to strange output. By default, Veusz enlarges the 3D rendered scene to fill the bounds of the 3D scene widget, so distance has no effect on the size of the plot. This scaling can be switched off by modifying the Size setting from "Auto" to a fixed number. A fixed size is useful if the user wants a graph to be the same size for any rotation. With this setting the size of the plot is affected by their distance. By default, a 3D graph has dimensions of 1 along the X, Y and Z axes. The size can be adjusted using the size settings in the graph3d widget. Care should be taken that the graph size does not lead to points being at negative viewing distances. The default position of the plot is at the origin 0,0,0. If the user wants to plot multiple graph3d widgets, the positions should be adjusted to prevent overlap. Normally in Veusz, sizes of objects (e.g. plot markers) are given in physical units. This makes less sense for a 3D plot as sizes can depend on distance. In a 3D graph sizes of plotting markers and line widths are given in 1/1000 of the graph bounding box maximum dimension. The main window =============== You should see the main window when you run Veusz (you can just type the veusz command in Unix). .. image:: _images/mainwindow.png The Veusz window is split into several sections. At the top is the menu bar and tool bar. These work in the usual way to other applications. Sometimes options are disabled (greyed out) if they do not make sense to be used. If you hold your mouse over a button for a few seconds, you will usually get an explanation for what it does called a "tool tip". Below the main toolbar is a second toolbar for constructing the graph by adding widgets (on the left), and some editing buttons. The add widget buttons add the request widget to the currently selected widget in the selection window. The widgets are arranged in a tree-like structure. Below these toolbars and to the right is the plot window. This is where the current page of the current document is shown. You can adjust the size of the plot on the screen (the zoom factor) using the "View" menu or the zoom tool bar button (the magnifying glass). Initially you will not see a plot in the plot window, but you will see the Veusz logo. At the moment you cannot do much else with the window. In the future you will be able to click on items in the plot to modify them. To the left of the plot window is the selection window, and the properties and formatting windows. The properties window lets you edit various aspects of the selected widget (such as the minimum and maximum values on an axis). Changing these values should update the plot. The formatting lets you modify the appearance of the selected widget. There are a series of tabs for choosing what aspect to modify. The various windows can be "dragged" from the main window to "float" by themselves on the screen. To the bottom of the window is the console. This window is not shown by default, but can be enabled in the View menu. The console is a Veusz and Python command line console. To read about the commands available see :ref:`Commands `. As this is a Python console, you can enter mathematical expressions (e.g. `1+2.0*cos(pi/4)`) here and they will be evaluated when you press Enter. The usual special functions and the operators are supported. You can also assign results to variables (e.g. `a=1+2`) for use later. The console also supports command history like many Unix shells. Press the up and down cursor keys to browse through the history. Command line completion is not available yet! There also exists a dataset browsing window, by default to the right of the screen. This window allows you to view the datasets currently loaded, their dimensions and type. Hovering a mouse over the size of the dataset will give you a preview of the data. My first plot ============= After opening Veusz, on the left of the main window, you will see a Document, containing a Page, which contains a Graph with its axes. The Graph is selected in the selection window. The toolbar above adds a new widget to the selected widget. If a widget cannot be added to a selected widget it is disabled. On opening a new document Veusz automatically adds a new Page and Graph (with axes) to the document. You will see something like this: .. image:: _images/winwithgraph.png Select the x axis which has been added to the document (click on `x` in the selection window). In the properties window you will see a variety of different properties you can modify. For instance you can enter a label for the axis by writing `Area (cm^{2})` in the box next to label and pressing enter. Veusz supports text in LaTeX-like form (without the dollar signs). Other important parameters is the `log` switch which switches between linear and logarithmic axes, and `min` and `max` which allow the user to specify the minimum and maximum values on the axes. The formatting dialog lets you edit various aspects of the graph appearance. For instance the "Line" tab allows you to edit the line of the axis. Click on "Line", then you can then modify its colour. Enter "green" instead of "black" and press enter. Try making the axis label bold. Now you can try plotting a function on the graph. If the graph, or its children are selected, you will then be able to click the "function" button at the top (a red curve on a graph). You will see a straight line (y=x) added to the plot. If you select "function1", you will be able to edit the functional form plotted and the style of its line. Change the function to `x**2` (x-squared). We will now try plotting data on the graph. Go to your favourite text editor and save the following data as test.dat: :: 1 0.1 -0.12 1.1 0.1 2.05 0.12 -0.14 4.08 0.12 2.98 0.08 -0.1 2.9 0.11 4.02 0.04 -0.1 15.3 1.0 The first three columns are the x data to plot plus its asymmetric errors. The final two columns are the y data plus its symmetric errors. In Veusz, go to the "Data" menu and select "Import". Type the filename into the filename box, or use the "Browse..." button to search for the file. You will see a preview of the data pop up in the box below. Enter `x,+,- y,+-` into the descriptors edit box (note that commas and spaces in the descriptor are almost interchangeable in Veusz 1.6 or newer). This describes the format of the data which describes dataset "x" plus its asymmetric errors, and "y" with its symmetric errors. If you now click "Import", you will see it has imported datasets `x` and `y`. To plot the data you should now click on `graph1` in the tree window. You are now able to click on the "xy" button (which looks like points plotted on a graph). You will see your data plotted on the graph. Veusz plots datasets `x` and `y` by default, but you can change these in the properties of the "xy" plotter. You are able to choose from a variety of markers to plot. You can remove the plot line by choosing the "Plot Line" subsetting, and clicking on the "hide" option. You can change the colour of the marker by going to the "Marker Fill" subsetting, and entering a new colour (e.g. red), into the colour property. veusz-3.0.1/Documents/manual-source/datasets.rst0000664000175000017500000006156013161413406021376 0ustar jssjss00000000000000Reading data ============ Currently Veusz supports reading data from files with text, CSV, HDF5, FITS, 2D text or CSV, QDP, binary and NPY/NPZ formats. Use the :menuselection:`Data --> Import` dialog to read data, or the importing commands in the API can be used. In addition, the user can load or write import plugins in Python which load data into Veusz in an arbitrary format. At the moment QDP, binary and NPY/NPZ files are supported with this method. The HDF5 file format is the most sophisticated, and is recommended for complex datasets. By default, data are "linked" to the file imported from. This means that the data are not stored in the Veusz saved file and are reloaded from the original data file when opening. In addition, the user can use the :menuselection:`Data --> Reload` menu option to reload data from linked files. Unselect the linked option when importing to remove the association with the data file and to store the data in the Veusz saved document. Note that a prefix and suffix can be given when importing. These are added to the front or back of each dataset name imported. They are convenient for grouping data together. .. image:: _images/importdialog.png We list the various types of import below. Standard text import -------------------- The default text import operates on simple text files. The data are assumed to be in columns separated by whitespace. Each column corresponds to dataset (or its error bars). Each row is an entry in the dataset. The way the data are read is goverened by a simple "descriptor". This can simply be a list of dataset names separated by spaces. If no descriptor is given, the columns are treated as separate datasets and are given names `col1`, `col2`, etc. Veusz attempts to automatically determine the type of the data. When reading in data, Veusz treats any whitespace as separating columns. The columns do not actually need to be aligned. Furthermore a `\\` symbol can be placed at the end of a line to mark a continuation. Veusz will read the next line as if it were placed at the end of the current line. In addition comments and blank lines are ignored (unless in block mode). Comments start with a `#`, `;`, `!` or `%`, and continue until the end of the line. The special value `nan` can be used to specify a break in a dataset. If the option to read data in blocks is enabled, Veusz treats blank lines (or lines starting with the word `no`) as block separators. For each dataset in the descriptor, separate datasets are created for each block, using a numeric suffix giving the block number, e.g. `_1`, `_2`. Data types in text import ````````````````````````` Veusz supports reading in several types of data. The type of data can be added in round brackets after the name in the descriptor. Veusz will try to guess the type of data based on the first value, so you should specify it if there is any form of ambiguity (e.g. is 3 text or a number). Supported types are numbers (use numeric in brackets) and text (use text in brackets). An example descriptor would be `x(numeric) +- y(numeric) + - label(text)` for an x dataset followed by its symmetric errors, a y dataset followed by two columns of asymmetric errors, and a final column of text for the label dataset. A text column does not need quotation unless it contains space characters or escape characters. However make sure you deselect the "ignore text" option in the import dialog. This ignores lines of text to ease the import of data from other applications. Quotation marks are recommended around text if you wish to avoid ambiguity. Text is quoted according to the Python rules for text. Double or single quotation marks can be used, e.g. `"A 'test'"`, `'A second "test"'`. Quotes can be escaped by prefixing them with a backslash, e.g. `"A new \\"test\\""`. If the data are generated from a Python script, the repr function provides the text in a suitable form. Dates and times are also supported with the syntax `dataset(date)`. Dates must be in ISO format `YYYY-MM-DD`. Times are in 24 hour format hh:mm:ss.ss. Dates with times are written YYYY-MM-DDThh:mm:ss.ss (this is a standard ISO format, see ``_). Dates are stored within Veusz as a number which is the number of seconds since the start of January 1st 2009. Veusz also supports dates and times in the local format, though take note that the same file and data may not work on a system in a different location. Descriptors ``````````` .. _Descriptors: A list of datasets, or a "Descriptor", is given in the Import dialog to describe how the data are formatted in the import file. The descriptor at its simplest is a space or comma-separated list of the names of the datasets to import. These are columns in the file. Following a dataset name the text `+`, `-`, or `+-` can be given to say that the following column is a positive error bar, negative error bar or symmetric error bar for the previous (non error bar) dataset. These symbols should be separated from the dataset name or previous symbol with a space or a comma symbol. In addition, if multiple numbered columns should be imported, the dataset name can be followed by square brackets containing a range in the form `[a:b]` to number columns a to b, or `[:]` to number remaining columns. See below for examples of this use. Dataset names can contain virtually any character, even unicode characters. If the name contains non alpha-numeric characters (characters outside of A-Z, a-z and 0-9), then the dataset name should be contained within back-tick characters. An example descriptor is :command:`\`length data (m)\`,+- \`speed (mps)\`,+,-`, for two datasets with spaces and brackets in their names. Instead of specifying the descriptor in the Import dialog, the descriptor can be placed in the data file using a descriptor statement on a separate line, consisting of "descriptor" followed by the descriptor. Multiple descriptors can be placed in a single file, for example: :: # here is one section descriptor x,+- y,+,- 1 0.5 2 0.1 -0.1 2 0.3 4 0.2 -0.1 # my next block descriptor alpha beta gamma 1 2 3 4 5 6 7 8 9 # etc... Descriptor examples ``````````````````` #. :command:`x y` two columns are present in the file, they will be read in as datasets `x` and `y`. #. :command:`x,+- y,+,-` or :command:`x +- y + -` two datasets are in the file. Dataset "x" consists of the first two columns. The first column are the values and the second are the symmetric errors. "y" consists of three columns (note the comma between + and -). The first column are the values, the second positive asymmetric errors, and the third negative asymmetric errors. Suppose the input file contains: :: 1.0 0.3 2 0.1 -0.2 1.5 0.2 2.3 2e-2 -0.3E0 2.19 0.02 5 0.1 -0.1 Then x will contain `1+-0.3`, `1.5+-0.2`, `2.19+-0.02`. y will contain `2 +0.1 -0.2`, `2.3 +0.02 -0.3`, `5 +0.1 -0.1`. #. :command:`x[1:2] y[:]` the first column is the data `x_1`, the second `x_2`. Subsequent columns are read as `y[1]` to `y[n]`. #. :command:`y[:]+-` read each pair of columns as a dataset and its symmetric error, calling them `y[1]` to `y[n]`. #. :command:`foo,,+-` read the first column as the foo dataset, skip a column, and read the third column as its symmetric error. CSV files --------- CVS (comma separated variable) files are often written from other programs, such as spreadsheets, including Excel and Gnumeric. Veusz supports reading from these files. In the import dialog choose "CSV", then choose a filename to import from. In the CSV file the user should place the data in either rows or columns. Veusz will use a name above a column or to the left of a row to specify what the dataset name should be. The user can use new names further down in columns or right in rows to specify a different dataset name. Names do not have to be used, and Veusz will assign default `col` and `row` names if not given. You can also specify a prefix which is prepended to each dataset name read from the file. To specify symmetric errors for a column, put `+-` as the dataset name in the next column or row. Asymmetric errors can be stated with `+` and `-` in the columns. The data type in CSV files are automatically detected unless specified. The data type can be given in brackets after the column name, e.g. `name (text)`, where the data type is `date`, `numeric` or `text`. Explicit data types are needed if the data look like a different data type (e.g. a text item of `1.23`). The date format in CSV files can be specified in the import dialog box - see the examples given. In addition CSV files support numbers in European format (e.g. 2,34 rather than 2.34), depending on the setting in the dialog box. HDF5 files ---------- HDF5 is a flexible data format. Datasets and tables can be stored in a hierarchical arrangements of groups within a file. Veusz supports reading 1D numeric, text, date-time, 2D numeric or n-dimensional numeric data from HDF files. The :command:`h5py` Python module must be installed to use HDF5 files (included in binary releases). In the import dialog box, choose which individual datasets to import, or selecting a group to import all the datasets within the group. If selecting a group, datasets in the group incompatible with Veusz are ignored. A name can be provided for each dataset imported by entering one under "Import as". If one is not given, the dataset or column name is used. The name can also be specified by setting the HDF5 dataset attribute ``vsz_name`` to the name. Note that for compound datasets (tables), ``vsz_`` attributes for columns are given by appending the suffix ``_columnname`` to the attribute. Error bars `````````` Error bars are supported in two ways. The first way is to combine 1D datasets. For the datasets which are error bars, use a name which is the same as the main dataset but with the suffix `(+-)`, `(+)` or `(-)`, for symmetric, postive or negative error bars, respectively. The second method is to use a 2D dataset with two or three columns, for symmetric or asymmetric error bars, respectively. Click on the dataset in the dialog and choose the option to import as a 1D dataset. This second method can also be enabled by adding an HDF5 attribute ``vsz_twod_as_oned`` set to a non-zero value for the dataset. Slices `````` You may wish to reduce the dimensions of a dataset before importing by slicing. You can also give a slice to import a subset of a dataset. When importing, in the slice column you can give a slice expression. This should have the same number of entries as the dataset has dimensions, separated by commas. An entry can be a single number, to select a particular row or column. Alternatively it could be an expression like ``a:b:c`` or ``a:b``, where ``a`` is the starting index, ``b`` is one beyond the stopping index and optionally ``c`` is the step size. A slice can also be specified by providing an HDF5 attribute ``vsz_slice`` for the dataset. 2D data ranges `````````````` 2D data have an associated X and Y range. By default the number of pixels of the image are used to give this range. A range can be specified by clicking on the dataset and entering a minimum and maximum X and Y coordinates. Alternatively, provide the HDF5 attribute for the dataset ``vsz_range``, which should be set to an array of four values (minimum x, minimum y, maximum x, maximum y). Dates ````` Date/time datasets can be made from a 1D numeric dataset or from a text dataset. For the 1D dataset, use the number of seconds relative to the start of the year 2009 (this is Veusz format) or the year 1970 (this is Unix format). In the import dialog, click on the name of the dataset and choose the date option. To specify a date format in the HDF5 file, set the attribute ``vsz_convert_datetime`` to either ``veusz`` or ``unix``. For text datasets, dates must be given in the right format, selected in the import dialog after clicking on the dataset name. As in other file formats, by default Veusz uses ISO 8601 format, which looks like `2013-12-22T21:08:07`, where the date and time parts are optional. The T is also optional. You can also provide your own format when importing by giving a date expression using YYYY, MM, DD, hh, mm and ss (e.g. `YYYY-MM-DD|T|hh:mm:ss`), where vertical bars mark optional parts of the expression. To automate this, set the attribute ``vsz_convert_datetime`` to the format expression or ``iso`` to specify ISO format. 2D text or CSV format --------------------- Veusz can import 2D data from standard text or CSV files. In this case the data should consist of a matrix of data values, with the columns separated by one or more spaces or tabs and the rows on different lines. In addition to the data the file can contain lines at the top which affect the import. Such specifiers are used, for example, to change the coordinates of the pixels in the file. By default the first pixels coordinates is between 0 and 1, with the centre at 0.5. Subsequent pixels are 1 greater. Note that the lowest coordinate pixel is the bottom-left value in the table of imported values. When using specifiers in CSV files, put the different parts (separated by spaces) in separate columns. Below are listed the specifiers: #. :command:`xrange A B` - make the 2D dataset span the coordinate range A to B in the x-axis (where A and B are numbers). Note that the range is inclusive, so a 1 pixel wide image with A=0 and B=1 would have the pixel centre at 0.5. The pixels are assumed to have the same spacing. Do not use this as the same time as the :command:`xedge` or :command:`xcent` options. #. :command:`yrange A B` - make the 2D dataset span the coordinate range A to B in the y-axis (where A and B are numbers). #. :command:`xedge A B C...` - rather than assume the pixels have the same spacing, give the coordinates of the edges of the pixels in the x-axis. The numbers should be space-separated and there should be one more number than pixels. Do not give :command:`xrange` or :command:`xcent` if this is given. If the values are increasing, the lowest coordinate value is at the left of the dataset, otherwise if they are decreasing, it is on the right (unless the rows/columns are inverted or transposed). #. :command:`yedge A B C...` - rather than assume the pixels have the same spacing, give the coordinates of the edges of the pixels in the y-axis. If the values are increasing, the lowest coordinate value is at the bottom row. If they instead decrease, it is at the top. #. :command:`xcent A B C...` - rather than give a total range or pixel edges, give the centres of the pixels. There should be the same number of values as pixels in the image. Do not give :command:`xrange` or :command:`xedge` if this is given. The order of the values specify whether the pixels are left to right or right to left. #. :command:`ycent A B C...` - rather than give a total range or pixel edges, give the centres of the pixels. The value order specifies whether the pixels are bottom to top, or top to bottom. #. :command:`invertrows` - invert the rows after reading the data. #. :command:`invertcols` - invert the columns after reading the data. #. :command:`transpose` - swap rows and columns after importing data. #. :command:`gridatedge` - the first row and leftmost column give the positions of the centres of the pixels. This is also an option in the import dialog. The values should be increasing or decreasing. FITS files ---------- 1D, 2D or n-dimensional data can be read from FITS files. 1D or 2D data can be read from image, primary or table HDUs. nD data can be read from from image or primary extensions. Note that pyfits or astropy must be installed to get FITS support. The import dialog box uses a tree to show the structure of the FITS file. The user can choose to import the whole file, by clicking the check box at the top. They can import data from a particular HDU by selecting that, or individual table columns can be selected. In the dialog box, a dataset can be given a name for the dataset. Otherwise the HDU or table column name is used. Note that a prefix and/or suffix can be specified to be added to all dataset names. If dataset y should have an error bar specified by column yerr, then in the name for yerr, enter 'y (+-)'. Asymmetric error bars can be specified using (+) and (-) on inidividual columns. The slice column can be used to only import a subset of the dataset imported. This uses Python slicing syntax, which is comma-separated list of ranges and steps. A range is specified like 10:20, which selects the 11th to 20th items (the indices are numbered from 0, and the final index is one past the index you actually want). A stepped range can look like 10:20:2, which selects every other item in that range. Each of these numbers are optional, so : selects all items on that dimension. For example the slice :,10:14:2 selects all values on the first dimension, but only the 11th and 13th items on the next axis. When importing 2D data the user can specify whether to treat this as 1D plus error bars (dimensions should have 2 or 3 columns), or specify a range in 2D space the data covers. Veusz will also attempt to use WCS information in the file for the 2D range if not specified. The standard mode is to use the CDELT, CRVAL and CRPIX keywords to specify a linear range for the data. Alternatively the user can specify pixel numbering (numbering from 0 to N-1). There is a fraction option for using a range of 0 to 1. Finally there is a pixel numbering scheme which numbers in pixels from the CRPIX keyword items. Some of these options can be specified in the FITS file using the 'VEUSZ' header keyword. This header keyword can be added with the value 'KEY=VALUE' (applying to the whole HDU) or 'COLUMN: KEY=VALUE' (applying to a particular column in a table). Supported options for KEY are: name provide name for dataset in VALUE slice VALUE is slice to apply when importing dataset range range of data for 2D dataset in form `[minx, miny, maxx, maxy]` xrange/yrange range of dataset individually in x or y xcent/ycent set to list of values giving centers of pixels xedge/yedge set to list of values giving edges of pixels twod_as_oned treat as 1D data with error bars if VALUE=1 wcsmode use specific WCS mode for 2D dataset (should be pixel/pixel_wcs/linear_wcs/fraction) Reading other data formats -------------------------- As mentioned above, a user may write some Python code to read a data file or set of data files. To write a plugin which is incorportated into Veusz, see ``_ You can also include Python code in an input file to read data, which we describe here. Suppose an input file "in.dat" contains the following data: :: 1 2 2 4 3 9 4 16 Of course this data could be read using the :ref:`ImportFile ` command. However, you could also read it with the following Veusz script (which could be saved to a file and loaded with :command:`execfile` or :ref:`Load `. The script also places symmetric errors of 0.1 on the x dataset. .. code-block:: python x = [] y = [] for line in open("in.dat"): parts = [float(i) for i in line.split()] x.append(parts[0]) y.append(parts[1]) SetData('x', x, symerr=0.1) SetData('y', y) Manipulating datasets ===================== Imported datasets can easily be modified in the Data Editor dialog box. This dialog box can also be used to create new datasets from scratch by typing them in. The Data Create dialog box is used to new datasets as a numerical sequence, parametrically or based on other datasets given expressions. If you want to plot a function of a dataset, you often do not have to create a new dataset. Veusz allows to enter expressions directly in many places. Using dataset plugins --------------------- Dataset plugins can be used to perform arbitrary manipulation of datasets. Veusz includes several plugins for mathematical operation of data and other dataset manipulations, such as concatenation or splitting. If you wish to write your own plugins look at ``_. Using expressions to create new datasets ---------------------------------------- For instance, if the user has already imported dataset d, then they can create d2 which consists of d**2. Expressions are in Python numpy syntax and can include the usual mathematical functions. .. image:: _images/createdataset.png Expressions for error bars can also be given. By appending :command:`_data`, :command:`_serr`, :command:`_perr` or :command:`_nerr` to the name of the dataset in the expression, the user can base their expression on particular parts of the given dataset (the main data, symmetric errors, positive errors or negative errors). Otherwise the program uses the same parts as is currently being specified. If a dataset name contains non alphanumeric characters, its name should be quoted in the expression in back-tick characters, e.g. :command:`\`length (cm)\`*2`. The numpy functionality is particularly useful for doing more complicated expressions. For instance, a conditional expression can be written as :command:`where(x`_ for details. Capturing data ============== In addition to the standard data import, data can be captured as it is created from an external program, a network socket or a file or named pipe. When capturing from a file, the behaviour is like the Unix :command:`tail -f` command, where new lines written to the file are captured. To use the capturing facility, the data must be written in the simple line based standard Veusz text format. Data are whitespace separated, with one value per dataset given on a single line. To capture data, use the dialog box :menuselection:`Data --> Capture`. A list of datasets should be given. This is the :ref:`standard descriptor format `. Choose the source of the data, which is either a a filename or named pipe, a network socket to connect to, or a command line for an external program. Capturing ends if the source of the data runs out (for external programs or network sockets) or the finish button is clicked. It can optionally end after a certain number of data lines or when a time period has expired. Normally the data are updated in Veusz when the capturing is finished. There is an option to update the document at intervals, which is useful for monitoring. A plot using the variables will update when the data are updated. Click the ``Capture`` button to start the capture. Click ``Finish`` or ``Cancel`` to stop. Cancelling destroys captured data. veusz-3.0.1/Documents/manual-source/make.bat0000664000175000017500000001612213161413406020433 0ustar jssjss00000000000000@ECHO OFF REM Command file for Sphinx documentation if "%SPHINXBUILD%" == "" ( set SPHINXBUILD=sphinx-build ) set BUILDDIR=..\manual set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% . set I18NSPHINXOPTS=%SPHINXOPTS% . if NOT "%PAPER%" == "" ( set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS% ) if "%1" == "" goto help if "%1" == "help" ( :help echo.Please use `make ^` where ^ is one of echo. html to make standalone HTML files echo. dirhtml to make HTML files named index.html in directories echo. singlehtml to make a single large HTML file echo. pickle to make pickle files echo. json to make JSON files echo. htmlhelp to make HTML files and a HTML help project echo. qthelp to make HTML files and a qthelp project echo. devhelp to make HTML files and a Devhelp project echo. epub to make an epub echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter echo. text to make text files echo. man to make manual pages echo. texinfo to make Texinfo files echo. gettext to make PO message catalogs echo. changes to make an overview over all changed/added/deprecated items echo. xml to make Docutils-native XML files echo. pseudoxml to make pseudoxml-XML files for display purposes echo. linkcheck to check all external links for integrity echo. doctest to run all doctests embedded in the documentation if enabled echo. coverage to run coverage check of the documentation if enabled goto end ) if "%1" == "clean" ( for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i del /q /s %BUILDDIR%\* goto end ) REM Check if sphinx-build is available and fallback to Python version if any %SPHINXBUILD% 1>NUL 2>NUL if errorlevel 9009 goto sphinx_python goto sphinx_ok :sphinx_python set SPHINXBUILD=python -m sphinx.__init__ %SPHINXBUILD% 2> nul if errorlevel 9009 ( echo. echo.The 'sphinx-build' command was not found. Make sure you have Sphinx echo.installed, then set the SPHINXBUILD environment variable to point echo.to the full path of the 'sphinx-build' executable. Alternatively you echo.may add the Sphinx directory to PATH. echo. echo.If you don't have Sphinx installed, grab it from echo.http://sphinx-doc.org/ exit /b 1 ) :sphinx_ok if "%1" == "html" ( %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html if errorlevel 1 exit /b 1 echo. echo.Build finished. The HTML pages are in %BUILDDIR%/html. goto end ) if "%1" == "dirhtml" ( %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml if errorlevel 1 exit /b 1 echo. echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. goto end ) if "%1" == "singlehtml" ( %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml if errorlevel 1 exit /b 1 echo. echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml. goto end ) if "%1" == "pickle" ( %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can process the pickle files. goto end ) if "%1" == "json" ( %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can process the JSON files. goto end ) if "%1" == "htmlhelp" ( %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can run HTML Help Workshop with the ^ .hhp project file in %BUILDDIR%/htmlhelp. goto end ) if "%1" == "qthelp" ( %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can run "qcollectiongenerator" with the ^ .qhcp project file in %BUILDDIR%/qthelp, like this: echo.^> qcollectiongenerator %BUILDDIR%\qthelp\veusz.qhcp echo.To view the help file: echo.^> assistant -collectionFile %BUILDDIR%\qthelp\veusz.ghc goto end ) if "%1" == "devhelp" ( %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp if errorlevel 1 exit /b 1 echo. echo.Build finished. goto end ) if "%1" == "epub" ( %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub if errorlevel 1 exit /b 1 echo. echo.Build finished. The epub file is in %BUILDDIR%/epub. goto end ) if "%1" == "latex" ( %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex if errorlevel 1 exit /b 1 echo. echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. goto end ) if "%1" == "latexpdf" ( %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex cd %BUILDDIR%/latex make all-pdf cd %~dp0 echo. echo.Build finished; the PDF files are in %BUILDDIR%/latex. goto end ) if "%1" == "latexpdfja" ( %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex cd %BUILDDIR%/latex make all-pdf-ja cd %~dp0 echo. echo.Build finished; the PDF files are in %BUILDDIR%/latex. goto end ) if "%1" == "text" ( %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text if errorlevel 1 exit /b 1 echo. echo.Build finished. The text files are in %BUILDDIR%/text. goto end ) if "%1" == "man" ( %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man if errorlevel 1 exit /b 1 echo. echo.Build finished. The manual pages are in %BUILDDIR%/man. goto end ) if "%1" == "texinfo" ( %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo if errorlevel 1 exit /b 1 echo. echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo. goto end ) if "%1" == "gettext" ( %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale if errorlevel 1 exit /b 1 echo. echo.Build finished. The message catalogs are in %BUILDDIR%/locale. goto end ) if "%1" == "changes" ( %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes if errorlevel 1 exit /b 1 echo. echo.The overview file is in %BUILDDIR%/changes. goto end ) if "%1" == "linkcheck" ( %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck if errorlevel 1 exit /b 1 echo. echo.Link check complete; look for any errors in the above output ^ or in %BUILDDIR%/linkcheck/output.txt. goto end ) if "%1" == "doctest" ( %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest if errorlevel 1 exit /b 1 echo. echo.Testing of doctests in the sources finished, look at the ^ results in %BUILDDIR%/doctest/output.txt. goto end ) if "%1" == "coverage" ( %SPHINXBUILD% -b coverage %ALLSPHINXOPTS% %BUILDDIR%/coverage if errorlevel 1 exit /b 1 echo. echo.Testing of coverage in the sources finished, look at the ^ results in %BUILDDIR%/coverage/python.txt. goto end ) if "%1" == "xml" ( %SPHINXBUILD% -b xml %ALLSPHINXOPTS% %BUILDDIR%/xml if errorlevel 1 exit /b 1 echo. echo.Build finished. The XML files are in %BUILDDIR%/xml. goto end ) if "%1" == "pseudoxml" ( %SPHINXBUILD% -b pseudoxml %ALLSPHINXOPTS% %BUILDDIR%/pseudoxml if errorlevel 1 exit /b 1 echo. echo.Build finished. The pseudo-XML files are in %BUILDDIR%/pseudoxml. goto end ) :end veusz-3.0.1/Documents/manual-source/Makefile0000664000175000017500000001675713161413406020504 0ustar jssjss00000000000000# Makefile for Sphinx documentation # # You can set these variables from the command line. SPHINXOPTS = SPHINXBUILD = sphinx-build PAPER = BUILDDIR = ../manual # User-friendly check for sphinx-build ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1) $(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/) endif # Internal variables. PAPEROPT_a4 = -D latex_paper_size=a4 PAPEROPT_letter = -D latex_paper_size=letter ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . # the i18n builder cannot share the environment and doctrees with the others I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . .PHONY: help help: @echo "Please use \`make ' where is one of" @echo " html to make standalone HTML files" @echo " dirhtml to make HTML files named index.html in directories" @echo " singlehtml to make a single large HTML file" @echo " pickle to make pickle files" @echo " json to make JSON files" @echo " htmlhelp to make HTML files and a HTML help project" @echo " qthelp to make HTML files and a qthelp project" @echo " applehelp to make an Apple Help Book" @echo " devhelp to make HTML files and a Devhelp project" @echo " epub to make an epub" @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" @echo " latexpdf to make LaTeX files and run them through pdflatex" @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx" @echo " text to make text files" @echo " man to make manual pages" @echo " texinfo to make Texinfo files" @echo " info to make Texinfo files and run them through makeinfo" @echo " gettext to make PO message catalogs" @echo " changes to make an overview of all changed/added/deprecated items" @echo " xml to make Docutils-native XML files" @echo " pseudoxml to make pseudoxml-XML files for display purposes" @echo " linkcheck to check all external links for integrity" @echo " doctest to run all doctests embedded in the documentation (if enabled)" @echo " coverage to run coverage check of the documentation (if enabled)" .PHONY: clean clean: rm -rf $(BUILDDIR)/* .PHONY: html html: $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." .PHONY: dirhtml dirhtml: $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." .PHONY: singlehtml singlehtml: $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml @echo @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." .PHONY: pickle pickle: $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle @echo @echo "Build finished; now you can process the pickle files." .PHONY: json json: $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json @echo @echo "Build finished; now you can process the JSON files." .PHONY: htmlhelp htmlhelp: $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp @echo @echo "Build finished; now you can run HTML Help Workshop with the" \ ".hhp project file in $(BUILDDIR)/htmlhelp." .PHONY: qthelp qthelp: $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp @echo @echo "Build finished; now you can run "qcollectiongenerator" with the" \ ".qhcp project file in $(BUILDDIR)/qthelp, like this:" @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/mbproj2.qhcp" @echo "To view the help file:" @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/mbproj2.qhc" .PHONY: applehelp applehelp: $(SPHINXBUILD) -b applehelp $(ALLSPHINXOPTS) $(BUILDDIR)/applehelp @echo @echo "Build finished. The help book is in $(BUILDDIR)/applehelp." @echo "N.B. You won't be able to view it unless you put it in" \ "~/Library/Documentation/Help or install it in your application" \ "bundle." .PHONY: devhelp devhelp: $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp @echo @echo "Build finished." @echo "To view the help file:" @echo "# mkdir -p $$HOME/.local/share/devhelp/mbproj2" @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/mbproj2" @echo "# devhelp" .PHONY: epub epub: $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub @echo @echo "Build finished. The epub file is in $(BUILDDIR)/epub." .PHONY: latex latex: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." @echo "Run \`make' in that directory to run these through (pdf)latex" \ "(use \`make latexpdf' here to do that automatically)." .PHONY: latexpdf latexpdf: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo "Running LaTeX files through pdflatex..." $(MAKE) -C $(BUILDDIR)/latex all-pdf @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." .PHONY: latexpdfja latexpdfja: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo "Running LaTeX files through platex and dvipdfmx..." $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." .PHONY: text text: $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text @echo @echo "Build finished. The text files are in $(BUILDDIR)/text." .PHONY: man man: $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man @echo @echo "Build finished. The manual pages are in $(BUILDDIR)/man." .PHONY: texinfo texinfo: $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo @echo @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." @echo "Run \`make' in that directory to run these through makeinfo" \ "(use \`make info' here to do that automatically)." .PHONY: info info: $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo @echo "Running Texinfo files through makeinfo..." make -C $(BUILDDIR)/texinfo info @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." .PHONY: gettext gettext: $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale @echo @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." .PHONY: changes changes: $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes @echo @echo "The overview file is in $(BUILDDIR)/changes." .PHONY: linkcheck linkcheck: $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck @echo @echo "Link check complete; look for any errors in the above output " \ "or in $(BUILDDIR)/linkcheck/output.txt." .PHONY: doctest doctest: $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest @echo "Testing of doctests in the sources finished, look at the " \ "results in $(BUILDDIR)/doctest/output.txt." .PHONY: coverage coverage: $(SPHINXBUILD) -b coverage $(ALLSPHINXOPTS) $(BUILDDIR)/coverage @echo "Testing of coverage in the sources finished, look at the " \ "results in $(BUILDDIR)/coverage/python.txt." .PHONY: xml xml: $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml @echo @echo "Build finished. The XML files are in $(BUILDDIR)/xml." .PHONY: pseudoxml pseudoxml: $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml @echo @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml." veusz-3.0.1/Documents/man-page/0000775000175000017500000000000013325026667015751 5ustar jssjss00000000000000veusz-3.0.1/Documents/man-page/veusz.man.txt0000664000175000017500000000772213325026533020440 0ustar jssjss00000000000000VEUSZ(1) Veusz VEUSZ(1) NAME Veusz - a scientific plotting and graphing application. SYNOPSIS veusz [options] [document.vsz]... DESCRIPTION Veusz is a scientific plotting and graphing package. It is designed to create publication-ready output in a variety of different output formats. Graphs are built-up combining plotting widgets. Veusz has a GUI user interface (started with the "veusz" command), a Python module interface and a scripting interface. If started without command line arguments, Veusz will open up with a new empty document. The program will otherwise open the listed documents. OPTIONS --unsafe-mode Do not check opened scripts for the presence of unsafe Python commands. This allows you to create or open complete Python scripts with Veusz commands if they come from a trusted source. --listen Read Veusz commands from stdin, executing them, then writing the results to stdout. This option replaces the old veusz_listen. In this mode Veusz does not read any input documents, but will use the first argument to the program as the window title, if given. --quiet If in listening mode, do not open a window before running commands, but execute them quietly. --export=FILE Export the next Veusz document file on the command line to the graphics file FILE. Supported file types include EPS, PDF, SVG, PNG, BMP, JPG and XPM. The extension of the output file is used to determine the output file format. There should be as many export options specified as input Veusz documents on the command line. --export-option=OPT Adds the option given when exporting. Supported options are dpi=DPI giving the resolution for bitmap output files (default 100), color=True/False to switch to monochrome output (default True), page=[X,...] gives a list of pages to export (default [0]), where the page numbers are numbered from 0, antialias=True/False enables or disables antialiasing in bitmap output files (default True), quality=VAL gives the JPEG quality value (default 85), backcolor='#RRGGBBAA' gives the background color of bitmap files (default '#ffffff00'), pdfdpi=DPI gives the DPI when outputing to PDF files (default 150) and svgtextastext=True/False outputs text in SVG files as text, rather than as curves (default False). --plugin=FILE Loads the Veusz plugin FILE when starting Veusz. This option provides a per-session alternative to adding the plugin in the preferences dialog box. --help Displays the options to the program and exits. --version Displays information about the currently installed version and exits. BUGS Please report bugs at https://github.com/veusz/veusz/issues AUTHORS Veusz was written by Jeremy Sanders . This manual page was written by Jeremy Sanders . COPYRIGHT Copyright (C) 2003-2018 Jeremy Sanders . This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. On Debian GNU/Linux systems, the complete text of the GNU General Public License can be found in `/usr/share/common-licenses/GPL'. 3.0.1 2018-04-22 VEUSZ(1) veusz-3.0.1/Documents/man-page/veusz.10000664000175000017500000001712513325026533017205 0ustar jssjss00000000000000.\" Automatically generated by Pod::Man 4.09 (Pod::Simple 3.35) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' . ds C` . ds C' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is >0, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .\" .\" Avoid warning from groff about undefined register 'F'. .de IX .. .if !\nF .nr F 0 .if \nF>0 \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . if !\nF==2 \{\ . nr % 0 . nr F 2 . \} .\} .\" .\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2). .\" Fear. Run. Save yourself. No user-serviceable parts. . \" fudge factors for nroff and troff .if n \{\ . ds #H 0 . ds #V .8m . ds #F .3m . ds #[ \f1 . ds #] \fP .\} .if t \{\ . ds #H ((1u-(\\\\n(.fu%2u))*.13m) . ds #V .6m . ds #F 0 . ds #[ \& . ds #] \& .\} . \" simple accents for nroff and troff .if n \{\ . ds ' \& . ds ` \& . ds ^ \& . ds , \& . ds ~ ~ . ds / .\} .if t \{\ . ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u" . ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u' . ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u' . ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u' . ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u' . ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u' .\} . \" troff and (daisy-wheel) nroff accents .ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V' .ds 8 \h'\*(#H'\(*b\h'-\*(#H' .ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#] .ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H' .ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u' .ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#] .ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#] .ds ae a\h'-(\w'a'u*4/10)'e .ds Ae A\h'-(\w'A'u*4/10)'E . \" corrections for vroff .if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u' .if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u' . \" for low resolution devices (crt and lpr) .if \n(.H>23 .if \n(.V>19 \ \{\ . ds : e . ds 8 ss . ds o a . ds d- d\h'-1'\(ga . ds D- D\h'-1'\(hy . ds th \o'bp' . ds Th \o'LP' . ds ae ae . ds Ae AE .\} .rm #[ #] #H #V #F C .\" ======================================================================== .\" .IX Title "VEUSZ 1" .TH VEUSZ 1 "2018-04-22" "3.0.1" "Veusz" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" Veusz \- a scientific plotting and graphing application. .SH "SYNOPSIS" .IX Header "SYNOPSIS" veusz [\fIoptions\fR] [\fIdocument.vsz\fR]... .SH "DESCRIPTION" .IX Header "DESCRIPTION" \&\fBVeusz\fR is a scientific plotting and graphing package. It is designed to create publication-ready output in a variety of different output formats. Graphs are built-up combining plotting widgets. Veusz has a \&\s-1GUI\s0 user interface (started with the \f(CW\*(C`veusz\*(C'\fR command), a Python module interface and a scripting interface. .PP If started without command line arguments, \fBVeusz\fR will open up with a new empty document. The program will otherwise open the listed documents. .SH "OPTIONS" .IX Header "OPTIONS" .IP "\fB\-\-unsafe\-mode\fR" 8 .IX Item "--unsafe-mode" Do not check opened scripts for the presence of unsafe Python commands. This allows you to create or open complete Python scripts with Veusz commands if they come from a trusted source. .IP "\fB\-\-listen\fR" 8 .IX Item "--listen" Read Veusz commands from stdin, executing them, then writing the results to stdout. This option replaces the old veusz_listen. .Sp In this mode Veusz does not read any input documents, but will use the first argument to the program as the window title, if given. .IP "\fB\-\-quiet\fR" 8 .IX Item "--quiet" If in listening mode, do not open a window before running commands, but execute them quietly. .IP "\fB\-\-export\fR=\fI\s-1FILE\s0\fR" 8 .IX Item "--export=FILE" Export the next Veusz document file on the command line to the graphics file \fI\s-1FILE\s0\fR. Supported file types include \s-1EPS, PDF, SVG, PNG, BMP, JPG\s0 and \s-1XPM.\s0 The extension of the output file is used to determine the output file format. There should be as many export options specified as input Veusz documents on the command line. .IP "\fB\-\-export\-option\fR=\fI\s-1OPT\s0\fR" 8 .IX Item "--export-option=OPT" Adds the option given when exporting. Supported options are dpi=\fI\s-1DPI\s0\fR giving the resolution for bitmap output files (default 100), color=True/False to switch to monochrome output (default True), page=[\fIX\fR,...] gives a list of pages to export (default [0]), where the page numbers are numbered from 0, antialias=True/False enables or disables antialiasing in bitmap output files (default True), quality=\fI\s-1VAL\s0\fR gives the \s-1JPEG\s0 quality value (default 85), backcolor='\fI#RRGGBBAA\fR' gives the background color of bitmap files (default '#ffffff00'), pdfdpi=\fI\s-1DPI\s0\fR gives the \s-1DPI\s0 when outputing to \&\s-1PDF\s0 files (default 150) and svgtextastext=True/False outputs text in \&\s-1SVG\s0 files as text, rather than as curves (default False). .IP "\fB\-\-plugin\fR=\fI\s-1FILE\s0\fR" 8 .IX Item "--plugin=FILE" Loads the Veusz plugin \fI\s-1FILE\s0\fR when starting Veusz. This option provides a per-session alternative to adding the plugin in the preferences dialog box. .IP "\fB\-\-help\fR" 8 .IX Item "--help" Displays the options to the program and exits. .IP "\fB\-\-version\fR" 8 .IX Item "--version" Displays information about the currently installed version and exits. .SH "BUGS" .IX Header "BUGS" Please report bugs at https://github.com/veusz/veusz/issues .SH "AUTHORS" .IX Header "AUTHORS" \&\fBVeusz\fR was written by Jeremy Sanders . .PP This manual page was written by Jeremy Sanders . .SH "COPYRIGHT" .IX Header "COPYRIGHT" Copyright (C) 2003\-2018 Jeremy Sanders . .PP This program is free software; you can redistribute it and/or modify it under the terms of the \s-1GNU\s0 General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. .PP On Debian GNU/Linux systems, the complete text of the \s-1GNU\s0 General Public License can be found in `/usr/share/common\-licenses/GPL'. veusz-3.0.1/Documents/man-page/veusz.pod0000664000175000017500000000645513267051440017633 0ustar jssjss00000000000000=head1 NAME Veusz - a scientific plotting and graphing application. =head1 SYNOPSIS veusz [I] [F]... =head1 DESCRIPTION B is a scientific plotting and graphing package. It is designed to create publication-ready output in a variety of different output formats. Graphs are built-up combining plotting widgets. Veusz has a GUI user interface (started with the C command), a Python module interface and a scripting interface. If started without command line arguments, B will open up with a new empty document. The program will otherwise open the listed documents. =head1 OPTIONS =over 8 =item B<--unsafe-mode> Do not check opened scripts for the presence of unsafe Python commands. This allows you to create or open complete Python scripts with Veusz commands if they come from a trusted source. =item B<--listen> Read Veusz commands from stdin, executing them, then writing the results to stdout. This option replaces the old veusz_listen. In this mode Veusz does not read any input documents, but will use the first argument to the program as the window title, if given. =item B<--quiet> If in listening mode, do not open a window before running commands, but execute them quietly. =item B<--export>=I Export the next Veusz document file on the command line to the graphics file I. Supported file types include EPS, PDF, SVG, PNG, BMP, JPG and XPM. The extension of the output file is used to determine the output file format. There should be as many export options specified as input Veusz documents on the command line. =item B<--export-option>=I Adds the option given when exporting. Supported options are dpi=I giving the resolution for bitmap output files (default 100), color=True/False to switch to monochrome output (default True), page=[I,...] gives a list of pages to export (default [0]), where the page numbers are numbered from 0, antialias=True/False enables or disables antialiasing in bitmap output files (default True), quality=I gives the JPEG quality value (default 85), backcolor='I<#RRGGBBAA>' gives the background color of bitmap files (default '#ffffff00'), pdfdpi=I gives the DPI when outputing to PDF files (default 150) and svgtextastext=True/False outputs text in SVG files as text, rather than as curves (default False). =item B<--plugin>=I Loads the Veusz plugin I when starting Veusz. This option provides a per-session alternative to adding the plugin in the preferences dialog box. =item B<--help> Displays the options to the program and exits. =item B<--version> Displays information about the currently installed version and exits. =back =head1 BUGS Please report bugs at https://github.com/veusz/veusz/issues =head1 AUTHORS B was written by Jeremy Sanders . This manual page was written by Jeremy Sanders . =head1 COPYRIGHT Copyright (C) 2003-2018 Jeremy Sanders . This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. On Debian GNU/Linux systems, the complete text of the GNU General Public License can be found in `/usr/share/common-licenses/GPL'. =cut veusz-3.0.1/Documents/Makefile0000664000175000017500000000366713161413406015725 0ustar jssjss00000000000000############################################################################ # Copyright (C) 2017 Jeremy S. Sanders # Email: Jeremy Sanders # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ############################################################################ verfile=../VERSION RELEASE=$(shell cat $(verfile)) mansources=$(shell find manual-source -name *.rst) all: man manual ############################################################################ # man pages %.1: %.pod $(verfile) pod2man --release=$(RELEASE) --center="Veusz" $< > $@ %.man.txt: %.1 MANWIDTH=76 man ./$< > $@ man: man-page/veusz.1 man-page/veusz.man.txt ############################################################################ # manual manual-pdf: manual/pdf/veusz.pdf manual-html: manual/html/index.html manual/pdf/veusz.pdf: $(mansources) $(verfile) make -C manual-source latexpdf mkdir -p manual/pdf mv manual/latex/veusz.pdf manual/pdf/ rm -rf manual/latex/ manual/html/index.html: $(mansources) $(verfile) make -C manual-source html manual: manual-html manual-pdf rm -rf manual/doctrees/ ############################################################################ clean: rm -f man-page/*.1 man-page/*.man.txt make -C manual-source clean veusz-3.0.1/pyqtdistutils.py0000664000175000017500000002427013316734155015642 0ustar jssjss00000000000000# Subclasses disutils.command.build_ext, # replacing it with a SIP version that compiles .sip -> .cpp # before calling the original build_ext command. # Written by Giovanni Bajo # Based on Pyrex.Distutils, written by Graham Fawcett and Darrel Gallion. from __future__ import division, print_function, absolute_import import os import sys import sysconfig import subprocess from distutils.sysconfig import customize_compiler import distutils.command.build_ext from distutils.dep_util import newer, newer_group import PyQt5.QtCore ################################################################## # try to get various useful things we need in order to build SIP_FLAGS = PyQt5.QtCore.PYQT_CONFIGURATION['sip_flags'] try: # sipconfig is deprecated but necessary to find sip reliably import sipconfig except ImportError: # try to guess locations DEF_SIP_DIR = None DEF_SIP_BIN = None DEF_SIP_INC_DIR = None else: # use sipconfig if found DEF_SIP_DIR = sipconfig.Configuration().default_sip_dir DEF_SIP_BIN = sipconfig.Configuration().sip_bin DEF_SIP_INC_DIR = sipconfig.Configuration().sip_inc_dir ################################################################## def replace_suffix(path, new_suffix): return os.path.splitext(path)[0] + new_suffix def find_on_path(names, mainname): """From a list of names of executables, find the 1st one on a path. mainname is the generic name to report """ path = os.getenv('PATH', os.path.defpath) pathparts = path.split(os.path.pathsep) for cmd in names: for dirname in pathparts: cmdtry = os.path.join(dirname.strip('"'), cmd) if os.path.isfile(cmdtry) and os.access(cmdtry, os.X_OK): return cmdtry raise RuntimeError('Could not find %s executable' % mainname) def read_command_output(cmd): """Get text from a run command.""" p = subprocess.Popen( cmd, stdout=subprocess.PIPE, universal_newlines=True) stdout, stderr = p.communicate() if p.returncode != 0: raise RuntimeError('Command %s returned error' % str(cmd)) return stdout.strip() class build_ext(distutils.command.build_ext.build_ext): description = ('Compile SIP descriptions, then build C/C++ extensions ' '(compile/link to build directory)') user_options = distutils.command.build_ext.build_ext.user_options + [ ('sip-exe=', None, 'override sip executable'), ('sip-dir=', None, 'override sip file directory'), ('sip-include-dir=', None, 'override sip include directory'), ('qmake-exe=', None, 'override qmake executable'), ('qt-include-dir=', None, 'override Qt include directory'), ('qt-library-dir=', None, 'override Qt library directory'), ] def initialize_options(self): distutils.command.build_ext.build_ext.initialize_options(self) self.sip_exe = None self.sip_dir = None self.sip_include_dir = None self.qmake_exe = None self.qt_include_dir = None self.qt_library_dir = None def _get_sip_output_list(self, sbf): ''' Parse the sbf file specified to extract the name of the generated source files. Make them absolute assuming they reside in the temp directory. ''' for line in open(sbf): key, value = line.split('=', 1) if key.strip() == 'sources': out = [] for o in value.split(): out.append(os.path.join(self.build_temp, o)) return out raise RuntimeError('cannot parse SIP-generated "%s"' % sbf) def _get_sip_exe(self, build_cmd): """Get exe for sip. Sources are: --sip-exe option, environment, DEF_SIP_BIN, search on path.""" return ( build_cmd.sip_exe or os.environ.get('SIP_EXE') or DEF_SIP_BIN or find_on_path( ('sip5', 'sip-qt5', 'sip', 'sip5.exe', 'sip.exe'), 'sip') ) def _get_sip_inc_dir(self, build_cmd): """Get include directory for sip.""" return ( build_cmd.sip_include_dir or os.environ.get('SIP_INCLUDE_DIR') or DEF_SIP_INC_DIR or sysconfig.get_path('include') ) def _get_sip_dir(self, build_cmd): """Get sip directory.""" data_dir = sys.prefix if sys.platform=='win32' else sys.prefix+'/share' return ( build_cmd.sip_dir or os.environ.get('SIP_DIR') or DEF_SIP_DIR or os.path.join(data_dir, 'sip') ) def _get_qmake(self, build_cmd): """Get qmake executable.""" return ( build_cmd.qmake_exe or os.environ.get('QMAKE_EXE') or find_on_path( ('qmake-qt5', 'qmake5', 'qmake', 'qmake5.exe', 'qmake.exe'), 'qmake') ) def _get_qt_inc_dir(self, build_cmd): """Get Qt include directory.""" return ( build_cmd.qt_include_dir or os.environ.get('QT_INCLUDE_DIR') or read_command_output( [self._get_qmake(build_cmd), '-query', 'QT_INSTALL_HEADERS']) ) def _get_qt_library_dir(self, build_cmd): """Get Qt library directory.""" return ( build_cmd.qt_library_dir or os.environ.get('QT_LIBRARY_DIR') or read_command_output( [self._get_qmake(build_cmd), '-query', 'QT_INSTALL_LIBS']) ) def _is_qt_framework(self, build_cmd): """Is the Qt a framework?""" return os.path.exists( os.path.join( self._get_qt_library_dir(build_cmd), 'QtCore.framework')) def _get_cpp_includes(self, build_cmd): """Get list of include directories to add.""" inc_dir = self._get_qt_inc_dir(build_cmd) incdirs = [inc_dir] for mod in ('QtCore', 'QtGui', 'QtWidgets', 'QtXml'): if self._is_qt_framework(build_cmd): incdirs.append( os.path.join( self._get_qt_library_dir(build_cmd), mod+'.framework', 'Headers') ) else: incdirs.append(os.path.join(inc_dir, mod)) return incdirs def swig_sources(self, sources, extension=None): """Compile SIP files and setup Qt compile options.""" if not self.extensions: return build_cmd = self.get_finalized_command('build_ext') # executable in order of priority using or sip_exe = self._get_sip_exe(build_cmd) sip_inc_dir = self._get_sip_inc_dir(build_cmd) # python data directory sip_dir = self._get_sip_dir(build_cmd) # add directory of input files as include path indirs = list(set([os.path.dirname(x) for x in sources])) # Add the SIP and Qt include directories to the include path extension.include_dirs += [sip_inc_dir] + indirs # link against libraries if extension.language == 'c++': extension.include_dirs += self._get_cpp_includes(build_cmd) lib_dir = self._get_qt_library_dir(build_cmd) if self._is_qt_framework(build_cmd): # Mac OS framework extension.extra_link_args = [ '-F', os.path.join(lib_dir), '-framework', 'QtGui', '-framework', 'QtCore', '-framework', 'QtXml', '-framework', 'QtWidgets', '-Wl,-rpath,@executable_path/Frameworks', '-Wl,-rpath,' + lib_dir ] extension.extra_compile_args = [ '-F', lib_dir, ] else: extension.libraries = [ 'Qt5Gui', 'Qt5Core', 'Qt5Xml', 'Qt5Widgets'] extension.library_dirs = [lib_dir] # may cause problems with compilers which don't allow this if self.compiler.compiler_type == 'unix': extension.extra_compile_args.append('-std=c++11') depends = extension.depends # Filter dependencies list: we are interested only in .sip files, # since the main .sip files can only depend on additional .sip # files. For instance, if a .h changes, there is no need to # run sip again. depends = [f for f in depends if os.path.splitext(f)[1] == '.sip'] # Create the temporary directory if it does not exist already if not os.path.isdir(self.build_temp): os.makedirs(self.build_temp) # Collect the names of the source (.sip) files sip_sources = [] sip_sources = [source for source in sources if source.endswith('.sip')] other_sources = [source for source in sources if not source.endswith('.sip')] generated_sources = [] for sip in sip_sources: # Use the sbf file as dependency check sipbasename = os.path.basename(sip) sbf = os.path.join(self.build_temp, replace_suffix(sipbasename, '.sbf')) if newer_group([sip]+depends, sbf) or self.force: self._sip_compile(sip_exe, sip_dir, sip, sbf) out = self._get_sip_output_list(sbf) generated_sources.extend(out) return generated_sources + other_sources def _sip_compile(self, sip_exe, sip_dir, source, sbf): """Compile sip file to sources.""" self.spawn( [ sip_exe, '-c', self.build_temp ] + SIP_FLAGS.split() + [ '-I', os.path.join(sip_dir, 'PyQt5'), '-b', sbf, source ] ) def build_extensions(self): # remove annoying flag which causes warning for c++ sources # https://stackoverflow.com/a/36293331/351771 customize_compiler(self.compiler) try: self.compiler.compiler_so.remove("-Wstrict-prototypes") except (AttributeError, ValueError): pass distutils.command.build_ext.build_ext.build_extensions(self) veusz-3.0.1/INSTALL0000664000175000017500000002072513324616156013357 0ustar jssjss00000000000000Veusz Installation ================== 1. INSTALLING FROM SOURCE ************************* Veusz uses distutils for its installation. See below for how to use it. Requirements: python2 >= 2.6 or python3 >= 3.3 https://www.python.org/ PyQt >= 5.2 https://www.riverbankcomputing.co.uk/software/pyqt/ numpy >= 1.0 http://www.numpy.org/ argparse https://pypi.python.org/pypi/argparse (Python 2.6 only) PyQt requires Qt5 >= 5.2 https://www.qt.io/developers/ latest version recommended SIP >= 4.15 https://www.riverbankcomputing.co.uk/software/sip/ Optional requirements: h5py http://www.h5py.org/ astropy >= 0.4 http://www.astropy.org/ pyemf >= 2.0.0 http://pyemf.sourceforge.net/ (for Python3 see https://github.com/jeremysanders/pyemf) iminuit https://github.com/iminuit/iminuit ( or PyMinuit http://code.google.com/p/pyminuit/ ) dbus-python http://dbus.freedesktop.org/doc/dbus-python/ Ghostscript https://www.ghostscript.com/ (for EPS/PS output) Sphinx http://www.sphinx-doc.org/en/stable/ (to rebuild manual) Note that the source code is compatible with both python 2.6+ and 3.3+. It does not need translation with 2to3. The optional dependency pyemf does not have Python 3 support, but an in-development version of pyemf for Python 3 can be found at https://github.com/jeremysanders/pyemf. 1.1 Full installation with distutils ==================================== There are a number of ways to install programs using distutils. I will list a few of the possible method here: To install on Linux to the standard location on the hard disk follow these instructions (note python in these commands should be replaced by python3 to do a Python 3 installation). # cd veusz-3.0.1 # python setup.py build # su [enter root password] # python setup.py install # exit If you do not have a root account (as is default on Ubuntu), do # sudo python setup.py install instead of the final three lines On Windows, it should just be a matter of running the python setup.py build and install steps with the requirements installed. 1.1.1 Testing ============= After veusz has been installed into the Python path (in the standard location or in PYTHONPATH), you can run the runselftest.py executable in the tests directory (note that resources may need to be specified: see below). This will compare the generated output of example documents with the expected output. The return code of the runselftest.py script is the number of tests that have failed (0 for success). On Unix/Linux, Qt requires the DISPLAY environment to be set to an X11 server for the self test to run. Packagers can use Xvfb in a non graphical environment to create a hidden X11 server: # xvfb-run -a --server-args "-screen 0 640x480x24" \ python tests/runselftest.py Alternatively, the Qt platform can be switched to minimal to avoid the use of X11: # QT_QPA_PLATFORM=minimal python tests/runselftest.py 1.1.2 Separate resources directory ================================== By default, setup.py installs certain resource files (VERSION, icons, ui, and examples) in the veusz python module directory. This may not be desired behaviour for unix packagers, for example, who want to separate the code from the data files. It is possible to install these files in a different location by using the setup.py option "--veusz-resource-dir" (for example with /usr/share/veusz). If you do this, then you need to tell veusz where these resources are on runtime or when testing. This can be done by using a symlink "resources" in the the veusz module directory which points to the location of these files and directories. Alternatively, the environment variable VEUSZ_RESOURCE_DIR can be set. There is an addition setup.py option "--disable-install-examples" which disables installation of the example files. This may be helpful for packagers who want to place the example files in /usr/share/doc. As veusz shows these files on the help menu and when testing, it is suggested that an "examples" symlink is added to the resources directory to point to the location of the example files. 1.1.3 SIP parameters ==================== By default the location of the SIP executable, the SIP include file location and the SIP files are obtained from sipconfig. sipconfig is now deprecated. If sipconfig is not found, the build will look for the SIP executable on the current path (as sip/sip5/sip.exe/sip5.exe), the SIP include file in the Python include directory and the SIP files in python-prefix/share/sip (excluding share on Windows). If you want to manually specify these, you can run the Python setup.py build_ext stage before build, with the parameters: --sip-exe=EXE SIP executable --sip-include-dir=DIR SIP include directory (location of sip.h) --sip-dir=DIR base directory for SIP files Alternatively, these can be given as environment variables SIP_EXE, SIP_INCLUDE_DIR and SIP_DIR, respectively. Environment variables have the advantage that the build_ext stage does not have to be done separately. 1.1.4 Qt build parameters ========================= By default, the qmake executable is used to find the locations of the Qt include and library directories. The build searches for qmake under the names qmake-qt5, qmake, qmake5.exe and qmake.exe. The location of qmake and its return values can be overriden with the following build_ext options: --qmake-exe=EXE qmake exe --qt-include-dir=DIR base Qt include directory --qt-library-dir=DIR Qt library directory Alternatively, these can be overridden with the enviroment variables QMAKE_EXE, QT_INCLUDE_DIR and QT_LIBRARY_DIR, respectively. Environment variables have the advantage that the build_ext stage does not have to be done separately. 1.3 Running in-place ==================== If you don't want to install veusz fully or are doing development, it can currently be run from its own directory. Before this can work, the helper modules must be compiled and copied into the appropriate location. # tar xzf veusz-3.0.1.tar.gz [change version here] # cd veusz-3.0.1 # python setup.py build [or use python3 here] # cp build/*/veusz/helpers/*.so veusz/helpers/ To run the program run # ./run_veusz_inplace or # python3 ./run_veusz_inplace 2. BINARY INSTALL ***************** 2.1 Linux binary ================ If your distribution does not include an up to date package, you can use the Linux binary instead (for i386/x86_64). Note that this may not work on all distributions due to glibc/library incompatibilities. Simply unpack the tar file and run the main executable: # tar xf veusz-3.0.1-linux-x86.tar.xz [change version here] # cd veusz-3.0.1-linux-x86 # ./veusz 2.2 Installing in Windows ========================= Simply run the setup.exe binary installer. Add the location of the embed.py file to your PYTHONPATH if you want to use the embedding module. 2.3 Installing on Mac OS X ========================== A binary is available for Mac OS X. Simply drag the Veusz application into your Applications directory. 3. NOTES FOR PACKAGERS ********************** - It is recommended to run the self test above (if possible) - Please see the above section on separate resource files which shows how to easily separate the installation of the code and data files (resources). - Veusz is mostly platform-independent python code and data files with a separate "helpers" module containing platform-dependent code. It may save space in repositories to separate out the helpers sub-module. - Veusz includes a man page in Documents/man-page/veusz.1 This are not automatically installed by distutils. - A manual in HTML and PDF format can be found in Documents/manual/ This and the the man page can be regenerated using the Makefile in Documents, if Sphinx is installed (make clean; make). - Veusz also includes freedesktop mime, desktop and appdata files in the support subdirectory which can be installed to better integrate with desktop environments. - Icons are also included in the icons directory with the names veusz_16.png, _32, _48, _64 and _128. A scalable icon can be found in veusz.svg. - Veusz will periodically (once per week) check for updates. This can be disabled by patching veusz/utils/version.py to set disableVersionChecks=True. - Veusz will automatically send anonymous feedback (after confirmation) to the developers giving version information and counts of feature use. This can be disabled by patching veusz/utils/feedback.py to set disableFeedback=True. veusz-3.0.1/examples/0000775000175000017500000000000013325026667014141 5ustar jssjss00000000000000veusz-3.0.1/examples/datebar.dat0000664000175000017500000000024413161413406016222 0ustar jssjss00000000000000descriptor d(date) value(numeric),+- 2009-03-10 1 0.1 2009-03-11 2 0.2 2009-03-12 1.3 0.12 2009-03-13 1.5 0.1 2009-03-14 4 0.2 2009-03-15 3 0.15 2009-03-16 1.8 0.2 veusz-3.0.1/examples/linked_datasets.vsz0000664000175000017500000000321213161413406020026 0ustar jssjss00000000000000# Veusz saved document (version 1.17.1) SetDataRange(u't', 100, (-3.141592, 3.141592), linked=True) SetDataExpression(u'x', u'sin(t)', linked=True) SetDataExpression(u'x2', u'sin(t*8)', linked=True) SetDataExpression(u'x3', u'sin(x*2)', linked=True) SetDataExpression(u'y', u'cos(t)', linked=True) SetDataExpression(u'y2', u'cos(t*16)', linked=True) Set('StyleSheet/Line/width', u'1pt') Set('StyleSheet/Font/font', u'Arial') Set('StyleSheet/axis/Label/size', u'18pt') Set('StyleSheet/axis/MajorTicks/number', 8) Set('StyleSheet/xy/markerSize', u'4pt') Add('page', name='page1', autoadd=False) To('page1') Add('graph', name='graph1', autoadd=False) To('graph1') Set('Background/color', u'#e9ffff') Add('axis', name='x', autoadd=False) To('x') Set('label', u'Experiments with linked datasets') Set('autoRange', u'exact') To('..') Add('axis', name='y', autoadd=False) To('y') Set('label', u'Another axis') Set('autoRange', u'exact') Set('direction', 'vertical') To('..') Add('xy', name='xy1', autoadd=False) To('xy1') Set('marker', u'diamond') Set('MarkerFill/color', u'blue') To('..') Add('xy', name='xy2', autoadd=False) To('xy2') Set('xData', u'x2') Set('yData', u'y2') Set('PlotLine/color', u'#00aa00') Set('MarkerFill/color', u'green') To('..') Add('xy', name='xy3', autoadd=False) To('xy3') Set('xData', u'x3') Set('marker', u'star') Set('markerSize', u'6pt') Set('PlotLine/color', u'red') Set('PlotLine/width', u'1pt') Set('MarkerLine/hide', False) Set('MarkerFill/color', u'red') To('..') Add('xy', name='xy4', autoadd=False) To('xy4') Set('xData', u'x/2*y') Set('yData', u'y/2') Set('marker', u'barhorz') Set('MarkerFill/color', u'yellow') To('..') To('..') To('..') veusz-3.0.1/examples/ternary.vsz0000664000175000017500000001445313161413406016365 0ustar jssjss00000000000000# Veusz saved document (version 1.25.1) # Saved at 2017-04-29T15:44:54.108769 ImportString('a1(numeric)',''' 3.785032e+01 2.887586e+01 3.090787e+01 3.991348e+01 2.194688e+01 6.907754e+00 3.731477e+01 2.729718e+01 4.962433e+01 3.593354e+01 2.980925e+01 1.883289e+01 2.864022e+01 2.385115e+01 1.916651e+01 1.163294e+01 3.201094e+01 4.940456e+01 2.211055e+01 5.347282e+01 3.966645e+01 1.129016e+01 2.900517e+01 2.285825e+01 1.434235e+01 2.491757e+01 4.324933e+01 -5.615836e-01 1.333683e+01 1.169526e+01 5.702774e+01 -4.698968e+00 2.472287e+01 3.090273e+01 2.121293e+01 2.293004e+00 3.181916e+01 3.576081e+01 2.548760e+01 3.387097e+01 1.090547e+01 4.670771e+01 3.679762e+01 1.870816e+01 4.207707e+01 8.373339e+00 1.622552e+01 4.240925e+01 1.243271e+01 9.907227e+00 ''') ImportString('a2(numeric)',''' 3.927892e+01 3.147607e+01 3.585074e+01 2.532616e+01 2.748505e+01 3.089898e+01 3.265939e+01 2.699355e+01 2.724263e+01 3.627490e+01 3.543455e+01 2.613919e+01 2.955639e+01 2.698287e+01 3.423267e+01 2.446838e+01 3.444203e+01 4.034653e+01 2.890829e+01 2.838100e+01 2.620180e+01 3.366264e+01 2.950382e+01 3.507761e+01 2.963094e+01 3.659981e+01 2.953002e+01 3.010142e+01 2.482832e+01 4.350083e+01 2.834519e+01 2.657412e+01 3.719120e+01 3.122923e+01 3.240051e+01 2.650656e+01 3.104269e+01 3.162769e+01 3.344840e+01 3.266331e+01 2.874867e+01 2.454526e+01 3.835166e+01 3.302628e+01 3.290526e+01 3.631759e+01 3.109008e+01 3.474628e+01 3.992190e+01 3.665463e+01 ''') ImportString('a3(numeric)',''' 2.974402e+01 2.992560e+01 2.879212e+01 2.289509e+01 3.151959e+01 2.514551e+01 2.878709e+01 3.809324e+01 2.142433e+01 3.253007e+01 3.495113e+01 3.219936e+01 3.676403e+01 2.577995e+01 2.996285e+01 2.957746e+01 2.770419e+01 2.726770e+01 3.137323e+01 3.328274e+01 2.981133e+01 2.227267e+01 3.234312e+01 2.694048e+01 2.859402e+01 3.514191e+01 3.444068e+01 2.544261e+01 2.895935e+01 2.397857e+01 2.591309e+01 3.282064e+01 2.463678e+01 3.006554e+01 3.302107e+01 3.165531e+01 3.265907e+01 2.977074e+01 2.642066e+01 2.612206e+01 2.921875e+01 3.187261e+01 2.956203e+01 2.660354e+01 3.243911e+01 3.434163e+01 3.642871e+01 2.220277e+01 3.788404e+01 2.830237e+01 ''') ImportString('a4(numeric)',''' 6.023658e+01 6.951139e+01 6.130916e+01 5.480901e+01 5.694097e+01 5.796104e+01 5.963835e+01 5.135586e+01 6.062245e+01 5.609685e+01 5.564031e+01 6.290610e+01 5.559147e+01 5.874261e+01 6.085174e+01 5.703215e+01 5.848376e+01 6.636230e+01 6.031409e+01 5.848922e+01 6.486602e+01 5.198642e+01 5.658263e+01 6.206651e+01 6.050066e+01 6.178179e+01 5.837051e+01 6.618575e+01 6.036681e+01 6.307698e+01 5.658911e+01 5.888594e+01 6.263513e+01 6.270467e+01 6.305208e+01 5.303999e+01 4.733556e+01 6.382467e+01 5.859786e+01 6.703914e+01 5.636757e+01 5.305304e+01 6.576543e+01 4.626336e+01 5.763185e+01 5.699938e+01 6.279347e+01 6.506666e+01 5.402032e+01 5.895224e+01 ''') ImportString('b1(numeric)',''' 6.650787e+00 1.687328e+01 1.010450e+01 1.502892e+01 8.124262e+00 1.579514e+01 2.195101e+01 3.239890e+00 1.600109e+01 4.296486e+00 2.970698e+00 9.968865e+00 1.679330e+01 6.402929e+00 1.273309e+01 1.358886e+01 1.144324e+01 -1.422098e+00 2.708607e+00 1.120078e+01 8.676094e+00 9.079920e+00 9.543885e+00 1.468984e+01 1.288232e+01 8.349164e+00 1.372535e+01 5.718161e+00 1.638648e+01 7.551304e+00 1.155270e+01 1.067230e+01 8.879579e+00 1.171511e+01 6.194712e+00 1.445493e+01 8.503971e+00 1.397565e+01 8.748016e+00 1.188018e+01 1.910491e+01 3.901901e+00 4.879108e+00 1.863455e+01 1.608425e+01 1.645056e+01 -5.258673e+00 8.866189e+00 3.559357e-01 6.020759e+00 ''') ImportString('b2(numeric)',''' 2.738027e+01 1.836565e+01 1.566644e+01 1.609443e+01 2.403639e+01 8.289908e+00 3.651296e+01 5.184957e+01 2.844856e+00 5.319719e+00 1.482401e+01 3.813406e+01 8.649367e+00 -1.039173e+01 2.710390e+01 2.282277e+01 2.110841e+01 2.748859e+01 2.756449e+01 2.152050e+01 1.344263e+01 2.089343e+01 1.775140e+01 1.806021e+01 4.594484e+01 9.522575e+00 2.831632e+01 2.687223e+01 5.372986e+01 3.397279e+01 1.318980e+01 2.891465e+00 2.525563e+01 2.537575e+01 2.810802e+01 3.799986e+01 2.522245e+01 3.272764e+01 1.361898e+01 3.343623e+01 2.053961e+01 3.908754e+01 5.705844e+01 2.483616e+01 3.003679e+01 9.125044e+00 1.994561e+01 2.138206e+01 1.927962e+01 1.461585e+01 ''') SetDataText(u'label', [ u'Nougat', u'Chocolate', ]) ImportString(u'lx(numeric)',''' 6.000000e+01 4.500000e+01 ''') ImportString(u'ly(numeric)',''' 2.500000e+01 5.000000e+01 ''') ImportString('sizes(numeric)',''' 3.703217e-01 4.291845e-01 2.342960e-01 1.000000e-01 7.013704e-01 4.164477e-01 1.065102e+00 7.756953e-01 9.482757e-01 3.618510e-01 7.314487e-01 4.392592e-01 2.367437e-01 1.065933e-01 6.584510e-01 4.378377e-01 1.532375e-01 7.235843e-01 3.666900e-01 7.430943e-01 4.697697e-01 4.473618e-01 3.818643e-01 5.482129e-01 3.484666e-01 2.090011e-01 2.950832e-01 2.944881e-01 4.601784e-01 7.396155e-01 4.672722e-01 1.195206e-01 2.994085e-01 3.087374e-01 5.446024e-01 3.683946e-01 4.009373e-01 8.466384e-01 2.609215e-01 5.970062e-01 4.180349e-01 5.768238e-01 2.538321e-01 8.176581e-01 7.842167e-01 6.022510e-01 3.519089e-01 4.928984e-01 8.822681e-01 3.058641e-01 ''') Set('width', '15cm') Set('height', '13.9cm') Set('colorTheme', u'colorbrewer2') Add('page', name='page1', autoadd=False) To('page1') Add('ternary', name='ternary1', autoadd=False) To('ternary1') Set('topMargin', '0.72cm') Set('bottomMargin', '1.28cm') Set('labelbottom', u'Earth') Set('labelleft', u'Air') Set('labelright', u'Fire') Set('Label/size', u'20pt') Set('Label/italic', True) Add('nonorthpoint', name='nonorthpoint4', autoadd=False) To('nonorthpoint4') Set('marker', u'star') Set('markerSize', u'5pt') Set('data1', u'lx') Set('data2', u'ly') Set('labels', u'label') Set('PlotLine/hide', True) Set('Label/size', u'18pt') To('..') Add('nonorthpoint', name='nonorthpoint1', autoadd=False) To('nonorthpoint1') Set('data1', u'a1') Set('data2', u'a2') Set('PlotLine/hide', True) To('..') Add('nonorthpoint', name='nonorthpoint2', autoadd=False) To('nonorthpoint2') Set('marker', u'diamond') Set('data1', u'a3') Set('data2', u'a4') Set('PlotLine/hide', True) To('..') Add('nonorthpoint', name='nonorthpoint3', autoadd=False) To('nonorthpoint3') Set('markerSize', u'5pt') Set('data1', u'b2') Set('data2', u'b1') Set('scalePoints', u'sizes') Set('PlotLine/hide', True) To('..') Add('nonorthfunc', name='nonorthfunc1', autoadd=False) To('nonorthfunc1') Set('function', u'40') Set('PlotLine/width', u'1.5pt') Set('PlotLine/style', u'dotted') To('..') To('..') To('..') veusz-3.0.1/examples/fixed_aspect.vsz0000664000175000017500000000314713161413406017335 0ustar jssjss00000000000000# Veusz saved document (version 1.17.1) Set('StyleSheet/function/steps', 200) Add('page', name='page1', autoadd=False) To('page1') Add('grid', name='grid1', autoadd=False) To('grid1') Set('scaleRows', [1.0, 0.25]) Set('scaleCols', [1.0, 0.25]) Set('leftMargin', u'1cm') Set('bottomMargin', u'1cm') Add('axis', name='x', autoadd=False) Add('axis', name='y', autoadd=False) To('y') Set('direction', 'vertical') To('..') Add('graph', name=u'main', autoadd=False) To(u'main') Set('leftMargin', '0cm') Set('rightMargin', '0cm') Set('topMargin', '0cm') Set('bottomMargin', '0cm') Set('aspect', 1.0) Add('function', name=u'lin', autoadd=False) Add('function', name=u'siny', autoadd=False) To(u'siny') Set('function', u'abs(sin(y*20)*y)') Set('variable', u'y') To('..') Add('function', name=u'sinx', autoadd=False) To(u'sinx') Set('function', u'abs(sin(x*20)*x)') Set('FillBelow/hide', False) To('..') To('..') Add('graph', name='graph3', autoadd=False) To('graph3') Set('leftMargin', '0cm') Set('rightMargin', '0cm') Set('topMargin', '0cm') Set('bottomMargin', '0cm') Set('aspect', 0.2) Add('function', name=u'lin', autoadd=False) Add('function', name=u'siny', autoadd=False) To(u'siny') Set('function', u'abs(sin(y*20)*y)') Set('variable', u'y') To('..') To('..') Add('graph', name='graph2', autoadd=False) To('graph2') Set('leftMargin', '0cm') Set('rightMargin', '0cm') Set('topMargin', '0cm') Set('bottomMargin', '0cm') Set('aspect', 5.0) Add('function', name=u'lin', autoadd=False) Add('function', name=u'sinx', autoadd=False) To(u'sinx') Set('function', u'abs(sin(x*20)*x)') Set('FillBelow/hide', False) To('..') To('..') To('..') To('..') veusz-3.0.1/examples/example_csv.vsz0000664000175000017500000000200513161413406017175 0ustar jssjss00000000000000# Veusz saved document (version 1.25.1) # Saved at 2017-04-30T11:00:14.681801 ImportFileCSV(u'example_csv.csv', linked=True) Set('colorTheme', u'colorbrewer1') Add('page', name='page1', autoadd=False) To('page1') Add('graph', name='graph1', autoadd=False) To('graph1') Add('axis', name='x', autoadd=False) To('x') Set('label', u'Imported CSV file example') Set('min', 0.0) To('..') Add('axis', name='y', autoadd=False) To('y') Set('label', u'Data read into from CSV') Set('direction', 'vertical') To('..') Add('xy', name='lineplot', autoadd=False) To('lineplot') Set('xData', u'Xval') Set('yData', u'Yval') Set('key', u'Line plot') Set('errorStyle', u'barends') To('..') Add('xy', name='histo', autoadd=False) To('histo') Set('marker', u'square') Set('xData', u'Xval2') Set('yData', u'Yval2') Set('key', u'Histogram') Set('PlotLine/steps', u'centre') Set('FillBelow/color', u'lightblue') Set('FillBelow/hide', False) To('..') Add('key', name='key1', autoadd=False) To('key1') Set('Border/hide', True) To('..') To('..') To('..') veusz-3.0.1/examples/boxplot.vsz0000664000175000017500000000364713161413406016373 0ustar jssjss00000000000000# Veusz saved document (version 1.9.99) # User: jss # Date: Thu, 02 Dec 2010 19:28:57 +0000 ImportString(u'd1(numeric)',''' 1.503414e+01 1.771985e+01 1.603614e+01 1.697182e+01 1.408432e+01 1.375135e+01 1.290579e+01 1.474151e+01 1.383836e+01 1.370710e+01 ''') SetDataExpression(u'd1_x', u'd1*0+0.6', linked=True) ImportString(u'd2(numeric)',''' 1.298826e+01 4.121945e+00 4.666195e+00 7.293386e+00 1.682599e+00 1.611030e+01 1.834109e+01 1.385013e+01 6.923665e+00 1.859132e+01 ''') SetDataExpression(u'd2_x', u'd2*0+2.4', linked=True) ImportString(u'label(text)',r''' u'Bees' u'Butterflys' ''') Set('width', '14.5cm') Set('height', '12cm') Set('StyleSheet/Font/font', u'Arial') Set('StyleSheet/boxplot/Border/width', u'1pt') Set('StyleSheet/boxplot/Whisker/width', u'1pt') Set('StyleSheet/boxplot/MarkersLine/width', u'1pt') Add('page', name='page1', autoadd=False) To('page1') Add('graph', name='graph1', autoadd=False) To('graph1') Set('leftMargin', u'1.3cm') Set('rightMargin', '0.2cm') Set('topMargin', '0.2cm') Set('bottomMargin', u'1.3cm') Set('Background/color', u'#fffeea') Add('axis', name='x', autoadd=False) To('x') Set('mode', u'labels') Set('TickLabels/size', u'18pt') To('..') Add('axis', name='y', autoadd=False) To('y') Set('label', u'Number of insects') Set('direction', 'vertical') To('..') Add('boxplot', name='boxplot1', autoadd=False) To('boxplot1') Set('values', (u'd1', u'd2')) Set('labels', u'label') Set('whiskermode', u'9/91 percentile') Set('fillfraction', 0.5) Set('Fill/color', u'white') To('..') Add('xy', name=u'd2vals', autoadd=False) To(u'd2vals') Set('xData', u'd2_x') Set('yData', u'd2') Set('marker', u'cross') Set('PlotLine/hide', True) Set('MarkerLine/hide', True) Set('MarkerFill/color', u'blue') To('..') Add('xy', name=u'd1vals', autoadd=False) To(u'd1vals') Set('xData', u'd1_x') Set('yData', u'd1') Set('PlotLine/hide', True) Set('MarkerLine/hide', True) Set('MarkerFill/color', u'magenta') To('..') To('..') To('..') veusz-3.0.1/examples/polar.vsz0000664000175000017500000000445613161413406016020 0ustar jssjss00000000000000# Veusz saved document (version 1.25.1) # Saved at 2017-04-30T10:56:21.427544 ImportString(u'r(numeric)',''' 2.029732e+00 2.688856e+00 2.708923e+00 2.711315e+00 2.640299e+00 2.735022e+00 2.407293e+00 2.130543e+00 2.007767e+00 2.792255e+00 2.070002e+00 2.555355e+00 2.644494e+00 2.639505e+00 2.372546e+00 2.595538e+00 2.646493e+00 2.698109e+00 ''') ImportString(u'theta(numeric)',''' 0.000000e+00 2.000000e+01 4.000000e+01 6.000000e+01 8.000000e+01 1.000000e+02 1.200000e+02 1.400000e+02 1.600000e+02 1.800000e+02 2.000000e+02 2.200000e+02 2.400000e+02 2.600000e+02 2.800000e+02 3.000000e+02 3.200000e+02 3.400000e+02 ''') Set('colorTheme', u'colorbrewer1') Set('StyleSheet/Font/font', u'Arial') Add('page', name='page1', autoadd=False) To('page1') Add('polar', name='polar2', autoadd=False) To('polar2') Set('rightMargin', u'7.8cm') Set('topMargin', u'0.5cm') Set('bottomMargin', u'8.3cm') Set('minradius', 0.1) Set('maxradius', 10.0) Set('log', True) Set('TickLabels/bold', True) Add('nonorthpoint', name='nonorthpoint1', autoadd=False) To('nonorthpoint1') Set('data1', u'r') Set('data2', u'theta') Set('PlotLine/color', u'blue') Set('PlotLine/hide', False) Set('MarkerLine/color', u'blue') Set('MarkerFill/color', u'blue') To('..') Add('nonorthfunc', name='nonorthfunc2', autoadd=False) To('nonorthfunc2') Set('steps', 100) Set('function', u'sin(b / 10)*10') Set('variable', u'b') Set('PlotLine/color', u'#00ff00') Set('PlotLine/width', u'1pt') Set('Fill1/color', u'#00ff00') Set('Fill1/hide', False) Set('Fill1/transparency', 80) To('..') To('..') Add('polar', name='polar1', autoadd=False) To('polar1') Set('maxradius', 3.0) Add('nonorthfunc', name='nonorthfunc1', autoadd=False) To('nonorthfunc1') Set('function', u'1+cos(b/180*pi)') Set('variable', u'b') Set('PlotLine/width', u'1pt') Set('Fill1/color', u'auto') Set('Fill1/hide', False) Set('Fill1/transparency', 80) To('..') Add('nonorthfunc', name='nonorthfunc2', autoadd=False) To('nonorthfunc2') Set('function', u'2+sin(b/180*pi)*cos(b/180*pi)') Set('variable', u'b') Set('PlotLine/width', u'1pt') Set('Fill1/color', u'auto') Set('Fill1/hide', False) Set('Fill1/transparency', 80) To('..') Add('nonorthpoint', name='nonorthpoint1', autoadd=False) To('nonorthpoint1') Set('marker', u'cross') Set('data1', u'r') Set('data2', u'theta') Set('PlotLine/hide', False) To('..') To('..') To('..') veusz-3.0.1/examples/custom_definitions.vsz0000664000175000017500000000235313161413406020602 0ustar jssjss00000000000000# Veusz saved document (version 1.25.1) # Saved at 2017-05-01T15:36:14.647425 AddCustom('import', u'numpy.fft', u'fft') AddCustom('definition', u'scale', u'3') AddCustom('definition', u'boxfn(x)', u'cos(scale*x)/sqrt(abs(x))') AddCustom('color', u'migraine', u'#b256bc') SetDataExpression(u'infn', u'boxfn(xv)', linked=True) SetDataExpression(u'outfn', u'imag(fft(infn))', linked=True) SetDataRange(u'xv', 200, (-4.0, 4.0), linked=True) Set('width', '16.728cm') Set('height', '9.899cm') Set('colorTheme', u'default-latest') Set('StyleSheet/Font/font', u'Calibri') Add('page', name='page1', autoadd=False) To('page1') Set('width', u'16cm') Set('height', u'10cm') Add('graph', name='graph1', autoadd=False) To('graph1') Set('bottomMargin', u'1.4cm') Add('axis', name='x', autoadd=False) To('x') Set('label', u'x') Set('Label/italic', True) To('..') Add('axis', name='y', autoadd=False) To('y') Set('label', u'Imaginary value of FFT function') Set('direction', 'vertical') To('..') Add('xy', name=u'fft', autoadd=False) To(u'fft') Set('marker', u'none') Set('xData', u'xv') Set('yData', u'outfn') Set('PlotLine/color', u'migraine') To('..') Add('xy', name=u'func', autoadd=False) To(u'func') Set('xData', u'xv') Set('yData', u'infn') To('..') To('..') To('..') veusz-3.0.1/examples/vectorfield.vsz0000664000175000017500000000245013161413406017201 0ustar jssjss00000000000000# Veusz saved document (version 1.8) # User: jss # Date: Thu, 26 Aug 2010 20:26:59 +0000 SetData2DXYFunc(u'xvec', (-1.0, 1.0, 0.10000000000000001), (-1.0, 1.0, 0.10000000000000001), u'x', linked=True) SetData2DXYFunc(u'yvec', (-1.0, 1.0, 0.10000000000000001), (-1.0, 1.0, 0.10000000000000001), u'y+x', linked=True) SetData2DXYFunc(u'img', (-1.0, 1.0, 0.02), (-1.0, 1.0, 0.02), u'sin( sqrt(x*x+y*y)*10) / (sqrt(x*x+y*y)*10)', linked=True) Set('StyleSheet/Font/font', u'Arial') Add('page', name='page1', autoadd=False) To('page1') Add('graph', name='graph1', autoadd=False) To('graph1') Set('leftMargin', u'1cm') Set('rightMargin', u'0.5cm') Set('topMargin', u'0.5cm') Set('bottomMargin', u'1cm') Add('axis', name='x', autoadd=False) To('x') Set('min', -1.0) Set('max', 1.0) To('..') Add('axis', name='y', autoadd=False) To('y') Set('min', -1.0) Set('max', 1.0) Set('direction', 'vertical') To('..') Add('vectorfield', name='vectorfield1', autoadd=False) To('vectorfield1') Set('arrowsize', u'3pt') Set('arrowfront', u'arrow') Set('data1', u'xvec') Set('data2', u'yvec') Set('mode', u'cartesian') Set('Line/color', u'white') Set('Line/width', u'1pt') Set('Fill/color', u'white') To('..') Add('image', name='image1', autoadd=False) To('image1') Set('data', u'img') Set('colorMap', u'bluegreen') To('..') To('..') To('..') veusz-3.0.1/examples/3d_volume.vsz0000664000175000017500000000523413302503653016574 0ustar jssjss00000000000000# Veusz saved document (version 3.0) # Saved at 2018-05-27T10:25:14.694378 ImportString(u'vv(numeric)',''' 0.000000e+00 1.111111e-01 2.222222e-01 1.111111e-01 2.222222e-01 3.333333e-01 2.222222e-01 3.333333e-01 4.444444e-01 1.111111e-01 2.222222e-01 3.333333e-01 2.222222e-01 3.333333e-01 4.444444e-01 3.333333e-01 4.444444e-01 5.555556e-01 2.222222e-01 3.333333e-01 4.444444e-01 3.333333e-01 4.444444e-01 5.555556e-01 4.444444e-01 5.555556e-01 6.666667e-01 ''') ImportString(u'xv(numeric)',''' 0.000000e+00 0.000000e+00 0.000000e+00 0.000000e+00 0.000000e+00 0.000000e+00 0.000000e+00 0.000000e+00 0.000000e+00 1.000000e+00 1.000000e+00 1.000000e+00 1.000000e+00 1.000000e+00 1.000000e+00 1.000000e+00 1.000000e+00 1.000000e+00 2.000000e+00 2.000000e+00 2.000000e+00 2.000000e+00 2.000000e+00 2.000000e+00 2.000000e+00 2.000000e+00 2.000000e+00 ''') ImportString(u'yv(numeric)',''' 0.000000e+00 0.000000e+00 0.000000e+00 1.000000e+00 1.000000e+00 1.000000e+00 2.000000e+00 2.000000e+00 2.000000e+00 0.000000e+00 0.000000e+00 0.000000e+00 1.000000e+00 1.000000e+00 1.000000e+00 2.000000e+00 2.000000e+00 2.000000e+00 0.000000e+00 0.000000e+00 0.000000e+00 1.000000e+00 1.000000e+00 1.000000e+00 2.000000e+00 2.000000e+00 2.000000e+00 ''') ImportString(u'zv(numeric)',''' 0.000000e+00 1.000000e+00 2.000000e+00 0.000000e+00 1.000000e+00 2.000000e+00 0.000000e+00 1.000000e+00 2.000000e+00 0.000000e+00 1.000000e+00 2.000000e+00 0.000000e+00 1.000000e+00 2.000000e+00 0.000000e+00 1.000000e+00 2.000000e+00 0.000000e+00 1.000000e+00 2.000000e+00 0.000000e+00 1.000000e+00 2.000000e+00 0.000000e+00 1.000000e+00 2.000000e+00 ''') Set('colorTheme', u'default-latest') Set('StyleSheet/Font/font', u'Courier') Set('StyleSheet/axis-function/autoRange', u'next-tick') Add('page', name=u'page1', autoadd=False) To(u'page1') Add('scene3d', name=u'scene3d1', autoadd=False) To(u'scene3d1') Set('xRotation', 35.0) Set('distance', 5.0) Add('graph3d', name=u'graph3d1', autoadd=False) To(u'graph3d1') Set('Back/color', u'#f0f0f0') Set('Back/hide', False) Add('axis3d', name=u'x', autoadd=False) To(u'x') Set('label', u'Axis \\textbf{x}') Set('min', -0.5) Set('max', 2.5) To('..') Add('axis3d', name=u'y', autoadd=False) To(u'y') Set('label', u'Axis \\textbf{y}') Set('min', -0.5) Set('max', 2.5) Set('direction', u'y') To('..') Add('axis3d', name=u'z', autoadd=False) To(u'z') Set('label', u'Axis \\textbf{z}') Set('min', -0.5) Set('max', 2.5) Set('direction', u'z') To('..') Add('volume3d', name=u'volume3d1', autoadd=False) To(u'volume3d1') Set('colorMap', u'hot_desaturated') Set('fillfactor', 0.7) Set('DataColor/points', u'vv') Set('xData', u'xv') Set('yData', u'yv') Set('zData', u'zv') To('..') To('..') To('..') To('..') veusz-3.0.1/examples/example_import.vsz0000664000175000017500000000545413166120037017727 0ustar jssjss00000000000000# Veusz saved document (version 0.10.cvs) # User: jss # Date: Sat, 17 Jun 2006 16:23:25 +0000 ImportFile('example_import_1.dat', 'x1 y1,+-', linked=True, useblocks=True) ImportFile('example_import_2.dat', '', linked=True) Set('width', u'10cm') Add('page', name='page1', autoadd=False) To('page1') Add('grid', name='grid1', autoadd=False) To('grid1') Set('rows', 3) Set('columns', 1) Set('leftMargin', u'0.1cm') Set('bottomMargin', u'0.1cm') Add('graph', name='samplefile1', autoadd=False) To('samplefile1') Add('axis', name='x', autoadd=False) To('x') Set('label', u'This is an \\emph{x-axis}') To('..') Add('axis', name='y', autoadd=False) To('y') Set('label', u'\\emph{y}^1') Set('direction', 'vertical') Set('Label/rotate', True) To('..') Add('xy', name='ds1', autoadd=False) To('ds1') Set('xData', u'x1_1') Set('yData', u'y1_1') Set('PlotLine/color', u'red') Set('MarkerFill/color', u'red') To('..') Add('xy', name='ds2', autoadd=False) To('ds2') Set('xData', u'x1_2') Set('yData', u'y1_2') Set('marker', u'diamond') Set('PlotLine/color', u'blue') Set('MarkerFill/color', u'blue') To('..') To('..') Add('graph', name='file2graph1', autoadd=False) To('file2graph1') Add('axis', name='x', autoadd=False) To('x') Set('label', u'Another \\bold{x-axis}') To('..') Add('axis', name='y', autoadd=False) To('y') Set('label', u'\\emph{y}^2') Set('direction', 'vertical') Set('Label/rotate', True) To('..') Add('xy', name='thisxy', autoadd=False) To('thisxy') Set('xData', u'thisx') Set('yData', u'thisy') Set('errorStyle', u'diamond') Set('PlotLine/color', u'green') Set('MarkerFill/color', u'green') Set('ErrorBarLine/color', u'green') To('..') Add('xy', name='anotherxy', autoadd=False) To('anotherxy') Set('xData', u'anotherx') Set('yData', u'anothery') Set('errorStyle', u'curve') Set('PlotLine/color', u'magenta') Set('MarkerFill/color', u'magenta') Set('ErrorBarLine/color', u'magenta') To('..') To('..') Add('graph', name='file2graph2', autoadd=False) To('file2graph2') Add('axis', name='x', autoadd=False) To('x') Set('label', u'Final \\underline{x axis}') To('..') Add('axis', name='y', autoadd=False) To('y') Set('label', u'\\emph{y}^3') Set('direction', 'vertical') Set('Label/rotate', True) To('..') Add('xy', name='noise1', autoadd=False) To('noise1') Set('xData', u'noisex') Set('yData', u'noisey_1') Set('PlotLine/steps', u'centre') Set('PlotLine/color', u'red') Set('MarkerFill/color', u'red') To('..') Add('xy', name='noise2', autoadd=False) To('noise2') Set('xData', u'noisex') Set('yData', u'noisey_2') Set('PlotLine/steps', u'centre') Set('PlotLine/color', u'cyan') Set('MarkerFill/color', u'cyan') To('..') Add('xy', name='noise3', autoadd=False) To('noise3') Set('xData', u'noisex') Set('yData', u'noisey_3') Set('PlotLine/steps', u'centre') Set('PlotLine/color', u'grey') Set('MarkerFill/color', u'grey') To('..') To('..') To('..') To('..') veusz-3.0.1/examples/datebar.vsz0000664000175000017500000000373413161413406016303 0ustar jssjss00000000000000# Veusz saved document (version 1.3) # User: jss # Date: Wed, 27 May 2009 19:46:06 +0000 ImportFile('datebar.dat', '', linked=True, ignoretext=True) Set('StyleSheet/Line/color', u'#005500') Set('StyleSheet/Font/font', u'Verdana') Set('StyleSheet/Font/color', u'#00557f') Add('page', name='page1', autoadd=False) To('page1') Add('graph', name='graph1', autoadd=False) To('graph1') Set('leftMargin', '1.59cm') Set('rightMargin', '0.416cm') Set('topMargin', '2.11cm') Set('bottomMargin', '4.02cm') Set('Background/color', u'#f5ffcd') Add('axis', name='x', autoadd=False) To('x') Set('label', u'Date') Set('mode', u'datetime') Set('TickLabels/rotate', True) To('..') Add('axis', name='y', autoadd=False) To('y') Set('label', u'Crazy bar value') Set('min', 0.0) Set('direction', 'vertical') To('..') Add('bar', name='bar1', autoadd=False) To('bar1') Set('lengths', (u'value',)) Set('posn', u'd') Set('keys', ('',)) Set('BarFill/fills', [('solid', u'#00aa7f', False)]) Set('BarLine/lines', [('solid', u'1pt', 'black', False)]) Set('ErrorBarLine/width', u'1pt') To('..') Add('xy', name='xy1', autoadd=False) To('xy1') Set('xData', u'd') Set('yData', u'value') Set('marker', u'none') Set('PlotLine/hide', True) Set('FillBelow/color', u'#d9ffe5') Set('FillBelow/hide', False) To('..') Add('label', name='label1', autoadd=False) To('label1') Set('label', u'Look!') Set('xPos', [0.31336981405284353]) Set('yPos', [0.63329357828966459]) Set('alignHorz', u'right') Set('angle', 50.0) Set('Text/size', u'20pt') To('..') Add('line', name='line1', autoadd=False) To('line1') Set('arrowleft', u'bar') Set('arrowright', u'arrow') Set('xPos', [0.30322686916441111]) Set('yPos', [0.63075731090716558]) Set('length', [0.24757735730509881]) Set('angle', [45.467843029728137]) To('..') To('..') Add('label', name='label1', autoadd=False) To('label1') Set('label', u'A graph title') Set('xPos', [0.5]) Set('yPos', [0.97999999999999998]) Set('alignHorz', u'centre') Set('alignVert', u'top') Set('Text/size', u'30pt') To('..') To('..') veusz-3.0.1/examples/hatching.vsz0000664000175000017500000000607113161413406016463 0ustar jssjss00000000000000# Veusz saved document (version 1.15) # Saved at 2012-04-21T09:57:53.271330 ImportString(u'y1(numeric)',''' 0.000000e+00 2.000000e+00 3.000000e+00 5.000000e+00 6.000000e+00 9.000000e+00 3.000000e+00 ''') ImportString(u'y2(numeric)',''' 1.000000e+00 4.000000e+00 5.000000e+00 8.000000e+00 8.000000e+00 1.000000e+01 9.000000e+00 ''') DatasetPlugin('Add', {'ds_out': u'y3', 'value': 1.0, 'ds_in': u'y2'}) Set('StyleSheet/Font/font', u'Lucida Sans') Set('StyleSheet/xy/marker', u'none') Add('page', name='page1', autoadd=False) To('page1') Add('graph', name='graph1', autoadd=False) To('graph1') Set('leftMargin', u'0.8cm') Set('rightMargin', u'0.8cm') Set('topMargin', u'0.8cm') Set('bottomMargin', u'0.8cm') Set('Border/width', u'1pt') Add('axis', name='x', autoadd=False) To('x') Set('min', 1.0) Set('max', 7.0) Set('MinorTicks/hide', True) To('..') Add('axis', name='y', autoadd=False) To('y') Set('max', 12.0) Set('direction', 'vertical') Set('MinorTicks/hide', True) To('..') Add('xy', name='xy1', autoadd=False) To('xy1') Set('xData', []) Set('yData', u'y1') Set('PlotLine/steps', u'centre') Set('PlotLine/color', u'blue') Set('PlotLine/width', u'1pt') Set('FillBelow/color', u'blue') Set('FillBelow/style', u'backward diagonals') Set('FillBelow/hide', False) Set('FillBelow/patternspacing', u'6pt') Set('FillBelow/backhide', False) To('..') Add('function', name='function3', autoadd=False) To('function3') Set('function', u'4-y/2.01') Set('variable', u'y') Set('Line/color', u'magenta') Set('Line/width', u'1pt') Set('FillBelow/color', u'magenta') Set('FillBelow/style', u'vertical forward') Set('FillBelow/hide', False) To('..') Add('xy', name='xy2', autoadd=False) To('xy2') Set('xData', []) Set('yData', u'y2') Set('PlotLine/color', u'red') Set('PlotLine/width', u'1pt') Set('FillBelow/color', u'red') Set('FillBelow/style', u'forward diagonals') Set('FillBelow/hide', False) Set('FillBelow/linestyle', u'dashed') Set('FillBelow/backhide', False) To('..') Add('xy', name='xy3', autoadd=False) To('xy3') Set('xData', []) Set('yData', u'y3') Set('PlotLine/color', u'grey') Set('PlotLine/width', u'1pt') Set('FillBelow/style', u'horizontal') Set('FillBelow/hide', False) Set('FillBelow/patternspacing', u'3pt') Set('FillBelow/backhide', False) To('..') Add('function', name='function1', autoadd=False) To('function1') Set('function', u'10-y/2') Set('variable', u'y') Set('Line/color', u'green') Set('FillAbove/color', u'#00ff7f') Set('FillAbove/style', u'horizontal double') Set('FillAbove/hide', False) Set('FillAbove/linewidth', u'0.25pt') Set('FillAbove/backcolor', u'green') Set('FillAbove/backhide', False) To('..') Add('function', name='function2', autoadd=False) To('function2') Set('function', u'9-y/2') Set('variable', u'y') Set('Line/color', u'#5500ff') Set('FillAbove/color', u'#5500ff') Set('FillAbove/style', u'diagonal cross 2') Set('FillAbove/hide', False) To('..') Add('label', name='label1', autoadd=False) To('label1') Set('label', u'\\color{green}{Hatch}ing') Set('xPos', [0.5]) Set('yPos', [1.06]) Set('alignHorz', u'centre') Set('alignVert', u'top') Set('Text/size', u'18pt') To('..') To('..') To('..') veusz-3.0.1/examples/3d_function.vsz0000664000175000017500000000203113302503667017107 0ustar jssjss00000000000000# Veusz saved document (version 3.0) # Saved at 2018-05-27T10:22:20.441558 Set('colorTheme', u'default-latest') Set('StyleSheet/axis-function/autoRange', u'next-tick') Add('page', name=u'page1', autoadd=False) To(u'page1') Add('scene3d', name=u'scene3d1', autoadd=False) To(u'scene3d1') Set('xRotation', -51.0) Set('yRotation', -20.0) Set('zRotation', 0.0) Add('graph3d', name=u'graph3d1', autoadd=False) To(u'graph3d1') Set('ySize', 0.5) Add('axis3d', name=u'x', autoadd=False) To(u'x') Set('label', u'X axis') Set('GridLines/hide', False) To('..') Add('axis3d', name=u'y', autoadd=False) To(u'y') Set('label', u'Y axis') Set('direction', u'y') Set('GridLines/hide', False) To('..') Add('axis3d', name=u'z', autoadd=False) To(u'z') Set('label', u'Z axis') Set('direction', u'z') Set('GridLines/hide', False) To('..') Add('function3d', name=u'function3d1', autoadd=False) To(u'function3d1') Set('mode', u'y=fn(x,z)') Set('fny', u'sin(2*pi*x)*sin(3*pi*z)') Set('surfacesteps', 40) Set('Surface/color', u'theme6') To('..') To('..') To('..') To('..') veusz-3.0.1/examples/contour.vsz0000664000175000017500000000146413161413406016370 0ustar jssjss00000000000000# Veusz saved document (version 0.9) # User: jss # Date: Wed, 18 Jan 2006 21:50:11 +0000 SetData2D('vals', fromfunction(lambda x, y: sin(x*0.05)+cos(x*0.1+y*0.1), (100, 100))) SetData2D('vals2', fromfunction(lambda x, y: sin(x*0.2+y*0.1), (100, 100))) Add('page', name='page1', autoadd=False) To('page1') Add('graph', name='graph1', autoadd=False) To('graph1') Add('axis', name='x', autoadd=False) Add('axis', name='y', autoadd=False) To('y') Set('direction', 'vertical') To('..') Add('contour', name='contour1', autoadd=False) To('contour1') Set('data', u'vals') Set('numLevels', 10) Set('lines', [('solid', '1pt', u'#5500ff', False), ('dotted', '1pt', u'#aa557f', False)]) To('..') Add('image', name='image1', autoadd=False) To('image1') Set('data', u'vals2') Set('colorMap', u'bluegreen') To('..') To('..') To('..') veusz-3.0.1/examples/mandelbrot.vsz0000664000175000017500000000254213161413406017024 0ustar jssjss00000000000000# Veusz script (version 0.7 or later) # Jeremy Sanders (2005) # computes the Mandelbrot set in real time size = 300 maxiters = 20 image = zeros( (size, size) ) print("This takes some time, please wait") minx = 100000 maxx = -100000 miny = 100000 maxy = -100000 for i in range(size): for j in range(int(size/2)): c1 = -2+4.*i/size c2 = 2-4.*j/size x = c1 y = c2 minx=min(x, minx) maxx=max(x, maxx) miny=min(y, miny) maxy=max(y, maxy) n = 0 while n < maxiters and x**2+y**2 < 4.: x1 = x**2-y**2+c1 y1 = 2*x*y+c2 x = x1 y = y1 n += 1 image[j, i] = n image[size-j-1, i] = n # set output data into veusz SetData2D('image', image, xrange=(minx, maxx), yrange=(miny, maxy)) # construct the graph To(Add('page')) To(Add('graph')) # Add a label Add('label', label='The Mandelbrot Set', yPos=0.95, alignHorz='centre', alignVert='top', Text__size='30pt') # add colorbar in front of image Add('colorbar', name='colorbar1', image='image1', direction='vertical', vertPosn='top') # add image Add('image', name='image1', data='image', min=1, colorScaling='log', colorMap='heat', colorInvert=True) # adjust axes Set('x/min', -2.2) Set('x/max', 1.2) Set('y/min', 0.3) Set('y/max', 1.9) To('/') veusz-3.0.1/examples/3d_errors.vsz0000664000175000017500000000367713306705570016620 0ustar jssjss00000000000000# Veusz saved document (version 2.999) # Saved at 2018-06-07T17:02:45.719713 ImportFileCSV(u'3d_errors.csv', linked=True, numericlocale=u'en_GB') Set('height', u'12cm') Set('colorTheme', u'default-latest') Set('StyleSheet/axis-function/autoRange', u'next-tick') Set('StyleSheet/axis3d/TickLabels/size', u'10pt') Set('StyleSheet/axis3d/GridLines/color', u'#dcdcdc') Set('StyleSheet/axis3d/GridLines/hide', False) Add('page', name=u'page1', autoadd=False) To(u'page1') Add('scene3d', name=u'scene3d', autoadd=False) To(u'scene3d') Set('xRotation', -108.0) Set('yRotation', -34.0) Set('zRotation', 0.0) Add('graph3d', name=u'graph3d1', autoadd=False) To(u'graph3d1') Set('xSize', 1.5) Set('Back/color', u'#efefef') Set('Back/hide', False) Add('axis3d', name=u'x', autoadd=False) To(u'x') Set('label', u'\\emph{x}') Set('autoRange', u'+2%') To('..') Add('axis3d', name=u'y', autoadd=False) To(u'y') Set('label', u'sin 3\\pi\\emph{x}') Set('autoRange', u'next-tick') Set('direction', u'y') To('..') Add('axis3d', name=u'z', autoadd=False) To(u'z') Set('label', u'cos 3\\pi\\emph{x}') Set('direction', u'z') To('..') Add('point3d', name=u'redpts', autoadd=False) To(u'redpts') Set('markerSize', 8.0) Set('xData', u'x1') Set('yData', u'y1') Set('zData', u'z1') Set('Error/width', 2.0) To('..') Add('point3d', name=u'greenpts', autoadd=False) To(u'greenpts') Set('markerSize', 5.0) Set('xData', u'x2') Set('yData', u'y2') Set('zData', u'z2') Set('Error/width', 1.0) Set('Error/hide', False) To('..') Add('function3d', name=u'function', autoadd=False) To(u'function') Set('fnx', u'(t-0.5)*2') Set('fny', u'sin((t-0.5)*2*3*pi)') Set('fnz', u'cos((t-0.5)*2*3*pi)') Set('linesteps', 200) Set('Line/color', u'green') Set('Line/width', 10.0) Set('Line/reflectivity', 40.0) To('..') Add('function3d', name=u'axisline', autoadd=False) To(u'axisline') Set('fnx', u'2.2*(t-0.5)') Set('fny', u'0') Set('fnz', u'0') Set('Line/color', u'grey') Set('Line/width', 4.0) To('..') To('..') To('..') To('..') veusz-3.0.1/examples/bar_labels.vsz0000664000175000017500000000500113161413406016754 0ustar jssjss00000000000000# Veusz saved document (version 1.17.1) ImportFile(u'bar_labels.dat', u'', linked=True, ignoretext=True) Set('width', '10.5cm') Set('height', '16.2cm') Set('StyleSheet/Font/font', u'Arial') Set('StyleSheet/axis/Label/bold', True) Add('page', name='page1', autoadd=False) To('page1') Add('grid', name='grid1', autoadd=False) To('grid1') Set('columns', 1) Set('scaleRows', [1.0, 0.8]) Set('leftMargin', u'0cm') Set('rightMargin', u'0cm') Set('topMargin', u'0cm') Set('bottomMargin', u'0cm') Add('graph', name='graph1', autoadd=False) To('graph1') Set('leftMargin', u'1.7cm') Set('rightMargin', u'1.7cm') Add('axis', name='x', autoadd=False) To('x') Set('label', u'Colour') Set('mode', u'labels') To('..') Add('axis', name='y', autoadd=False) To('y') Set('label', u'Number of balloons') Set('min', 0.0) Set('direction', 'vertical') Set('Label/atEdge', True) To('..') Add('bar', name='bar1', autoadd=False) To('bar1') Set('lengths', (u'spring', u'summer')) Set('keys', (u'Spring', u'Summer')) Set('labels', u'name') Set('errorstyle', u'barends') Set('BarFill/fills', [('solid', u'#aaffff', False), ('solid', u'#00aaff', False)]) Set('ErrorBarLine/width', u'2pt') To('..') Add('key', name='key1', autoadd=False) To('key1') Set('Border/hide', True) Set('horzPosn', 'left') Set('vertPosn', 'top') Set('horzManual', 0.0) Set('vertManual', 0.0) To('..') To('..') Add('graph', name='graph2', autoadd=False) To('graph2') Set('leftMargin', u'1.7cm') Set('rightMargin', u'1.7cm') Set('topMargin', u'0.6cm') Set('bottomMargin', '1.7cm') Add('axis', name='x', autoadd=False) To('x') Set('label', u'Spring') To('..') Add('axis', name='y', autoadd=False) To('y') Set('label', u'Summer') Set('direction', 'vertical') Set('Label/atEdge', True) To('..') Add('axis', name='x2', autoadd=False) To('x2') Set('mode', u'labels') Set('otherPosition', 1.0) To('..') Add('axis', name='y2', autoadd=False) To('y2') Set('mode', u'labels') Set('direction', u'vertical') Set('otherPosition', 1.0) To('..') Add('xy', name='data', autoadd=False) To('data') Set('xData', u'spring') Set('yData', u'summer') Set('labels', u'name') Set('PlotLine/hide', True) Set('MarkerLine/hide', True) Set('MarkerFill/hide', True) Set('ErrorBarLine/hide', True) Set('Label/hide', True) To('..') Add('xy', name='datacopy', autoadd=False) To('datacopy') Set('xData', u'spring') Set('yData', u'summer') Set('markerSize', u'5pt') Set('labels', u'name') Set('xAxis', u'x2') Set('yAxis', u'y2') Set('PlotLine/hide', True) Set('MarkerFill/color', u'#55aaff') Set('Label/hide', True) To('..') To('..') To('..') To('..') veusz-3.0.1/examples/3d_errors.csv0000664000175000017500000004166113304534157016563 0ustar jssjss00000000000000x1,+-,y1,+-,z1,+- -1.00448,0.06,0.0454015,0.06,-0.951914,0.06 -0.947443,0.06,-0.0942348,0.06,-0.978727,0.06 -0.955812,0.06,-0.159061,0.06,-0.965282,0.06 -0.920483,0.06,-0.232495,0.06,-0.968487,0.06 -0.884499,0.06,-0.330715,0.06,-0.876091,0.06 -0.96593,0.06,-0.418641,0.06,-0.938588,0.06 -0.953503,0.06,-0.535316,0.06,-0.847697,0.06 -0.902111,0.06,-0.623433,0.06,-0.668504,0.06 -0.852379,0.06,-0.672712,0.06,-0.785483,0.06 -0.900684,0.06,-0.820237,0.06,-0.62733,0.06 -0.902204,0.06,-0.756001,0.06,-0.532409,0.06 -0.882562,0.06,-0.960135,0.06,-0.551147,0.06 -0.903743,0.06,-0.954134,0.06,-0.425931,0.06 -0.741967,0.06,-0.931708,0.06,-0.231332,0.06 -0.756451,0.06,-0.978793,0.06,-0.311701,0.06 -0.794406,0.06,-1.01599,0.06,-0.161483,0.06 -0.814944,0.06,-0.972457,0.06,-0.0558779,0.06 -0.795097,0.06,-1.03041,0.06,0.059825,0.06 -0.864342,0.06,-0.981855,0.06,0.153996,0.06 -0.913998,0.06,-0.980855,0.06,0.271356,0.06 -0.805352,0.06,-0.907594,0.06,0.341329,0.06 -0.793659,0.06,-0.924213,0.06,0.304861,0.06 -0.737846,0.06,-0.843649,0.06,0.630507,0.06 -0.802382,0.06,-0.707305,0.06,0.556455,0.06 -0.73403,0.06,-0.712618,0.06,0.573294,0.06 -0.730333,0.06,-0.718281,0.06,0.693412,0.06 -0.738909,0.06,-0.624228,0.06,0.740766,0.06 -0.741116,0.06,-0.610928,0.06,0.779018,0.06 -0.685351,0.06,-0.526182,0.06,0.81063,0.06 -0.748649,0.06,-0.426492,0.06,0.862282,0.06 -0.655477,0.06,-0.389519,0.06,0.999507,0.06 -0.689694,0.06,-0.193482,0.06,1.03476,0.06 -0.61745,0.06,-0.0569515,0.06,1.00367,0.06 -0.632543,0.06,-0.0192845,0.06,1.02596,0.06 -0.701458,0.06,0.100714,0.06,1.04286,0.06 -0.602175,0.06,0.0535054,0.06,1.01693,0.06 -0.703399,0.06,0.203364,0.06,0.835908,0.06 -0.570363,0.06,0.301452,0.06,0.891448,0.06 -0.619694,0.06,0.524696,0.06,0.980087,0.06 -0.59222,0.06,0.493962,0.06,0.842771,0.06 -0.59944,0.06,0.558372,0.06,0.905672,0.06 -0.652552,0.06,0.646463,0.06,0.729694,0.06 -0.556336,0.06,0.741027,0.06,0.58216,0.06 -0.681687,0.06,0.819553,0.06,0.67249,0.06 -0.641079,0.06,0.851184,0.06,0.457407,0.06 -0.615908,0.06,1.01729,0.06,0.407514,0.06 -0.513977,0.06,0.86237,0.06,0.338065,0.06 -0.716902,0.06,0.994666,0.06,0.220135,0.06 -0.593159,0.06,0.868029,0.06,0.138589,0.06 -0.474601,0.06,0.940369,0.06,0.103171,0.06 -0.456399,0.06,1.03949,0.06,0.0482541,0.06 -0.482752,0.06,1.00334,0.06,-0.141322,0.06 -0.533101,0.06,0.918075,0.06,-0.212267,0.06 -0.556515,0.06,0.883325,0.06,-0.300071,0.06 -0.486339,0.06,1.00983,0.06,-0.404375,0.06 -0.612135,0.06,0.840026,0.06,-0.478285,0.06 -0.402383,0.06,0.909235,0.06,-0.570573,0.06 -0.472022,0.06,0.862358,0.06,-0.577869,0.06 -0.291023,0.06,0.6801,0.06,-0.696262,0.06 -0.39881,0.06,0.700709,0.06,-0.74615,0.06 -0.449917,0.06,0.625223,0.06,-0.757763,0.06 -0.374816,0.06,0.569918,0.06,-0.816722,0.06 -0.357099,0.06,0.54083,0.06,-0.831405,0.06 -0.282226,0.06,0.345871,0.06,-0.849463,0.06 -0.332244,0.06,0.214353,0.06,-0.938509,0.06 -0.279948,0.06,0.198775,0.06,-0.932524,0.06 -0.339269,0.06,0.181751,0.06,-1.07799,0.06 -0.365235,0.06,-0.215189,0.06,-1.06142,0.06 -0.306287,0.06,-0.10276,0.06,-1.00215,0.06 -0.347479,0.06,-0.172078,0.06,-0.967013,0.06 -0.30142,0.06,-0.158514,0.06,-0.880952,0.06 -0.186286,0.06,-0.549934,0.06,-0.924865,0.06 -0.332818,0.06,-0.427934,0.06,-0.914938,0.06 -0.302224,0.06,-0.606999,0.06,-0.860756,0.06 -0.276376,0.06,-0.60414,0.06,-0.844206,0.06 -0.276715,0.06,-0.656373,0.06,-0.791376,0.06 -0.314162,0.06,-0.78283,0.06,-0.674945,0.06 -0.269132,0.06,-0.80759,0.06,-0.639385,0.06 -0.219433,0.06,-0.913001,0.06,-0.494063,0.06 -0.120762,0.06,-1.01759,0.06,-0.42471,0.06 -0.145937,0.06,-0.98997,0.06,-0.280665,0.06 -0.0743879,0.06,-1.02008,0.06,-0.297798,0.06 -0.201471,0.06,-0.944159,0.06,-0.118325,0.06 -0.158172,0.06,-1.01026,0.06,0.0295954,0.06 -0.186688,0.06,-0.937426,0.06,0.0694743,0.06 -0.22076,0.06,-0.946719,0.06,0.175579,0.06 -0.10402,0.06,-0.906441,0.06,0.309233,0.06 -0.133595,0.06,-0.990401,0.06,0.324354,0.06 -0.276736,0.06,-0.825232,0.06,0.457692,0.06 -0.142423,0.06,-0.899158,0.06,0.550457,0.06 -0.0230324,0.06,-0.808618,0.06,0.593998,0.06 -0.097928,0.06,-0.752205,0.06,0.483746,0.06 -0.0332169,0.06,-0.682944,0.06,0.769176,0.06 -0.0196327,0.06,-0.597393,0.06,0.899899,0.06 0.0271343,0.06,-0.453542,0.06,0.795189,0.06 -0.0661508,0.06,-0.418441,0.06,0.91078,0.06 -0.0023447,0.06,-0.326181,0.06,0.917986,0.06 -0.0768333,0.06,-0.244604,0.06,0.981321,0.06 -0.0343207,0.06,-0.230395,0.06,0.949182,0.06 0.0127403,0.06,-0.0622033,0.06,0.921166,0.06 0.0110798,0.06,-0.0552179,0.06,0.970893,0.06 0.0972176,0.06,0.133507,0.06,0.944285,0.06 -0.0276843,0.06,0.0906245,0.06,0.968943,0.06 -0.00790523,0.06,0.185476,0.06,0.960495,0.06 0.0278674,0.06,0.375758,0.06,0.908298,0.06 0.0512346,0.06,0.382179,0.06,0.814695,0.06 0.0902207,0.06,0.52849,0.06,0.838766,0.06 0.140358,0.06,0.672878,0.06,0.767818,0.06 0.0987538,0.06,0.641095,0.06,0.696411,0.06 0.111532,0.06,0.811083,0.06,0.649827,0.06 0.0682611,0.06,0.973913,0.06,0.538531,0.06 0.133155,0.06,0.897701,0.06,0.497559,0.06 0.130593,0.06,0.928756,0.06,0.323769,0.06 0.120737,0.06,0.943233,0.06,0.463417,0.06 0.134873,0.06,0.942602,0.06,0.216087,0.06 0.100361,0.06,1.04524,0.06,0.0981518,0.06 0.109586,0.06,1.05729,0.06,0.121286,0.06 0.184291,0.06,0.989075,0.06,0.0810608,0.06 0.159288,0.06,1.0025,0.06,-0.0395205,0.06 0.15892,0.06,0.984464,0.06,-0.266308,0.06 0.238006,0.06,0.887419,0.06,-0.287985,0.06 0.101126,0.06,0.928253,0.06,-0.512826,0.06 0.259285,0.06,0.962868,0.06,-0.535387,0.06 0.138209,0.06,0.88451,0.06,-0.60212,0.06 0.242805,0.06,0.791398,0.06,-0.653316,0.06 0.300255,0.06,0.635543,0.06,-0.605936,0.06 0.219418,0.06,0.512595,0.06,-0.732576,0.06 0.211656,0.06,0.526431,0.06,-0.845843,0.06 0.30551,0.06,0.434423,0.06,-0.838139,0.06 0.304505,0.06,0.388966,0.06,-0.879645,0.06 0.257109,0.06,0.335583,0.06,-0.903018,0.06 0.290475,0.06,0.190718,0.06,-0.933301,0.06 0.284016,0.06,0.106706,0.06,-1.09854,0.06 0.342215,0.06,-0.00893046,0.06,-0.997684,0.06 0.30836,0.06,-0.121989,0.06,-1.08736,0.06 0.357738,0.06,-0.208717,0.06,-0.91332,0.06 0.413657,0.06,-0.273229,0.06,-0.952776,0.06 0.313334,0.06,-0.338769,0.06,-1.00538,0.06 0.356891,0.06,-0.404485,0.06,-1.00726,0.06 0.346918,0.06,-0.452744,0.06,-0.810078,0.06 0.388333,0.06,-0.620981,0.06,-0.824373,0.06 0.360005,0.06,-0.638577,0.06,-0.692562,0.06 0.537424,0.06,-0.696058,0.06,-0.73616,0.06 0.52648,0.06,-0.737965,0.06,-0.731104,0.06 0.431868,0.06,-0.816404,0.06,-0.587188,0.06 0.44435,0.06,-0.875048,0.06,-0.458394,0.06 0.444737,0.06,-0.903543,0.06,-0.422456,0.06 0.547175,0.06,-0.968495,0.06,-0.177905,0.06 0.563184,0.06,-0.909213,0.06,-0.302382,0.06 0.497507,0.06,-0.981565,0.06,-0.13926,0.06 0.4293,0.06,-1.00536,0.06,0.048411,0.06 0.573394,0.06,-1.05345,0.06,0.0674608,0.06 0.329,0.06,-0.971577,0.06,0.133591,0.06 0.642484,0.06,-1.05769,0.06,0.336996,0.06 0.573453,0.06,-0.971537,0.06,0.401747,0.06 0.582666,0.06,-0.878874,0.06,0.482375,0.06 0.55086,0.06,-0.891078,0.06,0.487663,0.06 0.461398,0.06,-0.878627,0.06,0.628176,0.06 0.562387,0.06,-0.791189,0.06,0.655457,0.06 0.591783,0.06,-0.575695,0.06,0.750382,0.06 0.640342,0.06,-0.562601,0.06,0.740086,0.06 0.621759,0.06,-0.530069,0.06,0.8361,0.06 0.478106,0.06,-0.467096,0.06,0.935849,0.06 0.658877,0.06,-0.31465,0.06,0.908321,0.06 0.580082,0.06,-0.258146,0.06,0.982489,0.06 0.61856,0.06,-0.108849,0.06,0.988082,0.06 0.663409,0.06,-0.0277383,0.06,1.01637,0.06 0.730002,0.06,0.0844698,0.06,1.01217,0.06 0.739735,0.06,0.179563,0.06,1.11704,0.06 0.694558,0.06,0.27633,0.06,0.9248,0.06 0.678681,0.06,0.209616,0.06,1.04935,0.06 0.67154,0.06,0.309866,0.06,0.917078,0.06 0.689889,0.06,0.506624,0.06,0.865021,0.06 0.690459,0.06,0.449516,0.06,0.85466,0.06 0.741328,0.06,0.607168,0.06,0.812978,0.06 0.811246,0.06,0.699107,0.06,0.750751,0.06 0.710025,0.06,0.715537,0.06,0.589062,0.06 0.870963,0.06,0.953516,0.06,0.656803,0.06 0.776706,0.06,0.937112,0.06,0.64089,0.06 0.813509,0.06,0.921478,0.06,0.392529,0.06 0.859788,0.06,1.03382,0.06,0.255707,0.06 0.881678,0.06,0.966239,0.06,0.23485,0.06 0.826734,0.06,1.0558,0.06,0.10058,0.06 0.767536,0.06,1.01161,0.06,0.0236558,0.06 0.863876,0.06,0.976319,0.06,-0.0543413,0.06 0.856297,0.06,0.938244,0.06,-0.223212,0.06 0.854218,0.06,0.984398,0.06,-0.302873,0.06 0.877578,0.06,1.00859,0.06,-0.245076,0.06 0.800352,0.06,0.896349,0.06,-0.374773,0.06 0.975218,0.06,0.783763,0.06,-0.592971,0.06 0.919722,0.06,0.880823,0.06,-0.663686,0.06 0.900947,0.06,0.848608,0.06,-0.560884,0.06 0.877875,0.06,0.653921,0.06,-0.863291,0.06 0.966517,0.06,0.6413,0.06,-0.771452,0.06 0.982776,0.06,0.592275,0.06,-0.881542,0.06 0.991501,0.06,0.469891,0.06,-1.00053,0.06 0.969254,0.06,0.351262,0.06,-1.0153,0.06 0.977696,0.06,0.34317,0.06,-0.947932,0.06 1.03488,0.06,0.13639,0.06,-0.974893,0.06 0.897537,0.06,0.112707,0.06,-0.971631,0.06 x1,+-,y1,+-,z1,+- x2,+-,y2,+-,z2,+- -0.909433,0.12,0.0315902,0.12,-0.903906,0.12 -1.02174,0.12,-0.353777,0.12,-1.19692,0.12 -1.00062,0.12,-0.0970991,0.12,-0.813362,0.12 -1.02655,0.12,-0.262343,0.12,-0.924805,0.12 -0.941188,0.12,-0.30539,0.12,-0.910257,0.12 -0.722192,0.12,-0.552945,0.12,-1.07446,0.12 -0.841842,0.12,-0.675634,0.12,-0.947185,0.12 -1.02391,0.12,-0.412016,0.12,-0.774787,0.12 -0.781499,0.12,-0.586711,0.12,-0.821311,0.12 -1.06088,0.12,-0.862899,0.12,-0.778453,0.12 -0.955733,0.12,-0.753154,0.12,-0.433627,0.12 -0.770117,0.12,-0.644526,0.12,-0.572019,0.12 -0.792273,0.12,-0.783844,0.12,-0.304108,0.12 -0.707307,0.12,-1.08669,0.12,-0.264245,0.12 -1.02189,0.12,-1.09713,0.12,-0.256242,0.12 -0.826438,0.12,-0.851456,0.12,-0.388577,0.12 -0.797927,0.12,-1.12783,0.12,0.100356,0.12 -0.858826,0.12,-0.979046,0.12,0.0889569,0.12 -0.898634,0.12,-0.792193,0.12,0.124879,0.12 -0.816441,0.12,-0.846331,0.12,-0.0593931,0.12 -0.809099,0.12,-0.834076,0.12,0.496743,0.12 -0.800937,0.12,-0.731285,0.12,0.476956,0.12 -0.95197,0.12,-0.818777,0.12,0.324861,0.12 -0.869312,0.12,-0.954588,0.12,0.565774,0.12 -0.736553,0.12,-0.953897,0.12,0.563197,0.12 -0.61914,0.12,-0.878201,0.12,0.776169,0.12 -0.946974,0.12,-0.512856,0.12,0.683642,0.12 -0.401227,0.12,-0.817905,0.12,0.663558,0.12 -0.607159,0.12,-0.431801,0.12,0.88856,0.12 -0.805828,0.12,-0.314896,0.12,1.01398,0.12 -0.484839,0.12,-0.298945,0.12,1.0122,0.12 -0.74084,0.12,-0.226083,0.12,1.09693,0.12 -0.807101,0.12,-0.165952,0.12,0.890793,0.12 -0.606136,0.12,0.0298083,0.12,1.04259,0.12 -0.667672,0.12,0.0331487,0.12,0.989486,0.12 -0.422148,0.12,0.304617,0.12,1.18486,0.12 -0.616629,0.12,0.0353205,0.12,0.862585,0.12 -0.743659,0.12,0.354096,0.12,1.04591,0.12 -0.485373,0.12,0.293625,0.12,0.825761,0.12 -0.774981,0.12,0.512691,0.12,0.856395,0.12 -0.538268,0.12,0.577049,0.12,0.933722,0.12 -0.810363,0.12,0.671042,0.12,0.629714,0.12 -0.50799,0.12,0.804967,0.12,0.762703,0.12 -0.549628,0.12,0.681533,0.12,0.670826,0.12 -0.514677,0.12,0.892494,0.12,0.372186,0.12 -0.535385,0.12,0.823993,0.12,0.476258,0.12 -0.554932,0.12,1.19799,0.12,0.523284,0.12 -0.601393,0.12,0.914906,0.12,0.263413,0.12 -0.319574,0.12,1.14588,0.12,0.113498,0.12 -0.383485,0.12,1.09979,0.12,0.381014,0.12 -0.467609,0.12,1.20223,0.12,0.13246,0.12 -0.497159,0.12,1.14303,0.12,-0.266137,0.12 -0.417588,0.12,1.00126,0.12,-0.317858,0.12 -0.559819,0.12,0.942225,0.12,-0.323795,0.12 -0.455969,0.12,0.993591,0.12,-0.466727,0.12 -0.331825,0.12,0.85609,0.12,-0.476818,0.12 -0.605026,0.12,0.75244,0.12,-0.460587,0.12 -0.455763,0.12,0.517601,0.12,-0.622688,0.12 -0.266917,0.12,0.672474,0.12,-0.563305,0.12 -0.69255,0.12,0.656359,0.12,-0.759959,0.12 -0.383587,0.12,0.666651,0.12,-0.84831,0.12 -0.4974,0.12,0.414115,0.12,-0.87658,0.12 -0.438031,0.12,0.396581,0.12,-0.98335,0.12 -0.338508,0.12,0.349877,0.12,-1.13561,0.12 -0.303525,0.12,0.163641,0.12,-0.862986,0.12 -0.406519,0.12,0.00329844,0.12,-0.802275,0.12 -0.326492,0.12,0.0736938,0.12,-1.00748,0.12 -0.347072,0.12,-0.00371628,0.12,-1.06831,0.12 -0.349673,0.12,0.0619909,0.12,-0.906562,0.12 -0.213545,0.12,-0.404877,0.12,-0.942114,0.12 -0.362784,0.12,-0.325917,0.12,-1.10029,0.12 -0.268399,0.12,-0.459882,0.12,-0.756055,0.12 -0.157159,0.12,-0.393835,0.12,-0.755544,0.12 -0.242887,0.12,-0.35005,0.12,-0.812651,0.12 -0.228671,0.12,-0.785302,0.12,-0.992836,0.12 -0.34414,0.12,-0.831177,0.12,-0.731814,0.12 0.0190399,0.12,-0.488212,0.12,-0.646227,0.12 -0.240007,0.12,-0.781336,0.12,-0.571424,0.12 -0.295698,0.12,-0.835257,0.12,-0.299837,0.12 0.0644919,0.12,-0.867674,0.12,-0.318956,0.12 -0.184019,0.12,-0.55664,0.12,-0.191202,0.12 -0.157936,0.12,-1.23523,0.12,-0.133067,0.12 -0.186898,0.12,-1.10548,0.12,-0.0179614,0.12 -0.179417,0.12,-1.02249,0.12,0.0126941,0.12 -0.0919219,0.12,-1.01522,0.12,0.0981146,0.12 -0.126226,0.12,-1.0423,0.12,0.101528,0.12 -0.293389,0.12,-0.835823,0.12,0.413815,0.12 0.0274906,0.12,-1.14584,0.12,0.304577,0.12 -0.198418,0.12,-1.17145,0.12,0.190972,0.12 -0.0884789,0.12,-0.932755,0.12,0.554711,0.12 -0.119623,0.12,-0.670497,0.12,0.679786,0.12 0.0214691,0.12,-0.834235,0.12,1.00152,0.12 -0.0545558,0.12,-0.586859,0.12,0.83154,0.12 -0.0425319,0.12,-0.482485,0.12,0.837654,0.12 0.0501673,0.12,-0.474153,0.12,1.16327,0.12 -0.0371996,0.12,-0.437858,0.12,0.944959,0.12 -0.0806935,0.12,-0.239838,0.12,0.855627,0.12 0.0340828,0.12,-0.240755,0.12,0.716904,0.12 -0.0452169,0.12,-0.132548,0.12,1.03281,0.12 -0.148071,0.12,0.0120849,0.12,1.16381,0.12 -0.0389847,0.12,0.0681241,0.12,1.26687,0.12 0.121726,0.12,0.0537357,0.12,0.888257,0.12 -0.203619,0.12,0.0929462,0.12,1.09402,0.12 -0.0867267,0.12,0.353926,0.12,0.994826,0.12 -0.0954137,0.12,0.306779,0.12,0.79981,0.12 0.278323,0.12,0.444491,0.12,0.905732,0.12 0.121056,0.12,0.577553,0.12,0.772352,0.12 0.217986,0.12,0.796494,0.12,0.939422,0.12 0.0707047,0.12,0.682515,0.12,0.72654,0.12 -0.0620369,0.12,0.523099,0.12,0.7082,0.12 0.238426,0.12,0.916179,0.12,0.432197,0.12 0.0942485,0.12,0.725326,0.12,0.515722,0.12 0.0367275,0.12,0.735607,0.12,0.566553,0.12 0.080833,0.12,0.900549,0.12,0.335532,0.12 0.10791,0.12,1.14166,0.12,0.32115,0.12 -0.00788657,0.12,1.01512,0.12,0.205514,0.12 0.150996,0.12,1.05579,0.12,0.160197,0.12 0.0933259,0.12,1.08882,0.12,-0.0819938,0.12 0.113346,0.12,0.997723,0.12,-0.260992,0.12 0.110619,0.12,0.879612,0.12,-0.349086,0.12 -0.0285561,0.12,0.879759,0.12,-0.183554,0.12 0.27938,0.12,1.00333,0.12,-0.381088,0.12 0.191022,0.12,1.0616,0.12,-0.531538,0.12 0.222763,0.12,0.880421,0.12,-0.754832,0.12 0.277048,0.12,0.731521,0.12,-0.505799,0.12 0.0934472,0.12,0.691075,0.12,-0.643674,0.12 0.265658,0.12,0.776819,0.12,-0.74412,0.12 0.48943,0.12,0.600402,0.12,-0.743878,0.12 0.266897,0.12,0.385564,0.12,-0.843687,0.12 0.303707,0.12,0.315583,0.12,-1.02855,0.12 0.0557784,0.12,0.26672,0.12,-1.12425,0.12 0.104817,0.12,0.0400483,0.12,-0.848662,0.12 0.360804,0.12,0.108123,0.12,-0.921334,0.12 0.455307,0.12,0.0298592,0.12,-0.946568,0.12 0.42703,0.12,0.0200414,0.12,-1.03722,0.12 0.237848,0.12,-0.179663,0.12,-0.885243,0.12 0.392521,0.12,-0.214629,0.12,-0.886784,0.12 0.424621,0.12,-0.282102,0.12,-0.910781,0.12 0.611196,0.12,-0.392988,0.12,-0.913619,0.12 0.260208,0.12,-0.703657,0.12,-0.622614,0.12 0.515329,0.12,-0.399928,0.12,-0.916595,0.12 0.28828,0.12,-0.468288,0.12,-0.503043,0.12 0.257514,0.12,-0.849601,0.12,-0.625014,0.12 0.620679,0.12,-0.800703,0.12,-0.62661,0.12 0.466035,0.12,-0.93404,0.12,-0.4374,0.12 0.527338,0.12,-0.890756,0.12,-0.353238,0.12 0.544966,0.12,-0.857517,0.12,-0.332989,0.12 0.378141,0.12,-0.736067,0.12,-0.533991,0.12 0.428424,0.12,-0.884144,0.12,-0.197298,0.12 0.110217,0.12,-1.00464,0.12,-0.0866555,0.12 0.434442,0.12,-0.729898,0.12,0.116029,0.12 0.648675,0.12,-1.07678,0.12,0.209971,0.12 0.374052,0.12,-0.9521,0.12,0.142265,0.12 0.55359,0.12,-0.965407,0.12,0.196062,0.12 0.500903,0.12,-0.838496,0.12,0.418338,0.12 0.690844,0.12,-1.03884,0.12,0.412219,0.12 0.551628,0.12,-0.834547,0.12,0.384225,0.12 0.479766,0.12,-0.728044,0.12,0.536056,0.12 0.674573,0.12,-0.704811,0.12,0.419727,0.12 0.504478,0.12,-0.531857,0.12,0.652931,0.12 0.872651,0.12,-0.251798,0.12,0.984995,0.12 0.623061,0.12,-0.666736,0.12,1.05535,0.12 0.627543,0.12,-0.485675,0.12,0.856055,0.12 0.454172,0.12,-0.401031,0.12,0.990898,0.12 0.621622,0.12,-0.359296,0.12,0.960324,0.12 0.672034,0.12,-0.175103,0.12,0.872143,0.12 0.857018,0.12,0.0407851,0.12,1.12041,0.12 0.742963,0.12,-0.0661234,0.12,0.99442,0.12 0.524733,0.12,0.0790237,0.12,1.1086,0.12 0.787866,0.12,0.385977,0.12,0.838645,0.12 0.778592,0.12,0.191902,0.12,0.966563,0.12 0.672106,0.12,0.545131,0.12,0.87752,0.12 0.850459,0.12,0.129775,0.12,1.02391,0.12 0.692764,0.12,0.743761,0.12,0.955367,0.12 0.815139,0.12,0.755035,0.12,0.78155,0.12 0.648953,0.12,0.583786,0.12,0.690138,0.12 0.546609,0.12,0.932504,0.12,0.885873,0.12 0.774896,0.12,0.905985,0.12,0.622208,0.12 0.795659,0.12,0.881863,0.12,0.464759,0.12 0.61754,0.12,0.806446,0.12,0.269759,0.12 1.03304,0.12,0.841877,0.12,0.489934,0.12 0.772701,0.12,1.15167,0.12,0.173307,0.12 0.660746,0.12,0.901521,0.12,0.262564,0.12 0.75427,0.12,1.18296,0.12,0.127982,0.12 0.947714,0.12,0.862046,0.12,-0.160287,0.12 0.692311,0.12,0.885546,0.12,-0.260943,0.12 0.875724,0.12,1.04515,0.12,-0.263695,0.12 0.587173,0.12,0.892202,0.12,-0.219522,0.12 0.881374,0.12,0.941562,0.12,-0.236475,0.12 0.829785,0.12,0.853008,0.12,-0.491868,0.12 1.05705,0.12,0.810299,0.12,-0.779272,0.12 1.14172,0.12,0.676051,0.12,-0.581968,0.12 1.1589,0.12,0.967967,0.12,-0.782796,0.12 0.920005,0.12,0.48899,0.12,-0.574947,0.12 1.12172,0.12,0.396424,0.12,-0.987884,0.12 0.992874,0.12,0.478546,0.12,-0.731112,0.12 0.919789,0.12,0.471079,0.12,-1.15559,0.12 1.02775,0.12,0.15092,0.12,-0.883386,0.12 0.990849,0.12,-0.00944671,0.12,-0.986405,0.12 1.00028,0.12,0.0578259,0.12,-1.17544,0.12 veusz-3.0.1/examples/spectrum.vsz0000664000175000017500000074234113161413406016547 0ustar jssjss00000000000000# Veusz saved document (version 1.8) # User: jss # Date: Thu, 17 Jun 2010 17:00:56 +0000 ImportString(u'flux(numeric),+-',''' 2.861010e+03 4.547170e+03 -7.409870e+01 6.536110e+01 -2.857510e+00 4.228670e+00 -1.980870e-01 2.777620e-01 1.785610e-01 7.082090e-02 -1.368460e-02 1.465760e-02 3.897360e-04 5.402620e-03 3.944980e-03 2.587620e-03 4.341220e-05 1.533630e-03 1.702510e-03 8.312080e-04 -1.016690e-03 6.990930e-04 7.531280e-04 5.704690e-04 5.805790e-04 4.560370e-04 3.678060e-04 4.197650e-04 5.169160e-05 2.739360e-04 4.095020e-04 1.523600e-04 1.338490e-04 8.217050e-05 3.564070e-05 6.596020e-05 -2.585000e-05 4.778170e-05 1.126610e-04 5.349490e-05 8.400350e-05 7.116170e-05 2.121370e-04 9.766310e-05 3.038190e-04 1.869580e-04 7.236700e-04 3.161350e-04 1.028230e-03 4.622540e-04 2.146910e-03 6.097640e-04 2.904600e-03 7.987310e-04 2.291840e-03 8.011260e-04 2.960800e-03 8.085590e-04 4.408690e-03 7.376950e-04 3.567680e-03 5.860230e-04 2.422900e-03 4.616410e-04 2.563590e-03 3.805690e-04 1.620900e-03 3.021680e-04 2.021810e-03 2.428460e-04 1.716790e-03 1.923180e-04 1.838320e-03 1.890360e-04 1.445380e-03 1.340410e-04 1.510370e-03 1.192620e-04 1.544470e-03 1.198910e-04 1.521150e-03 1.129190e-04 1.362110e-03 1.108320e-04 1.581290e-03 1.038470e-04 1.804590e-03 1.084650e-04 1.929750e-03 1.038210e-04 1.834490e-03 9.696040e-05 1.666100e-03 9.004330e-05 1.223830e-03 8.225680e-05 1.124530e-03 7.873860e-05 1.206090e-03 7.675500e-05 1.264110e-03 7.400770e-05 1.371810e-03 7.158190e-05 1.338920e-03 6.836650e-05 1.342980e-03 6.895620e-05 1.219650e-03 6.543910e-05 1.163210e-03 6.335340e-05 1.127750e-03 6.091640e-05 1.026730e-03 5.334660e-05 9.756010e-04 4.979970e-05 9.780990e-04 4.899700e-05 8.115800e-04 4.475730e-05 8.957100e-04 4.645520e-05 8.947580e-04 4.497950e-05 9.635150e-04 5.058840e-05 1.069390e-03 5.872320e-05 1.006650e-03 4.948750e-05 9.243670e-04 4.591560e-05 9.125620e-04 4.679670e-05 8.713890e-04 4.256130e-05 8.910070e-04 4.384970e-05 7.762210e-04 4.241900e-05 8.683510e-04 4.119610e-05 8.455850e-04 4.172860e-05 6.999520e-04 4.040850e-05 8.657580e-04 4.669020e-05 8.503220e-04 4.050280e-05 1.009740e-03 4.270630e-05 9.939050e-04 4.107680e-05 9.318460e-04 4.149810e-05 9.162650e-04 3.974780e-05 9.679990e-04 3.982370e-05 1.037850e-03 4.140730e-05 1.048320e-03 4.221370e-05 9.859770e-04 4.018910e-05 9.611630e-04 4.050540e-05 9.496320e-04 4.197060e-05 1.154220e-03 4.397390e-05 1.195480e-03 4.304880e-05 1.316100e-03 4.479180e-05 1.322080e-03 4.447060e-05 1.462290e-03 4.833050e-05 1.409960e-03 4.638030e-05 1.324160e-03 5.276210e-05 1.161440e-03 4.374760e-05 1.134130e-03 4.164150e-05 1.137740e-03 4.225890e-05 1.060220e-03 4.030560e-05 1.057720e-03 4.105860e-05 1.014710e-03 4.044670e-05 9.644090e-04 3.719090e-05 1.029160e-03 3.826920e-05 8.932350e-04 3.508860e-05 9.523760e-04 3.626080e-05 1.031550e-03 3.898160e-05 1.037600e-03 3.908580e-05 9.752100e-04 3.662610e-05 9.999040e-04 3.697830e-05 9.950660e-04 3.565950e-05 1.087500e-03 3.751450e-05 9.485900e-04 3.502090e-05 1.017960e-03 3.724980e-05 9.560120e-04 3.503670e-05 1.016640e-03 3.755340e-05 9.564330e-04 3.484880e-05 9.770200e-04 3.417970e-05 9.268900e-04 3.296080e-05 9.034370e-04 3.352500e-05 8.671910e-04 3.244000e-05 9.829280e-04 3.439780e-05 9.941800e-04 3.471960e-05 9.448990e-04 3.263870e-05 9.526980e-04 3.644530e-05 9.415620e-04 4.245090e-05 1.085490e-03 4.495640e-05 1.059780e-03 3.875400e-05 1.085230e-03 3.916330e-05 1.059810e-03 3.777580e-05 9.934540e-04 3.729870e-05 1.007470e-03 3.746450e-05 1.063620e-03 3.789320e-05 1.180250e-03 4.206580e-05 1.191950e-03 4.368860e-05 1.173540e-03 3.911450e-05 1.285680e-03 4.113160e-05 1.420680e-03 5.629710e-05 1.467120e-03 5.625290e-05 1.711210e-03 6.086900e-05 1.813310e-03 6.263700e-05 1.988670e-03 6.582080e-05 1.909580e-03 6.424930e-05 1.793240e-03 6.205990e-05 1.794340e-03 6.273630e-05 1.541830e-03 6.133070e-05 1.700930e-03 5.972990e-05 1.743430e-03 6.117250e-05 1.848860e-03 6.230040e-05 2.076060e-03 6.570080e-05 2.480300e-03 7.184680e-05 2.433870e-03 7.028620e-05 2.312970e-03 6.872160e-05 2.465270e-03 7.242480e-05 2.511480e-03 7.368900e-05 2.488910e-03 7.248220e-05 2.312320e-03 7.219380e-05 2.106310e-03 6.584510e-05 2.057450e-03 8.573220e-05 2.014860e-03 6.764160e-05 1.886570e-03 6.261900e-05 1.722480e-03 5.960430e-05 1.726690e-03 6.164920e-05 1.714720e-03 6.004540e-05 2.058800e-03 6.711510e-05 2.270420e-03 6.894370e-05 2.345830e-03 6.963730e-05 2.179790e-03 6.633480e-05 1.947280e-03 6.339100e-05 1.770830e-03 5.906180e-05 1.657760e-03 5.116800e-05 1.617140e-03 5.004290e-05 1.737280e-03 5.254400e-05 1.955750e-03 5.517160e-05 2.327110e-03 6.034000e-05 2.034150e-03 5.579600e-05 1.793670e-03 5.603270e-05 1.786760e-03 5.929700e-05 1.780900e-03 5.311330e-05 1.467690e-03 4.967820e-05 1.357520e-03 4.717840e-05 1.283180e-03 4.533820e-05 1.288750e-03 4.563510e-05 1.146980e-03 4.253180e-05 1.069530e-03 4.100800e-05 1.051730e-03 4.122660e-05 1.000000e-03 4.035170e-05 9.702090e-04 3.974090e-05 1.115700e-03 4.289800e-05 1.215350e-03 4.596170e-05 1.203690e-03 4.515030e-05 1.108780e-03 7.097760e-05 9.385710e-04 5.090720e-05 9.011720e-04 3.890710e-05 9.431780e-04 4.058840e-05 9.163000e-04 3.924170e-05 8.846180e-04 3.863360e-05 8.958540e-04 3.996850e-05 8.987240e-04 3.969160e-05 8.841800e-04 3.896110e-05 9.454700e-04 4.043850e-05 1.025550e-03 4.212830e-05 1.029330e-03 4.169800e-05 9.236210e-04 3.961090e-05 8.037460e-04 3.282890e-05 8.121830e-04 3.028440e-05 7.751830e-04 2.974850e-05 8.192630e-04 3.020080e-05 7.800070e-04 2.923660e-05 7.512600e-04 2.929310e-05 6.918980e-04 2.881160e-05 7.952500e-04 3.150440e-05 8.502090e-04 3.100650e-05 8.064780e-04 3.000180e-05 7.608890e-04 2.975180e-05 8.237940e-04 3.093400e-05 7.862610e-04 3.022490e-05 7.936110e-04 3.077030e-05 7.620850e-04 3.192690e-05 7.787270e-04 2.987040e-05 7.523590e-04 2.989110e-05 7.232450e-04 2.886770e-05 7.036520e-04 3.019410e-05 7.214750e-04 3.501420e-05 7.126900e-04 3.082310e-05 6.976470e-04 2.898830e-05 6.728590e-04 2.812690e-05 6.741780e-04 2.942630e-05 6.763330e-04 3.290670e-05 6.360100e-04 2.906300e-05 6.806230e-04 2.869370e-05 6.806820e-04 2.833820e-05 6.989240e-04 2.892490e-05 7.976530e-04 3.131240e-05 7.567250e-04 3.226120e-05 6.778250e-04 3.071740e-05 6.994180e-04 3.214040e-05 6.748710e-04 2.880460e-05 6.132930e-04 2.986610e-05 5.866450e-04 2.728230e-05 6.131070e-04 2.865200e-05 5.416110e-04 2.645350e-05 5.455280e-04 2.658340e-05 5.776050e-04 2.756840e-05 6.203470e-04 2.968180e-05 5.671440e-04 2.706110e-05 5.816770e-04 2.802630e-05 5.971860e-04 2.834120e-05 6.460490e-04 2.922830e-05 6.181770e-04 2.886470e-05 6.820700e-04 2.996520e-05 7.247100e-04 3.084360e-05 7.759860e-04 3.652260e-05 7.327970e-04 3.183040e-05 7.047140e-04 3.098690e-05 6.528700e-04 3.085540e-05 5.392520e-04 3.063810e-05 6.161510e-04 4.034180e-05 5.611310e-04 3.802640e-05 6.344880e-04 3.763000e-05 5.429920e-04 2.921130e-05 5.444720e-04 2.844010e-05 4.276200e-04 2.516010e-05 4.554120e-04 2.597290e-05 4.493410e-04 2.652790e-05 4.447980e-04 2.652980e-05 5.224540e-04 2.924500e-05 5.318340e-04 2.872930e-05 6.182950e-04 3.086820e-05 5.544040e-04 2.884330e-05 4.816930e-04 2.983900e-05 5.126870e-04 3.680080e-05 5.147660e-04 3.098070e-05 6.374550e-04 3.066170e-05 6.554620e-04 3.136400e-05 6.236090e-04 3.019030e-05 5.034590e-04 2.781270e-05 4.734420e-04 2.767240e-05 5.042050e-04 2.785790e-05 4.581950e-04 2.732260e-05 4.970930e-04 3.444130e-05 4.646210e-04 2.758950e-05 4.833420e-04 2.700570e-05 4.999550e-04 2.775630e-05 4.680400e-04 2.724540e-05 4.401610e-04 2.749640e-05 5.400870e-04 2.968690e-05 5.278720e-04 2.916140e-05 4.798640e-04 2.823670e-05 4.239470e-04 2.670990e-05 4.641170e-04 2.732610e-05 4.850710e-04 2.773380e-05 4.509850e-04 2.650590e-05 4.252190e-04 2.605830e-05 4.668860e-04 3.246880e-05 4.577890e-04 3.003070e-05 4.335870e-04 2.667710e-05 4.171130e-04 2.808100e-05 4.619760e-04 2.926000e-05 4.085890e-04 2.576520e-05 3.883100e-04 2.555600e-05 4.259280e-04 2.714870e-05 4.594990e-04 2.723480e-05 4.989350e-04 3.017650e-05 5.038000e-04 3.461010e-05 5.285230e-04 3.049320e-05 5.226110e-04 2.981470e-05 5.656870e-04 3.313260e-05 5.680540e-04 3.609410e-05 6.690510e-04 3.405850e-05 7.017520e-04 3.572780e-05 8.580000e-04 4.240700e-05 9.937860e-04 4.238600e-05 1.048150e-03 4.339060e-05 9.393550e-04 4.021780e-05 7.923770e-04 3.745260e-05 6.740910e-04 3.510610e-05 6.476020e-04 3.581350e-05 5.807330e-04 3.405770e-05 5.137690e-04 3.195450e-05 4.677410e-04 3.071010e-05 4.623170e-04 3.117640e-05 4.200170e-04 2.926980e-05 3.953640e-04 2.823510e-05 3.898140e-04 2.965010e-05 3.740230e-04 3.056350e-05 3.763400e-04 2.849310e-05 4.416930e-04 3.097710e-05 4.142190e-04 2.882360e-05 4.310560e-04 2.963170e-05 3.800950e-04 2.828830e-05 4.202660e-04 2.935760e-05 3.788250e-04 3.088230e-05 4.304580e-04 4.246700e-05 3.979740e-04 4.076640e-05 3.582540e-04 3.844520e-05 3.870910e-04 4.226250e-05 4.417540e-04 4.599760e-05 3.657650e-04 4.018400e-05 3.324060e-04 3.857810e-05 3.685220e-04 4.021680e-05 2.958760e-04 3.619180e-05 3.168040e-04 3.671770e-05 3.264960e-04 3.814460e-05 3.706650e-04 3.982650e-05 3.484040e-04 4.097060e-05 3.222550e-04 4.147550e-05 3.072360e-04 3.938360e-05 3.244510e-04 3.965230e-05 3.133700e-04 4.005980e-05 3.413290e-04 4.850170e-05 3.563580e-04 4.142410e-05 3.395470e-04 4.031830e-05 3.357620e-04 3.971920e-05 4.155700e-04 4.493060e-05 3.353600e-04 4.012680e-05 3.162100e-04 3.943580e-05 3.269470e-04 3.878660e-05 3.888780e-04 4.258220e-05 3.507230e-04 4.199000e-05 3.211280e-04 3.943140e-05 3.272560e-04 4.097850e-05 2.917890e-04 3.825580e-05 2.723260e-04 3.741270e-05 3.548400e-04 4.157350e-05 2.730940e-04 3.785460e-05 3.780600e-04 4.267290e-05 2.990880e-04 3.955590e-05 3.062080e-04 4.016820e-05 3.336380e-04 3.965360e-05 2.642930e-04 3.878470e-05 2.767200e-04 3.812250e-05 2.944730e-04 3.944330e-05 3.438840e-04 4.257430e-05 2.755910e-04 6.728730e-05 3.045650e-04 3.816990e-05 2.774550e-04 3.762880e-05 3.436050e-04 4.336140e-05 2.118240e-04 3.459580e-05 2.653250e-04 3.753270e-05 2.926040e-04 4.036290e-05 2.403630e-04 3.873070e-05 3.572570e-04 4.374210e-05 2.985860e-04 4.154380e-05 3.177100e-04 4.278440e-05 2.885790e-04 3.900820e-05 2.492620e-04 3.700790e-05 3.035410e-04 4.212120e-05 3.292170e-04 4.389020e-05 3.551910e-04 4.452550e-05 2.611890e-04 3.790380e-05 2.890790e-04 4.058870e-05 3.176660e-04 4.286220e-05 2.719400e-04 3.918250e-05 2.556290e-04 3.803010e-05 3.186170e-04 4.457240e-05 3.304370e-04 6.434260e-05 3.212150e-04 4.455150e-05 2.859880e-04 4.279710e-05 3.069420e-04 4.647420e-05 3.184630e-04 4.894080e-05 3.789710e-04 5.346840e-05 3.858650e-04 5.440110e-05 2.805850e-04 4.412170e-05 2.946850e-04 4.333800e-05 3.029370e-04 4.227280e-05 3.182710e-04 4.382730e-05 3.405070e-04 4.429830e-05 3.244850e-04 4.601550e-05 3.121400e-04 8.020120e-05 3.751010e-04 4.571470e-05 3.188810e-04 4.155220e-05 3.491500e-04 4.283890e-05 2.519610e-04 3.849190e-05 3.522580e-04 4.229360e-05 3.775990e-04 4.528940e-05 3.907480e-04 4.509510e-05 3.546410e-04 4.369960e-05 3.278480e-04 4.043990e-05 3.157380e-04 4.303600e-05 3.345870e-04 4.330240e-05 3.374060e-04 4.137300e-05 2.884940e-04 3.937530e-05 3.116010e-04 4.017600e-05 3.484180e-04 4.031820e-05 3.256430e-04 2.829330e-05 3.258680e-04 2.885560e-05 2.949130e-04 2.771760e-05 3.520740e-04 3.010880e-05 3.327740e-04 2.990660e-05 3.242210e-04 3.011480e-05 2.933610e-04 2.847450e-05 3.446060e-04 3.029480e-05 3.207560e-04 2.846260e-05 3.447900e-04 3.067590e-05 3.465690e-04 3.022480e-05 2.913870e-04 2.846990e-05 3.056820e-04 2.878110e-05 2.595220e-04 3.227520e-05 2.769480e-04 3.713070e-05 3.254210e-04 3.678700e-05 3.152450e-04 2.971290e-05 3.121030e-04 2.913680e-05 3.762530e-04 3.729750e-05 3.914630e-04 3.212950e-05 4.720770e-04 3.463980e-05 4.094890e-04 3.266230e-05 3.831580e-04 3.128650e-05 3.500760e-04 3.038000e-05 3.054450e-04 2.996120e-05 3.434850e-04 3.144790e-05 3.455080e-04 3.011890e-05 3.043760e-04 2.913640e-05 2.635850e-04 2.667480e-05 2.900000e-04 2.729600e-05 3.308770e-04 3.078570e-05 3.168560e-04 2.933630e-05 3.279870e-04 2.990530e-05 3.315650e-04 3.018430e-05 2.438170e-04 2.668190e-05 2.905260e-04 2.980360e-05 2.968570e-04 3.043200e-05 2.998830e-04 2.790780e-05 2.966130e-04 3.444750e-05 3.049130e-04 3.590900e-05 3.308690e-04 3.540300e-05 3.030670e-04 2.981530e-05 3.023410e-04 3.029530e-05 2.487670e-04 3.332600e-05 3.099690e-04 3.468300e-05 2.482250e-04 2.732090e-05 2.760110e-04 2.960090e-05 2.710220e-04 2.951250e-05 2.711740e-04 2.982640e-05 2.601570e-04 2.935910e-05 2.419090e-04 2.893340e-05 2.790100e-04 3.076040e-05 2.360450e-04 2.779860e-05 2.734820e-04 3.073880e-05 2.590530e-04 3.113010e-05 2.129930e-04 2.727480e-05 2.205300e-04 3.201980e-05 2.920970e-04 3.130020e-05 1.791730e-04 2.749190e-05 2.271190e-04 3.173030e-05 2.193500e-04 3.048110e-05 2.414980e-04 3.030750e-05 2.347810e-04 2.909560e-05 2.000460e-04 1.988060e-05 2.377080e-04 2.044350e-05 2.549620e-04 2.156820e-05 2.382640e-04 2.163620e-05 2.202990e-04 2.223030e-05 2.206130e-04 2.228180e-05 2.198130e-04 2.244550e-05 2.245690e-04 2.494680e-05 2.158890e-04 2.345740e-05 2.295680e-04 2.277040e-05 2.039340e-04 2.183360e-05 2.170470e-04 2.211360e-05 2.280540e-04 2.778760e-05 2.290840e-04 3.208900e-05 2.295780e-04 2.533740e-05 2.088020e-04 2.506950e-05 2.270230e-04 2.646760e-05 2.548750e-04 2.625970e-05 2.542780e-04 2.638950e-05 2.192710e-04 2.658520e-05 2.233170e-04 2.834870e-05 2.269950e-04 3.832980e-05 1.741830e-04 2.731850e-05 2.077740e-04 2.775460e-05 2.168850e-04 2.813770e-05 2.494080e-04 2.995190e-05 1.847190e-04 2.777430e-05 2.131180e-04 2.858370e-05 2.326600e-04 2.837060e-05 2.375710e-04 2.972310e-05 1.919940e-04 2.826280e-05 2.118940e-04 2.896070e-05 1.799910e-04 2.697110e-05 2.084800e-04 2.756780e-05 2.048710e-04 2.893760e-05 2.039400e-04 2.849890e-05 1.479110e-04 2.763830e-05 1.746140e-04 3.125820e-05 1.938630e-04 2.993970e-05 2.273160e-04 3.199730e-05 1.893220e-04 2.967660e-05 2.544100e-04 3.275580e-05 2.620560e-04 3.528150e-05 2.452550e-04 3.286810e-05 2.370690e-04 3.245880e-05 2.067780e-04 3.114670e-05 2.138080e-04 3.655750e-05 2.745210e-04 4.825130e-05 2.388920e-04 3.646220e-05 2.930640e-04 3.490560e-05 1.833240e-04 3.570090e-05 1.920090e-04 3.617750e-05 2.599530e-04 3.908610e-05 1.867340e-04 3.514540e-05 1.738400e-04 3.658140e-05 2.480720e-04 3.667500e-05 1.996910e-04 3.531350e-05 2.162660e-04 3.692390e-05 2.663410e-04 3.805280e-05 1.941180e-04 3.663090e-05 1.642050e-04 3.523890e-05 2.689110e-04 3.838150e-05 2.409360e-04 4.965310e-05 1.847900e-04 5.041100e-05 1.780040e-04 3.219900e-05 2.234630e-04 3.296830e-05 1.370700e-04 3.059550e-05 2.075870e-04 3.348930e-05 1.341410e-04 3.284120e-05 1.832390e-04 3.434580e-05 1.508510e-04 3.167450e-05 1.333470e-04 4.119730e-05 1.524640e-04 3.975300e-05 1.713540e-04 3.992650e-05 1.498740e-04 3.718810e-05 1.630350e-04 3.503340e-05 1.212000e-04 3.026400e-05 4.009120e-05 3.562720e-05 1.973950e-04 3.471480e-05 1.461300e-04 2.871250e-05 1.476770e-04 2.882960e-05 1.175550e-04 2.928400e-05 1.072990e-04 2.749720e-05 1.482710e-04 3.188290e-05 1.242280e-04 2.794560e-05 1.039130e-04 2.777680e-05 2.152580e-04 3.162130e-05 1.210340e-04 2.838330e-05 5.914980e-05 2.777230e-05 9.871170e-05 2.978000e-05 1.401770e-04 3.355030e-05 6.826660e-05 2.925510e-05 1.677930e-04 3.064590e-05 6.853110e-05 2.514600e-05 1.431750e-04 3.020160e-05 1.100100e-04 2.916770e-05 1.275130e-04 2.784630e-05 8.670860e-05 2.643850e-05 1.328280e-04 3.301160e-05 1.012330e-04 2.817270e-05 1.299050e-04 2.832730e-05 1.291000e-04 2.945080e-05 6.946560e-05 2.886690e-05 9.621770e-05 2.719220e-05 1.074810e-04 2.955330e-05 1.353190e-04 3.123540e-05 1.534920e-04 2.976960e-05 1.261350e-04 2.864630e-05 9.923770e-05 3.167100e-05 1.104340e-04 3.648610e-05 9.240890e-05 2.888430e-05 -5.476390e-06 2.996170e-05 1.150100e-04 3.324890e-05 1.000750e-04 3.427050e-05 6.576840e-05 3.566030e-05 -2.233520e-04 9.337150e-05 1.557920e-04 4.651850e-05 8.327750e-05 3.995890e-05 1.121130e-04 4.034960e-05 7.820600e-05 3.808170e-05 7.453110e-05 5.616330e-05 1.553410e-04 5.948510e-05 8.860320e-05 4.228480e-05 8.186370e-05 6.762710e-05 4.912510e-05 6.950520e-05 ''') ImportString(u'flux_090(numeric),+-',''' 2.189310e+03 3.957380e+03 -3.848260e+01 2.944600e+01 -2.235260e-01 2.569630e+00 1.148560e-01 1.804310e-01 1.187360e-02 3.671730e-02 -5.200170e-03 7.960460e-03 -1.414540e-03 2.831620e-03 2.513630e-04 1.136470e-03 4.521350e-04 9.816890e-04 3.368390e-04 4.359300e-04 -4.878070e-04 3.649430e-04 1.040890e-04 3.122470e-04 1.988550e-04 2.699880e-04 1.019220e-04 2.450900e-04 -1.046140e-04 1.368130e-04 1.014340e-04 7.702500e-05 1.627390e-05 4.172710e-05 3.418940e-05 3.641600e-05 -7.669300e-06 2.265170e-05 2.431390e-05 2.461040e-05 1.886670e-05 3.256710e-05 9.342400e-05 4.966980e-05 8.700760e-05 9.238170e-05 3.042900e-04 1.879750e-04 5.940840e-04 2.743870e-04 1.003320e-03 3.625380e-04 1.336580e-03 4.839930e-04 3.792900e-04 4.150900e-04 2.378790e-04 3.557360e-04 1.609710e-03 4.530660e-04 1.343500e-03 3.489400e-04 1.238390e-03 2.893310e-04 8.969520e-04 2.250110e-04 4.661140e-04 1.607770e-04 8.105390e-04 1.487010e-04 7.264460e-04 1.226090e-04 7.527700e-04 1.148020e-04 4.630340e-04 7.487960e-05 5.272710e-04 7.094880e-05 4.811790e-04 6.802030e-05 4.768310e-04 6.411700e-05 4.895900e-04 6.535510e-05 6.122410e-04 6.522840e-05 7.670080e-04 7.055660e-05 7.904070e-04 6.722970e-05 7.670350e-04 6.367560e-05 6.694930e-04 5.760900e-05 4.834540e-04 5.092890e-05 4.135160e-04 4.699490e-05 4.042320e-04 4.437900e-05 4.783140e-04 4.665430e-05 5.667440e-04 4.634570e-05 6.032080e-04 4.720030e-05 5.421420e-04 4.467770e-05 6.051960e-04 4.692450e-05 5.844670e-04 4.596860e-05 5.009010e-04 4.121890e-05 4.163550e-04 3.437820e-05 4.029660e-04 3.248040e-05 3.760420e-04 3.112840e-05 2.921990e-04 2.707350e-05 4.426360e-04 3.330880e-05 3.394490e-04 2.853650e-05 3.824900e-04 3.266230e-05 4.405500e-04 3.842730e-05 3.974210e-04 3.204660e-05 3.742320e-04 3.002110e-05 3.485960e-04 2.948280e-05 3.402540e-04 2.646820e-05 3.316290e-04 2.685680e-05 2.728030e-04 2.464370e-05 3.453200e-04 2.671720e-05 3.008220e-04 2.507950e-05 3.167260e-04 2.842040e-05 3.018550e-04 2.785470e-05 3.418200e-04 2.629130e-05 4.013920e-04 2.817840e-05 3.879520e-04 2.680130e-05 3.766470e-04 2.726420e-05 3.904920e-04 2.731480e-05 3.864770e-04 2.630150e-05 4.165190e-04 2.741630e-05 4.073560e-04 2.729510e-05 3.717290e-04 2.527240e-05 3.854870e-04 2.663850e-05 3.890700e-04 2.767200e-05 4.633250e-04 2.888230e-05 4.832750e-04 2.909130e-05 5.989080e-04 3.145740e-05 5.622960e-04 3.018420e-05 6.573950e-04 3.396660e-05 6.209600e-04 3.208950e-05 5.730690e-04 3.570650e-05 4.638390e-04 2.819540e-05 4.755720e-04 2.808470e-05 4.856120e-04 2.868640e-05 4.598610e-04 2.728380e-05 4.053160e-04 2.613350e-05 4.278810e-04 2.729730e-05 4.120400e-04 2.552130e-05 4.318480e-04 2.593390e-05 3.666020e-04 2.306130e-05 3.703610e-04 2.338260e-05 4.494530e-04 2.667210e-05 4.441930e-04 2.690620e-05 3.794860e-04 2.332380e-05 4.032390e-04 2.439100e-05 4.400150e-04 2.454610e-05 4.482450e-04 2.531100e-05 4.082950e-04 2.393610e-05 4.453790e-04 2.591680e-05 3.885490e-04 2.303500e-05 4.058890e-04 2.492030e-05 4.364210e-04 2.476390e-05 4.208970e-04 2.308230e-05 3.966090e-04 2.264030e-05 4.038420e-04 2.327710e-05 3.766320e-04 2.243100e-05 4.029770e-04 2.313100e-05 4.271150e-04 2.387070e-05 3.986970e-04 2.241500e-05 3.759110e-04 2.349600e-05 3.541990e-04 2.689230e-05 4.950470e-04 3.213940e-05 4.682630e-04 2.717000e-05 4.237930e-04 2.555710e-05 4.268360e-04 2.544000e-05 4.308350e-04 2.560270e-05 4.162720e-04 2.549180e-05 4.407000e-04 2.541610e-05 5.012660e-04 2.869820e-05 4.968080e-04 2.977780e-05 4.860740e-04 2.664370e-05 4.918510e-04 2.691120e-05 5.597910e-04 3.824190e-05 6.136890e-04 3.902550e-05 6.752500e-04 4.012790e-05 7.669390e-04 4.342700e-05 8.135920e-04 4.481290e-05 7.947810e-04 4.405070e-05 7.082690e-04 4.112800e-05 6.424060e-04 3.989990e-05 6.482680e-04 4.232460e-05 6.848350e-04 4.067380e-05 7.432990e-04 4.187700e-05 7.561130e-04 4.200300e-05 8.847240e-04 4.594600e-05 1.051090e-03 4.990120e-05 1.072130e-03 4.944570e-05 9.847120e-04 4.749810e-05 9.632770e-04 4.773560e-05 1.166510e-03 5.315660e-05 1.102740e-03 5.123650e-05 1.005590e-03 5.068740e-05 8.306470e-04 4.337450e-05 9.148520e-04 6.053090e-05 9.024780e-04 4.797280e-05 8.131140e-04 4.335410e-05 7.865150e-04 4.287560e-05 7.344510e-04 4.189260e-05 7.761440e-04 4.291760e-05 8.759570e-04 4.627650e-05 1.124620e-03 5.085710e-05 1.163600e-03 5.160870e-05 1.125660e-03 5.017850e-05 9.262800e-04 4.618390e-05 8.268290e-04 4.205060e-05 8.068440e-04 3.770970e-05 7.715470e-04 3.639450e-05 8.273580e-04 3.791650e-05 9.249840e-04 4.000860e-05 1.231950e-03 4.618910e-05 1.029860e-03 4.198100e-05 9.347270e-04 4.229940e-05 9.118660e-04 4.401100e-05 9.238550e-04 3.939530e-05 7.560730e-04 3.691880e-05 6.870150e-04 3.534350e-05 6.963400e-04 3.504840e-05 6.522050e-04 3.382380e-05 5.132750e-04 2.945600e-05 5.119080e-04 2.960120e-05 4.563810e-04 2.838800e-05 4.477500e-04 2.806660e-05 4.245150e-04 2.717070e-05 5.459650e-04 3.160100e-05 6.898200e-04 3.691700e-05 6.735890e-04 3.457740e-05 5.433300e-04 5.174020e-05 4.444810e-04 3.680070e-05 4.379060e-04 2.837160e-05 4.768070e-04 3.008840e-05 4.259950e-04 2.795140e-05 4.641260e-04 2.881110e-05 4.342080e-04 2.875370e-05 4.511970e-04 2.947330e-05 3.970060e-04 2.677690e-05 4.756860e-04 2.949110e-05 5.503190e-04 3.244230e-05 6.075410e-04 3.344380e-05 4.510560e-04 2.874830e-05 3.846050e-04 2.365440e-05 3.849010e-04 2.148730e-05 4.165880e-04 2.250840e-05 3.656070e-04 2.059810e-05 4.180400e-04 2.185650e-05 3.786980e-04 2.147910e-05 3.356630e-04 2.044950e-05 3.666890e-04 2.255640e-05 4.225550e-04 2.254930e-05 4.186750e-04 2.240740e-05 3.535560e-04 2.054840e-05 4.114510e-04 2.272900e-05 4.533170e-04 2.359970e-05 4.470720e-04 2.364200e-05 3.550670e-04 2.217940e-05 3.780510e-04 2.103430e-05 3.766290e-04 2.168190e-05 3.476090e-04 2.109610e-05 3.331580e-04 2.128430e-05 3.804490e-04 2.584950e-05 3.198560e-04 2.130740e-05 3.043750e-04 1.969080e-05 2.907190e-04 1.867410e-05 3.212390e-04 2.083660e-05 2.907430e-04 2.208830e-05 2.729850e-04 1.901210e-05 3.008320e-04 1.948910e-05 3.215500e-04 2.038920e-05 3.509110e-04 2.108900e-05 4.497720e-04 2.466890e-05 3.881780e-04 2.325600e-05 3.362740e-04 2.228140e-05 3.404450e-04 2.348080e-05 3.221980e-04 1.993270e-05 2.899130e-04 2.117170e-05 2.172050e-04 1.685090e-05 2.633540e-04 1.970370e-05 2.228880e-04 1.721260e-05 2.611010e-04 1.922450e-05 2.433760e-04 1.838170e-05 2.553230e-04 1.973050e-05 2.269310e-04 1.741710e-05 2.463680e-04 1.871310e-05 2.689320e-04 1.961470e-05 2.710120e-04 1.943940e-05 2.743060e-04 1.972980e-05 3.165980e-04 2.142750e-05 2.964300e-04 2.031540e-05 3.814600e-04 2.643690e-05 3.934200e-04 2.383800e-05 3.762790e-04 2.309830e-05 3.223700e-04 2.241240e-05 2.508420e-04 2.127430e-05 2.272260e-04 2.458500e-05 2.433310e-04 2.555020e-05 2.466050e-04 2.426480e-05 2.157370e-04 1.834870e-05 2.337090e-04 1.896410e-05 1.730880e-04 1.651370e-05 1.834370e-04 1.712470e-05 1.767030e-04 1.707730e-05 1.850980e-04 1.773440e-05 2.317430e-04 1.983130e-05 2.363810e-04 1.936410e-05 3.163970e-04 2.251870e-05 2.686570e-04 2.016570e-05 1.817340e-04 1.797690e-05 1.769360e-04 2.172880e-05 2.315540e-04 2.106780e-05 3.607100e-04 2.410770e-05 4.228070e-04 2.547590e-05 3.230370e-04 2.151710e-05 2.113980e-04 1.834630e-05 1.593280e-04 1.620070e-05 1.826030e-04 1.696200e-05 1.621360e-04 1.680160e-05 2.026230e-04 2.225970e-05 1.882860e-04 1.820230e-05 1.853480e-04 1.683110e-05 1.679850e-04 1.660570e-05 1.901490e-04 1.807590e-05 1.984030e-04 1.883860e-05 2.170980e-04 1.896430e-05 2.077690e-04 1.843080e-05 1.890880e-04 1.847100e-05 1.593040e-04 1.696240e-05 1.694350e-04 1.633860e-05 1.741820e-04 1.702330e-05 1.712220e-04 1.702000e-05 1.569070e-04 1.648740e-05 1.710360e-04 1.932610e-05 1.480450e-04 1.741920e-05 1.770950e-04 1.771470e-05 1.654610e-04 1.788410e-05 1.622280e-04 1.816770e-05 1.518910e-04 1.634970e-05 1.301070e-04 1.531500e-05 1.722310e-04 1.723240e-05 1.585080e-04 1.634870e-05 2.195240e-04 2.012820e-05 1.717000e-04 2.101260e-05 2.155510e-04 2.014470e-05 1.950330e-04 1.838060e-05 2.110570e-04 1.978050e-05 1.967610e-04 2.181210e-05 2.929480e-04 2.323980e-05 2.730930e-04 2.315850e-05 3.643080e-04 2.769350e-05 5.202760e-04 3.200170e-05 5.679730e-04 3.347840e-05 4.861390e-04 2.957460e-05 3.499850e-04 2.538360e-05 2.786060e-04 2.355510e-05 2.604830e-04 2.296670e-05 2.333050e-04 2.220100e-05 2.039960e-04 2.062450e-05 1.939900e-04 2.078750e-05 1.905580e-04 2.020830e-05 1.616390e-04 1.825540e-05 1.370730e-04 1.679840e-05 1.508030e-04 1.873990e-05 1.408870e-04 1.855940e-05 1.320390e-04 1.718750e-05 1.693530e-04 1.944680e-05 1.538960e-04 1.776660e-05 1.902140e-04 2.082780e-05 1.537900e-04 1.824720e-05 1.369430e-04 1.662840e-05 1.639220e-04 1.982100e-05 1.368490e-04 2.442950e-05 1.462460e-04 2.591340e-05 1.347690e-04 2.453050e-05 1.356830e-04 2.491550e-05 1.754900e-04 2.952930e-05 8.863090e-05 2.008080e-05 1.278080e-04 2.381000e-05 1.359020e-04 2.598260e-05 1.148680e-04 2.250130e-05 1.161110e-04 2.188610e-05 8.540910e-05 1.888680e-05 1.108980e-04 2.134620e-05 1.616390e-04 2.835570e-05 1.320740e-04 2.631360e-05 1.156300e-04 2.426370e-05 1.150670e-04 2.228250e-05 1.178350e-04 2.395820e-05 8.479670e-05 2.409060e-05 1.525950e-04 2.806050e-05 1.202580e-04 2.390140e-05 1.388730e-04 2.666730e-05 1.451350e-04 2.664400e-05 1.180570e-04 2.390850e-05 1.401550e-04 2.688890e-05 1.543920e-04 2.761250e-05 1.672210e-04 2.718470e-05 1.580430e-04 2.773210e-05 1.177980e-04 2.366220e-05 1.113850e-04 2.432650e-05 1.201190e-04 2.552750e-05 1.140480e-04 2.401440e-05 1.556630e-04 2.691640e-05 7.708020e-05 1.971010e-05 1.410430e-04 2.650430e-05 1.013070e-04 2.143620e-05 1.206690e-04 2.545310e-05 1.209640e-04 2.480310e-05 1.093820e-04 2.455250e-05 8.053830e-05 2.107410e-05 8.219740e-05 2.049970e-05 1.577030e-04 2.881110e-05 1.100950e-04 3.925630e-05 1.254610e-04 2.337630e-05 9.541680e-05 2.222230e-05 1.030650e-04 2.384190e-05 8.753700e-05 2.322180e-05 1.151360e-04 2.409380e-05 1.171970e-04 2.588510e-05 7.645200e-05 2.172200e-05 1.191450e-04 2.548730e-05 9.405710e-05 2.381870e-05 1.179810e-04 2.619000e-05 9.514210e-05 2.277540e-05 6.651240e-05 2.067810e-05 1.309440e-04 2.745420e-05 1.288110e-04 2.672590e-05 1.084650e-04 2.509860e-05 8.753660e-05 2.231390e-05 9.551640e-05 2.399950e-05 1.097720e-04 2.594050e-05 7.027060e-05 2.077640e-05 1.226920e-04 2.560470e-05 1.090420e-04 2.560110e-05 1.300320e-04 4.195140e-05 1.175640e-04 2.584890e-05 1.450130e-04 3.035600e-05 1.194760e-04 2.998020e-05 1.020770e-04 2.873730e-05 1.229290e-04 2.996850e-05 1.041010e-04 2.852600e-05 1.090500e-04 2.931620e-05 8.305970e-05 2.324780e-05 8.550330e-05 2.280670e-05 1.053820e-04 2.492270e-05 9.525930e-05 2.348280e-05 1.217570e-04 2.733860e-05 1.378520e-04 4.244890e-05 1.053260e-04 2.329600e-05 9.978090e-05 2.363680e-05 1.112200e-04 2.503330e-05 7.541960e-05 2.100170e-05 1.104820e-04 2.495930e-05 1.176530e-04 2.519410e-05 1.482200e-04 2.842620e-05 1.353610e-04 2.814750e-05 1.046080e-04 2.223810e-05 1.043620e-04 2.484270e-05 1.716590e-04 3.029100e-05 1.292850e-04 2.537210e-05 1.152540e-04 2.478310e-05 1.363750e-04 2.692090e-05 1.323550e-04 2.566820e-05 1.142670e-04 1.680600e-05 1.178140e-04 1.716560e-05 1.202570e-04 1.795190e-05 1.261820e-04 1.803730e-05 1.442150e-04 1.993030e-05 1.320060e-04 1.872940e-05 1.089160e-04 1.729060e-05 1.230990e-04 1.804120e-05 1.242430e-04 1.836550e-05 1.507060e-04 2.067550e-05 1.346240e-04 1.853340e-05 1.276210e-04 1.843930e-05 8.129750e-05 1.493320e-05 1.234800e-04 2.254670e-05 1.298080e-04 2.496220e-05 1.177570e-04 2.190980e-05 1.208600e-04 1.827070e-05 1.198560e-04 1.842010e-05 1.378740e-04 2.286000e-05 1.862510e-04 2.280360e-05 2.288730e-04 2.461690e-05 1.847750e-04 2.248560e-05 1.264400e-04 1.829690e-05 1.361440e-04 1.935220e-05 1.421310e-04 2.107330e-05 1.487440e-04 2.140700e-05 1.345510e-04 1.912050e-05 1.106680e-04 1.720580e-05 7.928700e-05 1.499060e-05 9.544570e-05 1.605820e-05 1.208860e-04 1.916950e-05 9.626840e-05 1.676630e-05 1.300420e-04 1.939780e-05 1.628070e-04 2.185710e-05 9.024460e-05 1.610480e-05 1.071200e-04 1.813280e-05 1.108670e-04 1.898680e-05 1.206780e-04 1.844400e-05 1.546930e-04 2.460740e-05 1.427230e-04 2.503730e-05 1.321720e-04 2.251250e-05 1.047630e-04 1.782200e-05 1.345880e-04 2.007590e-05 8.450010e-05 1.873870e-05 1.125290e-04 2.086700e-05 1.086640e-04 1.788820e-05 1.009180e-04 1.738930e-05 1.077490e-04 1.869770e-05 1.314380e-04 2.102130e-05 1.028240e-04 1.841490e-05 1.005090e-04 1.797710e-05 9.947960e-05 1.806900e-05 6.297460e-05 1.557010e-05 1.019190e-04 1.838910e-05 6.407510e-05 1.559560e-05 8.464310e-05 1.655980e-05 6.804990e-05 1.764170e-05 1.141680e-04 2.024150e-05 6.302310e-05 1.570300e-05 9.441250e-05 2.035660e-05 9.153530e-05 1.928880e-05 9.232240e-05 1.831240e-05 1.118250e-04 2.019520e-05 8.523570e-05 1.278950e-05 8.677740e-05 1.229410e-05 8.181380e-05 1.221410e-05 8.308390e-05 1.274030e-05 8.219700e-05 1.367640e-05 8.920160e-05 1.375670e-05 7.258440e-05 1.276070e-05 9.015920e-05 1.528490e-05 1.160040e-04 1.677330e-05 9.919070e-05 1.470680e-05 9.141510e-05 1.373840e-05 8.809160e-05 1.316330e-05 8.480170e-05 1.737330e-05 9.242640e-05 2.024270e-05 9.734510e-05 1.487870e-05 7.828420e-05 1.450600e-05 8.811210e-05 1.535970e-05 8.860830e-05 1.534490e-05 1.206280e-04 1.732040e-05 6.816770e-05 1.467670e-05 1.118430e-04 1.857100e-05 7.659590e-05 2.120910e-05 8.801850e-05 1.723730e-05 9.765050e-05 1.728740e-05 9.480540e-05 1.697640e-05 5.330690e-05 1.520880e-05 5.542910e-05 1.482640e-05 9.426510e-05 1.716280e-05 7.990070e-05 1.609890e-05 9.082360e-05 1.742080e-05 6.125430e-05 1.493280e-05 8.153730e-05 1.723310e-05 8.734000e-05 1.680450e-05 6.825010e-05 1.563060e-05 6.526390e-05 1.633400e-05 4.182360e-05 1.379650e-05 4.842100e-05 1.478030e-05 8.598900e-05 1.910990e-05 6.012170e-05 1.662580e-05 7.657060e-05 1.778200e-05 5.923120e-05 1.617660e-05 1.363290e-04 2.160890e-05 9.253890e-05 2.026420e-05 9.464420e-05 1.940600e-05 8.594680e-05 1.891840e-05 7.388890e-05 1.809060e-05 1.260230e-04 2.500290e-05 9.598130e-05 2.823840e-05 1.145890e-04 2.260720e-05 1.214390e-04 2.085150e-05 7.017010e-05 2.061090e-05 9.425200e-05 2.210500e-05 9.207530e-05 2.226070e-05 9.367040e-05 2.099240e-05 1.041640e-04 2.307210e-05 8.767790e-05 2.108680e-05 6.132360e-05 1.953140e-05 3.383470e-05 1.734240e-05 1.274730e-04 2.345420e-05 4.972170e-05 1.891460e-05 8.214450e-05 2.149860e-05 1.013380e-04 2.212960e-05 6.709950e-05 2.603140e-05 9.392430e-05 3.063280e-05 8.199390e-05 1.927460e-05 9.488300e-05 1.994580e-05 5.338870e-05 1.652970e-05 8.752200e-05 1.947110e-05 6.659600e-05 1.961090e-05 9.018510e-05 2.120990e-05 7.002800e-05 1.857560e-05 5.944480e-05 2.328890e-05 8.569420e-05 2.432770e-05 5.097920e-05 2.151620e-05 5.314880e-05 2.009670e-05 7.485610e-05 2.078880e-05 4.125950e-05 1.585860e-05 4.223670e-05 1.982160e-05 1.005500e-04 2.249630e-05 6.217580e-05 1.673370e-05 6.807440e-05 1.769030e-05 2.896580e-05 1.485400e-05 5.081910e-05 1.643620e-05 6.275850e-05 2.032010e-05 2.932820e-05 1.374720e-05 2.485330e-05 1.426590e-05 7.546890e-05 1.835550e-05 6.925820e-05 1.804420e-05 2.421510e-05 1.524950e-05 4.176270e-05 1.684660e-05 3.131620e-05 1.762550e-05 2.991900e-05 1.661930e-05 2.584090e-05 1.387280e-05 4.416010e-05 1.544960e-05 5.502680e-05 1.743100e-05 6.226740e-05 1.879430e-05 4.787250e-05 1.606620e-05 2.452870e-05 1.336580e-05 2.902590e-05 1.606590e-05 5.583530e-05 1.716640e-05 9.065510e-05 1.993000e-05 6.089810e-05 1.789940e-05 3.587730e-05 1.730450e-05 4.004220e-05 1.596700e-05 1.560880e-05 1.376360e-05 1.024310e-04 2.249720e-05 4.332750e-05 1.574300e-05 3.633940e-05 1.579770e-05 2.198550e-05 1.575820e-05 4.997950e-05 2.082940e-05 4.277200e-05 1.741470e-05 -3.043260e-06 1.529930e-05 5.554110e-05 2.039130e-05 3.265840e-05 1.832360e-05 5.363930e-06 1.629740e-05 -5.950040e-05 3.664910e-05 5.674340e-05 2.668630e-05 -5.359900e-06 1.441850e-05 4.501340e-05 2.293660e-05 5.012770e-06 1.789040e-05 2.662400e-05 2.937400e-05 1.719700e-05 2.522640e-05 2.278130e-05 2.083440e-05 2.830410e-05 3.645150e-05 7.612740e-05 4.944380e-05 ''') ImportString(u'flux_model_0_5(numeric)',''' 2.638349e-03 3.500185e-03 4.195564e-03 5.033681e-03 6.662102e-03 9.691017e-03 1.383798e-02 1.730952e-02 1.782465e-02 1.491672e-02 1.052511e-02 6.985221e-03 5.120528e-03 4.457958e-03 4.315053e-03 4.329839e-03 4.390443e-03 4.461482e-03 4.517663e-03 4.541635e-03 4.527193e-03 4.484314e-03 4.441404e-03 4.425652e-03 4.452141e-03 4.523108e-03 4.630249e-03 4.762861e-03 4.916740e-03 5.085519e-03 5.226392e-03 5.294072e-03 5.323343e-03 5.443004e-03 5.770480e-03 6.351340e-03 7.204733e-03 8.298443e-03 9.389546e-03 1.001495e-02 9.825170e-03 8.917985e-03 7.738226e-03 6.692107e-03 5.924180e-03 5.398249e-03 5.062076e-03 4.911711e-03 4.935686e-03 5.067608e-03 5.194373e-03 5.222608e-03 5.144365e-03 5.037660e-03 4.988319e-03 5.019739e-03 5.093912e-03 5.152969e-03 5.163832e-03 5.131372e-03 5.085257e-03 5.060005e-03 5.083899e-03 5.199510e-03 5.465772e-03 5.893727e-03 6.344101e-03 6.561472e-03 6.398052e-03 5.966652e-03 5.515189e-03 5.208256e-03 5.069245e-03 5.086064e-03 5.346950e-03 6.141645e-03 7.876758e-03 1.062103e-02 1.359337e-02 1.543759e-02 1.537860e-02 1.385804e-02 1.182906e-02 9.895545e-03 8.278267e-03 7.071151e-03 6.280646e-03 5.812481e-03 5.550927e-03 5.417207e-03 5.359876e-03 5.344851e-03 5.357774e-03 5.402117e-03 5.497721e-03 5.658911e-03 5.885693e-03 6.181025e-03 6.561367e-03 7.014364e-03 7.409760e-03 7.541663e-03 7.306973e-03 6.819452e-03 6.311415e-03 5.948962e-03 5.776437e-03 5.773050e-03 5.914581e-03 6.169031e-03 6.463208e-03 6.718062e-03 6.973356e-03 7.450235e-03 8.391633e-03 9.788708e-03 1.132921e-02 1.265226e-02 1.357322e-02 1.391399e-02 1.336779e-02 1.182625e-02 9.745662e-03 7.877703e-03 6.689730e-03 6.161845e-03 6.036965e-03 6.076394e-03 6.142457e-03 6.193032e-03 6.255145e-03 6.414870e-03 6.805693e-03 7.540205e-03 8.561670e-03 9.511556e-03 9.884710e-03 9.445193e-03 8.462913e-03 7.455984e-03 6.764556e-03 6.416781e-03 6.279466e-03 6.238789e-03 6.236223e-03 6.247693e-03 6.264526e-03 6.287873e-03 6.319859e-03 6.355495e-03 6.377765e-03 6.374531e-03 6.355951e-03 6.354557e-03 6.411426e-03 6.557892e-03 6.792885e-03 7.072779e-03 7.351488e-03 7.635578e-03 7.955848e-03 8.261680e-03 8.407689e-03 8.284276e-03 7.936441e-03 7.512128e-03 7.135670e-03 6.858952e-03 6.687729e-03 6.604043e-03 6.582204e-03 6.605375e-03 6.680189e-03 6.821240e-03 7.023224e-03 7.230743e-03 7.361530e-03 7.372550e-03 7.298509e-03 7.233538e-03 7.254974e-03 7.367637e-03 7.511028e-03 7.623095e-03 7.735142e-03 7.997609e-03 8.572497e-03 9.436619e-03 1.027271e-02 1.063284e-02 1.029918e-02 9.494261e-03 8.706038e-03 8.313137e-03 8.354012e-03 8.587754e-03 8.702097e-03 8.541980e-03 8.170747e-03 7.770373e-03 7.473888e-03 7.304975e-03 7.224075e-03 7.198561e-03 7.216503e-03 7.286777e-03 7.436890e-03 7.723366e-03 8.192892e-03 8.808263e-03 9.398174e-03 9.765725e-03 9.885055e-03 9.999391e-03 1.046360e-02 1.148160e-02 1.296156e-02 1.452561e-02 1.563003e-02 1.583690e-02 1.522935e-02 1.459465e-02 1.518075e-02 1.827562e-02 2.481496e-02 3.467502e-02 4.567079e-02 5.356495e-02 5.430129e-02 4.711052e-02 3.528816e-02 2.363950e-02 1.531288e-02 1.076014e-02 8.852233e-03 8.316687e-03 8.363831e-03 8.624770e-03 8.903523e-03 9.074247e-03 9.084827e-03 8.982937e-03 8.856234e-03 8.784448e-03 8.848024e-03 9.158531e-03 9.811356e-03 1.074434e-02 1.165930e-02 1.218393e-02 1.213135e-02 1.163627e-02 1.097359e-02 1.033227e-02 9.752006e-03 9.225862e-03 8.785322e-03 8.467982e-03 8.279487e-03 8.187440e-03 8.150270e-03 8.144439e-03 8.148754e-03 8.162733e-03 8.178647e-03 8.197497e-03 8.220041e-03 8.244955e-03 8.276387e-03 8.315169e-03 8.359424e-03 8.407638e-03 8.463450e-03 8.570347e-03 8.833080e-03 9.418809e-03 1.049098e-02 1.207705e-02 1.394416e-02 1.563976e-02 1.666920e-02 1.671853e-02 1.578877e-02 1.418209e-02 1.238207e-02 1.083961e-02 9.811778e-03 9.298561e-03 9.155245e-03 9.191974e-03 9.271901e-03 9.316526e-03 9.300929e-03 9.240680e-03 9.159161e-03 9.083294e-03 9.030324e-03 9.001008e-03 8.991310e-03 8.975531e-03 8.913346e-03 8.773651e-03 8.564204e-03 8.356713e-03 8.252824e-03 8.288762e-03 8.367720e-03 8.304888e-03 7.970950e-03 7.403291e-03 6.773978e-03 6.254741e-03 5.925057e-03 5.762625e-03 5.710123e-03 5.712811e-03 5.743801e-03 5.791515e-03 5.854992e-03 5.933499e-03 6.019581e-03 6.086817e-03 6.106109e-03 6.067309e-03 5.986337e-03 5.908203e-03 5.875052e-03 5.918834e-03 6.041366e-03 6.220534e-03 6.400033e-03 6.517583e-03 6.524953e-03 6.426368e-03 6.263959e-03 6.100332e-03 5.974766e-03 5.903430e-03 5.879032e-03 5.898911e-03 5.977836e-03 6.173112e-03 6.594283e-03 7.436107e-03 9.016654e-03 1.175857e-02 1.601445e-02 2.165502e-02 2.765495e-02 3.215739e-02 3.336180e-02 3.063611e-02 2.506963e-02 1.871558e-02 1.336728e-02 9.805501e-03 7.877279e-03 7.056336e-03 6.912922e-03 7.267903e-03 8.107245e-03 9.387062e-03 1.087789e-02 1.214594e-02 1.276774e-02 1.261191e-02 1.203416e-02 1.185113e-02 1.323157e-02 1.750119e-02 2.566630e-02 3.757828e-02 5.098214e-02 6.177396e-02 6.574720e-02 6.122219e-02 5.017444e-02 3.703641e-02 2.601842e-02 1.914604e-02 1.609990e-02 1.523513e-02 1.484655e-02 1.393847e-02 1.236248e-02 1.052907e-02 8.939297e-03 7.912371e-03 7.532069e-03 7.778645e-03 8.592328e-03 9.855602e-03 1.128326e-02 1.244546e-02 1.291175e-02 1.250033e-02 1.139416e-02 1.001516e-02 8.784835e-03 7.926752e-03 7.461348e-03 7.303685e-03 7.383199e-03 7.694687e-03 8.293150e-03 9.232636e-03 1.045986e-02 1.175012e-02 1.273095e-02 1.305434e-02 1.259108e-02 1.152228e-02 1.023135e-02 9.093793e-03 8.313852e-03 7.898860e-03 7.755840e-03 7.782374e-03 7.908337e-03 8.100160e-03 8.323000e-03 8.533517e-03 8.682410e-03 8.735127e-03 8.681234e-03 8.541031e-03 8.350113e-03 8.153214e-03 7.989999e-03 7.891449e-03 7.867648e-03 7.904958e-03 7.965628e-03 8.003064e-03 7.989327e-03 7.921439e-03 7.825663e-03 7.727321e-03 7.647310e-03 7.596543e-03 7.599218e-03 7.677603e-03 7.841384e-03 8.053457e-03 8.234243e-03 8.297769e-03 8.201583e-03 7.976229e-03 7.706096e-03 7.488867e-03 7.380434e-03 7.390142e-03 7.474693e-03 7.573418e-03 7.625229e-03 7.605841e-03 7.525440e-03 7.430649e-03 7.370103e-03 7.391247e-03 7.527406e-03 7.820009e-03 8.307527e-03 9.008409e-03 9.879768e-03 1.077957e-02 1.149549e-02 1.181835e-02 1.164388e-02 1.103751e-02 1.021328e-02 9.460298e-03 9.022510e-03 9.035720e-03 9.496499e-03 1.027977e-02 1.117551e-02 1.194092e-02 1.235750e-02 1.229592e-02 1.176092e-02 1.087313e-02 9.851906e-03 8.918431e-03 8.243966e-03 7.913210e-03 7.928296e-03 8.224633e-03 8.692377e-03 9.191621e-03 9.585596e-03 9.773852e-03 9.727390e-03 9.477054e-03 9.102014e-03 8.685155e-03 8.295125e-03 7.973884e-03 7.732108e-03 7.558865e-03 7.479201e-03 7.520754e-03 7.967748e-03 9.520626e-03 1.389450e-02 2.472043e-02 4.845768e-02 9.448146e-02 1.725475e-01 2.871600e-01 4.296480e-01 5.747074e-01 6.854643e-01 7.273508e-01 6.863860e-01 5.757480e-01 4.296219e-01 2.854793e-01 1.697547e-01 9.140206e-02 4.587189e-02 2.308636e-02 1.322964e-02 9.680926e-03 8.810654e-03 9.031567e-03 9.783368e-03 1.091781e-02 1.247661e-02 1.442600e-02 1.661938e-02 1.869979e-02 2.016831e-02 2.055129e-02 1.966201e-02 1.770881e-02 1.519817e-02 1.271533e-02 1.066971e-02 9.212579e-03 8.280414e-03 7.719573e-03 7.384676e-03 7.183567e-03 7.067337e-03 7.022019e-03 7.044664e-03 7.139591e-03 7.305093e-03 7.533553e-03 7.801603e-03 8.074812e-03 8.317829e-03 8.510754e-03 8.647176e-03 8.741611e-03 8.805587e-03 8.839296e-03 8.826273e-03 8.743930e-03 8.581818e-03 8.351759e-03 8.086007e-03 7.825758e-03 7.598645e-03 7.423246e-03 7.300393e-03 7.226048e-03 7.196899e-03 7.207924e-03 7.263809e-03 7.368215e-03 7.531663e-03 7.755765e-03 8.034494e-03 8.358277e-03 8.716793e-03 9.124364e-03 9.621641e-03 1.024680e-02 1.101464e-02 1.187623e-02 1.276852e-02 1.371017e-02 1.496196e-02 1.710768e-02 2.099020e-02 2.740384e-02 3.657487e-02 4.767098e-02 5.868538e-02 6.693014e-02 7.003922e-02 6.697556e-02 5.860499e-02 4.718079e-02 3.540182e-02 2.541912e-02 1.825895e-02 1.391639e-02 1.180377e-02 1.114952e-02 1.127039e-02 1.166577e-02 1.199422e-02 1.206024e-02 1.179497e-02 1.123414e-02 1.050128e-02 9.755202e-03 9.122123e-03 8.732170e-03 8.741889e-03 9.473883e-03 1.174996e-02 1.729896e-02 2.929594e-02 5.267659e-02 9.386796e-02 1.593664e-01 2.530190e-01 3.726299e-01 5.081310e-01 6.415411e-01 7.504432e-01 8.138642e-01 8.189348e-01 7.643213e-01 6.609489e-01 5.290110e-01 3.913017e-01 2.674466e-01 1.693763e-01 1.007277e-01 5.910326e-02 3.954862e-02 3.796503e-02 5.267582e-02 8.439147e-02 1.336887e-01 1.989496e-01 2.727730e-01 3.429033e-01 3.939203e-01 4.131436e-01 3.953264e-01 3.451843e-01 2.751369e-01 2.007517e-01 1.345754e-01 8.366989e-02 4.926526e-02 2.854155e-02 1.737755e-02 1.195803e-02 9.558667e-03 8.578883e-03 8.200795e-03 8.062436e-03 8.027728e-03 8.039646e-03 8.115903e-03 8.284763e-03 8.624448e-03 9.266158e-03 1.038986e-02 1.220045e-02 1.486349e-02 1.842647e-02 2.274805e-02 2.744008e-02 3.194724e-02 3.565375e-02 3.810749e-02 3.914260e-02 3.900992e-02 3.830663e-02 3.783181e-02 3.839888e-02 4.064266e-02 4.492343e-02 5.130484e-02 5.969504e-02 7.006256e-02 8.251904e-02 9.739518e-02 1.149415e-01 1.351648e-01 1.575816e-01 1.813980e-01 2.054499e-01 2.282755e-01 2.477685e-01 2.610628e-01 2.650966e-01 2.574116e-01 2.376814e-01 2.083251e-01 1.740047e-01 1.403001e-01 1.119055e-01 9.143031e-02 7.916461e-02 7.356905e-02 7.215858e-02 7.230131e-02 7.180182e-02 6.935077e-02 6.452194e-02 5.775045e-02 5.006570e-02 4.277694e-02 3.722509e-02 3.449823e-02 3.536098e-02 3.996757e-02 4.781812e-02 5.756850e-02 6.721530e-02 7.461750e-02 7.804290e-02 7.683382e-02 7.165094e-02 6.424576e-02 5.680025e-02 5.122445e-02 4.871994e-02 4.941294e-02 5.261513e-02 5.706711e-02 6.140390e-02 6.439516e-02 6.535518e-02 6.424193e-02 6.165434e-02 5.882503e-02 5.742332e-02 5.954136e-02 6.754334e-02 8.391391e-02 1.106450e-01 1.481907e-01 1.946876e-01 2.453059e-01 2.926229e-01 3.284052e-01 3.460627e-01 3.426641e-01 3.198676e-01 2.832318e-01 2.404668e-01 1.992538e-01 1.661250e-01 1.468392e-01 1.466236e-01 1.713802e-01 2.271228e-01 3.181776e-01 4.442966e-01 5.971096e-01 7.591323e-01 9.048728e-01 1.007239e+00 1.044810e+00 1.008758e+00 9.067758e-01 7.595049e-01 5.937905e-01 4.351478e-01 3.009174e-01 1.993869e-01 1.298028e-01 8.659863e-02 6.249543e-02 5.082114e-02 4.639960e-02 4.572211e-02 4.661896e-02 4.791437e-02 4.908005e-02 4.990833e-02 5.059090e-02 5.117670e-02 5.171545e-02 5.218405e-02 5.255086e-02 5.286922e-02 5.334352e-02 5.436102e-02 5.639414e-02 5.988001e-02 6.507341e-02 7.188922e-02 7.986892e-02 8.819487e-02 9.586426e-02 1.017880e-01 1.051853e-01 1.057029e-01 1.035529e-01 9.957407e-02 9.504312e-02 9.134782e-02 8.973418e-02 9.104694e-02 9.544356e-02 1.024400e-01 1.109466e-01 1.195670e-01 1.268357e-01 1.318688e-01 1.345993e-01 1.362082e-01 1.388552e-01 1.453705e-01 1.583458e-01 1.792234e-01 2.078033e-01 2.412799e-01 2.752272e-01 3.036751e-01 3.210806e-01 3.233637e-01 3.093626e-01 2.811786e-01 2.433937e-01 2.019809e-01 1.625546e-01 1.293085e-01 1.041221e-01 8.704689e-02 7.647927e-02 7.036209e-02 6.665612e-02 6.385951e-02 6.120854e-02 5.849326e-02 5.592061e-02 5.377958e-02 5.235954e-02 5.181576e-02 5.232011e-02 5.413318e-02 5.763409e-02 6.330797e-02 7.154430e-02 8.241149e-02 9.547129e-02 1.096732e-01 1.234733e-01 1.351841e-01 1.433816e-01 1.473067e-01 1.470847e-01 1.436253e-01 1.383880e-01 1.327625e-01 1.277958e-01 1.237546e-01 1.203331e-01 1.166890e-01 1.119667e-01 1.056409e-01 9.777334e-02 8.901619e-02 8.057575e-02 7.382485e-02 7.009993e-02 7.038209e-02 7.514348e-02 8.434866e-02 9.745461e-02 1.136247e-01 1.318253e-01 1.510347e-01 1.701826e-01 1.882948e-01 2.042028e-01 2.166449e-01 2.241429e-01 2.254958e-01 2.199047e-01 2.075176e-01 1.892840e-01 1.671504e-01 1.434139e-01 1.203324e-01 9.969115e-02 8.248828e-02 6.895737e-02 5.873585e-02 5.112160e-02 4.535965e-02 4.083057e-02 3.711594e-02 3.404782e-02 3.160733e-02 2.987048e-02 2.892143e-02 2.876273e-02 2.931211e-02 3.038354e-02 3.168973e-02 3.294695e-02 3.388460e-02 3.434770e-02 3.429913e-02 3.383702e-02 3.313122e-02 3.239895e-02 3.182370e-02 3.153666e-02 3.158685e-02 3.196087e-02 3.261534e-02 3.347092e-02 3.447293e-02 3.557301e-02 3.674406e-02 3.798774e-02 3.933380e-02 4.082875e-02 4.256457e-02 4.459205e-02 4.693062e-02 4.949188e-02 5.205773e-02 5.427919e-02 5.575475e-02 5.606927e-02 5.497336e-02 5.240401e-02 4.855488e-02 4.382132e-02 3.875063e-02 3.387529e-02 2.967808e-02 2.645281e-02 2.432960e-02 2.326413e-02 2.307578e-02 2.352270e-02 2.432186e-02 2.520607e-02 2.595749e-02 2.642561e-02 2.652886e-02 2.626673e-02 2.570507e-02 2.495895e-02 2.416488e-02 2.347292e-02 2.302354e-02 2.294590e-02 2.335289e-02 2.434823e-02 2.601423e-02 2.837873e-02 3.140305e-02 3.494032e-02 3.870603e-02 4.232711e-02 4.537388e-02 4.744958e-02 4.830851e-02 4.794395e-02 4.663095e-02 4.492792e-02 4.361794e-02 4.359085e-02 4.570501e-02 5.065759e-02 5.876291e-02 6.993286e-02 8.354696e-02 9.852751e-02 1.134592e-01 1.269359e-01 1.379156e-01 1.461330e-01 1.523122e-01 1.583177e-01 1.666957e-01 1.801465e-01 2.007979e-01 2.293932e-01 2.647364e-01 3.036382e-01 3.411888e-01 3.717816e-01 3.902693e-01 3.930625e-01 3.788314e-01 3.489024e-01 3.071269e-01 2.582935e-01 2.078015e-01 1.602484e-01 1.189369e-01 8.550802e-02 6.024265e-02 4.230240e-02 3.032477e-02 2.280726e-02 1.836381e-02 1.590226e-02 1.463268e-02 1.404961e-02 1.384926e-02 1.387409e-02 1.403950e-02 1.430554e-02 1.461664e-02 1.496651e-02 1.530011e-02 1.557845e-02 1.577500e-02 1.587397e-02 1.587393e-02 1.579509e-02 1.565550e-02 1.548538e-02 1.529199e-02 1.509846e-02 1.489909e-02 1.470036e-02 1.450694e-02 1.433823e-02 1.422143e-02 1.420098e-02 1.434165e-02 1.471554e-02 1.542653e-02 1.659263e-02 1.833684e-02 2.078942e-02 2.405400e-02 2.818321e-02 3.310191e-02 3.865159e-02 4.452503e-02 5.030255e-02 5.548391e-02 5.964105e-02 6.241574e-02 6.364170e-02 6.336790e-02 6.187263e-02 5.952355e-02 5.677003e-02 5.399612e-02 5.146874e-02 4.933130e-02 4.758076e-02 4.618588e-02 4.507313e-02 4.426687e-02 4.382282e-02 4.386159e-02 4.452045e-02 4.590153e-02 4.803662e-02 5.086217e-02 5.426025e-02 5.799365e-02 6.180144e-02 6.540419e-02 6.844754e-02 7.066695e-02 7.176594e-02 7.159640e-02 7.008035e-02 6.737152e-02 6.374633e-02 5.967122e-02 5.562399e-02 5.211744e-02 4.949958e-02 4.795275e-02 4.739699e-02 4.757820e-02 4.810593e-02 4.856107e-02 4.859089e-02 4.797609e-02 4.667964e-02 4.479880e-02 4.258006e-02 4.026378e-02 3.807516e-02 3.614673e-02 3.450849e-02 3.309107e-02 3.177366e-02 3.041578e-02 2.891623e-02 2.722954e-02 2.537291e-02 2.342805e-02 2.150257e-02 1.973460e-02 1.824065e-02 1.711811e-02 1.644959e-02 1.628067e-02 1.664571e-02 1.756117e-02 1.902115e-02 2.102814e-02 2.351150e-02 2.642153e-02 2.964258e-02 3.300985e-02 3.635193e-02 3.944889e-02 4.208095e-02 4.408540e-02 4.533329e-02 4.578921e-02 4.553366e-02 4.470013e-02 4.353885e-02 4.227551e-02 4.114458e-02 4.028976e-02 3.978297e-02 3.959569e-02 3.959918e-02 3.965691e-02 3.961235e-02 3.937503e-02 3.892206e-02 3.830651e-02 3.764031e-02 3.709797e-02 3.681472e-02 3.690688e-02 3.742620e-02 3.833306e-02 3.952827e-02 4.084288e-02 4.211354e-02 4.314433e-02 4.378544e-02 4.394560e-02 4.357168e-02 4.271093e-02 4.141686e-02 3.982539e-02 3.806920e-02 3.627692e-02 3.457224e-02 3.302431e-02 3.165900e-02 3.049761e-02 2.947465e-02 2.856253e-02 2.771864e-02 2.693199e-02 2.622434e-02 2.567737e-02 2.538521e-02 2.547181e-02 2.605723e-02 2.721981e-02 2.897656e-02 3.125219e-02 3.389325e-02 3.668338e-02 3.933952e-02 4.155437e-02 4.308696e-02 4.375868e-02 4.347071e-02 4.227866e-02 4.033370e-02 3.782945e-02 3.506845e-02 3.227886e-02 2.966729e-02 2.740748e-02 2.554401e-02 2.407567e-02 2.295741e-02 2.209264e-02 2.140014e-02 2.080927e-02 2.026089e-02 1.974611e-02 1.926425e-02 1.883507e-02 1.847976e-02 1.821231e-02 1.803388e-02 1.792849e-02 1.786335e-02 1.780168e-02 1.769990e-02 1.752105e-02 1.724133e-02 1.685517e-02 1.637444e-02 1.582658e-02 1.525036e-02 1.468875e-02 1.418456e-02 1.376329e-02 1.344957e-02 1.324027e-02 1.313056e-02 1.309439e-02 1.310936e-02 1.315087e-02 1.320275e-02 1.326127e-02 1.333897e-02 1.345635e-02 1.365533e-02 1.397447e-02 1.445892e-02 1.513258e-02 1.601526e-02 1.709214e-02 1.832762e-02 1.965429e-02 2.098380e-02 2.220943e-02 2.322753e-02 2.394287e-02 2.428674e-02 2.421407e-02 2.372807e-02 2.286914e-02 2.170193e-02 2.032072e-02 1.882877e-02 1.732172e-02 1.588930e-02 1.460006e-02 1.350113e-02 1.261872e-02 1.196262e-02 1.152414e-02 1.129208e-02 1.124444e-02 1.135728e-02 1.160919e-02 1.196668e-02 1.240124e-02 1.288021e-02 1.335682e-02 1.380515e-02 1.418290e-02 1.447086e-02 1.465572e-02 1.474356e-02 1.476641e-02 1.477162e-02 1.483754e-02 1.504779e-02 1.551388e-02 1.632500e-02 1.758486e-02 1.935606e-02 2.168384e-02 2.454337e-02 2.787941e-02 3.157571e-02 3.545602e-02 3.934967e-02 4.302173e-02 4.630700e-02 4.906765e-02 5.123317e-02 5.282579e-02 5.399374e-02 5.490644e-02 5.584837e-02 5.710117e-02 5.893781e-02 6.153298e-02 6.503397e-02 6.938579e-02 7.442034e-02 7.991625e-02 8.538711e-02 9.040926e-02 9.453254e-02 9.722890e-02 9.821959e-02 9.725589e-02 9.428483e-02 8.945446e-02 8.302819e-02 7.537649e-02 6.704493e-02 5.841343e-02 5.000979e-02 4.215549e-02 3.513132e-02 2.907826e-02 2.405951e-02 2.002849e-02 1.689931e-02 1.453568e-02 1.280307e-02 1.155725e-02 1.068270e-02 1.007643e-02 9.659143e-03 9.374692e-03 9.183250e-03 9.051901e-03 8.967238e-03 8.913040e-03 8.885162e-03 8.871778e-03 8.882857e-03 8.906255e-03 8.941452e-03 8.986862e-03 9.040207e-03 9.098881e-03 9.160437e-03 9.218813e-03 9.273719e-03 9.318804e-03 9.352072e-03 9.371513e-03 9.376320e-03 9.367999e-03 9.346261e-03 9.314484e-03 9.276175e-03 9.235414e-03 9.193471e-03 9.153292e-03 9.116177e-03 9.083540e-03 9.053201e-03 9.025086e-03 8.998054e-03 8.972420e-03 8.946167e-03 8.921467e-03 8.897748e-03 8.877953e-03 8.864176e-03 8.859962e-03 8.867055e-03 8.886534e-03 8.921131e-03 8.972697e-03 9.042797e-03 9.129112e-03 9.231208e-03 9.349464e-03 9.484662e-03 9.635434e-03 9.805445e-03 9.997042e-03 1.021887e-02 1.048111e-02 1.080183e-02 1.120056e-02 1.170683e-02 1.235152e-02 1.316813e-02 1.418812e-02 1.544437e-02 1.695284e-02 1.871013e-02 2.069847e-02 2.287522e-02 2.516653e-02 2.747615e-02 2.968562e-02 3.166974e-02 3.330914e-02 3.448523e-02 3.511149e-02 3.513787e-02 3.455374e-02 3.338686e-02 3.171256e-02 2.963505e-02 2.727793e-02 2.476913e-02 2.223936e-02 1.979735e-02 1.753071e-02 1.550065e-02 1.374792e-02 1.228110e-02 1.108893e-02 1.014744e-02 9.423874e-03 8.880975e-03 8.484643e-03 8.198850e-03 7.997288e-03 7.856138e-03 7.757690e-03 7.688685e-03 7.639956e-03 7.605058e-03 7.580275e-03 7.563166e-03 7.552290e-03 7.546707e-03 7.550621e-03 7.560409e-03 7.577932e-03 7.602875e-03 7.638169e-03 7.681763e-03 7.734643e-03 7.795600e-03 7.863609e-03 7.936549e-03 8.012085e-03 8.085007e-03 8.153823e-03 8.212381e-03 8.258990e-03 8.288606e-03 8.300312e-03 8.291860e-03 8.264585e-03 8.219114e-03 8.158323e-03 8.090279e-03 8.015811e-03 7.945888e-03 7.887148e-03 7.847767e-03 7.837943e-03 7.865272e-03 7.943645e-03 8.080632e-03 8.289542e-03 8.577652e-03 8.954437e-03 9.426774e-03 9.999397e-03 1.066581e-02 1.142005e-02 1.225144e-02 1.313384e-02 1.404050e-02 1.494247e-02 1.580281e-02 1.657990e-02 1.724642e-02 1.776798e-02 1.812316e-02 1.830252e-02 1.830034e-02 1.812877e-02 1.780071e-02 1.734398e-02 1.678766e-02 1.615835e-02 1.549427e-02 1.481173e-02 1.413755e-02 1.348670e-02 1.286342e-02 1.228088e-02 1.173174e-02 1.121922e-02 1.073802e-02 1.028599e-02 9.861251e-03 9.461015e-03 9.087383e-03 8.739196e-03 8.419509e-03 8.130627e-03 7.870640e-03 7.642432e-03 7.447290e-03 7.281275e-03 7.145165e-03 7.036773e-03 6.952893e-03 6.890526e-03 6.849853e-03 6.827150e-03 6.821097e-03 6.830801e-03 6.855831e-03 6.895635e-03 6.952364e-03 7.024466e-03 7.114236e-03 7.221397e-03 7.345633e-03 7.485582e-03 7.638624e-03 7.801395e-03 7.968460e-03 8.133336e-03 8.288811e-03 8.427146e-03 8.540767e-03 8.622836e-03 8.667905e-03 8.672392e-03 8.634987e-03 8.557046e-03 8.442277e-03 8.297291e-03 8.127714e-03 7.944903e-03 7.756228e-03 7.570719e-03 7.399185e-03 7.244757e-03 7.115132e-03 7.013617e-03 6.940790e-03 6.897121e-03 6.880559e-03 6.887561e-03 6.914087e-03 6.953882e-03 7.001883e-03 7.052859e-03 7.100278e-03 7.141502e-03 7.169474e-03 7.184171e-03 7.182305e-03 7.164305e-03 7.131800e-03 7.085876e-03 7.030878e-03 6.969559e-03 6.906633e-03 6.848049e-03 6.796605e-03 6.757377e-03 6.733291e-03 6.727885e-03 6.742743e-03 6.779601e-03 6.839526e-03 6.920415e-03 7.023579e-03 7.144612e-03 7.281519e-03 7.432118e-03 7.588707e-03 7.748181e-03 7.905746e-03 8.051397e-03 8.181236e-03 8.288217e-03 8.367534e-03 8.412886e-03 8.420967e-03 8.390971e-03 8.321675e-03 8.214463e-03 8.074391e-03 7.905345e-03 7.713270e-03 7.505975e-03 7.291161e-03 7.075286e-03 6.864079e-03 6.663804e-03 6.480105e-03 6.313652e-03 6.167606e-03 6.041391e-03 5.935911e-03 5.849035e-03 5.778483e-03 5.722936e-03 5.679493e-03 5.646421e-03 5.620203e-03 5.600877e-03 5.588255e-03 5.577736e-03 5.569988e-03 5.565634e-03 5.563863e-03 5.565796e-03 5.572741e-03 5.587682e-03 5.612521e-03 5.651387e-03 5.710060e-03 5.795211e-03 5.913449e-03 6.080307e-03 6.298789e-03 6.584401e-03 6.953921e-03 7.415485e-03 7.989698e-03 8.689798e-03 9.523786e-03 1.051211e-02 1.165300e-02 1.295330e-02 1.442386e-02 1.604155e-02 1.781374e-02 1.971530e-02 2.173810e-02 2.386487e-02 2.605632e-02 2.831540e-02 3.060111e-02 3.289827e-02 3.518417e-02 3.743684e-02 3.963682e-02 4.175098e-02 4.375294e-02 4.562883e-02 4.730240e-02 4.876744e-02 4.996438e-02 5.085127e-02 5.137542e-02 5.152012e-02 5.121696e-02 5.049440e-02 4.930513e-02 4.769605e-02 4.566825e-02 4.329624e-02 4.061590e-02 3.770883e-02 3.466233e-02 3.152380e-02 2.842455e-02 2.537463e-02 2.249338e-02 1.979249e-02 1.732462e-02 1.511666e-02 1.316143e-02 1.148534e-02 1.004682e-02 8.858242e-03 7.874160e-03 7.083517e-03 6.449248e-03 5.956428e-03 5.572488e-03 5.280823e-03 5.058397e-03 4.892586e-03 4.768420e-03 4.676393e-03 4.607568e-03 4.555750e-03 4.516646e-03 4.486604e-03 4.463510e-03 4.444591e-03 4.428985e-03 4.414631e-03 4.405474e-03 4.397450e-03 4.391353e-03 4.386404e-03 4.382994e-03 4.380961e-03 4.381061e-03 4.382855e-03 4.385829e-03 4.390928e-03 4.398308e-03 4.407142e-03 4.418648e-03 4.432066e-03 4.447742e-03 4.466152e-03 4.486805e-03 4.509978e-03 4.536948e-03 4.566298e-03 4.599927e-03 4.637376e-03 4.679364e-03 4.726703e-03 4.779200e-03 4.837668e-03 4.902700e-03 4.973756e-03 5.050999e-03 5.134274e-03 5.222184e-03 5.314091e-03 5.407699e-03 5.501437e-03 5.593168e-03 5.679897e-03 5.758334e-03 5.826017e-03 5.880856e-03 5.918420e-03 5.938502e-03 5.938773e-03 5.917946e-03 5.876645e-03 5.814843e-03 5.734962e-03 5.637266e-03 5.526572e-03 5.404097e-03 5.273930e-03 5.139766e-03 5.004484e-03 4.871504e-03 4.741901e-03 4.621025e-03 4.506769e-03 4.403403e-03 4.309687e-03 4.227107e-03 4.154173e-03 4.091449e-03 4.036526e-03 3.991012e-03 3.951338e-03 3.917466e-03 3.888335e-03 3.864072e-03 3.841539e-03 3.822243e-03 3.804785e-03 3.788214e-03 3.772735e-03 3.758597e-03 3.745252e-03 3.732125e-03 3.719699e-03 3.708538e-03 3.698255e-03 3.688577e-03 3.680091e-03 3.674097e-03 3.668491e-03 3.666191e-03 3.665461e-03 3.667222e-03 3.672532e-03 3.681333e-03 3.693135e-03 3.709894e-03 3.730801e-03 3.755893e-03 3.786145e-03 3.822221e-03 3.862096e-03 3.906501e-03 3.955491e-03 4.007366e-03 4.061602e-03 4.118127e-03 4.174478e-03 4.230238e-03 4.283223e-03 4.333597e-03 4.379446e-03 4.419715e-03 4.453434e-03 4.481107e-03 4.502275e-03 4.516671e-03 4.527106e-03 4.532146e-03 4.535846e-03 4.538127e-03 4.543382e-03 4.551327e-03 4.566181e-03 4.588503e-03 4.620214e-03 4.662624e-03 4.716004e-03 4.781436e-03 4.855120e-03 4.940313e-03 5.029117e-03 5.124064e-03 5.216646e-03 5.307801e-03 5.392458e-03 5.464574e-03 5.524390e-03 5.565697e-03 5.588395e-03 5.589273e-03 5.567571e-03 5.524592e-03 5.460197e-03 5.374582e-03 5.273169e-03 5.156769e-03 5.028174e-03 4.892947e-03 4.753880e-03 4.612705e-03 4.475305e-03 4.342746e-03 4.217169e-03 4.102355e-03 3.995995e-03 3.901991e-03 3.819203e-03 3.745981e-03 3.683340e-03 3.629418e-03 3.584208e-03 3.544403e-03 3.510816e-03 3.481371e-03 3.455680e-03 3.432212e-03 3.411545e-03 3.392261e-03 3.374840e-03 3.359202e-03 3.345325e-03 3.334221e-03 3.325130e-03 3.318844e-03 3.316535e-03 3.316875e-03 3.321186e-03 3.329320e-03 3.340562e-03 3.355175e-03 3.372550e-03 3.392473e-03 3.414195e-03 3.436053e-03 3.457704e-03 3.479450e-03 3.498063e-03 3.514023e-03 3.526964e-03 3.534634e-03 3.537397e-03 3.534730e-03 3.526534e-03 3.512039e-03 3.492296e-03 3.466590e-03 3.435964e-03 3.401382e-03 3.363075e-03 3.321658e-03 3.279458e-03 3.235549e-03 3.192392e-03 3.149727e-03 3.108457e-03 3.070451e-03 3.034149e-03 3.001840e-03 2.972155e-03 2.946574e-03 2.923901e-03 2.904076e-03 2.887001e-03 2.871828e-03 2.859690e-03 2.847789e-03 2.837868e-03 2.828863e-03 2.820081e-03 2.812003e-03 2.804105e-03 2.796941e-03 2.790734e-03 2.785791e-03 2.782240e-03 2.780687e-03 2.782696e-03 2.788354e-03 2.799341e-03 2.816835e-03 2.841675e-03 2.875651e-03 2.920392e-03 2.976797e-03 3.047024e-03 3.132120e-03 3.234657e-03 3.354471e-03 3.493628e-03 3.653697e-03 3.834097e-03 4.036427e-03 4.259773e-03 4.504780e-03 4.770263e-03 5.055184e-03 5.357638e-03 5.674262e-03 6.003961e-03 6.342714e-03 6.686941e-03 7.031950e-03 7.375701e-03 7.710483e-03 8.033918e-03 8.339347e-03 8.624727e-03 8.882888e-03 9.109266e-03 9.301693e-03 9.455527e-03 9.567361e-03 9.634796e-03 9.656575e-03 9.631566e-03 9.558519e-03 9.441144e-03 9.276438e-03 9.071078e-03 8.824799e-03 8.545139e-03 8.232559e-03 7.895027e-03 7.537460e-03 7.164015e-03 6.782109e-03 6.396457e-03 6.012612e-03 5.633474e-03 5.266165e-03 4.913589e-03 4.580073e-03 4.265802e-03 3.974963e-03 3.706911e-03 3.462721e-03 3.243220e-03 3.046754e-03 2.873544e-03 2.721871e-03 2.589482e-03 2.475830e-03 2.378059e-03 2.295153e-03 2.225146e-03 2.166375e-03 2.117377e-03 2.076008e-03 2.041560e-03 2.012841e-03 1.988944e-03 1.968940e-03 1.951496e-03 1.936801e-03 1.923706e-03 1.912164e-03 1.900983e-03 1.891125e-03 1.881528e-03 1.871682e-03 1.861989e-03 1.851783e-03 1.841096e-03 1.829296e-03 1.816996e-03 1.803498e-03 1.788756e-03 1.772265e-03 1.754189e-03 1.733962e-03 1.711560e-03 1.686997e-03 1.659762e-03 1.630046e-03 1.597348e-03 1.561778e-03 1.523427e-03 1.482097e-03 1.437636e-03 1.390258e-03 1.340285e-03 1.287592e-03 1.232648e-03 1.175462e-03 1.116603e-03 1.056219e-03 9.948738e-04 9.328031e-04 ''') ImportString(u'flux_model_0_5_reduce(numeric)',''' 1.319175e-03 1.750092e-03 2.097782e-03 2.516841e-03 3.331051e-03 4.845508e-03 6.918991e-03 8.654762e-03 8.912326e-03 7.458358e-03 5.262554e-03 3.492610e-03 2.560264e-03 2.228979e-03 2.157527e-03 2.164920e-03 2.195222e-03 2.230741e-03 2.258832e-03 2.270818e-03 2.263597e-03 2.242157e-03 2.220702e-03 2.212826e-03 2.226071e-03 2.261554e-03 2.315124e-03 2.381431e-03 2.458370e-03 2.542760e-03 2.613196e-03 2.647036e-03 2.661671e-03 2.721502e-03 2.885240e-03 3.175670e-03 3.602367e-03 4.149221e-03 4.694773e-03 5.007476e-03 4.912585e-03 4.458992e-03 3.869113e-03 3.346053e-03 2.962090e-03 2.699124e-03 2.531038e-03 2.455856e-03 2.467843e-03 2.533804e-03 2.597186e-03 2.611304e-03 2.572183e-03 2.518830e-03 2.494159e-03 2.509870e-03 2.546956e-03 2.576485e-03 2.581916e-03 2.565686e-03 2.542628e-03 2.530002e-03 2.541950e-03 2.599755e-03 2.732886e-03 2.946864e-03 3.172050e-03 3.280736e-03 3.199026e-03 2.983326e-03 2.757594e-03 2.604128e-03 2.534622e-03 2.543032e-03 2.673475e-03 3.070823e-03 3.938379e-03 5.310514e-03 6.796687e-03 7.718797e-03 7.689302e-03 6.929021e-03 5.914528e-03 4.947772e-03 4.139133e-03 3.535575e-03 3.140323e-03 2.906241e-03 2.775464e-03 2.708603e-03 2.679938e-03 2.672426e-03 2.678887e-03 2.701059e-03 2.748861e-03 2.829456e-03 2.942847e-03 3.090513e-03 3.280684e-03 3.507182e-03 3.704880e-03 3.770831e-03 3.653487e-03 3.409726e-03 3.155707e-03 2.974481e-03 2.888218e-03 2.886525e-03 2.957291e-03 3.084516e-03 3.231604e-03 3.359031e-03 3.486678e-03 3.725117e-03 4.195816e-03 4.894354e-03 5.664606e-03 6.326131e-03 6.786610e-03 6.956993e-03 6.683895e-03 5.913124e-03 4.872831e-03 3.938851e-03 3.344865e-03 3.080923e-03 3.018482e-03 3.038197e-03 3.071228e-03 3.096516e-03 3.127572e-03 3.207435e-03 3.402847e-03 3.770103e-03 4.280835e-03 4.755778e-03 4.942355e-03 4.722597e-03 4.231457e-03 3.727992e-03 3.382278e-03 3.208390e-03 3.139733e-03 3.119395e-03 3.118112e-03 3.123846e-03 3.132263e-03 3.143936e-03 3.159930e-03 3.177747e-03 3.188882e-03 3.187266e-03 3.177975e-03 3.177278e-03 3.205713e-03 3.278946e-03 3.396443e-03 3.536390e-03 3.675744e-03 3.817789e-03 3.977924e-03 4.130840e-03 4.203844e-03 4.142138e-03 3.968221e-03 3.756064e-03 3.567835e-03 3.429476e-03 3.343864e-03 3.302021e-03 3.291102e-03 3.302688e-03 3.340095e-03 3.410620e-03 3.511612e-03 3.615371e-03 3.680765e-03 3.686275e-03 3.649254e-03 3.616769e-03 3.627487e-03 3.683819e-03 3.755514e-03 3.811547e-03 3.867571e-03 3.998804e-03 4.286249e-03 4.718309e-03 5.136354e-03 5.316422e-03 5.149588e-03 4.747130e-03 4.353019e-03 4.156569e-03 4.177006e-03 4.293877e-03 4.351049e-03 4.270990e-03 4.085374e-03 3.885187e-03 3.736944e-03 3.652488e-03 3.612037e-03 3.599281e-03 3.608251e-03 3.643389e-03 3.718445e-03 3.861683e-03 4.096446e-03 4.404131e-03 4.699087e-03 4.882862e-03 4.942528e-03 4.999695e-03 5.231800e-03 5.740802e-03 6.480780e-03 7.262807e-03 7.815016e-03 7.918450e-03 7.614676e-03 7.297327e-03 7.590373e-03 9.137810e-03 1.240748e-02 1.733751e-02 2.283540e-02 2.678248e-02 2.715064e-02 2.355526e-02 1.764408e-02 1.181975e-02 7.656440e-03 5.380071e-03 4.426117e-03 4.158344e-03 4.181915e-03 4.312385e-03 4.451761e-03 4.537124e-03 4.542414e-03 4.491468e-03 4.428117e-03 4.392224e-03 4.424012e-03 4.579266e-03 4.905678e-03 5.372169e-03 5.829649e-03 6.091967e-03 6.065673e-03 5.818136e-03 5.486793e-03 5.166137e-03 4.876003e-03 4.612931e-03 4.392661e-03 4.233991e-03 4.139743e-03 4.093720e-03 4.075135e-03 4.072220e-03 4.074377e-03 4.081367e-03 4.089323e-03 4.098748e-03 4.110021e-03 4.122477e-03 4.138194e-03 4.157585e-03 4.179712e-03 4.203819e-03 4.231725e-03 4.285173e-03 4.416540e-03 4.709404e-03 5.245488e-03 6.038527e-03 6.972079e-03 7.819878e-03 8.334602e-03 8.359266e-03 7.894386e-03 7.091047e-03 6.191037e-03 5.419804e-03 4.905889e-03 4.649281e-03 4.577622e-03 4.595987e-03 4.635951e-03 4.658263e-03 4.650464e-03 4.620340e-03 4.579580e-03 4.541647e-03 4.515162e-03 4.500504e-03 4.495655e-03 4.487765e-03 4.456673e-03 4.386825e-03 4.282102e-03 4.178356e-03 4.126412e-03 4.144381e-03 4.183860e-03 4.152444e-03 3.985475e-03 3.701646e-03 3.386989e-03 3.127370e-03 2.962528e-03 2.881313e-03 2.855061e-03 2.856405e-03 2.871900e-03 2.895757e-03 2.927496e-03 2.966749e-03 3.009791e-03 3.043409e-03 3.053055e-03 3.033655e-03 2.993168e-03 2.954102e-03 2.937526e-03 2.959417e-03 3.020683e-03 3.110267e-03 3.200016e-03 3.258791e-03 3.262476e-03 3.213184e-03 3.131980e-03 3.050166e-03 2.987383e-03 2.951715e-03 2.939516e-03 2.949456e-03 2.988918e-03 3.086556e-03 3.297142e-03 3.718053e-03 4.508327e-03 5.879284e-03 8.007223e-03 1.082751e-02 1.382748e-02 1.607870e-02 1.668090e-02 1.531806e-02 1.253481e-02 9.357792e-03 6.683642e-03 4.902750e-03 3.938640e-03 3.528168e-03 3.456461e-03 3.633952e-03 4.053622e-03 4.693531e-03 5.438943e-03 6.072968e-03 6.383871e-03 6.305955e-03 6.017079e-03 5.925563e-03 6.615786e-03 8.750596e-03 1.283315e-02 1.878914e-02 2.549107e-02 3.088698e-02 3.287360e-02 3.061109e-02 2.508722e-02 1.851821e-02 1.300921e-02 9.573020e-03 8.049950e-03 7.617563e-03 7.423274e-03 6.969233e-03 6.181242e-03 5.264537e-03 4.469648e-03 3.956186e-03 3.766035e-03 3.889322e-03 4.296164e-03 4.927801e-03 5.641628e-03 6.222730e-03 6.455877e-03 6.250164e-03 5.697078e-03 5.007578e-03 4.392418e-03 3.963376e-03 3.730674e-03 3.651842e-03 3.691599e-03 3.847344e-03 4.146575e-03 4.616318e-03 5.229929e-03 5.875058e-03 6.365476e-03 6.527171e-03 6.295541e-03 5.761138e-03 5.115677e-03 4.546897e-03 4.156926e-03 3.949430e-03 3.877920e-03 3.891187e-03 3.954168e-03 4.050080e-03 4.161500e-03 4.266758e-03 4.341205e-03 4.367563e-03 4.340617e-03 4.270515e-03 4.175057e-03 4.076607e-03 3.994999e-03 3.945725e-03 3.933824e-03 3.952479e-03 3.982814e-03 4.001532e-03 3.994664e-03 3.960719e-03 3.912832e-03 3.863661e-03 3.823655e-03 3.798271e-03 3.799609e-03 3.838802e-03 3.920692e-03 4.026729e-03 4.117121e-03 4.148885e-03 4.100792e-03 3.988115e-03 3.853048e-03 3.744434e-03 3.690217e-03 3.695071e-03 3.737347e-03 3.786709e-03 3.812615e-03 3.802920e-03 3.762720e-03 3.715324e-03 3.685051e-03 3.695623e-03 3.763703e-03 3.910005e-03 4.153763e-03 4.504205e-03 4.939884e-03 5.389785e-03 5.747745e-03 5.909177e-03 5.821939e-03 5.518757e-03 5.106638e-03 4.730149e-03 4.511255e-03 4.517860e-03 4.748249e-03 5.139885e-03 5.587755e-03 5.970462e-03 6.178748e-03 6.147959e-03 5.880461e-03 5.436565e-03 4.925953e-03 4.459215e-03 4.121983e-03 3.956605e-03 3.964148e-03 4.112316e-03 4.346189e-03 4.595811e-03 4.792798e-03 4.886926e-03 4.863695e-03 4.738527e-03 4.551007e-03 4.342577e-03 4.147563e-03 3.986942e-03 3.866054e-03 3.779433e-03 3.739600e-03 3.760377e-03 3.983874e-03 4.760313e-03 6.947250e-03 1.236022e-02 2.422884e-02 4.724073e-02 8.627376e-02 1.435800e-01 2.148240e-01 2.873537e-01 3.427321e-01 3.636754e-01 3.431930e-01 2.878740e-01 2.148110e-01 1.427396e-01 8.487734e-02 4.570103e-02 2.293594e-02 1.154318e-02 6.614820e-03 4.840463e-03 4.405327e-03 4.515783e-03 4.891684e-03 5.458907e-03 6.238304e-03 7.213002e-03 8.309690e-03 9.349895e-03 1.008416e-02 1.027565e-02 9.831006e-03 8.854404e-03 7.599085e-03 6.357663e-03 5.334853e-03 4.606289e-03 4.140207e-03 3.859786e-03 3.692338e-03 3.591784e-03 3.533668e-03 3.511010e-03 3.522332e-03 3.569795e-03 3.652546e-03 3.766776e-03 3.900802e-03 4.037406e-03 4.158915e-03 4.255377e-03 4.323588e-03 4.370805e-03 4.402793e-03 4.419648e-03 4.413137e-03 4.371965e-03 4.290909e-03 4.175880e-03 4.043004e-03 3.912879e-03 3.799322e-03 3.711623e-03 3.650196e-03 3.613024e-03 3.598450e-03 3.603962e-03 3.631904e-03 3.684107e-03 3.765832e-03 3.877883e-03 4.017247e-03 4.179139e-03 4.358396e-03 4.562182e-03 4.810820e-03 5.123401e-03 5.507320e-03 5.938117e-03 6.384260e-03 6.855084e-03 7.480982e-03 8.553842e-03 1.049510e-02 1.370192e-02 1.828744e-02 2.383549e-02 2.934269e-02 3.346507e-02 3.501961e-02 3.348778e-02 2.930249e-02 2.359039e-02 1.770091e-02 1.270956e-02 9.129475e-03 6.958195e-03 5.901887e-03 5.574760e-03 5.635194e-03 5.832885e-03 5.997112e-03 6.030119e-03 5.897483e-03 5.617070e-03 5.250641e-03 4.877601e-03 4.561062e-03 4.366085e-03 4.370945e-03 4.736941e-03 5.874980e-03 8.649478e-03 1.464797e-02 2.633829e-02 4.693398e-02 7.968320e-02 1.265095e-01 1.863149e-01 2.540655e-01 3.207705e-01 3.752216e-01 4.069321e-01 4.094674e-01 3.821606e-01 3.304745e-01 2.645055e-01 1.956509e-01 1.337233e-01 8.468816e-02 5.036386e-02 2.955163e-02 1.977431e-02 1.898251e-02 2.633791e-02 4.219574e-02 6.684437e-02 9.947478e-02 1.363865e-01 1.714517e-01 1.969601e-01 2.065718e-01 1.976632e-01 1.725921e-01 1.375684e-01 1.003759e-01 6.728772e-02 4.183495e-02 2.463263e-02 1.427077e-02 8.688774e-03 5.979014e-03 4.779334e-03 4.289441e-03 4.100398e-03 4.031218e-03 4.013864e-03 4.019823e-03 4.057951e-03 4.142382e-03 4.312224e-03 4.633079e-03 5.194932e-03 6.100224e-03 7.431744e-03 9.213234e-03 1.137402e-02 1.372004e-02 1.597362e-02 1.782688e-02 1.905374e-02 1.957130e-02 1.950496e-02 1.915331e-02 1.891590e-02 1.919944e-02 2.032133e-02 2.246172e-02 2.565242e-02 2.984752e-02 3.503128e-02 4.125952e-02 4.869759e-02 5.747077e-02 6.758238e-02 7.879080e-02 9.069902e-02 1.027250e-01 1.141377e-01 1.238843e-01 1.305314e-01 1.325483e-01 1.287058e-01 1.188407e-01 1.041626e-01 8.700237e-02 7.015005e-02 5.595274e-02 4.571515e-02 3.958230e-02 3.678453e-02 3.607929e-02 3.615066e-02 3.590091e-02 3.467539e-02 3.226097e-02 2.887522e-02 2.503285e-02 2.138847e-02 1.861254e-02 1.724911e-02 1.768049e-02 1.998378e-02 2.390906e-02 2.878425e-02 3.360765e-02 3.730875e-02 3.902145e-02 3.841691e-02 3.582547e-02 3.212288e-02 2.840012e-02 2.561222e-02 2.435997e-02 2.470647e-02 2.630757e-02 2.853356e-02 3.070195e-02 3.219758e-02 3.267759e-02 3.212097e-02 3.082717e-02 2.941252e-02 2.871166e-02 2.977068e-02 3.377167e-02 4.195695e-02 5.532252e-02 7.409535e-02 9.734378e-02 1.226529e-01 1.463114e-01 1.642026e-01 1.730314e-01 1.713320e-01 1.599338e-01 1.416159e-01 1.202334e-01 9.962690e-02 8.306252e-02 7.341960e-02 7.331178e-02 8.569011e-02 1.135614e-01 1.590888e-01 2.221483e-01 2.985548e-01 3.795661e-01 4.524364e-01 5.036193e-01 5.224049e-01 5.043792e-01 4.533879e-01 3.797524e-01 2.968952e-01 2.175739e-01 1.504587e-01 9.969346e-02 6.490139e-02 4.329932e-02 3.124771e-02 2.541057e-02 2.319980e-02 2.286106e-02 2.330948e-02 2.395719e-02 2.454002e-02 2.495417e-02 2.529545e-02 2.558835e-02 2.585772e-02 2.609202e-02 2.627543e-02 2.643461e-02 2.667176e-02 2.718051e-02 2.819707e-02 2.994000e-02 3.253670e-02 3.594461e-02 3.993446e-02 4.409743e-02 4.793213e-02 5.089401e-02 5.259263e-02 5.285144e-02 5.177643e-02 4.978703e-02 4.752156e-02 4.567391e-02 4.486709e-02 4.552347e-02 4.772178e-02 5.122000e-02 5.547331e-02 5.978348e-02 6.341784e-02 6.593439e-02 6.729967e-02 6.810408e-02 6.942760e-02 7.268526e-02 7.917289e-02 8.961169e-02 1.039017e-01 1.206399e-01 1.376136e-01 1.518376e-01 1.605403e-01 1.616819e-01 1.546813e-01 1.405893e-01 1.216969e-01 1.009905e-01 8.127730e-02 6.465425e-02 5.206104e-02 4.352345e-02 3.823964e-02 3.518105e-02 3.332806e-02 3.192975e-02 3.060427e-02 2.924663e-02 2.796030e-02 2.688979e-02 2.617977e-02 2.590788e-02 2.616005e-02 2.706659e-02 2.881704e-02 3.165399e-02 3.577215e-02 4.120575e-02 4.773564e-02 5.483662e-02 6.173664e-02 6.759207e-02 7.169078e-02 7.365336e-02 7.354236e-02 7.181264e-02 6.919399e-02 6.638124e-02 6.389790e-02 6.187728e-02 6.016655e-02 5.834452e-02 5.598335e-02 5.282044e-02 4.888667e-02 4.450810e-02 4.028787e-02 3.691243e-02 3.504997e-02 3.519104e-02 3.757174e-02 4.217433e-02 4.872731e-02 5.681236e-02 6.591267e-02 7.551733e-02 8.509132e-02 9.414738e-02 1.021014e-01 1.083225e-01 1.120714e-01 1.127479e-01 1.099524e-01 1.037588e-01 9.464200e-02 8.357520e-02 7.170697e-02 6.016618e-02 4.984557e-02 4.124414e-02 3.447869e-02 2.936793e-02 2.556080e-02 2.267983e-02 2.041528e-02 1.855797e-02 1.702391e-02 1.580367e-02 1.493524e-02 1.446071e-02 1.438136e-02 1.465606e-02 1.519177e-02 1.584486e-02 1.647348e-02 1.694230e-02 1.717385e-02 1.714957e-02 1.691851e-02 1.656561e-02 1.619948e-02 1.591185e-02 1.576833e-02 1.579342e-02 1.598044e-02 1.630767e-02 1.673546e-02 1.723646e-02 1.778651e-02 1.837203e-02 1.899387e-02 1.966690e-02 2.041438e-02 2.128229e-02 2.229602e-02 2.346531e-02 2.474594e-02 2.602886e-02 2.713960e-02 2.787738e-02 2.803463e-02 2.748668e-02 2.620200e-02 2.427744e-02 2.191066e-02 1.937531e-02 1.693765e-02 1.483904e-02 1.322640e-02 1.216480e-02 1.163207e-02 1.153789e-02 1.176135e-02 1.216093e-02 1.260303e-02 1.297874e-02 1.321280e-02 1.326443e-02 1.313337e-02 1.285253e-02 1.247947e-02 1.208244e-02 1.173646e-02 1.151177e-02 1.147295e-02 1.167644e-02 1.217412e-02 1.300712e-02 1.418936e-02 1.570152e-02 1.747016e-02 1.935302e-02 2.116355e-02 2.268694e-02 2.372479e-02 2.415426e-02 2.397197e-02 2.331547e-02 2.246396e-02 2.180897e-02 2.179543e-02 2.285250e-02 2.532879e-02 2.938145e-02 3.496643e-02 4.177348e-02 4.926376e-02 5.672959e-02 6.346796e-02 6.895782e-02 7.306649e-02 7.615609e-02 7.915883e-02 8.334784e-02 9.007324e-02 1.003989e-01 1.146966e-01 1.323682e-01 1.518191e-01 1.705944e-01 1.858908e-01 1.951346e-01 1.965313e-01 1.894157e-01 1.744512e-01 1.535635e-01 1.291467e-01 1.039008e-01 8.012421e-02 5.946847e-02 4.275401e-02 3.012132e-02 2.115120e-02 1.516239e-02 1.140363e-02 9.181906e-03 7.951132e-03 7.316338e-03 7.024806e-03 6.924632e-03 6.937045e-03 7.019752e-03 7.152771e-03 7.308322e-03 7.483257e-03 7.650055e-03 7.789223e-03 7.887501e-03 7.936987e-03 7.936966e-03 7.897544e-03 7.827752e-03 7.742688e-03 7.645997e-03 7.549228e-03 7.449546e-03 7.350181e-03 7.253472e-03 7.169116e-03 7.110717e-03 7.100488e-03 7.170824e-03 7.357769e-03 7.713263e-03 8.296316e-03 9.168421e-03 1.039471e-02 1.202700e-02 1.409160e-02 1.655095e-02 1.932580e-02 2.226252e-02 2.515127e-02 2.774196e-02 2.982053e-02 3.120787e-02 3.182085e-02 3.168395e-02 3.093632e-02 2.976177e-02 2.838502e-02 2.699806e-02 2.573437e-02 2.466565e-02 2.379038e-02 2.309294e-02 2.253656e-02 2.213344e-02 2.191141e-02 2.193080e-02 2.226022e-02 2.295076e-02 2.401831e-02 2.543108e-02 2.713013e-02 2.899683e-02 3.090072e-02 3.270210e-02 3.422377e-02 3.533348e-02 3.588297e-02 3.579820e-02 3.504018e-02 3.368576e-02 3.187316e-02 2.983561e-02 2.781200e-02 2.605872e-02 2.474979e-02 2.397637e-02 2.369850e-02 2.378910e-02 2.405296e-02 2.428054e-02 2.429544e-02 2.398805e-02 2.333982e-02 2.239940e-02 2.129003e-02 2.013189e-02 1.903758e-02 1.807337e-02 1.725424e-02 1.654554e-02 1.588683e-02 1.520789e-02 1.445812e-02 1.361477e-02 1.268646e-02 1.171403e-02 1.075128e-02 9.867298e-03 9.120326e-03 8.559053e-03 8.224797e-03 8.140334e-03 8.322854e-03 8.780587e-03 9.510577e-03 1.051407e-02 1.175575e-02 1.321077e-02 1.482129e-02 1.650493e-02 1.817596e-02 1.972445e-02 2.104048e-02 2.204270e-02 2.266665e-02 2.289460e-02 2.276683e-02 2.235007e-02 2.176942e-02 2.113776e-02 2.057229e-02 2.014488e-02 1.989148e-02 1.979784e-02 1.979959e-02 1.982846e-02 1.980617e-02 1.968751e-02 1.946103e-02 1.915325e-02 1.882016e-02 1.854899e-02 1.840736e-02 1.845344e-02 1.871310e-02 1.916653e-02 1.976413e-02 2.042144e-02 2.105677e-02 2.157217e-02 2.189272e-02 2.197280e-02 2.178584e-02 2.135547e-02 2.070843e-02 1.991270e-02 1.903460e-02 1.813846e-02 1.728612e-02 1.651215e-02 1.582950e-02 1.524880e-02 1.473733e-02 1.428127e-02 1.385932e-02 1.346599e-02 1.311217e-02 1.283869e-02 1.269261e-02 1.273590e-02 1.302862e-02 1.360990e-02 1.448828e-02 1.562609e-02 1.694663e-02 1.834169e-02 1.966976e-02 2.077719e-02 2.154348e-02 2.187934e-02 2.173536e-02 2.113933e-02 2.016685e-02 1.891472e-02 1.753422e-02 1.613943e-02 1.483365e-02 1.370374e-02 1.277200e-02 1.203784e-02 1.147870e-02 1.104632e-02 1.070007e-02 1.040463e-02 1.013045e-02 9.873055e-03 9.632124e-03 9.417536e-03 9.239881e-03 9.106154e-03 9.016939e-03 8.964244e-03 8.931677e-03 8.900838e-03 8.849952e-03 8.760523e-03 8.620666e-03 8.427585e-03 8.187218e-03 7.913290e-03 7.625180e-03 7.344373e-03 7.092280e-03 6.881645e-03 6.724785e-03 6.620137e-03 6.565281e-03 6.547197e-03 6.554680e-03 6.575434e-03 6.601376e-03 6.630637e-03 6.669487e-03 6.728174e-03 6.827667e-03 6.987234e-03 7.229462e-03 7.566292e-03 8.007630e-03 8.546069e-03 9.163808e-03 9.827143e-03 1.049190e-02 1.110472e-02 1.161377e-02 1.197144e-02 1.214337e-02 1.210704e-02 1.186403e-02 1.143457e-02 1.085097e-02 1.016036e-02 9.414387e-03 8.660860e-03 7.944651e-03 7.300031e-03 6.750567e-03 6.309360e-03 5.981309e-03 5.762068e-03 5.646042e-03 5.622219e-03 5.678641e-03 5.804596e-03 5.983342e-03 6.200620e-03 6.440104e-03 6.678409e-03 6.902577e-03 7.091450e-03 7.235428e-03 7.327859e-03 7.371781e-03 7.383205e-03 7.385809e-03 7.418771e-03 7.523896e-03 7.756941e-03 8.162498e-03 8.792428e-03 9.678032e-03 1.084192e-02 1.227168e-02 1.393970e-02 1.578786e-02 1.772801e-02 1.967484e-02 2.151087e-02 2.315350e-02 2.453383e-02 2.561659e-02 2.641289e-02 2.699687e-02 2.745322e-02 2.792419e-02 2.855058e-02 2.946891e-02 3.076649e-02 3.251699e-02 3.469289e-02 3.721017e-02 3.995812e-02 4.269356e-02 4.520463e-02 4.726627e-02 4.861445e-02 4.910979e-02 4.862794e-02 4.714241e-02 4.472723e-02 4.151409e-02 3.768824e-02 3.352246e-02 2.920671e-02 2.500489e-02 2.107774e-02 1.756566e-02 1.453913e-02 1.202975e-02 1.001425e-02 8.449657e-03 7.267842e-03 6.401535e-03 5.778626e-03 5.341348e-03 5.038213e-03 4.829572e-03 4.687346e-03 4.591625e-03 4.525951e-03 4.483619e-03 4.456520e-03 4.442581e-03 4.435889e-03 4.441428e-03 4.453128e-03 4.470726e-03 4.493431e-03 4.520103e-03 4.549440e-03 4.580218e-03 4.609406e-03 4.636860e-03 4.659402e-03 4.676036e-03 4.685756e-03 4.688160e-03 4.683999e-03 4.673130e-03 4.657242e-03 4.638087e-03 4.617707e-03 4.596735e-03 4.576646e-03 4.558088e-03 4.541770e-03 4.526601e-03 4.512543e-03 4.499027e-03 4.486210e-03 4.473084e-03 4.460733e-03 4.448874e-03 4.438976e-03 4.432088e-03 4.429981e-03 4.433528e-03 4.443267e-03 4.460566e-03 4.486348e-03 4.521398e-03 4.564556e-03 4.615604e-03 4.674732e-03 4.742331e-03 4.817717e-03 4.902722e-03 4.998521e-03 5.109433e-03 5.240555e-03 5.400914e-03 5.600282e-03 5.853413e-03 6.175758e-03 6.584065e-03 7.094061e-03 7.722185e-03 8.476419e-03 9.355064e-03 1.034923e-02 1.143761e-02 1.258326e-02 1.373807e-02 1.484281e-02 1.583487e-02 1.665457e-02 1.724261e-02 1.755574e-02 1.756894e-02 1.727687e-02 1.669343e-02 1.585628e-02 1.481752e-02 1.363896e-02 1.238457e-02 1.111968e-02 9.898675e-03 8.765354e-03 7.750327e-03 6.873960e-03 6.140552e-03 5.544467e-03 5.073721e-03 4.711937e-03 4.440487e-03 4.242321e-03 4.099425e-03 3.998644e-03 3.928069e-03 3.878845e-03 3.844343e-03 3.819978e-03 3.802529e-03 3.790138e-03 3.781583e-03 3.776145e-03 3.773354e-03 3.775311e-03 3.780205e-03 3.788966e-03 3.801438e-03 3.819084e-03 3.840882e-03 3.867322e-03 3.897800e-03 3.931805e-03 3.968275e-03 4.006043e-03 4.042503e-03 4.076912e-03 4.106191e-03 4.129495e-03 4.144303e-03 4.150156e-03 4.145930e-03 4.132293e-03 4.109557e-03 4.079162e-03 4.045140e-03 4.007906e-03 3.972944e-03 3.943574e-03 3.923884e-03 3.918971e-03 3.932636e-03 3.971823e-03 4.040316e-03 4.144771e-03 4.288826e-03 4.477219e-03 4.713387e-03 4.999699e-03 5.332903e-03 5.710025e-03 6.125718e-03 6.566920e-03 7.020252e-03 7.471234e-03 7.901403e-03 8.289948e-03 8.623211e-03 8.883991e-03 9.061578e-03 9.151259e-03 9.150172e-03 9.064385e-03 8.900356e-03 8.671990e-03 8.393831e-03 8.079176e-03 7.747136e-03 7.405866e-03 7.068776e-03 6.743348e-03 6.431710e-03 6.140440e-03 5.865868e-03 5.609611e-03 5.369010e-03 5.142997e-03 4.930626e-03 4.730508e-03 4.543691e-03 4.369598e-03 4.209755e-03 4.065313e-03 3.935320e-03 3.821216e-03 3.723645e-03 3.640637e-03 3.572583e-03 3.518386e-03 3.476446e-03 3.445263e-03 3.424926e-03 3.413575e-03 3.410548e-03 3.415400e-03 3.427916e-03 3.447817e-03 3.476182e-03 3.512233e-03 3.557118e-03 3.610699e-03 3.672816e-03 3.742791e-03 3.819312e-03 3.900697e-03 3.984230e-03 4.066668e-03 4.144405e-03 4.213573e-03 4.270384e-03 4.311418e-03 4.333952e-03 4.336196e-03 4.317494e-03 4.278523e-03 4.221139e-03 4.148645e-03 4.063857e-03 3.972452e-03 3.878114e-03 3.785359e-03 3.699592e-03 3.622379e-03 3.557566e-03 3.506809e-03 3.470395e-03 3.448561e-03 3.440279e-03 3.443781e-03 3.457044e-03 3.476941e-03 3.500942e-03 3.526430e-03 3.550139e-03 3.570751e-03 3.584737e-03 3.592085e-03 3.591152e-03 3.582153e-03 3.565900e-03 3.542938e-03 3.515439e-03 3.484779e-03 3.453316e-03 3.424024e-03 3.398302e-03 3.378689e-03 3.366645e-03 3.363942e-03 3.371371e-03 3.389801e-03 3.419763e-03 3.460207e-03 3.511789e-03 3.572306e-03 3.640759e-03 3.716059e-03 3.794353e-03 3.874090e-03 3.952873e-03 4.025699e-03 4.090618e-03 4.144108e-03 4.183767e-03 4.206443e-03 4.210484e-03 4.195486e-03 4.160838e-03 4.107232e-03 4.037195e-03 3.952673e-03 3.856635e-03 3.752988e-03 3.645580e-03 3.537643e-03 3.432039e-03 3.331902e-03 3.240053e-03 3.156826e-03 3.083803e-03 3.020696e-03 2.967956e-03 2.924518e-03 2.889241e-03 2.861468e-03 2.839747e-03 2.823210e-03 2.810101e-03 2.800439e-03 2.794127e-03 2.788868e-03 2.784994e-03 2.782817e-03 2.781931e-03 2.782898e-03 2.786371e-03 2.793841e-03 2.806261e-03 2.825693e-03 2.855030e-03 2.897606e-03 2.956724e-03 3.040154e-03 3.149394e-03 3.292200e-03 3.476961e-03 3.707743e-03 3.994849e-03 4.344899e-03 4.761893e-03 5.256053e-03 5.826500e-03 6.476651e-03 7.211932e-03 8.020773e-03 8.906870e-03 9.857650e-03 1.086905e-02 1.193244e-02 1.302816e-02 1.415770e-02 1.530056e-02 1.644913e-02 1.759208e-02 1.871842e-02 1.981841e-02 2.087549e-02 2.187647e-02 2.281442e-02 2.365120e-02 2.438372e-02 2.498219e-02 2.542563e-02 2.568771e-02 2.576006e-02 2.560848e-02 2.524720e-02 2.465257e-02 2.384803e-02 2.283412e-02 2.164812e-02 2.030795e-02 1.885441e-02 1.733117e-02 1.576190e-02 1.421228e-02 1.268731e-02 1.124669e-02 9.896244e-03 8.662309e-03 7.558329e-03 6.580715e-03 5.742671e-03 5.023410e-03 4.429121e-03 3.937080e-03 3.541758e-03 3.224624e-03 2.978214e-03 2.786244e-03 2.640411e-03 2.529198e-03 2.446293e-03 2.384210e-03 2.338196e-03 2.303784e-03 2.277875e-03 2.258323e-03 2.243302e-03 2.231755e-03 2.222295e-03 2.214493e-03 2.207316e-03 2.202737e-03 2.198725e-03 2.195677e-03 2.193202e-03 2.191497e-03 2.190480e-03 2.190530e-03 2.191428e-03 2.192914e-03 2.195464e-03 2.199154e-03 2.203571e-03 2.209324e-03 2.216033e-03 2.223871e-03 2.233076e-03 2.243403e-03 2.254989e-03 2.268474e-03 2.283149e-03 2.299963e-03 2.318688e-03 2.339682e-03 2.363352e-03 2.389600e-03 2.418834e-03 2.451350e-03 2.486878e-03 2.525500e-03 2.567137e-03 2.611092e-03 2.657046e-03 2.703850e-03 2.750718e-03 2.796584e-03 2.839948e-03 2.879167e-03 2.913008e-03 2.940428e-03 2.959210e-03 2.969251e-03 2.969386e-03 2.958973e-03 2.938322e-03 2.907422e-03 2.867481e-03 2.818633e-03 2.763286e-03 2.702049e-03 2.636965e-03 2.569883e-03 2.502242e-03 2.435752e-03 2.370950e-03 2.310512e-03 2.253385e-03 2.201702e-03 2.154844e-03 2.113553e-03 2.077086e-03 2.045725e-03 2.018263e-03 1.995506e-03 1.975669e-03 1.958733e-03 1.944168e-03 1.932036e-03 1.920769e-03 1.911121e-03 1.902393e-03 1.894107e-03 1.886368e-03 1.879299e-03 1.872626e-03 1.866063e-03 1.859849e-03 1.854269e-03 1.849128e-03 1.844289e-03 1.840046e-03 1.837048e-03 1.834246e-03 1.833095e-03 1.832731e-03 1.833611e-03 1.836266e-03 1.840666e-03 1.846567e-03 1.854947e-03 1.865400e-03 1.877946e-03 1.893073e-03 1.911111e-03 1.931048e-03 1.953250e-03 1.977745e-03 2.003683e-03 2.030801e-03 2.059063e-03 2.087239e-03 2.115119e-03 2.141611e-03 2.166799e-03 2.189723e-03 2.209858e-03 2.226717e-03 2.240553e-03 2.251138e-03 2.258336e-03 2.263553e-03 2.266073e-03 2.267923e-03 2.269064e-03 2.271691e-03 2.275663e-03 2.283090e-03 2.294252e-03 2.310107e-03 2.331312e-03 2.358002e-03 2.390718e-03 2.427560e-03 2.470156e-03 2.514558e-03 2.562032e-03 2.608323e-03 2.653901e-03 2.696229e-03 2.732287e-03 2.762195e-03 2.782849e-03 2.794198e-03 2.794637e-03 2.783786e-03 2.762296e-03 2.730098e-03 2.687291e-03 2.636585e-03 2.578384e-03 2.514087e-03 2.446474e-03 2.376940e-03 2.306352e-03 2.237652e-03 2.171373e-03 2.108585e-03 2.051177e-03 1.997998e-03 1.950995e-03 1.909602e-03 1.872990e-03 1.841670e-03 1.814709e-03 1.792104e-03 1.772202e-03 1.755408e-03 1.740686e-03 1.727840e-03 1.716106e-03 1.705772e-03 1.696131e-03 1.687420e-03 1.679601e-03 1.672663e-03 1.667111e-03 1.662565e-03 1.659422e-03 1.658267e-03 1.658438e-03 1.660593e-03 1.664660e-03 1.670281e-03 1.677588e-03 1.686275e-03 1.696237e-03 1.707097e-03 1.718026e-03 1.728852e-03 1.739725e-03 1.749031e-03 1.757011e-03 1.763482e-03 1.767317e-03 1.768699e-03 1.767365e-03 1.763267e-03 1.756019e-03 1.746148e-03 1.733295e-03 1.717982e-03 1.700691e-03 1.681538e-03 1.660829e-03 1.639729e-03 1.617774e-03 1.596196e-03 1.574864e-03 1.554228e-03 1.535226e-03 1.517074e-03 1.500920e-03 1.486077e-03 1.473287e-03 1.461950e-03 1.452038e-03 1.443501e-03 1.435914e-03 1.429845e-03 1.423894e-03 1.418934e-03 1.414431e-03 1.410041e-03 1.406002e-03 1.402053e-03 1.398470e-03 1.395367e-03 1.392895e-03 1.391120e-03 1.390343e-03 1.391348e-03 1.394177e-03 1.399671e-03 1.408418e-03 1.420838e-03 1.437826e-03 1.460196e-03 1.488398e-03 1.523512e-03 1.566060e-03 1.617328e-03 1.677236e-03 1.746814e-03 1.826849e-03 1.917049e-03 2.018214e-03 2.129886e-03 2.252390e-03 2.385132e-03 2.527592e-03 2.678819e-03 2.837131e-03 3.001981e-03 3.171357e-03 3.343470e-03 3.515975e-03 3.687850e-03 3.855242e-03 4.016959e-03 4.169674e-03 4.312363e-03 4.441444e-03 4.554633e-03 4.650847e-03 4.727764e-03 4.783681e-03 4.817398e-03 4.828287e-03 4.815783e-03 4.779260e-03 4.720572e-03 4.638219e-03 4.535539e-03 4.412400e-03 4.272569e-03 4.116280e-03 3.947514e-03 3.768730e-03 3.582007e-03 3.391054e-03 3.198229e-03 3.006306e-03 2.816737e-03 2.633082e-03 2.456795e-03 2.290037e-03 2.132901e-03 1.987481e-03 1.853456e-03 1.731361e-03 1.621610e-03 1.523377e-03 1.436772e-03 1.360936e-03 1.294741e-03 1.237915e-03 1.189030e-03 1.147576e-03 1.112573e-03 1.083188e-03 1.058689e-03 1.038004e-03 1.020780e-03 1.006420e-03 9.944722e-04 9.844701e-04 9.757478e-04 9.684005e-04 9.618528e-04 9.560822e-04 9.504914e-04 9.455624e-04 9.407638e-04 9.358409e-04 9.309943e-04 9.258915e-04 9.205480e-04 9.146478e-04 9.084981e-04 9.017488e-04 8.943778e-04 8.861325e-04 8.770946e-04 8.669812e-04 8.557799e-04 8.434984e-04 8.298810e-04 8.150231e-04 7.986739e-04 7.808888e-04 7.617136e-04 7.410483e-04 7.188182e-04 6.951288e-04 6.701424e-04 6.437958e-04 6.163239e-04 5.877310e-04 5.583017e-04 5.281093e-04 4.974369e-04 4.664016e-04 ''') ImportString(u'flux_model_0_7(numeric)',''' 2.960127e-03 3.898180e-03 4.538399e-03 5.117529e-03 6.223361e-03 8.443569e-03 1.161904e-02 1.433340e-02 1.476923e-02 1.255534e-02 9.201769e-03 6.506684e-03 5.090013e-03 4.579968e-03 4.447838e-03 4.413846e-03 4.400058e-03 4.396189e-03 4.397586e-03 4.396905e-03 4.386854e-03 4.364470e-03 4.339086e-03 4.328808e-03 4.354148e-03 4.425474e-03 4.537631e-03 4.674295e-03 4.809780e-03 4.914867e-03 4.960325e-03 4.947345e-03 4.927428e-03 4.984884e-03 5.179337e-03 5.526901e-03 6.030719e-03 6.665152e-03 7.288787e-03 7.645638e-03 7.554182e-03 7.083422e-03 6.480433e-03 5.951771e-03 5.550399e-03 5.242111e-03 5.010124e-03 4.884662e-03 4.885051e-03 4.979834e-03 5.093996e-03 5.157995e-03 5.158198e-03 5.148880e-03 5.203719e-03 5.357216e-03 5.582650e-03 5.798474e-03 5.913054e-03 5.885488e-03 5.750393e-03 5.580127e-03 5.430615e-03 5.338663e-03 5.329562e-03 5.404790e-03 5.513447e-03 5.567058e-03 5.513664e-03 5.390704e-03 5.283020e-03 5.247741e-03 5.288353e-03 5.402729e-03 5.656997e-03 6.244950e-03 7.421661e-03 9.201732e-03 1.104040e-02 1.202964e-02 1.167755e-02 1.036167e-02 8.852511e-03 7.635790e-03 6.806801e-03 6.306770e-03 6.053404e-03 5.942179e-03 5.877909e-03 5.824433e-03 5.788146e-03 5.775002e-03 5.777035e-03 5.786637e-03 5.815971e-03 5.885222e-03 6.016816e-03 6.238043e-03 6.565017e-03 6.965520e-03 7.310446e-03 7.430524e-03 7.251556e-03 6.868551e-03 6.461494e-03 6.152929e-03 5.969613e-03 5.881832e-03 5.850369e-03 5.849684e-03 5.875109e-03 5.944403e-03 6.101490e-03 6.405338e-03 6.878087e-03 7.444120e-03 7.961059e-03 8.320566e-03 8.503940e-03 8.490849e-03 8.216070e-03 7.671875e-03 7.008395e-03 6.443795e-03 6.103557e-03 5.970719e-03 5.967635e-03 6.025407e-03 6.101462e-03 6.183536e-03 6.287287e-03 6.462595e-03 6.786587e-03 7.316158e-03 8.010244e-03 8.661106e-03 8.984734e-03 8.852773e-03 8.418823e-03 7.952355e-03 7.591396e-03 7.304902e-03 7.023414e-03 6.749637e-03 6.524888e-03 6.374770e-03 6.294181e-03 6.264651e-03 6.267293e-03 6.287976e-03 6.312986e-03 6.333719e-03 6.352297e-03 6.384126e-03 6.455818e-03 6.594409e-03 6.803618e-03 7.046162e-03 7.272353e-03 7.471230e-03 7.661150e-03 7.812639e-03 7.832351e-03 7.654584e-03 7.334662e-03 7.007094e-03 6.772770e-03 6.648619e-03 6.602378e-03 6.596916e-03 6.611587e-03 6.640989e-03 6.692581e-03 6.774412e-03 6.887186e-03 7.008251e-03 7.101106e-03 7.143964e-03 7.143591e-03 7.133951e-03 7.145017e-03 7.181084e-03 7.225574e-03 7.266162e-03 7.340247e-03 7.545477e-03 7.976945e-03 8.607261e-03 9.214542e-03 9.487975e-03 9.272526e-03 8.718247e-03 8.165139e-03 7.884301e-03 7.911400e-03 8.083208e-03 8.177343e-03 8.077710e-03 7.823295e-03 7.545377e-03 7.347345e-03 7.254073e-03 7.236221e-03 7.260816e-03 7.304771e-03 7.359999e-03 7.426362e-03 7.519101e-03 7.656467e-03 7.844352e-03 8.053956e-03 8.251062e-03 8.443192e-03 8.709261e-03 9.164157e-03 9.897548e-03 1.091347e-02 1.204785e-02 1.294289e-02 1.322353e-02 1.286212e-02 1.235710e-02 1.253288e-02 1.418151e-02 1.780613e-02 2.328819e-02 2.938928e-02 3.374957e-02 3.410266e-02 3.001171e-02 2.332717e-02 1.675964e-02 1.209145e-02 9.577644e-03 8.569764e-03 8.330896e-03 8.398942e-03 8.562882e-03 8.716597e-03 8.796167e-03 8.774783e-03 8.685703e-03 8.583850e-03 8.518599e-03 8.539189e-03 8.715560e-03 9.111929e-03 9.695668e-03 1.028540e-02 1.065277e-02 1.067826e-02 1.043651e-02 1.008176e-02 9.717317e-03 9.362831e-03 9.018431e-03 8.714753e-03 8.483836e-03 8.336628e-03 8.257042e-03 8.220593e-03 8.214160e-03 8.220356e-03 8.238998e-03 8.261262e-03 8.286742e-03 8.314819e-03 8.342952e-03 8.374476e-03 8.410899e-03 8.453525e-03 8.503521e-03 8.555354e-03 8.615251e-03 8.713386e-03 8.922225e-03 9.349107e-03 1.008609e-02 1.112222e-02 1.229568e-02 1.329855e-02 1.378705e-02 1.355963e-02 1.268444e-02 1.148297e-02 1.033928e-02 9.512042e-03 9.044262e-03 8.852127e-03 8.807704e-03 8.824340e-03 8.854005e-03 8.878395e-03 8.897493e-03 8.908582e-03 8.914967e-03 8.919770e-03 8.918653e-03 8.909144e-03 8.873051e-03 8.781673e-03 8.614950e-03 8.383610e-03 8.149443e-03 7.997592e-03 7.959776e-03 7.960403e-03 7.854051e-03 7.540452e-03 7.053298e-03 6.531589e-03 6.109568e-03 5.845498e-03 5.715793e-03 5.671904e-03 5.667936e-03 5.681901e-03 5.703205e-03 5.729217e-03 5.758529e-03 5.790872e-03 5.818757e-03 5.835803e-03 5.841740e-03 5.838498e-03 5.838447e-03 5.848585e-03 5.877578e-03 5.923159e-03 5.982175e-03 6.039282e-03 6.081440e-03 6.094282e-03 6.080564e-03 6.048785e-03 6.017065e-03 5.993270e-03 5.983217e-03 5.983445e-03 5.994094e-03 6.019390e-03 6.077086e-03 6.198476e-03 6.443219e-03 6.913088e-03 7.734777e-03 8.999220e-03 1.063516e-02 1.231249e-02 1.349413e-02 1.371116e-02 1.284685e-02 1.125386e-02 9.506701e-03 8.073179e-03 7.138472e-03 6.644627e-03 6.445741e-03 6.428681e-03 6.546698e-03 6.797493e-03 7.169737e-03 7.605185e-03 7.993665e-03 8.231495e-03 8.285820e-03 8.238515e-03 8.275026e-03 8.670697e-03 9.754004e-03 1.179051e-02 1.476615e-02 1.812732e-02 2.083617e-02 2.181327e-02 2.062007e-02 1.775078e-02 1.433463e-02 1.144887e-02 9.615536e-03 8.765321e-03 8.496395e-03 8.389816e-03 8.198531e-03 7.873841e-03 7.501112e-03 7.193843e-03 7.036048e-03 7.074254e-03 7.349217e-03 7.880680e-03 8.641529e-03 9.484537e-03 1.016947e-02 1.044655e-02 1.020689e-02 9.554788e-03 8.734426e-03 7.994536e-03 7.469190e-03 7.174354e-03 7.060587e-03 7.086135e-03 7.247129e-03 7.577963e-03 8.117152e-03 8.840683e-03 9.621995e-03 1.023873e-02 1.047593e-02 1.024391e-02 9.640235e-03 8.889017e-03 8.217474e-03 7.757142e-03 7.524492e-03 7.477149e-03 7.558837e-03 7.715782e-03 7.904020e-03 8.072513e-03 8.178604e-03 8.200102e-03 8.147780e-03 8.054144e-03 7.956036e-03 7.873085e-03 7.810992e-03 7.768456e-03 7.748119e-03 7.750516e-03 7.771073e-03 7.795711e-03 7.806086e-03 7.793751e-03 7.759152e-03 7.719363e-03 7.690059e-03 7.690627e-03 7.738977e-03 7.867218e-03 8.098211e-03 8.430336e-03 8.802360e-03 9.104138e-03 9.224736e-03 9.115278e-03 8.824034e-03 8.469356e-03 8.184228e-03 8.045198e-03 8.063433e-03 8.180297e-03 8.312327e-03 8.377594e-03 8.341962e-03 8.217651e-03 8.061583e-03 7.932048e-03 7.881698e-03 7.943803e-03 8.160812e-03 8.580476e-03 9.244765e-03 1.014443e-02 1.116321e-02 1.208268e-02 1.264495e-02 1.267334e-02 1.217734e-02 1.136190e-02 1.055139e-02 1.003285e-02 9.956781e-03 1.029413e-02 1.087588e-02 1.147101e-02 1.187052e-02 1.194258e-02 1.165980e-02 1.109132e-02 1.035357e-02 9.596229e-03 8.941106e-03 8.469497e-03 8.209771e-03 8.148061e-03 8.239838e-03 8.425917e-03 8.636608e-03 8.805538e-03 8.882228e-03 8.852070e-03 8.729234e-03 8.555645e-03 8.371741e-03 8.208545e-03 8.082299e-03 7.993716e-03 7.935530e-03 7.921733e-03 7.965598e-03 8.220197e-03 9.073138e-03 1.149528e-02 1.756746e-02 3.101540e-02 5.726705e-02 1.019838e-01 1.678098e-01 2.497843e-01 3.333407e-01 3.972229e-01 4.214890e-01 3.980621e-01 3.345206e-01 2.505518e-01 1.677327e-01 1.012805e-01 5.634250e-02 3.029798e-02 1.735596e-02 1.188816e-02 1.010739e-02 9.932546e-03 1.042359e-02 1.117907e-02 1.199363e-02 1.276577e-02 1.340851e-02 1.388496e-02 1.415901e-02 1.418437e-02 1.389882e-02 1.329144e-02 1.243257e-02 1.146172e-02 1.054420e-02 9.790503e-03 9.236124e-03 8.851115e-03 8.584013e-03 8.392323e-03 8.257817e-03 8.173894e-03 8.148514e-03 8.189110e-03 8.306450e-03 8.500712e-03 8.765319e-03 9.074441e-03 9.389206e-03 9.669512e-03 9.892940e-03 1.005243e-02 1.016487e-02 1.024334e-02 1.028762e-02 1.027809e-02 1.018810e-02 1.000528e-02 9.743224e-03 9.439408e-03 9.141863e-03 8.883017e-03 8.685010e-03 8.549401e-03 8.471791e-03 8.447894e-03 8.469976e-03 8.540663e-03 8.660073e-03 8.836240e-03 9.065991e-03 9.334003e-03 9.612817e-03 9.861641e-03 1.005731e-02 1.021012e-02 1.035765e-02 1.056609e-02 1.089637e-02 1.143287e-02 1.233342e-02 1.394713e-02 1.687716e-02 2.193286e-02 2.984014e-02 4.072876e-02 5.360405e-02 6.621631e-02 7.558471e-02 7.909726e-02 7.562791e-02 6.617672e-02 5.329244e-02 4.001451e-02 2.875574e-02 2.065354e-02 1.567703e-02 1.313946e-02 1.215824e-02 1.197477e-02 1.206765e-02 1.212130e-02 1.198997e-02 1.164727e-02 1.113854e-02 1.055708e-02 9.997049e-03 9.524122e-03 9.197932e-03 9.084083e-03 9.321747e-03 1.027066e-02 1.268970e-02 1.797892e-02 2.832676e-02 4.660252e-02 7.574301e-02 1.175657e-01 1.712523e-01 2.324975e-01 2.934030e-01 3.439203e-01 3.744144e-01 3.786828e-01 3.556364e-01 3.098219e-01 2.502314e-01 1.873092e-01 1.302553e-01 8.481877e-02 5.290705e-02 3.356128e-02 2.454664e-02 2.395526e-02 3.096322e-02 4.584013e-02 6.880349e-02 9.906499e-02 1.331854e-01 1.655032e-01 1.889249e-01 1.976456e-01 1.893034e-01 1.661206e-01 1.338478e-01 9.964475e-02 6.925201e-02 4.586915e-02 3.001861e-02 2.037991e-02 1.506296e-02 1.234541e-02 1.101848e-02 1.039086e-02 1.011298e-02 1.002831e-02 1.006738e-02 1.018961e-02 1.040265e-02 1.073894e-02 1.128613e-02 1.218518e-02 1.361638e-02 1.577201e-02 1.880303e-02 2.277419e-02 2.762404e-02 3.310011e-02 3.878719e-02 4.409776e-02 4.841720e-02 5.119253e-02 5.220903e-02 5.172019e-02 5.050914e-02 4.979609e-02 5.095143e-02 5.519238e-02 6.330054e-02 7.556026e-02 9.188043e-02 1.118142e-01 1.346575e-01 1.592054e-01 1.838305e-01 2.065712e-01 2.257908e-01 2.404048e-01 2.501293e-01 2.548637e-01 2.541668e-01 2.472168e-01 2.330752e-01 2.118809e-01 1.854399e-01 1.570281e-01 1.305438e-01 1.092020e-01 9.464280e-02 8.675305e-02 8.403017e-02 8.424836e-02 8.507838e-02 8.456123e-02 8.156984e-02 7.581957e-02 6.789507e-02 5.899438e-02 5.061303e-02 4.426210e-02 4.117208e-02 4.219978e-02 4.749728e-02 5.646183e-02 6.751933e-02 7.833651e-02 8.643230e-02 8.980897e-02 8.771384e-02 8.090673e-02 7.136626e-02 6.150588e-02 5.336856e-02 4.815053e-02 4.590474e-02 4.591396e-02 4.710210e-02 4.850046e-02 4.945895e-02 4.978151e-02 4.965020e-02 4.943003e-02 4.958477e-02 5.057728e-02 5.295086e-02 5.740999e-02 6.491864e-02 7.650673e-02 9.280539e-02 1.136113e-01 1.374047e-01 1.613400e-01 1.818870e-01 1.958475e-01 2.012718e-01 1.979657e-01 1.874851e-01 1.726043e-01 1.565975e-01 1.428584e-01 1.349196e-01 1.362181e-01 1.502829e-01 1.801733e-01 2.275842e-01 2.917465e-01 3.679773e-01 4.476063e-01 5.184686e-01 5.679940e-01 5.863932e-01 5.697607e-01 5.218078e-01 4.520990e-01 3.729249e-01 2.959581e-01 2.290827e-01 1.762598e-01 1.374384e-01 1.105785e-01 9.294958e-02 8.208787e-02 7.610568e-02 7.374725e-02 7.418338e-02 7.688164e-02 8.136597e-02 8.709437e-02 9.351331e-02 9.988935e-02 1.054833e-01 1.096629e-01 1.120196e-01 1.124609e-01 1.112301e-01 1.089661e-01 1.065675e-01 1.050636e-01 1.053783e-01 1.080589e-01 1.131090e-01 1.199156e-01 1.274435e-01 1.343687e-01 1.396195e-01 1.425686e-01 1.432068e-01 1.422292e-01 1.407882e-01 1.401087e-01 1.412751e-01 1.449906e-01 1.513014e-01 1.597328e-01 1.692872e-01 1.787416e-01 1.868190e-01 1.928353e-01 1.969443e-01 2.005860e-01 2.061909e-01 2.168368e-01 2.351935e-01 2.625233e-01 2.981558e-01 3.384342e-01 3.779880e-01 4.098327e-01 4.276902e-01 4.271323e-01 4.071823e-01 3.707059e-01 3.233549e-01 2.723569e-01 2.244421e-01 1.846067e-01 1.550906e-01 1.359179e-01 1.251092e-01 1.200927e-01 1.182635e-01 1.175941e-01 1.168988e-01 1.156118e-01 1.136303e-01 1.109796e-01 1.077289e-01 1.039257e-01 9.980093e-02 9.595789e-02 9.337254e-02 9.333248e-02 9.712571e-02 1.056385e-01 1.190841e-01 1.367336e-01 1.569803e-01 1.776668e-01 1.963953e-01 2.110761e-01 2.202471e-01 2.233006e-01 2.205243e-01 2.128153e-01 2.016585e-01 1.885353e-01 1.749376e-01 1.619066e-01 1.501627e-01 1.400890e-01 1.319053e-01 1.259214e-01 1.228726e-01 1.239032e-01 1.305530e-01 1.443705e-01 1.663614e-01 1.964972e-01 2.331833e-01 2.733651e-01 3.127477e-01 3.468107e-01 3.714429e-01 3.842555e-01 3.845891e-01 3.737481e-01 3.542330e-01 3.291938e-01 3.014634e-01 2.734221e-01 2.464585e-01 2.215368e-01 1.990747e-01 1.793052e-01 1.623819e-01 1.482981e-01 1.369209e-01 1.279397e-01 1.208448e-01 1.150556e-01 1.100659e-01 1.054762e-01 1.011948e-01 9.732743e-02 9.418336e-02 9.215418e-02 9.150715e-02 9.240822e-02 9.473208e-02 9.811165e-02 1.021007e-01 1.061455e-01 1.098765e-01 1.130830e-01 1.158335e-01 1.182925e-01 1.208093e-01 1.236048e-01 1.269020e-01 1.307782e-01 1.352202e-01 1.402484e-01 1.457827e-01 1.518479e-01 1.584845e-01 1.658068e-01 1.740890e-01 1.838063e-01 1.955232e-01 2.100108e-01 2.276698e-01 2.484301e-01 2.713525e-01 2.944449e-01 3.147860e-01 3.291861e-01 3.345089e-01 3.289011e-01 3.119166e-01 2.849467e-01 2.507058e-01 2.129171e-01 1.751275e-01 1.405208e-01 1.110897e-01 8.784912e-02 7.087205e-02 5.958149e-02 5.311729e-02 5.050030e-02 5.079257e-02 5.316955e-02 5.690591e-02 6.136206e-02 6.597739e-02 7.027803e-02 7.390693e-02 7.659137e-02 7.824707e-02 7.894589e-02 7.892942e-02 7.858036e-02 7.835929e-02 7.874124e-02 8.006661e-02 8.251293e-02 8.603037e-02 9.032673e-02 9.491573e-02 9.922995e-02 1.026992e-01 1.049265e-01 1.058098e-01 1.056169e-01 1.050260e-01 1.050749e-01 1.069597e-01 1.118166e-01 1.204290e-01 1.329270e-01 1.486479e-01 1.660892e-01 1.831774e-01 1.975922e-01 2.073913e-01 2.114555e-01 2.099018e-01 2.041217e-01 1.966978e-01 1.906923e-01 1.890707e-01 1.940735e-01 2.065068e-01 2.254821e-01 2.484998e-01 2.717602e-01 2.909664e-01 3.022251e-01 3.028333e-01 2.917553e-01 2.698899e-01 2.399825e-01 2.054327e-01 1.700943e-01 1.372408e-01 1.092233e-01 8.714168e-02 7.112133e-02 6.041747e-02 5.386949e-02 5.020262e-02 4.822029e-02 4.701348e-02 4.595758e-02 4.476451e-02 4.337365e-02 4.192777e-02 4.063855e-02 3.974693e-02 3.942517e-02 3.981235e-02 4.094990e-02 4.279568e-02 4.527697e-02 4.824365e-02 5.149234e-02 5.478309e-02 5.778987e-02 6.020859e-02 6.166871e-02 6.196611e-02 6.092510e-02 5.858558e-02 5.511833e-02 5.086701e-02 4.621620e-02 4.161150e-02 3.743857e-02 3.400281e-02 3.153643e-02 3.017650e-02 2.999426e-02 3.101825e-02 3.324782e-02 3.665177e-02 4.108953e-02 4.638637e-02 5.224596e-02 5.828870e-02 6.404173e-02 6.910512e-02 7.312435e-02 7.588940e-02 7.738502e-02 7.778990e-02 7.736056e-02 7.643516e-02 7.527815e-02 7.408263e-02 7.292838e-02 7.177950e-02 7.062083e-02 6.940363e-02 6.823558e-02 6.723811e-02 6.660286e-02 6.650222e-02 6.702153e-02 6.811781e-02 6.961613e-02 7.127337e-02 7.274231e-02 7.373298e-02 7.404064e-02 7.348102e-02 7.206271e-02 6.979762e-02 6.682780e-02 6.328631e-02 5.940868e-02 5.542029e-02 5.160024e-02 4.816841e-02 4.535224e-02 4.326293e-02 4.193963e-02 4.127519e-02 4.109443e-02 4.116395e-02 4.125036e-02 4.116836e-02 4.081281e-02 4.018335e-02 3.934353e-02 3.844560e-02 3.761313e-02 3.695516e-02 3.651576e-02 3.627198e-02 3.613722e-02 3.600078e-02 3.573894e-02 3.526843e-02 3.455390e-02 3.361388e-02 3.252738e-02 3.139973e-02 3.038570e-02 2.963231e-02 2.928328e-02 2.948091e-02 3.032427e-02 3.188384e-02 3.416584e-02 3.710841e-02 4.061935e-02 4.445050e-02 4.838966e-02 5.214581e-02 5.543761e-02 5.804991e-02 5.982273e-02 6.070132e-02 6.076403e-02 6.016629e-02 5.915629e-02 5.804235e-02 5.710211e-02 5.664089e-02 5.682870e-02 5.776628e-02 5.941824e-02 6.162021e-02 6.412568e-02 6.658619e-02 6.868313e-02 7.010636e-02 7.067198e-02 7.032243e-02 6.911733e-02 6.723961e-02 6.498842e-02 6.261174e-02 6.039079e-02 5.851097e-02 5.705302e-02 5.601151e-02 5.530527e-02 5.481886e-02 5.439768e-02 5.391862e-02 5.329728e-02 5.246486e-02 5.144593e-02 5.022991e-02 4.889421e-02 4.749600e-02 4.609647e-02 4.476835e-02 4.354208e-02 4.242963e-02 4.145037e-02 4.055157e-02 3.971256e-02 3.890549e-02 3.812588e-02 3.739881e-02 3.680819e-02 3.645562e-02 3.646203e-02 3.696283e-02 3.803680e-02 3.971346e-02 4.193015e-02 4.454412e-02 4.736454e-02 5.012731e-02 5.255828e-02 5.444351e-02 5.563060e-02 5.602951e-02 5.569450e-02 5.475479e-02 5.336096e-02 5.177049e-02 5.014921e-02 4.866247e-02 4.744599e-02 4.653377e-02 4.594832e-02 4.567862e-02 4.569458e-02 4.599379e-02 4.657990e-02 4.744823e-02 4.862688e-02 5.009979e-02 5.182566e-02 5.371227e-02 5.561664e-02 5.735354e-02 5.871481e-02 5.948927e-02 5.952343e-02 5.869602e-02 5.698837e-02 5.447241e-02 5.131383e-02 4.773987e-02 4.401726e-02 4.041862e-02 3.718454e-02 3.450470e-02 3.247772e-02 3.115165e-02 3.046328e-02 3.031845e-02 3.055913e-02 3.102795e-02 3.157305e-02 3.207159e-02 3.244737e-02 3.268160e-02 3.279494e-02 3.287294e-02 3.300608e-02 3.331000e-02 3.386491e-02 3.473795e-02 3.592481e-02 3.737498e-02 3.897784e-02 4.058292e-02 4.200234e-02 4.306228e-02 4.361171e-02 4.355433e-02 4.283898e-02 4.150205e-02 3.964364e-02 3.740039e-02 3.495293e-02 3.248767e-02 3.016669e-02 2.813500e-02 2.649316e-02 2.530023e-02 2.457601e-02 2.431395e-02 2.447162e-02 2.501110e-02 2.587348e-02 2.699473e-02 2.832873e-02 2.978788e-02 3.129971e-02 3.279286e-02 3.413889e-02 3.527329e-02 3.608095e-02 3.650489e-02 3.650095e-02 3.606167e-02 3.524594e-02 3.413271e-02 3.285788e-02 3.157253e-02 3.044845e-02 2.962556e-02 2.925727e-02 2.943243e-02 3.021303e-02 3.158290e-02 3.350799e-02 3.587971e-02 3.855207e-02 4.137921e-02 4.416614e-02 4.677434e-02 4.909067e-02 5.104851e-02 5.266101e-02 5.403892e-02 5.530748e-02 5.668296e-02 5.838356e-02 6.061503e-02 6.350230e-02 6.715403e-02 7.150391e-02 7.639694e-02 8.163513e-02 8.679625e-02 9.151164e-02 9.540792e-02 9.802556e-02 9.914309e-02 9.855767e-02 9.623404e-02 9.231404e-02 8.703026e-02 8.070613e-02 7.380795e-02 6.664458e-02 5.965659e-02 5.310174e-02 4.718643e-02 4.201253e-02 3.762017e-02 3.395115e-02 3.094013e-02 2.846894e-02 2.645507e-02 2.478925e-02 2.340713e-02 2.225163e-02 2.127120e-02 2.044374e-02 1.974852e-02 1.915416e-02 1.866037e-02 1.824254e-02 1.789810e-02 1.760971e-02 1.738637e-02 1.721545e-02 1.709527e-02 1.702742e-02 1.700741e-02 1.702975e-02 1.708912e-02 1.716951e-02 1.726313e-02 1.735208e-02 1.742391e-02 1.746621e-02 1.747148e-02 1.743848e-02 1.736454e-02 1.725686e-02 1.712511e-02 1.698140e-02 1.683477e-02 1.669584e-02 1.657170e-02 1.646731e-02 1.637947e-02 1.630653e-02 1.624411e-02 1.618933e-02 1.613590e-02 1.608600e-02 1.603887e-02 1.600243e-02 1.598462e-02 1.599718e-02 1.604947e-02 1.614808e-02 1.630196e-02 1.651616e-02 1.679230e-02 1.711973e-02 1.749009e-02 1.789357e-02 1.831934e-02 1.875161e-02 1.918723e-02 1.962367e-02 2.007545e-02 2.056768e-02 2.114892e-02 2.187826e-02 2.284282e-02 2.413810e-02 2.586759e-02 2.812824e-02 3.101975e-02 3.459636e-02 3.885938e-02 4.376695e-02 4.921389e-02 5.501051e-02 6.090492e-02 6.658990e-02 7.174029e-02 7.604320e-02 7.919050e-02 8.095347e-02 8.120037e-02 7.989922e-02 7.711794e-02 7.304280e-02 6.793827e-02 6.211698e-02 5.590190e-02 4.962214e-02 4.355265e-02 3.791244e-02 3.285453e-02 2.848062e-02 2.480849e-02 2.180897e-02 1.942129e-02 1.756335e-02 1.614374e-02 1.507852e-02 1.428290e-02 1.369345e-02 1.325582e-02 1.292942e-02 1.268481e-02 1.250101e-02 1.236320e-02 1.226215e-02 1.219057e-02 1.214238e-02 1.211281e-02 1.210717e-02 1.211438e-02 1.213393e-02 1.216283e-02 1.220249e-02 1.224840e-02 1.229963e-02 1.235426e-02 1.240973e-02 1.246393e-02 1.251377e-02 1.255355e-02 1.258344e-02 1.259675e-02 1.259487e-02 1.257320e-02 1.253339e-02 1.247558e-02 1.240342e-02 1.231937e-02 1.222744e-02 1.213866e-02 1.205271e-02 1.198126e-02 1.193189e-02 1.191045e-02 1.192796e-02 1.198997e-02 1.211116e-02 1.229794e-02 1.256329e-02 1.291397e-02 1.335930e-02 1.390528e-02 1.455658e-02 1.530457e-02 1.614228e-02 1.705658e-02 1.801984e-02 1.900309e-02 1.997506e-02 2.089825e-02 2.172907e-02 2.244101e-02 2.299971e-02 2.338571e-02 2.359213e-02 2.361527e-02 2.347034e-02 2.317205e-02 2.274990e-02 2.223397e-02 2.165067e-02 2.103571e-02 2.040232e-02 1.977492e-02 1.916324e-02 1.856771e-02 1.799791e-02 1.744308e-02 1.690552e-02 1.637867e-02 1.586221e-02 1.535522e-02 1.485901e-02 1.437861e-02 1.391646e-02 1.347965e-02 1.307484e-02 1.270038e-02 1.236292e-02 1.206605e-02 1.180458e-02 1.158154e-02 1.139454e-02 1.124016e-02 1.111560e-02 1.102149e-02 1.095369e-02 1.091157e-02 1.089420e-02 1.090139e-02 1.093257e-02 1.099101e-02 1.107385e-02 1.118416e-02 1.132105e-02 1.148341e-02 1.166881e-02 1.187320e-02 1.209187e-02 1.231711e-02 1.254017e-02 1.275150e-02 1.294097e-02 1.309883e-02 1.321627e-02 1.328635e-02 1.330457e-02 1.326930e-02 1.318232e-02 1.304834e-02 1.287596e-02 1.267171e-02 1.245072e-02 1.222175e-02 1.199619e-02 1.178851e-02 1.160161e-02 1.144602e-02 1.132617e-02 1.124265e-02 1.119614e-02 1.118445e-02 1.120344e-02 1.124852e-02 1.131170e-02 1.138720e-02 1.146905e-02 1.154944e-02 1.162584e-02 1.168916e-02 1.174121e-02 1.177828e-02 1.180243e-02 1.181722e-02 1.182543e-02 1.183505e-02 1.185085e-02 1.188132e-02 1.193687e-02 1.202223e-02 1.214572e-02 1.231269e-02 1.252814e-02 1.279409e-02 1.311164e-02 1.348040e-02 1.389460e-02 1.435364e-02 1.484385e-02 1.535884e-02 1.589094e-02 1.641805e-02 1.693094e-02 1.741737e-02 1.784985e-02 1.821818e-02 1.850497e-02 1.869781e-02 1.878159e-02 1.874940e-02 1.860034e-02 1.833319e-02 1.795438e-02 1.747851e-02 1.691910e-02 1.629412e-02 1.562749e-02 1.494114e-02 1.425477e-02 1.358522e-02 1.295166e-02 1.236938e-02 1.184227e-02 1.137826e-02 1.097655e-02 1.063849e-02 1.035917e-02 1.013189e-02 9.951614e-03 9.810468e-03 9.702484e-03 9.618912e-03 9.557200e-03 9.515783e-03 9.483976e-03 9.461894e-03 9.449512e-03 9.444560e-03 9.448319e-03 9.462571e-03 9.492229e-03 9.540240e-03 9.613574e-03 9.722074e-03 9.877603e-03 1.009114e-02 1.039193e-02 1.078277e-02 1.129164e-02 1.194947e-02 1.276964e-02 1.378874e-02 1.503251e-02 1.651280e-02 1.826878e-02 2.029762e-02 2.260968e-02 2.523040e-02 2.811489e-02 3.128116e-02 3.468496e-02 3.831253e-02 4.214123e-02 4.609709e-02 5.019291e-02 5.435932e-02 5.857211e-02 6.279117e-02 6.698547e-02 7.112133e-02 7.513537e-02 7.898439e-02 8.264580e-02 8.596457e-02 8.893831e-02 9.144354e-02 9.339614e-02 9.469099e-02 9.528888e-02 9.504757e-02 9.401723e-02 9.209470e-02 8.936236e-02 8.581278e-02 8.158410e-02 7.673472e-02 7.142035e-02 6.580388e-02 5.997267e-02 5.418349e-02 4.845010e-02 4.301064e-02 3.788661e-02 3.318371e-02 2.895980e-02 2.520096e-02 2.196790e-02 1.917741e-02 1.686382e-02 1.493693e-02 1.338185e-02 1.212660e-02 1.114562e-02 1.037645e-02 9.788728e-03 9.337643e-03 8.999838e-03 8.745925e-03 8.557712e-03 8.417606e-03 8.313276e-03 8.236103e-03 8.178626e-03 8.136319e-03 8.103930e-03 8.079169e-03 8.057298e-03 8.047121e-03 8.040157e-03 8.037925e-03 8.039122e-03 8.045240e-03 8.056566e-03 8.073693e-03 8.096946e-03 8.126091e-03 8.163713e-03 8.211005e-03 8.267676e-03 8.337256e-03 8.419885e-03 8.518060e-03 8.634836e-03 8.771677e-03 8.931952e-03 9.121128e-03 9.339885e-03 9.595656e-03 9.891369e-03 1.023235e-02 1.062407e-02 1.106942e-02 1.157298e-02 1.213775e-02 1.276301e-02 1.344779e-02 1.418830e-02 1.497597e-02 1.580190e-02 1.665023e-02 1.750493e-02 1.834696e-02 1.915304e-02 1.989824e-02 2.055993e-02 2.111785e-02 2.154451e-02 2.182956e-02 2.195752e-02 2.191971e-02 2.171759e-02 2.135359e-02 2.084243e-02 2.019366e-02 1.943582e-02 1.858639e-02 1.767397e-02 1.672603e-02 1.576696e-02 1.482156e-02 1.390596e-02 1.304660e-02 1.224551e-02 1.152100e-02 1.087245e-02 1.030560e-02 9.815045e-03 9.399401e-03 9.048929e-03 8.762146e-03 8.525607e-03 8.333687e-03 8.178554e-03 8.055588e-03 7.953460e-03 7.871676e-03 7.804023e-03 7.745854e-03 7.695487e-03 7.651979e-03 7.613001e-03 7.576570e-03 7.543098e-03 7.513281e-03 7.486089e-03 7.460333e-03 7.437165e-03 7.420018e-03 7.403485e-03 7.393188e-03 7.385157e-03 7.381142e-03 7.382731e-03 7.389425e-03 7.400057e-03 7.417765e-03 7.441052e-03 7.469546e-03 7.504709e-03 7.548724e-03 7.596594e-03 7.650353e-03 7.710819e-03 7.774602e-03 7.841828e-03 7.913326e-03 7.984827e-03 8.057108e-03 8.127184e-03 8.196381e-03 8.262491e-03 8.324887e-03 8.382776e-03 8.438149e-03 8.491314e-03 8.542188e-03 8.597136e-03 8.653245e-03 8.718872e-03 8.793177e-03 8.885135e-03 8.992364e-03 9.123140e-03 9.277182e-03 9.457156e-03 9.663965e-03 9.896304e-03 1.015585e-02 1.043147e-02 1.072995e-02 1.103075e-02 1.133999e-02 1.163617e-02 1.192088e-02 1.218172e-02 1.240421e-02 1.258884e-02 1.272065e-02 1.279959e-02 1.281820e-02 1.277505e-02 1.267336e-02 1.251373e-02 1.229669e-02 1.203610e-02 1.173450e-02 1.139876e-02 1.104379e-02 1.067616e-02 1.030113e-02 9.933656e-03 9.576710e-03 9.236231e-03 8.922411e-03 8.629629e-03 8.368453e-03 8.136621e-03 7.930825e-03 7.753832e-03 7.602089e-03 7.475012e-03 7.366045e-03 7.276549e-03 7.201903e-03 7.140825e-03 7.089948e-03 7.049944e-03 7.017271e-03 6.992685e-03 6.975495e-03 6.965241e-03 6.963434e-03 6.968759e-03 6.981758e-03 7.005359e-03 7.036176e-03 7.077074e-03 7.127347e-03 7.185685e-03 7.252536e-03 7.326387e-03 7.406772e-03 7.492342e-03 7.578539e-03 7.664872e-03 7.752018e-03 7.832199e-03 7.906002e-03 7.972893e-03 8.026704e-03 8.068410e-03 8.096425e-03 8.109995e-03 8.107465e-03 8.090753e-03 8.058514e-03 8.012610e-03 7.955951e-03 7.888776e-03 7.812858e-03 7.734107e-03 7.650464e-03 7.568231e-03 7.487124e-03 7.409463e-03 7.340601e-03 7.276530e-03 7.223582e-03 7.178548e-03 7.145296e-03 7.121032e-03 7.105664e-03 7.099282e-03 7.099570e-03 7.109569e-03 7.122087e-03 7.140874e-03 7.163182e-03 7.186696e-03 7.212156e-03 7.237755e-03 7.264045e-03 7.290324e-03 7.317571e-03 7.344144e-03 7.371196e-03 7.402068e-03 7.435552e-03 7.475697e-03 7.525118e-03 7.585420e-03 7.660910e-03 7.756053e-03 7.873261e-03 8.018486e-03 8.194804e-03 8.410383e-03 8.665429e-03 8.966440e-03 9.318208e-03 9.721901e-03 1.018257e-02 1.069909e-02 1.127472e-02 1.190878e-02 1.259960e-02 1.334318e-02 1.413327e-02 1.496807e-02 1.583794e-02 1.673456e-02 1.764704e-02 1.856994e-02 1.948340e-02 2.038150e-02 2.124615e-02 2.207101e-02 2.283658e-02 2.352924e-02 2.414135e-02 2.465886e-02 2.507050e-02 2.536754e-02 2.554434e-02 2.559512e-02 2.551420e-02 2.530930e-02 2.497033e-02 2.451342e-02 2.393633e-02 2.325793e-02 2.247875e-02 2.162012e-02 2.069508e-02 1.971542e-02 1.870148e-02 1.766688e-02 1.662782e-02 1.559308e-02 1.458253e-02 1.360669e-02 1.267801e-02 1.179814e-02 1.097954e-02 1.022133e-02 9.527385e-03 8.900329e-03 8.336098e-03 7.836776e-03 7.397303e-03 7.011916e-03 6.679283e-03 6.391686e-03 6.146113e-03 5.937971e-03 5.762449e-03 5.615505e-03 5.491430e-03 5.388102e-03 5.302466e-03 5.231951e-03 5.174008e-03 5.125059e-03 5.085493e-03 5.052250e-03 5.025141e-03 5.000809e-03 4.981800e-03 4.965204e-03 4.949530e-03 4.935818e-03 4.922143e-03 4.908557e-03 4.892848e-03 4.877406e-03 4.859760e-03 4.839820e-03 4.816145e-03 4.789037e-03 4.756798e-03 4.719343e-03 4.676615e-03 4.627055e-03 4.570822e-03 4.506440e-03 4.434051e-03 4.353672e-03 4.264285e-03 4.165221e-03 4.056819e-03 3.939735e-03 3.813271e-03 3.678292e-03 3.534746e-03 3.383996e-03 3.226229e-03 3.062945e-03 2.894667e-03 ''') ImportString(u'flux_model_1_0(numeric)',''' 2.795926e-03 3.664451e-03 4.234822e-03 4.692629e-03 5.493201e-03 7.071624e-03 9.315712e-03 1.123027e-02 1.154659e-02 1.001144e-02 7.680693e-03 5.805874e-03 4.816900e-03 4.457820e-03 4.360444e-03 4.325183e-03 4.299991e-03 4.291636e-03 4.306400e-03 4.331071e-03 4.345031e-03 4.338566e-03 4.319855e-03 4.304587e-03 4.308393e-03 4.338773e-03 4.395153e-03 4.470044e-03 4.540741e-03 4.578945e-03 4.571582e-03 4.540354e-03 4.519871e-03 4.531703e-03 4.578494e-03 4.662658e-03 4.799377e-03 4.997884e-03 5.222071e-03 5.384182e-03 5.409365e-03 5.310966e-03 5.176761e-03 5.080273e-03 5.020871e-03 4.956448e-03 4.868438e-03 4.788391e-03 4.753854e-03 4.770719e-03 4.811976e-03 4.846114e-03 4.861902e-03 4.876799e-03 4.921897e-03 5.022237e-03 5.185621e-03 5.387023e-03 5.569133e-03 5.667294e-03 5.649342e-03 5.533873e-03 5.371652e-03 5.225477e-03 5.143508e-03 5.141076e-03 5.192619e-03 5.246570e-03 5.263103e-03 5.246081e-03 5.232226e-03 5.257946e-03 5.337141e-03 5.483993e-03 5.751980e-03 6.257228e-03 7.124805e-03 8.315096e-03 9.462988e-03 1.001214e-02 9.682868e-03 8.743498e-03 7.714515e-03 6.932042e-03 6.458024e-03 6.227117e-03 6.150602e-03 6.129398e-03 6.084279e-03 6.003645e-03 5.925308e-03 5.880495e-03 5.867752e-03 5.872381e-03 5.901174e-03 5.976073e-03 6.113561e-03 6.305273e-03 6.509826e-03 6.677845e-03 6.761523e-03 6.737088e-03 6.622453e-03 6.475778e-03 6.353055e-03 6.268290e-03 6.210241e-03 6.161562e-03 6.101486e-03 6.017767e-03 5.928254e-03 5.877664e-03 5.910153e-03 6.044571e-03 6.264351e-03 6.521152e-03 6.762987e-03 6.946342e-03 7.039609e-03 7.009725e-03 6.846050e-03 6.585501e-03 6.310042e-03 6.099161e-03 5.990936e-03 5.974474e-03 6.021208e-03 6.102109e-03 6.191581e-03 6.278879e-03 6.372633e-03 6.507517e-03 6.735723e-03 7.091359e-03 7.550079e-03 7.991543e-03 8.250561e-03 8.247904e-03 8.066342e-03 7.852556e-03 7.669650e-03 7.484414e-03 7.255439e-03 7.005591e-03 6.783504e-03 6.620635e-03 6.522074e-03 6.480007e-03 6.480011e-03 6.504643e-03 6.533874e-03 6.555568e-03 6.573049e-03 6.606794e-03 6.691335e-03 6.861169e-03 7.119803e-03 7.414118e-03 7.664893e-03 7.835538e-03 7.945251e-03 7.998472e-03 7.947500e-03 7.750991e-03 7.456630e-03 7.178225e-03 7.004543e-03 6.948744e-03 6.971842e-03 7.023267e-03 7.069058e-03 7.099641e-03 7.132318e-03 7.196985e-03 7.318586e-03 7.485053e-03 7.646770e-03 7.750958e-03 7.773987e-03 7.739799e-03 7.695572e-03 7.681366e-03 7.715330e-03 7.796783e-03 7.937115e-03 8.171577e-03 8.524392e-03 8.944687e-03 9.279085e-03 9.351064e-03 9.108100e-03 8.690043e-03 8.329460e-03 8.179937e-03 8.228309e-03 8.349569e-03 8.402313e-03 8.324038e-03 8.146049e-03 7.958258e-03 7.834423e-03 7.791503e-03 7.796999e-03 7.813054e-03 7.819258e-03 7.821458e-03 7.829615e-03 7.851808e-03 7.892544e-03 7.971509e-03 8.120939e-03 8.384394e-03 8.789833e-03 9.322087e-03 9.913954e-03 1.049519e-02 1.105004e-02 1.156739e-02 1.193451e-02 1.196500e-02 1.163429e-02 1.123862e-02 1.128974e-02 1.227257e-02 1.447816e-02 1.781692e-02 2.152883e-02 2.417632e-02 2.437566e-02 2.185923e-02 1.776329e-02 1.375950e-02 1.096261e-02 9.547665e-03 9.101787e-03 9.107553e-03 9.197377e-03 9.206326e-03 9.109596e-03 8.951936e-03 8.778772e-03 8.628693e-03 8.520672e-03 8.465493e-03 8.481066e-03 8.605424e-03 8.878719e-03 9.280278e-03 9.691783e-03 9.963601e-03 1.001423e-02 9.895438e-03 9.720786e-03 9.575414e-03 9.469335e-03 9.361727e-03 9.215063e-03 9.018303e-03 8.803249e-03 8.618085e-03 8.496982e-03 8.450200e-03 8.454402e-03 8.494890e-03 8.552627e-03 8.624314e-03 8.711399e-03 8.817429e-03 8.955723e-03 9.137725e-03 9.365142e-03 9.616338e-03 9.831066e-03 9.943411e-03 9.926737e-03 9.842315e-03 9.836002e-03 1.007139e-02 1.062463e-02 1.142259e-02 1.222479e-02 1.271087e-02 1.264998e-02 1.204179e-02 1.112659e-02 1.022410e-02 9.563205e-03 9.191798e-03 9.049342e-03 9.031229e-03 9.066161e-03 9.114763e-03 9.164127e-03 9.215918e-03 9.261403e-03 9.289986e-03 9.289826e-03 9.252212e-03 9.187663e-03 9.099782e-03 8.973934e-03 8.785811e-03 8.525656e-03 8.230094e-03 7.970064e-03 7.790824e-03 7.660265e-03 7.486123e-03 7.195151e-03 6.803888e-03 6.404371e-03 6.087342e-03 5.891493e-03 5.796233e-03 5.765947e-03 5.767137e-03 5.786563e-03 5.818654e-03 5.862529e-03 5.915350e-03 5.972791e-03 6.021984e-03 6.052003e-03 6.060679e-03 6.050602e-03 6.036075e-03 6.024845e-03 6.025092e-03 6.034687e-03 6.055092e-03 6.082279e-03 6.119683e-03 6.167445e-03 6.231837e-03 6.308338e-03 6.388516e-03 6.450586e-03 6.482095e-03 6.480901e-03 6.465781e-03 6.462672e-03 6.496057e-03 6.575965e-03 6.707695e-03 6.908451e-03 7.206637e-03 7.629426e-03 8.158073e-03 8.697091e-03 9.084200e-03 9.176737e-03 8.929303e-03 8.443873e-03 7.891644e-03 7.417550e-03 7.087449e-03 6.892965e-03 6.796737e-03 6.769469e-03 6.798478e-03 6.884631e-03 7.025107e-03 7.204563e-03 7.390703e-03 7.551138e-03 7.665440e-03 7.739227e-03 7.804564e-03 7.931507e-03 8.224015e-03 8.781880e-03 9.627121e-03 1.060905e-02 1.141505e-02 1.170576e-02 1.133491e-02 1.044077e-02 9.362677e-03 8.429773e-03 7.806642e-03 7.486567e-03 7.367232e-03 7.338732e-03 7.335004e-03 7.332558e-03 7.336830e-03 7.357789e-03 7.400137e-03 7.467051e-03 7.576157e-03 7.742446e-03 7.978759e-03 8.251280e-03 8.492948e-03 8.614898e-03 8.562320e-03 8.350205e-03 8.048290e-03 7.747178e-03 7.509091e-03 7.359866e-03 7.294371e-03 7.306060e-03 7.400089e-03 7.599631e-03 7.931230e-03 8.383187e-03 8.881570e-03 9.288481e-03 9.466657e-03 9.352129e-03 8.998252e-03 8.541738e-03 8.128489e-03 7.849624e-03 7.722809e-03 7.726408e-03 7.823200e-03 7.969951e-03 8.130990e-03 8.268412e-03 8.357954e-03 8.392502e-03 8.385573e-03 8.357848e-03 8.325594e-03 8.289684e-03 8.245600e-03 8.190747e-03 8.131386e-03 8.074046e-03 8.024807e-03 7.986093e-03 7.957800e-03 7.943003e-03 7.940398e-03 7.953744e-03 7.980394e-03 8.022810e-03 8.085300e-03 8.190187e-03 8.355775e-03 8.587285e-03 8.846182e-03 9.057591e-03 9.144037e-03 9.071055e-03 8.873225e-03 8.634889e-03 8.451520e-03 8.375268e-03 8.412382e-03 8.516727e-03 8.628245e-03 8.689136e-03 8.680711e-03 8.614601e-03 8.532023e-03 8.466884e-03 8.448152e-03 8.491120e-03 8.627092e-03 8.896917e-03 9.342196e-03 9.967363e-03 1.069297e-02 1.136065e-02 1.177822e-02 1.181036e-02 1.145814e-02 1.086600e-02 1.026792e-02 9.866484e-03 9.761338e-03 9.918990e-03 1.020940e-02 1.047281e-02 1.058694e-02 1.050074e-02 1.023887e-02 9.875420e-03 9.483433e-03 9.132414e-03 8.858258e-03 8.677235e-03 8.588632e-03 8.583467e-03 8.643741e-03 8.746552e-03 8.859709e-03 8.949645e-03 8.988934e-03 8.972578e-03 8.911045e-03 8.832389e-03 8.757565e-03 8.697599e-03 8.653221e-03 8.617870e-03 8.586279e-03 8.569986e-03 8.583211e-03 8.719233e-03 9.207525e-03 1.062143e-02 1.419462e-02 2.214402e-02 3.770935e-02 6.427223e-02 1.034250e-01 1.522274e-01 2.020126e-01 2.401212e-01 2.546657e-01 2.408210e-01 2.030842e-01 1.531684e-01 1.039175e-01 6.439663e-02 3.767736e-02 2.221044e-02 1.455877e-02 1.138315e-02 1.043629e-02 1.047867e-02 1.093760e-02 1.154069e-02 1.211831e-02 1.255923e-02 1.278097e-02 1.277340e-02 1.257206e-02 1.223904e-02 1.182061e-02 1.135891e-02 1.089067e-02 1.044934e-02 1.007160e-02 9.773894e-03 9.557352e-03 9.407465e-03 9.308502e-03 9.247796e-03 9.220224e-03 9.216198e-03 9.232550e-03 9.262607e-03 9.306050e-03 9.358275e-03 9.420429e-03 9.487406e-03 9.551618e-03 9.603663e-03 9.640768e-03 9.659521e-03 9.667772e-03 9.670511e-03 9.670733e-03 9.666483e-03 9.652995e-03 9.627097e-03 9.589981e-03 9.546704e-03 9.507648e-03 9.475701e-03 9.458359e-03 9.454313e-03 9.463085e-03 9.485303e-03 9.514514e-03 9.551204e-03 9.589494e-03 9.632528e-03 9.678601e-03 9.728821e-03 9.786466e-03 9.849575e-03 9.923663e-03 1.002056e-02 1.015584e-02 1.034569e-02 1.059126e-02 1.088890e-02 1.122906e-02 1.163989e-02 1.220040e-02 1.305206e-02 1.434655e-02 1.614083e-02 1.829723e-02 2.043490e-02 2.202794e-02 2.260378e-02 2.194803e-02 2.023529e-02 1.791709e-02 1.554057e-02 1.353751e-02 1.211449e-02 1.126171e-02 1.084967e-02 1.071754e-02 1.072134e-02 1.076356e-02 1.078020e-02 1.074024e-02 1.063587e-02 1.047436e-02 1.027949e-02 1.007939e-02 9.896940e-03 9.752640e-03 9.656772e-03 9.622029e-03 9.677114e-03 9.884506e-03 1.036678e-02 1.132304e-02 1.301874e-02 1.572851e-02 1.963392e-02 2.467708e-02 3.048330e-02 3.633517e-02 4.129895e-02 4.444638e-02 4.514418e-02 4.322224e-02 3.905083e-02 3.345447e-02 2.743284e-02 2.190289e-02 1.746324e-02 1.433912e-02 1.246593e-02 1.163392e-02 1.165292e-02 1.242792e-02 1.396798e-02 1.627866e-02 1.927225e-02 2.260743e-02 2.573708e-02 2.798336e-02 2.880193e-02 2.798503e-02 2.576185e-02 2.269398e-02 1.946162e-02 1.659981e-02 1.439454e-02 1.288121e-02 1.192568e-02 1.135092e-02 1.100567e-02 1.079059e-02 1.065809e-02 1.058231e-02 1.055499e-02 1.056909e-02 1.061639e-02 1.070065e-02 1.083378e-02 1.103857e-02 1.135201e-02 1.181529e-02 1.246927e-02 1.333622e-02 1.442232e-02 1.571687e-02 1.717508e-02 1.872945e-02 2.025743e-02 2.160664e-02 2.258543e-02 2.305011e-02 2.296837e-02 2.246802e-02 2.185950e-02 2.156935e-02 2.205043e-02 2.366674e-02 2.663073e-02 3.099074e-02 3.659463e-02 4.313043e-02 5.011936e-02 5.705209e-02 6.350545e-02 6.933881e-02 7.462602e-02 7.951541e-02 8.386774e-02 8.705852e-02 8.812220e-02 8.608530e-02 8.057920e-02 7.212315e-02 6.200098e-02 5.183828e-02 4.302292e-02 3.632680e-02 3.185735e-02 2.922257e-02 2.780097e-02 2.696371e-02 2.620352e-02 2.522581e-02 2.391878e-02 2.234683e-02 2.069617e-02 1.920846e-02 1.813660e-02 1.768150e-02 1.798350e-02 1.905175e-02 2.077110e-02 2.285017e-02 2.486769e-02 2.638627e-02 2.706645e-02 2.679800e-02 2.575571e-02 2.432130e-02 2.292937e-02 2.191301e-02 2.141833e-02 2.137604e-02 2.159672e-02 2.188260e-02 2.212238e-02 2.231150e-02 2.252390e-02 2.287107e-02 2.340410e-02 2.411471e-02 2.491306e-02 2.569328e-02 2.638630e-02 2.703815e-02 2.781650e-02 2.897818e-02 3.080276e-02 3.348133e-02 3.704673e-02 4.134780e-02 4.607457e-02 5.080113e-02 5.502884e-02 5.829669e-02 6.026766e-02 6.084706e-02 6.028913e-02 5.921290e-02 5.846838e-02 5.896077e-02 6.140203e-02 6.609754e-02 7.288815e-02 8.108328e-02 8.966892e-02 9.738345e-02 1.031030e-01 1.060381e-01 1.059228e-01 1.030969e-01 9.826993e-02 9.226640e-02 8.585557e-02 7.950760e-02 7.355406e-02 6.811582e-02 6.329288e-02 5.917084e-02 5.583515e-02 5.335292e-02 5.179087e-02 5.118418e-02 5.155087e-02 5.279135e-02 5.467709e-02 5.684290e-02 5.883380e-02 6.019994e-02 6.062364e-02 5.996716e-02 5.828461e-02 5.576381e-02 5.271535e-02 4.949826e-02 4.649764e-02 4.407271e-02 4.251065e-02 4.197865e-02 4.249137e-02 4.395301e-02 4.615700e-02 4.889511e-02 5.198020e-02 5.525381e-02 5.861232e-02 6.196297e-02 6.516752e-02 6.810299e-02 7.067532e-02 7.279757e-02 7.448104e-02 7.577939e-02 7.679425e-02 7.763839e-02 7.845919e-02 7.948466e-02 8.104932e-02 8.354925e-02 8.736853e-02 9.268838e-02 9.936303e-02 1.068779e-01 1.142630e-01 1.204376e-01 1.242591e-01 1.249210e-01 1.220776e-01 1.159947e-01 1.075758e-01 9.804909e-02 8.882300e-02 8.112933e-02 7.585522e-02 7.340587e-02 7.375047e-02 7.644131e-02 8.079931e-02 8.595793e-02 9.095366e-02 9.488727e-02 9.699097e-02 9.674098e-02 9.399543e-02 8.898392e-02 8.230445e-02 7.476406e-02 6.734322e-02 6.101417e-02 5.659117e-02 5.470413e-02 5.565449e-02 5.943833e-02 6.569882e-02 7.376368e-02 8.276148e-02 9.166206e-02 9.946743e-02 1.053039e-01 1.085663e-01 1.089950e-01 1.066739e-01 1.021001e-01 9.597346e-02 8.916452e-02 8.247627e-02 7.661007e-02 7.207823e-02 6.924298e-02 6.841385e-02 7.001486e-02 7.458686e-02 8.279667e-02 9.526456e-02 1.122789e-01 1.335524e-01 1.579586e-01 1.835650e-01 2.077725e-01 2.279366e-01 2.416940e-01 2.477983e-01 2.460993e-01 2.376866e-01 2.243726e-01 2.082946e-01 1.912711e-01 1.746916e-01 1.592049e-01 1.451409e-01 1.325140e-01 1.213465e-01 1.117753e-01 1.039859e-01 9.818824e-02 9.450686e-02 9.286352e-02 9.297723e-02 9.443232e-02 9.668450e-02 9.923623e-02 1.016735e-01 1.037549e-01 1.054305e-01 1.067605e-01 1.079530e-01 1.091749e-01 1.105168e-01 1.120283e-01 1.136141e-01 1.152258e-01 1.168152e-01 1.184460e-01 1.201691e-01 1.221692e-01 1.245049e-01 1.272931e-01 1.305908e-01 1.344418e-01 1.389601e-01 1.441787e-01 1.501762e-01 1.570036e-01 1.647111e-01 1.735077e-01 1.837997e-01 1.961705e-01 2.114694e-01 2.302625e-01 2.526713e-01 2.779522e-01 3.042703e-01 3.287926e-01 3.483262e-01 3.596431e-01 3.606936e-01 3.506992e-01 3.305958e-01 3.025668e-01 2.697515e-01 2.351260e-01 2.015184e-01 1.707780e-01 1.440992e-01 1.220727e-01 1.048168e-01 9.230843e-02 8.430600e-02 8.038392e-02 7.998773e-02 8.233368e-02 8.650888e-02 9.155479e-02 9.656824e-02 1.008562e-01 1.039302e-01 1.056583e-01 1.061924e-01 1.059383e-01 1.054593e-01 1.053675e-01 1.062400e-01 1.084604e-01 1.122144e-01 1.174928e-01 1.240857e-01 1.316004e-01 1.395745e-01 1.474703e-01 1.548378e-01 1.614435e-01 1.674124e-01 1.733725e-01 1.804985e-01 1.902537e-01 2.041167e-01 2.230396e-01 2.469940e-01 2.745950e-01 3.031183e-01 3.290083e-01 3.485058e-01 3.587167e-01 3.582461e-01 3.476707e-01 3.293439e-01 3.070100e-01 2.846043e-01 2.655499e-01 2.521942e-01 2.451810e-01 2.436490e-01 2.456030e-01 2.483827e-01 2.492615e-01 2.461215e-01 2.377948e-01 2.241567e-01 2.061424e-01 1.856401e-01 1.646545e-01 1.453156e-01 1.292721e-01 1.176104e-01 1.105454e-01 1.077656e-01 1.083388e-01 1.110207e-01 1.145134e-01 1.175336e-01 1.191903e-01 1.189004e-01 1.166226e-01 1.126673e-01 1.077661e-01 1.028272e-01 9.889286e-02 9.700955e-02 9.816533e-02 1.033018e-01 1.129898e-01 1.276777e-01 1.471718e-01 1.707195e-01 1.969541e-01 2.236596e-01 2.484531e-01 2.683898e-01 2.813195e-01 2.853395e-01 2.798837e-01 2.653411e-01 2.433606e-01 2.161036e-01 1.863348e-01 1.565664e-01 1.289415e-01 1.049817e-01 8.543099e-02 7.049588e-02 5.979445e-02 5.282714e-02 4.890480e-02 4.739406e-02 4.781241e-02 4.976286e-02 5.299822e-02 5.730378e-02 6.254785e-02 6.857280e-02 7.518724e-02 8.214842e-02 8.919369e-02 9.595639e-02 1.020692e-01 1.071429e-01 1.107950e-01 1.127780e-01 1.128613e-01 1.110425e-01 1.074301e-01 1.023795e-01 9.630956e-02 8.975954e-02 8.326138e-02 7.724594e-02 7.202885e-02 6.777212e-02 6.449261e-02 6.206645e-02 6.034909e-02 5.920837e-02 5.850766e-02 5.823173e-02 5.835186e-02 5.891673e-02 5.990039e-02 6.128461e-02 6.290820e-02 6.458214e-02 6.600372e-02 6.690381e-02 6.699518e-02 6.615639e-02 6.434778e-02 6.172847e-02 5.860685e-02 5.541298e-02 5.264110e-02 5.078751e-02 5.029750e-02 5.148596e-02 5.454615e-02 5.943983e-02 6.592286e-02 7.354665e-02 8.166471e-02 8.951546e-02 9.630886e-02 1.012803e-01 1.038614e-01 1.037456e-01 1.009227e-01 9.569203e-02 8.855784e-02 8.025743e-02 7.152107e-02 6.303082e-02 5.538093e-02 4.894742e-02 4.392801e-02 4.033736e-02 3.804007e-02 3.683968e-02 3.643093e-02 3.656283e-02 3.697409e-02 3.747085e-02 3.793069e-02 3.830095e-02 3.859439e-02 3.889665e-02 3.930958e-02 3.994867e-02 4.093436e-02 4.232474e-02 4.418167e-02 4.647699e-02 4.916558e-02 5.215084e-02 5.527531e-02 5.837925e-02 6.122586e-02 6.361560e-02 6.530013e-02 6.612353e-02 6.594415e-02 6.472693e-02 6.250833e-02 5.946854e-02 5.579207e-02 5.177144e-02 4.768141e-02 4.378575e-02 4.028568e-02 3.731591e-02 3.496712e-02 3.322086e-02 3.202561e-02 3.130305e-02 3.093456e-02 3.082122e-02 3.085219e-02 3.094646e-02 3.104529e-02 3.110070e-02 3.109998e-02 3.103529e-02 3.090842e-02 3.074367e-02 3.055420e-02 3.036433e-02 3.021699e-02 3.015679e-02 3.024013e-02 3.054404e-02 3.114133e-02 3.209000e-02 3.344345e-02 3.520316e-02 3.733067e-02 3.972623e-02 4.224684e-02 4.473279e-02 4.699087e-02 4.883702e-02 5.014945e-02 5.085352e-02 5.092090e-02 5.042225e-02 4.947706e-02 4.820428e-02 4.679896e-02 4.538237e-02 4.408268e-02 4.300880e-02 4.220938e-02 4.173631e-02 4.162062e-02 4.188538e-02 4.257171e-02 4.371209e-02 4.531397e-02 4.738862e-02 4.988634e-02 5.271478e-02 5.572059e-02 5.869588e-02 6.139513e-02 6.355844e-02 6.493702e-02 6.535832e-02 6.469291e-02 6.293522e-02 6.018234e-02 5.663551e-02 5.256281e-02 4.827388e-02 4.408297e-02 4.026834e-02 3.705360e-02 3.456686e-02 3.288482e-02 3.196778e-02 3.174118e-02 3.205968e-02 3.276638e-02 3.369690e-02 3.469802e-02 3.564636e-02 3.646248e-02 3.709852e-02 3.757463e-02 3.792264e-02 3.821936e-02 3.852530e-02 3.891682e-02 3.941520e-02 4.001717e-02 4.067219e-02 4.129720e-02 4.177534e-02 4.199985e-02 4.187900e-02 4.136770e-02 4.044853e-02 3.918063e-02 3.767019e-02 3.604639e-02 3.447041e-02 3.310300e-02 3.207253e-02 3.149343e-02 3.143810e-02 3.194144e-02 3.300282e-02 3.461311e-02 3.671442e-02 3.927266e-02 4.222345e-02 4.547899e-02 4.898975e-02 5.260495e-02 5.619434e-02 5.963051e-02 6.265535e-02 6.514052e-02 6.685590e-02 6.768617e-02 6.754321e-02 6.640861e-02 6.438580e-02 6.161292e-02 5.831414e-02 5.474285e-02 5.115411e-02 4.774527e-02 4.473486e-02 4.222460e-02 4.028920e-02 3.891609e-02 3.810760e-02 3.777595e-02 3.784945e-02 3.824813e-02 3.887727e-02 3.966798e-02 4.056894e-02 4.152549e-02 4.253109e-02 4.360239e-02 4.475898e-02 4.605315e-02 4.754849e-02 4.929098e-02 5.130042e-02 5.359927e-02 5.612425e-02 5.878958e-02 6.148764e-02 6.401849e-02 6.621020e-02 6.791528e-02 6.893019e-02 6.919879e-02 6.866730e-02 6.736690e-02 6.542139e-02 6.297150e-02 6.021884e-02 5.739036e-02 5.464317e-02 5.216888e-02 5.008290e-02 4.843250e-02 4.723656e-02 4.648117e-02 4.609428e-02 4.601678e-02 4.614851e-02 4.643912e-02 4.679181e-02 4.716042e-02 4.748059e-02 4.768415e-02 4.774177e-02 4.761794e-02 4.726058e-02 4.668291e-02 4.586734e-02 4.485467e-02 4.367090e-02 4.239433e-02 4.107726e-02 3.978686e-02 3.860280e-02 3.757199e-02 3.672638e-02 3.609958e-02 3.567433e-02 3.544063e-02 3.536222e-02 3.539432e-02 3.548363e-02 3.558963e-02 3.567381e-02 3.570129e-02 3.566108e-02 3.555542e-02 3.539691e-02 3.520093e-02 3.498723e-02 3.477743e-02 3.458125e-02 3.439154e-02 3.419948e-02 3.398778e-02 3.373783e-02 3.343020e-02 3.306127e-02 3.262984e-02 3.215880e-02 3.168054e-02 3.123889e-02 3.087650e-02 3.062948e-02 3.053585e-02 3.061738e-02 3.088107e-02 3.130593e-02 3.186111e-02 3.250693e-02 3.319563e-02 3.386579e-02 3.447651e-02 3.498825e-02 3.538908e-02 3.568423e-02 3.592072e-02 3.616339e-02 3.652279e-02 3.712539e-02 3.811113e-02 3.962206e-02 4.180121e-02 4.474155e-02 4.847582e-02 5.298829e-02 5.818734e-02 6.388614e-02 6.982973e-02 7.569645e-02 8.114515e-02 8.584672e-02 8.947176e-02 9.178067e-02 9.262960e-02 9.198175e-02 8.990769e-02 8.659013e-02 8.230025e-02 7.735087e-02 7.204840e-02 6.671435e-02 6.159553e-02 5.686951e-02 5.265192e-02 4.899383e-02 4.586882e-02 4.320647e-02 4.091783e-02 3.890569e-02 3.707888e-02 3.537948e-02 3.375115e-02 3.218054e-02 3.066824e-02 2.922474e-02 2.787920e-02 2.665301e-02 2.556583e-02 2.463348e-02 2.386296e-02 2.325097e-02 2.278499e-02 2.246181e-02 2.225277e-02 2.214239e-02 2.210993e-02 2.214231e-02 2.222133e-02 2.233022e-02 2.245948e-02 2.259236e-02 2.272211e-02 2.283235e-02 2.291221e-02 2.295770e-02 2.295613e-02 2.291455e-02 2.282823e-02 2.270608e-02 2.256065e-02 2.240296e-02 2.225082e-02 2.211109e-02 2.201041e-02 2.194591e-02 2.192732e-02 2.196518e-02 2.204658e-02 2.217952e-02 2.235100e-02 2.256584e-02 2.281594e-02 2.310099e-02 2.341673e-02 2.376463e-02 2.413798e-02 2.454158e-02 2.495682e-02 2.538180e-02 2.579834e-02 2.619184e-02 2.654395e-02 2.683530e-02 2.705168e-02 2.717242e-02 2.719581e-02 2.711320e-02 2.693029e-02 2.666062e-02 2.631481e-02 2.592059e-02 2.549502e-02 2.506432e-02 2.465195e-02 2.427426e-02 2.394634e-02 2.366965e-02 2.344865e-02 2.327626e-02 2.313961e-02 2.303096e-02 2.292905e-02 2.282413e-02 2.269937e-02 2.254736e-02 2.235759e-02 2.212728e-02 2.185309e-02 2.153510e-02 2.117725e-02 2.078748e-02 2.036304e-02 1.991628e-02 1.945864e-02 1.899108e-02 1.852865e-02 1.807907e-02 1.765315e-02 1.725927e-02 1.690996e-02 1.661111e-02 1.637345e-02 1.620303e-02 1.610521e-02 1.608403e-02 1.614726e-02 1.629115e-02 1.651933e-02 1.682942e-02 1.721620e-02 1.767145e-02 1.818409e-02 1.873996e-02 1.932029e-02 1.990362e-02 2.046674e-02 2.098541e-02 2.143616e-02 2.179763e-02 2.205243e-02 2.218844e-02 2.219966e-02 2.208719e-02 2.185894e-02 2.153037e-02 2.111501e-02 2.064161e-02 2.012892e-02 1.960033e-02 1.908431e-02 1.859033e-02 1.813943e-02 1.774217e-02 1.740266e-02 1.712425e-02 1.690593e-02 1.674360e-02 1.663254e-02 1.656290e-02 1.652844e-02 1.652289e-02 1.653700e-02 1.656938e-02 1.660892e-02 1.665938e-02 1.671581e-02 1.678099e-02 1.685818e-02 1.694972e-02 1.706476e-02 1.720658e-02 1.738410e-02 1.760905e-02 1.788510e-02 1.821960e-02 1.861714e-02 1.908196e-02 1.961233e-02 2.020505e-02 2.085679e-02 2.155388e-02 2.229246e-02 2.304876e-02 2.381130e-02 2.456826e-02 2.528897e-02 2.596204e-02 2.657348e-02 2.709075e-02 2.750537e-02 2.779916e-02 2.796160e-02 2.797855e-02 2.784465e-02 2.756269e-02 2.713188e-02 2.656155e-02 2.587004e-02 2.507397e-02 2.419507e-02 2.326148e-02 2.230017e-02 2.133542e-02 2.038859e-02 1.948361e-02 1.864170e-02 1.786804e-02 1.717582e-02 1.656500e-02 1.603984e-02 1.559515e-02 1.522430e-02 1.492208e-02 1.467970e-02 1.448988e-02 1.434165e-02 1.423126e-02 1.415606e-02 1.410312e-02 1.407127e-02 1.405910e-02 1.406222e-02 1.408027e-02 1.411371e-02 1.416499e-02 1.423629e-02 1.433189e-02 1.446033e-02 1.463161e-02 1.485426e-02 1.515670e-02 1.554000e-02 1.602896e-02 1.665398e-02 1.742626e-02 1.838020e-02 1.954115e-02 2.091845e-02 2.255020e-02 2.443332e-02 2.657689e-02 2.900821e-02 3.168219e-02 3.461963e-02 3.777942e-02 4.115019e-02 4.471676e-02 4.840804e-02 5.224307e-02 5.616073e-02 6.014201e-02 6.415134e-02 6.816690e-02 7.215945e-02 7.606728e-02 7.985399e-02 8.350006e-02 8.684655e-02 8.989569e-02 9.252098e-02 9.463299e-02 9.612498e-02 9.695093e-02 9.696107e-02 9.620063e-02 9.455725e-02 9.210631e-02 8.883255e-02 8.486944e-02 8.026683e-02 7.517815e-02 6.976228e-02 6.410280e-02 5.845976e-02 5.284063e-02 4.749144e-02 4.243108e-02 3.776897e-02 3.356740e-02 2.981168e-02 2.657098e-02 2.375932e-02 2.141889e-02 1.945891e-02 1.786830e-02 1.657597e-02 1.555893e-02 1.475485e-02 1.413580e-02 1.365594e-02 1.329355e-02 1.301895e-02 1.281408e-02 1.266100e-02 1.254731e-02 1.246392e-02 1.240286e-02 1.236010e-02 1.232943e-02 1.230807e-02 1.229154e-02 1.228845e-02 1.228984e-02 1.229818e-02 1.231126e-02 1.233306e-02 1.236492e-02 1.240463e-02 1.245517e-02 1.251713e-02 1.259587e-02 1.269494e-02 1.281607e-02 1.296771e-02 1.315374e-02 1.338259e-02 1.366462e-02 1.400868e-02 1.442803e-02 1.493979e-02 1.555508e-02 1.629677e-02 1.718104e-02 1.822841e-02 1.945954e-02 2.088977e-02 2.253628e-02 2.441058e-02 2.651455e-02 2.884533e-02 3.138914e-02 3.411918e-02 3.700255e-02 3.998591e-02 4.301087e-02 4.600924e-02 4.889948e-02 5.159426e-02 5.401164e-02 5.607701e-02 5.769854e-02 5.883249e-02 5.942576e-02 5.944755e-02 5.889928e-02 5.778960e-02 5.616545e-02 5.406386e-02 5.157664e-02 4.876849e-02 4.573561e-02 4.257141e-02 3.936114e-02 3.618891e-02 3.311545e-02 3.022208e-02 2.752819e-02 2.508773e-02 2.290483e-02 2.099642e-02 1.934851e-02 1.795328e-02 1.678384e-02 1.582695e-02 1.504624e-02 1.441955e-02 1.392080e-02 1.353068e-02 1.321910e-02 1.297647e-02 1.278443e-02 1.262865e-02 1.250108e-02 1.239614e-02 1.230685e-02 1.222764e-02 1.215747e-02 1.209614e-02 1.204107e-02 1.198868e-02 1.194064e-02 1.190495e-02 1.186972e-02 1.184364e-02 1.181967e-02 1.180051e-02 1.178789e-02 1.178049e-02 1.177620e-02 1.177886e-02 1.178632e-02 1.179729e-02 1.181336e-02 1.183843e-02 1.186443e-02 1.189483e-02 1.193193e-02 1.197019e-02 1.201152e-02 1.205844e-02 1.210504e-02 1.215458e-02 1.220413e-02 1.225677e-02 1.231086e-02 1.236693e-02 1.242454e-02 1.248748e-02 1.255648e-02 1.263144e-02 1.272114e-02 1.282052e-02 1.294014e-02 1.307768e-02 1.324432e-02 1.343408e-02 1.365712e-02 1.391160e-02 1.419935e-02 1.451954e-02 1.487013e-02 1.525203e-02 1.565056e-02 1.607310e-02 1.649381e-02 1.692009e-02 1.732486e-02 1.771054e-02 1.806198e-02 1.836046e-02 1.860792e-02 1.878578e-02 1.889429e-02 1.892450e-02 1.887455e-02 1.874986e-02 1.855037e-02 1.827668e-02 1.794748e-02 1.756444e-02 1.713763e-02 1.668436e-02 1.621408e-02 1.573260e-02 1.525842e-02 1.479677e-02 1.435342e-02 1.394325e-02 1.355759e-02 1.321184e-02 1.290219e-02 1.262562e-02 1.238569e-02 1.217837e-02 1.200434e-02 1.185411e-02 1.173068e-02 1.162843e-02 1.154605e-02 1.147847e-02 1.142785e-02 1.138854e-02 1.136180e-02 1.134647e-02 1.134178e-02 1.134942e-02 1.136743e-02 1.139552e-02 1.143911e-02 1.149144e-02 1.155748e-02 1.163610e-02 1.172520e-02 1.182570e-02 1.193545e-02 1.205440e-02 1.218049e-02 1.230787e-02 1.243618e-02 1.256665e-02 1.268922e-02 1.280382e-02 1.291074e-02 1.300094e-02 1.307624e-02 1.313442e-02 1.317457e-02 1.319419e-02 1.319611e-02 1.317838e-02 1.314342e-02 1.309606e-02 1.303593e-02 1.296626e-02 1.289557e-02 1.282111e-02 1.275243e-02 1.268939e-02 1.263614e-02 1.260112e-02 1.257921e-02 1.258060e-02 1.260184e-02 1.264916e-02 1.271863e-02 1.281179e-02 1.292856e-02 1.306636e-02 1.323029e-02 1.340884e-02 1.360609e-02 1.381751e-02 1.403719e-02 1.426505e-02 1.449561e-02 1.472740e-02 1.495387e-02 1.517561e-02 1.538488e-02 1.558046e-02 1.576540e-02 1.593224e-02 1.608709e-02 1.623172e-02 1.636704e-02 1.650000e-02 1.663798e-02 1.678575e-02 1.695549e-02 1.715278e-02 1.739780e-02 1.769201e-02 1.805059e-02 1.848474e-02 1.900364e-02 1.961914e-02 2.033193e-02 2.115216e-02 2.208495e-02 2.312920e-02 2.427937e-02 2.553037e-02 2.688110e-02 2.831550e-02 2.982059e-02 3.138006e-02 3.298398e-02 3.459661e-02 3.620942e-02 3.778851e-02 3.932116e-02 4.077209e-02 4.211444e-02 4.333187e-02 4.439691e-02 4.528596e-02 4.597996e-02 4.646552e-02 4.672812e-02 4.675386e-02 4.655365e-02 4.610557e-02 4.543627e-02 4.453855e-02 4.344463e-02 4.215353e-02 4.070328e-02 3.911645e-02 3.741483e-02 3.563528e-02 3.380293e-02 3.194850e-02 3.008839e-02 2.825892e-02 2.648267e-02 2.478329e-02 2.316443e-02 2.165051e-02 2.024069e-02 1.894312e-02 1.776342e-02 1.669457e-02 1.574322e-02 1.489915e-02 1.415265e-02 1.350258e-02 1.293480e-02 1.244454e-02 1.202494e-02 1.166755e-02 1.136578e-02 1.110976e-02 1.089647e-02 1.072091e-02 1.057920e-02 1.046730e-02 1.037903e-02 1.031607e-02 1.027290e-02 1.025003e-02 1.024122e-02 1.025165e-02 1.027635e-02 1.031259e-02 1.036325e-02 1.042395e-02 1.049590e-02 1.057399e-02 1.066391e-02 1.075971e-02 1.086228e-02 1.096802e-02 1.107674e-02 1.118333e-02 1.128782e-02 1.138905e-02 1.148184e-02 1.156307e-02 1.162847e-02 1.167670e-02 1.170516e-02 1.170694e-02 1.167800e-02 1.161672e-02 1.152223e-02 1.138944e-02 1.121664e-02 1.100222e-02 1.074785e-02 1.045185e-02 1.011713e-02 9.743785e-03 ''') ImportString(u'flux_model_1_0_scaled(numeric)',''' 3.886337e-03 5.093587e-03 5.886402e-03 6.522754e-03 7.635550e-03 9.829557e-03 1.294884e-02 1.561008e-02 1.604977e-02 1.391590e-02 1.067616e-02 8.070165e-03 6.695492e-03 6.196370e-03 6.061017e-03 6.012004e-03 5.976988e-03 5.965375e-03 5.985896e-03 6.020189e-03 6.039592e-03 6.030606e-03 6.004598e-03 5.983376e-03 5.988667e-03 6.030895e-03 6.109263e-03 6.213361e-03 6.311629e-03 6.364734e-03 6.354499e-03 6.311092e-03 6.282620e-03 6.299068e-03 6.364106e-03 6.481094e-03 6.671134e-03 6.947059e-03 7.258679e-03 7.484013e-03 7.519017e-03 7.382242e-03 7.195698e-03 7.061579e-03 6.979010e-03 6.889463e-03 6.767129e-03 6.655864e-03 6.607857e-03 6.631299e-03 6.688647e-03 6.736098e-03 6.758044e-03 6.778751e-03 6.841437e-03 6.980910e-03 7.208014e-03 7.487961e-03 7.741094e-03 7.877539e-03 7.852586e-03 7.692083e-03 7.466596e-03 7.263413e-03 7.149476e-03 7.146096e-03 7.217741e-03 7.292732e-03 7.315713e-03 7.292052e-03 7.272794e-03 7.308546e-03 7.418625e-03 7.622750e-03 7.995253e-03 8.697547e-03 9.903479e-03 1.155798e-02 1.315355e-02 1.391688e-02 1.345919e-02 1.215346e-02 1.072318e-02 9.635538e-03 8.976653e-03 8.655692e-03 8.549337e-03 8.519863e-03 8.457147e-03 8.345066e-03 8.236178e-03 8.173887e-03 8.156175e-03 8.162610e-03 8.202632e-03 8.306742e-03 8.497850e-03 8.764330e-03 9.048658e-03 9.282205e-03 9.398518e-03 9.364552e-03 9.205209e-03 9.001332e-03 8.830747e-03 8.712923e-03 8.632236e-03 8.564571e-03 8.481066e-03 8.364697e-03 8.240273e-03 8.169953e-03 8.215113e-03 8.401954e-03 8.707448e-03 9.064402e-03 9.400551e-03 9.655415e-03 9.785057e-03 9.743517e-03 9.516009e-03 9.153847e-03 8.770958e-03 8.477834e-03 8.327402e-03 8.304519e-03 8.369479e-03 8.481931e-03 8.606298e-03 8.727641e-03 8.857960e-03 9.045449e-03 9.362656e-03 9.856989e-03 1.049461e-02 1.110824e-02 1.146828e-02 1.146459e-02 1.121222e-02 1.091505e-02 1.066081e-02 1.040334e-02 1.008506e-02 9.737771e-03 9.429071e-03 9.202683e-03 9.065683e-03 9.007210e-03 9.007216e-03 9.041454e-03 9.082085e-03 9.112240e-03 9.136538e-03 9.183444e-03 9.300955e-03 9.537025e-03 9.896526e-03 1.030562e-02 1.065420e-02 1.089140e-02 1.104390e-02 1.111788e-02 1.104702e-02 1.077388e-02 1.036472e-02 9.977732e-03 9.736314e-03 9.658755e-03 9.690860e-03 9.762340e-03 9.825991e-03 9.868500e-03 9.913922e-03 1.000381e-02 1.017283e-02 1.040422e-02 1.062901e-02 1.077383e-02 1.080584e-02 1.075832e-02 1.069685e-02 1.067710e-02 1.072431e-02 1.083753e-02 1.103259e-02 1.135849e-02 1.184890e-02 1.243311e-02 1.289793e-02 1.299798e-02 1.266026e-02 1.207916e-02 1.157795e-02 1.137011e-02 1.143735e-02 1.160590e-02 1.167922e-02 1.157041e-02 1.132301e-02 1.106198e-02 1.088985e-02 1.083019e-02 1.083783e-02 1.086015e-02 1.086877e-02 1.087183e-02 1.088316e-02 1.091401e-02 1.097064e-02 1.108040e-02 1.128811e-02 1.165431e-02 1.221787e-02 1.295770e-02 1.378040e-02 1.458832e-02 1.535955e-02 1.607867e-02 1.658897e-02 1.663135e-02 1.617166e-02 1.562168e-02 1.569273e-02 1.705887e-02 2.012465e-02 2.476552e-02 2.992508e-02 3.360508e-02 3.388217e-02 3.038433e-02 2.469098e-02 1.912571e-02 1.523802e-02 1.327125e-02 1.265148e-02 1.265950e-02 1.278435e-02 1.279679e-02 1.266234e-02 1.244319e-02 1.220249e-02 1.199388e-02 1.184373e-02 1.176704e-02 1.178868e-02 1.196154e-02 1.234142e-02 1.289959e-02 1.347158e-02 1.384941e-02 1.391978e-02 1.375466e-02 1.351189e-02 1.330982e-02 1.316238e-02 1.301280e-02 1.280894e-02 1.253544e-02 1.223652e-02 1.197914e-02 1.181081e-02 1.174578e-02 1.175162e-02 1.180790e-02 1.188815e-02 1.198780e-02 1.210884e-02 1.225623e-02 1.244845e-02 1.270144e-02 1.301755e-02 1.336671e-02 1.366518e-02 1.382134e-02 1.379816e-02 1.368082e-02 1.367204e-02 1.399923e-02 1.476824e-02 1.587741e-02 1.699246e-02 1.766811e-02 1.758348e-02 1.673809e-02 1.546596e-02 1.421149e-02 1.329285e-02 1.277660e-02 1.257859e-02 1.255341e-02 1.260196e-02 1.266952e-02 1.273814e-02 1.281013e-02 1.287335e-02 1.291308e-02 1.291286e-02 1.286057e-02 1.277085e-02 1.264870e-02 1.247377e-02 1.221228e-02 1.185066e-02 1.143983e-02 1.107839e-02 1.082925e-02 1.064777e-02 1.040571e-02 1.000126e-02 9.457405e-03 8.902076e-03 8.461405e-03 8.189175e-03 8.056764e-03 8.014666e-03 8.016321e-03 8.043322e-03 8.087929e-03 8.148916e-03 8.222336e-03 8.302180e-03 8.370558e-03 8.412285e-03 8.424343e-03 8.410336e-03 8.390144e-03 8.374534e-03 8.374878e-03 8.388215e-03 8.416578e-03 8.454367e-03 8.506359e-03 8.572748e-03 8.662253e-03 8.768590e-03 8.880037e-03 8.966314e-03 9.010111e-03 9.008452e-03 8.987435e-03 8.983114e-03 9.029520e-03 9.140591e-03 9.323696e-03 9.602747e-03 1.001723e-02 1.060490e-02 1.133972e-02 1.208896e-02 1.262704e-02 1.275566e-02 1.241173e-02 1.173698e-02 1.096938e-02 1.031039e-02 9.851554e-03 9.581221e-03 9.447464e-03 9.409562e-03 9.449885e-03 9.569637e-03 9.764898e-03 1.001434e-02 1.027308e-02 1.049608e-02 1.065496e-02 1.075753e-02 1.084834e-02 1.102479e-02 1.143138e-02 1.220681e-02 1.338170e-02 1.474658e-02 1.586692e-02 1.627100e-02 1.575552e-02 1.451267e-02 1.301412e-02 1.171738e-02 1.085123e-02 1.040633e-02 1.024045e-02 1.020084e-02 1.019566e-02 1.019226e-02 1.019819e-02 1.022733e-02 1.028619e-02 1.037920e-02 1.053086e-02 1.076200e-02 1.109047e-02 1.146928e-02 1.180520e-02 1.197471e-02 1.190162e-02 1.160678e-02 1.118712e-02 1.076858e-02 1.043764e-02 1.023021e-02 1.013918e-02 1.015542e-02 1.028612e-02 1.056349e-02 1.102441e-02 1.165263e-02 1.234538e-02 1.291099e-02 1.315865e-02 1.299946e-02 1.250757e-02 1.187302e-02 1.129860e-02 1.091098e-02 1.073470e-02 1.073971e-02 1.087425e-02 1.107823e-02 1.130208e-02 1.149309e-02 1.161756e-02 1.166558e-02 1.165595e-02 1.161741e-02 1.157258e-02 1.152266e-02 1.146138e-02 1.138514e-02 1.130263e-02 1.122292e-02 1.115448e-02 1.110067e-02 1.106134e-02 1.104077e-02 1.103715e-02 1.105570e-02 1.109275e-02 1.115171e-02 1.123857e-02 1.138436e-02 1.161453e-02 1.193633e-02 1.229619e-02 1.259005e-02 1.271021e-02 1.260877e-02 1.233378e-02 1.200250e-02 1.174761e-02 1.164162e-02 1.169321e-02 1.183825e-02 1.199326e-02 1.207790e-02 1.206619e-02 1.197429e-02 1.185951e-02 1.176897e-02 1.174293e-02 1.180266e-02 1.199166e-02 1.236671e-02 1.298565e-02 1.385464e-02 1.486323e-02 1.579130e-02 1.637173e-02 1.641640e-02 1.592682e-02 1.510374e-02 1.427241e-02 1.371441e-02 1.356826e-02 1.378740e-02 1.419106e-02 1.455721e-02 1.471585e-02 1.459603e-02 1.423203e-02 1.372683e-02 1.318197e-02 1.269406e-02 1.231298e-02 1.206136e-02 1.193820e-02 1.193102e-02 1.201480e-02 1.215771e-02 1.231500e-02 1.244001e-02 1.249462e-02 1.247188e-02 1.238635e-02 1.227702e-02 1.217302e-02 1.208966e-02 1.202798e-02 1.197884e-02 1.193493e-02 1.191228e-02 1.193066e-02 1.211973e-02 1.279846e-02 1.476379e-02 1.973052e-02 3.078019e-02 5.241600e-02 8.933840e-02 1.437608e-01 2.115961e-01 2.807975e-01 3.337684e-01 3.539854e-01 3.347412e-01 2.822871e-01 2.129040e-01 1.444454e-01 8.951132e-02 5.237153e-02 3.087252e-02 2.023669e-02 1.582258e-02 1.450644e-02 1.456535e-02 1.520326e-02 1.604156e-02 1.684445e-02 1.745733e-02 1.776555e-02 1.775502e-02 1.747516e-02 1.701226e-02 1.643065e-02 1.578889e-02 1.513803e-02 1.452458e-02 1.399952e-02 1.358571e-02 1.328472e-02 1.307638e-02 1.293882e-02 1.285444e-02 1.281611e-02 1.281051e-02 1.283324e-02 1.287502e-02 1.293541e-02 1.300800e-02 1.309440e-02 1.318749e-02 1.327675e-02 1.334909e-02 1.340067e-02 1.342673e-02 1.343820e-02 1.344201e-02 1.344232e-02 1.343641e-02 1.341766e-02 1.338167e-02 1.333007e-02 1.326992e-02 1.321563e-02 1.317123e-02 1.314712e-02 1.314149e-02 1.315369e-02 1.318457e-02 1.322517e-02 1.327617e-02 1.332940e-02 1.338921e-02 1.345326e-02 1.352306e-02 1.360319e-02 1.369091e-02 1.379389e-02 1.392858e-02 1.411661e-02 1.438050e-02 1.472185e-02 1.513557e-02 1.560839e-02 1.617945e-02 1.695856e-02 1.814236e-02 1.994170e-02 2.243576e-02 2.543315e-02 2.840451e-02 3.061884e-02 3.141925e-02 3.050776e-02 2.812706e-02 2.490476e-02 2.160140e-02 1.881714e-02 1.683914e-02 1.565377e-02 1.508104e-02 1.489737e-02 1.490267e-02 1.496135e-02 1.498447e-02 1.492893e-02 1.478386e-02 1.455936e-02 1.428849e-02 1.401035e-02 1.375675e-02 1.355617e-02 1.342291e-02 1.337462e-02 1.345119e-02 1.373946e-02 1.440982e-02 1.573903e-02 1.809605e-02 2.186262e-02 2.729115e-02 3.430114e-02 4.237179e-02 5.050589e-02 5.740554e-02 6.178047e-02 6.275041e-02 6.007892e-02 5.428066e-02 4.650172e-02 3.813165e-02 3.044502e-02 2.427390e-02 1.993138e-02 1.732765e-02 1.617115e-02 1.619756e-02 1.727481e-02 1.941549e-02 2.262734e-02 2.678843e-02 3.142433e-02 3.577454e-02 3.889687e-02 4.003469e-02 3.889919e-02 3.580897e-02 3.154463e-02 2.705166e-02 2.307374e-02 2.000842e-02 1.790489e-02 1.657669e-02 1.577778e-02 1.529788e-02 1.499892e-02 1.481474e-02 1.470940e-02 1.467143e-02 1.469104e-02 1.475679e-02 1.487391e-02 1.505896e-02 1.534361e-02 1.577929e-02 1.642325e-02 1.733228e-02 1.853735e-02 2.004703e-02 2.184644e-02 2.387336e-02 2.603393e-02 2.815782e-02 3.003323e-02 3.139374e-02 3.203966e-02 3.192604e-02 3.123055e-02 3.038470e-02 2.998139e-02 3.065009e-02 3.289676e-02 3.701671e-02 4.307712e-02 5.086653e-02 5.995129e-02 6.966591e-02 7.930240e-02 8.827257e-02 9.638095e-02 1.037302e-01 1.105264e-01 1.165762e-01 1.210113e-01 1.224899e-01 1.196586e-01 1.120051e-01 1.002512e-01 8.618137e-02 7.205521e-02 5.980185e-02 5.049425e-02 4.428172e-02 4.061937e-02 3.864335e-02 3.747955e-02 3.642290e-02 3.506387e-02 3.324710e-02 3.106209e-02 2.876767e-02 2.669977e-02 2.520987e-02 2.457728e-02 2.499707e-02 2.648193e-02 2.887184e-02 3.176174e-02 3.456608e-02 3.667691e-02 3.762236e-02 3.724922e-02 3.580043e-02 3.380660e-02 3.187182e-02 3.045908e-02 2.977148e-02 2.971270e-02 3.001944e-02 3.041681e-02 3.075011e-02 3.101298e-02 3.130822e-02 3.179078e-02 3.253170e-02 3.351945e-02 3.462915e-02 3.571365e-02 3.667695e-02 3.758303e-02 3.866494e-02 4.027967e-02 4.281583e-02 4.653904e-02 5.149496e-02 5.747344e-02 6.404365e-02 7.061357e-02 7.649008e-02 8.103240e-02 8.377204e-02 8.457741e-02 8.380190e-02 8.230594e-02 8.127104e-02 8.195547e-02 8.534883e-02 9.187558e-02 1.013145e-01 1.127058e-01 1.246398e-01 1.353630e-01 1.433132e-01 1.473929e-01 1.472327e-01 1.433047e-01 1.365952e-01 1.282503e-01 1.193392e-01 1.105156e-01 1.022401e-01 9.468098e-02 8.797711e-02 8.224747e-02 7.761085e-02 7.416056e-02 7.198931e-02 7.114602e-02 7.165571e-02 7.337998e-02 7.600115e-02 7.901163e-02 8.177898e-02 8.367791e-02 8.426686e-02 8.335435e-02 8.101560e-02 7.751170e-02 7.327433e-02 6.880258e-02 6.463172e-02 6.126107e-02 5.908980e-02 5.835032e-02 5.906301e-02 6.109468e-02 6.415823e-02 6.796421e-02 7.225248e-02 7.680279e-02 8.147113e-02 8.612853e-02 9.058285e-02 9.466316e-02 9.823870e-02 1.011886e-01 1.035286e-01 1.053333e-01 1.067440e-01 1.079174e-01 1.090583e-01 1.104837e-01 1.126586e-01 1.161335e-01 1.214423e-01 1.288369e-01 1.381146e-01 1.485602e-01 1.588256e-01 1.674083e-01 1.727201e-01 1.736402e-01 1.696879e-01 1.612326e-01 1.495303e-01 1.362882e-01 1.234640e-01 1.127698e-01 1.054388e-01 1.020342e-01 1.025131e-01 1.062534e-01 1.123110e-01 1.194815e-01 1.264256e-01 1.318933e-01 1.348174e-01 1.344700e-01 1.306536e-01 1.236877e-01 1.144032e-01 1.039220e-01 9.360708e-02 8.480969e-02 7.866173e-02 7.603874e-02 7.735974e-02 8.261928e-02 9.132136e-02 1.025315e-01 1.150385e-01 1.274103e-01 1.382597e-01 1.463724e-01 1.509071e-01 1.515031e-01 1.482767e-01 1.419191e-01 1.334031e-01 1.239387e-01 1.146420e-01 1.064880e-01 1.001887e-01 9.624775e-02 9.509525e-02 9.732065e-02 1.036757e-01 1.150874e-01 1.324177e-01 1.560677e-01 1.856379e-01 2.195624e-01 2.551554e-01 2.888038e-01 3.168318e-01 3.359547e-01 3.444396e-01 3.420780e-01 3.303844e-01 3.118778e-01 2.895295e-01 2.658668e-01 2.428213e-01 2.212948e-01 2.017458e-01 1.841945e-01 1.686717e-01 1.553676e-01 1.445404e-01 1.364816e-01 1.313645e-01 1.290803e-01 1.292384e-01 1.312609e-01 1.343915e-01 1.379384e-01 1.413262e-01 1.442193e-01 1.465485e-01 1.483971e-01 1.500546e-01 1.517530e-01 1.536184e-01 1.557193e-01 1.579236e-01 1.601639e-01 1.623731e-01 1.646399e-01 1.670350e-01 1.698152e-01 1.730618e-01 1.769374e-01 1.815212e-01 1.868741e-01 1.931546e-01 2.004084e-01 2.087449e-01 2.182350e-01 2.289485e-01 2.411758e-01 2.554815e-01 2.726770e-01 2.939425e-01 3.200649e-01 3.512131e-01 3.863536e-01 4.229357e-01 4.570218e-01 4.841734e-01 4.999039e-01 5.013641e-01 4.874719e-01 4.595282e-01 4.205678e-01 3.749545e-01 3.268251e-01 2.801105e-01 2.373814e-01 2.002979e-01 1.696811e-01 1.456953e-01 1.283087e-01 1.171853e-01 1.117336e-01 1.111830e-01 1.144438e-01 1.202473e-01 1.272612e-01 1.342299e-01 1.401901e-01 1.444630e-01 1.468650e-01 1.476074e-01 1.472542e-01 1.465885e-01 1.464608e-01 1.476736e-01 1.507600e-01 1.559780e-01 1.633150e-01 1.724791e-01 1.829246e-01 1.940085e-01 2.049837e-01 2.152246e-01 2.244065e-01 2.327033e-01 2.409878e-01 2.508929e-01 2.644527e-01 2.837222e-01 3.100251e-01 3.433217e-01 3.816870e-01 4.213345e-01 4.573215e-01 4.844231e-01 4.986162e-01 4.979621e-01 4.832623e-01 4.577881e-01 4.267439e-01 3.956000e-01 3.691144e-01 3.505500e-01 3.408016e-01 3.386721e-01 3.413882e-01 3.452520e-01 3.464735e-01 3.421088e-01 3.305348e-01 3.115778e-01 2.865380e-01 2.580398e-01 2.288698e-01 2.019887e-01 1.796882e-01 1.634784e-01 1.536581e-01 1.497941e-01 1.505910e-01 1.543188e-01 1.591736e-01 1.633716e-01 1.656745e-01 1.652715e-01 1.621054e-01 1.566075e-01 1.497948e-01 1.429298e-01 1.374611e-01 1.348433e-01 1.364498e-01 1.435896e-01 1.570558e-01 1.774720e-01 2.045688e-01 2.373000e-01 2.737662e-01 3.108869e-01 3.453497e-01 3.730618e-01 3.910341e-01 3.966219e-01 3.890383e-01 3.688241e-01 3.382712e-01 3.003840e-01 2.590054e-01 2.176273e-01 1.792287e-01 1.459246e-01 1.187491e-01 9.798927e-02 8.311429e-02 7.342973e-02 6.797767e-02 6.587774e-02 6.645924e-02 6.917038e-02 7.366753e-02 7.965225e-02 8.694151e-02 9.531619e-02 1.045103e-01 1.141863e-01 1.239792e-01 1.333794e-01 1.418762e-01 1.489286e-01 1.540050e-01 1.567615e-01 1.568773e-01 1.543491e-01 1.493279e-01 1.423075e-01 1.338703e-01 1.247658e-01 1.157333e-01 1.073719e-01 1.001201e-01 9.420325e-02 8.964473e-02 8.627237e-02 8.388523e-02 8.229964e-02 8.132565e-02 8.094210e-02 8.110908e-02 8.189426e-02 8.326154e-02 8.518560e-02 8.744240e-02 8.976917e-02 9.174517e-02 9.299629e-02 9.312330e-02 9.195739e-02 8.944342e-02 8.580258e-02 8.146352e-02 7.702404e-02 7.317113e-02 7.059464e-02 6.991352e-02 7.156548e-02 7.581916e-02 8.262137e-02 9.163277e-02 1.022298e-01 1.135139e-01 1.244265e-01 1.338693e-01 1.407796e-01 1.443673e-01 1.442064e-01 1.402826e-01 1.330119e-01 1.230954e-01 1.115578e-01 9.941429e-02 8.761284e-02 7.697950e-02 6.803692e-02 6.105994e-02 5.606893e-02 5.287570e-02 5.120715e-02 5.063899e-02 5.082233e-02 5.139399e-02 5.208449e-02 5.272366e-02 5.323832e-02 5.364620e-02 5.406634e-02 5.464032e-02 5.552865e-02 5.689876e-02 5.883139e-02 6.141252e-02 6.460301e-02 6.834016e-02 7.248966e-02 7.683268e-02 8.114715e-02 8.510395e-02 8.842568e-02 9.076718e-02 9.191171e-02 9.166237e-02 8.997043e-02 8.688658e-02 8.266128e-02 7.755098e-02 7.196231e-02 6.627716e-02 6.086219e-02 5.599710e-02 5.186911e-02 4.860430e-02 4.617700e-02 4.451559e-02 4.351124e-02 4.299904e-02 4.284149e-02 4.288455e-02 4.301558e-02 4.315296e-02 4.322998e-02 4.322897e-02 4.313906e-02 4.296270e-02 4.273370e-02 4.247033e-02 4.220642e-02 4.200161e-02 4.191794e-02 4.203379e-02 4.245622e-02 4.328645e-02 4.460511e-02 4.648639e-02 4.893239e-02 5.188963e-02 5.521946e-02 5.872311e-02 6.217858e-02 6.531731e-02 6.788346e-02 6.970774e-02 7.068640e-02 7.078006e-02 7.008693e-02 6.877312e-02 6.700395e-02 6.505055e-02 6.308149e-02 6.127493e-02 5.978223e-02 5.867103e-02 5.801347e-02 5.785267e-02 5.822068e-02 5.917467e-02 6.075981e-02 6.298642e-02 6.587019e-02 6.934201e-02 7.327354e-02 7.745162e-02 8.158727e-02 8.533923e-02 8.834623e-02 9.026245e-02 9.084806e-02 8.992314e-02 8.747995e-02 8.365345e-02 7.872335e-02 7.306230e-02 6.710069e-02 6.127533e-02 5.597300e-02 5.150450e-02 4.804793e-02 4.570991e-02 4.443522e-02 4.412024e-02 4.456296e-02 4.554526e-02 4.683869e-02 4.823024e-02 4.954845e-02 5.068284e-02 5.156695e-02 5.222874e-02 5.271247e-02 5.312491e-02 5.355016e-02 5.409438e-02 5.478712e-02 5.562387e-02 5.653435e-02 5.740311e-02 5.806773e-02 5.837980e-02 5.821181e-02 5.750111e-02 5.622346e-02 5.446108e-02 5.236157e-02 5.010449e-02 4.791387e-02 4.601317e-02 4.458081e-02 4.377587e-02 4.369896e-02 4.439860e-02 4.587392e-02 4.811222e-02 5.103304e-02 5.458900e-02 5.869060e-02 6.321580e-02 6.809575e-02 7.312088e-02 7.811014e-02 8.288641e-02 8.709094e-02 9.054532e-02 9.292970e-02 9.408378e-02 9.388506e-02 9.230797e-02 8.949626e-02 8.564196e-02 8.105666e-02 7.609256e-02 7.110421e-02 6.636592e-02 6.218146e-02 5.869220e-02 5.600199e-02 5.409336e-02 5.296957e-02 5.250857e-02 5.261074e-02 5.316489e-02 5.403941e-02 5.513849e-02 5.639083e-02 5.772044e-02 5.911822e-02 6.060733e-02 6.221499e-02 6.401388e-02 6.609240e-02 6.851446e-02 7.130758e-02 7.450299e-02 7.801271e-02 8.171752e-02 8.546782e-02 8.898570e-02 9.203218e-02 9.440224e-02 9.581297e-02 9.618631e-02 9.544755e-02 9.363999e-02 9.093573e-02 8.753039e-02 8.370419e-02 7.977260e-02 7.595400e-02 7.251474e-02 6.961523e-02 6.732117e-02 6.565882e-02 6.460882e-02 6.407105e-02 6.396332e-02 6.414643e-02 6.455037e-02 6.504062e-02 6.555298e-02 6.599803e-02 6.628097e-02 6.636107e-02 6.618893e-02 6.569220e-02 6.488925e-02 6.375561e-02 6.234799e-02 6.070256e-02 5.892812e-02 5.709740e-02 5.530373e-02 5.365790e-02 5.222506e-02 5.104967e-02 5.017841e-02 4.958732e-02 4.926248e-02 4.915349e-02 4.919810e-02 4.932224e-02 4.946959e-02 4.958659e-02 4.962479e-02 4.956889e-02 4.942204e-02 4.920171e-02 4.892929e-02 4.863225e-02 4.834063e-02 4.806793e-02 4.780424e-02 4.753727e-02 4.724301e-02 4.689559e-02 4.646798e-02 4.595516e-02 4.535547e-02 4.470073e-02 4.403594e-02 4.342206e-02 4.291833e-02 4.257498e-02 4.244484e-02 4.255816e-02 4.292469e-02 4.351524e-02 4.428694e-02 4.518463e-02 4.614192e-02 4.707344e-02 4.792236e-02 4.863367e-02 4.919083e-02 4.960108e-02 4.992980e-02 5.026712e-02 5.076668e-02 5.160429e-02 5.297447e-02 5.507466e-02 5.810368e-02 6.219076e-02 6.738140e-02 7.365373e-02 8.088040e-02 8.880173e-02 9.706333e-02 1.052181e-01 1.127918e-01 1.193269e-01 1.243658e-01 1.275751e-01 1.287551e-01 1.278546e-01 1.249717e-01 1.203603e-01 1.143974e-01 1.075177e-01 1.001473e-01 9.273295e-02 8.561778e-02 7.904862e-02 7.318617e-02 6.810142e-02 6.375766e-02 6.005700e-02 5.687579e-02 5.407891e-02 5.153964e-02 4.917748e-02 4.691409e-02 4.473096e-02 4.262886e-02 4.062238e-02 3.875209e-02 3.704769e-02 3.553650e-02 3.424054e-02 3.316951e-02 3.231885e-02 3.167114e-02 3.122192e-02 3.093136e-02 3.077792e-02 3.073281e-02 3.077781e-02 3.088764e-02 3.103901e-02 3.121867e-02 3.140338e-02 3.158373e-02 3.173696e-02 3.184798e-02 3.191121e-02 3.190902e-02 3.185123e-02 3.173124e-02 3.156145e-02 3.135930e-02 3.114012e-02 3.092863e-02 3.073441e-02 3.059448e-02 3.050481e-02 3.047898e-02 3.053159e-02 3.064474e-02 3.082954e-02 3.106789e-02 3.136651e-02 3.171415e-02 3.211037e-02 3.254925e-02 3.303283e-02 3.355179e-02 3.411279e-02 3.468998e-02 3.528071e-02 3.585969e-02 3.640665e-02 3.689610e-02 3.730107e-02 3.760184e-02 3.776967e-02 3.780217e-02 3.768735e-02 3.743310e-02 3.705826e-02 3.657759e-02 3.602962e-02 3.543808e-02 3.483941e-02 3.426621e-02 3.374122e-02 3.328541e-02 3.290081e-02 3.259362e-02 3.235401e-02 3.216406e-02 3.201303e-02 3.187137e-02 3.172553e-02 3.155213e-02 3.134083e-02 3.107705e-02 3.075692e-02 3.037579e-02 2.993379e-02 2.943638e-02 2.889460e-02 2.830463e-02 2.768363e-02 2.704750e-02 2.639761e-02 2.575482e-02 2.512990e-02 2.453788e-02 2.399039e-02 2.350484e-02 2.308945e-02 2.275909e-02 2.252221e-02 2.238625e-02 2.235680e-02 2.244469e-02 2.264470e-02 2.296187e-02 2.339290e-02 2.393052e-02 2.456332e-02 2.527588e-02 2.604855e-02 2.685520e-02 2.766604e-02 2.844876e-02 2.916972e-02 2.979626e-02 3.029870e-02 3.065287e-02 3.084193e-02 3.085752e-02 3.070120e-02 3.038393e-02 2.992721e-02 2.934987e-02 2.869184e-02 2.797919e-02 2.724446e-02 2.652719e-02 2.584056e-02 2.521381e-02 2.466162e-02 2.418969e-02 2.380270e-02 2.349925e-02 2.327361e-02 2.311923e-02 2.302243e-02 2.297454e-02 2.296682e-02 2.298642e-02 2.303144e-02 2.308639e-02 2.315653e-02 2.323498e-02 2.332557e-02 2.343287e-02 2.356011e-02 2.372002e-02 2.391714e-02 2.416391e-02 2.447658e-02 2.486029e-02 2.532524e-02 2.587783e-02 2.652392e-02 2.726113e-02 2.808502e-02 2.899094e-02 2.995990e-02 3.098652e-02 3.203778e-02 3.309770e-02 3.414988e-02 3.515167e-02 3.608724e-02 3.693713e-02 3.765615e-02 3.823246e-02 3.864083e-02 3.886662e-02 3.889018e-02 3.870406e-02 3.831214e-02 3.771332e-02 3.692056e-02 3.595935e-02 3.485282e-02 3.363115e-02 3.233345e-02 3.099724e-02 2.965624e-02 2.834015e-02 2.708222e-02 2.591196e-02 2.483657e-02 2.387439e-02 2.302536e-02 2.229538e-02 2.167725e-02 2.116177e-02 2.074169e-02 2.040478e-02 2.014093e-02 1.993489e-02 1.978145e-02 1.967692e-02 1.960333e-02 1.955907e-02 1.954215e-02 1.954648e-02 1.957158e-02 1.961806e-02 1.968934e-02 1.978845e-02 1.992133e-02 2.009986e-02 2.033794e-02 2.064743e-02 2.106782e-02 2.160060e-02 2.228026e-02 2.314903e-02 2.422250e-02 2.554848e-02 2.716220e-02 2.907664e-02 3.134478e-02 3.396232e-02 3.694187e-02 4.032141e-02 4.403824e-02 4.812128e-02 5.251339e-02 5.719876e-02 6.215629e-02 6.728717e-02 7.261786e-02 7.806341e-02 8.359739e-02 8.917036e-02 9.475199e-02 1.003016e-01 1.057335e-01 1.109970e-01 1.160651e-01 1.207167e-01 1.249550e-01 1.286042e-01 1.315399e-01 1.336137e-01 1.347618e-01 1.347759e-01 1.337189e-01 1.314346e-01 1.280278e-01 1.234772e-01 1.179685e-01 1.115709e-01 1.044976e-01 9.696957e-02 8.910289e-02 8.125906e-02 7.344847e-02 6.601310e-02 5.897919e-02 5.249888e-02 4.665868e-02 4.143824e-02 3.693367e-02 3.302546e-02 2.977225e-02 2.704789e-02 2.483694e-02 2.304060e-02 2.162692e-02 2.050925e-02 1.964877e-02 1.898176e-02 1.847804e-02 1.809634e-02 1.781158e-02 1.759878e-02 1.744076e-02 1.732485e-02 1.723997e-02 1.718054e-02 1.713791e-02 1.710821e-02 1.708524e-02 1.708095e-02 1.708288e-02 1.709447e-02 1.711265e-02 1.714295e-02 1.718724e-02 1.724244e-02 1.731269e-02 1.739881e-02 1.750826e-02 1.764597e-02 1.781433e-02 1.802512e-02 1.828370e-02 1.860180e-02 1.899382e-02 1.947207e-02 2.005496e-02 2.076631e-02 2.162156e-02 2.265251e-02 2.388165e-02 2.533748e-02 2.704876e-02 2.903678e-02 3.132543e-02 3.393070e-02 3.685523e-02 4.009501e-02 4.363090e-02 4.742566e-02 5.143354e-02 5.558042e-02 5.978511e-02 6.395284e-02 6.797028e-02 7.171602e-02 7.507617e-02 7.794705e-02 8.020098e-02 8.177715e-02 8.260181e-02 8.263209e-02 8.186999e-02 8.032754e-02 7.806997e-02 7.514877e-02 7.169153e-02 6.778821e-02 6.357250e-02 5.917427e-02 5.471198e-02 5.030258e-02 4.603048e-02 4.200869e-02 3.826419e-02 3.487194e-02 3.183772e-02 2.918503e-02 2.689443e-02 2.495506e-02 2.332954e-02 2.199946e-02 2.091428e-02 2.004317e-02 1.934991e-02 1.880764e-02 1.837454e-02 1.803729e-02 1.777036e-02 1.755382e-02 1.737650e-02 1.723063e-02 1.710652e-02 1.699643e-02 1.689888e-02 1.681363e-02 1.673708e-02 1.666426e-02 1.659749e-02 1.654788e-02 1.649891e-02 1.646266e-02 1.642933e-02 1.640270e-02 1.638516e-02 1.637488e-02 1.636892e-02 1.637262e-02 1.638298e-02 1.639823e-02 1.642057e-02 1.645541e-02 1.649156e-02 1.653381e-02 1.658539e-02 1.663856e-02 1.669601e-02 1.676124e-02 1.682601e-02 1.689487e-02 1.696374e-02 1.703691e-02 1.711209e-02 1.719004e-02 1.727011e-02 1.735760e-02 1.745350e-02 1.755770e-02 1.768238e-02 1.782052e-02 1.798680e-02 1.817797e-02 1.840960e-02 1.867337e-02 1.898340e-02 1.933712e-02 1.973709e-02 2.018216e-02 2.066947e-02 2.120032e-02 2.175429e-02 2.234161e-02 2.292639e-02 2.351892e-02 2.408156e-02 2.461765e-02 2.510616e-02 2.552104e-02 2.586501e-02 2.611224e-02 2.626306e-02 2.630505e-02 2.623563e-02 2.606231e-02 2.578501e-02 2.540459e-02 2.494700e-02 2.441457e-02 2.382130e-02 2.319126e-02 2.253757e-02 2.186831e-02 2.120920e-02 2.056751e-02 1.995125e-02 1.938111e-02 1.884504e-02 1.836445e-02 1.793404e-02 1.754962e-02 1.721611e-02 1.692794e-02 1.668604e-02 1.647721e-02 1.630564e-02 1.616352e-02 1.604902e-02 1.595507e-02 1.588471e-02 1.583008e-02 1.579290e-02 1.577159e-02 1.576507e-02 1.577570e-02 1.580072e-02 1.583977e-02 1.590037e-02 1.597311e-02 1.606490e-02 1.617418e-02 1.629803e-02 1.643772e-02 1.659028e-02 1.675561e-02 1.693089e-02 1.710794e-02 1.728629e-02 1.746764e-02 1.763802e-02 1.779731e-02 1.794593e-02 1.807130e-02 1.817598e-02 1.825684e-02 1.831265e-02 1.833992e-02 1.834259e-02 1.831795e-02 1.826935e-02 1.820352e-02 1.811994e-02 1.802311e-02 1.792485e-02 1.782134e-02 1.772587e-02 1.763826e-02 1.756423e-02 1.751555e-02 1.748511e-02 1.748703e-02 1.751655e-02 1.758233e-02 1.767889e-02 1.780839e-02 1.797071e-02 1.816223e-02 1.839011e-02 1.863829e-02 1.891246e-02 1.920634e-02 1.951170e-02 1.982841e-02 2.014890e-02 2.047108e-02 2.078588e-02 2.109410e-02 2.138498e-02 2.165684e-02 2.191391e-02 2.214581e-02 2.236106e-02 2.256208e-02 2.275019e-02 2.293500e-02 2.312679e-02 2.333219e-02 2.356812e-02 2.384236e-02 2.418295e-02 2.459190e-02 2.509032e-02 2.569379e-02 2.641506e-02 2.727060e-02 2.826138e-02 2.940150e-02 3.069808e-02 3.214959e-02 3.374832e-02 3.548722e-02 3.736473e-02 3.935855e-02 4.145062e-02 4.361828e-02 4.584774e-02 4.808928e-02 5.033109e-02 5.252603e-02 5.465641e-02 5.667321e-02 5.853907e-02 6.023129e-02 6.171171e-02 6.294749e-02 6.391215e-02 6.458707e-02 6.495209e-02 6.498787e-02 6.470957e-02 6.408674e-02 6.315641e-02 6.190858e-02 6.038803e-02 5.859340e-02 5.657756e-02 5.437187e-02 5.200661e-02 4.953304e-02 4.698608e-02 4.440842e-02 4.182286e-02 3.927990e-02 3.681092e-02 3.444877e-02 3.219855e-02 3.009421e-02 2.813456e-02 2.633094e-02 2.469116e-02 2.320545e-02 2.188307e-02 2.070981e-02 1.967218e-02 1.876859e-02 1.797937e-02 1.729791e-02 1.671467e-02 1.621789e-02 1.579843e-02 1.544256e-02 1.514609e-02 1.490207e-02 1.470508e-02 1.454954e-02 1.442685e-02 1.433933e-02 1.427933e-02 1.424755e-02 1.423530e-02 1.424979e-02 1.428413e-02 1.433449e-02 1.440492e-02 1.448929e-02 1.458930e-02 1.469784e-02 1.482283e-02 1.495600e-02 1.509857e-02 1.524554e-02 1.539666e-02 1.554483e-02 1.569007e-02 1.583078e-02 1.595975e-02 1.607266e-02 1.616358e-02 1.623061e-02 1.627017e-02 1.627265e-02 1.623242e-02 1.614724e-02 1.601590e-02 1.583132e-02 1.559113e-02 1.529309e-02 1.493951e-02 1.452807e-02 1.406282e-02 1.354386e-02 ''') ImportString(u'fluxdelta(numeric)',''' 6.717000e+02 -3.561610e+01 -2.633984e+00 -3.129430e-01 1.666874e-01 -8.484430e-03 1.804276e-03 3.693617e-03 -4.087228e-04 1.365671e-03 -5.288830e-04 6.490390e-04 3.817240e-04 2.658840e-04 1.563056e-04 3.080680e-04 1.175751e-04 1.451300e-06 -1.818070e-05 8.834710e-05 6.513680e-05 1.187130e-04 2.168114e-04 4.193800e-04 4.341460e-04 1.143590e-03 1.568020e-03 1.912550e-03 2.722921e-03 2.798980e-03 2.224180e-03 1.184510e-03 1.666638e-03 1.154786e-03 1.211271e-03 9.903440e-04 1.085550e-03 9.823460e-04 9.830990e-04 1.063291e-03 1.044319e-03 8.725200e-04 9.690490e-04 1.037582e-03 1.139343e-03 1.067455e-03 9.966070e-04 7.403760e-04 7.110140e-04 8.018580e-04 7.857960e-04 8.050660e-04 7.357120e-04 8.008380e-04 6.144540e-04 5.787430e-04 6.268490e-04 6.103750e-04 5.726350e-04 6.020570e-04 5.193810e-04 4.530740e-04 5.553090e-04 5.810250e-04 6.288400e-04 6.092290e-04 5.501350e-04 5.639660e-04 5.311350e-04 5.593780e-04 5.034180e-04 5.230310e-04 5.447630e-04 3.832260e-04 5.639030e-04 5.085020e-04 6.083480e-04 6.059530e-04 5.551990e-04 5.257730e-04 5.815220e-04 6.213310e-04 6.409640e-04 6.142480e-04 5.756760e-04 5.605620e-04 6.908950e-04 7.122050e-04 7.171920e-04 7.597840e-04 8.048950e-04 7.890000e-04 7.510910e-04 6.976010e-04 6.585580e-04 6.521280e-04 6.003590e-04 6.524040e-04 5.868290e-04 5.523690e-04 5.973120e-04 5.266330e-04 5.820150e-04 5.820970e-04 5.934070e-04 5.957240e-04 5.966650e-04 5.550510e-04 6.392550e-04 5.402950e-04 5.725810e-04 5.674630e-04 6.107510e-04 5.200120e-04 5.561230e-04 5.302810e-04 4.995950e-04 4.905590e-04 5.799510e-04 5.670650e-04 5.462020e-04 5.767870e-04 5.873630e-04 5.904430e-04 5.915170e-04 6.614370e-04 6.329740e-04 5.626190e-04 5.911980e-04 6.229200e-04 6.789840e-04 6.951420e-04 6.874660e-04 7.938290e-04 8.608890e-04 8.534310e-04 1.035960e-03 1.046371e-03 1.175078e-03 1.114799e-03 1.084971e-03 1.151934e-03 8.935620e-04 1.016095e-03 1.000131e-03 1.092747e-03 1.191336e-03 1.429210e-03 1.361740e-03 1.328258e-03 1.501993e-03 1.344970e-03 1.386170e-03 1.306730e-03 1.275663e-03 1.142598e-03 1.112382e-03 1.073456e-03 9.359650e-04 9.922390e-04 9.385760e-04 1.182843e-03 1.145800e-03 1.182230e-03 1.054130e-03 1.021000e-03 9.440010e-04 8.509160e-04 8.455930e-04 9.099220e-04 1.030766e-03 1.095160e-03 1.004290e-03 8.589430e-04 8.748940e-04 8.570450e-04 7.116170e-04 6.705050e-04 5.868400e-04 6.365450e-04 6.337050e-04 5.576220e-04 5.953490e-04 5.522500e-04 5.456940e-04 5.697350e-04 5.255300e-04 5.301010e-04 5.654500e-04 4.940900e-04 4.632660e-04 4.663710e-04 4.903050e-04 4.204920e-04 4.616460e-04 4.475270e-04 4.871740e-04 4.697840e-04 4.752310e-04 4.217890e-04 4.725650e-04 4.191410e-04 4.272820e-04 3.585950e-04 4.536560e-04 3.619670e-04 3.725620e-04 3.562350e-04 4.285610e-04 4.276540e-04 3.878030e-04 4.073330e-04 4.123430e-04 3.329440e-04 3.465390e-04 4.070180e-04 4.006760e-04 3.757300e-04 3.756360e-04 3.704940e-04 3.410260e-04 3.928340e-04 3.932720e-04 3.821400e-04 3.529390e-04 3.855900e-04 3.630250e-04 3.797910e-04 3.591320e-04 3.480130e-04 3.478810e-04 3.685470e-04 3.415510e-04 3.589730e-04 3.526730e-04 3.233800e-04 3.694400e-04 3.497530e-04 3.187230e-04 2.844270e-04 3.342290e-04 3.650240e-04 3.402130e-04 3.353090e-04 3.282540e-04 3.750370e-04 3.438710e-04 3.654720e-04 4.282800e-04 3.945260e-04 3.393770e-04 3.284350e-04 3.305000e-04 2.884100e-04 3.889250e-04 3.178000e-04 3.878830e-04 3.272550e-04 3.107630e-04 2.545320e-04 2.719750e-04 2.726380e-04 2.597000e-04 2.907110e-04 2.954530e-04 3.018980e-04 2.857470e-04 2.999590e-04 3.357510e-04 2.832120e-04 2.767450e-04 2.326550e-04 3.005720e-04 2.920610e-04 3.141140e-04 3.216020e-04 2.960590e-04 2.944700e-04 2.763350e-04 2.979940e-04 3.319700e-04 2.778910e-04 2.417580e-04 3.229890e-04 3.201030e-04 2.907760e-04 2.646430e-04 2.946820e-04 3.108890e-04 2.797630e-04 2.683120e-04 2.958500e-04 3.097440e-04 2.564920e-04 2.516520e-04 2.997480e-04 2.566980e-04 2.582030e-04 2.536970e-04 3.009910e-04 2.794110e-04 3.321000e-04 3.129720e-04 3.275780e-04 3.546300e-04 3.712930e-04 3.761030e-04 4.286590e-04 4.936920e-04 4.735100e-04 4.801770e-04 4.532160e-04 4.423920e-04 3.954850e-04 3.871190e-04 3.474280e-04 3.097730e-04 2.737510e-04 2.717590e-04 2.583780e-04 2.582910e-04 2.390110e-04 2.331360e-04 2.443010e-04 2.723400e-04 2.603230e-04 2.408420e-04 2.263050e-04 2.833230e-04 2.149030e-04 2.936090e-04 2.517280e-04 2.234850e-04 2.514080e-04 2.662640e-04 2.771341e-04 2.045980e-04 2.326200e-04 1.810080e-04 2.006930e-04 2.410869e-04 2.597670e-04 1.867650e-04 1.901810e-04 1.916060e-04 2.093840e-04 1.955350e-04 2.565323e-04 2.037630e-04 2.192890e-04 1.968890e-04 2.704350e-04 2.173030e-04 1.760550e-04 1.725550e-04 2.216570e-04 1.926800e-04 2.033300e-04 2.158710e-04 1.716700e-04 1.582780e-04 1.991770e-04 1.960138e-04 2.370170e-04 1.977810e-04 1.855390e-04 2.126740e-04 1.549110e-04 1.961817e-04 2.122756e-04 1.861810e-04 1.654960e-04 1.791040e-04 1.820382e-04 2.405400e-04 1.242870e-04 1.501890e-04 1.754070e-04 1.639110e-04 2.381120e-04 2.045289e-04 1.997290e-04 1.934369e-04 1.827496e-04 1.725970e-04 2.004060e-04 2.467260e-04 1.736524e-04 1.935626e-04 2.078940e-04 2.016694e-04 1.329370e-04 2.095750e-04 2.004050e-04 2.036510e-04 1.409750e-04 1.874660e-04 2.163860e-04 2.560420e-04 2.817640e-04 1.715350e-04 2.116253e-04 2.174337e-04 2.128890e-04 2.452477e-04 2.027280e-04 1.742880e-04 2.697750e-04 2.191001e-04 2.379300e-04 1.765414e-04 2.417760e-04 2.599460e-04 2.425280e-04 2.192800e-04 2.232400e-04 2.113760e-04 1.629280e-04 2.081210e-04 1.732400e-04 1.752260e-04 2.160630e-04 2.113760e-04 2.080540e-04 1.746560e-04 2.258920e-04 1.885590e-04 1.922150e-04 1.844450e-04 2.215070e-04 1.965130e-04 1.940840e-04 2.119450e-04 1.637660e-04 2.243845e-04 1.360420e-04 1.471400e-04 2.076640e-04 1.943850e-04 1.922470e-04 2.383790e-04 2.052120e-04 2.432040e-04 2.247140e-04 2.567180e-04 2.139320e-04 1.633140e-04 1.947410e-04 2.109570e-04 1.937080e-04 1.842980e-04 1.945543e-04 2.099910e-04 2.205876e-04 1.979450e-04 1.687580e-04 1.535724e-04 1.834060e-04 1.859900e-04 1.792050e-04 1.419200e-04 1.621900e-04 1.986970e-04 1.983040e-04 1.677530e-04 1.642669e-04 1.974400e-04 1.395610e-04 1.750930e-04 1.632730e-04 1.397360e-04 1.573330e-04 1.414000e-04 1.795304e-04 1.730704e-04 1.715630e-04 1.949779e-04 1.283499e-04 1.524801e-04 1.779290e-04 1.161499e-04 1.327065e-04 1.278147e-04 1.491756e-04 1.229560e-04 1.148103e-04 1.509306e-04 1.731482e-04 1.551801e-04 1.381020e-04 1.314114e-04 1.472286e-04 1.344098e-04 9.988500e-05 1.303773e-04 1.125189e-04 1.289554e-04 1.432523e-04 1.366576e-04 1.322329e-04 1.305178e-04 1.389109e-04 1.662667e-04 1.336500e-04 1.511033e-04 1.114740e-04 1.503991e-04 8.616450e-05 1.101235e-04 1.220796e-04 1.961011e-04 1.292899e-04 1.188529e-04 1.527593e-04 1.467474e-04 1.307397e-04 1.303567e-04 9.265100e-05 1.402299e-04 1.396071e-04 1.621164e-04 9.949000e-05 8.862500e-05 1.337413e-04 1.507454e-04 1.300908e-04 1.180810e-04 1.695171e-04 1.506108e-04 1.511222e-04 1.328891e-04 8.778500e-05 1.785397e-04 1.243030e-04 1.716250e-04 1.131539e-04 9.775700e-05 1.678777e-04 9.306360e-05 6.967600e-05 1.603941e-04 1.383674e-04 1.824313e-04 1.388680e-04 1.443963e-04 8.206050e-05 1.675730e-04 1.738365e-04 9.086570e-05 9.601010e-05 1.285800e-04 8.368130e-05 1.200650e-04 6.754500e-05 9.305390e-05 8.082300e-05 7.390220e-05 6.676980e-05 1.203748e-04 9.672520e-05 8.817890e-05 7.994050e-05 -2.145500e-06 9.684500e-05 8.395420e-05 7.960260e-05 8.858920e-05 5.647990e-05 8.551250e-05 9.489980e-05 7.905970e-05 1.397891e-04 5.177580e-05 3.493470e-05 5.694900e-05 1.088608e-04 3.834760e-05 1.419521e-04 2.437100e-05 8.814820e-05 4.774260e-05 7.964050e-05 6.217990e-05 1.038021e-04 4.539770e-05 3.924990e-05 6.820190e-05 3.358830e-05 5.617550e-05 9.187220e-05 3.288800e-05 1.101645e-04 8.979560e-05 7.725220e-05 6.045450e-05 4.963690e-05 -2.433130e-06 5.946890e-05 6.741660e-05 6.040447e-05 -1.638516e-04 9.904860e-05 8.863740e-05 6.709960e-05 7.319323e-05 4.790710e-05 1.381440e-04 6.582190e-05 5.355960e-05 -2.700230e-05 ''') ImportString(u'lambda_model(numeric),+-',''' 3.098155e+01 1.449783e-02 3.095257e+01 1.448427e-02 3.092361e+01 1.447072e-02 3.089469e+01 1.445718e-02 3.086578e+01 1.444366e-02 3.083691e+01 1.443015e-02 3.080806e+01 1.441665e-02 3.077924e+01 1.440316e-02 3.075045e+01 1.438969e-02 3.072169e+01 1.437623e-02 3.069295e+01 1.436278e-02 3.066423e+01 1.434934e-02 3.063555e+01 1.433592e-02 3.060689e+01 1.432251e-02 3.057826e+01 1.430911e-02 3.054965e+01 1.429573e-02 3.052108e+01 1.428235e-02 3.049253e+01 1.426899e-02 3.046400e+01 1.425564e-02 3.043550e+01 1.424231e-02 3.040703e+01 1.422899e-02 3.037859e+01 1.421567e-02 3.035017e+01 1.420238e-02 3.032178e+01 1.418909e-02 3.029341e+01 1.417582e-02 3.026507e+01 1.416256e-02 3.023676e+01 1.414931e-02 3.020848e+01 1.413607e-02 3.018022e+01 1.412285e-02 3.015198e+01 1.410964e-02 3.012378e+01 1.409644e-02 3.009560e+01 1.408325e-02 3.006745e+01 1.407008e-02 3.003932e+01 1.405691e-02 3.001122e+01 1.404376e-02 2.998314e+01 1.403063e-02 2.995510e+01 1.401750e-02 2.992707e+01 1.400439e-02 2.989908e+01 1.399129e-02 2.987111e+01 1.397820e-02 2.984316e+01 1.396512e-02 2.981525e+01 1.395206e-02 2.978736e+01 1.393901e-02 2.975949e+01 1.392597e-02 2.973165e+01 1.391294e-02 2.970384e+01 1.389993e-02 2.967605e+01 1.388692e-02 2.964829e+01 1.387393e-02 2.962056e+01 1.386095e-02 2.959285e+01 1.384799e-02 2.956516e+01 1.383503e-02 2.953751e+01 1.382209e-02 2.950988e+01 1.380916e-02 2.948227e+01 1.379624e-02 2.945469e+01 1.378334e-02 2.942714e+01 1.377044e-02 2.939961e+01 1.375756e-02 2.937211e+01 1.374469e-02 2.934463e+01 1.373184e-02 2.931718e+01 1.371899e-02 2.928975e+01 1.370616e-02 2.926236e+01 1.369333e-02 2.923498e+01 1.368052e-02 2.920763e+01 1.366773e-02 2.918031e+01 1.365494e-02 2.915301e+01 1.364217e-02 2.912574e+01 1.362941e-02 2.909850e+01 1.361666e-02 2.907128e+01 1.360392e-02 2.904408e+01 1.359119e-02 2.901691e+01 1.357848e-02 2.898977e+01 1.356578e-02 2.896265e+01 1.355309e-02 2.893555e+01 1.354041e-02 2.890849e+01 1.352774e-02 2.888144e+01 1.351509e-02 2.885443e+01 1.350244e-02 2.882743e+01 1.348981e-02 2.880047e+01 1.347719e-02 2.877353e+01 1.346459e-02 2.874661e+01 1.345199e-02 2.871972e+01 1.343941e-02 2.869285e+01 1.342683e-02 2.866601e+01 1.341427e-02 2.863919e+01 1.340173e-02 2.861240e+01 1.338919e-02 2.858564e+01 1.337666e-02 2.855890e+01 1.336415e-02 2.853218e+01 1.335165e-02 2.850549e+01 1.333916e-02 2.847882e+01 1.332668e-02 2.845218e+01 1.331421e-02 2.842557e+01 1.330176e-02 2.839898e+01 1.328931e-02 2.837241e+01 1.327688e-02 2.834587e+01 1.326446e-02 2.831935e+01 1.325206e-02 2.829286e+01 1.323966e-02 2.826639e+01 1.322727e-02 2.823995e+01 1.321490e-02 2.821353e+01 1.320254e-02 2.818714e+01 1.319019e-02 2.816077e+01 1.317785e-02 2.813443e+01 1.316552e-02 2.810811e+01 1.315320e-02 2.808182e+01 1.314090e-02 2.805555e+01 1.312861e-02 2.802930e+01 1.311633e-02 2.800308e+01 1.310406e-02 2.797688e+01 1.309180e-02 2.795071e+01 1.307955e-02 2.792457e+01 1.306732e-02 2.789845e+01 1.305509e-02 2.787235e+01 1.304288e-02 2.784627e+01 1.303068e-02 2.782022e+01 1.301849e-02 2.779420e+01 1.300631e-02 2.776820e+01 1.299414e-02 2.774222e+01 1.298199e-02 2.771627e+01 1.296984e-02 2.769034e+01 1.295771e-02 2.766444e+01 1.294559e-02 2.763856e+01 1.293348e-02 2.761271e+01 1.292138e-02 2.758688e+01 1.290929e-02 2.756107e+01 1.289722e-02 2.753529e+01 1.288515e-02 2.750953e+01 1.287310e-02 2.748379e+01 1.286106e-02 2.745808e+01 1.284902e-02 2.743240e+01 1.283700e-02 2.740674e+01 1.282500e-02 2.738110e+01 1.281300e-02 2.735548e+01 1.280101e-02 2.732989e+01 1.278904e-02 2.730433e+01 1.277707e-02 2.727879e+01 1.276512e-02 2.725327e+01 1.275318e-02 2.722777e+01 1.274125e-02 2.720230e+01 1.272933e-02 2.717686e+01 1.271742e-02 2.715143e+01 1.270553e-02 2.712603e+01 1.269364e-02 2.710066e+01 1.268177e-02 2.707531e+01 1.266990e-02 2.704998e+01 1.265805e-02 2.702467e+01 1.264621e-02 2.699939e+01 1.263438e-02 2.697414e+01 1.262256e-02 2.694890e+01 1.261075e-02 2.692369e+01 1.259896e-02 2.689851e+01 1.258717e-02 2.687334e+01 1.257540e-02 2.684821e+01 1.256363e-02 2.682309e+01 1.255188e-02 2.679800e+01 1.254014e-02 2.677293e+01 1.252841e-02 2.674788e+01 1.251669e-02 2.672286e+01 1.250498e-02 2.669786e+01 1.249328e-02 2.667289e+01 1.248159e-02 2.664794e+01 1.246992e-02 2.662301e+01 1.245825e-02 2.659810e+01 1.244660e-02 2.657322e+01 1.243495e-02 2.654836e+01 1.242332e-02 2.652353e+01 1.241170e-02 2.649872e+01 1.240009e-02 2.647393e+01 1.238849e-02 2.644917e+01 1.237690e-02 2.642442e+01 1.236532e-02 2.639970e+01 1.235375e-02 2.637501e+01 1.234220e-02 2.635033e+01 1.233065e-02 2.632569e+01 1.231912e-02 2.630106e+01 1.230759e-02 2.627645e+01 1.229608e-02 2.625187e+01 1.228458e-02 2.622732e+01 1.227309e-02 2.620278e+01 1.226160e-02 2.617827e+01 1.225013e-02 2.615378e+01 1.223867e-02 2.612931e+01 1.222723e-02 2.610487e+01 1.221579e-02 2.608045e+01 1.220436e-02 2.605606e+01 1.219294e-02 2.603168e+01 1.218154e-02 2.600733e+01 1.217014e-02 2.598300e+01 1.215876e-02 2.595869e+01 1.214738e-02 2.593441e+01 1.213602e-02 2.591015e+01 1.212467e-02 2.588591e+01 1.211333e-02 2.586170e+01 1.210199e-02 2.583750e+01 1.209067e-02 2.581333e+01 1.207936e-02 2.578919e+01 1.206806e-02 2.576506e+01 1.205677e-02 2.574096e+01 1.204550e-02 2.571688e+01 1.203423e-02 2.569282e+01 1.202297e-02 2.566879e+01 1.201172e-02 2.564478e+01 1.200049e-02 2.562078e+01 1.198926e-02 2.559682e+01 1.197804e-02 2.557287e+01 1.196684e-02 2.554895e+01 1.195564e-02 2.552505e+01 1.194446e-02 2.550117e+01 1.193329e-02 2.547732e+01 1.192212e-02 2.545348e+01 1.191097e-02 2.542967e+01 1.189983e-02 2.540589e+01 1.188870e-02 2.538212e+01 1.187757e-02 2.535837e+01 1.186646e-02 2.533465e+01 1.185536e-02 2.531095e+01 1.184427e-02 2.528728e+01 1.183319e-02 2.526362e+01 1.182212e-02 2.523999e+01 1.181106e-02 2.521638e+01 1.180002e-02 2.519279e+01 1.178898e-02 2.516922e+01 1.177795e-02 2.514568e+01 1.176693e-02 2.512215e+01 1.175592e-02 2.509865e+01 1.174493e-02 2.507517e+01 1.173394e-02 2.505172e+01 1.172296e-02 2.502828e+01 1.171200e-02 2.500487e+01 1.170104e-02 2.498148e+01 1.169010e-02 2.495811e+01 1.167916e-02 2.493476e+01 1.166823e-02 2.491143e+01 1.165732e-02 2.488813e+01 1.164641e-02 2.486485e+01 1.163552e-02 2.484159e+01 1.162463e-02 2.481835e+01 1.161376e-02 2.479513e+01 1.160290e-02 2.477194e+01 1.159204e-02 2.474877e+01 1.158120e-02 2.472561e+01 1.157036e-02 2.470248e+01 1.155954e-02 2.467937e+01 1.154873e-02 2.465629e+01 1.153792e-02 2.463322e+01 1.152713e-02 2.461018e+01 1.151635e-02 2.458716e+01 1.150557e-02 2.456416e+01 1.149481e-02 2.454118e+01 1.148406e-02 2.451822e+01 1.147331e-02 2.449528e+01 1.146258e-02 2.447237e+01 1.145186e-02 2.444948e+01 1.144115e-02 2.442661e+01 1.143044e-02 2.440376e+01 1.141975e-02 2.438093e+01 1.140907e-02 2.435812e+01 1.139839e-02 2.433533e+01 1.138773e-02 2.431257e+01 1.137708e-02 2.428983e+01 1.136644e-02 2.426710e+01 1.135580e-02 2.424440e+01 1.134518e-02 2.422172e+01 1.133457e-02 2.419906e+01 1.132396e-02 2.417643e+01 1.131337e-02 2.415381e+01 1.130279e-02 2.413122e+01 1.129221e-02 2.410864e+01 1.128165e-02 2.408609e+01 1.127110e-02 2.406356e+01 1.126055e-02 2.404105e+01 1.125002e-02 2.401856e+01 1.123950e-02 2.399609e+01 1.122898e-02 2.397364e+01 1.121848e-02 2.395122e+01 1.120798e-02 2.392881e+01 1.119750e-02 2.390643e+01 1.118702e-02 2.388406e+01 1.117656e-02 2.386172e+01 1.116610e-02 2.383940e+01 1.115566e-02 2.381710e+01 1.114522e-02 2.379482e+01 1.113480e-02 2.377256e+01 1.112438e-02 2.375032e+01 1.111397e-02 2.372810e+01 1.110358e-02 2.370590e+01 1.109319e-02 2.368373e+01 1.108281e-02 2.366157e+01 1.107245e-02 2.363944e+01 1.106209e-02 2.361732e+01 1.105174e-02 2.359523e+01 1.104140e-02 2.357316e+01 1.103107e-02 2.355111e+01 1.102075e-02 2.352908e+01 1.101044e-02 2.350706e+01 1.100014e-02 2.348507e+01 1.098985e-02 2.346311e+01 1.097957e-02 2.344116e+01 1.096930e-02 2.341923e+01 1.095904e-02 2.339732e+01 1.094879e-02 2.337543e+01 1.093855e-02 2.335357e+01 1.092831e-02 2.333172e+01 1.091809e-02 2.330989e+01 1.090788e-02 2.328809e+01 1.089767e-02 2.326630e+01 1.088748e-02 2.324454e+01 1.087729e-02 2.322280e+01 1.086712e-02 2.320107e+01 1.085695e-02 2.317937e+01 1.084680e-02 2.315768e+01 1.083665e-02 2.313602e+01 1.082651e-02 2.311438e+01 1.081638e-02 2.309275e+01 1.080627e-02 2.307115e+01 1.079616e-02 2.304957e+01 1.078606e-02 2.302801e+01 1.077597e-02 2.300647e+01 1.076589e-02 2.298495e+01 1.075582e-02 2.296344e+01 1.074575e-02 2.294196e+01 1.073570e-02 2.292050e+01 1.072566e-02 2.289906e+01 1.071563e-02 2.287764e+01 1.070560e-02 2.285624e+01 1.069559e-02 2.283486e+01 1.068558e-02 2.281349e+01 1.067559e-02 2.279215e+01 1.066560e-02 2.277083e+01 1.065562e-02 2.274953e+01 1.064565e-02 2.272825e+01 1.063570e-02 2.270699e+01 1.062575e-02 2.268575e+01 1.061581e-02 2.266452e+01 1.060588e-02 2.264332e+01 1.059595e-02 2.262214e+01 1.058604e-02 2.260098e+01 1.057614e-02 2.257984e+01 1.056625e-02 2.255871e+01 1.055636e-02 2.253761e+01 1.054649e-02 2.251653e+01 1.053662e-02 2.249546e+01 1.052676e-02 2.247442e+01 1.051692e-02 2.245340e+01 1.050708e-02 2.243239e+01 1.049725e-02 2.241141e+01 1.048743e-02 2.239044e+01 1.047762e-02 2.236950e+01 1.046782e-02 2.234857e+01 1.045803e-02 2.232767e+01 1.044824e-02 2.230678e+01 1.043847e-02 2.228591e+01 1.042870e-02 2.226506e+01 1.041895e-02 2.224424e+01 1.040920e-02 2.222343e+01 1.039946e-02 2.220264e+01 1.038974e-02 2.218187e+01 1.038002e-02 2.216112e+01 1.037031e-02 2.214039e+01 1.036061e-02 2.211967e+01 1.035091e-02 2.209898e+01 1.034123e-02 2.207831e+01 1.033156e-02 2.205766e+01 1.032189e-02 2.203702e+01 1.031224e-02 2.201641e+01 1.030259e-02 2.199581e+01 1.029295e-02 2.197524e+01 1.028332e-02 2.195468e+01 1.027370e-02 2.193414e+01 1.026409e-02 2.191362e+01 1.025449e-02 2.189312e+01 1.024490e-02 2.187264e+01 1.023531e-02 2.185218e+01 1.022574e-02 2.183174e+01 1.021617e-02 2.181132e+01 1.020662e-02 2.179091e+01 1.019707e-02 2.177053e+01 1.018753e-02 2.175016e+01 1.017800e-02 2.172982e+01 1.016848e-02 2.170949e+01 1.015897e-02 2.168918e+01 1.014946e-02 2.166889e+01 1.013997e-02 2.164862e+01 1.013048e-02 2.162837e+01 1.012101e-02 2.160814e+01 1.011154e-02 2.158792e+01 1.010208e-02 2.156773e+01 1.009263e-02 2.154755e+01 1.008319e-02 2.152740e+01 1.007376e-02 2.150726e+01 1.006433e-02 2.148714e+01 1.005492e-02 2.146704e+01 1.004551e-02 2.144696e+01 1.003611e-02 2.142689e+01 1.002673e-02 2.140685e+01 1.001735e-02 2.138682e+01 1.000798e-02 2.136682e+01 9.998613e-03 2.134683e+01 9.989260e-03 2.132686e+01 9.979915e-03 2.130691e+01 9.970579e-03 2.128698e+01 9.961252e-03 2.126706e+01 9.951934e-03 2.124717e+01 9.942624e-03 2.122729e+01 9.933324e-03 2.120744e+01 9.924031e-03 2.118760e+01 9.914747e-03 2.116778e+01 9.905472e-03 2.114798e+01 9.896207e-03 2.112819e+01 9.886948e-03 2.110843e+01 9.877700e-03 2.108868e+01 9.868460e-03 2.106895e+01 9.859229e-03 2.104925e+01 9.850005e-03 2.102955e+01 9.840791e-03 2.100988e+01 9.831585e-03 2.099023e+01 9.822388e-03 2.097059e+01 9.813200e-03 2.095098e+01 9.804020e-03 2.093138e+01 9.794848e-03 2.091180e+01 9.785686e-03 2.089223e+01 9.776532e-03 2.087269e+01 9.767386e-03 2.085316e+01 9.758249e-03 2.083366e+01 9.749120e-03 2.081417e+01 9.740001e-03 2.079470e+01 9.730889e-03 2.077524e+01 9.721786e-03 2.075581e+01 9.712691e-03 2.073639e+01 9.703606e-03 2.071700e+01 9.694529e-03 2.069761e+01 9.685460e-03 2.067825e+01 9.676400e-03 2.065891e+01 9.667347e-03 2.063958e+01 9.658304e-03 2.062028e+01 9.649269e-03 2.060099e+01 9.640242e-03 2.058171e+01 9.631224e-03 2.056246e+01 9.622214e-03 2.054323e+01 9.613213e-03 2.052401e+01 9.604220e-03 2.050481e+01 9.595236e-03 2.048563e+01 9.586261e-03 2.046646e+01 9.577293e-03 2.044732e+01 9.568334e-03 2.042819e+01 9.559383e-03 2.040908e+01 9.550440e-03 2.038999e+01 9.541506e-03 2.037092e+01 9.532580e-03 2.035186e+01 9.523663e-03 2.033282e+01 9.514754e-03 2.031380e+01 9.505853e-03 2.029480e+01 9.496961e-03 2.027581e+01 9.488077e-03 2.025685e+01 9.479201e-03 2.023790e+01 9.470333e-03 2.021896e+01 9.461475e-03 2.020005e+01 9.452623e-03 2.018115e+01 9.443781e-03 2.016228e+01 9.434947e-03 2.014341e+01 9.426121e-03 2.012457e+01 9.417303e-03 2.010574e+01 9.408494e-03 2.008694e+01 9.399692e-03 2.006815e+01 9.390899e-03 2.004937e+01 9.382114e-03 2.003062e+01 9.373337e-03 2.001188e+01 9.364569e-03 1.999316e+01 9.355809e-03 1.997446e+01 9.347057e-03 1.995577e+01 9.338313e-03 1.993710e+01 9.329577e-03 1.991845e+01 9.320850e-03 1.989982e+01 9.312131e-03 1.988120e+01 9.303420e-03 1.986261e+01 9.294717e-03 1.984402e+01 9.286022e-03 1.982546e+01 9.277334e-03 1.980692e+01 9.268656e-03 1.978839e+01 9.259986e-03 1.976987e+01 9.251324e-03 1.975138e+01 9.242669e-03 1.973290e+01 9.234023e-03 1.971445e+01 9.225384e-03 1.969600e+01 9.216755e-03 1.967758e+01 9.208133e-03 1.965917e+01 9.199519e-03 1.964078e+01 9.190913e-03 1.962241e+01 9.182315e-03 1.960405e+01 9.173726e-03 1.958571e+01 9.165144e-03 1.956739e+01 9.156571e-03 1.954909e+01 9.148004e-03 1.953080e+01 9.139447e-03 1.951253e+01 9.130898e-03 1.949427e+01 9.122356e-03 1.947604e+01 9.113822e-03 1.945782e+01 9.105297e-03 1.943962e+01 9.096779e-03 1.942143e+01 9.088269e-03 1.940327e+01 9.079767e-03 1.938511e+01 9.071274e-03 1.936698e+01 9.062788e-03 1.934886e+01 9.054310e-03 1.933076e+01 9.045840e-03 1.931268e+01 9.037378e-03 1.929461e+01 9.028924e-03 1.927656e+01 9.020478e-03 1.925853e+01 9.012039e-03 1.924051e+01 9.003608e-03 1.922252e+01 8.995187e-03 1.920453e+01 8.986772e-03 1.918657e+01 8.978365e-03 1.916862e+01 8.969965e-03 1.915069e+01 8.961575e-03 1.913277e+01 8.953191e-03 1.911488e+01 8.944816e-03 1.909699e+01 8.936449e-03 1.907913e+01 8.928088e-03 1.906128e+01 8.919737e-03 1.904345e+01 8.911393e-03 1.902564e+01 8.903056e-03 1.900784e+01 8.894729e-03 1.899006e+01 8.886407e-03 1.897229e+01 8.878094e-03 1.895455e+01 8.869790e-03 1.893682e+01 8.861492e-03 1.891910e+01 8.853203e-03 1.890140e+01 8.844920e-03 1.888372e+01 8.836647e-03 1.886605e+01 8.828380e-03 1.884841e+01 8.820121e-03 1.883077e+01 8.811871e-03 1.881316e+01 8.803627e-03 1.879556e+01 8.795392e-03 1.877798e+01 8.787164e-03 1.876041e+01 8.778944e-03 1.874286e+01 8.770732e-03 1.872533e+01 8.762527e-03 1.870781e+01 8.754330e-03 1.869031e+01 8.746141e-03 1.867283e+01 8.737959e-03 1.865536e+01 8.729785e-03 1.863791e+01 8.721619e-03 1.862047e+01 8.713460e-03 1.860305e+01 8.705309e-03 1.858565e+01 8.697165e-03 1.856827e+01 8.689029e-03 1.855090e+01 8.680901e-03 1.853354e+01 8.672780e-03 1.851620e+01 8.664668e-03 1.849888e+01 8.656561e-03 1.848158e+01 8.648464e-03 1.846429e+01 8.640374e-03 1.844702e+01 8.632291e-03 1.842976e+01 8.624216e-03 1.841252e+01 8.616148e-03 1.839530e+01 8.608088e-03 1.837809e+01 8.600036e-03 1.836090e+01 8.591990e-03 1.834372e+01 8.583953e-03 1.832656e+01 8.575923e-03 1.830942e+01 8.567900e-03 1.829229e+01 8.559885e-03 1.827518e+01 8.551878e-03 1.825808e+01 8.543878e-03 1.824100e+01 8.535885e-03 1.822394e+01 8.527901e-03 1.820689e+01 8.519923e-03 1.818986e+01 8.511953e-03 1.817284e+01 8.503990e-03 1.815584e+01 8.496035e-03 1.813886e+01 8.488088e-03 1.812189e+01 8.480147e-03 1.810494e+01 8.472214e-03 1.808800e+01 8.464289e-03 1.807108e+01 8.456371e-03 1.805417e+01 8.448460e-03 1.803728e+01 8.440557e-03 1.802041e+01 8.432661e-03 1.800356e+01 8.424773e-03 1.798671e+01 8.416892e-03 1.796989e+01 8.409018e-03 1.795308e+01 8.401152e-03 1.793628e+01 8.393292e-03 1.791950e+01 8.385441e-03 1.790274e+01 8.377597e-03 1.788599e+01 8.369760e-03 1.786926e+01 8.361930e-03 1.785255e+01 8.354108e-03 1.783585e+01 8.346293e-03 1.781916e+01 8.338485e-03 1.780249e+01 8.330685e-03 1.778584e+01 8.322892e-03 1.776920e+01 8.315106e-03 1.775258e+01 8.307328e-03 1.773597e+01 8.299557e-03 1.771938e+01 8.291792e-03 1.770280e+01 8.284036e-03 1.768624e+01 8.276287e-03 1.766970e+01 8.268544e-03 1.765317e+01 8.260810e-03 1.763666e+01 8.253082e-03 1.762016e+01 8.245361e-03 1.760367e+01 8.237648e-03 1.758721e+01 8.229942e-03 1.757075e+01 8.222243e-03 1.755432e+01 8.214552e-03 1.753790e+01 8.206868e-03 1.752149e+01 8.199190e-03 1.750510e+01 8.191520e-03 1.748872e+01 8.183857e-03 1.747236e+01 8.176201e-03 1.745602e+01 8.168553e-03 1.743969e+01 8.160911e-03 1.742337e+01 8.153277e-03 1.740708e+01 8.145650e-03 1.739079e+01 8.138030e-03 1.737452e+01 8.130417e-03 1.735827e+01 8.122812e-03 1.734203e+01 8.115213e-03 1.732581e+01 8.107621e-03 1.730960e+01 8.100037e-03 1.729341e+01 8.092460e-03 1.727723e+01 8.084889e-03 1.726107e+01 8.077326e-03 1.724492e+01 8.069770e-03 1.722879e+01 8.062221e-03 1.721267e+01 8.054679e-03 1.719657e+01 8.047145e-03 1.718048e+01 8.039617e-03 1.716441e+01 8.032097e-03 1.714836e+01 8.024583e-03 1.713231e+01 8.017076e-03 1.711629e+01 8.009576e-03 1.710028e+01 8.002084e-03 1.708428e+01 7.994598e-03 1.706830e+01 7.987119e-03 1.705233e+01 7.979647e-03 1.703638e+01 7.972183e-03 1.702044e+01 7.964725e-03 1.700452e+01 7.957274e-03 1.698861e+01 7.949831e-03 1.697272e+01 7.942394e-03 1.695684e+01 7.934964e-03 1.694098e+01 7.927542e-03 1.692513e+01 7.920125e-03 1.690930e+01 7.912716e-03 1.689348e+01 7.905315e-03 1.687768e+01 7.897919e-03 1.686189e+01 7.890531e-03 1.684612e+01 7.883149e-03 1.683036e+01 7.875775e-03 1.681462e+01 7.868407e-03 1.679889e+01 7.861047e-03 1.678317e+01 7.853693e-03 1.676747e+01 7.846346e-03 1.675179e+01 7.839006e-03 1.673611e+01 7.831673e-03 1.672046e+01 7.824347e-03 1.670482e+01 7.817028e-03 1.668919e+01 7.809715e-03 1.667358e+01 7.802410e-03 1.665798e+01 7.795111e-03 1.664240e+01 7.787819e-03 1.662683e+01 7.780533e-03 1.661128e+01 7.773255e-03 1.659574e+01 7.765983e-03 1.658021e+01 7.758718e-03 1.656470e+01 7.751461e-03 1.654921e+01 7.744209e-03 1.653373e+01 7.736965e-03 1.651826e+01 7.729727e-03 1.650281e+01 7.722497e-03 1.648737e+01 7.715272e-03 1.647194e+01 7.708055e-03 1.645654e+01 7.700845e-03 1.644114e+01 7.693640e-03 1.642576e+01 7.686444e-03 1.641040e+01 7.679253e-03 1.639504e+01 7.672070e-03 1.637971e+01 7.664892e-03 1.636439e+01 7.657722e-03 1.634908e+01 7.650559e-03 1.633378e+01 7.643402e-03 1.631850e+01 7.636252e-03 1.630324e+01 7.629108e-03 1.628799e+01 7.621971e-03 1.627275e+01 7.614842e-03 1.625753e+01 7.607718e-03 1.624232e+01 7.600601e-03 1.622713e+01 7.593491e-03 1.621194e+01 7.586388e-03 1.619678e+01 7.579291e-03 1.618163e+01 7.572201e-03 1.616649e+01 7.565117e-03 1.615137e+01 7.558040e-03 1.613626e+01 7.550970e-03 1.612116e+01 7.543907e-03 1.610608e+01 7.536849e-03 1.609102e+01 7.529799e-03 1.607596e+01 7.522755e-03 1.606092e+01 7.515718e-03 1.604590e+01 7.508687e-03 1.603089e+01 7.501663e-03 1.601589e+01 7.494646e-03 1.600091e+01 7.487635e-03 1.598594e+01 7.480630e-03 1.597099e+01 7.473633e-03 1.595605e+01 7.466641e-03 1.594112e+01 7.459656e-03 1.592621e+01 7.452678e-03 1.591131e+01 7.445707e-03 1.589643e+01 7.438741e-03 1.588156e+01 7.431783e-03 1.586670e+01 7.424830e-03 1.585186e+01 7.417885e-03 1.583703e+01 7.410946e-03 1.582221e+01 7.404013e-03 1.580741e+01 7.397087e-03 1.579263e+01 7.390167e-03 1.577785e+01 7.383254e-03 1.576309e+01 7.376347e-03 1.574835e+01 7.369447e-03 1.573361e+01 7.362553e-03 1.571890e+01 7.355665e-03 1.570419e+01 7.348785e-03 1.568950e+01 7.341910e-03 1.567482e+01 7.335042e-03 1.566016e+01 7.328180e-03 1.564551e+01 7.321325e-03 1.563088e+01 7.314476e-03 1.561625e+01 7.307634e-03 1.560165e+01 7.300798e-03 1.558705e+01 7.293968e-03 1.557247e+01 7.287145e-03 1.555790e+01 7.280328e-03 1.554335e+01 7.273518e-03 1.552881e+01 7.266713e-03 1.551428e+01 7.259916e-03 1.549977e+01 7.253124e-03 1.548527e+01 7.246339e-03 1.547078e+01 7.239561e-03 1.545631e+01 7.232788e-03 1.544185e+01 7.226022e-03 1.542741e+01 7.219262e-03 1.541297e+01 7.212509e-03 1.539856e+01 7.205762e-03 1.538415e+01 7.199022e-03 1.536976e+01 7.192287e-03 1.535538e+01 7.185559e-03 1.534102e+01 7.178837e-03 1.532667e+01 7.172122e-03 1.531233e+01 7.165412e-03 1.529801e+01 7.158709e-03 1.528369e+01 7.152013e-03 1.526940e+01 7.145322e-03 1.525511e+01 7.138638e-03 1.524084e+01 7.131960e-03 1.522659e+01 7.125288e-03 1.521234e+01 7.118623e-03 1.519811e+01 7.111964e-03 1.518389e+01 7.105311e-03 1.516969e+01 7.098664e-03 1.515550e+01 7.092023e-03 1.514132e+01 7.085389e-03 1.512716e+01 7.078761e-03 1.511301e+01 7.072139e-03 1.509887e+01 7.065523e-03 1.508474e+01 7.058914e-03 1.507063e+01 7.052310e-03 1.505653e+01 7.045713e-03 1.504245e+01 7.039122e-03 1.502838e+01 7.032537e-03 1.501432e+01 7.025959e-03 1.500027e+01 7.019386e-03 1.498624e+01 7.012820e-03 1.497222e+01 7.006260e-03 1.495822e+01 6.999705e-03 1.494422e+01 6.993158e-03 1.493024e+01 6.986616e-03 1.491628e+01 6.980080e-03 1.490232e+01 6.973550e-03 1.488838e+01 6.967027e-03 1.487446e+01 6.960509e-03 1.486054e+01 6.953998e-03 1.484664e+01 6.947493e-03 1.483275e+01 6.940994e-03 1.481888e+01 6.934501e-03 1.480501e+01 6.928014e-03 1.479116e+01 6.921533e-03 1.477733e+01 6.915058e-03 1.476350e+01 6.908590e-03 1.474969e+01 6.902127e-03 1.473590e+01 6.895670e-03 1.472211e+01 6.889219e-03 1.470834e+01 6.882775e-03 1.469458e+01 6.876336e-03 1.468083e+01 6.869903e-03 1.466710e+01 6.863477e-03 1.465338e+01 6.857057e-03 1.463967e+01 6.850642e-03 1.462598e+01 6.844233e-03 1.461230e+01 6.837831e-03 1.459863e+01 6.831434e-03 1.458497e+01 6.825044e-03 1.457133e+01 6.818659e-03 1.455769e+01 6.812281e-03 1.454408e+01 6.805908e-03 1.453047e+01 6.799541e-03 1.451688e+01 6.793180e-03 1.450330e+01 6.786826e-03 1.448973e+01 6.780477e-03 1.447618e+01 6.774134e-03 1.446263e+01 6.767797e-03 1.444911e+01 6.761466e-03 1.443559e+01 6.755141e-03 1.442208e+01 6.748822e-03 1.440859e+01 6.742509e-03 1.439511e+01 6.736201e-03 1.438165e+01 6.729899e-03 1.436819e+01 6.723604e-03 1.435475e+01 6.717314e-03 1.434133e+01 6.711030e-03 1.432791e+01 6.704753e-03 1.431451e+01 6.698481e-03 1.430112e+01 6.692214e-03 1.428774e+01 6.685954e-03 1.427437e+01 6.679700e-03 1.426102e+01 6.673451e-03 1.424768e+01 6.667208e-03 1.423435e+01 6.660971e-03 1.422103e+01 6.654740e-03 1.420773e+01 6.648515e-03 1.419444e+01 6.642296e-03 1.418116e+01 6.636082e-03 1.416790e+01 6.629874e-03 1.415464e+01 6.623672e-03 1.414140e+01 6.617476e-03 1.412817e+01 6.611286e-03 1.411496e+01 6.605101e-03 1.410175e+01 6.598922e-03 1.408856e+01 6.592749e-03 1.407538e+01 6.586582e-03 1.406221e+01 6.580420e-03 1.404906e+01 6.574264e-03 1.403592e+01 6.568115e-03 1.402279e+01 6.561970e-03 1.400967e+01 6.555832e-03 1.399656e+01 6.549699e-03 1.398347e+01 6.543572e-03 1.397039e+01 6.537451e-03 1.395732e+01 6.531335e-03 1.394426e+01 6.525225e-03 1.393122e+01 6.519121e-03 1.391819e+01 6.513023e-03 1.390517e+01 6.506930e-03 1.389216e+01 6.500843e-03 1.387916e+01 6.494762e-03 1.386618e+01 6.488686e-03 1.385321e+01 6.482617e-03 1.384025e+01 6.476552e-03 1.382730e+01 6.470494e-03 1.381437e+01 6.464441e-03 1.380144e+01 6.458393e-03 1.378853e+01 6.452352e-03 1.377564e+01 6.446316e-03 1.376275e+01 6.440286e-03 1.374988e+01 6.434261e-03 1.373701e+01 6.428242e-03 1.372416e+01 6.422229e-03 1.371132e+01 6.416221e-03 1.369850e+01 6.410219e-03 1.368568e+01 6.404222e-03 1.367288e+01 6.398231e-03 1.366009e+01 6.392246e-03 1.364731e+01 6.386266e-03 1.363454e+01 6.380292e-03 1.362179e+01 6.374324e-03 1.360905e+01 6.368360e-03 1.359632e+01 6.362403e-03 1.358360e+01 6.356451e-03 1.357089e+01 6.350505e-03 1.355820e+01 6.344565e-03 1.354551e+01 6.338629e-03 1.353284e+01 6.332700e-03 1.352018e+01 6.326776e-03 1.350753e+01 6.320857e-03 1.349490e+01 6.314944e-03 1.348227e+01 6.309037e-03 1.346966e+01 6.303135e-03 1.345706e+01 6.297239e-03 1.344447e+01 6.291348e-03 1.343190e+01 6.285463e-03 1.341933e+01 6.279583e-03 1.340678e+01 6.273708e-03 1.339424e+01 6.267840e-03 1.338171e+01 6.261976e-03 1.336919e+01 6.256118e-03 1.335668e+01 6.250266e-03 1.334419e+01 6.244419e-03 1.333170e+01 6.238578e-03 1.331923e+01 6.232742e-03 1.330677e+01 6.226911e-03 1.329433e+01 6.221086e-03 1.328189e+01 6.215267e-03 1.326946e+01 6.209453e-03 1.325705e+01 6.203644e-03 1.324465e+01 6.197840e-03 1.323226e+01 6.192043e-03 1.321988e+01 6.186250e-03 1.320751e+01 6.180463e-03 1.319516e+01 6.174682e-03 1.318282e+01 6.168906e-03 1.317048e+01 6.163135e-03 1.315816e+01 6.157369e-03 1.314585e+01 6.151609e-03 1.313356e+01 6.145855e-03 1.312127e+01 6.140105e-03 1.310900e+01 6.134361e-03 1.309673e+01 6.128623e-03 1.308448e+01 6.122890e-03 1.307224e+01 6.117162e-03 1.306001e+01 6.111440e-03 1.304780e+01 6.105723e-03 1.303559e+01 6.100011e-03 1.302340e+01 6.094305e-03 1.301121e+01 6.088604e-03 1.299904e+01 6.082908e-03 1.298688e+01 6.077218e-03 1.297473e+01 6.071533e-03 1.296260e+01 6.065853e-03 1.295047e+01 6.060179e-03 1.293835e+01 6.054510e-03 1.292625e+01 6.048846e-03 1.291416e+01 6.043187e-03 1.290208e+01 6.037534e-03 1.289001e+01 6.031887e-03 1.287795e+01 6.026244e-03 1.286590e+01 6.020607e-03 1.285387e+01 6.014974e-03 1.284184e+01 6.009348e-03 1.282983e+01 6.003726e-03 1.281783e+01 5.998110e-03 1.280584e+01 5.992499e-03 1.279386e+01 5.986893e-03 1.278189e+01 5.981293e-03 1.276993e+01 5.975697e-03 1.275799e+01 5.970107e-03 1.274605e+01 5.964522e-03 1.273413e+01 5.958943e-03 1.272222e+01 5.953368e-03 1.271032e+01 5.947799e-03 1.269843e+01 5.942235e-03 1.268655e+01 5.936677e-03 1.267468e+01 5.931123e-03 1.266282e+01 5.925575e-03 1.265098e+01 5.920032e-03 1.263914e+01 5.914493e-03 1.262732e+01 5.908961e-03 1.261551e+01 5.903433e-03 1.260371e+01 5.897911e-03 1.259192e+01 5.892394e-03 1.258014e+01 5.886881e-03 1.256837e+01 5.881374e-03 1.255661e+01 5.875872e-03 1.254486e+01 5.870376e-03 1.253313e+01 5.864884e-03 1.252141e+01 5.859398e-03 1.250969e+01 5.853917e-03 1.249799e+01 5.848441e-03 1.248630e+01 5.842970e-03 1.247462e+01 5.837504e-03 1.246295e+01 5.832043e-03 1.245129e+01 5.826587e-03 1.243964e+01 5.821137e-03 1.242801e+01 5.815691e-03 1.241638e+01 5.810251e-03 1.240476e+01 5.804816e-03 1.239316e+01 5.799386e-03 1.238157e+01 5.793960e-03 1.236998e+01 5.788540e-03 1.235841e+01 5.783125e-03 1.234685e+01 5.777715e-03 1.233530e+01 5.772311e-03 1.232376e+01 5.766911e-03 1.231223e+01 5.761516e-03 1.230072e+01 5.756126e-03 1.228921e+01 5.750742e-03 1.227771e+01 5.745362e-03 1.226623e+01 5.739988e-03 1.225475e+01 5.734618e-03 1.224329e+01 5.729253e-03 1.223184e+01 5.723894e-03 1.222039e+01 5.718539e-03 1.220896e+01 5.713190e-03 1.219754e+01 5.707846e-03 1.218613e+01 5.702506e-03 1.217473e+01 5.697172e-03 1.216334e+01 5.691842e-03 1.215196e+01 5.686518e-03 1.214060e+01 5.681198e-03 1.212924e+01 5.675884e-03 1.211789e+01 5.670574e-03 1.210656e+01 5.665269e-03 1.209523e+01 5.659970e-03 1.208392e+01 5.654675e-03 1.207261e+01 5.649385e-03 1.206132e+01 5.644100e-03 1.205004e+01 5.638821e-03 1.203876e+01 5.633546e-03 1.202750e+01 5.628276e-03 1.201625e+01 5.623010e-03 1.200501e+01 5.617750e-03 1.199378e+01 5.612495e-03 1.198256e+01 5.607245e-03 1.197135e+01 5.602000e-03 1.196015e+01 5.596759e-03 1.194896e+01 5.591524e-03 1.193779e+01 5.586293e-03 1.192662e+01 5.581067e-03 1.191546e+01 5.575846e-03 1.190432e+01 5.570631e-03 1.189318e+01 5.565419e-03 1.188205e+01 5.560213e-03 1.187094e+01 5.555012e-03 1.185983e+01 5.549815e-03 1.184874e+01 5.544623e-03 1.183766e+01 5.539437e-03 1.182658e+01 5.534254e-03 1.181552e+01 5.529078e-03 1.180447e+01 5.523905e-03 1.179342e+01 5.518738e-03 1.178239e+01 5.513575e-03 1.177137e+01 5.508418e-03 1.176036e+01 5.503265e-03 1.174936e+01 5.498117e-03 1.173836e+01 5.492973e-03 1.172738e+01 5.487835e-03 1.171641e+01 5.482701e-03 1.170545e+01 5.477572e-03 1.169450e+01 5.472448e-03 1.168356e+01 5.467329e-03 1.167263e+01 5.462214e-03 1.166171e+01 5.457105e-03 1.165080e+01 5.452000e-03 1.163991e+01 5.446900e-03 1.162902e+01 5.441804e-03 1.161814e+01 5.436714e-03 1.160727e+01 5.431628e-03 1.159641e+01 5.426547e-03 1.158556e+01 5.421470e-03 1.157473e+01 5.416399e-03 1.156390e+01 5.411332e-03 1.155308e+01 5.406270e-03 1.154227e+01 5.401213e-03 1.153148e+01 5.396160e-03 1.152069e+01 5.391112e-03 1.150991e+01 5.386069e-03 1.149914e+01 5.381030e-03 1.148839e+01 5.375997e-03 1.147764e+01 5.370968e-03 1.146690e+01 5.365943e-03 1.145618e+01 5.360924e-03 1.144546e+01 5.355909e-03 1.143475e+01 5.350898e-03 1.142406e+01 5.345893e-03 1.141337e+01 5.340892e-03 1.140269e+01 5.335896e-03 1.139203e+01 5.330904e-03 1.138137e+01 5.325917e-03 1.137072e+01 5.320935e-03 1.136008e+01 5.315958e-03 1.134946e+01 5.310985e-03 1.133884e+01 5.306017e-03 1.132823e+01 5.301053e-03 1.131764e+01 5.296094e-03 1.130705e+01 5.291140e-03 1.129647e+01 5.286190e-03 1.128590e+01 5.281245e-03 1.127535e+01 5.276305e-03 1.126480e+01 5.271369e-03 1.125426e+01 5.266438e-03 1.124373e+01 5.261511e-03 1.123322e+01 5.256589e-03 1.122271e+01 5.251672e-03 1.121221e+01 5.246759e-03 1.120172e+01 5.241851e-03 1.119124e+01 5.236947e-03 1.118077e+01 5.232048e-03 1.117031e+01 5.227154e-03 1.115986e+01 5.222264e-03 1.114942e+01 5.217379e-03 1.113899e+01 5.212498e-03 1.112857e+01 5.207622e-03 1.111816e+01 5.202751e-03 1.110776e+01 5.197884e-03 1.109737e+01 5.193021e-03 1.108699e+01 5.188163e-03 1.107662e+01 5.183310e-03 1.106626e+01 5.178461e-03 1.105591e+01 5.173617e-03 1.104556e+01 5.168777e-03 1.103523e+01 5.163942e-03 1.102491e+01 5.159111e-03 1.101459e+01 5.154285e-03 1.100429e+01 5.149464e-03 1.099400e+01 5.144646e-03 1.098371e+01 5.139834e-03 1.097344e+01 5.135025e-03 1.096317e+01 5.130222e-03 1.095292e+01 5.125423e-03 1.094267e+01 5.120628e-03 1.093243e+01 5.115838e-03 1.092221e+01 5.111052e-03 1.091199e+01 5.106271e-03 1.090178e+01 5.101494e-03 1.089158e+01 5.096722e-03 1.088140e+01 5.091954e-03 1.087122e+01 5.087191e-03 1.086105e+01 5.082432e-03 1.085089e+01 5.077678e-03 1.084074e+01 5.072928e-03 1.083059e+01 5.068182e-03 1.082046e+01 5.063441e-03 1.081034e+01 5.058704e-03 1.080023e+01 5.053972e-03 1.079012e+01 5.049245e-03 1.078003e+01 5.044521e-03 1.076995e+01 5.039802e-03 1.075987e+01 5.035087e-03 1.074981e+01 5.030377e-03 1.073975e+01 5.025672e-03 1.072970e+01 5.020970e-03 1.071967e+01 5.016273e-03 1.070964e+01 5.011581e-03 1.069962e+01 5.006893e-03 1.068961e+01 5.002209e-03 1.067961e+01 4.997530e-03 1.066962e+01 4.992854e-03 1.065964e+01 4.988184e-03 1.064967e+01 4.983518e-03 1.063971e+01 4.978856e-03 1.062975e+01 4.974198e-03 1.061981e+01 4.969545e-03 1.060987e+01 4.964896e-03 1.059995e+01 4.960252e-03 1.059003e+01 4.955612e-03 1.058013e+01 4.950976e-03 1.057023e+01 4.946344e-03 1.056034e+01 4.941717e-03 1.055046e+01 4.937094e-03 1.054059e+01 4.932476e-03 1.053073e+01 4.927862e-03 1.052088e+01 4.923252e-03 1.051104e+01 4.918647e-03 1.050121e+01 4.914045e-03 1.049138e+01 4.909448e-03 1.048157e+01 4.904856e-03 1.047176e+01 4.900267e-03 1.046197e+01 4.895683e-03 1.045218e+01 4.891104e-03 1.044240e+01 4.886528e-03 1.043264e+01 4.881957e-03 1.042288e+01 4.877390e-03 1.041313e+01 4.872827e-03 1.040338e+01 4.868269e-03 1.039365e+01 4.863715e-03 1.038393e+01 4.859165e-03 1.037422e+01 4.854619e-03 1.036451e+01 4.850078e-03 1.035482e+01 4.845541e-03 1.034513e+01 4.841008e-03 1.033545e+01 4.836480e-03 1.032578e+01 4.831955e-03 1.031612e+01 4.827436e-03 1.030647e+01 4.822920e-03 1.029683e+01 4.818408e-03 1.028720e+01 4.813900e-03 1.027758e+01 4.809397e-03 1.026796e+01 4.804898e-03 1.025836e+01 4.800403e-03 1.024876e+01 4.795913e-03 1.023917e+01 4.791426e-03 1.022960e+01 4.786944e-03 1.022003e+01 4.782466e-03 1.021047e+01 4.777992e-03 1.020091e+01 4.773523e-03 1.019137e+01 4.769057e-03 1.018184e+01 4.764596e-03 1.017231e+01 4.760139e-03 1.016280e+01 4.755686e-03 1.015329e+01 4.751237e-03 1.014379e+01 4.746792e-03 1.013430e+01 4.742352e-03 1.012482e+01 4.737916e-03 1.011535e+01 4.733484e-03 1.010589e+01 4.729056e-03 1.009643e+01 4.724632e-03 1.008699e+01 4.720212e-03 1.007755e+01 4.715797e-03 1.006813e+01 4.711385e-03 1.005871e+01 4.706978e-03 1.004930e+01 4.702575e-03 1.003990e+01 4.698175e-03 1.003051e+01 4.693781e-03 1.002112e+01 4.689389e-03 1.001175e+01 4.685003e-03 1.000238e+01 4.680620e-03 9.993026e+00 4.676241e-03 9.983678e+00 4.671867e-03 9.974339e+00 4.667497e-03 9.965008e+00 4.663131e-03 9.955686e+00 4.658768e-03 9.946373e+00 4.654410e-03 9.937068e+00 4.650056e-03 9.927773e+00 4.645706e-03 9.918486e+00 4.641360e-03 9.909207e+00 4.637018e-03 9.899938e+00 4.632681e-03 9.890676e+00 4.628347e-03 9.881424e+00 4.624018e-03 9.872180e+00 4.619692e-03 9.862946e+00 4.615370e-03 9.853719e+00 4.611053e-03 9.844501e+00 4.606739e-03 9.835292e+00 4.602430e-03 9.826092e+00 4.598124e-03 9.816899e+00 4.593823e-03 9.807716e+00 4.589526e-03 9.798541e+00 4.585233e-03 9.789375e+00 4.580943e-03 9.780217e+00 4.576658e-03 9.771069e+00 4.572377e-03 9.761928e+00 4.568099e-03 9.752796e+00 4.563826e-03 9.743672e+00 4.559556e-03 9.734558e+00 4.555292e-03 9.725451e+00 4.551030e-03 9.716353e+00 4.546773e-03 9.707264e+00 4.542519e-03 9.698184e+00 4.538270e-03 9.689112e+00 4.534025e-03 9.680048e+00 4.529783e-03 9.670992e+00 4.525546e-03 9.661945e+00 4.521312e-03 9.652907e+00 4.517083e-03 9.643877e+00 4.512857e-03 9.634855e+00 4.508635e-03 9.625842e+00 4.504418e-03 9.616838e+00 4.500204e-03 9.607841e+00 4.495994e-03 9.598854e+00 4.491788e-03 9.589874e+00 4.487587e-03 9.580903e+00 4.483389e-03 9.571941e+00 4.479195e-03 9.562986e+00 4.475005e-03 9.554041e+00 4.470818e-03 9.545103e+00 4.466636e-03 9.536175e+00 4.462458e-03 9.527253e+00 4.458283e-03 9.518341e+00 4.454113e-03 9.509438e+00 4.449946e-03 9.500542e+00 4.445783e-03 9.491654e+00 4.441625e-03 9.482775e+00 4.437469e-03 9.473904e+00 4.433318e-03 9.465042e+00 4.429171e-03 9.456187e+00 4.425028e-03 9.447342e+00 4.420888e-03 9.438504e+00 4.416753e-03 9.429674e+00 4.412621e-03 9.420854e+00 4.408493e-03 9.412041e+00 4.404369e-03 9.403236e+00 4.400249e-03 9.394440e+00 4.396133e-03 9.385652e+00 4.392020e-03 9.376871e+00 4.387912e-03 9.368100e+00 4.383807e-03 9.359336e+00 4.379706e-03 9.350581e+00 4.375609e-03 9.341834e+00 4.371516e-03 9.333095e+00 4.367427e-03 9.324364e+00 4.363341e-03 9.315641e+00 4.359259e-03 9.306927e+00 4.355181e-03 9.298221e+00 4.351107e-03 9.289522e+00 4.347037e-03 9.280832e+00 4.342970e-03 9.272151e+00 4.338908e-03 9.263477e+00 4.334849e-03 9.254811e+00 4.330794e-03 9.246154e+00 4.326743e-03 9.237504e+00 4.322695e-03 9.228863e+00 4.318651e-03 9.220230e+00 4.314611e-03 9.211604e+00 4.310575e-03 9.202988e+00 4.306543e-03 9.194378e+00 4.302514e-03 9.185778e+00 4.298489e-03 9.177184e+00 4.294468e-03 9.168599e+00 4.290451e-03 9.160023e+00 4.286437e-03 9.151454e+00 4.282427e-03 9.142893e+00 4.278421e-03 9.134340e+00 4.274419e-03 9.125795e+00 4.270420e-03 9.117258e+00 4.266425e-03 9.108729e+00 4.262435e-03 9.100208e+00 4.258447e-03 9.091696e+00 4.254464e-03 9.083191e+00 4.250484e-03 9.074694e+00 4.246508e-03 9.066205e+00 4.242535e-03 9.057724e+00 4.238566e-03 9.049251e+00 4.234601e-03 9.040785e+00 4.230640e-03 9.032328e+00 4.226682e-03 9.023878e+00 4.222728e-03 9.015437e+00 4.218778e-03 9.007004e+00 4.214832e-03 8.998578e+00 4.210889e-03 8.990160e+00 4.206950e-03 8.981750e+00 4.203015e-03 8.973348e+00 4.199083e-03 8.964953e+00 4.195155e-03 8.956567e+00 4.191230e-03 8.948189e+00 4.187309e-03 8.939817e+00 4.183393e-03 8.931455e+00 4.179479e-03 8.923100e+00 4.175569e-03 8.914753e+00 4.171663e-03 8.906413e+00 4.167761e-03 8.898082e+00 4.163862e-03 8.889758e+00 4.159967e-03 8.881442e+00 4.156075e-03 8.873134e+00 4.152187e-03 8.864833e+00 4.148303e-03 8.856541e+00 4.144423e-03 8.848255e+00 4.140546e-03 8.839978e+00 4.136672e-03 8.831709e+00 4.132803e-03 8.823447e+00 4.128937e-03 8.815193e+00 4.125074e-03 8.806947e+00 4.121215e-03 8.798708e+00 4.117360e-03 8.790477e+00 4.113508e-03 8.782254e+00 4.109660e-03 8.774038e+00 4.105816e-03 8.765831e+00 4.101975e-03 8.757630e+00 4.098138e-03 8.749438e+00 4.094304e-03 8.741254e+00 4.090474e-03 8.733076e+00 4.086648e-03 8.724907e+00 4.082825e-03 8.716745e+00 4.079005e-03 8.708591e+00 4.075190e-03 8.700444e+00 4.071377e-03 8.692306e+00 4.067569e-03 8.684174e+00 4.063764e-03 8.676050e+00 4.059962e-03 8.667934e+00 4.056164e-03 8.659825e+00 4.052370e-03 8.651725e+00 4.048579e-03 8.643631e+00 4.044792e-03 8.635546e+00 4.041008e-03 8.627467e+00 4.037227e-03 8.619396e+00 4.033451e-03 8.611334e+00 4.029678e-03 8.603278e+00 4.025908e-03 8.595230e+00 4.022142e-03 8.587190e+00 4.018379e-03 8.579156e+00 4.014621e-03 8.571131e+00 4.010865e-03 8.563113e+00 4.007113e-03 8.555102e+00 4.003365e-03 8.547099e+00 3.999619e-03 8.539104e+00 3.995878e-03 8.531116e+00 3.992140e-03 8.523135e+00 3.988406e-03 8.515162e+00 3.984674e-03 8.507196e+00 3.980947e-03 8.499238e+00 3.977223e-03 8.491287e+00 3.973502e-03 8.483344e+00 3.969785e-03 8.475409e+00 3.966072e-03 8.467480e+00 3.962362e-03 8.459559e+00 3.958655e-03 8.451646e+00 3.954952e-03 8.443739e+00 3.951252e-03 8.435841e+00 3.947556e-03 8.427949e+00 3.943863e-03 8.420065e+00 3.940174e-03 8.412189e+00 3.936488e-03 8.404319e+00 3.932805e-03 8.396457e+00 3.929127e-03 8.388602e+00 3.925451e-03 8.380755e+00 3.921779e-03 8.372915e+00 3.918110e-03 8.365083e+00 3.914445e-03 8.357258e+00 3.910783e-03 8.349440e+00 3.907125e-03 8.341629e+00 3.903470e-03 8.333826e+00 3.899818e-03 8.326030e+00 3.896170e-03 8.318241e+00 3.892525e-03 8.310460e+00 3.888884e-03 8.302686e+00 3.885246e-03 8.294919e+00 3.881611e-03 8.287159e+00 3.877980e-03 8.279407e+00 3.874353e-03 8.271662e+00 3.870728e-03 8.263924e+00 3.867107e-03 8.256193e+00 3.863490e-03 8.248470e+00 3.859876e-03 8.240754e+00 3.856265e-03 8.233045e+00 3.852658e-03 8.225343e+00 3.849054e-03 8.217649e+00 3.845453e-03 8.209961e+00 3.841856e-03 8.202281e+00 3.838262e-03 8.194609e+00 3.834671e-03 8.186942e+00 3.831084e-03 8.179284e+00 3.827500e-03 8.171633e+00 3.823920e-03 8.163988e+00 3.820342e-03 8.156351e+00 3.816769e-03 8.148722e+00 3.813198e-03 8.141098e+00 3.809631e-03 8.133483e+00 3.806067e-03 8.125875e+00 3.802507e-03 8.118273e+00 3.798950e-03 8.110679e+00 3.795396e-03 8.103091e+00 3.791846e-03 8.095510e+00 3.788298e-03 8.087938e+00 3.784755e-03 8.080372e+00 3.781214e-03 8.072813e+00 3.777677e-03 8.065261e+00 3.774143e-03 8.057716e+00 3.770612e-03 8.050179e+00 3.767085e-03 8.042648e+00 3.763561e-03 8.035125e+00 3.760040e-03 8.027608e+00 3.756523e-03 8.020099e+00 3.753009e-03 8.012596e+00 3.749498e-03 8.005100e+00 3.745991e-03 7.997612e+00 3.742486e-03 7.990130e+00 3.738986e-03 7.982656e+00 3.735488e-03 7.975189e+00 3.731993e-03 7.967728e+00 3.728502e-03 7.960275e+00 3.725014e-03 7.952828e+00 3.721530e-03 7.945388e+00 3.718049e-03 7.937956e+00 3.714571e-03 7.930530e+00 3.711096e-03 7.923111e+00 3.707624e-03 7.915699e+00 3.704156e-03 7.908295e+00 3.700691e-03 7.900897e+00 3.697229e-03 7.893506e+00 3.693770e-03 7.886122e+00 3.690315e-03 7.878745e+00 3.686863e-03 7.871374e+00 3.683414e-03 7.864011e+00 3.679968e-03 7.856655e+00 3.676526e-03 7.849305e+00 3.673086e-03 7.841962e+00 3.669650e-03 7.834626e+00 3.666217e-03 7.827297e+00 3.662788e-03 7.819975e+00 3.659361e-03 7.812660e+00 3.655938e-03 7.805351e+00 3.652518e-03 7.798049e+00 3.649101e-03 7.790755e+00 3.645688e-03 7.783467e+00 3.642277e-03 7.776186e+00 3.638870e-03 7.768911e+00 3.635466e-03 7.761644e+00 3.632065e-03 7.754383e+00 3.628667e-03 7.747129e+00 3.625273e-03 7.739882e+00 3.621882e-03 7.732642e+00 3.618494e-03 7.725408e+00 3.615109e-03 7.718181e+00 3.611727e-03 7.710961e+00 3.608348e-03 7.703748e+00 3.604973e-03 7.696541e+00 3.601600e-03 7.689342e+00 3.598231e-03 7.682148e+00 3.594865e-03 7.674962e+00 3.591502e-03 7.667782e+00 3.588143e-03 7.660609e+00 3.584786e-03 7.653443e+00 3.581433e-03 7.646284e+00 3.578082e-03 7.639131e+00 3.574735e-03 7.631985e+00 3.571391e-03 7.624846e+00 3.568050e-03 7.617712e+00 3.564712e-03 7.610587e+00 3.561378e-03 7.603467e+00 3.558046e-03 7.596354e+00 3.554718e-03 7.589248e+00 3.551393e-03 7.582149e+00 3.548070e-03 7.575056e+00 3.544751e-03 7.567970e+00 3.541435e-03 7.560890e+00 3.538122e-03 7.553817e+00 3.534813e-03 7.546751e+00 3.531506e-03 7.539691e+00 3.528202e-03 7.532638e+00 3.524902e-03 7.525591e+00 3.521604e-03 7.518552e+00 3.518310e-03 7.511518e+00 3.515019e-03 7.504491e+00 3.511731e-03 7.497471e+00 3.508446e-03 7.490458e+00 3.505164e-03 7.483451e+00 3.501885e-03 7.476450e+00 3.498609e-03 7.469456e+00 3.495336e-03 7.462469e+00 3.492066e-03 7.455488e+00 3.488799e-03 7.448514e+00 3.485536e-03 7.441546e+00 3.482275e-03 7.434585e+00 3.479018e-03 7.427630e+00 3.475763e-03 7.420681e+00 3.472512e-03 7.413740e+00 3.469263e-03 7.406805e+00 3.466018e-03 7.399876e+00 3.462776e-03 7.392953e+00 3.459536e-03 7.386037e+00 3.456300e-03 7.379128e+00 3.453067e-03 7.372225e+00 3.449837e-03 7.365329e+00 3.446609e-03 7.358439e+00 3.443385e-03 7.351555e+00 3.440164e-03 7.344678e+00 3.436946e-03 7.337808e+00 3.433731e-03 7.330943e+00 3.430519e-03 7.324085e+00 3.427309e-03 7.317234e+00 3.424103e-03 7.310389e+00 3.420900e-03 7.303550e+00 3.417700e-03 7.296718e+00 3.414503e-03 7.289892e+00 3.411309e-03 7.283073e+00 3.408118e-03 7.276260e+00 3.404930e-03 7.269453e+00 3.401744e-03 7.262653e+00 3.398562e-03 7.255859e+00 3.395383e-03 7.249072e+00 3.392207e-03 7.242290e+00 3.389033e-03 7.235515e+00 3.385863e-03 7.228747e+00 3.382696e-03 7.221984e+00 3.379531e-03 7.215229e+00 3.376370e-03 7.208479e+00 3.373211e-03 7.201735e+00 3.370056e-03 7.194999e+00 3.366903e-03 7.188268e+00 3.363754e-03 7.181544e+00 3.360607e-03 7.174826e+00 3.357463e-03 7.168114e+00 3.354323e-03 7.161408e+00 3.351185e-03 7.154709e+00 3.348050e-03 7.148016e+00 3.344918e-03 7.141329e+00 3.341789e-03 7.134649e+00 3.338663e-03 7.127975e+00 3.335539e-03 7.121307e+00 3.332419e-03 7.114645e+00 3.329302e-03 7.107990e+00 3.326187e-03 7.101340e+00 3.323076e-03 7.094697e+00 3.319967e-03 7.088060e+00 3.316862e-03 7.081430e+00 3.313759e-03 7.074805e+00 3.310659e-03 7.068187e+00 3.307562e-03 7.061575e+00 3.304468e-03 7.054969e+00 3.301376e-03 7.048370e+00 3.298288e-03 7.041776e+00 3.295203e-03 7.035189e+00 3.292120e-03 7.028608e+00 3.289041e-03 7.022033e+00 3.285964e-03 7.015464e+00 3.282890e-03 7.008901e+00 3.279819e-03 7.002345e+00 3.276751e-03 6.995794e+00 3.273685e-03 6.989250e+00 3.270623e-03 6.982712e+00 3.267563e-03 6.976180e+00 3.264507e-03 6.969654e+00 3.261453e-03 6.963134e+00 3.258402e-03 6.956620e+00 3.255354e-03 6.950112e+00 3.252309e-03 6.943611e+00 3.249266e-03 6.937115e+00 3.246227e-03 6.930626e+00 3.243190e-03 6.924142e+00 3.240156e-03 6.917665e+00 3.237125e-03 6.911194e+00 3.234097e-03 6.904729e+00 3.231071e-03 6.898270e+00 3.228049e-03 6.891817e+00 3.225029e-03 6.885370e+00 3.222012e-03 6.878929e+00 3.218998e-03 6.872494e+00 3.215987e-03 6.866065e+00 3.212978e-03 6.859642e+00 3.209973e-03 6.853225e+00 3.206970e-03 6.846814e+00 3.203970e-03 6.840409e+00 3.200973e-03 6.834010e+00 3.197978e-03 6.827617e+00 3.194987e-03 6.821230e+00 3.191998e-03 6.814849e+00 3.189012e-03 6.808474e+00 3.186029e-03 6.802105e+00 3.183048e-03 6.795742e+00 3.180071e-03 6.789384e+00 3.177096e-03 6.783033e+00 3.174124e-03 6.776688e+00 3.171155e-03 6.770349e+00 3.168188e-03 6.764015e+00 3.165224e-03 6.757688e+00 3.162263e-03 6.751366e+00 3.159305e-03 6.745050e+00 3.156350e-03 6.738741e+00 3.153397e-03 6.732437e+00 3.150447e-03 6.726139e+00 3.147500e-03 6.719847e+00 3.144556e-03 6.713561e+00 3.141614e-03 6.707281e+00 3.138675e-03 6.701006e+00 3.135739e-03 6.694737e+00 3.132806e-03 6.688475e+00 3.129875e-03 6.682218e+00 3.126947e-03 6.675967e+00 3.124022e-03 6.669722e+00 3.121100e-03 6.663483e+00 3.118180e-03 6.657249e+00 3.115263e-03 6.651021e+00 3.112349e-03 6.644800e+00 3.109437e-03 6.638584e+00 3.106529e-03 6.632374e+00 3.103623e-03 6.626169e+00 3.100719e-03 6.619971e+00 3.097819e-03 6.613778e+00 3.094921e-03 6.607591e+00 3.092026e-03 6.601410e+00 3.089133e-03 6.595235e+00 3.086244e-03 6.589065e+00 3.083356e-03 6.582901e+00 3.080472e-03 6.576743e+00 3.077590e-03 6.570591e+00 3.074711e-03 6.564444e+00 3.071835e-03 6.558303e+00 3.068961e-03 6.552168e+00 3.066091e-03 6.546039e+00 3.063222e-03 6.539916e+00 3.060357e-03 6.533798e+00 3.057494e-03 6.527686e+00 3.054634e-03 6.521579e+00 3.051776e-03 6.515479e+00 3.048921e-03 6.509384e+00 3.046069e-03 6.503294e+00 3.043220e-03 6.497211e+00 3.040373e-03 6.491133e+00 3.037529e-03 6.485061e+00 3.034687e-03 6.478994e+00 3.031848e-03 6.472933e+00 3.029012e-03 6.466878e+00 3.026179e-03 6.460828e+00 3.023348e-03 6.454784e+00 3.020520e-03 6.448746e+00 3.017694e-03 6.442714e+00 3.014871e-03 6.436687e+00 3.012051e-03 6.430665e+00 3.009233e-03 6.424650e+00 3.006418e-03 6.418640e+00 3.003606e-03 6.412635e+00 3.000796e-03 6.406637e+00 2.997989e-03 6.400643e+00 2.995184e-03 6.394656e+00 2.992383e-03 6.388674e+00 2.989583e-03 6.382698e+00 2.986787e-03 6.376727e+00 2.983992e-03 6.370761e+00 2.981201e-03 6.364802e+00 2.978412e-03 6.358848e+00 2.975626e-03 6.352900e+00 2.972843e-03 6.346957e+00 2.970062e-03 6.341019e+00 2.967283e-03 6.335087e+00 2.964507e-03 6.329161e+00 2.961734e-03 6.323240e+00 2.958964e-03 6.317325e+00 2.956196e-03 6.311416e+00 2.953430e-03 6.305511e+00 2.950667e-03 6.299613e+00 2.947907e-03 6.293720e+00 2.945150e-03 6.287832e+00 2.942394e-03 6.281950e+00 2.939642e-03 6.276074e+00 2.936892e-03 6.270203e+00 2.934145e-03 6.264337e+00 2.931400e-03 6.258477e+00 2.928658e-03 6.252623e+00 2.925918e-03 6.246774e+00 2.923181e-03 6.240930e+00 2.920446e-03 6.235092e+00 2.917714e-03 6.229259e+00 2.914985e-03 6.223432e+00 2.912258e-03 6.217610e+00 2.909534e-03 6.211794e+00 2.906812e-03 6.205983e+00 2.904093e-03 6.200177e+00 2.901376e-03 6.194377e+00 2.898662e-03 6.188583e+00 2.895950e-03 6.182794e+00 2.893241e-03 6.177010e+00 2.890535e-03 6.171231e+00 2.887831e-03 6.165458e+00 2.885129e-03 6.159691e+00 2.882431e-03 6.153929e+00 2.879734e-03 6.148172e+00 2.877040e-03 6.142420e+00 2.874349e-03 6.136674e+00 2.871660e-03 6.130934e+00 2.868974e-03 6.125198e+00 2.866290e-03 6.119469e+00 2.863609e-03 6.113744e+00 2.860930e-03 6.108025e+00 2.858253e-03 6.102311e+00 2.855580e-03 6.096602e+00 2.852908e-03 6.090899e+00 2.850240e-03 6.085202e+00 2.847573e-03 6.079509e+00 2.844909e-03 6.073822e+00 2.842248e-03 6.068140e+00 2.839589e-03 6.062464e+00 2.836933e-03 6.056792e+00 2.834279e-03 6.051126e+00 2.831628e-03 6.045466e+00 2.828979e-03 6.039811e+00 2.826333e-03 6.034161e+00 2.823689e-03 6.028516e+00 2.821047e-03 6.022876e+00 2.818408e-03 6.017242e+00 2.815772e-03 6.011613e+00 2.813138e-03 6.005990e+00 2.810506e-03 6.000371e+00 2.807877e-03 5.994758e+00 2.805250e-03 5.989151e+00 2.802626e-03 5.983548e+00 2.800004e-03 5.977950e+00 2.797385e-03 5.972358e+00 2.794768e-03 5.966771e+00 2.792154e-03 5.961190e+00 2.789542e-03 5.955613e+00 2.786932e-03 5.950042e+00 2.784325e-03 5.944476e+00 2.781720e-03 5.938915e+00 2.779118e-03 5.933359e+00 2.776518e-03 5.927809e+00 2.773921e-03 5.922264e+00 2.771326e-03 5.916724e+00 2.768734e-03 5.911189e+00 2.766144e-03 5.905659e+00 2.763556e-03 5.900135e+00 2.760971e-03 5.894615e+00 2.758388e-03 5.889101e+00 2.755808e-03 5.883592e+00 2.753230e-03 5.878088e+00 2.750654e-03 5.872589e+00 2.748081e-03 5.867095e+00 2.745510e-03 5.861607e+00 2.742942e-03 5.856124e+00 2.740376e-03 5.850646e+00 2.737813e-03 5.845172e+00 2.735252e-03 5.839705e+00 2.732693e-03 5.834242e+00 2.730136e-03 5.828784e+00 2.727583e-03 5.823331e+00 2.725031e-03 5.817884e+00 2.722482e-03 5.812441e+00 2.719935e-03 5.807004e+00 2.717391e-03 5.801572e+00 2.714849e-03 5.796145e+00 2.712309e-03 5.790723e+00 2.709772e-03 5.785306e+00 2.707237e-03 5.779894e+00 2.704704e-03 5.774487e+00 2.702174e-03 5.769085e+00 2.699646e-03 5.763689e+00 2.697121e-03 5.758296e+00 2.694598e-03 5.752910e+00 2.692077e-03 5.747529e+00 2.689559e-03 5.742152e+00 2.687043e-03 5.736780e+00 2.684529e-03 5.731413e+00 2.682018e-03 5.726052e+00 2.679509e-03 5.720695e+00 2.677002e-03 5.715344e+00 2.674498e-03 5.709998e+00 2.671996e-03 5.704656e+00 2.669497e-03 5.699319e+00 2.667000e-03 5.693988e+00 2.664505e-03 5.688662e+00 2.662012e-03 5.683340e+00 2.659522e-03 5.678023e+00 2.657034e-03 5.672712e+00 2.654548e-03 5.667405e+00 2.652065e-03 5.662104e+00 2.649584e-03 5.656807e+00 2.647106e-03 5.651515e+00 2.644629e-03 5.646228e+00 2.642155e-03 5.640946e+00 2.639684e-03 5.635670e+00 2.637214e-03 5.630398e+00 2.634747e-03 5.625131e+00 2.632283e-03 5.619869e+00 2.629820e-03 5.614612e+00 2.627360e-03 5.609359e+00 2.624902e-03 5.604112e+00 2.622447e-03 5.598869e+00 2.619994e-03 5.593632e+00 2.617543e-03 5.588399e+00 2.615094e-03 5.583171e+00 2.612648e-03 5.577949e+00 2.610204e-03 5.572731e+00 2.607762e-03 5.567518e+00 2.605323e-03 5.562309e+00 2.602885e-03 5.557106e+00 2.600451e-03 5.551908e+00 2.598018e-03 5.546714e+00 2.595588e-03 5.541525e+00 2.593159e-03 5.536341e+00 2.590734e-03 5.531162e+00 2.588310e-03 5.525988e+00 2.585889e-03 5.520819e+00 2.583470e-03 5.515654e+00 2.581053e-03 5.510494e+00 2.578639e-03 5.505340e+00 2.576226e-03 5.500189e+00 2.573817e-03 5.495044e+00 2.571409e-03 5.489904e+00 2.569003e-03 5.484768e+00 2.566600e-03 5.479638e+00 2.564199e-03 5.474512e+00 2.561800e-03 5.469390e+00 2.559404e-03 5.464274e+00 2.557010e-03 5.459162e+00 2.554618e-03 5.454055e+00 2.552228e-03 5.448953e+00 2.549841e-03 5.443856e+00 2.547455e-03 5.438764e+00 2.545072e-03 5.433676e+00 2.542691e-03 5.428593e+00 2.540313e-03 5.423514e+00 2.537936e-03 5.418441e+00 2.535562e-03 5.413372e+00 2.533190e-03 5.408308e+00 2.530821e-03 5.403249e+00 2.528453e-03 5.398194e+00 2.526088e-03 5.393145e+00 2.523725e-03 5.388100e+00 2.521364e-03 5.383059e+00 2.519005e-03 5.378024e+00 2.516649e-03 5.372993e+00 2.514295e-03 5.367966e+00 2.511943e-03 5.362945e+00 2.509593e-03 5.357928e+00 2.507245e-03 5.352916e+00 2.504900e-03 5.347908e+00 2.502556e-03 5.342906e+00 2.500215e-03 5.337907e+00 2.497877e-03 5.332914e+00 2.495540e-03 5.327925e+00 2.493205e-03 5.322941e+00 2.490873e-03 5.317962e+00 2.488543e-03 5.312987e+00 2.486215e-03 5.308017e+00 2.483889e-03 5.303051e+00 2.481566e-03 5.298090e+00 2.479244e-03 5.293135e+00 2.476925e-03 5.288183e+00 2.474608e-03 5.283236e+00 2.472293e-03 5.278294e+00 2.469980e-03 5.273356e+00 2.467670e-03 5.268423e+00 2.465361e-03 5.263494e+00 2.463055e-03 5.258571e+00 2.460751e-03 5.253652e+00 2.458449e-03 5.248737e+00 2.456149e-03 5.243827e+00 2.453852e-03 5.238922e+00 2.451556e-03 5.234021e+00 2.449263e-03 5.229125e+00 2.446972e-03 5.224233e+00 2.444682e-03 5.219346e+00 2.442396e-03 5.214463e+00 2.440111e-03 5.209586e+00 2.437828e-03 5.204712e+00 2.435548e-03 5.199843e+00 2.433269e-03 5.194979e+00 2.430993e-03 5.190119e+00 2.428719e-03 5.185264e+00 2.426447e-03 5.180414e+00 2.424177e-03 5.175568e+00 2.421909e-03 5.170726e+00 2.419644e-03 5.165889e+00 2.417380e-03 5.161057e+00 2.415119e-03 5.156229e+00 2.412860e-03 5.151405e+00 2.410603e-03 5.146586e+00 2.408348e-03 5.141771e+00 2.406095e-03 5.136961e+00 2.403844e-03 5.132156e+00 2.401595e-03 5.127355e+00 2.399348e-03 5.122559e+00 2.397104e-03 5.117767e+00 2.394862e-03 5.112979e+00 2.392621e-03 5.108196e+00 2.390383e-03 5.103418e+00 2.388147e-03 5.098644e+00 2.385913e-03 5.093874e+00 2.383681e-03 5.089109e+00 2.381451e-03 5.084348e+00 2.379223e-03 5.079592e+00 2.376998e-03 5.074841e+00 2.374774e-03 5.070093e+00 2.372553e-03 5.065350e+00 2.370333e-03 5.060612e+00 2.368116e-03 5.055878e+00 2.365900e-03 5.051148e+00 2.363687e-03 5.046423e+00 2.361476e-03 5.041702e+00 2.359267e-03 5.036986e+00 2.357060e-03 5.032274e+00 2.354855e-03 5.027566e+00 2.352652e-03 5.022863e+00 2.350451e-03 5.018165e+00 2.348253e-03 5.013470e+00 2.346056e-03 5.008780e+00 2.343861e-03 5.004095e+00 2.341669e-03 4.999413e+00 2.339478e-03 4.994737e+00 2.337290e-03 4.990065e+00 2.335103e-03 4.985396e+00 2.332919e-03 4.980733e+00 2.330736e-03 4.976074e+00 2.328556e-03 4.971418e+00 2.326378e-03 4.966768e+00 2.324202e-03 4.962122e+00 2.322027e-03 4.957480e+00 2.319855e-03 4.952842e+00 2.317685e-03 4.948209e+00 2.315517e-03 4.943580e+00 2.313351e-03 4.938956e+00 2.311187e-03 4.934336e+00 2.309025e-03 4.929719e+00 2.306865e-03 4.925108e+00 2.304707e-03 4.920501e+00 2.302551e-03 4.915898e+00 2.300397e-03 4.911299e+00 2.298245e-03 4.906705e+00 2.296095e-03 4.902115e+00 2.293947e-03 4.897529e+00 2.291801e-03 4.892948e+00 2.289657e-03 4.888371e+00 2.287515e-03 4.883798e+00 2.285375e-03 4.879229e+00 2.283238e-03 4.874665e+00 2.281102e-03 4.870105e+00 2.278968e-03 4.865549e+00 2.276836e-03 4.860997e+00 2.274706e-03 4.856450e+00 2.272578e-03 4.851907e+00 2.270452e-03 4.847368e+00 2.268328e-03 4.842834e+00 2.266207e-03 4.838303e+00 2.264086e-03 4.833777e+00 2.261969e-03 4.829256e+00 2.259853e-03 4.824738e+00 2.257739e-03 4.820224e+00 2.255626e-03 4.815715e+00 2.253516e-03 4.811211e+00 2.251408e-03 4.806710e+00 2.249302e-03 4.802213e+00 2.247198e-03 4.797721e+00 2.245096e-03 4.793233e+00 2.242996e-03 4.788749e+00 2.240897e-03 4.784269e+00 2.238801e-03 4.779794e+00 2.236707e-03 4.775322e+00 2.234614e-03 4.770855e+00 2.232524e-03 ''') ImportString(u'wave(numeric)',''' 4.108090e+00 4.236760e+00 4.283090e+00 4.329410e+00 4.370590e+00 4.411770e+00 4.452940e+00 4.494120e+00 4.545590e+00 4.597060e+00 4.638240e+00 4.694850e+00 4.751470e+00 4.792650e+00 4.838970e+00 4.885290e+00 4.926470e+00 4.998530e+00 5.070590e+00 5.111760e+00 5.163240e+00 5.214710e+00 5.266180e+00 5.317650e+00 5.358820e+00 5.400000e+00 5.441180e+00 5.482350e+00 5.523530e+00 5.564710e+00 5.605880e+00 5.647060e+00 5.688240e+00 5.729410e+00 5.780880e+00 5.832350e+00 5.873530e+00 5.930150e+00 5.986760e+00 6.027940e+00 6.069120e+00 6.110290e+00 6.151470e+00 6.192650e+00 6.233820e+00 6.275000e+00 6.316180e+00 6.372790e+00 6.429410e+00 6.491180e+00 6.568380e+00 6.625000e+00 6.666180e+00 6.707350e+00 6.748530e+00 6.789710e+00 6.830880e+00 6.882350e+00 6.933820e+00 6.975000e+00 7.016180e+00 7.057350e+00 7.098530e+00 7.139710e+00 7.180880e+00 7.242650e+00 7.304410e+00 7.345590e+00 7.397060e+00 7.448530e+00 7.489710e+00 7.530880e+00 7.572060e+00 7.649260e+00 7.736760e+00 7.788240e+00 7.829410e+00 7.870590e+00 7.922060e+00 7.973530e+00 8.014710e+00 8.055880e+00 8.097060e+00 8.138240e+00 8.179410e+00 8.220590e+00 8.272060e+00 8.323530e+00 8.364710e+00 8.405880e+00 8.447060e+00 8.488240e+00 8.534560e+00 8.596320e+00 8.652940e+00 8.694120e+00 8.735290e+00 8.776470e+00 8.817650e+00 8.858820e+00 8.900000e+00 8.941180e+00 8.982350e+00 9.023530e+00 9.064710e+00 9.131620e+00 9.198530e+00 9.239710e+00 9.280880e+00 9.322060e+00 9.373530e+00 9.425000e+00 9.476470e+00 9.527940e+00 9.569120e+00 9.610290e+00 9.666910e+00 9.723530e+00 9.764710e+00 9.805880e+00 9.857350e+00 9.908820e+00 9.970590e+00 1.004780e+01 1.010440e+01 1.014560e+01 1.018680e+01 1.022790e+01 1.026910e+01 1.031030e+01 1.036180e+01 1.041320e+01 1.045440e+01 1.049560e+01 1.055220e+01 1.060880e+01 1.065000e+01 1.069120e+01 1.073240e+01 1.077350e+01 1.081470e+01 1.085590e+01 1.089710e+01 1.093820e+01 1.097940e+01 1.102060e+01 1.106180e+01 1.110290e+01 1.114410e+01 1.118530e+01 1.122650e+01 1.126760e+01 1.130880e+01 1.137060e+01 1.143240e+01 1.148380e+01 1.155070e+01 1.160740e+01 1.164850e+01 1.170000e+01 1.175150e+01 1.179260e+01 1.183380e+01 1.187500e+01 1.191620e+01 1.195740e+01 1.201400e+01 1.207060e+01 1.211180e+01 1.216320e+01 1.221470e+01 1.225590e+01 1.229710e+01 1.234340e+01 1.238970e+01 1.243090e+01 1.248240e+01 1.253380e+01 1.257500e+01 1.261620e+01 1.265740e+01 1.269850e+01 1.273970e+01 1.278090e+01 1.282210e+01 1.286320e+01 1.292500e+01 1.298680e+01 1.305370e+01 1.314630e+01 1.321320e+01 1.325440e+01 1.331100e+01 1.336760e+01 1.341910e+01 1.347060e+01 1.351180e+01 1.355290e+01 1.359410e+01 1.363530e+01 1.369190e+01 1.375880e+01 1.382570e+01 1.388240e+01 1.392350e+01 1.396470e+01 1.400590e+01 1.404710e+01 1.411400e+01 1.418090e+01 1.422210e+01 1.426320e+01 1.430440e+01 1.434560e+01 1.439710e+01 1.446910e+01 1.453600e+01 1.458240e+01 1.462350e+01 1.466470e+01 1.470590e+01 1.476250e+01 1.481910e+01 1.486030e+01 1.490150e+01 1.494260e+01 1.498380e+01 1.502500e+01 1.506620e+01 1.510740e+01 1.514850e+01 1.522060e+01 1.530290e+01 1.536990e+01 1.542650e+01 1.548310e+01 1.553970e+01 1.560660e+01 1.568900e+01 1.574560e+01 1.578680e+01 1.584340e+01 1.590000e+01 1.594120e+01 1.598240e+01 1.602350e+01 1.606470e+01 1.610590e+01 1.614710e+01 1.618820e+01 1.622940e+01 1.627060e+01 1.631180e+01 1.636320e+01 1.641470e+01 1.645590e+01 1.651250e+01 1.656910e+01 1.661030e+01 1.665150e+01 1.670290e+01 1.675440e+01 1.680590e+01 1.685740e+01 1.689850e+01 1.693970e+01 1.699120e+01 1.705810e+01 1.711470e+01 1.715590e+01 1.719710e+01 1.725370e+01 1.731030e+01 1.735150e+01 1.739260e+01 1.743380e+01 1.749040e+01 1.754710e+01 1.759850e+01 1.765000e+01 1.769120e+01 1.773240e+01 1.777350e+01 1.781470e+01 1.785590e+01 1.791760e+01 1.797940e+01 1.802060e+01 1.806180e+01 1.811320e+01 1.816470e+01 1.820590e+01 1.826250e+01 1.831910e+01 1.836030e+01 1.840150e+01 1.845290e+01 1.850440e+01 1.854560e+01 1.860220e+01 1.865880e+01 1.870000e+01 1.875660e+01 1.881320e+01 1.885440e+01 1.892130e+01 1.898820e+01 1.902940e+01 1.907060e+01 1.912210e+01 1.917350e+01 1.921470e+01 1.925590e+01 1.929710e+01 1.933820e+01 1.937940e+01 1.942060e+01 1.946180e+01 1.950290e+01 1.954410e+01 1.958530e+01 1.962650e+01 1.968310e+01 1.973970e+01 1.978090e+01 1.982210e+01 1.986320e+01 1.990440e+01 1.994560e+01 1.998680e+01 2.002790e+01 2.006910e+01 2.011030e+01 2.015150e+01 2.019260e+01 2.023380e+01 2.027500e+01 2.031620e+01 2.035740e+01 2.039850e+01 2.043970e+01 2.048090e+01 2.052210e+01 2.056320e+01 2.060440e+01 2.064560e+01 2.068680e+01 2.077940e+01 2.087210e+01 2.091320e+01 2.095440e+01 2.099560e+01 2.103680e+01 2.107790e+01 2.111910e+01 2.116030e+01 2.120150e+01 2.124260e+01 2.128380e+01 2.132500e+01 2.136620e+01 2.140740e+01 2.144850e+01 2.148970e+01 2.153090e+01 2.157210e+01 2.161320e+01 2.165440e+01 2.169560e+01 2.173680e+01 2.177790e+01 2.181910e+01 2.186030e+01 2.190150e+01 2.194260e+01 2.198380e+01 2.202500e+01 2.206620e+01 2.210740e+01 2.214850e+01 2.218970e+01 2.223090e+01 2.227210e+01 2.231320e+01 2.235440e+01 2.239560e+01 2.243680e+01 2.247790e+01 2.251910e+01 2.256030e+01 2.260150e+01 2.264260e+01 2.268380e+01 2.276100e+01 2.283820e+01 2.287940e+01 2.294120e+01 2.300290e+01 2.304410e+01 2.308530e+01 2.312650e+01 2.316760e+01 2.320880e+01 2.325000e+01 2.329120e+01 2.333240e+01 2.338900e+01 2.344560e+01 2.348680e+01 2.352790e+01 2.356910e+01 2.361030e+01 2.365150e+01 2.369260e+01 2.373380e+01 2.377500e+01 2.381620e+01 2.385740e+01 2.389850e+01 2.393970e+01 2.398090e+01 2.404260e+01 2.410440e+01 2.414560e+01 2.418680e+01 2.422790e+01 2.428970e+01 2.435150e+01 2.439260e+01 2.443380e+01 2.447500e+01 2.451620e+01 2.455740e+01 2.459850e+01 2.463970e+01 2.469120e+01 2.474260e+01 2.478380e+01 2.482500e+01 2.486620e+01 2.490740e+01 2.496910e+01 2.503090e+01 2.507210e+01 2.511320e+01 2.515440e+01 2.519560e+01 2.523680e+01 2.527790e+01 2.531910e+01 2.536030e+01 2.540150e+01 2.544260e+01 2.548380e+01 2.552500e+01 2.556620e+01 2.560740e+01 2.564850e+01 2.568970e+01 2.573090e+01 2.577210e+01 2.583900e+01 2.590590e+01 2.594710e+01 2.598820e+01 2.605000e+01 2.611180e+01 2.616840e+01 2.622500e+01 2.626620e+01 2.630740e+01 2.634850e+01 2.638970e+01 2.643090e+01 2.647210e+01 2.651320e+01 2.655440e+01 2.659560e+01 2.665220e+01 2.670880e+01 2.675000e+01 2.681180e+01 2.687350e+01 2.691470e+01 2.695590e+01 2.701760e+01 2.710000e+01 2.718240e+01 2.726470e+01 2.737280e+01 2.748090e+01 2.756320e+01 2.767130e+01 2.777940e+01 2.786180e+01 2.794410e+01 2.802650e+01 2.810880e+01 2.823240e+01 2.835590e+01 2.843820e+01 2.852060e+01 2.860290e+01 2.868530e+01 2.878820e+01 2.890150e+01 2.900960e+01 2.910740e+01 2.918970e+01 2.927210e+01 2.935440e+01 2.943680e+01 2.951910e+01 2.960150e+01 2.968380e+01 2.976620e+01 2.984850e+01 2.993090e+01 3.001320e+01 3.009560e+01 3.017790e+01 3.026030e+01 3.037870e+01 3.049710e+01 3.057940e+01 3.066180e+01 3.074410e+01 3.082650e+01 3.090880e+01 3.099120e+01 3.107350e+01 3.117130e+01 3.130510e+01 3.142350e+01 3.150590e+01 3.158820e+01 3.167060e+01 3.175290e+01 3.183530e+01 3.191760e+01 3.200000e+01 3.208240e+01 3.216470e+01 3.224710e+01 3.232940e+01 3.241180e+01 3.249410e+01 3.257650e+01 3.267430e+01 3.277210e+01 3.285440e+01 3.293680e+01 3.301910e+01 3.310150e+01 3.318380e+01 3.326620e+01 3.334850e+01 3.344120e+01 3.353380e+01 3.363680e+01 3.373970e+01 3.382210e+01 3.390440e+01 3.400740e+01 3.411030e+01 3.419260e+01 3.427500e+01 3.435740e+01 3.445510e+01 3.455290e+01 3.463530e+01 3.471760e+01 3.480000e+01 3.488240e+01 3.499040e+01 3.509850e+01 3.520150e+01 3.530440e+01 3.538680e+01 3.546910e+01 3.555150e+01 3.563380e+01 3.571620e+01 3.582940e+01 3.594260e+01 3.602500e+01 3.610740e+01 3.620510e+01 3.630290e+01 3.638530e+01 3.646760e+01 3.655000e+01 3.663240e+01 3.671470e+01 3.679710e+01 3.690000e+01 3.700290e+01 3.708530e+01 3.718310e+01 3.728090e+01 3.736320e+01 3.744560e+01 3.752790e+01 3.761030e+01 3.769260e+01 3.777500e+01 3.785740e+01 3.793970e+01 3.802210e+01 3.810440e+01 ''') ImportString(u'wave_090(numeric)',''' 4.108090e+00 4.236760e+00 4.283090e+00 4.329410e+00 4.370590e+00 4.411770e+00 4.452940e+00 4.494120e+00 4.545590e+00 4.597060e+00 4.638240e+00 4.694850e+00 4.751470e+00 4.792650e+00 4.838970e+00 4.885290e+00 4.926470e+00 4.998530e+00 5.070590e+00 5.111760e+00 5.163240e+00 5.214710e+00 5.266180e+00 5.317650e+00 5.358820e+00 5.400000e+00 5.441180e+00 5.482350e+00 5.523530e+00 5.564710e+00 5.605880e+00 5.647060e+00 5.688240e+00 5.729410e+00 5.780880e+00 5.832350e+00 5.873530e+00 5.930150e+00 5.986760e+00 6.027940e+00 6.069120e+00 6.110290e+00 6.151470e+00 6.192650e+00 6.233820e+00 6.275000e+00 6.316180e+00 6.372790e+00 6.429410e+00 6.491180e+00 6.568380e+00 6.625000e+00 6.666180e+00 6.707350e+00 6.748530e+00 6.789710e+00 6.830880e+00 6.882350e+00 6.933820e+00 6.975000e+00 7.016180e+00 7.057350e+00 7.098530e+00 7.139710e+00 7.180880e+00 7.242650e+00 7.304410e+00 7.345590e+00 7.397060e+00 7.448530e+00 7.489710e+00 7.530880e+00 7.572060e+00 7.649260e+00 7.736760e+00 7.788240e+00 7.829410e+00 7.870590e+00 7.922060e+00 7.973530e+00 8.014710e+00 8.055880e+00 8.097060e+00 8.138240e+00 8.179410e+00 8.220590e+00 8.272060e+00 8.323530e+00 8.364710e+00 8.405880e+00 8.447060e+00 8.488240e+00 8.534560e+00 8.596320e+00 8.652940e+00 8.694120e+00 8.735290e+00 8.776470e+00 8.817650e+00 8.858820e+00 8.900000e+00 8.941180e+00 8.982350e+00 9.023530e+00 9.064710e+00 9.131620e+00 9.198530e+00 9.239710e+00 9.280880e+00 9.322060e+00 9.373530e+00 9.425000e+00 9.476470e+00 9.527940e+00 9.569120e+00 9.610290e+00 9.666910e+00 9.723530e+00 9.764710e+00 9.805880e+00 9.857350e+00 9.908820e+00 9.970590e+00 1.004780e+01 1.010440e+01 1.014560e+01 1.018680e+01 1.022790e+01 1.026910e+01 1.031030e+01 1.036180e+01 1.041320e+01 1.045440e+01 1.049560e+01 1.055220e+01 1.060880e+01 1.065000e+01 1.069120e+01 1.073240e+01 1.077350e+01 1.081470e+01 1.085590e+01 1.089710e+01 1.093820e+01 1.097940e+01 1.102060e+01 1.106180e+01 1.110290e+01 1.114410e+01 1.118530e+01 1.122650e+01 1.126760e+01 1.130880e+01 1.137060e+01 1.143240e+01 1.148380e+01 1.155070e+01 1.160740e+01 1.164850e+01 1.170000e+01 1.175150e+01 1.179260e+01 1.183380e+01 1.187500e+01 1.191620e+01 1.195740e+01 1.201400e+01 1.207060e+01 1.211180e+01 1.216320e+01 1.221470e+01 1.225590e+01 1.229710e+01 1.234340e+01 1.238970e+01 1.243090e+01 1.248240e+01 1.253380e+01 1.257500e+01 1.261620e+01 1.265740e+01 1.269850e+01 1.273970e+01 1.278090e+01 1.282210e+01 1.286320e+01 1.292500e+01 1.298680e+01 1.305370e+01 1.314630e+01 1.321320e+01 1.325440e+01 1.331100e+01 1.336760e+01 1.341910e+01 1.347060e+01 1.351180e+01 1.355290e+01 1.359410e+01 1.363530e+01 1.369190e+01 1.375880e+01 1.382570e+01 1.388240e+01 1.392350e+01 1.396470e+01 1.400590e+01 1.404710e+01 1.411400e+01 1.418090e+01 1.422210e+01 1.426320e+01 1.430440e+01 1.434560e+01 1.439710e+01 1.446910e+01 1.453600e+01 1.458240e+01 1.462350e+01 1.466470e+01 1.470590e+01 1.476250e+01 1.481910e+01 1.486030e+01 1.490150e+01 1.494260e+01 1.498380e+01 1.502500e+01 1.506620e+01 1.510740e+01 1.514850e+01 1.522060e+01 1.530290e+01 1.536990e+01 1.542650e+01 1.548310e+01 1.553970e+01 1.560660e+01 1.568900e+01 1.574560e+01 1.578680e+01 1.584340e+01 1.590000e+01 1.594120e+01 1.598240e+01 1.602350e+01 1.606470e+01 1.610590e+01 1.614710e+01 1.618820e+01 1.622940e+01 1.627060e+01 1.631180e+01 1.636320e+01 1.641470e+01 1.645590e+01 1.651250e+01 1.656910e+01 1.661030e+01 1.665150e+01 1.670290e+01 1.675440e+01 1.680590e+01 1.685740e+01 1.689850e+01 1.693970e+01 1.699120e+01 1.705810e+01 1.711470e+01 1.715590e+01 1.719710e+01 1.725370e+01 1.731030e+01 1.735150e+01 1.739260e+01 1.743380e+01 1.749040e+01 1.754710e+01 1.759850e+01 1.765000e+01 1.769120e+01 1.773240e+01 1.777350e+01 1.781470e+01 1.785590e+01 1.791760e+01 1.797940e+01 1.802060e+01 1.806180e+01 1.811320e+01 1.816470e+01 1.820590e+01 1.826250e+01 1.831910e+01 1.836030e+01 1.840150e+01 1.845290e+01 1.850440e+01 1.854560e+01 1.860220e+01 1.865880e+01 1.870000e+01 1.875660e+01 1.881320e+01 1.885440e+01 1.892130e+01 1.898820e+01 1.902940e+01 1.907060e+01 1.912210e+01 1.917350e+01 1.921470e+01 1.925590e+01 1.929710e+01 1.933820e+01 1.937940e+01 1.942060e+01 1.946180e+01 1.950290e+01 1.954410e+01 1.958530e+01 1.962650e+01 1.968310e+01 1.973970e+01 1.978090e+01 1.982210e+01 1.986320e+01 1.990440e+01 1.994560e+01 1.998680e+01 2.002790e+01 2.006910e+01 2.011030e+01 2.015150e+01 2.019260e+01 2.023380e+01 2.027500e+01 2.031620e+01 2.035740e+01 2.039850e+01 2.043970e+01 2.048090e+01 2.052210e+01 2.056320e+01 2.060440e+01 2.064560e+01 2.068680e+01 2.077940e+01 2.087210e+01 2.091320e+01 2.095440e+01 2.099560e+01 2.103680e+01 2.107790e+01 2.111910e+01 2.116030e+01 2.120150e+01 2.124260e+01 2.128380e+01 2.132500e+01 2.136620e+01 2.140740e+01 2.144850e+01 2.148970e+01 2.153090e+01 2.157210e+01 2.161320e+01 2.165440e+01 2.169560e+01 2.173680e+01 2.177790e+01 2.181910e+01 2.186030e+01 2.190150e+01 2.194260e+01 2.198380e+01 2.202500e+01 2.206620e+01 2.210740e+01 2.214850e+01 2.218970e+01 2.223090e+01 2.227210e+01 2.231320e+01 2.235440e+01 2.239560e+01 2.243680e+01 2.247790e+01 2.251910e+01 2.256030e+01 2.260150e+01 2.264260e+01 2.268380e+01 2.276100e+01 2.283820e+01 2.287940e+01 2.294120e+01 2.300290e+01 2.304410e+01 2.308530e+01 2.312650e+01 2.316760e+01 2.320880e+01 2.325000e+01 2.329120e+01 2.333240e+01 2.338900e+01 2.344560e+01 2.348680e+01 2.352790e+01 2.356910e+01 2.361030e+01 2.365150e+01 2.369260e+01 2.373380e+01 2.377500e+01 2.381620e+01 2.385740e+01 2.389850e+01 2.393970e+01 2.398090e+01 2.404260e+01 2.410440e+01 2.414560e+01 2.418680e+01 2.422790e+01 2.428970e+01 2.435150e+01 2.439260e+01 2.443380e+01 2.447500e+01 2.451620e+01 2.455740e+01 2.459850e+01 2.463970e+01 2.469120e+01 2.474260e+01 2.478380e+01 2.482500e+01 2.486620e+01 2.490740e+01 2.496910e+01 2.503090e+01 2.507210e+01 2.511320e+01 2.515440e+01 2.519560e+01 2.523680e+01 2.527790e+01 2.531910e+01 2.536030e+01 2.540150e+01 2.544260e+01 2.548380e+01 2.552500e+01 2.556620e+01 2.560740e+01 2.564850e+01 2.568970e+01 2.573090e+01 2.577210e+01 2.583900e+01 2.590590e+01 2.594710e+01 2.598820e+01 2.605000e+01 2.611180e+01 2.616840e+01 2.622500e+01 2.626620e+01 2.630740e+01 2.634850e+01 2.638970e+01 2.643090e+01 2.647210e+01 2.651320e+01 2.655440e+01 2.659560e+01 2.665220e+01 2.670880e+01 2.675000e+01 2.681180e+01 2.687350e+01 2.691470e+01 2.695590e+01 2.701760e+01 2.710000e+01 2.718240e+01 2.726470e+01 2.737280e+01 2.748090e+01 2.756320e+01 2.767130e+01 2.777940e+01 2.786180e+01 2.794410e+01 2.802650e+01 2.810880e+01 2.823240e+01 2.835590e+01 2.843820e+01 2.852060e+01 2.860290e+01 2.868530e+01 2.878820e+01 2.890150e+01 2.900960e+01 2.910740e+01 2.918970e+01 2.927210e+01 2.935440e+01 2.943680e+01 2.951910e+01 2.960150e+01 2.968380e+01 2.976620e+01 2.984850e+01 2.993090e+01 3.001320e+01 3.009560e+01 3.017790e+01 3.026030e+01 3.037870e+01 3.049710e+01 3.057940e+01 3.066180e+01 3.074410e+01 3.082650e+01 3.090880e+01 3.099120e+01 3.107350e+01 3.117130e+01 3.130510e+01 3.142350e+01 3.150590e+01 3.158820e+01 3.167060e+01 3.175290e+01 3.183530e+01 3.191760e+01 3.200000e+01 3.208240e+01 3.216470e+01 3.224710e+01 3.232940e+01 3.241180e+01 3.249410e+01 3.257650e+01 3.267430e+01 3.277210e+01 3.285440e+01 3.293680e+01 3.301910e+01 3.310150e+01 3.318380e+01 3.326620e+01 3.334850e+01 3.344120e+01 3.353380e+01 3.363680e+01 3.373970e+01 3.382210e+01 3.390440e+01 3.400740e+01 3.411030e+01 3.419260e+01 3.427500e+01 3.435740e+01 3.445510e+01 3.455290e+01 3.463530e+01 3.471760e+01 3.480000e+01 3.488240e+01 3.499040e+01 3.509850e+01 3.520150e+01 3.530440e+01 3.538680e+01 3.546910e+01 3.555150e+01 3.563380e+01 3.571620e+01 3.582940e+01 3.594260e+01 3.602500e+01 3.610740e+01 3.620510e+01 3.630290e+01 3.638530e+01 3.646760e+01 3.655000e+01 3.663240e+01 3.671470e+01 3.679710e+01 3.690000e+01 3.700290e+01 3.708530e+01 3.718310e+01 3.728090e+01 3.736320e+01 3.744560e+01 3.752790e+01 3.761030e+01 3.769260e+01 3.777500e+01 3.785740e+01 3.793970e+01 3.802210e+01 3.810440e+01 ''') Set('width', '24.7cm') Set('height', '16.1cm') Set('StyleSheet/Font/font', u'Arial') Add('page', name='page1', autoadd=False) To('page1') Add('grid', name='grid1', autoadd=False) To('grid1') Set('columns', 1) Set('scaleRows', [1.0, 0.5]) Add('axis', name='x', autoadd=False) To('x') Set('label', u'Wavelength (\\AA)') Set('min', 6.0) Set('max', 26.0) Set('MajorTicks/number', 30) To('..') Add('graph', name='graph1', autoadd=False) To('graph1') Set('leftMargin', '0cm') Set('rightMargin', '0cm') Set('topMargin', '0cm') Set('bottomMargin', '0cm') Add('axis', name='y', autoadd=False) To('y') Set('label', u'Flux (10^{-3} photon cm^{-2} s^{-1} \\AA^{-1})') Set('min', 0.0) Set('max', 0.0027000000000000001) Set('direction', 'vertical') Set('TickLabels/scale', 1000.0) To('..') Add('xy', name='psf099', autoadd=False) To('psf099') Set('xData', u'wave') Set('yData', u'flux') Set('marker', u'none') Set('key', u'99% PSF') Set('PlotLine/steps', u'centre') Set('PlotLine/width', u'1pt') Set('ErrorBarLine/color', u'grey') To('..') Add('label', name='labelOVIII', autoadd=False) To('labelOVIII') Set('label', u'O \\size{-2}{VIII}') Set('xPos', [19.23]) Set('yPos', [0.0011999999999999999]) Set('positioning', u'axes') Set('alignHorz', u'centre') Set('angle', 90.0) Set('Text/size', u'12pt') To('..') Add('label', name='labelOVIII2', autoadd=False) To('labelOVIII2') Set('label', u'O \\size{-2}{VIII} Fe \\size{-2}{XVIII}') Set('xPos', [16.25]) Set('yPos', [0.00089999999999999998]) Set('positioning', u'axes') Set('alignHorz', u'centre') Set('angle', 90.0) Set('Text/size', u'12pt') To('..') Add('label', name='labelFeXVII', autoadd=False) To('labelFeXVII') Set('label', u'Fe \\size{-2}{XVII}') Set('xPos', [17.300000000000001]) Set('yPos', [0.00073999999999999999]) Set('positioning', u'axes') Set('alignHorz', u'centre') Set('angle', 90.0) Set('Text/size', u'12pt') To('..') Add('label', name='labelFeXVII2', autoadd=False) To('labelFeXVII2') Set('label', u'Fe \\size{-2}{XVII}') Set('xPos', [15.25]) Set('yPos', [0.00089999999999999998]) Set('positioning', u'axes') Set('alignHorz', u'centre') Set('angle', 90.0) Set('Text/size', u'12pt') To('..') Add('label', name='labelNVII', autoadd=False) To('labelNVII') Set('label', u'N \\size{-2}{VII}') Set('xPos', [25.100000000000001]) Set('yPos', [0.00059999999999999995]) Set('positioning', u'axes') Set('alignHorz', u'centre') Set('angle', 90.0) Set('Text/size', u'12pt') To('..') Add('label', name='MgXII', autoadd=False) To('MgXII') Set('label', u'Mg \\size{-2}{XII}') Set('xPos', [8.5]) Set('yPos', [0.0016000000000000001]) Set('positioning', u'axes') Set('alignHorz', u'centre') Set('angle', 90.0) Set('Text/size', u'12pt') To('..') Add('xy', name='psf090', autoadd=False) To('psf090') Set('xData', u'wave_090') Set('yData', u'flux_090') Set('marker', u'none') Set('key', u'90% PSF') Set('PlotLine/steps', u'centre') Set('PlotLine/color', u'darkgreen') Set('PlotLine/width', u'1pt') Set('ErrorBarLine/color', u'#55aa7f') To('..') Add('key', name='key1', autoadd=False) To('key1') Set('Border/hide', True) Set('vertPosn', u'top') To('..') Add('label', name='labelSiXIV', autoadd=False) To('labelSiXIV') Set('label', u'Si \\size{-2}{XIV}') Set('xPos', [6.2060000000000004]) Set('yPos', [0.0020999999999999999]) Set('positioning', u'axes') Set('angle', 90.0) Set('Text/size', u'12pt') To('..') Add('label', name='labelSiXIII', autoadd=False) To('labelSiXIII') Set('label', u'Si \\size{-3}{XIII}') Set('xPos', [6.75]) Set('yPos', [0.0015]) Set('positioning', u'axes') Set('alignHorz', u'centre') Set('angle', 90.0) Set('Text/size', u'12pt') To('..') Add('xy', name='psfdelta', autoadd=False) To('psfdelta') Set('xData', u'wave') Set('yData', u'fluxdelta') Set('marker', u'none') Set('key', u'99% - 90%') Set('PlotLine/steps', u'centre') Set('PlotLine/color', u'#ff007f') Set('PlotLine/width', u'1pt') Set('ErrorBarLine/color', u'#ff557f') To('..') Add('label', name='labelNeIXFeXIX', autoadd=False) To('labelNeIXFeXIX') Set('label', u'Fe \\size{-2}{XIX}') Set('xPos', [13.529999999999999]) Set('yPos', [0.0011999999999999999]) Set('positioning', u'axes') Set('angle', 90.0) Set('Text/size', u'12pt') To('..') Add('label', name='FeXX_XXII', autoadd=False) To('FeXX_XXII') Set('label', u'Fe \\size{-2}{XX}-\\size{-2}{XXII}') Set('xPos', [12.9]) Set('yPos', [0.0013500000000000001]) Set('positioning', u'axes') Set('angle', 90.0) Set('Text/size', u'12pt') To('..') Add('label', name='FeXVII_3', autoadd=False) To('FeXVII_3') Set('label', u'Fe \\size{-2}{XVII}') Set('xPos', [17.0]) Set('yPos', [0.00073999999999999999]) Set('positioning', u'axes') Set('alignHorz', u'centre') Set('angle', 90.0) Set('Text/size', u'12pt') To('..') Add('label', name='FeXVII_4', autoadd=False) To('FeXVII_4') Set('label', u'Fe \\size{-2}{XVII}') Set('xPos', [15.5]) Set('yPos', [0.00089999999999999998]) Set('positioning', u'axes') Set('alignHorz', u'centre') Set('angle', 90.0) Set('Text/size', u'12pt') To('..') Add('label', name='FeXVIII', autoadd=False) To('FeXVIII') Set('label', u'Fe \\size{-2}{XVIII}') Set('xPos', [17.899999999999999]) Set('yPos', [0.00067000000000000002]) Set('positioning', u'axes') Set('alignHorz', u'centre') Set('angle', 90.0) Set('Text/size', u'12pt') To('..') Add('label', name='FeXVIII_2', autoadd=False) To('FeXVIII_2') Set('label', u'Fe \\size{-2}{XVIII}') Set('xPos', [14.4]) Set('yPos', [0.00095]) Set('positioning', u'axes') Set('alignHorz', u'centre') Set('angle', 90.0) Set('Text/size', u'12pt') To('..') Add('label', name='FeXVII_5', autoadd=False) To('FeXVII_5') Set('label', u'Ne \\size{-2}{X}, Fe \\size{-2}{XVII}-\\size{-2}{XVIII}') Set('xPos', [12.1]) Set('yPos', [0.0023999999999999998]) Set('positioning', u'axes') Set('Text/size', u'12pt') To('..') Add('label', name='FeXXI_XXIII', autoadd=False) To('FeXXI_XXIII') Set('label', u'Fe \\size{-2}{XXII}-\\size{-2}{XXIII}') Set('xPos', [11.699999999999999]) Set('yPos', [0.0025000000000000001]) Set('positioning', u'axes') Set('Text/size', u'12pt') To('..') Add('label', name='FeXIX_XX', autoadd=False) To('FeXIX_XX') Set('label', u'Fe \\size{-2}{XXIV}') Set('xPos', [10.779999999999999]) Set('yPos', [0.0020999999999999999]) Set('positioning', u'axes') Set('alignHorz', u'centre') Set('angle', 90.0) Set('Text/size', u'12pt') To('..') Add('label', name='NeX', autoadd=False) To('NeX') Set('label', u'Ne \\size{-2}{X}') Set('xPos', [10.4]) Set('yPos', [0.0012999999999999999]) Set('positioning', u'axes') Set('alignHorz', u'centre') Set('angle', 90.0) Set('Text/size', u'12pt') To('..') Add('label', name='MgXII_2', autoadd=False) To('MgXII_2') Set('label', u'Mg \\size{-2}{XII}') Set('xPos', [7.25]) Set('yPos', [0.0011999999999999999]) Set('positioning', u'axes') Set('alignHorz', u'centre') Set('angle', 90.0) Set('Text/size', u'12pt') To('..') Add('label', name='FeXXIV', autoadd=False) To('FeXXIV') Set('label', u'Fe \\size{-2}{XXIV}') Set('xPos', [8.0999999999999996]) Set('yPos', [0.0011999999999999999]) Set('positioning', u'axes') Set('alignHorz', u'centre') Set('angle', 90.0) Set('Text/size', u'12pt') To('..') Add('label', name='FeXXIII_XIV', autoadd=False) To('FeXXIII_XIV') Set('label', u'Fe \\size{-2}{XXIII}-\\size{-2}{XXIV}') Set('xPos', [10.196808119541004]) Set('yPos', [0.0025058612874790131]) Set('positioning', u'axes') Set('alignHorz', u'centre') Set('Text/size', u'12pt') To('..') Add('label', name='FeXXI', autoadd=False) To('FeXXI') Set('label', u'Fe \\size{-2}{XXI}') Set('xPos', [12.6]) Set('yPos', [0.0019]) Set('positioning', u'axes') Set('alignHorz', u'centre') Set('angle', 90.0) Set('Text/size', u'12pt') To('..') To('..') Add('graph', name='graph2', autoadd=False) To('graph2') Set('leftMargin', '0cm') Set('rightMargin', '0cm') Set('bottomMargin', '0cm') Add('axis', name='y', autoadd=False) To('y') Set('label', u'Arbitrary units') Set('direction', 'vertical') To('..') Add('xy', name='model05', autoadd=False) To('model05') Set('xData', u'lambda_model') Set('yData', u'flux_model_0_5_reduce') Set('marker', u'none') Set('key', u'0.5 keV model spectrum') Set('PlotLine/width', u'1pt') Set('ErrorBarLine/hide', True) To('..') Add('xy', name='model07', autoadd=False) To('model07') Set('xData', u'lambda_model') Set('yData', u'flux_model_0_7') Set('marker', u'none') Set('key', u'0.7 keV model spectrum') Set('PlotLine/color', u'#ff007f') Set('PlotLine/width', u'1pt') Set('ErrorBarLine/hide', True) To('..') Add('xy', name='model10', autoadd=False) To('model10') Set('xData', u'lambda_model') Set('yData', u'flux_model_1_0_scaled') Set('marker', u'none') Set('key', u'1.0 keV model spectrum') Set('PlotLine/color', u'darkgreen') Set('PlotLine/width', u'1pt') Set('ErrorBarLine/hide', True) To('..') Add('key', name='key1', autoadd=False) To('key1') Set('Border/hide', True) Set('horzPosn', 'right') Set('vertPosn', 'top') Set('horzManual', 0.0) Set('vertManual', 0.0) To('..') To('..') To('..') To('..') veusz-3.0.1/examples/dataset_operations.vsz0000664000175000017500000001172713161413406020572 0ustar jssjss00000000000000# Veusz saved document (version 1.8.99) # User: jss # Date: Thu, 26 Aug 2010 20:55:46 +0000 ImportString('x(numeric)',''' 0.000000e+00 1.000000e+00 2.000000e+00 3.000000e+00 4.000000e+00 5.000000e+00 6.000000e+00 7.000000e+00 8.000000e+00 9.000000e+00 1.000000e+01 1.100000e+01 1.200000e+01 1.300000e+01 1.400000e+01 1.500000e+01 1.600000e+01 1.700000e+01 1.800000e+01 1.900000e+01 ''') DatasetPlugin('Thin', {'ds_out': u'xthin', 'start': 1, 'interval': 2, 'ds_in': u'x'}) ImportString('y1(numeric)',''' -8.627642e-01 1.213047e+00 -1.682870e+00 2.540669e+00 9.385744e-04 -6.966657e-01 -8.769203e-01 -6.930465e-01 -2.456379e-01 -6.419765e-01 -1.485679e+00 -7.142200e-01 8.639527e-02 -1.155861e+00 -9.576156e-01 6.018372e-02 -1.027861e+00 2.953903e-01 -3.615840e-01 2.474292e-01 ''') DatasetPlugin('Add Datasets', {'ds_out': u'y1plusy2', 'ds_in': (u'y1', u'y2')}) ImportString('y2(numeric)',''' 7.356253e-01 2.187511e+00 9.680102e-01 -7.393343e-01 1.071199e+00 1.763134e+00 1.589872e+00 2.015283e+00 7.102356e-01 1.808795e+00 8.750188e-01 1.477934e+00 3.591239e-02 3.046406e+00 3.515513e+00 7.194178e-01 3.498590e+00 4.465251e+00 1.638100e+00 3.577523e+00 ''') ImportString('y3(numeric),+-',''' 1.537872e+00 3.000000e-01 2.879103e-01 3.000000e-01 7.127184e+00 3.000000e-01 5.775675e+00 3.000000e-01 3.390224e+00 3.000000e-01 2.470264e+00 3.000000e-01 1.019945e+00 3.000000e-01 -5.690097e-01 3.000000e-01 4.276276e+00 3.000000e-01 -4.449537e+00 3.000000e-01 -7.127589e-02 3.000000e-01 -9.531333e-01 3.000000e-01 -1.129021e+00 3.000000e-01 2.561764e+00 3.000000e-01 -1.763882e+00 3.000000e-01 -3.791216e-01 3.000000e-01 2.752641e-02 3.000000e-01 -1.044617e+00 3.000000e-01 2.075609e+00 3.000000e-01 -7.859457e-01 3.000000e-01 ''') DatasetPlugin('Add', {'ds_out': u'yadd', 'ds_in': u'y1', 'value': 2.0}) DatasetPlugin('Extremes', {'ds_min': '', 'ds_max': '', 'ds_errorbar': u'yextreme', 'errorbars': False, 'ds_in': (u'ysub', u'y1')}) DatasetPlugin('Mean', {'ds_out': u'ymean', 'ds_in': (u'yadd', u'y1plusy2', u'y1', u'y2')}) DatasetPlugin('Multiply', {'ds_out': u'yscale', 'ds_in': u'ymean', 'factor': 0.5}) DatasetPlugin('Subtract Datasets', {'ds_out': u'ysub', 'ds_in2': u'ymean', 'ds_in1': u'y1'}) DatasetPlugin('Thin', {'ds_out': u'ythin', 'start': 1, 'interval': 2, 'ds_in': u'yadd'}) Set('width', '18cm') Set('height', '15cm') Set('StyleSheet/Font/font', u'Verdana') Add('page', name=u'page1', autoadd=False) To(u'page1') Add('graph', name='graph1', autoadd=False) To('graph1') Set('Background/color', u'#f9faff') Add('axis', name='x', autoadd=False) Add('axis', name='y', autoadd=False) To('y') Set('direction', 'vertical') To('..') Add('xy', name=u'y1', autoadd=False) To(u'y1') Set('yData', u'y1') Set('marker', u'plus') Set('key', u'data') Set('PlotLine/bezierJoin', False) Set('PlotLine/color', '#659f23') Set('MarkerFill/color', '#659f23') Set('ErrorBarLine/color', '#659f23') To('..') Add('xy', name=u'add', autoadd=False) To(u'add') Set('yData', u'yadd') Set('key', u'add') Set('PlotLine/color', '#de9578') Set('MarkerFill/color', '#de9578') Set('ErrorBarLine/color', '#de9578') To('..') Add('xy', name=u'plus', autoadd=False) To(u'plus') Set('yData', u'y1plusy2') Set('key', u'plus') Set('PlotLine/bezierJoin', False) Set('PlotLine/color', '#d02bf1') Set('MarkerFill/color', '#d02bf1') Set('ErrorBarLine/color', '#d02bf1') To('..') Add('xy', name=u'ymean', autoadd=False) To(u'ymean') Set('yData', u'ymean') Set('marker', u'square') Set('key', u'mean') Set('PlotLine/color', '#00a2b7') Set('MarkerFill/color', '#00a2b7') Set('ErrorBarLine/color', '#00a2b7') To('..') Add('xy', name=u'ysub', autoadd=False) To(u'ysub') Set('xData', u'x') Set('yData', u'ysub') Set('marker', u'diamond') Set('key', u'sub') Set('PlotLine/color', '#696b69') Set('MarkerFill/color', '#696b69') Set('ErrorBarLine/color', '#696b69') To('..') Add('xy', name=u'yscale', autoadd=False) To(u'yscale') Set('yData', u'yscale') Set('marker', u'squashbox') Set('key', u'scale') Set('PlotLine/color', '#684f5f') Set('MarkerFill/color', u'#cf9ebe') Set('ErrorBarLine/color', '#684f5f') To('..') Add('label', name='label1', autoadd=False) To('label1') Set('label', u'Using data operations to combine datasets') Set('xPos', [0.5]) Set('yPos', [-0.097615808953379762]) Set('alignHorz', u'centre') Set('Text/size', u'15pt') To('..') Add('xy', name=u'extremes', autoadd=False) To(u'extremes') Set('yData', u'yextreme') Set('marker', u'star6') Set('markerSize', u'5pt') Set('key', u'extremes') Set('errorStyle', u'fillvert') Set('MarkerFill/color', u'#bcfff2') Set('ErrorBarLine/color', u'grey') Set('FillBelow/color', u'#dbe0ed') Set('FillAbove/color', u'#dbe0ed') To('..') Add('key', name='key1', autoadd=False) To('key1') Set('Background/hide', True) Set('Border/hide', True) Set('horzPosn', 'centre') Set('vertPosn', 'top') Set('horzManual', 0.0) Set('vertManual', 0.0) Set('columns', 2) To('..') Add('xy', name=u'thin', autoadd=False) To(u'thin') Set('xData', u'xthin') Set('yData', u'ythin') Set('markerSize', u'6pt') Set('key', u'thin') Set('MarkerFill/color', u'#c7b266') To('..') To('..') To('..') veusz-3.0.1/examples/tutorialdata.csv0000664000175000017500000000007613161413406017343 0ustar jssjss00000000000000"alpha","beta","gamma" 1,2,4 2,5,6 3,6,5 4,13,10 5,9,6 6,3,14 veusz-3.0.1/examples/histo.vsz0000664000175000017500000003131213161413406016020 0ustar jssjss00000000000000# Veusz saved document (version 0.5) # User: jss # Date: Sat, 16 Apr 2005 14:43:47 +0000 ImportString('y,+-',''' 0.000000e+00 1.000000e+00 0.000000e+00 1.000000e+00 0.000000e+00 1.000000e+00 0.000000e+00 1.000000e+00 0.000000e+00 1.000000e+00 0.000000e+00 1.000000e+00 0.000000e+00 1.000000e+00 0.000000e+00 1.000000e+00 0.000000e+00 1.000000e+00 0.000000e+00 1.000000e+00 0.000000e+00 1.000000e+00 0.000000e+00 1.000000e+00 0.000000e+00 1.000000e+00 0.000000e+00 1.000000e+00 1.000000e+00 1.414214e+00 0.000000e+00 1.000000e+00 0.000000e+00 1.000000e+00 0.000000e+00 1.000000e+00 0.000000e+00 1.000000e+00 0.000000e+00 1.000000e+00 0.000000e+00 1.000000e+00 0.000000e+00 1.000000e+00 0.000000e+00 1.000000e+00 0.000000e+00 1.000000e+00 2.000000e+00 1.732051e+00 1.000000e+00 1.414214e+00 0.000000e+00 1.000000e+00 0.000000e+00 1.000000e+00 0.000000e+00 1.000000e+00 0.000000e+00 1.000000e+00 1.000000e+00 1.414214e+00 1.000000e+00 1.414214e+00 3.000000e+00 2.000000e+00 1.000000e+00 1.414214e+00 0.000000e+00 1.000000e+00 1.000000e+00 1.414214e+00 1.000000e+00 1.414214e+00 6.000000e+00 2.645751e+00 4.000000e+00 2.236068e+00 2.000000e+00 1.732051e+00 6.000000e+00 2.645751e+00 1.200000e+01 3.605551e+00 7.000000e+00 2.828427e+00 9.000000e+00 3.162278e+00 9.000000e+00 3.162278e+00 9.000000e+00 3.162278e+00 1.000000e+01 3.316625e+00 1.400000e+01 3.872983e+00 1.300000e+01 3.741657e+00 2.000000e+01 4.582576e+00 1.500000e+01 4.000000e+00 1.700000e+01 4.242641e+00 2.200000e+01 4.795832e+00 3.400000e+01 5.916080e+00 2.400000e+01 5.000000e+00 3.900000e+01 6.324555e+00 4.300000e+01 6.633250e+00 4.000000e+01 6.403124e+00 5.700000e+01 7.615773e+00 4.700000e+01 6.928203e+00 6.000000e+01 7.810250e+00 6.000000e+01 7.810250e+00 8.000000e+01 9.000000e+00 7.800000e+01 8.888194e+00 9.300000e+01 9.695360e+00 1.010000e+02 1.009950e+01 9.700000e+01 9.899495e+00 1.260000e+02 1.126943e+01 1.330000e+02 1.157584e+01 1.200000e+02 1.100000e+01 1.340000e+02 1.161895e+01 1.680000e+02 1.300000e+01 1.630000e+02 1.280625e+01 1.680000e+02 1.300000e+01 1.880000e+02 1.374773e+01 1.930000e+02 1.392839e+01 2.200000e+02 1.486607e+01 2.120000e+02 1.459452e+01 2.180000e+02 1.479865e+01 2.370000e+02 1.542725e+01 2.610000e+02 1.618641e+01 2.420000e+02 1.558846e+01 2.710000e+02 1.649242e+01 2.920000e+02 1.711724e+01 2.980000e+02 1.729162e+01 3.060000e+02 1.752142e+01 3.020000e+02 1.740690e+01 3.450000e+02 1.860108e+01 3.550000e+02 1.886796e+01 3.130000e+02 1.772005e+01 3.390000e+02 1.843909e+01 3.890000e+02 1.974842e+01 3.530000e+02 1.881489e+01 3.950000e+02 1.989975e+01 4.320000e+02 2.080865e+01 3.670000e+02 1.918333e+01 4.090000e+02 2.024846e+01 4.170000e+02 2.044505e+01 3.970000e+02 1.994994e+01 4.000000e+02 2.002498e+01 4.100000e+02 2.027313e+01 3.980000e+02 1.997498e+01 4.320000e+02 2.080865e+01 4.200000e+02 2.051828e+01 3.590000e+02 1.897367e+01 3.990000e+02 2.000000e+01 3.740000e+02 1.936492e+01 3.830000e+02 1.959592e+01 3.450000e+02 1.860108e+01 3.320000e+02 1.824829e+01 3.700000e+02 1.926136e+01 3.460000e+02 1.862794e+01 3.350000e+02 1.833030e+01 3.480000e+02 1.868154e+01 2.950000e+02 1.720465e+01 2.910000e+02 1.708801e+01 2.660000e+02 1.634013e+01 2.400000e+02 1.552417e+01 2.680000e+02 1.640122e+01 2.530000e+02 1.593738e+01 2.400000e+02 1.552417e+01 2.290000e+02 1.516575e+01 1.950000e+02 1.400000e+01 1.930000e+02 1.392839e+01 1.850000e+02 1.363818e+01 1.800000e+02 1.345362e+01 1.560000e+02 1.252996e+01 1.500000e+02 1.228821e+01 1.260000e+02 1.126943e+01 1.400000e+02 1.187434e+01 1.270000e+02 1.131371e+01 1.140000e+02 1.072381e+01 1.040000e+02 1.024695e+01 9.500000e+01 9.797959e+00 7.600000e+01 8.774964e+00 1.010000e+02 1.009950e+01 6.300000e+01 8.000000e+00 7.600000e+01 8.774964e+00 5.700000e+01 7.615773e+00 6.500000e+01 8.124038e+00 5.800000e+01 7.681146e+00 4.300000e+01 6.633250e+00 4.300000e+01 6.633250e+00 3.400000e+01 5.916080e+00 2.200000e+01 4.795832e+00 4.000000e+01 6.403124e+00 3.300000e+01 5.830952e+00 2.900000e+01 5.477226e+00 1.700000e+01 4.242641e+00 1.100000e+01 3.464102e+00 1.500000e+01 4.000000e+00 1.400000e+01 3.872983e+00 1.500000e+01 4.000000e+00 8.000000e+00 3.000000e+00 1.600000e+01 4.123106e+00 3.000000e+00 2.000000e+00 5.000000e+00 2.449490e+00 7.000000e+00 2.828427e+00 9.000000e+00 3.162278e+00 6.000000e+00 2.645751e+00 4.000000e+00 2.236068e+00 3.000000e+00 2.000000e+00 5.000000e+00 2.449490e+00 2.000000e+00 1.732051e+00 1.000000e+00 1.414214e+00 4.000000e+00 2.236068e+00 2.000000e+00 1.732051e+00 3.000000e+00 2.000000e+00 1.000000e+00 1.414214e+00 1.000000e+00 1.414214e+00 2.000000e+00 1.732051e+00 2.000000e+00 1.732051e+00 1.000000e+00 1.414214e+00 1.000000e+00 1.414214e+00 0.000000e+00 1.000000e+00 0.000000e+00 1.000000e+00 0.000000e+00 1.000000e+00 0.000000e+00 1.000000e+00 0.000000e+00 1.000000e+00 0.000000e+00 1.000000e+00 1.000000e+00 1.414214e+00 0.000000e+00 1.000000e+00 0.000000e+00 1.000000e+00 0.000000e+00 1.000000e+00 0.000000e+00 1.000000e+00 0.000000e+00 1.000000e+00 0.000000e+00 1.000000e+00 0.000000e+00 1.000000e+00 0.000000e+00 1.000000e+00 0.000000e+00 1.000000e+00 0.000000e+00 1.000000e+00 0.000000e+00 1.000000e+00 0.000000e+00 1.000000e+00 0.000000e+00 1.000000e+00 0.000000e+00 1.000000e+00 0.000000e+00 1.000000e+00 0.000000e+00 1.000000e+00 0.000000e+00 1.000000e+00 0.000000e+00 1.000000e+00 0.000000e+00 1.000000e+00 0.000000e+00 1.000000e+00 0.000000e+00 1.000000e+00 0.000000e+00 1.000000e+00 0.000000e+00 1.000000e+00 0.000000e+00 1.000000e+00 0.000000e+00 1.000000e+00 0.000000e+00 1.000000e+00 0.000000e+00 1.000000e+00 0.000000e+00 1.000000e+00 0.000000e+00 1.000000e+00 0.000000e+00 1.000000e+00 0.000000e+00 1.000000e+00 0.000000e+00 1.000000e+00 0.000000e+00 1.000000e+00 0.000000e+00 1.000000e+00 0.000000e+00 1.000000e+00 0.000000e+00 1.000000e+00 0.000000e+00 1.000000e+00 0.000000e+00 1.000000e+00 0.000000e+00 1.000000e+00 0.000000e+00 1.000000e+00 0.000000e+00 1.000000e+00 0.000000e+00 1.000000e+00 0.000000e+00 1.000000e+00 0.000000e+00 1.000000e+00 0.000000e+00 1.000000e+00 0.000000e+00 1.000000e+00 0.000000e+00 1.000000e+00 0.000000e+00 1.000000e+00 0.000000e+00 1.000000e+00 0.000000e+00 1.000000e+00 0.000000e+00 1.000000e+00 0.000000e+00 1.000000e+00 0.000000e+00 1.000000e+00 0.000000e+00 1.000000e+00 0.000000e+00 1.000000e+00 0.000000e+00 1.000000e+00 0.000000e+00 1.000000e+00 0.000000e+00 1.000000e+00 0.000000e+00 1.000000e+00 0.000000e+00 1.000000e+00 0.000000e+00 1.000000e+00 0.000000e+00 1.000000e+00 0.000000e+00 1.000000e+00 0.000000e+00 1.000000e+00 0.000000e+00 1.000000e+00 0.000000e+00 1.000000e+00 0.000000e+00 1.000000e+00 0.000000e+00 1.000000e+00 0.000000e+00 1.000000e+00 0.000000e+00 1.000000e+00 0.000000e+00 1.000000e+00 0.000000e+00 1.000000e+00 0.000000e+00 1.000000e+00 0.000000e+00 1.000000e+00 0.000000e+00 1.000000e+00 0.000000e+00 1.000000e+00 0.000000e+00 1.000000e+00 0.000000e+00 1.000000e+00 0.000000e+00 1.000000e+00 0.000000e+00 1.000000e+00 0.000000e+00 1.000000e+00 0.000000e+00 1.000000e+00 0.000000e+00 1.000000e+00 0.000000e+00 1.000000e+00 0.000000e+00 1.000000e+00 0.000000e+00 1.000000e+00 0.000000e+00 1.000000e+00 0.000000e+00 1.000000e+00 0.000000e+00 1.000000e+00 0.000000e+00 1.000000e+00 0.000000e+00 1.000000e+00 0.000000e+00 1.000000e+00 0.000000e+00 1.000000e+00 0.000000e+00 1.000000e+00 0.000000e+00 1.000000e+00 0.000000e+00 1.000000e+00 0.000000e+00 1.000000e+00 0.000000e+00 1.000000e+00 0.000000e+00 1.000000e+00 0.000000e+00 1.000000e+00 0.000000e+00 1.000000e+00 0.000000e+00 1.000000e+00 0.000000e+00 1.000000e+00 0.000000e+00 1.000000e+00 0.000000e+00 1.000000e+00 0.000000e+00 1.000000e+00 0.000000e+00 1.000000e+00 0.000000e+00 1.000000e+00 0.000000e+00 1.000000e+00 0.000000e+00 1.000000e+00 0.000000e+00 1.000000e+00 0.000000e+00 1.000000e+00 0.000000e+00 1.000000e+00 0.000000e+00 1.000000e+00 0.000000e+00 1.000000e+00 0.000000e+00 1.000000e+00 0.000000e+00 1.000000e+00 0.000000e+00 1.000000e+00 0.000000e+00 1.000000e+00 ''') ImportString('x',''' 0.000000e+00 1.000000e+00 2.000000e+00 3.000000e+00 4.000000e+00 5.000000e+00 6.000000e+00 7.000000e+00 8.000000e+00 9.000000e+00 1.000000e+01 1.100000e+01 1.200000e+01 1.300000e+01 1.400000e+01 1.500000e+01 1.600000e+01 1.700000e+01 1.800000e+01 1.900000e+01 2.000000e+01 2.100000e+01 2.200000e+01 2.300000e+01 2.400000e+01 2.500000e+01 2.600000e+01 2.700000e+01 2.800000e+01 2.900000e+01 3.000000e+01 3.100000e+01 3.200000e+01 3.300000e+01 3.400000e+01 3.500000e+01 3.600000e+01 3.700000e+01 3.800000e+01 3.900000e+01 4.000000e+01 4.100000e+01 4.200000e+01 4.300000e+01 4.400000e+01 4.500000e+01 4.600000e+01 4.700000e+01 4.800000e+01 4.900000e+01 5.000000e+01 5.100000e+01 5.200000e+01 5.300000e+01 5.400000e+01 5.500000e+01 5.600000e+01 5.700000e+01 5.800000e+01 5.900000e+01 6.000000e+01 6.100000e+01 6.200000e+01 6.300000e+01 6.400000e+01 6.500000e+01 6.600000e+01 6.700000e+01 6.800000e+01 6.900000e+01 7.000000e+01 7.100000e+01 7.200000e+01 7.300000e+01 7.400000e+01 7.500000e+01 7.600000e+01 7.700000e+01 7.800000e+01 7.900000e+01 8.000000e+01 8.100000e+01 8.200000e+01 8.300000e+01 8.400000e+01 8.500000e+01 8.600000e+01 8.700000e+01 8.800000e+01 8.900000e+01 9.000000e+01 9.100000e+01 9.200000e+01 9.300000e+01 9.400000e+01 9.500000e+01 9.600000e+01 9.700000e+01 9.800000e+01 9.900000e+01 1.000000e+02 1.010000e+02 1.020000e+02 1.030000e+02 1.040000e+02 1.050000e+02 1.060000e+02 1.070000e+02 1.080000e+02 1.090000e+02 1.100000e+02 1.110000e+02 1.120000e+02 1.130000e+02 1.140000e+02 1.150000e+02 1.160000e+02 1.170000e+02 1.180000e+02 1.190000e+02 1.200000e+02 1.210000e+02 1.220000e+02 1.230000e+02 1.240000e+02 1.250000e+02 1.260000e+02 1.270000e+02 1.280000e+02 1.290000e+02 1.300000e+02 1.310000e+02 1.320000e+02 1.330000e+02 1.340000e+02 1.350000e+02 1.360000e+02 1.370000e+02 1.380000e+02 1.390000e+02 1.400000e+02 1.410000e+02 1.420000e+02 1.430000e+02 1.440000e+02 1.450000e+02 1.460000e+02 1.470000e+02 1.480000e+02 1.490000e+02 1.500000e+02 1.510000e+02 1.520000e+02 1.530000e+02 1.540000e+02 1.550000e+02 1.560000e+02 1.570000e+02 1.580000e+02 1.590000e+02 1.600000e+02 1.610000e+02 1.620000e+02 1.630000e+02 1.640000e+02 1.650000e+02 1.660000e+02 1.670000e+02 1.680000e+02 1.690000e+02 1.700000e+02 1.710000e+02 1.720000e+02 1.730000e+02 1.740000e+02 1.750000e+02 1.760000e+02 1.770000e+02 1.780000e+02 1.790000e+02 1.800000e+02 1.810000e+02 1.820000e+02 1.830000e+02 1.840000e+02 1.850000e+02 1.860000e+02 1.870000e+02 1.880000e+02 1.890000e+02 1.900000e+02 1.910000e+02 1.920000e+02 1.930000e+02 1.940000e+02 1.950000e+02 1.960000e+02 1.970000e+02 1.980000e+02 1.990000e+02 2.000000e+02 2.010000e+02 2.020000e+02 2.030000e+02 2.040000e+02 2.050000e+02 2.060000e+02 2.070000e+02 2.080000e+02 2.090000e+02 2.100000e+02 2.110000e+02 2.120000e+02 2.130000e+02 2.140000e+02 2.150000e+02 2.160000e+02 2.170000e+02 2.180000e+02 2.190000e+02 2.200000e+02 2.210000e+02 2.220000e+02 2.230000e+02 2.240000e+02 2.250000e+02 2.260000e+02 2.270000e+02 2.280000e+02 2.290000e+02 2.300000e+02 2.310000e+02 2.320000e+02 2.330000e+02 2.340000e+02 2.350000e+02 2.360000e+02 2.370000e+02 2.380000e+02 2.390000e+02 2.400000e+02 2.410000e+02 2.420000e+02 2.430000e+02 2.440000e+02 2.450000e+02 2.460000e+02 2.470000e+02 2.480000e+02 2.490000e+02 2.500000e+02 2.510000e+02 2.520000e+02 2.530000e+02 2.540000e+02 2.550000e+02 2.560000e+02 2.570000e+02 2.580000e+02 2.590000e+02 2.600000e+02 2.610000e+02 2.620000e+02 2.630000e+02 2.640000e+02 2.650000e+02 2.660000e+02 2.670000e+02 2.680000e+02 2.690000e+02 2.700000e+02 2.710000e+02 2.720000e+02 2.730000e+02 2.740000e+02 2.750000e+02 2.760000e+02 2.770000e+02 2.780000e+02 2.790000e+02 2.800000e+02 2.810000e+02 2.820000e+02 2.830000e+02 2.840000e+02 2.850000e+02 2.860000e+02 2.870000e+02 2.880000e+02 2.890000e+02 2.900000e+02 2.910000e+02 2.920000e+02 2.930000e+02 2.940000e+02 2.950000e+02 2.960000e+02 2.970000e+02 2.980000e+02 2.990000e+02 ''') Add('page', name='page1', autoadd=False) To('page1') Add('graph', name='graph1', autoadd=False) To('graph1') Add('axis', name='x', autoadd=False) To('x') Set('label', 'Wingspan (m)') Set('max', 200.0) To('..') Add('axis', name='y', autoadd=False) To('y') Set('label', 'Dragons') Set('direction', 'vertical') To('..') Add('fit', name='fit1', autoadd=False) To('fit1') Set('function', 'exp( -(x-b)**2 / c )*a') Set('values', {'a': 402.44000280769995, 'c': 791.92768286084686, 'b': 99.159997652570837}) Set('key', 'Fit to histogram') Set('Line/color', 'blue') To('..') Add('key', name='key1', autoadd=False) To('key1') Set('Text/size', '12pt') Set('horzPosn', 'left') Set('vertPosn', 'top') To('..') Add('xy', name='xy1', autoadd=False) To('xy1') Set('marker', 'none') Set('key', 'Histogram') Set('PlotLine/steps', 'centre') Set('ErrorBarLine/hide', True) Set('FillBelow/color', 'lightgreen') Set('FillBelow/hide', False) To('..') Add('label', name='label1', autoadd=False) To('label1') Set('label', 'Example \\emph{histogram}') Set('xPos', 0.84999999999999998) Set('alignVert', 'centre') Set('angle', 90.0) Set('Text/size', '35pt') To('..') To('..') To('..') veusz-3.0.1/examples/nd.vsz0000664000175000017500000000456013161413406015300 0ustar jssjss00000000000000# Veusz saved document (version 1.24) # Saved at 2016-11-20T10:15:49.326108 ImportFileND(u'nd-1.csv', u'ndim', csvlocale=u'en_GB', linked=True, mode='csv') Set('StyleSheet/Font/font', u'DejaVu Sans') Add('page', name='page1', autoadd=False) To('page1') Add('graph', name=u'inset', autoadd=False) To(u'inset') Set('leftMargin', '3.235cm') Set('rightMargin', '8.745cm') Set('topMargin', '9.487cm') Set('bottomMargin', u'2.6cm') Add('axis', name='x', autoadd=False) To('x') Set('TickLabels/hide', True) To('..') Add('axis', name='y', autoadd=False) To('y') Set('direction', 'vertical') Set('TickLabels/hide', True) To('..') Add('image', name='image1', autoadd=False) To('image1') Set('data', u'ndim[:,:,0]') Set('colorMap', u'brown-blue') To('..') To('..') Add('graph', name=u'inset2', autoadd=False) To(u'inset2') Set('leftMargin', '10.379cm') Set('rightMargin', '1.601cm') Set('topMargin', '9.487cm') Set('bottomMargin', u'2.6cm') Add('axis', name='x', autoadd=False) To('x') Set('TickLabels/hide', True) To('..') Add('axis', name='y', autoadd=False) To('y') Set('direction', 'vertical') Set('TickLabels/hide', True) To('..') Add('image', name='image1', autoadd=False) To('image1') Set('data', u'ndim[:,2,:]') Set('colorMap', u'blue-darkorange') To('..') To('..') Add('graph', name=u'main', autoadd=False) To(u'main') Set('Background/color', u'#ffe9ff') Add('axis', name='x', autoadd=False) To('x') Set('autoRange', u'+5%') To('..') Add('axis', name='y', autoadd=False) To('y') Set('autoRange', u'+5%') Set('direction', 'vertical') To('..') Add('xy', name='xy1', autoadd=False) To('xy1') Set('xData', u'ndim[1,2,:]') Set('yData', u'ndim[0,1,:]') Set('scalePoints', u'ndim[2,0,:]') Set('PlotLine/hide', True) To('..') Add('xy', name='xy2', autoadd=False) To('xy2') Set('marker', u'square') Set('color', u'#ffaa00') Set('xData', u'ndim[0,:,2]') Set('yData', u'ndim[1,:,2]') Set('scalePoints', u'ndim[0,:,2]') Set('PlotLine/hide', True) To('..') Add('xy', name='xy3', autoadd=False) To('xy3') Set('marker', u'diamond') Set('color', u'#00aaff') Set('xData', u'ndim[0,1,:]') Set('yData', u'ndim[0,2,:]') Set('scalePoints', u'ndim[2,0,:]') Set('PlotLine/hide', True) To('..') Add('xy', name='xy4', autoadd=False) To('xy4') Set('marker', u'pentagon') Set('color', u'#55aa00') Set('xData', u'ndim[:,1,2]') Set('yData', u'ndim[0,:,1]') Set('scalePoints', u'ndim[2,0,:]') Set('PlotLine/hide', True) To('..') To('..') To('..') veusz-3.0.1/examples/contour_labels.vsz0000664000175000017500000000173313161413406017711 0ustar jssjss00000000000000# Veusz saved document (version 1.20.99) # Saved at 2014-04-27T12:24:58.888827 SetData2DXYFunc(u'foo', (0.0, 10.0, 0.1), (0.0, 10.0, 0.1), u'(sin(x)+2)*(cos(y+x)+2)', linked=True) Set('StyleSheet/Font/font', u'Liberation Serif') Add('page', name='page1', autoadd=False) To('page1') Add('graph', name='graph1', autoadd=False) To('graph1') Set('leftMargin', u'1cm') Set('bottomMargin', u'1cm') Add('axis', name='x', autoadd=False) To('x') Set('autoRange', u'exact') Set('TickLabels/format', u'%Vg \\emph{a}') Set('MajorTicks/number', 10) To('..') Add('axis', name='y', autoadd=False) To('y') Set('autoRange', u'exact') Set('direction', 'vertical') Set('TickLabels/format', u'%Vg \\emph{b}') Set('MajorTicks/number', 10) To('..') Add('contour', name='contour1', autoadd=False) To('contour1') Set('data', u'foo') Set('numLevels', 10) Set('ContourLabels/hide', False) Set('Lines/lines', [('solid', '1pt', u'#5500ff', False), ('dotted', '1pt', u'#aa557f', False)]) To('..') To('..') To('..') veusz-3.0.1/examples/2d_irregular.csv0000664000175000017500000000110113161413406017215 0ustar jssjss00000000000000,1,2.15,4.64,10,21.5,26.4,100,215,464,1000 1,2.16,1.94,1.76,1.62,1.54,1.54,1.6,1.73,1.91,2.12 2.15,1.94,1.69,1.48,1.31,1.21,1.2,1.29,1.44,1.65,1.9 4.64,1.76,1.48,1.22,1.01,0.89,0.87,0.98,1.18,1.43,1.7 10,1.62,1.31,1.01,0.75,0.57,0.55,0.71,0.96,1.25,1.56 21.5,1.54,1.21,0.89,0.57,0.28,0.24,0.51,0.83,1.15,1.48 46.4,1.54,1.2,0.87,0.55,0.24,0.19,0.49,0.81,1.14,1.48 100,1.6,1.29,0.98,0.71,0.51,0.49,0.66,0.93,1.23,1.54 215,1.73,1.44,1.18,0.96,0.83,0.81,0.93,1.14,1.39,1.68 464,1.91,1.65,1.43,1.25,1.15,1.14,1.23,1.39,1.61,1.86 1000,2.12,1.9,1.7,1.56,1.48,1.48,1.54,1.68,1.86,2.08 veusz-3.0.1/examples/embedexample.py0000664000175000017500000000352213161413406017132 0ustar jssjss00000000000000# Copyright (C) 2009 Jeremy S. Sanders # Email: Jeremy Sanders # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ############################################################################## """An example embedding program. Veusz needs to be installed into the Python path for this to work (use setup.py) This animates a sin plot, then finishes """ import time import numpy import veusz.embed as veusz # construct a Veusz embedded window # many of these can be opened at any time g = veusz.Embedded('window title') g.EnableToolbar() # construct the plot g.To( g.Add('page') ) g.To( g.Add('graph') ) g.Add('xy', marker='tiehorz', MarkerFill__color='green') # this stops intelligent axis extending g.Set('x/autoRange', 'exact') # zoom out g.Zoom(0.8) # loop, changing the values of the x and y datasets for i in range(10): x = numpy.arange(0+i/2., 7.+i/2., 0.05) y = numpy.sin(x) g.SetData('x', x) g.SetData('y', y) # wait to animate the graph time.sleep(2) # let the user see the final result print("Waiting for 10 seconds") time.sleep(10) print("Done!") # close the window (this is not strictly necessary) g.Close() veusz-3.0.1/examples/starchart.vsz0000664000175000017500000003770513161413406016701 0ustar jssjss00000000000000# Veusz saved document (version 1.12.99) # Saved at 2011-08-17T21:38:56.668330 ImportString('size(numeric)',''' 4.050e-01 3.455e-01 5.800e-01 8.497e-01 8.651e-01 8.397e-01 7.715e-01 3.617e-01 2.565e-01 6.906e-01 5.932e-01 8.708e-01 2.335e-01 2.521e-01 4.786e-01 7.606e-01 3.874e-01 3.600e-01 5.842e-01 7.647e-01 4.191e-01 7.555e-01 8.471e-01 4.407e-01 4.300e-01 1.249e-01 6.256e-02 4.905e-01 2.311e-02 3.583e-01 7.830e-01 5.041e-01 3.003e-01 6.946e-01 4.093e-01 3.897e-01 4.539e-01 6.352e-01 8.445e-01 2.948e-01 5.420e-01 4.801e-01 8.155e-01 4.433e-02 3.789e-01 5.625e-01 6.695e-02 7.941e-01 3.804e-01 9.344e-01 8.059e-01 1.631e-01 4.861e-02 6.394e-01 3.932e-01 2.929e-01 8.481e-01 3.624e-01 3.574e-02 1.552e-01 3.909e-01 4.646e-02 8.587e-01 1.063e-02 9.837e-01 7.219e-01 7.637e-01 6.057e-01 8.209e-01 4.198e-01 6.915e-01 9.860e-01 9.955e-01 3.070e-01 4.891e-01 9.027e-02 1.901e-01 4.686e-01 4.856e-01 8.366e-01 2.897e-02 9.239e-01 7.923e-01 4.719e-01 7.960e-01 9.449e-01 5.453e-01 6.638e-01 7.995e-01 9.712e-02 3.511e-01 5.018e-01 1.632e-01 1.294e-02 6.344e-01 6.543e-02 7.970e-01 2.727e-01 2.513e-01 8.460e-01 3.325e-02 7.681e-02 5.476e-01 7.590e-01 8.837e-01 1.755e-01 9.289e-01 1.040e-01 4.746e-01 6.847e-02 6.449e-01 6.703e-01 5.274e-02 4.158e-01 9.168e-01 1.963e-01 4.417e-01 4.567e-01 2.895e-01 5.929e-03 9.152e-01 8.444e-01 8.529e-01 5.076e-01 4.550e-01 6.566e-01 7.721e-01 3.514e-01 3.746e-01 9.664e-01 1.667e-01 9.201e-02 4.317e-01 4.654e-01 6.015e-01 6.814e-01 4.866e-01 9.389e-01 8.908e-01 4.130e-02 4.716e-01 5.987e-01 4.767e-02 8.905e-01 8.800e-01 7.935e-01 7.342e-01 3.535e-01 9.705e-01 1.858e-01 9.075e-01 9.458e-02 1.833e-01 2.194e-01 1.657e-01 2.087e-01 4.359e-01 3.506e-01 9.515e-01 8.851e-01 4.048e-02 1.991e-01 3.447e-01 2.877e-01 1.286e-01 3.182e-01 4.290e-01 7.130e-01 5.366e-01 5.687e-01 5.497e-01 7.495e-01 7.154e-01 2.551e-01 4.554e-01 6.916e-01 6.407e-01 2.440e-01 9.261e-01 5.933e-02 8.469e-01 3.196e-01 8.569e-01 6.384e-01 2.937e-01 5.292e-02 1.931e-01 2.760e-01 2.912e-02 3.069e-01 9.244e-01 1.239e-01 2.516e-01 4.644e-01 9.013e-02 9.745e-01 3.796e-01 2.707e-02 9.780e-01 2.674e-01 ''') ImportString('size2(numeric)',''' 4.149e-01 6.146e-01 2.448e-01 7.628e-02 1.768e-01 2.063e-01 5.393e-01 2.900e-01 3.645e-01 8.713e-02 5.542e-01 8.222e-02 4.418e-01 6.340e-01 6.466e-01 6.498e-01 6.988e-01 1.329e-01 3.229e-01 5.532e-01 4.700e-01 1.371e-01 3.681e-01 5.719e-01 6.000e-01 5.295e-01 3.881e-01 2.361e-01 1.955e-01 1.751e-01 7.426e-01 5.149e-01 3.111e-01 2.939e-01 1.782e-01 3.758e-01 6.307e-01 5.059e-01 7.406e-01 4.687e-01 8.772e-03 6.461e-02 4.382e-02 9.037e-02 4.053e-01 5.267e-01 9.926e-02 5.119e-01 4.667e-01 2.564e-01 1.839e-01 3.433e-01 3.153e-01 3.838e-01 5.806e-01 4.922e-01 3.729e-02 1.960e-01 6.638e-01 5.121e-01 6.760e-01 2.950e-01 2.766e-01 2.515e-01 7.347e-01 1.599e-02 2.976e-01 1.024e-01 3.245e-01 2.576e-02 4.938e-01 5.979e-01 6.476e-01 5.961e-01 2.611e-01 5.783e-01 4.673e-01 3.781e-01 7.286e-01 2.363e-01 6.622e-01 4.862e-01 7.304e-01 3.534e-01 6.367e-01 1.068e-01 3.883e-01 6.653e-01 5.703e-01 2.747e-01 3.201e-01 5.755e-01 6.744e-01 3.886e-01 7.911e-02 8.700e-02 3.535e-01 1.245e-01 1.195e-01 5.841e-01 6.398e-01 1.762e-01 5.250e-01 5.847e-01 4.941e-01 6.708e-01 3.526e-01 4.299e-01 4.609e-01 4.045e-02 3.338e-01 4.018e-01 5.591e-01 2.480e-01 3.281e-01 2.539e-01 6.600e-01 7.943e-02 3.481e-01 1.625e-02 5.123e-01 3.390e-01 3.904e-01 4.350e-01 5.448e-01 4.653e-01 4.141e-01 1.425e-01 4.031e-01 3.517e-01 6.214e-01 7.483e-01 6.904e-01 5.430e-02 2.179e-01 4.177e-01 1.834e-01 5.792e-02 2.297e-01 5.871e-01 2.647e-01 1.269e-01 2.579e-01 7.247e-01 4.171e-01 2.275e-01 7.145e-01 6.673e-01 3.532e-01 6.283e-01 9.327e-02 1.819e-01 6.711e-01 2.019e-01 5.201e-01 3.551e-01 8.790e-02 4.421e-01 6.433e-01 5.816e-01 4.612e-01 5.547e-02 2.555e-01 6.520e-01 2.823e-01 4.777e-01 4.286e-01 3.108e-01 1.322e-01 4.525e-01 3.051e-01 1.148e-01 6.799e-01 7.144e-01 1.421e-01 2.629e-01 1.992e-01 4.288e-01 2.898e-01 5.219e-01 1.484e-01 6.617e-01 2.670e-01 7.308e-01 4.514e-01 5.690e-01 2.678e-01 7.160e-02 3.514e-02 2.425e-01 3.181e-01 3.302e-01 3.846e-01 2.463e-01 5.676e-02 5.476e-01 2.593e-01 1.812e-01 2.168e-01 5.940e-01 ''') ImportString('x(numeric)',''' -1.717564e-01 4.242809e-01 1.582449e+00 9.042384e-01 -7.558175e-01 9.180096e-01 1.688422e+00 -5.624878e-01 1.148113e+00 -6.757747e-01 -7.184862e-01 9.559501e-02 -7.735163e-01 -1.368390e+00 2.839300e-01 3.863967e-01 -1.018726e+00 -1.057551e+00 1.480676e+00 -1.436318e+00 -6.077504e-01 -6.134922e-01 -1.451637e+00 6.367692e-01 -2.051966e+00 -1.061056e-01 1.132593e+00 -2.332081e+00 -1.483644e-01 -1.709232e+00 7.715420e-01 1.825963e-01 -1.242801e+00 5.126108e-01 7.855827e-01 -1.391835e+00 3.679549e-01 -1.126583e+00 7.381246e-01 -7.205989e-01 2.466142e-01 -1.842360e+00 -1.307182e+00 7.427424e-01 -3.075417e-01 1.075113e+00 -1.109696e+00 -3.353667e-02 -1.087587e+00 1.517819e-03 1.402492e+00 -4.629109e-01 -2.314234e-01 -1.404300e+00 -7.136651e-01 1.143241e-01 -2.593171e-01 5.261581e-01 7.312414e-01 9.634989e-01 6.653014e-02 -1.525272e+00 -1.979862e+00 8.790669e-02 -3.367087e-01 -1.487463e+00 -6.323704e-01 -2.691609e-01 -1.192839e+00 7.129419e-01 -2.026876e+00 -1.670084e+00 -1.105418e+00 4.942608e-01 1.440158e+00 9.895613e-01 -5.584712e-01 3.488847e-02 -5.603513e-03 9.674879e-01 6.670954e-02 9.170016e-01 -1.313607e+00 -2.246257e+00 2.042009e+00 1.025452e+00 -8.301401e-01 4.508979e-01 5.296303e-01 1.255025e+00 1.236309e+00 -8.178501e-01 1.658664e+00 -1.020029e+00 -7.597083e-01 1.642019e+00 1.163782e+00 3.648611e-01 1.205375e+00 -2.208854e+00 1.961078e-01 2.150545e-01 1.335152e+00 2.279445e-01 -3.136242e-02 9.497465e-01 2.207430e-01 -7.895290e-01 -8.275847e-01 7.666461e-01 -1.008892e-01 -1.343381e+00 -1.484571e+00 5.167052e-01 -3.572661e-01 1.440624e+00 9.023146e-01 -3.818540e-01 -1.613737e+00 -1.362967e-01 7.986278e-01 5.329052e-01 -7.147625e-02 1.977507e+00 6.095448e-01 -1.774797e+00 -5.755160e-01 -6.896392e-01 3.003489e-01 1.888296e-01 7.108283e-01 8.797144e-01 2.526157e-01 3.947440e-01 8.264080e-01 -2.686278e-01 -5.568894e-01 -8.562743e-01 -6.682676e-01 1.613319e-01 8.180479e-02 7.551501e-01 1.345002e-01 -5.441405e-01 -4.750194e-01 -1.144256e+00 -3.917083e-02 5.153497e-01 -3.864621e-01 -9.947071e-01 4.151202e-01 1.694649e-01 8.304631e-01 -2.744186e+00 -5.189704e-01 5.950722e-01 4.409983e-01 -3.860202e-01 2.654908e+00 -4.632929e-01 3.676008e-02 3.220285e-01 -1.910937e-01 -7.468510e-01 -8.650068e-01 -9.713170e-01 -1.130643e-01 -3.678296e-01 -4.921382e-01 1.181965e-01 6.255350e-01 -1.242620e+00 1.156373e+00 7.030362e-01 9.894105e-02 5.791196e-01 -8.927444e-01 1.267695e+00 -7.141264e-01 2.025816e+00 -1.293909e+00 -1.477149e+00 -3.851663e-02 -6.038046e-01 -5.637100e-01 -7.411827e-01 -1.302341e+00 -9.121214e-01 1.058442e+00 1.873947e+00 -6.876215e-01 -9.217540e-02 -1.163100e+00 1.145855e+00 -4.506662e-01 -1.423829e+00 1.236884e-01 2.303835e-01 1.388673e+00 9.321516e-01 ''') ImportString('x2(numeric)',''' -5.904152e-01 6.036018e-01 1.861803e-01 -1.182390e-01 4.273179e-01 -5.329131e-01 3.477783e-01 -4.442659e-01 4.112825e-01 -6.975073e-01 -7.894340e-02 3.737914e-01 5.502770e-01 -1.807715e-01 2.749290e-01 8.347531e-01 -2.566171e-01 4.480129e-02 -1.661492e-01 -1.005103e+00 6.827219e-02 1.267272e-01 -7.289300e-02 7.991350e-02 -4.951967e-01 1.395421e+00 -1.094929e+00 3.143240e-01 1.436579e-01 5.987043e-01 8.988077e-04 6.491476e-01 -1.945068e-01 -1.464254e-01 1.318037e-01 5.736032e-01 -6.424045e-01 1.929405e-02 1.083004e-01 -4.876157e-01 -1.975081e-01 -9.470392e-01 7.348370e-02 -3.555848e-01 -4.303004e-01 -1.937558e-01 -3.060530e-01 -1.239214e+00 -7.159538e-01 -1.180139e-01 -8.582931e-01 9.332523e-01 8.186905e-02 1.658889e-01 3.878644e-01 4.596426e-01 -2.580098e-01 1.466808e-01 -2.334527e-01 -7.628955e-01 -4.923554e-02 -1.866111e-01 -1.203111e-01 7.535110e-01 3.363780e-01 -7.143939e-01 9.262992e-01 4.826713e-02 -3.288597e-01 -3.829378e-01 6.682712e-01 -7.421102e-01 4.511552e-02 -7.936474e-01 5.949329e-01 6.510491e-02 -9.386907e-02 7.775805e-01 1.218719e-01 -1.494402e-02 -6.653168e-03 -1.096615e+00 -5.639730e-01 -2.858281e-01 4.137544e-01 9.787053e-02 -3.736432e-02 1.178890e-01 8.644677e-02 8.907128e-01 -2.575110e-01 8.681292e-01 3.192787e-01 -4.730755e-02 3.308247e-02 5.196318e-01 1.612190e-02 -9.430100e-02 -3.501541e-01 1.988158e-01 -6.610444e-02 3.183876e-01 9.110975e-01 3.537435e-01 2.405701e-02 -1.046873e+00 3.043448e-01 1.020531e-01 -5.537825e-01 -1.344010e-01 -4.608990e-01 9.335337e-01 -9.321411e-02 3.717192e-01 3.558881e-01 -2.581769e-01 7.042958e-01 4.890943e-02 -3.936620e-01 -1.006861e+00 -4.333877e-01 7.150886e-01 -4.349794e-01 4.448926e-01 4.621154e-01 1.543000e-01 -1.210074e-01 -9.640434e-02 6.018297e-01 -1.054106e-02 -9.582333e-02 -1.221224e+00 -2.510498e-01 8.605007e-01 -2.235232e-01 6.005776e-01 5.389756e-02 2.774058e-01 -7.967761e-02 -2.286575e-01 -2.433566e-01 -1.338816e-01 -1.915733e-01 -9.346132e-02 -3.945893e-01 -1.000764e+00 -4.389132e-01 -5.512491e-01 -3.093361e-01 -5.310395e-01 5.232963e-01 -4.543161e-01 1.269040e-01 -2.244547e-01 6.759115e-01 2.601059e-01 -4.742974e-01 7.627344e-02 1.575795e-01 2.050366e-01 3.363180e-01 5.956497e-01 7.390307e-01 6.510501e-01 -7.047546e-01 8.470191e-01 -2.368321e-01 -2.557962e-01 9.486894e-01 -3.198666e-01 -6.815602e-02 -2.756185e-01 9.079499e-01 6.653649e-01 1.566721e-01 -1.089555e+00 -1.078753e-01 -1.503253e-01 1.093731e+00 6.795102e-01 9.591976e-01 7.418597e-02 -4.870427e-01 -1.311627e-01 3.575294e-01 2.585647e-01 -1.156918e-01 -9.740063e-02 7.856365e-01 7.415529e-01 2.188530e-01 5.858654e-01 6.431388e-01 7.472439e-01 -3.185660e-01 1.466183e-01 -3.765560e-01 -2.363125e-01 -7.311099e-03 4.543543e-01 ''') ImportString('y(numeric)',''' -1.179418e+00 9.963364e-01 1.035245e+00 -4.930856e-01 -3.079140e-01 -8.363301e-01 3.772350e-01 -1.793423e+00 -3.955435e-01 -7.903513e-01 2.248519e-01 8.868708e-01 1.385193e+00 -9.564458e-02 -2.326000e-01 -1.571620e-01 6.207154e-01 3.515783e-01 -6.039606e-01 -4.724230e-01 -2.933388e-01 -8.633148e-01 -1.216204e-01 -9.216055e-01 -1.218777e+00 7.417790e-01 -5.619947e-02 5.039686e-01 -2.296762e-01 -6.494386e-01 1.621411e+00 -2.070276e-01 3.945420e-01 -6.584078e-02 -1.396862e-01 1.662038e+00 -1.202367e+00 2.562593e-01 -2.478231e-01 -8.922410e-01 1.175726e+00 2.447118e-01 1.086628e+00 -1.745885e+00 1.342561e+00 -9.685250e-01 1.514767e+00 -8.541269e-01 -7.186263e-01 -2.286539e-01 -4.432723e-03 -2.748991e-01 -1.319403e-01 -1.297866e+00 -1.293355e+00 -2.800920e+00 4.857350e-01 1.273159e+00 -8.956792e-01 1.418659e+00 1.314727e+00 9.482291e-01 8.534508e-01 2.193894e+00 8.981459e-01 -8.840747e-02 -8.619552e-01 4.299827e-01 -4.838336e-01 1.537284e+00 -1.290272e+00 4.619874e-01 -1.756571e-01 1.502658e+00 -1.084621e+00 1.366688e+00 6.150953e-01 -1.909314e+00 1.499255e+00 -5.910319e-01 1.289383e-01 -1.935533e+00 -7.818810e-02 1.060331e+00 -8.665276e-01 3.874504e-01 5.042166e-01 6.286272e-01 1.567413e+00 3.106100e-01 7.147362e-01 -1.696396e+00 9.068055e-01 -1.004093e+00 -5.714048e-01 -9.721016e-01 5.559200e-01 2.595251e+00 4.741763e-01 -4.348529e-01 -1.789185e+00 -3.293816e-01 -6.850946e-01 2.887457e-01 6.801073e-01 -3.200756e-01 5.279586e-01 -1.002568e+00 -1.523962e+00 -8.351697e-01 1.209814e+00 -7.876147e-01 6.289090e-01 -2.341422e-01 -3.740775e-01 2.385541e-01 -8.140004e-02 9.481686e-02 1.225121e+00 -3.815732e-01 -7.840012e-01 2.927405e+00 9.265183e-01 7.812431e-01 1.302915e+00 6.347094e-01 5.027754e-01 -7.110444e-01 -4.297201e-01 1.828868e+00 -4.414072e-02 -1.110879e+00 3.847165e-01 -4.791328e-01 3.638377e-01 -1.728797e+00 -4.163380e-02 2.300275e-01 -8.915757e-01 -6.725092e-01 9.569900e-01 -4.703625e-01 1.274738e+00 1.264137e+00 -4.649065e-01 -4.831641e-02 -1.410860e+00 6.752751e-01 -2.005785e+00 -8.789505e-01 -5.581799e-01 6.318567e-01 -7.021317e-01 2.698140e-01 -2.171639e+00 -4.838061e-01 -4.254585e-02 7.594293e-01 1.628929e+00 -7.460201e-01 -3.770777e-01 8.849620e-02 6.164952e-02 1.475529e+00 -1.113757e+00 -1.621159e+00 4.006426e-01 1.077418e-01 -1.913829e+00 -1.317677e+00 4.880339e-01 -4.312087e-01 -1.442873e-01 -2.004930e-02 5.019437e-01 -7.684092e-01 1.541577e+00 -3.599171e-01 -9.827931e-01 -1.514225e-01 3.947186e-02 1.175757e+00 1.097290e+00 -5.108434e-01 -2.445287e+00 -7.330248e-02 -4.411181e-01 9.852479e-01 -1.161989e+00 -1.082814e+00 -5.290654e-02 1.425348e+00 -2.933696e-01 -8.431636e-01 1.201905e+00 7.220561e-02 -6.565752e-02 2.359271e+00 4.880444e-01 1.560229e+00 ''') ImportString('y2(numeric)',''' 3.277343e-01 -4.791839e-01 -5.622894e-01 -8.147905e-01 1.461145e-01 -4.588977e-01 3.968568e-01 1.423664e-01 5.319692e-01 5.065758e-01 -6.637735e-01 -2.946463e-01 -4.624716e-01 6.319409e-01 4.678516e-02 2.219609e-01 1.170330e+00 7.035864e-01 -3.346529e-01 1.734514e-03 7.248274e-01 6.135434e-01 1.028304e-01 -1.206416e-01 2.208649e-02 6.919419e-01 3.821196e-01 2.178115e-01 1.770095e-01 -1.652230e-02 9.046649e-01 -4.432710e-01 2.206628e-01 -4.543656e-01 1.301324e+00 5.292525e-01 -4.944746e-01 2.174136e-01 -3.947546e-01 4.118183e-01 3.404201e-01 6.837868e-01 8.153092e-01 -1.681122e-01 -8.328832e-01 -9.128235e-02 -7.871977e-01 1.963190e-01 6.868842e-01 7.185369e-01 -1.860621e-01 9.277049e-01 -5.712796e-01 9.867310e-01 -7.430959e-01 -4.200795e-01 -3.312437e-01 3.417390e-01 5.875478e-02 -8.900409e-01 4.459047e-01 -2.478977e-01 2.576785e-01 -1.180131e+00 3.503088e-01 2.095792e-01 -5.624632e-01 -1.504682e-01 1.082096e-01 2.748657e-02 -2.188184e-01 -3.769354e-01 3.687838e-01 -1.177511e-01 -2.714600e-01 -1.006103e-01 -1.509157e-01 7.425458e-02 1.376503e-01 -4.981824e-01 -4.858082e-01 2.773604e-01 1.011194e-02 7.998077e-01 5.213352e-01 -2.153188e-01 7.149105e-01 -4.479349e-02 4.948704e-02 -3.989437e-01 -2.229656e-01 2.417801e-01 4.207912e-01 -2.746693e-01 2.094705e-01 1.712305e-01 -5.440275e-01 2.324411e-01 1.052073e+00 -1.872281e-01 6.171029e-01 1.811711e-02 3.791602e-01 -8.040386e-02 2.399947e-01 -3.413228e-01 1.895484e-01 -2.544584e-01 -4.259011e-02 6.655630e-01 -7.508104e-01 3.565060e-01 6.546992e-01 -5.122739e-01 -5.014537e-01 4.045582e-01 9.928116e-01 6.045285e-01 5.509190e-02 6.477872e-01 4.653931e-01 3.743647e-01 8.731676e-01 2.466262e-01 1.092800e-01 -2.989571e-02 1.348799e-01 1.836956e-01 2.409095e-01 4.300016e-01 -1.057074e+00 -6.740874e-01 -9.931708e-02 -1.897434e-01 8.633123e-01 1.121861e+00 7.219913e-01 5.390399e-01 -1.552721e-02 -2.794775e-01 1.651937e-01 3.403011e-01 -4.625169e-01 2.946608e-01 2.377226e-01 -3.711845e-01 1.413453e-01 2.804835e-01 -1.872180e-01 4.300357e-01 -4.654751e-01 4.160271e-01 1.007708e+00 -1.936004e-02 2.207483e-01 -6.654413e-01 -4.479407e-01 -1.364038e-01 4.005023e-01 -2.469230e-01 -4.057847e-01 -4.814272e-01 -4.971257e-01 -2.414501e-01 -1.648537e-01 8.348442e-02 -1.048533e-01 3.140404e-01 3.173624e-01 2.430033e-02 -2.236744e-01 -2.959903e-01 -2.569196e-02 5.527547e-01 -2.281922e-01 -2.492468e-01 4.654103e-01 7.154733e-01 -5.553536e-01 -6.881153e-01 8.689605e-01 6.762373e-01 -1.865834e-01 3.385609e-01 1.006165e-01 -1.133610e+00 3.109142e-01 1.346475e-01 -2.651954e-01 7.159829e-02 -4.473595e-01 -8.366994e-01 4.135409e-01 -6.879046e-01 -2.671874e-01 5.195351e-01 -2.111901e-01 5.907788e-01 6.770766e-02 -5.751893e-02 ''') Add('page', name='page1', autoadd=False) To('page1') Add('graph', name='graph1', autoadd=False) To('graph1') Set('leftMargin', u'0.2cm') Set('bottomMargin', u'0.2cm') Add('axis', name='x', autoadd=False) To('x') Set('TickLabels/hide', True) To('..') Add('axis', name='y', autoadd=False) To('y') Set('direction', 'vertical') Set('TickLabels/hide', True) To('..') Add('xy', name='xy1', autoadd=False) To('xy1') Set('scalePoints', u'size') Set('PlotLine/hide', True) To('..') Add('xy', name='xy2', autoadd=False) To('xy2') Set('xData', u'x2') Set('yData', u'y2') Set('marker', u'circle') Set('scalePoints', u'size2') Set('PlotLine/hide', True) Set('MarkerLine/hide', True) Set('MarkerFill/color', u'#5555ff') To('..') Add('function', name=u'horz', autoadd=False) To(u'horz') Set('function', u'0') Set('Line/color', u'lightgrey') To('..') Add('function', name=u'vert', autoadd=False) To(u'vert') Set('function', u'0') Set('variable', u'y') Set('Line/color', u'lightgrey') To('..') Add('ellipse', name='ellipse1', autoadd=False) To('ellipse1') Set('xPos', [0.5]) Set('yPos', [0.5]) Set('width', [0.8]) Set('height', [0.8]) Set('rotate', [0.0]) Set('Border/color', u'grey') Set('Border/width', u'0.25pt') To('..') To('..') To('..') veusz-3.0.1/examples/3d_points.vsz0000664000175000017500000012247013304744067016613 0ustar jssjss00000000000000# Veusz saved document (version 2.99) # Saved at 2018-06-03T10:51:35.407722 AddImportPath(u'/home/jss/code/veusz/examples') ImportString(u'x(numeric)',''' 6.575945e-01 -4.597165e-01 -1.294541e+00 6.046494e-01 -1.542826e+00 -3.421373e-01 2.613059e+00 6.217758e-01 -5.709364e-01 -4.273195e-01 -3.705635e-01 -6.938285e-01 1.886360e-01 3.356537e-01 -8.721663e-01 3.262024e-01 8.843625e-01 -7.829539e-01 1.494213e+00 -1.472435e+00 1.394765e-01 5.483739e-01 -4.679755e-01 -1.493616e-01 -4.994069e-01 -9.298878e-01 -9.137490e-01 4.627606e-01 -1.092922e+00 -1.428952e+00 6.704154e-01 7.226900e-01 2.920627e+00 -5.469504e-02 -1.234474e-01 -1.827588e+00 -4.678403e-01 -2.104938e+00 1.447594e+00 -1.523884e+00 2.361838e-01 3.838988e-01 7.595115e-01 1.479267e-01 -2.283692e+00 -5.810091e-02 1.170725e+00 1.396499e+00 -5.571049e-01 4.147858e-01 2.961782e-01 -1.889235e-01 -2.145004e+00 1.003659e+00 1.724178e+00 1.120252e+00 1.016123e+00 -9.144657e-01 8.485133e-01 1.159397e+00 -2.090049e-01 -1.556244e-01 -1.741192e+00 -1.128013e+00 -1.680936e-02 -5.643035e-01 -3.418768e-01 -1.381331e+00 1.572115e-01 -5.324926e-01 -6.211649e-01 -6.427787e-01 -2.067742e+00 1.503553e-01 4.212680e-01 1.002436e+00 9.319358e-01 1.488413e+00 1.611216e+00 1.961401e-01 -1.960468e+00 -1.180701e+00 4.136037e-01 1.628905e+00 -3.425286e-01 -8.424676e-01 1.896311e-01 -2.951910e-01 -2.345707e-01 7.745899e-01 -5.985022e-01 -5.268234e-01 8.910938e-01 9.483483e-01 -1.485835e+00 -3.059546e-01 -6.529805e-01 1.241925e+00 -8.935098e-01 2.013549e+00 -5.429805e-01 -7.380430e-01 -2.733409e-01 -1.623544e-01 -1.831957e+00 3.826615e-01 -3.291744e-01 6.616031e-01 1.935669e-01 5.167964e-01 -1.162472e+00 4.861778e-01 6.349518e-01 -1.531453e+00 4.228641e-01 -5.552344e-01 -6.534992e-01 1.133047e-01 -7.680106e-01 -6.059610e-01 -6.632049e-01 -9.432127e-02 6.371894e-02 -8.184750e-01 -4.357186e-01 5.305605e-01 -3.734920e-01 -4.181240e-01 -1.532215e+00 -2.079682e+00 9.794448e-01 -1.564106e-01 -8.107372e-01 1.300245e+00 9.623794e-02 -9.838281e-02 -1.137283e+00 7.555458e-01 -1.144728e+00 6.750858e-01 4.795718e-01 -2.764706e-01 -1.267393e+00 -6.154198e-01 1.633117e-01 -2.817693e+00 -5.658915e-01 1.011051e+00 -4.370464e-01 -1.188365e-01 2.575782e-01 -7.056303e-01 -1.068010e+00 4.032612e-01 -7.153901e-01 -1.179347e-01 9.350778e-01 -1.999376e-01 3.372098e-01 -9.558382e-02 -1.279142e+00 -7.675026e-01 5.983604e-01 2.474651e-01 1.283235e+00 -9.074506e-01 2.597134e-01 -1.632636e+00 5.169415e-02 -1.606035e+00 -1.192602e+00 5.146239e-01 -5.556324e-01 -8.466126e-02 1.301321e+00 -8.783866e-01 8.062031e-01 -8.171129e-01 -4.724771e-01 6.760570e-01 1.511368e+00 -1.231819e+00 6.993175e-01 -9.808857e-01 2.299139e-01 -2.304337e+00 5.872667e-01 -8.249454e-01 -1.208952e+00 -1.787110e+00 -1.545373e+00 5.693720e-02 4.141107e-01 1.428096e+00 2.207367e-01 -5.586436e-01 -8.101057e-01 -3.000182e-01 1.360002e-01 -1.187395e+00 -4.147265e-01 -3.548813e-01 -9.105162e-01 8.440424e-01 4.427881e-01 1.087030e-01 -1.247606e+00 -2.388772e-01 2.824777e+00 -4.144348e-01 -3.685747e-01 -1.934565e-01 -5.280971e-01 5.459984e-01 -3.684189e-01 1.827164e+00 5.313002e-01 -1.911488e-01 -3.478762e-01 1.881151e+00 -9.231239e-02 6.301862e-01 -1.223395e-01 3.502475e-01 7.351573e-01 -2.110288e+00 1.084455e+00 -5.102715e-01 3.541769e-01 1.640319e+00 9.571136e-01 6.688606e-03 1.502540e-01 -1.353644e-01 -1.076577e+00 1.377542e-01 -7.415674e-01 4.831345e-01 -1.822664e+00 -3.267646e-01 -6.142384e-01 -1.485771e+00 -9.968889e-01 -6.338091e-01 8.110989e-01 1.091243e+00 1.031676e+00 6.449091e-01 -1.499401e+00 5.577764e-01 -6.767706e-01 9.673607e-01 -9.198047e-01 -1.451543e+00 2.679038e-01 -4.431318e-01 2.973232e-01 -5.374791e-02 1.733204e-01 -8.192104e-01 -6.794176e-01 8.508852e-01 1.063347e+00 -1.786298e+00 -9.144968e-01 1.970740e-01 5.041221e-01 7.165846e-01 1.026105e+00 5.896582e-01 -1.265926e+00 -4.087630e-01 -8.948241e-01 3.512503e-01 -1.038324e+00 -5.956805e-01 -2.536114e+00 -1.771895e+00 1.683401e+00 -1.159233e-01 1.773992e+00 5.772451e-01 4.930165e-01 -1.349837e+00 -2.741179e-01 1.949583e+00 1.350020e+00 -2.500606e-01 8.029942e-01 -1.458258e-01 6.535212e-01 -6.592328e-01 -1.720582e+00 9.176958e-01 1.361313e+00 1.991351e+00 -1.309955e+00 -1.249655e+00 -1.503138e+00 4.991277e-01 -1.376587e+00 1.400103e+00 -4.608737e-01 1.553510e+00 8.417668e-01 3.342957e-01 9.638643e-01 -1.296579e+00 1.584869e-02 9.270067e-01 -1.088615e+00 2.354753e-01 2.091593e+00 4.294130e-01 6.816036e-01 1.211737e+00 6.725487e-01 1.625473e+00 2.720051e-01 -7.019385e-01 -1.403672e+00 5.889905e-01 2.299162e-01 -1.307476e+00 -1.453698e+00 4.215200e-01 2.710028e-01 -1.168193e+00 2.128908e-01 3.514228e-01 1.097068e+00 -8.832506e-01 -1.302681e+00 5.235724e-01 4.299795e-01 -9.025606e-01 1.060824e+00 -7.277160e-01 -6.846875e-01 -4.698416e-01 -2.854868e-01 -1.073637e+00 1.593418e-01 -8.198313e-01 -9.414586e-01 4.649506e-01 1.145734e+00 5.660846e-01 -5.430044e-01 1.052291e+00 1.118337e+00 -1.427335e+00 6.679527e-01 8.030293e-01 2.230485e-01 5.702001e-01 -1.662920e+00 -6.068394e-01 -1.797897e+00 2.319649e+00 -9.496267e-01 -3.275762e-01 1.078122e+00 -4.700142e-01 -7.403032e-01 -1.733204e+00 -5.484752e-01 -3.894122e-01 1.308303e+00 9.878048e-01 -9.314316e-01 2.464047e-01 -1.836936e+00 -2.132210e-01 4.679691e-02 -7.145880e-01 1.409683e+00 1.065991e+00 -8.897803e-01 -1.895685e+00 -1.115425e+00 -1.220561e+00 -7.670861e-01 -5.082983e-01 -2.843515e-01 -1.817493e-01 5.607535e-02 1.080978e-01 2.921833e-01 -9.935305e-02 5.958283e-02 -2.430395e+00 -4.595276e-01 1.141080e+00 1.213483e+00 4.475026e-01 1.472489e-01 -1.131198e+00 -2.209366e+00 8.553038e-01 2.214906e-01 -1.468653e+00 -1.513175e-01 4.331195e-01 -7.296202e-02 -1.023579e+00 1.556930e+00 8.731691e-01 -1.977918e+00 -3.200059e-01 6.229458e-01 -2.967699e-02 5.539329e-01 -8.100015e-01 -3.150827e-01 8.080528e-01 -1.172249e+00 1.922353e-02 1.459456e+00 1.224271e+00 6.403237e-01 -2.932877e-01 -2.847627e-01 1.068048e-02 -6.161769e-01 2.415137e-01 2.202804e-01 1.087585e+00 -1.784008e+00 8.322558e-02 -7.339344e-01 -2.624561e-01 1.464645e+00 -1.162290e+00 -1.410460e+00 -4.095846e-01 -1.193438e+00 7.371463e-02 3.359293e-01 5.336460e-01 8.579012e-01 9.053431e-01 -5.406544e-01 6.622075e-01 1.668189e+00 4.042456e-01 9.598668e-02 -2.054503e-02 -5.388980e-01 1.053801e+00 3.770962e-01 -1.521012e+00 7.800601e-01 8.273950e-01 1.672286e-01 -5.972263e-01 2.277154e-02 -2.126093e-01 -1.171955e+00 4.221391e-02 -7.768830e-01 -4.823665e-01 6.399463e-01 -7.477009e-01 -2.382514e-01 -4.108919e-01 8.648755e-01 -3.926469e-01 9.939130e-01 -1.727891e+00 9.252085e-01 2.232149e-01 4.464207e-01 -3.929183e-01 -1.022040e+00 -4.442969e-01 -9.667512e-01 -1.943685e-01 4.243596e-02 1.898147e+00 3.700324e-01 -9.915933e-01 -9.664601e-01 7.837597e-01 1.572864e+00 1.172413e+00 1.987776e+00 1.455706e+00 -1.954383e-01 3.792386e-01 3.359054e-01 6.918419e-01 -5.821810e-01 -1.153517e-01 -8.528201e-01 -9.943133e-01 -9.749337e-01 -7.081749e-02 2.157507e-01 2.748330e-01 6.118113e-01 -1.129072e+00 1.086344e+00 3.349272e-01 -2.993181e-01 1.594021e+00 3.126491e-01 7.714834e-02 6.644557e-01 -1.333031e-01 -2.798737e-01 8.913666e-03 -6.720548e-01 7.015418e-02 -1.719491e+00 -4.831361e-01 9.808272e-01 -1.548688e+00 1.505073e+00 1.326813e+00 5.544685e-01 7.674057e-01 -1.796936e-01 1.219698e-01 -5.725746e-02 -3.251488e-01 -6.866173e-01 -7.829653e-01 9.025977e-01 1.367934e-01 5.744103e-01 3.187247e-02 8.858256e-01 6.072003e-01 -1.073681e+00 9.383712e-01 3.376787e-01 -1.130181e+00 -4.136191e-01 5.442963e-01 -1.536490e+00 1.359972e-01 -4.364696e-01 2.024946e-01 -1.026101e+00 -1.300867e+00 9.985169e-01 -1.199269e+00 9.747430e-01 -1.387852e+00 -8.322361e-01 -1.870326e-01 1.070328e-01 -6.794822e-01 1.495956e-01 -2.322286e+00 3.773476e-01 1.386413e-01 -4.907990e-01 3.988202e-01 -2.893963e-01 -2.014782e+00 4.747069e-01 -6.271057e-01 1.352911e+00 -1.331574e-01 5.297801e-01 -9.333435e-01 1.045870e+00 -2.063103e-01 1.590666e+00 1.253344e+00 1.082861e+00 8.184424e-02 -3.422746e-01 1.411141e-01 2.214340e-01 1.971397e+00 9.284486e-01 -3.794990e-01 -1.697609e+00 -1.542876e-01 6.038788e-02 1.119731e-01 -1.342292e+00 6.167371e-01 -2.909344e-01 2.907736e-01 -2.176797e+00 -9.045213e-01 -3.477301e-01 8.765699e-01 1.556017e-01 2.819929e-01 -2.632806e+00 -8.653749e-01 -6.810726e-01 4.914074e-02 3.701784e-01 6.820778e-01 1.416828e+00 -2.437820e-01 5.850421e-01 -6.056892e-01 -5.769027e-01 7.731703e-01 -2.750924e-01 1.519029e-01 7.487665e-01 -9.524992e-01 9.006439e-01 4.869181e-01 4.875184e-01 5.749718e-01 -3.208374e-01 -7.736690e-01 3.676714e-01 -1.706511e-01 -8.774383e-01 5.466246e-01 -5.341475e-02 5.580740e-01 -7.605753e-01 -9.633301e-01 -3.971460e-01 -2.052371e+00 -8.760304e-01 9.065972e-01 -8.839572e-01 1.277992e-02 6.543460e-01 -4.584419e-02 -1.394060e+00 -1.658369e-01 -1.504845e+00 3.317637e-01 2.782118e-01 -1.044088e+00 2.077313e-01 -2.216897e+00 -8.352427e-01 2.301431e-01 -9.593006e-01 1.636713e-01 4.682062e-01 6.151720e-02 8.953983e-02 6.831235e-01 -6.273403e-01 -1.647120e+00 -5.462123e-01 -1.895513e-01 -1.531171e+00 6.105233e-02 -3.882906e-01 -1.153499e+00 -2.847800e-01 -6.303580e-01 4.223693e-01 1.911396e+00 -1.601234e+00 -3.354251e-01 2.066401e+00 -1.059778e-01 -1.077062e+00 -1.678919e+00 3.025515e-02 -4.772348e-01 -5.363986e-01 2.972284e-01 6.684480e-01 5.052774e-01 -1.652544e-01 3.517295e-01 6.970493e-01 1.598840e-01 -2.547021e-01 7.865080e-01 -3.003841e-01 -5.172842e-01 -1.115236e+00 5.065615e-01 4.737269e-01 3.818390e-01 9.459315e-01 -1.323630e-01 -3.932525e-01 -9.084781e-01 1.282015e+00 7.667112e-01 9.281049e-01 -8.250696e-01 -3.271636e-01 -1.558873e+00 1.058722e+00 3.356877e-01 1.811349e+00 -1.234934e+00 -1.228348e+00 -1.093391e-01 6.125383e-01 -8.354238e-01 -7.390329e-01 4.630171e-01 4.458936e-01 -1.754351e+00 4.219322e-01 2.264223e+00 -1.105411e-01 1.425981e+00 2.244412e-01 1.900381e+00 -1.214930e+00 1.080752e+00 -1.337981e+00 1.056898e+00 1.099333e-01 -5.241048e-01 3.602661e-02 -7.649673e-01 1.958873e+00 1.941562e-01 1.197552e+00 -6.343648e-01 -3.382831e-01 -2.045386e+00 3.157945e-01 1.723993e+00 7.709652e-01 1.477289e+00 -9.910441e-01 5.315044e-01 -1.117195e-01 -8.840283e-01 -1.291101e+00 9.515637e-01 -7.834144e-01 -1.049730e+00 -6.238364e-01 7.201101e-02 -8.432643e-01 -5.352861e-01 2.114590e+00 1.000134e+00 2.029229e+00 1.302714e+00 4.575563e-01 1.000153e+00 3.980944e-02 -1.391373e+00 1.969100e+00 -8.664561e-01 1.215493e+00 9.503796e-01 8.556876e-01 -5.944360e-01 1.221865e+00 1.153391e+00 -1.070534e+00 -3.838239e-01 -2.792139e-01 -2.371496e+00 -1.125563e+00 -1.221992e+00 -5.713789e-01 -2.946280e-01 -4.317868e-01 3.049696e-01 -1.205854e+00 1.699346e-01 1.895042e+00 7.151819e-01 -1.121639e+00 4.237503e-01 -6.699489e-01 -2.197595e+00 1.029328e+00 -3.190738e-01 -1.320169e-01 8.963858e-01 -1.056699e+00 8.763092e-01 2.670012e-01 1.842868e-01 -5.067628e-02 1.679468e+00 -9.966635e-01 7.939771e-02 2.557930e-01 7.430268e-01 8.982660e-01 -8.284885e-01 -9.247194e-01 -4.975854e-01 -9.566421e-01 -3.928469e-01 -1.022061e+00 1.188656e+00 -6.267707e-01 2.976422e-01 1.824212e-01 -5.874295e-01 5.161827e-01 -7.793258e-01 -9.506438e-02 1.154648e-01 8.453825e-01 -7.378995e-01 1.199362e+00 -2.802611e-01 -1.428392e-01 3.124712e-01 -1.080440e+00 -6.550114e-01 -1.405661e+00 -7.452874e-02 -2.192632e-02 4.439580e-01 8.875571e-01 -7.867961e-01 2.233032e+00 9.082789e-02 6.887143e-02 -2.588297e-01 7.131068e-01 -1.571485e-01 -8.860935e-02 -1.105863e+00 -1.646700e-02 6.312749e-01 1.668317e+00 9.922411e-01 1.922205e-01 1.783587e+00 4.834640e-01 1.583260e+00 -1.823795e+00 3.555235e-01 -7.036149e-01 5.271822e-01 8.661789e-01 1.133166e-01 1.653892e+00 4.659449e-01 -5.937315e-01 -7.376169e-01 8.406163e-01 -5.022034e-01 7.451615e-01 -1.808010e-01 -7.003776e-01 -1.958248e-01 2.321085e-01 -7.430493e-01 -3.253167e-01 -1.203605e+00 -1.450469e+00 5.262856e-01 2.078431e-01 4.753250e-03 5.936968e-01 2.876322e-02 -9.874079e-01 -1.035870e+00 8.792489e-01 -6.808972e-01 -1.159200e+00 1.500860e+00 -1.380023e+00 1.570848e+00 -1.064337e+00 -1.205273e-01 -1.318482e+00 -6.110916e-01 -1.064924e+00 1.642567e+00 6.977881e-01 -2.226448e+00 1.006434e-01 -7.178818e-01 1.305210e+00 7.156374e-01 -4.976225e-01 -2.612897e-01 -2.024431e-01 8.144161e-01 -3.054805e-01 1.247295e+00 5.271582e-01 -2.637209e-01 1.114477e+00 2.163940e+00 7.072077e-01 -2.085300e-01 1.098515e+00 -6.654391e-01 -4.740257e-01 7.466231e-01 7.318385e-01 2.240750e-01 -4.229206e-01 1.922796e+00 -5.395348e-01 -2.809058e-01 -1.975230e+00 1.271656e+00 9.955049e-01 1.860179e+00 1.892061e-01 1.732155e+00 1.005167e-01 -1.484021e+00 1.920999e-01 -3.023095e-01 8.843114e-01 -1.006511e+00 -1.497801e-01 9.983697e-01 1.425861e+00 1.195617e+00 3.891273e-01 -5.191268e-01 2.329882e+00 2.491395e-01 -9.906358e-01 1.024922e+00 5.629539e-01 -2.725049e-01 -8.872036e-01 1.261194e+00 -6.390369e-01 1.049518e+00 9.597792e-01 3.160019e-01 5.184906e-01 4.561846e-02 -1.241051e+00 -8.811230e-01 -8.267438e-01 4.605573e-01 9.115633e-02 1.437520e-01 -4.257189e-01 6.370570e-01 -4.430406e-01 4.938789e-01 1.030215e+00 3.937871e-01 1.166418e-01 1.219745e+00 2.519173e+00 -1.007534e+00 7.487956e-02 -3.006207e-01 1.400654e+00 -4.173045e-01 2.332372e+00 -9.855278e-02 -2.204193e-01 -7.833379e-01 -5.930612e-01 6.203890e-01 6.918916e-01 -2.457826e-01 -2.239103e-01 -2.541524e-01 2.017348e-01 -1.114162e+00 8.782095e-02 1.280646e+00 -3.371414e-01 -7.590542e-01 1.162541e+00 -9.399703e-01 -8.202273e-01 9.670926e-02 -1.117342e+00 -8.433232e-01 1.703636e+00 -6.360844e-01 2.142734e-01 4.644740e-01 9.238880e-01 -1.224998e+00 1.081625e+00 1.569911e-01 -7.144891e-01 1.559839e+00 2.750588e+00 -1.581774e+00 1.287410e-01 1.433503e+00 -6.963654e-01 -8.255084e-01 -1.669735e+00 2.189557e-01 -3.294103e-01 -5.388540e-01 1.110638e+00 -3.469740e-01 ''') ImportString(u'y(numeric)',''' 3.932371e-01 -1.582959e+00 5.639805e-01 -4.209155e-01 -2.840821e-01 6.408206e-01 -7.390411e-01 3.825284e-01 -1.379400e+00 -1.711983e-01 -9.741243e-01 -7.781656e-01 -7.696066e-01 3.288936e-01 2.273921e-01 -1.679596e+00 -5.323418e-01 -1.234213e-01 -1.502803e-01 1.660829e-01 1.045394e+00 -4.277209e-01 -1.567734e-01 -8.733210e-01 -2.309812e-01 1.385935e+00 -1.367174e+00 1.962207e+00 -3.543044e-01 -9.408144e-01 -2.384841e-01 -5.962890e-01 -3.933873e-01 -3.591330e-01 2.983786e-01 -2.154394e+00 -4.237328e-01 1.799076e+00 -4.075952e-01 -2.527959e-01 1.484929e-01 -1.122012e+00 2.567494e-01 2.257781e+00 1.408580e+00 -1.838744e-01 -8.479936e-01 -8.813885e-01 8.217646e-01 1.382208e+00 9.125305e-01 1.449450e+00 3.368751e-01 -4.707423e-01 2.858057e-02 1.438892e+00 1.421494e+00 -1.247074e+00 3.060058e-01 -4.609234e-01 -1.653281e+00 1.163529e+00 -4.526101e-01 6.436300e-01 1.693627e-01 -9.034139e-01 -1.201051e+00 1.730370e+00 1.067229e+00 -5.098118e-01 -2.143111e+00 1.103707e+00 8.941059e-01 1.011513e+00 -6.433531e-01 -1.093247e+00 2.044315e-01 -4.466687e-02 -1.986216e+00 6.005112e-01 -3.595900e-01 3.122588e-01 8.733257e-01 -3.756510e-01 6.344985e-01 1.084911e-01 4.731764e-01 6.680183e-01 -6.914217e-02 4.185931e-01 -6.284703e-01 8.357489e-01 3.101189e-01 -1.147504e+00 4.209453e-01 2.695551e-01 2.411478e+00 6.122571e-01 -9.317608e-01 3.639432e-03 -9.294555e-01 2.811682e-01 -4.166895e-01 4.573156e-02 -5.474721e-02 -1.747724e+00 3.551489e-01 5.224617e-01 1.446118e+00 3.276609e-01 5.820254e-01 -5.322398e-02 2.055228e-01 1.801155e-01 -2.481681e-01 5.918132e-01 6.371853e-01 -1.024074e+00 4.422508e-01 -1.765453e+00 7.069166e-01 -5.264746e-02 -3.758746e-01 5.259605e-01 3.545584e-01 2.188309e-01 9.912918e-01 -1.013570e+00 -1.477655e+00 -5.033191e-01 3.486164e-01 -1.232589e+00 1.994362e+00 2.092551e-01 -5.625635e-02 -1.823023e+00 2.400668e-01 1.961823e-01 -7.217717e-01 7.581531e-01 -9.114739e-01 -1.574672e+00 1.871565e+00 1.284189e+00 7.478859e-01 -1.089580e+00 5.053718e-02 -4.249463e-02 -1.214802e+00 1.859115e+00 5.022301e-01 -3.924076e-01 -2.126089e-01 4.158613e-01 -1.045746e+00 -1.399560e+00 1.619683e+00 -1.997212e-01 -6.238194e-03 -4.513871e-01 -7.496450e-01 -1.152565e+00 8.236517e-02 -1.775334e+00 2.416004e-02 -1.848644e-01 1.869392e-01 4.852247e-01 4.632710e-02 -3.783947e-01 -1.227083e-01 5.313990e-01 -8.304557e-01 -1.008873e+00 1.074884e+00 -1.284199e+00 -6.146878e-01 6.438271e-01 -1.531116e-01 -5.930658e-02 2.429786e-01 -2.118694e+00 7.008306e-01 -6.406849e-02 1.158005e+00 2.228708e+00 9.916924e-02 1.194782e+00 1.821870e+00 -1.885672e-01 2.959221e-01 -6.931851e-01 -1.021772e+00 6.732243e-01 -9.865323e-01 4.900014e-01 2.691894e-01 -1.122846e+00 -2.574113e-01 -3.236723e-01 -4.315339e-02 1.349121e+00 5.061446e-01 -3.886879e-01 1.587633e+00 1.415048e+00 1.676257e+00 -4.297688e-01 8.510519e-02 -6.488549e-01 -1.439997e+00 1.349764e+00 6.135247e-01 -1.329405e-01 -4.191632e-02 1.060990e-02 -4.130632e-01 2.028254e+00 -3.499138e-01 -4.000574e-01 3.352219e-01 -1.050624e+00 -2.947831e-01 1.676320e-01 7.101210e-01 2.009047e-01 -1.850736e+00 1.079852e+00 5.902247e-04 4.389123e-02 -8.182755e-01 1.204594e+00 -1.503707e+00 -2.058583e+00 -2.339826e+00 -8.221993e-01 -6.796711e-01 -6.445563e-01 3.371314e-01 -1.587939e+00 2.960443e-01 7.720967e-01 -9.666331e-01 8.993444e-01 6.678222e-01 1.221794e+00 1.604052e+00 -9.272673e-02 -1.875841e-01 1.100507e-01 -1.546954e+00 4.947272e-01 2.205192e+00 -1.405555e-01 3.731822e-01 -6.788546e-01 -4.340164e-01 1.060992e+00 -5.465468e-01 6.412677e-01 -2.706933e-02 1.828302e+00 -7.294630e-01 9.823054e-01 2.365861e-01 1.916252e-01 -1.524339e+00 -5.292441e-01 7.555220e-01 -1.122103e+00 9.718868e-01 -4.893493e-02 3.735030e-01 -1.977115e-01 2.882180e-01 -9.586215e-01 -4.503156e-01 -7.516168e-02 4.809204e-01 1.346558e-01 5.781377e-02 -1.940264e+00 -3.115461e-02 5.027543e-01 -4.005917e-01 1.039220e+00 -7.702607e-01 9.440791e-01 -6.094218e-01 1.918193e-01 1.056156e+00 -1.141662e-01 1.290747e-01 -2.509332e-01 1.967084e-01 -4.832118e-01 -4.388375e-01 -1.304407e+00 2.297287e-01 4.549696e-01 7.767487e-01 4.552344e-01 3.697836e-02 1.873039e-01 -1.302834e-01 -1.117705e+00 -9.652155e-01 -1.307381e-01 -9.352506e-01 8.056285e-01 1.214269e+00 -8.566561e-01 1.057879e-01 -4.398890e-01 5.096065e-01 -6.513120e-02 -1.037480e+00 1.068292e+00 -3.507502e-01 6.291006e-02 -3.001781e-01 5.281380e-01 -1.494001e-02 -5.740658e-01 2.984556e+00 6.218464e-01 -1.376889e+00 1.061706e+00 -1.286104e+00 -1.699789e+00 1.261380e+00 -4.677932e-01 -3.622073e-01 6.836566e-02 1.254518e+00 4.904745e-01 8.300104e-01 -2.285958e+00 1.857965e+00 -3.774363e-01 -9.399611e-01 7.714613e-01 3.430392e-01 -5.007758e-01 4.299628e-01 -7.219747e-01 -1.090905e+00 -3.200828e-01 4.410291e-01 1.618375e+00 1.420907e+00 1.125899e+00 2.033106e+00 -6.093470e-01 -1.399675e+00 -1.339142e+00 -1.876168e+00 5.365142e-02 1.485535e-01 -2.005376e+00 5.609491e-01 -4.897901e-01 -1.227592e+00 1.371558e+00 1.101981e-01 4.678906e-02 7.791065e-01 -9.781870e-02 -2.032345e+00 -3.758659e-01 -2.528873e+00 2.183229e+00 -5.313727e-01 -7.634927e-01 2.440659e-01 3.913867e-01 -2.015077e-01 -1.095292e+00 4.028327e-01 1.026561e+00 3.919004e-01 5.357946e-01 -1.588330e+00 1.169936e+00 2.179398e-01 1.364682e-01 -2.934466e-01 6.934988e-01 -1.648580e+00 1.425994e-01 5.505594e-01 2.414212e-01 -3.012310e-01 6.218957e-01 5.355172e-01 7.627946e-01 -2.527079e-01 -6.135520e-01 3.643912e-02 -1.064595e+00 -4.111659e-01 -1.775738e+00 -9.660140e-01 -4.868182e-01 -1.303937e+00 6.234303e-01 -1.242822e+00 1.590462e+00 -3.265493e-01 -2.476236e-01 9.256326e-01 -7.610898e-02 -2.000399e+00 -5.115644e-01 1.145176e+00 -7.545145e-01 -9.218792e-01 1.415490e+00 4.481356e-01 -9.620362e-01 -6.145877e-02 -1.011754e+00 2.154308e-01 -1.402027e-01 9.464294e-01 -1.036070e+00 4.722462e-01 -5.807692e-01 1.698257e-01 -7.244112e-01 -1.473803e+00 -3.641428e-01 -6.069139e-01 -9.956652e-01 -2.951348e-01 2.235166e+00 -8.322240e-01 6.849967e-01 8.756941e-01 4.042897e-01 -5.393525e-01 8.209435e-01 4.977953e-01 1.666981e+00 7.478246e-01 -2.945763e-02 -1.029361e-01 2.359407e-01 5.865684e-02 -1.781956e+00 2.178313e-01 -2.385683e+00 -3.531961e-01 2.058910e+00 7.891987e-01 1.042087e+00 -3.175582e-01 -1.377191e+00 -8.909847e-01 3.561236e-01 8.656861e-01 -1.954663e-01 -3.661821e-01 -1.375294e-01 -2.403522e+00 -1.986324e-01 5.819100e-01 -7.373902e-01 -4.499149e-01 8.719289e-01 1.298706e+00 -3.182287e+00 -5.742752e-01 -9.083678e-01 -2.217010e-01 -2.537349e-01 1.294201e+00 -4.326940e-01 4.552136e-01 6.538497e-01 1.345794e+00 3.525184e-01 5.390328e-01 -5.594728e-01 -4.996157e-01 6.317604e-01 -2.354051e-01 1.031342e+00 1.975591e+00 1.846348e-01 1.160285e+00 -5.172219e-01 -1.888788e-01 4.076943e-01 5.390197e-02 2.173531e-01 1.987465e+00 -4.666512e-01 4.483548e-01 -8.045693e-01 -8.486981e-01 6.491545e-01 4.648544e-01 -5.018078e-01 -5.364009e-01 5.144620e-01 3.737562e-01 1.890108e+00 2.030449e+00 -1.560410e-01 1.644853e+00 -3.340909e-02 6.389421e-01 2.426280e+00 -1.111343e+00 -4.457454e-01 1.329075e+00 7.346571e-01 -7.188043e-01 1.160373e+00 -1.817547e-01 -2.780691e-01 3.192265e-02 -5.137647e-01 1.395590e+00 1.945559e+00 2.001905e+00 1.147559e+00 1.140161e+00 -2.293118e+00 6.007260e-01 -4.833672e-01 8.251780e-01 1.535413e+00 -1.487085e+00 -1.454428e+00 -3.722988e-01 2.246815e+00 -2.054728e+00 -1.001841e+00 7.640610e-01 2.903577e-01 7.666735e-01 5.869354e-01 7.751743e-01 -1.005991e+00 -1.352267e+00 -1.058788e+00 -2.300255e-01 -2.637628e-01 -1.062565e+00 1.744088e+00 -1.407350e-01 -3.484135e-01 1.606894e+00 -1.092350e+00 6.723452e-01 2.141113e-01 1.434764e+00 1.351444e+00 -1.666509e+00 -7.641075e-01 -6.471014e-01 -1.332456e+00 3.682715e-01 -3.079205e-01 -1.595911e+00 6.692573e-01 -1.998867e-01 6.396873e-01 -1.598305e+00 -1.341974e-01 -2.101893e-01 7.197350e-02 -5.952265e-01 1.341946e+00 -5.678863e-01 -8.720932e-01 -8.484702e-01 -1.021891e+00 -5.272400e-02 1.480465e-01 1.414474e+00 1.548843e+00 -1.239546e+00 -1.587811e+00 -1.271240e+00 2.562468e-01 7.243228e-01 1.324251e+00 -7.628859e-01 1.739111e+00 -1.274184e+00 9.364039e-03 1.207429e-01 7.950231e-01 2.841331e-01 4.656906e-01 -1.901329e-01 -7.923896e-01 -9.420709e-01 -2.790712e-01 -5.807472e-01 2.080056e-01 -1.672686e+00 -2.257231e-01 1.172839e-01 -1.939220e-01 -2.661242e-01 -1.322225e+00 1.185829e+00 1.417243e+00 1.512327e+00 1.052523e-01 2.380308e+00 -1.208700e+00 4.562890e-01 -1.299606e+00 4.054185e-01 -9.863968e-01 3.568976e-01 3.572235e-02 1.699743e+00 1.090761e+00 -1.682176e-01 -7.771571e-01 -3.125552e-01 -1.170066e-01 -1.677533e-01 1.687242e+00 1.703563e+00 3.849954e-01 -9.055027e-01 -5.173879e-01 -2.038771e-01 -3.587446e-01 1.631174e+00 -1.646487e+00 1.854425e+00 -6.639214e-01 1.556347e+00 4.650627e-01 -2.463398e-01 -4.494595e-01 -7.579675e-01 1.424824e-01 2.812915e-01 5.264050e-01 -3.686507e-01 -7.841638e-01 -1.934529e-01 -1.919301e-01 8.572278e-01 5.314435e-02 3.691078e-01 5.639160e-01 1.023537e+00 9.984448e-01 1.450397e+00 -9.567811e-01 8.457202e-01 -3.622481e-01 9.941448e-01 -5.705442e-01 -1.228013e+00 -2.234976e-01 -1.640725e-01 1.034762e+00 1.250345e+00 -9.049104e-01 1.912883e+00 1.208996e+00 9.532647e-01 -9.318555e-01 -1.195537e-01 3.094413e-01 -7.219911e-01 1.396230e+00 1.842543e+00 -7.802086e-01 -9.981788e-02 -1.067894e+00 -1.689682e+00 1.086151e+00 5.733487e-01 -1.414983e-01 3.612763e-01 -8.163656e-01 4.004171e-01 1.125378e+00 5.088157e-01 -4.116386e-01 -1.060203e+00 4.838729e-01 1.142800e-01 5.314439e-01 7.727579e-02 3.061978e-01 9.567301e-01 9.829676e-01 1.545265e-01 9.348914e-01 -1.000633e+00 -1.490523e+00 -9.539526e-01 2.046905e+00 -1.110688e+00 1.147324e+00 1.485592e-01 -7.582957e-01 -1.302014e+00 1.090494e-01 3.244139e-01 -2.363166e-01 -5.279968e-01 -6.718101e-01 -1.594947e+00 4.522281e-01 -2.417138e-01 1.472195e+00 -2.208723e+00 3.637797e-01 -1.320206e+00 -4.895941e-01 4.612642e-02 2.495521e-01 2.224073e+00 4.501034e-01 4.568064e-01 5.603486e-01 6.759001e-01 -5.655860e-01 4.352051e-01 1.602611e-01 -4.877645e-01 1.428139e+00 8.369197e-01 -2.577961e-01 -1.160335e+00 1.785797e+00 -1.662134e+00 -1.363248e+00 6.123524e-01 1.036716e+00 -2.738276e-01 -7.910977e-01 -3.348705e-03 -1.848675e-01 -6.722455e-01 -7.117441e-01 1.389100e+00 -4.541758e-02 -5.854265e-01 -7.252556e-01 7.001459e-01 -9.019571e-01 -8.873456e-02 -1.075132e+00 2.243137e+00 1.664306e+00 -1.931127e+00 -1.948877e+00 6.779943e-02 2.910524e-01 1.525550e+00 1.508687e+00 -9.066085e-01 -2.172251e-01 4.967755e-01 -6.154131e-01 -2.575240e-01 -1.070978e-01 2.840445e-02 -4.196119e-01 1.393602e-01 7.170359e-01 -5.250783e-01 6.302569e-01 -9.172322e-01 -5.114203e-01 4.369124e-02 9.394352e-01 -4.506192e-01 1.715684e+00 -6.914746e-01 1.789039e-01 8.307744e-01 -1.154395e+00 -1.520288e-01 1.009718e+00 -4.290862e-01 2.135492e-02 -4.668846e-02 1.743555e+00 -3.547423e-01 9.578598e-01 -1.353028e+00 -3.628132e-01 6.170067e-01 7.323501e-01 -1.060122e-01 2.491226e-01 8.753891e-02 -5.549667e-01 -9.168476e-01 1.088550e+00 2.333576e-01 5.015771e-01 -1.262313e+00 -1.315090e+00 7.726966e-02 -6.951779e-01 -1.139529e+00 -6.260286e-01 1.504778e+00 -2.642568e-01 -2.840521e-01 -9.846766e-01 1.901104e-01 -1.204189e+00 -4.966436e-01 -1.755672e+00 1.525483e+00 -1.006680e+00 -1.890595e+00 -1.200779e+00 -2.297278e+00 -2.838081e-01 -1.216795e+00 -1.326741e+00 -4.108279e-01 2.677426e-01 7.843040e-01 1.309823e-01 -1.534391e+00 -2.555160e+00 9.155972e-01 2.113447e-01 1.268114e+00 1.470958e+00 -4.702841e-01 -1.021876e+00 -9.366943e-01 -3.637923e-01 -1.498159e+00 1.761923e+00 4.501180e-01 1.372347e+00 8.374543e-01 6.882874e-01 4.252370e-02 -6.344340e-01 1.249382e+00 -7.924815e-01 2.471674e-01 -1.864870e-01 1.780994e-01 -5.795990e-01 -5.587400e-01 1.027417e+00 5.264932e-01 8.906055e-01 6.590811e-01 -9.883269e-01 -8.630445e-02 -9.324242e-01 1.787138e-01 1.028099e+00 -4.893161e-01 -4.065239e-01 -3.433250e-01 1.640376e+00 4.680883e-02 -1.623418e+00 -1.142504e+00 4.060014e-01 2.168760e+00 8.404647e-01 6.314116e-01 1.182431e+00 -5.353272e-01 -4.148524e-01 3.281316e-03 -2.789835e-01 -4.922670e-01 -9.302447e-01 1.060128e-01 5.129096e-01 -8.855251e-01 -1.141156e+00 6.887528e-01 6.549765e-01 -1.094914e-01 -1.486057e+00 7.081615e-01 1.312470e-01 3.594081e-01 -5.418875e-01 -1.200705e+00 -5.408218e-01 1.745847e-01 1.720798e-01 -1.847429e+00 -1.105646e+00 4.536132e-01 7.377695e-01 -3.811371e-01 -3.116868e+00 8.641893e-01 6.064553e-01 3.369040e-01 -1.178487e+00 6.368583e-01 -3.826430e-01 1.209088e+00 -1.449879e-01 2.219701e+00 4.911733e-01 6.506198e-01 -9.109766e-02 -1.618034e+00 -2.797424e-01 -1.610702e-01 -3.987549e-01 -4.945086e-01 6.102682e-01 1.248452e+00 -3.095489e-01 5.172187e-01 2.678557e-01 -1.658133e+00 6.342826e-01 1.632721e+00 1.185928e+00 -1.360191e-01 1.014570e+00 -7.034644e-01 3.718831e-02 -8.366071e-01 5.599224e-01 -2.637624e-01 1.140206e+00 8.534978e-01 1.297153e+00 1.135451e+00 -7.120764e-01 6.384592e-01 1.156055e+00 -2.100665e-02 -6.538522e-01 -1.530185e+00 3.109468e+00 5.859244e-01 -7.391558e-01 -3.628279e-01 6.243281e-01 -1.570161e+00 -5.720894e-01 -2.474385e-01 1.057523e+00 -1.790446e-01 3.685237e-01 4.103773e-01 7.946287e-01 3.567124e-01 -8.499539e-01 4.020882e-01 1.032976e+00 1.844131e+00 1.600370e+00 -1.100882e-01 4.288627e-03 -1.056849e+00 1.031846e+00 -2.259335e-01 -3.825079e-01 -1.194869e+00 -9.977636e-01 -4.862139e-01 -3.032945e-01 2.000288e+00 -9.066691e-01 -1.463193e+00 1.011585e-01 -1.852685e+00 -5.380555e-01 2.755147e-01 1.166210e+00 -5.430469e-01 7.706536e-01 9.594393e-01 -1.257793e+00 1.903705e-03 -8.890706e-01 1.878056e+00 -1.172014e+00 1.025801e+00 5.429561e-01 7.763084e-01 -1.564020e-02 ''') ImportString(u'z(numeric)',''' 2.585124e-01 -3.621594e-01 -3.362464e-01 1.369053e+00 -8.254266e-01 7.422251e-01 -1.954610e+00 3.650947e-02 -6.858271e-01 -1.243814e+00 -1.664889e+00 -7.977561e-01 3.466253e-01 -1.535422e+00 8.397635e-01 1.567881e+00 5.976959e-01 1.134070e-01 -8.929715e-01 6.816980e-01 2.639347e+00 -1.043619e+00 -3.587930e-01 1.002658e-01 -1.285316e+00 4.122554e-01 -3.858058e-01 1.705446e-02 2.220461e-01 -9.783683e-01 -8.124249e-01 -3.022652e-02 -1.073504e+00 -7.765433e-01 4.251885e-01 2.491647e+00 3.460916e-01 4.443503e-01 -1.426232e+00 -4.354762e-01 2.756633e-01 2.339466e+00 4.653072e-02 5.550512e-01 1.368076e+00 1.339571e+00 6.397211e-01 1.011712e+00 -3.222135e-01 -7.788511e-01 -7.933866e-01 -1.116588e-01 1.926416e+00 1.019082e+00 9.574673e-01 6.907565e-01 3.436105e-01 1.655001e-01 1.402880e+00 -3.581220e-01 1.964680e-01 -1.868636e-02 -4.817226e-01 1.699465e-01 1.536638e+00 -4.059367e-01 -1.273578e-01 -5.847269e-01 1.263602e-01 -6.890247e-01 -4.001757e-01 3.225069e-02 -8.063705e-01 1.789159e-02 -1.209436e+00 1.391639e+00 1.422019e-01 5.445035e-01 2.458286e-01 -6.708228e-01 1.564246e+00 1.212119e+00 3.248720e-01 -9.459534e-01 -6.910714e-02 4.664664e-01 -9.649608e-01 -1.335687e+00 1.379591e-01 -1.461925e+00 8.825221e-01 -1.009761e+00 5.406367e-01 3.860539e-01 -2.803823e-01 -1.538217e-01 -1.032766e+00 1.245947e+00 -2.023908e-01 -1.160539e+00 7.809575e-01 1.643777e+00 -1.075351e+00 5.529961e-01 -1.303024e+00 -8.234931e-01 -8.101609e-01 1.347241e+00 6.252987e-01 1.929561e+00 -5.669542e-01 1.173373e+00 6.093799e-01 1.610744e-01 -6.418367e-01 -1.329674e+00 1.511295e+00 1.511393e+00 1.044301e+00 -1.474085e+00 -3.155298e-01 -2.735621e-01 9.933588e-02 -1.062701e+00 -5.916408e-01 2.079336e+00 3.529638e-01 -3.467181e-01 5.234193e-01 6.428589e-01 5.780336e-01 -8.347922e-01 -1.420017e+00 -1.201656e+00 -9.300869e-01 -3.502185e-01 -6.800455e-01 -6.088768e-01 -8.848059e-01 -3.343537e-01 3.870007e-01 -5.430839e-01 -4.681815e-02 -5.416057e-01 -2.432865e-01 -6.379053e-02 -6.511470e-01 1.881969e+00 9.758933e-01 -4.940425e-01 -1.394886e+00 1.711576e+00 -1.470189e+00 -1.794204e-01 4.092419e-01 -4.134734e-01 -9.754690e-01 -3.749084e-01 -9.029411e-02 -5.032741e-01 2.474297e-01 -1.955919e-01 1.775407e-01 -2.411008e-01 -7.822606e-01 5.769579e-01 -1.721572e-01 1.215446e+00 2.052208e-01 -3.074519e-01 -7.706240e-02 1.519038e+00 8.807025e-01 -1.585167e-01 4.243187e-01 9.780747e-01 -1.396611e+00 1.044805e+00 1.790709e-01 -2.075171e+00 -5.381977e-01 -1.086812e+00 1.629580e+00 -3.523767e-01 -4.580308e-01 8.912587e-01 -9.622508e-01 1.434092e-01 -8.496362e-02 -8.812133e-01 -1.072952e+00 -1.405015e+00 6.310400e-01 9.739107e-01 1.444567e+00 8.062342e-01 -6.600388e-01 4.193512e-01 2.479962e-01 2.324284e-01 -9.444726e-01 -3.762773e-01 8.569956e-02 -1.328576e+00 4.218906e-01 3.844989e-01 -7.983798e-01 -2.374757e-01 -2.315260e-01 -1.054345e+00 1.530421e+00 1.675454e-01 7.579300e-01 1.028575e+00 2.957112e-01 -5.314750e-01 5.874530e-01 9.691618e-01 3.019277e-01 1.441539e+00 -4.772380e-01 4.815975e-01 1.225708e-01 -1.457261e+00 -1.330886e+00 -1.865746e+00 -1.176625e+00 -7.795251e-01 1.383677e-01 -9.573186e-02 -1.030020e+00 -1.380945e+00 -1.773086e+00 1.471225e+00 9.092974e-01 1.354386e+00 1.826739e+00 -2.672536e-01 1.971561e+00 -7.626420e-01 -1.712796e-02 1.736490e-01 -2.643906e+00 -1.373408e+00 -2.824308e-01 1.921421e+00 6.186645e-01 5.050497e-01 -2.030730e-02 6.818366e-01 1.676915e+00 3.223001e-01 8.564125e-01 -2.677544e-01 -4.576856e-01 9.263894e-01 9.787021e-02 -5.616808e-01 -1.151988e-01 6.198074e-01 5.914537e-01 2.882203e-01 1.180824e-01 -3.067208e-01 1.548442e+00 1.400205e+00 7.673310e-02 1.021416e+00 -8.931725e-01 9.449799e-01 1.814839e+00 -9.494349e-01 1.382910e+00 -3.403693e-01 -3.777397e-01 8.465820e-01 1.394869e+00 9.869167e-01 -6.394826e-01 -3.763791e-01 1.886511e+00 -6.097335e-01 -5.258733e-01 -1.869829e-01 7.796295e-01 1.237616e+00 1.105017e+00 -1.095073e-01 7.177697e-01 1.024888e+00 5.233369e-01 7.650879e-01 -1.314864e+00 -1.366366e+00 1.991361e-01 -1.486437e+00 -1.053310e+00 1.085244e+00 -2.633038e-01 -4.477647e-01 -7.207680e-01 1.157931e+00 -6.487558e-01 -5.811588e-01 -1.611772e+00 1.864208e-01 -1.374934e-01 -2.934687e-01 -2.897571e-02 3.090903e-01 4.741060e-01 1.798565e+00 9.970022e-03 -8.822807e-02 -8.295105e-01 1.684503e-01 9.444340e-01 -9.358998e-01 1.214353e-01 1.173810e+00 1.222968e+00 3.600859e-01 -6.619315e-01 -1.844572e-01 -1.110370e+00 -9.249619e-02 3.395120e-01 -1.147691e+00 -1.382644e+00 -4.532500e-01 -7.164101e-01 2.936572e-01 -1.294749e-03 2.140201e+00 -7.425970e-01 -1.768755e+00 -7.296999e-01 1.973653e+00 5.989070e-01 -1.018535e+00 -5.031009e-01 6.320869e-01 -1.869015e+00 -1.080869e+00 -4.701249e-01 -5.031075e-01 -2.517951e-01 -5.469718e-01 1.166882e-01 -9.249221e-01 -9.505002e-01 -4.953162e-01 -1.443558e-01 1.474803e+00 -4.755980e-01 -3.884190e-01 3.399306e-02 2.448301e-01 4.042991e-01 -1.156946e+00 6.987705e-01 7.367199e-01 2.957738e-01 -1.624863e+00 1.723718e+00 4.559619e-02 -1.058394e+00 6.939875e-01 3.584054e-01 8.200664e-01 6.660282e-01 1.373863e+00 1.273398e+00 -2.869770e-01 3.575857e-01 -1.200924e+00 1.310366e+00 -7.113495e-01 -1.127242e+00 6.702748e-01 -4.982955e-01 3.287422e-01 -5.264012e-01 7.939113e-01 -5.292562e-01 2.041380e+00 5.659068e-02 -1.009590e+00 4.180506e-01 1.901190e-01 2.298075e-01 -2.071087e+00 -9.253677e-01 -1.023254e+00 8.850496e-01 -6.594116e-01 -1.129760e+00 1.630084e+00 2.265119e-01 -6.369647e-01 8.613065e-01 -9.250974e-01 -4.932135e-01 8.038200e-01 3.140276e-01 1.579702e+00 -9.382626e-02 2.210291e-01 -5.109907e-01 7.594310e-01 7.166853e-01 -9.086427e-01 -1.319832e+00 -3.314390e-02 -6.135461e-01 -6.958078e-01 1.225159e+00 1.220069e+00 3.929668e-01 -1.089660e+00 -1.893285e-01 8.993739e-01 -4.547133e-01 1.432441e-01 -3.996126e-01 9.758001e-02 -2.609300e+00 1.526834e+00 -1.920914e-01 -4.568548e-01 -4.063453e-01 -6.642582e-01 1.290938e+00 -2.399058e-01 -4.148933e-01 -1.363557e+00 2.169188e-01 7.234683e-01 -8.023429e-01 1.383312e-02 -1.221103e+00 -3.188968e-01 2.125510e-01 -3.841998e-01 -4.760617e-01 -7.748145e-02 1.655260e+00 1.188678e+00 -1.195451e+00 -1.064790e+00 1.038166e+00 -6.610258e-01 -1.075910e-01 -1.517229e+00 7.830957e-01 1.411836e+00 1.042148e+00 3.655885e-01 7.889021e-01 1.693395e-01 -1.478764e+00 6.815500e-01 8.662115e-01 7.480363e-01 -4.007134e-01 3.223507e-02 -2.590076e-01 -3.369393e-01 4.203520e-01 -1.323461e+00 -7.465016e-01 2.070762e+00 -2.704397e-01 4.962808e-01 9.651119e-01 -2.327540e+00 -1.132920e-01 -9.753759e-01 -1.153462e+00 1.682578e+00 8.298706e-01 -2.080299e-01 -6.516481e-01 -1.675483e+00 4.724520e-01 9.309400e-01 -5.310677e-01 1.524590e+00 3.026503e-01 1.779514e-01 -1.041767e+00 -5.157362e-01 -7.482514e-01 -1.246505e+00 -5.028855e-01 -1.167130e+00 -6.720969e-01 5.256936e-01 -1.749783e+00 4.641571e-01 2.796533e-01 3.491008e-01 -5.368692e-02 -1.904010e-02 2.784772e-01 -1.219532e+00 -7.443262e-01 -2.141013e+00 -1.386493e+00 -1.544324e-01 -4.144796e-01 5.905382e-01 -1.553227e+00 -1.819778e-01 8.391287e-01 8.238390e-01 -1.384637e+00 -8.241725e-01 -1.547758e-01 1.022386e+00 -1.456228e+00 7.896890e-01 4.679365e-02 -8.585604e-01 -8.985668e-01 1.288854e+00 -6.959927e-01 -1.207789e+00 3.727052e-01 9.640116e-01 -1.169159e+00 2.443580e-01 9.193023e-01 1.125692e+00 3.601926e-01 8.638569e-01 7.420253e-01 1.522107e-01 -1.500816e-01 1.743427e+00 6.550054e-01 -1.274786e+00 2.165509e-01 6.926703e-01 3.811580e-01 8.346831e-01 -1.613869e+00 1.839921e+00 -1.206818e+00 2.285600e+00 1.161488e+00 1.512968e+00 1.402303e+00 -1.187602e+00 -7.206889e-01 4.083404e-01 7.036619e-02 1.314753e+00 8.573338e-01 -6.304310e-01 2.086854e+00 8.031530e-01 -2.438823e-02 -7.401340e-01 1.839287e+00 -1.833501e+00 8.010072e-01 -2.234436e+00 -8.760587e-01 1.182264e-02 6.857118e-01 -2.254950e-01 -2.122797e-02 1.143813e+00 -1.011296e+00 7.342104e-01 1.485392e+00 4.196308e-01 2.617035e-01 6.879227e-01 3.571768e-01 8.949981e-01 -5.077636e-01 5.817390e-01 1.141418e+00 -1.749510e+00 9.259647e-02 1.540473e+00 -1.070901e+00 1.812850e-01 -2.367091e+00 4.852527e-01 7.433367e-01 1.796000e+00 1.276029e+00 -2.704099e+00 1.918611e+00 3.633172e-01 2.813344e-01 9.409001e-02 -1.064923e+00 -1.025242e+00 -1.837653e+00 1.147092e+00 -4.098245e-01 6.274501e-02 1.964419e+00 -1.862331e+00 3.062052e-01 -1.083968e+00 -4.086014e-01 1.173068e+00 9.812741e-01 1.117599e+00 1.391336e+00 6.478516e-01 -2.425759e-01 -1.255619e-01 6.042870e-01 9.574196e-01 5.195939e-01 7.481490e-01 1.348330e+00 -4.233709e-01 -1.235270e+00 1.851136e+00 6.848952e-01 -1.415427e+00 4.030583e-01 -1.177983e+00 -2.010303e-02 -3.295340e-01 -5.013605e-01 -9.144780e-01 -3.620505e-01 2.279397e-01 1.072892e+00 -2.507584e-01 -7.001320e-01 -1.211265e+00 -8.339065e-01 8.775561e-01 -1.343211e+00 1.558305e+00 8.250233e-01 6.875088e-01 -8.088337e-01 1.016112e+00 -4.781918e-01 -1.213810e+00 -2.796827e-01 6.451446e-01 -8.197062e-01 -2.258683e+00 1.410402e-02 -6.073303e-01 -2.765476e-01 1.030058e+00 4.812041e-01 8.669035e-01 -2.505432e+00 3.843354e-01 7.003544e-01 4.843252e-01 1.803420e-01 2.320328e+00 -8.576429e-01 1.142924e+00 -6.189955e-01 2.468795e-01 1.097721e+00 1.021246e+00 1.087350e+00 -1.520356e+00 1.097720e+00 1.770987e+00 -9.881774e-01 -9.335670e-01 -3.127292e-01 5.497244e-01 -7.370000e-02 1.359025e+00 -6.639344e-01 -8.270750e-01 -2.396394e-01 6.504370e-01 -1.264599e+00 -2.257524e+00 3.775632e-02 5.357455e-01 -1.061849e+00 5.132836e-01 -3.293724e-01 3.506395e-01 -1.650590e+00 -9.250902e-01 1.187444e+00 -7.510623e-02 -4.541657e-01 1.445879e+00 -7.943625e-01 -7.799880e-01 -3.725501e-01 1.269383e+00 5.367141e-01 -1.424729e-01 8.851281e-01 1.238739e+00 -6.906163e-01 -5.425865e-02 -6.204112e-02 1.001858e+00 1.026721e+00 2.855558e-01 -1.201186e-01 4.362160e-01 2.741036e-01 9.045000e-01 -1.919551e+00 3.188357e-01 -9.750236e-01 -6.335045e-01 -4.095299e-01 5.746152e-01 -1.802580e-01 -6.780395e-01 1.619407e+00 8.616760e-01 -2.340705e-01 -1.039612e+00 -5.593649e-02 -1.038075e+00 1.436443e+00 -1.507875e+00 -5.871937e-01 8.877236e-01 -5.983720e-01 -8.186280e-01 -6.615903e-01 -3.065735e-01 -1.234406e+00 4.821829e-01 4.490892e-03 2.798574e-01 -1.453091e+00 -4.484982e-01 -1.370361e+00 -8.616174e-01 1.707642e+00 2.298833e-01 2.092646e-02 1.103821e-01 -7.741913e-01 -2.707327e-01 5.368884e-01 2.573979e+00 -9.198998e-01 6.187621e-01 -5.881747e-01 1.005345e+00 1.216202e+00 3.654672e-01 1.357809e-01 -5.004497e-01 7.177418e-01 -9.009589e-01 1.547596e+00 -5.621156e-01 -5.117527e-01 3.359931e-01 -9.190018e-01 3.158099e-01 -7.925663e-01 -1.568325e+00 -7.654562e-01 -2.228072e-01 -1.096124e+00 -8.111155e-02 1.473470e-01 -1.118364e+00 -1.008722e+00 -8.745870e-01 9.824594e-01 3.661195e-01 -6.078988e-01 -2.979247e-01 -1.507154e+00 -2.689349e-01 1.800347e-01 3.073099e+00 -1.243290e+00 1.618598e+00 -2.396721e-01 7.735937e-02 8.741934e-01 4.314956e-01 1.052271e-01 -9.134700e-01 4.182897e-01 1.575084e-01 6.834283e-01 2.726073e-01 -7.213277e-01 1.071223e+00 -3.069117e-01 1.042755e+00 -8.093882e-01 -4.134371e-01 -3.045434e-01 -9.411626e-01 2.357752e-01 -3.350466e-01 1.357284e+00 -1.082559e+00 -1.723317e-01 -3.496065e-01 1.271304e+00 -1.649923e+00 1.895695e+00 1.617635e+00 -1.972169e-01 1.005186e+00 5.726425e-01 -7.458940e-01 -1.088330e-01 -4.283947e-01 -4.207978e-02 1.259761e+00 1.461163e+00 -1.955475e+00 -7.899510e-01 -4.200169e-01 -5.587312e-01 -3.625693e-01 -8.631515e-01 6.028814e-02 5.027076e-01 1.213409e+00 1.244280e+00 7.022279e-01 4.106748e-01 1.770016e-01 -1.028317e+00 3.560821e-01 3.468450e-01 6.491817e-01 1.087497e+00 -2.406570e+00 -8.925762e-01 -6.164151e-02 -7.587522e-01 1.399356e+00 -3.025961e-02 3.653700e-02 7.175774e-01 2.729497e+00 2.252336e-01 -1.941696e+00 1.039888e+00 1.364227e+00 1.516146e+00 -6.986744e-01 -1.575327e+00 2.633241e-02 -1.689698e-01 -3.715600e-01 -1.805040e-01 2.608946e-01 -1.449035e+00 -6.926451e-01 1.412442e+00 -7.684670e-01 1.172246e+00 -2.908938e-01 -1.100874e+00 3.949832e-02 -9.552162e-01 1.736296e+00 5.145332e-01 1.399988e-01 -6.348142e-01 -1.519341e-02 -7.362714e-01 -1.069279e-01 1.978534e-01 -2.039806e+00 -9.949323e-01 1.157322e+00 1.155086e+00 1.312763e+00 7.542824e-01 -9.311008e-01 -4.313420e-01 -5.936274e-01 -4.472546e-02 -1.335925e+00 -5.641775e-01 1.101634e-01 8.738716e-01 -3.862692e-01 -1.225933e+00 7.789448e-01 8.371751e-01 8.410697e-02 2.176983e+00 5.251057e-01 -1.994871e-01 8.621558e-01 -5.118690e-01 5.185876e-01 -1.827715e+00 -1.355636e-01 -3.863470e-02 1.080836e+00 3.523941e-01 4.856182e-01 7.901373e-01 5.162217e-01 -5.621288e-01 -1.700239e+00 -4.602851e-01 4.029624e-01 1.427634e-01 -1.616454e+00 1.098561e+00 -1.112002e+00 -1.152793e+00 -3.211671e-01 -1.071950e+00 2.161876e-01 -3.120942e-01 2.308785e-01 -1.205848e+00 3.109868e-01 -6.633172e-01 -1.672628e+00 -8.504309e-01 -4.203359e-01 -1.117056e+00 -1.022934e+00 3.481180e-01 -6.916692e-01 3.786110e-01 -4.705941e-02 2.080961e-01 -3.766698e-01 2.703859e-01 8.458251e-01 7.826743e-01 7.432621e-01 -1.492020e+00 2.404204e-01 -7.249685e-01 1.638078e-01 2.569972e-01 9.179744e-01 5.258623e-01 8.532875e-01 -6.928088e-01 1.044247e+00 -5.774945e-01 5.572421e-01 1.613017e-01 4.478320e-01 -1.541305e-01 -2.730369e-01 3.783091e-01 1.215180e+00 -3.641251e-01 2.483346e-01 4.686201e-01 -7.972798e-01 -1.435424e-01 -8.458201e-01 1.374821e+00 3.266820e-01 1.875084e-01 -4.958268e-01 5.853594e-02 -8.599376e-01 -4.748160e-01 -7.174775e-01 -1.604461e+00 -1.551600e-01 -2.210851e-01 -2.139527e+00 6.267240e-01 -4.132748e-01 -8.553546e-01 2.217151e-01 -2.759494e-01 8.979897e-01 1.693196e-01 -8.314581e-02 8.972428e-01 -2.183998e+00 8.282507e-01 -1.746775e+00 -1.070504e-01 1.140170e+00 ''') Set('colorTheme', u'default1') Set('StyleSheet/axis-function/autoRange', u'next-tick') Set('StyleSheet/axis3d/autoRange', u'+2%') Set('StyleSheet/axis3d/MinorGridLines/color', u'lightgreen') Add('page', name=u'page1', autoadd=False) To(u'page1') Add('scene3d', name=u'scene3d1', autoadd=False) To(u'scene3d1') Set('xRotation', -40.0) Set('yRotation', 62.0) Set('zRotation', -142.0) Set('Lighting2/x', -2.0) Add('graph3d', name=u'graph3d1', autoadd=False) To(u'graph3d1') Add('axis3d', name=u'x', autoadd=False) To(u'x') Set('label', u'x') Set('TickLabels/hide', False) Set('GridLines/color', u'green') Set('GridLines/hide', False) Set('MinorGridLines/hide', False) To('..') Add('axis3d', name=u'y', autoadd=False) To(u'y') Set('label', u'y') Set('direction', u'y') Set('TickLabels/hide', False) Set('MinorTicks/number', 40) Set('GridLines/color', u'green') Set('GridLines/hide', False) Set('MinorGridLines/hide', False) To('..') Add('axis3d', name=u'z', autoadd=False) To(u'z') Set('label', u'z') Set('direction', u'z') Set('GridLines/color', u'green') Set('GridLines/hide', False) Set('MinorGridLines/hide', False) To('..') Add('point3d', name=u'point3d1', autoadd=False) To(u'point3d1') Set('Color/points', u'sqrt(x**2+y**2+z**2)') Set('Color/min', 0.2) Set('Color/max', 4.0) Set('MarkerFill/colorMap', u'green') To('..') Add('function3d', name=u'function3d1', autoadd=False) To(u'function3d1') Set('mode', u'z=fn(x,y)') Set('fnz', u'(x**2+y**2)/20') Set('Surface/color', u'theme2') Set('Surface/transparency', 40.0) To('..') To('..') To('..') To('..') veusz-3.0.1/examples/nd-1.csv0000664000175000017500000000022313161413406015377 0ustar jssjss000000000000001.0,2.3,1.4,1.7 2.1,2.5,1.0,1.8 1.3,2.2,1.7,2.9 3.3,2.1,2.5,1.5 1.3,1.9,2.8,1.3 1.8,2.9,2.0,2.7 1.6,1.8,2.5,1.7 2.5,2.0,1.1,2.5 3.1,1.1,2.1,1.8 veusz-3.0.1/examples/markerspolygon.vsz0000664000175000017500000000375713161413406017762 0ustar jssjss00000000000000# Veusz saved document (version 1.6) # User: jss # Date: Tue, 19 Jan 2010 21:30:02 +0000 SetDataExpression(u'stary_offset', u'stary+0.1', linked=True) SetDataExpression(u'starx_offset', u'starx+0.1', linked=True) # star shape for polygon ImportString(u'stary(numeric)',''' -1.200000e+00 -3.708000e-01 -3.708000e-01 1.416000e-01 9.708000e-01 4.584000e-01 9.708000e-01 1.416000e-01 -3.708000e-01 -3.708000e-01 ''') ImportString(u'starx(numeric)',''' 0.000000e+00 -2.700000e-01 -1.141200e+00 -4.356000e-01 -7.056000e-01 0.000000e+00 7.056000e-01 4.356000e-01 1.141200e+00 2.700000e-01 ''') Set('StyleSheet/xy/markerSize', u'5pt') Set('StyleSheet/xy/MarkerFill/color', u'#aaaaff') Set('StyleSheet/xy/PlotLine/color', 'grey') Add('page', name='page1', autoadd=False) To('page1') Add('graph', name='graph1', autoadd=False) To('graph1') Add('axis', name='x', autoadd=False) To('x') Set('label', u'Outward ticks on this x axis') Set('min', -2.0) Set('max', 2.0) Set('outerticks', True) To('..') Add('axis', name='y', autoadd=False) To('y') Set('label', u'Outward ticks on this y axis') Set('min', -2.0) Set('max', 2.0) Set('outerticks', True) Set('direction', 'vertical') To('..') Add('polygon', name='polygon2', autoadd=False) To('polygon2') Set('xPos', u'starx') Set('yPos', u'stary') Set('positioning', u'axes') Set('Line/hide', True) Set('Fill/color', u'cyan') Set('Fill/transparency', 10) To('..') Add('polygon', name='polygon1', autoadd=False) To('polygon1') Set('xPos', u'starx_offset') Set('yPos', u'stary_offset') Set('positioning', u'axes') Set('Line/hide', True) Set('Fill/color', u'blue') To('..') colours = ['blue', 'cyan', 'lightgreen', 'purple', 'pink'] # add plot symbols for each type of plot symbol codes = veusz_markercodes for i, mcode in enumerate(codes): r = 1.6 + 0.2 * sin(16*pi/len(codes)*i) x = r*sin(2*pi/len(codes)*i) y = r*cos(2*pi/len(codes)*i) Add('xy', name=mcode, marker=mcode, xData=[x,x*1.2], yData=[y,y*1.2], MarkerFill__color=colours[i % len(colours)]) To('..') To('..') veusz-3.0.1/examples/labels.dat0000664000175000017500000000010213161413406016053 0ustar jssjss000000000000001 1 "A test" 2 4 test2 3 4 "A^{200}" 4 6 "\\alpha \\beta \\gamma" veusz-3.0.1/examples/axis_function_linked.vsz0000664000175000017500000000463713302252613021101 0ustar jssjss00000000000000# Veusz saved document (version 1.23.1) # Saved at 2015-06-21T15:38:09.523457 ImportFileCSV(u'axis_function_linked.csv', blanksaredata=True, delimiter='\t', linked=True, numericlocale='en_GB') Set('height', u'12cm') Set('StyleSheet/Font/font', u'DejaVu Serif') Set('StyleSheet/axis/TickLabels/size', u'12pt') Set('StyleSheet/axis/GridLines/style', u'solid') Set('StyleSheet/axis/MinorGridLines/style', u'solid') Set('StyleSheet/axis-function/GridLines/style', u'solid') Set('StyleSheet/axis-function/MinorGridLines/style', u'solid') Add('page', name='page1', autoadd=False) To('page1') Add('graph', name='graph1', autoadd=False) To('graph1') Set('leftMargin', '1.7cm') Set('rightMargin', u'0.2cm') Set('topMargin', u'1.2cm') Set('bottomMargin', u'1.2cm') Add('axis', name='x', autoadd=False) To('x') Set('label', u'Redshift') Set('min', 0.0) Set('autoRange', u'exact') Set('MajorTicks/number', 8) To('..') Add('axis', name='y', autoadd=False) To('y') Set('label', u'Maximum B magnitude') Set('log', False) Set('direction', 'vertical') Set('GridLines/hide', False) Set('MinorGridLines/hide', True) To('..') Add('axis-function', name=u'time-bb', autoadd=False) To(u'time-bb') Set('linked', True) Set('function', u'28 / (1+(1+t)**2)') Set('label', u'Time since big bang (Gyr)') Set('linkedaxis', u'x') Set('mint', 0.0) Set('log', True) Set('autoRange', 'exact') Set('otherPosition', 1.0) Set('TickLabels/size', u'12pt') Set('GridLines/hide', False) Set('MinorGridLines/hide', False) To('..') Add('label', name='label1', autoadd=False) To('label1') Set('label', u'Data taken from Kowalski et al. (2008)') Set('xPos', [0.95]) Set('yPos', [0.05]) Set('alignHorz', u'right') To('..') Add('xy', name=u'snmag', autoadd=False) To(u'snmag') Set('xData', u'z') Set('yData', u'Bmag') Set('Color/points', u'colour') Set('Color/min', -0.301) Set('Color/max', 0.302) Set('PlotLine/hide', True) Set('MarkerFill/colorMap', u'spectrum2-step') To('..') Add('colorbar', name='colorbar1', autoadd=False) To('colorbar1') Set('widgetName', u'snmag') Set('label', u'Supernova colour') Set('autoRange', u'exact') Set('Label/size', u'8pt') Set('TickLabels/size', u'8pt') Set('horzPosn', u'centre') Set('vertPosn', u'top') Set('width', u'10cm') To('..') Add('function', name='function1', autoadd=False) To('function1') Set('function', u'20+2*sin(x)') Set('xAxis', u'time-bb') Set('Line/color', u'magenta') Set('Line/width', u'1.5pt') Set('Line/style', u'dotted') To('..') To('..') To('..') veusz-3.0.1/examples/profile.vsz0000664000175000017500000002347613161413406016346 0ustar jssjss00000000000000# Veusz saved document (version 1.8) # User: jss # Date: Thu, 17 Jun 2010 19:05:07 +0000 ImportString(u'XMM_T(numeric),+,-',''' 5.628000e+00 4.444000e-02 -4.377000e-02 7.894300e+00 9.637000e-02 -9.432000e-02 8.572800e+00 1.505600e-01 -1.452800e-01 8.935100e+00 2.016700e-01 -1.921900e-01 8.812300e+00 2.500800e-01 -2.317200e-01 9.093300e+00 3.304300e-01 -3.117300e-01 8.778200e+00 3.833500e-01 -3.486300e-01 9.014000e+00 5.364900e-01 -4.666900e-01 8.116000e+00 6.585900e-01 -5.105600e-01 7.009700e+00 6.368200e-01 -4.733100e-01 7.267300e+00 7.396100e-01 -6.892600e-01 7.608800e+00 1.070300e+00 -7.781600e-01 6.167300e+00 9.326400e-01 -7.295500e-01 4.853700e+00 8.235500e-01 -5.775300e-01 5.289800e+00 1.210600e+00 -6.668300e-01 ''') ImportString(u'XMM_r(numeric),+,-',''' 4.079410e+01 4.079410e+01 -4.079410e+01 1.223820e+02 4.079420e+01 -4.079420e+01 2.039710e+02 4.079410e+01 -4.079410e+01 2.855590e+02 4.079410e+01 -4.079410e+01 3.671470e+02 4.079420e+01 -4.079420e+01 4.487360e+02 4.079410e+01 -4.079410e+01 5.303240e+02 4.079410e+01 -4.079410e+01 6.119120e+02 4.079420e+01 -4.079420e+01 6.935010e+02 4.079410e+01 -4.079410e+01 7.750890e+02 4.079410e+01 -4.079410e+01 8.566770e+02 4.079420e+01 -4.079420e+01 9.382660e+02 4.079410e+01 -4.079410e+01 1.019850e+03 4.079410e+01 -4.079410e+01 1.101440e+03 4.079420e+01 -4.079420e+01 1.183030e+03 4.079410e+01 -4.079410e+01 ''') ImportString(u'join_T(numeric),+,-',''' 2.848300e+00 2.038400e-01 -1.924600e-01 3.486000e+00 2.454100e-01 -2.203600e-01 4.130900e+00 2.842800e-01 -2.521200e-01 5.275100e+00 5.145600e-01 -4.345200e-01 5.181000e+00 5.605500e-01 -4.667900e-01 5.945600e+00 1.172900e+00 -8.520800e-01 5.172900e+00 7.715200e-01 -6.454600e-01 8.381900e+00 2.117600e+00 -1.484000e+00 9.151200e+00 2.933500e+00 -1.821600e+00 8.095900e+00 1.144400e+00 -9.110800e-01 6.760900e+00 7.825600e-01 -6.630700e-01 7.512700e+00 1.078900e+00 -8.886900e-01 8.290300e+00 1.508700e+00 -1.175700e+00 8.545500e+00 1.509800e+00 -1.083000e+00 1.002600e+01 2.347800e+00 -1.586200e+00 8.040800e+00 1.518800e+00 -1.075000e+00 8.924100e+00 1.402500e+00 -1.070000e+00 8.500300e+00 1.747800e+00 -1.243100e+00 1.193400e+01 2.563800e+00 -1.914100e+00 7.966200e+00 1.015300e+00 -8.143500e-01 1.115500e+01 1.851900e+00 -1.732100e+00 8.335400e+00 9.302900e-01 -8.166900e-01 1.087300e+01 2.340300e+00 -1.582200e+00 9.084100e+00 3.011000e+00 -1.691800e+00 5.619500e+00 2.557100e+00 -1.645100e+00 4.188800e+00 5.971800e-01 -5.137700e-01 ''') ImportString(u'join_Tcool(numeric)',''' 4.866533e+08 6.460709e+08 8.457038e+08 1.309789e+09 1.646014e+09 2.438356e+09 2.569063e+09 3.818538e+09 4.713869e+09 5.285466e+09 5.610075e+09 8.007782e+09 1.066876e+10 1.256449e+10 1.707578e+10 1.696616e+10 2.241039e+10 2.815604e+10 4.075918e+10 4.094509e+10 7.030750e+10 8.146804e+10 1.612034e+11 2.300251e+11 2.865877e+11 2.415128e+11 ''') ImportString(u'join_ne(numeric),+,-',''' 1.013051e-01 3.885018e-03 -3.855963e-03 8.459975e-02 2.732787e-03 -2.742314e-03 7.253090e-02 1.800797e-03 -1.736446e-03 5.611583e-02 1.295804e-03 -1.302452e-03 4.173251e-02 1.210027e-03 -1.232160e-03 3.347415e-02 1.139675e-03 -1.112957e-03 2.727904e-02 1.157072e-03 -1.168092e-03 2.577121e-02 8.503261e-04 -8.617833e-04 2.115231e-02 8.235903e-04 -8.098673e-04 1.808490e-02 3.904686e-04 -3.737143e-04 1.425932e-02 3.416427e-04 -3.560778e-04 1.145005e-02 2.591201e-04 -2.492951e-04 8.995093e-03 3.524567e-04 -2.439155e-04 7.476746e-03 1.812584e-04 -1.850032e-04 6.574554e-03 1.239572e-04 -1.802632e-04 5.492044e-03 1.489112e-04 -1.482904e-04 4.334784e-03 9.548168e-05 -9.709612e-05 3.633731e-03 7.439211e-05 -8.608619e-05 2.939645e-03 7.883378e-05 -7.137054e-05 2.261917e-03 4.206105e-05 -4.172175e-05 1.721952e-03 2.050454e-05 -3.562590e-05 1.240906e-03 1.624617e-05 -2.028029e-05 6.830295e-04 1.871479e-05 -1.764928e-05 4.394545e-04 1.473820e-05 -1.445650e-05 2.567464e-04 2.377862e-05 -2.075020e-05 2.581937e-04 9.016915e-06 -9.065599e-06 ''') ImportString(u'join_proj_T(numeric),+,-',''' 3.921200e+00 8.241000e-02 -7.806000e-02 4.463200e+00 1.025400e-01 -1.001800e-01 5.158200e+00 1.329200e-01 -1.324300e-01 5.936800e+00 1.843800e-01 -1.741000e-01 6.327500e+00 2.304300e-01 -2.105600e-01 6.879600e+00 2.833800e-01 -2.641800e-01 7.067100e+00 3.068400e-01 -2.814800e-01 8.386500e+00 4.438700e-01 -4.020400e-01 8.368100e+00 4.678800e-01 -4.193100e-01 7.987900e+00 3.082700e-01 -2.867900e-01 7.755000e+00 3.112900e-01 -2.885900e-01 8.491100e+00 3.669700e-01 -3.308300e-01 8.926800e+00 4.274000e-01 -3.907600e-01 9.433600e+00 4.752000e-01 -4.266000e-01 9.448900e+00 4.931200e-01 -4.461500e-01 8.913100e+00 4.832100e-01 -4.371800e-01 9.483900e+00 4.748600e-01 -4.375600e-01 9.535400e+00 5.495100e-01 -4.887600e-01 9.956600e+00 5.910000e-01 -5.276500e-01 8.940600e+00 4.640400e-01 -4.203600e-01 9.809900e+00 6.470500e-01 -5.633500e-01 9.168100e+00 5.598600e-01 -4.978400e-01 9.803500e+00 8.398200e-01 -7.446100e-01 7.418000e+00 7.233800e-01 -5.940900e-01 5.493000e+00 6.999100e-01 -5.576600e-01 4.669900e+00 8.224700e-01 -5.288900e-01 ''') ImportString(u'join_r(numeric),+,-',''' 1.166880e+01 5.838770e+00 -5.838770e+00 2.140010e+01 3.892510e+00 -3.892510e+00 2.918510e+01 3.892510e+00 -3.892510e+00 3.697010e+01 3.892510e+00 -3.892510e+00 4.475520e+01 3.892510e+00 -3.892510e+00 5.254020e+01 3.892510e+00 -3.892510e+00 6.032520e+01 3.892510e+00 -3.892510e+00 6.811020e+01 3.892510e+00 -3.892510e+00 7.589530e+01 3.892510e+00 -3.892510e+00 8.757280e+01 7.785030e+00 -7.785030e+00 1.031430e+02 7.785030e+00 -7.785030e+00 1.206590e+02 9.731290e+00 -9.731290e+00 1.401220e+02 9.731290e+00 -9.731290e+00 1.615310e+02 1.167750e+01 -1.167750e+01 1.848860e+02 1.167750e+01 -1.167750e+01 2.082410e+02 1.167750e+01 -1.167750e+01 2.374350e+02 1.751630e+01 -1.751630e+01 2.724670e+02 1.751630e+01 -1.751630e+01 3.113920e+02 2.140880e+01 -2.140880e+01 3.639410e+02 3.114010e+01 -3.114010e+01 4.320600e+02 3.697890e+01 -3.697890e+01 5.247020e+02 5.830020e+01 -5.830020e+01 6.607350e+02 7.773360e+01 -7.773360e+01 8.356360e+02 9.716700e+01 -9.716700e+01 1.029970e+03 9.716700e+01 -9.716700e+01 1.272890e+03 1.457500e+02 -1.457500e+02 ''') Set('width', '15.9cm') Set('height', '11.8cm') Set('StyleSheet/Font/font', u'Verdana') Set('StyleSheet/axis/Label/atEdge', True) Set('StyleSheet/graph/leftMargin', '0cm') Set('StyleSheet/graph/rightMargin', '0cm') Set('StyleSheet/graph/bottomMargin', '0cm') Add('page', name='page1', autoadd=False) To('page1') Add('grid', name='grid1', autoadd=False) To('grid1') Set('columns', 1) Set('leftMargin', '1.83cm') Set('rightMargin', '1.77cm') Set('topMargin', '0cm') Set('bottomMargin', '1.23cm') Add('axis', name='x', autoadd=False) To('x') Set('label', u'Radius (kpc)') Set('min', 8.0) Set('log', True) To('..') Add('graph', name='T', autoadd=False) To('T') Add('axis', name='y', autoadd=False) To('y') Set('label', u'Temperature (keV)') Set('log', True) Set('direction', 'vertical') To('..') Add('xy', name='deproj', autoadd=False) To('deproj') Set('xData', u'join_r') Set('yData', u'join_T') Set('marker', u'square') Set('markerSize', u'1pt') Set('key', u'Chandra deprojected') Set('errorStyle', u'diamond') Set('PlotLine/hide', True) Set('MarkerLine/hide', True) Set('MarkerFill/color', u'magenta') Set('ErrorBarLine/color', u'grey') To('..') Add('xy', name='proj', autoadd=False) To('proj') Set('xData', u'join_r') Set('yData', u'join_proj_T') Set('marker', u'diamond') Set('markerSize', u'2.5pt') Set('key', u'Chandra projected') Set('errorStyle', u'barends') Set('PlotLine/hide', True) Set('MarkerFill/color', u'cyan') Set('ErrorBarLine/color', u'blue') Set('ErrorBarLine/width', '0.5pt') To('..') Add('xy', name='xmm', autoadd=False) To('xmm') Set('xData', u'XMM_r') Set('yData', u'XMM_T') Set('marker', u'star') Set('markerSize', u'3pt') Set('key', u'XMM projected') Set('errorStyle', u'curve') Set('PlotLine/hide', True) Set('MarkerLine/hide', True) Set('MarkerFill/color', u'#ff0000') Set('ErrorBarLine/color', u'red') Set('ErrorBarLine/width', '0.5pt') To('..') Add('key', name='key1', autoadd=False) To('key1') Set('Text/size', u'12pt') Set('Border/hide', True) Set('horzPosn', 'manual') Set('vertPosn', 'manual') Set('keyLength', u'0.5cm') Set('horzManual', 0.44233856029204632) Set('vertManual', 0.028242379921377202) To('..') To('..') Add('graph', name='ne', autoadd=False) To('ne') Add('axis', name='y', autoadd=False) To('y') Set('label', u'Electron density (cm^{-3})') Set('max', 0.20000000000000001) Set('log', True) Set('direction', 'vertical') To('..') Add('xy', name='deproj', autoadd=False) To('deproj') Set('xData', u'join_r') Set('yData', u'join_ne') Set('marker', u'square') Set('markerSize', u'1pt') Set('key', u'Chandra deproj. density') Set('errorStyle', u'diamond') Set('PlotLine/hide', True) Set('MarkerLine/hide', True) Set('MarkerFill/color', u'magenta') Set('ErrorBarLine/color', u'grey') Set('ErrorBarLine/width', '0.5pt') To('..') Add('xy', name='tcool', autoadd=False) To('tcool') Set('xData', u'join_r') Set('yData', u'join_Tcool') Set('marker', u'linecross') Set('markerSize', u'3pt') Set('key', u'Chandra cooling time') Set('yAxis', u'y2') Set('errorStyle', u'bar') Set('PlotLine/hide', True) Set('MarkerLine/hide', False) Set('MarkerFill/color', u'magenta') Set('ErrorBarLine/color', u'grey') Set('ErrorBarLine/width', '0.5pt') To('..') Add('axis', name='y2', autoadd=False) To('y2') Set('label', u'Cooling time (yr)') Set('min', 100000000.0) Set('max', 1000000000000.0) Set('log', True) Set('reflect', False) Set('direction', u'vertical') Set('lowerPosition', 0.0) Set('upperPosition', 1.0) Set('otherPosition', 1.0) To('..') Add('label', name='label1', autoadd=False) To('label1') Set('label', u'Cooling time') Set('xPos', [0.22084923837100287]) Set('yPos', [0.29071858444521959]) Set('angle', 340.0) Set('Text/size', u'14pt') To('..') Add('label', name='label2', autoadd=False) To('label2') Set('label', u'Density') Set('xPos', [0.43108631326520735]) Set('yPos', [0.65525239745398034]) Set('angle', 22.0) Set('Text/size', u'14pt') Set('Text/color', u'magenta') To('..') To('..') To('..') To('..') veusz-3.0.1/examples/histogramming.vsz0000664000175000017500000001027413161413406017546 0ustar jssjss00000000000000# Veusz saved document (version 1.17.1) ImportString('a(numeric)',''' 4.064151e-01 -1.955427e+00 5.367960e-01 -1.201281e+00 -8.823834e-01 3.370551e-01 -1.877312e-02 1.088443e+00 -1.707573e+00 -2.634517e-01 -4.137204e-01 -1.097392e+00 -5.821462e-01 1.091239e-01 1.074642e+00 2.023820e-01 -2.822896e+00 -9.460682e-01 -2.210939e+00 -7.071375e-01 -6.229418e-02 4.740030e-01 1.142621e+00 -1.115908e+00 -1.119911e-01 1.821279e+00 1.493768e+00 -3.421813e-01 1.325412e+00 -1.768811e-01 -1.314423e+00 -3.753827e-02 1.842780e-02 9.716551e-01 1.956101e+00 1.649411e+00 -7.339221e-01 3.996795e-01 6.276289e-02 3.893068e-02 9.249883e-01 -4.728369e-02 5.743185e-01 1.701567e-01 -5.380860e-01 -4.650062e-01 -3.322537e-01 -1.565993e+00 -3.993179e-01 6.449638e-01 ''') CreateHistogram(u'a+0', u'a_p', u'a_h', binparams=(11, -2.5, 2.5, False), binmanual=None, method=u'density', cumulative='none', errors=False) CreateHistogram(u'a', u'a_p_cuml', u'a_h_cuml', binparams=(11, -2.5, 2.5, False), binmanual=None, method=u'fractions', cumulative='smalltolarge', errors=False) ImportString('b(numeric)',''' -5.119920e-01 2.569989e-01 8.461096e-01 -7.280449e-01 3.753062e-01 -1.363823e+00 -3.881765e-01 5.583240e-01 4.682697e-01 -1.904293e-01 -1.090520e+00 -1.606417e+00 -3.863972e-01 -3.592417e-01 -4.671219e-01 -1.263556e+00 -9.915580e-02 -8.630928e-01 2.531121e-01 8.082021e-01 1.220910e+00 1.136309e+00 -6.283861e-01 -9.666840e-01 1.243800e-02 5.026563e-01 -4.936574e-01 -2.520510e-01 -6.160691e-01 9.710006e-02 -1.182792e+00 8.105359e-01 -1.931395e+00 1.213877e-01 -8.875079e-01 -3.964456e-01 -5.933237e-01 -3.436124e-01 1.381866e+00 -1.315651e+00 1.829677e+00 6.486507e-01 3.684826e-01 1.283694e+00 5.930318e-01 -5.533747e-01 6.386597e-01 1.467606e+00 8.377984e-01 1.625319e-01 ''') CreateHistogram(u'b', u'b_p', u'b_h', binparams=(11, -2.5, 2.5, False), binmanual=None, method=u'density', cumulative='none', errors=False) CreateHistogram(u'b', u'b_p_cuml', u'b_h_cuml', binparams=(11, -2.5, 2.5, False), binmanual=None, method=u'fractions', cumulative='smalltolarge', errors=False) Set('StyleSheet/Font/font', u'Bitstream Vera Sans') Set('StyleSheet/xy/ErrorBarLine/hide', True) Add('page', name='page1', autoadd=False) To('page1') Add('graph', name='graph1', autoadd=False) To('graph1') Set('rightMargin', u'1.7cm') Set('topMargin', u'1.7cm') Add('axis', name='x', autoadd=False) To('x') Set('label', u'X value') Set('min', -2.5) Set('max', 2.5) To('..') Add('axis', name='y', autoadd=False) To('y') Set('label', u'Y value') Set('min', -2.5) Set('max', 2.5) Set('direction', 'vertical') To('..') Add('xy', name=u'b_hist', autoadd=False) To(u'b_hist') Set('xData', u'b_h') Set('yData', u'b_p') Set('marker', u'none') Set('xAxis', u'x2') Set('PlotLine/steps', u'vcentre') Set('PlotLine/color', u'red') Set('PlotLine/width', u'2pt') Set('MarkerFill/color', u'red') To('..') Add('axis', name=u'x2', autoadd=False) To(u'x2') Set('label', u'Y histogram') Set('max', 1.02) Set('upperPosition', 0.5) Set('otherPosition', 1.0) To('..') Add('xy', name=u'b_hist_cuml', autoadd=False) To(u'b_hist_cuml') Set('xData', u'b_h_cuml') Set('yData', u'b_p_cuml') Set('marker', u'none') Set('hide', False) Set('xAxis', u'x2') Set('PlotLine/steps', u'vcentre') Set('PlotLine/color', u'#00ff00') Set('PlotLine/width', u'2pt') To('..') Add('xy', name=u'a_hist', autoadd=False) To(u'a_hist') Set('xData', u'a_p') Set('yData', u'a_h') Set('marker', u'none') Set('yAxis', u'y2') Set('PlotLine/steps', u'centre') Set('PlotLine/color', u'blue') Set('PlotLine/width', u'2pt') Set('MarkerFill/color', u'red') To('..') Add('axis', name=u'y2', autoadd=False) To(u'y2') Set('label', u'X histogram') Set('max', 1.02) Set('direction', 'vertical') Set('upperPosition', 0.5) Set('otherPosition', 1.0) To('..') Add('xy', name=u'a_hist_cuml', autoadd=False) To(u'a_hist_cuml') Set('xData', u'a_p_cuml') Set('yData', u'a_h_cuml') Set('marker', u'none') Set('hide', False) Set('yAxis', u'y2') Set('PlotLine/steps', u'centre') Set('PlotLine/color', u'magenta') Set('PlotLine/width', u'2pt') To('..') Add('xy', name=u'datapoints', autoadd=False) To(u'datapoints') Set('xData', u'a') Set('yData', u'b') Set('marker', u'diamond') Set('markerSize', u'6pt') Set('PlotLine/hide', True) Set('MarkerFill/color', u'white') To('..') To('..') To('..') veusz-3.0.1/examples/isolatedaxes.vsz0000664000175000017500000000216013161413406017356 0ustar jssjss00000000000000# Veusz saved document (version 1.3) # User: jss # Date: Wed, 27 May 2009 19:18:06 +0000 Set('width', '9.6cm') Set('height', '8.25cm') Add('page', name='page1', autoadd=False) To('page1') Add('graph', name='graph1', autoadd=False) To('graph1') Set('leftMargin', '1.75cm') Set('rightMargin', '0.116cm') Set('topMargin', '0.163cm') Set('bottomMargin', '0.137cm') Set('Border/hide', True) Add('axis', name='x', autoadd=False) To('x') Set('label', u'\\emph{x}-axis\\\\(erg)') Set('log', True) Set('autoMirror', False) Set('lowerPosition', 0.17369650762420075) Set('upperPosition', 0.94726788504290027) Set('otherPosition', 0.47642172482345269) To('..') Add('axis', name='y', autoadd=False) To('y') Set('label', u'\\emph{y}-axis\\\\(cm^{-3}})') Set('autoMirror', False) Set('direction', 'vertical') Set('lowerPosition', 0.043559240567171316) Set('upperPosition', 0.93040302493489757) Set('otherPosition', 0.089153959665518978) Set('Label/rotate', True) To('..') Add('function', name='function1', autoadd=False) To('function1') Set('Line/color', u'red') Set('Line/width', u'1pt') Set('Line/style', u'dotted-fine') To('..') To('..') To('..') veusz-3.0.1/examples/sin_byhand.vsz0000664000175000017500000000105713161413406017013 0ustar jssjss00000000000000# this is sin.vsz # but this is what we would use if we were programming it by hand x=arange(16)/15. * 2* pi y=sin(x) SetData('x', x) SetData('y', y) To( Add('page') ) To( Add('graph') ) Set('x/label', '\\italic{x}') Set('y/label', 'sin \\italic{x}') # we could assume that the name of the xy is xy1, but # this code means other xys could be inserted before this one # but it would still work xy = Add('xy') Set('%s/MarkerFill/color' % xy, 'cyan') fn = Add('function') Set('%s/function' % fn, 'sin(x)') Set('%s/Line/color' % fn, 'red') To('..') To('..') veusz-3.0.1/examples/example_csv.csv0000664000175000017500000000034413161413406017152 0ustar jssjss00000000000000"Xval","Yval","+-", 1,0.1,0.03, 1.2,0.5,0.12, 1.5,0.6,0.05, 2.2,1.5,0.14, 3,2.2,0.2, 4,3.2,0.21, 4.3,4.01,0.22, 4.99,4,0.4, 5.2,4.6,0.32, 7,4.4,1, 7.1,6,0.22, ,,, "Xval2","Yval2","+","-" 1,1,0.1,-0.1 2,2,0.1,-0.12 3,3,0.12,-0.1 veusz-3.0.1/examples/broken_axis.vsz0000664000175000017500000000251213161413406017176 0ustar jssjss00000000000000# Veusz saved document (version 1.16) # Saved at 2013-02-16T17:16:25.362109 ImportString(u'vals(numeric)',''' 3.000000e+00 1.000000e+00 5.000000e+00 8.000000e+00 1.000000e+00 3.000000e+00 1.010000e+02 1.030000e+02 1.100000e+02 9.500000e+01 8.000000e+01 5.000000e+00 2.000000e+00 4.000000e+00 1.000000e+00 ''') SetDataRange(u'x', 15, (0.0, 14.0), linked=True) Set('width', u'14cm') Set('height', u'10cm') Set('StyleSheet/Font/font', u'Arial') Add('page', name='page1', autoadd=False) To('page1') Add('graph', name='graph1', autoadd=False) To('graph1') Set('Border/hide', True) Add('axis', name='x', autoadd=False) To('x') Set('label', u'Axis without breaks') Set('autoRange', u'exact') Set('Label/size', u'16pt') Set('Label/position', u'at-minimum') Set('MajorTicks/number', 7) To('..') Add('axis-broken', name=u'y', autoadd=False) To(u'y') Set('label', u'An axis with a break in it') Set('breakPoints', [25.0, 75.0]) Set('direction', u'vertical') Set('Label/size', u'16pt') Set('Label/position', u'at-minimum') To('..') Add('xy', name='xy1', autoadd=False) To('xy1') Set('xData', u'x') Set('yData', u'vals') Set('marker', u'squashbox') Set('markerSize', u'5pt') Set('PlotLine/width', u'1pt') Set('MarkerFill/color', u'red') Set('FillBelow/color', u'red') Set('FillBelow/style', u'diagonal cross') Set('FillBelow/hide', False) To('..') To('..') To('..') veusz-3.0.1/examples/sin.vsz0000664000175000017500000000211713161413406015464 0ustar jssjss00000000000000# Veusz saved document (version 0.4) # User: jss # Date: Sun, 13 Mar 2005 18:05:32 +0000 ImportString('y',''' 0.000000e+00 4.067366e-01 7.431448e-01 9.510565e-01 9.945219e-01 8.660254e-01 5.877853e-01 2.079117e-01 -2.079117e-01 -5.877853e-01 -8.660254e-01 -9.945219e-01 -9.510565e-01 -7.431448e-01 -4.067366e-01 -2.449213e-16 ''') ImportString('x',''' 0.000000e+00 4.188790e-01 8.377580e-01 1.256637e+00 1.675516e+00 2.094395e+00 2.513274e+00 2.932153e+00 3.351032e+00 3.769911e+00 4.188790e+00 4.607669e+00 5.026548e+00 5.445427e+00 5.864306e+00 6.283185e+00 ''') Add('page', name='page1', autoadd=False) To('page1') Add('graph', name='graph1', autoadd=False) To('graph1') Add('axis', name='x', autoadd=False) To('x') Set('label', '\\italic{x}') To('..') Add('axis', name='y', autoadd=False) To('y') Set('label', 'sin \\italic{x}') Set('direction', 'vertical') To('..') Add('xy', name='xy1', autoadd=False) To('xy1') Set('MarkerFill/color', 'cyan') To('..') Add('function', name='function1', autoadd=False) To('function1') Set('function', 'sin(x)') Set('Line/color', 'red') To('..') To('..') To('..') veusz-3.0.1/examples/example_import_1.dat0000664000175000017500000000126413161413406020070 0ustar jssjss00000000000000# Example data file 1 for importing # this is read in with blocks enabled 1 0.1 0.04 1.5 1.2 0.21 2 2.1 0.11 2.4 2.0 0.11 6.3 3.5 0.21 6.9 4.0 0.34 8.1 5.1 0.11 0 0.598891867123 0.571271171527 1 1.27652599928 0.293211984296 2 1.80910012902 0.59316037268 3 2.49652366924 0.212350438791 4 2.46688314965 0.418003360397 5 2.84211196873 0.303143691697 6 3.03125458979 0.905546142022 7 3.34096409413 0.276983451243 8 3.38876182999 0.644970504048 9 3.3431954804 0.0314161044416 10 3.72957202336 0.586131190135 11 3.32604926178 0.611351645308 12 4.31563271648 0.703522901568 13 4.59995446895 0.844015647958 14 4.15530039017 0.724286803521 veusz-3.0.1/examples/bar_labels.dat0000664000175000017500000000020513161413406016703 0ustar jssjss00000000000000descriptor name spring,+,- summer,+- "Red" 2 0.2 -0.2 4 0.3 "Green" 3 0.1 -0.1 2.5 0.1 "Blue" 5 0.3 -0.2 3 0.2 veusz-3.0.1/examples/2d_irregular.vsz0000664000175000017500000000156013161413406017255 0ustar jssjss00000000000000# Veusz saved document (version 1.19.1) # Saved at 2013-12-29T17:02:38.658934 ImportFile2D(u'2d_irregular.csv', [u'irreg'], gridatedge=True, mode='csv', linked=True) Add('page', name='page1', autoadd=False) To('page1') Add('graph', name='graph1', autoadd=False) To('graph1') Add('axis', name='x', autoadd=False) To('x') Set('label', u'X coordinate') Set('min', 0.8) Set('max', 1100.0) Set('log', True) Set('autoRange', u'exact') To('..') Add('axis', name='y', autoadd=False) To('y') Set('label', u'Y coordinate') Set('min', 0.8) Set('max', 1100.0) Set('log', True) Set('autoRange', u'exact') Set('direction', 'vertical') To('..') Add('contour', name='contour1', autoadd=False) To('contour1') Set('data', u'irreg') Set('numLevels', 7) To('..') Add('image', name='image1', autoadd=False) To('image1') Set('data', u'irreg') Set('colorMap', u'complement') To('..') To('..') To('..') veusz-3.0.1/examples/coloredpoints.vsz0000664000175000017500000001247213161413406017564 0ustar jssjss00000000000000# Veusz saved document (version 1.19) # Saved at 2013-11-23T13:54:44.879031 ImportString('c(numeric)',''' 1.459606e+02 1.702896e+02 7.934817e+01 1.227687e+01 5.599601e+03 1.539531e+01 1.828166e+04 5.054989e+03 3.191051e+02 7.949710e+00 2.216535e+03 5.143240e+03 3.148290e+01 4.965444e+01 7.777005e+02 3.185130e+02 1.588387e+04 2.813152e+02 1.339360e+02 4.215790e+03 6.843036e+03 8.415999e+01 1.362926e+02 6.243027e+03 6.179793e+02 1.897636e+03 2.796312e+02 1.026779e+01 4.765936e+03 1.570359e+04 1.467323e+02 1.164478e+03 1.902282e+02 1.520207e+04 2.973087e+00 2.337408e+00 4.134565e+02 7.166886e+03 4.217587e+03 9.953222e+02 8.267064e+01 5.035324e+00 9.556994e+00 8.546561e+01 1.500701e+01 1.351431e+01 4.917555e+03 2.496430e+00 2.103088e+01 1.077053e+03 1.563147e+02 3.163246e+03 1.626294e+04 4.627788e+03 8.483016e+03 5.805220e+03 1.600690e+01 2.339657e+02 1.526946e+02 4.282832e+02 2.799937e+02 3.737549e+02 2.842098e+01 1.275867e+03 4.526153e+04 2.403380e+04 4.756101e+01 2.977493e+04 5.519853e+01 2.365612e+02 4.460124e+03 2.131700e+03 6.597176e+03 8.787685e+02 3.755261e+04 5.750869e+02 7.043527e+02 8.845572e+03 1.043050e+02 5.741879e+00 2.603017e+02 1.178276e+01 9.850848e+00 9.117799e+02 1.102115e+02 9.833058e+01 2.374284e+02 1.129614e+04 2.973567e+03 2.594940e+02 3.478364e+01 2.132164e+00 7.268064e+03 1.879249e+01 1.321168e+02 1.057914e+03 7.070878e+01 4.039144e+00 7.826971e+01 1.123204e+04 ''') SetDataExpression(u'sizef', u'abs(x-0.5)+0.5', linked=True) ImportString('x(numeric)',''' 3.259640e-01 2.936538e-01 3.162201e-01 2.638131e-01 8.803033e-01 2.577980e-01 9.479319e-01 5.931048e-01 5.222732e-01 1.018496e-01 7.321032e-01 7.753362e-01 2.971560e-01 3.875175e-01 4.714681e-01 5.461489e-01 8.912572e-01 3.138063e-01 3.838737e-01 7.173045e-01 9.487288e-01 3.029883e-01 4.863407e-01 8.589109e-01 5.710536e-01 7.576283e-01 5.049896e-01 2.308246e-01 8.570804e-01 8.612760e-01 4.759630e-01 5.248471e-01 4.719874e-01 7.990987e-01 9.636075e-02 6.375893e-02 6.057062e-01 9.102558e-01 7.864667e-01 6.260576e-01 4.087506e-01 1.741066e-01 1.165042e-01 3.446296e-01 2.170302e-01 9.573020e-02 8.219256e-01 9.182756e-02 2.018576e-01 6.840850e-01 3.888053e-01 8.064366e-01 9.381433e-01 8.748492e-01 8.716737e-01 8.351529e-01 1.047454e-01 4.707636e-01 4.919583e-01 5.672392e-01 4.050533e-01 4.783146e-01 3.282565e-01 6.755866e-01 9.435113e-01 9.404685e-01 2.656279e-01 9.456492e-01 3.985884e-01 5.263482e-01 6.925591e-01 6.756365e-01 9.412488e-01 6.768923e-01 8.147229e-01 5.731504e-01 5.591458e-01 8.806983e-01 4.030426e-01 1.638092e-01 4.160340e-01 2.343386e-01 1.942402e-01 6.532868e-01 3.740195e-01 3.338149e-01 3.336983e-01 8.617688e-01 7.426878e-01 4.504087e-01 2.690837e-01 5.143151e-02 9.142272e-01 1.996254e-01 5.223122e-01 7.060125e-01 3.945721e-01 1.191278e-01 3.037703e-01 8.536895e-01 ''') ImportString('y(numeric)',''' 1.720759e+00 -2.113146e+00 -1.269313e+00 6.767068e-02 -4.538878e-01 -3.123929e-01 -9.405761e-01 -2.662602e+00 -8.296818e-01 -9.859058e-01 -8.345237e-01 1.219784e+00 6.189014e-01 2.917761e-01 -2.009880e+00 -6.370632e-01 1.271855e+00 -2.387936e+00 -1.182805e+00 1.511322e+00 8.066724e-02 -1.426305e+00 -3.782192e-01 -7.195032e-01 -1.013519e+00 -4.953992e-01 -8.532545e-01 -1.763571e-01 4.996532e-01 -1.501790e+00 -5.253474e-01 -1.933486e+00 7.826508e-01 1.971016e+00 1.755292e-01 2.273975e-01 3.872100e-01 -4.286146e-01 9.583945e-01 -9.874666e-01 5.646978e-01 1.120199e-02 1.028609e+00 -1.106546e+00 6.163468e-01 -1.495746e+00 -8.080937e-01 6.001841e-02 -1.030854e+00 -5.917945e-01 -1.277557e+00 -5.487731e-01 -9.172515e-01 3.319534e-01 -8.837110e-01 8.464141e-01 1.570651e+00 -9.721957e-01 -4.319809e-01 7.255485e-01 1.653870e+00 -1.318657e+00 2.812261e-01 -8.069181e-01 -1.763368e+00 -1.237897e+00 -1.229479e+00 -1.382508e+00 2.951479e-01 -5.371013e-01 -1.758221e+00 -1.252360e+00 -1.087257e-01 -4.726106e-01 -2.631497e+00 -9.342638e-01 1.222414e+00 -8.478654e-01 8.122695e-01 2.076344e-01 -1.502682e+00 2.677853e-01 4.330256e-01 -6.934856e-01 1.092298e+00 1.314858e+00 -2.081479e+00 -1.211710e+00 1.005053e+00 -1.224985e+00 -9.300805e-01 2.461890e-01 -4.090199e-01 9.509655e-01 -6.341827e-02 4.008008e-01 5.423699e-01 2.595563e-01 1.357025e+00 -1.271401e+00 ''') Set('StyleSheet/Font/font', u'Arial') Set('StyleSheet/axis/Line/width', u'1pt') Add('page', name='page1', autoadd=False) To('page1') Add('graph', name='graph1', autoadd=False) To('graph1') Set('Background/color', u'#e5e9ff') Set('Border/width', u'1pt') Add('axis', name='x', autoadd=False) To('x') Set('label', u'Time (yr)') Set('GridLines/hide', False) To('..') Add('axis', name='y', autoadd=False) To('y') Set('label', u'Offset (m)') Set('min', -3.0) Set('max', 3.0) Set('direction', 'vertical') Set('GridLines/hide', False) To('..') Add('colorbar', name='colorbar1', autoadd=False) To('colorbar1') Set('widgetName', u'xy1') Set('label', u'Power (W)') Set('autoRange', u'exact') Set('lowerPosition', 0.0) Set('upperPosition', 1.0) Set('otherPosition', 0.0) Set('TickLabels/format', u'%VE') Set('horzPosn', u'centre') Set('vertPosn', u'top') Set('width', u'8cm') To('..') Add('xy', name='xy1', autoadd=False) To('xy1') Set('marker', u'circle') Set('markerSize', u'5pt') Set('scalePoints', u'sizef') Set('Color/points', u'c') Set('Color/min', 2.0) Set('Color/max', 4500.0) Set('Color/scaling', u'log') Set('PlotLine/hide', True) Set('MarkerLine/scaleLine', False) Set('MarkerFill/colorMap', u'complement') To('..') To('..') To('..') veusz-3.0.1/examples/multixy.vsz0000664000175000017500000000536013161413406016411 0ustar jssjss00000000000000# Veusz saved document (version 1.15) # Saved at 2012-03-28T19:15:56.421913 ImportString('x(numeric)',''' 0.000000e+00 1.000000e+00 2.000000e+00 3.000000e+00 4.000000e+00 5.000000e+00 6.000000e+00 7.000000e+00 8.000000e+00 9.000000e+00 1.000000e+01 1.100000e+01 1.200000e+01 1.300000e+01 1.400000e+01 1.500000e+01 1.600000e+01 1.700000e+01 1.800000e+01 1.900000e+01 ''') ImportString('y1(numeric)',''' -8.627642e-01 1.213047e+00 -1.682870e+00 2.540669e+00 9.385744e-04 -6.966657e-01 -8.769203e-01 -6.930465e-01 -2.456379e-01 -6.419765e-01 -1.485679e+00 -7.142200e-01 8.639527e-02 -1.155861e+00 -9.576156e-01 6.018372e-02 -1.027861e+00 2.953903e-01 -3.615840e-01 2.474292e-01 ''') ImportString('y2(numeric)',''' 7.356253e-01 2.187511e+00 9.680102e-01 -7.393343e-01 1.071199e+00 1.763134e+00 1.589872e+00 2.015283e+00 7.102356e-01 1.808795e+00 8.750188e-01 1.477934e+00 3.591239e-02 3.046406e+00 3.515513e+00 7.194178e-01 3.498590e+00 4.465251e+00 1.638100e+00 3.577523e+00 ''') ImportString('y3(numeric),+-',''' 1.537872e+00 3.000000e-01 2.879103e-01 3.000000e-01 7.127184e+00 3.000000e-01 5.775675e+00 3.000000e-01 3.390224e+00 3.000000e-01 2.470264e+00 3.000000e-01 1.019945e+00 3.000000e-01 -5.690097e-01 3.000000e-01 4.276276e+00 3.000000e-01 -4.449537e+00 3.000000e-01 -7.127589e-02 3.000000e-01 -9.531333e-01 3.000000e-01 -1.129021e+00 3.000000e-01 2.561764e+00 3.000000e-01 -1.763882e+00 3.000000e-01 -3.791216e-01 3.000000e-01 2.752641e-02 3.000000e-01 -1.044617e+00 3.000000e-01 2.075609e+00 3.000000e-01 -7.859457e-01 3.000000e-01 ''') Add('page', name='page1', autoadd=False) To('page1') Add('graph', name='graph1', autoadd=False) To('graph1') Add('axis', name='x', autoadd=False) To('x') Set('label', 'Winged warriors') To('..') Add('axis', name='y', autoadd=False) To('y') Set('label', 'Death rate') Set('direction', 'vertical') To('..') Add('key', name='key1', autoadd=False) To('key1') Set('Background/color', '#f0f0f0') Set('title', u'Datasets') To('..') Add('xy', name='xy1', autoadd=False) To('xy1') Set('yData', 'y1') Set('key', 'Valkyries') To('..') Add('xy', name='xy2', autoadd=False) To('xy2') Set('yData', 'y2') Set('marker', 'diamond') Set('key', 'Swindon') Set('PlotLine/style', 'dotted') Set('MarkerFill/color', 'red') To('..') Add('xy', name='xy3', autoadd=False) To('xy3') Set('yData', 'y3') Set('marker', 'square') Set('key', 'Discworld') Set('PlotLine/style', 'dashed') Set('MarkerFill/color', 'blue') To('..') Add('label', name='label1', autoadd=False) To('label1') Set('label', 'The joy of plots') Set('yPos', [0.9]) Set('alignHorz', 'centre') Set('Text/size', '20pt') To('..') Add('function', name='function1', autoadd=False) To('function1') Set('function', '2.5') Set('key', 'Model') Set('Line/color', '#20f020') Set('Line/width', '2pt') To('..') To('..') To('..') veusz-3.0.1/examples/multiaxes.vsz0000664000175000017500000001432613161413406016713 0ustar jssjss00000000000000# Veusz saved document (version 1.17.1) ImportString('e_vapec_Fe(numeric),+,-',''' 1.048560e+00 2.058700e-01 -1.548750e-01 1.100720e+00 1.566200e-01 -1.362230e-01 1.458910e+00 1.215400e-01 -1.124400e-01 1.385790e+00 9.432000e-02 -8.882000e-02 1.554010e+00 1.010600e-01 -9.609000e-02 1.606450e+00 9.489000e-02 -8.930000e-02 1.529250e+00 6.969000e-02 -6.718000e-02 1.313880e+00 7.225000e-02 -8.319000e-02 1.225040e+00 6.154000e-02 -6.191000e-02 1.032810e+00 6.701000e-02 -6.126800e-02 9.529260e-01 4.919400e-02 -4.681300e-02 9.398610e-01 3.345500e-02 -4.384400e-02 8.007790e-01 6.300700e-02 -5.745300e-02 8.208400e-01 5.647500e-02 -5.382100e-02 7.343680e-01 7.786000e-02 -7.430000e-02 ''') ImportString('e_vapec_r(numeric),+,-',''' 6.572440e-01 6.572440e-01 -6.572440e-01 1.971730e+00 6.572440e-01 -6.572440e-01 3.812020e+00 1.183040e+00 -1.183040e+00 6.178090e+00 1.183040e+00 -1.183040e+00 8.675620e+00 1.314490e+00 -1.314490e+00 1.130460e+01 1.314490e+00 -1.314490e+00 1.472230e+01 2.103180e+00 -2.103180e+00 1.892860e+01 2.103180e+00 -2.103180e+00 2.366080e+01 2.628980e+00 -2.628980e+00 2.891870e+01 2.628980e+00 -2.628980e+00 3.417670e+01 2.628980e+00 -2.628980e+00 3.943460e+01 2.628980e+00 -2.628980e+00 4.469260e+01 2.628980e+00 -2.628980e+00 4.995050e+01 2.628980e+00 -2.628980e+00 5.573430e+01 3.154770e+00 -3.154770e+00 ''') ImportString('w_vapec_Fe(numeric),+,-',''' 1.142760e+00 2.147200e-01 -2.006420e-01 1.038570e+00 1.464800e-01 -1.187820e-01 1.420330e+00 8.939000e-02 -6.251000e-02 1.497210e+00 9.649000e-02 -7.899000e-02 1.583470e+00 8.227000e-02 -7.777000e-02 1.706010e+00 9.421000e-02 -8.042000e-02 1.700910e+00 6.814000e-02 -6.507000e-02 1.767900e+00 3.961000e-02 -5.456000e-02 1.492170e+00 4.743000e-02 -4.632000e-02 1.527640e+00 3.653000e-02 -5.372000e-02 1.387660e+00 5.398000e-02 -5.368000e-02 1.125260e+00 5.653000e-02 -5.886000e-02 8.423050e-01 5.660000e-02 -5.480100e-02 7.408840e-01 5.404300e-02 -5.544300e-02 7.804360e-01 5.989600e-02 -5.716000e-02 4.899940e-01 5.548800e-02 -5.086800e-02 ''') ImportString('w_vapec_r(numeric),+,-',''' 6.572440e-01 6.572440e-01 -6.572440e-01 1.971730e+00 6.572440e-01 -6.572440e-01 3.812020e+00 1.183040e+00 -1.183040e+00 6.178090e+00 1.183040e+00 -1.183040e+00 8.675620e+00 1.314490e+00 -1.314490e+00 1.130460e+01 1.314490e+00 -1.314490e+00 1.472230e+01 2.103180e+00 -2.103180e+00 1.892860e+01 2.103180e+00 -2.103180e+00 2.366080e+01 2.628980e+00 -2.628980e+00 2.891870e+01 2.628980e+00 -2.628980e+00 3.417670e+01 2.628980e+00 -2.628980e+00 3.943460e+01 2.628980e+00 -2.628980e+00 4.469260e+01 2.628980e+00 -2.628980e+00 4.995050e+01 2.628980e+00 -2.628980e+00 5.573430e+01 3.154770e+00 -3.154770e+00 6.204380e+01 3.154770e+00 -3.154770e+00 ''') ImportString('xmm_vapec_Fe(numeric),+,-',''' 1.449610e+00 9.015000e-02 -8.800000e-02 1.678040e+00 9.248000e-02 -8.888000e-02 1.397970e+00 5.039000e-02 -4.878000e-02 1.268240e+00 4.763000e-02 -4.640000e-02 9.716710e-01 2.446800e-02 -3.534100e-02 6.921560e-01 3.152200e-02 -2.766700e-02 5.156140e-01 5.149700e-02 -3.500000e-02 5.237700e-01 2.780900e-02 -2.708700e-02 4.533620e-01 2.902700e-02 -2.945400e-02 3.195460e-01 2.948100e-02 -2.584100e-02 ''') ImportString(u'xmm_vapec_Fe_outer(numeric),+,-',''' 9.716710e-01 2.446800e-02 -3.534100e-02 6.921560e-01 3.152200e-02 -2.766700e-02 5.156140e-01 5.149700e-02 -3.500000e-02 5.237700e-01 2.780900e-02 -2.708700e-02 4.533620e-01 2.902700e-02 -2.945400e-02 3.195460e-01 2.948100e-02 -2.584100e-02 ''') ImportString('xmm_vapec_r(numeric),+,-',''' 4.274760e+00 4.274760e+00 -4.274760e+00 1.207620e+01 3.526680e+00 -3.526680e+00 1.998450e+01 4.381630e+00 -4.381630e+00 2.917520e+01 4.809100e+00 -4.809100e+00 3.954150e+01 5.557190e+00 -5.557190e+00 5.183140e+01 6.732740e+00 -6.732740e+00 6.700680e+01 8.442650e+00 -8.442650e+00 8.570890e+01 1.025940e+01 -1.025940e+01 1.066550e+02 1.068690e+01 -1.068690e+01 1.292050e+02 1.186250e+01 -1.186250e+01 ''') ImportString(u'xmm_vapec_r_outer(numeric),+,-',''' 3.954150e+01 5.557190e+00 -5.557190e+00 5.183140e+01 6.732740e+00 -6.732740e+00 6.700680e+01 8.442650e+00 -8.442650e+00 8.570890e+01 1.025940e+01 -1.025940e+01 1.066550e+02 1.068690e+01 -1.068690e+01 1.292050e+02 1.186250e+01 -1.186250e+01 ''') Set('width', u'20cm') Add('page', name='flux', autoadd=False) To('flux') Add('graph', name='graph1', autoadd=False) To('graph1') Set('rightMargin', u'1.7cm') Add('axis', name='x', autoadd=False) To('x') Set('label', u'Radius (kpc)') Set('min', 8.0) Set('log', True) To('..') Add('axis', name='y', autoadd=False) To('y') Set('label', u'Optical surface brightness (mag arcsec^{-2})') Set('min', 31.0) Set('max', 22.0) Set('direction', 'vertical') To('..') Add('axis', name='ydensity', autoadd=False) To('ydensity') Set('label', u'Electron density (cm^{-3})') Set('min', 0.001) Set('max', 0.03) Set('log', True) Set('direction', u'vertical') Set('otherPosition', 1.0) To('..') Add('function', name='Bsb', autoadd=False) To('Bsb') Set('function', u'(1.39+22.6)-8.33+8.33*(x/15.43)**0.25') Set('key', u'Optical surface brightness') Set('Line/color', u'red') To('..') Add('function', name='jamesne', autoadd=False) To('jamesne') Set('function', u'((x/0.096)**-0.87-0.00055)*1.2') Set('key', u'Electron density') Set('yAxis', u'ydensity') Set('Line/color', u'black') Set('Line/style', u'dashed') To('..') Add('xy', name='FeW', autoadd=False) To('FeW') Set('xData', u'w_vapec_r') Set('yData', u'w_vapec_Fe') Set('key', u'Iron \\emph{Chandra} Western') Set('yAxis', u'yZ') Set('MarkerFill/color', u'blue') To('..') Add('xy', name='FeE', autoadd=False) To('FeE') Set('xData', u'e_vapec_r') Set('yData', u'e_vapec_Fe') Set('marker', u'cross') Set('key', u'Iron \\emph{Chandra} Eastern') Set('yAxis', u'yZ') Set('MarkerFill/color', u'grey') To('..') Add('axis', name='yZ', autoadd=False) To('yZ') Set('label', u'Iron metallicity (solar units)') Set('min', 0.3) Set('max', 1.9) Set('direction', u'vertical') Set('otherPosition', 0.8) Set('MajorTicks/number', 8) To('..') Add('xy', name='FeWXMM', autoadd=False) To('FeWXMM') Set('xData', u'xmm_vapec_r_outer') Set('yData', u'xmm_vapec_Fe_outer') Set('marker', u'square') Set('key', u'Iron \\emph{XMM}') Set('yAxis', u'yZ') Set('MarkerFill/color', u'white') To('..') Add('key', name='key1', autoadd=False) To('key1') Set('Text/size', u'14pt') Set('Border/hide', True) Set('horzPosn', u'left') To('..') To('..') To('..') veusz-3.0.1/examples/functions.vsz0000664000175000017500000000320213161413406016677 0ustar jssjss00000000000000# Veusz saved document (version 1.17.1) Add('page', name='page1', autoadd=False) To('page1') Add('graph', name='graph1', autoadd=False) To('graph1') Set('rightMargin', '1.7cm') Add('axis', name='x', autoadd=False) To('x') Set('label', u'\\emph{x} axis') Set('min', -1.5) Set('max', 1.5) To('..') Add('axis', name='y', autoadd=False) To('y') Set('label', 'Left axis') Set('min', 0.0) Set('max', 30.0) Set('direction', 'vertical') To('..') Add('axis', name='axis1', autoadd=False) To('axis1') Set('label', 'Another axis') Set('min', 0.01) Set('max', 1.0) Set('log', True) Set('direction', 'vertical') Set('otherPosition', 1.0) Set('TickLabels/format', u'%Ve') To('..') Add('function', name='function3', autoadd=False) To('function3') Set('function', 'exp( -x**2 )*20') Set('Line/color', 'purple') Set('Line/width', '3pt') To('..') Add('function', name='function2', autoadd=False) To('function2') Set('function', 'sin(x*4)*5+5') Set('FillBelow/color', 'red') Set('FillBelow/hide', False) Set('FillBelow/transparency', 60) To('..') Add('function', name='function1', autoadd=False) To('function1') Set('function', 'sin(y)') Set('variable', 'y') Set('steps', 100) Set('FillBelow/color', 'cyan') Set('FillBelow/hide', False) Set('FillBelow/transparency', 60) To('..') Add('label', name='label1', autoadd=False) To('label1') Set('label', '\\delta') Set('xPos', [0.98]) Set('yPos', [0.95]) Set('alignHorz', 'right') Set('alignVert', 'top') Set('Text/size', '100pt') To('..') Add('function', name='function4', autoadd=False) To('function4') Set('function', 'x**2') Set('yAxis', 'axis1') Set('FillAbove/color', 'green') Set('FillAbove/hide', False) To('..') To('..') To('..') veusz-3.0.1/examples/axis_function.vsz0000664000175000017500000000362413161413406017550 0ustar jssjss00000000000000# Veusz saved document (version 1.25.1) # Saved at 2017-04-29T15:04:45.027651 Set('width', '12cm') Set('height', '12cm') Set('colorTheme', u'default1') Set('StyleSheet/Font/font', u'DejaVu Sans') Set('StyleSheet/Font/size', u'16pt') Set('StyleSheet/axis/TickLabels/color', u'darkgray') Set('StyleSheet/function/steps', 100) Set('StyleSheet/function/Line/width', u'2pt') Add('page', name='page1', autoadd=False) To('page1') Add('graph', name='graph1', autoadd=False) To('graph1') Set('leftMargin', '1.7cm') Set('rightMargin', '0.2cm') Set('topMargin', '0.2cm') Set('bottomMargin', '1.7cm') Add('axis', name='x', autoadd=False) To('x') Set('label', u'Linear axis') Set('max', 2.0) To('..') Add('axis-function', name=u'y', autoadd=False) To(u'y') Set('function', u't**2') Set('label', u'Squared axis') Set('mint', 0.0) Set('direction', u'vertical') Set('TickLabels/color', u'darkgrey') Set('TickLabels/rotate', u'45') Set('GridLines/style', u'solid') Set('GridLines/hide', False) Set('MinorGridLines/style', u'solid') Set('MinorGridLines/hide', False) To('..') Add('function', name=u'xsquared', autoadd=False) To(u'xsquared') Set('function', u'x**2') To('..') Add('function', name=u'xlinear', autoadd=False) Add('function', name=u'xcubed', autoadd=False) To(u'xcubed') Set('function', u'x**3') To('..') Add('label', name='label1', autoadd=False) To('label1') Set('label', u'\\emph{x}^2') Set('xPos', [0.8986329307542781]) Set('yPos', [0.5768936617698607]) Set('Text/size', u'20pt') Set('Text/color', u'theme1') To('..') Add('label', name='label2', autoadd=False) To('label2') Set('label', u'\\emph{x}^3') Set('xPos', [0.8986329307542781]) Set('yPos', [0.8]) Set('Text/size', u'20pt') Set('Text/color', u'theme3') To('..') Add('label', name='label3', autoadd=False) To('label3') Set('label', u'\\emph{x}^1') Set('xPos', [0.8986329307542781]) Set('yPos', [0.4]) Set('Text/size', u'20pt') Set('Text/color', u'theme2') To('..') To('..') To('..') veusz-3.0.1/examples/mathml.vsz0000664000175000017500000001022713161413406016156 0ustar jssjss00000000000000# Veusz saved document (version 1.15.99) # Saved at 2012-07-01T14:28:01.630526 Add('page', name='page1', autoadd=False) To('page1') Add('graph', name='graph1', autoadd=False) To('graph1') Set('leftMargin', u'2.5cm') Set('rightMargin', '0.2cm') Set('topMargin', '0.412cm') Set('bottomMargin', u'2.5cm') Add('axis', name='x', autoadd=False) To('x') Set('label', u"f(a)=γf(z)zadz\n") To('..') Add('axis', name='y', autoadd=False) To('y') Set('label', u"x=b±b24ac2a\n") Set('direction', 'vertical') To('..') Add('label', name=u'Christoffel', autoadd=False) To(u'Christoffel') Set('label', u"(XY)k=Xi(iY)k=Xi(Ykxi+ΓimkYm)") Set('alignHorz', u'centre') Set('alignVert', u'centre') Set('Text/size', u'16pt') To('..') Add('label', name=u'URL', autoadd=False) To(u'URL') Set('label', u'Taken from http://www.mathjax.org/demos/mathml-samples/') Set('xPos', [0.02]) Set('yPos', [0.02]) To('..') Add('label', name=u'CurlVector', autoadd=False) To(u'CurlVector') Set('label', u"×F=(FzyFyz)i+(FxzFzx)j+(FyxFxy)k\n") Set('xPos', [0.5]) Set('yPos', [0.2]) Set('alignHorz', u'centre') Set('alignVert', u'centre') To('..') Add('label', name='label1', autoadd=False) To('label1') Set('label', u'MathML examples') Set('yPos', [0.9]) Set('alignHorz', u'centre') Set('alignVert', u'centre') Set('Text/size', u'28pt') Set('Text/bold', True) To('..') To('..') To('..') veusz-3.0.1/examples/shapes.vsz0000664000175000017500000000540513161413406016161 0ustar jssjss00000000000000# Veusz saved document (version 1.2) # User: jss # Date: Sun, 23 Nov 2008 13:04:18 +0000 Add('page', name='page1', autoadd=False) To('page1') Add('graph', name='graph1', autoadd=False) To('graph1') Set('leftMargin', u'1.2cm') Set('bottomMargin', u'1.2cm') Add('axis', name='x', autoadd=False) To('x') Set('min', 0.0) Set('max', 1.0) To('..') Add('axis', name='y', autoadd=False) To('y') Set('min', 0.0) Set('max', 1.5) Set('direction', 'vertical') To('..') Add('rect', name='diamons', autoadd=False) To('diamons') Set('Fill/transparency', 50) Set('Fill/hide', False) Set('xPos', [0.10000000000000001, 0.5]) Set('yPos', [0.10000000000000001, 0.5]) Set('width', [0.10000000000000001, 0.20000000000000001]) Set('height', [0.10000000000000001, 0.20000000000000001]) Set('rotate', [45.0]) To('..') Add('line', name='arrow', autoadd=False) To('arrow') Set('arrowleft', u'arrow') Set('arrowright', u'arrow') Set('xPos', [0.16651257389999999]) Set('yPos', [0.17450029668958472]) Set('length', [0.33063522110660781]) Set('angle', [315.0]) To('..') Add('ellipse', name='circle', autoadd=False) To('circle') Set('Fill/color', u'blue') Set('Fill/transparency', 50) Set('Fill/hide', False) Set('xPos', [0.75361019828480202]) Set('yPos', [0.71367158438168354]) Set('width', [0.10000000000000001]) Set('height', [0.10000000000000001]) Set('rotate', [0.0]) To('..') Add('xy', name='pts', autoadd=False) To('pts') Set('xData', [0.10000000000000001, 0.20000000000000001, 0.5, 0.5]) Set('yData', [0.14999999999999999, 0.20000000000000001, 0.5, 0.75]) Set('marker', u'diamondhole') Set('markerSize', u'5pt') Set('MarkerFill/color', u'red') To('..') Add('label', name='alpha', autoadd=False) To('alpha') Set('label', u'\\alpha') Set('xPos', [0.26436217797160116]) Set('yPos', [0.31827930674081106]) Set('Text/size', u'20pt') To('..') Add('label', name='title', autoadd=False) To('title') Set('label', u'Some shapes') Set('xPos', [0.5]) Set('yPos', [0.90000000000000002]) Set('alignHorz', u'centre') Set('alignVert', u'centre') Set('Text/size', u'30pt') To('..') Add('function', name='function1', autoadd=False) To('function1') Set('function', u'x**2+0.5') Set('Line/hide', True) Set('FillBelow/color', u'green') Set('FillBelow/hide', False) To('..') Add('line', name='line2', autoadd=False) To('line2') Set('arrowleft', u'arrowreverse') Set('arrowright', u'circle') Set('xPos', [0.40548435678619482]) Set('yPos', [0.80000000000000004]) Set('length', [0.20000000000000001]) Set('angle', [90.471358757255615]) Set('Line/color', u'blue') To('..') Add('rect', name='rect2', autoadd=False) To('rect2') Set('Fill/color', u'#aaff7f') Set('Fill/hide', False) Set('Border/style', u'dotted') Set('xPos', [0.5]) Set('yPos', [0.89000000000000001]) Set('width', [0.5]) Set('height', [0.10000000000000001]) Set('rotate', [0.0]) To('..') To('..') To('..') veusz-3.0.1/examples/bar_options.vsz0000664000175000017500000000646113161413406017220 0ustar jssjss00000000000000# Veusz saved document (version 1.15.99) # Saved at 2012-06-10T10:13:43.570573 ImportString(u'x(numeric)',''' 1.000000e+00 2.000000e+00 3.000000e+00 4.000000e+00 5.000000e+00 6.000000e+00 7.000000e+00 8.000000e+00 ''') ImportString(u'y1(numeric)',''' 4.000000e+00 2.000000e+00 6.000000e+00 7.000000e+00 3.000000e+00 5.000000e+00 7.000000e+00 2.000000e+00 ''') ImportString(u'y2(numeric)',''' 1.000000e+00 3.000000e+00 2.000000e+00 2.000000e+00 3.000000e+00 3.000000e+00 1.000000e+00 2.000000e+00 ''') ImportString(u'y3(numeric)',''' 5.000000e-01 1.000000e+00 3.000000e-01 5.000000e-01 7.000000e-01 1.000000e+00 1.500000e+00 1.000000e+00 ''') Set('width', '12cm') Set('height', '15cm') Set('StyleSheet/Font/font', u'Georgia') Set('StyleSheet/bar/BarFill/fills', [('solid', '#5555ff', False), ('solid', '#00ffff', False), ('solid', '#00aaff', False)]) Set('StyleSheet/graph/bottomMargin', u'0.3cm') Add('page', name='page1', autoadd=False) To('page1') Add('grid', name='grid1', autoadd=False) To('grid1') Set('columns', 1) Set('leftMargin', u'0cm') Set('rightMargin', u'0cm') Set('topMargin', u'0.cm') Set('bottomMargin', u'1.cm') Add('graph', name=u'stackedarea', autoadd=False) To(u'stackedarea') Add('axis', name='x', autoadd=False) To('x') Set('autoRange', u'exact') To('..') Add('axis', name='y', autoadd=False) To('y') Set('min', 0.0) Set('direction', 'vertical') To('..') Add('key', name='key1', autoadd=False) To('key1') Set('Text/size', u'10pt') Set('horzPosn', 'centre') Set('vertPosn', 'bottom') Set('horzManual', 0.0) Set('vertManual', 0.0) To('..') Add('bar', name='bar1', autoadd=False) To('bar1') Set('lengths', (u'y1', u'y2', u'y3')) Set('posn', u'x') Set('mode', u'stacked-area') Set('keys', (u'Balloons', u'Slinkys', u'Jigsaws')) Set('BarLine/lines', [('solid', u'1.5pt', 'black', False), ('solid', u'1pt', 'black', False), ('solid', u'0.5pt', 'black', False)]) To('..') Add('label', name='label1', autoadd=False) To('label1') Set('label', u'Stacked area') Set('xPos', [0.025]) Set('yPos', [0.95]) Set('alignVert', u'top') To('..') To('..') Add('graph', name=u'stacked', autoadd=False) To(u'stacked') Add('axis', name='x', autoadd=False) To('x') Set('autoRange', u'exact') To('..') Add('axis', name='y', autoadd=False) To('y') Set('min', 0.0) Set('direction', 'vertical') To('..') Add('bar', name=u'bar1', autoadd=False) To(u'bar1') Set('lengths', (u'y1', u'y2', u'y3')) Set('posn', u'x') Set('mode', u'stacked') Set('barfill', 1.0) Set('BarLine/lines', []) To('..') Add('label', name='label1', autoadd=False) To('label1') Set('label', u'Stacked') Set('xPos', [0.025]) Set('yPos', [0.95]) Set('alignVert', u'top') To('..') To('..') Add('graph', name=u'grouped', autoadd=False) To(u'grouped') Add('axis', name='x', autoadd=False) To('x') Set('label', u'Area') Set('autoRange', u'exact') Set('Label/position', u'at-minimum') To('..') Add('axis', name='y', autoadd=False) To('y') Set('label', u'Sales') Set('min', 0.0) Set('direction', 'vertical') Set('Label/position', u'at-minimum') To('..') Add('bar', name=u'bar1', autoadd=False) To(u'bar1') Set('lengths', (u'y1', u'y2', u'y3')) Set('posn', u'x') Set('mode', u'grouped') Set('barfill', 1.0) Set('BarLine/lines', []) To('..') Add('label', name='label1', autoadd=False) To('label1') Set('label', u'Grouped') Set('xPos', [0.025]) Set('yPos', [0.95]) Set('alignVert', u'top') To('..') To('..') To('..') To('..') veusz-3.0.1/examples/3d_surface.vsz0000664000175000017500000000405413304743765016730 0ustar jssjss00000000000000# Veusz saved document (version 2.99) # Saved at 2018-06-03T10:50:08.474574 SetData2DXYFunc(u'radius', (-10.0, 10.0, 0.5), (-10.0, 10.0, 0.5), u'sqrt(x**2+y**2)', linked=True) SetData2DXYFunc(u'sincfn', (-10.0, 10.0, 0.5), (-10.0, 10.0, 0.5), u'sin(sqrt(x**2+y**2))/(sqrt(x**2+y**2)+0.2)', linked=True) Set('StyleSheet/Font/font', u'DejaVu Sans') Set('StyleSheet/axis-function/autoRange', u'next-tick') Add('page', name=u'page1', autoadd=False) To(u'page1') Add('scene3d', name=u'scene3d1', autoadd=False) To(u'scene3d1') Set('xRotation', -52.0) Set('yRotation', -166.0) Set('zRotation', 0.0) Set('Lighting1/enable', True) Set('Lighting1/intensity', 50.0) Set('Lighting1/z', -7.0) Add('graph3d', name=u'graph3d1', autoadd=False) To(u'graph3d1') Set('zSize', 0.7) Set('Border/color', u'blue') Add('axis3d', name=u'x', autoadd=False) To(u'x') Set('min', -9.9) Set('max', 9.9) Set('autoRange', u'exact') Set('Line/color', u'blue') Set('Label/color', u'blue') Set('TickLabels/size', u'10pt') Set('TickLabels/color', u'blue') Set('MajorTicks/number', 4) To('..') Add('axis3d', name=u'y', autoadd=False) To(u'y') Set('min', -9.9) Set('max', 9.9) Set('autoRange', u'exact') Set('direction', u'y') Set('Line/color', u'blue') Set('Label/color', u'blue') Set('TickLabels/size', u'10pt') Set('TickLabels/color', u'blue') Set('MajorTicks/number', 4) To('..') Add('axis3d', name=u'z', autoadd=False) To(u'z') Set('autoRange', u'exact') Set('direction', u'z') Set('Line/color', u'blue') Set('Label/color', u'blue') Set('TickLabels/size', u'10pt') Set('TickLabels/color', u'blue') To('..') Add('function3d', name=u'circleline', autoadd=False) To(u'circleline') Set('fnx', u'sin(t*2*pi)*4') Set('fny', u'cos(t*2*pi)*4') Set('fnz', u'0.2') Set('fncolor', u'sin(t*4*pi)') Set('Line/width', 4.0) Set('Line/colorMap', u'brown-blue') To('..') Add('surface3d', name=u'sinsurface', autoadd=False) To(u'sinsurface') Set('data', u'sincfn') Set('DataColor/points', u'radius') Set('DataColor/max', 20.0) Set('hide', False) Set('Line/color', u'grey') Set('Surface/colorMap', u'seq') To('..') To('..') To('..') To('..') veusz-3.0.1/examples/filtered.vsz0000664000175000017500000000454213161413406016475 0ustar jssjss00000000000000# Veusz saved document (version 1.22.99) # Saved at 2015-04-14T17:42:14.659501 FilterDatasets(u'filter_x>6', [u'filter_x', u'filter_y'], prefix=u'filter2_') FilterDatasets(u'y>2.5', [u'x', 'y'], prefix=u'filter_') SetDataRange(u'x', 50, (0.0, 10.0), linked=True) ImportString('y(numeric)',''' 2.528453e+00 1.816528e+00 1.502053e+00 3.643409e+00 2.191947e+00 1.178337e+00 4.517599e-01 2.776858e+00 4.134110e+00 1.368582e+00 1.520669e+00 1.740393e+00 1.145020e+00 2.784998e+00 1.718806e+00 2.290992e-01 2.750012e+00 3.003834e+00 8.065036e-01 2.700813e+00 8.253849e-01 3.472289e+00 1.620575e+00 3.641267e+00 4.154029e-01 3.113261e+00 1.846461e+00 1.974401e+00 2.230803e+00 1.855437e+00 -4.376838e-01 1.718318e+00 -3.487412e-01 4.913468e+00 2.695930e+00 1.504972e+00 1.737499e+00 2.401471e+00 3.216212e+00 1.021641e+00 2.452775e+00 3.390762e+00 3.383650e+00 5.650132e-01 2.609646e+00 2.381425e+00 2.313510e+00 1.720346e+00 2.206088e+00 8.747802e-01 ''') Set('StyleSheet/Font/font', u'Liberation Mono') Add('page', name='page1', autoadd=False) To('page1') Add('graph', name='graph1', autoadd=False) To('graph1') Add('axis', name='x', autoadd=False) To('x') Set('label', u'X axis') To('..') Add('axis', name='y', autoadd=False) To('y') Set('label', u'Y axis') Set('autoRange', u'+10%') Set('direction', 'vertical') To('..') Add('xy', name=u'filtered', autoadd=False) To(u'filtered') Set('marker', u'square') Set('markerSize', u'5pt') Set('color', u'#ffaa00') Set('xData', u'filter_x') Set('yData', u'filter_y') Set('key', u'Filtered') Set('PlotLine/hide', True) To('..') Add('xy', name=u'original', autoadd=False) To(u'original') Set('key', u'Data') To('..') Add('function', name='function1', autoadd=False) To('function1') Set('function', u'2.5') Set('Line/color', u'grey') Set('Line/width', u'1pt') To('..') Add('key', name='key1', autoadd=False) To('key1') Set('Border/hide', True) Set('horzPosn', 'left') Set('vertPosn', 'bottom') Set('horzManual', 0.0) Set('vertManual', 0.0) To('..') Add('xy', name='xy1', autoadd=False) To('xy1') Set('marker', u'diamond') Set('markerSize', u'8pt') Set('color', u'#aaaaff') Set('xData', u'filter2_filter_x') Set('yData', u'filter2_filter_y') Set('PlotLine/hide', True) To('..') Add('function', name='function2', autoadd=False) To('function2') Set('function', u'6') Set('variable', u'y') Set('Line/color', u'grey') Set('Line/width', u'1pt') To('..') To('..') To('..') veusz-3.0.1/examples/stackedxy.vsz0000664000175000017500000000416313161413406016675 0ustar jssjss00000000000000# Example stacked plot # Load in datasets x, y1, y2 and y3+symerrors ImportString('x y1 y2 y3,+-',''' 0.0 -8.627642e-01 7.356253e-01 1.537872e+00 0.3 1.0 1.213047e+00 2.187511e+00 2.879103e-01 0.3 2.0 -1.682870e+00 9.680102e-01 7.127184e+00 0.3 3.0 2.540669e+00 -7.393343e-01 5.775675e+00 0.3 4.0 9.385744e-04 1.071199e+00 3.390224e+00 0.3 5.0 -6.966657e-01 1.763134e+00 2.470264e+00 0.3 6.0 -8.769203e-01 1.589872e+00 1.019945e+00 0.3 7.0 -6.930465e-01 2.015283e+00 -5.690097e-01 0.3 8.0 -2.456379e-01 7.102356e-01 4.276276e+00 0.3 9.0 -6.419765e-01 1.808795e+00 -4.449537e+00 0.3 10.0 -1.485679e+00 8.750188e-01 -7.127589e-02 0.3 11.0 -7.142200e-01 1.477934e+00 -9.531333e-01 0.3 12.0 8.639527e-02 3.591239e-02 -1.129021e+00 0.3 13.0 -1.155861e+00 3.046406e+00 2.561764e+00 0.3 14.0 -9.576156e-01 3.515513e+00 -1.763882e+00 0.3 15.0 6.018372e-02 7.194178e-01 -3.791216e-01 0.3 16.0 -1.027861e+00 3.498590e+00 2.752641e-02 0.3 17.0 2.953903e-01 4.465251e+00 -1.044617e+00 0.3 18.0 -3.615840e-01 1.638100e+00 2.075609e+00 0.3 19.0 2.474292e-01 3.577523e+00 -7.859457e-01 0.3 ''') To(Add('page')) # grid container holds sets of plots To(Add('grid')) Set('rows', 3) Set('columns', 1) # x axis is shared by all graphs in grid Add('axis', name='x') Set('x/label', 'Traffic police') # add the first graph in the grid To(Add('graph')) Set('y/label', 'Valkyries') To(Add('xy')) Set('yData', 'y1') To('../..') # add 2nd To(Add('graph')) Set('y/label', 'Swindon') To(Add('xy')) Set('yData', 'y2') Set('marker', 'diamond') Set('PlotLine/style', 'dotted') Set('MarkerFill/color', 'red') To('../..') # add 3rd To(Add('graph')) Set('y/label', 'Discworld') To(Add('xy')) Set('yData', 'y3') Set('marker', 'square') Set('PlotLine/style', 'dashed') Set('MarkerFill/color', 'blue') To('../..') # this puts the label at the side of the plot, so # all the labels line up # of course, this seems a silly way to do it, but it's just # an example for i in GetChildren(): if 'y' in GetChildren(i): Set('%s/y/Label/atEdge' % i, True) # collapse margins of all the items in the grid Action('zeroMargins') To('/') veusz-3.0.1/examples/barplots.vsz0000664000175000017500000001152013161413406016517 0ustar jssjss00000000000000# Veusz saved document (version 1.25.1) # Saved at 2017-04-30T10:42:41.492556 AddCustom('color', u'theme2', u'#0055ff') AddCustom('color', u'theme1', u'#ff557f') ImportString(u'ds1(numeric)',''' 0.000000e+00 3.894183e-01 7.173561e-01 9.320391e-01 9.995736e-01 9.092974e-01 6.754632e-01 3.349882e-01 -5.837414e-02 -4.425204e-01 -7.568025e-01 -9.516021e-01 -9.961646e-01 -8.834547e-01 -6.312666e-01 -2.794155e-01 1.165492e-01 4.941134e-01 7.936679e-01 9.679197e-01 9.893582e-01 ''') ImportString(u'ds2(numeric)',''' 1.000000e+00 9.210610e-01 6.967067e-01 3.623578e-01 -2.919952e-02 -4.161468e-01 -7.373937e-01 -9.422223e-01 -9.982948e-01 -8.967584e-01 -6.536436e-01 -3.073329e-01 8.749898e-02 4.685167e-01 7.755659e-01 9.601703e-01 9.931849e-01 8.693975e-01 6.083513e-01 2.512598e-01 -1.455000e-01 ''') ImportString(u'dswerr(numeric),+-',''' 1.000000e+00 1.000000e-01 9.210610e-01 1.000000e-01 6.967067e-01 1.000000e-01 3.623578e-01 1.000000e-01 -2.919952e-02 1.000000e-01 -4.161468e-01 1.000000e-01 -7.373937e-01 1.000000e-01 -9.422223e-01 1.000000e-01 -9.982948e-01 1.000000e-01 -8.967584e-01 1.000000e-01 -6.536436e-01 1.000000e-01 -3.073329e-01 1.000000e-01 8.749898e-02 1.000000e-01 4.685167e-01 1.000000e-01 7.755659e-01 1.000000e-01 9.601703e-01 1.000000e-01 9.931849e-01 1.000000e-01 8.693975e-01 1.000000e-01 6.083513e-01 1.000000e-01 2.512598e-01 1.000000e-01 -1.455000e-01 1.000000e-01 ''') ImportString(u'xvals(numeric)',''' 1.000000e+00 2.718282e+00 7.389056e+00 2.008554e+01 5.459815e+01 1.484132e+02 4.034288e+02 1.096633e+03 2.980958e+03 8.103084e+03 2.202647e+04 5.987414e+04 1.627548e+05 4.424134e+05 1.202604e+06 3.269017e+06 8.886111e+06 2.415495e+07 6.565997e+07 1.784823e+08 4.851652e+08 ''') Set('colorTheme', u'black') Set('StyleSheet/Font/font', u'Arial') Add('page', name='page1', autoadd=False) To('page1') Add('grid', name='grid1', autoadd=False) To('grid1') Set('leftMargin', u'0.1cm') Set('bottomMargin', u'0.1cm') Add('graph', name='graph1', autoadd=False) To('graph1') Set('leftMargin', u'1.cm') Set('bottomMargin', u'1.cm') Add('axis', name='x', autoadd=False) Add('axis', name='y', autoadd=False) To('y') Set('direction', 'vertical') Set('GridLines/style', u'dotted-fine') Set('GridLines/hide', False) To('..') Add('bar', name='bar1', autoadd=False) To('bar1') Set('lengths', (u'ds1', u'ds2')) Set('mode', u'stacked') Set('keys', (u'a', u'b')) Set('BarFill/fills', [('solid', u'auto', False)]) To('..') Add('label', name='label1', autoadd=False) To('label1') Set('label', u'stacked\\\\mode') Set('xPos', [0.5]) Set('yPos', [0.7750554974893026]) Set('alignHorz', u'centre') To('..') Add('key', name='key1', autoadd=False) To('key1') Set('Background/hide', True) Set('Border/hide', True) Set('horzPosn', 'manual') Set('vertPosn', 'manual') Set('keyLength', u'0.5cm') Set('horzManual', 0.7084593492454648) Set('vertManual', 0.03438193718616283) To('..') To('..') Add('graph', name='graph2', autoadd=False) To('graph2') Set('leftMargin', u'1.cm') Set('bottomMargin', u'1.cm') Add('axis', name='x', autoadd=False) Add('axis', name='y', autoadd=False) To('y') Set('direction', 'vertical') To('..') Add('bar', name='bar1', autoadd=False) To('bar1') Set('lengths', (u'ds1', u'ds2')) Set('keys', ('',)) Set('BarFill/fills', [('solid', u'auto', False)]) To('..') Add('label', name='label1', autoadd=False) To('label1') Set('label', u'grouped\\\\mode') Set('xPos', [0.5]) Set('yPos', [0.7750554974893026]) Set('alignHorz', u'centre') To('..') To('..') Add('graph', name='graph3', autoadd=False) To('graph3') Set('leftMargin', u'1.cm') Set('bottomMargin', u'1.cm') Add('axis', name='x', autoadd=False) Add('axis', name='y', autoadd=False) To('y') Set('direction', 'vertical') To('..') Add('bar', name='bar1', autoadd=False) To('bar1') Set('lengths', (u'dswerr',)) Set('keys', ('',)) Set('errorstyle', u'barends') Set('BarFill/fills', [('solid', u'auto', False)]) To('..') Add('label', name='label1', autoadd=False) To('label1') Set('label', u'error bars') Set('xPos', [0.5]) Set('yPos', [0.9]) Set('alignHorz', u'centre') To('..') To('..') Add('graph', name='graph4', autoadd=False) To('graph4') Set('leftMargin', u'1.cm') Set('bottomMargin', u'1.cm') Add('axis', name='x', autoadd=False) To('x') Set('GridLines/style', u'dotted-fine') Set('GridLines/hide', False) To('..') Add('axis', name='y', autoadd=False) To('y') Set('log', True) Set('direction', 'vertical') Set('GridLines/style', u'dotted-fine') Set('GridLines/hide', False) To('..') Add('bar', name='bar1', autoadd=False) To('bar1') Set('lengths', (u'dswerr',)) Set('posn', u'xvals') Set('direction', u'horizontal') Set('mode', u'stacked') Set('keys', ('',)) Set('barfill', 1.0) Set('groupfill', 1.0) Set('errorstyle', u'barends') Set('BarFill/fills', [('solid', u'auto', False)]) To('..') Add('label', name='label1', autoadd=False) To('label1') Set('label', u'horizontal\\\\with values') Set('xPos', [0.05]) Set('yPos', [0.8]) To('..') To('..') To('..') To('..') veusz-3.0.1/examples/labels.vsz0000664000175000017500000000211613161413406016134 0ustar jssjss00000000000000# Veusz saved document (version 1.0) # User: jss # Date: Sat, 27 Oct 2007 14:31:12 +0000 ImportFile('labels.dat', u'x y label', linked=True) Add('page', name='page1', autoadd=False) To('page1') Add('graph', name='graph1', autoadd=False) To('graph1') Add('axis', name='x', autoadd=False) To('x') Set('label', u'X axis') Set('max', 5.0) To('..') Add('axis', name='y', autoadd=False) To('y') Set('label', u'Y axis') Set('max', 8.0) Set('direction', 'vertical') To('..') Add('xy', name='test1', autoadd=False) To('test1') Set('labels', u'label') Set('PlotLine/hide', True) Set('Label/posnVert', u'top') Set('Label/size', u'16pt') To('..') Add('xy', name='test2', autoadd=False) To('test2') Set('xData', [0.5, 2.1000000000000001, 4.0999999999999996]) Set('yData', [0.10000000000000001, 0.10000000000000001, 3.1000000000000001]) Set('marker', u'square') Set('labels', u'dataset 2') Set('PlotLine/hide', True) Set('MarkerFill/hide', True) Set('Label/posnHorz', u'centre') Set('Label/posnVert', u'top') Set('Label/angle', 90.0) Set('Label/size', u'16pt') Set('Label/color', u'red') To('..') To('..') To('..') veusz-3.0.1/examples/inside.vsz0000664000175000017500000002037113161413406016150 0ustar jssjss00000000000000# Veusz saved document (version 0.6) # User: jss # Date: Thu, 19 May 2005 19:04:53 +0000 ImportString('y',''' -2.587729e+00 -1.609362e+01 -7.226746e+00 1.700540e+01 1.138952e+01 4.296140e+00 -3.227336e+00 -4.241213e+00 -5.803339e-02 4.611423e+00 -3.984561e+00 7.906768e+00 -7.745864e+00 2.987510e+00 3.210930e-01 -6.378710e+00 2.515961e+01 2.522258e+01 -3.091128e+00 5.041150e+00 -1.050379e+01 -1.316306e+01 -2.276787e+00 -5.742196e+00 -9.082636e+00 -1.266413e+01 3.199799e+00 -3.311777e+00 1.562370e+01 1.462025e+00 2.950148e+00 -2.691806e+00 -6.179390e+00 5.307104e+00 -6.554100e+00 7.560850e+00 5.671131e-01 2.216967e+00 1.233732e+01 -9.873626e+00 6.128890e+00 -1.371993e+01 8.882399e-01 1.446327e+01 2.803639e+00 -6.576762e-01 5.930219e+00 2.008943e+00 -1.090018e-01 -6.018549e+00 -1.934561e+00 -3.101619e+00 1.802873e+00 -5.754469e+00 2.941526e+01 1.362095e+01 1.984763e+00 5.046622e+00 -1.380654e+01 7.847492e+00 -4.982190e+00 4.118090e-01 -1.053461e+01 -4.595230e+00 -8.601879e+00 -1.179473e+01 -1.863332e+00 1.987707e+00 7.777890e-01 1.101049e+01 2.914746e+00 9.698452e+00 3.350153e+00 -1.289850e+01 5.864994e+00 4.532047e+00 5.557848e+00 1.153725e+00 6.995080e-01 -5.830288e+00 1.251062e+01 1.392709e+01 -1.595572e+01 1.185186e+00 -7.681659e+00 -1.987566e+01 -1.177874e+00 6.851930e+00 -8.103260e-01 -6.909352e+00 2.749254e-01 5.844212e+00 -1.041544e+00 -7.869795e+00 -1.132402e+01 8.992709e+00 1.866769e+01 1.701594e+01 3.893783e+00 5.528125e+00 -7.268189e+00 1.346805e+01 1.946215e+01 8.250416e+00 -1.334784e+01 2.024725e+01 4.509477e+00 -9.515657e+00 -4.556112e+00 -4.316370e+00 2.908727e+00 1.554901e+01 -2.681748e+00 1.779937e+01 -5.736510e+00 -3.372016e+00 -3.216765e+00 1.344067e+01 5.409029e+00 6.133639e+00 -6.941178e+00 -1.122230e+01 1.461280e+01 1.189957e+00 -8.297384e+00 -7.565539e+00 4.255451e+00 8.152416e+00 5.124337e+00 3.879700e+00 -1.763969e+01 1.595328e+01 -7.472883e+00 -6.698127e+00 -1.692927e+01 -7.813697e+00 2.329430e+00 -1.743128e+00 4.229842e+00 -1.035399e+01 -6.785784e+00 -2.798282e+00 5.799159e+00 -1.373382e+01 7.725470e+00 1.154953e+01 1.586832e+01 4.111123e+00 -9.513406e+00 1.032531e+01 -9.989098e+00 1.777364e+01 4.158636e+00 1.737422e+00 1.059494e+01 4.625862e+00 1.722851e+00 2.564189e+01 7.694167e+00 -6.319989e+00 -8.148266e-01 5.716334e+00 -4.233620e+00 1.226502e+01 5.164003e+00 2.041126e+01 9.797467e+00 -1.132923e+01 7.612042e-01 -1.930018e+00 1.436253e+01 -1.407661e+00 -1.920934e+00 1.114966e+01 2.273019e+01 -4.461129e+00 7.384006e+00 3.493240e+00 -1.636330e+00 -1.843285e+01 1.681489e+01 1.153851e+01 -1.673654e+01 1.622110e+01 3.797960e+00 1.071981e+01 2.440005e+00 4.263002e+00 1.356043e+01 7.384417e+00 2.971312e+01 3.127700e+00 -6.236787e-01 1.118915e+01 1.870411e+01 1.712239e+00 1.117415e+01 -9.288272e+00 9.190616e+00 -1.792989e+01 ''') ImportString(u'x',''' 0.000000e+00 1.000000e+00 2.000000e+00 3.000000e+00 4.000000e+00 5.000000e+00 6.000000e+00 7.000000e+00 8.000000e+00 9.000000e+00 1.000000e+01 1.100000e+01 1.200000e+01 1.300000e+01 1.400000e+01 1.500000e+01 1.600000e+01 1.700000e+01 1.800000e+01 1.900000e+01 2.000000e+01 2.100000e+01 2.200000e+01 2.300000e+01 2.400000e+01 2.500000e+01 2.600000e+01 2.700000e+01 2.800000e+01 2.900000e+01 3.000000e+01 3.100000e+01 3.200000e+01 3.300000e+01 3.400000e+01 3.500000e+01 3.600000e+01 3.700000e+01 3.800000e+01 3.900000e+01 4.000000e+01 4.100000e+01 4.200000e+01 4.300000e+01 4.400000e+01 4.500000e+01 4.600000e+01 4.700000e+01 4.800000e+01 4.900000e+01 5.000000e+01 5.100000e+01 5.200000e+01 5.300000e+01 5.400000e+01 5.500000e+01 5.600000e+01 5.700000e+01 5.800000e+01 5.900000e+01 6.000000e+01 6.100000e+01 6.200000e+01 6.300000e+01 6.400000e+01 6.500000e+01 6.600000e+01 6.700000e+01 6.800000e+01 6.900000e+01 7.000000e+01 7.100000e+01 7.200000e+01 7.300000e+01 7.400000e+01 7.500000e+01 7.600000e+01 7.700000e+01 7.800000e+01 7.900000e+01 8.000000e+01 8.100000e+01 8.200000e+01 8.300000e+01 8.400000e+01 8.500000e+01 8.600000e+01 8.700000e+01 8.800000e+01 8.900000e+01 9.000000e+01 9.100000e+01 9.200000e+01 9.300000e+01 9.400000e+01 9.500000e+01 9.600000e+01 9.700000e+01 9.800000e+01 9.900000e+01 1.000000e+02 1.010000e+02 1.020000e+02 1.030000e+02 1.040000e+02 1.050000e+02 1.060000e+02 1.070000e+02 1.080000e+02 1.090000e+02 1.100000e+02 1.110000e+02 1.120000e+02 1.130000e+02 1.140000e+02 1.150000e+02 1.160000e+02 1.170000e+02 1.180000e+02 1.190000e+02 1.200000e+02 1.210000e+02 1.220000e+02 1.230000e+02 1.240000e+02 1.250000e+02 1.260000e+02 1.270000e+02 1.280000e+02 1.290000e+02 1.300000e+02 1.310000e+02 1.320000e+02 1.330000e+02 1.340000e+02 1.350000e+02 1.360000e+02 1.370000e+02 1.380000e+02 1.390000e+02 1.400000e+02 1.410000e+02 1.420000e+02 1.430000e+02 1.440000e+02 1.450000e+02 1.460000e+02 1.470000e+02 1.480000e+02 1.490000e+02 1.500000e+02 1.510000e+02 1.520000e+02 1.530000e+02 1.540000e+02 1.550000e+02 1.560000e+02 1.570000e+02 1.580000e+02 1.590000e+02 1.600000e+02 1.610000e+02 1.620000e+02 1.630000e+02 1.640000e+02 1.650000e+02 1.660000e+02 1.670000e+02 1.680000e+02 1.690000e+02 1.700000e+02 1.710000e+02 1.720000e+02 1.730000e+02 1.740000e+02 1.750000e+02 1.760000e+02 1.770000e+02 1.780000e+02 1.790000e+02 1.800000e+02 1.810000e+02 1.820000e+02 1.830000e+02 1.840000e+02 1.850000e+02 1.860000e+02 1.870000e+02 1.880000e+02 1.890000e+02 1.900000e+02 1.910000e+02 1.920000e+02 1.930000e+02 1.940000e+02 1.950000e+02 1.960000e+02 1.970000e+02 1.980000e+02 1.990000e+02 ''') ImportString(u'y3',''' 0.000000e+00 1.557408e+00 -2.185040e+00 -1.425465e-01 1.157821e+00 -3.380515e+00 -2.910062e-01 8.714480e-01 -6.799711e+00 -4.523157e-01 ''') ImportString(u'y2,+-',''' 0.000000e+00 1.000000e+00 1.000000e+00 5.000000e-01 1.414214e+00 1.200000e+00 1.732051e+00 8.000000e-01 2.000000e+00 1.000000e+00 2.236068e+00 0.000000e+00 2.449490e+00 0.000000e+00 2.645751e+00 0.000000e+00 2.828427e+00 0.000000e+00 3.000000e+00 0.000000e+00 ''') ImportString(u'x2',''' 0.000000e+00 1.000000e+00 2.000000e+00 3.000000e+00 4.000000e+00 5.000000e+00 6.000000e+00 7.000000e+00 8.000000e+00 9.000000e+00 ''') Add('page', name='page1', autoadd=False) To('page1') Add('graph', name='graph2', autoadd=False) To('graph2') Set('leftMargin', u'10.5cm') Set('rightMargin', u'0.5cm') Set('topMargin', u'0.5cm') Set('bottomMargin', u'10.5cm') Set('Background/hide', True) Add('axis', name='x', autoadd=False) To('x') Set('label', u'an x-axis') Set('Label/italic', True) To('..') Add('axis', name='y', autoadd=False) To('y') Set('label', u'sin 2\\emph{\\pi x}') Set('min', -1.0) Set('max', 1.0) Set('direction', 'vertical') To('..') Add('function', name='function1', autoadd=False) To('function1') Set('function', u'sin(x*pi*2)') Set('FillBelow/color', u'cyan') Set('FillBelow/hide', False) To('..') To('..') Add('grid', name='grid1', autoadd=False) To('grid1') Set('columns', 1) Set('leftMargin', u'2.3cm') Set('rightMargin', u'9cm') Set('topMargin', u'0.5cm') Set('bottomMargin', u'10.5cm') Add('axis', name='x', autoadd=False) Add('graph', name='graph1', autoadd=False) To('graph1') Set('leftMargin', '0cm') Set('rightMargin', '0cm') Set('topMargin', '0cm') Set('bottomMargin', '0cm') Set('Background/color', u'#ffffc0') Add('axis', name='y', autoadd=False) To('y') Set('direction', 'vertical') Set('otherPosition', 1.0) To('..') Add('xy', name='xy1', autoadd=False) To('xy1') Set('xData', u'x2') Set('yData', u'y3') Set('marker', u'pentagon') Set('MarkerFill/color', u'magenta') To('..') To('..') Add('graph', name='graph2', autoadd=False) To('graph2') Set('leftMargin', '0cm') Set('rightMargin', '0cm') Set('topMargin', '0cm') Set('bottomMargin', '0cm') Set('Background/color', u'#ffffc0') Add('axis', name='y', autoadd=False) To('y') Set('direction', 'vertical') Set('otherPosition', 1.0) To('..') Add('xy', name='xy1', autoadd=False) To('xy1') Set('xData', u'x2') Set('yData', u'y2') Set('marker', u'square') Set('MarkerFill/color', u'green') To('..') To('..') To('..') Add('graph', name='graph1', autoadd=False) To('graph1') Add('axis', name='x', autoadd=False) To('x') Set('label', u'Random axis, maybe something interesting^{2}...') To('..') Add('axis', name='y', autoadd=False) To('y') Set('label', u'All the cheese in the world') Set('min', -50.0) Set('max', 100.0) Set('direction', 'vertical') To('..') Add('xy', name='xy1', autoadd=False) Add('function', name='function1', autoadd=False) To('function1') Set('function', u'0') Set('Line/color', u'red') Set('Line/width', u'2pt') To('..') To('..') To('..') veusz-3.0.1/examples/axis_function_linked.csv0000664000175000017500000002471613161413406021054 0ustar jssjss00000000000000z Bmag +- colour 0.0500 17.79 0.05 0.09 0.0529 17.61 0.05 -0.01 0.0251 16.74 0.09 0.21 0.0701 18.38 0.09 0.04 0.0627 18.18 0.05 -0.03 0.0876 19.40 0.11 0.03 0.0786 18.28 0.03 -0.04 0.0172 15.75 0.13 0.03 0.0422 17.29 0.06 -0.01 0.0453 17.59 0.05 0.10 0.0196 15.07 0.11 -0.06 0.1009 19.30 0.03 -0.05 0.0135 14.44 0.16 -0.05 0.0273 16.24 0.09 0.15 0.0746 18.39 0.05 -0.02 0.0265 16.03 0.08 -0.01 0.0499 17.73 0.04 0.00 0.0306 16.20 0.08 0.00 0.0285 16.07 0.08 -0.12 0.0589 18.04 0.09 -0.03 0.0365 16.53 0.10 -0.06 0.0603 17.47 0.14 -0.25 0.0111 15.09 0.20 0.22 0.0461 18.28 0.23 0.40 0.0139 14.16 0.17 -0.05 0.0324 16.33 0.13 0.05 0.0561 17.66 0.07 -0.02 0.0387 17.50 0.14 0.24 0.0397 17.26 0.18 0.08 0.0163 15.03 0.13 0.11 0.0154 15.22 0.14 0.17 0.0144 15.26 0.15 0.22 0.0305 16.18 0.07 -0.04 0.0245 15.51 0.09 -0.06 0.0240 15.94 0.09 0.10 0.0260 15.99 0.09 0.04 0.0139 14.81 0.16 0.07 0.0125 16.45 0.20 0.41 0.0104 13.93 0.21 -0.02 0.0087 14.84 0.25 1.08 0.0165 17.04 0.16 0.53 0.0266 16.72 0.11 0.08 0.0489 17.80 0.13 0.08 0.0243 16.30 0.10 0.11 0.0152 14.77 0.15 -0.01 0.0357 17.05 0.11 0.02 0.0065 13.23 0.33 0.00 0.0117 16.69 0.19 0.78 0.0043 13.10 0.51 0.13 0.0050 13.29 0.43 0.12 0.0488 17.03 0.05 0.01 0.0220 15.87 0.14 0.07 0.0144 15.26 0.18 0.31 0.0275 16.65 0.08 0.12 0.0070 12.94 0.31 -0.01 0.0086 14.19 0.26 0.24 0.1244 19.53 0.03 -0.08 0.0030 16.89 0.72 1.61 0.0360 16.62 0.07 0.03 0.0167 15.35 0.14 0.22 0.0163 15.82 0.13 0.37 0.0180 15.22 0.12 -0.07 0.0299 16.17 0.12 0.02 0.0066 14.84 0.33 0.49 0.0218 15.84 0.11 0.08 0.0164 15.33 0.13 0.05 0.0232 16.53 0.10 0.17 0.0365 17.09 0.07 0.05 0.0193 16.94 0.11 0.43 0.0176 15.59 0.17 0.17 0.0128 15.44 0.18 0.05 0.0135 14.39 0.16 0.12 0.0315 16.78 0.07 0.06 0.0096 13.80 0.23 0.10 0.0235 16.10 0.10 0.05 0.0167 14.81 0.13 0.02 0.0537 17.64 0.06 0.08 0.0077 13.83 0.28 0.12 0.0156 17.21 0.14 0.49 0.0170 15.70 0.13 0.11 0.0102 15.31 0.21 0.29 0.0279 16.05 0.08 0.10 0.0172 15.10 0.13 0.05 0.0061 15.40 0.36 0.56 0.0105 14.32 0.21 0.12 0.0300 16.83 0.08 0.03 0.0096 14.44 0.23 0.16 0.0094 13.95 0.23 0.24 0.0166 15.31 0.13 0.04 0.0133 15.09 0.17 0.09 0.0201 15.83 0.22 0.18 0.0088 14.20 0.25 0.22 0.0380 17.05 0.14 0.02 0.0113 14.14 0.29 0.12 0.0258 16.24 0.18 0.06 0.0201 16.16 0.18 0.19 0.0056 14.71 0.40 0.30 0.0120 14.61 0.24 0.09 0.0170 16.33 0.15 0.21 0.0160 15.88 0.14 0.38 0.0150 14.69 0.15 -0.02 0.0095 14.13 0.23 0.10 0.0544 17.89 0.06 0.07 0.1561 19.97 0.03 -0.01 0.0393 16.78 0.06 0.01 0.1241 19.76 0.05 0.21 0.1441 20.43 0.04 0.12 0.1299 19.60 0.03 0.01 0.0784 18.39 0.03 0.00 0.6200 23.50 0.09 -0.12 0.5700 23.40 0.07 -0.06 0.3000 22.03 0.10 0.07 0.3800 22.64 0.05 -0.10 0.4300 22.61 0.05 -0.21 0.2400 21.60 0.08 0.18 0.3000 21.53 0.38 0.05 0.2400 20.99 0.03 -0.10 0.4400 22.80 0.06 -0.04 0.5000 23.14 0.05 -0.04 0.9700 24.72 0.15 0.42 0.4790 22.72 0.06 -0.12 0.4300 22.38 0.19 0.07 0.1600 0.8300 24.34 0.09 0.06 0.4160 22.46 0.07 -0.08 0.5810 23.16 0.09 0.22 0.4500 22.92 0.05 0.05 0.5790 23.57 0.10 -0.16 0.3200 21.89 0.04 0.03 0.6570 23.92 0.12 0.05 0.4300 22.82 0.07 0.97 0.4720 23.13 0.06 0.11 0.3740 23.32 0.15 -0.18 0.5260 23.18 0.06 0.17 0.7630 24.37 0.29 -0.36 0.5800 23.41 0.07 -0.04 0.4300 23.22 0.05 0.25 0.4500 23.25 0.06 0.11 0.8280 24.55 0.17 0.05 0.6560 23.77 0.11 -0.13 0.4950 22.82 0.05 -0.04 0.4900 23.07 0.05 0.23 0.5700 23.31 0.06 -0.01 0.3880 22.55 0.07 -0.14 0.4500 22.61 0.06 -0.16 0.4800 23.06 0.06 0.01 0.6150 23.22 0.10 0.02 0.4000 22.18 0.05 -0.17 0.6550 23.22 0.11 0.16 0.4980 23.66 0.06 -0.01 0.4650 23.37 0.11 0.33 0.4530 23.20 0.06 -0.12 0.4250 22.34 0.15 0.09 0.1800 20.47 0.03 0.00 0.1720 20.23 0.03 0.00 0.3780 22.63 0.11 0.00 0.3720 22.00 0.08 0.00 0.4200 22.89 0.06 0.00 0.3740 21.80 0.08 0.00 0.3540 22.51 0.16 0.00 0.4580 22.83 0.05 0.00 0.6120 0.5500 0.5920 0.6190 0.2780 21.72 0.06 0.09 0.4770 22.72 0.07 0.02 0.9500 24.30 0.10 0.04 1.0570 24.77 0.13 -0.03 0.8160 24.22 0.09 -0.11 0.4550 23.21 0.07 0.00 1.1950 23.88 0.54 -0.85 0.3690 23.45 0.06 0.44 0.5140 23.06 0.24 -0.09 0.4230 22.60 0.05 0.10 0.9460 24.60 0.19 0.30 0.8590 24.73 0.10 -0.10 1.0310 24.47 0.18 0.67 0.9360 24.96 0.16 -0.15 0.5280 22.89 0.08 -0.10 0.6450 24.55 0.17 0.10 0.9780 24.50 0.10 -0.01 0.8850 24.31 0.11 -0.28 0.8150 25.19 0.20 -0.07 0.6980 24.39 0.08 -0.30 0.5680 23.07 0.06 -0.13 0.7110 23.80 0.08 -0.09 0.3396 22.10 0.09 0.07 0.3965 22.47 0.06 0.15 0.8120 24.95 0.09 0.03 0.7990 24.78 0.08 0.38 0.8820 24.91 0.10 0.63 0.8330 24.26 0.09 0.12 0.8740 25.12 0.14 0.37 0.7720 23.75 0.08 -0.19 0.7190 0.5430 23.03 0.06 -0.01 0.7500 23.91 0.07 -0.01 0.6400 23.80 0.08 0.02 0.4300 22.87 0.08 -0.03 0.6400 23.72 0.09 -0.06 0.4970 23.15 0.06 0.07 0.4400 23.20 0.05 0.23 0.3550 22.67 0.05 0.13 0.7800 24.26 0.09 0.08 0.5400 23.16 0.05 -0.02 0.8600 24.48 0.08 0.02 1.0200 24.97 0.10 0.03 1.1400 24.73 0.12 -0.03 0.8540 24.53 0.09 -0.07 1.3700 25.73 0.18 0.06 0.9750 24.88 0.09 0.10 0.9700 25.02 0.09 -0.12 0.7400 23.84 0.12 -0.11 1.3900 25.82 0.17 0.34 0.4600 23.59 0.11 0.24 1.0200 24.83 0.20 0.11 1.1200 25.07 0.11 0.03 1.2300 26.02 0.12 0.14 1.1900 25.76 0.13 0.25 0.8390 24.20 0.14 -0.04 1.0100 25.03 0.10 -0.01 0.5210 23.07 0.08 0.09 0.4750 23.09 0.15 0.00 0.9500 24.66 0.10 0.13 1.3000 25.65 0.14 0.01 1.3050 25.41 0.16 0.05 0.5260 24.04 0.12 0.16 0.2160 22.13 0.08 0.32 0.7350 24.02 0.08 0.04 1.1400 25.35 0.16 0.27 1.5510 26.64 0.26 0.29 1.2650 25.68 0.12 0.15 1.3400 25.77 0.14 0.18 0.9000 24.08 0.09 0.08 0.8400 24.35 0.08 0.02 0.3590 23.97 0.05 0.00 0.9540 24.37 0.24 -0.17 0.6380 23.59 0.06 0.13 1.2300 24.93 0.16 -0.12 1.4000 26.57 1.16 0.00 1.3070 26.54 0.17 0.08 0.6700 24.21 0.10 0.11 0.6400 24.00 0.35 0.12 0.9540 24.50 0.34 -0.17 0.9350 24.31 0.11 0.00 0.4900 0.5720 24.60 0.09 0.03 0.4680 23.86 0.05 0.29 0.8400 24.31 0.09 0.14 0.9600 24.54 0.13 0.07 0.8218 24.34 0.09 0.02 0.9300 24.87 0.17 -0.26 0.4510 23.23 0.04 0.28 0.6100 23.57 0.06 -0.08 0.8300 24.24 0.10 -0.45 0.7070 24.15 0.09 -0.05 0.4150 22.53 0.04 0.01 0.5570 23.00 0.06 -0.18 0.7910 24.21 0.08 0.04 0.6950 24.03 0.08 -0.06 0.6330 23.74 0.07 -0.08 0.2486 21.13 0.08 -0.09 0.5320 23.49 0.06 0.03 0.3310 21.80 0.03 0.04 0.3460 22.45 0.04 0.12 0.9610 24.57 0.15 -0.12 0.6130 24.02 0.06 0.06 0.3402 22.14 0.03 0.08 0.9830 24.93 0.24 0.02 0.7100 24.26 0.08 0.22 0.7300 24.13 0.07 0.01 0.4700 23.47 0.05 0.06 0.6200 23.78 0.06 -0.06 0.5210 23.32 0.06 0.18 0.3690 22.34 0.04 0.00 0.5710 23.26 0.07 0.03 0.6040 23.32 0.06 0.11 0.9271 24.72 0.16 -0.29 0.2850 21.21 0.03 -0.08 0.2912 22.05 0.05 0.26 0.5480 24.12 0.08 0.24 0.8680 24.37 0.14 -0.11 0.4960 22.96 0.05 -0.05 0.8110 24.39 0.09 -0.16 0.7560 24.08 0.09 -0.23 0.8170 24.27 0.08 0.06 0.7520 23.88 0.08 0.02 0.5516 23.47 0.05 0.11 0.3578 22.53 0.04 0.15 1.0100 25.12 0.27 -0.07 0.7410 24.10 0.07 -0.07 0.4300 22.80 0.05 -0.01 0.5260 23.63 0.06 0.02 0.5920 23.40 0.07 0.08 0.9050 24.29 0.12 0.02 0.9490 24.50 0.14 0.09 0.4607 22.56 0.10 0.02 0.3709 22.20 0.04 -0.02 0.8000 24.52 0.10 -0.39 0.6790 24.10 0.07 0.00 0.5817 23.59 0.07 0.01 0.5500 23.36 0.06 0.11 0.8100 24.27 0.10 -0.07 0.9500 24.55 0.13 0.13 0.3373 21.97 0.03 0.07 0.9100 24.71 0.11 -0.20 0.2630 21.68 0.03 0.09 0.6430 23.80 0.07 0.02 0.6910 24.26 0.07 0.07 0.3570 22.42 0.04 0.13 0.7210 23.90 0.08 0.07 0.5810 23.35 0.06 -0.05 0.6268 23.39 0.06 0.01 0.8180 24.65 0.10 0.03 0.4490 22.55 0.05 -0.05 0.6880 23.63 0.07 -0.07 0.8700 24.46 0.11 -0.04 0.5043 22.98 0.05 0.03 0.4627 22.62 0.19 -0.05 0.5910 23.40 0.08 -0.14 0.4260 22.68 0.05 0.08 0.3290 22.50 0.21 0.26 0.5310 23.23 0.06 -0.15 0.5830 23.59 0.10 0.18 0.3330 21.03 0.04 -0.02 0.5190 23.64 0.09 -0.01 0.4010 22.48 0.07 0.08 0.3400 21.91 0.18 0.04 0.4360 22.50 0.05 0.00 0.3630 21.89 0.07 -0.07 0.4360 22.50 0.05 0.09 0.3090 22.36 0.07 0.07 0.3420 22.19 0.06 0.07 0.3320 22.52 0.06 0.17 0.4690 22.55 0.05 -0.05 0.2390 21.70 0.05 0.19 0.3520 22.80 0.06 0.19 0.6120 24.05 0.19 0.68 0.6310 23.39 0.07 0.05 0.6450 23.38 0.07 -0.09 0.4290 22.65 0.05 -0.02 0.4970 22.90 0.07 0.01 0.5390 23.29 0.07 0.03 0.5610 23.09 0.06 -0.05 0.4100 22.37 0.05 0.09 0.4120 23.06 0.11 0.37 0.5990 23.75 0.09 -0.07 0.6190 23.45 0.08 -0.07 0.4220 22.45 0.07 -0.03 0.5400 23.30 0.06 -0.02 0.4010 23.07 0.08 0.07 0.2180 21.32 0.07 0.23 0.6330 23.18 0.09 0.09 0.3830 22.33 0.08 -0.10 0.3020 23.28 0.14 0.57 0.3400 22.27 0.06 0.19 0.5100 22.79 0.07 0.06 0.4210 23.17 0.12 0.30 0.3990 23.46 0.11 0.32 0.4930 22.92 0.06 0.08 0.6870 23.40 0.09 -0.10 0.5020 23.45 0.13 0.18 0.6870 23.52 0.08 0.02 0.4950 22.90 0.06 0.05 0.6030 23.48 0.07 0.02 0.4210 22.44 0.08 -0.05 0.3480 22.65 0.05 0.15 0.2130 22.01 0.09 0.36 0.3440 21.71 0.05 -0.08 0.2710 21.84 0.07 0.15 0.5640 22.89 0.13 -0.12 0.2740 21.94 0.09 0.15 0.5820 23.81 0.07 -0.08 0.6800 23.73 0.07 0.16 0.4010 23.34 0.07 0.29 0.4160 23.93 0.11 0.61 0.2860 22.52 0.12 0.24 0.3140 21.99 0.11 0.03 0.5810 23.28 0.09 -0.34 0.4630 23.09 0.07 0.19 0.3410 21.66 0.08 -0.07 0.6710 23.64 0.09 0.37 0.6310 23.41 0.07 -0.06 0.5220 23.29 0.07 -0.08 0.3680 22.04 0.05 -0.05 0.3090 21.87 0.05 0.08 0.5280 23.27 0.09 0.11 0.2680 22.11 0.06 0.09 0.2160 21.85 0.07 0.19 0.6950 23.93 0.11 -0.02 0.2840 21.66 0.06 -0.01 0.5080 22.91 0.06 0.04 0.7810 24.12 0.11 0.00 0.6130 23.40 0.11 -0.08 0.4250 22.47 0.18 0.07 0.2050 20.94 0.03 0.00 0.2110 20.88 0.10 -0.06 0.1590 20.65 0.03 0.00 0.1810 20.34 0.03 0.00 0.6530 22.90 0.25 -0.08 0.1550 20.21 0.14 0.00 0.5620 22.90 0.15 -0.30 0.2490 22.01 0.24 0.30 0.1840 21.01 0.10 0.00 0.7500 0.2400 0.2660 0.1020 0.2660 0.3440 0.4620 0.4530 0.5480 veusz-3.0.1/examples/fit.vsz0000664000175000017500000000300313161413406015450 0ustar jssjss00000000000000# Veusz saved document (version 0.4) # User: jss # Date: Sun, 13 Mar 2005 20:22:08 +0000 ImportString('y,+-',''' 2.051912e+00 1.000000e+00 8.445439e-01 1.000000e+00 3.071220e+00 1.000000e+00 3.570666e-01 1.000000e+00 4.607197e+00 1.000000e+00 5.686059e+00 1.000000e+00 6.768538e+00 1.000000e+00 6.120451e+00 1.000000e+00 8.245063e+00 1.000000e+00 8.996650e+00 1.000000e+00 1.106673e+01 1.000000e+00 1.167119e+01 1.000000e+00 1.352823e+01 1.000000e+00 1.212483e+01 1.000000e+00 1.528200e+01 1.000000e+00 1.613522e+01 1.000000e+00 1.563430e+01 1.000000e+00 1.673132e+01 1.000000e+00 1.914110e+01 1.000000e+00 1.954017e+01 1.000000e+00 ''') ImportString('x',''' 1.000000e+00 2.000000e+00 3.000000e+00 4.000000e+00 5.000000e+00 6.000000e+00 7.000000e+00 8.000000e+00 9.000000e+00 1.000000e+01 1.100000e+01 1.200000e+01 1.300000e+01 1.400000e+01 1.500000e+01 1.600000e+01 1.700000e+01 1.800000e+01 1.900000e+01 2.000000e+01 ''') Add('page', name='page1', autoadd=False) To('page1') Add('graph', name='graph1', autoadd=False) To('graph1') Add('axis', name='x', autoadd=False) To('x') Set('label', 'A wonderful \\emph{x} axis') Set('log', True) To('..') Add('axis', name='y', autoadd=False) To('y') Set('label', 'A dubious \\emph{y} axis') Set('direction', 'vertical') To('..') Add('xy', name='xy1', autoadd=False) To('xy1') Set('key', 'data') Set('PlotLine/hide', True) Set('MarkerFill/color', 'red') To('..') Add('fit', name='fit1', autoadd=False) To('fit1') Set('key', 'fit') To('..') Add('key', name='key1', autoadd=False) To('..') To('..') veusz-3.0.1/examples/dsexpressions.vsz0000664000175000017500000000405213161413406017604 0ustar jssjss00000000000000# Veusz saved document (version 1.17.1) ImportString('x(numeric)',''' 0.000000e+00 4.188790e-01 8.377580e-01 1.256637e+00 1.675516e+00 2.094395e+00 2.513274e+00 2.932153e+00 3.351032e+00 3.769911e+00 4.188790e+00 4.607669e+00 5.026548e+00 5.445427e+00 5.864306e+00 6.283185e+00 ''') ImportString('y(numeric)',''' 0.000000e+00 4.067366e-01 7.431448e-01 9.510565e-01 9.945219e-01 8.660254e-01 5.877853e-01 2.079117e-01 -2.079117e-01 -5.877853e-01 -8.660254e-01 -9.945219e-01 -9.510565e-01 -7.431448e-01 -4.067366e-01 -2.449213e-16 ''') Set('StyleSheet/Font/font', u'Bitstream Vera Serif') Add('page', name='page1', autoadd=False) To('page1') Add('graph', name='graph1', autoadd=False) To('graph1') Add('axis', name='x', autoadd=False) To('x') Set('label', '\\italic{x}') Set('autoRange', u'exact') To('..') Add('axis', name='y', autoadd=False) To('y') Set('label', u'\\italic{y}') Set('direction', 'vertical') To('..') Add('xy', name='xy1', autoadd=False) To('xy1') Set('PlotLine/color', '#3b57d0') Set('MarkerFill/color', '#3b57d0') Set('ErrorBarLine/color', '#3b57d0') To('..') Add('xy', name='xy2', autoadd=False) To('xy2') Set('yData', u'y*1.5') Set('PlotLine/color', '#d7dede') Set('MarkerFill/color', '#d7dede') Set('ErrorBarLine/color', '#d7dede') To('..') Add('xy', name='xy3', autoadd=False) To('xy3') Set('yData', u'(-y*1.5,0.1)') Set('PlotLine/color', '#526c38') Set('MarkerFill/color', '#526c38') Set('ErrorBarLine/color', '#526c38') To('..') Add('xy', name='xy4', autoadd=False) To('xy4') Set('yData', u'(-y, 0.1, -0.2)') Set('PlotLine/color', '#5e136d') Set('MarkerFill/color', '#5e136d') Set('ErrorBarLine/color', '#5e136d') To('..') Add('xy', name='xy5', autoadd=False) To('xy5') Set('xData', u'(x+y,0.1)') Set('yData', u'(-y*1.5,y*0.2)') Set('PlotLine/color', '#a60523') Set('MarkerFill/color', '#a60523') Set('ErrorBarLine/color', '#a60523') To('..') Add('label', name='label1', autoadd=False) To('label1') Set('label', u'Using expressions of datasets') Set('xPos', [0.5]) Set('yPos', [0.98]) Set('alignHorz', u'centre') Set('alignVert', u'top') To('..') To('..') To('..') veusz-3.0.1/examples/example_import_2.dat0000664000175000017500000000531613161413406020073 0ustar jssjss00000000000000# second example file # descriptors can be placed down the file multiple times # to specify sets of data descriptor thisx,+- thisy,+,- 1.0048e-01 2.1160e-01 9.5824e-01 1.3893e-01 -6.0814e-01 1.2665e+00 9.5604e-01 6.7138e-01 4.1650e-01 -6.7098e-01 2.4097e+00 6.2898e-01 2.1296e-01 4.9144e-01 -3.3067e-02 3.9563e+00 4.7805e-01 2.3593e-01 4.9881e-01 -2.7282e-02 4.5674e+00 4.2886e-01 3.8157e-01 5.3305e-01 -3.2969e-01 5.4321e+00 5.1148e-02 1.5812e+00 1.2613e-01 -2.2894e-01 6.9457e+00 8.1255e-01 1.6777e+00 9.8834e-01 -9.7390e-01 7.4897e+00 9.3973e-01 1.7167e+00 4.3308e-01 -3.7178e-01 8.0426e+00 5.6998e-01 1.5577e+00 8.7185e-01 -1.1033e-01 9.9084e+00 9.6405e-01 1.0996e+00 5.9948e-01 -3.3716e-01 1.0963e+01 9.5036e-01 1.7831e+00 3.6308e-03 -1.3141e-01 1.1522e+01 3.2964e-02 1.8577e+00 3.3410e-01 -4.1994e-01 1.2634e+01 2.4651e-01 1.5259e+00 6.8782e-01 -8.0710e-01 1.3764e+01 5.7442e-01 1.6989e+00 6.6327e-01 -4.2052e-01 1.4102e+01 9.0656e-01 1.1045e+00 8.2707e-01 -2.7248e-01 descriptor anotherx,+- anothery,+,- 4.4622e-01 6.4802e-01 1.0884e+00 2.5945e-01 -4.7283e-01 1.2226e+00 3.2900e-01 1.5053e+00 2.8890e-01 -3.6051e-01 2.6112e+00 2.7019e-01 1.6834e+00 7.0039e-01 -7.7591e-01 3.7616e+00 1.5996e-01 1.3153e+00 7.8240e-01 -5.5298e-01 4.2028e+00 5.4426e-01 1.6729e+00 8.0976e-01 -1.2393e-01 5.5345e+00 2.6658e-01 8.9366e-01 1.2642e-01 -8.6600e-02 6.4766e+00 9.2462e-01 1.1606e+00 8.4187e-01 -6.3709e-01 7.1592e+00 7.4514e-01 6.7465e-01 2.9700e-01 -2.9862e-01 8.2514e+00 6.7011e-01 1.3665e+00 2.1944e-01 -2.1589e-01 9.1714e+00 7.5465e-01 1.1409e+00 7.2534e-01 -8.5968e-01 1.0905e+01 4.0861e-02 9.5769e-02 4.7422e-01 -9.2371e-02 1.1232e+01 8.9452e-01 -9.0869e-02 3.2122e-01 -8.0847e-01 1.2988e+01 4.4257e-01 2.5282e-01 5.9169e-02 -5.4564e-01 1.3888e+01 6.8980e-01 -1.0379e-01 4.0765e-01 -3.7785e-01 1.4439e+01 1.9561e-01 3.7001e-01 3.4897e-01 -9.3970e-01 descriptor noisex noisey[1:3] 0.0000e+00 4.2279e-01 6.4775e-01 2.8592e+00 1.0000e+00 9.2479e-01 1.0987e+00 8.5007e-01 2.0000e+00 8.8329e-01 6.8191e-01 5.7917e-01 3.0000e+00 9.5791e-01 1.1615e+00 5.1192e-01 4.0000e+00 1.5129e-01 8.5718e-01 2.3247e+00 5.0000e+00 5.3964e-01 1.5094e+00 7.9621e-01 6.0000e+00 1.8649e-01 1.2638e+00 1.5540e-01 7.0000e+00 8.4299e-01 1.1033e+00 6.9083e-01 8.0000e+00 8.0603e-01 1.5430e+00 1.2785e+00 9.0000e+00 3.9557e-02 1.4083e+00 1.9398e+00 1.0000e+01 6.6562e-02 1.1485e+00 2.7042e+00 1.1000e+01 2.3951e-01 3.8934e-01 9.6851e-01 1.2000e+01 9.2450e-01 7.2653e-01 1.1478e-01 1.3000e+01 5.3937e-01 1.1756e+00 1.6637e+00 1.4000e+01 4.9036e-01 3.5158e-01 2.3081e+00 1.5000e+01 3.0818e-01 1.5758e-01 9.5081e-01 1.6000e+01 1.6127e-01 1.0475e-01 9.0180e-01 1.7000e+01 4.1190e-02 9.0279e-01 2.8207e+00 1.8000e+01 9.4011e-01 1.4106e+00 5.1369e-01 1.9000e+01 6.2362e-01 1.3872e-01 4.2210e-01 veusz-3.0.1/tests/0000775000175000017500000000000013325026667013465 5ustar jssjss00000000000000veusz-3.0.1/tests/selftests/0000775000175000017500000000000013325026667015501 5ustar jssjss00000000000000veusz-3.0.1/tests/selftests/reverseaxis.vsz0000664000175000017500000000152313161413406020573 0ustar jssjss00000000000000# Veusz saved document (version 1.16) # Saved at 2013-01-19T16:49:34.677582 AddImportPath(u'/home/jss/code/veusz-git/veusz/tests/selftests') Add('page', name='page1', autoadd=False) To('page1') Add('grid', name='grid1', autoadd=False) To('grid1') Add('graph', name='graph1', autoadd=False) To('graph1') Add('axis', name='x', autoadd=False) To('x') Set('min', 1.0) Set('max', -1.0) To('..') Add('axis', name='y', autoadd=False) To('y') Set('direction', 'vertical') To('..') Add('function', name='function1', autoadd=False) To('..') Add('graph', name='graph2', autoadd=False) To('graph2') Add('axis', name='x', autoadd=False) To('x') Set('min', 10.0) Set('max', 1.0) Set('log', True) To('..') Add('axis', name='y', autoadd=False) To('y') Set('direction', 'vertical') To('..') Add('function', name='function1', autoadd=False) To('..') To('..') To('..') veusz-3.0.1/tests/selftests/csv_renaming.vsz0000664000175000017500000000102613161413406020704 0ustar jssjss00000000000000# Veusz saved document (version 1.19.1) # Saved at 2013-12-30T15:31:37.518893 ImportFileCSV(u'csv1.csv', linked=True, renames={u'aerg': u'b', u'foo': u'a', u'test': u'c'}) Add('page', name='page1', autoadd=False) To('page1') Add('graph', name='graph1', autoadd=False) To('graph1') Add('axis', name='x', autoadd=False) Add('axis', name='y', autoadd=False) To('y') Set('direction', 'vertical') To('..') Add('xy', name='xy1', autoadd=False) To('xy1') Set('xData', u'a') Set('yData', u'c') Set('labels', u'b') To('..') To('..') To('..') veusz-3.0.1/tests/selftests/autodetect.csv0000664000175000017500000000030313161413406020340 0ustar jssjss00000000000000a,b,c,,d,e (text) 1,01/01/10,hello,,1.00E+001,01/01/10 2,01/02/10,foo,,1.00E+002,02/01/11 3,20/02/10,bar,,1.00E+003,03/02/12 4,15/03/10,xxx,,1.00E+004,01/11/11 5,10/04/10,aaa,,1.00E+003,11/11/11 veusz-3.0.1/tests/selftests/qdp_2d.qdp0000664000175000017500000001211213161413406017342 0ustar jssjss00000000000000@qdp_2d.pco ! 40.4e1 408 412 416 420 424 427 431 434 438 441 444 448 451 454 457 460 463 466 468 471 474 476 479 481- 484 486 488 490 493 495 497 499 501 503 505 507 509 511 513 514 377 381 384 388 392 395 399 403 406 410 413 416 420 423 426 429 432 435 438 441 443 446 449 451 454 456- 459 461 463 466 468 470 472 474 476 478 480 482 484 486 488 350 354 357 361 364 368 371 375 378 382 385 389 392 395 398 401 404 407 410 413 416 419 422 424 427- 429 432 434 437 439 441 444 446 448 450 452 454 457 459 460 462 324 327 331 334 338 341 345 348 351 355 358 362 365 368 371 374 378 381 384 387 390 392 395 398 401- 403 406 408 411 413 416 418 421 423 425 427 429 432 434 436 438 300 302 306 309 312 315 318 322 325 329 332 335 338 342 345 348 351 355 358 361 364 367 370 372 375 378- 381 383 386 389 391 394 396 398 401 403 405 408 410 412 414 277 280 282 285 288 291 294 297 300 304 307 310 313 317 320 323 326 329 333 336 339 342 345 348 351 354- 357 359 362 365 368 370 373 375 378 380 383 385 388 390 392 258 259 262 264 266 269 272 275 278 281 284 287 290 293 297 300 303 306 310 313 316 319 322 325 328- 331 334 337 340 343 346 349 351 354 357 360 362 365 367 370 372 244 245 247 248 251 253 255 258 260 263 266 269 272 275 278 281 284 288 291 294 297 300 303 306 309- 312 315 319 321 324 327 330 333 336 339 341 344 347 350 352 355 234 235 236 238 239 241 243 245 247 250 252 255 258 261 263 266 269 272 275 278 281 284 287 290 293- 296 299 302 305 308 310 313 316 319 322 324 327 330 332 335 337 227 227 227 228 229 230 232 234 236 238 240 242 245 247 250 252 255 258 260 263 266 269 272 274 277- 280 283 286 288 291 294 297 300 302 305 308 310 313 315 318 321 220 220 220 220 221 221 222 224 225 227 229 231 233 235 237 239 242 244 247 249 252 254 257 260 262- 265 268 270 273 276 278 281 284 286 289 292 294 297 299 302 304 216 215 214 214 214 214 214 215 216 217 219 220 222 224 226 228 230 232 234 236 239 241 244 246 249- 251 254 256 259 261 264 266 269 271 274 276 279 281 284 286 289 213 211 210 209 209 208 208 208 209 210 211 212 213 214 216 218 219 221 223 225 227 229 231 234 236 238- 240 243 245 247 250 252 255 257 259 262 264 267 269 271 274 213 210 208 207 205 204 204 204 204 204 204 205 206 207 208 209 210 212 214 215 217 219 221 223 225 227- 229 231 233 235 237 239 242 244 246 248 251 253 255 257 260 214 211 208 206 204 203 202 201 200 200 200 200 200 201 202 202 203 204 206 207 208 210 211 213 215 217- 218 220 222 224 226 228 230 232 234 236 238 241 243 245 247 218 214 211 208 205 203 201 200 199 198 198 197 197 197 197 198 198 199 200 201 202 203 204 206 207 208- 210 212 213 215 217 218 220 222 224 226 228 230 232 234 235 224 219 215 212 209 206 204 202 200 199 198 197 196 196 195 195 196 196 196 197 198 198 199 200 201- 202 204 205 206 208 209 211 212 214 215 217 219 221 222 224 226 232 227 223 219 215 212 209 206 204 202 200 199 198 197 196 196 195 195 195 195 196 196 197 197 198 199- 200 201 202 203 204 205 207 208 209 211 212 214 215 217 218 244 238 233 228 224 220 216 213 211 208 206 204 203 201 200 199 198 198 197 197 197 197 197 197 198- 198 199 199 200 201 202 203 204 205 206 207 208 209 211 212 213 258 251 246 240 236 231 227 224 220 217 215 212 210 208 207 205 204 203 202 202 201 201 200 200 200 200- 201 201 201 202 202 203 204 205 205 206 207 208 209 211 212 274 267 261 255 250 245 241 237 233 230 226 224 221 219 217 215 213 212 211 209 209 208 207 207 206- 206 206 206 206 206 207 207 207 208 208 209 210 211 211 212 213 281 274 268 262 256 251 246 242 238 234 231 228 225 223 220 218 217 215 213 212 211 210 209 209 208 208- 207 207 207 207 207 207 208 208 208 209 209 210 211 211 212 289 282 275 269 263 258 253 248 244 240 237 233 230 227 225 223 221 219 217 216 214 213 212 211 210- 210 209 209 209 209 208 209 209 209 209 209 210 210 211 211 212 298 290 283 277 271 265 260 255 251 247 243 239 236 233 230 228 225 223 221 220 218 217 216 215 214- 213 212 212 211 211 211 210 210 210 211 211 211 211 212 212 213 307 299 292 286 279 273 268 263 258 254 250 246 243 239 236 234 231 229 227 225 223 221 220 219 218 217- 216 215 214 214 214 213 213 213 213 213 213 213 214 214 214 317 309 302 295 288 282 277 271 267 262 258 254 250 246 243 240 238 235 233 231 229 227 225 224 222 221- 220 219 219 218 217 217 217 216 216 216 216 216 216 216 216 328 320 312 305 298 292 286 281 276 271 266 262 258 254 251 248 245 242 240 237 235 233 231 230 228 227- 226 225 224 223 222 221 221 220 220 220 220 219 219 219 220 339 331 323 316 309 303 296 291 285 280 276 271 267 263 260 256 253 250 247 245 242 240 238 236 235- 233 232 231 229 228 228 227 226 225 225 224 224 224 224 224 224 352 343 335 328 321 314 308 302 296 291 286 281 277 273 269 265 262 259 256 253 251 248 246 244 242 241- 239 238 236 235 234 233 232 231 231 230 230 229 229 229 228 365 356 348 340 333 326 319 313 307 302 297 292 287 283 279 275 272 269 265 262 260 257 255 253 251 249- 247 245 244 243 241 240 239 238 237 237 236 235 235 235 234 378 369 361 353 346 339 332 326 320 314 309 304 299 294 290 286 283 279 276 273 270 267 265 262 260 258- 256 254 253 251 250 248 247 246 245 244 243 243 242 241 241 veusz-3.0.1/tests/selftests/noheader.csv0000664000175000017500000000010613161413406017765 0ustar jssjss000000000000001,hello,10:20:30 2,5,20:30:10 3,2.2, 4,2,04:20:10.1010 5,foo,10:10:10 veusz-3.0.1/tests/selftests/csv1.csv0000664000175000017500000000015513161413406017060 0ustar jssjss00000000000000"test","foo","+-","aerg" 1,7,0.1,"fdfd" 2,4,0.1,"dsfh" 3,3,0.1,"bgtr" 4,2,0.1,"RR" 5,3,0.2,"ZZ" 6,4,0.2,"AA" veusz-3.0.1/tests/selftests/custom.vsz0000664000175000017500000000156713161413406017555 0ustar jssjss00000000000000# Veusz saved document (version 1.13) # Saved at 2011-09-06T18:47:34.442485 AddImportPath(u'/home/jss/code/veusz/veusz/tests/selftests') AddCustom('constant', u'myconst', u'10') AddCustom('constant', 'myconst', '41', mode='replace') AddCustom('constant', 'myconst', '42', mode='append') AddCustom('function', 'myfunc(x)', 'myconst*x**2') AddCustom(u'import', u'numpy.linalg', u'inv') Add('page', name='page1', autoadd=False) To('page1') Add('graph', name='graph1', autoadd=False) To('graph1') Add('axis', name='x', autoadd=False) Add('axis', name='y', autoadd=False) To('y') Set('direction', 'vertical') To('..') Add('function', name='function1', autoadd=False) To('function1') Set('function', u'inv([[1,0],[0,1]])[0,0] * x') To('..') Add('function', name='function2', autoadd=False) To('function2') Set('function', u'myfunc(x)') Set('Line/color', u'red') To('..') To('..') To('..') veusz-3.0.1/tests/selftests/csv_missing.csv0000664000175000017500000000013413161413406020525 0ustar jssjss00000000000000a,b,c,d,e (numeric) ,,,6, 2,,00:02:33.40,,nan 3,hello,01:32:44,4,3 4,foo,01:01:01,8,invalid veusz-3.0.1/tests/selftests/hdftest.hdf50000664000175000017500000004712013161413406017703 0ustar jssjss00000000000000HDF  PN`TREEX:pHEAP` (*HhTREE`HEAPX abc8SNOD`0P8x!# HhPDX(* @`P@ںRx!#  $1@Q?d%%@u @u溆a@ ~@@+!@*{IJ#@Tl&@??@?ɿ@?ɿ??@? @??@@@@@@@@@@@@ @@@ @"@$@ @"@$@&@(@ @"@$@&@(@$@&@(@*@,@(@*@,@.@0@,@.@0@1@2@0@1@2@3@4@0@1@2@3@4@2@3@4@5@6@4@5@6@7@8@6@7@8@9@:@8@9@:@;@<@8@9@:@;@<@:@;@<@=@>@<@=@>@?@@@>@?@@@@@A@@@@@A@A@B@@@@@A@A@B@A@A@B@B@C@B@B@C@C@D@C@C@D@D@E@D@D@E@E@F@2001-01-012002-02-022003-03-03T12:12:00 @@abcSNOD( @PHںR  ?@4 4 PںR 0 vsz_nameb (+-)@( ?@4 4P HA0R @ vsz_twod_as_oned  ( ?@4 4 0ۺRh 8 ?@4 4 `ܺR 0 vsz_slice1,:4,:!h8 ?@4 4`ܺRX???@??@@@@@@ @@@ @@@@@@@@@@@@ @@@ @@@@@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@ @@@ @!@"@ @!@"@#@$@@@@@ @@@ @!@"@ @!@"@#@$@"@#@$@%@&@$@%@&@'@(@ @!@"@#@$@"@#@$@%@&@$@%@&@'@(@&@'@(@)@*@(@)@*@+@,@groupnd1d_2threed_slice_1threed_slice_2dategrptextcompoundtwod_as_1d0 ` vsz_range@TREE %HEAPX #txtdatenumdateidxdate8 9zݺR 8vsz_convert_datetimeisoHSNODx'h&$ ?@4 4 ܺRx@ܺRTREE0,HEAPX *xytext8@7ݺRSNOD. +x-@1>ݺRIIݺR7Xxval  yval  text7HR??hi?33?ho33??silver 0vsz_slice_yval::-1`8:TREEA:pHEAPX:grpHSNOD0@p88`8:(?HATREEC:pHEAPX(hAtwod_as_1d_1twod_as_1d_20SNOD?(?HASNOD8 ?@4 4PLhXX?@@@@@ @$@&@(@*@.@0@1@2@4@5@6@7@9@:@;@<@>@?@@@@@A@B@B@C@D@D@E@E@F@G@G@H@I@I@J@J@K@L@L@M@N@N@O@O@@P@P@P@Q@Q@Q@R@@R@R@S@@S@S@veusz-3.0.1/tests/selftests/hdf5_nd.vsz0000664000175000017500000000217713161413406017550 0ustar jssjss00000000000000# Veusz saved document (version 1.24) # Saved at 2016-11-20T10:17:54.552211 ImportFileHDF5(u'hdftest.hdf5', [u'/nd'], linked=True) Add('page', name='page1', autoadd=False) To('page1') Add('grid', name='grid1', autoadd=False) To('grid1') Set('leftMargin', u'0.2cm') Set('bottomMargin', u'0.2cm') Add('graph', name='graph1', autoadd=False) To('graph1') Add('axis', name='x', autoadd=False) Add('axis', name='y', autoadd=False) To('y') Set('direction', 'vertical') To('..') Add('xy', name='xy1', autoadd=False) To('xy1') Set('xData', u'nd[0,1,:]') Set('yData', u'nd[0,2,:]') To('..') Add('xy', name='xy2', autoadd=False) To('xy2') Set('xData', u'nd[1,1,:]') Set('yData', u'nd[:,2,0]') To('..') Add('xy', name='xy3', autoadd=False) To('xy3') Set('xData', u'ravel(nd[:,:,0])') Set('yData', u'ravel(nd[2:,:])') To('..') To('..') Add('graph', name='graph2', autoadd=False) To('graph2') Add('axis', name='x', autoadd=False) Add('axis', name='y', autoadd=False) To('y') Set('direction', 'vertical') To('..') Add('image', name='image1', autoadd=False) To('image1') Set('data', u'nd[:,:,0]') Set('min', 0.0) Set('max', 70.0) To('..') To('..') To('..') To('..') veusz-3.0.1/tests/selftests/testdat.npy0000664000175000017500000000036013161413406017665 0ustar jssjss00000000000000NUMPYF{'descr': '@Zd;ߏ @xf$@veusz-3.0.1/tests/selftests/gnuplot_2d.vsz0000664000175000017500000000105513161413406020310 0ustar jssjss00000000000000# Veusz saved document (version 1.23) # Saved at 2015-06-12T19:04:07.152029 ImportFilePlugin(u'Gnuplot 2D data import plugin', u'gnuplot_2d.dat', linked=True, name=u'name') Add('page', name='page1', autoadd=False) To('page1') Add('graph', name='graph1', autoadd=False) To('graph1') Add('axis', name='x', autoadd=False) Add('axis', name='y', autoadd=False) To('y') Set('direction', 'vertical') To('..') Add('contour', name='contour1', autoadd=False) To('contour1') Set('data', u'name') Set('numLevels', 4) Set('scaling', u'sqrt') To('..') To('..') To('..') veusz-3.0.1/tests/selftests/testcontour.vsz0000664000175000017500000000436313161413406020631 0ustar jssjss00000000000000# Veusz saved document (version 0.99.0) # User: jss # Date: Fri, 21 Sep 2007 19:00:30 +0000 # A test to make sure 2d arrays are working with different dimensions # in x and y ImportString2D(u'test', ''' xrange 0.000000e+00 1.000000e+01 yrange 0.000000e+00 1.200000e+01 0.000000e+00 0.000000e+00 0.000000e+00 0.000000e+00 0.000000e+00 0.000000e+00 0.000000e+00 0.000000e+00 0.000000e+00 0.000000e+00 0.000000e+00 1.000000e+00 2.000000e+00 3.000000e+00 4.000000e+00 5.000000e+00 6.000000e+00 7.000000e+00 8.000000e+00 9.000000e+00 0.000000e+00 2.000000e+00 4.000000e+00 6.000000e+00 8.000000e+00 1.000000e+01 1.200000e+01 1.400000e+01 1.600000e+01 1.800000e+01 0.000000e+00 3.000000e+00 6.000000e+00 9.000000e+00 1.200000e+01 1.500000e+01 1.800000e+01 2.100000e+01 2.400000e+01 2.700000e+01 0.000000e+00 4.000000e+00 8.000000e+00 1.200000e+01 1.600000e+01 2.000000e+01 2.400000e+01 2.800000e+01 3.200000e+01 3.600000e+01 0.000000e+00 5.000000e+00 1.000000e+01 1.500000e+01 2.000000e+01 2.500000e+01 3.000000e+01 3.500000e+01 4.000000e+01 4.500000e+01 0.000000e+00 6.000000e+00 1.200000e+01 1.800000e+01 2.400000e+01 3.000000e+01 3.600000e+01 4.200000e+01 4.800000e+01 5.400000e+01 0.000000e+00 7.000000e+00 1.400000e+01 2.100000e+01 2.800000e+01 3.500000e+01 4.200000e+01 4.900000e+01 5.600000e+01 6.300000e+01 0.000000e+00 8.000000e+00 1.600000e+01 2.400000e+01 3.200000e+01 4.000000e+01 4.800000e+01 5.600000e+01 6.400000e+01 7.200000e+01 0.000000e+00 9.000000e+00 1.800000e+01 2.700000e+01 3.600000e+01 4.500000e+01 5.400000e+01 6.300000e+01 7.200000e+01 8.100000e+01 0.000000e+00 1.000000e+01 2.000000e+01 3.000000e+01 4.000000e+01 5.000000e+01 6.000000e+01 7.000000e+01 8.000000e+01 9.000000e+01 0.000000e+00 1.100000e+01 2.200000e+01 3.300000e+01 4.400000e+01 5.500000e+01 6.600000e+01 7.700000e+01 8.800000e+01 9.900000e+01 ''') Add('page', name='page1', autoadd=False) To('page1') Add('graph', name='graph1', autoadd=False) To('graph1') Add('axis', name='x', autoadd=False) Add('axis', name='y', autoadd=False) To('y') Set('direction', 'vertical') To('..') Add('contour', name='contour1', autoadd=False) To('contour1') Set('data', u'test') To('..') Add('image', name='image1', autoadd=False) To('image1') Set('data', u'test') Set('colorMap', u'spectrum2') To('..') To('..') To('..') veusz-3.0.1/tests/selftests/autodetect.vsz0000664000175000017500000000226413161413406020377 0ustar jssjss00000000000000# Veusz saved document (version 1.13) # Saved at 2011-10-29T16:58:13.552813 ImportFileCSV(u'autodetect.csv', linked=True, blanksaredata=True, dateformat=u'DD/MM/YY| |hh:mm:ss', headermode='1st', numericlocale='en_GB') ImportFileCSV(u'autodetect.csv', linked=True, blanksaredata=True, dateformat=u'DD/MM/YY| |hh:mm:ss', dsprefix=u'p_', headerignore=1, headermode='1st', numericlocale='en_GB', rowsignore=2) Add('page', name='page1', autoadd=False) To('page1') Add('grid', name='grid1', autoadd=False) To('grid1') Add('graph', name='graph1', autoadd=False) To('graph1') Add('axis', name='x', autoadd=False) Add('axis', name='y', autoadd=False) To('y') Set('mode', u'datetime') Set('direction', 'vertical') To('..') Add('xy', name='xy1', autoadd=False) To('xy1') Set('xData', u'a') Set('yData', u'b') Set('labels', u'c') To('..') To('..') Add('graph', name='graph2', autoadd=False) To('graph2') Add('axis', name='x', autoadd=False) To('x') Set('log', True) To('..') Add('axis', name='y', autoadd=False) To('y') Set('direction', 'vertical') To('..') Add('xy', name='xy2', autoadd=False) To('xy2') Set('xData', u'p_1.00E+002') Set('yData', u'p_2') Set('labels', u'p_foo') To('..') To('..') To('..') To('..') veusz-3.0.1/tests/selftests/numberstotext.vsz0000664000175000017500000000171413161413406021160 0ustar jssjss00000000000000# Veusz saved document (version 1.18) # Saved at 2013-10-03T14:25:30.785711 AddImportPath(u'/home/jss/code/veusz-git/veusz/tests/selftests') DatasetPlugin('NumbersToText', {'ds_out': u'text', 'ds_in': u'x', 'format': u'%Vg'}) ImportString(u'x(numeric)',''' 1.000000e+00 2.000000e+05 2.000000e-05 ''') ImportString(u'y(numeric)',''' 1.000000e+00 2.000000e+00 3.000000e+00 ''') Add('page', name='page1', autoadd=False) To('page1') Add('graph', name='graph1', autoadd=False) To('graph1') Add('axis', name='x', autoadd=False) To('x') Set('log', True) To('..') Add('axis', name='y', autoadd=False) To('y') Set('log', True) Set('direction', 'vertical') To('..') Add('xy', name='xy1', autoadd=False) To('xy1') Set('labels', u'text') Set('PlotLine/hide', True) Set('Label/angle', 90.0) To('..') Add('xy', name='xy2', autoadd=False) To('xy2') Set('xData', u'y') Set('yData', u'x') Set('labels', u'y') Set('PlotLine/hide', True) Set('Label/angle', 90.0) To('..') To('..') To('..') veusz-3.0.1/tests/selftests/fits_test.vsz0000664000175000017500000000220213161413406020232 0ustar jssjss00000000000000# Veusz saved document (version 1.26.1) # Saved at 2017-06-04T08:23:25.425218 ImportFileFITS(u'fits_test.fits', ['/primary', '/hdu1'], linked=True, namemap={'/hdu1/v_mag': u'mag', '/primary': u'img'}) Add('page', name='page1', autoadd=False) To('page1') Add('grid', name='grid1', autoadd=False) To('grid1') Set('leftMargin', u'0cm') Set('rightMargin', u'0cm') Set('topMargin', u'0cm') Set('bottomMargin', u'0cm') Add('graph', name='graph1', autoadd=False) To('graph1') Add('axis', name='x', autoadd=False) To('x') Set('min', 0.0) Set('max', 4.0) To('..') Add('axis', name='y', autoadd=False) To('y') Set('min', 10.0) Set('max', 16.0) Set('direction', 'vertical') To('..') Add('xy', name='xy1', autoadd=False) To('xy1') Set('xData', []) Set('yData', u'mag') Set('labels', u'target') To('..') To('..') Add('graph', name='graph2', autoadd=False) To('graph2') Add('axis', name='x', autoadd=False) Add('axis', name='y', autoadd=False) To('y') Set('direction', 'vertical') To('..') Add('image', name='image1', autoadd=False) To('image1') Set('data', u'img') Set('min', -0.1) Set('max', 12.1) Set('colorMap', u'grey-step6') To('..') To('..') To('..') To('..') veusz-3.0.1/tests/selftests/blockeddata.dat0000664000175000017500000000003613161413406020414 0ustar jssjss000000000000001 2 4 5 6 7 8 9 10 2 1 3 3 6 veusz-3.0.1/tests/selftests/reversed_broken_axis.vsz0000664000175000017500000000331413161413406022436 0ustar jssjss00000000000000# Veusz saved document (version 1.17.1) # Saved at 2013-06-01T09:15:46.507768 AddImportPath(u'/home/jss/code/veusz-git/veusz') Set('StyleSheet/Line/width', u'0.25pt') Set('StyleSheet/Font/font', u'Simplex Enhanced') Set('StyleSheet/Font/size', u'10pt') Set('StyleSheet/grid/leftMargin', u'0.2cm') Set('StyleSheet/grid/bottomMargin', u'0.2cm') Add('page', name='page1', autoadd=False) To('page1') Set('width', u'20cm') Set('height', u'7cm') Add('grid', name='grid1', autoadd=False) To('grid1') Set('columns', 4) Add('graph', name='graph1', autoadd=False) To('graph1') Add('axis', name='x', autoadd=False) Add('axis-broken', name=u'a', autoadd=False) To(u'a') Set('min', 0.0) Set('max', 10.0) Set('breakPoints', [1.0, 3.0, 4.0, 7.0]) Set('direction', u'vertical') To('..') Add('function', name='function1', autoadd=False) To('function1') Set('function', u'x*10') Set('yAxis', u'a') To('..') To('..') Add('graph', name='graph2', autoadd=False) To('graph2') Add('axis', name='x', autoadd=False) Add('axis-broken', name=u'b', autoadd=False) To(u'b') Set('min', 10.0) Set('max', 0.0) Set('breakPoints', [1.0, 3.0, 4.0, 7.0]) Set('direction', u'vertical') To('..') Add('function', name='function2', autoadd=False) To('function2') Set('function', u'x*10') Set('yAxis', u'b') To('..') To('..') Add('graph', name='graph3', autoadd=False) To('graph3') Add('axis', name='x', autoadd=False) To('x') Set('min', 1.0) Set('max', 0.0) To('..') Add('axis-broken', name=u'c', autoadd=False) To(u'c') Set('min', 10.0) Set('max', 0.0) Set('breakPoints', [7.0, 4.0, 3.0, 1.0]) Set('direction', u'vertical') To('..') Add('function', name='function1', autoadd=False) To('function1') Set('function', u'x*10') Set('yAxis', u'c') To('..') To('..') To('..') To('..') veusz-3.0.1/tests/selftests/noheader.vsz0000664000175000017500000000112313161413406020014 0ustar jssjss00000000000000# Veusz saved document (version 1.13) # Saved at 2011-10-29T19:22:19.911232 ImportFileCSV(u'noheader.csv', linked=True, blanksaredata=True, headermode='none', numericlocale='en_GB') Add('page', name='page1', autoadd=False) To('page1') Add('graph', name='graph1', autoadd=False) To('graph1') Add('axis', name='x', autoadd=False) To('x') Set('mode', u'datetime') To('..') Add('axis', name='y', autoadd=False) To('y') Set('direction', 'vertical') To('..') Add('xy', name='xy1', autoadd=False) To('xy1') Set('xData', u'col3') Set('yData', u'col1') Set('labels', u'col2') To('..') To('..') To('..') veusz-3.0.1/tests/selftests/rangeds.vsz0000664000175000017500000000173213161413406017660 0ustar jssjss00000000000000# Veusz saved document (version 1.11) # User: jss # Date: Fri, 10 Jun 2011 19:21:55 +0000 AddImportPath(u'/home/jss/code/veusz/veusz/tests/selftests') SetDataExpression(u'para', u't**2', parametric=(0.0, 1.0, 10), linked=True) SetDataRange(u'x', 10, (0.0, 10.0), linked=True) SetDataRange(u'x2', 10, (2.0, 5.0), poserr=(0.1, 0.1), negerr=(-0.1, -0.1), linked=True) SetDataExpression(u'y', u'(para**2 + x**3)/100', symerr=u'2', linked=True) Add('page', name='page1', autoadd=False) To('page1') Add('graph', name='graph1', autoadd=False) To('graph1') Add('axis', name='x', autoadd=False) Add('axis', name='y', autoadd=False) To('y') Set('direction', 'vertical') To('..') Add('xy', name='xy1', autoadd=False) To('xy1') Set('yData', u'para') To('..') Add('xy', name='xy2', autoadd=False) To('xy2') Set('marker', u'plus') Set('errorStyle', u'linevertbar') To('..') Add('xy', name='xy3', autoadd=False) To('xy3') Set('xData', u'x2') Set('errorStyle', u'fillvert') To('..') To('..') To('..') veusz-3.0.1/tests/selftests/fits_wcs1.vsz0000664000175000017500000000132213161413406020132 0ustar jssjss00000000000000# Veusz saved document (version 1.26.1) # Saved at 2017-06-04T09:01:38.276617 ImportFileFITS(u'fits_test.fits', ['/wcstest42'], linked=True) Set('colorTheme', 'default1') Add('page', name='page1', autoadd=False) To('page1') Add('graph', name='graph1', autoadd=False) To('graph1') Add('axis', name='x', autoadd=False) To('x') Set('autoRange', u'exact') To('..') Add('axis', name='y', autoadd=False) To('y') Set('autoRange', u'exact') Set('direction', 'vertical') To('..') Add('xy', name='xy1', autoadd=False) To('xy1') Set('xData', [3.5, 4.5]) Set('yData', [20.0, 21.5]) To('..') Add('image', name='image1', autoadd=False) To('image1') Set('data', u'wcstest42') Set('min', -2.0) Set('max', 2.0) To('..') To('..') To('..') veusz-3.0.1/tests/selftests/qdp_1d.vsz0000664000175000017500000000155513161413406017410 0ustar jssjss00000000000000# Veusz saved document (version 1.14) # Saved at 2011-12-10T15:40:10.998898 ImportFilePlugin(u'QDP import', u'qdp_1d_2.qdp', linked=True, names=(u'a', u'b')) ImportFilePlugin(u'QDP import', u'qdp_1d_1.qdp', linked=True, names=('',)) Add('page', name='page1', autoadd=False) To('page1') Add('graph', name='graph1', autoadd=False) To('graph1') Add('axis', name='x', autoadd=False) Add('axis', name='y', autoadd=False) To('y') Set('direction', 'vertical') To('..') Add('xy', name='xy1', autoadd=False) To('xy1') Set('xData', u'vec1') Set('yData', u'vec2') To('..') Add('xy', name='xy2', autoadd=False) To('xy2') Set('xData', u'vec1') Set('yData', u'vec3') To('..') Add('xy', name='xy3', autoadd=False) To('xy3') Set('xData', u'a_1') Set('yData', u'b_1') To('..') Add('xy', name='xy4', autoadd=False) To('xy4') Set('xData', u'a_2') Set('yData', u'b_2') To('..') To('..') To('..') veusz-3.0.1/tests/selftests/twod_xyzexpr.vsz0000664000175000017500000000126113161413406021020 0ustar jssjss00000000000000# Veusz saved document (version 1.25) # Saved at 2016-12-19T09:47:28.291928 SetData2DExpressionXYZ(u'twodx', u'xds*1', u'yds + 0', u'xds+yds*2', linked=True) ImportString(u'xds(numeric)',''' 0.000000e+00 0.000000e+00 1.000000e+00 1.000000e+00 ''') ImportString(u'yds(numeric)',''' 0.000000e+00 1.000000e+00 0.000000e+00 1.000000e+00 ''') Add('page', name='page1', autoadd=False) To('page1') Add('graph', name='graph1', autoadd=False) To('graph1') Add('axis', name='x', autoadd=False) Add('axis', name='y', autoadd=False) To('y') Set('direction', 'vertical') To('..') Add('xy', name='xy1', autoadd=False) To('xy1') Set('xData', []) Set('yData', u'ravel(twodx)') To('..') To('..') To('..') veusz-3.0.1/tests/selftests/label_functions.vsz0000664000175000017500000000207213161413406021402 0ustar jssjss00000000000000# Veusz saved document (version 1.25.1) # Saved at 2016-12-20T12:31:21.275724 AddCustom('constant', u'Crate', u'42.') SetDataRange(u'CumLoss', 100, (1.0, 10.0), linked=True) Add('page', name='page1', autoadd=False) To('page1') Add('graph', name='graph1', autoadd=False) To('graph1') Add('axis', name='x', autoadd=False) Add('axis', name='y', autoadd=False) To('y') Set('direction', 'vertical') To('..') Add('label', name='label1', autoadd=False) To('label1') Set('label', u"Loss: %{{ round(100*max(DATA('CumLoss'))/Crate, 0) }}%% vs %{{Crate}}% mAh/g") Set('xPos', [0.101]) Set('yPos', [0.44]) To('..') Add('label', name='label2', autoadd=False) To('label2') Set('label', u"Escaped text: %{{ ESCAPE(r'\\\\2 % ^ _ [] {}') }}%") Set('xPos', [0.241]) Set('yPos', [0.578]) To('..') Add('label', name='label3', autoadd=False) To('label3') Set('label', u'<<%{{ BASENAME() }}%>> is basename') Set('xPos', [0.526]) Set('yPos', [0.637]) To('..') Add('label', name='label4', autoadd=False) To('label4') Set('label', u"Page width: %{{ SETTING('/page1/width') }}%") To('..') To('..') To('..') veusz-3.0.1/tests/selftests/blockeddata.vsz0000664000175000017500000000113513161413406020467 0ustar jssjss00000000000000# Veusz saved document (version 1.13) # Saved at 2011-11-06T14:18:14.450714 ImportFile(u'blockeddata.dat', u'x y', linked=True, ignoretext=True, useblocks=True) Add('page', name='page1', autoadd=False) To('page1') Add('graph', name='graph1', autoadd=False) To('graph1') Add('axis', name='x', autoadd=False) Add('axis', name='y', autoadd=False) To('y') Set('direction', 'vertical') To('..') Add('xy', name='xy1', autoadd=False) To('xy1') Set('xData', u'x_1') Set('yData', u'y_1') To('..') Add('xy', name='xy2', autoadd=False) To('xy2') Set('xData', u'x_2') Set('yData', u'y_2') To('..') To('..') To('..') veusz-3.0.1/tests/selftests/hdf5_group.vsz0000664000175000017500000000104513161413406020274 0ustar jssjss00000000000000# Veusz saved document (version 1.19.1) # Saved at 2013-12-25T13:30:42.256782 ImportFileHDF5(u'hdftest.hdf5', [u'group'], namemap={u'/group/a': u'z'}, linked=True) Set('StyleSheet/axis/autoRange', u'exact') Add('page', name='page1', autoadd=False) To('page1') Add('graph', name='graph1', autoadd=False) To('graph1') Add('axis', name='x', autoadd=False) Add('axis', name='y', autoadd=False) To('y') Set('direction', 'vertical') To('..') Add('xy', name='xy1', autoadd=False) To('xy1') Set('xData', u'z') Set('yData', u'b') To('..') To('..') To('..') veusz-3.0.1/tests/selftests/hdf5_date.vsz0000664000175000017500000000215013161413406020053 0ustar jssjss00000000000000# Veusz saved document (version 1.19.1) # Saved at 2013-12-25T13:38:10.275226 ImportFileHDF5(u'hdftest.hdf5', [u'dategrp'], convert_datetime={u'/dategrp/numdate': 'veusz'}, linked=True) Add('page', name='page1', autoadd=False) To('page1') Add('grid', name='grid1', autoadd=False) To('grid1') Set('columns', 1) Add('axis', name='x', autoadd=False) Add('graph', name='graph1', autoadd=False) To('graph1') Set('leftMargin', '0cm') Set('rightMargin', '0cm') Set('topMargin', '0cm') Set('bottomMargin', '0cm') Add('axis', name='y', autoadd=False) To('y') Set('mode', u'datetime') Set('direction', 'vertical') To('..') Add('xy', name='xy1', autoadd=False) To('xy1') Set('xData', u'idxdate') Set('yData', u'numdate') To('..') To('..') Add('graph', name='graph2', autoadd=False) To('graph2') Set('leftMargin', '0cm') Set('rightMargin', '0cm') Set('topMargin', '0cm') Set('bottomMargin', '0cm') Add('axis', name='y', autoadd=False) To('y') Set('mode', u'datetime') Set('direction', 'vertical') To('..') Add('xy', name='xy2', autoadd=False) To('xy2') Set('xData', u'idxdate') Set('yData', u'txtdate') To('..') To('..') To('..') To('..') veusz-3.0.1/tests/selftests/testcsverr.csv0000664000175000017500000000034713161413406020413 0ustar jssjss00000000000000"test","test+","test-","foo","+-","foo2ððð","+","-" 1,0.1,2,3,0.3,6,0.6,-0.1 2,0.2,3,3,0.2,7,0.8,-0.3 3,0.3,4,4,0.3,4,0.2,-0.4 4,0.2,5,5,0.2,2,0.1,-0.1 ,,,"a","b","+",,"c" ,,,5,4,0.1,,9 ,,,6,4,0.3,,8 ,,,6,3,0.2,,4 ,,,5,3,0.1,,1 veusz-3.0.1/tests/selftests/testdat.npz0000664000175000017500000000233613161413406017673 0ustar jssjss00000000000000PK[)?\Ca.npyNUMPYF{'descr': 'EB?UHI??oB!?Db9?1%(@Jvl @5$A@)$@PK[)?1l@@d.npyNUMPYF{'descr': '@(>@@?ݳ>1\@(>] @@>L@g?2I@@>d.AJ?s@@q>zAD?B@@)S>vdAs?#A@5>A?@A@@>Adf@ XTENSION= 'IMAGE ' / Image extension BITPIX = -64 / array data type NAXIS = 2 / number of array dimensions NAXIS1 = 8 NAXIS2 = 8 PCOUNT = 0 / number of parameters GCOUNT = 1 / number of groups CDELT1 = 0.25 CDELT2 = 0.5 CRPIX1 = 3 CRPIX2 = 4 CRVAL2 = 20 CRVAL1 = 3.5 EXTNAME = 'WCSTEST ' EXTVER = 42 CTYPE1 = 'LINEAR ' CTYPE2 = 'LINEAR ' END /&տ$3?uK\CBry?fܺn?9 ? )?dP=h[?CWE!'?w1 ?iman0{^8uŒ '9@?FIJeT,b?7hljP?1T?9\?h懲*@%+SW*;V`?, 0zh/$Z!d?y|?LTR]I\2?eN݅W?6HPU?OUKCg؉/*?ǩ]:? Z??哑?nb6ܿ>dveusz-3.0.1/tests/selftests/csv_locale.vsz0000664000175000017500000000144313161413406020346 0ustar jssjss00000000000000# Veusz saved document (version 1.13) # Saved at 2011-10-29T17:11:42.391345 ImportFileCSV(u'csv_locale.csv', linked=True, delimiter='\t', dsprefix=u'a_') ImportFileCSV(u'csv_locale.csv', linked=True, dateformat=u'DD/MM/YY| |hh:mm:ss', delimiter='\t', dsprefix=u'b_', numericlocale='de_DE') Add('page', name='page1', autoadd=False) To('page1') Add('graph', name='graph1', autoadd=False) To('graph1') Add('axis', name='x', autoadd=False) Add('axis', name='y', autoadd=False) To('y') Set('mode', u'datetime') Set('direction', 'vertical') To('..') Add('xy', name='xy1', autoadd=False) To('xy1') Set('xData', u'a_b') Set('yData', u'a_d') Set('labels', u'a_a') To('..') Add('xy', name='xy2', autoadd=False) To('xy2') Set('xData', u'b_a') Set('yData', u'b_c') Set('labels', u'b_d') To('..') To('..') To('..') veusz-3.0.1/tests/selftests/testcsverr.vsz0000664000175000017500000000267413161413406020447 0ustar jssjss00000000000000# Veusz saved document (version 1.12.99) # Saved at 2011-08-14T14:43:33.721297 AddImportPath(u'/home/jss/code/veusz-git/veusz') ImportFileCSV(u'testcsverr.csv', linked=True, blanksaredata=True) Add('page', name='page1', autoadd=False) To('page1') Add('graph', name='graph1', autoadd=False) To('graph1') Add('axis', name='x', autoadd=False) Add('axis', name='y', autoadd=False) To('y') Set('direction', 'vertical') To('..') Add('xy', name=u'footest', autoadd=False) To(u'footest') Set('xData', u'foo') Set('yData', u'test') To('..') Add('xy', name=u'foo2foo', autoadd=False) To(u'foo2foo') Set('xData', u'foo2\xf0\xf0\xf0') Set('yData', u'foo') Set('marker', u'diamond') Set('MarkerFill/color', u'red') To('..') Add('xy', name=u'testplusminus', autoadd=False) To(u'testplusminus') Set('xData', u'test+') Set('yData', u'test-') Set('marker', u'lineplus') Set('MarkerFill/color', u'blue') To('..') Add('xy', name=u'testplustest', autoadd=False) To(u'testplustest') Set('xData', u'test+') Set('yData', u'test') Set('marker', u'barvert') Set('MarkerFill/color', u'cyan') To('..') Add('xy', name=u'ab', autoadd=False) To(u'ab') Set('xData', u'a') Set('yData', u'b') Set('marker', u'pentagon') Set('errorStyle', u'barends') Set('MarkerFill/color', u'darkred') To('..') Add('xy', name=u'ac', autoadd=False) To(u'ac') Set('xData', u'c') Set('yData', u'a') Set('PlotLine/steps', u'centre') Set('PlotLine/color', u'red') Set('PlotLine/width', u'1pt') To('..') To('..') To('..') veusz-3.0.1/tests/selftests/1dto2d.vsz0000664000175000017500000000146313161413406017333 0ustar jssjss00000000000000# Veusz saved document (version 1.12.99) # Saved at 2011-08-16T14:20:29.340311 xv = [] yv = [] zv = [] for x in range(10): for y in range(10): z = sqrt((x-5.)**2 + (y-5.)**2) xv.append(x) yv.append(y) zv.append( round(z, 3) ) SetData("x", xv) SetData("y", yv) SetData("z", zv) SetData2DExpressionXYZ(u'data2d', u'x', u'y', u'z', linked=True) Add('page', name='page1', autoadd=False) To('page1') Add('graph', name='graph1', autoadd=False) To('graph1') Add('axis', name='x', autoadd=False) To('x') Set('autoRange', 'exact') To('..') Add('axis', name='y', autoadd=False) To('y') Set('autoRange', 'exact') Set('direction', 'vertical') To('..') Add('contour', name='contour1', autoadd=False) To('contour1') Set('data', u'data2d') Set('SubLines/hide', False) To('..') To('..') To('..') veusz-3.0.1/tests/selftests/customcolormap.vsz0000664000175000017500000000134313161413406021302 0ustar jssjss00000000000000# Veusz saved document (version 1.15.99) # Saved at 2012-06-10T09:45:52.827597 AddCustom('colormap', 'xyzzy', ((0,0,0,255), (255,255,0), (0,0,0))) SetData2DXYFunc(u'img', (0.0, 1.0, 0.1), (0.0, 1.0, 0.1), u'x*y', linked=True) Add('page', name='page1', autoadd=False) To('page1') Add('graph', name='graph1', autoadd=False) To('graph1') Add('axis', name='x', autoadd=False) Add('axis', name='y', autoadd=False) To('y') Set('direction', 'vertical') To('..') Add('colorbar', name='colorbar1', autoadd=False) To('colorbar1') Set('widgetName', u'image1') Set('horzPosn', u'centre') Set('vertPosn', u'top') To('..') Add('image', name='image1', autoadd=False) To('image1') Set('data', u'img') Set('colorMap', u'xyzzy') To('..') To('..') To('..') veusz-3.0.1/tests/selftests/polar_units.vsz0000664000175000017500000000305613262170026020575 0ustar jssjss00000000000000# Veusz saved document (version 2.2.2) # Saved at 2018-04-07T16:21:10.124292 AddImportPath(u'/home/jss/code/veusz') Set('colorTheme', 'default-latest') Add('page', name='page1', autoadd=False) To('page1') Add('grid', name='grid1', autoadd=False) To('grid1') Set('leftMargin', u'0cm') Set('rightMargin', u'0cm') Set('topMargin', u'0cm') Set('bottomMargin', u'0cm') Add('polar', name=u'frac', autoadd=False) To(u'frac') Set('units', u'fractions') Set('SpokeLine/number', 10) Add('nonorthpoint', name='nonorthpoint1', autoadd=False) To('nonorthpoint1') Set('data1', [1.0, 2.0, 3.0, 4.0, 5.0]) Set('data2', [0.2, 0.3, 0.4, 0.5, 0.8]) To('..') To('..') Add('polar', name=u'perc', autoadd=False) To(u'perc') Set('units', u'percentages') Set('SpokeLine/number', 10) Add('nonorthpoint', name='nonorthpoint1', autoadd=False) To('nonorthpoint1') Set('data1', [1.0, 2.0, 3.0, 4.0, 5.0]) Set('data2', [20.0, 40.0, 60.0, 70.0, 80.0]) To('..') To('..') Add('polar', name=u'rad', autoadd=False) To(u'rad') Set('units', u'radians') Set('SpokeLine/number', 12) Add('nonorthpoint', name='nonorthpoint1', autoadd=False) To('nonorthpoint1') Set('data1', [1.0, 2.0, 3.0, 4.0, 5.0]) Set('data2', [0.0, 1.04719733, 2.09439467, 3.141592, 4.18878933]) To('..') To('..') Add('polar', name=u'deg', autoadd=False) To(u'deg') Set('units', u'degrees') Set('direction', u'clockwise') Set('SpokeLine/number', 12) Add('nonorthpoint', name='nonorthpoint1', autoadd=False) To('nonorthpoint1') Set('data1', [1.0, 2.0, 3.0, 4.0, 5.0]) Set('data2', [30.0, 60.0, 90.0, 150.0, 210.0]) To('..') To('..') To('..') To('..') veusz-3.0.1/tests/selftests/hdf5_twod.vsz0000664000175000017500000000140113161413406020111 0ustar jssjss00000000000000# Veusz saved document (version 1.19.1) # Saved at 2013-12-31T09:54:51.600445 ImportFileHDF5(u'hdftest.hdf5', [u'/threed_slice_1', u'/threed_slice_2'], linked=True, slices={u'/threed_slice_2': ((None, None, None), (None, None, -1), 3)}, twodranges={u'/threed_slice_2': (0.0, 0.0, 1.0, 1.0)}) Add('page', name='page1', autoadd=False) To('page1') Add('graph', name='graph1', autoadd=False) To('graph1') Add('axis', name='x', autoadd=False) Add('axis', name='y', autoadd=False) To('y') Set('direction', 'vertical') To('..') Add('contour', name='contour1', autoadd=False) To('contour1') Set('data', u'threed_slice_2') To('..') Add('image', name='image1', autoadd=False) To('image1') Set('data', u'threed_slice_1') Set('colorMap', u'grey-step6') To('..') To('..') To('..') veusz-3.0.1/tests/selftests/irregular_data.dat0000664000175000017500000000017713161413406021152 0ustar jssjss00000000000000xedge 1 2 4 6 7 yedge 0 2 3 6 7 1 2 3 4 2 3 4 5 3 4 5 6 2 3 4 5 xcent 1 2 4 5 ycent 2 3 5 6 1 2 3 4 4 5 6 7 6 7 8 9 7 8 9 10 veusz-3.0.1/tests/selftests/twod_expr.vsz0000664000175000017500000000127313161413406020250 0ustar jssjss00000000000000# Veusz saved document (version 1.25) # Saved at 2016-12-16T14:06:11.666981 SetData2DXYFunc(u'a', (0.0, 4.0, 1.0), (0.0, 4.0, 1.0), u'x+10*y', linked=True) SetData2DExpression(u'a2', u'a*2', linked=True) Add('page', name='page1', autoadd=False) To('page1') Add('graph', name='graph1', autoadd=False) To('graph1') Add('axis', name='x', autoadd=False) Add('axis', name='y', autoadd=False) To('y') Set('direction', 'vertical') To('..') Add('xy', name='xy1', autoadd=False) To('xy1') Set('xData', []) Set('yData', u'ravel(a)') To('..') Add('xy', name='xy2', autoadd=False) To('xy2') Set('marker', u'diamond') Set('color', u'blue') Set('xData', []) Set('yData', u'ravel(a2)') To('..') To('..') To('..') veusz-3.0.1/tests/selftests/axisautoranges.vsz0000664000175000017500000001141413161413406021270 0ustar jssjss00000000000000# Veusz saved document (version 1.25.1) # Saved at 2017-04-02T10:45:47.723302 AddImportPath(u'/home/jss/code/veusz/tests/selftests') SetDataExpression(u'data', u'1+arange(5)*1.1', linked=True) Set('StyleSheet/graph/leftMargin', u'0cm') Set('StyleSheet/graph/rightMargin', u'0cm') Set('StyleSheet/graph/topMargin', u'0cm') Set('StyleSheet/graph/bottomMargin', u'0cm') Add('page', name='page1', autoadd=False) To('page1') Add('grid', name=u'outergrid', autoadd=False) To(u'outergrid') Set('rows', 1) Set('leftMargin', u'0cm') Set('rightMargin', u'0cm') Set('topMargin', u'0cm') Set('bottomMargin', u'0cm') Add('grid', name=u'linear', autoadd=False) To(u'linear') Set('columns', 1) Add('axis', name='x', autoadd=False) To('x') Set('autoRange', u'exact') To('..') Add('graph', name=u'ticks', autoadd=False) To(u'ticks') Add('axis', name='y', autoadd=False) To('y') Set('direction', 'vertical') To('..') Add('xy', name='xy1', autoadd=False) To('xy1') Set('xData', u'data') Set('yData', u'data') To('..') To('..') Add('graph', name=u'exact', autoadd=False) To(u'exact') Add('axis', name='y', autoadd=False) To('y') Set('autoRange', u'exact') Set('direction', 'vertical') To('..') Add('xy', name='xy1', autoadd=False) To('xy1') Set('xData', u'data') Set('yData', u'data') To('..') To('..') Add('graph', name=u'plus10', autoadd=False) To(u'plus10') Add('axis', name='y', autoadd=False) To('y') Set('autoRange', u'+10%') Set('direction', 'vertical') To('..') Add('xy', name='xy1', autoadd=False) To('xy1') Set('xData', u'data') Set('yData', u'data') To('..') To('..') Add('graph', name=u'minus10', autoadd=False) To(u'minus10') Add('axis', name='y', autoadd=False) To('y') Set('autoRange', u'-10%') Set('direction', 'vertical') To('..') Add('xy', name='xy1', autoadd=False) To('xy1') Set('xData', u'data') Set('yData', u'data') To('..') To('..') Add('graph', name=u'20to70', autoadd=False) To(u'20to70') Add('axis', name='y', autoadd=False) To('y') Set('autoRange', u'20-70%') Set('direction', 'vertical') To('..') Add('xy', name='xy1', autoadd=False) To('xy1') Set('xData', u'data') Set('yData', u'data') To('..') To('..') Add('graph', name=u'less80', autoadd=False) To(u'less80') Add('axis', name='y', autoadd=False) To('y') Set('autoRange', u'<80%') Set('direction', 'vertical') To('..') Add('xy', name='xy1', autoadd=False) To('xy1') Set('xData', u'data') Set('yData', u'data') To('..') To('..') Add('graph', name=u'gtr50', autoadd=False) To(u'gtr50') Add('axis', name='y', autoadd=False) To('y') Set('autoRange', u'>50%') Set('direction', 'vertical') To('..') Add('xy', name='xy1', autoadd=False) To('xy1') Set('xData', u'data') Set('yData', u'data') To('..') To('..') To('..') Add('grid', name=u'log', autoadd=False) To(u'log') Set('columns', 1) Add('axis', name='x', autoadd=False) To('x') Set('autoRange', u'exact') To('..') Add('graph', name=u'ticks', autoadd=False) To(u'ticks') Add('axis', name='y', autoadd=False) To('y') Set('log', True) Set('direction', 'vertical') To('..') Add('xy', name='xy1', autoadd=False) To('xy1') Set('xData', u'data') Set('yData', u'data') To('..') To('..') Add('graph', name=u'exact', autoadd=False) To(u'exact') Add('axis', name='y', autoadd=False) To('y') Set('log', True) Set('autoRange', u'exact') Set('direction', 'vertical') To('..') Add('xy', name='xy1', autoadd=False) To('xy1') Set('xData', u'data') Set('yData', u'data') To('..') To('..') Add('graph', name=u'plus10', autoadd=False) To(u'plus10') Add('axis', name='y', autoadd=False) To('y') Set('log', True) Set('autoRange', u'+10%') Set('direction', 'vertical') To('..') Add('xy', name='xy1', autoadd=False) To('xy1') Set('xData', u'data') Set('yData', u'data') To('..') To('..') Add('graph', name=u'minus10', autoadd=False) To(u'minus10') Add('axis', name='y', autoadd=False) To('y') Set('log', True) Set('autoRange', u'-10%') Set('direction', 'vertical') To('..') Add('xy', name='xy1', autoadd=False) To('xy1') Set('xData', u'data') Set('yData', u'data') To('..') To('..') Add('graph', name=u'20to70', autoadd=False) To(u'20to70') Add('axis', name='y', autoadd=False) To('y') Set('log', True) Set('autoRange', u'20-70%') Set('direction', 'vertical') To('..') Add('xy', name='xy1', autoadd=False) To('xy1') Set('xData', u'data') Set('yData', u'data') To('..') To('..') Add('graph', name=u'less80', autoadd=False) To(u'less80') Add('axis', name='y', autoadd=False) To('y') Set('log', True) Set('autoRange', u'<80%') Set('direction', 'vertical') To('..') Add('xy', name='xy1', autoadd=False) To('xy1') Set('xData', u'data') Set('yData', u'data') To('..') To('..') Add('graph', name=u'gtr50', autoadd=False) To(u'gtr50') Add('axis', name='y', autoadd=False) To('y') Set('log', True) Set('autoRange', u'>50%') Set('direction', 'vertical') To('..') Add('xy', name='xy1', autoadd=False) To('xy1') Set('xData', u'data') Set('yData', u'data') To('..') To('..') To('..') To('..') To('..') veusz-3.0.1/tests/selftests/test_npy_npz.vsz0000664000175000017500000000176213161413406020774 0ustar jssjss00000000000000# Veusz saved document (version 1.13) # Saved at 2011-09-09T18:43:18.107056 ImportFilePlugin(u'Numpy NPZ import', u'testdat.npz', linked=True, errorsin2d=True) ImportFilePlugin(u'Numpy NPY import', u'testdat.npy', linked=True, errorsin2d=True, name=u'c') Set('StyleSheet/xy/marker', u'none') Add('page', name='page1', autoadd=False) To('page1') Add('graph', name='graph1', autoadd=False) To('graph1') Add('axis', name='x', autoadd=False) Add('axis', name='y', autoadd=False) To('y') Set('direction', 'vertical') To('..') Add('xy', name='xy1', autoadd=False) To('xy1') Set('xData', u'a') Set('yData', u'b') To('..') Add('xy', name='xy2', autoadd=False) To('xy2') Set('xData', u'a') Set('yData', u'c') Set('PlotLine/color', u'green') To('..') Add('xy', name='xy3', autoadd=False) To('xy3') Set('xData', u'a') Set('yData', u'd') Set('PlotLine/color', u'blue') To('..') Add('xy', name='xy4', autoadd=False) To('xy4') Set('xData', u'a') Set('yData', u'e') Set('PlotLine/color', u'cyan') To('..') To('..') To('..') veusz-3.0.1/tests/selftests/csv1.vsz0000664000175000017500000000076413161413406017115 0ustar jssjss00000000000000# Veusz saved document (version 1.12) # User: jss # Date: Sat, 06 Aug 2011 10:31:14 +0000 ImportFileCSV(u'csv1.csv', linked=True) Add('page', name='page1', autoadd=False) To('page1') Add('graph', name='graph1', autoadd=False) To('graph1') Add('axis', name='x', autoadd=False) Add('axis', name='y', autoadd=False) To('y') Set('direction', 'vertical') To('..') Add('xy', name='xy1', autoadd=False) To('xy1') Set('xData', u'foo') Set('yData', u'test') Set('labels', u'aerg') To('..') To('..') To('..') veusz-3.0.1/tests/selftests/qdp_2d.pco0000664000175000017500000000122413161413406017341 0ustar jssjss00000000000000LWIDTH 2. COL OFF 1..41 DG 1 1 1 31 41 LIne ON 1 LW 1.0 ON 1 LIne ON 2 LW 1.0 ON 2 CONT 1 LEVEL 197.9456 200.2556 204.85561 CONT 1 COLOR 1 2 3 CONT 1 LSTYL 1 1 1 CONT 1 LWID 1 1 1 XAX LIN 1.5 3.33333015E-2 YAX LIN 0.69999999 2.49999762E-2 LOC 0 0 1 1 LAB 1 POS 2.0544801 1.11201 "+" LAB 10 CS 0.75 CEN Top JUS Lef LAB 10 POS 1.52 1.6799999 "min = 1.956456e+02; Levels = 1.979456e+02 2.002556e+02 2.048556e+02" LAB F LAB T Confidence contours: Chi-Squared LAB X Parameter: kT (keV) LAB Y Parameter: Abundanc R X1 1.5 2.5 R Y1 0.69999999 1.7 veusz-3.0.1/tests/selftests/embed_test.py0000664000175000017500000000111213161413406020146 0ustar jssjss00000000000000import sys import numpy as N import veusz.embed as veusz def main(outfile): # note - avoid putting text in here to avoid font issues embed = veusz.Embedded(hidden=True) x = N.arange(5) y = x**2 embed.SetData('a', x) embed.SetData('b', y) page = embed.Root.Add('page') graph = page.Add('graph') xy = graph.Add('xy', xData='a', yData='b', marker='square') graph.x.TickLabels.hide.val = True graph.y.TickLabels.hide.val = True xy.MarkerFill.color.val = 'blue' embed.Export(outfile) if __name__ == '__main__': main(sys.argv[1]) veusz-3.0.1/tests/selftests/qdp_1d_1.qdp0000664000175000017500000000010213161413406017555 0ustar jssjss00000000000000! example qdp file (no skipping) 1 2 3 2 6 4 no no no 5 6 8 7 2 1 veusz-3.0.1/tests/selftests/qdp_2d.vsz0000664000175000017500000000117313161413406017405 0ustar jssjss00000000000000# Veusz saved document (version 1.14) # Saved at 2011-12-10T15:32:18.271490 ImportFilePlugin(u'QDP import', u'qdp_2d.qdp', linked=True, names=('',)) Add('page', name='page1', autoadd=False) To('page1') Add('graph', name='graph1', autoadd=False) To('graph1') Add('axis', name='x', autoadd=False) To('x') Set('autoRange', 'exact') To('..') Add('axis', name='y', autoadd=False) To('y') Set('autoRange', 'exact') Set('direction', 'vertical') To('..') Add('contour', name='contour1', autoadd=False) To('contour1') Set('data', u'vec2d1') Set('scaling', u'manual') Set('manualLevels', [197.946, 200.256, 204.856]) To('..') To('..') To('..') veusz-3.0.1/tests/runselftest.py0000775000175000017500000002343013322660165016414 0ustar jssjss00000000000000#!/usr/bin/env python # Copyright (C) 2011 Jeremy S. Sanders # Email: Jeremy Sanders # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ############################################################################## """A program to self test the Veusz installation. This code compares the output of example + self test input files with expected output. It returns 0 if the tests succeeded, otherwise the number of tests failed. If you use an argument "regenerate" to the program, the comparison files will be recreated. This program requires the veusz module to be on the PYTHONPATH. On Unix/Linux, Qt requires the DISPLAY environment to be set to an X11 server for the self test to run. In a non graphical environment Xvfb can be used to create a hidden X11 server. Alternatively, set the environment variable QT_QPA_PLATFORM=minimal to avoid the X11 dependency. The comparison files are close to being SVG files, but use XPM for any images and use a fixed (hacked) font metric to give the same results on each platform. In addition Unicode characters are expanded to their Unicode code to work around different font handling on platforms. """ # messes up loaded files if set # from __future__ import division from __future__ import print_function import glob import os import os.path import sys import subprocess import optparse # this needs to be set before main imports os.environ['LC_ALL'] = 'C' try: import h5py except ImportError: h5py = None from veusz.compat import cexec, cstr import veusz.qtall as qt4 import veusz.utils as utils import veusz.document as document import veusz.setting as setting import veusz.dataimport import veusz.document.svg_export as svg_export # required to get structures initialised import veusz.windows.mainwindow try: from astropy.io import fits as pyfits except ImportError: try: import pyfits except ImportError: pyfits = None # these tests fail for some reason which haven't been debugged # it appears the failures aren't important however excluded_tests = set([ # fails on Linux Arm 'spectrum.vsz', 'hatching.vsz', # fails on suse / fedora 'contour_labels.vsz', # new arm self test failures 'example_import.vsz', 'profile.vsz', '1dto2d.vsz', # don't expect this to work 'mathml.vsz', # 3d rendering needs more work '3d_errors.vsz', '3d_function.vsz', '3d_points.vsz', '3d_surface.vsz', '3d_volume.vsz', ]) class StupidFontMetrics(object): """This is a fake font metrics device which should return the same results on all systems with any font.""" def __init__(self, font, device): self.font = font self.device = device def height(self): return self.device.logicalDpiY() * (self.font.pointSizeF()/72.) def width(self, text): return len(text)*self.height()*0.5 def ascent(self): return 0.1*self.height() def descent(self): return 0.1*self.height() def leading(self): return 0.1*self.height() def boundingRect(self, c): return qt4.QRectF(0, 0, self.height()*0.5, self.height()) def boundingRectChar(self, c): return qt4.QRectF(0, 0, self.height()*0.5, self.height()) def lineSpacing(self): return 0.1*self.height() _pt = utils.textrender.PartText class PartTextAscii(_pt): """Text renderer which converts text to ascii.""" def __init__(self, text): text = text.encode('ascii', 'xmlcharrefreplace').decode('ascii') _pt.__init__(self, text) def render(self, state): _pt.render(self, state) def addText(self, text): self.text += text.encode('ascii', 'xmlcharrefreplace').decode('ascii') def renderVszTest(invsz, outfile, test_saves=False, test_unlink=False): """Render vsz document to create outfile.""" doc = document.Document() mode = 'hdf5' if os.path.splitext(invsz)[1] == '.vszh5' else 'vsz' doc.load(invsz, mode=mode) if test_unlink: for d in doc.data: doc.data[d].linked = None if test_saves and h5py is not None: tempfilename = 'self-test-temporary.vszh5' doc.save(tempfilename, mode='hdf5') doc = document.Document() doc.load(tempfilename, mode='hdf5') os.unlink(tempfilename) if test_saves: tempfilename = 'self-test-temporary.vsz' doc.save(tempfilename, mode='vsz') doc = document.Document() doc.load(tempfilename, mode='vsz') os.unlink(tempfilename) ifc = document.CommandInterface(doc) ifc.Export(outfile) def renderPyTest(inpy, outfile): """Render py embedded script to create outfile.""" retn = subprocess.call([sys.executable, inpy, outfile]) return retn == 0 class Dirs(object): """Directories and files object.""" def __init__(self): self.thisdir = os.path.dirname(__file__) self.exampledir = os.path.join(self.thisdir, '..', 'examples') self.testdir = os.path.join(self.thisdir, 'selftests') self.comparisondir = os.path.join(self.thisdir, 'comparison') self.infiles = ( glob.glob( os.path.join(self.exampledir, '*.vsz') ) + glob.glob( os.path.join(self.testdir, '*.vsz') ) + glob.glob( os.path.join(self.testdir, '*.vszh5') ) ) self.infiles += glob.glob(os.path.join(self.testdir, '*.py')) def renderAllTests(): """Check documents produce same output as in comparison directory.""" print("Regenerating all test output") d = Dirs() for infile in d.infiles: base = os.path.basename(infile) print(base) outfile = os.path.join(d.comparisondir, base + '.selftest') ext = os.path.splitext(base)[1] if ext == '.vsz' or ext == '.vszh5': renderVszTest(infile, outfile) elif ext == '.py': renderPyTest(infile, outfile) def runTests(test_saves=False, test_unlink=False): print("Testing output") fails = 0 passes = 0 skipped_support = 0 skipped_wip = 0 d = Dirs() for infile in sorted(d.infiles): base = os.path.basename(infile) print(base) ext = os.path.splitext(infile)[1] if ( (base[:5] == 'hdf5_' and h5py is None) or (base[:5] == 'fits_' and pyfits is None) or (ext == '.vszh5' and h5py is None) ): print(" SKIPPED: missing support module") skipped_support += 1 continue outfile = os.path.join(d.thisdir, base + '.temp.selftest') if ext == '.vsz' or ext == '.vszh5': renderVszTest(infile, outfile, test_saves=test_saves, test_unlink=test_unlink) elif ext == '.py': if not renderPyTest(infile, outfile): print(" FAIL: did not execute cleanly") fails += 1 continue else: raise RuntimeError('Invalid input file') if base in excluded_tests: print(" SKIPWIP: rendered, but comparison skipped") skipped_wip += 1 os.unlink(outfile) continue comparfile = os.path.join(d.thisdir, 'comparison', base + '.selftest') with open(outfile, 'rU') as f1: with open(comparfile, 'rU') as f2: comp = f1.read() == f2.read() if not comp: print(" FAIL: results differed") fails += 1 else: print(" PASS") passes += 1 os.unlink(outfile) print() if skipped_support != 0: print('Skipped %i tests (missing support)' % skipped_support) if skipped_wip != 0: print('Skipped %i comparisons (work in progress)' % skipped_wip) if fails == 0: print("All tests %i/%i PASSED" % (passes, passes)) sys.exit(0) else: print("%i/%i tests FAILED" % (fails, passes+fails)) sys.exit(fails) oldflt = svg_export.fltStr def fltStr(v, prec=1): """Only output floats to 1 dp.""" return oldflt(v, prec=prec) if __name__ == '__main__': app = qt4.QApplication([]) setting.transient_settings['unsafe_mode'] = True # hack metrics object to always return same metrics # and replace text renderer with one that encodes unicode symbols utils.textrender.FontMetrics = StupidFontMetrics utils.FontMetrics = StupidFontMetrics utils.textrender.PartText = PartTextAscii # nasty hack to remove underlining del utils.textrender.part_commands[r'\underline'] # dpi (use old values) svg_export.fltStr = fltStr parser = optparse.OptionParser() parser.add_option("", "--test-saves", action="store_true", help="tests saving documents and reloading them") parser.add_option("", "--test-unlink", action="store_true", help="unlinks data from files before --test-saves") options, args = parser.parse_args() if len(args) == 0: runTests(test_saves=options.test_saves, test_unlink=options.test_unlink) elif args == ['regenerate']: renderAllTests() else: parser.error("argument must be empty or 'regenerate'") veusz-3.0.1/tests/comparison/0000775000175000017500000000000013325026667015637 5ustar jssjss00000000000000veusz-3.0.1/tests/comparison/hdf5_text.vsz.selftest0000664000175000017500000001141313302221105022100 0ustar jssjss00000000000000 Veusz output document a b c 0 1 2 3 4 5 0 1 2 3 4 5 veusz-3.0.1/tests/comparison/broken_axis_rng.vsz.selftest0000664000175000017500000001655713302221105023376 0ustar jssjss00000000000000 Veusz output document 0 0.05 0.1 0.15 0.2 0.6 0.7 0.8 0.9 1 0 0.05 0.1 0.15 0.2 0.6 0.7 0.8 0.9 1 veusz-3.0.1/tests/comparison/sizetest.vsz.selftest0000664000175000017500000001573513302221105022073 0ustar jssjss00000000000000 Veusz output document 0.5 1 1.5 2 2.5 3 3.5 4 0.5 1 1.5 2 2.5 3 veusz-3.0.1/tests/comparison/blockeddata.vsz.selftest0000664000175000017500000001052213302221104022442 0ustar jssjss00000000000000 Veusz output document 2 4 6 8 10 0 2 4 6 8 10 veusz-3.0.1/tests/comparison/fits_keywords1.vsz.selftest0000664000175000017500000001443613302221105023173 0ustar jssjss00000000000000 Veusz output document 0 5 10 15 20 25 0 2 4 6 8 veusz-3.0.1/tests/comparison/custom.vsz.selftest0000664000175000017500000001202513302221103021516 0ustar jssjss00000000000000 Veusz output document 0 10 20 30 40 0 0.2 0.4 0.6 0.8 1 veusz-3.0.1/tests/comparison/testcontour.vsz.selftest0000664000175000017500000001257413302221103022606 0ustar jssjss00000000000000 Veusz output document 0 2.5 5 7.5 10 12.5 0 2 4 6 8 10 veusz-3.0.1/tests/comparison/noheader.vsz.selftest0000664000175000017500000001152613302221104021777 0ustar jssjss00000000000000 Veusz output document hello 5 2 foo 1 2 3 4 5 2009-01-01 00:00 2009-01-01 06:00 2009-01-01 12:00 2009-01-01 18:00 veusz-3.0.1/tests/comparison/histogramming.vsz.selftest0000664000175000017500000002432013302221100023052 0ustar jssjss00000000000000 Veusz output document X histogram 0 0.2 0.4 0.6 0.8 1 Y histogram 0 0.2 0.4 0.6 0.8 1 Y value −2 −1 0 1 2 X value −2 −1 0 1 2 veusz-3.0.1/tests/comparison/fixed_aspect.vsz.selftest0000664000175000017500000007030713302221076022662 0ustar jssjss00000000000000 Veusz output document 0 0.2 0.4 0.6 0.8 1 0 0.4 0.8 0 0.2 0.4 0.6 0.8 1 veusz-3.0.1/tests/comparison/test_npy_npz.vsz.selftest0000664000175000017500000001306613302221106022751 0ustar jssjss00000000000000 Veusz output document 0 25 50 75 100 125 0 2 4 6 8 10 veusz-3.0.1/tests/comparison/multixy.vsz.selftest0000664000175000017500000002650213302221101021722 0ustar jssjss00000000000000 Veusz output document The joy of plots Datasets Valkyries Swindon Discworld Model Death rate −5 −2.5 0 2.5 5 7.5 Winged warriors 0 5 10 15 20 veusz-3.0.1/tests/comparison/polar.vsz.selftest0000664000175000017500000002634013302221076021337 0ustar jssjss00000000000000 Veusz output document 0.5 1 1.5 2 2.5 3 330° 300° 270° 240° 210° 180° 150° 120° 90° 60° 30° 0.1 1 10 330° 300° 270° 240° 210° 180° 150° 120° 90° 60° 30° veusz-3.0.1/tests/comparison/profile.vsz.selftest0000664000175000017500000005411513161413406021666 0ustar jssjss00000000000000 Veusz output document Chandra deprojected Chandra projected XMM projected Temperature (keV) 10 5 Density Cooling time Cooling time (yr) 10 8 10 9 10 10 10 11 10 12 Electron density (cm -3 ) 0.01 0.1 Radius (kpc) 10 100 1000 veusz-3.0.1/tests/comparison/embed_test.py.selftest0000664000175000017500000001046613302221110022132 0ustar jssjss00000000000000 Veusz output document veusz-3.0.1/tests/comparison/hatching.vsz.selftest0000664000175000017500000005030113161413406022004 0ustar jssjss00000000000000 Veusz output document Hatch ing 0 2 4 6 8 10 12 1 2 3 4 5 6 7 veusz-3.0.1/tests/comparison/hdf5_date.vsz.selftest0000664000175000017500000001652513302221105022042 0ustar jssjss00000000000000 Veusz output document 00:00 00:15 00:30 00:45 01:00 01:15 01:30 2001-01 2001-05 2001-09 2002-01 2002-05 2002-09 2003-01 2003-05 1 1.5 2 2.5 3 veusz-3.0.1/tests/comparison/shapes.vsz.selftest0000664000175000017500000001621313302252431021502 0ustar jssjss00000000000000 Veusz output document Some shapes α 0 0.25 0.5 0.75 1 1.25 1.5 0 0.2 0.4 0.6 0.8 1 veusz-3.0.1/tests/comparison/1dto2d.vsz.selftest0000664000175000017500000002072313161413406021321 0ustar jssjss00000000000000 Veusz output document 0 2 4 6 8 0 2 4 6 8 veusz-3.0.1/tests/comparison/axisautoranges.vsz.selftest0000664000175000017500000011461613302221106023255 0ustar jssjss00000000000000 Veusz output document 1 3 5 1 3 5 1 3 5 1.5 3 4 2 3 4 1 2.5 3.5 3.5 4.5 1 2 3 4 5 1 2 5 1 2 5 1 2 5 2 2 1 2 5 1 2 3 4 5 veusz-3.0.1/tests/comparison/hdf5_nd.vsz.selftest0000664000175000017500000002122513302221103021515 0ustar jssjss00000000000000 Veusz output document 0 20 40 60 80 0 20 40 60 80 0 1 2 3 4 0 1 2 3 4 veusz-3.0.1/tests/comparison/contour.vsz.selftest0000664000175000017500000022715113302221077021717 0ustar jssjss00000000000000 Veusz output document 0 20 40 60 80 100 0 20 40 60 80 100 veusz-3.0.1/tests/comparison/contour_labels.vsz.selftest0000664000175000017500000014571013161413406023243 0ustar jssjss00000000000000 Veusz output document 1.89 1.89 1.89 2.78 2.78 2.78 2.78 2.78 2.78 2.78 3.67 3.67 3.67 3.67 3.67 3.67 4.56 4.56 4.56 4.56 4.56 5.44 5.44 5.44 5.44 6.33 6.33 6.33 7.22 7.22 7.22 8.11 8.11 0 b 1 b 2 b 3 b 4 b 5 b 6 b 7 b 8 b 9 b 10 b 0 a 1 a 2 a 3 a 4 a 5 a 6 a 7 a 8 a 9 a 10 a veusz-3.0.1/tests/comparison/multiaxes.vsz.selftest0000664000175000017500000003020013302221101022210 0ustar jssjss00000000000000 Veusz output document Optical surface brightness Electron density Iron Chandra Western Iron Chandra Eastern Iron XMM Iron metallicity (solar units) 0.4 0.6 0.8 1 1.2 1.4 1.6 1.8 Electron density (cm -3 ) 10 −3 0.01 Optical surface brightness (mag arcsec -2 ) 22 24 26 28 30 Radius (kpc) 10 100 veusz-3.0.1/tests/comparison/sin.vsz.selftest0000664000175000017500000001326113302221101020776 0ustar jssjss00000000000000 Veusz output document sin x −1 −0.5 0 0.5 1 x 0 1 2 3 4 5 6 7 veusz-3.0.1/tests/comparison/dsexpressions.vsz.selftest0000664000175000017500000002502313302221103023117 0ustar jssjss00000000000000 Veusz output document Using expressions of datasets y −2 −1 0 1 2 x 0 1 2 3 4 5 6 veusz-3.0.1/tests/comparison/hdf5_compound.vsz.selftest0000664000175000017500000001041313302221105022737 0ustar jssjss00000000000000 Veusz output document silver ho hi 0 0.5 1 1.5 2 0 0.5 1 1.5 2 veusz-3.0.1/tests/comparison/histo.vsz.selftest0000664000175000017500000004646313302221077021361 0ustar jssjss00000000000000 Veusz output document Example histogram Fit to histogram Histogram Dragons 0 100 200 300 400 500 Wingspan (m) 0 50 100 150 200 veusz-3.0.1/tests/comparison/mandelbrot.vsz.selftest0000664000175000017500000072640513302221077022363 0ustar jssjss00000000000000 Veusz output document 1 10 The Mandelbrot Set 0.5 0.75 1 1.25 1.5 1.75 −2 −1 0 0.5 1 veusz-3.0.1/tests/comparison/datebar.vsz.selftest0000664000175000017500000001454513302252431021627 0ustar jssjss00000000000000 Veusz output document A graph title Look! Crazy bar value 0 1 2 3 4 Date 2009-03-10 2009-03-11 2009-03-12 2009-03-13 2009-03-14 2009-03-15 2009-03-16 veusz-3.0.1/tests/comparison/twod_xyzexpr.vsz.selftest0000664000175000017500000000766513302221104023011 0ustar jssjss00000000000000 Veusz output document 0 0.5 1 1.5 2 2.5 3 1 1.5 2 2.5 3 3.5 4 veusz-3.0.1/tests/comparison/axis_function.vsz.selftest0000664000175000017500000002136713302221101023064 0ustar jssjss00000000000000 Veusz output document x 1 x 3 x 2 Squared axis 0 2 4 6 8 Linear axis 0 0.5 1 1.5 2 veusz-3.0.1/tests/comparison/polar_units.vsz.selftest0000664000175000017500000002561313302221106022555 0ustar jssjss00000000000000 Veusz output document 1 2 3 4 5 0 0.9 0.8 0.7 0.6 0.5 0.4 0.3 0.2 0.1 1 2 3 4 5 0 90 80 70 60 50 40 30 20 10 1 2 3 4 5 0 π/6 5π/3 3π/2 4π/3 7π/6 π 5π/6 2π/3 π/2 π/3 π/6 1 2 3 4 5 30° 60° 90° 120° 150° 180° 210° 240° 270° 300° 330° veusz-3.0.1/tests/comparison/customcolormap.vsz.selftest0000664000175000017500000001436113302221106023263 0ustar jssjss00000000000000 Veusz output document 0 0.2 0.4 0.6 0.8 1 0 0.25 0.5 0.75 1 0 0.25 0.5 0.75 1 veusz-3.0.1/tests/comparison/hdf5_twod_as_oned.vsz.selftest0000664000175000017500000001111313302221105023556 0ustar jssjss00000000000000 Veusz output document 1.5 2 2.5 3 3.5 1 1.5 2 2.5 3 3.5 veusz-3.0.1/tests/comparison/irregular_data.vsz.selftest0000664000175000017500000001040713302221105023175 0ustar jssjss00000000000000 Veusz output document 0 2 4 6 8 0 1 2 3 4 5 6 7 veusz-3.0.1/tests/comparison/dataset_operations.vsz.selftest0000664000175000017500000004576413302221077024126 0ustar jssjss00000000000000 Veusz output document data add plus mean sub scale extremes thin Using data operations to combine datasets −2 0 2 4 0 5 10 15 20 veusz-3.0.1/tests/comparison/filtered.vsz.selftest0000664000175000017500000002430413302221102022004 0ustar jssjss00000000000000 Veusz output document Filtered Data Y axis 0 1 2 3 4 5 X axis 0 2 4 6 8 10 veusz-3.0.1/tests/comparison/example_import.vsz.selftest0000664000175000017500000005642413161413406023260 0ustar jssjss00000000000000 Veusz output document y 1 0 1 2 3 4 5 6 This is an x-axis 0 2.5 5 7.5 10 12.5 15 y 2 −1 0 1 2 3 Another x-axis 0 2.5 5 7.5 10 12.5 15 y 3 0 0.5 1 1.5 2 2.5 Final \underline x axis 0 5 10 15 20 veusz-3.0.1/tests/comparison/csv_renaming.vsz.selftest0000664000175000017500000001246713302221103022671 0ustar jssjss00000000000000 Veusz output document fdfd dsfh bgtr RR ZZ AA 1 2 3 4 5 6 2 3 4 5 6 7 veusz-3.0.1/tests/comparison/functions.vsz.selftest0000664000175000017500000002215013302221101022212 0ustar jssjss00000000000000 Veusz output document δ Another axis 10 −2 10 0 Left axis 0 5 10 15 20 25 30 x axis −1.5 −0.5 0 0.5 1 1.5 veusz-3.0.1/tests/comparison/autodetect.vsz.selftest0000664000175000017500000001735313302221103022356 0ustar jssjss00000000000000 Veusz output document hello foo bar xxx aaa 2010-01-01 2010-01-15 2010-01-29 2010-02-12 2010-02-26 2010-03-12 2010-03-26 2010-04-09 1 2 3 4 5 xxx aaa 4 4.2 4.4 4.6 4.8 5 1000 10 4 veusz-3.0.1/tests/comparison/rangeds.vsz.selftest0000664000175000017500000001760413302221104021640 0ustar jssjss00000000000000 Veusz output document −2.5 0 2.5 5 7.5 10 12.5 0 2 4 6 8 10 veusz-3.0.1/tests/comparison/reverseaxis.vsz.selftest0000664000175000017500000001664213302221103022555 0ustar jssjss00000000000000 Veusz output document −1 −0.5 0 0.5 1 −1 0 0.5 1 0 2 4 6 8 10 1 10 veusz-3.0.1/tests/comparison/gnuplot_2d.vsz.selftest0000664000175000017500000001424013302221103022262 0ustar jssjss00000000000000 Veusz output document 0 1 2 3 4 5 0.5 1 1.5 2 2.5 veusz-3.0.1/tests/comparison/coloredpoints.vsz.selftest0000664000175000017500000005241513302221101023075 0ustar jssjss00000000000000 Veusz output document Power (W) 10 100 1k Offset (m) −3 −2 −1 0 1 2 3 Time (yr) 0 0.2 0.4 0.6 0.8 1 veusz-3.0.1/tests/comparison/2d_irregular.vsz.selftest0000664000175000017500000003702113302221101022566 0ustar jssjss00000000000000 Veusz output document Y coordinate 1 10 100 1000 X coordinate 1 10 100 1000 veusz-3.0.1/tests/comparison/bar_labels.vsz.selftest0000664000175000017500000002224613302221077022312 0ustar jssjss00000000000000 Veusz output document Spring Summer Number of balloons 0 1 2 3 4 5 6 Colour Red Green Blue Red Green Blue Red Green Blue Summer 2.5 3 3.5 4 4.5 Spring 2 3 4 5 veusz-3.0.1/tests/comparison/vectorfield.vsz.selftest0000664000175000017500000061364013302252431022534 0ustar jssjss00000000000000 Veusz output document −1 −0.5 0 0.5 1 −1 −0.5 0 0.5 1 veusz-3.0.1/tests/comparison/boxplot.vsz.selftest0000664000175000017500000001413613302221076021711 0ustar jssjss00000000000000 Veusz output document Number of insects 0 5 10 15 20 Bees Butterflys veusz-3.0.1/tests/comparison/example_csv.vsz.selftest0000664000175000017500000001652613302221076022535 0ustar jssjss00000000000000 Veusz output document Line plot Histogram Data read into from CSV 0 1 2 3 4 5 6 7 Imported CSV file example 0 2 4 6 8 veusz-3.0.1/tests/comparison/hdf5_group.vsz.selftest0000664000175000017500000001103113302221105022244 0ustar jssjss00000000000000 Veusz output document 0 20 40 60 80 0 2 4 6 8 veusz-3.0.1/tests/comparison/broken_axis.vsz.selftest0000664000175000017500000002700713302221101022514 0ustar jssjss00000000000000 Veusz output document An axis with a break in it 0 5 10 15 20 25 80 90 100 110 120 Axis without breaks 0 2 4 6 8 10 12 14 veusz-3.0.1/tests/comparison/testcsverr.vsz.selftest0000664000175000017500000001623213302221106022417 0ustar jssjss00000000000000 Veusz output document 1 2 3 4 5 6 0 2 4 6 8 10 veusz-3.0.1/tests/comparison/label_functions.vsz.selftest0000664000175000017500000001027713302221104023363 0ustar jssjss00000000000000 Veusz output document Page width: 15cm <> is basename Escaped text: \ \2 % ^ _ [] {} Loss: 24.0% vs 42.0 mAh/g 0 0.2 0.4 0.6 0.8 1 0 0.2 0.4 0.6 0.8 1 veusz-3.0.1/tests/comparison/labels.vsz.selftest0000664000175000017500000001243413302221102021451 0ustar jssjss00000000000000 Veusz output document dataset 2 dataset 2 dataset 2 A test test2 A 200 α β γ Y axis 0 2 4 6 8 X axis 0 1 2 3 4 5 veusz-3.0.1/tests/comparison/fits_wcs1.vsz.selftest0000664000175000017500000001275613302221104022122 0ustar jssjss00000000000000 Veusz output document 18.5 19 19.5 20 20.5 21 21.5 22 3 3.25 3.5 3.75 4 4.25 4.5 4.75 veusz-3.0.1/tests/comparison/sin_byhand.vsz.selftest0000664000175000017500000001326113302221100022322 0ustar jssjss00000000000000 Veusz output document sin x −1 −0.5 0 0.5 1 x 0 1 2 3 4 5 6 7 veusz-3.0.1/tests/comparison/numberstotext.vsz.selftest0000664000175000017500000001154413302221103023134 0ustar jssjss00000000000000 Veusz output document 1 2 3 1 2×10 5 2×10 −5 10 −6 10 −3 1 1000 10 6 10 −6 10 −3 1 1000 10 6 veusz-3.0.1/tests/comparison/inside.vsz.selftest0000664000175000017500000007030413302221102021462 0ustar jssjss00000000000000 Veusz output document All the cheese in the world −50 −25 0 25 50 75 100 Random axis, maybe something interesting 2 ... 0 50 100 150 200 −8 −4 0 −1 0 1 2 0 2 4 6 8 10 sin 2 π x −1 −0.5 0 0.5 1 an x-axis 0 0.2 0.4 0.6 0.8 1 veusz-3.0.1/tests/comparison/markerspolygon.vsz.selftest0000664000175000017500000010024113302221100023253 0ustar jssjss00000000000000 Veusz output document Outward ticks on this y axis −2 0 1 2 Outward ticks on this x axis −2 −1 0 1 2 veusz-3.0.1/tests/comparison/isolatedaxes.vsz.selftest0000664000175000017500000000706513302221100022676 0ustar jssjss00000000000000 Veusz output document y -axis (cm -3 ) 0 0.2 0.4 0.6 0.8 1 x -axis (erg) 0.01 0.1 1 veusz-3.0.1/tests/comparison/starchart.vsz.selftest0000664000175000017500000011035313302221100022177 0ustar jssjss00000000000000 Veusz output document veusz-3.0.1/tests/comparison/linked_datasets.vsz.selftest0000664000175000017500000006463213302221075023365 0ustar jssjss00000000000000 Veusz output document Another axis −0.8 −0.6 −0.4 0 0.2 0.4 0.6 0.8 Experiments with linked datasets −0.8 −0.4 0 0.2 0.4 0.6 0.8 veusz-3.0.1/tests/comparison/twod_expr.vsz.selftest0000664000175000017500000001613713302221106022232 0ustar jssjss00000000000000 Veusz output document 0 20 40 60 80 100 0 5 10 15 20 25 veusz-3.0.1/tests/comparison/spectrum.vsz.selftest0000664000175000017500000064701313161413406022075 0ustar jssjss00000000000000 Veusz output document Fe XXI Fe XXIII - XXIV Fe XXIV Mg XII Ne X Fe XXIV Fe XXII - XXIII Ne X , Fe XVII - XVIII Fe XVIII Fe XVIII Fe XVII Fe XVII Fe XX - XXII Fe XIX Si XIII Si XIV 99% PSF 90% PSF 99% - 90% Mg XII N VII Fe XVII Fe XVII O VIII Fe XVIII O VIII Flux (10 -3 photon cm -2 s -1 -1 ) 0 0.5 1 1.5 2 2.5 0.5 keV model spectrum 0.7 keV model spectrum 1.0 keV model spectrum Arbitrary units 0 0.1 0.2 0.3 0.4 0.5 0.6 Wavelength (Å) 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 veusz-3.0.1/tests/comparison/barplots.vsz.selftest0000664000175000017500000005304413302221102022037 0ustar jssjss00000000000000 Veusz output document a b stacked mode −1.5 −1 −0.5 0 0.5 1 1.5 0 5 10 15 20 grouped mode −1 −0.5 0 0.5 1 0 5 10 15 20 error bars −1 −0.5 0 0.5 1 0 5 10 15 20 horizontal with values 1 1000 10 6 10 9 −1 0 0.5 1 veusz-3.0.1/tests/comparison/nd.vsz.selftest0000664000175000017500000002712713302221077020630 0ustar jssjss00000000000000 Veusz output document 1 1.25 1.5 1.75 2 2.25 2.5 2.75 1 1.25 1.5 1.75 2 2.25 2.5 2.75 veusz-3.0.1/tests/comparison/qdp_2d.vsz.selftest0000664000175000017500000001323013302221107021360 0ustar jssjss00000000000000 Veusz output document 0.8 1 1.2 1.4 1.6 1.6 1.8 2 2.2 2.4 veusz-3.0.1/tests/comparison/fit.vsz.selftest0000664000175000017500000001447313302221102020776 0ustar jssjss00000000000000 Veusz output document data fit A dubious y axis 0 5 10 15 20 A wonderful x axis 1 10 veusz-3.0.1/tests/comparison/hdf5_doc.vszh5.selftest0000664000175000017500000003301013302221107022115 0ustar jssjss00000000000000 Veusz output document a b c d é ƒ g h i j k l m n ö p sin x −1 −0.5 0 0.5 1 1990 1995 2000 2005 2010 2015 0 1 2 3 4 5 6 x 0 1 2 3 4 5 6 7 veusz-3.0.1/tests/comparison/csv_locale.vsz.selftest0000664000175000017500000001416413302221106022327 0ustar jssjss00000000000000 Veusz output document 2012-12-06 2011-03-01 2011-04-05 2012-01-01 1,23 100,3 1.001,2 10 2008-09 2009-03 2009-09 2010-03 2010-09 2011-03 2011-09 2012-03 2012-09 2013-03 0 250 500 750 1000 1250 1500 veusz-3.0.1/tests/comparison/custom_definitions.vsz.selftest0000664000175000017500000004261213302221076024127 0ustar jssjss00000000000000 Veusz output document Imaginary value of FFT function −7.5 0 2.5 5 7.5 x −4 −2 0 2 4 veusz-3.0.1/tests/comparison/axis_function_linked.vsz.selftest0000664000175000017500000014647713302221100024423 0ustar jssjss00000000000000 Veusz output document Supernova colour −0.3 −0.2 −0.1 0 0.1 0.2 0.3 Data taken from Kowalski et al. (2008) Time since big bang (Gyr) 10 5 Maximum B magnitude 12.5 15 17.5 20 22.5 25 27.5 30 Redshift 0 0.2 0.4 0.6 0.8 1 1.2 1.4 veusz-3.0.1/tests/comparison/csv1.vsz.selftest0000664000175000017500000001246713302221107021076 0ustar jssjss00000000000000 Veusz output document fdfd dsfh bgtr RR ZZ AA 1 2 3 4 5 6 2 3 4 5 6 7 veusz-3.0.1/tests/comparison/hdf5_twod.vsz.selftest0000664000175000017500000001052413302221106022074 0ustar jssjss00000000000000 Veusz output document 0 0.2 0.4 0.6 0.8 1 0 0.2 0.4 0.6 0.8 1 veusz-3.0.1/tests/comparison/bar_options.vsz.selftest0000664000175000017500000003263513302221102022533 0ustar jssjss00000000000000 Veusz output document Stacked area Balloons Slinkys Jigsaws 0 2 4 6 8 10 Stacked 0 2 4 6 8 10 Grouped Sales 0 2 4 6 8 Area 1 2 3 4 5 6 7 8 veusz-3.0.1/tests/comparison/stackedxy.vsz.selftest0000664000175000017500000003413313302221102022206 0ustar jssjss00000000000000 Veusz output document Valkyries −2 0 1 2 3 Swindon −1 1 2 3 4 Discworld −5 0 2.5 5 7.5 Traffic police 0 5 10 15 20 veusz-3.0.1/tests/comparison/csv_missing.vsz.selftest0000664000175000017500000001212313302221105022531 0ustar jssjss00000000000000 Veusz output document hello foo 0 1 2 3 4 5 6 0 2 4 6 8 veusz-3.0.1/tests/comparison/fits_test.vsz.selftest0000664000175000017500000002012513302221104022211 0ustar jssjss00000000000000 Veusz output document NGC1001 NGC1002 NGC1003 10 11 12 13 14 15 16 0 1 2 3 4 0 1 2 3 4 5 0 1 2 3 4 5 veusz-3.0.1/tests/comparison/reversed_broken_axis.vsz.selftest0000664000175000017500000004660413302221104024422 0ustar jssjss00000000000000 Veusz output document 0 0.4 0.8 3 3.4 3.8 7 8 9 10 0 0.2 0.4 0.6 0.8 1 7 8 9 10 3 3.4 3.8 0 0.4 0.8 0 0.2 0.4 0.6 0.8 1 7 8 9 10 3 3.4 3.8 0 0.4 0.8 0 0.2 0.4 0.6 0.8 1 veusz-3.0.1/tests/comparison/ternary.vsz.selftest0000664000175000017500000004266513302221075021715 0ustar jssjss00000000000000 Veusz output document 0 10 20 30 40 50 60 70 80 90 100 0 10 20 30 40 50 60 70 80 90 100 0 10 20 30 40 50 60 70 80 90 100 Earth Air Fire Nougat Chocolate veusz-3.0.1/tests/comparison/qdp_1d.vsz.selftest0000664000175000017500000001242313302221104021357 0ustar jssjss00000000000000 Veusz output document 0 2 4 6 8 1 2 3 4 5 6 7 veusz-3.0.1/tests/test_badpy_expr_link.vsz0000664000175000017500000000121413161413406020425 0ustar jssjss00000000000000# Veusz saved document (version 0.99.0) # User: jss # Date: Tue, 09 Oct 2007 11:04:05 +0000 SetDataExpression(u'y', u'x_0.5**2', linked=True) ImportString(u'x_0.5(numeric)',''' 0.000000e+00 1.000000e+00 2.000000e+00 3.000000e+00 4.000000e+00 5.000000e+00 6.000000e+00 7.000000e+00 8.000000e+00 9.000000e+00 1.000000e+01 ''') Add('page', name='page1', autoadd=False) To('page1') Add('graph', name='graph1', autoadd=False) To('graph1') Add('axis', name='x', autoadd=False) Add('axis', name='y', autoadd=False) To('y') Set('direction', 'vertical') To('..') Add('xy', name='xy1', autoadd=False) To('xy1') Set('xData', u'x_0.5') To('..') To('..') To('..') veusz-3.0.1/tests/test_all_examples.vsz0000664000175000017500000000305113161413406017722 0ustar jssjss00000000000000# Copyright (C) 2009 Jeremy S. Sanders # Email: Jeremy Sanders # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ############################################################################## # load all the example documents in this directory # generate an image for each example # run with veusz --unsafe-mode testallexamples.vsz import glob import os import os.path thisdir = os.path.abspath(os.path.dirname(__file__)) exampledir = os.path.join(thisdir, '..', 'examples') examples = glob.glob(os.path.join(exampledir, '*.vsz')) for filename in examples: To('/') for child in GetChildren(): Remove(child) Set('width', '15cm') Set('height', '15cm') if filename == 'testall.vsz': continue __file__ = filename execfile(filename) Export(os.path.join(thisdir, 'test_%s.png' % os.path.basename(filename))) veusz-3.0.1/PKG-INFO0000664000175000017500000000243613325026670013417 0ustar jssjss00000000000000Metadata-Version: 1.1 Name: veusz Version: 3.0.1 Summary: A scientific plotting package Home-page: https://veusz.github.io/ Author: Jeremy Sanders Author-email: jeremy@jeremysanders.net License: GPL Description: Veusz is a 2D and 3D scientific plotting package, designed to create publication-ready PDF and SVG output. It features GUI, command-line, and scripting interfaces. Graphs are constructed from "widgets", allowing complex layouts to be designed. Veusz supports plotting functions, data with errors, keys, labels, stacked plots, multiple plots, and fitting data. Platform: UNKNOWN Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 2.6 Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.3 Classifier: Programming Language :: Python :: 3.4 Classifier: Programming Language :: Python :: 3.5 Classifier: Programming Language :: Python :: 3.6 Classifier: Development Status :: 5 - Production/Stable Classifier: Environment :: X11 Applications :: Qt Classifier: Intended Audience :: Science/Research Classifier: License :: OSI Approved :: GNU General Public License (GPL) Classifier: Topic :: Scientific/Engineering :: Visualization veusz-3.0.1/ui/0000775000175000017500000000000013325026667012740 5ustar jssjss00000000000000veusz-3.0.1/ui/preferences.ui0000664000175000017500000004224413302252431015567 0ustar jssjss00000000000000 PrefsDialog 0 0 730 353 Preferences 0 View Use antialiasing to smooth jagged edges Antialiasing Please restart Veusz after changing this option Override system locale settings to show Veusz in US/English Update interval How often Veusz will update the plot if it has changed Toolbar icon size 8 16 24 32 48 64 Number of drawing threads Maximum number of parallel threads to use for drawing plots. Set to 0 to disable threads. 16 Translation <html><head/><body><p>Load an externally-provided translation file on startup</p></body></html> Browse... File Document default directory Previous session Current working directory Export filename templates Multiple files <html><head/><body><p>Template filename when exorting to multiple files. This can include %DOCNAME% for the document name, %PAGENAME% for the page name and %PAGE% for the page number. %PAGE00% and %PAGE000% force two and three-character page numbers. An extension is automatically added.</p></body></html> <html><head/><body><p>Template filename when written to a single file. This can include %DOCNAME% for the document name. A filename extension is automatically added.</p></body></html> Single file Export default directory Document director&y Previous session Curren&t working directory New documents 0 0 Default stylesheet and custom definition files to load in new documents true Stylesheet A stylesheet file name specified here will be automatically loaded when creating a new document. Leave blank for no stylesheet to be loaded. Browse... Custom definitons A custom definiton file name specified here will be automatically loaded when creating a new document. Leave blank for no file to be loaded. Browse... Colors Document color theme Default Colors used by the Veusz user interface Picker Print picked points to internal console Copy picked points to clipboard Plugins Qt::Horizontal 40 20 Add... Add entries here to load Veusz import plugins. Entries should consist of a Python file to load. true 0 0 Remove External Python path <html><head/><body><p>Enter a colon-separated list of directories to be added to the beginning of the Python path. This can be used to find Python modules located in non-default locations. Changing this setting requires a restart.</p></body></html> Ghostscript location <html><head/><body><p>Location of the Ghostscript executable. Used for creating EPS files. On Linux/Unix the PATH will be searched if this is not set. Requires a restart after changing.</p></body></html> Browse... Disable new version checks Disable sending automatic feedback QDialogButtonBox::Cancel|QDialogButtonBox::Ok buttonBox accepted() PrefsDialog accept() 169 236 169 126 buttonBox rejected() PrefsDialog reject() 169 236 169 126 veusz-3.0.1/ui/capturing.ui0000664000175000017500000000400413161413406015256 0ustar jssjss00000000000000 CapturingDialog 0 0 400 300 Capturing data - Veusz Reading from: %s %i bytes read in %i seconds false true Dataset Items Qt::Horizontal 40 20 &Finish &Cancel veusz-3.0.1/ui/import_standard.ui0000664000175000017500000000674313161413406016470 0ustar jssjss00000000000000 standardtab 0 0 265 277 File preview: QTextEdit::NoWrap true 6 0 Dataset &names descriptoredit The import descriptor, consisting of the dataset names used during import, e.g. "x y" or "a[:]" Help 0 0 <html><head/><body><p>Ignores lines consisting of text when importing the data. This option only works if a list of datasets is given.</p></body></html> I&gnore text lines true 0 0 If this is selected, blank lines or the word "no" are used to separate the file into blocks. An underscore followed by the block number is added to the dataset names Read data in bloc&ks HistoryCombo QComboBox

        historycombo.h
        HistoryCheck QCheckBox
        historycheck.h
        veusz-3.0.1/ui/importhelpcsv.ui0000664000175000017500000001001613161413406016161 0ustar jssjss00000000000000 ImportCSVHelpDialog 0 0 457 400 CSV import help true <html><head/><body><p><span style=" font-size:xx-large; font-weight:600;">CSV (Comma Separated Value)</span></p><p>CSV files are often used to export data from applications such as Excel and OpenOffice.</p><p><span style=" font-size:x-large; font-weight:600;">Headers</span></p><p>Place a dataset name at the top of each column. To import error bars, columns with the names "+", "-" or "+-" should be given in columns immediately to the right of the dataset, for positive, negative or symmetric errors, respectively.</p><p>In the standard Multiple header mode, multiple datasets can be placed below each other if new names are given. If your data only consist of a single header, you can choose the Single header mode, which will prevent Veusz from starting new datasets if it sees text. If your data have no header, choose None and your columns will be named automatically.</p><p>Veusz can also read data organised in rows rather than columns (choose the rows option under Directions). Veusz can also ignore the specified number of lines in columns which follow header items.</p><p><span style=" font-size:x-large; font-weight:600;">Data types</span></p><p>Veusz will try to guss the data type (numeric, text or date) depending on what values it sees. You can override this by putting the datatype name in brackets in the column header. If you want to read in text use (text) after the name of the dataset in the top column, e.g. "name (text)". Date-times can have " (date)" after the column name. Veusz uses ISO dates by default YYYY-MM-DDThh:mm:ss, but this can be changed.</p><p><span style=" font-size:x-large; font-weight:600;">Options</span></p><p>There are several variants of CSV files used. You may wish to change the delimiter to be a tab (TSV) or space. Your file may also use European or English numerical values e.g. (1,23 or 1.23), so you may wish to override your computer's default format.</p><p>There are many different date and time formats. You can enter your own format in the box given [a combination of YYYY (or YY), MM (or M), DD (or D), HH, MM and SS].</p><p>The "Treat blanks as data values" option will insert NaN values or empty strings into datasets, if blank data values are encountered.</p><p>If you are using non-ASCII characters in your text you need to encode the text in your file with a Unicode encoding (e.g. UTF-8) and choose the correct encoding in the encoding drop down box. </p></body></html> Qt::Horizontal QDialogButtonBox::Close buttonBox clicked(QAbstractButton*) ImportCSVHelpDialog close() 248 254 157 274 veusz-3.0.1/ui/import_plugins.ui0000664000175000017500000000323513161413406016342 0ustar jssjss00000000000000 plugintab 0 0 282 346 Plugin: Description goes here true Preview true Parameters veusz-3.0.1/ui/reloaddata.ui0000664000175000017500000000613213161413406015366 0ustar jssjss00000000000000 ReloadDialog 0 0 587 381 Reload data - Veusz Results of reload data Qt::Horizontal 0 0 Reload if files change every false s 1 99999 5 Sans Serif 9 50 false false false false QTextEdit::NoWrap true QDialogButtonBox::Close buttonBox rejected() ReloadDialog close() 20 20 20 20 intervalCheck toggled(bool) intervalTime setEnabled(bool) 20 20 20 20 veusz-3.0.1/ui/import_csv.ui0000664000175000017500000001701113161413406015451 0ustar jssjss00000000000000 csvtab 0 0 548 491 File preview: QAbstractItemView::NoEditTriggers false Behaviour Header mode 'Multiple': allow multiple headers in file 'Single': 1st non-blank row is header 'None': no headers, guess data types &Direction csvdirectioncombo Are the data arranged in columns or rows? Ignore rows at top Ignore N row at the top of the file. If data are arranged in rows, ignores columns instead. Ignore rows after headers After reading a header, ignore N rows in that column. If Direction is set to Rows, ignore N columns instead. Treat blanks as data values Help on how CSV files should be formatted Help Locale Numerics Numerical format of numbers in file: System - what this computer is set to use English - format 123,456.78 European - format 123.456,78 Dates Format for dates and times in file. This will be combination of YYYY, YY, MM, M, DD, D, hh, h, mm, m, ss and s separated by | true Delimiters Column Delimiter between fields. This is usually a comma. true Skip initial white space Text Character to delimit text, usually a quote ("). true HistoryCombo QComboBox
        historycombo.h
        HistoryCheck QCheckBox
        historycheck.h
        HistorySpinBox QSpinBox
        historyspinbox.h
        HistoryValueCombo QComboBox
        historyvaluecombo.h
        veusz-3.0.1/ui/import_2d.ui0000664000175000017500000002121513161413406015164 0ustar jssjss00000000000000 tab2d 0 0 623 646 File preview: QTextEdit::NoWrap true Options true Invert &rows true CSV text delimeter <html><head/><body><p>Use text mode, with space-separated values or CSV mode. When using CSV mode, the delimiters and numeric locale are given below.</p></body></html> Invert &columns Mode Transpose rows and columns Trans&pose CSV delimeter CSV locale Numerical format of numbers in file: System - what this computer is set to use English - format 123,456.78 European - format 123.456,78 X, Y Ranges The centres of the pixels are given in the top row and the left column Grid points at edges Range of X: twod_xminedit Enter a number for the minimum coordinate of the X axis (default 0) to Enter a value for a maximum coordinate of the X axis (default is number of columns in file) Range of Y: twod_yminedit Enter a number for the minimum coordinate of the Y axis (default 0) to Enter a number for the maximum coordinate of the Y axis (default number of rows in file) Qt::Vertical 20 40 Datasets: A space separated list of dataset names to import from the file HistoryCombo QComboBox
        historycombo.h
        HistoryCheck QCheckBox
        historycheck.h
        HistoryValueCombo QComboBox
        historyvaluecombo.h
        veusz-3.0.1/ui/stylesheet.ui0000664000175000017500000001227613161413406015465 0ustar jssjss00000000000000 StylesheetDialog 0 0 489 526 Default styles - Veusz 1 0 2 0 Qt::Vertical 0 0 &Properties true 0 0 195 301 0 0 &Formatting Save definitions to a vsz script file Save... false Load definitions from a vsz script file Load... false Recent false Qt::Horizontal 40 20 Qt::Horizontal QDialogButtonBox::Close RecentFilesButton QPushButton
        recentfilesbutton.h
        buttonBox accepted() StylesheetDialog accept() 248 254 157 274 buttonBox rejected() StylesheetDialog reject() 316 260 286 274
        veusz-3.0.1/ui/license.ui0000664000175000017500000000425513161413406014714 0ustar jssjss00000000000000 LicenseDialog 0 0 609 373 License - Veusz 9 6 Veusz license 9 6 QTextEdit::NoWrap true 0 6 Qt::Horizontal 131 31 OK okButton clicked() LicenseDialog accept() 542 347 294 186 veusz-3.0.1/ui/filter.ui0000664000175000017500000001461213161413406014555 0ustar jssjss00000000000000 FilterDialog 0 0 468 528 Filter data Filtering Filter &expression exprcombo 0 0 <html><head/><body><p>Expression to filter datasets, e.g. ((x&lt;3) | (x&gt;10)) &amp; (y&gt;2)</p></body></html> true &Invert filter invertcheck <html><head/><body><p>Logical not of filter expression</p></body></html> &Replace filtered replaceblankscheck <html><head/><body><p>Do not remove filtered values, but replace with blanks or NaN (not a number) values</p></body></html> Output &prefix prefixcombo 0 0 <html><head/><body><p>Prefix to prepend to each output dataset name</p></body></html> true Output &suffix suffixcombo 0 0 <html><head/><body><p>Suffix to add to each output dataset name</p></body></html> true Datasets to filter Qt::Horizontal QDialogButtonBox::Apply|QDialogButtonBox::Close|QDialogButtonBox::Reset HistoryCombo QComboBox
        historycombo.h
        HistoryCheck QCheckBox
        historycheck.h
        buttonBox accepted() FilterDialog accept() 248 254 157 274 buttonBox rejected() FilterDialog reject() 316 260 286 274
        veusz-3.0.1/ui/about.ui0000664000175000017500000001103613242310603014372 0ustar jssjss00000000000000 AboutDialog 0 0 525 586 About Veusz 6 9 9 9 9 QFrame::StyledPanel QFrame::Raised 9 9 9 9 <html><head/><body><p><span style=" font-weight:600; color:#800080;">Veusz %(version)s</span><br/>Copyright © 2003-2018 Jeremy Sanders and contributors<br/><a href="https://veusz.github.io/"><span style=" text-decoration: underline; color:#0057ae;">https://veusz.github.io/</span></a><br/><br/>Main author:<br/>Jeremy Sanders<br/><br/>Other authors and contributors:<br/>Graham Bell<br/>James Graham<br/>Bryan Harris<br/>Dave Hughes<br/>Valerio Mussi<br/>Benjamin K. Stuhl</p><p><br/>Contains the following external code and resources: Danny Allen (icons), John D. Hunter (contouring), Nokia Corporation (MathML), Philip J. Schneider (bezier fitting)</p><p><br/>Veusz comes with ABSOLUTELY NO WARRANTY. Veusz is Free Software and you are entitled to distribute it under the terms of the GNU Public License (GPL). See the file COPYING for details, or click &quot;Show license&quot;. </p></body></html> Qt::AlignCenter true true 6 0 0 0 0 Qt::Horizontal 131 31 Show license false OK true okButton clicked() AboutDialog accept() 278 253 96 254 veusz-3.0.1/ui/custom.ui0000664000175000017500000002031313161413406014575 0ustar jssjss00000000000000 CustomDialog 0 0 621 644 Custom definitions 0 Definitions Define constants and functions for use in expressions. Functions should be specified as f(x,y) to specify arguments. Functions and constants are evaluated in order. true QAbstractItemView::SingleSelection QAbstractItemView::SelectItems true Imports Add external Python functions and constants using an import. Enter a module name and list of symbols to import (comma-separated or "*" for all symbols). true QAbstractItemView::SingleSelection QAbstractItemView::SelectItems true Colors Add user-defined colors. The definition of a color is an RGB value, e.g. #102030 or another color name. true QAbstractItemView::SingleSelection QAbstractItemView::SelectItems true Colormaps Define colormaps as (C1, C2, ...) where CX is a color specified as (R,G,B) or (R,G,B,A), where R,G,B and A are integers between 0 and 255 (red, green, blue and alpha). Specify a stepped colormap using (-1,0,0,0) as the first entry. true QAbstractItemView::SingleSelection QAbstractItemView::SelectItems true Qt::Horizontal 40 20 Move up Move down &Remove Save definitions to a vsz script file Save... Load definitions from a vsz script file Load... Recent false Qt::Horizontal 40 18 Qt::Horizontal QDialogButtonBox::Close RecentFilesButton QPushButton
        recentfilesbutton.h
        buttonBox_2 accepted() CustomDialog accept() 531 620 310 321 buttonBox_2 rejected() CustomDialog reject() 531 620 310 321
        veusz-3.0.1/ui/dataedit.ui0000664000175000017500000001233213161413406015044 0ustar jssjss00000000000000 Jeremy Sanders DataEditDialog 0 0 749 439 Dataset editor - Veusz 6 9 Qt::Horizontal false 6 0 6 0 0 0 true Linked file: None 0 0 Edit 0 0 Unlink 6 0 Qt::Horizontal 35 31 &Delete D&uplicate New false Crea&te... &Import... Qt::Horizontal QSizePolicy::Fixed 10 20 &Close closebutton clicked() DataEditDialog close() 369 253 179 282 veusz-3.0.1/ui/datacreate2d.ui0000664000175000017500000001327213161413406015614 0ustar jssjss00000000000000 DataCreate2D 0 0 638 396 Create 2D dataset &Name namecombo 0 0 true Method of creating dataset From x, y and z values based on &1D datasets or expressions From expression based on existing &2D dataset(s) From &function of x and y Values Enter range of values in form min:max:step or expression &X expression or range xexprcombo 0 0 true &Y expression or range yexprcombo 0 0 true &Z expression zexprcombo 0 0 true &Link this dataset to these expressions true QDialogButtonBox::Close buttonBox rejected() DataCreate2D close() 318 383 318 199 veusz-3.0.1/ui/histodata.ui0000664000175000017500000002305513161413406015251 0ustar jssjss00000000000000 HistogramDialog 0 0 472 558 Histogram data Count data values in bins to calculate a histogram Datasets &Input dataset expression indataset true &Output bin height dataset name outdataset Output &bin position dataset name outbins Automatic bin parameters &Number of bins numbins 1 999999 10 &Minimum value minval Minimum value of lowest value bin or "Auto" to get from dataset Ma&ximum value maxval Maximum value of highest value bin or " Auto" to get from dataset &Logarithmic Manual bin boundaries Add Remove Generate bin boundaries from parameters Generate Calculate Count number of items in bin &Counts true Compute probability density in bins &Density Compute fraction of items in bin &Fractions Cumulative Not cumulative true Small to large Large to small Compute error bars (Gehrels Poisson approximation) Qt::Horizontal QDialogButtonBox::Apply|QDialogButtonBox::Close|QDialogButtonBox::Reset HistoryCombo QComboBox
        historycombo.h
        HistoryGroupBox QGroupBox
        historygroupbox.h
        1
        HistoryCheck QCheckBox
        historycheck.h
        buttonBox accepted() HistogramDialog accept() 248 254 157 274 buttonBox rejected() HistogramDialog reject() 316 260 286 274
        veusz-3.0.1/ui/capture.ui0000664000175000017500000002036213161413406014732 0ustar jssjss00000000000000 CaptureDialog 0 0 392 507 Capture data - Veusz &Datasets: descriptorEdit Enter a descriptor to describe the format of the incoming data, e.g. "x,+,- y,+-" (see the Data->Import dialog box for details) true Capture method &File or named pipe true Filename: filenameEdit 0 0 Browse for file ... false Connect to &socket Host: hostEdit Port: TCP port to connect to E&xternal program Command line: commandLineEdit Stop after Clicking fi&nish button true Number of input &lines Total &time period (s) 0 0 Update document at intervals (s) 0 0 Only retain latest N values Maximum number of values to retain QDialogButtonBox::Close HistoryCombo QComboBox
        historycombo.h
        HistoryCheck QCheckBox
        historycheck.h
        buttonBox rejected() CaptureDialog close() 195 461 180 243
        veusz-3.0.1/ui/exceptionlist.ui0000664000175000017500000000715013161413406016161 0ustar jssjss00000000000000 exceptiondialog 0 0 576 431 Problem occured - Veusz erroricon A problem occured within Veusz. This means you have encountered a bug. You can help improve Veusz by sending a bug report to the developers. It doesn't take very long to submit a problem! true TextLabel true Details true Qt::Horizontal 131 31 &Send report Save report &Ignore this time Ignore in sessio&n okButton clicked() exceptiondialog accept() 278 253 96 254 cancelButton clicked() exceptiondialog reject() 369 253 179 282 veusz-3.0.1/ui/importhelp.ui0000664000175000017500000002671113161413406015456 0ustar jssjss00000000000000 ImportHelpDialog 0 0 581 368 Import data help true <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Arial'; font-size:9pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:10pt;">Veusz assumes that data are stored as columns in a text file separated by tabs or spaces. Names should be entered for the datasets read from each column, separated by spaces or commas (in the dataset names or descriptor box). If you leave the descriptor blank, automatic dataset names will be used (prefix + column + suffix, or &quot;colX&quot; if the prefix and suffix are blank).</span></p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:10pt;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:10pt;">If you want to supply errors or uncertainties on the data, these can be given in the columns following the dataset column (one column for symmetric errors or two for asymmetric errors). To tell Veusz that a dataset has errors, add &quot;</span><span style=" font-family:'Courier New,courier'; font-size:10pt;">+-</span><span style=" font-size:10pt;">&quot; or &quot;</span><span style=" font-family:'Courier New,courier'; font-size:10pt;">+,-</span><span style=" font-size:10pt;">&quot; to the dataset name to specify symmetric or asymmetric errors, respectively.</span></p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:10pt;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:10pt;">Commas or spaces separate the dataset name and the error bars. They are interchangable, except multiple commas will skip an input columns. </span></p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:10pt;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:10pt; font-weight:600;">Examples</span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Courier New,courier'; font-size:10pt;">x y</span><span style=" font-size:10pt;"> x and y with no errors (2 columns for 2 datasets)</span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Courier New,courier'; font-size:10pt;">x,+-</span><span style=" font-size:10pt;"> x with symmetric errors (2 columns for single dataset)</span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Courier New,courier'; font-size:10pt;">y + -</span><span style=" font-size:10pt;"> y with asymmetric errors (3 columns for dataset)</span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Courier New,courier'; font-size:10pt;">x[1:5]+,-</span><span style=" font-size:10pt;"> x_1 to x_5, each with asymmetric errors (15 columns in total)</span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Courier New,courier'; font-size:10pt;">x y +-</span><span style=" font-size:10pt;"> x with no errors, y with symmetric errors (3 columns in total)</span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Courier New,courier'; font-size:10pt;">,x,y,-,+</span><span style=" font-size:10pt;"> skip first column, x with no errors, y followed by negative then postive error bars</span></p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:10pt;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:10pt; font-weight:600;">Data types</span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:10pt;">A file can contain different types of data. This type is specified immediately after the dataset name in round brackets, e.g. &quot;x(float)&quot;, &quot;labels(text)&quot; or &quot;y(float),+-&quot;.</span></p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:10pt;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:10pt;">Only numerical (use float, number or numeric) and text (using text or string) data are supported. If a text column has spaces it should be surrounded by quotation marks.</span></p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:10pt; font-weight:600;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:10pt; font-weight:600;">Comments</span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:10pt;">If any of the &quot;</span><span style=" font-family:'Courier New,courier'; font-size:10pt;">#</span><span style=" font-size:10pt;">&quot;, &quot;</span><span style=" font-family:'Courier New,courier'; font-size:10pt;">!</span><span style=" font-size:10pt;">&quot;, &quot;</span><span style=" font-family:'Courier New,courier'; font-size:10pt;">;</span><span style=" font-size:10pt;">&quot; or &quot;</span><span style=" font-family:'Courier New,courier'; font-size:10pt;">%</span><span style=" font-size:10pt;">&quot; characters are found without being inside quotation marks, the rest of a line is ignored. Use these characters to add comments to a file.</span></p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:10pt; font-weight:600;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:10pt; font-weight:600;">Further notes</span></p> <ol style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" font-size:10pt;" style=" margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Extra tabs or spaces between columns are ignored</li> <li style=" font-size:10pt;" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Extra data at the end of a line are ignored</li> <li style=" font-size:10pt;" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">The text <span style=" font-family:'Courier New,courier';">nan</span> or <span style=" font-family:'Courier New,courier';">inf</span> translates to the usual numerical values. These values aren't plotted in a plot, giving a break in the line.</li> <li style=" font-size:10pt;" style=" margin-top:0px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">You can encode the descriptor describing the data in the file itself with a line <span style=" font-family:'Courier New,courier';">descriptor XXX</span> before the data. Leave the descriptor blank in the import dialog if you do this. Multiple descriptors can be placed in the file to store multiple sets of data. </li></ol> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:10pt; font-weight:600;"></p></body></html> Qt::Horizontal QDialogButtonBox::Close buttonBox clicked(QAbstractButton*) ImportHelpDialog close() 248 254 157 274 veusz-3.0.1/ui/import_nd.ui0000664000175000017500000002125713161413406015266 0ustar jssjss00000000000000 tabnd 0 0 623 646 File preview: QTextEdit::NoWrap true Options Shape <html><head/><body><p>List of numerical entries (separated by space or comma), giving the dimensions of each axis. Use -1 to automatically detect axis length (only one axis can be -1). Auto means autodetect shape, based on number of blank lines.</p></body></html> Numerical format of numbers in file: System - what this computer is set to use English - format 123,456.78 European - format 123.456,78 CSV text delimeter <html><head/><body><p>Use text mode, with space-separated values or CSV mode. When using CSV mode, the delimiters and numeric locale are given below.</p></body></html> Mode CSV delimeter CSV locale true true <html><head/><body><p>Transpose dimensions. Normally data are in 'C' order, with last axis changing fastest. Setting this will change to 'Fortran' order, with 1st axis changing fastest.</p></body></html> Trans&pose true <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Oxygen-Sans'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">File should consist of either:</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">1. A set of numbers, in free-form style, and the shape specified above or using the command &quot;shape A B C...&quot; in the file. Shapes should be positive integers, though one dimension can be -1 to automatically detect the length.</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">2. A set of numbers arranged in rows and columns. 1D datasets are on a single row, 2D datasets are a set of 1D rows, 3D as a set of 2D datasets separated by blank rows, 4D as a set of 3D datasets separated by two blank rows, etc.</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Data are ordered in C format, with the rightmost index increasing most rapidly. The transpose option can be used to change the option. This can also be included in the file with a single row containing &quot;transpose&quot;.</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p></body></html> Qt::Vertical 20 40 Dataset: <html><head/><body><p>Dataset name to import</p></body></html> HistoryCombo QComboBox
        historycombo.h
        HistoryCheck QCheckBox
        historycheck.h
        HistoryValueCombo QComboBox
        historyvaluecombo.h
        veusz-3.0.1/ui/import.ui0000664000175000017500000001540513161413406014603 0ustar jssjss00000000000000 importdialog 0 0 645 640 Import data - Veusz 6 0 &Filename filenameedit Enter the filename to be imported here &Browse... Read in data from clipboard rather than file -1 General options 0 0 Data imported are not stored in the Veusz saved file, but are reloaded each time the Veusz file is opened, or Data-&gt;Reload is selected on the menu &Link datasets to file true &Prefix prefixcombo 0 0 Prefix to prepend to each dataset name imported, or enter $FILENAME to have filename prepended true Character encoding Character encoding of input file Suffi&x suffixcombo 0 0 Suffix to append to each dataset name imported, or enter $FILENAME to have filename appended true Tag Enter a list of tags to apply to the imported datasets true QDialogButtonBox::Close|QDialogButtonBox::Reset qPixmapFromMimeSource HistoryCombo QComboBox
        historycombo.h
        HistoryCheck QCheckBox
        historycheck.h
        HistoryValueCombo QComboBox
        historyvaluecombo.h
        buttonBox rejected() importdialog close() 322 623 322 319
        veusz-3.0.1/ui/export.ui0000664000175000017500000003665113322660165014625 0ustar jssjss00000000000000 Dialog 0 0 540 680 Export Filename <html><head/><body><p>Name of file to export. When exporting multiple pages to multiple files %PAGE% is the number of the page and %PAGENAME% is the name of the page. %PAGE00% and %PAGE000% are the page numbers prefixed with zeros to make two or three characters.</p></body></html> Browse... Pages <html><head/><body><p>Export the current page</p></body></html> S&ingle <html><head/><body><p>Export all pages</p></body></html> A&ll <html><head/><body><p>Export given pages</p></body></html> P&ages 0 0 <html><head/><body><p>Enter a comma-separated list of pages or page ranges (e.g. 1-2,4,5)</p></body></html> <html><head/><body><p>Write multiple pages to the output file, if supported by the file format</p></body></html> Multiple pages per file Format Vector Single page encapsulated Postscript for embedding EPS Single page scalable vector graphics SV&G Single or multi-page PDF PDF Multi-page Postscript PS Windows embedded metafile E&MF Bitmap P&NG BMP &JPG &TIFF &XPM Options Overwrite without confirmation <html><head/><body><p>Overwrite any files with the same name without confirmation</p></body></html> Bitmap DPI <html><head/><body><p>Dots Per Inch is used to convert from the physical size of the plot to the number of pixels in output bitmaps. Increase this to make output bitmap files have more pixels.</p></body></html> true Appy antialiasing, or smoothing, to output bitmap images. This is recommended for most purposes. Antialias PDF/EPS DPI <html><head/><body><p>The number of dots per inch used for writing PDF and EPS files. As these are vector formats, this does not make much difference to the output, but larger values improve the placement of characters.</p></body></html> true SVG DPI <html><head/><body><p>The number of dots per inch used for writing SVG files. Modern inkscape uses 96.</p></body></html> true Bitmap background <html><head/><body><p>The background color for bitmap files. Use alpha channel values of 0 for transparency</p></body></html> Jpeg quality <html><head/><body><p>Choose Jpeg quality setting. Lower values give poorer quality results and are more compressed, but have smaller file sizes.</p></body></html> 0 100 85 Color <html><head/><body><p>Output Postscript or PDF as full color, or convert to greyscale</p></body></html> Color Greyscale Editable text in SVG <html><head/><body><p>Exports text in SVG files as text, rather than curves. Curves mean that the file will display the same on any system, but text can be edited easily in other programs.</p></body></html> Qt::Vertical 20 40 label Qt::Horizontal QDialogButtonBox::Close|QDialogButtonBox::Save buttonBox accepted() Dialog accept() 248 254 157 274 buttonBox rejected() Dialog reject() 316 260 286 274 veusz-3.0.1/ui/errorloading.ui0000664000175000017500000000407213161413406015756 0ustar jssjss00000000000000 ErrorLoadingDialog Qt::WindowModal 0 0 488 229 Error opening file icon Veusz could not open the file '%s'. The following error occured: true 75 true TextLabel true Qt::Horizontal QDialogButtonBox::Ok true buttonBox accepted() ErrorLoadingDialog accept() veusz-3.0.1/ui/import_fits.ui0000664000175000017500000000756513161413406015640 0ustar jssjss00000000000000 Form 0 0 773 524 Form 2D options Import as 1D dataset with error bars Image WCS mode Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter Linear (WCS) Pixel (simple) Pixel (WCS) Fractional Range: x = y = veusz-3.0.1/ui/import_hdf5.ui0000664000175000017500000001162013161413406015504 0ustar jssjss00000000000000 Form 0 0 596 447 Form Text options Import as date/time <html><head/><body><p>If you want to treat this dataset as date/times, choose a text format for this.</p></body></html> true 1D options Import as date/time No Veusz format (seconds since start of 2009) Unix format (seconds since start of 1970) 2D options Import as 1D dataset with error bars Range: Qt::Horizontal 40 20 x = Qt::Horizontal 40 20 y = veusz-3.0.1/ui/datacreate.ui0000664000175000017500000002050013161413406015356 0ustar jssjss00000000000000 DataCreateDialog 0 0 567 534 Create dataset - Veusz 6 0 &Name nameedit 0 0 Method of creating dataset 6 9 &Value or range 6 0 Number of steps &Parametric (as an expression of t) 6 0 t = to in steps (inclusive) &Expression using existing datasets Dataset values or expressions Enter expressions as a function of t, or leave blank Enter constant values here, leave blank if appropriate, or enter an inclusive range, e.g. 1:10 true Enter expressions as a function of other datasets. Append suffixes _data, _serr, _nerr and _perr to access different parts of datasets. If a dataset name contains punctuation or spaces, surround the name with backticks (`). true &Symmetric error symerroredit P&ositive error poserroredit Ne&gative error negerroredit V&alue valueedit &Link this dataset to these expressions true QDialogButtonBox::Close|QDialogButtonBox::Reset HistoryCombo QComboBox
        historycombo.h
        HistoryGroupBox QGroupBox
        historygroupbox.h
        1
        buttonBox rejected() DataCreateDialog close() 283 498 283 257
        veusz-3.0.1/ui/exceptionsend.ui0000664000175000017500000000552713161413406016145 0ustar jssjss00000000000000 ExceptSendDialog 0 0 469 509 Send problem - Veusz <b>Email address</b> (optional). If provided you can be notified about the bug status or to get further details. true <b>What you were doing when the problem occured</b> (optional). This is very helpful for trying to reproduce the bug. true This is what Veusz will send: true No personal details will be sent other than those listed here and the IP address. You will need an internet connection to send the error report. true QDialogButtonBox::Cancel|QDialogButtonBox::Ok buttonBox accepted() ExceptSendDialog accept() 234 487 234 254 buttonBox rejected() ExceptSendDialog reject() 234 487 234 254 veusz-3.0.1/ui/plugin.ui0000664000175000017500000000327313161413406014567 0ustar jssjss00000000000000 WorkerPluginDialog Dialog 0 0 TextLabel Options Qt::Horizontal QDialogButtonBox::Apply|QDialogButtonBox::Close|QDialogButtonBox::Reset buttonBox rejected() WorkerPluginDialog close() 316 260 286 274 veusz-3.0.1/veusz/0000775000175000017500000000000013325026667013477 5ustar jssjss00000000000000veusz-3.0.1/veusz/widgets/0000775000175000017500000000000013325026670015137 5ustar jssjss00000000000000veusz-3.0.1/veusz/widgets/graph3d.py0000664000175000017500000002016213303771337017045 0ustar jssjss00000000000000# -*- coding: utf-8 -*- # Copyright (C) 2013 Jeremy S. Sanders # Email: Jeremy Sanders # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ############################################################################## from __future__ import division, print_function import math import numpy as N from ..compat import cvalues from .. import qtall as qt from .. import document from .. import setting from . import widget from ..helpers import threed def _(text, disambiguation=None, context='Graph3D'): """Translate text.""" return qt.QCoreApplication.translate(context, text, disambiguation) # non-reflective back of cube class BackSurface(setting.Surface3D): def __init__(self, name, **args): setting.Surface3D.__init__(self, name, **args) self.get('color').newDefault('white') self.get('reflectivity').newDefault(0) self.get('hide').newDefault(True) class Graph3D(widget.Widget): """3D graph (orthogonal axes) containing other plotting widgets.""" typename='graph3d' allowusercreation = True description = _('3d graph') # start and end points of edges of cube _borderedges = ( ((0,0,0), (0,0,1)), ((0,0,0), (0,1,0)), ((0,0,0), (1,0,0)), ((0,0,1), (0,1,1)), ((0,0,1), (1,0,1)), ((0,1,0), (0,1,1)), ((0,1,0), (1,1,0)), ((0,1,1), (1,1,1)), ((1,0,0), (1,0,1)), ((1,0,0), (1,1,0)), ((1,0,1), (1,1,1)), ((1,1,0), (1,1,1)), ) # centres of each face _facecentres = ( (0.0, 0.5, 0.5), (0.5, 0.0, 0.5), (0.5, 0.5, 0.0), (0.5, 0.5, 1.0), (0.5, 1.0, 0.5), (1.0, 0.5, 0.5), ) @classmethod def addSettings(klass, s): """Construct list of settings.""" widget.Widget.addSettings(s) s.add( setting.Float( 'xSize', 1., minval=0.01, maxval=100, descr=_('X size'), usertext=_('X size') )) s.add( setting.Float( 'ySize', 1., minval=0.01, maxval=100, descr=_('Y size'), usertext=_('Y size') )) s.add( setting.Float( 'zSize', 1., minval=0.01, maxval=100, descr=_('Z size'), usertext=_('Z size') )) s.add( setting.Float( 'xPos', 0., minval=-100, maxval=100, descr=_('X position'), usertext=_('X position') )) s.add( setting.Float( 'yPos', 0., minval=-100, maxval=100, descr=_('Y position'), usertext=_('Y position') )) s.add( setting.Float( 'zPos', 0., minval=-100, maxval=100, descr=_('Z position'), usertext=_('Z position') )) s.add(setting.Line3D( 'Border', descr = _('Graph border'), usertext = _('Border')), pixmap = 'settings_border' ) s.add(BackSurface( 'Back', descr = _('Graph back'), usertext = _('Back')), pixmap = 'settings_bgfill' ) @classmethod def allowedParentTypes(self): from . import scene3d return (scene3d.Scene3D,) def addDefaultSubWidgets(self): """Add axes automatically.""" from . import axis3d for n in ('x', 'y', 'z'): if self.parent.getChild(n) is None: ax = axis3d.Axis3D(self, name=n) ax.linkToStylesheet() def getAxesDict(self, axesnames, ignoremissing=False): """Get the axes for widgets to plot against. axesnames is a list/set of names to find. Returns a dict of objects """ axes = {} # recursively go back up the tree to find axes w = self while w is not None and len(axes) < len(axesnames): for c in w.children: name = c.name if ( name in axesnames and name not in axes and hasattr(c, 'isaxis3d') and c.isaxis3d ): axes[name] = c w = w.parent # didn't find everything... if w is None and not ignoremissing: for name in axesnames: if name not in axes: axes[name] = None # return list of found axes return axes def getAxes(self, axesnames): """Return a list of axes widgets given a list of names.""" ad = self.getAxesDict(axesnames) return [ad[n] for n in axesnames] def addBorder(self, painter, root): s = self.settings if s.Border.hide: return lineprop = s.Border.makeLineProp(painter) edges = N.array(self._borderedges) ls = threed.LineSegments( threed.ValVector(N.ravel(edges[:,0,:])), threed.ValVector(N.ravel(edges[:,1,:])), lineprop) root.addObject(ls) def addBackSurface(self, painter, root): back = self.settings.Back if back.hide: return prop = back.makeSurfaceProp(painter) # triangles with the correct orientation of the norm vector # not to draw the surface if it is pointing towards the viewer for p1, p2, p3 in ( ((0,0,0), (0,0,1), (1,0,0)), ((0,0,1), (0,0,0), (0,1,0)), ((0,1,0), (0,1,1), (0,0,1)), ((0,1,0), (1,1,0), (0,1,1)), ((0,1,0), (0,0,0), (1,0,0)), ((0,1,1), (1,0,1), (0,0,1)), ((0,1,1), (1,1,1), (1,0,1)), ((1,0,0), (1,1,0), (0,1,0)), ((1,0,1), (1,0,0), (0,0,1)), ((1,0,1), (1,1,0), (1,0,0)), ((1,0,1), (1,1,1), (1,1,0)), ((1,1,0), (1,1,1), (0,1,1)), ): root.addObject(threed.TriangleFacing( threed.Vec3(*p1), threed.Vec3(*p2), threed.Vec3(*p3), prop)) def drawToObject(self, painter, painthelper): """Make objects, returning root""" s = self.settings # do no painting if hidden if s.hide: return # do axis min-max computation axestodraw = {} axesofwidget = painthelper.plotteraxismap for c in self.children: try: for a in axesofwidget[c]: axestodraw[a.name] = a except (KeyError, AttributeError): if c.isaxis: axestodraw[c.name] = c for axis in cvalues(axestodraw): axis.computePlottedRange() cont = threed.ObjectContainer() cont.objM = ( # graph position threed.translationM4(threed.Vec3( s.xPos - 0.5*s.xSize, s.yPos - 0.5*s.ySize, s.zPos - 0.5*s.zSize)) * # graph size threed.scaleM4(threed.Vec3(s.xSize, s.ySize, s.zSize)) ) # add graph box self.addBorder(painter, cont) # add graph behind fill self.addBackSurface(painter, cont) # make clickable cont.assignWidgetId(id(self)) # reset counter and compute automatic colors painthelper.autoplottercount = 0 for c in self.children: c.setupAutoColor(painter) # build 3d scene from children for c in self.children: obj = c.drawToObject(painter, painthelper) if obj: cont.addObject(obj) return cont document.thefactory.register(Graph3D) veusz-3.0.1/veusz/widgets/colorbar.py0000664000175000017500000002136113164704651017322 0ustar jssjss00000000000000# Copyright (C) 2007 Jeremy S. Sanders # Email: Jeremy Sanders # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ############################################################################## """A colorbar widget for the image widget. Should show the scale of the image.""" from __future__ import division from .. import qtall as qt4 import numpy as N from .. import document from .. import setting from .. import utils from . import widget from . import axis def _(text, disambiguation=None, context='ColorBar'): """Translate text.""" return qt4.QCoreApplication.translate(context, text, disambiguation) class ColorBar(axis.Axis): """Color bar for showing scale of image. This naturally is descended from an axis """ typename='colorbar' allowusercreation = True description = _('Image color bar') isaxis = False @classmethod def addSettings(klass, s): """Construct list of settings.""" axis.Axis.addSettings(s) s.add( setting.WidgetChoice('widgetName', '', descr=_('Corresponding widget'), widgettypes=('image', 'xy', 'nonorthpoint'), usertext = _('Widget')), 0 ) s.get('log').readonly = True s.get('datascale').readonly = True s.add( setting.AlignHorzWManual( 'horzPosn', 'right', descr = _('Horizontal position'), usertext=_('Horz posn'), formatting=True) ) s.add( setting.AlignVertWManual( 'vertPosn', 'bottom', descr = _('Vertical position'), usertext=_('Vert posn'), formatting=True) ) s.add( setting.DistanceOrAuto('width', 'Auto', descr = _('Width of colorbar'), usertext=_('Width'), formatting=True) ) s.add( setting.DistanceOrAuto('height', 'Auto', descr = _('Height of colorbar'), usertext=_('Height'), formatting=True) ) s.add( setting.Float( 'horzManual', 0., descr = _('Manual horizontal fractional position'), usertext=_('Horz manual'), formatting=True) ) s.add( setting.Float( 'vertManual', 0., descr = _('Manual vertical fractional position'), usertext=_('Vert manual'), formatting=True) ) s.add( setting.Line('Border', descr = _('Colorbar border line'), usertext=_('Border')), pixmap='settings_border') s.add( setting.SettingBackwardCompat('image', 'widgetName', None) ) @classmethod def allowedParentTypes(klass): from . import graph, grid, nonorthgraph return (graph.Graph, grid.Grid, nonorthgraph.NonOrthGraph) @property def userdescription(self): return _("widget='%s', label='%s'") % ( self.settings.widgetName, self.settings.label) def chooseName(self): """Get name of widget.""" # override axis naming of x and y return widget.Widget.chooseName(self) def _axisDraw(self, posn, parentposn, outerbounds, painter, phelper): """Do actual drawing.""" s = self.settings # get height of label font bounds = self.computeBounds(parentposn, phelper) font = s.get('Label').makeQFont(phelper) painter.setFont(font) fontheight = utils.FontMetrics(font, painter.device()).height() horz = s.direction == 'horizontal' # use above to estimate width and height if necessary w = s.get('width') if w.isAuto(): if horz: totalwidth = bounds[2] - bounds[0] - 2*fontheight else: totalwidth = fontheight else: totalwidth = w.convert(painter) h = s.get('height') if h.isAuto(): if horz: totalheight = fontheight else: totalheight = bounds[3] - bounds[1] - 2*fontheight else: totalheight = h.convert(painter) # work out horizontal position h = s.horzPosn if h == 'left': bounds[0] += fontheight bounds[2] = bounds[0] + totalwidth elif h == 'right': bounds[2] -= fontheight bounds[0] = bounds[2] - totalwidth elif h == 'centre': delta = (bounds[2]-bounds[0]-totalwidth)/2. bounds[0] += delta bounds[2] -= delta elif h == 'manual': bounds[0] += (bounds[2]-bounds[0])*s.horzManual bounds[2] = bounds[0] + totalwidth # work out vertical position v = s.vertPosn if v == 'top': bounds[1] += fontheight bounds[3] = bounds[1] + totalheight elif v == 'bottom': bounds[3] -= fontheight bounds[1] = bounds[3] - totalheight elif v == 'centre': delta = (bounds[3]-bounds[1]-totalheight)/2. bounds[1] += delta bounds[3] -= delta elif v == 'manual': bounds[1] += (bounds[3]-bounds[1])*s.vertManual bounds[3] = bounds[1] + totalheight # FIXME: this is ugly - update bounds in helper state phelper.states[(self,0)].bounds = bounds # do no painting if hidden or no image imgwidget = s.get('widgetName').findWidget() if s.hide: return bounds self.updateAxisLocation(bounds) # update image if necessary with new settings if imgwidget is not None: minval, maxval, axisscale, cmapname, trans, invert = \ imgwidget.getColorbarParameters() cmap = self.document.evaluate.getColormap(cmapname, invert) img = utils.makeColorbarImage( minval, maxval, axisscale, cmap, trans, direction=s.direction) else: # couldn't find widget minval, maxval, axisscale = 0., 1., 'linear' img = None s.get('log').setSilent(axisscale == 'log') self.setAutoRange([minval, maxval]) self.computePlottedRange(force=True) # now draw image on axis... minpix, maxpix = self.graphToPlotterCoords( bounds, N.array([minval, maxval]) ) routside = qt4.QRectF( bounds[0], bounds[1], bounds[2]-bounds[0], bounds[3]-bounds[1] ) # really draw the img if img is not None: # coordinates to draw image and to clip rectangle if s.direction == 'horizontal': c = [ minpix, bounds[1], maxpix, bounds[3] ] cl = [ self.coordParr1, bounds[1], self.coordParr2, bounds[3] ] else: c = [ bounds[0], maxpix, bounds[2], minpix ] cl = [ bounds[0], self.coordParr1, bounds[2], self.coordParr2 ] r = qt4.QRectF(c[0], c[1], c[2]-c[0], c[3]-c[1]) rclip = qt4.QRectF(cl[0], cl[1], cl[2]-cl[0], cl[3]-cl[1]) painter.save() painter.setClipRect(rclip & routside) painter.drawImage(r, img) painter.restore() # if there's a border if not s.Border.hide: painter.setPen( s.get('Border').makeQPen(painter) ) painter.setBrush( qt4.QBrush() ) painter.drawRect( routside ) # actually draw axis axis.Axis._axisDraw(self, bounds, parentposn, None, painter, phelper) # allow the factory to instantiate a colorbar document.thefactory.register( ColorBar ) veusz-3.0.1/veusz/widgets/shape.py0000664000175000017500000003327313262164277016627 0ustar jssjss00000000000000# Copyright (C) 2008 Jeremy S. Sanders # Email: Jeremy Sanders # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ############################################################################### """For plotting shapes.""" from __future__ import division, print_function import codecs import itertools import os from ..compat import czip, cbytes from .. import qtall as qt4 from .. import setting from .. import document from .. import utils from . import widget from . import controlgraph from . import plotters def _(text, disambiguation=None, context='Shape'): """Translate text.""" return qt4.QCoreApplication.translate(context, text, disambiguation) class Shape(plotters.FreePlotter): """A shape on a page/graph.""" def __init__(self, parent, name=None): plotters.FreePlotter.__init__(self, parent, name=name) @classmethod def addSettings(klass, s): """Construct list of settings.""" plotters.FreePlotter.addSettings(s) s.add( setting.ShapeFill('Fill', descr = _('Shape fill'), usertext=_('Fill')), pixmap = 'settings_bgfill' ) s.add( setting.Line('Border', descr = _('Shape border'), usertext=_('Border')), pixmap = 'settings_border' ) s.add( setting.Bool('clip', False, descr=_('Clip shape to its container'), usertext=_('Clip'), formatting=True) ) class BoxShape(Shape): """For drawing box-like shapes.""" def __init__(self, parent, name=None): Shape.__init__(self, parent, name=name) @classmethod def addSettings(klass, s): """Construct list of settings.""" Shape.addSettings(s) s.add( setting.DatasetExtended( 'width', [0.1], descr=_('List of fractional widths, dataset or expression'), usertext=_('Widths'), formatting=False), 3 ) s.add( setting.DatasetExtended( 'height', [0.1], descr=_('List of fractional heights, dataset or expression'), usertext=_('Heights'), formatting=False), 4 ) s.add( setting.DatasetExtended( 'rotate', [0.], descr=_('Rotation angles of shape, dataset or expression'), usertext=_('Rotate'), formatting=False), 5 ) def drawShape(self, painter, rect): pass def draw(self, posn, phelper, outerbounds = None): """Plot the key on a plotter.""" s = self.settings d = self.document if s.hide: return # get positions of shapes width = s.get('width').getFloatArray(d) height = s.get('height').getFloatArray(d) rotate = s.get('rotate').getFloatArray(d) if width is None or height is None or rotate is None: return # translate coordinates from axes or relative values xpos, ypos = self._getPlotterCoords(posn) if xpos is None or ypos is None: # we can't calculate coordinates return # if a dataset is used, we can't use control items isnotdataset = ( not s.get('xPos').isDataset(d) and not s.get('yPos').isDataset(d) and not s.get('width').isDataset(d) and not s.get('height').isDataset(d) and not s.get('rotate').isDataset(d) ) clip = None if s.clip: clip = qt4.QRectF( qt4.QPointF(posn[0], posn[1]), qt4.QPointF(posn[2], posn[3]) ) painter = phelper.painter(self, posn, clip=clip) with painter: # drawing settings for shape if not s.Border.hide: painter.setPen( s.get('Border').makeQPen(painter) ) else: painter.setPen( qt4.QPen(qt4.Qt.NoPen) ) # iterate over positions index = 0 dx, dy = posn[2]-posn[0], posn[3]-posn[1] x = y = w = h = r = None for x, y, w, h, r in czip(xpos, ypos, itertools.cycle(width), itertools.cycle(height), itertools.cycle(rotate)): wp, hp = dx*w, dy*h painter.save() painter.translate(x, y) if r != 0: painter.rotate(r) self.drawShape(painter, qt4.QRectF(-wp*0.5, -hp*0.5, wp, hp)) painter.restore() controlgraphitems = [] if x is not None and isnotdataset: cgi = controlgraph.ControlResizableBox( self, phelper, [x, y], [wp, hp], r, allowrotate=True) cgi.index = index cgi.widgetposn = posn index += 1 controlgraphitems.append(cgi) phelper.setControlGraph(self, controlgraphitems) def updateControlItem(self, cgi): """If control item is moved or resized, this is called.""" s = self.settings # calculate new position coordinate for item xpos, ypos = self._getGraphCoords(cgi.widgetposn, cgi.posn[0], cgi.posn[1]) if xpos is None or ypos is None: return xw = abs(cgi.dims[0] / (cgi.widgetposn[2]-cgi.widgetposn[0])) yw = abs(cgi.dims[1] / (cgi.widgetposn[1]-cgi.widgetposn[3])) # actually do the adjustment on the document xp = list(s.get('xPos').getFloatArray(self.document)) yp = list(s.get('yPos').getFloatArray(self.document)) w = list(s.get('width').getFloatArray(self.document)) h = list(s.get('height').getFloatArray(self.document)) r = list(s.get('rotate').getFloatArray(self.document)) xp[min(cgi.index, len(xp)-1)] = xpos yp[min(cgi.index, len(yp)-1)] = ypos w[min(cgi.index, len(w)-1)] = xw h[min(cgi.index, len(h)-1)] = yw r[min(cgi.index, len(r)-1)] = cgi.angle operations = ( document.OperationSettingSet(s.get('xPos'), xp), document.OperationSettingSet(s.get('yPos'), yp), document.OperationSettingSet(s.get('width'), w), document.OperationSettingSet(s.get('height'), h), document.OperationSettingSet(s.get('rotate'), r) ) self.document.applyOperation( document.OperationMultiple(operations, descr=_('adjust shape')) ) class Rectangle(BoxShape): """Draw a rectangle, or rounded rectangle.""" typename = 'rect' description = _('Rectangle') allowusercreation = True @classmethod def addSettings(klass, s): """Construct list of settings.""" BoxShape.addSettings(s) s.add( setting.Int('rounding', 0, minval=0, maxval=100, descr=_('Round corners with this percentage'), usertext=_('Rounding corners'), formatting=True) ) def drawShape(self, painter, rect): s = self.settings path = qt4.QPainterPath() if s.rounding == 0: path.addRect(rect) else: path.addRoundedRect(rect, s.rounding, s.rounding) utils.brushExtFillPath(painter, s.Fill, path, stroke=painter.pen()) class Ellipse(BoxShape): """Draw an ellipse.""" typename = 'ellipse' description = _('Ellipse') allowusercreation = True def drawShape(self, painter, rect): s = self.settings path = qt4.QPainterPath() path.addEllipse(rect) utils.brushExtFillPath(painter, s.Fill, path, stroke=painter.pen()) class ImageFile(BoxShape): """Draw an image.""" typename = 'imagefile' description = _('Image file') allowusercreation = True def __init__(self, parent, name=None): BoxShape.__init__(self, parent, name=name) self.cacheimage = None self.cachefilename = None self.cachestat = None self.cacheembeddata = None self.addAction( widget.Action('embed', self.actionEmbed, descr = _('Embed image in Veusz document ' 'to remove dependency on external file'), usertext = _('Embed image')) ) @classmethod def addSettings(klass, s): """Construct list of settings.""" BoxShape.addSettings(s) s.add( setting.ImageFilename('filename', '', descr=_('Image filename'), usertext=_('Filename'), formatting=False), posn=0 ) s.add( setting.Str('embeddedImageData', '', descr=_('Embedded base 64-encoded image data, ' 'used if filename set to {embedded}'), usertext=_('Embedded data'), hidden=True) ) s.add( setting.Bool('aspect', True, descr=_('Preserve aspect ratio'), usertext=_('Preserve aspect'), formatting=True), posn=0 ) s.Border.get('hide').newDefault(True) def actionEmbed(self): """Embed external image into veusz document.""" s = self.settings if s.filename == '{embedded}': print("Data already embedded") return # get data from external file try: f = open(s.filename, 'rb') data = f.read() f.close() except EnvironmentError: print("Could not find file. Not embedding.") return # convert to base 64 to make it nicer in the saved file encoded = codecs.encode(data, 'base64').decode('ascii') # now put embedded data in hidden setting ops = [ document.OperationSettingSet(s.get('filename'), '{embedded}'), document.OperationSettingSet(s.get('embeddedImageData'), encoded) ] self.document.applyOperation( document.OperationMultiple(ops, descr=_('embed image')) ) def updateCachedImage(self): """Update cache.""" s = self.settings self.cachestat = os.stat(s.filename) self.cacheimage = qt4.QImage(s.filename) self.cachefilename = s.filename def updateCachedEmbedded(self): """Update cached image from embedded data.""" s = self.settings self.cacheimage = qt4.QImage() # convert the embedded data from base64 and load into the image decoded = codecs.decode(s.embeddedImageData.encode('ascii'), 'base64') self.cacheimage.loadFromData(decoded) # we cache the data we have decoded self.cacheembeddata = s.embeddedImageData def drawShape(self, painter, rect): """Draw image.""" s = self.settings # draw border and fill painter.drawRect(rect) # check to see whether image needs reloading image = None if s.filename != '' and os.path.isfile(s.filename): if (self.cachefilename != s.filename or os.stat(s.filename) != self.cachestat): # update the image cache self.updateCachedImage() # clear any embedded image data self.settings.get('embeddedImageData').set('') image = self.cacheimage # or needs recreating from embedded data if s.filename == '{embedded}': if s.embeddedImageData is not self.cacheembeddata: self.updateCachedEmbedded() image = self.cacheimage # if no image, then use default image if ( not image or image.isNull() or image.width() == 0 or image.height() == 0 ): # load replacement image fname = os.path.join(utils.imagedir, 'button_imagefile.svg') r = qt4.QSvgRenderer(fname) r.render(painter, rect) else: # image rectangle irect = qt4.QRectF(image.rect()) # preserve aspect ratio if s.aspect: xr = rect.width() / irect.width() yr = rect.height() / irect.height() if xr > yr: rect = qt4.QRectF( rect.left()+(rect.width()-irect.width()*yr)*0.5, rect.top(), irect.width()*yr, rect.height()) else: rect = qt4.QRectF( rect.left(), rect.top()+(rect.height()-irect.height()*xr)*0.5, rect.width(), irect.height()*xr) # finally draw image painter.drawImage(rect, image, irect) document.thefactory.register( Ellipse ) document.thefactory.register( Rectangle ) document.thefactory.register( ImageFile ) veusz-3.0.1/veusz/widgets/volume3d.py0000664000175000017500000002224113302302030017226 0ustar jssjss00000000000000# Copyright (C) 2017 Jeremy S. Sanders # Email: Jeremy Sanders # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ############################################################################### """3D volume plotting widget.""" from __future__ import division, print_function import numpy as N from .. import qtall as qt from .. import setting from .. import document from .. import utils from ..helpers import threed from . import plotters3d def _(text, disambiguation=None, context='Volume3D'): """Translate text.""" return qt.QCoreApplication.translate(context, text, disambiguation) def autoedges(data, fillfactor): # make sure boxes do not overlap fillfactor = min(0.999, fillfactor) unique, idxs = N.unique(data, return_inverse=True) if len(unique) < 2: return None, None deltas = unique[1:]-unique[:-1] minvals = N.hstack(( [unique[0]-0.5*fillfactor*deltas[0]], unique[1:]-0.5*fillfactor*deltas)) maxvals = N.hstack(( unique[:-1]+0.5*fillfactor*deltas, [unique[-1]+0.5*fillfactor*deltas[-1]])) edges = N.column_stack((minvals,maxvals)) return edges, idxs class DataColor(setting.DataColor): def __init__(self, *args, **argsv): setting.DataColor.__init__(self, *args, **argsv) self.get('points').newDefault('v') class Volume3D(plotters3d.GenericPlotter3D): """Plotting points in 3D.""" typename='volume3d' description=_('3D volume') allowusercreation=True @classmethod def addSettings(klass, s): plotters3d.GenericPlotter3D.addSettings(s) s.add( setting.DatasetExtended( 'transData', '', descr=_('Transparency dataset, optional, 0-1'), usertext=_('Transparency')), 0 ) s.add( setting.DatasetExtended( 'zData', 'z', descr=_('Z dataset'), usertext=_('Z data')), 0 ) s.add( setting.DatasetExtended( 'yData', 'y', descr=_('Y dataset'), usertext=_('Y data')), 0 ) s.add( setting.DatasetExtended( 'xData', 'x', descr=_('X dataset'), usertext=_('X data')), 0 ) s.add(DataColor( 'DataColor', dimensions=1), 0) s.add(setting.Colormap( 'colorMap', 'grey', descr = _('Set of colors to plot data with'), usertext=_('Colormap'), formatting=True), 0) s.add(setting.Bool( 'colorInvert', False, descr = _('Invert color map'), usertext=_('Invert colormap'), formatting=True), 1) s.add(setting.Int( 'transparency', 50, descr = _('Transparency percentage'), usertext = _('Transparency'), minval = 0, maxval = 100, formatting=True), 2) s.add(setting.Int( 'reflectivity', 0, minval=0, maxval=100, descr=_('Reflectivity percentage'), usertext=_('Reflectivity'), formatting=True), 3) s.add(setting.Float( 'fillfactor', 1, minval=0, maxval=1, descr=_('Filling factor (0-1)'), usertext=_('Fill factor'), formatting=True), 4) s.add(setting.Line3D( 'Line', descr = _('Line settings'), usertext = _('Box line')), pixmap = 'settings_plotline' ) def affectsAxisRange(self): """Which axes this widget affects.""" s = self.settings return ((s.xAxis, 'sx'), (s.yAxis, 'sy'), (s.zAxis, 'sz')) def getRange(self, axis, depname, axrange): """Update axis range from data.""" dataname = {'sx': 'xData', 'sy': 'yData', 'sz': 'zData'}[depname] dsetn = self.settings.get(dataname) data = dsetn.getData(self.document) if axis.settings.log: def updateRange(v): with N.errstate(invalid='ignore'): chopzero = v[(v>0) & N.isfinite(v)] if len(chopzero) > 0: axrange[0] = min(axrange[0], chopzero.min()) axrange[1] = max(axrange[1], chopzero.max()) else: def updateRange(v): fvals = v[N.isfinite(v)] if len(fvals) > 0: axrange[0] = min(axrange[0], fvals.min()) axrange[1] = max(axrange[1], fvals.max()) if data: data.rangeVisit(updateRange) def _getdata(self, axes): s = self.settings doc = self.document xv = s.get('xData').getData(doc) yv = s.get('yData').getData(doc) zv = s.get('zData').getData(doc) vv = s.DataColor.get('points').getData(doc) if not xv or not yv or not zv or not vv: return trans = s.get('transData').getData(doc) # numpy arrays xv = xv.data yv = yv.data zv = zv.data vv = vv.data trans = None if trans is None else N.clip(trans.data, 0, 1) # trim to length minlen = min(len(xv),len(yv),len(zv),len(vv)) if trans is not None: minlen = min(minlen, len(trans)) xv = axes[0].transformToAxis(xv[:minlen]) yv = axes[1].transformToAxis(yv[:minlen]) zv = axes[2].transformToAxis(zv[:minlen]) vv = vv[:minlen] trans = None if trans is None else trans[:minlen] # get bits with valid coordinates valid = N.isfinite(xv) & N.isfinite(yv) & N.isfinite(zv) if not N.all(valid): xv = xv[valid] yv = yv[valid] zv = zv[valid] vv = vv[valid] trans = None if trans is None else trans[valid] # get edges of boxes in graph coordinates ff = self.settings.fillfactor xedges, xidxs = autoedges(xv, ff) yedges, yidxs = autoedges(yv, ff) zedges, zidxs = autoedges(zv, ff) if xedges is None or yedges is None or zedges is None: return # select finite values valid = N.isfinite(vv) if trans is not None: valid &= N.isfinite(trans) if not N.all(valid): xidxs = xidxs[valid] yidxs = yidxs[valid] zidxs = zidxs[valid] vv = vv[valid] trans = None if trans is None else trans[valid] # transform back edges from axis coordinates xedges = axes[0].transformFromAxis(xedges) yedges = axes[1].transformFromAxis(yedges) zedges = axes[2].transformFromAxis(zedges) return utils.Struct( xedges=xedges, xidxs=xidxs, yedges=yedges, yidxs=yidxs, zedges=zedges, zidxs=zidxs, vals=vv, trans=trans) def dataDrawToObject(self, painter, axes): """Do drawing of axis.""" s = self.settings axes = self.fetchAxes() if axes is None: return data = self._getdata(axes) if data is None: return cmap = self.document.evaluate.getColormap( s.colorMap, s.colorInvert) cdata = data.vals.reshape((1, data.vals.size)) if data.trans is not None: transimg = data.trans.reshape((1, data.vals.size)) else: transimg = None colorimg = utils.applyColorMap( cmap, s.DataColor.scaling, cdata, s.DataColor.min, s.DataColor.max, s.transparency, transimg=transimg) surfprop = threed.SurfaceProp(refl=s.reflectivity*0.01) surfprop.setRGBs(colorimg) lineprop = s.Line.makeLineProp(painter) # convert from axis coordinates xedges = axes[0].dataToLogicalCoords(data.xedges) yedges = axes[1].dataToLogicalCoords(data.yedges) zedges = axes[2].dataToLogicalCoords(data.zedges) # get minimum and maximum of cubes xmin = threed.ValVector(xedges[data.xidxs,0]) xmax = threed.ValVector(xedges[data.xidxs,1]) ymin = threed.ValVector(yedges[data.yidxs,0]) ymax = threed.ValVector(yedges[data.yidxs,1]) zmin = threed.ValVector(zedges[data.zidxs,0]) zmax = threed.ValVector(zedges[data.zidxs,1]) clipcont = self.makeClipContainer(axes) cuboids = threed.MultiCuboid( xmin, xmax, ymin, ymax, zmin, zmax, lineprop, surfprop) clipcont.addObject(cuboids) clipcont.assignWidgetId(id(self)) return clipcont document.thefactory.register(Volume3D) veusz-3.0.1/veusz/widgets/axisbroken.py0000664000175000017500000003561313324614312017661 0ustar jssjss00000000000000# Copyright (C) 2013 Jeremy S. Sanders # Email: Jeremy Sanders # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ############################################################################## '''An axis which can be broken in places.''' from __future__ import division import bisect import numpy as N from ..compat import crange, czip from .. import qtall as qt4 from .. import setting from .. import document from .. import utils from . import axis from . import controlgraph def _(text, disambiguation=None, context='BrokenAxis'): '''Translate text.''' return qt4.QCoreApplication.translate(context, text, disambiguation) class AxisBroken(axis.Axis): '''An axis widget which can have gaps in it.''' typename = 'axis-broken' description = 'Axis with breaks in it' def __init__(self, parent, name=None): """Initialise axis.""" axis.Axis.__init__(self, parent, name=name) self.rangeswitch = None self.breakchangeset = -1 @classmethod def addSettings(klass, s): '''Construct list of settings.''' axis.Axis.addSettings(s) s.add( setting.FloatList( 'breakPoints', [], descr = _('Pairs of values to start and stop breaks'), usertext = _('Break pairs'), ), 4 ) s.add( setting.FloatList( 'breakPosns', [], descr = _('Positions (fractions) along axis where to break'), usertext = _('Break positions'), formatting=True, ) ) def switchBreak(self, num, posn, otherposition=None): """Switch to break given (or None to disable).""" self.rangeswitch = num if num is None: self.plottedrange = self.orig_plottedrange else: self.plottedrange = [self.breakvstarts[num], self.breakvstops[num]] self.updateAxisLocation(posn, otherposition=otherposition) def plotterToGraphCoords(self, bounds, vals): """Convert values in plotter coordinates to data values. This needs to know about whether we've not switched between the breaks. Note that this implementation is very slow! Hopefully it won't be called often. """ if self.rangeswitch is not None: return axis.Axis.plotterToGraphCoords(self, bounds, vals) # support single int/float values try: iter(vals) issingle = False except TypeError: vals = N.array([vals]) issingle = True # scaled to be fractional coordinates in bounds if self.settings.direction == 'horizontal': svals = (vals - bounds[0]) / (bounds[2] - bounds[0]) else: svals = (vals - bounds[3]) / (bounds[1] - bounds[3]) # first work out which break region the values are in out = [] for sval, val in czip(svals, vals): # find index for appropriated scaled starting value breaki = bisect.bisect_left(self.posstarts, sval) - 1 if ( breaki >= 0 and breaki < self.breakvnum and sval <= self.posstops[breaki] ): self.switchBreak(breaki, bounds) coord = axis.Axis.plotterToGraphCoords( self, bounds, N.array([val]))[0] else: coord = N.nan out.append(coord) self.switchBreak(None, bounds) if issingle: return out[0] else: return N.array(out) def _graphToPlotter(self, vals): """Convert graph values to plotter coords. This could be slow if no range selected """ if self.rangeswitch is not None: return axis.Axis._graphToPlotter(self, vals) out = [] for val in vals: breaki = bisect.bisect_left(self.breakvstarts, val) - 1 if breaki >= 0 and breaki < self.breakvnum: if val > self.breakvstops[breaki] and breaki < self.breakvnum-1: # in gap, so use half-value coord = 0.5*(self.posstops[breaki]+self.posstarts[breaki+1]) b = self.currentbounds if self.settings.direction == 'horizontal': coord = coord*(b[2] - b[0]) + b[0] else: coord = coord*(b[3] - b[1]) + b[1] else: # lookup value self.switchBreak(breaki, self.currentbounds) coord = axis.Axis._graphToPlotter(self, N.array([val])) else: coord = N.nan out.append(coord) self.switchBreak(None, self.currentbounds) return N.array(out) def updateAxisLocation(self, bounds, otherposition=None): """Recalculate broken axis positions.""" s = self.settings if self.document.changeset != self.breakchangeset: self.breakchangeset = self.document.changeset # actually start and stop values on axis num = len(s.breakPoints) // 2 posns = list(s.breakPosns) posns.sort() # add on more break positions if not specified if len(posns) < num: start = 0. if len(posns) != 0: start = posns[-1] posns = posns + list( N.arange(1,num-len(posns)+1) * ( (1.-start) / (num-len(posns)+1) + start )) # fractional difference between starts and stops breakgap = 0.05 # collate fractional positions for starting and stopping starts = [0.] stops = [] for pos in posns: stops.append( pos - breakgap/2. ) starts.append( pos + breakgap/2. ) stops.append(1.) # scale according to allowable range d = s.upperPosition - s.lowerPosition self.posstarts = N.array(starts)*d + s.lowerPosition self.posstops = N.array(stops)*d + s.lowerPosition # pass lower and upper ranges if a particular range is chosen if self.rangeswitch is None: lowerupper = None else: lowerupper = ( self.posstarts[self.rangeswitch], self.posstops[self.rangeswitch] ) axis.Axis.updateAxisLocation( self, bounds, otherposition=otherposition, lowerupperposition=lowerupper) def computePlottedRange(self): """Given range of data, recompute stops and start values of breaks.""" axis.Axis.computePlottedRange(self) r = self.orig_plottedrange = self.plottedrange points = list(self.settings.breakPoints) points.sort() if r[1] < r[0]: points.reverse() # filter to range newpoints = [] for i in crange(0, len(points)//2 * 2, 2): if points[i] >= min(r) and points[i+1] <= max(r): newpoints += [points[i], points[i+1]] self.breakvnum = num = len(newpoints)//2 + 1 self.breakvlist = [self.plottedrange[0]] + newpoints + [ self.plottedrange[1]] # axis values for starting and stopping self.breakvstarts = [ self.breakvlist[i*2] for i in crange(num) ] self.breakvstops = [ self.breakvlist[i*2+1] for i in crange(num) ] # compute ticks for each range self.minorticklist = [] self.majorticklist = [] for i in crange(self.breakvnum): self.plottedrange = [self.breakvstarts[i], self.breakvstops[i]] reverse = self.plottedrange[0] > self.plottedrange[1] if reverse: self.plottedrange.reverse() self.computeTicks(allowauto=False) if reverse: self.plottedrange.reverse() self.minorticklist.append(self.minortickscalc) self.majorticklist.append(self.majortickscalc) self.plottedrange = self.orig_plottedrange def _drawAutoMirrorTicks(self, posn, painter): """Mirror axis to opposite side of graph if there isn't an axis there already.""" # swap axis to other side s = self.settings if s.otherPosition < 0.5: otheredge = 1. else: otheredge = 0. # temporarily change position of axis to other side for drawing self.updateAxisLocation(posn, otherposition=otheredge) if not s.Line.hide: self._drawAxisLine(painter, posn) for i in crange(self.breakvnum): self.switchBreak(i, posn, otherposition=otheredge) # plot coordinates of ticks coordticks = self._graphToPlotter(self.majorticklist[i]) coordminorticks = self._graphToPlotter(self.minorticklist[i]) if not s.MinorTicks.hide: self._drawMinorTicks(painter, coordminorticks) if not s.MajorTicks.hide: self._drawMajorTicks(painter, coordticks) self.switchBreak(None, posn) def _drawAxisLine(self, painter, posn): """Draw the line of the axis, indicating broken positions. We currently use a triangle to mark the broken position """ # these are x and y, or y and x coordinates p1 = [self.posstarts[0]] p2 = [0.] # mirror shape using this setting markdirn = -1 if self.coordReflected: markdirn = -markdirn # add shape for each break for start, stop in czip( self.posstarts[1:], self.posstops[:-1] ): p1 += [stop, (start+stop)*0.5, start] p2 += [0, markdirn*(start-stop)*0.5, 0] # end point p1.append(self.posstops[-1]) p2.append(0.) # scale points by length of axis and add correct origin s = self.settings if s.direction == 'vertical': delta = posn[1]-posn[3] minv = posn[3] else: delta = posn[2]-posn[0] minv = posn[0] p1 = N.array(p1)*delta + minv p2 = N.array(p2)*delta + self.coordPerp if s.direction == 'vertical': p1, p2 = p2, p1 # convert to polygon and draw poly = qt4.QPolygonF() utils.addNumpyToPolygonF(poly, p1, p2) pen = s.get('Line').makeQPen(painter) pen.setCapStyle(qt4.Qt.FlatCap) painter.setPen(pen) painter.drawPolyline(poly) def drawGrid(self, parentposn, phelper, outerbounds=None, ontop=False): """Code to draw gridlines. This is separate from the main draw routine because the grid should be behind/infront the data points. """ s = self.settings if ( s.hide or (s.MinorGridLines.hide and s.GridLines.hide) or s.GridLines.onTop != bool(ontop) ): return # draw grid on a different layer, depending on whether on top or not layer = (-2, -1)[bool(ontop)] painter = phelper.painter(self, parentposn, layer=layer) self.updateAxisLocation(parentposn) with painter: painter.save() painter.setClipRect( qt4.QRectF( qt4.QPointF(parentposn[0], parentposn[1]), qt4.QPointF(parentposn[2], parentposn[3]) ) ) for i in crange(self.breakvnum): self.switchBreak(i, parentposn) if not s.MinorGridLines.hide: coordminorticks = self._graphToPlotter(self.minorticklist[i]) self._drawGridLines('MinorGridLines', painter, coordminorticks, parentposn) if not s.GridLines.hide: coordticks = self._graphToPlotter(self.majorticklist[i]) self._drawGridLines('GridLines', painter, coordticks, parentposn) self.switchBreak(None, parentposn) painter.restore() def _axisDraw(self, posn, parentposn, outerbounds, painter, phelper): """Main drawing routine of axis.""" s = self.settings # multiplication factor if reflection on the axis is requested sign = 1 if s.direction == 'vertical': sign *= -1 if self.coordReflected: sign *= -1 # keep track of distance from axis # text to output texttorender = [] # plot the line along the axis if not s.Line.hide: self._drawAxisLine(painter, posn) max_delta = 0 for i in crange(self.breakvnum): self.switchBreak(i, posn) if self.plottedrange[0]==self.plottedrange[1]: continue # plot coordinates of ticks coordticks = self._graphToPlotter(self.majorticklist[i]) coordminorticks = self._graphToPlotter(self.minorticklist[i]) self._delta_axis = 0 # plot minor ticks if not s.MinorTicks.hide: self._drawMinorTicks(painter, coordminorticks) # plot major ticks if not s.MajorTicks.hide: self._drawMajorTicks(painter, coordticks) # plot tick labels suppresstext = self._suppressText(painter, parentposn, outerbounds) if not s.TickLabels.hide and not suppresstext: self._drawTickLabels(phelper, painter, coordticks, sign, outerbounds, self.majorticklist[i], texttorender) # this is the maximum delta of any of the breaks max_delta = max(max_delta, self._delta_axis) self.switchBreak(None, posn) self._delta_axis = max_delta # draw an axis label if not s.Label.hide and not suppresstext: self._drawAxisLabel(painter, sign, outerbounds, texttorender) self._drawTextWithoutOverlap(painter, texttorender) # make control item for axis phelper.setControlGraph(self, [ controlgraph.ControlAxisLine( self, phelper, self.settings.direction, self.coordParr1, self.coordParr2, self.coordPerp, posn) ]) # allow the factory to instantiate the widget document.thefactory.register( AxisBroken ) veusz-3.0.1/veusz/widgets/image.py0000664000175000017500000002707013164703032016574 0ustar jssjss00000000000000# Copyright (C) 2005 Jeremy S. Sanders # Email: Jeremy Sanders # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ############################################################################### """Image plotting from 2d datasets.""" from __future__ import division from .. import qtall as qt4 import numpy as N from .. import setting from .. import document from .. import utils from . import plotters def _(text, disambiguation=None, context='Image'): """Translate text.""" return qt4.QCoreApplication.translate(context, text, disambiguation) def cropLinearImageToBox(image, pltx, plty, posn): """Given a plotting range pltx[0]->pltx[1], plty[0]->plty[1] and plotting bounds posn, return an image which is cropped to posn. Returns: - updated pltx range - updated plty range - cropped image """ x1, y1, x2, y2 = posn pltx1, pltx2 = pltx pltw = pltx2-pltx1 plty2, plty1 = plty plth = plty2-plty1 imw = image.width() imh = image.height() pixw = pltw / imw pixh = plth / imh cutr = [0, 0, imw-1, imh-1] # work out where image intercepts posn, and make sure image # fills at least that area # need to chop left if pltx1 < x1: d = int((x1-pltx1) / pixw) cutr[0] += d pltx[0] += d*pixw # need to chop right if pltx2 > x2: d = max(0, int((pltx2-x2) / pixw) - 1) cutr[2] -= d pltx[1] -= d*pixw # chop top if plty1 < y1: d = int((y1-plty1) / pixh) cutr[1] += d plty[1] += d*pixh # chop bottom if plty2 > y2: d = max(0, int((plty2-y2) / pixh) - 1) cutr[3] -= d plty[0] -= d*pixh # create chopped-down image newimage = image.copy( cutr[0], cutr[1], cutr[2]-cutr[0]+1, cutr[3]-cutr[1]+1) # return new image coordinates and image return pltx, plty, newimage def cropGridImageToBox(image, gridx, gridy, posn): """Given an image, pixel coordinates and box, crop image to box.""" def trimGrid(grid, p1, p2): """Trim grid to bounds given, returning index range.""" if grid[0] < grid[-1]: # fwd order i1 = max(N.searchsorted(grid, p1, side='right')-1, 0) i2 = min(N.searchsorted(grid, p2, side='left'), len(grid)) + 1 else: # reverse order of grid gridr = grid[::-1] i1 = max( len(grid) - N.searchsorted(gridr, p2, side='left')-1, 0) i2 = min( len(grid) - N.searchsorted(gridr, p1, side='right'), len(grid) ) + 1 return i1, i2 def trimEdge(grid, minval, maxval): """Trim outer gridpoints to minval and maxval.""" if grid[0] < grid[-1]: grid[0] = max(grid[0], minval) grid[-1] = min(grid[-1], maxval) else: grid[0] = min(grid[0], maxval) grid[-1] = max(grid[-1], minval) # see whether cropping necessary x1, x2 = trimGrid(gridx, posn[0], posn[2]) y1, y2 = trimGrid(gridy, posn[1], posn[3]) if x1 > 0 or y1 > 0 or x2 < len(gridx)-1 or y2 < len(gridy)-1: # do cropping image = image.copy(x1, len(gridy)-y2, x2-x1-1, y2-y1-1) gridx = N.array(gridx[x1:x2]) gridy = N.array(gridy[y1:y2]) # trim outer grid point to viewable range trimEdge(gridx, posn[0], posn[2]) trimEdge(gridy, posn[1], posn[3]) return gridx, gridy, image class Image(plotters.GenericPlotter): """A class which plots an image on a graph with a specified coordinate system.""" typename='image' allowusercreation=True description=_('Plot a 2d dataset as an image') @classmethod def addSettings(klass, s): """Construct list of settings.""" plotters.GenericPlotter.addSettings(s) s.add( setting.DatasetExtended( 'data', '', dimensions = 2, descr = _('Dataset to plot'), usertext=_('Dataset')), 0 ) s.add( setting.FloatOrAuto( 'min', 'Auto', descr = _('Minimum value of image scale'), usertext=_('Min. value')), 1 ) s.add( setting.FloatOrAuto( 'max', 'Auto', descr = _('Maximum value of image scale'), usertext=_('Max. value')), 2 ) s.add( setting.Choice( 'colorScaling', ['linear', 'sqrt', 'log', 'squared'], 'linear', descr = _('Scaling to transform numbers to color'), usertext=_('Scaling')), 3 ) s.add( setting.DatasetExtended( 'transparencyData', '', dimensions = 2, descr = _('Dataset to use for transparency (0 to 1)'), usertext=_('Trans. data')), 4 ) s.add( setting.Colormap( 'colorMap', 'grey', descr = _('Set of colors to plot data with'), usertext=_('Colormap'), formatting=True), 5 ) s.add( setting.Bool( 'colorInvert', False, descr = _('Invert color map'), usertext=_('Invert colormap'), formatting=True), 6 ) s.add( setting.Int( 'transparency', 0, descr = _('Transparency percentage'), usertext = _('Transparency'), minval = 0, maxval = 100, formatting=True), 7 ) s.add( setting.Bool( 'smooth', False, descr = _('Smooth image to display resolution'), usertext = _('Smooth'), formatting = True ) ) @property def userdescription(self): """User friendly description.""" s = self.settings out = [] if s.data: out.append(s.data) out += [s.colorScaling, s.colorMap] return ', '.join(out) def getDataValueRange(self, data): """Update data range from data.""" s = self.settings minval = s.min if minval == 'Auto': if data is not None: minval = N.nanmin(data.data) else: minval = 0. maxval = s.max if maxval == 'Auto': if data is not None: maxval = N.nanmax(data.data) else: maxval = minval + 1 # this is used currently by colorbar objects return (minval, maxval) def affectsAxisRange(self): """Range information provided by widget.""" s = self.settings return ( (s.xAxis, 'sx'), (s.yAxis, 'sy') ) def getRange(self, axis, depname, axrange): """Automatically determine the ranges of variable on the axes.""" # this is copied from Image, probably should combine s = self.settings d = self.document # return if no data data = s.get('data').getData(d) if data is None or data.dimensions != 2: return xr, yr = data.getDataRanges() if depname == 'sx': axrange[0] = min( axrange[0], xr[0] ) axrange[1] = max( axrange[1], xr[1] ) elif depname == 'sy': axrange[0] = min( axrange[0], yr[0] ) axrange[1] = max( axrange[1], yr[1] ) def getColorbarParameters(self): """Return parameters for colorbar.""" s = self.settings d = self.document data = s.get('data').getData(d) minval, maxval = self.getDataValueRange(data) return (minval, maxval, s.colorScaling, s.colorMap, s.transparency, s.colorInvert) def dataDraw(self, painter, axes, posn, clip): """Draw image.""" s = self.settings d = self.document data = s.get('data').getData(d) if s.hide or data is None or data.dimensions != 2: return transimg = s.get('transparencyData').getData(d) if transimg is not None: transimg = transimg.data rangex, rangey = data.getDataRanges() pltrangex = axes[0].dataToPlotterCoords(posn, N.array(rangex)) pltrangey = axes[1].dataToPlotterCoords(posn, N.array(rangey)) # abort if coordinate range is too small if(abs(pltrangex[0]-pltrangex[1])<1e-2 or abs(pltrangey[0]-pltrangey[1])<1e-2): return # make QImage from data cmap = d.evaluate.getColormap(s.colorMap, s.colorInvert) datavaluerange = self.getDataValueRange(data) image = utils.applyColorMap( cmap, s.colorScaling, data.data, datavaluerange[0], datavaluerange[1], s.transparency, transimg=transimg) if data.isLinearImage(): # linearly spaced grid if ( pltrangex[0] < posn[0] or pltrangex[1] > posn[2] or pltrangey[0] < posn[1] or pltrangey[1] > posn[3] ): # need to crop image pltrangex, pltrangey, image = cropLinearImageToBox( image, pltrangex, pltrangey, posn) else: # get pixel edges, converted to plotter coordinates xedgep, yedgep = data.getPixelEdges( scalefnx=lambda v: axes[0].dataToPlotterCoords(posn, v), scalefny=lambda v: axes[1].dataToPlotterCoords(posn, v)) # crop any pixels completely outside posn xedgep, yedgep, image = cropGridImageToBox( image, xedgep, yedgep, posn) # make image on linear grid image = utils.resampleLinearImage(image, xedgep, yedgep) pltrangex = xedgep[0], xedgep[-1] pltrangey = yedgep[0], yedgep[-1] # optionally smooth images before displaying if s.smooth: image = image.scaled( pltrangex[1]-pltrangex[0], pltrangey[0]-pltrangey[1], qt4.Qt.IgnoreAspectRatio, qt4.Qt.SmoothTransformation) # get position and size of output image xp, yp = pltrangex[0], pltrangey[1] xw = pltrangex[1]-pltrangex[0] yw = pltrangey[0]-pltrangey[1] # invert output drawing if axes go from positive->negative # we only translate the coordinate system if this is the case xscale = 1 if xw > 0 else -1 yscale = 1 if yw > 0 else -1 if xscale != 1 or yscale != 1: painter.save() painter.translate(xp, yp) xp = yp = 0 painter.scale(xscale, yscale) # draw image #image = image.copy(qt4.QRect(qt4.QPoint(0, 0), qt4.QPoint(20, 20))) painter.drawImage(qt4.QRectF(xp, yp, abs(xw), abs(yw)), image) # restore painter if image was inverted if xscale != 1 or yscale != 1: painter.restore() # allow the factory to instantiate an image document.thefactory.register(Image) veusz-3.0.1/veusz/widgets/function3d.py0000664000175000017500000004104313306262326017566 0ustar jssjss00000000000000# Copyright (C) 2014 Jeremy S. Sanders # Email: Jeremy Sanders # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ############################################################################### """3D function plotting widget.""" from __future__ import division, print_function import numpy as N from .. import qtall as qt from .. import setting from .. import document from .. import utils from ..helpers import threed from . import plotters3d def _(text, disambiguation=None, context='Function3D'): """Translate text.""" return qt.QCoreApplication.translate(context, text, disambiguation) class FunctionSurface(setting.Surface3DWColorMap): def __init__(self, *args, **argsv): setting.Surface3DWColorMap.__init__(self, *args, **argsv) self.get('color').newDefault(setting.Reference('../color')) class FunctionLine(setting.Line3DWColorMap): def __init__(self, *args, **argsv): setting.Line3DWColorMap.__init__(self, *args, **argsv) self.get('color').newDefault(setting.Reference('../color')) self.get('reflectivity').newDefault(20) class Function3D(plotters3d.GenericPlotter3D): """Plotting functions in 3D.""" typename='function3d' description=_('3D function') allowusercreation=True # list of the supported modes _modes = [ 'x=fn(y,z)', 'y=fn(x,z)', 'z=fn(x,y)', 'x,y,z=fns(t)', 'x,y=fns(z)', 'y,z=fns(x)', 'x,z=fns(y)' ] # which axes are affected by which modes _affects = { 'z=fn(x,y)': (('zAxis', 'both'),), 'x=fn(y,z)': (('xAxis', 'both'),), 'y=fn(x,z)': (('yAxis', 'both'),), 'x,y,z=fns(t)': (('xAxis', 'sx'), ('yAxis', 'sy'), ('zAxis', 'sz')), 'x,y=fns(z)': (('xAxis', 'both'), ('yAxis', 'both')), 'y,z=fns(x)': (('yAxis', 'both'), ('zAxis', 'both')), 'x,z=fns(y)': (('xAxis', 'both'), ('zAxis', 'both')), } # which modes require which axes as inputs _requires = { 'z=fn(x,y)': (('both', 'xAxis'), ('both', 'yAxis')), 'x=fn(y,z)': (('both', 'yAxis'), ('both', 'zAxis')), 'y=fn(x,z)': (('both', 'xAxis'), ('both', 'zAxis')), 'x,y,z=fns(t)': (), 'x,y=fns(z)': (('both', 'zAxis'),), 'y,z=fns(x)': (('both', 'xAxis'),), 'x,z=fns(y)': (('both', 'yAxis'),), } # which modes require which variables _varmap = { 'x,y=fns(z)': ('x', 'y', 'z'), 'y,z=fns(x)': ('y', 'z', 'x'), 'x,z=fns(y)': ('x', 'z', 'y'), } @staticmethod def _fnsetnshowhide(v): """Return which function settings to show or hide depending on mode.""" return { 'z=fn(x,y)': (('fnz',), ('fnx', 'fny')), 'x=fn(y,z)': (('fnx',), ('fny', 'fnz')), 'y=fn(x,z)': (('fny',), ('fnx', 'fnz')), 'x,y,z=fns(t)': (('fnx', 'fny', 'fnz'), ()), 'x,y=fns(z)': (('fnx', 'fny'), ('fnz',)), 'y,z=fns(x)': (('fny', 'fnz'), ('fnx',)), 'x,z=fns(y)': (('fnx', 'fnz'), ('fny',)), }[v] @classmethod def addSettings(klass, s): plotters3d.GenericPlotter3D.addSettings(s) s.add(setting.Int( 'linesteps', 50, minval = 3, descr = _('Number of steps to evaluate the function over for lines'), usertext=_('Line steps'), formatting=True )) s.add(setting.Int( 'surfacesteps', 20, minval = 3, descr = _('Number of steps to evaluate the function over for surfaces' ' in each direction'), usertext=_('Surface steps'), formatting=True )) s.add(setting.ChoiceSwitch( 'mode', klass._modes, 'x,y,z=fns(t)', descr=_('Type of function to plot'), usertext=_('Mode'), showfn=klass._fnsetnshowhide), 0) s.add(setting.Str( 'fnx', '', descr=_('Function for x coordinate'), usertext=_('X function') ), 1) s.add(setting.Str( 'fny', '', descr=_('Function for y coordinate'), usertext=_('Y function') ), 2) s.add(setting.Str( 'fnz', '', descr=_('Function for z coordinate'), usertext=_('Z function') ), 3) s.add(setting.Str( 'fncolor', '', descr=_('Function to give color (0-1)'), usertext=_('Color function') ), 4) s.add( setting.Color( 'color', 'auto', descr = _('Master color'), usertext = _('Color'), formatting=True), 0 ) s.add(FunctionLine( 'Line', descr = _('Line settings'), usertext = _('Plot line')), pixmap = 'settings_plotline' ) s.add(setting.LineGrid3D( 'GridLine', descr = _('Grid line settings'), usertext = _('Grid line')), pixmap = 'settings_gridline' ) s.add(FunctionSurface( 'Surface', descr = _('Surface fill settings'), usertext=_('Surface')), pixmap='settings_bgfill' ) def affectsAxisRange(self): """Which axes this widget affects.""" s = self.settings affects = self._affects[s.mode] return [(getattr(s, v[0]), v[1]) for v in affects] def requiresAxisRange(self): """Which axes this widget depends on.""" s = self.settings requires = self._requires[s.mode] return [(v[0], getattr(s, v[1])) for v in requires] def getLineVals(self): """Get vals for line plot by evaluating function.""" s = self.settings mode = s.mode if mode == 'x,y,z=fns(t)': if not s.fnx or not s.fny or not s.fnz: return None xcomp = self.document.evaluate.compileCheckedExpression(s.fnx) ycomp = self.document.evaluate.compileCheckedExpression(s.fny) zcomp = self.document.evaluate.compileCheckedExpression(s.fnz) if xcomp is None or ycomp is None or zcomp is None: return None # evaluate each expression env = self.document.evaluate.context.copy() env['t'] = N.linspace(0, 1, s.linesteps) zeros = N.zeros(s.linesteps, dtype=N.float64) try: valsx = eval(xcomp, env) + zeros valsy = eval(ycomp, env) + zeros valsz = eval(zcomp, env) + zeros except: # something wrong in the evaluation return None fncolor = s.fncolor.strip() if fncolor: fncolor = self.document.evaluate.compileCheckedExpression( fncolor) try: valscolor = eval(fncolor, env) + zeros except: return None else: valscolor = None retn = (valsx, valsy, valsz, valscolor) else: # lookup variables to go with function var = self._varmap[mode] fns = [getattr(s, 'fn'+var[0]), getattr(s, 'fn'+var[1])] if not fns[0] or not fns[1]: return None # get points to evaluate functions over axis = self.fetchAxis(var[2]) if not axis: return arange = axis.getPlottedRange() if axis.settings.log: evalpts = N.logspace( N.log10(arange[0]), N.log10(arange[1]), s.linesteps) else: evalpts = N.linspace(arange[0], arange[1], s.linesteps) # evaluate expressions env = self.document.evaluate.context.copy() env[var[2]] = evalpts zeros = N.zeros(s.linesteps, dtype=N.float64) try: vals1 = eval(fns[0], env) + zeros vals2 = eval(fns[1], env) + zeros except: # something wrong in the evaluation return None fncolor = s.fncolor.strip() if fncolor: fncolor = self.document.evaluate.compileCheckedExpression( fncolor) try: valscolor = eval(fncolor, env) + zeros except: return None else: valscolor = None # assign correct output points retn = [None]*4 idxs = ('x', 'y', 'z') retn[idxs.index(var[0])] = vals1 retn[idxs.index(var[1])] = vals2 retn[idxs.index(var[2])] = evalpts retn[3] = valscolor return retn def getGridVals(self): """Get values for 2D grid. Return steps1, steps2, height, axidx, depvariable axidx are the indices into the axes for height, step1, step2 """ s = self.settings mode = s.mode var, ovar1, ovar2, axidx = { 'x=fn(y,z)': ('x', 'y', 'z', (0, 1, 2)), 'y=fn(x,z)': ('y', 'z', 'x', (1, 2, 0)), 'z=fn(x,y)': ('z', 'x', 'y', (2, 0, 1)), }[mode] axes = self.fetchAxes() if axes is None: return None # range of other axes ax1, ax2 = axes[axidx[1]], axes[axidx[2]] pr1 = ax1.getPlottedRange() pr2 = ax2.getPlottedRange() steps = s.surfacesteps logax1, logax2 = ax1.settings.log, ax2.settings.log # convert log ranges to linear temporarily if logax1: pr1 = N.log(pr1) if logax2: pr2 = N.log(pr2) # set variables in environment grid1, grid2 = N.indices((steps, steps)) del1 = (pr1[1]-pr1[0])/(steps-1.) steps1 = N.arange(steps)*del1 + pr1[0] grid1 = grid1*del1 + pr1[0] del2 = (pr2[1]-pr2[0])/(steps-1.) steps2 = N.arange(steps)*del2 + pr2[0] grid2 = grid2*del2 + pr2[0] fncolor = s.fncolor.strip() if fncolor: colgrid1 = 0.5*(grid1[1:,1:]+grid1[:-1,:-1]) colgrid2 = 0.5*(grid2[1:,1:]+grid2[:-1,:-1]) if logax1: colgrid1 = N.exp(colgrid1) if logax2: colgrid2 = N.exp(colgrid2) # convert back to log if logax1: grid1 = N.exp(grid1) if logax2: grid2 = N.exp(grid2) env = self.document.evaluate.context.copy() env[ovar1] = grid1 env[ovar2] = grid2 fn = getattr(s, 'fn%s' % var) # get function from user if not fn: return comp = self.document.evaluate.compileCheckedExpression(fn) if comp is None: return None try: height = eval(comp, env) + N.zeros(grid1.shape, dtype=N.float64) except Exception: # something wrong in the evaluation return None if fncolor: compcolor = self.document.evaluate.compileCheckedExpression( fncolor) if not compcolor: return env[ovar1] = colgrid1 env[ovar2] = colgrid2 try: colors = eval(compcolor, env) + N.zeros( colgrid1.shape, dtype=N.float64) except Exception: # something wrong in the evaluation return None colors = N.clip(colors, 0, 1) else: colors = None return height, steps1, steps2, axidx, var, colors def getRange(self, axis, depname, axrange): """Get range of axis.""" mode = self.settings.mode if mode == 'x,y,z=fns(t)': # get range of each variable retn = self.getLineVals() if not retn: return valsx, valsy, valsz, valscolor = retn coord = {'sx': valsx, 'sy': valsy, 'sz': valsz}[depname] elif mode in ('x,y=fns(z)', 'y,z=fns(x)', 'x,z=fns(y)'): # is this axis one of the ones we affect? var = self._varmap[mode] if self.fetchAxis(var[0]) is axis: v = var[0] elif self.fetchAxis(var[1]) is axis: v = var[1] else: return retn = self.getLineVals() if not retn: return coord = retn[('x', 'y', 'z').index(v)] elif mode in ('z=fn(x,y)', 'x=fn(y,z)', 'y=fn(x,z)'): retn = self.getGridVals() if not retn: return height, steps1, steps2, axidx, var, color = retn if axis is not self.fetchAxis(var): return coord = height finite = coord[N.isfinite(coord)] if len(finite) > 0: axrange[0] = min(axrange[0], finite.min()) axrange[1] = max(axrange[1], finite.max()) def updatePropColorMap(self, prop, setn, colorvals): """Update line/surface properties given color map values. prop is updated to use the data values colorvars (0-1) to apply a color map from the setting setn given.""" cmap = self.document.evaluate.getColormap( setn.colorMap, setn.colorMapInvert) color2d = colorvals.reshape((1, colorvals.size)) colorimg = utils.applyColorMap( cmap, 'linear', color2d, 0., 1., setn.transparency) prop.setRGBs(colorimg) def dataDrawSurface(self, painter, axes, container): """Draw a surface plot.""" retn = self.getGridVals() if not retn: return height, steps1, steps2, axidx, depvar, colors = retn lheight = axes[axidx[0]].dataToLogicalCoords(height) lsteps1 = axes[axidx[1]].dataToLogicalCoords(steps1) lsteps2 = axes[axidx[2]].dataToLogicalCoords(steps2) # draw grid over each axis surfprop = lineprop = None s = self.settings if not s.Surface.hide: surfprop = s.Surface.makeSurfaceProp(painter) if colors is not None: self.updatePropColorMap(surfprop, s.Surface, colors) if not s.GridLine.hide: lineprop = s.GridLine.makeLineProp(painter) dirn = {'x': threed.Mesh.X_DIRN, 'y': threed.Mesh.Y_DIRN, 'z': threed.Mesh.Z_DIRN}[depvar] mesh = threed.Mesh( threed.ValVector(lsteps1), threed.ValVector(lsteps2), threed.ValVector(N.ravel(lheight)), dirn, lineprop, surfprop, s.GridLine.hidehorz, s.GridLine.hidevert) container.addObject(mesh) def dataDrawLine(self, painter, axes, clipcontainer): """Draw a line function.""" s = self.settings if s.Line.hide: return retn = self.getLineVals() if not retn: return valsx, valsy, valsz, valscolor = retn lineprop = s.Line.makeLineProp(painter) if valscolor is not None: self.updatePropColorMap(lineprop, s.Line, valscolor) lx = axes[0].dataToLogicalCoords(valsx) ly = axes[1].dataToLogicalCoords(valsy) lz = axes[2].dataToLogicalCoords(valsz) line = threed.PolyLine(lineprop) line.addPoints( threed.ValVector(lx), threed.ValVector(ly), threed.ValVector(lz)) clipcontainer.addObject(line) def dataDrawToObject(self, painter, axes): """Do actual drawing of function.""" s = self.settings mode = s.mode axes = self.fetchAxes() if axes is None: return s = self.settings clipcontainer = self.makeClipContainer(axes) if mode in ('x,y,z=fns(t)', 'x,y=fns(z)', 'y,z=fns(x)', 'x,z=fns(y)'): self.dataDrawLine(painter, axes, clipcontainer) elif mode in ('z=fn(x,y)', 'x=fn(y,z)', 'y=fn(x,z)'): self.dataDrawSurface(painter, axes, clipcontainer) clipcontainer.assignWidgetId(id(self)) return clipcontainer document.thefactory.register(Function3D) veusz-3.0.1/veusz/widgets/vectorfield.py0000664000175000017500000002172013164704547020030 0ustar jssjss00000000000000# -*- coding: utf-8 -*- # Copyright (C) 2010 Jeremy S. Sanders # Email: Jeremy Sanders # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ############################################################################### from __future__ import division import numpy as N from ..compat import czip from .. import setting from .. import document from .. import utils from .. import qtall as qt4 from . import plotters def _(text, disambiguation=None, context='VectorField'): """Translate text.""" return qt4.QCoreApplication.translate(context, text, disambiguation) class VectorField(plotters.GenericPlotter): '''A plotter for plotting a vector field.''' typename = 'vectorfield' allowusercreation = True description = _('Plot a vector field') @classmethod def addSettings(klass, s): '''Construct list of settings.''' plotters.GenericPlotter.addSettings(s) # datasets s.add( setting.DatasetExtended( 'data1', '', dimensions = 2, descr = _('X coordinate length or vector magnitude'), usertext = _('dx or r')), 0 ) s.add( setting.DatasetExtended( 'data2', '', dimensions = 2, descr = _('Y coordinate length or vector angle'), usertext = _('dy or theta')), 1 ) s.add( setting.Choice('mode', ['cartesian', 'polar'], 'cartesian', descr = _('Cartesian (dx,dy) or polar (r,theta)'), usertext = _('Mode')), 2 ) s.add( setting.FloatChoice( 'rotate', [0., 45., 90., 135., 180., -135., -90., -45.], 0., descr = _('Rotate vector clockwise by this angle in degrees'), usertext = _('Rotate')), 3 ) s.add( setting.Bool( 'reflectx', False, descr = _('Reflect vector in X direction'), usertext = _('Reflect X')), 4 ) s.add( setting.Bool( 'reflecty', False, descr = _('Reflect vector in Y direction'), usertext = _('Reflect Y')), 5 ) # formatting s.add( setting.DistancePt('baselength', '10pt', descr = _('Base length of unit vector'), usertext = _('Base length'), formatting=True), 0 ) s.add( setting.DistancePt('arrowsize', '2pt', descr = _('Size of any arrows'), usertext = _('Arrow size'), formatting=True), 1 ) s.add( setting.Bool('scalearrow', True, descr = _('Scale arrow head by length'), usertext = _('Scale arrow'), formatting=True), 2 ) s.add( setting.Arrow('arrowfront', 'none', descr = _('Arrow in front direction'), usertext=_('Arrow front'), formatting=True), 3) s.add( setting.Arrow('arrowback', 'none', descr = _('Arrow in back direction'), usertext=_('Arrow back'), formatting=True), 4) s.add( setting.Line('Line', descr = _('Line style'), usertext = _('Line')), pixmap = 'settings_plotline' ) s.add( setting.ArrowFill('Fill', descr = _('Arrow fill settings'), usertext = _('Arrow fill')), pixmap = 'settings_plotmarkerfill' ) def affectsAxisRange(self): """Range information provided by widget.""" s = self.settings return ( (s.xAxis, 'sx'), (s.yAxis, 'sy') ) def getRange(self, axis, depname, axrange): """Automatically determine the ranges of variable on the axes.""" for name in ('data1', 'data2'): data = self.settings.get(name).getData(self.document) if data is None: continue if data.dimensions == 2: xr, yr = data.getDataRanges() if depname == 'sx': dxrange = xr axrange[0] = min( axrange[0], dxrange[0] ) axrange[1] = max( axrange[1], dxrange[1] ) elif depname == 'sy': dyrange = yr axrange[0] = min( axrange[0], dyrange[0] ) axrange[1] = max( axrange[1], dyrange[1] ) def drawKeySymbol(self, number, painter, x, y, width, height): """Draw the plot symbol and/or line.""" painter.save() s = self.settings painter.setPen( s.Line.makeQPenWHide(painter) ) painter.setBrush( s.get('Fill').makeQBrushWHide(painter) ) utils.plotLineArrow( painter, x+width, y+height*0.5, width, 180, height*0.25, arrowleft=s.arrowfront, arrowright=s.arrowback) painter.restore() def dataDraw(self, painter, axes, posn, cliprect): """Draw the widget.""" s = self.settings d = self.document # ignore non existing datasets data1 = s.get('data1').getData(d) data2 = s.get('data2').getData(d) if data1 is None or data2 is None: return # require 2d datasets if data1.dimensions != 2 or data2.dimensions != 2: return # get base length (ensure > 0) baselength = max(s.get('baselength').convert(painter), 1e-6) # try to be nice if the datasets don't match data1st, data2nd = data1.data, data2.data xw = min(data1st.shape[1], data2nd.shape[1]) yw = min(data1st.shape[0], data2nd.shape[0]) # get pixel coordinates xc, yc = data1.getPixelCentres() xc, yc = xc[:xw], yc[:yw] xdsvals = N.reshape(N.tile(xc, yw), xw*yw) ydsvals = N.reshape(N.tile(yc[:, N.newaxis], xw), xw*yw) # convert using axes to plotter values xplotter = axes[0].dataToPlotterCoords(posn, xdsvals) yplotter = axes[1].dataToPlotterCoords(posn, ydsvals) pen = s.Line.makeQPenWHide(painter) painter.setPen(pen) if s.mode == 'cartesian': dx = (data1st[:yw, :xw] * baselength).ravel() dy = (data2nd[:yw, :xw] * baselength).ravel() elif s.mode == 'polar': r = data1st[:yw, :xw].ravel() * baselength theta = data2nd[:yw, :xw].ravel() dx = r * N.cos(theta) dy = r * N.sin(theta) if s.rotate != 0.: angle = -s.rotate / 180 * N.pi rotx = dx*N.cos(angle) - dy*N.sin(angle) roty = dx*N.sin(angle) + dy*N.cos(angle) dx, dy = rotx, roty if s.reflectx: dx = -dx if s.reflecty: dy = -dy x1, x2 = xplotter-dx, xplotter+dx y1, y2 = yplotter+dy, yplotter-dy if s.arrowfront == 'none' and s.arrowback == 'none': utils.plotLinesToPainter(painter, x1, y1, x2, y2, cliprect) else: arrowsize = s.get('arrowsize').convert(painter) painter.setBrush( s.get('Fill').makeQBrushWHide(painter) ) # this is backward - have to convert from dx, dy to angle, length angles = 180 - N.arctan2(dy, dx) * (180./N.pi) lengths = N.sqrt(dx**2+dy**2) * 2 # scale arrow heads by arrow length if requested if s.scalearrow: arrowsizes = (arrowsize/baselength/2) * lengths else: arrowsizes = N.zeros(lengths.shape) + arrowsize for x, y, l, a, asize in czip(x2, y2, lengths, angles, arrowsizes): if l != 0.: utils.plotLineArrow(painter, x, y, l, a, asize, arrowleft=s.arrowfront, arrowright=s.arrowback) # allow the factory to instantiate a vector field document.thefactory.register( VectorField ) veusz-3.0.1/veusz/widgets/point3d.py0000664000175000017500000002632213306262316017074 0ustar jssjss00000000000000# Copyright (C) 2015 Jeremy S. Sanders # Email: Jeremy Sanders # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ############################################################################### """3D point plotting widget.""" from __future__ import division, print_function import numpy as N from .. import qtall as qt from .. import setting from .. import document from .. import utils from ..helpers import threed from . import plotters3d def _(text, disambiguation=None, context='Point3D'): """Translate text.""" return qt.QCoreApplication.translate(context, text, disambiguation) class PlotLine3D(setting.Line3D): """Plot line.""" def __init__(self, name, **args): setting.Line3D.__init__(self, name, **args) self.get('hide').newDefault(True) self.add(setting.Colormap( 'colorMap', 'none', descr = _('If color markers dataset is given, use this colormap ' 'for the line color'), usertext=_('Color map'), formatting=True) ) self.add( setting.Bool( 'colorMapInvert', False, descr = _('Invert color map'), usertext = _('Invert map'), formatting=True) ) self.get('color').newDefault(setting.Reference('../color')) self.get('reflectivity').newDefault(20) class MarkerFill3D(setting.Surface3DWColorMap): """Fill for markers.""" def __init__(self, name, **args): setting.Surface3DWColorMap.__init__(self, name, **args) self.get('reflectivity').hidden = True self.get('color').newDefault(setting.Reference('../color')) class MarkerLine3D(setting.Line3D): """Border around markers.""" def __init__(self, name, **args): setting.Line3D.__init__(self, name, **args) self.add( setting.Bool( 'scale', True, descr=_('Scale border with marker size'), usertext=_('Scale'), formatting=True), 4) class ErrorLine3D(setting.Line3D): """Error bar line.""" def __init__(self, name, **args): setting.Line3D.__init__(self, name, **args) self.get('color').newDefault(setting.Reference('../color')) class Point3D(plotters3d.GenericPlotter3D): """Plotting points in 3D.""" typename='point3d' description=_('3D points') allowusercreation=True @classmethod def addSettings(klass, s): plotters3d.GenericPlotter3D.addSettings(s) s.add( setting.DatasetExtended( 'zData', 'z', descr=_('Z values, given by dataset, expression or list of values'), usertext=_('Z data')), 0 ) s.add( setting.DatasetExtended( 'yData', 'y', descr=_('Y values, given by dataset, expression or list of values'), usertext=_('Y data')), 0 ) s.add( setting.DatasetExtended( 'xData', 'x', descr=_('X values, given by dataset, expression or list of values'), usertext=_('X data')), 0 ) # s.add( setting.DatasetOrStr( # 'labels', '', # descr=_('Dataset or string to label points'), # usertext=_('Labels')), 5 ) s.add( setting.DatasetExtended( 'scalePoints', '', descr = _('Scale size of markers given by dataset, expression' ' or list of values'), usertext=_('Scale markers')), 6 ) s.add( setting.Bool( 'scalePersp', True, descr=_('Scale marker size using perspective'), usertext=_('Perspective'), formatting=True), 0) s.add( setting.Float( 'markerSize', 10, minval=0, maxval=1000, descr=_('Size of markers (relative to plot)'), usertext=_('Size'), formatting=True), 0 ) s.add( setting.Color( 'color', 'auto', descr = _('Master color'), usertext = _('Color'), formatting=True), 0 ) s.add( setting.Marker( 'marker', 'circle', descr = _('Type of marker to plot'), usertext=_('Marker'), formatting=True), 0 ) s.add( setting.DataColor('Color') ) s.add( PlotLine3D( 'PlotLine', descr = _('Plot line settings'), usertext = _('Plot line')), pixmap = 'settings_plotline' ) s.add( MarkerFill3D( 'MarkerFill', descr = _('Marker fill settings'), usertext=_('Marker fill')), pixmap='settings_plotmarkerfill' ) s.add( MarkerLine3D( 'MarkerLine', descr = _('Marker border settings'), usertext = _('Marker border')), pixmap = 'settings_plotmarkerline' ) s.add( ErrorLine3D( 'Error', descr = _('Error bar settings'), usertext = _('Error bar')), pixmap = 'settings_ploterrorline' ) def affectsAxisRange(self): """Which axes this widget affects.""" s = self.settings return ((s.xAxis, 'sx'), (s.yAxis, 'sy'), (s.zAxis, 'sz')) def getRange(self, axis, depname, axrange): """Update axis range from data.""" dataname = {'sx': 'xData', 'sy': 'yData', 'sz': 'zData'}[depname] dsetn = self.settings.get(dataname) data = dsetn.getData(self.document) if axis.settings.log: def updateRange(v): with N.errstate(invalid='ignore'): chopzero = v[(v>0) & N.isfinite(v)] if len(chopzero) > 0: axrange[0] = min(axrange[0], chopzero.min()) axrange[1] = max(axrange[1], chopzero.max()) else: def updateRange(v): fvals = v[N.isfinite(v)] if len(fvals) > 0: axrange[0] = min(axrange[0], fvals.min()) axrange[1] = max(axrange[1], fvals.max()) if data: data.rangeVisit(updateRange) def dataDrawPointsLine(self, painter, cont, coord): """Draw point and plot line.""" s = self.settings doc = self.document scalepts = s.get('scalePoints').getData(doc) pointpath, filled = utils.getPointPainterPath( s.marker, s.markerSize, s.MarkerLine.width) markerlineprop = markerfillprop = None # convert color values to 0..1 quantity cvals = s.Color.get('points').getData(doc) if cvals is not None: colorvals = utils.applyScaling( cvals.data, s.Color.scaling, s.Color.min, s.Color.max) color2d = colorvals.reshape(1, len(colorvals)) else: colorvals = color2d = None if not s.MarkerLine.hide: markerlineprop = s.MarkerLine.makeLineProp(painter) if filled and not s.MarkerFill.hide: markerfillprop = s.MarkerFill.makeSurfaceProp(painter) cmapname = s.MarkerFill.colorMap if color2d is not None and cmapname != 'none': cmap = self.document.evaluate.getColormap( cmapname, s.MarkerFill.colorMapInvert) colorimg = utils.applyColorMap( cmap, 'linear', color2d, 0., 1., s.MarkerFill.transparency) markerfillprop.setRGBs(colorimg) if markerlineprop or markerfillprop: ptobj = threed.Points( coord[0], coord[1], coord[2], pointpath, markerlineprop, markerfillprop) if scalepts: ptobj.setSizes(threed.ValVector(scalepts.data)) ptobj.scalepersp = s.scalePersp ptobj.scaleline = s.MarkerLine.scale cont.addObject(ptobj) if not s.PlotLine.hide: lineprop = s.PlotLine.makeLineProp(painter) cmapname = s.PlotLine.colorMap if color2d is not None and cmapname != 'none': cmap = self.document.evaluate.getColormap( cmapname, s.PlotLine.colorMapInvert) colorimg = utils.applyColorMap( cmap, 'linear', color2d, 0., 1., s.PlotLine.transparency) lineprop.setRGBs(colorimg) lineobj = threed.PolyLine(lineprop) lineobj.addPoints(*coord) cont.addObject(lineobj) def dataDrawErrorBars(self, painter, cont, axes, coord, datasets): """Draw error bars for points.""" # TODO: Different error styles err = self.settings.Error if err.hide: return prop = err.makeLineProp(painter) for i in range(3): ds = datasets[i] if not ds.hasErrors(): continue neg = pos = None if ds.nerr is not None: neg = ds.data+ds.nerr elif ds.serr is not None: neg = ds.data-ds.serr if ds.perr is not None: pos = ds.data+ds.perr elif ds.serr is not None: pos = ds.data+ds.serr coordend = list(coord) if neg is not None: coordend[i] = threed.ValVector(axes[i].dataToLogicalCoords(neg)) line = threed.LineSegments( coord[0], coord[1], coord[2], coordend[0], coordend[1], coordend[2], prop) cont.addObject(line) if pos is not None: coordend[i] = threed.ValVector(axes[i].dataToLogicalCoords(pos)) line = threed.LineSegments( coord[0], coord[1], coord[2], coordend[0], coordend[1], coordend[2], prop) cont.addObject(line) def dataDrawToObject(self, painter, axes): """Do drawing of axis.""" s = self.settings axes = self.fetchAxes() if axes is None: return doc = self.document xv = s.get('xData').getData(doc) yv = s.get('yData').getData(doc) zv = s.get('zData').getData(doc) if not xv or not yv or not zv: return coord = [ threed.ValVector(axes[0].dataToLogicalCoords(xv.data)), threed.ValVector(axes[1].dataToLogicalCoords(yv.data)), threed.ValVector(axes[2].dataToLogicalCoords(zv.data)) ] clipcont = self.makeClipContainer(axes) self.dataDrawPointsLine(painter, clipcont, coord) self.dataDrawErrorBars(painter, clipcont, axes, coord, [xv, yv, zv]) clipcont.assignWidgetId(id(self)) return clipcont document.thefactory.register(Point3D) veusz-3.0.1/veusz/widgets/nonorthgraph.py0000664000175000017500000001472013164512632020225 0ustar jssjss00000000000000# Copyright (C) 2010 Jeremy S. Sanders # Email: Jeremy Sanders # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ############################################################################## """Non orthogonal graph root.""" from __future__ import division from . import controlgraph from .widget import Widget from .. import qtall as qt4 from .. import setting filloptions = ('center', 'outside', 'top', 'bottom', 'left', 'right', 'polygon') def _(text, disambiguation=None, context='NonOrthGraph'): """Translate text.""" return qt4.QCoreApplication.translate(context, text, disambiguation) class FillBrush(setting.BrushExtended): '''Brush for filling point region.''' def __init__(self, *args, **argsv): setting.BrushExtended.__init__(self, *args, **argsv) self.add( setting.Choice('filltype', filloptions, 'center', descr=_('Fill to this edge/position'), usertext=_('Fill type')) ) self.get('hide').newDefault(True) class NonOrthGraph(Widget): '''Non-orthogonal graph base widget.''' @classmethod def addSettings(klass, s): '''Construct list of settings.''' Widget.addSettings(s) s.add( setting.Distance( 'leftMargin', '1.7cm', descr=_('Distance from left of graph to edge'), usertext=_('Left margin'), formatting=True) ) s.add( setting.Distance( 'rightMargin', '0.2cm', descr=_('Distance from right of graph to edge'), usertext=_('Right margin'), formatting=True) ) s.add( setting.Distance( 'topMargin', '0.2cm', descr=_('Distance from top of graph to edge'), usertext=_('Top margin'), formatting=True) ) s.add( setting.Distance( 'bottomMargin', '1.7cm', descr=_('Distance from bottom of graph to edge'), usertext=_('Bottom margin'), formatting=True) ) s.add( setting.GraphBrush( 'Background', descr = _('Background plot fill'), usertext=_('Background')), pixmap='settings_bgfill' ) s.add( setting.Line('Border', descr = _('Graph border line'), usertext=_('Border')), pixmap='settings_border') @classmethod def allowedParentTypes(klass): from . import page, grid return (page.Page, grid.Grid) def graphToPlotCoords(self, coorda, coordb): '''Convert graph to plotting coordinates. Returns (plta, pltb) coordinates ''' def coordRanges(self): '''Return coordinate ranges of plot. This is in the form [[mina, maxa], [minb, maxb]].''' def drawFillPts(self, painter, extfill, bounds, ptsx, ptsy): '''Draw set of points for filling. extfill: extended fill brush bounds: usual tuple (minx, miny, maxx, maxy) ptsx, ptsy: translated plotter coordinates ''' def drawGraph(self, painter, bounds, datarange, outerbounds=None): '''Plot graph area. datarange is [mina, maxa, minb, maxb] or None ''' def drawAxes(self, painter, bounds, datarange, outerbounds=None): '''Plot axes. datarange is [mina, maxa, minb, maxb] or None ''' def setClip(self, painter, bounds): '''Set clipping for graph.''' def getDataRange(self): """Get automatic data range. Return None if no data.""" drange = [1e199, -1e199, 1e199, -1e199] for c in self.children: if hasattr(c, 'updateDataRanges'): c.updateDataRanges(drange) # no data if drange[0] > drange[1] or drange[2] > drange[3]: drange = None return drange def getMargins(self, painthelper): """Use settings to compute margins.""" s = self.settings return ( s.get('leftMargin').convert(painthelper), s.get('topMargin').convert(painthelper), s.get('rightMargin').convert(painthelper), s.get('bottomMargin').convert(painthelper) ) def draw(self, parentposn, phelper, outerbounds=None): '''Update the margins before drawing.''' s = self.settings bounds = self.computeBounds(parentposn, phelper) maxbounds = self.computeBounds(parentposn, phelper, withmargin=False) # do no painting if hidden if s.hide: return bounds painter = phelper.painter(self, bounds) with painter: # reset counter and compute automatic colors phelper.autoplottercount = 0 for c in self.children: c.setupAutoColor(painter) # plot graph datarange = self.getDataRange() self.drawGraph(painter, bounds, datarange, outerbounds=outerbounds) self.drawAxes(painter, bounds, datarange, outerbounds=outerbounds) # paint children for c in reversed(self.children): c.draw(bounds, phelper, outerbounds=outerbounds) # controls for adjusting margins phelper.setControlGraph(self, [ controlgraph.ControlMarginBox(self, bounds, maxbounds, phelper)]) return bounds def updateControlItem(self, cgi): """Graph resized or moved - call helper routine to move self.""" cgi.setWidgetMargins() veusz-3.0.1/veusz/widgets/graph.py0000664000175000017500000002666413164706377016642 0ustar jssjss00000000000000# graph widget for containing other sorts of widget # Copyright (C) 2004 Jeremy S. Sanders # Email: Jeremy Sanders # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ############################################################################## from __future__ import division import textwrap from .. import qtall as qt4 from .. import setting from .. import utils from .. import document from . import widget from . import controlgraph def _(text, disambiguation=None, context='Graph'): """Translate text.""" return qt4.QCoreApplication.translate(context, text, disambiguation) class Graph(widget.Widget): """Graph for containing other sorts of widgets""" typename='graph' allowusercreation = True description = _('Base graph') @classmethod def addSettings(klass, s): """Construct list of settings.""" widget.Widget.addSettings(s) s.add( setting.Distance( 'leftMargin', '1.7cm', descr=_('Distance from left of graph to edge'), usertext=_('Left margin'), formatting=True) ) s.add( setting.Distance( 'rightMargin', '0.2cm', descr=_('Distance from right of graph to edge'), usertext=_('Right margin'), formatting=True) ) s.add( setting.Distance( 'topMargin', '0.2cm', descr=_('Distance from top of graph to edge'), usertext=_('Top margin'), formatting=True) ) s.add( setting.Distance( 'bottomMargin', '1.7cm', descr=_('Distance from bottom of graph to edge'), usertext=_('Bottom margin'), formatting=True) ) s.add( setting.FloatOrAuto('aspect', 'Auto', descr=_('Fix aspect ratio of graph to this value'), usertext=_('Aspect ratio'), minval = 0.01, maxval = 100., formatting=True) ) s.add( setting.Notes( 'notes', '', descr=_('User-defined notes'), usertext=_('Notes') ) ) s.add( setting.GraphBrush( 'Background', descr = _('Background plot fill'), usertext=_('Background')), pixmap='settings_bgfill' ) s.add( setting.Line('Border', descr = _('Graph border line'), usertext=_('Border')), pixmap='settings_border') @classmethod def allowedParentTypes(klass): from . import page, grid return (page.Page, grid.Grid) @property def userdescription(self): """Return user-friendly description.""" return textwrap.fill(self.settings.notes, 60) def addDefaultSubWidgets(self): """Add axes automatically.""" from . import axis if self.parent.getChild('x') is None: ax = axis.Axis(self, name='x') ax.linkToStylesheet() if self.parent.getChild('y') is None: ay = axis.Axis(self, name='y') ay.linkToStylesheet() def getAxesDict(self, axesnames, ignoremissing=False): """Get the axes for widgets to plot against. axesnames is a list/set of names to find. Returns a dict of objects """ axes = {} # recursively go back up the tree to find axes w = self while w is not None and len(axes) < len(axesnames): for c in w.children: name = c.name if ( name in axesnames and name not in axes and c.isaxis ): axes[name] = c w = w.parent # didn't find everything... if w is None and not ignoremissing: for name in axesnames: if name not in axes: axes[name] = None # return list of found axes return axes def getAxes(self, axesnames): """Return a list of axes widgets given a list of names.""" ad = self.getAxesDict(axesnames) return [ad[n] for n in axesnames] def adjustBoundsForAspect(self, bounds): s = self.settings if s.aspect != 'Auto': saspect = s.aspect width = bounds[2]-bounds[0] height = bounds[3]-bounds[1] gaspect = width/height bounds = list(bounds) if saspect > gaspect: # want a graph which is wider than the current size # => add space to top/bottom newheight = width / saspect delta = (height-newheight) / 2 bounds[1] += delta bounds[3] -= delta else: # want a graph which is narrower than the current size # => add space to left/right newwidth = height * saspect delta = (width-newwidth) / 2 bounds[0] += delta bounds[2] -= delta return bounds def getMargins(self, painthelper): """Use settings to compute margins.""" s = self.settings return ( s.get('leftMargin').convert(painthelper), s.get('topMargin').convert(painthelper), s.get('rightMargin').convert(painthelper), s.get('bottomMargin').convert(painthelper) ) def draw(self, parentposn, painthelper, outerbounds = None): '''Update the margins before drawing.''' # yuck, avoid circular imports from . import axisbroken s = self.settings bounds = self.computeBounds(parentposn, painthelper) maxbounds = self.computeBounds(parentposn, painthelper, withmargin=False) # do no painting if hidden if s.hide: return bounds # controls for adjusting graph margins painter = painthelper.painter(self, bounds) painthelper.setControlGraph(self, [ controlgraph.ControlMarginBox(self, bounds, maxbounds, painthelper) ]) bounds = self.adjustBoundsForAspect(bounds) with painter: # set graph rectangle attributes path = qt4.QPainterPath() path.addRect( qt4.QRectF(qt4.QPointF(bounds[0], bounds[1]), qt4.QPointF(bounds[2], bounds[3])) ) utils.brushExtFillPath(painter, s.Background, path, stroke=s.Border.makeQPenWHide(painter)) # debugging positions (uncomment) # painter.drawRect( qt4.QRectF( # qt4.QPointF(parentposn[0], parentposn[1]), # qt4.QPointF(parentposn[2], parentposn[3]) )) # if outerbounds: # painter.drawRect( qt4.QRectF( # qt4.QPointF(outerbounds[0], outerbounds[1]), # qt4.QPointF(outerbounds[2], outerbounds[3]) )) # child drawing algorithm is a bit complex due to axes # being shared between graphs and broken axes # this is a map of axis names to plot to axis widgets axestodraw = {} # axes widgets for each plotter (precalculated by Page) axesofwidget = painthelper.plotteraxismap for c in self.children: try: for a in axesofwidget[c]: axestodraw[a.name] = a except (KeyError, AttributeError): if c.isaxis: axestodraw[c.name] = c # grid lines are normally plotted before other child widgets axisdrawlist = sorted(axestodraw.items(), reverse=True) for aname, awidget in axisdrawlist: awidget.updateAxisLocation(bounds) awidget.computePlottedRange() awidget.drawGrid(bounds, painthelper, outerbounds=outerbounds, ontop=False) # broken axis handling brokenaxes = set() for axis in axestodraw.values(): if isinstance(axis, axisbroken.AxisBroken): brokenaxes.add(axis) # don't duplicate drawing axes axesdrawn = set() # reset counter and compute automatic colors painthelper.autoplottercount = 0 for c in self.children: c.setupAutoColor(painter) # do normal drawing of children # iterate over children in reverse order for c in reversed(self.children): if c.isaxis: axesdrawn.add(c) axes = axesofwidget.get(c, None) if axes is not None and any((a in brokenaxes for a in axes)): # handle broken axes childbrokenaxes = sorted( [(a.name, a) for a in axes if a in brokenaxes], key=lambda x: x[0]) def iteratebrokenaxes(b): """Recursively iterate over each broken axis and redraw child for each. We might have more than one broken axis per child, so hence this rather strange iteration. """ ax = b[0][1] for i in range(ax.breakvnum): ax.switchBreak(i, bounds) if len(b) == 1: c.draw(bounds, painthelper, outerbounds=outerbounds) else: iteratebrokenaxes(b[1:]) ax.switchBreak(None, bounds) iteratebrokenaxes(childbrokenaxes) else: # standard non broken axis drawing c.draw(bounds, painthelper, outerbounds=outerbounds) # then for grid lines on top axiswidgets = [axis for name, axis in axisdrawlist] for awidget in axiswidgets: awidget.drawGrid(bounds, painthelper, outerbounds=outerbounds, ontop=True) awidget.drawAutoMirror(bounds, painthelper, axiswidgets) # draw remaining axes for awidget in axiswidgets: if awidget not in axesdrawn: awidget.draw(bounds, painthelper, outerbounds=outerbounds) return bounds def updateControlItem(self, cgi): """Graph resized or moved - call helper routine to move self.""" cgi.setWidgetMargins() # allow users to make Graph objects document.thefactory.register( Graph ) veusz-3.0.1/veusz/widgets/boxplot.py0000664000175000017500000004330013164704641017202 0ustar jssjss00000000000000# Copyright (C) 2010 Jeremy S. Sanders # Email: Jeremy Sanders # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ############################################################################### """For making box plots.""" from __future__ import division import math import numpy as N from ..compat import crange, czip from .. import qtall as qt4 from .. import setting from .. import document from .. import utils from .plotters import GenericPlotter def _(text, disambiguation=None, context='BoxPlot'): """Translate text.""" return qt4.QCoreApplication.translate(context, text, disambiguation) def percentile(sortedds, perc): """Given a sorted dataset, get the percentile perc. Interpolates between data points.""" index = perc * 0.01 * (sortedds.shape[0]-1) # interpolate between indices frac, index = math.modf(index) index = int(index) indexplus1 = min(index+1, sortedds.shape[0]-1) interpol = (1-frac)*sortedds[index] + frac*sortedds[indexplus1] return interpol def swapline(painter, x1, y1, x2, y2, swap): """Draw line, swapping x and y coordinates if swap is True.""" if swap: painter.drawLine( qt4.QPointF(y1, x1), qt4.QPointF(y2, x2) ) else: painter.drawLine( qt4.QPointF(x1, y1), qt4.QPointF(x2, y2) ) def swapbox(painter, x1, y1, x2, y2, swap): """Return box, swapping x and y coordinates if swap is True.""" if swap: return qt4.QRectF(qt4.QPointF(y1, x1), qt4.QPointF(y2, x2)) else: return qt4.QRectF(qt4.QPointF(x1, y1), qt4.QPointF(x2, y2)) class _Stats(object): """Store statistics about box.""" def calculate(self, data, whiskermode): """Calculate statistics for data.""" cleaned = data[ N.isfinite(data) ] cleaned.sort() if len(cleaned) == 0: self.median = self.botquart = self.topquart = self.mean = \ self.botwhisker = self.topwhisker = N.nan return self.median = percentile(cleaned, 50) self.botquart = percentile(cleaned, 25) self.topquart = percentile(cleaned, 75) self.mean = N.mean(cleaned) if whiskermode == 'min/max': self.botwhisker = cleaned.min() self.topwhisker = cleaned.max() elif whiskermode == '1.5IQR': iqr = self.topquart - self.botquart eltop = N.searchsorted(cleaned, self.topquart+1.5*iqr)-1 self.topwhisker = cleaned[eltop] elbot = max(N.searchsorted(cleaned, self.botquart-1.5*iqr)-1, 0) self.botwhisker = cleaned[elbot] elif whiskermode == '1 stddev': stddev = N.std(cleaned) self.topwhisker = self.mean+stddev self.botwhisker = self.mean-stddev elif whiskermode == '9/91 percentile': self.topwhisker = percentile(cleaned, 91) self.botwhisker = percentile(cleaned, 9) elif whiskermode == '2/98 percentile': self.topwhisker = percentile(cleaned, 98) self.botwhisker = percentile(cleaned, 2) else: raise RuntimeError("Invalid whisker mode") self.outliers = cleaned[ (cleaned < self.botwhisker) | (cleaned > self.topwhisker) ] class BoxPlot(GenericPlotter): """Plot bar charts.""" typename='boxplot' allowusercreation=True description=_('Plot box plots') @classmethod def addSettings(klass, s): """Construct list of settings.""" GenericPlotter.addSettings(s) s.remove('key') s.add( setting.Choice('whiskermode', ('min/max', '1.5IQR', '1 stddev', '9/91 percentile', '2/98 percentile'), '1.5IQR', descr = _('Whisker mode'), usertext=_('Whisker mode')), 0 ) s.add( setting.Choice('direction', ('horizontal', 'vertical'), 'vertical', descr = _('Horizontal or vertical boxes'), usertext=_('Direction')), 0 ) s.add( setting.DatasetOrStr('labels', '', descr=_('Dataset or string to label bars'), usertext=_('Labels')), 0 ) s.add( setting.DatasetExtended( 'posn', '', descr = _('Dataset or list of values giving ' 'positions of boxes (optional)'), usertext=_('Positions')), 0 ) # calculate statistics from these datasets s.add( setting.Datasets('values', ('data',), descr = _('Datasets containing values to ' 'calculate statistics for'), usertext=_('Datasets')), 0 ) # alternate mode where data are provided for boxes s.add( setting.DatasetExtended( 'whiskermax', '', descr=_('Dataset with whisker maxima or list of values'), usertext=_('Whisker max')), 0 ) s.add( setting.DatasetExtended( 'whiskermin', '', descr=_('Dataset with whisker minima or list of values'), usertext=_('Whisker min')), 0 ) s.add( setting.DatasetExtended( 'boxmax', '', descr=_('Dataset with box maxima or list of values'), usertext=_('Box max')), 0 ) s.add( setting.DatasetExtended( 'boxmin', '', descr=_('Dataset with box minima or list of values'), usertext=_('Box min')), 0 ) s.add( setting.DatasetExtended( 'median', '', descr=_('Dataset with medians or list of values'), usertext=_('Median')), 0 ) s.add( setting.DatasetExtended( 'mean', '', descr=_('Dataset with means or list of values'), usertext=_('Mean')), 0 ) # switch between different modes s.add( setting.BoolSwitch('calculate', True, descr = _('Calculate statistics from datasets' ' rather than given manually'), usertext = _('Calculate'), settingstrue=('whiskermode', 'values'), settingsfalse=('boxmin', 'whiskermin', 'boxmax', 'whiskermax', 'mean', 'median')), 0 ) # formatting options s.add( setting.Float('fillfraction', 0.75, descr = _('Fill fraction of boxes'), usertext=_('Fill fraction'), formatting=True) ) s.add( setting.Marker('outliersmarker', 'circle', descr = _('Marker for outliers'), usertext=_('Outliers'), formatting=True) ) s.add( setting.Marker('meanmarker', 'linecross', descr = _('Marker for mean'), usertext=_('Mean'), formatting=True) ) s.add( setting.DistancePt('markerSize', '3pt', descr = _('Size of markers to plot'), usertext=_('Markers size'), formatting=True) ) s.add( setting.GraphBrush( 'Fill', descr = _('Box fill'), usertext=_('Box fill')), pixmap='settings_bgfill' ) s.add( setting.Line('Border', descr = _('Box border line'), usertext=_('Box border')), pixmap='settings_border') s.add( setting.Line('Whisker', descr = _('Whisker line'), usertext=_('Whisker line')), pixmap='settings_whisker') s.add( setting.Line('MarkersLine', descr = _('Line around markers'), usertext = _('Markers border')), pixmap = 'settings_plotmarkerline' ) s.add( setting.BoxPlotMarkerFillBrush('MarkersFill', descr = _('Markers fill'), usertext = _('Markers fill')), pixmap = 'settings_plotmarkerfill' ) @property def userdescription(self): """Friendly description for user.""" s = self.settings return "values='%s', position='%s'" % ( ', '.join(s.values), s.posn) def affectsAxisRange(self): """This widget provides range information about these axes.""" s = self.settings return ( (s.xAxis, 'sx'), (s.yAxis, 'sy') ) def rangeManual(self): """For updating range in manual mode.""" s = self.settings ds = [] for name in ('whiskermin', 'whiskermax', 'boxmin', 'boxmax', 'mean', 'median'): ds.append( s.get(name).getData(self.document) ) r = [N.inf, -N.inf] if None not in ds: concat = N.concatenate([d.data for d in ds]) r[0] = N.nanmin(concat) r[1] = N.nanmax(concat) return r def getPosns(self): """Get values of positions of bars.""" s = self.settings doc = self.document posns = s.get('posn').getData(doc) if posns is not None: # manual positions return posns.data else: if s.calculate: # number of datasets vals = s.get('values').getData(doc) else: # length of mean array vals = s.get('mean').getData(doc) if vals: vals = vals.data if vals is None: return N.array([]) else: return N.arange(1, len(vals)+1, dtype=N.float64) def getRange(self, axis, depname, axrange): """Update axis range from data.""" s = self.settings doc = self.document if ( (depname == 'sx' and s.direction == 'horizontal') or (depname == 'sy' and s.direction == 'vertical') ): # update axis in direction of data if s.calculate: # update from values values = s.get('values').getData(doc) if values: for v in values: if len(v.data) > 0: axrange[0] = min(axrange[0], N.nanmin(v.data)) axrange[1] = max(axrange[1], N.nanmax(v.data)) else: # update from manual entries drange = self.rangeManual() axrange[0] = min(axrange[0], drange[0]) axrange[1] = max(axrange[1], drange[1]) else: # update axis in direction of datasets posns = self.getPosns() if len(posns) > 0: axrange[0] = min(axrange[0], N.nanmin(posns)-0.5) axrange[1] = max(axrange[1], N.nanmax(posns)+0.5) def getAxisLabels(self, direction): """Get labels for axis if using a label axis.""" s = self.settings doc = self.document text = s.get('labels').getData(doc, checknull=True) values = s.get('values').getData(doc) if text is None or values is None: return (None, None) positions = self.getPosns() return (text, positions) def plotBox(self, painter, axes, boxposn, posn, width, clip, stats): """Draw box for dataset.""" if not N.isfinite(stats.median): # skip bad datapoints return s = self.settings horz = (s.direction == 'horizontal') # convert quartiles, top and bottom whiskers to plotter medplt, botplt, topplt, botwhisplt, topwhisplt = tuple( axes[not horz].dataToPlotterCoords( posn, N.array([ stats.median, stats.botquart, stats.topquart, stats.botwhisker, stats.topwhisker ])) ) # draw whisker top to bottom p = s.Whisker.makeQPenWHide(painter) p.setCapStyle(qt4.Qt.FlatCap) painter.setPen(p) swapline(painter, boxposn, topwhisplt, boxposn, botwhisplt, horz) # draw ends of whiskers endsize = width/2 swapline(painter, boxposn-endsize/2, topwhisplt, boxposn+endsize/2, topwhisplt, horz) swapline(painter, boxposn-endsize/2, botwhisplt, boxposn+endsize/2, botwhisplt, horz) # draw box fill boxpath = qt4.QPainterPath() boxpath.addRect( swapbox(painter, boxposn-width/2, botplt, boxposn+width/2, topplt, horz) ) utils.brushExtFillPath(painter, s.Fill, boxpath) # draw line across box p = s.Whisker.makeQPenWHide(painter) p.setCapStyle(qt4.Qt.FlatCap) painter.setPen(p) swapline(painter, boxposn-width/2, medplt, boxposn+width/2, medplt, horz) # draw box painter.strokePath(boxpath, s.Border.makeQPenWHide(painter) ) # draw outliers painter.setPen( s.MarkersLine.makeQPenWHide(painter) ) painter.setBrush( s.MarkersFill.makeQBrushWHide(painter) ) markersize = s.get('markerSize').convert(painter) if stats.outliers.shape[0] != 0: pltvals = axes[not horz].dataToPlotterCoords(posn, stats.outliers) otherpos = N.zeros(pltvals.shape) + boxposn if horz: x, y = pltvals, otherpos else: x, y = otherpos, pltvals utils.plotMarkers( painter, x, y, s.outliersmarker, markersize, clip=clip ) # draw mean meanplt = axes[not horz].dataToPlotterCoords( posn, N.array([stats.mean]))[0] if horz: x, y = meanplt, boxposn else: x, y = boxposn, meanplt utils.plotMarker( painter, x, y, s.meanmarker, markersize ) def dataDraw(self, painter, axes, widgetposn, clip): """Plot the data on a plotter.""" s = self.settings # get data doc = self.document positions = self.getPosns() if s.calculate: # calculate from data values = s.get('values').getData(doc) if values is None: return else: # use manual datasets datasets = [ s.get(x).getData(doc) for x in ('whiskermin', 'whiskermax', 'boxmin', 'boxmax', 'mean', 'median') ] if any((d is None for d in datasets)): return # get axes widgets axes = self.parent.getAxes( (s.xAxis, s.yAxis) ) # return if there are no proper axes if ( axes[0] is None or axes[1] is None or axes[0].settings.direction != 'horizontal' or axes[1].settings.direction != 'vertical' ): return # get boxes visible along direction of boxes to work out width horz = (s.direction == 'horizontal') plotposns = axes[horz].dataToPlotterCoords(widgetposn, positions) if horz: inplot = (plotposns > widgetposn[1]) & (plotposns < widgetposn[3]) else: inplot = (plotposns > widgetposn[0]) & (plotposns < widgetposn[2]) inplotposn = plotposns[inplot] if inplotposn.shape[0] < 2: if horz: width = (widgetposn[3]-widgetposn[1])*0.5 else: width = (widgetposn[2]-widgetposn[0])*0.5 else: # use minimum different between points to get width inplotposn.sort() width = N.nanmin(inplotposn[1:] - inplotposn[:-1]) # adjust width width = width * s.fillfraction if s.calculate: # calculated boxes for vals, plotpos in czip(values, plotposns): stats = _Stats() stats.calculate(vals.data, s.whiskermode) self.plotBox(painter, axes, plotpos, widgetposn, width, clip, stats) else: # manually given boxes vals = [d.data for d in datasets] + [plotposns] lens = [len(d) for d in vals] for i in crange(min(lens)): stats = _Stats() stats.topwhisker = vals[0][i] stats.botwhisker = vals[1][i] stats.botquart = vals[2][i] stats.topquart = vals[3][i] stats.mean = vals[4][i] stats.median = vals[5][i] stats.outliers = N.array([]) self.plotBox(painter, axes, vals[6][i], widgetposn, width, clip, stats) # allow the factory to instantiate a boxplot document.thefactory.register( BoxPlot ) veusz-3.0.1/veusz/widgets/polygon.py0000664000175000017500000000705713302252613017202 0ustar jssjss00000000000000# Copyright (C) 2009 Jeremy S. Sanders # Email: Jeremy Sanders # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ############################################################################### from __future__ import division from .. import document from .. import datasets from .. import setting from .. import qtall as qt4 from .. import utils from . import plotters def _(text, disambiguation=None, context='Polygon'): """Translate text.""" return qt4.QCoreApplication.translate(context, text, disambiguation) class Polygon(plotters.FreePlotter): """For plotting polygons.""" typename = 'polygon' allowusercreeation = True description = _('Plot a polygon') def __init__(self, parent, name=None): """Initialise object, setting axes.""" plotters.FreePlotter.__init__(self, parent, name=name) @classmethod def addSettings(klass, s): """Construct list of settings.""" plotters.FreePlotter.addSettings(s) s.add( setting.Line('Line', descr = _('Line around polygon'), usertext = _('Line')), pixmap = 'settings_plotline' ) s.add( setting.BrushExtended('Fill', descr = _('Fill within polygon'), usertext = _('Fill')), pixmap = 'settings_plotfillbelow' ) def draw(self, posn, phelper, outerbounds=None): """Plot the data on a plotter.""" s = self.settings # exit if hidden if s.hide: return # get points in plotter coordinates xp, yp = self._getPlotterCoords(posn) if xp is None or yp is None: # we can't calculate coordinates return x1, y1, x2, y2 = posn cliprect = qt4.QRectF( qt4.QPointF(x1, y1), qt4.QPointF(x2, y2) ) painter = phelper.painter(self, posn, clip=cliprect) with painter: pen = s.Line.makeQPenWHide(painter) pw = pen.widthF()*2 lineclip = qt4.QRectF( qt4.QPointF(x1-pw, y1-pw), qt4.QPointF(x2+pw, y2+pw) ) # this is a hack as we generate temporary fake datasets path = qt4.QPainterPath() for xvals, yvals in datasets.generateValidDatasetParts( [datasets.Dataset(xp), datasets.Dataset(yp)]): path = qt4.QPainterPath() poly = qt4.QPolygonF() utils.addNumpyToPolygonF(poly, xvals.data, yvals.data) clippedpoly = qt4.QPolygonF() utils.polygonClip(poly, lineclip, clippedpoly) path.addPolygon(clippedpoly) path.closeSubpath() utils.brushExtFillPath(painter, s.Fill, path, stroke=pen) # allow the factory to instantiate this document.thefactory.register( Polygon ) veusz-3.0.1/veusz/widgets/plotters.py0000664000175000017500000002202413164703032017360 0ustar jssjss00000000000000# plotters.py # plotting classes # Copyright (C) 2004 Jeremy S. Sanders # Email: Jeremy Sanders # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ############################################################################### """A generic plotter widget which is inherited by function and point.""" from __future__ import division from .. import qtall as qt4 import numpy as N from .. import setting from . import widget def _(text, disambiguation=None, context='Plotters'): """Translate text.""" return qt4.QCoreApplication.translate(context, text, disambiguation) class GenericPlotter(widget.Widget): """Generic plotter.""" typename='genericplotter' isplotter = True @classmethod def allowedParentTypes(klass): from . import graph return (graph.Graph,) @classmethod def addSettings(klass, s): """Construct list of settings.""" widget.Widget.addSettings(s) s.add( setting.Str('key', '', descr = _('Description of the plotted data to appear in key'), usertext=_('Key text')) ) s.add( setting.Axis('xAxis', 'x', 'horizontal', descr = _('Name of X-axis to use'), usertext=_('X axis')) ) s.add( setting.Axis('yAxis', 'y', 'vertical', descr = _('Name of Y-axis to use'), usertext=_('Y axis')) ) def autoColor(self, painter, dataindex=0): """Automatic color for plotting.""" return painter.docColorAuto( painter.helper.autoColorIndex((self, dataindex))) def getAxesNames(self): """Returns names of axes used.""" s = self.settings return (s.xAxis, s.yAxis) def getNumberKeys(self): """Return number of key entries.""" if self.settings.key: return 1 else: return 0 def getKeyText(self, number): """Get key entry.""" return self.settings.key def drawKeySymbol(self, number, painter, x, y, width, height): """Draw the plot symbol and/or line at (x,y) in a box width*height. This is used to plot a key """ pass def clipAxesBounds(self, axes, bounds): """Returns clipping rectange for start and stop values of axis.""" # get range x1, x2 = axes[0].coordParr1, axes[0].coordParr2 if x1 > x2: x1, x2 = x2, x1 y1, y2 = axes[1].coordParr2, axes[1].coordParr1 if y1 > y2: y1, y2 = y2, y1 # actually clip the data cliprect = qt4.QRectF(qt4.QPointF(x1, y1), qt4.QPointF(x2, y2)) return cliprect def getAxisLabels(self, direction): """Get labels for datapoints and coordinates, or None if none. direction is 'horizontal' or 'vertical' return (labels, coordinates) """ return (None, None) def fetchAxes(self): """Returns the axes for this widget""" axes = self.parent.getAxes( (self.settings.xAxis, self.settings.yAxis) ) # fail if we don't have good axes if ( axes[0] is None or axes[1] is None or axes[0].settings.direction != 'horizontal' or axes[1].settings.direction != 'vertical' ): return None return axes def lookupAxis(self, axisname): """Find widget associated with axisname.""" w = self.parent while w: for c in w.children: if c.name == axisname and c.isaxis: return c w = w.parent return None def affectsAxisRange(self): """Returns information on the following axes. format is ( ('x', 'sx'), ('y', 'sy') ) where key is the axis and value is a provided bound """ return () def requiresAxisRange(self): """Requires information about the axis given before providing information. Format (('sx', 'x'), ('sy', 'y')) """ return () def getRange(self, axis, depname, therange): """Update range variable for axis with dependency name given.""" pass def draw(self, parentposn, painthelper, outerbounds = None): """Draw for generic plotters.""" posn = self.computeBounds(parentposn, painthelper) # exit if hidden or function blank if self.settings.hide: return # get axes widgets axes = self.fetchAxes() if not axes: return # clip data within bounds of plotter cliprect = self.clipAxesBounds(axes, posn) painter = painthelper.painter(self, posn, clip=cliprect) with painter: self.dataDraw(painter, axes, posn, cliprect) for c in self.children: c.draw(posn, painthelper, outerbounds) return posn def dataDraw(self, painter, axes, posn, cliprect): """Actually plot the data.""" pass class FreePlotter(widget.Widget): """A plotter which can be plotted on the page or in a graph.""" def __init__(self, parent, name=None): """Initialise object, setting axes.""" widget.Widget.__init__(self, parent, name=name) @classmethod def allowedParentTypes(klass): from . import page, graph return (graph.Graph, page.Page) @classmethod def addSettings(klass, s): """Construct list of settings.""" widget.Widget.addSettings(s) s.add( setting.DatasetExtended( 'xPos', [0.5], descr=_('List of fractional X coordinates or dataset'), usertext=_('X positions'), formatting=False) ) s.add( setting.DatasetExtended( 'yPos', [0.5], descr=_('List of fractional Y coordinates or dataset'), usertext=_('Y positions'), formatting=False) ) s.add( setting.Choice('positioning', ['axes', 'relative'], 'relative', descr=_('Use axes or fractional ' 'position to place label'), usertext=_('Position mode'), formatting=False) ) s.add( setting.Axis('xAxis', 'x', 'horizontal', descr = _('Name of X-axis to use'), usertext=_('X axis')) ) s.add( setting.Axis('yAxis', 'y', 'vertical', descr = _('Name of Y-axis to use'), usertext=_('Y axis')) ) def _getPlotterCoords(self, posn, xsetting='xPos', ysetting='yPos'): """Calculate coordinates from relative or axis positioning. xsetting and ysetting are the settings to get data from """ s = self.settings xpos = s.get(xsetting).getFloatArray(self.document) ypos = s.get(ysetting).getFloatArray(self.document) if xpos is None or ypos is None: return None, None if s.positioning == 'axes': if hasattr(self.parent, 'getAxes'): axes = self.parent.getAxes( (s.xAxis, s.yAxis) ) else: return None, None if axes[0] is None or axes[1] is None: return None, None xpos = axes[0].dataToPlotterCoords(posn, xpos) ypos = axes[1].dataToPlotterCoords(posn, ypos) else: xpos = posn[0] + (posn[2]-posn[0])*xpos ypos = posn[3] - (posn[3]-posn[1])*ypos return xpos, ypos def _getGraphCoords(self, posn, xplt, yplt): """Calculate graph coodinates given plot coordinates xplt, yplt.""" s = self.settings xplt = N.array(xplt) yplt = N.array(yplt) if s.positioning == 'axes': if hasattr(self.parent, 'getAxes'): axes = self.parent.getAxes( (s.xAxis, s.yAxis) ) else: return None, None if axes[0] is None or axes[1] is None: return None, None xpos = axes[0].plotterToDataCoords(posn, xplt) ypos = axes[1].plotterToDataCoords(posn, yplt) else: xpos = (xplt - posn[0]) / (posn[2]-posn[0]) ypos = (yplt - posn[3]) / (posn[1]-posn[3]) return xpos, ypos veusz-3.0.1/veusz/widgets/grid.py0000664000175000017500000003516013164704704016445 0ustar jssjss00000000000000# Copyright (C) 2004 Jeremy S. Sanders # Email: Jeremy Sanders # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ############################################################################## """The grid class allows graphs to be arranged in a regular grid. The graphs may share axes if they are stored in the grid widget. """ from __future__ import division from ..compat import crange from .. import document from .. import setting from .. import qtall as qt4 from . import widget from . import graph from . import controlgraph def _(text, disambiguation=None, context='Grid'): """Translate text.""" return qt4.QCoreApplication.translate(context, text, disambiguation) class _gridengine: """Internal class to build up grid of widgets.""" def __init__(self, columns, rows): """Initialise allocater, with N columns or rows (other set to None).""" # we don't allocate space until it is used self.alloced = [] self.rows = rows self.columns = columns # starting position self.row = 0 self.col = 0 def isAlloced(self, c, r): """Returns whether element (c,r) allocated.""" if r >= len(self.alloced): return False row = self.alloced[r] if c >= len( row ): return False else: return row[c] def isAllocedBlock(self, c, r, w, h): """Is the block (c,r) -> (c+w,r+h) allocated?""" for y in crange(h): for x in crange(w): if self.isAlloced(c+x, y+r): return True return False def setAlloced(self, c, r): """Set element (c,r) as allocated.""" while r >= len(self.alloced): self.alloced.append( [] ) row = self.alloced[r] while c >= len(row): row.append( False ) row[c] = True def setAllocedBlock(self, c, r, w, h): """Set block (c,r)->(c+w,r+h) as allocated.""" for y in crange(h): for x in crange(w): self.setAlloced(x+c, y+r) def add(self, width, height): """Add a block of width x height, returning position as tuple.""" if self.columns is not None: # wrap around if item too wide # (providing we didn't request more columns than we have - # in that case we ignore the request) if ((self.col + width) > self.columns) and (width <= self.columns): self.col = 0 self.row += 1 # increase column until we can allocate the block # if we run out of columns, move to the next row while self.isAllocedBlock(self.col, self.row, width, height): self.col += 1 if (self.col + width > self.columns) and \ (width <= self.columns): self.col = 0 self.row += 1 # save position c = self.col r = self.row self.col += width else: # work in row based layout now if ((self.row + height) > self.rows) and (height <= self.rows): self.row = 0 self.col += 1 # increase row until we can allocate the next block # if we run out of rows, move to the next column while self.isAllocedBlock(self.col, self.row, width, height): self.row += 1 if (self.row + height > self.rows) and (height <= self.rows): self.row = 0 self.col += 1 # save position c = self.col r = self.row self.row += height # allocate and return block position self.setAllocedBlock(c, r, width, height) return (c, r) def getAllocedDimensions(self): """Return the columns x rows allocated.""" # assumes blocks don't get unset h = len(self.alloced) w = 0 for l in self.alloced: w = max(w, len(l)) return (w, h) class Grid(widget.Widget): """Class to hold plots in a grid arrangement. The idea is we either specify how many rows or columns to use. If we specify no of rows, then we fill vertically until we exceed rows, then we add another column. The same is true if cols is specified. """ typename='grid' allowusercreation=True description=_('Arrange graphs in a grid') def __init__(self, parent, name=None): """Initialise the grid. """ widget.Widget.__init__(self, parent, name=name) self.addAction( widget.Action( 'zeroMargins', self.actionZeroMargins, descr = _('Zero margins of graphs in grid'), usertext = _('Zero margins')) ) # calculated positions for children self.childpositions = {} # watch for changes to these variables to decide whether to # recalculate positions self.lastdimensions = None self.lastscalings = None self.lastchildren = None @classmethod def addSettings(klass, s): """Construct list of settings.""" widget.Widget.addSettings(s) s.add(setting.Int('rows', 2, descr = _('Number of rows in grid'), usertext=_('Number of rows')) ) s.add(setting.Int('columns', 2, descr = _('Number of columns in grid'), usertext=_('Number of columns')) ) s.add( setting.FloatList( 'scaleRows', [], descr = _('Row scaling factors. A sequence' ' of values\nby which to scale rows ' 'relative to each other.'), usertext=_('Row scalings')) ) s.add( setting.FloatList( 'scaleCols', [], descr = _('Column scaling factors. A sequence' ' of values\nby which to scale columns' ' relative to each other.'), usertext=_('Column scalings')) ) s.add( setting.Distance( 'leftMargin', '1.7cm', descr=_('Distance from left of grid to edge of page'), usertext=_('Left margin'), formatting=True) ) s.add( setting.Distance( 'rightMargin', '0.2cm', descr=_('Distance from right of grid to edge of page'), usertext=_('Right margin'), formatting=True) ) s.add( setting.Distance( 'topMargin', '0.2cm', descr=_('Distance from top of grid to edge of page'), usertext=_('Top margin'), formatting=True) ) s.add( setting.Distance( 'bottomMargin', '1.7cm', descr=_('Distance from bottom of grid to edge of page'), usertext=_('Bottom margin'), formatting=True) ) s.add( setting.Distance( 'internalMargin', '0cm', descr=_('Gap between grid members'), usertext=_('Internal margin'), formatting=True) ) @classmethod def allowedParentTypes(klass): from . import page return (page.Page, Grid) @property def userdescription(self): """User friendly description.""" s = self.settings return "%(rows)i rows, %(columns)i columns" % s def _recalcPositions(self): """(internal) recalculate the positions of the children.""" # class to handle management ge = _gridengine(self.settings.columns, self.settings.rows) # copy children, and remove any which are axes children = [ c for c in self.children if not c.isaxis ] child_dimensions = {} child_posns = {} for c in children: dims = (1, 1) child_dimensions[c] = dims child_posns[c] = ge.add(*dims) nocols, norows = ge.getAllocedDimensions() self.dims = (nocols, norows) # exit if there aren't any children if nocols == 0 or norows == 0: return # get total scaling factors for cols scalecols = list(self.settings.scaleCols[:nocols]) scalecols += [1.]*(nocols-len(scalecols)) totscalecols = sum(scalecols) if totscalecols == 0.: totscalecols = 1. # fractional starting positions of columns last = 0. startcols = [last] for scale in scalecols: last += scale/totscalecols startcols.append(last) # similarly get total scaling factors for rows scalerows = list(self.settings.scaleRows[:norows]) scalerows += [1.]*(norows-len(scalerows)) totscalerows = sum(scalerows) if totscalerows == 0.: totscalerows = 1. # fractional starting positions of rows last = 0. startrows = [last] for scale in scalerows: last += scale/totscalerows startrows.append(last) # iterate over children, and modify positions self.childpositions.clear() for child in children: dims = child_dimensions[child] pos = child_posns[child] self.childpositions[child] = ( ( pos[0], pos[1] ), ( startcols[pos[0]], startrows[pos[1]], startcols[pos[0]+dims[0]], startrows[pos[1]+dims[1]] ), ) def actionZeroMargins(self): """Zero margins of plots inside this grid.""" operations = [] for c in self.children: if isinstance(c, graph.Graph): s = c.settings for v in ('leftMargin', 'topMargin', 'rightMargin', 'bottomMargin'): operations.append( document.OperationSettingSet(s.get(v), '0cm') ) self.document.applyOperation( document.OperationMultiple(operations, descr='zero margins') ) def _drawChild(self, phelper, child, bounds, parentposn): """Draw child at correct position, with correct bounds.""" # default positioning coutbound = newbounds = parentposn if child in self.childpositions: intmargin = self.settings.get('internalMargin').convert(phelper) cidx, cpos = self.childpositions[child] # calculate size after margins dx = bounds[2]-bounds[0] dy = bounds[3]-bounds[1] marx = intmargin*max(0, self.dims[0]-1) mary = intmargin*max(0, self.dims[1]-1) if dx > marx and dy > mary: dx -= marx dy -= mary else: # margins too big intmargin = 0 # bounds for child newbounds = [ bounds[0]+dx*cpos[0]+intmargin*cidx[0], bounds[1]+dy*cpos[1]+intmargin*cidx[1], bounds[0]+dx*cpos[2]+intmargin*cidx[0], bounds[1]+dy*cpos[3]+intmargin*cidx[1] ] # bounds the axes can spread into coutbound = list(newbounds) # adjust outer bounds to half the internal margin space if cidx[0] > 0: coutbound[0] -= intmargin/2. if cidx[1] > 0: coutbound[1] -= intmargin/2. if cidx[0] < self.dims[0]-1: coutbound[2] += intmargin/2. if cidx[1] < self.dims[1]-1: coutbound[3] += intmargin/2. # work out bounds for graph in box # this is the space available for axes, etc # FIXME: should consider case if no graphs to side if cidx[0] == 0: coutbound[0] = parentposn[0] if cidx[1] == 0: coutbound[1] = parentposn[1] if cidx[0] == self.dims[0]-1: coutbound[2] = parentposn[2] if cidx[1] == self.dims[1]-1: coutbound[3] = parentposn[3] # draw widget child.draw(newbounds, phelper, outerbounds=coutbound) def getMargins(self, painthelper): """Use settings to compute margins.""" s = self.settings return ( s.get('leftMargin').convert(painthelper), s.get('topMargin').convert(painthelper), s.get('rightMargin').convert(painthelper), s.get('bottomMargin').convert(painthelper) ) def draw(self, parentposn, phelper, outerbounds=None): """Draws the widget's children.""" s = self.settings # if the contents have been modified, recalculate the positions dimensions = (s.columns, s.rows) scalings = (s.scaleRows, s.scaleCols) if ( self.children != self.lastchildren or self.lastdimensions != dimensions or self.lastscalings != scalings ): self._recalcPositions() self.lastchildren = list(self.children) self.lastdimensions = dimensions self.lastscalings = scalings bounds = self.computeBounds(parentposn, phelper) maxbounds = self.computeBounds(parentposn, phelper, withmargin=False) painter = phelper.painter(self, bounds) # controls for adjusting grid margins phelper.setControlGraph(self,[ controlgraph.ControlMarginBox(self, bounds, maxbounds, phelper)]) with painter: for child in self.children: if not child.isaxis: self._drawChild(phelper, child, bounds, parentposn) # do not call widget.Widget.draw, do not collect 200 pounds pass def updateControlItem(self, cgi): """Grid resized or moved - call helper routine to move self.""" cgi.setWidgetMargins() # allow the factory to instantiate a grid document.thefactory.register( Grid ) veusz-3.0.1/veusz/widgets/nonorthpoint.py0000664000175000017500000002533613302252613020254 0ustar jssjss00000000000000# Copyright (C) 2010 Jeremy S. Sanders # Email: Jeremy Sanders # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ############################################################################## """Non orthogonal point plotting.""" from __future__ import division import numpy as N from ..compat import czip from .. import qtall as qt4 from .. import document from .. import datasets from .. import setting from .. import utils from . import pickable from .nonorthgraph import NonOrthGraph, FillBrush from .widget import Widget from .point import MarkerFillBrush def _(text, disambiguation=None, context='NonOrthPoint'): """Translate text.""" return qt4.QCoreApplication.translate(context, text, disambiguation) class NonOrthPoint(Widget): '''Widget for plotting points in a non-orthogonal plot.''' typename = 'nonorthpoint' allowusercreation = True description = _('Plot points on a graph with non-orthogonal axes') @classmethod def addSettings(klass, s): '''Settings for widget.''' Widget.addSettings(s) s.add( setting.DatasetExtended( 'data1', 'x', descr=_('Dataset containing 1st dataset, list of values ' 'or expression'), usertext=_('Dataset 1')) ) s.add( setting.DatasetExtended( 'data2', 'y', descr=_('Dataset containing 2nd dataset, list of values ' 'or expression'), usertext=_('Dataset 2')) ) s.add( setting.DatasetOrStr( 'labels', '', descr=_('Dataset or string to label points'), usertext=_('Labels')) ) s.add( setting.DatasetExtended( 'scalePoints', '', descr = _('Scale size of plotted markers by this dataset, ' ' list of values or expression'), usertext=_('Scale markers')) ) s.add( setting.DataColor('Color') ) s.add( setting.Color('color', 'auto', descr = _('Master color'), usertext = _('Color'), formatting=True), 0 ) s.add( setting.DistancePt('markerSize', '3pt', descr = _('Size of marker to plot'), usertext=_('Marker size'), formatting=True), 0 ) s.add( setting.Marker('marker', 'circle', descr = _('Type of marker to plot'), usertext=_('Marker'), formatting=True), 0 ) s.add( setting.Line('PlotLine', descr = _('Plot line settings'), usertext = _('Plot line')), pixmap = 'settings_plotline' ) s.PlotLine.get('color').newDefault( setting.Reference('../color') ) s.add( setting.MarkerLine('MarkerLine', descr = _('Line around the marker settings'), usertext = _('Marker border')), pixmap = 'settings_plotmarkerline' ) s.add( MarkerFillBrush('MarkerFill', descr = _('Marker fill settings'), usertext = _('Marker fill')), pixmap = 'settings_plotmarkerfill' ) s.add( FillBrush('Fill1', descr = _('Fill settings (1)'), usertext = _('Area fill 1')), pixmap = 'settings_plotfillbelow' ) s.add( FillBrush('Fill2', descr = _('Fill settings (2)'), usertext = _('Area fill 2')), pixmap = 'settings_plotfillbelow' ) s.add( setting.PointLabel('Label', descr = _('Label settings'), usertext=_('Label')), pixmap = 'settings_axislabel' ) @classmethod def allowedParentTypes(klass): return (NonOrthGraph,) @property def userdescription(self): return _("data1='%s', data2='%s'") % ( self.settings.data1, self.settings.data2) def updateDataRanges(self, inrange): '''Extend inrange to range of data.''' d1 = self.settings.get('data1').getData(self.document) if d1: inrange[0] = min( N.nanmin(d1.data), inrange[0] ) inrange[1] = max( N.nanmax(d1.data), inrange[1] ) d2 = self.settings.get('data2').getData(self.document) if d2: inrange[2] = min( N.nanmin(d2.data), inrange[2] ) inrange[3] = max( N.nanmax(d2.data), inrange[3] ) def pickPoint(self, x0, y0, bounds, distance = 'radial'): p = pickable.DiscretePickable(self, 'data1', 'data2', lambda v1, v2: self.parent.graphToPlotCoords(v1, v2)) return p.pickPoint(x0, y0, bounds, distance) def pickIndex(self, oldindex, direction, bounds): p = pickable.DiscretePickable(self, 'data1', 'data2', lambda v1, v2: self.parent.graphToPlotCoords(v1, v2)) return p.pickIndex(oldindex, direction, bounds) def drawLabels(self, painter, xplotter, yplotter, textvals, markersize): """Draw labels for the points. This is copied from the xy (point) widget class, so it probably should be somehow be shared. FIXME: sane automatic placement of labels """ s = self.settings lab = s.get('Label') # work out offset an alignment deltax = markersize*1.5*{'left':-1, 'centre':0, 'right':1}[lab.posnHorz] deltay = markersize*1.5*{'top':-1, 'centre':0, 'bottom':1}[lab.posnVert] alignhorz = {'left':1, 'centre':0, 'right':-1}[lab.posnHorz] alignvert = {'top':-1, 'centre':0, 'bottom':1}[lab.posnVert] # make font and len textpen = lab.makeQPen(painter) painter.setPen(textpen) font = lab.makeQFont(painter) angle = lab.angle # iterate over each point and plot each label for x, y, t in czip(xplotter+deltax, yplotter+deltay, textvals): utils.Renderer( painter, font, x, y, t, alignhorz, alignvert, angle, doc=self.document).render() def getColorbarParameters(self): """Return parameters for colorbar.""" s = self.settings c = s.Color return (c.min, c.max, c.scaling, s.MarkerFill.colorMap, 0, s.MarkerFill.colorMapInvert) def autoColor(self, painter, dataindex=0): """Automatic color for plotting.""" return painter.docColorAuto( painter.helper.autoColorIndex((self, dataindex))) def draw(self, parentposn, phelper, outerbounds=None): '''Plot the data on a plotter.''' posn = self.computeBounds(parentposn, phelper) s = self.settings d = self.document # exit if hidden if s.hide: return d1 = s.get('data1').getData(d) d2 = s.get('data2').getData(d) dscale = s.get('scalePoints').getData(d) colorpoints = s.Color.get('points').getData(d) text = s.get('labels').getData(d, checknull=True) if not d1 or not d2: return x1, y1, x2, y2 = posn cliprect = qt4.QRectF( qt4.QPointF(x1, y1), qt4.QPointF(x2, y2) ) painter = phelper.painter(self, posn) with painter: self.parent.setClip(painter, posn) # split parts separated by NaNs for v1, v2, scalings, cvals, textitems in datasets.generateValidDatasetParts( [d1, d2, dscale, colorpoints, text]): # convert data (chopping down length) v1d, v2d = v1.data, v2.data minlen = min(v1d.shape[0], v2d.shape[0]) v1d, v2d = v1d[:minlen], v2d[:minlen] px, py = self.parent.graphToPlotCoords(v1d, v2d) # do fill1 (if any) if not s.Fill1.hide: self.parent.drawFillPts(painter, s.Fill1, cliprect, px, py) # do fill2 if not s.Fill2.hide: self.parent.drawFillPts(painter, s.Fill2, cliprect, px, py) # plot line if not s.PlotLine.hide: painter.setBrush( qt4.QBrush() ) painter.setPen(s.PlotLine.makeQPen(painter)) pts = qt4.QPolygonF() utils.addNumpyToPolygonF(pts, px, py) utils.plotClippedPolyline(painter, cliprect, pts) # plot markers markersize = s.get('markerSize').convert(painter) if not s.MarkerLine.hide or not s.MarkerFill.hide: pscale = colorvals = cmap = None if scalings: pscale = scalings.data # color point individually cmapname = s.MarkerFill.colorMap if cvals and not s.MarkerFill.hide and cmapname != 'none': colorvals = utils.applyScaling( cvals.data, s.Color.scaling, s.Color.min, s.Color.max) cmap = self.document.evaluate.getColormap( cmapname, s.MarkerFill.colorMapInvert) painter.setBrush(s.MarkerFill.makeQBrushWHide(painter)) painter.setPen(s.MarkerLine.makeQPenWHide(painter)) utils.plotMarkers(painter, px, py, s.marker, markersize, scaling=pscale, clip=cliprect, cmap=cmap, colorvals=colorvals, scaleline=s.MarkerLine.scaleLine) # finally plot any labels if textitems and not s.Label.hide: self.drawLabels(painter, px, py, textitems, markersize) # allow the factory to instantiate plotter document.thefactory.register( NonOrthPoint ) veusz-3.0.1/veusz/widgets/point.py0000664000175000017500000011025613302252613016640 0ustar jssjss00000000000000# Copyright (C) 2008 Jeremy S. Sanders # Email: Jeremy Sanders # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ############################################################################### """For plotting xy points.""" from __future__ import division import numpy as N from ..compat import czip from .. import qtall as qt4 from .. import datasets from .. import document from .. import setting from .. import utils from . import pickable from .plotters import GenericPlotter try: from ..helpers import qtloops hasqtloops = True except ImportError: hasqtloops = False def _(text, disambiguation=None, context='XY'): """Translate text.""" return qt4.QCoreApplication.translate(context, text, disambiguation) # functions for plotting error bars # different styles are made up of combinations of these functions # each function takes the same arguments def _errorBarsBar(style, xmin, xmax, ymin, ymax, xplotter, yplotter, s, painter, clip): """Draw bar style error lines.""" # vertical error bars if ymin is not None and ymax is not None and not s.ErrorBarLine.hideVert: utils.plotLinesToPainter(painter, xplotter, ymin, xplotter, ymax, clip) # horizontal error bars if xmin is not None and xmax is not None and not s.ErrorBarLine.hideHorz: utils.plotLinesToPainter(painter, xmin, yplotter, xmax, yplotter, clip) def _errorBarsEnds(style, xmin, xmax, ymin, ymax, xplotter, yplotter, s, painter, clip): """Draw perpendiclar ends on error bars.""" size = ( s.get('markerSize').convert(painter) * s.ErrorBarLine.endsize ) if ymin is not None and ymax is not None and not s.ErrorBarLine.hideVert: utils.plotLinesToPainter( painter, xplotter-size, ymin, xplotter+size, ymin, clip) utils.plotLinesToPainter( painter, xplotter-size, ymax, xplotter+size, ymax, clip) if xmin is not None and xmax is not None and not s.ErrorBarLine.hideHorz: utils.plotLinesToPainter( painter, xmin, yplotter-size, xmin, yplotter+size, clip) utils.plotLinesToPainter( painter, xmax, yplotter-size, xmax, yplotter+size, clip) def _errorBarsBox(style, xmin, xmax, ymin, ymax, xplotter, yplotter, s, painter, clip): """Draw box around error region.""" if utils.allNotNone(xmin, xmax, ymin, ymax): painter.setBrush( qt4.QBrush() ) utils.plotBoxesToPainter(painter, xmin, ymin, xmax, ymax, clip) def _errorBarsBoxFilled(style, xmin, xmax, ymin, ymax, xplotter, yplotter, s, painter, clip): """Draw box filled region inside error bars.""" if utils.allNotNone(xmin, xmax, ymin, ymax): # filled region below if not s.FillBelow.hideerror: path = qt4.QPainterPath() utils.addNumpyPolygonToPath( path, clip, xmin, ymin, xmin, yplotter, xmax, yplotter, xmax, ymin) utils.brushExtFillPath(painter, s.FillBelow, path, ignorehide=True) # filled region above if not s.FillAbove.hideerror: path = qt4.QPainterPath() utils.addNumpyPolygonToPath( path, clip, xmin, yplotter, xmax, yplotter, xmax, ymax, xmin, ymax) utils.brushExtFillPath(painter, s.FillAbove, path, ignorehide=True) def _errorBarsDiamond(style, xmin, xmax, ymin, ymax, xplotter, yplotter, s, painter, clip): """Draw diamond around error region.""" if utils.allNotNone(xmin, xmax, ymin, ymax): # expand clip by pen width (urgh) pw = painter.pen().widthF()*2 clip = qt4.QRectF( qt4.QPointF(clip.left()-pw,clip.top()-pw), qt4.QPointF(clip.right()+pw,clip.bottom()+pw)) path = qt4.QPainterPath() utils.addNumpyPolygonToPath( path, clip, xmin, yplotter, xplotter, ymax, xmax, yplotter, xplotter, ymin) painter.setBrush( qt4.QBrush() ) painter.drawPath(path) def _errorBarsDiamondFilled(style, xmin, xmax, ymin, ymax, xplotter, yplotter, s, painter, clip): """Draw diamond filled region inside error bars.""" if utils.allNotNone(xmin, xmax, ymin, ymax): if not s.FillBelow.hideerror: path = qt4.QPainterPath() utils.addNumpyPolygonToPath( path, clip, xmin, yplotter, xplotter, ymin, xmax, yplotter) utils.brushExtFillPath(painter, s.FillBelow, path, ignorehide=True) if not s.FillAbove.hideerror: path = qt4.QPainterPath() utils.addNumpyPolygonToPath( path, clip, xmin, yplotter, xplotter, ymax, xmax, yplotter) utils.brushExtFillPath(painter, s.FillAbove, path, ignorehide=True) def _errorBarsCurve(style, xmin, xmax, ymin, ymax, xplotter, yplotter, s, painter, clip): """Draw curve around error region.""" if utils.allNotNone(xmin, xmax, ymin, ymax): # non-filling brush painter.setBrush( qt4.QBrush() ) for xp, yp, xmn, ymn, xmx, ymx in czip( xplotter, yplotter, xmin, ymin, xmax, ymax): p = qt4.QPainterPath() p.moveTo(xp + (xmx-xp), yp) p.arcTo(qt4.QRectF( xp - (xmx-xp), yp - (yp-ymx), (xmx-xp)*2, (yp-ymx)*2), 0., 90.) p.arcTo(qt4.QRectF( xp - (xp-xmn), yp - (yp-ymx), (xp-xmn)*2, (yp-ymx)*2), 90., 90.) p.arcTo(qt4.QRectF( xp - (xp-xmn), yp - (ymn-yp), (xp-xmn)*2, (ymn-yp)*2), 180., 90.) p.arcTo(qt4.QRectF( xp - (xmx-xp), yp - (ymn-yp), (xmx-xp)*2, (ymn-yp)*2), 270., 90.) painter.drawPath(p) def _errorBarsCurveFilled(style, xmin, xmax, ymin, ymax, xplotter, yplotter, s, painter, clip): """Fill area around error region.""" if utils.allNotNone(xmin, xmax, ymin, ymax): for xp, yp, xmn, ymn, xmx, ymx in czip( xplotter, yplotter, xmin, ymin, xmax, ymax): if not s.FillAbove.hideerror: p = qt4.QPainterPath() p.moveTo(xp + (xmx-xp), yp) p.arcTo(qt4.QRectF( xp - (xmx-xp), yp - (yp-ymx), (xmx-xp)*2, (yp-ymx)*2), 0., 90.) p.arcTo(qt4.QRectF( xp - (xp-xmn), yp - (yp-ymx), (xp-xmn)*2, (yp-ymx)*2), 90., 90.) utils.brushExtFillPath(painter, s.FillAbove, p, ignorehide=True) if not s.FillBelow.hideerror: p = qt4.QPainterPath() p.moveTo(xp + (xp-xmn), yp) p.arcTo(qt4.QRectF( xp - (xp-xmn), yp - (ymn-yp), (xp-xmn)*2, (ymn-yp)*2), 180., 90.) p.arcTo(qt4.QRectF( xp - (xmx-xp), yp - (ymn-yp), (xmx-xp)*2, (ymn-yp)*2), 270., 90.) utils.brushExtFillPath(painter, s.FillBelow, p, ignorehide=True) def _errorBarsFilled(style, xmin, xmax, ymin, ymax, xplotter, yplotter, s, painter, clip): """Draw filled region as error region.""" ptsabove = qt4.QPolygonF() ptsbelow = qt4.QPolygonF() hidevert = True # keep track of what's shown hidehorz = True if ( 'vert' in style and (ymin is not None and ymax is not None) and not s.ErrorBarLine.hideVert ): hidevert = False # lines above/below points utils.addNumpyToPolygonF(ptsbelow, xplotter, ymin) utils.addNumpyToPolygonF(ptsabove, xplotter, ymax) elif ( 'horz' in style and (xmin is not None and xmax is not None) and not s.ErrorBarLine.hideHorz ): hidehorz = False # lines left/right points utils.addNumpyToPolygonF(ptsbelow, xmin, yplotter) utils.addNumpyToPolygonF(ptsabove, xmax, yplotter) # draw filled regions above/left and below/right if 'fill' in style and not (hidehorz and hidevert): # construct points for error bar regions retnpts = qt4.QPolygonF() utils.addNumpyToPolygonF(retnpts, xplotter[::-1], yplotter[::-1]) # polygons consist of lines joining the points and continuing # back along the plot line (retnpts) if not s.FillBelow.hideerror: utils.brushExtFillPolygon( painter, s.FillBelow, clip, ptsbelow+retnpts, ignorehide=True) if not s.FillAbove.hideerror: utils.brushExtFillPolygon( painter, s.FillAbove, clip, ptsabove+retnpts, ignorehide=True) # draw optional line (on top of fill) utils.plotClippedPolyline(painter, clip, ptsabove) utils.plotClippedPolyline(painter, clip, ptsbelow) # map error bar names to lists of functions (above) _errorBarFunctionMap = { 'none': (), 'bar': (_errorBarsBar,), 'bardiamond': (_errorBarsBar, _errorBarsDiamond,), 'barcurve': (_errorBarsBar, _errorBarsCurve,), 'barbox': (_errorBarsBar, _errorBarsBox,), 'barends': (_errorBarsBar, _errorBarsEnds,), 'box': (_errorBarsBox,), 'boxfill': (_errorBarsBoxFilled, _errorBarsBox,), 'diamond': (_errorBarsDiamond,), 'diamondfill': (_errorBarsDiamond, _errorBarsDiamondFilled), 'curve': (_errorBarsCurve,), 'curvefill': (_errorBarsCurveFilled, _errorBarsCurve,), 'fillhorz': (_errorBarsFilled,), 'fillvert': (_errorBarsFilled,), 'linehorz': (_errorBarsFilled,), 'linevert': (_errorBarsFilled,), 'linehorzbar': (_errorBarsBar, _errorBarsFilled), 'linevertbar': (_errorBarsBar, _errorBarsFilled), } def fillPtsToEdge(painter, pts, posn, cliprect, fillstyle): """Fill points depending on fill mode.""" ft = fillstyle.fillto if ft == 'top': x1, x2 = pts[0].x(), pts[-1].x() y1 = y2 = posn[1] elif ft == 'bottom': x1, x2 = pts[0].x(), pts[-1].x() y1 = y2 = posn[3] elif ft == 'left': y1, y2 = pts[0].y(), pts[-1].y() x1 = x2 = posn[0] elif ft == 'right': y1, y2 = pts[0].y(), pts[-1].y() x1 = x2 = posn[2] else: raise RuntimeError('Invalid fillto mode') polypts = qt4.QPolygonF([qt4.QPointF(x1, y1)]) polypts += pts polypts.append(qt4.QPointF(x2, y2)) utils.brushExtFillPolygon(painter, fillstyle, cliprect, polypts) class MarkerFillBrush(setting.Brush): def __init__(self, name, **args): setting.Brush.__init__(self, name, **args) self.get('color').newDefault( setting.Reference('../color') ) self.add( setting.Colormap( 'colorMap', 'grey', descr = _('If color markers dataset is given, use this colormap ' 'instead of the fill color'), usertext=_('Color map'), formatting=True) ) self.add( setting.Bool( 'colorMapInvert', False, descr = _('Invert color map'), usertext = _('Invert map'), formatting=True) ) class PointPlotter(GenericPlotter): """A class for plotting points and their errors.""" typename='xy' allowusercreation=True description=_('Plot points with lines and errorbars') @classmethod def addSettings(klass, s): """Construct list of settings.""" GenericPlotter.addSettings(s) # non-formatting s.add( setting.DatasetExtended( 'yData', 'y', descr=_('Y values, given by dataset, expression or list of values'), usertext=_('Y data')), 0 ) s.add( setting.DatasetExtended( 'xData', 'x', descr=_('X values, given by dataset, expression or list of values'), usertext=_('X data')), 0 ) s.add( setting.DatasetOrStr( 'labels', '', descr=_('Dataset or string to label points'), usertext=_('Labels')), 5 ) s.add( setting.DatasetExtended( 'scalePoints', '', descr = _('Scale size of markers given by dataset, expression' ' or list of values'), usertext=_('Scale markers')), 6 ) # formatting s.add( setting.Int( 'errorthin', 1, minval=1, descr=_('Thin number of error bars plotted by this factor'), usertext=_('Thin errors'), formatting=True), 0 ) s.add( setting.Int( 'thinfactor', 1, minval=1, descr=_('Thin number of markers plotted' ' for each datapoint by this factor'), usertext=_('Thin markers'), formatting=True), 0 ) s.add( setting.Color( 'color', 'auto', descr = _('Master color'), usertext = _('Color'), formatting=True), 0 ) s.add( setting.DistancePt( 'markerSize', '3pt', descr = _('Size of marker to plot'), usertext=_('Marker size'), formatting=True), 0 ) s.add( setting.Marker( 'marker', 'circle', descr = _('Type of marker to plot'), usertext=_('Marker'), formatting=True), 0 ) s.add( setting.DataColor('Color') ) s.add( setting.ErrorStyle( 'errorStyle', 'bar', descr=_('Style of error bars to plot'), usertext=_('Error style'), formatting=True) ) s.add( setting.XYPlotLine( 'PlotLine', descr = _('Plot line settings'), usertext = _('Plot line')), pixmap = 'settings_plotline' ) s.add( setting.MarkerLine( 'MarkerLine', descr = _('Line around the marker settings'), usertext = _('Marker border')), pixmap = 'settings_plotmarkerline' ) s.add( MarkerFillBrush( 'MarkerFill', descr = _('Marker fill settings'), usertext = _('Marker fill')), pixmap = 'settings_plotmarkerfill' ) s.add( setting.ErrorBarLine( 'ErrorBarLine', descr = _('Error bar line settings'), usertext = _('Error bar line')), pixmap = 'settings_ploterrorline' ) s.ErrorBarLine.get('color').newDefault( setting.Reference('../color') ) s.add( setting.PointFill( 'FillBelow', descr = _('Fill mode 1'), usertext = _('Fill 1')), pixmap = 'settings_plotfillbelow' ) s.FillBelow.get('fillto').newDefault('bottom') s.add( setting.PointFill( 'FillAbove', descr = _('Fill mode 2'), usertext = _('Fill 2')), pixmap = 'settings_plotfillabove' ) s.add( setting.PointLabel( 'Label', descr = _('Label settings'), usertext=_('Label')), pixmap = 'settings_axislabel' ) @property def userdescription(self): """User-friendly description.""" s = self.settings return "x='%s', y='%s', marker='%s'" % ( s.xData, s.yData, s.marker) def _plotErrors(self, posn, painter, xplotter, yplotter, axes, xdata, ydata, cliprect): """Plot error bars (horizontal and vertical). """ s = self.settings style = s.errorStyle if style == 'none': return # optional thinning of error bars plotted thin = s.errorthin # default is no error bars xmin = xmax = ymin = ymax = None # draw horizontal error bars if xdata.hasErrors(): xmin, xmax = xdata.getPointRanges() if thin>1: xmin, xmax = xmin[::thin], xmax[::thin] # convert xmin and xmax to graph coordinates xmin = axes[0].dataToPlotterCoords(posn, xmin) xmax = axes[0].dataToPlotterCoords(posn, xmax) # draw vertical error bars if ydata.hasErrors(): ymin, ymax = ydata.getPointRanges() if thin>1: ymin, ymax = ymin[::thin], ymax[::thin] # convert ymin and ymax to graph coordinates ymin = axes[1].dataToPlotterCoords(posn, ymin) ymax = axes[1].dataToPlotterCoords(posn, ymax) # no error bars - break out of processing below if ymin is None and ymax is None and xmin is None and xmax is None: return if thin>1: xplotter, yplotter = xplotter[::thin], yplotter[::thin] # iterate to call the error bars functions required to draw style pen = s.ErrorBarLine.makeQPenWHide(painter) pen.setCapStyle(qt4.Qt.FlatCap) painter.setPen(pen) for function in _errorBarFunctionMap[style]: function(style, xmin, xmax, ymin, ymax, xplotter, yplotter, s, painter, cliprect) def affectsAxisRange(self): """This widget provides range information about these axes.""" s = self.settings return ( (s.xAxis, 'sx'), (s.yAxis, 'sy') ) def getRange(self, axis, depname, axrange): """Compute the effect of data on the axis range.""" dataname = {'sx': 'xData', 'sy': 'yData'}[depname] dsetn = self.settings.get(dataname) data = dsetn.getData(self.document) if axis.settings.log: def updateRange(v): with N.errstate(invalid='ignore'): chopzero = v[(v>0) & N.isfinite(v)] if len(chopzero) > 0: axrange[0] = min(axrange[0], chopzero.min()) axrange[1] = max(axrange[1], chopzero.max()) else: def updateRange(v): fvals = v[N.isfinite(v)] if len(fvals) > 0: axrange[0] = min(axrange[0], fvals.min()) axrange[1] = max(axrange[1], fvals.max()) if data: data.rangeVisit(updateRange) elif dsetn.isEmpty(): # no valid dataset. # check if there a valid dataset for the other axis. # if there is, treat this as a row number dataname = {'sy': 'xData', 'sx': 'yData'}[depname] data = self.settings.get(dataname).getData(self.document) if data: length = data.data.shape[0] axrange[0] = min(axrange[0], 1) axrange[1] = max(axrange[1], length) def _getLinePoints( self, xvals, yvals, posn, xdata, ydata ): """Get the points corresponding to the line connecting the points.""" pts = qt4.QPolygonF() s = self.settings steps = s.PlotLine.steps # simple continuous line if steps == 'off': utils.addNumpyToPolygonF(pts, xvals, yvals) # stepped line, with points on left elif steps[:4] == 'left': x1 = xvals[:-1] x2 = xvals[1:] y1 = yvals[:-1] y2 = yvals[1:] utils.addNumpyToPolygonF(pts, x1, y1, x2, y1, x2, y2) # stepped line, with points on right elif steps[:5] == 'right': x1 = xvals[:-1] x2 = xvals[1:] y1 = yvals[:-1] y2 = yvals[1:] utils.addNumpyToPolygonF(pts, x1, y1, x1, y2, x2, y2) # stepped line, with points in centre # this is complex as we can't use the mean of the plotter coords, # as the axis could be log elif steps[:6] == 'centre': axes = self.parent.getAxes( (s.xAxis, s.yAxis) ) if xdata.hasErrors(): # Special case if error bars on x points: # here we use the error bars to define the steps xmin, xmax = xdata.getPointRanges() # this is duplicated from drawing error bars: bad # convert xmin and xmax to graph coordinates xmin = axes[0].dataToPlotterCoords(posn, xmin) xmax = axes[0].dataToPlotterCoords(posn, xmax) utils.addNumpyToPolygonF(pts, xmin, yvals, xmax, yvals) else: # we put the bin edges half way between the points # we assume this is the correct thing to do even in log space x1 = xvals[:-1] x2 = xvals[1:] y1 = yvals[:-1] y2 = yvals[1:] xc = 0.5*(x1+x2) utils.addNumpyToPolygonF(pts, x1, y1, xc, y1, xc, y2) if len(xvals) > 0: pts.append( qt4.QPointF(xvals[-1], yvals[-1]) ) elif steps[:7] == 'vcentre': axes = self.parent.getAxes( (s.xAxis, s.yAxis) ) if ydata.hasErrors(): # Special case if error bars on y points: # here we use the error bars to define the steps ymin, ymax = ydata.getPointRanges() # this is duplicated from drawing error bars: bad # convert ymin and ymax to graph coordinates ymin = axes[1].dataToPlotterCoords(posn, ymin) ymax = axes[1].dataToPlotterCoords(posn, ymax) utils.addNumpyToPolygonF(pts, xvals, ymin, xvals, ymax) else: # we put the bin edges half way between the points # we assume this is the correct thing to do even in log space y1 = yvals[:-1] y2 = yvals[1:] x1 = xvals[:-1] x2 = xvals[1:] yc = 0.5*(y1+y2) utils.addNumpyToPolygonF(pts, x1, y1, x1, yc, x2, yc) if len(yvals) > 0: pts.append( qt4.QPointF(xvals[-1], yvals[-1]) ) else: assert False return pts def _getBezierLine(self, poly, cliprect): """Try to draw a bezier line connecting the points.""" # clip to a larger box to help the lines get right angle bigclip = qt4.QRectF( cliprect.left()-cliprect.width()*0.5, cliprect.top()-cliprect.height()*0.5, cliprect.width()*2, cliprect.height()*2) # clip poly to the rectangle and return the parts polys = qtloops.clipPolyline(bigclip, poly) # add each part as a bezier path = qt4.QPainterPath() for lpoly in polys: if len(lpoly) >= 2: npts = qtloops.bezier_fit_cubic_multi(lpoly, 0.1, len(lpoly)+1) qtloops.addCubicsToPainterPath(path, npts); return path def _drawBezierLine( self, painter, xvals, yvals, posn, xdata, ydata, cliprect ): """Handle bezier lines and fills.""" pts = self._getLinePoints(xvals, yvals, posn, xdata, ydata) if len(pts) < 2: return path = self._getBezierLine(pts, cliprect) s = self.settings # do filling for fillstyle in s.FillBelow, s.FillAbove: if not fillstyle.hide: x1, y1, x2, y2 = { 'top': (pts[0].x(), posn[1], pts[-1].x(), posn[1]), 'bottom': (pts[0].x(), posn[3], pts[-1].x(), posn[3]), 'left': (posn[0], pts[0].y(), posn[0], pts[-1].y()), 'right': (posn[2], pts[0].y(), posn[2], pts[-1].y()) }[fillstyle.fillto] temppath = qt4.QPainterPath(path) temppath.lineTo(x2, y2) temppath.lineTo(x1, y1) utils.brushExtFillPath(painter, fillstyle, temppath) if not s.PlotLine.hide: painter.strokePath(path, s.PlotLine.makeQPen(painter)) def _drawPlotLine( self, painter, xvals, yvals, posn, xdata, ydata, cliprect ): """Draw the line connecting the points.""" pts = self._getLinePoints(xvals, yvals, posn, xdata, ydata) if len(pts) < 2: return s = self.settings # do filling for fillstyle in s.FillBelow, s.FillAbove: if not fillstyle.hide: fillPtsToEdge(painter, pts, posn, cliprect, fillstyle) # draw line between points if not s.PlotLine.hide: painter.setPen( s.PlotLine.makeQPen(painter) ) utils.plotClippedPolyline(painter, cliprect, pts) def drawKeySymbol(self, number, painter, x, y, width, height): """Draw the plot symbol and/or line.""" s = self.settings # datasets from document xv = s.get('xData').getData(self.document) yv = s.get('yData').getData(self.document) # whether data has errors hasxerrs = xv and xv.hasErrors() hasyerrs = yv and yv.hasErrors() # convert horizontal errors to vertical ones errstyle = s.errorStyle if errstyle in ('linehorz', 'fillhorz', 'likehorzbar'): errstyle = errstyle.replace('horz', 'vert') hasxerrs, hasyerrs = hasyerrs, hasxerrs # make some fake error bar data to plot yp = y + height/2 xpts = N.array([x-width, x+width/2, x+2*width]) ypts = N.array([yp, yp, yp]) # start drawing painter.save() cliprect = qt4.QRectF(qt4.QPointF(x,y), qt4.QPointF(x+width,y+height)) painter.setClipRect(cliprect) # draw fill setting if not s.FillBelow.hide: path = qt4.QPainterPath() path.addRect(qt4.QRectF( qt4.QPointF(x, yp), qt4.QPointF(x+width, yp+height*0.45))) utils.brushExtFillPath(painter, s.FillBelow, path) if not s.FillAbove.hide: path = qt4.QPainterPath() path.addRect(qt4.QRectF( qt4.QPointF(x, yp), qt4.QPointF(x+width, yp-height*0.45))) utils.brushExtFillPath(painter, s.FillAbove, path) # make points for error bars (if any) errorsize = height*0.4 if xv and hasxerrs: xneg = N.array([x-width, x+width/2-errorsize, x+2*width]) xpos = N.array([x-width, x+width/2+errorsize, x+2*width]) else: xneg = xpos = xpts if yv and hasyerrs: yneg = N.array([yp-errorsize, yp-errorsize, yp-errorsize]) ypos = N.array([yp+errorsize, yp+errorsize, yp+errorsize]) else: yneg = ypos = ypts # plot error bar painter.setPen( s.ErrorBarLine.makeQPenWHide(painter) ) for function in _errorBarFunctionMap[errstyle]: function(errstyle, xneg, xpos, yneg, ypos, xpts, ypts, s, painter, cliprect) # draw line if not s.PlotLine.hide: painter.setPen( s.PlotLine.makeQPen(painter) ) painter.drawLine( qt4.QPointF(x, yp), qt4.QPointF(x+width, yp) ) # draw marker if not s.MarkerLine.hide or not s.MarkerFill.hide: if not s.MarkerFill.hide: painter.setBrush( s.MarkerFill.makeQBrush(painter) ) if not s.MarkerLine.hide: painter.setPen( s.MarkerLine.makeQPen(painter) ) else: painter.setPen( qt4.QPen( qt4.Qt.NoPen ) ) size = s.get('markerSize').convert(painter) utils.plotMarker(painter, x+width/2, yp, s.marker, size) painter.restore() def drawLabels(self, painter, xplotter, yplotter, textvals, markersize): """Draw labels for the points.""" s = self.settings lab = s.get('Label') # work out offset an alignment deltax = markersize*1.5*{'left':-1, 'centre':0, 'right':1}[lab.posnHorz] deltay = markersize*1.5*{'top':-1, 'centre':0, 'bottom':1}[lab.posnVert] alignhorz = {'left':1, 'centre':0, 'right':-1}[lab.posnHorz] alignvert = {'top':-1, 'centre':0, 'bottom':1}[lab.posnVert] # make font and len textpen = lab.makeQPen(painter) painter.setPen(textpen) font = lab.makeQFont(painter) angle = lab.angle # iterate over each point and plot each label for x, y, t in czip(xplotter+deltax, yplotter+deltay, textvals): utils.Renderer( painter, font, x, y, t, alignhorz, alignvert, angle, doc=self.document).render() def getAxisLabels(self, direction): """Get labels for axis if using a label axis.""" s = self.settings doc = self.document text = s.get('labels').getData(doc, checknull=True) xv = s.get('xData').getData(doc) yv = s.get('yData').getData(doc) # handle missing dataset if yv and not xv and s.get('xData').isEmpty(): length = yv.data.shape[0] xv = datasets.DatasetRange(length, (1,length)) elif xv and not yv and s.get('yData').isEmpty(): length = xv.data.shape[0] yv = datasets.DatasetRange(length, (1,length)) if text is None or xv is None or yv is None: return (None, None) if direction == 'horizontal': return (text, xv.data) else: return (text, yv.data) def _pickable(self, bounds): axes = self.fetchAxes() if axes is None: map_fn = None else: map_fn = lambda x, y: ( axes[0].dataToPlotterCoords(bounds, x), axes[1].dataToPlotterCoords(bounds, y) ) return pickable.DiscretePickable(self, 'xData', 'yData', map_fn) def pickPoint(self, x0, y0, bounds, distance = 'radial'): return self._pickable(bounds).pickPoint(x0, y0, bounds, distance) def pickIndex(self, oldindex, direction, bounds): return self._pickable(bounds).pickIndex(oldindex, direction, bounds) def getColorbarParameters(self): """Return parameters for colorbar.""" s = self.settings c = s.Color return (c.min, c.max, c.scaling, s.MarkerFill.colorMap, 0, s.MarkerFill.colorMapInvert) def dataDraw(self, painter, axes, posn, cliprect): """Plot the data on a plotter.""" # get data s = self.settings doc = self.document xv = s.get('xData').getData(doc) yv = s.get('yData').getData(doc) text = s.get('labels').getData(doc, checknull=True) scalepoints = s.get('scalePoints').getData(doc) colorpoints = s.Color.get('points').getData(doc) # if a missing dataset, make a fake dataset for the second one # based on a row number if xv and not yv and s.get('yData').isEmpty(): # use index for y data length = xv.data.shape[0] yv = datasets.DatasetRange(length, (1,length)) elif yv and not xv and s.get('xData').isEmpty(): # use index for x data length = yv.data.shape[0] xv = datasets.DatasetRange(length, (1,length)) if not xv or not yv: # no valid dataset, so exit return # if text entered, then multiply up to get same number of values # as datapoints if text: length = min( len(xv.data), len(yv.data) ) text = text*(length // len(text)) + text[:length % len(text)] # loop over chopped up values for xvals, yvals, tvals, ptvals, cvals in ( datasets.generateValidDatasetParts( [xv, yv, text, scalepoints, colorpoints])): #print "Calculating coordinates" # calc plotter coords of x and y points xplotter = axes[0].dataToPlotterCoords(posn, xvals.data) yplotter = axes[1].dataToPlotterCoords(posn, yvals.data) # points are plotted offset in shift-points modes if s.PlotLine.steps != 'off': xpltpoint = N.array(xplotter) if s.PlotLine.steps == 'right-shift-points': xpltpoint[1:] = 0.5*(xplotter[:-1] + xplotter[1:]) elif s.PlotLine.steps == 'left-shift-points': xpltpoint[:-1] = 0.5*(xplotter[:-1] + xplotter[1:]) else: xpltpoint = xplotter ypltpoint = yplotter # plot filled error bars if s.errorStyle in ('fillvert', 'fillhorz'): # filled region errors are painted first self._plotErrors( posn, painter, xpltpoint, ypltpoint, axes, xvals, yvals, cliprect) #print "Painting plot line" # plot data line (and/or filling above or below) if not s.PlotLine.hide or not s.FillAbove.hide or not s.FillBelow.hide: if s.PlotLine.bezierJoin and hasqtloops: self._drawBezierLine( painter, xplotter, yplotter, posn, xvals, yvals, cliprect ) else: self._drawPlotLine( painter, xplotter, yplotter, posn, xvals, yvals, cliprect ) #print "Painting error bars" # plot normal errors bars if s.errorStyle not in ('fillvert', 'fillhorz'): # normally the error bar is painted after the line self._plotErrors(posn, painter, xpltpoint, ypltpoint, axes, xvals, yvals, cliprect) # plot the points (we do this last so they are on top) markersize = s.get('markerSize').convert(painter) if not s.MarkerLine.hide or not s.MarkerFill.hide: #print "Painting marker fill" if not s.MarkerFill.hide: # filling for markers painter.setBrush( s.MarkerFill.makeQBrush(painter) ) else: # no-filling brush painter.setBrush( qt4.QBrush() ) #print "Painting marker lines" if not s.MarkerLine.hide: # edges of markers painter.setPen( s.MarkerLine.makeQPen(painter) ) else: # invisible pen painter.setPen( qt4.QPen(qt4.Qt.NoPen) ) # thin datapoints as required if s.thinfactor <= 1: xplt, yplt = xpltpoint, ypltpoint else: xplt, yplt = ( xpltpoint[::s.thinfactor], ypltpoint[::s.thinfactor]) # whether to scale markers scaling = colorvals = cmap = None if ptvals: scaling = ptvals.data if s.thinfactor > 1: scaling = scaling[::s.thinfactor] # color point individually cmapname = s.MarkerFill.colorMap if cvals and not s.MarkerFill.hide and cmapname != 'none': colorvals = utils.applyScaling( cvals.data, s.Color.scaling, s.Color.min, s.Color.max) if s.thinfactor > 1: colorvals = colorvals[::s.thinfactor] cmap = self.document.evaluate.getColormap( cmapname, s.MarkerFill.colorMapInvert) # actually plot datapoints utils.plotMarkers( painter, xplt, yplt, s.marker, markersize, scaling=scaling, clip=cliprect, cmap=cmap, colorvals=colorvals, scaleline=s.MarkerLine.scaleLine) # finally plot any labels if tvals and not s.Label.hide: self.drawLabels( painter, xpltpoint, ypltpoint, tvals, markersize) # allow the factory to instantiate an x,y plotter document.thefactory.register( PointPlotter ) veusz-3.0.1/veusz/widgets/covariance.py0000664000175000017500000002076413171725116017634 0ustar jssjss00000000000000# Copyright (C) 2016 Jeremy S. Sanders # Email: Jeremy Sanders # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ############################################################################### """Widget for plotting data covariance.""" from __future__ import division import numpy as N from .. import qtall as qt4 from ..compat import czip from . import plotters from .. import document from .. import setting from .. import utils def _(text, disambiguation=None, context='Covariance'): """Translate text.""" return qt4.QCoreApplication.translate(context, text, disambiguation) class CovarianceLine(setting.Line): def __init__(self, name, **args): setting.Line.__init__(self, name, **args) self.add( setting.Int( 'steps', 25, minval=4, descr=_('Number of line steps to draw'), usertext=_('Steps') )) self.get('color').newDefault('auto') class Covariance(plotters.GenericPlotter): """Plot covariance matrix for points as shapes.""" typename = 'covariance' allowusercreation=True description=_('Plot covariance ellipses') def __init__(self, parent, **args): """Initialise plotter.""" plotters.GenericPlotter.__init__(self, parent, **args) self._elpts = [] self._changeset = -1 @classmethod def addSettings(klass, s): """Construct list of settings.""" plotters.GenericPlotter.addSettings(s) s.add( setting.DatasetExtended( 'covyy', '', descr=_('Covariance matrix entry (Y,Y) [computed from data if blank]'), usertext=_('Cov(Y,Y)')), 0 ) s.add( setting.DatasetExtended( 'covxy', '', descr=_('Covariance matrix entry (X,Y) [computed from data if blank]'), usertext=_('Cov(X,Y)')), 0 ) s.add( setting.DatasetExtended( 'covyx', '', descr=_('Covariance matrix entry (Y,X) [computed from data if blank]'), usertext=_('Cov(Y,X)')), 0 ) s.add( setting.DatasetExtended( 'covxx', '', descr=_('Covariance matrix entry (X,X) [computed from data if blank]'), usertext=_('Cov(X,X)')), 0 ) s.add( setting.DatasetExtended( 'yData', 'y', descr=_('Y values, given by dataset, expression or list of values'), usertext=_('Y data')), 0 ) s.add( setting.DatasetExtended( 'xData', 'x', descr=_('X values, given by dataset, expression or list of values'), usertext=_('X data')), 0 ) s.add( CovarianceLine( 'Line', descr = _('Line'), usertext = _('Ellipse line')), pixmap = 'settings_plotline' ) s.add( setting.PlotterFill( 'Fill', descr = _('Fill'), usertext = _('Ellipse fill')), pixmap = 'settings_plotfillbelow' ) def _computeCovFromData(self, data): """Compute a single covariance matrix given data.""" minlen = min(len(data['xData']), len(data['yData'])) xd = data['xData'][:minlen] yd = data['yData'][:minlen] finite = N.isfinite(xd) & N.isfinite(yd) xd = xd[finite] yd = yd[finite] if len(xd) < 2: cov = N.array([[0,0],[0,0]]) else: cov = N.cov(xd, y=yd) data['xData'] = N.array([N.mean(xd)]) data['yData'] = N.array([N.mean(yd)]) data['covxx'] = N.array([cov[0,0]]) data['covxy'] = N.array([cov[1,0]]) data['covyx'] = N.array([cov[0,1]]) data['covyy'] = N.array([cov[1,1]]) def _computeEllipses(self): """Calculate points for ellipses.""" s = self.settings d = self.document # cache existing value if dataset unchanged if self._changeset == d.changeset: return self._elpts = [] minlen = 1e99 data = {} anynone = False for attr in 'xData', 'yData', 'covxx', 'covxy', 'covyx', 'covyy': dataset = s.get(attr).getData(d) # needs to be defined if dataset is not None: ds = dataset.data minlen = min(minlen, len(ds)) data[attr] = ds else: data[attr] = None anynone = True # if covariance matrix not provided, compute from xy data if ( data['xData'] is not None and data['yData'] is not None and s.get('covxx').isEmpty() and s.get('covxy').isEmpty() and s.get('covyx').isEmpty() and s.get('covyy').isEmpty() ): self._computeCovFromData(data) minlen=1 elif anynone: # invalid return # chop to minimum length for attr in data: if minlen != len(data[attr]): data[attr] = data[attr][:minlen] # remove invalid values valid = N.all([N.isfinite(ds) for ds in data.values()], axis=0) for attr in data: data[attr] = data[attr][valid] # construct covariance matrices cov = N.column_stack(( data['covxx'], data['covyx'], data['covxy'], data['covyy'] )).reshape(minlen, 2, 2) # compute eigenvalues and vectors from covariance matrices try: eigvals, eigvecs = N.linalg.eig(cov) except N.linalg.LinAlgError: return # multiply vectors be sqrt eigenvalues (error is sqrt) sqrtvals = N.sqrt(eigvals) eigcomb = eigvecs * sqrtvals[:,:,None] # generate points in ellipse numsteps = s.Line.steps x = N.linspace(0, N.pi*2, numsteps, endpoint=False) f1, f2 = N.cos(x), N.sin(x) combv = f1*eigcomb[:,0,:,None] + f2*eigcomb[:,1,:,None] xpts = data['xData'][:,None] - combv[:,0,:] ypts = data['yData'][:,None] + combv[:,1,:] # funny covariance matrix does this if N.any(N.iscomplex(xpts)) or N.any(N.iscomplex(ypts)): return # now we have the points self._elpts = [xpts, ypts] def affectsAxisRange(self): """This widget provides range information about these axes.""" s = self.settings return ( (s.xAxis, 'sx'), (s.yAxis, 'sy') ) def getRange(self, axis, depname, axrange): """Return range of data.""" self._computeEllipses() if not self._elpts: return vals = self._elpts[{'sx': 0, 'sy': 1}[depname]] if axis.settings.log: vals = N.clip(vals, 1e-99, 1e99) if len(vals) > 0: axrange[0] = min(axrange[0], vals.min()) axrange[1] = max(axrange[1], vals.max()) def dataDraw(self, painter, axes, posn, cliprect): """Draw dataset.""" s = self.settings self._computeEllipses() if not self._elpts: return if axes[0].settings.log: self._elpts[0] = N.clip(self._elpts[0], 1e-99, 1e99) if axes[1].settings.log: self._elpts[1] = N.clip(self._elpts[1], 1e-99, 1e99) ptsx = axes[0].dataToPlotterCoords(posn, self._elpts[0]) ptsy = axes[1].dataToPlotterCoords(posn, self._elpts[1]) pen = s.Line.makeQPenWHide(painter) pw = pen.widthF()*2 x1, y1, x2, y2 = posn lineclip = qt4.QRectF( qt4.QPointF(x1-pw, y1-pw), qt4.QPointF(x2+pw, y2+pw)) for xvals, yvals in czip(ptsx, ptsy): path = qt4.QPainterPath() poly = qt4.QPolygonF() utils.addNumpyToPolygonF(poly, xvals, yvals) clippedpoly = qt4.QPolygonF() utils.polygonClip(poly, lineclip, clippedpoly) path.addPolygon(clippedpoly) path.closeSubpath() utils.brushExtFillPath(painter, s.Fill, path, stroke=pen) document.thefactory.register(Covariance) veusz-3.0.1/veusz/widgets/__init__.py0000664000175000017500000000412613304514354017251 0ustar jssjss00000000000000# Copyright (C) 2004 Jeremy S. Sanders # Email: Jeremy Sanders # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ############################################################################## """Widgets are defined in this module.""" from .widget import Widget, Action from .axis import Axis from .axisbroken import AxisBroken from .axisfunction import AxisFunction from .graph import Graph from .grid import Grid from .plotters import GenericPlotter, FreePlotter from .pickable import PickInfo from .point import PointPlotter from .function import FunctionPlotter from .textlabel import TextLabel from .page import Page from .root import Root from .key import Key from .fit import Fit from .image import Image from .contour import Contour from .colorbar import ColorBar from .shape import Shape, BoxShape, Rectangle, Ellipse, ImageFile from .line import Line from .bar import BarPlotter from .polygon import Polygon from .vectorfield import VectorField from .boxplot import BoxPlot from .polar import Polar from .ternary import Ternary from .nonorthpoint import NonOrthPoint from .nonorthfunction import NonOrthFunction from .scene3d import Scene3D from .graph3d import Graph3D from .axis3d import Axis3D from .plotters3d import GenericPlotter3D from .function3d import Function3D from .point3d import Point3D from .surface3d import Surface3D from .covariance import Covariance from .volume3d import Volume3D veusz-3.0.1/veusz/widgets/axisticks.py0000664000175000017500000005041513164512632017517 0ustar jssjss00000000000000# axisticks.py # algorithm to work out what tick-marks to put on an axis # Copyright (C) 2003 Jeremy S. Sanders # Email: Jeremy Sanders # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ############################################################################## from __future__ import division import math import numpy as N from ..compat import crange from .. import utils """Algorithms for working with axis ticks. These algorithms were designed by me (Jeremy Sanders), so there may well be bugs. Please report them. The idea is to try to achieve a set number of major and minor ticks by looking though a list of allowable interval values (after taking account of what power of 10 the coordinates are in). """ class AxisTicksBase(object): """Base class of axis ticks classes.""" def __init__( self, minval, maxval, numticks, numminorticks, logaxis = False, prefermore = True, extendmin = False, extendmax = False, forceinterval = None ): """Initialise the class. minval and maxval are the range of the data to be plotted numticks number of major ticks to aim for logaxis: axis logarithmic? prefermore: prefer more ticks rather than fewer extendbounds: extend minval and maxval to nearest tick if okay forceinterval: force interval to one given (if allowed). interval is tuple as returned in self.interval after calling getTicks() """ # clip to sensible range self.minval = max(min(minval, 1e100), -1e100) self.maxval = max(min(maxval, 1e100), -1e100) # tick parameters self.numticks = numticks self.numminorticks = numminorticks self.logaxis = logaxis self.prefermore = prefermore self.extendmin = extendmin self.extendmax = extendmax self.forceinterval = forceinterval def getTicks( self ): """Calculate and return the position of the major ticks. Results are returned as attributes of this object in interval, minval, maxval, tickvals, minorticks, autoformat """ class AxisTicks(AxisTicksBase): """Class to work out at what values axis major ticks should appear.""" # the allowed values we allow ticks to increase by # first values are the major tick intervals, followed by a list # of allowed minors allowed_minorintervals_linear = { 1.: (0.1, 0.2, 0.5), 2.: (0.2, 0.5, 1.), 5.: (0.5, 1., 2.5), 2.5: (0.5,) } # just get the allowable majors allowed_intervals_linear = sorted(allowed_minorintervals_linear) # the allowed values we can increase by in log space # by default we increase by 10^3 # if the first value is chosen we can use the "special" log minor ticks allowed_intervals_log = (1., 3., 6., 9., 12., 15., 19.) # positions we're allowed to put minor intervals allowed_minorintervals_log = (1., 3., 6., 9., 12., 15., 19.) # how much we should allow axes to extend to a tick max_extend_factor = 0.15 def _calcTickValues( self, minval, maxval, delta ): """Compute the tick values, given minval, maxval and delta.""" startmult = int( math.ceil( minval / delta ) ) stopmult = int( math.floor( maxval / delta ) ) return N.arange(startmult, stopmult+1) * delta def _tickNums(self, minval, maxval, delta): """Calculate number of ticks between minval and maxval with delta.""" startmult = int( math.ceil( minval / delta ) ) stopmult = int( math.floor( maxval / delta ) ) return (stopmult-startmult)+1 def _calcNoTicks( self, interval, logdelta ): """Return the number of ticks with spacing interval*10^logdelta. Returns a tuple (noticks, minval, maxval). """ # store these for modification (if we extend bounds) minval = self.minval maxval = self.maxval # calculate tick spacing and maximum extension factor delta = interval * (10**logdelta) maxextend = (maxval - minval) * AxisTicks.max_extend_factor # should we try to extend to nearest interval*10^logdelta? if self.extendmin: # extend minval if possible if math.fabs( math.modf( minval / delta )[0] ) > 1e-8: d = minval - ( math.floor( minval / delta ) * delta ) if d <= maxextend: minval -= d if self.extendmax: # extend maxval if possible if math.fabs( math.modf( maxval / delta)[0] ) > 1e-8: d = ( (math.floor(maxval / delta)+1.) * delta) - maxval if d <= maxextend: maxval += d numticks = self._tickNums(minval, maxval, delta) return (numticks, minval, maxval) def _calcLinearMinorTickValues(self, minval, maxval, interval, logstep, allowedintervals): """Get the best values for minor ticks on a linear axis Algorithm tries to look for best match to nominorticks Pass routine major ticks from minval to maxval with steps of interval*(10**logstep) """ # iterate over allowed minor intervals best = -1 best_numticks = -1 best_delta = 1000000 mult = 10.**logstep # iterate over allowed minor intervals for minint in allowedintervals: numticks = self._tickNums(minval, maxval, minint*mult) d = abs( self.numminorticks - numticks ) # if this is a better match to the number of ticks # we want, choose this if ((d < best_delta ) or (d == best_delta and (self.prefermore and numticks > best_numticks) or (not self.prefermore and numticks < best_numticks)) ): best = minint best_delta = d best_numticks = numticks # use best value to return tick values return self._calcTickValues(minval, maxval, best*mult) def _calcLogMinorTickValues( self, minval, maxval ): """Calculate minor tick values with a log scale.""" # this is a scale going e.g. 1,2,3,...8,9,10,20,30...90,100,200... # round down to nearest power of 10 for each alpha = int( math.floor( N.log10(minval) ) ) beta = int( math.floor( N.log10(maxval) ) ) ticks = [] # iterate over range in log space for i in crange(alpha, beta+1): power = 10.**i # add ticks for values in correct range for j in crange(2, 10): v = power*j # blah log conversions mean we have to use 'fuzzy logic' if ( math.fabs(v - minval)/v < 1e-6 or v > minval ) and \ ( math.fabs(v - maxval)/v < 1e-6 or v < maxval ) : ticks.append(v) return N.array( ticks ) def _selectBestTickFromSelection(self, selection): """Choose best tick from selection given.""" # we now try to find the best matching value minabsdelta = 1e99 mindelta = 1e99 bestsel = () # find the best set of tick labels for s in selection: # difference between what we want and what we have delta = s[0] - self.numticks absdelta = abs(delta) # if it matches better choose this if absdelta < minabsdelta: minabsdelta = absdelta mindelta = delta bestsel = s # if we find two closest matching label sets, we # test whether we prefer too few to too many labels if absdelta == minabsdelta: if (self.prefermore and (delta > mindelta)) or \ (not self.prefermore and (delta < mindelta)): minabsdelta = absdelta mindelta = delta bestsel = s return bestsel def _getBestTickSelection(self, allowed_intervals): """Go through allowed tick intervals and find one best matching requested parameters.""" # work out range and log range therange = self.maxval - self.minval intlogrange = int( N.log10( therange ) ) # we step variable to move through log space to find best ticks logstep = intlogrange + 1 # we iterate down in log spacing, until we have more than twice # the number of ticks requested. # Maybe a better algorithm is required selection = [] # keep track of largest number of ticks calculated largestno = 0 while True: for interval in allowed_intervals: no, minval, maxval = self._calcNoTicks( interval, logstep ) selection.append( (no, interval, logstep, minval, maxval ) ) largestno = max(largestno, no) if largestno > self.numticks*2: break logstep -= 1 # necessary as we don't want 10**x on axis if |x|<1 # :-( if logstep < 0 and self.logaxis: break return selection def _tickSelector(self, allowed_intervals): """With minval and maxval find best tick positions.""" if self.forceinterval is None: # get selection of closely matching ticks selection = self._getBestTickSelection(allowed_intervals) # now we have the best, we work out the ticks and return bestsel = self._selectBestTickFromSelection(selection) dummy, interval, loginterval, minval, maxval = bestsel else: # forced specific interval requested interval, loginterval = self.forceinterval no, minval, maxval = self._calcNoTicks(interval, loginterval) # calculate the positions of the ticks from parameters tickdelta = interval * 10.**loginterval ticks = self._calcTickValues( minval, maxval, tickdelta ) return (minval, maxval, ticks, interval, loginterval) def getTicks(self): """Calculate and return the position of the major ticks. """ if self.logaxis: # which intervals we'll accept for major ticks intervals = AxisTicks.allowed_intervals_log # transform range into log space self.minval = N.log10( self.minval ) self.maxval = N.log10( self.maxval ) else: # which linear intervals we'll allow intervals = AxisTicks.allowed_intervals_linear # avoid breakage if range is zero if abs(self.minval - self.maxval) < 1e-99: self.maxval = self.minval + 1. minval, maxval, tickvals, interval, loginterval = self._tickSelector( intervals ) # work out the most appropriate minor tick intervals if not self.logaxis: # just plain minor ticks # try to achieve no of minors close to value requested minorticks = self._calcLinearMinorTickValues( minval, maxval, interval, loginterval, AxisTicks.allowed_minorintervals_linear[interval] ) else: # log axis if interval == 1.: # calculate minor ticks # here we use 'conventional' minor log tick spacing # e.g. 0.9, 1, 2, .., 8, 9, 10, 20, 30 ... minorticks = self._calcLogMinorTickValues( 10.**minval, 10.**maxval) # Here we test whether more log major tick values are needed... # often we might only have one tick value, and so we add 2, then 5 # this is a bit of a hack: better ideas please!! if len(tickvals) < 2: # get lower power of 10 low10 = int( math.floor(minval) ) # could use numpy here for i in (2., 5., 20., 50.): n = low10 + math.log10(i) if n >= minval and n <= maxval: tickvals = N.concatenate( (tickvals, N.array([n]) )) else: # if we increase by more than one power of 10 on the # axis, we can't do the above, so we do linear ticks # in log space # aim is to choose powers of 3 for majors and minors # to make it easy to read the axis. comments? minorticks = self._calcLinearMinorTickValues( minval, maxval, interval, loginterval, AxisTicks.allowed_minorintervals_log) minorticks = 10.**minorticks # transform normal ticks back to real space minval = 10.**minval maxval = 10.**maxval tickvals = 10.**tickvals self.interval = (interval, loginterval) self.minorticks = minorticks self.minval = minval self.maxval = maxval self.tickvals = tickvals self.autoformat = '%Vg' class DateTicks(AxisTicksBase): """For formatting dates. We want something that chooses appropriate intervals So we want to choose most apropriate interval depending on number of ticks requested """ # possible intervals for a time/date axis # tuples of ((y, m, d, h, m, s, msec), autoformat) intervals = ( ((200, 0, 0, 0, 0, 0, 0), '%VDY'), ((100, 0, 0, 0, 0, 0, 0), '%VDY'), ((50, 0, 0, 0, 0, 0, 0), '%VDY'), ((20, 0, 0, 0, 0, 0, 0), '%VDY'), ((10, 0, 0, 0, 0, 0, 0), '%VDY'), ((5, 0, 0, 0, 0, 0, 0), '%VDY'), ((2, 0, 0, 0, 0, 0, 0), '%VDY'), ((1, 0, 0, 0, 0, 0, 0), '%VDY'), ((0, 6, 0, 0, 0, 0, 0), '%VDY-%VDm'), ((0, 4, 0, 0, 0, 0, 0), '%VDY-%VDm'), ((0, 3, 0, 0, 0, 0, 0), '%VDY-%VDm'), ((0, 2, 0, 0, 0, 0, 0), '%VDY-%VDm'), ((0, 1, 0, 0, 0, 0, 0), '%VDY-%VDm'), ((0, 0, 28, 0, 0, 0, 0), '%VDY-%VDm-%VDd'), ((0, 0, 14, 0, 0, 0, 0), '%VDY-%VDm-%VDd'), ((0, 0, 7, 0, 0, 0, 0), '%VDY-%VDm-%VDd'), ((0, 0, 2, 0, 0, 0, 0), '%VDY-%VDm-%VDd'), ((0, 0, 1, 0, 0, 0, 0), '%VDY-%VDm-%VDd'), ((0, 0, 0, 12, 0, 0, 0), '%VDY-%VDm-%VDd\\\\%VDH:%VDM'), ((0, 0, 0, 6, 0, 0, 0), '%VDY-%VDm-%VDd\\\\%VDH:%VDM'), ((0, 0, 0, 4, 0, 0, 0), '%VDY-%VDm-%VDd\\\\%VDH:%VDM'), ((0, 0, 0, 3, 0, 0, 0), '%VDY-%VDm-%VDd\\\\%VDH:%VDM'), ((0, 0, 0, 2, 0, 0, 0), '%VDH:%VDM'), ((0, 0, 0, 1, 0, 0, 0), '%VDH:%VDM'), ((0, 0, 0, 0, 30, 0, 0), '%VDH:%VDM'), ((0, 0, 0, 0, 15, 0, 0), '%VDH:%VDM'), ((0, 0, 0, 0, 10, 0, 0), '%VDH:%VDM'), ((0, 0, 0, 0, 5, 0, 0), '%VDH:%VDM'), ((0, 0, 0, 0, 2, 0, 0), '%VDH:%VDM'), ((0, 0, 0, 0, 1, 0, 0), '%VDH:%VDM'), ((0, 0, 0, 0, 0, 30, 0), '%VDH:%VDM:%VDS'), ((0, 0, 0, 0, 0, 15, 0), '%VDH:%VDM:%VDS'), ((0, 0, 0, 0, 0, 10, 0), '%VDH:%VDM:%VDS'), ((0, 0, 0, 0, 0, 5, 0), '%VDH:%VDM:%VDS'), ((0, 0, 0, 0, 0, 2, 0), '%VDH:%VDM:%VDS'), ((0, 0, 0, 0, 0, 1, 0), '%VDH:%VDM:%VDS'), ((0, 0, 0, 0, 0, 0, 500000), '%VDH:%VDM:%VDVS'), ((0, 0, 0, 0, 0, 0, 200000), '%VDVS'), ((0, 0, 0, 0, 0, 0, 100000), '%VDVS'), ((0, 0, 0, 0, 0, 0, 50000), '%VDVS'), ((0, 0, 0, 0, 0, 0, 10000), '%VDVS'), ) intervals_sec = N.array([(ms*1e-6+s+mi*60+hr*60*60+dy*24*60*60+ mn*(365/12.)*24*60*60+ yr*365*24*60*60) for (yr, mn, dy, hr, mi, s, ms), fmt in intervals]) def bestTickFinder(self, minval, maxval, numticks, extendmin, extendmax, intervals, intervals_sec): """Try to find best choice of numticks ticks between minval and maxval intervals is an array similar to self.intervals intervals_sec is an array similar to self.intervals_sec Returns a tuple (minval, maxval, estimatedsize, ticks, textformat)""" delta = maxval - minval # iterate over different intervals and find one closest to what we want estimated = delta / intervals_sec tick1 = max(estimated.searchsorted(numticks)-1, 0) tick2 = min(tick1+1, len(estimated)-1) del1 = abs(estimated[tick1] - numticks) del2 = abs(estimated[tick2] - numticks) if del1 < del2: best = tick1 else: best = tick2 besttt, format = intervals[best] mindate = utils.floatToDateTime(minval) maxdate = utils.floatToDateTime(maxval) # round min and max to nearest minround = utils.tupleToDateTime(utils.roundDownToTimeTuple(mindate, besttt)) maxround = utils.tupleToDateTime(utils.roundDownToTimeTuple(maxdate, besttt)) if minround == mindate: mintick = minround else: # rounded down, so move on to next tick mintick = utils.addTimeTupleToDateTime(minround, besttt) maxtick = maxround # extend bounds if requested deltamin = utils.datetimeToFloat(mindate)-utils.datetimeToFloat(mintick) if extendmin and (deltamin != 0. and deltamin < delta*0.15): mindate = utils.addTimeTupleToDateTime(minround, [-x for x in besttt]) mintick = mindate deltamax = utils.datetimeToFloat(maxdate)-utils.datetimeToFloat(maxtick) if extendmax and (deltamax != 0. and deltamax < delta*0.15): maxdate = utils.addTimeTupleToDateTime(maxtick, besttt) maxtick = maxdate # make ticks ticks = [] dt = mintick while dt <= maxtick: ticks.append( utils.datetimeToFloat(dt)) dt = utils.addTimeTupleToDateTime(dt, besttt) return ( utils.datetimeToFloat(mindate), utils.datetimeToFloat(maxdate), intervals_sec[best], N.array(ticks), format ) def filterIntervals(self, estint): """Filter intervals and intervals_sec to be multiples of estint seconds.""" intervals = [] intervals_sec = [] for i, inter in enumerate(self.intervals_sec): ratio = estint / inter if abs(ratio-int(ratio)) < ratio*.01: intervals.append(self.intervals[i]) intervals_sec.append(inter) return intervals, N.array(intervals_sec) def getTicks(self): """Calculate and return the position of the major ticks. """ # find minor ticks mindate, maxdate, est, ticks, format = self.bestTickFinder( self.minval, self.maxval, self.numticks, self.extendmin, self.extendmax, self.intervals, self.intervals_sec) # try to make minor ticks divide evenly into major ticks intervals, intervals_sec = self.filterIntervals(est) # get minor ticks ig, ig, ig, minorticks, ig = self.bestTickFinder( mindate, maxdate, self.numminorticks, False, False, intervals, intervals_sec) self.interval = (intervals, intervals_sec) self.minval = mindate self.maxval = maxdate self.minorticks = minorticks self.tickvals = ticks self.autoformat = format veusz-3.0.1/veusz/widgets/nonorthfunction.py0000664000175000017500000001613213164704521020750 0ustar jssjss00000000000000# Copyright (C) 2010 Jeremy S. Sanders # Email: Jeremy Sanders # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ############################################################################## '''Non orthogonal function plotting.''' from __future__ import division import numpy as N from ..compat import cstr from .. import qtall as qt4 from .. import document from .. import setting from .. import utils from . import pickable from .nonorthgraph import NonOrthGraph, FillBrush from .widget import Widget def _(text, disambiguation=None, context='NonOrthFunction'): """Translate text.""" return qt4.QCoreApplication.translate(context, text, disambiguation) class NonOrthFunction(Widget): '''Widget for plotting a function on a non-orthogonal plot.''' typename = 'nonorthfunc' allowusercreation = True description = _('Plot a function on graphs with non-orthogonal axes') @classmethod def addSettings(klass, s): '''Settings for widget.''' Widget.addSettings(s) s.add( setting.Str('function', 'a', descr=_('Function expression'), usertext=_('Function')) ) s.add( setting.Choice('variable', ['a', 'b'], 'a', descr=_('Variable the function is a function of'), usertext=_('Variable')) ) s.add(setting.FloatOrAuto('min', 'Auto', descr=_('Minimum value at which to plot function'), usertext=_('Min'))) s.add(setting.FloatOrAuto('max', 'Auto', descr=_('Maximum value at which to plot function'), usertext=_('Max'))) s.add( setting.Line('PlotLine', descr = _('Plot line settings'), usertext = _('Plot line')), pixmap = 'settings_plotline' ) s.get('PlotLine').get('color').newDefault('auto') s.add( FillBrush('Fill1', descr = _('Fill settings (1)'), usertext = _('Area fill 1')), pixmap = 'settings_plotfillbelow' ) s.add( FillBrush('Fill2', descr = _('Fill settings (2)'), usertext = _('Area fill 2')), pixmap = 'settings_plotfillbelow' ) s.add( setting.Int('steps', 50, descr = _('Number of steps to evaluate the function' ' over'), usertext=_('Steps'), formatting=True), 0 ) @classmethod def allowedParentTypes(klass): return (NonOrthGraph,) @property def userdescription(self): return _("function='%s'") % self.settings.function def initEnviron(self): '''Set up function environment.''' return self.document.evaluate.context.copy() def logEvalError(self, ex): '''Write error message to document log for exception ex.''' self.document.log( "Error evaluating expression in function widget '%s': '%s'" % ( self.name, cstr(ex))) def getFunctionPoints(self): '''Get points for plotting function. Return (apts, bpts) ''' # get range of variable in expression s = self.settings crange = self.parent.coordRanges()[ {'a': 0, 'b': 1}[s.variable] ] if s.min != 'Auto': crange[0] = s.min if s.max != 'Auto': crange[1] = s.max steps = max(2, s.steps) # input values for function invals = ( N.arange(steps)*(1./(steps-1))*(crange[1]-crange[0]) + crange[0] ) # do evaluation env = self.initEnviron() env[s.variable] = invals comp = self.document.evaluate.compileCheckedExpression(s.function) if comp is None: return N.array([]), N.array([]) try: vals = eval(comp, env) + invals*0. except Exception as e: self.logEvalError(e) vals = invals = N.array([]) # return points if s.variable == 'a': return invals, vals else: return vals, invals def updateDataRanges(self, inrange): '''Update ranges of data given function.''' def _pickable(self): apts, bpts = self.getFunctionPoints() px, py = self.parent.graphToPlotCoords(apts, bpts) if self.settings.variable == 'a': labels = ('a', 'b(a)') else: labels = ('a(b)', 'b') return pickable.GenericPickable( self, labels, (apts, bpts), (px, py) ) def pickPoint(self, x0, y0, bounds, distance='radial'): return self._pickable().pickPoint(x0, y0, bounds, distance) def pickIndex(self, oldindex, direction, bounds): return self._pickable().pickIndex(oldindex, direction, bounds) def autoColor(self, painter, dataindex=0): """Automatic color for plotting.""" return painter.docColorAuto( painter.helper.autoColorIndex((self, dataindex))) def draw(self, parentposn, phelper, outerbounds=None): '''Plot the function on a plotter.''' posn = self.computeBounds(parentposn, phelper) s = self.settings # exit if hidden if s.hide: return apts, bpts = self.getFunctionPoints() px, py = self.parent.graphToPlotCoords(apts, bpts) x1, y1, x2, y2 = posn cliprect = qt4.QRectF( qt4.QPointF(x1, y1), qt4.QPointF(x2, y2) ) painter = phelper.painter(self, posn) with painter: self.parent.setClip(painter, posn) # plot line painter.setBrush(qt4.QBrush()) painter.setPen( s.PlotLine.makeQPenWHide(painter) ) for x, y in utils.validLinePoints(px, py): if not s.Fill1.hide: self.parent.drawFillPts(painter, s.Fill1, cliprect, x, y) if not s.Fill2.hide: self.parent.drawFillPts(painter, s.Fill2, cliprect, x, y) if not s.PlotLine.hide: p = qt4.QPolygonF() utils.addNumpyToPolygonF(p, x, y) painter.setBrush(qt4.QBrush()) painter.setPen( s.PlotLine.makeQPen(painter) ) utils.plotClippedPolyline(painter, cliprect, p) document.thefactory.register( NonOrthFunction ) veusz-3.0.1/veusz/widgets/root.py0000664000175000017500000001426013164703136016477 0ustar jssjss00000000000000# root.py # Represents the root widget for plotting the document # Copyright (C) 2004 Jeremy S. Sanders # Email: Jeremy Sanders # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ############################################################################## from __future__ import division import textwrap from .. import qtall as qt4 from .. import document from .. import setting from . import widget from . import controlgraph def _(text, disambiguation=None, context='Root'): """Translate text.""" return qt4.QCoreApplication.translate(context, text, disambiguation) class Root(widget.Widget): """Root widget class for plotting the document.""" typename='document' allowusercreation = False def __init__(self, parent, name=None, document=None): """Initialise object.""" widget.Widget.__init__(self, parent, name=name) s = self.settings self.document = document # don't want user to be able to hide entire document stylesheet = setting.StyleSheet( descr=_('Master settings for document'), usertext=_('Style sheet')) s.add(stylesheet) self.fillStylesheet(stylesheet) s.get('englishlocale').setOnModified(self.changeLocale) s.get('colorTheme').setOnModified(self.changeColorTheme) @classmethod def addSettings(klass, s): widget.Widget.addSettings(s) s.remove('hide') s.add( setting.DistancePhysical( 'width', '15cm', descr=_('Width of the pages'), usertext=_('Page width'), formatting=True) ) s.add( setting.DistancePhysical( 'height', '15cm', descr=_('Height of the pages'), usertext=_('Page height'), formatting=True) ) s.add( setting.Bool( 'englishlocale', False, descr=_('Use US/English number formatting for ' 'document'), usertext=_('English locale'), formatting=True) ) themes = sorted(list(document.colors.colorthemes)) s.add( setting.Choice( 'colorTheme', themes, 'black', descr=_('Color theme'), usertext=_('Color theme'), formatting=True) ) s.add( setting.Notes( 'notes', '', descr=_('User-defined notes'), usertext=_('Notes') ) ) @classmethod def allowedParentTypes(klass): return (None,) @property def userdescription(self): """Return user-friendly description.""" return textwrap.fill(self.settings.notes, 60) def changeLocale(self): """Update locale of document if changed by user.""" if self.settings.englishlocale: self.document.locale = qt4.QLocale.c() else: self.document.locale = qt4.QLocale() self.document.locale.setNumberOptions(qt4.QLocale.OmitGroupSeparator) def changeColorTheme(self): """Change color theme used by document.""" self.document.evaluate.colors.setColorTheme( self.settings.colorTheme) def getPage(self, pagenum): """Get page widget.""" try: return self.children[pagenum] except IndexError: return None def draw(self, painthelper, pagenum): """Draw the page requested on the painter.""" xw, yw = painthelper.pagesize posn = [0, 0, xw, yw] painter = painthelper.painter(self, posn) with painter: page = self.children[pagenum] page.draw( posn, painthelper ) # w and h are non integer w = self.settings.get('width').convert(painter) h = self.settings.get('height').convert(painter) painthelper.setControlGraph(self, [ controlgraph.ControlMarginBox(self, [0, 0, w, h], [-10000, -10000, 10000, 10000], painthelper, ismovable = False) ] ) def updateControlItem(self, cgi): """Call helper to set page size.""" cgi.setPageSize() def fillStylesheet(self, stylesheet): """Register widgets with stylesheet.""" for widgetname in document.thefactory.listWidgets(): klass = document.thefactory.getWidgetClass(widgetname) if klass.allowusercreation or klass == Root: newsett = setting.Settings(name=klass.typename, usertext = klass.typename, pixmap="button_%s" % klass.typename) classset = setting.Settings('temp') klass.addSettings(classset) # copy formatting settings to stylesheet for name in classset.setnames: # might become recursive if name == 'StyleSheet': continue sett = classset.setdict[name] # skip non formatting settings #if hasattr(sett, 'formatting') and not sett.formatting: # continue newsett.add( sett.copy() ) stylesheet.add(newsett) # allow the factory to instantiate this document.thefactory.register( Root ) veusz-3.0.1/veusz/widgets/pickable.py0000664000175000017500000002242513322346625017272 0ustar jssjss00000000000000# pickable.py # stuff related to the Picker (aka Read Data) tool # Copyright (C) 2011 Benjamin K. Stuhl # Email: Benjamin K. Stuhl # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ############################################################################### from __future__ import division import numpy as N from ..compat import CBool from .. import document class PickInfo(CBool): """Encapsulates the results of a Pick operation. graphpos and coords are numeric (x,y) tuples, labels are the textual labels for the x and y datasets, and index is some object that the picker can use to figure out what the 'next' and 'previous' points are. index must implement __str__(); return '' if it has no user-visible meaning.""" def __init__(self, widget=None, graphpos=None, labels=None, coords=None, index=None): self.widget = widget self.graphpos = graphpos self.labels = labels self.coords = coords self.index = index self.distance = float('inf') self.displaytype = ('numeric', 'numeric') def cbool(self): return bool( self.widget and self.graphpos and self.labels and self.coords) class Index: """A class containing all the state a GenericPickable needs to find the next or previous point""" def __init__(self, ivar, index, sign): self.ivar = ivar self.index = index self.sign = sign # default to not trusting the actual index to be meaningful self.useindex = False def __str__(self): if not self.useindex: return '' else: # 1-based index return str(self.index+1) def _chooseOrderingSign(m, c, p): """Figures out whether p or m is visually right of c""" assert c is not None if p is not None and m is not None: if p[0] > m[0] or (p[0] == m[0] and p[1] < m[1]): # p is visually to the right of or above m return 1 else: return -1 elif p is not None: if p[0] > c[0]: # p is visually right of c return 1 else: return -1 elif m is not None: if m[0] < c[0]: # m is visually left of c return 1 else: return -1 else: assert m is not None or p is not None class GenericPickable: """Utility class which abstracts the math of picking the closest point out of a list of points""" def __init__(self, widget, labels, vals, graphvals): self.widget = widget self.labels = labels self.xvals, self.yvals = vals self.xgraph, self.ygraph = graphvals def _pickSign(self, i): if len(self.xgraph) <= 1: # we only have one element, so it doesn't matter anyways return 1 # go backwards to get previous finite point mi = i-1 while mi >= 0 and not N.isfinite(self.xgraph[mi]+self.ygraph[mi]): mi -= 1 if mi < 0: m = None else: m = self.xgraph[mi], self.ygraph[mi] # point in centre c = self.xgraph[i], self.ygraph[i] # find next finite point pi = i+1 while pi < len(self.xgraph) and not N.isfinite(self.xgraph[pi]+self.ygraph[pi]): pi += 1 if pi == len(self.xgraph): p = None else: p = self.xgraph[pi], self.ygraph[pi] if p is None and m is None: # only 1 point after removing non-finite points return 1 return _chooseOrderingSign(m, c, p) def pickPoint(self, x0, y0, bounds, distance_direction): info = PickInfo(self.widget, labels=self.labels) if self.widget.settings.hide: return info if self.xvals is None or self.yvals is None: return info if len(self.xgraph) == 0 or len(self.ygraph) == 0: return info # calculate distances if distance_direction == 'vertical': # measure distance along y dist = N.abs(self.ygraph - y0) elif distance_direction == 'horizontal': # measure distance along x dist = N.abs(self.xgraph - x0) elif distance_direction == 'radial': # measure radial distance dist = N.sqrt((self.xgraph - x0)**2 + (self.ygraph - y0)**2) else: # programming error assert (distance_direction == 'radial' or distance_direction == 'vertical' or distance_direction == 'horizontal') # ignore points which are offgraph or not finite with N.errstate(invalid='ignore'): outofbounds = ( (self.xgraph < bounds[0]) | (self.xgraph > bounds[2]) | (self.ygraph < bounds[1]) | (self.ygraph > bounds[3]) | ~N.isfinite(self.xgraph) | ~N.isfinite(self.ygraph) ) dist[outofbounds] = N.inf m = N.min(dist) # if there are multiple equidistant points, arbitrarily take # the first one try: i = N.nonzero(dist == m)[0][0] except IndexError: return info info.graphpos = self.xgraph[i], self.ygraph[i] info.coords = self.xvals[i], self.yvals[i] info.distance = m info.index = Index(self.xvals[i], i, self._pickSign(i)) return info def pickIndex(self, oldindex, direction, bounds): info = PickInfo(self.widget, labels=self.labels) if self.widget.settings.hide: return info if self.xvals is None or self.yvals is None: return info if oldindex.index is None: # no explicit index, so find the closest location to the previous # independent variable value i = N.logical_not( N.logical_or( self.xvals < oldindex.ivar, self.xvals > oldindex.ivar) ) # and pick the next if oldindex.sign == 1: i = max(N.nonzero(i)[0]) else: i = min(N.nonzero(i)[0]) else: i = oldindex.index if direction == 'right': incr = oldindex.sign elif direction == 'left': incr = -oldindex.sign else: assert direction == 'right' or direction == 'left' i += incr # skip points that are outside of the bounds or are not finite while (i >= 0 and i < len(self.xgraph) and ( not N.isfinite(self.xgraph[i]) or not N.isfinite(self.ygraph[i]) or (self.xgraph[i] < bounds[0] or self.xgraph[i] > bounds[2] or self.ygraph[i] < bounds[1] or self.ygraph[i] > bounds[3]) )): i += incr if i < 0 or i >= len(self.xgraph): return info info.graphpos = self.xgraph[i], self.ygraph[i] info.coords = self.xvals[i], self.yvals[i] info.index = Index(self.xvals[i], i, oldindex.sign) return info class DiscretePickable(GenericPickable): """A specialization of GenericPickable that knows how to deal with widgets with axes and data sets""" def __init__(self, widget, xdata_propname, ydata_propname, mapdata_fn): s = widget.settings doc = widget.document self.xdata = xdata = s.get(xdata_propname).getData(doc) self.ydata = ydata = s.get(ydata_propname).getData(doc) labels = s.__getattr__(xdata_propname), s.__getattr__(ydata_propname) if not xdata or not ydata or not mapdata_fn: GenericPickable.__init__( self, widget, labels, (None, None), (None, None) ) return # take data, ensure same size and map it x, y = xdata.data, ydata.data minlen = min(len(x), len(y)) x, y = x[:minlen], y[:minlen] xs, ys = mapdata_fn(x, y) # and set us up with the mapped data GenericPickable.__init__( self, widget, labels, (x, y), (xs, ys) ) def pickPoint(self, x0, y0, bounds, distance_direction): info = GenericPickable.pickPoint(self, x0, y0, bounds, distance_direction) if not info: return None info.displaytype = (self.xdata.displaytype, self.ydata.displaytype) # indicies are persistent info.index.useindex = True return info def pickIndex(self, oldindex, direction, bounds): info = GenericPickable.pickIndex(self, oldindex, direction, bounds) info.displaytype = (self.xdata.displaytype, self.ydata.displaytype) if not info: return info # indicies are persistent info.index.useindex = True return info veusz-3.0.1/veusz/widgets/fit.py0000664000175000017500000003461313324564015016301 0ustar jssjss00000000000000# fit.py # fitting plotter # Copyright (C) 2005 Jeremy S. Sanders # Email: Jeremy Sanders # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ############################################################################### from __future__ import division, absolute_import, print_function import re import sys import numpy as N from ..compat import czip, cstr from .. import document from .. import setting from .. import utils from .. import qtall as qt4 from .function import FunctionPlotter from . import widget # try importing iminuit first, then minuit, then None try: import iminuit as minuit except ImportError: try: import minuit except ImportError: minuit = None def _(text, disambiguation=None, context='Fit'): """Translate text.""" return qt4.QCoreApplication.translate(context, text, disambiguation) def minuitFit(evalfunc, params, names, values, xvals, yvals, yserr): """Do fitting with minuit (if installed).""" def chi2(params): """generate a lambda function to impedance-match between PyMinuit's use of multiple parameters versus our use of a single numpy vector.""" c = ((evalfunc(params, xvals) - yvals)**2 / yserr**2).sum() if chi2.runningFit: chi2.iters += 1 p = [chi2.iters, c] + params.tolist() str = ("%5i " + "%8g " * (len(params)+1)) % tuple(p) print(str) return c namestr = ', '.join(names) fnstr = 'lambda %s: chi2(N.array([%s]))' % (namestr, namestr) # this is safe because the only user-controlled variable is len(names) fn = eval(fnstr, {'chi2' : chi2, 'N' : N}) print(_('Fitting via Minuit:')) m = minuit.Minuit(fn, **values) # run the fit chi2.runningFit = True chi2.iters = 0 m.migrad() # do some error analysis have_symerr, have_err = False, False try: chi2.runningFit = False m.hesse() have_symerr = True m.minos() have_err = True except Exception as e: print(e) if str(e).startswith('Discovered a new minimum'): # the initial fit really failed raise # print the results retchi2 = m.fval dof = len(yvals) - len(params) redchi2 = retchi2 / dof if have_err: print(_('Fit results:\n') + "\n".join([ u" %s = %g \u00b1 %g (+%g / %g)" % (n, m.values[n], m.errors[n], m.merrors[(n, 1.0)], m.merrors[(n, -1.0)]) for n in names])) elif have_symerr: print(_('Fit results:\n') + "\n".join([ u" %s = %g \u00b1 %g" % (n, m.values[n], m.errors[n]) for n in names])) print(_('MINOS error estimate not available.')) else: print(_('Fit results:\n') + "\n".join([ ' %s = %g' % (n, m.values[n]) for n in names])) print(_('No error analysis available: fit quality uncertain')) print("chi^2 = %g, dof = %i, reduced-chi^2 = %g" % (retchi2, dof, redchi2)) vals = dict(m.values) return vals, retchi2, dof class Fit(FunctionPlotter): """A plotter to fit a function to data.""" typename='fit' allowusercreation=True description=_('Fit a function to data') def __init__(self, parent, name=None): FunctionPlotter.__init__(self, parent, name=name) self.addAction( widget.Action('fit', self.actionFit, descr = _('Fit function'), usertext = _('Fit function')) ) @classmethod def addSettings(klass, s): """Construct list of settings.""" FunctionPlotter.addSettings(s) s.add( setting.FloatDict( 'values', {'a': 0.0, 'b': 1.0}, descr = _('Variables and fit values'), usertext=_('Parameters')), 1 ) s.add( setting.DatasetExtended( 'xData', 'x', descr = _('X data to fit (dataset name, list of values ' 'or expression)'), usertext=_('X data')), 2 ) s.add( setting.DatasetExtended( 'yData', 'y', descr = _('Y data to fit (dataset name, list of values ' 'or expression)'), usertext=_('Y data')), 3 ) s.add( setting.Bool( 'fitRange', False, descr = _('Fit only the data between the ' 'minimum and maximum of the axis for ' 'the function variable'), usertext=_('Fit only range')), 4 ) s.add( setting.WidgetChoice( 'outLabel', '', descr=_('Write best fit parameters to this text label ' 'after fitting'), widgettypes=('label',), usertext=_('Output label')), 5 ) s.add( setting.Str('outExpr', '', descr = _('Output best fitting expression'), usertext=_('Output expression')), 6, readonly=True ) s.add( setting.Float('chi2', -1, descr = 'Output chi^2 from fitting', usertext=_('Fit χ2')), 7, readonly=True ) s.add( setting.Int('dof', -1, descr = _('Output degrees of freedom from fitting'), usertext=_('Fit d.o.f.')), 8, readonly=True ) s.add( setting.Float('redchi2', -1, descr = _('Output reduced-chi-squared from fitting'), usertext=_('Fit reduced χ2')), 9, readonly=True ) f = s.get('function') f.newDefault('a + b*x') f.descr = _('Function to fit') # modify description s.get('min').usertext=_('Min. fit range') s.get('max').usertext=_('Max. fit range') def affectsAxisRange(self): """This widget provides range information about these axes.""" s = self.settings return ( (s.xAxis, 'sx'), (s.yAxis, 'sy') ) def getRange(self, axis, depname, axrange): """Update range with range of data.""" dataname = {'sx': 'xData', 'sy': 'yData'}[depname] data = self.settings.get(dataname).getData(self.document) if data: drange = data.getRange() if drange: axrange[0] = min(axrange[0], drange[0]) axrange[1] = max(axrange[1], drange[1]) def initEnviron(self): """Copy data into environment.""" env = self.document.evaluate.context.copy() env.update( self.settings.values ) return env def updateOutputLabel(self, ops, vals, chi2, dof): """Use best fit parameters to update text label.""" s = self.settings labelwidget = s.get('outLabel').findWidget() if labelwidget is not None: # build up a set of X=Y values loc = self.document.locale txt = [] for l, v in sorted(vals.items()): val = utils.formatNumber(v, '%.4Vg', locale=loc) txt.append( '%s = %s' % (l, val) ) # add chi2 output txt.append( r'\chi^{2}_{\nu} = %s/%i = %s' % ( utils.formatNumber(chi2, '%.4Vg', locale=loc), dof, utils.formatNumber(chi2/dof, '%.4Vg', locale=loc) )) # update label with text text = r'\\'.join(txt) ops.append( document.OperationSettingSet( labelwidget.settings.get('label') , text ) ) def actionFit(self): """Fit the data.""" s = self.settings # check and get compiled for of function compiled = self.document.evaluate.compileCheckedExpression(s.function) if compiled is None: return # populate the input parameters paramnames = sorted(s.values) params = N.array( [s.values[p] for p in paramnames] ) # FIXME: loads of error handling!! d = self.document # choose dataset depending on fit variable if s.variable == 'x': xvals = s.get('xData').getData(d).data ydata = s.get('yData').getData(d) else: xvals = s.get('yData').getData(d).data ydata = s.get('xData').getData(d) yvals = ydata.data yserr = ydata.serr # if there are no errors on data if yserr is None: if ydata.perr is not None and ydata.nerr is not None: print("Warning: Symmeterising positive and negative errors") yserr = N.sqrt( 0.5*(ydata.perr**2 + ydata.nerr**2) ) else: print("Warning: No errors on y values. Assuming 5% errors.") yserr = yvals*0.05 yserr[yserr < 1e-8] = 1e-8 # if the fitRange parameter is on, we chop out data outside the # range of the axis if s.fitRange: # get ranges for axes if s.variable == 'x': drange = self.parent.getAxes((s.xAxis,))[0].getPlottedRange() mask = N.logical_and(xvals >= drange[0], xvals <= drange[1]) else: drange = self.parent.getAxes((s.yAxis,))[0].getPlottedRange() mask = N.logical_and(yvals >= drange[0], yvals <= drange[1]) xvals, yvals, yserr = xvals[mask], yvals[mask], yserr[mask] print("Fitting %s from %g to %g" % (s.variable, drange[0], drange[1])) evalenv = self.initEnviron() def evalfunc(params, xvals): # update environment with variable and parameters evalenv[self.settings.variable] = xvals evalenv.update( czip(paramnames, params) ) try: return eval(compiled, evalenv) + xvals*0. except Exception as e: self.document.log(cstr(e)) return N.nan # minimum set for fitting if s.min != 'Auto': if s.variable == 'x': mask = xvals >= s.min else: mask = yvals >= s.min xvals, yvals, yserr = xvals[mask], yvals[mask], yserr[mask] # maximum set for fitting if s.max != 'Auto': if s.variable == 'x': mask = xvals <= s.max else: mask = yvals <= s.max xvals, yvals, yserr = xvals[mask], yvals[mask], yserr[mask] if s.min != 'Auto' or s.max != 'Auto': print("Fitting %s between %s and %s" % (s.variable, s.min, s.max)) # various error checks if len(xvals) != len(yvals) or len(xvals) != len(yserr): sys.stderr.write(_('Fit data not equal in length. Not fitting.\n')) return if len(params) > len(xvals): sys.stderr.write(_('No degrees of freedom for fit. Not fitting\n')) return # actually do the fit, either via Minuit or our own LM fitter chi2 = 1 dof = 1 # only consider finite values finite = N.isfinite(xvals) & N.isfinite(yvals) & N.isfinite(yserr) xvals = xvals[finite] yvals = yvals[finite] yserr = yserr[finite] # check length after excluding non-finite values if len(xvals) == 0: sys.stderr.write(_('No data values. Not fitting.\n')) return if minuit is not None: vals, chi2, dof = minuitFit( evalfunc, params, paramnames, s.values, xvals, yvals, yserr) else: print(_('Minuit not available, falling back to simple L-M fitting:')) retn, chi2, dof = utils.fitLM( evalfunc, params, xvals, yvals, yserr) vals = {} for i, v in czip(paramnames, retn): vals[i] = float(v) # list of operations do we can undo the changes operations = [] # populate the return parameters operations.append( document.OperationSettingSet(s.get('values'), vals) ) # populate the read-only fit quality params operations.append( document.OperationSettingSet(s.get('chi2'), float(chi2)) ) operations.append( document.OperationSettingSet(s.get('dof'), int(dof)) ) if dof <= 0: print(_('No degrees of freedom in fit.\n')) redchi2 = -1. else: redchi2 = float(chi2/dof) operations.append( document.OperationSettingSet(s.get('redchi2'), redchi2) ) # expression for fit expr = self.generateOutputExpr(vals) operations.append( document.OperationSettingSet(s.get('outExpr'), expr) ) self.updateOutputLabel(operations, vals, chi2, dof) # actually change all the settings d.applyOperation( document.OperationMultiple(operations, descr=_('fit')) ) def generateOutputExpr(self, vals): """Try to generate text form of output expression. vals is a dict of variable: value pairs returns the expression """ paramvals = dict(vals) s = self.settings # also substitute in data name for variable if s.variable == 'x': paramvals['x'] = s.xData else: paramvals['y'] = s.yData # split expression up into parts of text and nums, separated # by non-text/nums parts = re.split('([^A-Za-z0-9.])', s.function) # replace part by things in paramvals, if they exist for i, p in enumerate(parts): if p in paramvals: parts[i] = str(paramvals[p]) return ''.join(parts) # allow the factory to instantiate an x,y plotter document.thefactory.register( Fit ) veusz-3.0.1/veusz/widgets/bar.py0000664000175000017500000005247113242711144016261 0ustar jssjss00000000000000# Copyright (C) 2009 Jeremy S. Sanders # Email: Jeremy Sanders # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ############################################################################### """For plotting bar graphs.""" from __future__ import division import numpy as N from ..compat import crange, czip from .. import qtall as qt4 from .. import document from .. import setting from .. import utils from .plotters import GenericPlotter def _(text, disambiguation=None, context='BarPlotter'): """Translate text.""" return qt4.QCoreApplication.translate(context, text, disambiguation) class BarFill(setting.Settings): '''Filling of bars.''' def __init__(self, name, **args): setting.Settings.__init__(self, name, **args) self.add( setting.FillSet('fills', [('solid', 'auto', False)], descr = _('Fill styles for dataset bars'), usertext=_('Fill styles')) ) class BarLine(setting.Settings): '''Edges of bars.''' def __init__(self, name, **args): setting.Settings.__init__(self, name, **args) self.add( setting.LineSet('lines', [('solid', '0.5pt', 'black', False)], descr = _('Line styles for dataset bars'), usertext=_('Line styles')) ) def extend1DArray(array, length, missing=0.): """Return array with length given (original if appropriate. Values are extended with value given.""" if len(array) == length: return array retn = N.resize(array, length) retn[len(array):] = missing return retn class BarPlotter(GenericPlotter): """Plot bar charts.""" typename='bar' allowusercreation=True description=_('Plot bar charts') @classmethod def addSettings(klass, s): """Construct list of settings.""" GenericPlotter.addSettings(s) # get rid of default key setting s.remove('key') s.add( setting.Strings('keys', ('',), descr=_('Key text for each dataset'), usertext=_('Key text')), 0) s.add( setting.DatasetOrStr('labels', '', descr=_('Dataset or string to label bars'), usertext=_('Labels')), 5 ) s.add( setting.Choice('mode', ('grouped', 'stacked', 'stacked-area'), 'grouped', descr=_('Show datasets grouped ' 'together or as a single bar'), usertext=_('Mode')), 0) s.add( setting.Choice('direction', ('horizontal', 'vertical'), 'vertical', descr = _('Horizontal or vertical bar chart'), usertext=_('Direction')), 0 ) s.add( setting.DatasetExtended('posn', '', descr = _('Position of bars, dataset ' ' or expression (optional)'), usertext=_('Positions')), 0 ) s.add( setting.Datasets('lengths', ('y',), descr = _('Datasets containing lengths of bars'), usertext=_('Lengths')), 0 ) s.add( setting.Float('barfill', 0.75, minval = 0., maxval = 1., descr = _('Filling fraction of bars' ' (between 0 and 1)'), usertext=_('Bar fill'), formatting=True) ) s.add( setting.Float('groupfill', 0.9, minval = 0., maxval = 1., descr = _('Filling fraction of groups of bars' ' (between 0 and 1)'), usertext=_('Group fill'), formatting=True) ) s.add( setting.Choice('errorstyle', ('none', 'bar', 'barends'), 'bar', descr=_('Error bar style to show'), usertext=_('Error style'), formatting=True) ) s.add(BarFill('BarFill', descr=_('Bar fill'), usertext=_('Fill')), pixmap = 'settings_bgfill') s.add(BarLine('BarLine', descr=_('Bar line'), usertext=_('Line')), pixmap = 'settings_border') s.add( setting.ErrorBarLine('ErrorBarLine', descr = _('Error bar line settings'), usertext = _('Error bar line')), pixmap = 'settings_ploterrorline' ) @property def userdescription(self): """User-friendly description.""" s = self.settings return _("lengths='%s', position='%s'") % (', '.join(s.lengths), s.posn) def affectsAxisRange(self): """This widget provides range information about these axes.""" s = self.settings return ( (s.xAxis, 'sx'), (s.yAxis, 'sy') ) def getAxisLabels(self, direction): """Get labels for bar for appropriate axis.""" s = self.settings if s.direction != direction: # if horizontal bars, want labels on vertical axis and vice versa doc = self.document labels = s.get('labels').getData(doc, checknull=True) positions = s.get('posn').getData(doc) if positions is None: lengths = s.get('lengths').getData(doc) if not lengths: return (None, None) p = N.arange( max([len(d.data) for d in lengths]) )+1. else: p = positions.data return (labels, p) else: return (None, None) def singleBarDataRange(self, datasets): """For single bars where multiple datasets are added, compute maximum range.""" minv, maxv = 0., 0. for data in czip(*[ds.data for ds in datasets]): totpos = sum( [d for d in data if d > 0] ) totneg = sum( [d for d in data if d < 0] ) minv = min(minv, totneg) maxv = max(maxv, totpos) return minv, maxv def getRange(self, axis, depname, axrange): """Update axis range from data.""" s = self.settings if ((s.direction == 'horizontal' and depname == 'sx') or (s.direction == 'vertical' and depname == 'sy')): # update from lengths data = s.get('lengths').getData(self.document) if s.mode == 'grouped': # update range from individual datasets for d in data: drange = d.getRange() if drange is not None: axrange[0] = min(axrange[0], drange[0]) axrange[1] = max(axrange[1], drange[1]) else: # update range from sum of datasets minv, maxv = self.singleBarDataRange(data) axrange[0] = min(axrange[0], minv) axrange[1] = max(axrange[1], maxv) else: if s.posn: # use given positions data = s.get('posn').getData(self.document) if data: drange = data.getRange() if drange is not None: axrange[0] = min(axrange[0], drange[0]) axrange[1] = max(axrange[1], drange[1]) else: # count bars data = s.get('lengths').getData(self.document) if data: maxlen = max([len(d) for d in data]) axrange[0] = min(1-0.5, axrange[0]) axrange[1] = max(maxlen+0.5, axrange[1]) def findBarPositions(self, lengths, positions, axes, posn): """Work out centres of bar / bar groups and maximum width.""" ishorz = self.settings.direction == 'horizontal' if positions is None: p = N.arange( max([len(d.data) for d in lengths]) )+1. else: p = positions.data # work out positions of bars # get vertical axis if horz, and vice-versa axis = axes[ishorz] posns = axis.dataToPlotterCoords(posn, p) if len(posns) <= 1: if ishorz: maxwidth = posn[2]-posn[0] else: maxwidth = posn[3]-posn[1] else: maxwidth = N.nanmin(N.abs(posns[1:]-posns[:-1])) return posns, maxwidth def calculateErrorBars(self, dataset, vals): """Get values for error bars.""" minval = None maxval = None if 'serr' in dataset: s = N.nan_to_num(dataset['serr']) minval = vals - s maxval = vals + s else: if 'nerr' in dataset: minval = vals + N.nan_to_num(dataset['nerr']) if 'perr' in dataset: maxval = vals + N.nan_to_num(dataset['perr']) return minval, maxval def drawErrorBars(self, painter, posns, barwidth, yvals, dataset, axes, widgetposn): """Draw (optional) error bars on bars.""" s = self.settings if s.errorstyle == 'none': return minval, maxval = self.calculateErrorBars(dataset, yvals) if minval is None and maxval is None: return # handle one sided errors if minval is None: minval = yvals if maxval is None: maxval = yvals # convert errors to coordinates ishorz = s.direction == 'horizontal' mincoord = axes[not ishorz].dataToPlotterCoords(widgetposn, minval) mincoord = N.clip(mincoord, -32767, 32767) maxcoord = axes[not ishorz].dataToPlotterCoords(widgetposn, maxval) maxcoord = N.clip(maxcoord, -32767, 32767) # draw error bars ebl = self.settings.ErrorBarLine painter.setPen( ebl.makeQPenWHide(painter) ) w = barwidth*0.25*ebl.endsize if ishorz and not ebl.hideHorz: utils.plotLinesToPainter(painter, mincoord, posns, maxcoord, posns) if s.errorstyle == 'barends': utils.plotLinesToPainter(painter, mincoord, posns-w, mincoord, posns+w) utils.plotLinesToPainter(painter, maxcoord, posns-w, maxcoord, posns+w) elif not ishorz and not ebl.hideVert: utils.plotLinesToPainter(painter, posns, mincoord, posns, maxcoord) if s.errorstyle == 'barends': utils.plotLinesToPainter(painter, posns-w, mincoord, posns+w, mincoord) utils.plotLinesToPainter(painter, posns-w, maxcoord, posns+w, maxcoord) def plotBars(self, painter, s, dsnum, clip, corners): """Plot a set of boxes.""" # get style brush = s.BarFill.get('fills').returnBrushExtended(dsnum) pen = s.BarLine.get('lines').makePen(painter, dsnum) lw = pen.widthF() * 2 # make clip box bigger to avoid lines showing extclip = qt4.QRectF(qt4.QPointF(clip.left()-lw, clip.top()-lw), qt4.QPointF(clip.right()+lw, clip.bottom()+lw)) # plot bars path = qt4.QPainterPath() utils.addNumpyPolygonToPath( path, extclip, corners[0], corners[1], corners[2], corners[1], corners[2], corners[3], corners[0], corners[3]) utils.brushExtFillPath( painter, brush, path, stroke=pen, dataindex=dsnum) def barDrawGroup(self, painter, posns, maxwidth, dsvals, axes, widgetposn, clip): """Draw groups of bars.""" s = self.settings # calculate bar and group widths numgroups = len(dsvals) groupwidth = maxwidth usablewidth = groupwidth * s.groupfill bardelta = usablewidth / numgroups barwidth = bardelta * s.barfill ishorz = s.direction == 'horizontal' # bar extends from these coordinates zeropt = axes[not ishorz].dataToPlotterCoords(widgetposn, N.array([0.])) for dsnum, dataset in enumerate(dsvals): # convert bar length to plotter coords lengthcoord = axes[not ishorz].dataToPlotterCoords( widgetposn, dataset['data']) # these are the coordinates perpendicular to the bar posns1 = posns + (-usablewidth*0.5 + bardelta*dsnum + (bardelta-barwidth)*0.5) posns2 = posns1 + barwidth if ishorz: p = (zeropt + N.zeros(posns1.shape), posns1, lengthcoord, posns2) else: p = (posns1, zeropt + N.zeros(posns2.shape), posns2, lengthcoord) self.plotBars(painter, s, dsnum, clip, p) # draw error bars self.drawErrorBars(painter, posns2-barwidth*0.5, barwidth, dataset['data'], dataset, axes, widgetposn) def calcStackedPoints(self, dsvals, axis, widgetposn): """Calculate stacked dataset coordinates for plotting.""" # keep track of last most negative or most positive values in bars poslen = len(dsvals[0]['data']) lastneg = N.zeros(poslen) lastpos = N.zeros(poslen) # returned stacked values and coordinates stackedvals = [] stackedcoords = [] for dsnum, data in enumerate(dsvals): # add on value to last value in correct direction data = data['data'] new = N.where(data < 0., lastneg+data, lastpos+data) # work out maximum extents for next time lastneg = N.min( N.vstack((lastneg, new)), axis=0 ) lastpos = N.max( N.vstack((lastpos, new)), axis=0 ) # convert values to plotter coordinates newplt = axis.dataToPlotterCoords(widgetposn, new) stackedvals.append(new) stackedcoords.append(newplt) return stackedvals, stackedcoords def barDrawStacked(self, painter, posns, maxwidth, dsvals, axes, widgetposn, clip): """Draw each dataset in a single bar.""" s = self.settings # get positions of groups of bars barwidth = maxwidth * s.barfill # get axis which values are plotted along ishorz = s.direction == 'horizontal' vaxis = axes[not ishorz] # compute stacked coordinates stackedvals, stackedcoords = self.calcStackedPoints( dsvals, vaxis, widgetposn) # coordinates of origin zerocoords = vaxis.dataToPlotterCoords(widgetposn, N.zeros(posns.shape)) # positions of bar perpendicular to bar direction posns1 = posns - barwidth*0.5 posns2 = posns1 + barwidth # draw bars (reverse order, so edges are plotted correctly) for dsnum, coords in czip( crange(len(stackedcoords)-1, -1, -1), stackedcoords[::-1]): # we iterate over each of these coordinates if ishorz: p = (zerocoords, posns1, coords, posns2) else: p = (posns1, zerocoords, posns2, coords) self.plotBars(painter, s, dsnum, clip, p) # draw error bars for barval, dsval in czip(stackedvals, dsvals): self.drawErrorBars(painter, posns, barwidth, barval, dsval, axes, widgetposn) def areaDrawStacked(self, painter, posns, maxwidth, dsvals, axes, widgetposn, clip): """Draw a stacked area plot""" s = self.settings # get axis which values are plotted along ishorz = s.direction == 'horizontal' vaxis = axes[not ishorz] # compute stacked coordinates stackedvals, stackedcoords = self.calcStackedPoints( dsvals, vaxis, widgetposn) # coordinates of origin zerocoords = vaxis.dataToPlotterCoords(widgetposn, N.zeros(posns.shape)) # bail out if problem if len(zerocoords) == 0 or len(posns) == 0: return # draw areas (reverse order, so edges are plotted correctly) for dsnum, coords in czip( crange(len(stackedcoords)-1, -1, -1), stackedcoords[::-1]): # add points at end to make polygon p1 = N.hstack( [ [zerocoords[0]], coords, [zerocoords[-1]] ] ) p2 = N.hstack( [ [posns[0]], posns, [posns[-1]] ] ) # construct polygon on path, clipped poly = qt4.QPolygonF() if ishorz: utils.addNumpyToPolygonF(poly, p1, p2) else: utils.addNumpyToPolygonF(poly, p2, p1) clippoly = qt4.QPolygonF() utils.polygonClip(poly, clip, clippoly) path = qt4.QPainterPath() path.addPolygon(clippoly) path.closeSubpath() # actually draw polygon brush = s.BarFill.get('fills').returnBrushExtended(dsnum) utils.brushExtFillPath(painter, brush, path, dataindex=dsnum) # now draw lines poly = qt4.QPolygonF() if ishorz: utils.addNumpyToPolygonF(poly, coords, posns) else: utils.addNumpyToPolygonF(poly, posns, coords) pen = s.BarLine.get('lines').makePen(painter, dsnum) painter.setPen(pen) utils.plotClippedPolyline(painter, clip, poly) # draw error bars barwidth = maxwidth * s.barfill for barval, dsval in czip(stackedvals, dsvals): self.drawErrorBars(painter, posns, barwidth, barval, dsval, axes, widgetposn) def getNumberKeys(self): """Return maximum number of keys.""" lengths = self.settings.get('lengths').getData(self.document) if not lengths: return 0 return min( len([k for k in self.settings.keys if k]), len(lengths) ) def setupAutoColor(self, painter): """Initialise correct number of colors.""" lengths = self.settings.get('lengths').getData(self.document) for i in crange(len(lengths)): self.autoColor(painter, dataindex=i) def getKeyText(self, number): """Get key entry.""" return [k for k in self.settings.keys if k][number] def drawKeySymbol(self, number, painter, x, y, width, height): """Draw a fill rectangle for key entry.""" self.plotBars(painter, self.settings, number, qt4.QRectF(0,0,32767,32767), ([x], [y+height*0.1], [x+width], [y+height*0.8])) def dataDraw(self, painter, axes, widgetposn, clip): """Plot the data on a plotter.""" s = self.settings # get data doc = self.document positions = s.get('posn') positions = None if positions.isEmpty() else positions.getData(doc) lengths = s.get('lengths').getData(doc) if not lengths: return # where the bars are to be placed horizontally barposns, maxwidth = self.findBarPositions(lengths, positions, axes, widgetposn) # only use finite positions origposnlen = len(barposns) validposn = N.isfinite(barposns) barposns = barposns[validposn] # this is a bit rubbish - we take the datasets and # make sure they have the same lengths as posns and remove NaNs # Datasets are stored as dicts dsvals = [] for dataset in lengths: vals = {} for key in ('data', 'serr', 'nerr', 'perr'): v = getattr(dataset, key) if v is not None: vals[key] = extend1DArray(N.nan_to_num(v), origposnlen)[validposn] dsvals.append(vals) # actually do the drawing fn = {'stacked': self.barDrawStacked, 'stacked-area': self.areaDrawStacked, 'grouped': self.barDrawGroup}[s.mode] fn(painter, barposns, maxwidth, dsvals, axes, widgetposn, clip) # allow the factory to instantiate a bar plotter document.thefactory.register( BarPlotter ) veusz-3.0.1/veusz/widgets/scene3d.py0000664000175000017500000002315013314251237017033 0ustar jssjss00000000000000# -*- coding: utf-8 -*- # Copyright (C) 2018 Jeremy S. Sanders # Email: Jeremy Sanders # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ############################################################################## from __future__ import division, print_function import math import numpy as N from .. import qtall as qt from .. import document from .. import setting from . import widget from . import controlgraph from ..helpers import threed def _(text, disambiguation=None, context='Scene3D'): """Translate text.""" return qt.QCoreApplication.translate(context, text, disambiguation) # define 2nd and 3rd lighting classes class Lighting3D_2(setting.Lighting3D): def __init__(self, name, **args): setting.Lighting3D.__init__(self, name, **args) self.get('enable').newDefault(False) self.get('color').newDefault('red') self.get('x').newDefault(2) class Lighting3D_3(setting.Lighting3D): def __init__(self, name, **args): setting.Lighting3D.__init__(self, name, **args) self.get('enable').newDefault(False) self.get('color').newDefault('blue') self.get('x').newDefault(-2) class Scene3D(widget.Widget): """3D scene containing other widgets.""" typename='scene3d' allowusercreation = True description = _('3d scene') @classmethod def addSettings(klass, s): """Construct list of settings.""" widget.Widget.addSettings(s) s.add( setting.FloatSlider( 'xRotation', 0, minval=-180, maxval=180, step=15, tick=45, descr=_(u'Rotation around x axis (°)'), usertext=_('X rotation') )) s.add( setting.FloatSlider( 'yRotation', 35., minval=-180, maxval=180, step=15, tick=45, descr=_(u'Rotation around y axis (°)'), usertext=_('Y rotation') )) s.add( setting.FloatSlider( 'zRotation', 0, minval=-180, maxval=180, step=15, tick=45, descr=_(u'Rotation around z axis (°)'), usertext=_('Z rotation') )) s.add( setting.FloatSlider( 'distance', 5, minval=1, maxval=50, step=0.5, tick=5, scale=0.1, descr=_(u'Viewing distance'), usertext=_('Distance') )) s.add( setting.FloatOrAuto( 'size', 'Auto', minval=0, descr=_('Automatic or fixed graph size scaling value'), usertext=_('Size'), formatting=True )) s.add( setting.Choice( 'rendermode', ('painters', 'bsp'), 'painters', uilist=("Fast (Painter's)", "Accurate (BSP)"), usertext=_('Render method'), descr=_('Method used to draw 3D plot') )) s.add( setting.Distance( 'leftMargin', '1cm', descr=_('Distance from left of graph to edge'), usertext=_('Left margin'), formatting=True) ) s.add( setting.Distance( 'rightMargin', '1cm', descr=_('Distance from right of graph to edge'), usertext=_('Right margin'), formatting=True) ) s.add( setting.Distance( 'topMargin', '1cm', descr=_('Distance from top of graph to edge'), usertext=_('Top margin'), formatting=True) ) s.add( setting.Distance( 'bottomMargin', '1cm', descr=_('Distance from bottom of graph to edge'), usertext=_('Bottom margin'), formatting=True) ) s.add(setting.Lighting3D( 'Lighting1', descr=_('Lighting (1)'), usertext=_('Lighting (1)')), pixmap = 'settings_lighting' ) s.add(Lighting3D_2( 'Lighting2', descr=_('Lighting (2)'), usertext=_('Lighting (2)')), pixmap = 'settings_lighting' ) s.add(Lighting3D_3( 'Lighting3', descr=_('Lighting (3)'), usertext=_('Lighting (3)')), pixmap = 'settings_lighting' ) @classmethod def allowedParentTypes(self): from . import page, grid return (page.Page, grid.Grid) def getMargins(self, painthelper): """Use settings to compute margins.""" s = self.settings return ( s.get('leftMargin').convert(painthelper), s.get('topMargin').convert(painthelper), s.get('rightMargin').convert(painthelper), s.get('bottomMargin').convert(painthelper) ) def makeObjects(self, painter, bounds, painthelper): """Make objects, returning root""" s = self.settings # do no painting if hidden if s.hide: return root = threed.ObjectContainer() root.objM = threed.rotate3M4( s.xRotation/180.*math.pi, s.yRotation/180.*math.pi, s.zRotation/180.*math.pi) # build 3d scene from children for c in self.children: obj = c.drawToObject(painter, painthelper) if obj: root.addObject(obj) return root def makeScene(self, painter): """Make Scene and Camera objects.""" s = self.settings camera = threed.Camera() # camera necessary to make x,y,z coordinates point in the # right direction, with the origin in the lower left towards # the viewer camera.setPointing( threed.Vec3(0, 0, -s.distance), threed.Vec3(0, 0, 0), threed.Vec3(0, -1, 0)) camera.setPerspective(90, 1, 100) mode = { 'painters': threed.Scene.RENDER_PAINTERS, 'bsp': threed.Scene.RENDER_BSP, }[s.rendermode] scene = threed.Scene(mode) # add lighting if enabled for light in s.Lighting1, s.Lighting2, s.Lighting3: if light.enable: scene.addLight( # FIXME: z and y negative here to make direction # correct relative to camera origin threed.Vec3(light.x, -light.y, -light.z), light.get('color').color(painter), light.intensity*0.01) return scene, camera def draw(self, parentposn, painthelper, outerbounds=None): '''Update the margins before drawing.''' bounds = self.computeBounds(parentposn, painthelper) painter = painthelper.painter(self, bounds) painthelper.setControlGraph(self, [ controlgraph.ControlMarginBox( self, bounds, outerbounds, painthelper)]) root = self.makeObjects(painter, bounds, painthelper) if root is None: return bounds scene, camera = self.makeScene(painter) # def ptToScreen(pt): # pt_v = (camera.viewM*root.objM)*threed.Vec4(pt[0],pt[1],pt[2],1) # pt_proj = threed.calcProjVec(camera.perspM, pt_v) # pt_screen = threed.projVecToScreen(scene.screenM, pt_proj) # return pt_screen,qt.QPointF(pt_screen.get(0), pt_screen.get(1)) # finally render the scene scale = self.settings.size if scale == 'Auto': scale = -1 with painter: scene.render( root, painter, camera, bounds[0], bounds[1], bounds[2], bounds[3], scale) # painter.setPen(qt.QPen(qt.Qt.red)) # origin = ptToScreen((0,0,0))[1] # for axpt in ((0.5,0,0),(0,0.5,0),(0,0,0.5)): # painter.drawLine(origin, ptToScreen(axpt)[1]) # axx = threed.Vec4(0,0.5,0,1) # threed.solveInverseRotation(camera.viewM, camera.perspM, scene.screenM, axx, ptToScreen((0,0.5,0))[0]) def identifyWidgetAtPoint(self, painthelper, bounds, scaling, x, y): painter = document.PainterRoot() painter.updateMetaData(painthelper) root = self.makeObjects(painter, bounds, painthelper) if root is None: return self scene, camera = self.makeScene(painter) sizescale = self.settings.size if sizescale == 'Auto': sizescale = -1 widgetid = scene.idPixel( root, painter, camera, bounds[0], bounds[1], bounds[2], bounds[3], sizescale, scaling, x, y) # recursive check id of children against returned value widget = [self] def checkwidget(r): for c in r.children: if id(c) == widgetid: widget[0] = c checkwidget(c) checkwidget(self) return widget[0] def updateControlItem(self, cgi): """Area moved or resized - call helper routine to move self.""" cgi.setWidgetMargins() document.thefactory.register(Scene3D) veusz-3.0.1/veusz/widgets/page.py0000664000175000017500000003154113302252613016422 0ustar jssjss00000000000000# Copyright (C) 2004 Jeremy S. Sanders # Email: Jeremy Sanders # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ############################################################################## """Widget that represents a page in the document.""" from __future__ import division, print_function import collections import textwrap import numpy as N from ..compat import crange, citems from .. import qtall as qt4 from .. import document from .. import setting from .. import utils from . import widget from . import controlgraph def _(text, disambiguation=None, context='Page'): """Translate text.""" return qt4.QCoreApplication.translate(context, text, disambiguation) defaultrange = [1e99, -1e99] def _resolveLinkedAxis(axis): """Follow a chain of axis function dependencies.""" loopcheck = set() while axis is not None and axis.isLinked(): loopcheck.add(axis) axis = axis.getLinkedAxis() if axis in loopcheck: # fail if loop return None return axis class AxisDependHelper(object): """A class to work out the dependency of widgets on axes and vice versa, in terms of ranges of the axes. Note: Here a widget is really (widget, depname), as each widget can have a different dependency (e.g. sx and sy dependencies for plotters). It then works out the ranges for each of the axes from the plotters. connection types: plotter->axis : axis needs to know data range axis->plotter : plotter needs to know axis range axis<->axis : axes are mutually dependent aim: calculate ranges of axes given plotters problem: cycles in the graph f1<-x: function f1 depends on axis x f2<-y: function f2 depends on axis y y<-f1: axis y depends on function f1 x<-f2: axis x depends on function f2 solution: break dependency cycle: choose somewhere - probably better to choose where widget depends on axis however, axis<->axis cycle can't be broken additional solution: convert all dependencies on axis1 or axis2 to axiscomb x <-> axis1 <-> axis2 For linked axes (e.g. AxisFunction): * Don't keep track of range separately -> propagate to real axis * For dependency order resolution, use real axis * In self.deps, use axisfunction axis so we know which axis to use """ def __init__(self): # map widgets to widgets it depends on self.deps = collections.defaultdict(list) # list of axes self.axes = [] # list of plotters associated with each axis self.axis_plotter_map = collections.defaultdict(list) # ranges for each axis self.ranges = {} # pairs of dependent widgets self.pairs = [] # track axes which map from one axis to another self.axis_to_axislinked = {} self.axislinked_to_axis = {} def recursivePlotterSearch(self, widget): """Find a list of plotters below widget. Builds up a dict of "nodes" representing each widget: plotter/axis Each node is a list of tuples saying which widgets need evaling first The tuples are (widget, depname), where depname is a name for the part of the plotter, e.g. "sx" or "sy" for x or y. """ if widget.isplotter: # keep track of which widgets depend on which axes widgetaxes = {} for axname in widget.getAxesNames(): axis = widget.lookupAxis(axname) widgetaxes[axname] = axis self.axis_plotter_map[axis].append(widget) # if the widget is a plotter, find which axes the widget # can provide range information about for axname, depname in widget.affectsAxisRange(): origaxis = widgetaxes[axname] resolvedaxis = _resolveLinkedAxis(origaxis) if resolvedaxis is not None and resolvedaxis.usesAutoRange(): # only add dependency if axis has an automatic range self.deps[(origaxis, None)].append((widget, depname)) self.pairs.append( ((widget, depname), (resolvedaxis, None)) ) # find which axes the plotter needs information from for depname, axname in widget.requiresAxisRange(): origaxis = widgetaxes[axname] resolvedaxis = _resolveLinkedAxis(origaxis) if resolvedaxis is not None and resolvedaxis.usesAutoRange(): self.deps[(widget, depname)].append((origaxis, None)) self.pairs.append( ((resolvedaxis, None), (widget, depname)) ) elif widget.isaxis: if widget.isaxis and widget.isLinked(): # function of another axis linked = widget.getLinkedAxis() if linked is not None: self.axis_to_axislinked[linked] = widget self.axislinked_to_axis[widget] = linked else: # make a range for a normal axis self.axes.append(widget) self.ranges[widget] = list(defaultrange) for c in widget.children: self.recursivePlotterSearch(c) def breakCycles(self, origcyclic): """Remove cycles if possible.""" numcyclic = len(origcyclic) best = -1 for i in crange(len(self.pairs)): if not self.pairs[i][0][0].isaxis: p = self.pairs[:i] + self.pairs[i+1:] ordered, cyclic = utils.topological_sort(p) if len(cyclic) <= numcyclic: numcyclic = len(cyclic) best = i # delete best, or last one if none better found p = self.pairs[best] del self.pairs[best] try: idx = self.deps[p[1]].index(p[0]) del self.deps[p[1]][idx] except ValueError: pass def _updateAxisAutoRange(self, axis): """Update auto range for axis.""" # set actual range on axis, as axis no longer has a # dependency axrange = self.ranges[axis] if axrange == defaultrange: axrange = None axis.setAutoRange(axrange) del self.ranges[axis] def _updateRangeFromPlotter(self, axis, plotter, plotterdep): """Update the range for axis from the plotter.""" if axis.isLinked(): # take range and map back to real axis therange = list(defaultrange) plotter.getRange(axis, plotterdep, therange) if therange != defaultrange: # follow up chain loopcheck = set() while axis.isLinked(): loopcheck.add(axis) therange = axis.invertFunctionVals(therange) axis = axis.getLinkedAxis() if axis in loopcheck: axis = None if axis is not None and therange is not None: self.ranges[axis] = [ N.nanmin((self.ranges[axis][0], therange[0])), N.nanmax((self.ranges[axis][1], therange[1])) ] else: plotter.getRange(axis, plotterdep, self.ranges[axis]) def processWidgetDeps(self, dep): """Process dependencies for a single widget.""" widget, widget_dep = dep # iterate over dependent widgets for widgetd, widgetd_dep in self.deps[dep]: if ( widgetd.isplotter and (not widgetd.settings.isSetting('hide') or not widgetd.settings.hide) ): self._updateRangeFromPlotter(widget, widgetd, widgetd_dep) elif widgetd.isaxis: axis = _resolveLinkedAxis(widgetd) if axis in self.ranges: self._updateAxisAutoRange(axis) def processDepends(self): """Go through dependencies of widget. If the dependency has no dependency itself, then update the axis with the widget or vice versa Algorithm: Iterate over dependencies for widget. If the widget has a dependency on a widget which doesn't have a dependency itself, update range from that widget. Then delete that depency from the dependency list. """ # get ordered list, breaking cycles while True: ordered, cyclic = utils.topological_sort(self.pairs) if not cyclic: break self.breakCycles(cyclic) # iterate over widgets in order for dep in ordered: self.processWidgetDeps(dep) # process deps for any axis functions while dep[0] in self.axis_to_axislinked: dep = (self.axis_to_axislinked[dep[0]], None) self.processWidgetDeps(dep) def findAxisRanges(self): """Find the ranges from the plotters and set the axis ranges. Follows the dependencies calculated above. """ self.processDepends() # set any remaining ranges for axis in list(self.ranges.keys()): self._updateAxisAutoRange(axis) class Page(widget.Widget): """A class for representing a page of plotting.""" typename='page' allowusercreation = True description=_('Blank page') @classmethod def addSettings(klass, s): widget.Widget.addSettings(s) # page sizes are initially linked to the document page size s.add( setting.DistancePhysical( 'width', setting.Reference('/width'), descr=_('Width of page'), usertext=_('Page width'), formatting=True) ) s.add( setting.DistancePhysical( 'height', setting.Reference('/height'), descr=_('Height of page'), usertext=_('Page height'), formatting=True) ) s.add( setting.Notes( 'notes', '', descr=_('User-defined notes'), usertext=_('Notes') ) ) @classmethod def allowedParentTypes(klass): from . import root return (root.Root,) @property def userdescription(self): """Return user-friendly description.""" return textwrap.fill(self.settings.notes, 60) def draw(self, parentposn, painthelper, outerbounds=None): """Draw the plotter. Clip graph inside bounds.""" # document should pass us the page bounds x1, y1, x2, y2 = parentposn # find ranges of axes axisdependhelper = AxisDependHelper() axisdependhelper.recursivePlotterSearch(self) axisdependhelper.findAxisRanges() # store axis->plotter mappings in painthelper painthelper.axisplottermap.update(axisdependhelper.axis_plotter_map) # reverse mapping pamap = collections.defaultdict(list) for axis, plotters in citems(painthelper.axisplottermap): for plot in plotters: pamap[plot].append(axis) painthelper.plotteraxismap.update(pamap) if self.settings.hide: bounds = self.computeBounds(parentposn, painthelper) return bounds # clip to page painter = painthelper.painter(self, parentposn) with painter: # w and h are non integer w = self.settings.get('width').convert(painter) h = self.settings.get('height').convert(painter) painthelper.setControlGraph(self, [ controlgraph.ControlMarginBox(self, [0, 0, w, h], [-10000, -10000, 10000, 10000], painthelper, ismovable = False) ] ) bounds = widget.Widget.draw(self, parentposn, painthelper, parentposn) return bounds def updateControlItem(self, cgi): """Call helper to set page size.""" cgi.setPageSize() # allow the factory to instantiate this document.thefactory.register( Page ) veusz-3.0.1/veusz/widgets/surface3d.py0000664000175000017500000001447313303561077017402 0ustar jssjss00000000000000# Copyright (C) 2015 Jeremy S. Sanders # Email: Jeremy Sanders # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ############################################################################### """3D surface plotting widget.""" from __future__ import division, print_function import numpy as N from .. import qtall as qt from .. import setting from .. import document from .. import utils from ..helpers import threed from . import plotters3d def _(text, disambiguation=None, context='Surface3D'): """Translate text.""" return qt.QCoreApplication.translate(context, text, disambiguation) class Surface3D(plotters3d.GenericPlotter3D): """Plotting surface in 3D.""" typename = 'surface3d' description = _('3D surface') allowusercreation=True # list of modes allowed modes = ( 'x(y,z)', 'x(z,y)', 'y(x,z)', 'y(z,x)', 'z(x,y)', 'z(y,x)', ) # above, in numeric form mode_idxs = { 'x(y,z)': (0,1,2), 'x(z,y)': (0,2,1), 'y(x,z)': (1,0,2), 'y(z,x)': (1,2,0), 'z(x,y)': (2,0,1), 'z(y,x)': (2,1,0), } @classmethod def addSettings(klass, s): plotters3d.GenericPlotter3D.addSettings(s) s.add(setting.Choice( 'mode', klass.modes, 'z(x,y)', descr=_('Axes of plot surface'), usertext=_('Mode')), 0) s.add(setting.DatasetExtended( 'data', '', dimensions=2, descr=_('Dataset to plot'), usertext=_('Dataset')), 1) s.add(setting.DataColor( 'DataColor', dimensions=2), 2) s.add(setting.Bool( 'highres', False, descr=_('High resolution surface (accurate bin centres)'), usertext=_('High res.'), formatting=True) ) s.add(setting.LineGrid3D( 'Line', descr = _('Grid line settings'), usertext = _('Grid line')), pixmap = 'settings_gridline' ) s.add(setting.Surface3DWColorMap( 'Surface', descr=_('Surface fill settings'), usertext=_('Surface')), pixmap='settings_bgfill') def affectsAxisRange(self): """Which axes this widget affects.""" s = self.settings return ((s.xAxis, 'sx'), (s.yAxis, 'sy'), (s.zAxis, 'sz')) def getRange(self, axis, depname, axrange): """Update axis range from data.""" s = self.settings # get real dataset data = s.get('data').getData(self.document) if data is None or data.dimensions != 2: return # convert axis dependency into an index (0,1,2) which # specifies whether to get value range, or 2d range axidx = {'sx': 0, 'sy': 1, 'sz': 2}[depname] idx = self.mode_idxs[s.mode].index(axidx) rng = None if idx == 0: # get range of values in 2D data data = N.ravel(data.data) findata = data[N.isfinite(data)] if len(findata) > 0: rng = findata.min(), findata.max() elif idx == 1: # range of data from 1st coordinate rng = data.getDataRanges()[0] elif idx == 2: # range of data from 2nd coordinate rng = data.getDataRanges()[1] if rng: axrange[0] = min(axrange[0], rng[0]) axrange[1] = max(axrange[1], rng[1]) def drawSurface(self, painter, container, axes, dataset): """Add the surface to the container.""" s = self.settings if s.Surface.hide and s.Line.hide: return surfprop = s.Surface.makeSurfaceProp(painter) lineprop = s.Line.makeLineProp(painter) highres = s.highres # axes to plot coordinates on idxs = self.mode_idxs[self.settings.mode] # convert to logical coordinates data = axes[idxs[0]].dataToLogicalCoords( N.ravel(N.transpose(dataset.data))) e = dataset.getPixelEdges() edges1 = axes[idxs[1]].dataToLogicalCoords(e[0]) edges2 = axes[idxs[2]].dataToLogicalCoords(e[1]) # compute colors, if set colordata = s.DataColor.get('points').getData(self.document) if surfprop is not None and colordata is not None: cmap = self.document.evaluate.getColormap( s.Surface.colorMap, s.Surface.colorMapInvert) cdata = N.transpose(colordata.data) cdata = cdata.reshape((1, cdata.size)) colorimg = utils.applyColorMap( cmap, s.DataColor.scaling, cdata, s.DataColor.min, s.DataColor.max, s.Surface.transparency) surfprop.setRGBs(colorimg) mesh = threed.DataMesh( threed.ValVector(edges1), threed.ValVector(edges2), threed.ValVector(data), idxs[0], idxs[1], idxs[2], highres, lineprop, surfprop, s.Line.hidehorz, s.Line.hidevert) container.addObject(mesh) def dataDrawToObject(self, painter, axes): """Do actual drawing of function.""" s = self.settings mode = s.mode axes = self.fetchAxes() if axes is None: return s = self.settings data = s.get('data').getData(self.document) if s.hide or data is None or data.dimensions != 2: return clipcontainer = self.makeClipContainer(axes) self.drawSurface(painter, clipcontainer, axes, data) clipcontainer.assignWidgetId(id(self)) return clipcontainer document.thefactory.register(Surface3D) veusz-3.0.1/veusz/widgets/axis3d.py0000664000175000017500000006301313302475575016716 0ustar jssjss00000000000000# Copyright (C) 2014 Jeremy S. Sanders # Email: Jeremy Sanders # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ############################################################################## """Widget to plot 3D axes, and to handle conversion of coordinates to plot positions.""" from __future__ import division, print_function, absolute_import import math import numpy as N import itertools from . import widget from . import axisticks from . import axis from ..compat import czip from .. import qtall as qt from .. import document from .. import setting from .. import utils from ..helpers import threed def _(text, disambiguation=None, context='Axis3D'): """Translate text.""" return qt.QCoreApplication.translate(context, text, disambiguation) class _AxisLabels(threed.AxisLabels): """For drawing tick labels. box1,2: points at corners of graph tickfracs: fractions along axis for tick labels ticklabels: list of labels to plot at fracs ticklabelsprop: font properties for ticks axislabel: label for axis axislabelposn: position of axis label (or -1 for no label) axislabelprop: font properties for axislabel """ def __init__(self, box1, box2, tickfracs, ticklabels, ticklabelsprop, axislabel, axislabelposn, axislabelprop): threed.AxisLabels.__init__(self, box1, box2, tickfracs, axislabelposn) self.ticklabels = ticklabels self.ticklabelsprop = ticklabelsprop self.axislabel = axislabel self.axislabelposn = axislabelposn self.axislabelprop = axislabelprop def drawLabel(self, painter, index, pt, ax1, ax2, axangle): """Called when scene wants to paint label.""" painter.translate(pt.x(), pt.y()) # angle of axis inclination angle = math.atan2(ax2.y()-ax1.y(), ax2.x()-ax1.x()) * (180/math.pi) # vector to axis from graph centre cvecy = math.sin(axangle*math.pi/180) cvecx = math.cos(axangle*math.pi/180) # character upright vector avecy = math.sin((angle-90)*math.pi/180) avecx = math.cos((angle-90)*math.pi/180) dot = cvecx*avecx+cvecy*avecy #print('axangle % 7.2f angle % 7.2f delta % 7.2f dot % 7.2f' % ( # axangle, angle, axangle+angle, dot), self.ticklabels[index]) # flip depending on relative direction of label and graph centre if dot < 0: angle = angle+180 if angle > 180: angle = angle-360 # flip if upside down if angle < -90 or angle > 90: angle = angle+180 valign = 1 else: valign = -1 painter.rotate(angle) if index >= 0: self.drawTickLabel(painter, pt.x(), pt.y(), angle, index, valign) else: self.drawAxisLabel(painter, valign) def drawTickLabel(self, painter, x, y, angle, index, valign): """Draw a tick label.""" font = self.ticklabelsprop.makeQFont(painter) painter.setFont(font) label = self.ticklabels[index] renderer = utils.Renderer( painter, font, 0, 0, label, alignhorz=0, alignvert=valign, usefullheight=True) # get text bounds rect = renderer.getTightBounds() rect.rotateAboutOrigin(angle * math.pi/180) rect.translate(x, y) # draw text if it doesn't overlap with existing text if not painter.textrects.willOverlap(rect): pen = self.ticklabelsprop.makeQPen(painter) painter.setPen(pen) renderer.render() painter.textrects.addRect(rect) def drawAxisLabel(self, painter, valign): """Draw label for axis.""" # y increment from labels closer to axis deltay = 0 if self.ticklabels: font = self.ticklabelsprop.makeQFont(painter) fm = utils.FontMetrics(font, painter.device()) deltay = valign*fm.lineSpacing() # change alignment depending on label position if self.axislabelposn==0.: halign = -1 elif self.axislabelposn==1.: halign = 1 else: halign = 0 # draw label font = self.axislabelprop.makeQFont(painter) painter.setFont(font) pen = self.axislabelprop.makeQPen(painter) painter.setPen(pen) renderer = utils.Renderer( painter, font, 0, deltay, self.axislabel, alignhorz=halign, alignvert=valign, usefullheight=True) renderer.render() class MajorTick(setting.Line3D): '''Major tick settings.''' def __init__(self, name, **args): setting.Line3D.__init__(self, name, **args) self.get('color').newDefault('grey') self.add(setting.Float( 'length', 20., descr = _('Length of major ticks'), usertext= _('Length'))) self.add(setting.Int( 'number', 6, descr = _('Number of major ticks to aim for'), usertext= _('Number'))) self.add(setting.FloatList( 'manualTicks', [], descr = _('List of tick values' ' overriding defaults'), usertext= _('Manual ticks'))) class MinorTick(setting.Line3D): '''Minor tick settings.''' def __init__(self, name, **args): setting.Line3D.__init__(self, name, **args) self.get('color').newDefault('grey') self.add( setting.Float( 'length', 10, descr = _('Length of minor ticks'), usertext= _('Length'))) self.add( setting.Int( 'number', 20, descr = _('Number of minor ticks to aim for'), usertext= _('Number'))) class GridLine(setting.Line3D): '''Grid line settings.''' def __init__(self, name, **args): setting.Line3D.__init__(self, name, **args) self.get('color').newDefault('grey') self.get('hide').newDefault(True) class MinorGridLine(setting.Line3D): '''Minor tick grid line settings.''' def __init__(self, name, **args): setting.Line3D.__init__(self, name, **args) self.get('color').newDefault('lightgrey') self.get('hide').newDefault(True) class AxisLabel(setting.Text): """Axis label.""" def __init__(self, name, **args): setting.Text.__init__(self, name, **args) self.add( setting.Choice( 'position', ('at-minimum', 'centre', 'at-maximum'), 'centre', descr = _('Position of axis label'), usertext = _('Position') ) ) class TickLabel(axis.TickLabel): """3D axis tick label.""" def __init__(self, name, **args): axis.TickLabel.__init__(self, name, **args) self.remove('rotate') self.remove('offset') class Axis3D(widget.Widget): """Manages and draws an axis.""" typename = 'axis3d' allowusercreation = True description = _('3D axis') isaxis = True isaxis3d = True def __init__(self, parent, name=None): """Initialise axis.""" widget.Widget.__init__(self, parent, name=name) s = self.settings for n in ('x', 'y', 'z'): if self.name == n and s.direction != n: s.direction = n # automatic range self.setAutoRange(None) # document updates change set variable when things need recalculating self.docchangeset = -1 @classmethod def addSettings(klass, s): """Construct list of settings.""" widget.Widget.addSettings(s) s.add( setting.Str( 'label', '', descr=_('Axis label text'), usertext=_('Label')) ) s.add( setting.AxisBound( 'min', 'Auto', descr=_('Minimum value of axis'), usertext=_('Min')) ) s.add( setting.AxisBound( 'max', 'Auto', descr=_('Maximum value of axis'), usertext=_('Max')) ) s.add( setting.Bool( 'log', False, descr = _('Whether axis is logarithmic'), usertext=_('Log')) ) s.add( axis.AutoRange( 'autoRange', 'next-tick') ) s.add( setting.Choice( 'mode', ('numeric', 'datetime', 'labels'), 'numeric', descr = _('Type of ticks to show on on axis'), usertext=_('Mode')) ) s.add( setting.Bool( 'autoMirror', True, descr = _('Place axis on opposite side of graph ' 'if none'), usertext=_('Auto mirror'), formatting=True) ) s.add( setting.Float( 'datascale', 1., descr=_('Scale data plotted by this factor'), usertext=_('Scale')) ) s.add( setting.Choice( 'direction', ['x', 'y', 'z'], 'x', descr = _('Direction of axis'), usertext=_('Direction')) ) s.add( setting.Float( 'lowerPosition', 0., descr=_('Fractional position of lower end of ' 'axis on graph'), usertext=_('Min position')) ) s.add( setting.Float( 'upperPosition', 1., descr=_('Fractional position of upper end of ' 'axis on graph'), usertext=_('Max position')) ) s.add( setting.Float( 'otherPosition1', 0., descr=_('Fractional position of axis ' 'in its perpendicular direction 1'), usertext=_('Axis position 1')) ) s.add( setting.Float( 'otherPosition2', 0., descr=_('Fractional position of axis ' 'in its perpendicular direction 2'), usertext=_('Axis position 2')) ) s.add( setting.Line3D( 'Line', descr = _('Axis line settings'), usertext = _('Axis line')), pixmap='settings_axisline' ) s.add( AxisLabel( 'Label', descr = _('Axis label settings'), usertext = _('Axis label')), pixmap='settings_axislabel' ) s.add( TickLabel( 'TickLabels', descr = _('Tick label settings'), usertext = _('Tick labels')), pixmap='settings_axisticklabels' ) s.add( MajorTick( 'MajorTicks', descr = _('Major tick line settings'), usertext = _('Major ticks')), pixmap='settings_axismajorticks' ) s.add( MinorTick( 'MinorTicks', descr = _('Minor tick line settings'), usertext = _('Minor ticks')), pixmap='settings_axisminorticks' ) s.add( GridLine( 'GridLines', descr = _('Grid line settings'), usertext = _('Grid lines')), pixmap='settings_axisgridlines' ) s.add( MinorGridLine( 'MinorGridLines', descr = _('Minor grid line settings'), usertext = _('Grid lines for minor ticks')), pixmap='settings_axisminorgridlines' ) @classmethod def allowedParentTypes(self): from . import graph3d return (graph3d.Graph3D,) @property def userdescription(self): """User friendly description.""" s = self.settings return "range %s to %s%s" % ( str(s.min), str(s.max), ['',' (log)'][s.log]) def isLinked(self): """Whether is an axis linked to another.""" return False def setAutoRange(self, autorange): """Set the automatic range of this axis (called from page helper).""" if autorange: scale = self.settings.datascale self.autorange = ar = [x*scale for x in autorange] if self.settings.log: ar[0] = max(1e-99, ar[0]) else: if self.settings.log: self.autorange = [1e-2, 1.] else: self.autorange = [0., 1.] def usesAutoRange(self): """Return whether any of the bounds are automatically determined.""" return self.settings.min == 'Auto' or self.settings.max == 'Auto' def computePlottedRange(self, force=False, overriderange=None): """Convert the range requested into a plotted range.""" if self.docchangeset == self.document.changeset and not force: return s = self.settings if overriderange is None: self.plottedrange = [s.min, s.max] else: self.plottedrange = overriderange # automatic lookup of minimum if overriderange is None: if s.min == 'Auto': self.plottedrange[0] = self.autorange[0] if s.max == 'Auto': self.plottedrange[1] = self.autorange[1] # yuck, but sometimes it's true # tweak range to make sure things don't blow up further down the # line if ( abs(self.plottedrange[0] - self.plottedrange[1]) < ( abs(self.plottedrange[0]) + abs(self.plottedrange[1]) )*1e-8 ): self.plottedrange[1] = ( self.plottedrange[0] + max(1., self.plottedrange[0]*0.1) ) # handle axis values round the wrong way invertaxis = self.plottedrange[0] > self.plottedrange[1] if invertaxis: self.plottedrange = self.plottedrange[::-1] # make sure log axes don't blow up if s.log: if self.plottedrange[0] < 1e-99: self.plottedrange[0] = 1e-99 if self.plottedrange[1] < 1e-99: self.plottedrange[1] = 1e-99 if self.plottedrange[0] == self.plottedrange[1]: self.plottedrange[1] = self.plottedrange[0]*2 s.get('autoRange').adjustPlottedRange( self.plottedrange, s.min=='Auto', s.max=='Auto', s.log, self.document) self.computeTicks() # invert bounds if axis was inverted if invertaxis: self.plottedrange = self.plottedrange[::-1] self.docchangeset = self.document.changeset def computeTicks(self, allowauto=True): """Update ticks given plotted range. if allowauto is False, then do not allow ticks to be updated """ s = self.settings if s.mode in ('numeric', 'labels'): tickclass = axisticks.AxisTicks else: tickclass = axisticks.DateTicks nexttick = s.autoRange == 'next-tick' extendmin = nexttick and s.min == 'Auto' and allowauto extendmax = nexttick and s.max == 'Auto' and allowauto # create object to compute ticks axs = tickclass(self.plottedrange[0], self.plottedrange[1], s.MajorTicks.number, s.MinorTicks.number, extendmin = extendmin, extendmax = extendmax, logaxis = s.log ) axs.getTicks() self.plottedrange[0] = axs.minval self.plottedrange[1] = axs.maxval self.majortickscalc = axs.tickvals self.minortickscalc = axs.minorticks self.autoformat = axs.autoformat # override values if requested if len(s.MajorTicks.manualTicks) > 0: ticks = [] for i in s.MajorTicks.manualTicks: if i >= self.plottedrange[0] and i <= self.plottedrange[1]: ticks.append(i) self.majortickscalc = N.array(ticks) def getPlottedRange(self): """Return the range plotted by the axes.""" self.computePlottedRange() return (self.plottedrange[0], self.plottedrange[1]) def dataToLogicalCoords(self, vals, scaling=True): """Compute coordinates on graph to logical graph coordinates (0..1) If scaling is True, apply scaling factor for data """ self.computePlottedRange() s = self.settings svals = vals*s.datascale if scaling else vals if s.log: fracposns = self.logConvertToPlotter(svals) else: fracposns = self.linearConvertToPlotter(svals) lower, upper = s.lowerPosition, s.upperPosition return lower + fracposns*(upper-lower) def linearConvertToPlotter(self, v): """Convert graph coordinates to 0..1 coordinates""" return ( (v - self.plottedrange[0]) / (self.plottedrange[1] - self.plottedrange[0]) ) def logConvertToPlotter(self, v): """Convert graph coordinates to 0..1 coordinates""" log1 = N.log(self.plottedrange[0]) log2 = N.log(self.plottedrange[1]) return (N.log(N.clip(v, 1e-99, 1e99)) - log1) / (log2 - log1) def transformToAxis(self, v): """Return value to give equal-spaced values in transformed coordinates.""" x = v*self.settings.datascale return N.log(x) if self.settings.log else x def transformFromAxis(self, v): """Convert transformed values back.""" x = v/self.settings.datascale return N.exp(x) if self.settings.log else x def getAutoMirrorCombs(self): """Get combinations of other position for auto mirroring.""" s = self.settings op1 = s.otherPosition1 op2 = s.otherPosition2 if not s.autoMirror: return ((op1, op2),) if op1 == 0 or op1 == 1: op1list = [1, 0] else: op1list = [op1] if op2 == 0 or op2 == 1: op2list = [1, 0] else: op2list = [op2] return itertools.product(op1list, op2list) def addAxisLine(self, painter, cont, dirn): """Build list of lines to draw axis line, mirroring if necessary. Returns list of start and end points of axis lines """ s = self.settings lower, upper = s.lowerPosition, s.upperPosition outstart = [] outend = [] for op1, op2 in self.getAutoMirrorCombs(): if dirn == 'x': outstart += [(lower, op1, op2)] outend += [(upper, op1, op2)] elif dirn == 'y': outstart += [(op1, lower, op2)] outend += [(op1, upper, op2)] else: outstart += [(op1, op2, lower)] outend += [(op1, op2, upper)] if not s.Line.hide: startpts = threed.ValVector(N.ravel(outstart)) endpts = threed.ValVector(N.ravel(outend)) lineprop = s.Line.makeLineProp(painter) cont.addObject(threed.LineSegments(startpts, endpts, lineprop)) return list(zip(outstart, outend)) def addLabels(self, cont, linecoords, ticklabelsprop, tickfracs, tickvals, axislabel, axislabelprop): """Make tick labels for axis.""" if not ticklabelsprop.hide: # make strings for labels fmt = ticklabelsprop.format if fmt.lower() == 'auto': fmt = self.autoformat scale = ticklabelsprop.scale ticklabels = [ utils.formatNumber(v*scale, fmt, locale=self.document.locale) for v in tickvals ] else: return # disable drawing of label if axislabelprop.hide: axislabel = "" axislabelposn = -1 else: axislabelposn = { 'at-minimum': 0, 'centre': 0.5, 'at-maximum': 1}[axislabelprop.position] # this is to get the z ordering of the ends of axes correct # where two axes join together tf = N.array(tickfracs) tf[tf==0.] = 0.00001 tf[tf==1.] = 0.99999 atl = _AxisLabels( threed.Vec3(0,0,0), threed.Vec3(1,1,1), threed.ValVector(tf), ticklabels, ticklabelsprop, axislabel, axislabelposn, axislabelprop) for startpos, endpos in linecoords: atl.addAxisChoice(threed.Vec3(*startpos), threed.Vec3(*endpos)) cont.addObject(atl) def addAxisTicks(self, painter, cont, dirn, linecoords, tickprops, ticklabelsprop, tickvals): """Add ticks for the vals and tick properties class given. linecoords: coordinates of start and end points of lines labelprops: properties of label, or None cont: container to add ticks dirn: 'x', 'y', 'z' for axis """ ticklen = tickprops.length * 1e-3 tfracs = self.dataToLogicalCoords(tickvals, scaling=False) outstart = [] outend = [] for op1, op2 in self.getAutoMirrorCombs(): # where to draw tick from op1pts = N.full_like(tfracs, op1) op2pts = N.full_like(tfracs, op2) # where to draw tick to op1pts2 = N.full_like(tfracs, op1+ticklen*(1 if op1 < 0.5 else -1)) op2pts2 = N.full_like(tfracs, op2+ticklen*(1 if op2 < 0.5 else -1)) # swap coordinates depending on axis direction if dirn == 'x': ptsonaxis = (tfracs, op1pts, op2pts) ptsoff1 = (tfracs, op1pts2, op2pts) ptsoff2 = (tfracs, op1pts, op2pts2) elif dirn == 'y': ptsonaxis = (op1pts, tfracs, op2pts) ptsoff1 = (op1pts2, tfracs, op2pts) ptsoff2 = (op1pts, tfracs, op2pts2) else: ptsonaxis = (op1pts, op2pts, tfracs) ptsoff1 = (op1pts2, op2pts, tfracs) ptsoff2 = (op1pts, op2pts2, tfracs) outstart += [N.ravel(N.column_stack(ptsonaxis)), N.ravel(N.column_stack(ptsonaxis))] outend += [N.ravel(N.column_stack(ptsoff1)), N.ravel(N.column_stack(ptsoff2))] # add labels for ticks and axis label if ticklabelsprop is not None: self.addLabels( cont, linecoords, ticklabelsprop, tfracs, tickvals, self.settings.label, self.settings.Label) # add ticks themselves if not tickprops.hide: startpts = threed.ValVector(N.concatenate(outstart)) endpts = threed.ValVector(N.concatenate(outend)) lineprop = tickprops.makeLineProp(painter) cont.addObject(threed.LineSegments(startpts, endpts, lineprop)) def addGridLines(self, painter, cont, dirn, linecoords, gridprops, tickvals): """Add ticks for the vals and tick properties class given. linecoords: coordinates of start and end points of lines cont: container to add ticks dirn: 'x', 'y', 'z' for axis """ if gridprops.hide: return tfracs = self.dataToLogicalCoords(tickvals, scaling=False) ones = N.ones(tfracs.shape) zeros = N.zeros(tfracs.shape) outstart = [] outend = [] # positions of grid lines for x axis pts1 = [ (tfracs, zeros, zeros), (tfracs, zeros, zeros), (tfracs, ones, ones), (tfracs, ones, ones) ] pts2 = [ (tfracs, zeros, ones), (tfracs, ones, zeros), (tfracs, zeros, ones), (tfracs, ones, zeros) ] # norm for each face, so we can draw back of cube only norms = [ (0, 1, 0), (0, 0, 1), (0, 0, -1), (0, -1, 0) ] # swap coordinates for other axes if dirn == 'y': pts1 = [(c,a,b) for a,b,c in pts1] pts2 = [(c,a,b) for a,b,c in pts2] norms = [(c,a,b) for a,b,c in norms] elif dirn == 'z': pts1 = [(b,c,a) for a,b,c in pts1] pts2 = [(b,c,a) for a,b,c in pts2] norms = [(b,c,a) for a,b,c in norms] # add lines on each face lineprop = gridprops.makeLineProp(painter) for p1, p2, n in czip(pts1, pts2, norms): # container only shows face if norm points to observer face = threed.FacingContainer(threed.Vec3(*n)) c1 = threed.ValVector(N.ravel(N.column_stack(p1))) c2 = threed.ValVector(N.ravel(N.column_stack(p2))) face.addObject(threed.LineSegments(c1, c2, lineprop)) cont.addObject(face) def drawToObject(self, painter, painthelper): s = self.settings dirn = s.direction cont = threed.ObjectContainer() linecoords = self.addAxisLine(painter, cont, dirn) self.addAxisTicks( painter, cont, dirn, linecoords, s.MajorTicks, s.TickLabels, self.majortickscalc) self.addAxisTicks( painter, cont, dirn, linecoords, s.MinorTicks, None, self.minortickscalc) self.addGridLines( painter, cont, dirn, linecoords, s.GridLines, self.majortickscalc) self.addGridLines( painter, cont, dirn, linecoords, s.MinorGridLines, self.minortickscalc) cont.assignWidgetId(id(self)) return cont # allow the factory to instantiate an axis document.thefactory.register(Axis3D) veusz-3.0.1/veusz/widgets/controlgraph.py0000664000175000017500000006645113245323107020223 0ustar jssjss00000000000000# Copyright (C) 2008 Jeremy S. Sanders # Email: Jeremy Sanders # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ############################################################################## """ Classes for moving widgets around Control items have a createGraphicsItem method which returns a graphics item to control the object """ from __future__ import division import math from ..compat import crange, czip from .. import qtall as qt from .. import document from .. import setting def _(text, disambiguation=None, context='controlgraph'): """Translate text.""" return qt.QCoreApplication.translate(context, text, disambiguation) ############################################################################## class _ScaledShape: """Mixing class for shapes which can return scaled positions. The control items are plotted on a zoomed plot, so the raw unscaled coordinates need to be scaled by params.cgscale to be in the correct position. We could scale everything to achieve this, but the line widths and size of objects would be changed. """ def setScaledPos(self, x, y): self.setPos(x*self.params.cgscale, y*self.params.cgscale) def scaledPos(self): return self.pos()/self.params.cgscale def setScaledLine(self, x1, y1, x2, y2): s = self.params.cgscale self.setLine(x1*s, y1*s, x2*s, y2*s) def setScaledLinePos(self, x1, y1, x2, y2): s = self.params.cgscale self.setPos(x1*s, y1*s) self.setLine(0,0,(x2-x1)*s,(y2-y1)*s) def setScaledRect(self, x, y, w, h): s = self.params.cgscale self.setRect(x*s, y*s, w*s, h*s) def scaledX(self): return self.x()/self.params.cgscale def scaledY(self): return self.y()/self.params.cgscale def scaledRect(self): r = self.rect() s = self.params.cgscale return qt.QRectF(r.left()/s, r.top()/s, r.width()/s, r.height()/s) ############################################################################## class _ShapeCorner(qt.QGraphicsRectItem, _ScaledShape): """Representing the corners of the rectangle.""" def __init__(self, parent, params, rotator=False): qt.QGraphicsRectItem.__init__(self, parent) self.params = params if rotator: self.setBrush( qt.QBrush(setting.settingdb.color('cntrlline')) ) self.setRect(-3, -3, 6, 6) else: self.setBrush(qt.QBrush(setting.settingdb.color('cntrlcorner')) ) self.setRect(-5, -5, 10, 10) self.setPen(qt.QPen(qt.Qt.NoPen)) self.setFlag(qt.QGraphicsItem.ItemIsMovable) self.setZValue(3.) def mouseMoveEvent(self, event): """Notify parent on move.""" qt.QGraphicsRectItem.mouseMoveEvent(self, event) self.parentItem().updateFromCorner(self, event) def mouseReleaseEvent(self, event): """Notify parent on unclicking.""" qt.QGraphicsRectItem.mouseReleaseEvent(self, event) self.parentItem().updateWidget() ############################################################################## def controlLinePen(): """Get pen for lines around shapes.""" return qt.QPen(setting.settingdb.color('cntrlline'), 2, qt.Qt.DotLine) class _EdgeLine(qt.QGraphicsLineItem, _ScaledShape): """Line used for edges of resizing box.""" def __init__(self, parent, params, ismovable=True): qt.QGraphicsLineItem.__init__(self, parent) self.setPen(controlLinePen()) self.setZValue(2.) self.params = params if ismovable: self.setFlag(qt.QGraphicsItem.ItemIsMovable) self.setCursor(qt.Qt.SizeAllCursor) def mouseMoveEvent(self, event): """Notify parent on move.""" qt.QGraphicsLineItem.mouseMoveEvent(self, event) self.parentItem().updateFromLine(self, self.scaledPos()) def mouseReleaseEvent(self, event): """Notify parent on unclicking.""" qt.QGraphicsLineItem.mouseReleaseEvent(self, event) self.parentItem().updateWidget() ############################################################################## class ControlMarginBox(object): def __init__(self, widget, posn, maxposn, painthelper, ismovable = True, isresizable = True): """Create control box item. widget: widget this is controllng posn: coordinates of box [x1, y1, x2, y2] maxposn: coordinates of biggest possibe box painthelper: painterhelper to get scaling from ismovable: box can be moved isresizable: box can be resized """ # save values self.posn = posn self.maxposn = maxposn self.widget = widget self.ismovable = ismovable self.isresizable = isresizable # we need these later to convert back to original units self.document = painthelper.document self.pagesize = painthelper.pagesize self.cgscale = painthelper.cgscale self.dpi = painthelper.dpi def createGraphicsItem(self, parent): return _GraphMarginBox(parent, self) def setWidgetMargins(self): """A helpful routine for setting widget margins after moving or resizing. This is called by the widget after receiving updateControlItem """ s = self.widget.settings # get margins in pixels left = self.posn[0] - self.maxposn[0] right = self.maxposn[2] - self.posn[2] top = self.posn[1] - self.maxposn[1] bottom = self.maxposn[3] - self.posn[3] # set up fake painthelper containing veusz scalings helper = document.PaintHelper( self.document, self.pagesize, scaling=self.cgscale, dpi=self.dpi) # convert to physical units left = s.get('leftMargin').convertInverse(left, helper) right = s.get('rightMargin').convertInverse(right, helper) top = s.get('topMargin').convertInverse(top, helper) bottom = s.get('bottomMargin').convertInverse(bottom, helper) # modify widget margins operations = ( document.OperationSettingSet(s.get('leftMargin'), left), document.OperationSettingSet(s.get('rightMargin'), right), document.OperationSettingSet(s.get('topMargin'), top), document.OperationSettingSet(s.get('bottomMargin'), bottom) ) self.widget.document.applyOperation( document.OperationMultiple(operations, descr=_('resize margins'))) def setPageSize(self): """Helper for setting document/page widget size. This is called by the widget after receiving updateControlItem """ s = self.widget.settings # get margins in pixels width = self.posn[2] - self.posn[0] height = self.posn[3] - self.posn[1] # set up fake painter containing veusz scalings helper = document.PaintHelper( self.document, self.pagesize, scaling=self.cgscale, dpi=self.dpi) # convert to physical units width = s.get('width').convertInverse(width, helper) height = s.get('height').convertInverse(height, helper) # modify widget margins operations = ( document.OperationSettingSet(s.get('width'), width), document.OperationSettingSet(s.get('height'), height), ) self.widget.document.applyOperation( document.OperationMultiple(operations, descr=_('change page size'))) class _GraphMarginBox(qt.QGraphicsItem): """A box which can be moved or resized. Can automatically set margins or widget """ # posn coords of each corner mapcornertoposn = ( (0, 1), (2, 1), (0, 3), (2, 3) ) def __init__(self, parent, params): """Create control box item.""" qt.QGraphicsItem.__init__(self, parent) self.params = params self.setZValue(2.) # create corners of box self.corners = [_ShapeCorner(self, params) for i in crange(4)] # lines connecting corners self.lines = [ _EdgeLine(self, params, ismovable=params.ismovable) for i in crange(4)] # hide corners if box is not resizable if not params.isresizable: for c in self.corners: c.hide() self.updateCornerPosns() def updateCornerPosns(self): """Update all corners from updated box.""" par = self.params pos = par.posn # update cursors self.corners[0].setCursor(qt.Qt.SizeFDiagCursor) self.corners[1].setCursor(qt.Qt.SizeBDiagCursor) self.corners[2].setCursor(qt.Qt.SizeBDiagCursor) self.corners[3].setCursor(qt.Qt.SizeFDiagCursor) # trim box to maximum size pos[0] = max(pos[0], par.maxposn[0]) pos[1] = max(pos[1], par.maxposn[1]) pos[2] = min(pos[2], par.maxposn[2]) pos[3] = min(pos[3], par.maxposn[3]) # move corners for corner, (xindex, yindex) in czip(self.corners, self.mapcornertoposn): corner.setScaledPos(pos[xindex], pos[yindex]) # move lines w, h = pos[2]-pos[0], pos[3]-pos[1] self.lines[0].setScaledLinePos(pos[0], pos[1], pos[0]+w, pos[1]) self.lines[1].setScaledLinePos(pos[2], pos[1], pos[2], pos[1]+h) self.lines[2].setScaledLinePos(pos[2], pos[3], pos[2]-w, pos[3]) self.lines[3].setScaledLinePos(pos[0], pos[3], pos[0], pos[3]-h) def updateFromLine(self, line, thispos): """Edge line of box was moved - update bounding box.""" par = self.params # need old coordinate to work out how far line has moved try: li = self.lines.index(line) except ValueError: return ox = par.posn[ (0, 2, 2, 0)[li] ] oy = par.posn[ (1, 1, 3, 3)[li] ] # add on deltas to box coordinates dx, dy = thispos.x()-ox, thispos.y()-oy # make sure box can't be moved outside the allowed region if dx > 0: dx = min(dx, par.maxposn[2]-par.posn[2]) else: dx = -min(abs(dx), abs(par.maxposn[0]-par.posn[0])) if dy > 0: dy = min(dy, par.maxposn[3]-par.posn[3]) else: dy = -min(abs(dy), abs(par.maxposn[1]-par.posn[1])) # move the box par.posn[0] += dx par.posn[1] += dy par.posn[2] += dx par.posn[3] += dy # update corner coords and other line coordinates self.updateCornerPosns() def updateFromCorner(self, corner, event): """Move corner of box to new position.""" try: index = self.corners.index(corner) except ValueError: return pos = self.params.posn pos[ self.mapcornertoposn[index][0] ] = corner.scaledX() pos[ self.mapcornertoposn[index][1] ] = corner.scaledY() # this is needed if the corners move past each other if pos[0] > pos[2]: # swap x pos[0], pos[2] = pos[2], pos[0] self.corners[0], self.corners[1] = self.corners[1], self.corners[0] self.corners[2], self.corners[3] = self.corners[3], self.corners[2] if pos[1] > pos[3]: # swap y pos[1], pos[3] = pos[3], pos[1] self.corners[0], self.corners[2] = self.corners[2], self.corners[0] self.corners[1], self.corners[3] = self.corners[3], self.corners[1] self.updateCornerPosns() def boundingRect(self): return qt.QRectF(0, 0, 0, 0) def paint(self, painter, option, widget): pass def updateWidget(self): """Update widget margins.""" self.params.widget.updateControlItem(self.params) ############################################################################## class ControlResizableBox(object): """Control a resizable box. Item resizes centred around a position """ def __init__(self, widget, phelper, posn, dims, angle, allowrotate=False): """Initialise with widget and boxbounds shape. Rotation is allowed if allowrotate is set """ self.widget = widget self.posn = posn self.dims = dims self.angle = angle self.allowrotate = allowrotate self.cgscale = phelper.cgscale def createGraphicsItem(self, parent): return _GraphResizableBox(parent, self) class _GraphResizableBox(qt.QGraphicsItem): """Control a resizable box. Item resizes centred around a position """ def __init__(self, parent, params): """Initialise with widget and boxbounds shape. Rotation is allowed if allowrotate is set """ qt.QGraphicsItem.__init__(self, parent) self.params = params # create child graphicsitem for each corner self.corners = [_ShapeCorner(self, params) for i in crange(4)] self.corners[0].setCursor(qt.Qt.SizeFDiagCursor) self.corners[1].setCursor(qt.Qt.SizeBDiagCursor) self.corners[2].setCursor(qt.Qt.SizeBDiagCursor) self.corners[3].setCursor(qt.Qt.SizeFDiagCursor) for c in self.corners: c.setToolTip(_('Hold shift to resize symmetrically')) # lines connecting corners self.lines = [ _EdgeLine(self, params, ismovable=True) for i in crange(4)] # whether box is allowed to be rotated self.rotator = None if params.allowrotate: self.rotator = _ShapeCorner(self, params, rotator=True) self.rotator.setCursor(qt.Qt.CrossCursor) self.updateCorners() def updateFromCorner(self, corner, event): """Take position and update corners.""" par = self.params x = corner.scaledX()-par.posn[0] y = corner.scaledY()-par.posn[1] if corner in self.corners: # rotate position back angle = -par.angle/180.*math.pi s, c = math.sin(angle), math.cos(angle) tx = x*c-y*s ty = x*s+y*c if event.modifiers() & qt.Qt.ShiftModifier: # expand around centre par.dims[0] = abs(tx*2) par.dims[1] = abs(ty*2) else: # moved distances of corner point mdx = par.dims[0]*0.5 - abs(tx) mdy = par.dims[1]*0.5 - abs(ty) # The direction to move the centre depends on which corner # it is. This makes the other side of the box stay in the # same place. signx = 1 if tx<0 else -1 signy = 1 if ty<0 else -1 # compute how much to move box centre by dx = 0.5*signx*mdx dy = 0.5*signy*mdy rdx = dx*c+dy*s # rotate forwards again rdy = -dx*s+dy*c par.posn[0] += rdx par.posn[1] += rdy par.dims[0] -= mdx par.dims[1] -= mdy elif corner is self.rotator: # work out angle relative to centre of widget angle = math.atan2(y, x) # change to degrees from correct direction par.angle = round((angle*(180/math.pi) + 90.) % 360, 2) self.updateCorners() def updateCorners(self): """Update corners on size.""" par = self.params # update corners angle = par.angle/180.*math.pi s, c = math.sin(angle), math.cos(angle) for corn, (xd, yd) in czip( self.corners, ((-1, -1), (1, -1), (-1, 1), (1, 1))): dx, dy = xd*par.dims[0]*0.5, yd*par.dims[1]*0.5 corn.setScaledPos( dx*c-dy*s + par.posn[0], dx*s+dy*c + par.posn[1]) if self.rotator: # set rotator position (constant distance) dx, dy = 0, -par.dims[1]*0.5 nx = dx*c-dy*s ny = dx*s+dy*c self.rotator.setScaledPos(nx+par.posn[0], ny+par.posn[1]) self.linepos = [] corn = self.corners for i, (ci1, ci2) in enumerate(((0, 1), (2, 0), (1, 3), (2, 3))): pos1 = corn[ci1].scaledX(), corn[ci1].scaledY() self.lines[i].setScaledLinePos( pos1[0], pos1[1], corn[ci2].scaledX(), corn[ci2].scaledY()) self.linepos.append(pos1) def updateFromLine(self, line, thispos): """Edge line of box was moved - update bounding box.""" # need old coordinate to work out how far line has moved oldpos = self.linepos[self.lines.index(line)] dx = line.scaledX() - oldpos[0] dy = line.scaledY() - oldpos[1] self.params.posn[0] += dx self.params.posn[1] += dy # update corner coords and other line coordinates self.updateCorners() def updateWidget(self): """Tell the user the graphicsitem has been moved or resized.""" self.params.widget.updateControlItem(self.params) def boundingRect(self): """Intentionally zero bounding rect.""" return qt.QRectF(0, 0, 0, 0) def paint(self, painter, option, widget): """Intentionally empty painter.""" ############################################################################## class ControlMovableBox(ControlMarginBox): """Item for user display for controlling widget. This is a dotted movable box with an optional "cross" where the real position of the widget is """ def __init__(self, widget, posn, painthelper, crosspos=None): ControlMarginBox.__init__(self, widget, posn, [-10000, -10000, 10000, 10000], painthelper, isresizable=False) self.deltacrosspos = (crosspos[0] - self.posn[0], crosspos[1] - self.posn[1]) def createGraphicsItem(self, parent): return _GraphMovableBox(parent, self) class _GraphMovableBox(_GraphMarginBox): def __init__(self, parent, params): _GraphMarginBox.__init__(self, parent, params) self.cross = _ShapeCorner(self, params) self.cross.setCursor(qt.Qt.SizeAllCursor) self.updateCornerPosns() def updateCornerPosns(self): _GraphMarginBox.updateCornerPosns(self) par = self.params if hasattr(self, 'cross'): # this fails if called before self.cross is initialised! self.cross.setScaledPos( par.deltacrosspos[0] + par.posn[0], par.deltacrosspos[1] + par.posn[1]) def updateFromCorner(self, corner, event): if corner == self.cross: # if cross moves, move whole box par = self.params cx, cy = self.cross.scaledX(), self.cross.scaledY() dx = cx - (par.deltacrosspos[0] + par.posn[0]) dy = cy - (par.deltacrosspos[1] + par.posn[1]) par.posn[0] += dx par.posn[1] += dy par.posn[2] += dx par.posn[3] += dy self.updateCornerPosns() else: _GraphMarginBox.updateFromCorner(self, corner, event) ############################################################################## class ControlLine(object): """For controlling the position and ends of a line.""" def __init__(self, widget, phelper, x1, y1, x2, y2): self.widget = widget self.line = x1, y1, x2, y2 self.cgscale = phelper.cgscale def createGraphicsItem(self, parent): return _GraphLine(parent, self) class _GraphLine(qt.QGraphicsLineItem, _ScaledShape): """Represents the line as a graphics item.""" def __init__(self, parent, params): qt.QGraphicsLineItem.__init__(self, parent) self.params = params l = self.params.line self.setScaledLine(l[0], l[1], l[2], l[3]) self.setCursor(qt.Qt.SizeAllCursor) self.setFlag(qt.QGraphicsItem.ItemIsMovable) self.setPen(controlLinePen()) self.setZValue(1.) self.p0 = _ShapeCorner(self, params, rotator=True) self.p0.setScaledPos(params.line[0], params.line[1]) self.p0.setCursor(qt.Qt.CrossCursor) self.p1 = _ShapeCorner(self, params, rotator=True) self.p1.setScaledPos(params.line[2], params.line[3]) self.p1.setCursor(qt.Qt.CrossCursor) def updateFromCorner(self, corner, event): """Take position and update ends of line.""" c = (self.p0.scaledX(), self.p0.scaledY(), self.p1.scaledX(), self.p1.scaledY()) self.setScaledLine(*c) def mouseReleaseEvent(self, event): """If widget has moved, tell it.""" qt.QGraphicsItem.mouseReleaseEvent(self, event) self.updateWidget() def updateWidget(self): """Update caller with position and line positions.""" x, y = self.scaledX(), self.scaledY() pt1 = self.p0.scaledX()+x, self.p0.scaledY()+y pt2 = self.p1.scaledX()+x, self.p1.scaledY()+y self.params.widget.updateControlItem(self.params, pt1, pt2) ############################################################################# class _AxisGraphicsLineItem(qt.QGraphicsLineItem, _ScaledShape): def __init__(self, parent, params): qt.QGraphicsLineItem.__init__(self, parent) self.parent = parent self.params = params self.setPen(controlLinePen()) self.setZValue(2.) self.setFlag(qt.QGraphicsItem.ItemIsMovable) def mouseReleaseEvent(self, event): """Notify finished.""" qt.QGraphicsLineItem.mouseReleaseEvent(self, event) self.parent.updateWidget() def mouseMoveEvent(self, event): """Move the axis.""" qt.QGraphicsLineItem.mouseMoveEvent(self, event) self.parent.doLineUpdate() class ControlAxisLine(object): """Controlling position of an axis.""" def __init__(self, widget, painthelper, direction, minpos, maxpos, axispos, maxposn): self.widget = widget self.direction = direction if minpos > maxpos: minpos, maxpos = maxpos, minpos self.minpos = self.minzoom = self.minorig = minpos self.maxpos = self.maxzoom = self.maxorig = maxpos self.axisorigpos = self.axispos = axispos self.maxposn = maxposn self.cgscale = painthelper.cgscale def zoomed(self): """Is this a zoom?""" return self.minzoom != self.minorig or self.maxzoom != self.maxorig def moved(self): """Has axis moved?""" return ( self.minpos != self.minorig or self.maxpos != self.maxorig or self.axisorigpos != self.axispos ) def createGraphicsItem(self, parent): return _GraphAxisLine(parent, self) class _GraphAxisLine(qt.QGraphicsItem): curs = {True: qt.Qt.SizeVerCursor, False: qt.Qt.SizeHorCursor} curs_zoom = {True: qt.Qt.SplitVCursor, False: qt.Qt.SplitHCursor} def __init__(self, parent, params): """Line is about to be shown.""" qt.QGraphicsItem.__init__(self, parent) self.params = params self.pts = [ _ShapeCorner(self, params), _ShapeCorner(self, params), _ShapeCorner(self, params), _ShapeCorner(self, params) ] self.line = _AxisGraphicsLineItem(self, params) # set cursors and tooltips for items self.horz = (params.direction == 'horizontal') for p in self.pts[0:2]: p.setCursor(self.curs[not self.horz]) p.setToolTip("Move axis ends") for p in self.pts[2:]: p.setCursor(self.curs_zoom[not self.horz]) p.setToolTip("Change axis scale") self.line.setCursor( self.curs[self.horz] ) self.line.setToolTip("Move axis position") self.setZValue(2.) self.updatePos() def updatePos(self): """Set ends of line and line positions from stored values.""" par = self.params scaling = par.cgscale mxp = par.maxposn def _clip(*args): """Clip positions to bounds of box given coords.""" par.minpos = max(par.minpos, mxp[args[0]]) par.maxpos = min(par.maxpos, mxp[args[1]]) par.axispos = max(par.axispos, mxp[args[2]]) par.axispos = min(par.axispos, mxp[args[3]]) # distance zoom boxes offset from axis offset = 15/scaling if self.horz: _clip(0, 2, 1, 3) # set positions if par.zoomed(): self.line.setScaledPos(par.minzoom, par.axispos) self.line.setScaledLine(0, 0, par.maxzoom-par.minzoom, 0) else: self.line.setScaledPos(par.minpos, par.axispos) self.line.setScaledLine(0, 0, par.maxpos-par.minpos, 0) self.pts[0].setScaledPos(par.minpos, par.axispos) self.pts[1].setScaledPos(par.maxpos, par.axispos) self.pts[2].setScaledPos(par.minzoom, par.axispos-offset) self.pts[3].setScaledPos(par.maxzoom, par.axispos-offset) else: _clip(1, 3, 0, 2) # set positions if par.zoomed(): self.line.setScaledPos(par.axispos, par.minzoom) self.line.setScaledLine(0, 0, 0, par.maxzoom-par.minzoom) else: self.line.setScaledPos(par.axispos, par.minpos) self.line.setScaledLine(0, 0, 0, par.maxpos-par.minpos) self.pts[0].setScaledPos(par.axispos, par.minpos) self.pts[1].setScaledPos(par.axispos, par.maxpos) self.pts[2].setScaledPos(par.axispos+offset, par.minzoom) self.pts[3].setScaledPos(par.axispos+offset, par.maxzoom) def updateFromCorner(self, corner, event): """Ends of axis have moved, so update values.""" par = self.params pt = (corner.scaledY(), corner.scaledX())[self.horz] # which end has moved? if corner is self.pts[0]: # horizonal or vertical axis? par.minpos = pt elif corner is self.pts[1]: par.maxpos = pt elif corner is self.pts[2]: par.minzoom = pt elif corner is self.pts[3]: par.maxzoom = pt # swap round end points if min > max if par.minpos > par.maxpos: par.minpos, par.maxpos = par.maxpos, par.minpos self.pts[0], self.pts[1] = self.pts[1], self.pts[0] self.updatePos() def doLineUpdate(self): """Line has moved, so update position.""" if self.horz: self.params.axispos = self.line.scaledY() else: self.params.axispos = self.line.scaledX() self.updatePos() def updateWidget(self): """Tell widget to update.""" self.params.widget.updateControlItem(self.params) def boundingRect(self): """Intentionally zero bounding rect.""" return qt.QRectF(0, 0, 0, 0) def paint(self, painter, option, widget): """Intentionally empty painter.""" veusz-3.0.1/veusz/widgets/line.py0000664000175000017500000002765713302252613016452 0ustar jssjss00000000000000# Copyright (C) 2008 Jeremy S. Sanders # Email: Jeremy Sanders # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ############################################################################### """Plotting a line with arrowheads or labels.""" from __future__ import division import math import itertools import numpy as N from ..compat import czip from .. import qtall as qt4 from .. import setting from .. import document from .. import utils from . import controlgraph from . import plotters def _(text, disambiguation=None, context='Line'): """Translate text.""" return qt4.QCoreApplication.translate(context, text, disambiguation) class Line(plotters.FreePlotter): """A line on the plot/graph.""" typename='line' description=_('Line or arrow') allowusercreation = True @staticmethod def showOrHideSetn(v): """Whether to show or hide length/angle or position.""" a = ('length', 'angle') b = ('xPos2', 'yPos2') return (a,b) if v=='length-angle' else (b,a) @classmethod def addSettings(klass, s): """Construct list of settings.""" plotters.FreePlotter.addSettings(s) s.add( setting.ChoiceSwitch( 'mode', ('length-angle', 'point-to-point'), 'length-angle', descr=_('Provide line position and length,angle or ' 'first and second points'), usertext=_('Mode'), formatting=False, showfn=klass.showOrHideSetn), 0) s.add( setting.DatasetExtended( 'length', [0.2], descr=_('List of fractional lengths, dataset or expression'), usertext=_('Lengths'), formatting=False), 4 ) s.add( setting.DatasetExtended( 'angle', [0.], descr=_('List of angle of lines, dataset or expression ' '(degrees)'), usertext=_('Angles'), formatting=False), 5 ) s.add( setting.DatasetExtended( 'xPos2', [1.], descr=_('List of fractional X coordinates, dataset or ' 'expression for point 2'), usertext=_('X positions 2'), formatting=False), 6 ) s.add( setting.DatasetExtended( 'yPos2', [1.], descr=_('List of fractional Y coordinates, dataset or ' 'expression for point 2'), usertext=_('Y positions 2'), formatting=False), 7 ) s.add( setting.Bool('clip', False, descr=_('Clip line to its container'), usertext=_('Clip'), formatting=True), 0 ) s.add( setting.Line('Line', descr = _('Line style'), usertext = _('Line')), pixmap = 'settings_plotline' ) s.add( setting.ArrowFill('Fill', descr = _('Arrow fill settings'), usertext = _('Arrow fill')), pixmap = 'settings_plotmarkerfill' ) s.add( setting.DistancePt('arrowSize', '5pt', descr = _('Size of arrow to plot'), usertext=_('Arrow size'), formatting=True), 0) s.add( setting.Arrow('arrowright', 'none', descr = _('Arrow to plot on right side'), usertext=_('Arrow right'), formatting=True), 0) s.add( setting.Arrow('arrowleft', 'none', descr = _('Arrow to plot on left side'), usertext=_('Arrow left'), formatting=True), 0) def _computeLinesLengthAngle(self, posn, lengthscaling): """Return set of lines to plot for length-angle.""" s = self.settings d = self.document # translate coordinates from axes or relative values xpos, ypos = self._getPlotterCoords(posn) # get lengths and angles of lines length = s.get('length').getFloatArray(d) angle = s.get('angle').getFloatArray(d) if xpos is None or ypos is None or length is None or angle is None: return None length *= lengthscaling maxlen = max( len(xpos), len(ypos), len(length), len(angle) ) if maxlen > 1: if len(xpos) == 1: xpos = itertools.cycle(xpos) if len(ypos) == 1: ypos = itertools.cycle(ypos) if len(length) == 1: length = itertools.cycle(length) if len(angle) == 1: angle = itertools.cycle(angle) out = [] for v in czip(xpos, ypos, length, angle): # skip lines which have nans if N.all( N.isfinite(v) ): out.append(v) return out def _computeLinesPointToPoint(self, posn): """Return set of lines for point to point.""" # translate coordinates from axes or relative values xpos, ypos = self._getPlotterCoords(posn) xpos2, ypos2 = self._getPlotterCoords(posn, xsetting='xPos2', ysetting='yPos2') if xpos is None or ypos is None or xpos2 is None or ypos2 is None: return None maxlen = max( len(xpos), len(ypos), len(xpos2), len(ypos2) ) if maxlen > 1: if len(xpos) == 1: xpos = itertools.cycle(xpos) if len(ypos) == 1: ypos = itertools.cycle(ypos) if len(xpos2) == 1: xpos2 = itertools.cycle(xpos2) if len(ypos2) == 1: ypos2 = itertools.cycle(ypos2) out = [] for v in czip(xpos, ypos, xpos2, ypos2): # skip nans again if N.all( N.isfinite(v) ): length = math.sqrt( (v[0]-v[2])**2 + (v[1]-v[3])**2 ) angle = math.atan2( v[3]-v[1], v[2]-v[0] ) / math.pi * 180. out.append( (v[0], v[1], length, angle) ) return out def draw(self, posn, phelper, outerbounds = None): """Plot the key on a plotter.""" s = self.settings d = self.document if s.hide: return # if a dataset is used, we can't use control items isnotdataset = ( not s.get('xPos').isDataset(d) and not s.get('yPos').isDataset(d) ) if s.mode == 'length-angle': isnotdataset = ( isnotdataset and not s.get('length').isDataset(d) and not s.get('angle').isDataset(d) ) else: isnotdataset = ( isnotdataset and not s.get('xPos2').isDataset(d) and not s.get('yPos2').isDataset(d) ) # now do the drawing clip = None if s.clip: clip = qt4.QRectF( qt4.QPointF(posn[0], posn[1]), qt4.QPointF(posn[2], posn[3]) ) painter = phelper.painter(self, posn, clip=clip) with painter: # adjustable positions for the lines arrowsize = s.get('arrowSize').convert(painter) # drawing settings for line if not s.Line.hide: painter.setPen( s.get('Line').makeQPen(painter) ) else: painter.setPen( qt4.QPen(qt4.Qt.NoPen) ) # settings for fill if not s.Fill.hide: painter.setBrush( s.get('Fill').makeQBrush(painter) ) else: painter.setBrush( qt4.QBrush() ) # iterate over positions scaling = posn[2]-posn[0] if s.mode == 'length-angle': lines = self._computeLinesLengthAngle(posn, scaling) else: lines = self._computeLinesPointToPoint(posn) if lines is None: return controlgraphitems = [] for index, (x, y, l, a) in enumerate(lines): utils.plotLineArrow(painter, x, y, l, a, arrowsize=arrowsize, arrowleft=s.arrowleft, arrowright=s.arrowright) if isnotdataset: cgi = controlgraph.ControlLine( self, phelper, x, y, x + l*math.cos(a/180.*math.pi), y + l*math.sin(a/180.*math.pi)) cgi.index = index cgi.widgetposn = posn controlgraphitems.append(cgi) phelper.setControlGraph(self, controlgraphitems) def updateControlItem(self, cgi, pt1, pt2): """If control items are moved, update line.""" s = self.settings # calculate new position coordinate for item xpos, ypos = self._getGraphCoords( cgi.widgetposn, [pt1[0], pt1[0]+1], [pt1[1], pt1[1]+1]) if xpos is None or ypos is None: return x, y = list(s.xPos), list(s.yPos) idx = min(cgi.index, len(x)-1) if not N.allclose(x[idx], xpos[0]): x[idx] = utils.round2delt(xpos[0], xpos[1]) idx = min(cgi.index, len(y)-1) if not N.allclose(y[idx], ypos[0]): y[idx] = utils.round2delt(ypos[0], ypos[1]) operations = [ document.OperationSettingSet(s.get('xPos'), x), document.OperationSettingSet(s.get('yPos'), y), ] if s.mode == 'length-angle': # convert 2nd point to length, angle def la(ptx, pty): length = ( math.sqrt( (ptx-pt1[0])**2 + (pty-pt1[1])**2 ) / (cgi.widgetposn[2]-cgi.widgetposn[0]) ) angle = ( (math.atan2( pty-pt1[1], ptx-pt1[0] ) * 180. / math.pi) % 360. ) return length, angle length, angle = la(pt2[0], pt2[1]) # calculate length angle for neighbouring point, to get delta ldelt, adelt = la(pt2[0]+1, pt2[1]+1) # update values l, a = list(s.length), list(s.angle) idx = min(cgi.index, len(l)-1) if abs(l[idx]-length) > 1e-8: l[idx] = utils.round2delt(length, ldelt) idx = min(cgi.index, len(a)-1) if abs(a[idx]-angle) > 1e-8: a[idx] = utils.round2delt(angle, adelt) operations += [ document.OperationSettingSet(s.get('length'), l), document.OperationSettingSet(s.get('angle'), a), ] else: xpos2, ypos2 = self._getGraphCoords( cgi.widgetposn, [pt2[0], pt2[0]+1], [pt2[1], pt2[1]+1]) if xpos2 is not None and ypos2 is not None: x2, y2 = list(s.xPos2), list(s.yPos2) idx = min(cgi.index, len(x2)-1) if not N.allclose(x2[idx], xpos2[0]): x2[idx] = utils.round2delt(xpos2[0], xpos2[1]) idx = min(cgi.index, len(y2)-1) if not N.allclose(y2[idx], ypos2[0]): y2[idx] = utils.round2delt(ypos2[0], ypos2[1]) operations += [ document.OperationSettingSet(s.get('xPos2'), x2), document.OperationSettingSet(s.get('yPos2'), y2) ] self.document.applyOperation( document.OperationMultiple(operations, descr=_('adjust lines')) ) document.thefactory.register( Line ) veusz-3.0.1/veusz/widgets/widget.py0000664000175000017500000002753013165473420017004 0ustar jssjss00000000000000# widget.py # fundamental graph plotting widget # Copyright (C) 2004 Jeremy S. Sanders # Email: Jeremy Sanders # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ############################################################################## from __future__ import division import itertools from ..compat import czip, crepr from .. import document from .. import setting from .. import qtall as qt4 def _(text, disambiguation=None, context='Widget'): """Translate text.""" return qt4.QCoreApplication.translate(context, text, disambiguation) class Action(object): """A class to wrap functions operating on widgets. Attributes: name: name of action function: function to call with no arguments descr: description of action usertext: name of action to display to user """ def __init__(self, name, function, descr='', usertext=''): """Initialise Action Name of action is name Calls function function() on invocation Action has description descr Usertext is short form of name to display to user.""" self.name = name self.function = function self.descr = descr self.usertext = usertext class Widget(object): """ Fundamental plotting widget interface.""" # differentiate widgets, settings and setting nodetype = 'widget' typename = 'generic' allowusercreation = False isaxis = False isplotter = False # various items in class hierarchy iswidget = True issetting = False issettings = False def __init__(self, parent, name=None): """Initialise a blank widget.""" # save parent widget for later self.parent = parent self.document = None if not self.isAllowedParent(parent): raise RuntimeError("Widget parent is of incorrect type") if name is None: name = self.chooseName() self.name = name # propagate document if parent is not None: self.document = parent.document parent.addChild(self) # store child widgets self.children = [] # settings for widget self.settings = setting.Settings( 'Widget_' + self.typename, setnsmode='widgetsettings') self.settings.parent = self self.addSettings(self.settings) # actions for widget self.actions = [] @classmethod def allowedParentTypes(klass): """Get types of widgets this can be a child of.""" return () @classmethod def addSettings(klass, s): """Add items to settings s.""" s.add( setting.Bool('hide', False, descr = _('Hide object'), usertext = _('Hide'), formatting = True) ) def getDocument(self): """Return document. Unfortunately we need this as document is shadowed in StyleSheet, sigh.""" return self.document def rename(self, name): """Change name of self.""" if self.parent is None: raise ValueError('Cannot rename root widget') if name.find('/') != -1: raise ValueError('Names cannot contain "/"') # check whether name already exists in siblings for i in self.parent.children: if i != self and i.name == name: raise ValueError('New name "%s" already exists' % name) self.name = name def addDefaultSubWidgets(self): '''Add default sub widgets to widget, if any''' pass def addAction(self, action): """Assign name to operation. action is action class above """ self.actions.append( action ) def getAction(self, name): """Get action associated with name.""" for a in self.actions: if a.name == name: return a return None def isAllowedParent(self, parent): """Is the parent a suitable type?""" return parent is None or any( ( isinstance(parent, t) for t in self.allowedParentTypes() ) ) @classmethod def willAllowParent(cls, parent): """Is the parent of an allowed type to have this type as a child?""" # allow base widget to have no parent ap = cls.allowedParentTypes() if parent is None and len(ap) > 0 and ap[0] is None: return True for p in ap: if isinstance(parent, p): return True return False def addChild(self, child, index=9999999): """Add child to list. index is a position to place the new child """ self.children.insert(index, child) def createUniqueName(self, prefix): """Create a name using the prefix which hasn't been used before.""" names = self.childnames i = 1 while "%s%i" % (prefix, i) in names: i += 1 return "%s%i" % (prefix, i) def chooseName(self): """Make a name for widget if not specified.""" if self.parent is None: return '/' else: return self.parent.createUniqueName(self.typename) @property def userdescription(self): """Return a user-friendly description of what this is (e.g. function).""" return '' def getChild(self, name): """Return a child with a name.""" #print('getChild', self, name) for i in self.children: if i.name == name: return i return None def hasChild(self, name): """Return whether there is a child with a name.""" return self.getChild(name) is not None @property def childnames(self): """Return the child names.""" return [i.name for i in self.children] def removeChild(self, name): """Remove a child.""" i = 0 nc = len(self.children) while i < nc and self.children[i].name != name: i += 1 if i < nc: self.children.pop(i) else: raise ValueError("Cannot remove graph '%s' - does not exist" % name) def widgetSiblingIndex(self): """Get index of widget in its siblings.""" if self.parent is None: return 0 else: return self.parent.children.index(self) @property def path(self): """Returns a path for the object, e.g. /plot1/x.""" obj = self build = '' while obj.parent is not None: build = '/' + obj.name + build obj = obj.parent if len(build) == 0: build = '/' return build def getMargins(self, painthelper): """Return margins of widget.""" return (0., 0., 0., 0.) def computeBounds(self, parentposn, painthelper, withmargin=True): """Compute a bounds array, giving the bounding box for the widget.""" if withmargin: x1, y1, x2, y2 = parentposn dx1, dy1, dx2, dy2 = self.getMargins(painthelper) return [ x1+dx1, y1+dy1, x2-dx2, y2-dy2 ] else: return parentposn def draw(self, parentposn, painthelper, outerbounds = None): """Draw the widget and its children in posn (a tuple with x1,y1,x2,y2). painter is the widget.Painter to draw on outerbounds contains "ultimate" bounds we don't go outside """ bounds = self.computeBounds(parentposn, painthelper) if not self.settings.hide: # iterate over children in reverse order for c in reversed(self.children): c.draw(bounds, painthelper, outerbounds=outerbounds) # return our final bounds return bounds def getSaveText(self, saveall = False): """Return text to restore object If saveall is true, save everything, including defaults.""" # set everything first text = self.settings.saveText(saveall) # now go throught the subwidgets for c in self.children: text += ( "Add('%s', name=%s, autoadd=False)\n" % (c.typename, crepr(c.name)) ) # if we need to go to the child, go there ctext = c.getSaveText(saveall) if ctext != '': text += ("To(%s)\n" "%s" "To('..')\n") % (crepr(c.name), ctext) return text def linkToStylesheet(self): """Links settings to stylesheet.""" self.settings.linkToStylesheet() def buildFlatWidgetList(self, thelist): """Return a built up list of the widgets in the tree.""" thelist.append(self) for child in self.children: child.buildFlatWidgetList(thelist) def _recursiveBuildSlots(self, slots): """Build up a flat representation of the places where widgets can be placed The list consists of (parent, index) tuples """ slots.append( (self, 0) ) for child, index in czip(self.children, itertools.count(1)): child._recursiveBuildSlots(slots) slots.append( (self, index) ) def moveChild(self, w, direction): """Move the child widget w up in the hierarchy in the direction. direction is -1 for 'up' or +1 for 'down' Returns True if succeeded """ # find position of child in self c = self.children oldindex = c.index(w) # remove the widget from its current location c.pop(oldindex) # build a list of places widgets can be placed (slots) slots = [] self.document.basewidget._recursiveBuildSlots(slots) # find self list - must be a better way to do this - # probably doesn't matter too much, however ourslot = (self, oldindex) ourindex = 0 while ourindex < len(slots) and slots[ourindex] != ourslot: ourindex += 1 # should never happen assert ourindex < len(slots) # move up or down the list until we find a suitable parent ourindex += direction while ( ourindex >= 0 and ourindex < len(slots) and not w.isAllowedParent(slots[ourindex][0]) ): ourindex += direction # we failed to find a new parent if ourindex < 0 or ourindex >= len(slots): c.insert(oldindex, w) return False else: newparent, newindex = slots[ourindex] existingname = w.name in newparent.childnames newparent.children.insert(newindex, w) w.parent = newparent # require a new name because of a clash if existingname: w.name = w.chooseName() return True def updateControlItem(self, controlitem, pos): """Update the widget's control point. controlitem is the control item in question.""" pass def autoColor(self, painter, dataindex=0): """Return automatic color for plotting.""" return 'foreground' def setupAutoColor(self, painter): """Initialise colors for widget automatically.""" self.autoColor(painter) # allow the factory to instantiate a generic widget document.thefactory.register( Widget ) veusz-3.0.1/veusz/widgets/axisfunction.py0000664000175000017500000004555213164704627020244 0ustar jssjss00000000000000# Copyright (C) 2013 Jeremy S. Sanders # Email: Jeremy Sanders # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ############################################################################## '''An axis based on a function of another axis.''' from __future__ import division import numpy as N from ..compat import crange, cstr from .. import qtall as qt4 from .. import setting from .. import document from . import axis def _(text, disambiguation=None, context='FunctionAxis'): '''Translate text.''' return qt4.QCoreApplication.translate(context, text, disambiguation) class AxisError(RuntimeError): pass class FunctionError(AxisError): pass def solveFunction(function, vals, mint=None, maxt=None): '''Solve a function for a list of values (vals), if we don't know where the solution lies. function is a function to call. This tries a range of possible input values, and uses binary search to refine the solution. mint and maxt are the bounds to use when solving ''' xvals = N.array( ( -1e90, -1e70, -1e50, -1e40, -1e30, -1e20, -1e10, -1e8, -1e6, -1e5, -1e4, -1e3, -1e2, -1e1, -4e0, -2e0, -1e0, -1e-1, -1e-2, -1e-3, -1e-4, -1e-6, -1e-8, -1e-10, -1e-12, -1e-14, -1e-18, -1e-22, -1e-26, -1e-30, -1e-34, -1e-40, -1e-50, -1e-70, -1e-90, 0, 1e-90, 1e-70, 1e-50, 1e-40, 1e-34, 1e-30, 1e-26, 1e-22, 1e-18, 1e-14, 1e-12, 1e-10, 1e-8, 1e-6, 1e-4, 1e-3, 1e-2, 1e-1, 1e0, 2e0, 4e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e8, 1e10, 1e20, 1e30, 1e40, 1e50, 1e70, 1e90 )) if mint is not None: xvals = N.hstack(( mint, xvals[xvals > mint] )) if maxt is not None: xvals = N.hstack(( xvals[xvals < maxt], maxt )) # yvalue in correct shape try: yvals = function(xvals) + N.zeros(len(xvals)) except Exception as e: raise FunctionError(_('Error evaluating function: %s') % cstr(e)) anynan = N.any( N.isnan(yvals) ) if anynan: raise FunctionError(_('Invalid regions in function ' '(try setting minimum or maximum t)')) # remove any infinite regions f = N.isfinite(yvals) xfilt = xvals[f] yfilt = yvals[f] if len(yfilt) < 2: raise FunctionError(_('Solutions to equation cannot be found')) # check for monotonicity delta = yfilt[1:] - yfilt[:-1] pos, neg = N.all(delta >= 0), N.all(delta <= 0) if not (pos or neg): raise FunctionError(_('Not a monotonic function ' '(try setting minimum or maximum t)')) if pos and neg: raise FunctionError(_('Constant function')) # easier if the values are increasing only if neg: yfilt = yfilt[::-1] xfilt = xfilt[::-1] # do binary search for each input value out = [] for thisval in vals: # renorm to zero ydelta = yfilt - thisval # solution is between this and the next idx = N.searchsorted(ydelta, 0.) if idx == 0: if ydelta[0] == 0.: # work around value being at start of array idx = 1 else: raise AxisError(_('No solution found')) elif idx == len(ydelta): raise AxisError(_('No solution found')) x1, x2 = xfilt[idx-1], xfilt[idx] y1, y2 = ydelta[idx-1], ydelta[idx] # binary search tol = abs(1e-6 * thisval) for i in crange(30): # print x1, y1, "->", x2, y2 if abs(y1) <= tol and abs(y1) < abs(y2): x2, y2 = x1, y1 break # found solution if abs(y2) <= tol: x1, y1 = x2, y2 break # found solution if y1 == y2 or ((y1<0) and (y2<0)) or ((y1>0) and (y2>0)): raise AxisError(_('No solution found')) ### This is a bit faster, but bisection is simpler # xv = N.linspace(x1, x2, num=100) # yv = function(xv) + xv*0. - thisval # idx = N.searchsorted(yv, 0.) # if idx == 0: # idx = 1 # x1 = xv[idx-1] # y1 = yv[idx-1] # x2 = xv[idx] # y2 = yv[idx] x3 = 0.5*(x1+x2) y3 = function(x3) - thisval if not N.isfinite(y3): raise AxisError(_('Non-finite value encountered')) if y3 < 0: x1 = x3 y1 = y3 else: x2 = x3 y2 = y3 out.append(0.5*(x1+x2)) return out class AxisFunction(axis.Axis): '''An axis using an function of another axis.''' typename = 'axis-function' description = 'An axis based on a function of the values of another axis' def __init__(self, *args, **argsv): axis.Axis.__init__(self, *args, **argsv) self.cachedfuncobj = None self.cachedbounds = None self.funcchangeset = -1 self.boundschangeset = -1 @classmethod def addSettings(klass, s): '''Construct list of settings.''' axis.Axis.addSettings(s) s.add( setting.BoolSwitch( 'linked', False, settingsfalse=('min', 'max'), settingstrue=('linkedaxis',), descr=_('Link axis to another axis'), usertext=_('Linked') ), 0 ) s.add( setting.Str('function', 't', descr=_('Monotonic function (use t as variable)'), usertext=_('Function')), 1 ) s.add( setting.Axis('linkedaxis', '', 'both', descr = _('Axis which this axis is based on'), usertext=_('Linked axis')), 6 ) s.add( setting.FloatOrAuto('mint', 'Auto', descr=_('Minimum value of t or Auto'), usertext=('Min t')), 7 ) s.add( setting.FloatOrAuto('maxt', 'Auto', descr=_('Maximum value of t or Auto'), usertext=('Max t')), 8 ) s.get('autoRange').hidden = True s.get('autoRange').newDefault('exact') @property def userdescription(self): """User friendly description.""" s = self.settings return _("axis='%s', function='%s'") % (s.linkedaxis, s.function) def logError(self, ex): '''Write error message to document log for exception ex.''' self.document.log( _("Error in axis-function (%s): '%s'") % ( self.settings.function, cstr(ex))) def getMinMaxT(self): '''Get minimum and maximum t.''' mint = self.settings.mint if mint == 'Auto': mint = None maxt = self.settings.maxt if maxt == 'Auto': maxt = None return mint, maxt def getFunction(self): '''Check whether function needs to be compiled.''' if self.funcchangeset == self.document.changeset: return self.cachedfuncobj self.funcchangeset = self.document.changeset compiled = self.document.evaluate.compileCheckedExpression( self.settings.function.strip()) if compiled is None: self.cachedfuncobj = None else: # a python function for doing the evaluation and handling # errors env = self.document.evaluate.context.copy() def function(t): env['t'] = t try: return eval(compiled, env) except Exception as e: self.logError(e) return N.nan + t self.cachedfuncobj = function mint, maxt = self.getMinMaxT() try: solveFunction(function, [0.], mint=mint, maxt=maxt) except FunctionError as e: self.logError(e) self.cachedfuncobj = None except AxisError: pass return self.cachedfuncobj def invertFunctionVals(self, vals): '''Convert values which are a function of fn and compute t.''' fn = self.getFunction() if fn is None: return None mint, maxt = self.getMinMaxT() try: return solveFunction(fn, vals, mint=mint, maxt=maxt) except Exception as e: self.logError(e) return None def lookupAxis(self, axisname): '''Find widget associated with axisname.''' w = self.parent while w: for c in w.children: if ( c.name == axisname and c.isaxis and c is not self ): return c w = w.parent return None def isLinked(self): '''Is this axis linked to another?''' return self.settings.linked def getLinkedAxis(self): '''Get the widget for the linked axis.''' if not self.settings.linked: return None linked = self.lookupAxis(self.settings.linkedaxis) if linked is self: return None return linked def computePlottedRange(self, force=False): '''Use other axis to compute range.''' if self.docchangeset == self.document.changeset and not force: return therange = None linked = self.getLinkedAxis() fn = self.getFunction() if linked is not None and fn is not None: # compute our range from the linked axis linked.computePlottedRange() try: therange = fn(N.array(linked.plottedrange)) * N.ones(2) except Exception as e: self.logError(e) if not N.all( N.isfinite(therange) ): therange = None axis.Axis.computePlottedRange(self, force=force, overriderange=therange) def _orderCoordinates(self): '''Put coordinates in correct order for linear interpolation.''' if len(self.graphcoords) == 0: self.graphcoords = None return if self.graphcoords[0] > self.graphcoords[-1]: # order must be increasing (for forward conversion) self.graphcoords = self.graphcoords[::-1] self.pixcoords = self.pixcoords[::-1] if self.pixcoords_inv[0] > self.pixcoords_inv[-1]: # likewise increasing order for inverse self.pixcoords_inv = self.pixcoords_inv[::-1] self.graphcoords_inv = self.graphcoords_inv[::-1] def _updateLinkedAxis(self, bounds, fraccoords): '''Calculate coordinate conversion for linked axes.''' link = self.getLinkedAxis() if link is None: return # To do the inverse calculation, we define a grid of pixel # values. We need some sensitivity outside the axis range to # get angles of lines correct. We start with fractional graph # coordinates to translate to the other axis coordinates. # coordinate values on the other axis try: linkwidth = link.coordParr2-link.coordParr1 linkorigin = link.coordParr1 linkbounds = link.currentbounds except AttributeError: # if hasn't been initialised return # lookup what pixels are on linked axis in values linkpixcoords = fraccoords*linkwidth + linkorigin linkgraphcoords = link.plotterToGraphCoords(linkbounds, linkpixcoords) # flip round if coordinates reversed if linkgraphcoords[0] > linkgraphcoords[-1]: linkgraphcoords = linkgraphcoords[::-1] linkpixcoords = linkpixcoords[::-1] fraccoords = fraccoords[::-1] # Chop to range. This is rather messy as there are several # sets of coordinates to extend and chop: graph coordinates, # pixel coordinates and fractional coordinates. mint, maxt = self.getMinMaxT() if mint is not None and mint > linkgraphcoords[0]: mintpix = link.graphToPlotterCoords(linkbounds, N.array([mint])) sel = linkgraphcoords > mint linkgraphcoords = N.hstack((mint, linkgraphcoords[sel])) linkpixcoords = N.hstack((mintpix, linkpixcoords[sel])) frac = (mintpix - linkorigin) / linkwidth fraccoords = N.hstack((frac, fraccoords[sel])) if maxt is not None and maxt < linkgraphcoords[-1]: maxtpix = link.graphToPlotterCoords(linkbounds, N.array([maxt])) sel = linkgraphcoords < maxt linkgraphcoords = N.hstack((linkgraphcoords[sel], maxt)) linkpixcoords = N.hstack((linkpixcoords[sel], maxtpix)) frac = (maxtpix - linkorigin) / linkwidth fraccoords = N.hstack((fraccoords[sel], frac)) try: ourgraphcoords = self.getFunction()(linkgraphcoords) except: return deltas = ourgraphcoords[1:] - ourgraphcoords[:-1] pos = N.all(deltas >= 0.) neg = N.all(deltas <= 0.) if (not pos and not neg) or (pos and neg): self.logError(_('Not a monotonic function')) return # Select only finite vals. We store _inv coords separately # as linear interpolation requires increasing values. f = N.isfinite(linkgraphcoords + ourgraphcoords) self.graphcoords = self.graphcoords_inv = ourgraphcoords[f] # This is true if the axis is plotting on the same graph in # the same direction. If this is the case, use our coordinates # directly. if ( link.settings.direction == self.settings.direction and link.currentbounds == bounds ): self.pixcoords = self.pixcoords_inv = linkpixcoords[f] else: # convert fractions to our coordinates self.pixcoords = self.pixcoords_inv = ( fraccoords[f]*(self.coordParr2-self.coordParr1)+self.coordParr1) # put output coordinates in correct order self._orderCoordinates() def _updateFreeAxis(self, bounds, fraccoords): '''Calculate coordinates for a free axis.''' self.computePlottedRange() trange = self.invertFunctionVals(N.array(self.plottedrange)) if trange is None: return tvals = fraccoords*(trange[1]-trange[0]) + trange[0] if tvals[0] > tvals[-1]: # simplifies below if t is in order tvals = tvals[::-1] fraccoords = fraccoords[::-1] # limit t to the range if given mint, maxt = self.getMinMaxT() if mint is not None and mint > tvals[0]: sel = tvals > mint minfrac = (mint - trange[0]) / (trange[1]-trange[0]) fraccoords = N.hstack( (minfrac, fraccoords[sel]) ) tvals = N.hstack( (mint, tvals[sel]) ) if maxt is not None and maxt < tvals[-1]: sel = tvals < maxt maxfrac = (maxt - trange[0]) / (trange[1]-trange[0]) fraccoords = N.hstack( (fraccoords[sel], maxfrac) ) tvals = N.hstack( (tvals[sel], maxt) ) try: ourgraphcoords = self.getFunction()(tvals) except Exception: return deltas = ourgraphcoords[1:] - ourgraphcoords[:-1] pos = N.all(deltas >= 0.) neg = N.all(deltas <= 0.) if (not pos and not neg) or (pos and neg): self.logError(_('Not a monotonic function')) return # Select only finite vals. We store _inv coords separately # as linear interpolation requires increasing values. f = N.isfinite(ourgraphcoords) self.graphcoords = self.graphcoords_inv = ourgraphcoords[f] self.pixcoords = self.pixcoords_inv = ( fraccoords[f]*(self.coordParr2-self.coordParr1) + self.coordParr1 ) # put output coordinates in correct order self._orderCoordinates() def updateAxisLocation(self, bounds, otherposition=None, lowerupperposition=None): '''Calculate conversion from pixels to axis values.''' axis.Axis.updateAxisLocation(self, bounds, otherposition=otherposition, lowerupperposition=lowerupperposition) if ( self.boundschangeset == self.document.changeset and bounds == self.cachedbounds ): # don't recalculate unless document updated or bounds changes return self.cachedbounds = list(bounds) self.boundschangeset = self.document.changeset self.graphcoords = None # fractional coordinate grid to evaluate functions fraccoords = N.hstack(( N.linspace(-10., -2.0, 5, endpoint=False), N.linspace(-2.0, -0.2, 25, endpoint=False), N.linspace(-0.2, +1.2, 250, endpoint=False), N.linspace(+1.2, +3.0, 25, endpoint=False), N.linspace(+3.0, +11., 5, endpoint=False) )) if self.isLinked(): self._updateLinkedAxis(bounds, fraccoords) else: self._updateFreeAxis(bounds, fraccoords) def _linearInterpolWarning(self, vals, xcoords, ycoords): '''Linear interpolation, giving out of bounds warning.''' if N.any(vals < xcoords[0]) or N.any(vals > xcoords[-1]): self.document.log( _('Warning: values exceed bounds in axis-function')) return N.interp(vals, xcoords, ycoords) def _graphToPlotter(self, vals): '''Override normal axis graph->plotter coords to do lookup.''' if self.graphcoords is None: return axis.Axis._graphToPlotter(self, vals) else: return self._linearInterpolWarning( vals, self.graphcoords, self.pixcoords) def plotterToGraphCoords(self, bounds, vals): '''Override normal axis plotter->graph coords to do lookup.''' if self.graphcoords is None: return axis.Axis.plotterToGraphCoords(self, bounds, vals) else: self.updateAxisLocation(bounds) return self._linearInterpolWarning( vals, self.pixcoords_inv, self.graphcoords_inv) # allow the factory to instantiate the widget document.thefactory.register( AxisFunction ) veusz-3.0.1/veusz/widgets/ternary.py0000664000175000017500000005013013164704737017204 0ustar jssjss00000000000000# -*- coding: utf-8 -*- # Copyright (C) 2011 Jeremy S. Sanders # Email: Jeremy Sanders # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ############################################################################## """Ternary plot widget.""" from __future__ import division import numpy as N import math from ..compat import czip from .nonorthgraph import NonOrthGraph from .axisticks import AxisTicks from .axis import MajorTick, MinorTick, GridLine, MinorGridLine, AxisLabel, \ TickLabel from .. import qtall as qt4 from .. import document from .. import setting from .. import utils def _(text, disambiguation=None, context='Ternary'): """Translate text.""" return qt4.QCoreApplication.translate(context, text, disambiguation) def rotatePts(x, y, theta): '''Rotate points by theta degrees.''' s = math.sin(theta*math.pi/180.) c = math.cos(theta*math.pi/180.) return x*c-y*s, x*s+y*c # translate coordinates a,b,c from user to plot # user can select different coordinate systems coord_lookup = { 'bottom-left': (0, 1, 2), 'bottom-right': (0, 2, 1), 'left-bottom': (1, 0, 2), 'left-right': (2, 0, 1), 'right-bottom': (1, 2, 0), 'right-left': (2, 1, 0) } # useful trigonometric identities sin30 = 0.5 sin60 = cos30 = 0.86602540378 tan30 = 0.5773502691 class Ternary(NonOrthGraph): '''Ternary plotter.''' typename='ternary' allowusercreation = True description = _('Ternary graph') @classmethod def addSettings(klass, s): '''Construct list of settings.''' NonOrthGraph.addSettings(s) s.add( setting.Choice('mode', ('percentage', 'fraction'), 'percentage', descr=_('Show percentages or fractions'), usertext=_('Mode')) ) s.add( setting.Choice('coords', ('bottom-left', 'bottom-right', 'left-bottom', 'left-right', 'right-left', 'right-bottom'), 'bottom-left', descr=_('Axes to use for plotting coordinates'), usertext=_('Coord system')) ) s.add( setting.Str('labelbottom', '', descr=_('Bottom axis label text'), usertext=_('Label bottom')) ) s.add( setting.Str('labelleft', '', descr=_('Left axis label text'), usertext=_('Label left')) ) s.add( setting.Str('labelright', '', descr=_('Right axis label text'), usertext=_('Label right')) ) s.add( setting.Float('originleft', 0., descr=_('Fractional origin of left axis at its top'), usertext=_('Left origin')) ) s.add( setting.Float('originbottom', 0., descr=_('Fractional origin of bottom axis at its left'), usertext=_('Bottom origin')) ) s.add( setting.Float('fracsize', 1., descr=_('Fractional size of plot'), usertext=_('Size')) ) s.add( setting.Bool('reverse', False, descr=_('Reverse axes'), usertext=_('Reverse')) ) s.add( AxisLabel('Label', descr = _('Axis label settings'), usertext = _('Axis label')), pixmap='settings_axislabel' ) s.add( TickLabel('TickLabels', descr = _('Tick label settings'), usertext = _('Tick labels')), pixmap='settings_axisticklabels' ) s.add( MajorTick('MajorTicks', descr = _('Major tick line settings'), usertext = _('Major ticks')), pixmap='settings_axismajorticks' ) s.add( MinorTick('MinorTicks', descr = _('Minor tick line settings'), usertext = _('Minor ticks')), pixmap='settings_axisminorticks' ) s.add( GridLine('GridLines', descr = _('Grid line settings'), usertext = _('Grid lines')), pixmap='settings_axisgridlines' ) s.add( MinorGridLine('MinorGridLines', descr = _('Minor grid line settings'), usertext = _('Grid lines for minor ticks')), pixmap='settings_axisminorgridlines' ) s.get('leftMargin').newDefault('1cm') s.get('rightMargin').newDefault('1cm') s.get('topMargin').newDefault('1cm') s.get('bottomMargin').newDefault('1cm') s.MajorTicks.get('number').newDefault(10) s.MinorTicks.get('number').newDefault(50) s.GridLines.get('hide').newDefault(False) s.TickLabels.remove('rotate') @property def userdescription(self): return _("mode='%s'") % self.settings.mode def _maxVal(self): '''Get maximum value on axis.''' if self.settings.mode == 'percentage': return 100. else: return 1. def coordRanges(self): '''Get ranges of coordinates.''' mv = self._maxVal() # ranges for each coordinate ra = [self._orgbot*mv, (self._orgbot+self._size)*mv] rb = [self._orgleft*mv, (self._orgleft+self._size)*mv] rc = [self._orgright*mv, (self._orgright+self._size)*mv] ranges = [ra, rb, rc] lookup = coord_lookup[self.settings.coords] return ranges[lookup.index(0)], ranges[lookup.index(1)] def graphToPlotCoords(self, coorda, coordb): '''Convert coordinates in r, theta to x, y.''' s = self.settings # normalize coordinates maxval = self._maxVal() coordan = coorda / maxval coordbn = coordb / maxval # the three coordinates on the plot clist = [coordan, coordbn, 1.-coordan-coordbn] # select the right coordinates for a, b and c given the system # requested by the user # normalise by origins and plot size lookup = coord_lookup[s.coords] cbot = ( clist[ lookup[0] ] - self._orgbot ) / self._size cleft = ( clist[ lookup[1] ] - self._orgleft ) / self._size cright = ( clist[ lookup[2] ] - self._orgright ) / self._size # from Ingram, 1984, Area, 16, 175 # remember that y goes in the opposite direction here if s.reverse: cleft, cright = cright, cleft x = (1-(0.5*cright + cbot))*self._width + self._box[0] else: x = (0.5*cright + cbot)*self._width + self._box[0] y = self._box[3] - cright * sin60 * self._width return x, y def drawFillPts(self, painter, brushext, cliprect, ptsx, ptsy): '''Draw points for plotting a fill.''' pts = qt4.QPolygonF() utils.addNumpyToPolygonF(pts, ptsx, ptsy) filltype = brushext.filltype # this is broken: FIXME if filltype == 'left': dyend = ptsy[-1]-self._box[1] pts.append( qt4.QPointF(ptsx[-1]-dyend*tan30, self._box[1]) ) dystart = ptsy[0]-self._box[1] pts.append( qt4.QPointF(ptsx[0]-dystart*tan30, self._box[1]) ) elif filltype == 'right': pts.append( qt4.QPointF(self._box[2], ptsy[-1]) ) pts.append( qt4.QPointF(self._box[2], ptsy[0]) ) elif filltype == 'bottom': dyend = self._box[3]-ptsy[-1] pts.append( qt4.QPointF(ptsx[-1]-dyend*tan30, self._box[3]) ) dystart = self._box[3]-ptsy[0] pts.append( qt4.QPointF(ptsx[0]-dystart*tan30, self._box[3]) ) elif filltype == 'polygon': pass else: pts = None if pts is not None: utils.brushExtFillPolygon(painter, brushext, cliprect, pts) def drawGraph(self, painter, bounds, datarange, outerbounds=None): '''Plot graph area and axes.''' s = self.settings xw, yw = bounds[2]-bounds[0], bounds[3]-bounds[1] d60 = 60./180.*math.pi ang = math.atan2(yw, xw/2.) if ang > d60: # taller than wider widthh = xw/2 height = math.tan(d60) * widthh else: # wider than taller height = yw widthh = height / math.tan(d60) # box for equilateral triangle self._box = ( (bounds[2]+bounds[0])/2 - widthh, (bounds[1]+bounds[3])/2 - height/2, (bounds[2]+bounds[0])/2 + widthh, (bounds[1]+bounds[3])/2 + height/2 ) self._width = widthh*2 self._height = height # triangle shaped polygon for graph self._tripoly = p = qt4.QPolygonF() p.append( qt4.QPointF(self._box[0], self._box[3]) ) p.append( qt4.QPointF(self._box[0]+widthh, self._box[1]) ) p.append( qt4.QPointF(self._box[2], self._box[3]) ) path = qt4.QPainterPath() path.addPolygon(p) path.closeSubpath() utils.brushExtFillPath(painter, s.Background, path, stroke=s.Border.makeQPenWHide(painter)) # work out origins and size self._size = max(min(s.fracsize, 1.), 0.) # make sure we don't go past the ends of the allowed range # value of origin of left axis at top self._orgleft = min(s.originleft, 1.-self._size) # value of origin of bottom axis at left self._orgbot = min(s.originbottom, 1.-self._size) # origin of right axis at bottom self._orgright = 1. - self._orgleft - (self._orgbot + self._size) def _computeTickVals(self): """Compute tick values.""" s = self.settings # this is a hack as we lose ends off the axis otherwise d = 1e-6 # get ticks along left axis atickleft = AxisTicks(self._orgleft-d, self._orgleft+self._size+d, s.MajorTicks.number, s.MinorTicks.number, extendmin=False, extendmax=False) atickleft.getTicks() # use the interval from above to calculate ticks for right atickright = AxisTicks(self._orgright-d, self._orgright+self._size+d, s.MajorTicks.number, s.MinorTicks.number, extendmin=False, extendmax=False, forceinterval = atickleft.interval) atickright.getTicks() # then calculate for bottom atickbot = AxisTicks(self._orgbot-d, self._orgbot+self._size+d, s.MajorTicks.number, s.MinorTicks.number, extendmin=False, extendmax=False, forceinterval = atickleft.interval) atickbot.getTicks() return atickbot, atickleft, atickright def setClip(self, painter, bounds): '''Set clipping for graph.''' p = qt4.QPainterPath() p.addPolygon( self._tripoly ) painter.setClipPath(p) def _getLabels(self, ticks, autoformat): """Return tick labels.""" labels = [] tl = self.settings.TickLabels format = tl.format scale = tl.scale if format.lower() == 'auto': format = autoformat for v in ticks: l = utils.formatNumber(v*scale, format, locale=self.document.locale) labels.append(l) if self.settings.reverse: labels = labels[::-1] return labels def _drawTickSet(self, painter, tickSetn, gridSetn, tickbot, tickleft, tickright, tickLabelSetn=None, labelSetn=None): '''Draw a set of ticks (major or minor). tickSetn: tick setting to get line details gridSetn: setting for grid line (if any) tickXXX: tick arrays for each axis tickLabelSetn: setting used to label ticks, or None if minor ticks labelSetn: setting for labels, if any ''' # this is mostly a lot of annoying trigonometry # compute line ends for ticks and grid lines tl = tickSetn.get('length').convert(painter) mv = self._maxVal() reverse = bool(self.settings.reverse) revsign = [1, -1][reverse] # bottom ticks x1 = (tickbot - self._orgbot)/self._size*self._width + self._box[0] y1 = self._box[3] + N.zeros(x1.shape) x2 = x1 - revsign * tl * sin30 y2 = y1 + tl * cos30 tickbotline = (x1, y1, x2, y2) # bottom grid (removing lines at edge of plot) scaletick = 1 - (tickbot-self._orgbot)/self._size gx = x1 + scaletick*self._width*sin30 gy = y1 - scaletick*self._width*cos30 ne = (scaletick > 1e-6) & (scaletick < (1-1e-6)) gridbotline = (x1[ne], y1[ne], gx[ne], gy[ne]) # left ticks x1 = -(tickleft - self._orgleft)/self._size*self._width*sin30 + ( self._box[0] + self._box[2])*0.5 y1 = (tickleft - self._orgleft)/self._size*self._width*cos30 + self._box[1] if reverse: x2 = x1 - tl y2 = y1 else: x2 = x1 - tl * sin30 y2 = y1 - tl * cos30 tickleftline = (x1, y1, x2, y2) # left grid scaletick = 1 - (tickleft-self._orgleft)/self._size gx = x1 + scaletick*self._width*sin30 gy = self._box[3] + N.zeros(y1.shape) ne = (scaletick > 1e-6) & (scaletick < (1-1e-6)) gridleftline = (x1[ne], y1[ne], gx[ne], gy[ne]) # right ticks x1 = -(tickright - self._orgright)/self._size*self._width*sin30+self._box[2] y1 = -(tickright - self._orgright)/self._size*self._width*cos30+self._box[3] if reverse: x2 = x1 + tl * sin30 y2 = y1 - tl * cos30 else: x2 = x1 + tl y2 = y1 tickrightline = (x1, y1, x2, y2) # right grid scaletick = 1 - (tickright-self._orgright)/self._size gx = x1 - scaletick*self._width gy = y1 gridrightline = (x1[ne], y1[ne], gx[ne], gy[ne]) if not gridSetn.hide: # draw the grid pen = gridSetn.makeQPen(painter) painter.setPen(pen) utils.plotLinesToPainter(painter, *gridbotline) utils.plotLinesToPainter(painter, *gridleftline) utils.plotLinesToPainter(painter, *gridrightline) # calculate deltas for ticks bdelta = ldelta = rdelta = 0 if not tickSetn.hide: # draw ticks themselves pen = tickSetn.makeQPen(painter) pen.setCapStyle(qt4.Qt.FlatCap) painter.setPen(pen) utils.plotLinesToPainter(painter, *tickbotline) utils.plotLinesToPainter(painter, *tickleftline) utils.plotLinesToPainter(painter, *tickrightline) ldelta += tl*sin30 bdelta += tl*cos30 rdelta += tl if tickLabelSetn is not None and not tickLabelSetn.hide: # compute the labels for the ticks tleftlabels = self._getLabels(tickleft*mv, '%Vg') trightlabels = self._getLabels(tickright*mv, '%Vg') tbotlabels = self._getLabels(tickbot*mv, '%Vg') painter.setPen( tickLabelSetn.makeQPen(painter) ) font = tickLabelSetn.makeQFont(painter) painter.setFont(font) fm = utils.FontMetrics(font, painter.device()) sp = fm.leading() + fm.descent() off = tickLabelSetn.get('offset').convert(painter) # draw tick labels in each direction hlabbot = wlableft = wlabright = 0 for l, x, y in czip(tbotlabels, tickbotline[2], tickbotline[3]+off): r = utils.Renderer(painter, font, x, y, l, 0, 1, 0, doc=self.document) bounds = r.render() hlabbot = max(hlabbot, bounds[3]-bounds[1]) for l, x, y in czip(tleftlabels, tickleftline[2]-off-sp, tickleftline[3]): r = utils.Renderer(painter, font, x, y, l, 1, 0, 0, doc=self.document) bounds = r.render() wlableft = max(wlableft, bounds[2]-bounds[0]) for l, x, y in czip(trightlabels,tickrightline[2]+off+sp, tickrightline[3]): r = utils.Renderer(painter, font, x, y, l, -1, 0, 0, doc=self.document) bounds = r.render() wlabright = max(wlabright, bounds[2]-bounds[0]) bdelta += hlabbot+off+sp ldelta += wlableft+off+sp rdelta += wlabright+off+sp if labelSetn is not None and not labelSetn.hide: # draw label on edges (if requested) painter.setPen( labelSetn.makeQPen(painter) ) font = labelSetn.makeQFont(painter) painter.setFont(font) fm = utils.FontMetrics(font, painter.device()) sp = fm.leading() + fm.descent() off = labelSetn.get('offset').convert(painter) # alignment align = {'at-minimum': -1, 'centre': 0, 'at-maximum': 1}[ labelSetn.position] aoffset = {'at-minimum': -self._width/2, 'centre': 0, 'at-maximum': self._width/2}[labelSetn.position] # bottom label r = utils.Renderer( painter, font, 0, 0, self.settings.labelbottom, align, 1, doc=self.document) painter.save() painter.translate(self._box[0]+self._width/2, self._box[3] + bdelta + off) painter.translate(aoffset, 0) r.render() painter.restore() # left label - rotate frame before drawing so we can get # the bounds correct r = utils.Renderer( painter, font, 0, -sp, self.settings.labelleft, -align, -1, doc=self.document) painter.save() painter.translate(self._box[0]+self._width*0.25, 0.5*(self._box[1]+self._box[3])) painter.rotate(-60) painter.translate(-aoffset, -ldelta - off) r.render() painter.restore() # right label r = utils.Renderer( painter, font, 0, -sp, self.settings.labelright, -align, -1, doc=self.document) painter.save() painter.translate(self._box[0]+self._width*0.75 , 0.5*(self._box[1]+self._box[3]) ) painter.rotate(60) painter.translate(-aoffset, -ldelta - off) r.render() painter.restore() def drawAxes(self, painter, bounds, datarange, outerbounds=None): '''Draw plot axes.''' s = self.settings # compute tick values for later when plotting axes tbot, tleft, tright = self._computeTickVals() # draw the major ticks self._drawTickSet(painter, s.MajorTicks, s.GridLines, tbot.tickvals, tleft.tickvals, tright.tickvals, tickLabelSetn=s.TickLabels, labelSetn=s.Label) # now draw the minor ones self._drawTickSet(painter, s.MinorTicks, s.MinorGridLines, tbot.minorticks, tleft.minorticks, tright.minorticks) document.thefactory.register(Ternary) veusz-3.0.1/veusz/widgets/key.py0000664000175000017500000004464113245323251016306 0ustar jssjss00000000000000# key symbol plotting # Copyright (C) 2005 Jeremy S. Sanders # Email: Jeremy Sanders # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ############################################################################### from __future__ import division import math from ..compat import crange, citems from .. import qtall as qt4 from .. import document from .. import setting from .. import utils from . import widget from . import controlgraph def _(text, disambiguation=None, context='Key'): """Translate text.""" return qt4.QCoreApplication.translate(context, text, disambiguation) ############################################################################# # classes for controlling key position interactively class ControlKey(object): """Control the position of a key on a plot.""" def __init__( self, widget, phelper, parentposn, boxposn, boxdims, textheight ): """widget is widget to adjust phelper: paint helper parentposn: posn of parent on plot xpos, ypos: position of key width, height: size of key textheight: """ self.widget = widget self.parentposn = tuple(parentposn) self.posn = tuple(boxposn) self.dims = tuple(boxdims) self.textheight = textheight self.cgscale = phelper.cgscale def createGraphicsItem(self, parent): return _GraphControlKey(parent, self) class _GraphControlKey(qt4.QGraphicsRectItem, controlgraph._ScaledShape): """The graphical rectangle which is dragged around to reposition the key.""" def __init__(self, parent, params): qt4.QGraphicsRectItem.__init__(self, parent) self.params = params self.setScaledRect( params.posn[0], params.posn[1], params.dims[0], params.dims[1]) self.setCursor(qt4.Qt.SizeAllCursor) self.setZValue(1.) self.setFlag(qt4.QGraphicsItem.ItemIsMovable) self.highlightpen = qt4.QPen(qt4.Qt.red, 2, qt4.Qt.DotLine) pposn, dims = params.parentposn, params.dims th = params.textheight # special places on the plot xposn = { 'left': pposn[0] + th, 'centre': pposn[0] + 0.5*(pposn[2]-pposn[0]-dims[0]), 'right': pposn[2] - th - dims[0] } yposn = { 'top': pposn[1] + th, 'centre': pposn[1] + 0.5*(pposn[3]-pposn[1]-dims[1]), 'bottom': pposn[3] - th - dims[1] } # these are special places where the key is aligned self.highlightpoints = {} for xname, xval in citems(xposn): for yname, yval in citems(yposn): self.highlightpoints[(xname, yname)] = qt4.QPointF( xval*params.cgscale, yval*params.cgscale) self.updatePen() def checkHighlight(self): """Check to see whether box is over hightlight area. Returns (x, y) name or None if not.""" rect = self.rect() rect.translate(self.pos()) highlight = None highlightrect = qt4.QRectF(rect.left()-10, rect.top()-10, 20, 20) for name, point in citems(self.highlightpoints): if highlightrect.contains(point): highlight = name break return highlight def updatePen(self): """Update color of rectangle if it is over a hightlight area.""" if self.checkHighlight(): self.setPen(self.highlightpen) else: self.setPen(controlgraph.controlLinePen()) def mouseMoveEvent(self, event): """Set correct pen for box.""" qt4.QGraphicsRectItem.mouseMoveEvent(self, event) self.updatePen() def mouseReleaseEvent(self, event): """Update widget with position.""" qt4.QGraphicsRectItem.mouseReleaseEvent(self, event) highlight = self.checkHighlight() if highlight: # in a highlight zone so use highlight zone name to set position hp, vp = highlight hm, vm = 0., 0. else: # calculate the position of the box to work out Manual fractions rect = self.scaledRect() rect.translate(self.scaledPos()) pposn = self.params.parentposn hp, vp = 'manual', 'manual' hm = (rect.left() - pposn[0]) / (pposn[2] - pposn[0]) vm = (pposn[3] - rect.bottom()) / (pposn[3] - pposn[1]) # update widget with positions s = self.params.widget.settings operations = ( document.OperationSettingSet(s.get('horzPosn'), hp), document.OperationSettingSet(s.get('vertPosn'), vp), document.OperationSettingSet(s.get('horzManual'), hm), document.OperationSettingSet(s.get('vertManual'), vm), ) self.params.widget.document.applyOperation( document.OperationMultiple(operations, descr=_('move key'))) ############################################################################ class Key(widget.Widget): """Key on graph.""" typename = 'key' description = _('Plot key') allowusercreation = True @classmethod def addSettings(klass, s): """Construct list of settings.""" widget.Widget.addSettings(s) s.add( setting.Text('Text', descr = _('Text settings'), usertext=_('Text')), pixmap = 'settings_axislabel' ) s.add( setting.KeyBrush('Background', descr = _('Key background fill'), usertext=_('Background')), pixmap = 'settings_bgfill' ) s.add( setting.Line('Border', descr = _('Key border line'), usertext=_('Border')), pixmap = 'settings_border' ) s.add( setting.Str('title', '', descr=_('Key title text'), usertext=_('Title')) ) s.add( setting.AlignHorzWManual( 'horzPosn', 'right', descr = _('Horizontal key position'), usertext=_('Horz posn'), formatting=True) ) s.add( setting.AlignVertWManual( 'vertPosn', 'bottom', descr = _('Vertical key position'), usertext=_('Vert posn'), formatting=True) ) s.add( setting.Distance('keyLength', '1cm', descr = _('Length of line to show in sample'), usertext=_('Key length'), formatting=True) ) s.add( setting.AlignVert( 'keyAlign', 'top', descr = _('Alignment of key symbols relative to text'), usertext = _('Key alignment'), formatting = True) ) s.add( setting.Float( 'horzManual', 0., descr = _('Manual horizontal fractional position'), usertext=_('Horz manual'), formatting=True) ) s.add( setting.Float( 'vertManual', 0., descr = _('Manual vertical fractional position'), usertext=_('Vert manual'), formatting=True) ) s.add( setting.Float( 'marginSize', 1., minval = 0., descr = _('Width of margin in characters'), usertext=_('Margin size'), formatting=True) ) s.add( setting.Int( 'columns', 1, descr = _('Number of columns in key'), usertext = _('Columns'), minval = 1, maxval = 100, formatting = True) ) s.add( setting.Bool( 'symbolswap', False, descr=_('Put key symbol on right and text on left'), usertext=_('Swap symbol'), formatting=True) ) @classmethod def allowedParentTypes(klass): from . import graph return (graph.Graph,) @staticmethod def _layoutChunk(entries, start, dims): """Layout the entries into the given box, starting at start""" row, col = start numrows, numcols = dims colstats = [0] * numcols layout = [] for (plotter, num, lines) in entries: if row+lines > numrows: # this item doesn't fit in this column, so move to the next col += 1 row = 0 if col >= numcols: # this layout failed, suggest expanding the box by 1 row return ([], [], numrows+1) if lines > numrows: # this layout failed, suggest expanding the box to |lines| return ([], [], lines) # col -> yp, row -> xp layout.append( (plotter, num, col, row, lines) ) row += lines colstats[col] += 1 return (layout, colstats, numrows) def _layout(self, entries, totallines): """Layout the items, trying to keep the box as small as possible while still filling the columns""" maxcols = self.settings.columns numcols = min(maxcols, max(len(entries), 1)) if not entries: return (list(), (0, 0)) # start with evenly-sized rows and expand to fit numrows = totallines // numcols layout = [] while not layout: # try to do a first cut of the layout, and expand the box until # everything fits (layout, colstats, newrows) = self._layoutChunk(entries, (0, 0), (numrows, numcols)) if not layout: numrows = newrows # ok, we've got a layout where everything fits, now pull items right # to fill the remaining columns, if need be while colstats[-1] == 0: # shift 1 item to the right, up to the first column that has # excess items meanoccupation = max(1, sum(colstats)/numcols) # loop until we find a victim item which can be safely moved victimcol = numcols while True: # find the right-most column with excess occupation number for i in reversed(crange(victimcol)): if colstats[i] > meanoccupation: victimcol = i break # find the last item in the victim column victim = 0 for i in reversed(crange(len(layout))): if layout[i][2] == victimcol: victim = i break # try to relayout with the victim item shoved to the next column (newlayout, newcolstats, newrows) = self._layoutChunk(entries[victim:], (0, victimcol+1), (numrows, numcols)) if newlayout: # the relayout worked, so accept it layout = layout[0:victim] + newlayout colstats[victimcol] -= 1 del colstats[victimcol+1:] colstats += newcolstats[victimcol+1:] break # if we've run out of potential victims, just return what we have if victimcol == 0: return (layout, (numrows, numcols)) return (layout, (numrows, numcols)) def draw(self, parentposn, phelper, outerbounds = None): """Plot the key on a plotter.""" s = self.settings if s.hide: return painter = phelper.painter(self, parentposn) with painter: self._doDrawing(painter, phelper, parentposn) def _doDrawing(self, painter, phelper, parentposn): """Do the actual drawing.""" s = self.settings font = s.get('Text').makeQFont(painter) painter.setFont(font) height = utils.FontMetrics(font, painter.device()).height() margin = s.marginSize * height showtext = not s.Text.hide # total number of layout lines required totallines = 0 # reserve space for the title titlewidth, titleheight = 0, 0 if s.title != '': titlefont = qt4.QFont(font) titlefont.setPointSize(max(font.pointSize() * 1.2, font.pointSize() + 2)) titlewidth, titleheight = utils.Renderer( painter, titlefont, 0, 0, s.title, doc=self.document).getDimensions() titleheight += 0.5*margin # maximum width of text required maxwidth = 1 entries = [] # iterate over children and find widgets which are suitable for c in self.parent.children: try: num = c.getNumberKeys() except AttributeError: continue if not c.settings.hide: # add an entry for each key entry for each widget for i in crange(num): lines = 1 if showtext: w, h = utils.Renderer( painter, font, 0, 0, c.getKeyText(i), doc=self.document).getDimensions() maxwidth = max(maxwidth, w) lines = max(1, math.ceil(h/height)) totallines += lines entries.append( (c, i, lines) ) # layout the box layout, (numrows, numcols) = self._layout(entries, totallines) # width of key part of key symbolwidth = s.get('keyLength').convert(painter) keyswidth = ( (maxwidth + height + symbolwidth)*numcols + height*(numcols-1) ) # total width of box totalwidth = max(keyswidth, titlewidth) totalheight = numrows * height + titleheight if not s.Border.hide: totalwidth += 2*margin totalheight += margin # work out horizontal position h = s.horzPosn if h == 'left': x = parentposn[0] + height elif h == 'right': x = parentposn[2] - height - totalwidth elif h == 'centre': x = ( parentposn[0] + 0.5*(parentposn[2] - parentposn[0] - totalwidth) ) elif h == 'manual': x = parentposn[0] + (parentposn[2]-parentposn[0])*s.horzManual # work out vertical position v = s.vertPosn if v == 'top': y = parentposn[1] + height elif v == 'bottom': y = parentposn[3] - totalheight - height elif v == 'centre': y = ( parentposn[1] + 0.5*(parentposn[3] - parentposn[1] - totalheight) ) elif v == 'manual': y = ( parentposn[3] - (parentposn[3]-parentposn[1])*s.vertManual - totalheight ) # for controlgraph boxposn = (x, y) boxdims = (totalwidth, totalheight) # draw surrounding box boxpath = qt4.QPainterPath() boxpath.addRect(qt4.QRectF(x, y, totalwidth, totalheight)) if not s.Background.hide: utils.brushExtFillPath(painter, s.Background, boxpath) if not s.Border.hide: painter.strokePath(boxpath, s.get('Border').makeQPen(painter) ) y += margin*0.5 # center and draw the title if s.title: xpos = x + (totalwidth-titlewidth)/2 utils.Renderer( painter, titlefont, xpos, y, s.title, alignvert=1, doc=self.document).render() y += titleheight # centres key below title x += (totalwidth-keyswidth)/2 textpen = s.get('Text').makeQPen(painter) swap = s.symbolswap # plot dataset entries for (plotter, num, xp, yp, lines) in layout: xpos = x + xp*(maxwidth+2*height+symbolwidth) ypos = y + yp*height # plot key symbol painter.save() keyoffset = 0 if s.keyAlign == 'centre': keyoffset = (lines-1)*height/2.0 elif s.keyAlign == 'bottom': keyoffset = (lines-1)*height sx = xpos if swap: sx += maxwidth + height plotter.drawKeySymbol(num, painter, sx, ypos+keyoffset, symbolwidth, height) painter.restore() # write key text if showtext: painter.setPen(textpen) if swap: lx = xpos + maxwidth alignx = 1 else: lx = xpos + height + symbolwidth alignx = -1 utils.Renderer( painter, font, lx, ypos, plotter.getKeyText(num), alignx, 1, doc=self.document).render() phelper.setControlGraph( self, [ControlKey(self, phelper, parentposn, boxposn, boxdims, height)] ) document.thefactory.register( Key ) veusz-3.0.1/veusz/widgets/polar.py0000664000175000017500000003610613262167355016642 0ustar jssjss00000000000000# -*- coding: utf-8 -*- # Copyright (C) 2010 Jeremy S. Sanders # Email: Jeremy Sanders # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ############################################################################## """Polar plot widget.""" from __future__ import division import numpy as N import fractions import math from .nonorthgraph import NonOrthGraph from .axisticks import AxisTicks from . import axis from ..compat import crange from .. import qtall as qt4 from .. import document from .. import setting from .. import utils def _(text, disambiguation=None, context='Polar'): """Translate text.""" return qt4.QCoreApplication.translate(context, text, disambiguation) def radianToAlign(a): # fraction of circle f = a / (2*math.pi) # slow, but ok here as we don't expect values much outside range while f < 0: f += 1 while f >= 1: f -= 1 if f == 0: return (-1, 1) elif f < 0.25: return (-1, 1) elif f == 0.25: return (0, 1) elif f < 0.5: return (1, 1) elif f == 0.5: return (1, 0) elif f < 0.75: return (1, -1) elif f == 0.75: return (0, -1) else: return (-1, -1) class OldLine(setting.Line): '''Polar tick settings.''' def __init__(self, name): setting.Line.__init__(self, name, setnsmode='hide') self.add( setting.SettingBackwardCompat( 'number', '../RadiiLine/number', None)) self.add( setting.SettingBackwardCompat( 'hidespokes', '../SpokeLine/length', None)) self.add( setting.SettingBackwardCompat( 'hideannuli', '../SpokeAnnuli/length', None)) class SpokeLine(setting.Line): '''Spokes in polar plot.''' def __init__(self, name, **args): setting.Line.__init__(self, name, **args) self.add( setting.Int( 'number', 12, minval=1, descr = _('Number of spokes to use'), usertext=_('Number')) ) self.get('color').newDefault('grey') class RadiiLine(setting.Line): '''Radii in polar plot.''' def __init__(self, name, **args): setting.Line.__init__(self, name, **args) self.add( setting.Int( 'number', 6, minval=1, descr = _('Number of radial ticks to aim for'), usertext=_('Number')) ) self.get('color').newDefault('grey') class TickLabel(axis.TickLabel): """For tick label.""" def __init__(self, *args, **argsv): axis.TickLabel.__init__(self, *args, **argsv) self.remove('offset') self.remove('rotate') self.remove('hide') self.add( setting.Bool( 'hideradial', False, descr = _('Hide radial labels'), usertext=_('Hide radial') ) ) self.add( setting.Bool( 'hidetangential', False, descr = _('Hide spoke labels'), usertext=_('Hide spokes') ) ) class Polar(NonOrthGraph): '''Polar plotter.''' typename='polar' allowusercreation = True description = _('Polar graph') @classmethod def addSettings(klass, s): '''Construct list of settings.''' NonOrthGraph.addSettings(s) s.add( setting.FloatOrAuto( 'minradius', 'Auto', descr=_('Minimum value of radius'), usertext=_('Min radius')) ) s.add( setting.FloatOrAuto( 'maxradius', 'Auto', descr=_('Maximum value of radius'), usertext=_('Max radius')) ) s.add( setting.Choice( 'units', ('degrees', 'radians', 'fractions', 'percentages'), 'degrees', descr = _('Angular units'), usertext=_('Units')) ) s.add( setting.Choice( 'direction', ('clockwise', 'anticlockwise'), 'anticlockwise', descr = _('Angle direction'), usertext = _('Direction')) ) s.add( setting.Choice( 'position0', ('right', 'top', 'left', 'bottom'), 'right', descr = _('Direction of 0 angle'), usertext = _(u'Position of 0°')) ) s.add( setting.Bool( 'log', False, descr = _('Logarithmic radial axis'), usertext = _('Log')) ) s.add( TickLabel( 'TickLabels', descr = _('Radial tick labels'), usertext=_('Radial tick labels')), pixmap='settings_axisticklabels' ) s.add( OldLine('Tick') ) s.add( SpokeLine( 'SpokeLine', descr = _('Spoke line'), usertext=_('Spoke line')), pixmap='settings_axismajorticks' ) s.add( RadiiLine( 'RadiiLine', descr = _('Radii line'), usertext=_('Radii line')), pixmap='settings_contourline' ) s.get('leftMargin').newDefault('1cm') s.get('rightMargin').newDefault('1cm') s.get('topMargin').newDefault('1cm') s.get('bottomMargin').newDefault('1cm') @property def userdescription(self): s = self.settings return _("'units=%s, direction=%s, log=%s") % ( s.units, s.direction, str(s.log)) def coordRanges(self): '''Get ranges of coordinates.''' angularrange = { 'degrees': [0., 360.], 'radians': [0., 2*math.pi], 'fractions': [0., 1.], 'percentages': [0., 100.], }[self.settings.units] return [ [self._minradius, self._maxradius], angularrange ] def toPlotAngle(self, angles): """Convert one or more angles to angle on plot.""" s = self.settings # unit conversions mult = { 'degrees': math.pi/180, 'radians': 1, 'fractions': 2*math.pi, 'percentages': 0.02*math.pi, } angles = angles * mult[s.units] # change direction if self.settings.direction == 'anticlockwise': angles = -angles # add offset angles -= {'right': 0, 'top': 0.5*math.pi, 'left': math.pi, 'bottom': 1.5*math.pi}[self.settings.position0] return angles def toPlotRadius(self, radii): """Convert radii to a plot radii.""" if self.settings.log: logmin = N.log(self._minradius) logmax = N.log(self._maxradius) r = ( N.log(N.clip(radii, 1e-99, 1e99)) - logmin ) / ( logmax - logmin) else: r = (radii - self._minradius) / ( self._maxradius - self._minradius) return N.where(r > 0., r, 0.) def graphToPlotCoords(self, coorda, coordb): '''Convert coordinates in r, theta to x, y.''' ca = self.toPlotRadius(coorda) cb = self.toPlotAngle(coordb) x = self._xc + ca * N.cos(cb) * self._xscale y = self._yc + ca * N.sin(cb) * self._yscale return x, y def drawFillPts(self, painter, extfill, cliprect, ptsx, ptsy): '''Draw points for plotting a fill.''' pts = qt4.QPolygonF() utils.addNumpyToPolygonF(pts, ptsx, ptsy) filltype = extfill.filltype if filltype == 'center': pts.append( qt4.QPointF(self._xc, self._yc) ) utils.brushExtFillPolygon(painter, extfill, cliprect, pts) elif filltype == 'outside': pp = qt4.QPainterPath() pp.moveTo(self._xc, self._yc) pp.arcTo(cliprect, 0, 360) pp.addPolygon(pts) utils.brushExtFillPath(painter, extfill, pp) elif filltype == 'polygon': utils.brushExtFillPolygon(painter, extfill, cliprect, pts) def drawGraph(self, painter, bounds, datarange, outerbounds=None): '''Plot graph area and axes.''' s = self.settings if datarange is None: datarange = [0., 1., 0., 1.] if s.maxradius == 'Auto': self._maxradius = datarange[1] else: self._maxradius = s.maxradius if s.minradius == 'Auto': if s.log: if datarange[0] > 0.: self._minradius = datarange[0] else: self._minradius = self._maxradius / 100. else: if datarange[0] >= 0: self._minradius = 0. else: self._minradius = datarange[0] else: self._minradius = s.minradius # stop negative values if s.log: self._minradius = N.clip(self._minradius, 1e-99, 1e99) self._maxradius = N.clip(self._maxradius, 1e-99, 1e99) if self._minradius == self._maxradius: self._maxradius = self._minradius + 1 self._xscale = (bounds[2]-bounds[0])*0.5 self._yscale = (bounds[3]-bounds[1])*0.5 self._xc = 0.5*(bounds[0]+bounds[2]) self._yc = 0.5*(bounds[3]+bounds[1]) path = qt4.QPainterPath() path.addEllipse( qt4.QRectF( qt4.QPointF(bounds[0], bounds[1]), qt4.QPointF(bounds[2], bounds[3]) ) ) utils.brushExtFillPath(painter, s.Background, path, stroke=s.Border.makeQPenWHide(painter)) def setClip(self, painter, bounds): '''Set clipping for graph.''' p = qt4.QPainterPath() p.addEllipse( qt4.QRectF( qt4.QPointF(bounds[0], bounds[1]), qt4.QPointF(bounds[2], bounds[3]) ) ) painter.setClipPath(p) def drawAxes(self, painter, bounds, datarange, outerbounds=None): '''Plot axes.''' s = self.settings spokesL = s.SpokeLine radiiL = s.RadiiLine # handle reversed axes using min and max below r = [self._minradius, self._maxradius] atick = AxisTicks( min(r), max(r), radiiL.number, radiiL.number*4, extendmin=False, extendmax=False, logaxis=s.log) atick.getTicks() majtick = atick.tickvals # drop 0 at origin if self._minradius == 0. and not s.log: majtick = majtick[1:] # pen for radii circles and axis painter.setPen( radiiL.makeQPenWHide(painter) ) painter.setBrush( qt4.QBrush() ) # draw ticks as circles if not radiiL.hide: for tick in majtick: radius = self.toPlotRadius(tick) if radius > 0: rect = qt4.QRectF( qt4.QPointF( self._xc - radius*self._xscale, self._yc - radius*self._yscale ), qt4.QPointF( self._xc + radius*self._xscale, self._yc + radius*self._yscale ) ) painter.drawEllipse(rect) # setup axes plot tl = s.TickLabels scale, fmt = tl.scale, tl.format if fmt == 'Auto': fmt = atick.autoformat painter.setPen( tl.makeQPen(painter) ) font = tl.makeQFont(painter) # draw ticks if not s.TickLabels.hideradial: for tick in majtick: num = utils.formatNumber( tick*scale, fmt,locale=self.document.locale) x = self.toPlotRadius(tick) * self._xscale + self._xc r = utils.Renderer( painter, font, x, self._yc, num, alignhorz=-1, alignvert=-1, usefullheight=True, doc=self.document) r.render() numspokes = spokesL.number if s.units == 'degrees': vals = N.linspace(0., 360., numspokes+1)[:-1] labels = [u'%g°' % x for x in vals] elif s.units == 'radians': labels = [] for i in crange(numspokes): # use fraction module to work out labels # angle in radians (/pi) f = fractions.Fraction(2*i, numspokes) if f.numerator == 0: txt = '0' else: txt = u'%iπ/%i' % (f.numerator, f.denominator) # remove superfluous 1* or /1 if txt[-2:] == '/1': txt = txt[:-2] txt = txt.lstrip('1') labels.append(txt) elif s.units == 'fractions': labels = [] for i in crange(numspokes): val = i/numspokes label = ('%.2f' % val).rstrip('0').rstrip('.') labels.append(label) elif s.units == 'percentages': labels = [] for i in crange(numspokes): txt = '%.1f' % (100*i/numspokes) if txt[-2:] == '.0': txt = txt[:-2] labels.append(txt) else: raise RuntimeError() if s.direction == 'anticlockwise': labels = labels[0:1] + labels[1:][::-1] # these are the angles the spokes lie in # (by default 0 is to the right) angles = 2 * math.pi * N.arange(numspokes) / numspokes # rotate labels if zero not at right if s.position0 == 'top': angles -= math.pi/2 elif s.position0 == 'left': angles += math.pi elif s.position0 == 'bottom': angles += math.pi/2 # draw labels around plot if not s.TickLabels.hidetangential: for angle, label in zip(angles, labels): align = radianToAlign(angle) x = self._xc + N.cos(angle) * self._xscale y = self._yc + N.sin(angle) * self._yscale r = utils.Renderer( painter, font, x, y, label, alignhorz=align[0], alignvert=align[1], usefullheight=True) r.render() # draw spokes if not spokesL.hide: painter.setPen( spokesL.makeQPenWHide(painter) ) painter.setBrush( qt4.QBrush() ) angle = 2 * math.pi / numspokes lines = [] for i in crange(numspokes): x = self._xc + N.cos(angle*i) * self._xscale y = self._yc + N.sin(angle*i) * self._yscale lines.append( qt4.QLineF( qt4.QPointF(self._xc, self._yc), qt4.QPointF(x, y)) ) painter.drawLines(lines) document.thefactory.register(Polar) veusz-3.0.1/veusz/widgets/axis.py0000664000175000017500000013543413302252613016460 0ustar jssjss00000000000000# Copyright (C) 2003 Jeremy S. Sanders # Email: Jeremy Sanders # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ############################################################################## """Widget to plot axes, and to handle conversion of coordinates to plot positions.""" from __future__ import division import numpy as N import re from ..compat import czip from .. import qtall as qt4 from .. import document from .. import setting from .. import utils from . import widget from . import axisticks from . import controlgraph ############################################################################### def _(text, disambiguation=None, context='Axis'): """Translate text.""" return qt4.QCoreApplication.translate(context, text, disambiguation) class MajorTick(setting.Line): '''Major tick settings.''' def __init__(self, name, **args): setting.Line.__init__(self, name, **args) self.add( setting.DistancePt( 'length', '6pt', descr = _('Length of major ticks'), usertext= _('Length') ) ) self.add( setting.Int( 'number', 6, descr = _('Number of major ticks to aim for'), usertext= _('Number') ) ) self.add( setting.FloatList('manualTicks', [], descr = _('List of tick values' ' overriding defaults'), usertext= _('Manual ticks') ) ) def getLength(self, painter): '''Return tick length in painter coordinates''' return self.get('length').convert(painter) class MinorTick(setting.Line): '''Minor tick settings.''' def __init__(self, name, **args): setting.Line.__init__(self, name, **args) self.add( setting.DistancePt( 'length', '3pt', descr = _('Length of minor ticks'), usertext= _('Length')) ) self.add( setting.Int( 'number', 20, descr = _('Number of minor ticks to aim for'), usertext= _('Number') ) ) def getLength(self, painter): '''Return tick length in painter coordinates''' return self.get('length').convert(painter) class GridLine(setting.Line): '''Grid line settings.''' def __init__(self, name, **args): setting.Line.__init__(self, name, **args) self.get('color').newDefault( 'grey' ) self.get('hide').newDefault( True ) self.get('style').newDefault( 'dotted' ) self.add( setting.Bool( 'onTop', False, descr = _('Put grid lines on top of graph'), usertext = _('On top') ) ) class MinorGridLine(setting.Line): '''Minor tick grid line settings.''' def __init__(self, name, **args): setting.Line.__init__(self, name, **args) self.get('color').newDefault( 'lightgrey' ) self.get('hide').newDefault( True ) self.get('style').newDefault( 'dotted' ) class AxisLabel(setting.Text): """For axis labels.""" def __init__(self, name, **args): setting.Text.__init__(self, name, **args) self.add( setting.Bool( 'atEdge', False, descr = _('Place axis label close to edge' ' of graph'), usertext= _('At edge') ) ) self.add( setting.RotateInterval( 'rotate', '0', descr = 'Angle by which to rotate label by', usertext='Rotate') ) self.add( setting.DistancePt( 'offset', '0pt', descr = _('Additional offset of axis label' ' from axis tick labels'), usertext= _('Label offset') ) ) self.add( setting.Choice( 'position', ('at-minimum', 'centre', 'at-maximum'), 'centre', descr = _('Position of axis label'), usertext = _('Position') ) ) class TickLabel(setting.Text): """For tick labels on axes.""" formatchoices = ('Auto', '%Vg', '%Ve', '%VE', '%g', '%e', '%.2f') descriptions = ( _('Automatic'), _('General numerical format'), _('Scientific notation'), _('Engineering suffix notation'), _('C-style general format'), _('C-style scientific notation'), _('2 decimal places always shown') ) def __init__(self, name, **args): setting.Text.__init__(self, name, **args) self.add( setting.RotateInterval( 'rotate', '0', descr = _('Angle by which to rotate label by'), usertext= _('Rotate') ) ) self.add( setting.ChoiceOrMore( 'format', TickLabel.formatchoices, 'Auto', descr = _('Format of the tick labels'), descriptions=TickLabel.descriptions, usertext= _('Format') ) ) self.add( setting.Float('scale', 1., descr=_('A scale factor to apply to the values ' 'of the tick labels'), usertext=_('Scale') ) ) self.add( setting.DistancePt( 'offset', '0pt', descr = _('Additional offset of axis tick ' 'labels from axis'), usertext= _('Tick offset') ) ) class AutoRange(setting.ChoiceOrMore): """Choose how to choose range of axis.""" # +5% or -5% re_dr_plusminus = re.compile(r'^\s*([+-][0-9]+)\s*%\s*$') # 5-10% re_dr_percrange = re.compile(r'^\s*(-?[0-9]+)\s*-\s*([0-9]+)\s*%\s*$') # < 5% re_dr_lower = re.compile(r'^\s*<\s*([0-9]+)\s*%\s*$') # > 95% re_dr_upper = re.compile(r'^\s*>\s*([0-9]+)\s*%\s*$') def __init__(self, name, value, **args): setting.ChoiceOrMore.__init__( self, 'autoRange', ('exact', 'next-tick', '+2%', '+5%', '+10%', '+15%', '-2%', '-5%', '-10%', '-15%', '20-80%', '<20%', '>80%', ), 'next-tick', descr = _('If axis range not specified, use range of ' 'data and this setting'), descriptions = ( _('Use exact data range'), _('Round up to tick marks from data range'), _('Expand 2% beyond data range'), _('Expand 5% beyond data range'), _('Expand 10% beyond data range'), _('Expand 15% beyond data range'), _('Shrink 2% inside data range'), _('Shrink 5% inside data range'), _('Shrink 10% inside data range'), _('Shrink 15% inside data range'), _('20 to 80% of the data range'), _('Up to 20% of the data range'), _('Above 80% of the data range'), ), formatting = True, usertext = _('Auto range'), ) def copy(self): """Return copy of this setting.""" return self._copyHelper((), (), {}) def autoRangeToFracs(self, rng, doc): """Convert auto range setting to fractions to expand range by. Returns tuple of expansion fractions for left and right """ # +X% or -Y% m = self.re_dr_plusminus.match(rng) if m: v = float(m.group(1))*0.01 return v, v # X-Y% m = self.re_dr_percrange.match(rng) if m: v1 = -float(m.group(1))*0.01 v2 = -(1-float(m.group(2))*0.01) return v1, v2 # Y% m = self.re_dr_upper.match(rng) if m: return -(float(m.group(1))*0.01), 0 # error doc.log(_("Invalid axis range '%s'") % rng) return 0, 0 def adjustPlottedRange(self, plottedrange, adjustmin, adjustmax, log, doc): """Adjust plotted range list given settings. plottedrange: [minval, maxval] adjustmin/adjustmax: bool whether to adjust log: is log axis doc: Document object """ rng = self.val if rng == 'exact': pass elif rng == 'next-tick': pass else: # get fractions to expand range by expandfrac1, expandfrac2 = self.autoRangeToFracs( rng, doc) origrange = list(plottedrange) if log: # logarithmic logrng = abs( N.log(plottedrange[1]) - N.log(plottedrange[0]) ) if adjustmin: plottedrange[0] /= N.exp(logrng * expandfrac1) if adjustmax: plottedrange[1] *= N.exp(logrng * expandfrac2) else: # linear rng = plottedrange[1] - plottedrange[0] if adjustmin: plottedrange[0] -= rng*expandfrac1 if adjustmax: plottedrange[1] += rng*expandfrac2 # if order is wrong, then give error! if plottedrange[1] <= plottedrange[0]: doc.log(_("Invalid axis range '%s'") % rng) plottedrange[:] = origrange ############################################################################### class Axis(widget.Widget): """Manages and draws an axis.""" typename = 'axis' allowusercreation = True description = 'Axis to a plot or shared in a grid' isaxis = True def __init__(self, parent, name=None): """Initialise axis.""" widget.Widget.__init__(self, parent, name=name) s = self.settings if self.name == 'y' and s.direction != 'vertical': s.direction = 'vertical' elif self.name == 'x' and s.direction != 'horizontal': s.direction = 'horizontal' # automatic range self.setAutoRange(None) # document updates change set variable when things need recalculating self.docchangeset = -1 self.currentbounds = [0,0,1,1] @classmethod def addSettings(klass, s): """Construct list of settings.""" widget.Widget.addSettings(s) s.add( setting.Str('label', '', descr=_('Axis label text'), usertext=_('Label')) ) s.add( setting.AxisBound('min', 'Auto', descr=_('Minimum value of axis'), usertext=_('Min')) ) s.add( setting.AxisBound('max', 'Auto', descr=_('Maximum value of axis'), usertext=_('Max')) ) s.add( setting.Bool('log', False, descr = _('Whether axis is logarithmic'), usertext=_('Log')) ) s.add( AutoRange('autoRange', 'next-tick') ) s.add( setting.Choice('mode', ('numeric', 'datetime', 'labels'), 'numeric', descr = _('Type of ticks to show on on axis'), usertext=_('Mode')) ) s.add( setting.SettingBackwardCompat( 'autoExtend', 'autoRange', True, translatefn = lambda x: ('exact', 'next-tick')[x], formatting=True ) ) # this setting no longer used s.add( setting.Bool('autoExtendZero', True, descr = _('Extend axis to zero if close (UNUSED)'), usertext=_('Zero extend'), hidden=True, formatting=True) ) s.add( setting.Bool('autoMirror', True, descr = _('Place axis on opposite side of graph ' 'if none'), usertext=_('Auto mirror'), formatting=True) ) s.add( setting.Bool('reflect', False, descr = _('Place axis text and ticks on other side' ' of axis'), usertext=_('Reflect'), formatting=True) ) s.add( setting.Bool('outerticks', False, descr = _('Place ticks on outside of graph'), usertext=_('Outer ticks'), formatting=True) ) s.add( setting.Float('datascale', 1., descr=_('Scale data plotted by this factor'), usertext=_('Scale')) ) s.add( setting.Choice('direction', ['horizontal', 'vertical'], 'horizontal', descr = _('Direction of axis'), usertext=_('Direction')) ) s.add( setting.Float('lowerPosition', 0., descr=_('Fractional position of lower end of ' 'axis on graph'), usertext=_('Min position')) ) s.add( setting.Float('upperPosition', 1., descr=_('Fractional position of upper end of ' 'axis on graph'), usertext=_('Max position')) ) s.add( setting.Float('otherPosition', 0., descr=_('Fractional position of axis ' 'in its perpendicular direction'), usertext=_('Axis position')) ) s.add( setting.WidgetPath('match', '', descr = _('Match the scale of this axis to the ' 'axis specified'), usertext=_('Match'), allowedwidgets = [Axis] )) s.add( setting.Line('Line', descr = _('Axis line settings'), usertext = _('Axis line')), pixmap='settings_axisline' ) s.add( AxisLabel('Label', descr = _('Axis label settings'), usertext = _('Axis label')), pixmap='settings_axislabel' ) s.add( TickLabel('TickLabels', descr = _('Tick label settings'), usertext = _('Tick labels')), pixmap='settings_axisticklabels' ) s.add( MajorTick('MajorTicks', descr = _('Major tick line settings'), usertext = _('Major ticks')), pixmap='settings_axismajorticks' ) s.add( MinorTick('MinorTicks', descr = _('Minor tick line settings'), usertext = _('Minor ticks')), pixmap='settings_axisminorticks' ) s.add( GridLine('GridLines', descr = _('Grid line settings'), usertext = _('Grid lines')), pixmap='settings_axisgridlines' ) s.add( MinorGridLine('MinorGridLines', descr = _('Minor grid line settings'), usertext = _('Grid lines for minor ticks')), pixmap='settings_axisminorgridlines' ) @classmethod def allowedParentTypes(klass): from . import graph, grid return (graph.Graph, grid.Grid) @property def userdescription(self): """User friendly description.""" s = self.settings return "range %s to %s%s" % ( str(s.min), str(s.max), ['',' (log)'][self.plottedLog()]) def isLinked(self): """Whether is an axis linked to another.""" return False def getLinkedAxis(self): """Return axis linked to this one (or None).""" return None def setAutoRange(self, autorange): """Set the automatic range of this axis (called from page helper).""" if autorange: scale = self.settings.datascale self.autorange = ar = [x*scale for x in autorange] if self.settings.log: ar[0] = max(1e-99, ar[0]) else: if self.settings.log: self.autorange = [1e-2, 1.] else: self.autorange = [0., 1.] def usesAutoRange(self): """Return whether any of the bounds are automatically determined.""" return self.settings.min == 'Auto' or self.settings.max == 'Auto' def computePlottedRange(self, force=False, overriderange=None): """Convert the range requested into a plotted range.""" if self.docchangeset == self.document.changeset and not force: return s = self.settings if overriderange is None: self.plottedrange = [s.min, s.max] else: self.plottedrange = overriderange # match the scale of this axis to another matched = False if s.match != '': # locate widget we're matching # this is ensured to be an Axis try: widget = s.get('match').getReferredWidget() except utils.InvalidType: widget = None # this looks valid + sanity checks if (widget is not None and widget != self and widget.settings.match == ''): # update if out of date if widget.docchangeset != self.document.changeset: widget.computePlottedRange() # copy the range self.plottedrange = list(widget.plottedrange) matched = True # automatic lookup of minimum if not matched and overriderange is None: if s.min == 'Auto': self.plottedrange[0] = self.autorange[0] if s.max == 'Auto': self.plottedrange[1] = self.autorange[1] # yuck, but sometimes it's true # tweak range to make sure things don't blow up further down the # line if ( abs(self.plottedrange[0] - self.plottedrange[1]) < ( abs(self.plottedrange[0]) + abs(self.plottedrange[1]) )*1e-12 ): self.plottedrange[1] = ( self.plottedrange[0] + max(1., self.plottedrange[0]*0.1) ) # handle axis values round the wrong way invertaxis = self.plottedrange[0] > self.plottedrange[1] if invertaxis: self.plottedrange = self.plottedrange[::-1] # make sure log axes don't blow up if s.log: if self.plottedrange[0] < 1e-99: self.plottedrange[0] = 1e-99 if self.plottedrange[1] < 1e-99: self.plottedrange[1] = 1e-99 if self.plottedrange[0] == self.plottedrange[1]: self.plottedrange[1] = self.plottedrange[0]*2 s.get('autoRange').adjustPlottedRange( self.plottedrange, s.min=='Auto', s.max=='Auto', s.log, self.document) self.computeTicks() # invert bounds if axis was inverted if invertaxis: self.plottedrange = self.plottedrange[::-1] self.docchangeset = self.document.changeset def plottedLog(self): """Plotted in log? This is overridden if the mode is incorrect.""" return (self.settings.log and self.settings.mode in ('numeric', 'labels')) def computeTicks(self, allowauto=True): """Update ticks given plotted range. if allowauto is False, then do not allow ticks to be updated """ s = self.settings if s.mode in ('numeric', 'labels'): tickclass = axisticks.AxisTicks else: tickclass = axisticks.DateTicks nexttick = s.autoRange == 'next-tick' extendmin = nexttick and s.min == 'Auto' and allowauto extendmax = nexttick and s.max == 'Auto' and allowauto # create object to compute ticks axs = tickclass(self.plottedrange[0], self.plottedrange[1], s.MajorTicks.number, s.MinorTicks.number, extendmin = extendmin, extendmax = extendmax, logaxis = self.plottedLog()) axs.getTicks() self.plottedrange[0] = axs.minval self.plottedrange[1] = axs.maxval self.majortickscalc = axs.tickvals self.minortickscalc = axs.minorticks self.autoformat = axs.autoformat # override values if requested if len(s.MajorTicks.manualTicks) > 0: ticks = [] for i in s.MajorTicks.manualTicks: if i >= self.plottedrange[0] and i <= self.plottedrange[1]: ticks.append(i) self.majortickscalc = N.array(ticks) def getPlottedRange(self): """Return the range plotted by the axes.""" self.computePlottedRange() return (self.plottedrange[0], self.plottedrange[1]) def updateAxisLocation(self, bounds, otherposition=None, lowerupperposition=None): """Recalculate coordinates on plotter of axis. otherposition: override otherPosition setting lowerupperposition: set to tuple (lower, upper) to override lowerPosition and upperPosition settings """ s = self.settings if lowerupperposition is None: p1, p2 = s.lowerPosition, s.upperPosition else: p1, p2 = lowerupperposition if otherposition is None: otherposition = s.otherPosition x1, y1, x2, y2 = self.currentbounds = bounds dx = x2 - x1 dy = y2 - y1 if s.direction == 'horizontal': # horizontal self.coordParr1 = x1 + dx*p1 self.coordParr2 = x1 + dx*p2 # other axis coordinates self.coordPerp = y2 - dy*otherposition self.coordPerp1 = y1 self.coordPerp2 = y2 else: # vertical self.coordParr1 = y2 - dy*p1 self.coordParr2 = y2 - dy*p2 # other axis coordinates self.coordPerp = x1 + dx*otherposition self.coordPerp1 = x1 self.coordPerp2 = x2 # is this axis reflected if otherposition > 0.5: self.coordReflected = not s.reflect else: self.coordReflected = s.reflect def graphToPlotterCoords(self, bounds, vals): """Convert graph coordinates to plotter coordinates on this axis. bounds specifies the plot bounds vals is numpy of coordinates Returns positions as a numpy """ # if the doc was modified, recompute the range self.updateAxisLocation(bounds) return self._graphToPlotter(vals) def _graphToPlotter(self, vals): """Convert the coordinates assuming the machinery is in place.""" # work out fractional posistions, then convert to pixels if self.plottedLog(): fracposns = self.logConvertToPlotter(vals) else: fracposns = self.linearConvertToPlotter(vals) return self.coordParr1 + fracposns*(self.coordParr2-self.coordParr1) def dataToPlotterCoords(self, posn, data): """Convert data values to plotter coordinates, scaling if necessary.""" self.updateAxisLocation(posn) return self._graphToPlotter(data*self.settings.datascale) def plotterToGraphCoords(self, bounds, vals): """Convert plotter coordinates on this axis to graph coordinates. bounds specifies the plot bounds vals is a numpy of coordinates returns a numpy of floats """ self.updateAxisLocation( bounds ) # work out fractional positions of the plotter coords frac = ( (vals.astype(N.float64) - self.coordParr1) / (self.coordParr2 - self.coordParr1) ) # convert from fractional to graph if self.plottedLog(): return self.logConvertFromPlotter(frac) else: return self.linearConvertFromPlotter(frac) def plotterToDataCoords(self, bounds, vals): """Convert plotter coordinates to data, removing scaling.""" try: scale = 1./self.settings.datascale except ZeroDivisionError: scale = 0. return scale * self.plotterToGraphCoords(bounds, vals) def linearConvertToPlotter(self, v): """Convert graph coordinates to fractional plotter units for linear scale. """ return ( (v - self.plottedrange[0]) / (self.plottedrange[1] - self.plottedrange[0]) ) def linearConvertFromPlotter(self, v): """Convert from (fractional) plotter coords to graph coords. """ return ( self.plottedrange[0] + v * (self.plottedrange[1]-self.plottedrange[0]) ) def logConvertToPlotter(self, v): """Convert graph coordinates to fractional plotter units for log10 scale. """ log1 = N.log(self.plottedrange[0]) log2 = N.log(self.plottedrange[1]) return (N.log(N.clip(v, 1e-99, 1e99)) - log1)/(log2 - log1) def logConvertFromPlotter(self, v): """Convert from fraction plotter coords to graph coords with log scale. """ return ( self.plottedrange[0] * ( self.plottedrange[1]/self.plottedrange[0] )**v ) def againstWhichEdge(self): """Returns edge this axis is against, if any. Returns 0-3,None for (left, top, right, bottom, None) """ s = self.settings op = abs(s.otherPosition) if op > 1e-3 and op < 0.999: return None else: if s.direction == 'vertical': if op <= 1e-3: return 0 else: return 2 else: if op <= 1e-3: return 3 else: return 1 def swapline(self, painter, a1, b1, a2, b2): """Draw line, but swap x & y coordinates if vertical axis.""" if self.settings.direction == 'horizontal': painter.drawLine(qt4.QPointF(a1, b1), qt4.QPointF(a2, b2)) else: painter.drawLine(qt4.QPointF(b1, a1), qt4.QPointF(b2, a2)) def swaplines(self, painter, a1, b1, a2, b2): """Multiline version of swapline where a1, b1, a2, b2 are arrays.""" if self.settings.direction == 'horizontal': a = (a1, b1, a2, b2) else: a = (b1, a1, b2, a2) utils.plotLinesToPainter(painter, a[0], a[1], a[2], a[3]) def _drawGridLines(self, subset, painter, coordticks, parentposn): """Draw grid lines on the plot.""" painter.setPen( self.settings.get(subset).makeQPen(painter) ) # drop points which overlap with graph box (if used) if self.parent.typename == 'graph': if not self.parent.settings.Border.hide: if self.settings.direction == 'horizontal': ok = ( (N.abs(coordticks-parentposn[0]) > 1e-3) & (N.abs(coordticks-parentposn[2]) > 1e-3) ) else: ok = ( (N.abs(coordticks-parentposn[1]) > 1e-3) & (N.abs(coordticks-parentposn[3]) > 1e-3) ) coordticks = coordticks[ok] self.swaplines(painter, coordticks, coordticks*0.+self.coordPerp1, coordticks, coordticks*0.+self.coordPerp2) def _drawAxisLine(self, painter, posn): """Draw the line of the axis.""" pen = self.settings.get('Line').makeQPen(painter) pen.setCapStyle(qt4.Qt.FlatCap) painter.setPen(pen) self.swapline( painter, self.coordParr1, self.coordPerp, self.coordParr2, self.coordPerp ) def _drawMinorTicks(self, painter, coordminorticks): """Draw minor ticks on plot.""" s = self.settings mt = s.get('MinorTicks') pen = mt.makeQPen(painter) pen.setCapStyle(qt4.Qt.FlatCap) painter.setPen(pen) delta = mt.getLength(painter) if s.direction == 'vertical': delta *= -1 if self.coordReflected: delta *= -1 if s.outerticks: delta *= -1 y = coordminorticks*0.+self.coordPerp self.swaplines( painter, coordminorticks, y, coordminorticks, y-delta ) def _drawMajorTicks(self, painter, tickcoords): """Draw major ticks on the plot.""" s = self.settings mt = s.get('MajorTicks') pen = mt.makeQPen(painter) pen.setCapStyle(qt4.Qt.FlatCap) painter.setPen(pen) startdelta = mt.getLength(painter) delta = startdelta if s.direction == 'vertical': delta *= -1 if self.coordReflected: delta *= -1 if s.outerticks: delta *= -1 y = tickcoords*0.+self.coordPerp self.swaplines( painter, tickcoords, y, tickcoords, y-delta ) # account for ticks if they are in the direction of the label if s.outerticks and not self.coordReflected: self._delta_axis += abs(delta) def generateLabelLabels(self, phelper): """Generate list of positions and labels from widgets using this axis.""" try: plotters = phelper.axisplottermap[self] except (AttributeError, KeyError): return dir = self.settings.direction minval, maxval = self.plottedrange for plotter in plotters: # get label and label coordinates from plotter (if any) labels, coords = plotter.getAxisLabels(dir) if labels is not None and coords is not None: # convert coordinates to plotter coordinates pcoords = self._graphToPlotter(coords) for coord, pcoord, lab in czip(coords, pcoords, labels): # return labels that are within the plotted range # of coordinates if N.isfinite(coord) and (minval <= coord <= maxval): yield pcoord, lab def _drawTickLabels(self, phelper, painter, coordticks, sign, outerbounds, tickvals, texttorender): """Draw tick labels on the plot. texttorender is a list which contains text for the axis to render after checking for collisions """ s = self.settings vertical = s.direction == 'vertical' font = s.get('TickLabels').makeQFont(painter) painter.setFont(font) fm = utils.FontMetrics(font, painter.device()) tl_spacing = fm.leading() + fm.descent() # work out font alignment angle = int(s.TickLabels.rotate) if not self.coordReflected and angle != 0: angle = 360-angle if vertical: # limit tick labels to be directly below/besides axis ax, ay = 1, 0 else: ax, ay = 0, 1 if self.coordReflected: ax, ay = -ax, -ay # get information about text scales tl = s.get('TickLabels') scale = tl.scale pen = tl.makeQPen(painter) # an extra offset if required self._delta_axis += tl.get('offset').convert(painter) def generateTickLabels(): """Return plotter position of labels and label text.""" # get format for labels format = s.TickLabels.format if format.lower() == 'auto': format = self.autoformat # generate positions and labels for posn, tickval in czip(coordticks, tickvals): text = utils.formatNumber(tickval*scale, format, locale=self.document.locale) yield posn, text # position of label perpendicular to axis perpposn = self.coordPerp + sign*(self._delta_axis+tl_spacing) # use generator function to get labels and positions if s.mode == 'labels': ticklabels = self.generateLabelLabels(phelper) else: ticklabels = generateTickLabels() # iterate over each label maxdim = 0 for parlposn, text in ticklabels: # x and y round other way if vertical if vertical: x, y = perpposn, parlposn else: x, y = parlposn, perpposn r = utils.Renderer( painter, font, x, y, text, alignhorz=ax, alignvert=ay, angle=angle, doc=self.document) if outerbounds is not None: # make sure ticks are within plot if vertical: r.ensureInBox(miny=outerbounds[1], maxy=outerbounds[3], extraspace=True) else: r.ensureInBox(minx=outerbounds[0], maxx=outerbounds[2], extraspace=True) bnd = r.getBounds() texttorender.append( (r, pen) ) # keep track of maximum extent of label perpendicular to axis if vertical: maxdim = max(maxdim, bnd[2] - bnd[0]) else: maxdim = max(maxdim, bnd[3] - bnd[1]) # keep track of where we are self._delta_axis += 2*tl_spacing + maxdim def _drawAxisLabel(self, painter, sign, outerbounds, texttorender): """Draw an axis label on the plot. texttorender is a list which contains text for the axis to render after checking for collisions """ s = self.settings sl = s.Label label = s.get('Label') font = label.makeQFont(painter) painter.setFont(font) fm = utils.FontMetrics(font, painter.device()) al_spacing = fm.leading() + fm.descent() # an extra offset if required self._delta_axis += label.get('offset').convert(painter) text = s.label # avoid adding blank text to plot if not text: return horz = s.direction == 'horizontal' align1 = 1 align2 = {'centre': 0, 'at-minimum': -1, 'at-maximum': 1}[sl.position] if horz: ax, ay = align2, align1 else: ax, ay = align1, align2 reflected = self.coordReflected if reflected: if horz: ay = -ay else: ax = -ax # angle of text (logic is slightly complex) angle = int(sl.rotate) if horz: if not reflected: angle = 360-angle else: angle = angle+270 if reflected: angle = 360-angle angle = angle % 360 if sl.position == 'centre': x = 0.5*(self.coordParr1 + self.coordParr2) elif sl.position == 'at-minimum': x = self.coordParr1 else: x = self.coordParr2 y = self.coordPerp + sign*(self._delta_axis+al_spacing) if not horz: x, y = y, x # make axis label flush with edge of plot if # it's appropriate if outerbounds is not None and sl.atEdge: if abs(s.otherPosition) < 1e-4 and not reflected: if horz: y = outerbounds[3] ay = -ay else: x = outerbounds[0] ax = -ax elif abs(s.otherPosition-1.) < 1e-4 and reflected: if horz: y = outerbounds[1] ay = -ay else: x = outerbounds[2] ax = -ax r = utils.Renderer( painter, font, x, y, text, ax, ay, angle, usefullheight=True, doc=self.document) # make sure text is in plot rectangle if outerbounds is not None: r.ensureInBox( minx=outerbounds[0], maxx=outerbounds[2], miny=outerbounds[1], maxy=outerbounds[3] ) texttorender.insert(0, (r, s.get('Label').makeQPen(painter)) ) def chooseName(self): """Get default name for axis. Make x and y axes, then axisN.""" try: widgets = set(self.parent.childnames) except AttributeError: widgets = set() for name in ('x', 'y'): if name not in widgets: return name return widget.Widget.chooseName(self) def _suppressText(self, painter, parentposn, outerbounds): """Whether to suppress drawing text on this axis because it is too close to the edge of its parent bounding box. If the edge of the plot is within textheight then suppress text """ if outerbounds is None: return False s = self.settings height = utils.FontMetrics( s.get('Label').makeQFont(painter), painter.device()).height() otherposition = s.otherPosition if s.direction == 'vertical': if ( ( otherposition < 0.01 and abs(parentposn[0]-outerbounds[0]) < height) or ( otherposition > 0.99 and abs(parentposn[2]-outerbounds[2]) < height) ): return True else: if ( ( otherposition < 0.01 and abs(parentposn[3]-outerbounds[3]) < height) or ( otherposition > 0.99 and abs(parentposn[1]-outerbounds[1]) < height) ): return True return False def drawGrid(self, parentposn, phelper, outerbounds=None, ontop=False): """Code to draw gridlines. This is separate from the main draw routine because the grid should be behind/infront the data points. """ s = self.settings if ( s.hide or (s.MinorGridLines.hide and s.GridLines.hide) or s.GridLines.onTop != bool(ontop) ): return # draw grid on a different layer, depending on whether on top or not layer = (-2, -1)[bool(ontop)] painter = phelper.painter(self, parentposn, layer=layer) self.updateAxisLocation(parentposn) with painter: painter.save() painter.setClipRect( qt4.QRectF( qt4.QPointF(parentposn[0], parentposn[1]), qt4.QPointF(parentposn[2], parentposn[3]) ) ) if not s.MinorGridLines.hide: coordminorticks = self._graphToPlotter(self.minortickscalc) self._drawGridLines('MinorGridLines', painter, coordminorticks, parentposn) if not s.GridLines.hide: coordticks = self._graphToPlotter(self.majortickscalc) self._drawGridLines('GridLines', painter, coordticks, parentposn) painter.restore() def _drawAutoMirrorTicks(self, posn, painter): s = self.settings coordticks = self._graphToPlotter(self.majortickscalc) coordminorticks = self._graphToPlotter(self.minortickscalc) if s.otherPosition < 0.5: otheredge = 1. else: otheredge = 0. # temporarily change position of axis to other side for drawing self.updateAxisLocation(posn, otherposition=otheredge) if not s.Line.hide: self._drawAxisLine(painter, posn) if not s.MinorTicks.hide: self._drawMinorTicks(painter, coordminorticks) if not s.MajorTicks.hide: self._drawMajorTicks(painter, coordticks) def drawAutoMirror(self, parentposn, phelper, allaxes): """Draw mirrored ticks.""" s = self.settings # if there's another axis in this direction, we don't mirror count = 0 thisdir = s.direction for a in allaxes: if a.settings.direction == thisdir and not a.settings.hide: count += 1 if count > 1 or not s.autoMirror or s.hide: return painter = phelper.painter(self, parentposn, layer=-1) self.updateAxisLocation(parentposn) with painter: painter.save() self._drawAutoMirrorTicks(parentposn, painter) painter.restore() def draw(self, parentposn, phelper, outerbounds=None): """Plot the axis on the painter. """ self.updateAxisLocation(parentposn) # exit if axis is hidden if self.settings.hide: return self.computePlottedRange() painter = phelper.painter(self, parentposn) with painter: self._axisDraw( parentposn, parentposn, outerbounds, painter, phelper) def _drawTextWithoutOverlap(self, painter, texttorender): """Aall the text is drawn at the end so that we can check it doesn't overlap. texttorender is a list of (Renderer, QPen) tuples. """ overlaps = utils.RectangleOverlapTester() for r, pen in texttorender: rect = r.getTightBounds() if not overlaps.willOverlap(rect): painter.setPen(pen) r.render() overlaps.addRect(rect) # debug # poly = rect.makePolygon() # painter.drawPolygon(poly) def _axisDraw(self, posn, parentposn, outerbounds, painter, phelper): """Internal drawing routine.""" s = self.settings # make control item for axis phelper.setControlGraph(self, [ controlgraph.ControlAxisLine( self, phelper, s.direction, self.coordParr1, self.coordParr2, self.coordPerp, posn) ]) # get tick vals coordticks = self._graphToPlotter(self.majortickscalc) coordminorticks = self._graphToPlotter(self.minortickscalc) texttorender = [] # multiplication factor if reflection on the axis is requested sign = 1 if s.direction == 'vertical': sign *= -1 if self.coordReflected: sign *= -1 # plot the line along the axis if not s.Line.hide: self._drawAxisLine(painter, posn) # plot minor ticks if not s.MinorTicks.hide: self._drawMinorTicks(painter, coordminorticks) # keep track of distance from axis self._delta_axis = 0 # plot major ticks if not s.MajorTicks.hide: self._drawMajorTicks(painter, coordticks) # plot tick labels suppresstext = self._suppressText(painter, parentposn, outerbounds) if not s.TickLabels.hide and not suppresstext: self._drawTickLabels(phelper, painter, coordticks, sign, outerbounds, self.majortickscalc, texttorender) # draw an axis label if not s.Label.hide and not suppresstext: self._drawAxisLabel(painter, sign, outerbounds, texttorender) self._drawTextWithoutOverlap(painter, texttorender) def updateControlItem(self, cgi): """Update axis position from control item.""" s = self.settings p = cgi.maxposn if cgi.zoomed(): # zoom axis scale # we convert a neighbouring pixel to see how we should # round the text c1, c2, c1delt, c2delt = self.plotterToGraphCoords( cgi.maxposn, N.array([cgi.minzoom, cgi.maxzoom, cgi.minzoom+1, cgi.maxzoom-1])) if c1 > c2: c1, c2 = c2, c1 c1delt, c2delt = c2delt, c1delt round1 = utils.round2delt(c1, c1delt) round2 = utils.round2delt(c2, c2delt) ops = [] if ( (s.min == 'Auto' or not N.allclose(c1, s.min, rtol=1e-8)) and N.isfinite(round1) ): ops.append( document.OperationSettingSet( s.get('min'), round1) ) if ( (s.max == 'Auto' or not N.allclose(c2, s.max, rtol=1e-8)) and N.isfinite(round2) ): ops.append( document.OperationSettingSet( s.get('max'), round2) ) self.document.applyOperation( document.OperationMultiple(ops, descr=_('zoom axis'))) elif cgi.moved(): # move axis # convert positions to fractions pt1, pt2, ppt1, ppt2 = ( (3, 1, 0, 2), (0, 2, 3, 1) ) [s.direction == 'horizontal'] minfrac = abs((cgi.minpos - p[pt1]) / (p[pt2] - p[pt1])) maxfrac = abs((cgi.maxpos - p[pt1]) / (p[pt2] - p[pt1])) axisfrac = abs((cgi.axispos - p[ppt1]) / (p[ppt2] - p[ppt1])) # swap if wrong way around if minfrac > maxfrac: minfrac, maxfrac = maxfrac, minfrac # update doc ops = [] if s.lowerPosition != minfrac: ops.append( document.OperationSettingSet( s.get('lowerPosition'), round(minfrac, 3)) ) if s.upperPosition != maxfrac: ops.append( document.OperationSettingSet( s.get('upperPosition'), round(maxfrac, 3)) ) if s.otherPosition != axisfrac: ops.append( document.OperationSettingSet( s.get('otherPosition'), round(axisfrac, 3)) ) self.document.applyOperation( document.OperationMultiple(ops, descr=_('adjust axis'))) # allow the factory to instantiate an axis document.thefactory.register( Axis ) veusz-3.0.1/veusz/widgets/plotters3d.py0000664000175000017500000001404513302474345017621 0ustar jssjss00000000000000# Copyright (C) 2014 Jeremy S. Sanders # Email: Jeremy Sanders # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ############################################################################### """Generic 3D plotting widet.""" from __future__ import division, print_function from .. import qtall as qt import numpy as N from .. import setting from . import widget from ..helpers import threed def _(text, disambiguation=None, context='Plotters3D'): """Translate text.""" return qt.QCoreApplication.translate(context, text, disambiguation) class GenericPlotter3D(widget.Widget): """Generic plotter.""" typename = 'genericplotter3d' isplotter = True @classmethod def allowedParentTypes(klass): from . import graph3d return (graph3d.Graph3D,) @classmethod def addSettings(klass, s): """Construct list of settings.""" widget.Widget.addSettings(s) # s.add( setting.Str( # 'key', '', # descr = _('Description of the plotted data to appear in key'), # usertext=_('Key text')) ) s.add( setting.Axis( 'xAxis', 'x', 'x', descr = _('Name of X-axis to use'), usertext=_('X axis')) ) s.add( setting.Axis( 'yAxis', 'y', 'y', descr = _('Name of Y-axis to use'), usertext=_('Y axis')) ) s.add( setting.Axis( 'zAxis', 'z', 'z', descr = _('Name of Z-axis to use'), usertext=_('Z axis')) ) def autoColor(self, painter, dataindex=0): """Automatic color for plotting.""" return painter.docColorAuto( painter.helper.autoColorIndex((self, dataindex))) def getAxesNames(self): """Returns names of axes used.""" s = self.settings return (s.xAxis, s.yAxis, s.zAxis) def getNumberKeys(self): """Return number of key entries.""" if self.settings.key: return 1 else: return 0 def getKeyText(self, number): """Get key entry.""" return self.settings.key def drawKeySymbol(self, number, painter, x, y, width, height): """Draw the plot symbol and/or line at (x,y) in a box width*height. This is used to plot a key """ pass def getAxisLabels(self, direction): """Get labels for datapoints and coordinates, or None if none. direction is 'horizontal' or 'vertical' return (labels, coordinates) """ return (None, None, None) def fetchAxes(self): """Returns the axes for this widget""" axes = self.parent.getAxes( (self.settings.xAxis, self.settings.yAxis, self.settings.zAxis)) # fail if we don't have good axes if ( axes[0] is None or axes[0].settings.direction != 'x' or axes[1] is None or axes[1].settings.direction != 'y' or axes[2] is None or axes[2].settings.direction != 'z' ): return None return axes def fetchAxis(self, var): """Return a particular axis given x, y or z""" if var == 'x': setn = self.settings.xAxis elif var == 'y': setn = self.settings.yAxis elif var == 'z': setn = self.settings.zAxis return self.parent.getAxes((setn,))[0] def lookupAxis(self, axisname): """Find widget associated with axisname.""" w = self.parent while w: for c in w.children: if c.name == axisname and c.isaxis: return c w = w.parent return None def affectsAxisRange(self): """Returns information on the following axes. format is ( ('x', 'sx'), ('y', 'sy') ) where key is the axis and value is a provided bound """ return () def requiresAxisRange(self): """Requires information about the axis given before providing information. Format (('sx', 'x'), ('sy', 'y')) """ return () def getRange(self, axis, depname, therange): """Update range variable for axis with dependency name given.""" pass def simplifyObjectList(self, objlist): """Simplify list of objects, if possible.""" if len(objlist) == 0: return None elif len(objlist) == 1: return objlist[0] else: cont = threed.ObjectContainer() for o in objlist: cont.addObject(o) return cont def makeClipContainer(self, axes): """Make an object container to clip data to axes.""" return threed.ClipContainer( threed.Vec3(axes[0].settings.lowerPosition, axes[1].settings.lowerPosition, axes[2].settings.lowerPosition), threed.Vec3(axes[0].settings.upperPosition, axes[1].settings.upperPosition, axes[2].settings.upperPosition)) def drawToObject(self, painter, painthelper): # exit if hidden or function blank if self.settings.hide: return # get axes widgets axes = self.fetchAxes() if not axes: return else: return self.dataDrawToObject(painter, axes) def dataDrawToObject(self, painter, axes): """Actually plot the data.""" pass veusz-3.0.1/veusz/widgets/function.py0000664000175000017500000003373413164703032017343 0ustar jssjss00000000000000# Copyright (C) 2008 Jeremy S. Sanders # Email: Jeremy Sanders # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ############################################################################### """For plotting numerical functions.""" from __future__ import division import numpy as N from ..compat import czip, cstr from .. import qtall as qt4 from .. import document from .. import setting from .. import utils from . import pickable from .plotters import GenericPlotter def _(text, disambiguation=None, context='Function'): """Translate text.""" return qt4.QCoreApplication.translate(context, text, disambiguation) class FunctionPlotter(GenericPlotter): """Function plotting class.""" typename='function' allowusercreation=True description=_('Plot a function') @classmethod def addSettings(klass, s): """Construct list of settings.""" GenericPlotter.addSettings(s) s.add( setting.Int( 'steps', 50, minval = 3, descr = _('Number of steps to evaluate the function' ' over'), usertext=_('Steps'), formatting=True), 0 ) s.add( setting.Choice( 'variable', ['x', 'y'], 'x', descr=_('Variable the function is a function of'), usertext=_('Variable')), 0 ) s.add( setting.Str( 'function', 'x', descr=_('Function expression'), usertext=_('Function')), 0 ) s.add(setting.FloatOrAuto( 'min', 'Auto', descr=_('Minimum value at which to plot function'), usertext=_('Min'))) s.add(setting.FloatOrAuto( 'max', 'Auto', descr=_('Maximum value at which to plot function'), usertext=_('Max'))) s.add( setting.Line( 'Line', descr = _('Function line settings'), usertext = _('Plot line')), pixmap = 'settings_plotline' ) s.Line.get('color').newDefault('auto') s.add( setting.PlotterFill( 'FillBelow', descr = _('Fill below/left function'), usertext = _('Fill below')), pixmap = 'settings_plotfillbelow' ) s.add( setting.PlotterFill( 'FillAbove', descr = _('Fill mode above/right function'), usertext = _('Fill above')), pixmap = 'settings_plotfillabove' ) @property def userdescription(self): """User-friendly description.""" return "%(variable)s = %(function)s" % self.settings def logEvalError(self, ex): """Write error message to document log for exception ex.""" self.document.log( "Error evaluating expression in function widget '%s': '%s'" % ( self.name, cstr(ex))) def affectsAxisRange(self): s = self.settings if s.variable == 'x': return ((s.yAxis, 'both'),) else: return ((s.xAxis, 'both'),) def requiresAxisRange(self): s = self.settings if s.variable == 'x': return (('both', s.xAxis),) else: return (('both', s.yAxis),) def getRange(self, axis, depname, axrange): """Adjust the range of the axis depending on the values plotted.""" s = self.settings # ignore empty function if s.function.strip() == '': return # ignore if function isn't sensible compiled = self.document.evaluate.compileCheckedExpression(s.function) if compiled is None: return # find axis to find variable range over varaxis = self.lookupAxis( {'x': s.xAxis, 'y': s.yAxis}[s.variable] ) if not varaxis: return # get range of that axis varaxrange = list(varaxis.getPlottedRange()) if varaxrange[0] == varaxrange[1]: return # trim to range if s.min != 'Auto': varaxrange[0] = max(s.min, varaxrange[0]) if s.max != 'Auto': varaxrange[1] = min(s.max, varaxrange[1]) # work out function in steps try: if varaxis.settings.log: # log spaced steps l1, l2 = N.log(varaxrange[1]), N.log(varaxrange[0]) delta = (l2-l1)/20. points = N.exp(N.arange(l1, l2+delta, delta)) else: # linear spaced steps delta = (varaxrange[1] - varaxrange[0])/20. points = N.arange(varaxrange[0], varaxrange[1]+delta, delta) except ZeroDivisionError: # delta is zero return env = self.initEnviron() env[s.variable] = points try: vals = eval(compiled, env) + points*0. except: # something wrong in the evaluation return # get values which are finite: excluding nan and inf finitevals = vals[N.isfinite(vals)] if axis.settings.log: finitevals = finitevals[finitevals > 0] # update the automatic range if len(finitevals) > 0: axrange[0] = min(N.min(finitevals), axrange[0]) axrange[1] = max(N.max(finitevals), axrange[1]) def _plotLine(self, painter, xpts, ypts, bounds, clip): """ Plot the points in xpts, ypts.""" x1, y1, x2, y2 = bounds maxdeltax = (x2-x1)*3/4 maxdeltay = (y2-y1)*3/4 # idea is to collect points until we go out of the bounds # or reach the end, then plot them pts = qt4.QPolygonF() lastx = lasty = -65536 for x, y in czip(xpts, ypts): # ignore point if it outside sensible bounds if x < -32767 or y < -32767 or x > 32767 or y > 32767: if len(pts) >= 2: utils.plotClippedPolyline(painter, clip, pts) pts.clear() else: # if the jump wasn't too large, add the point to the points if abs(x-lastx) < maxdeltax and abs(y-lasty) < maxdeltay: pts.append( qt4.QPointF(x, y) ) else: # draw what we have until now, and start a new line if len(pts) >= 2: utils.plotClippedPolyline(painter, clip, pts) pts.clear() pts.append( qt4.QPointF(x, y) ) lastx = x lasty = y # draw remaining points if len(pts) >= 2: utils.plotClippedPolyline(painter, clip, pts) def _fillRegion(self, painter, pxpts, pypts, bounds, belowleft, clip, brush): """Fill the region above/below or left/right of the points. belowleft fills below if the variable is 'x', or left if 'y' otherwise it fills above/right.""" # find starting and ending points for the filled region x1, y1, x2, y2 = bounds # trimming can lead to too few points if len(pxpts) < 2 or len(pypts) < 2: return pts = qt4.QPolygonF() if self.settings.variable == 'x': if belowleft: pts.append(qt4.QPointF(pxpts[0], y2)) endpt = qt4.QPointF(pxpts[-1], y2) else: pts.append(qt4.QPointF(pxpts[0], y1)) endpt = qt4.QPointF(pxpts[-1], y1) else: if belowleft: pts.append(qt4.QPointF(x1, pypts[0])) endpt = qt4.QPointF(x1, pypts[-1]) else: pts.append(qt4.QPointF(x2, pypts[0])) endpt = qt4.QPointF(x2, pypts[-1]) # add the points between utils.addNumpyToPolygonF(pts, pxpts, pypts) # stick on the ending point pts.append(endpt) # draw the clipped polygon clipped = qt4.QPolygonF() utils.polygonClip(pts, clip, clipped) path = qt4.QPainterPath() path.addPolygon(clipped) utils.brushExtFillPath(painter, brush, path) def drawKeySymbol(self, number, painter, x, y, width, height): """Draw the plot symbol and/or line.""" s = self.settings yp = y + height/2 # draw line if not s.Line.hide: painter.setBrush( qt4.QBrush() ) painter.setPen( s.Line.makeQPen(painter) ) painter.drawLine( qt4.QPointF(x, yp), qt4.QPointF(x+width, yp) ) def initEnviron(self): """Set up function environment.""" return self.document.evaluate.context.copy() def getIndependentPoints(self, axes, posn): """Calculate the real and screen points to plot for the independent axis""" s = self.settings if ( axes[0] is None or axes[1] is None or axes[0].settings.direction != 'horizontal' or axes[1].settings.direction != 'vertical' ): return None, None # get axes function is plotted along and on and # plot coordinates along axis function plotted along if s.variable == 'x': axis1, axis2 = axes[0], axes[1] minval, maxval = posn[0], posn[2] else: axis1, axis2 = axes[1], axes[0] minval, maxval = posn[1], posn[3] # get equally spaced coordinates along axis in plotter coords plotpts = N.arange(s.steps) * ((maxval-minval) / (s.steps-1)) + minval # convert to axis coordinates axispts = axis1.plotterToDataCoords(posn, plotpts) # trim according to min and max. have to convert back to plotter too. if s.min != 'Auto': axispts = axispts[ axispts >= s.min ] plotpts = axis1.dataToPlotterCoords(posn, axispts) if s.max != 'Auto': axispts = axispts[ axispts <= s.max ] plotpts = axis1.dataToPlotterCoords(posn, axispts) return axispts, plotpts def calcDependentPoints(self, axispts, axes, posn): """Calculate the real and screen points to plot for the dependent axis""" s = self.settings if ( axes[0] is None or axes[1] is None or axes[0].settings.direction != 'horizontal' or axes[1].settings.direction != 'vertical' ): return None, None if axispts is None: return None, None compiled = self.document.evaluate.compileCheckedExpression(s.function) if not compiled: return None, None axis2 = axes[1] if s.variable == 'x' else axes[0] # evaluate function env = self.initEnviron() env[s.variable] = axispts try: results = eval(compiled, env) + N.zeros(axispts.shape) resultpts = axis2.dataToPlotterCoords(posn, results) except Exception as e: self.logEvalError(e) results = None resultpts = None return results, resultpts def calcFunctionPoints(self, axes, posn): ipts, pipts = self.getIndependentPoints(axes, posn) dpts, pdpts = self.calcDependentPoints(ipts, axes, posn) if self.settings.variable == 'x': return (ipts, dpts), (pipts, pdpts) else: return (dpts, ipts), (pdpts, pipts) def _pickable(self, posn): s = self.settings axisnames = [s.xAxis, s.yAxis] axes = self.parent.getAxes(axisnames) if s.variable == 'x': axisnames[1] = axisnames[1] + '(' + axisnames[0] + ')' else: axisnames[0] = axisnames[0] + '(' + axisnames[1] + ')' (xpts, ypts), (pxpts, pypts) = self.calcFunctionPoints(axes, posn) return pickable.GenericPickable( self, axisnames, (xpts, ypts), (pxpts, pypts) ) def pickPoint(self, x0, y0, bounds, distance='radial'): return self._pickable(bounds).pickPoint(x0, y0, bounds, distance) def pickIndex(self, oldindex, direction, bounds): return self._pickable(bounds).pickIndex(oldindex, direction, bounds) def dataDraw(self, painter, axes, posn, cliprect): """Draw the function.""" s = self.settings # exit if hidden or function blank if s.function.strip() == '': return # get the points to plot by evaluating the function (xpts, ypts), (pxpts, pypts) = self.calcFunctionPoints(axes, posn) # draw the function line if ( pxpts is None or pypts is None or pxpts.ndim != 1 or pypts.ndim != 1 ): # not sure how to deal with errors here painter.setPen( setting.settingdb.color('error') ) f = qt4.QFont() f.setPointSize(20) painter.setFont(f) painter.drawText( cliprect, qt4.Qt.AlignCenter, "Cannot evaluate '%s'" % s.function ) else: if not s.FillBelow.hide: self._fillRegion(painter, pxpts, pypts, posn, True, cliprect, s.FillBelow) if not s.FillAbove.hide: self._fillRegion(painter, pxpts, pypts, posn, False, cliprect, s.FillAbove) if not s.Line.hide: painter.setBrush( qt4.QBrush() ) painter.setPen( s.Line.makeQPen(painter) ) self._plotLine(painter, pxpts, pypts, posn, cliprect) # allow the factory to instantiate an function plotter document.thefactory.register( FunctionPlotter ) veusz-3.0.1/veusz/widgets/contour.py0000664000175000017500000005463013164703753017217 0ustar jssjss00000000000000# Copyright (C) 2005 Jeremy S. Sanders # Email: Jeremy Sanders # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ############################################################################### """Contour plotting from 2d datasets. Contour plotting requires that the veusz_helpers package is installed, as a C routine (taken from matplotlib) is used to trace the contours. """ from __future__ import division, print_function import sys import math from ..compat import czip, crange from .. import qtall as qt4 import numpy as N from .. import setting from .. import document from .. import utils from . import plotters try: from ..helpers._nc_cntr import Cntr from ..helpers.qtloops import LineLabeller except ImportError: Cntr = None LineLabeller = object # allow class definition below def _(text, disambiguation=None, context='Contour'): """Translate text.""" return qt4.QCoreApplication.translate(context, text, disambiguation) def finitePoly(poly): """Remove non-finite coordinates from numpy arrays of coordinates.""" out = [] for line in poly: finite = N.isfinite(line) validrows = N.logical_and(finite[:,0], finite[:,1]) out.append( line[validrows] ) return out class ContourLineLabeller(LineLabeller): def __init__(self, clip, rot, painter, font, doc): LineLabeller.__init__(self, clip, rot) self.clippath = qt4.QPainterPath() self.clippath.addRect(clip) self.labels = [] self.painter = painter self.font = font self.document = doc def drawAt(self, idx, rect): """Called to draw the label with the index given.""" text = self.labels[idx] if not text: return angle = rect.angle*180/math.pi if angle < -90 or angle > 90: angle += 180 rend = utils.Renderer( self.painter, self.font, rect.cx, rect.cy, text, alignhorz=0, alignvert=0, angle=angle, doc=self.document) rend.render() if rect.xw > 0: p = qt4.QPainterPath() p.addPolygon(rect.makePolygon()) self.clippath -= p class ContourFills(setting.Settings): """Settings for contour fills.""" def __init__(self, name, **args): setting.Settings.__init__(self, name, **args) self.add( setting.FillSet( 'fills', [], descr = _('Fill styles to plot between contours'), usertext=_('Fill styles'), formatting=True) ) self.add( setting.Bool('hide', False, descr = _('Hide fills'), usertext = _('Hide'), formatting = True) ) class ContourLines(setting.Settings): """Settings for contour lines.""" def __init__(self, name, **args): setting.Settings.__init__(self, name, **args) self.add( setting.LineSet( 'lines', [('solid', '1pt', 'black', False)], descr = _('Line styles to plot the contours ' 'using'), usertext=_('Line styles'), formatting=True) ) self.add( setting.Bool('hide', False, descr = _('Hide lines'), usertext = _('Hide'), formatting = True) ) class SubContourLines(setting.Settings): """Sub-dividing contour line settings.""" def __init__(self, name, **args): setting.Settings.__init__(self, name, **args) self.add( setting.LineSet( 'lines', [('dot1', '1pt', 'black', False)], descr = _('Line styles used for sub-contours'), usertext=_('Line styles'), formatting=True) ) self.add( setting.Int('numLevels', 5, minval=2, descr=_('Number of sub-levels to plot between ' 'each contour'), usertext='Levels') ) self.add( setting.Bool('hide', True, descr=_('Hide lines'), usertext=_('Hide'), formatting=True) ) class ContourLabel(setting.Text): """For tick labels on axes.""" def __init__(self, name, **args): setting.Text.__init__(self, name, **args) self.add( setting.Str( 'format', '%.3Vg', descr = _('Format of the tick labels'), usertext=_('Format')) ) self.add( setting.Float('scale', 1., descr=_('A scale factor to apply to the values ' 'of the tick labels'), usertext=_('Scale')) ) self.add( setting.Bool('rotate', True, descr=_('Rotate labels to follow lines'), usertext=_('Rotate')) ) self.get('hide').newDefault(True) class Contour(plotters.GenericPlotter): """A class which plots contours on a graph with a specified coordinate system.""" typename='contour' allowusercreation=True description=_('Plot a 2d dataset as contours') def __init__(self, parent, name=None): """Initialise plotter with axes.""" plotters.GenericPlotter.__init__(self, parent, name=name) if Cntr is None: print(('WARNING: Veusz cannot import contour module\n' 'Please run python setup.py build\n' 'Contour support is disabled'), file=sys.stderr) # keep track of settings so we recalculate when necessary self.lastdataset = None self.contsettings = None # cached traced contours self._cachedcontours = None self._cachedpolygons = None self._cachedsubcontours = None @classmethod def addSettings(klass, s): """Construct list of settings.""" plotters.GenericPlotter.addSettings(s) s.add( setting.Dataset('data', '', dimensions = 2, descr = _('Dataset to plot'), usertext=_('Dataset')), 0 ) s.add( setting.FloatOrAuto('min', 'Auto', descr = _('Minimum value of contour scale'), usertext=_('Min. value')), 1 ) s.add( setting.FloatOrAuto('max', 'Auto', descr = _('Maximum value of contour scale'), usertext=_('Max. value')), 2 ) s.add( setting.Int('numLevels', 5, minval = 1, descr = _('Number of contour levels to plot'), usertext=_('Number levels')), 3 ) s.add( setting.Choice('scaling', ['linear', 'sqrt', 'log', 'squared', 'manual'], 'linear', descr = _('Scaling between contour levels'), usertext=_('Scaling')), 4 ) s.add( setting.FloatList('manualLevels', [], descr = _('Levels to use for manual scaling'), usertext=_('Manual levels')), 5 ) s.add( setting.Bool('keyLevels', False, descr=_('Show levels in key'), usertext=_('Levels in key')), 6 ) s.add( setting.FloatList('levelsOut', [], descr = _('Levels used in the plot'), usertext=_('Output levels')), 7, readonly=True ) s.add( ContourLabel('ContourLabels', descr = _('Contour label settings'), usertext = _('Contour labels')), pixmap = 'settings_axisticklabels' ) s.add( ContourLines('Lines', descr=_('Contour lines'), usertext=_('Contour lines')), pixmap = 'settings_contourline' ) s.add( ContourFills('Fills', descr=_('Fill within contours'), usertext=_('Contour fills')), pixmap = 'settings_contourfill' ) s.add( SubContourLines('SubLines', descr=_('Sub-contour lines'), usertext=_('Sub-contour lines')), pixmap = 'settings_subcontourline' ) s.add( setting.SettingBackwardCompat('lines', 'Lines/lines', None) ) s.add( setting.SettingBackwardCompat('fills', 'Fills/fills', None) ) s.remove('key') @property def userdescription(self): """User friendly description.""" s = self.settings out = [] if s.data: out.append( s.data ) if s.scaling == 'manual': out.append('manual levels (%s)' % ( ', '.join([str(i) for i in s.manualLevels]))) else: out.append('%(numLevels)i %(scaling)s levels (%(min)s to %(max)s)' % s) return ', '.join(out) def calculateLevels(self): """Calculate contour levels from data and settings. Returns levels as 1d numpy """ # get dataset s = self.settings d = self.document minval, maxval = 0., 1. if s.data in d.data: # scan data data = d.data[s.data].data minval, maxval = N.nanmin(data), N.nanmax(data) if not N.isfinite(minval): minval = 0. if not N.isfinite(maxval): maxval = 1. # override if not auto if s.min != 'Auto': minval = s.min if s.max != 'Auto': maxval = s.max numlevels = s.numLevels scaling = s.scaling if numlevels == 1 and scaling != 'manual': # calculations below assume numlevels > 1 levels = N.array([minval,]) else: # trap out silly cases if minval == maxval: minval = 0. maxval = 1. # calculate levels for each scaling if scaling == 'linear': delta = (maxval - minval) / (numlevels-1) levels = minval + N.arange(numlevels)*delta elif scaling == 'sqrt': delta = N.sqrt(maxval - minval) / (numlevels-1) levels = minval + (N.arange(numlevels)*delta)**2 elif scaling == 'log': if minval == 0.: minval = 1. if minval == maxval: maxval = minval + 1 delta = N.log(maxval/minval) / (numlevels-1) levels = N.exp(N.arange(numlevels)*delta)*minval elif scaling == 'squared': delta = (maxval - minval)**2 / (numlevels-1) levels = minval + N.sqrt(N.arange(numlevels)*delta) else: # manual levels = N.array(s.manualLevels) # for the user later # we do this to convert array to list of floats s.levelsOut = [float(i) for i in levels] return minval, maxval, levels def calculateSubLevels(self, minval, maxval, levels): """Calculate sublevels between contours.""" s = self.settings num = s.SubLines.numLevels if s.SubLines.hide or len(s.SubLines.lines) == 0 or len(levels) <= 1: return N.array([]) # indices where contour levels should be placed numcont = (len(levels)-1) * num indices = N.arange(numcont) indices = indices[indices % num != 0] scaling = s.scaling if scaling == 'linear': delta = (maxval-minval) / numcont slev = indices*delta + minval elif scaling == 'log': delta = N.log( maxval/minval ) / numcont slev = N.exp(indices*delta) * minval elif scaling == 'sqrt': delta = N.sqrt( maxval-minval ) / numcont slev = minval + (indices*delta)**2 elif scaling == 'squared': delta = (maxval-minval)**2 / numcont slev = minval + N.sqrt(indices*delta) elif scaling == 'manual': drange = N.arange(1, num) out = [[]] for conmin, conmax in czip(levels[:-1], levels[1:]): delta = (conmax-conmin) / num out.append( conmin+drange*delta ) slev = N.hstack(out) return slev def affectsAxisRange(self): """Range information provided by widget.""" s = self.settings return ( (s.xAxis, 'sx'), (s.yAxis, 'sy') ) def getRange(self, axis, depname, axrange): """Automatically determine the ranges of variable on the axes.""" # this is copied from Image, probably should combine s = self.settings d = self.document # return if no data or if the dataset isn't two dimensional data = d.data.get(s.data, None) if data is None or data.dimensions != 2: return xr, yr = data.getDataRanges() if depname == 'sx': axrange[0] = min( axrange[0], xr[0] ) axrange[1] = max( axrange[1], xr[1] ) elif depname == 'sy': axrange[0] = min( axrange[0], yr[0] ) axrange[1] = max( axrange[1], yr[1] ) def getNumberKeys(self): """How many keys to show.""" self.checkContoursUpToDate() if self.settings.keyLevels: return len( self.settings.levelsOut ) else: return 0 def getKeyText(self, number): """Get key entry.""" s = self.settings if s.keyLevels: cl = s.get('ContourLabels') return utils.formatNumber( s.levelsOut[number] * cl.scale, cl.format, locale=self.document.locale ) else: return '' def drawKeySymbol(self, number, painter, x, y, width, height): """Draw key for contour level.""" painter.setPen( self.settings.Lines.get('lines').makePen(painter, number)) painter.drawLine(x, y+height/2, x+width, y+height/2) def checkContoursUpToDate(self): """Update contours if necessary. Returns True if okay to plot contours, False if error """ s = self.settings d = self.document # return if no data or if the dataset isn't two dimensional data = d.data.get(s.data, None) if data is None or data.dimensions != 2 or data.data.size == 0: self.contsettings = self.lastdataset = None s.levelsOut = [] return False contsettings = ( s.min, s.max, s.numLevels, s.scaling, s.SubLines.numLevels, len(s.Fills.fills) == 0 or s.Fills.hide, len(s.SubLines.lines) == 0 or s.SubLines.hide, tuple(s.manualLevels) ) if data is not self.lastdataset or contsettings != self.contsettings: self.updateContours() self.lastdataset = data self.contsettings = contsettings return True def dataDraw(self, painter, axes, posn, cliprect): """Draw the contours.""" # update contours if necessary if not self.checkContoursUpToDate(): return self.plotContourFills(painter, posn, axes, cliprect) self.plotContours(painter, posn, axes, cliprect) self.plotSubContours(painter, posn, axes, cliprect) def updateContours(self): """Update calculated contours.""" s = self.settings d = self.document minval, maxval, levels = self.calculateLevels() sublevels = self.calculateSubLevels(minval, maxval, levels) # find coordinates of image coordinate bounds data = d.data[s.data] rangex, rangey = data.getDataRanges() yw, xw = data.data.shape if xw == 0 or yw == 0: return xc, yc = data.getPixelCentres() xpts = N.reshape( N.tile(xc, yw), (yw, xw) ) ypts = N.tile(yc[:, N.newaxis], xw) # only keep finite data points mask = N.logical_not(N.isfinite(data.data)) # iterate over the levels and trace the contours self._cachedcontours = None self._cachedpolygons = None self._cachedsubcontours = None if Cntr is not None: c = Cntr(xpts, ypts, data.data, mask) # trace the contour levels if len(s.Lines.lines) != 0: self._cachedcontours = [] for level in levels: linelist = c.trace(level) self._cachedcontours.append( finitePoly(linelist) ) # trace the polygons between the contours if len(s.Fills.fills) != 0 and len(levels) > 1 and not s.Fills.hide: self._cachedpolygons = [] for level1, level2 in czip(levels[:-1], levels[1:]): linelist = c.trace(level1, level2) self._cachedpolygons.append( finitePoly(linelist) ) # trace sub-levels if len(sublevels) > 0: self._cachedsubcontours = [] for level in sublevels: linelist = c.trace(level) self._cachedsubcontours.append( finitePoly(linelist) ) def _plotContours(self, painter, posn, axes, linestyles, contours, showlabels, hidelines, clip): """Plot a set of contours. """ s = self.settings # no lines cached as no line styles if contours is None: return cl = s.get('ContourLabels') font = cl.makeQFont(painter) labelpen = cl.makeQPen(painter) descent = qt4.QFontMetricsF(font).descent() # linelabeller does clipping and labelling of contours linelabeller = ContourLineLabeller( clip, cl.rotate, painter, font, self.document) levels = [] # iterate over each level, and list of lines for num, linelist in enumerate(contours): if showlabels: number = s.levelsOut[num] text = utils.formatNumber( number * cl.scale, cl.format, locale=self.document.locale) rend = utils.Renderer( painter, font, 0, 0, text, alignhorz=0, alignvert=0, angle=0, doc=self.document) textdims = qt4.QSizeF(*rend.getDimensions()) textdims += qt4.QSizeF(descent*2, descent*2) else: textdims = qt4.QSizeF(0, 0) # iterate over each complete line of the contour for curve in linelist: # convert coordinates from graph to plotter xplt = axes[0].dataToPlotterCoords(posn, curve[:,0]) yplt = axes[1].dataToPlotterCoords(posn, curve[:,1]) pts = qt4.QPolygonF() utils.addNumpyToPolygonF(pts, xplt, yplt) linelabeller.addLine(pts, textdims) if showlabels: linelabeller.labels.append(text) else: linelabeller.labels.append(None) levels.append(num) painter.save() painter.setPen(labelpen) linelabeller.process() painter.setClipPath(linelabeller.clippath) for i in crange(linelabeller.getNumPolySets()): polyset = linelabeller.getPolySet(i) painter.setPen(linestyles.makePen(painter, levels[i])) for poly in polyset: painter.drawPolyline(poly) painter.restore() def plotContours(self, painter, posn, axes, clip): """Plot the traced contours on the painter.""" s = self.settings self._plotContours(painter, posn, axes, s.Lines.get('lines'), self._cachedcontours, not s.ContourLabels.hide, s.Lines.hide, clip) def plotSubContours(self, painter, posn, axes, clip): """Plot sub contours on painter.""" s = self.settings self._plotContours(painter, posn, axes, s.SubLines.get('lines'), self._cachedsubcontours, False, s.SubLines.hide, clip) def plotContourFills(self, painter, posn, axes, clip): """Plot the traced contours on the painter.""" s = self.settings # don't draw if there are no cached polygons if self._cachedpolygons is None or s.Fills.hide: return # iterate over each level, and list of lines for num, polylist in enumerate(self._cachedpolygons): # iterate over each complete line of the contour path = qt4.QPainterPath() for poly in polylist: # convert coordinates from graph to plotter xplt = axes[0].dataToPlotterCoords(posn, poly[:,0]) yplt = axes[1].dataToPlotterCoords(posn, poly[:,1]) pts = qt4.QPolygonF() utils.addNumpyToPolygonF(pts, xplt, yplt) clippedpoly = qt4.QPolygonF() utils.polygonClip(pts, clip, clippedpoly) path.addPolygon(clippedpoly) # fill polygons brush = s.Fills.get('fills').returnBrushExtended(num) utils.brushExtFillPath(painter, brush, path) # allow the factory to instantiate a contour document.thefactory.register( Contour ) veusz-3.0.1/veusz/widgets/textlabel.py0000664000175000017500000002035213164704766017511 0ustar jssjss00000000000000# Copyright (C) 2008 Jeremy S. Sanders # Email: Jeremy Sanders # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ############################################################################### """For plotting one or more text labels on a graph.""" from __future__ import division import itertools from ..compat import czip from .. import document from .. import setting from .. import utils from .. import qtall as qt4 from . import plotters from . import controlgraph def _(text, disambiguation=None, context='TextLabel'): """Translate text.""" return qt4.QCoreApplication.translate(context, text, disambiguation) class BorderLine(setting.Line): '''Plot line around text.''' def __init__(self, name, **args): setting.Line.__init__(self, name, **args) self.get('hide').newDefault(True) class TextLabel(plotters.FreePlotter): """Add a text label to a graph.""" typename = 'label' description = _('Text label') allowusercreation = True @classmethod def addSettings(klass, s): """Construct list of settings.""" plotters.FreePlotter.addSettings(s) s.add( setting.DatasetOrStr('label', '', descr=_('Text to show or text dataset'), usertext=_('Label')), 0 ) s.add( setting.AlignHorz('alignHorz', 'left', descr=_('Horizontal alignment of label'), usertext=_('Horz alignment'), formatting=True), 7) s.add( setting.AlignVert('alignVert', 'bottom', descr=_('Vertical alignment of label'), usertext=_('Vert alignment'), formatting=True), 8) s.add( setting.Float('angle', 0., descr=_('Angle of the label in degrees'), usertext=_('Angle'), formatting=True), 9 ) s.add( setting.DistancePt( 'margin', '4pt', descr = _('Margin of fill/border'), usertext=_('Margin'), formatting=True), 10 ) s.add( setting.Bool('clip', False, descr=_('Clip text to its container'), usertext=_('Clip'), formatting=True), 11 ) s.add( setting.Text('Text', descr = _('Text settings'), usertext=_('Text')), pixmap = 'settings_axislabel' ) s.add( setting.ShapeFill( 'Background', descr=_('Fill behind text'), usertext=_('Background')), pixmap = 'settings_bgfill' ) s.add( BorderLine( 'Border', descr=_('Border around text'), usertext=_('Border')), pixmap = 'settings_border' ) # convert text to alignments used by Renderer cnvtalignhorz = { 'left': -1, 'centre': 0, 'right': 1 } cnvtalignvert = { 'top': 1, 'centre': 0, 'bottom': -1 } @property def userdescription(self): """User friendly description.""" s = self.settings return _("text='%s'") % s.label def draw(self, posn, phelper, outerbounds = None): """Draw the text label.""" s = self.settings d = self.document # exit if hidden if s.hide or s.Text.hide: return text = s.get('label').getData(d) xp, yp = self._getPlotterCoords(posn) if xp is None or yp is None: # we can't calculate coordinates return clip = None if s.clip: clip = qt4.QRectF( qt4.QPointF(posn[0], posn[1]), qt4.QPointF(posn[2], posn[3]) ) borderorfill = not s.Border.hide or not s.Background.hide painter = phelper.painter(self, posn, clip=clip) with painter: textpen = s.get('Text').makeQPen(painter) painter.setPen(textpen) font = s.get('Text').makeQFont(painter) margin = s.get('margin').convert(painter) # we should only be able to move non-dataset labels isnotdataset = ( not s.get('xPos').isDataset(d) and not s.get('yPos').isDataset(d) ) controlgraphitems = [] for index, (x, y, t) in enumerate(czip( xp, yp, itertools.cycle(text))): # render the text dx = dy = 0 if borderorfill: dx = -TextLabel.cnvtalignhorz[s.alignHorz]*margin dy = TextLabel.cnvtalignvert[s.alignVert]*margin r = utils.Renderer( painter, font, x+dx, y+dy, t, TextLabel.cnvtalignhorz[s.alignHorz], TextLabel.cnvtalignvert[s.alignVert], s.angle, doc=d) tbounds = r.getBounds() if borderorfill: tbounds = [ tbounds[0]-margin, tbounds[1]-margin, tbounds[2]+margin, tbounds[3]+margin ] rect = qt4.QRectF( qt4.QPointF(tbounds[0], tbounds[1]), qt4.QPointF(tbounds[2], tbounds[3])) path = qt4.QPainterPath() path.addRect(rect) pen = s.get('Border').makeQPenWHide(painter) utils.brushExtFillPath(painter, s.Background, path, stroke=pen) r.render() # add cgi for adjustable positions if isnotdataset: cgi = controlgraph.ControlMovableBox(self, tbounds, phelper, crosspos = (x, y)) cgi.labelpt = (x, y) cgi.widgetposn = posn cgi.index = index controlgraphitems.append(cgi) phelper.setControlGraph(self, controlgraphitems) def updateControlItem(self, cgi): """Update position of point given new name and vals.""" s = self.settings pointsX = list(s.xPos) # make a copy here so original is not modifed pointsY = list(s.yPos) ind = cgi.index # calculate new position coordinate for item xpos, ypos = self._getGraphCoords(cgi.widgetposn, cgi.deltacrosspos[0]+cgi.posn[0], cgi.deltacrosspos[1]+cgi.posn[1]) # this is a small distance away to get delta xposd, yposd = self._getGraphCoords(cgi.widgetposn, cgi.deltacrosspos[0]+cgi.posn[0]+1, cgi.deltacrosspos[1]+cgi.posn[1]+1) if xpos is None or ypos is None: return roundx = utils.round2delt(xpos, xposd) roundy = utils.round2delt(ypos, yposd) pointsX[ind], pointsY[ind] = roundx, roundy operations = ( document.OperationSettingSet(s.get('xPos'), pointsX), document.OperationSettingSet(s.get('yPos'), pointsY) ) self.document.applyOperation( document.OperationMultiple(operations, descr=_('move label')) ) # allow the factory to instantiate a text label document.thefactory.register( TextLabel ) veusz-3.0.1/veusz/helpers/0000775000175000017500000000000013325026670015133 5ustar jssjss00000000000000veusz-3.0.1/veusz/helpers/__init__.py0000664000175000017500000000166413161413406017246 0ustar jssjss00000000000000# Copyright (C) 2008 Jeremy S. Sanders # Email: Jeremy Sanders # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ############################################################################## """Helper compiled routines.""" veusz-3.0.1/veusz/helpers/src/0000775000175000017500000000000013325026667015730 5ustar jssjss00000000000000veusz-3.0.1/veusz/helpers/src/recordpaint/0000775000175000017500000000000013325026670020234 5ustar jssjss00000000000000veusz-3.0.1/veusz/helpers/src/recordpaint/recordpaintengine.h0000664000175000017500000000516213161413406024104 0ustar jssjss00000000000000// Copyright (C) 2011 Jeremy S. Sanders // Email: Jeremy Sanders // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program 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 General Public License for more details. // // You should have received a copy of the GNU General Public License along // with this program; if not, write to the Free Software Foundation, Inc., // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ///////////////////////////////////////////////////////////////////////////// #ifndef RECORD_PAINT_ENGINE__H #define RECORD_PAINT_ENGINE__H #include #include #include #include #include #include #include #include #include #include class RecordPaintDevice; class RecordPaintEngine : public QPaintEngine { public: RecordPaintEngine(); // standard methods to be overridden in engine bool begin(QPaintDevice* pdev); void drawEllipse(const QRectF& rect); void drawEllipse(const QRect& rect); void drawImage(const QRectF& rectangle, const QImage& image, const QRectF& sr, Qt::ImageConversionFlags flags = Qt::AutoColor); void drawLines(const QLineF* lines, int lineCount); void drawLines(const QLine* lines, int lineCount); void drawPath(const QPainterPath& path); void drawPixmap(const QRectF& r, const QPixmap& pm, const QRectF& sr); void drawPoints(const QPointF* points, int pointCount); void drawPoints(const QPoint* points, int pointCount); void drawPolygon(const QPointF* points, int pointCount, QPaintEngine::PolygonDrawMode mode); void drawPolygon(const QPoint* points, int pointCount, QPaintEngine::PolygonDrawMode mode); void drawRects(const QRectF* rects, int rectCount); void drawRects(const QRect* rects, int rectCount); void drawTextItem(const QPointF& p, const QTextItem& textItem); void drawTiledPixmap(const QRectF& rect, const QPixmap& pixmap, const QPointF& p); bool end (); QPaintEngine::Type type () const; void updateState(const QPaintEngineState& state); // return an estimate of number of items drawn int drawItemCount() const { return _drawitemcount; } private: int _drawitemcount; RecordPaintDevice* _pdev; }; #endif veusz-3.0.1/veusz/helpers/src/recordpaint/recordpaintdevice.h0000664000175000017500000000334013161413406024072 0ustar jssjss00000000000000// Copyright (C) 2011 Jeremy S. Sanders // Email: Jeremy Sanders // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program 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 General Public License for more details. // // You should have received a copy of the GNU General Public License along // with this program; if not, write to the Free Software Foundation, Inc., // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ///////////////////////////////////////////////////////////////////////////// #ifndef RECORD_PAINT_DEVICE__H #define RECORD_PAINT_DEVICE__H #include #include #include "paintelement.h" #include "recordpaintengine.h" class RecordPaintDevice : public QPaintDevice { public: RecordPaintDevice(int width, int height, int dpix, int dpiy); ~RecordPaintDevice(); QPaintEngine* paintEngine() const; // play back all void play(QPainter& painter); int metric(QPaintDevice::PaintDeviceMetric metric) const; int drawItemCount() const { return _engine->drawItemCount(); } public: friend class RecordPaintEngine; private: // add an element to the list of maintained elements void addElement(PaintElement* el) { _elements.push_back(el); } private: int _width, _height, _dpix, _dpiy; RecordPaintEngine* _engine; QVector _elements; }; #endif veusz-3.0.1/veusz/helpers/src/recordpaint/recordpaintengine.cpp0000664000175000017500000003507413260405505024445 0ustar jssjss00000000000000// Copyright (C) 2011 Jeremy S. Sanders // Email: Jeremy Sanders // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program 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 General Public License for more details. // // You should have received a copy of the GNU General Public License along // with this program; if not, write to the Free Software Foundation, Inc., // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ///////////////////////////////////////////////////////////////////////////// #include #include #include #include #include #include #include "paintelement.h" #include "recordpaintengine.h" #include "recordpaintdevice.h" namespace { ////////////////////////////////////////////////////////////// // Drawing Elements // these are defined for each type of painting // the QPaintEngine does // draw an ellipse (QRect and QRectF) template class ellipseElement : public PaintElement { public: ellipseElement(const T &rect) : _ellipse(rect) {} void paint(QPainter& painter, const QTransform&) { painter.drawEllipse(_ellipse); } private: T _ellipse; }; typedef ellipseElement EllipseElement; typedef ellipseElement EllipseFElement; // draw QImage class ImageElement : public PaintElement { public: ImageElement(const QRectF& rect, const QImage& image, const QRectF& sr, Qt::ImageConversionFlags flags) : _image(image), _rect(rect), _sr(sr), _flags(flags) {} void paint(QPainter& painter, const QTransform&) { painter.drawImage(_rect, _image, _sr, _flags); } private: QImage _image; QRectF _rect, _sr; Qt::ImageConversionFlags _flags; }; // draw lines // this is for painting QLine and QLineF template class lineElement : public PaintElement { public: lineElement(const T *lines, int linecount) { for(int i = 0; i < linecount; i++) _lines << lines[i]; } void paint(QPainter& painter, const QTransform&) { painter.drawLines(_lines); } private: QVector _lines; }; // specific Line and LineF variants typedef lineElement LineElement; typedef lineElement LineFElement; // draw QPainterPath class PathElement : public PaintElement { public: PathElement(const QPainterPath& path) : _path(path) {} void paint(QPainter& painter, const QTransform&) { painter.drawPath(_path); } private: QPainterPath _path; }; // draw Pixmap class PixmapElement : public PaintElement { public: PixmapElement(const QRectF& r, const QPixmap& pm, const QRectF& sr) : _r(r), _pm(pm), _sr(sr) {} void paint(QPainter& painter, const QTransform&) { painter.drawPixmap(_r, _pm, _sr); } private: QRectF _r; QPixmap _pm; QRectF _sr; }; // draw points (QPoint and QPointF) template class pointElement : public PaintElement { public: pointElement(const T* points, int pointcount) { for(int i=0; i PointElement; typedef pointElement PointFElement; // for QPolygon and QPolygonF template class polyElement: public PaintElement { public: polyElement(const T* points, int pointcount, QPaintEngine::PolygonDrawMode mode) : _mode(mode) { for(int i=0; i PolygonElement; typedef polyElement PolygonFElement; // for QRect and QRectF template class rectElement : public PaintElement { public: rectElement(const T* rects, int rectcount) { for(int i=0; i _rects; }; typedef rectElement RectElement; typedef rectElement RectFElement; // draw Text class TextElement : public PaintElement { public: TextElement(const QPointF& pt, const QTextItem& txt) : _pt(pt), _text(txt.text()) {} void paint(QPainter& painter, const QTransform&) { painter.drawText(_pt, _text); } private: QPointF _pt; QString _text; }; class TiledPixmapElement : public PaintElement { public: TiledPixmapElement(const QRectF& rect, const QPixmap& pixmap, const QPointF& pt) : _rect(rect), _pixmap(pixmap), _pt(pt) {} void paint(QPainter& painter, const QTransform&) { painter.drawTiledPixmap(_rect, _pixmap, _pt); } private: QRectF _rect; QPixmap _pixmap; QPointF _pt; }; /////////////////////////////////////////////////////////////////// // State paint elements // these define and change the state of the painter class BackgroundBrushElement : public PaintElement { public: BackgroundBrushElement(const QBrush& brush) : _brush(brush) {} void paint(QPainter& painter, const QTransform&) { painter.setBackground(_brush); } private: QBrush _brush; }; class BackgroundModeElement : public PaintElement { public: BackgroundModeElement(Qt::BGMode mode) : _mode(mode) {} void paint(QPainter& painter, const QTransform&) { painter.setBackgroundMode(_mode); } private: Qt::BGMode _mode; }; class BrushElement : public PaintElement { public: BrushElement(const QBrush& brush) : _brush(brush) {} void paint(QPainter& painter, const QTransform&) { painter.setBrush(_brush); } private: QBrush _brush; }; class BrushOriginElement : public PaintElement { public: BrushOriginElement(const QPointF& origin) : _origin(origin) {} void paint(QPainter& painter, const QTransform&) { painter.setBrushOrigin(_origin); } private: QPointF _origin; }; class ClipRegionElement : public PaintElement { public: ClipRegionElement(Qt::ClipOperation op, const QRegion& region) : _op(op), _region(region) {} void paint(QPainter& painter, const QTransform&) { painter.setClipRegion(_region, _op); } private: Qt::ClipOperation _op; QRegion _region; }; class ClipPathElement : public PaintElement { public: ClipPathElement(Qt::ClipOperation op, const QPainterPath& region) : _op(op), _region(region) {} void paint(QPainter& painter, const QTransform&) { painter.setClipPath(_region, _op); } private: Qt::ClipOperation _op; QPainterPath _region; }; class CompositionElement : public PaintElement { public: CompositionElement(QPainter::CompositionMode mode) : _mode(mode) {} void paint(QPainter& painter, const QTransform&) { painter.setCompositionMode(_mode); } private: QPainter::CompositionMode _mode; }; class FontElement : public PaintElement { public: FontElement(const QFont& font, int dpi) : _dpi(dpi), _font(font) {} void paint(QPainter& painter, const QTransform&) { QFont tempfont(_font); if( tempfont.pointSizeF() > 0. ) { // scale font sizes in points using dpi ratio int thisdpi = painter.device()->logicalDpiY(); double scale = tempfont.pointSizeF() / thisdpi * _dpi; tempfont.setPointSizeF(scale); } painter.setFont(tempfont); } private: int _dpi; QFont _font; }; class TransformElement : public PaintElement { public: TransformElement(const QTransform& t) : _t(t) {} void paint(QPainter& painter, const QTransform& origtransform) { painter.setWorldTransform(origtransform); painter.setWorldTransform(_t, true); } private: QTransform _t; }; class ClipEnabledElement : public PaintElement { public: ClipEnabledElement(bool enabled) : _enabled(enabled) {} void paint(QPainter& painter, const QTransform&) { painter.setClipping(_enabled); } private: bool _enabled; }; class PenElement : public PaintElement { public: PenElement(const QPen& pen) : _pen(pen) {} void paint(QPainter& painter, const QTransform&) { painter.setPen(_pen); } private: QPen _pen; }; class HintsElement : public PaintElement { public: HintsElement(QPainter::RenderHints hints) : _hints(hints) {} void paint(QPainter& painter, const QTransform&) { painter.setRenderHints(_hints); } private: QPainter::RenderHints _hints; }; // end anonymous block } /////////////////////////////////////////////////////////////////// // Paint engine follows RecordPaintEngine::RecordPaintEngine() : QPaintEngine(QPaintEngine::AllFeatures), _drawitemcount(0), _pdev(0) { } bool RecordPaintEngine::begin(QPaintDevice* pdev) { // old style C cast - probably should use dynamic_cast _pdev = (RecordPaintDevice*)(pdev); // signal started ok return 1; } // for each type of drawing command we add a new element // to the list maintained by the device void RecordPaintEngine::drawEllipse(const QRectF& rect) { _pdev->addElement( new EllipseFElement(rect) ); _drawitemcount++; } void RecordPaintEngine::drawEllipse(const QRect& rect) { _pdev->addElement( new EllipseElement(rect) ); _drawitemcount++; } void RecordPaintEngine::drawImage(const QRectF& rectangle, const QImage& image, const QRectF& sr, Qt::ImageConversionFlags flags) { _pdev->addElement( new ImageElement(rectangle, image, sr, flags) ); _drawitemcount++; } void RecordPaintEngine::drawLines(const QLineF* lines, int lineCount) { _pdev->addElement( new LineFElement(lines, lineCount) ); _drawitemcount += lineCount; } void RecordPaintEngine::drawLines(const QLine* lines, int lineCount) { _pdev->addElement( new LineElement(lines, lineCount) ); _drawitemcount += lineCount; } void RecordPaintEngine::drawPath(const QPainterPath& path) { _pdev->addElement( new PathElement(path) ); _drawitemcount++; } void RecordPaintEngine::drawPixmap(const QRectF& r, const QPixmap& pm, const QRectF& sr) { _pdev->addElement( new PixmapElement(r, pm, sr) ); _drawitemcount++; } void RecordPaintEngine::drawPoints(const QPointF* points, int pointCount) { _pdev->addElement( new PointFElement(points, pointCount) ); _drawitemcount += pointCount; } void RecordPaintEngine::drawPoints(const QPoint* points, int pointCount) { _pdev->addElement( new PointElement(points, pointCount) ); _drawitemcount += pointCount; } void RecordPaintEngine::drawPolygon(const QPointF* points, int pointCount, QPaintEngine::PolygonDrawMode mode) { _pdev->addElement( new PolygonFElement(points, pointCount, mode) ); _drawitemcount += pointCount; } void RecordPaintEngine::drawPolygon(const QPoint* points, int pointCount, QPaintEngine::PolygonDrawMode mode) { _pdev->addElement( new PolygonElement(points, pointCount, mode) ); _drawitemcount += pointCount; } void RecordPaintEngine::drawRects(const QRectF* rects, int rectCount) { _pdev->addElement( new RectFElement( rects, rectCount ) ); _drawitemcount += rectCount; } void RecordPaintEngine::drawRects(const QRect* rects, int rectCount) { _pdev->addElement( new RectElement( rects, rectCount ) ); _drawitemcount += rectCount; } void RecordPaintEngine::drawTextItem(const QPointF& p, const QTextItem& textItem) { _pdev->addElement( new TextElement(p, textItem) ); _drawitemcount += textItem.text().length(); } void RecordPaintEngine::drawTiledPixmap(const QRectF& rect, const QPixmap& pixmap, const QPointF& p) { _pdev->addElement( new TiledPixmapElement(rect, pixmap, p) ); _drawitemcount += 1; } bool RecordPaintEngine::end() { // signal finished ok return 1; } QPaintEngine::Type RecordPaintEngine::type () const { // some sort of random number for the ID of the engine type return QPaintEngine::Type(int(QPaintEngine::User)+34); } void RecordPaintEngine::updateState(const QPaintEngineState& state) { // we add a new element for each change of state // these are replayed later const int flags = state.state(); if( flags & QPaintEngine::DirtyPen ) _pdev->addElement( new PenElement( state.pen() ) ); if( flags & QPaintEngine::DirtyBrush ) _pdev->addElement( new BrushElement( state.brush() ) ); if( flags & QPaintEngine::DirtyBrushOrigin ) _pdev->addElement( new BrushOriginElement( state.brushOrigin() ) ); if( flags & QPaintEngine::DirtyFont ) _pdev->addElement( new FontElement( state.font(), _pdev->_dpiy ) ); if( flags & QPaintEngine::DirtyBackground ) _pdev->addElement( new BackgroundBrushElement( state.backgroundBrush() ) ); if( flags & QPaintEngine::DirtyBackgroundMode ) _pdev->addElement( new BackgroundModeElement( state.backgroundMode() ) ); if( flags & QPaintEngine::DirtyTransform ) _pdev->addElement( new TransformElement( state.transform() ) ); if( flags & QPaintEngine::DirtyClipRegion ) _pdev->addElement( new ClipRegionElement( state.clipOperation(), state.clipRegion() ) ); if( flags & QPaintEngine::DirtyClipPath ) _pdev->addElement( new ClipPathElement( state.clipOperation(), state.clipPath() ) ); if( flags & QPaintEngine::DirtyHints ) _pdev->addElement( new HintsElement( state.renderHints() ) ); if( flags & QPaintEngine::DirtyCompositionMode ) _pdev->addElement( new CompositionElement( state.compositionMode() ) ); if( flags & QPaintEngine::DirtyClipEnabled ) _pdev->addElement( new ClipEnabledElement( state.isClipEnabled() ) ); } veusz-3.0.1/veusz/helpers/src/recordpaint/paintelement.h0000664000175000017500000000221013161413406023060 0ustar jssjss00000000000000// Copyright (C) 2011 Jeremy S. Sanders // Email: Jeremy Sanders // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program 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 General Public License for more details. // // You should have received a copy of the GNU General Public License along // with this program; if not, write to the Free Software Foundation, Inc., // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ///////////////////////////////////////////////////////////////////////////// #ifndef PAINTELEMENT_H #define PAINTELEMENT_H class QPainter; class QTransform; class PaintElement { public: virtual ~PaintElement() {}; virtual void paint(QPainter& painter, const QTransform& origtransform) = 0; }; #endif veusz-3.0.1/veusz/helpers/src/recordpaint/recordpaint.sip0000664000175000017500000000254513161413406023264 0ustar jssjss00000000000000// Copyright (C) 2009 Jeremy S. Sanders // Email: Jeremy Sanders // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program 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 General Public License for more details. // // You should have received a copy of the GNU General Public License along // with this program; if not, write to the Free Software Foundation, Inc., // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ///////////////////////////////////////////////////////////////////////////// %Module(name=recordpaint) %Import(name=QtCore/QtCoremod.sip) %Import(name=QtGui/QtGuimod.sip) class RecordPaintDevice : QPaintDevice { %TypeHeaderCode #include %End public: RecordPaintDevice(int width, int height, int dpix, int dpiy); ~RecordPaintDevice(); void play(QPainter& painter); QPaintEngine* paintEngine() const; int metric(QPaintDevice::PaintDeviceMetric metric) const; int drawItemCount() const; }; veusz-3.0.1/veusz/helpers/src/recordpaint/recordpaintdevice.cpp0000664000175000017500000000452313242310603024424 0ustar jssjss00000000000000// Copyright (C) 2011 Jeremy S. Sanders // Email: Jeremy Sanders // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program 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 General Public License for more details. // // You should have received a copy of the GNU General Public License along // with this program; if not, write to the Free Software Foundation, Inc., // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ///////////////////////////////////////////////////////////////////////////// #include #include #include "recordpaintdevice.h" #include "recordpaintengine.h" #define INCH_MM 25.4 RecordPaintDevice::RecordPaintDevice(int width, int height, int dpix, int dpiy) :_width(width), _height(height), _dpix(dpix), _dpiy(dpiy), _engine(new RecordPaintEngine) { } RecordPaintDevice::~RecordPaintDevice() { delete _engine; qDeleteAll(_elements); } QPaintEngine* RecordPaintDevice::paintEngine() const { return _engine; } int RecordPaintDevice::metric(QPaintDevice::PaintDeviceMetric metric) const { switch(metric) { case QPaintDevice::PdmWidth: return _width; case QPaintDevice::PdmHeight: return _height; case QPaintDevice::PdmWidthMM: return int(_width * INCH_MM / _dpix); case QPaintDevice::PdmHeightMM: return int(_height * INCH_MM / _dpiy); case QPaintDevice::PdmNumColors: return std::numeric_limits::max(); case QPaintDevice::PdmDepth: return 24; case QPaintDevice::PdmDpiX: case QPaintDevice::PdmPhysicalDpiX: return _dpix; case QPaintDevice::PdmDpiY: case QPaintDevice::PdmPhysicalDpiY: return _dpiy; case QPaintDevice::PdmDevicePixelRatio: return 1; default: // fallback return QPaintDevice::metric(metric); } } void RecordPaintDevice::play(QPainter& painter) { QTransform origtransform(painter.worldTransform()); foreach(PaintElement* el, _elements) { el->paint(painter, origtransform); } } veusz-3.0.1/veusz/helpers/src/qtmml/0000775000175000017500000000000013325026670017054 5ustar jssjss00000000000000veusz-3.0.1/veusz/helpers/src/qtmml/QtMmlDocument0000664000175000017500000000003113161413406021515 0ustar jssjss00000000000000#include "qtmmlwidget.h" veusz-3.0.1/veusz/helpers/src/qtmml/qtmml.sip0000664000175000017500000000637413161413406020730 0ustar jssjss00000000000000// Copyright (C) 2012 Jeremy S. Sanders // Email: Jeremy Sanders // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program 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 General Public License for more details. // // You should have received a copy of the GNU General Public License along // with this program; if not, write to the Free Software Foundation, Inc., // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ///////////////////////////////////////////////////////////////////////////// %Module(name=qtmml) %Import(name=QtCore/QtCoremod.sip) %Import(name=QtGui/QtGuimod.sip) %Import(name=QtWidgets/QtWidgetsmod.sip) %ModuleHeaderCode // we have to do this twice, so define as a template namespace { template void callSetContent(T* obj, const QString &txt, int *sipIsErr) { QString errormsg; int errorline, errorcol; bool retn; Py_BEGIN_ALLOW_THREADS; retn = obj->setContent(txt, &errormsg, &errorline, &errorcol); Py_END_ALLOW_THREADS; if( ! retn ) { // if fails, create an exception QByteArray extxt( QString("Error on line %1, column %2: \"%3\""). arg(errorline).arg(errorcol).arg(errormsg). toUtf8() ); PyObject *pystr = PyUnicode_DecodeUTF8(extxt.data(), extxt.size(), "ignore"); if( pystr != 0 ) { PyErr_SetObject(PyExc_ValueError, pystr); Py_DECREF(pystr); *sipIsErr = 1; } } } } %End class QtMmlWidget : QFrame { %TypeHeaderCode #include %End public: enum MmlFont { NormalFont, FrakturFont, SansSerifFont, ScriptFont, MonospaceFont, DoublestruckFont }; QtMmlWidget(QWidget *parent = 0); ~QtMmlWidget(); QString fontName(MmlFont type) const; void setFontName(MmlFont type, const QString &name); int baseFontPointSize() const; void setBaseFontPointSize(int size); // bool setContent(const QString &text, QString *errorMsg = 0, // int *errorLine = 0, int *errorColumn = 0); void setContent(const QString &text); %MethodCode callSetContent(sipCpp, *a0, &sipIsErr); %End void dump() const; virtual QSize sizeHint() const; void setDrawFrames(bool b); bool drawFrames() const; void clear(); protected: virtual void paintEvent(QPaintEvent *e); }; class QtMmlDocument { %TypeHeaderCode #include %End public: QtMmlDocument(); ~QtMmlDocument(); void clear(); // bool setContent(QString text, QString *errorMsg = 0, // int *errorLine = 0, int *errorColumn = 0); void setContent(const QString &text); %MethodCode callSetContent(sipCpp, *a0, &sipIsErr); %End void paint(QPainter *p, const QPoint &pos) const; QSize size() const; QString fontName(QtMmlWidget::MmlFont type) const; void setFontName(QtMmlWidget::MmlFont type, const QString &name); int baseFontPointSize() const; void setBaseFontPointSize(int size); }; veusz-3.0.1/veusz/helpers/src/qtmml/qtmmlwidget.h0000664000175000017500000001006013161413406021553 0ustar jssjss00000000000000/**************************************************************************** ** ** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). ** All rights reserved. ** Contact: Nokia Corporation (qt-info@nokia.com) ** ** This file is part of a Qt Solutions component. ** ** Commercial Usage ** Licensees holding valid Qt Commercial licenses may use this file in ** accordance with the Qt Solutions Commercial License Agreement provided ** with the Software or, alternatively, in accordance with the terms ** contained in a written agreement between you and Nokia. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Nokia gives you certain ** additional rights. These rights are described in the Nokia Qt LGPL ** Exception version 1.1, included in the file LGPL_EXCEPTION.txt in this ** package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** Please note Third Party Software included with Qt Solutions may impose ** additional restrictions and it is the user's responsibility to ensure ** that they have met the licensing requirements of the GPL, LGPL, or Qt ** Solutions Commercial license and the relevant license of the Third ** Party Software they are using. ** ** If you are unsure which license is appropriate for your use, please ** contact Nokia at qt-info@nokia.com. ** ****************************************************************************/ #ifndef QTMMLWIDGET_H #define QTMMLWIDGET_H #include #include class MmlDocument; #if defined(Q_WS_WIN) # if !defined(QT_QTMMLWIDGET_EXPORT) && !defined(QT_QTMMLWIDGET_IMPORT) # define QT_QTMMLWIDGET_EXPORT # elif defined(QT_QTMMLWIDGET_IMPORT) # if defined(QT_QTMMLWIDGET_EXPORT) # undef QT_QTMMLWIDGET_EXPORT # endif # define QT_QTMMLWIDGET_EXPORT __declspec(dllimport) # elif defined(QT_QTMMLWIDGET_EXPORT) # undef QT_QTMMLWIDGET_EXPORT # define QT_QTMMLWIDGET_EXPORT __declspec(dllexport) # endif #else # define QT_QTMMLWIDGET_EXPORT #endif class QT_QTMMLWIDGET_EXPORT QtMmlWidget : public QFrame { public: enum MmlFont { NormalFont, FrakturFont, SansSerifFont, ScriptFont, MonospaceFont, DoublestruckFont }; QtMmlWidget(QWidget *parent = 0); ~QtMmlWidget(); QString fontName(MmlFont type) const; void setFontName(MmlFont type, const QString &name); int baseFontPointSize() const; void setBaseFontPointSize(int size); bool setContent(const QString &text, QString *errorMsg = 0, int *errorLine = 0, int *errorColumn = 0); void dump() const; virtual QSize sizeHint() const; void setDrawFrames(bool b); bool drawFrames() const; void clear(); protected: virtual void paintEvent(QPaintEvent *e); private: MmlDocument *m_doc; }; class QT_QTMMLWIDGET_EXPORT QtMmlDocument { public: QtMmlDocument(); ~QtMmlDocument(); void clear(); bool setContent(QString text, QString *errorMsg = 0, int *errorLine = 0, int *errorColumn = 0); void paint(QPainter *p, const QPoint &pos) const; QSize size() const; QString fontName(QtMmlWidget::MmlFont type) const; void setFontName(QtMmlWidget::MmlFont type, const QString &name); int baseFontPointSize() const; void setBaseFontPointSize(int size); private: MmlDocument *m_doc; }; #endif veusz-3.0.1/veusz/helpers/src/qtmml/qtmmlwidget.cpp0000664000175000017500000071762013161413406022126 0ustar jssjss00000000000000/**************************************************************************** ** ** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). ** All rights reserved. ** Contact: Nokia Corporation (qt-info@nokia.com) ** ** This file is part of a Qt Solutions component. ** ** Commercial Usage ** Licensees holding valid Qt Commercial licenses may use this file in ** accordance with the Qt Solutions Commercial License Agreement provided ** with the Software or, alternatively, in accordance with the terms ** contained in a written agreement between you and Nokia. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Nokia gives you certain ** additional rights. These rights are described in the Nokia Qt LGPL ** Exception version 1.1, included in the file LGPL_EXCEPTION.txt in this ** package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** Please note Third Party Software included with Qt Solutions may impose ** additional restrictions and it is the user's responsibility to ensure ** that they have met the licensing requirements of the GPL, LGPL, or Qt ** Solutions Commercial license and the relevant license of the Third ** Party Software they are using. ** ** If you are unsure which license is appropriate for your use, please ** contact Nokia at qt-info@nokia.com. ** ****************************************************************************/ #include #include #include #include #include #include #include "qtmmlwidget.h" // ******************************************************************* // Declarations // ******************************************************************* #define ROUND(a) (int)((a)+.5) static bool g_draw_frames = false; static const double g_mfrac_spacing = 0.1; static const double g_mroot_base_margin = 0.1; static const double g_script_size_multiplier = 0.7071; // sqrt(1/2) static const int g_min_font_point_size = 8; static const QChar g_radical_char = QChar(0x1A, 0x22); static const unsigned g_oper_spec_rows = 9; // use unnamed namespace namespace { struct Mml { enum NodeType { NoNode = 0, MiNode, MnNode, MfracNode, MrowNode, MsqrtNode, MrootNode, MsupNode, MsubNode, MsubsupNode, MoNode, MstyleNode, TextNode, MphantomNode, MfencedNode, MtableNode, MtrNode, MtdNode, MoverNode, MunderNode, MunderoverNode, MerrorNode, MtextNode, MpaddedNode, MspaceNode, MalignMarkNode, UnknownNode }; enum MathVariant { NormalMV = 0x0000, BoldMV = 0x0001, ItalicMV = 0x0002, DoubleStruckMV = 0x0004, ScriptMV = 0x0008, FrakturMV = 0x0010, SansSerifMV = 0x0020, MonospaceMV = 0x0040 }; enum FormType { PrefixForm, InfixForm, PostfixForm }; enum ColAlign { ColAlignLeft, ColAlignCenter, ColAlignRight }; enum RowAlign { RowAlignTop, RowAlignCenter, RowAlignBottom, RowAlignAxis, RowAlignBaseline }; enum FrameType { FrameNone, FrameSolid, FrameDashed }; struct FrameSpacing { FrameSpacing(int hor = 0, int ver = 0) : m_hor(hor), m_ver(ver) {} int m_hor, m_ver; }; }; struct OperSpec { enum StretchDir { NoStretch, HStretch, VStretch, HVStretch }; const char *name; Mml::FormType form; const char *attributes[g_oper_spec_rows]; StretchDir stretch_dir; }; struct NodeSpec { Mml::NodeType type; const char *tag; const char *type_str; int child_spec; const char *child_types; const char *attributes; enum ChildSpec { ChildAny = -1, // any number of children allowed ChildIgnore = -2, // do not build subexpression of children ImplicitMrow = -3 // if more than one child, build mrow }; }; struct EntitySpec { const char *name; const char *value; }; typedef QMap MmlAttributeMap; class MmlNode; } // namespace class MmlDocument : public Mml { public: MmlDocument(); ~MmlDocument(); void clear(); bool setContent(QString text, QString *errorMsg = 0, int *errorLine = 0, int *errorColumn = 0); void paint(QPainter *p, const QPoint &pos) const; void dump() const; QSize size() const; void layout(); QString fontName(QtMmlWidget::MmlFont type) const; void setFontName(QtMmlWidget::MmlFont type, const QString &name); int baseFontPointSize() const { return m_base_font_point_size; } void setBaseFontPointSize(int size) { m_base_font_point_size = size; } QColor foregroundColor() const { return m_foreground_color; } void setForegroundColor(const QColor &color) { m_foreground_color = color; } QColor backgroundColor() const { return m_background_color; } void setBackgroundColor(const QColor &color) { m_background_color = color; } private: void _dump(const MmlNode *node, QString &indent) const; bool insertChild(MmlNode *parent, MmlNode *new_node, QString *errorMsg); MmlNode *domToMml(const QDomNode &dom_node, bool *ok, QString *errorMsg); MmlNode *createNode(NodeType type, const MmlAttributeMap &mml_attr, const QString &mml_value, QString *errorMsg); MmlNode *createImplicitMrowNode(const QDomNode &dom_node, bool *ok, QString *errorMsg); void insertOperator(MmlNode *node, const QString &text); MmlNode *m_root_node; QString m_normal_font_name; QString m_fraktur_font_name; QString m_sans_serif_font_name; QString m_script_font_name; QString m_monospace_font_name; QString m_doublestruck_font_name; int m_base_font_point_size; QColor m_foreground_color; QColor m_background_color; }; namespace { class MmlNode : public Mml { friend class ::MmlDocument; public: MmlNode(NodeType type, MmlDocument *document, const MmlAttributeMap &attribute_map); virtual ~MmlNode(); // Mml stuff NodeType nodeType() const { return m_node_type; } virtual QString toStr() const; void setRelOrigin(const QPoint &rel_origin); QPoint relOrigin() const { return m_rel_origin; } void stretchTo(const QRect &rect); bool isStretched() const { return m_stretched; } QPoint devicePoint(const QPoint &p) const; QRect myRect() const { return m_my_rect; } QRect parentRect() const; virtual QRect deviceRect() const; void updateMyRect(); virtual void setMyRect(const QRect &rect) { m_my_rect = rect; } virtual void stretch(); virtual void layout(); virtual void paint(QPainter *p); int basePos() const; int overlinePos() const; int underlinePos() const; int em() const; int ex() const; QString explicitAttribute(const QString &name, const QString &def = QString::null) const; QString inheritAttributeFromMrow(const QString &name, const QString &def = QString::null) const; virtual QFont font() const; virtual QColor color() const; virtual QColor background() const; virtual int scriptlevel(const MmlNode *child = 0) const; // Node stuff MmlDocument *document() const { return m_document; } MmlNode *parent() const { return m_parent; } MmlNode *firstChild() const { return m_first_child; } MmlNode *nextSibling() const { return m_next_sibling; } MmlNode *previousSibling() const { return m_previous_sibling; } MmlNode *lastSibling() const; MmlNode *firstSibling() const; bool isLastSibling() const { return m_next_sibling == 0; } bool isFirstSibling() const { return m_previous_sibling == 0; } bool hasChildNodes() const { return m_first_child != 0; } protected: virtual void layoutSymbol(); virtual void paintSymbol(QPainter *p) const; virtual QRect symbolRect() const { return QRect(0, 0, 0, 0); } MmlNode *parentWithExplicitAttribute(const QString &name, NodeType type = NoNode); int interpretSpacing(const QString &value, bool *ok) const; private: MmlAttributeMap m_attribute_map; bool m_stretched; QRect m_my_rect, m_parent_rect; QPoint m_rel_origin; NodeType m_node_type; MmlDocument *m_document; MmlNode *m_parent, *m_first_child, *m_next_sibling, *m_previous_sibling; }; class MmlTokenNode : public MmlNode { public: MmlTokenNode(NodeType type, MmlDocument *document, const MmlAttributeMap &attribute_map) : MmlNode(type, document, attribute_map) {} QString text() const; }; class MmlMphantomNode : public MmlNode { public: MmlMphantomNode(MmlDocument *document, const MmlAttributeMap &attribute_map) : MmlNode(MphantomNode, document, attribute_map) {} virtual void paint(QPainter *) {} }; class MmlUnknownNode : public MmlNode { public: MmlUnknownNode(MmlDocument *document, const MmlAttributeMap &attribute_map) : MmlNode(UnknownNode, document, attribute_map) {} }; class MmlMfencedNode : public MmlNode { public: MmlMfencedNode(MmlDocument *document, const MmlAttributeMap &attribute_map) : MmlNode(MfencedNode, document, attribute_map) {} }; class MmlMalignMarkNode : public MmlNode { public: MmlMalignMarkNode(MmlDocument *document) : MmlNode(MalignMarkNode, document, MmlAttributeMap()) {} }; class MmlMfracNode : public MmlNode { public: MmlMfracNode(MmlDocument *document, const MmlAttributeMap &attribute_map) : MmlNode(MfracNode, document, attribute_map) {} MmlNode *numerator() const; MmlNode *denominator() const; protected: virtual void layoutSymbol(); virtual void paintSymbol(QPainter *p) const; virtual QRect symbolRect() const; }; class MmlMrowNode : public MmlNode { public: MmlMrowNode(MmlDocument *document, const MmlAttributeMap &attribute_map) : MmlNode(MrowNode, document, attribute_map) {} }; class MmlRootBaseNode : public MmlNode { public: MmlRootBaseNode(NodeType type, MmlDocument *document, const MmlAttributeMap &attribute_map) : MmlNode(type, document, attribute_map) {} MmlNode *base() const; MmlNode *index() const; virtual int scriptlevel(const MmlNode *child = 0) const; protected: virtual void layoutSymbol(); virtual void paintSymbol(QPainter *p) const; virtual QRect symbolRect() const; int tailWidth() const; }; class MmlMrootNode : public MmlRootBaseNode { public: MmlMrootNode(MmlDocument *document, const MmlAttributeMap &attribute_map) : MmlRootBaseNode(MrootNode, document, attribute_map) {} }; class MmlMsqrtNode : public MmlRootBaseNode { public: MmlMsqrtNode(MmlDocument *document, const MmlAttributeMap &attribute_map) : MmlRootBaseNode(MsqrtNode, document, attribute_map) {} }; class MmlTextNode : public MmlNode { public: MmlTextNode(const QString &text, MmlDocument *document); virtual QString toStr() const; QString text() const { return m_text; } // TextNodes are not xml elements, so they can't have attributes of // their own. Everything is taken from the parent. virtual QFont font() const { return parent()->font(); } virtual int scriptlevel(const MmlNode* = 0) const { return parent()->scriptlevel(this); } virtual QColor color() const { return parent()->color(); } virtual QColor background() const { return parent()->background(); } protected: virtual void paintSymbol(QPainter *p) const; virtual QRect symbolRect() const; QString m_text; }; class MmlMiNode : public MmlTokenNode { public: MmlMiNode(MmlDocument *document, const MmlAttributeMap &attribute_map) : MmlTokenNode(MiNode, document, attribute_map) {} }; class MmlMnNode : public MmlTokenNode { public: MmlMnNode(MmlDocument *document, const MmlAttributeMap &attribute_map) : MmlTokenNode(MnNode, document, attribute_map) {} }; class MmlSubsupBaseNode : public MmlNode { public: MmlSubsupBaseNode(NodeType type, MmlDocument *document, const MmlAttributeMap &attribute_map) : MmlNode(type, document, attribute_map) {} MmlNode *base() const; MmlNode *sscript() const; virtual int scriptlevel(const MmlNode *child = 0) const; }; class MmlMsupNode : public MmlSubsupBaseNode { public: MmlMsupNode(MmlDocument *document, const MmlAttributeMap &attribute_map) : MmlSubsupBaseNode(MsupNode, document, attribute_map) {} protected: virtual void layoutSymbol(); }; class MmlMsubNode : public MmlSubsupBaseNode { public: MmlMsubNode(MmlDocument *document, const MmlAttributeMap &attribute_map) : MmlSubsupBaseNode(MsubNode, document, attribute_map) {} protected: virtual void layoutSymbol(); }; class MmlMsubsupNode : public MmlNode { public: MmlMsubsupNode(MmlDocument *document, const MmlAttributeMap &attribute_map) : MmlNode(MsubsupNode, document, attribute_map) {} MmlNode *base() const; MmlNode *superscript() const; MmlNode *subscript() const; virtual int scriptlevel(const MmlNode *child = 0) const; protected: virtual void layoutSymbol(); }; class MmlMoNode : public MmlTokenNode { public: MmlMoNode(MmlDocument *document, const MmlAttributeMap &attribute_map); QString dictionaryAttribute(const QString &name) const; virtual void stretch(); virtual int lspace() const; virtual int rspace() const; virtual QString toStr() const; protected: virtual void layoutSymbol(); virtual QRect symbolRect() const; virtual FormType form() const; private: const OperSpec *m_oper_spec; }; class MmlMstyleNode : public MmlNode { public: MmlMstyleNode(MmlDocument *document, const MmlAttributeMap &attribute_map) : MmlNode(MstyleNode, document, attribute_map) {} }; class MmlTableBaseNode : public MmlNode { public: MmlTableBaseNode(NodeType type, MmlDocument *document, const MmlAttributeMap &attribute_map) : MmlNode(type, document, attribute_map) {} }; class MmlMtableNode : public MmlTableBaseNode { public: MmlMtableNode(MmlDocument *document, const MmlAttributeMap &attribute_map) : MmlTableBaseNode(MtableNode, document, attribute_map) {} int rowspacing() const; int columnspacing() const; int framespacing_hor() const; int framespacing_ver() const; FrameType frame() const; FrameType columnlines(int idx) const; FrameType rowlines(int idx) const; protected: virtual void layoutSymbol(); virtual QRect symbolRect() const; virtual void paintSymbol(QPainter *p) const; private: struct CellSizeData { void init(const MmlNode *first_row); QList col_widths, row_heights; int numCols() const { return col_widths.count(); } int numRows() const { return row_heights.count(); } uint colWidthSum() const; uint rowHeightSum() const; }; CellSizeData m_cell_size_data; int m_content_width, m_content_height; }; class MmlMtrNode : public MmlTableBaseNode { public: MmlMtrNode(MmlDocument *document, const MmlAttributeMap &attribute_map) : MmlTableBaseNode(MtrNode, document, attribute_map) {} void layoutCells(const QList &col_widths, int col_spc); }; class MmlMtdNode : public MmlTableBaseNode { public: MmlMtdNode(MmlDocument *document, const MmlAttributeMap &attribute_map) : MmlTableBaseNode(MtdNode, document, attribute_map) { m_scriptlevel_adjust = 0; } virtual void setMyRect(const QRect &rect); ColAlign columnalign(); RowAlign rowalign(); uint colNum(); uint rowNum(); virtual int scriptlevel(const MmlNode *child = 0) const; private: int m_scriptlevel_adjust; // added or subtracted to scriptlevel to // make contents fit the cell }; class MmlMoverNode : public MmlNode { public: MmlMoverNode(MmlDocument *document, const MmlAttributeMap &attribute_map) : MmlNode(MoverNode, document, attribute_map) {} virtual int scriptlevel(const MmlNode *node = 0) const; protected: virtual void layoutSymbol(); }; class MmlMunderNode : public MmlNode { public: MmlMunderNode(MmlDocument *document, const MmlAttributeMap &attribute_map) : MmlNode(MunderNode, document, attribute_map) {} virtual int scriptlevel(const MmlNode *node = 0) const; protected: virtual void layoutSymbol(); }; class MmlMunderoverNode : public MmlNode { public: MmlMunderoverNode(MmlDocument *document, const MmlAttributeMap &attribute_map) : MmlNode(MunderoverNode, document, attribute_map) {} virtual int scriptlevel(const MmlNode *node = 0) const; protected: virtual void layoutSymbol(); }; class MmlMerrorNode : public MmlNode { public: MmlMerrorNode(MmlDocument *document, const MmlAttributeMap &attribute_map) : MmlNode(MerrorNode, document, attribute_map) {} }; class MmlMtextNode : public MmlNode { public: MmlMtextNode(MmlDocument *document, const MmlAttributeMap &attribute_map) : MmlNode(MtextNode, document, attribute_map) {} }; class MmlMpaddedNode : public MmlNode { public: MmlMpaddedNode(MmlDocument *document, const MmlAttributeMap &attribute_map) : MmlNode(MpaddedNode, document, attribute_map) {} public: int lspace() const; int width() const; int height() const; int depth() const; protected: int interpretSpacing(QString value, int base_value, bool *ok) const; virtual void layoutSymbol(); virtual QRect symbolRect() const; }; class MmlMspaceNode : public MmlNode { public: MmlMspaceNode(MmlDocument *document, const MmlAttributeMap &attribute_map) : MmlNode(MspaceNode, document, attribute_map) {} }; } static const NodeSpec *mmlFindNodeSpec(Mml::NodeType type); static const NodeSpec *mmlFindNodeSpec(const QString &tag); static bool mmlCheckChildType(Mml::NodeType parent_type, Mml::NodeType child_type, QString *error_str); static bool mmlCheckAttributes(Mml::NodeType child_type, const MmlAttributeMap &attr, QString *error_str); static QString mmlDictAttribute(const QString &name, const OperSpec *spec); static const OperSpec *mmlFindOperSpec(const QString &name, Mml::FormType form); static int interpretSpacing(QString name, int em, int ex, bool *ok); static int interpretPercentSpacing(QString value, int base, bool *ok); static uint interpretMathVariant(const QString &value, bool *ok); static Mml::FormType interpretForm(const QString &value, bool *ok); static Mml::FrameType interpretFrameType(const QString &value_list, uint idx, bool *ok); static Mml::FrameSpacing interpretFrameSpacing(const QString &value_list, int em, int ex, bool *ok); static Mml::ColAlign interpretColAlign(const QString &value_list, uint colnum, bool *ok); static Mml::RowAlign interpretRowAlign(const QString &value_list, uint rownum, bool *ok); static Mml::FrameType interpretFrameType(const QString &value_list, uint idx, bool *ok); static QFont interpretDepreciatedFontAttr(const MmlAttributeMap &font_attr, QFont &fn, int em, int ex); static QFont interpretMathSize(QString value, QFont &fn, int em, int ex, bool *ok); static QString interpretListAttr(const QString &value_list, int idx, const QString &def); static QString rectToStr(const QRect &rect); static QString entityDeclarations(); #define MML_ATT_COMMON " class style id xref actiontype " #define MML_ATT_FONTSIZE " fontsize fontweight fontstyle fontfamily color " #define MML_ATT_MATHVARIANT " mathvariant mathsize mathcolor mathbackground " #define MML_ATT_FONTINFO MML_ATT_FONTSIZE MML_ATT_MATHVARIANT #define MML_ATT_OPINFO " form fence separator lspace rspace stretchy symmetric " \ " maxsize minsize largeop movablelimits accent " #define MML_ATT_SIZEINFO " width height depth " #define MML_ATT_TABLEINFO " align rowalign columnalign columnwidth groupalign " \ " alignmentscope side rowspacing columnspacing rowlines " \ " columnlines width frame framespacing equalrows " \ " equalcolumns displaystyle " #define MML_ATT_MFRAC " bevelled numalign denomalign linethickness " #define MML_ATT_MSTYLE MML_ATT_FONTINFO MML_ATT_OPINFO \ " scriptlevel lquote rquote linethickness displaystyle " \ " scriptsizemultiplier scriptminsize background " \ " veryverythinmathspace verythinmathspace thinmathspace " \ " mediummathspace thickmathspace verythickmathspace " \ " veryverythickmathspace open close separators " \ " subscriptshift superscriptshift accentunder tableinfo " \ " rowspan columnspan edge selection bevelled " #define MML_ATT_MTABLE " align rowalign columnalign groupalign alignmentscope " \ " columnwidth width rowspacing columnspacing rowlines columnlines " \ " frame framespacing equalrows equalcolumns displaystyle side " \ " minlabelspacing " static const NodeSpec g_node_spec_data[] = { // type tag type_str child_spec child_types attributes ""=none, 0=any // ----------------------- --------------- ------------------- ----------------------- ------------------------ ------------------------------------ { Mml::MiNode, "mi", "MiNode", NodeSpec::ChildAny, " TextNode MalignMark ", MML_ATT_COMMON MML_ATT_FONTINFO }, { Mml::MnNode, "mn", "MnNode", NodeSpec::ChildAny, " TextNode MalignMark ", MML_ATT_COMMON MML_ATT_FONTINFO }, { Mml::MfracNode, "mfrac", "MfracNode", 2, 0, MML_ATT_COMMON MML_ATT_MFRAC }, { Mml::MrowNode, "mrow", "MrowNode", NodeSpec::ChildAny, 0, MML_ATT_COMMON " display mode " }, { Mml::MsqrtNode, "msqrt", "MsqrtNode", NodeSpec::ImplicitMrow, 0, MML_ATT_COMMON }, { Mml::MrootNode, "mroot", "MrootNode", 2, 0, MML_ATT_COMMON }, { Mml::MsupNode, "msup", "MsupNode", 2, 0, MML_ATT_COMMON " subscriptshift " }, { Mml::MsubNode, "msub", "MsubNode", 2, 0, MML_ATT_COMMON " superscriptshift " }, { Mml::MsubsupNode, "msubsup", "MsubsupNode", 3, 0, MML_ATT_COMMON " subscriptshift superscriptshift " }, { Mml::MoNode, "mo", "MoNode", NodeSpec::ChildAny, " TextNode MalignMark ", MML_ATT_COMMON MML_ATT_FONTINFO MML_ATT_OPINFO }, { Mml::MstyleNode, "mstyle", "MstyleNode", NodeSpec::ImplicitMrow, 0, MML_ATT_COMMON MML_ATT_MSTYLE }, { Mml::MphantomNode, "mphantom", "MphantomNode", NodeSpec::ImplicitMrow, 0, MML_ATT_COMMON }, { Mml::MalignMarkNode, "malignmark", "MalignMarkNode", 0, 0, "" }, { Mml::MfencedNode, "mfenced", "MfencedNode", NodeSpec::ChildAny, 0, MML_ATT_COMMON " open close separators " }, { Mml::MtableNode, "mtable", "MtableNode", NodeSpec::ChildAny, " MtrNode ", MML_ATT_COMMON MML_ATT_MTABLE }, { Mml::MtrNode, "mtr", "MtrNode", NodeSpec::ChildAny, " MtdNode ", MML_ATT_COMMON " rowalign columnalign groupalign " }, { Mml::MtdNode, "mtd", "MtdNode", NodeSpec::ImplicitMrow, 0, MML_ATT_COMMON " rowspan columnspan rowalign columnalign groupalign " }, { Mml::MoverNode, "mover", "MoverNode", 2, 0, MML_ATT_COMMON " accent " }, { Mml::MunderNode, "munder", "MunderNode", 2, 0, MML_ATT_COMMON " accentunder " }, { Mml::MunderoverNode, "munderover", "MunderoverNode", 3, 0, MML_ATT_COMMON " accentunder accent " }, { Mml::MerrorNode, "merror", "MerrorNode", NodeSpec::ImplicitMrow, 0, MML_ATT_COMMON }, { Mml::MtextNode, "mtext", "MtextNode", 1, " TextNode ", MML_ATT_COMMON " width height depth linebreak " }, { Mml::MpaddedNode, "mpadded", "MpaddedNode", NodeSpec::ImplicitMrow, 0, MML_ATT_COMMON " width height depth lspace " }, { Mml::MspaceNode, "mspace", "MspaceNode", NodeSpec::ImplicitMrow, 0, MML_ATT_COMMON " width height depth linebreak " }, { Mml::TextNode, 0, "TextNode", NodeSpec::ChildIgnore, 0, "" }, { Mml::UnknownNode, 0, "UnknownNode", NodeSpec::ChildAny, 0, 0 }, { Mml::NoNode, 0, 0, 0, 0, 0 } }; static const char *g_oper_spec_names[g_oper_spec_rows] = { "accent", "fence", "largeop", "lspace", "minsize", "movablelimits", "rspace", "separator", "stretchy" /* stretchdir */ }; static const OperSpec g_oper_spec_data[] = { { "!!" , Mml::PostfixForm, { 0, 0, 0, "verythinmathspace", 0, 0, "0em", 0, 0 }, OperSpec::NoStretch }, // "!!" { "!" , Mml::PostfixForm, { 0, 0, 0, "verythinmathspace", 0, 0, "0em", 0, 0 }, OperSpec::NoStretch }, // "!" { "!=" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "!=" { "⩓" , Mml::InfixForm, { 0, 0, 0, "mediummathspace", 0, 0, "mediummathspace", 0, "true" }, OperSpec::VStretch }, // "⩓" { "⁡" , Mml::InfixForm, { 0, 0, 0, "0em", 0, 0, "0em", 0, 0 }, OperSpec::NoStretch }, // "⁡" { "≔" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "≔" { "∖" , Mml::InfixForm, { 0, 0, 0, "thinmathspace", 0, 0, "thinmathspace", 0, "true" }, OperSpec::HVStretch }, // "∖" { "∵" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "∵" { "˘" , Mml::PostfixForm, { "true", 0, 0, "0em", 0, 0, "0em", 0, 0 }, OperSpec::NoStretch }, // "˘" { "⋒" , Mml::InfixForm, { 0, 0, 0, "thinmathspace", 0, 0, "thinmathspace", 0, 0 }, OperSpec::NoStretch }, // "⋒" { "ⅅ" , Mml::PrefixForm, { 0, 0, 0, "0em", 0, 0, "verythinmathspace", 0, 0 }, OperSpec::NoStretch }, // ⅅ" { "¸" , Mml::PostfixForm, { "true", 0, 0, "0em", 0, 0, "0em", 0, 0 }, OperSpec::NoStretch }, // "¸" { "·" , Mml::InfixForm, { 0, 0, 0, "thinmathspace", 0, 0, "thinmathspace", 0, 0 }, OperSpec::NoStretch }, // "·" { "⊙" , Mml::InfixForm, { 0, 0, 0, "verythinmathspace", 0, 0, "verythinmathspace", 0, 0 }, OperSpec::NoStretch }, // "⊙" { "⊖" , Mml::InfixForm, { 0, 0, 0, "thinmathspace", 0, 0, "thinmathspace", 0, 0 }, OperSpec::NoStretch }, // "⊖" { "⊕" , Mml::InfixForm, { 0, 0, 0, "thinmathspace", 0, 0, "thinmathspace", 0, 0 }, OperSpec::NoStretch }, // "⊕" { "⊗" , Mml::InfixForm, { 0, 0, 0, "thinmathspace", 0, 0, "thinmathspace", 0, 0 }, OperSpec::NoStretch }, // "⊗" { "∲" , Mml::PrefixForm, { 0, 0, "true", "0em", 0, 0, "0em", 0, "true"}, OperSpec::VStretch }, // ∲" { "”" , Mml::PostfixForm, { 0, "true", 0, "0em", 0, 0, "0em", 0, 0 }, OperSpec::NoStretch }, // ”" { "’" , Mml::PostfixForm, { 0, "true", 0, "0em", 0, 0, "0em", 0, 0 }, OperSpec::NoStretch }, // "’" { "∷" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "∷" { "≡" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "≡" { "∮" , Mml::PrefixForm, { 0, 0, "true", "0em", 0, 0, "0em", 0, "true" }, OperSpec::VStretch }, // "∮" { "∐" , Mml::InfixForm, { 0, 0, 0, "thinmathspace", 0, 0, "thinmathspace", 0, 0 }, OperSpec::NoStretch }, // "∐" { "∳", Mml::PrefixForm, { 0, 0, "true", "0em", 0, 0, "0em", 0, "true" }, OperSpec::VStretch }, // &CounterClockwiseContourInteg { "⨯" , Mml::InfixForm, { 0, 0, 0, "verythinmathspace", 0, 0, "verythinmathspace", 0, 0 }, OperSpec::NoStretch }, // "⨯" { "⋓" , Mml::InfixForm, { 0, 0, 0, "thinmathspace", 0, 0, "thinmathspace", 0, 0 }, OperSpec::NoStretch }, // "⋓" { "≍" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "≍" { "∇" , Mml::PrefixForm, { 0, 0, 0, "0em", 0, 0, "verythinmathspace", 0, 0 }, OperSpec::NoStretch }, // "∇" { "´" , Mml::PostfixForm, { "true", 0, 0, "0em", 0, 0, "0em", 0, 0 }, OperSpec::NoStretch }, // "´" { "˙" , Mml::PostfixForm, { "true", 0, 0, "0em", 0, 0, "0em", 0, 0 }, OperSpec::NoStretch }, // "˙" { "˝" , Mml::PostfixForm, { "true", 0, 0, "0em", 0, 0, "0em", 0, 0 }, OperSpec::NoStretch }, // ˝" { "`" , Mml::PostfixForm, { "true", 0, 0, "0em", 0, 0, "0em", 0, 0 }, OperSpec::NoStretch }, // "`" { "&DiacriticalLeftArrow;" , Mml::PostfixForm, { "true", 0, 0, "0em", 0, 0, "0em", 0, "true" }, OperSpec::HStretch }, // &DiacriticalLeftArrow;" { "&DiacriticalLeftRightArrow;" , Mml::PostfixForm, { "true", 0, 0, "0em", 0, 0, "0em", 0, "true" }, OperSpec::HStretch }, // &DiacriticalLeftRightArrow;" { "&DiacriticalLeftRightVector;" , Mml::PostfixForm, { "true", 0, 0, "0em", 0, 0, "0em", 0, "true" }, OperSpec::HStretch }, // &DiacriticalLeftRightVector;" { "&DiacriticalLeftVector;" , Mml::PostfixForm, { "true", 0, 0, "0em", 0, 0, "0em", 0, "true" }, OperSpec::HStretch }, // &DiacriticalLeftVector;" { "&DiacriticalRightArrow;" , Mml::PostfixForm, { "true", 0, 0, "0em", 0, 0, "0em", 0, "true" }, OperSpec::HStretch }, // &DiacriticalRightArrow;" { "&DiacriticalRightVector;" , Mml::PostfixForm, { "true", 0, 0, "0em", 0, 0, "0em", 0, "true" }, OperSpec::HStretch }, // &DiacriticalRightVector;" { "˜" , Mml::PostfixForm, { "true", 0, 0, "0em", 0, 0, "0em", 0, "true" }, OperSpec::NoStretch }, // "˜" { "⋄" , Mml::InfixForm, { 0, 0, 0, "thinmathspace", 0, 0, "thinmathspace", 0, 0 }, OperSpec::NoStretch }, // "⋄" { "ⅆ" , Mml::PrefixForm, { 0, 0, 0, "0em", 0, 0, "verythinmathspace", 0, 0 }, OperSpec::NoStretch }, // "ⅆ" { "≐" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "≐" { "∯" , Mml::PrefixForm, { 0, 0, "true", "0em", 0, 0, "0em", 0, "true" }, OperSpec::VStretch }, // ∯" { "¨" , Mml::PostfixForm, { "true", 0, 0, "0em", 0, 0, "0em", 0, 0 }, OperSpec::NoStretch }, // "¨" { "⇓" , Mml::InfixForm, { 0, 0, 0, "verythinmathspace", 0, 0, "verythinmathspace", 0, "true" }, OperSpec::VStretch }, // "⇓" { "⇐" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, "true" }, OperSpec::HStretch }, // "⇐" { "⇔" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, "true" }, OperSpec::HStretch }, // ⇔" { "⫤" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "⫤" { "⟸" , Mml::InfixForm, { 0, 0, 0, "verythinmathspace", 0, 0, "verythinmathspace", 0, "true" }, OperSpec::HStretch }, // "⟸" { "⟺" , Mml::InfixForm, { 0, 0, 0, "verythinmathspace", 0, 0, "verythinmathspace", 0, "true" }, OperSpec::HStretch }, // ⟺" { "⟹" , Mml::InfixForm, { 0, 0, 0, "verythinmathspace", 0, 0, "verythinmathspace", 0, "true" }, OperSpec::HStretch }, // ⟹" { "⇒" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, "true" }, OperSpec::HStretch }, // "⇒" { "⊨" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "⊨" { "⇑" , Mml::InfixForm, { 0, 0, 0, "verythinmathspace", 0, 0, "verythinmathspace", 0, "true" }, OperSpec::VStretch }, // "⇑" { "⇕" , Mml::InfixForm, { 0, 0, 0, "verythinmathspace", 0, 0, "verythinmathspace", 0, "true" }, OperSpec::VStretch }, // "⇕" { "∥" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "∥" { "↓" , Mml::InfixForm, { 0, 0, 0, "verythinmathspace", 0, 0, "verythinmathspace", 0, "true" }, OperSpec::VStretch }, // "↓" { "⤓" , Mml::InfixForm, { 0, 0, 0, "verythinmathspace", 0, 0, "verythinmathspace", 0, "true" }, OperSpec::VStretch }, // "⤓" { "⇵" , Mml::InfixForm, { 0, 0, 0, "verythinmathspace", 0, 0, "verythinmathspace", 0, "true" }, OperSpec::VStretch }, // "⇵" { "̑" , Mml::PostfixForm, { "true", 0, 0, "0em", 0, 0, "0em", 0, 0 }, OperSpec::NoStretch }, // "̑" { "⥐" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, "true" }, OperSpec::HVStretch }, // "⥐" { "⥞" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, "true" }, OperSpec::HVStretch }, // "⥞" { "↽" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, "true" }, OperSpec::HVStretch }, // "↽" { "⥖" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, "true" }, OperSpec::HVStretch }, // "⥖" { "⥟" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, "true" }, OperSpec::HVStretch }, // "⥟" { "⇁" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, "true" }, OperSpec::HVStretch }, // "⇁" { "⥗" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, "true" }, OperSpec::HVStretch }, // "⥗" { "⊤" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "⊤" { "↧" , Mml::InfixForm, { 0, 0, 0, "verythinmathspace", 0, 0, "verythinmathspace", 0, "true" }, OperSpec::VStretch }, // "↧" { "∈" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "∈" { "⩵" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "⩵" { "≂" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "≂" { "⇌" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, "true" }, OperSpec::HStretch }, // "⇌" { "∃" , Mml::PrefixForm, { 0, 0, 0, "0em", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "∃" { "∀" , Mml::PrefixForm, { 0, 0, 0, "0em", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "∀" { "≥" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "≥" { "⋛" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "⋛" { "≧" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "≧" { "⪢" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "⪢" { "≷" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "≷" { "⩾" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "⩾" { "≳" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "≳" { "ˇ" , Mml::PostfixForm, { "true", 0, 0, "0em", 0, 0, "0em", 0, "true" }, OperSpec::NoStretch }, // "ˇ" { "^" , Mml::PostfixForm, { "true", 0, 0, "0em", 0, 0, "0em", 0, "true" }, OperSpec::HStretch }, // "^" { "─" , Mml::InfixForm, { 0, 0, 0, "0em", "0", 0, "0em", 0, "true" }, OperSpec::HStretch }, // "─" { "≎" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "≎" { "≏" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "≏" { "⇒" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, "true" }, OperSpec::HStretch }, // "⇒" { "∫" , Mml::PrefixForm, { 0, 0, "true", "0em", 0, 0, "0em", 0, 0 }, OperSpec::NoStretch }, // "∫" { "⋂" , Mml::PrefixForm, { 0, 0, "true", "0em", 0, "true", "thinmathspace", 0, "true" }, OperSpec::HVStretch }, // "⋂" { "⁣" , Mml::InfixForm, { 0, 0, 0, "0em", 0, 0, "0em", "true", 0 }, OperSpec::NoStretch }, // "⁣" { "⁢" , Mml::InfixForm, { 0, 0, 0, "0em", 0, 0, "0em", 0, 0 }, OperSpec::NoStretch }, // "⁢" { "⟨" , Mml::PrefixForm, { 0, "true", 0, "0em", 0, 0, "0em", 0, "true" }, OperSpec::VStretch }, // "⟨" { "←" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, "true" }, OperSpec::HStretch }, // "←" { "⇤" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, "true" }, OperSpec::HStretch }, // "⇤" { "⇆" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, "true" }, OperSpec::HStretch }, // "⇆" { "&LeftBracketingBar;" , Mml::PrefixForm, { 0, "true", 0, "0em", 0, 0, "0em", 0, "true" }, OperSpec::VStretch }, // "&LeftBracketingBar;" { "⌈" , Mml::PrefixForm, { 0, "true", 0, "0em", 0, 0, "0em", 0, "true" }, OperSpec::VStretch }, // "⌈" { "⟦" , Mml::PrefixForm, { 0, "true", 0, "0em", 0, 0, "0em", 0, "true" }, OperSpec::HStretch }, // "⟦" { "&LeftDoubleBracketingBar;" , Mml::PrefixForm, { 0, "true", 0, "0em", 0, 0, "0em", 0, "true" }, OperSpec::HStretch }, // &LeftDoubleBracketingBar;" { "⥡" , Mml::InfixForm, { 0, 0, 0, "verythinmathspace", 0, 0, "verythinmathspace", 0, "true" }, OperSpec::HVStretch }, // "⥡" { "⇃" , Mml::InfixForm, { 0, 0, 0, "verythinmathspace", 0, 0, "verythinmathspace", 0, "true" }, OperSpec::HVStretch }, // "⇃" { "⥙" , Mml::InfixForm, { 0, 0, 0, "verythinmathspace", 0, 0, "verythinmathspace", 0, "true" }, OperSpec::HVStretch }, // "⥙" { "⌊" , Mml::PrefixForm, { 0, "true", 0, "0em", 0, 0, "0em", 0, "true" }, OperSpec::VStretch }, // "⌊" { "↔" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, "true" }, OperSpec::HStretch }, // "↔" { "⥎" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, "true" }, OperSpec::HStretch }, // "⥎" { "&LeftSkeleton;" , Mml::PrefixForm, { 0, "true", 0, "0em", 0, 0, "0em", 0, 0 }, OperSpec::NoStretch }, // "&LeftSkeleton;" { "⊣" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "⊣" { "↤" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, "true" }, OperSpec::HStretch }, // "↤" { "⥚" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, "true" }, OperSpec::HStretch }, // "⥚" { "⊲" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "⊲" { "⧏" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "⧏" { "⊴" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "⊴" { "⥑" , Mml::InfixForm, { 0, 0, 0, "verythinmathspace", 0, 0, "verythinmathspace", 0, "true" }, OperSpec::HVStretch }, // "⥑" { "⥠" , Mml::InfixForm, { 0, 0, 0, "verythinmathspace", 0, 0, "verythinmathspace", 0, "true" }, OperSpec::HVStretch }, // "⥠" { "↿" , Mml::InfixForm, { 0, 0, 0, "verythinmathspace", 0, 0, "verythinmathspace", 0, "true" }, OperSpec::HVStretch }, // "↿" { "⥘" , Mml::InfixForm, { 0, 0, 0, "verythinmathspace", 0, 0, "verythinmathspace", 0, "true" }, OperSpec::HVStretch }, // "⥘" { "↼" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, "true" }, OperSpec::HStretch }, // "↼" { "⥒" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, "true" }, OperSpec::VStretch }, // "⥒" { "⋚" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "⋚" { "≦" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "≦" { "≶" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "≶" { "⪡" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "⪡" { "⩽" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "⩽" { "≲" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "≲" { "⟵" , Mml::InfixForm, { 0, 0, 0, "verythinmathspace", 0, 0, "verythinmathspace", 0, "true" }, OperSpec::HStretch }, // "⟵" { "⟷" , Mml::InfixForm, { 0, 0, 0, "verythinmathspace", 0, 0, "verythinmathspace", 0, "true" }, OperSpec::HStretch }, // "⟷" { "⟶" , Mml::InfixForm, { 0, 0, 0, "verythinmathspace", 0, 0, "verythinmathspace", 0, "true" }, OperSpec::HStretch }, // "⟶" { "↙" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, "true" }, OperSpec::HVStretch }, // "↙" { "↘" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, "true" }, OperSpec::HVStretch }, // "↘" { "∓" , Mml::PrefixForm, { 0, 0, 0, "0em", 0, 0, "veryverythinmathspace", 0, 0 }, OperSpec::NoStretch }, // "∓" { "≫" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // ≫" { "≪" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "≪" { "⫬" , Mml::PrefixForm, { 0, 0, 0, "0em", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "⫬" { "≢" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "≢" { "≭" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "≭" { "∦" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // ∦" { "∉" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "∉" { "≠" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "≠" { "≂̸" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "≂̸" { "∄" , Mml::PrefixForm, { 0, 0, 0, "0em", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "∄" { "≯" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "≯" { "≱" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "≱" { "≧̸" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "≧̸" { "≫̸" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "≫̸" { "≹" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "≹" { "⩾̸" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // ⩾̸" { "≵" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "≵" { "≎̸" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "≎̸" { "≏̸" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "≏̸" { "⋪" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "⋪" { "⧏̸" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "⧏̸" { "⋬" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // ⋬" { "≮" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "≮" { "≰" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "≰" { "&NotLessFullEqual;" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "&NotLessFullEqual;" { "≸" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "≸" { "≪̸" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "≪̸" { "⩽̸" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "⩽̸" { "≴" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "≴" { "⪢̸" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // ⪢̸" { "⪡̸" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "⪡̸" { "⊀" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "⊀" { "⪯̸" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "⪯̸" { "⋠" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // ⋠" { "&NotPrecedesTilde;" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "&NotPrecedesTilde;" { "∌" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "∌" { "⋫" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "⋫" { "⧐̸" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "⧐̸" { "⋭" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // ⋭" { "⊏̸" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "⊏̸" { "⋢" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // ⋢" { "⊐̸" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "⊐̸" { "⋣" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // ⋣" { "⊂⃒" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "⊂⃒" { "⊈" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "⊈" { "⊁" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "⊁" { "⪰̸" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "⪰̸" { "⋡" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // ⋡" { "≿̸" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "≿̸" { "⊃⃒" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "⊃⃒" { "⊉" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "⊉" { "≁" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "≁" { "≄" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "≄" { "≇" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "≇" { "≉" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "≉" { "∤" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "∤" { "“" , Mml::PrefixForm, { 0, "true", 0, "0em", 0, 0, "0em", 0, 0 }, OperSpec::NoStretch }, // “" { "‘" , Mml::PrefixForm, { 0, "true", 0, "0em", 0, 0, "0em", 0, 0 }, OperSpec::NoStretch }, // "‘" { "⩔" , Mml::InfixForm, { 0, 0, 0, "mediummathspace", 0, 0, "mediummathspace", 0, "true" }, OperSpec::VStretch }, // "⩔" { "‾" , Mml::PostfixForm, { "true", 0, 0, "0em", 0, 0, "0em", 0, "true" }, OperSpec::HStretch }, // "‾" { "⏞" , Mml::PostfixForm, { "true", 0, 0, "0em", 0, 0, "0em", 0, "true" }, OperSpec::HStretch }, // "⏞" { "⎴" , Mml::PostfixForm, { "true", 0, 0, "0em", 0, 0, "0em", 0, "true" }, OperSpec::HStretch }, // "⎴" { "⏜" , Mml::PostfixForm, { "true", 0, 0, "0em", 0, 0, "0em", 0, "true" }, OperSpec::VStretch }, // "⏜" { "∂" , Mml::PrefixForm, { 0, 0, 0, "0em", 0, 0, "verythinmathspace", 0, 0 }, OperSpec::NoStretch }, // "∂" { "±" , Mml::PrefixForm, { 0, 0, 0, "0em", 0, 0, "veryverythinmathspace", 0, 0 }, OperSpec::NoStretch }, // "±" { "≺" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "≺" { "⪯" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "⪯" { "≼" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "≼" { "≾" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "≾" { "∏" , Mml::PrefixForm, { 0, 0, "true", "0em", 0, "true", "thinmathspace", 0, 0 }, OperSpec::NoStretch }, // "∏" { "∷" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "∷" { "∝" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "∝" { "∋" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "∋" { "⇋" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, "true" }, OperSpec::HStretch }, // "⇋" { "⥯" , Mml::InfixForm, { 0, 0, 0, "verythinmathspace", 0, 0, "verythinmathspace", 0, "true" }, OperSpec::HStretch }, // ⥯" { "⟩" , Mml::PostfixForm, { 0, "true", 0, "0em", 0, 0, "0em", 0, "true" }, OperSpec::VStretch }, // "⟩" { "→" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, "true" }, OperSpec::HStretch }, // "→" { "⇥" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, "true" }, OperSpec::HStretch }, // "⇥" { "⇄" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, "true" }, OperSpec::HStretch }, // "⇄" { "&RightBracketingBar;" , Mml::PostfixForm, { 0, "true", 0, "0em", 0, 0, "0em", 0, "true" }, OperSpec::VStretch }, // "&RightBracketingBar;" { "⌉" , Mml::PostfixForm, { 0, "true", 0, "0em", 0, 0, "0em", 0, "true" }, OperSpec::VStretch }, // "⌉" { "⟧" , Mml::PostfixForm, { 0, "true", 0, "0em", 0, 0, "0em", 0, "true" }, OperSpec::VStretch }, // "⟧" { "&RightDoubleBracketingBar;" , Mml::PostfixForm, { 0, "true", 0, "0em", 0, 0, "0em", 0, "true" }, OperSpec::HStretch }, // &RightDoubleBracketingBar;" { "⥝" , Mml::InfixForm, { 0, 0, 0, "verythinmathspace", 0, 0, "verythinmathspace", 0, "true" }, OperSpec::HVStretch }, // "⥝" { "⇂" , Mml::InfixForm, { 0, 0, 0, "verythinmathspace", 0, 0, "verythinmathspace", 0, "true" }, OperSpec::HVStretch }, // "⇂" { "⥕" , Mml::InfixForm, { 0, 0, 0, "verythinmathspace", 0, 0, "verythinmathspace", 0, "true" }, OperSpec::HVStretch }, // "⥕" { "⌋" , Mml::PostfixForm, { 0, "true", 0, "0em", 0, 0, "0em", 0, "true" }, OperSpec::VStretch }, // "⌋" { "&RightSkeleton;" , Mml::PostfixForm, { 0, "true", 0, "0em", 0, 0, "0em", 0, 0 }, OperSpec::NoStretch }, // "&RightSkeleton;" { "⊢" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "⊢" { "↦" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, "true" }, OperSpec::HStretch }, // "↦" { "⥛" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, "true" }, OperSpec::HStretch }, // "⥛" { "⊳" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "⊳" { "⧐" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "⧐" { "⊵" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "⊵" { "⥏" , Mml::InfixForm, { 0, 0, 0, "verythinmathspace", 0, 0, "verythinmathspace", 0, "true" }, OperSpec::VStretch }, // "⥏" { "⥜" , Mml::InfixForm, { 0, 0, 0, "verythinmathspace", 0, 0, "verythinmathspace", 0, "true" }, OperSpec::HVStretch }, // "⥜" { "↾" , Mml::InfixForm, { 0, 0, 0, "verythinmathspace", 0, 0, "verythinmathspace", 0, "true" }, OperSpec::HVStretch }, // "↾" { "⥔" , Mml::InfixForm, { 0, 0, 0, "verythinmathspace", 0, 0, "verythinmathspace", 0, "true" }, OperSpec::HVStretch }, // "⥔" { "⇀" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, "true" }, OperSpec::HStretch }, // "⇀" { "⥓" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, "true" }, OperSpec::HStretch }, // "⥓" { "⥰" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "⥰" { "↓" , Mml::InfixForm, { 0, 0, 0, "verythinmathspace", 0, 0, "verythinmathspace", 0, 0 }, OperSpec::NoStretch }, // "↓" { "←" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::HStretch }, // "←" { "→" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::HStretch }, // "→" { "↑" , Mml::InfixForm, { 0, 0, 0, "verythinmathspace", 0, 0, "verythinmathspace", 0, 0 }, OperSpec::VStretch }, // "↑" { "∘" , Mml::InfixForm, { 0, 0, 0, "verythinmathspace", 0, 0, "verythinmathspace", 0, 0 }, OperSpec::NoStretch }, // "∘" { "√" , Mml::PrefixForm, { 0, 0, 0, "0em", 0, 0, "verythinmathspace", 0, "true" }, OperSpec::VStretch }, // "√" { "□" , Mml::PrefixForm, { 0, 0, 0, "0em", 0, 0, "verythinmathspace", 0, 0 }, OperSpec::NoStretch }, // "□" { "⊓" , Mml::InfixForm, { 0, 0, 0, "mediummathspace", 0, 0, "mediummathspace", 0, "true" }, OperSpec::HVStretch }, // "⊓" { "⊏" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "⊏" { "⊑" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "⊑" { "⊐" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "⊐" { "⊒" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "⊒" { "⊔" , Mml::InfixForm, { 0, 0, 0, "mediummathspace", 0, 0, "mediummathspace", 0, "true" }, OperSpec::HVStretch }, // "⊔" { "⋆" , Mml::InfixForm, { 0, 0, 0, "thinmathspace", 0, 0, "thinmathspace", 0, 0 }, OperSpec::NoStretch }, // "⋆" { "⋐" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "⋐" { "⊆" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "⊆" { "≻" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "≻" { "⪰" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "⪰" { "≽" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "≽" { "≿" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "≿" { "∋" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "∋" { "∑" , Mml::PrefixForm, { 0, 0, "true", "0em", 0, "true", "thinmathspace", 0, 0 }, OperSpec::NoStretch }, // "∑" { "⊃" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "⊃" { "⊇" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "⊇" { "∴" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "∴" { "∼" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "∼" { "≃" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "≃" { "≅" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "≅" { "≈" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "≈" { "⃛" , Mml::PostfixForm, { "true", 0, 0, "0em", 0, 0, "0em", 0, 0 }, OperSpec::NoStretch }, // "⃛" { "_" , Mml::PostfixForm, { "true", 0, 0, "0em", 0, 0, "0em", 0, "true" }, OperSpec::HStretch }, // "_" { "⏟" , Mml::PostfixForm, { "true", 0, 0, "0em", 0, 0, "0em", 0, "true" }, OperSpec::HStretch }, // "⏟" { "⎵" , Mml::PostfixForm, { "true", 0, 0, "0em", 0, 0, "0em", 0, "true" }, OperSpec::HStretch }, // "⎵" { "⏝" , Mml::PostfixForm, { "true", 0, 0, "0em", 0, 0, "0em", 0, "true" }, OperSpec::HStretch }, // "⏝" { "⋃" , Mml::PrefixForm, { 0, 0, "true", "0em", 0, "true", "thinmathspace", 0, "true" }, OperSpec::HVStretch }, // "⋃" { "⊎" , Mml::PrefixForm, { 0, 0, "true", "0em", 0, "true", "thinmathspace", 0, "true" }, OperSpec::HVStretch }, // "⊎" { "↑" , Mml::InfixForm, { 0, 0, 0, "verythinmathspace", 0, 0, "verythinmathspace", 0, "true" }, OperSpec::VStretch }, // "↑" { "⤒" , Mml::InfixForm, { 0, 0, 0, "verythinmathspace", 0, 0, "verythinmathspace", 0, "true" }, OperSpec::VStretch }, // "⤒" { "⇅" , Mml::InfixForm, { 0, 0, 0, "verythinmathspace", 0, 0, "verythinmathspace", 0, "true" }, OperSpec::VStretch }, // "⇅" { "↕" , Mml::InfixForm, { 0, 0, 0, "verythinmathspace", 0, 0, "verythinmathspace", 0, "true" }, OperSpec::VStretch }, // "↕" { "⥮" , Mml::InfixForm, { 0, 0, 0, "verythinmathspace", 0, 0, "verythinmathspace", 0, "true" }, OperSpec::VStretch }, // "⥮" { "⊥" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "⊥" { "↥" , Mml::InfixForm, { 0, 0, 0, "verythinmathspace", 0, 0, "verythinmathspace", 0, "true" }, OperSpec::VStretch }, // "↥" { "↖" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, "true" }, OperSpec::HVStretch }, // "↖" { "↗" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, "true" }, OperSpec::HVStretch }, // "↗" { "⋁" , Mml::InfixForm, { 0, 0, 0, "thinmathspace", 0, 0, "thinmathspace", 0, 0 }, OperSpec::NoStretch }, // "⋁" { "∣" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "∣" { "|" , Mml::InfixForm, { 0, 0, 0, "0em", "0", 0, "0em", 0, "true" }, OperSpec::VStretch }, // "|" { "❘" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, "true" }, OperSpec::VStretch }, // "❘" { "≀" , Mml::InfixForm, { 0, 0, 0, "thinmathspace", 0, 0, "thinmathspace", 0, 0 }, OperSpec::NoStretch }, // "≀" { "⋀" , Mml::InfixForm, { 0, 0, 0, "thinmathspace", 0, 0, "thinmathspace", 0, 0 }, OperSpec::NoStretch }, // "⋀" { "&" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "&" { "&&" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "&&" { "≤" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "≤" { "<" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "<" { "<=" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "<=" { "<>" , Mml::InfixForm, { 0, 0, 0, "verythinmathspace", 0, 0, "verythinmathspace", 0, 0 }, OperSpec::NoStretch }, // "<>" { "'" , Mml::PostfixForm, { 0, 0, 0, "verythinmathspace", 0, 0, "0em", 0, 0 }, OperSpec::NoStretch }, // "'" { "(" , Mml::PrefixForm, { 0, "true", 0, "0em", 0, 0, "0em", 0, "true" }, OperSpec::VStretch }, // "(" { ")" , Mml::PostfixForm, { 0, "true", 0, "0em", 0, 0, "0em", 0, "true" }, OperSpec::VStretch }, // ")" { "*" , Mml::InfixForm, { 0, 0, 0, "thinmathspace", 0, 0, "thinmathspace", 0, 0 }, OperSpec::NoStretch }, // "*" { "**" , Mml::InfixForm, { 0, 0, 0, "verythinmathspace", 0, 0, "verythinmathspace", 0, 0 }, OperSpec::NoStretch }, // "**" { "*=" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "*=" { "+" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "+" { "+" , Mml::PrefixForm, { 0, 0, 0, "0em", 0, 0, "veryverythinmathspace", 0, 0 }, OperSpec::NoStretch }, // "+" { "++" , Mml::PrefixForm, { 0, 0, 0, "0em", 0, 0, "verythinmathspace", 0, 0 }, OperSpec::NoStretch }, // "++" { "+=" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "+=" { "," , Mml::InfixForm, { 0, 0, 0, "0em", 0, 0, "verythickmathspace", "true", 0 }, OperSpec::NoStretch }, // "," { "-" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "-" { "-" , Mml::PrefixForm, { 0, 0, 0, "0em", 0, 0, "veryverythinmathspace", 0, 0 }, OperSpec::NoStretch }, // "-" { "--" , Mml::PrefixForm, { 0, 0, 0, "0em", 0, 0, "verythinmathspace", 0, 0 }, OperSpec::NoStretch }, // "--" { "-=" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "-=" { "->" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "->" { "." , Mml::InfixForm, { 0, 0, 0, "0em", 0, 0, "0em", 0, 0 }, OperSpec::NoStretch }, // "." { ".." , Mml::PostfixForm, { 0, 0, 0, "mediummathspace", 0, 0, "0em", 0, 0 }, OperSpec::NoStretch }, // ".." { "..." , Mml::PostfixForm, { 0, 0, 0, "mediummathspace", 0, 0, "0em", 0, 0 }, OperSpec::NoStretch }, // "..." { "/" , Mml::InfixForm, { 0, 0, 0, "thinmathspace", 0, 0, "thinmathspace", 0, "true" }, OperSpec::VStretch }, // "/" { "//" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "//" { "/=" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "/=" { ":" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // ":" { ":=" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // ":=" { ";" , Mml::InfixForm, { 0, 0, 0, "0em", 0, 0, "verythickmathspace", "true", 0 }, OperSpec::NoStretch }, // ";" { ";" , Mml::PostfixForm, { 0, 0, 0, "0em", 0, 0, "0em", "true", 0 }, OperSpec::NoStretch }, // ";" { "=" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "=" { "==" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "==" { ">" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // ">" { ">=" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // ">=" { "?" , Mml::InfixForm, { 0, 0, 0, "verythinmathspace", 0, 0, "verythinmathspace", 0, 0 }, OperSpec::NoStretch }, // "?" { "@" , Mml::InfixForm, { 0, 0, 0, "verythinmathspace", 0, 0, "verythinmathspace", 0, 0 }, OperSpec::NoStretch }, // "@" { "[" , Mml::PrefixForm, { 0, "true", 0, "0em", 0, 0, "0em", 0, "true" }, OperSpec::VStretch }, // "[" { "]" , Mml::PostfixForm, { 0, "true", 0, "0em", 0, 0, "0em", 0, "true" }, OperSpec::VStretch }, // "]" { "^" , Mml::InfixForm, { 0, 0, 0, "verythinmathspace", 0, 0, "verythinmathspace", 0, 0 }, OperSpec::NoStretch }, // "^" { "_" , Mml::InfixForm, { 0, 0, 0, "verythinmathspace", 0, 0, "verythinmathspace", 0, 0 }, OperSpec::NoStretch }, // "_" { "lim" , Mml::PrefixForm, { 0, 0, 0, "0em", 0, "true", "thinmathspace", 0, 0 }, OperSpec::NoStretch }, // "lim" { "max" , Mml::PrefixForm, { 0, 0, 0, "0em", 0, "true", "thinmathspace", 0, 0 }, OperSpec::NoStretch }, // "max" { "min" , Mml::PrefixForm, { 0, 0, 0, "0em", 0, "true", "thinmathspace", 0, 0 }, OperSpec::NoStretch }, // "min" { "{" , Mml::PrefixForm, { 0, "true", 0, "0em", 0, 0, "0em", 0, "true" }, OperSpec::VStretch }, // "{" { "|" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, "true" }, OperSpec::VStretch }, // "|" { "||" , Mml::InfixForm, { 0, 0, 0, "mediummathspace", 0, 0, "mediummathspace", 0, 0 }, OperSpec::NoStretch }, // "||" { "}" , Mml::PostfixForm, { 0, "true", 0, "0em", 0, 0, "0em", 0, "true" }, OperSpec::VStretch }, // "}" { "~" , Mml::InfixForm, { 0, 0, 0, "verythinmathspace", 0, 0, "verythinmathspace", 0, 0 }, OperSpec::NoStretch }, // "~" { 0 , Mml::InfixForm, { 0, 0, 0, 0, 0, 0, 0, 0, 0 }, OperSpec::NoStretch } }; static const OperSpec g_oper_spec_defaults = { 0 , Mml::InfixForm, { "false", "false", "false", "thickmathspace", "1", "false", "thickmathspace", "false", "false" }, OperSpec::NoStretch }; static const uint g_oper_spec_count = sizeof(g_oper_spec_data)/sizeof(OperSpec) - 1; static const EntitySpec g_xml_entity_data[] = { { "angzarr", "⍼" }, { "cirmid", "⫯" }, { "cudarrl", "⤸" }, { "cudarrr", "⤵" }, { "cularr", "↶" }, { "cularrp", "⤽" }, { "curarr", "↷" }, { "curarrm", "⤼" }, { "Darr", "↡" }, { "dArr", "⇓" }, { "ddarr", "⇊" }, { "DDotrahd", "⤑" }, { "dfisht", "⥿" }, { "dHar", "⥥" }, { "dharl", "⇃" }, { "dharr", "⇂" }, { "duarr", "⇵" }, { "duhar", "⥯" }, { "dzigrarr", "⟿" }, { "erarr", "⥱" }, { "hArr", "⇔" }, { "harr", "↔" }, { "harrcir", "⥈" }, { "harrw", "↭" }, { "hoarr", "⇿" }, { "imof", "⊷" }, { "lAarr", "⇚" }, { "Larr", "↞" }, { "larrbfs", "⤟" }, { "larrfs", "⤝" }, { "larrhk", "↩" }, { "larrlp", "↫" }, { "larrpl", "⤹" }, { "larrsim", "⥳" }, { "larrtl", "↢" }, { "lAtail", "⤛" }, { "latail", "⤙" }, { "lBarr", "⤎" }, { "lbarr", "⤌" }, { "ldca", "⤶" }, { "ldrdhar", "⥧" }, { "ldrushar", "⥋" }, { "ldsh", "↲" }, { "lfisht", "⥼" }, { "lHar", "⥢" }, { "lhard", "↽" }, { "lharu", "↼" }, { "lharul", "⥪" }, { "llarr", "⇇" }, { "llhard", "⥫" }, { "loarr", "⇽" }, { "lrarr", "⇆" }, { "lrhar", "⇋" }, { "lrhard", "⥭" }, { "lsh", "↰" }, { "lurdshar", "⥊" }, { "luruhar", "⥦" }, { "Map", "⤅" }, { "map", "↦" }, { "midcir", "⫰" }, { "mumap", "⊸" }, { "nearhk", "⤤" }, { "neArr", "⇗" }, { "nearr", "↗" }, { "nesear", "⤨" }, { "nhArr", "⇎" }, { "nharr", "↮" }, { "nlArr", "⇍" }, { "nlarr", "↚" }, { "nrArr", "⇏" }, { "nrarr", "↛" }, { "nrarrc", "⤳̸" }, { "nrarrw", "↝̸" }, { "nvHarr", "⤄" }, { "nvlArr", "⤂" }, { "nvrArr", "⤃" }, { "nwarhk", "⤣" }, { "nwArr", "⇖" }, { "nwarr", "↖" }, { "nwnear", "⤧" }, { "olarr", "↺" }, { "orarr", "↻" }, { "origof", "⊶" }, { "rAarr", "⇛" }, { "Rarr", "↠" }, { "rarrap", "⥵" }, { "rarrbfs", "⤠" }, { "rarrc", "⤳" }, { "rarrfs", "⤞" }, { "rarrhk", "↪" }, { "rarrlp", "↬" }, { "rarrpl", "⥅" }, { "rarrsim", "⥴" }, { "Rarrtl", "⤖" }, { "rarrtl", "↣" }, { "rarrw", "↝" }, { "rAtail", "⤜" }, { "ratail", "⤚" }, { "RBarr", "⤐" }, { "rBarr", "⤏" }, { "rbarr", "⤍" }, { "rdca", "⤷" }, { "rdldhar", "⥩" }, { "rdsh", "↳" }, { "rfisht", "⥽" }, { "rHar", "⥤" }, { "rhard", "⇁" }, { "rharu", "⇀" }, { "rharul", "⥬" }, { "rlarr", "⇄" }, { "rlhar", "⇌" }, { "roarr", "⇾" }, { "rrarr", "⇉" }, { "rsh", "↱" }, { "ruluhar", "⥨" }, { "searhk", "⤥" }, { "seArr", "⇘" }, { "searr", "↘" }, { "seswar", "⤩" }, { "simrarr", "⥲" }, { "slarr", "←" }, { "srarr", "→" }, { "swarhk", "⤦" }, { "swArr", "⇙" }, { "swarr", "↙" }, { "swnwar", "⤪" }, { "Uarr", "↟" }, { "uArr", "⇑" }, { "Uarrocir", "⥉" }, { "udarr", "⇅" }, { "udhar", "⥮" }, { "ufisht", "⥾" }, { "uHar", "⥣" }, { "uharl", "↿" }, { "uharr", "↾" }, { "uuarr", "⇈" }, { "vArr", "⇕" }, { "varr", "↕" }, { "xhArr", "⟺" }, { "xharr", "⟷" }, { "xlArr", "⟸" }, { "xlarr", "⟵" }, { "xmap", "⟼" }, { "xrArr", "⟹" }, { "xrarr", "⟶" }, { "zigrarr", "⇝" }, { "ac", "∾" }, { "acE", "∾̳" }, { "amalg", "⨿" }, { "barvee", "⊽" }, { "Barwed", "⌆" }, { "barwed", "⌅" }, { "bsolb", "⧅" }, { "Cap", "⋒" }, { "capand", "⩄" }, { "capbrcup", "⩉" }, { "capcap", "⩋" }, { "capcup", "⩇" }, { "capdot", "⩀" }, { "caps", "∩︀" }, { "ccaps", "⩍" }, { "ccups", "⩌" }, { "ccupssm", "⩐" }, { "coprod", "∐" }, { "Cup", "⋓" }, { "cupbrcap", "⩈" }, { "cupcap", "⩆" }, { "cupcup", "⩊" }, { "cupdot", "⊍" }, { "cupor", "⩅" }, { "cups", "∪︀" }, { "cuvee", "⋎" }, { "cuwed", "⋏" }, { "Dagger", "‡" }, { "dagger", "†" }, { "diam", "⋄" }, { "divonx", "⋇" }, { "eplus", "⩱" }, { "hercon", "⊹" }, { "intcal", "⊺" }, { "iprod", "⨼" }, { "loplus", "⨭" }, { "lotimes", "⨴" }, { "lthree", "⋋" }, { "ltimes", "⋉" }, { "midast", "*" }, { "minusb", "⊟" }, { "minusd", "∸" }, { "minusdu", "⨪" }, { "ncap", "⩃" }, { "ncup", "⩂" }, { "oast", "⊛" }, { "ocir", "⊚" }, { "odash", "⊝" }, { "odiv", "⨸" }, { "odot", "⊙" }, { "odsold", "⦼" }, { "ofcir", "⦿" }, { "ogt", "⧁" }, { "ohbar", "⦵" }, { "olcir", "⦾" }, { "olt", "⧀" }, { "omid", "⦶" }, { "ominus", "⊖" }, { "opar", "⦷" }, { "operp", "⦹" }, { "oplus", "⊕" }, { "osol", "⊘" }, { "Otimes", "⨷" }, { "otimes", "⊗" }, { "otimesas", "⨶" }, { "ovbar", "⌽" }, { "plusacir", "⨣" }, { "plusb", "⊞" }, { "pluscir", "⨢" }, { "plusdo", "∔" }, { "plusdu", "⨥" }, { "pluse", "⩲" }, { "plussim", "⨦" }, { "plustwo", "⨧" }, { "prod", "∏" }, { "race", "⧚" }, { "roplus", "⨮" }, { "rotimes", "⨵" }, { "rthree", "⋌" }, { "rtimes", "⋊" }, { "sdot", "⋅" }, { "sdotb", "⊡" }, { "setmn", "∖" }, { "simplus", "⨤" }, { "smashp", "⨳" }, { "solb", "⧄" }, { "sqcap", "⊓" }, { "sqcaps", "⊓︀" }, { "sqcup", "⊔" }, { "sqcups", "⊔︀" }, { "ssetmn", "∖" }, { "sstarf", "⋆" }, { "subdot", "⪽" }, { "sum", "∑" }, { "supdot", "⪾" }, { "timesb", "⊠" }, { "timesbar", "⨱" }, { "timesd", "⨰" }, { "tridot", "◬" }, { "triminus", "⨺" }, { "triplus", "⨹" }, { "trisb", "⧍" }, { "tritime", "⨻" }, { "uplus", "⊎" }, { "veebar", "⊻" }, { "wedbar", "⩟" }, { "wreath", "≀" }, { "xcap", "⋂" }, { "xcirc", "◯" }, { "xcup", "⋃" }, { "xdtri", "▽" }, { "xodot", "⨀" }, { "xoplus", "⨁" }, { "xotime", "⨂" }, { "xsqcup", "⨆" }, { "xuplus", "⨄" }, { "xutri", "△" }, { "xvee", "⋁" }, { "xwedge", "⋀" }, { "dlcorn", "⌞" }, { "drcorn", "⌟" }, { "gtlPar", "⦕" }, { "langd", "⦑" }, { "lbrke", "⦋" }, { "lbrksld", "⦏" }, { "lbrkslu", "⦍" }, { "lceil", "⌈" }, { "lfloor", "⌊" }, { "lmoust", "⎰" }, { "lparlt", "⦓" }, { "ltrPar", "⦖" }, { "rangd", "⦒" }, { "rbrke", "⦌" }, { "rbrksld", "⦎" }, { "rbrkslu", "⦐" }, { "rceil", "⌉" }, { "rfloor", "⌋" }, { "rmoust", "⎱" }, { "rpargt", "⦔" }, { "ulcorn", "⌜" }, { "urcorn", "⌝" }, { "gnap", "⪊" }, { "gnE", "≩" }, { "gne", "⪈" }, { "gnsim", "⋧" }, { "gvnE", "≩︀" }, { "lnap", "⪉" }, { "lnE", "≨" }, { "lne", "⪇" }, { "lnsim", "⋦" }, { "lvnE", "≨︀" }, { "nap", "≉" }, { "napE", "⩰̸" }, { "napid", "≋̸" }, { "ncong", "≇" }, { "ncongdot", "⩭̸" }, { "nequiv", "≢" }, { "ngE", "≧̸" }, { "nge", "≱" }, { "nges", "⩾̸" }, { "nGg", "⋙̸" }, { "ngsim", "≵" }, { "nGt", "≫⃒" }, { "ngt", "≯" }, { "nGtv", "≫̸" }, { "nlE", "≦̸" }, { "nle", "≰" }, { "nles", "⩽̸" }, { "nLl", "⋘̸" }, { "nlsim", "≴" }, { "nLt", "≪⃒" }, { "nlt", "≮" }, { "nltri", "⋪" }, { "nltrie", "⋬" }, { "nLtv", "≪̸" }, { "nmid", "∤" }, { "npar", "∦" }, { "npr", "⊀" }, { "nprcue", "⋠" }, { "npre", "⪯̸" }, { "nrtri", "⋫" }, { "nrtrie", "⋭" }, { "nsc", "⊁" }, { "nsccue", "⋡" }, { "nsce", "⪰̸" }, { "nsim", "≁" }, { "nsime", "≄" }, { "nsmid", "∤" }, { "nspar", "∦" }, { "nsqsube", "⋢" }, { "nsqsupe", "⋣" }, { "nsub", "⊄" }, { "nsubE", "⫅̸" }, { "nsube", "⊈" }, { "nsup", "⊅" }, { "nsupE", "⫆̸" }, { "nsupe", "⊉" }, { "ntgl", "≹" }, { "ntlg", "≸" }, { "nvap", "≍⃒" }, { "nVDash", "⊯" }, { "nVdash", "⊮" }, { "nvDash", "⊭" }, { "nvdash", "⊬" }, { "nvge", "≥⃒" }, { "nvgt", ">⃒" }, { "nvle", "≤⃒" }, { "nvlt", "<⃒" }, { "nvltrie", "⊴⃒" }, { "nvrtrie", "⊵⃒" }, { "nvsim", "∼⃒" }, { "parsim", "⫳" }, { "prnap", "⪹" }, { "prnE", "⪵" }, { "prnsim", "⋨" }, { "rnmid", "⫮" }, { "scnap", "⪺" }, { "scnE", "⪶" }, { "scnsim", "⋩" }, { "simne", "≆" }, { "solbar", "⌿" }, { "subnE", "⫋" }, { "subne", "⊊" }, { "supnE", "⫌" }, { "supne", "⊋" }, { "vnsub", "⊂⃒" }, { "vnsup", "⊃⃒" }, { "vsubnE", "⫋︀" }, { "vsubne", "⊊︀" }, { "vsupnE", "⫌︀" }, { "vsupne", "⊋︀" }, { "ang", "∠" }, { "ange", "⦤" }, { "angmsd", "∡" }, { "angmsdaa", "⦨" }, { "angmsdab", "⦩" }, { "angmsdac", "⦪" }, { "angmsdad", "⦫" }, { "angmsdae", "⦬" }, { "angmsdaf", "⦭" }, { "angmsdag", "⦮" }, { "angmsdah", "⦯" }, { "angrtvb", "⊾" }, { "angrtvbd", "⦝" }, { "bbrk", "⎵" }, { "bemptyv", "⦰" }, { "beth", "ℶ" }, { "boxbox", "⧉" }, { "bprime", "‵" }, { "bsemi", "⁏" }, { "cemptyv", "⦲" }, { "cirE", "⧃" }, { "cirscir", "⧂" }, { "comp", "∁" }, { "daleth", "ℸ" }, { "demptyv", "⦱" }, { "ell", "ℓ" }, { "empty", "∅" }, { "emptyv", "∅" }, { "gimel", "ℷ" }, { "iiota", "℩" }, { "image", "ℑ" }, { "imath", "ı" }, { "jmath", "j" }, { "laemptyv", "⦴" }, { "lltri", "◺" }, { "lrtri", "⊿" }, { "mho", "℧" }, { "nang", "∠⃒" }, { "nexist", "∄" }, { "oS", "Ⓢ" }, { "planck", "ℏ" }, { "plankv", "ℏ" }, { "raemptyv", "⦳" }, { "range", "⦥" }, { "real", "ℜ" }, { "tbrk", "⎴" }, { "ultri", "◸" }, { "urtri", "◹" }, { "vzigzag", "⦚" }, { "weierp", "℘" }, { "apE", "⩰" }, { "ape", "≊" }, { "apid", "≋" }, { "asymp", "≈" }, { "Barv", "⫧" }, { "bcong", "≌" }, { "bepsi", "϶" }, { "bowtie", "⋈" }, { "bsim", "∽" }, { "bsime", "⋍" }, { "bsolhsub", "\⊂" }, { "bump", "≎" }, { "bumpE", "⪮" }, { "bumpe", "≏" }, { "cire", "≗" }, { "Colon", "∷" }, { "Colone", "⩴" }, { "colone", "≔" }, { "congdot", "⩭" }, { "csub", "⫏" }, { "csube", "⫑" }, { "csup", "⫐" }, { "csupe", "⫒" }, { "cuepr", "⋞" }, { "cuesc", "⋟" }, { "Dashv", "⫤" }, { "dashv", "⊣" }, { "easter", "⩮" }, { "ecir", "≖" }, { "ecolon", "≕" }, { "eDDot", "⩷" }, { "eDot", "≑" }, { "efDot", "≒" }, { "eg", "⪚" }, { "egs", "⪖" }, { "egsdot", "⪘" }, { "el", "⪙" }, { "els", "⪕" }, { "elsdot", "⪗" }, { "equest", "≟" }, { "equivDD", "⩸" }, { "erDot", "≓" }, { "esdot", "≐" }, { "Esim", "⩳" }, { "esim", "≂" }, { "fork", "⋔" }, { "forkv", "⫙" }, { "frown", "⌢" }, { "gap", "⪆" }, { "gE", "≧" }, { "gEl", "⪌" }, { "gel", "⋛" }, { "ges", "⩾" }, { "gescc", "⪩" }, { "gesdot", "⪀" }, { "gesdoto", "⪂" }, { "gesdotol", "⪄" }, { "gesl", "⋛︀" }, { "gesles", "⪔" }, { "Gg", "⋙" }, { "gl", "≷" }, { "gla", "⪥" }, { "glE", "⪒" }, { "glj", "⪤" }, { "gsim", "≳" }, { "gsime", "⪎" }, { "gsiml", "⪐" }, { "Gt", "≫" }, { "gtcc", "⪧" }, { "gtcir", "⩺" }, { "gtdot", "⋗" }, { "gtquest", "⩼" }, { "gtrarr", "⥸" }, { "homtht", "∻" }, { "lap", "⪅" }, { "lat", "⪫" }, { "late", "⪭" }, { "lates", "⪭︀" }, { "lE", "≦" }, { "lEg", "⪋" }, { "leg", "⋚" }, { "les", "⩽" }, { "lescc", "⪨" }, { "lesdot", "⩿" }, { "lesdoto", "⪁" }, { "lesdotor", "⪃" }, { "lesg", "⋚︀" }, { "lesges", "⪓" }, { "lg", "≶" }, { "lgE", "⪑" }, { "Ll", "⋘" }, { "lsim", "≲" }, { "lsime", "⪍" }, { "lsimg", "⪏" }, { "Lt", "≪" }, { "ltcc", "⪦" }, { "ltcir", "⩹" }, { "ltdot", "⋖" }, { "ltlarr", "⥶" }, { "ltquest", "⩻" }, { "ltrie", "⊴" }, { "mcomma", "⨩" }, { "mDDot", "∺" }, { "mid", "∣" }, { "mlcp", "⫛" }, { "models", "⊧" }, { "mstpos", "∾" }, { "Pr", "⪻" }, { "pr", "≺" }, { "prap", "⪷" }, { "prcue", "≼" }, { "prE", "⪳" }, { "pre", "⪯" }, { "prsim", "≾" }, { "prurel", "⊰" }, { "ratio", "∶" }, { "rtrie", "⊵" }, { "rtriltri", "⧎" }, { "Sc", "⪼" }, { "sc", "≻" }, { "scap", "⪸" }, { "sccue", "≽" }, { "scE", "⪴" }, { "sce", "⪰" }, { "scsim", "≿" }, { "sdote", "⩦" }, { "simg", "⪞" }, { "simgE", "⪠" }, { "siml", "⪝" }, { "simlE", "⪟" }, { "smid", "∣" }, { "smile", "⌣" }, { "smt", "⪪" }, { "smte", "⪬" }, { "smtes", "⪬︀" }, { "spar", "∥" }, { "sqsub", "⊏" }, { "sqsube", "⊑" }, { "sqsup", "⊐" }, { "sqsupe", "⊒" }, { "Sub", "⋐" }, { "subE", "⫅" }, { "subedot", "⫃" }, { "submult", "⫁" }, { "subplus", "⪿" }, { "subrarr", "⥹" }, { "subsim", "⫇" }, { "subsub", "⫕" }, { "subsup", "⫓" }, { "Sup", "⋑" }, { "supdsub", "⫘" }, { "supE", "⫆" }, { "supedot", "⫄" }, { "suphsol", "⊅" }, { "suphsub", "⫗" }, { "suplarr", "⥻" }, { "supmult", "⫂" }, { "supplus", "⫀" }, { "supsim", "⫈" }, { "supsub", "⫔" }, { "supsup", "⫖" }, { "thkap", "≈" }, { "topfork", "⫚" }, { "trie", "≜" }, { "twixt", "≬" }, { "Vbar", "⫫" }, { "vBar", "⫨" }, { "vBarv", "⫩" }, { "VDash", "⊫" }, { "Vdash", "⊩" }, { "vDash", "⊨" }, { "vdash", "⊢" }, { "Vdashl", "⫦" }, { "vltri", "⊲" }, { "vprop", "∝" }, { "vrtri", "⊳" }, { "Vvdash", "⊪" }, { "alpha", "α" }, { "beta", "β" }, { "chi", "χ" }, { "Delta", "Δ" }, { "delta", "δ" }, { "epsi", "ε" }, { "epsiv", "ɛ" }, { "eta", "η" }, { "Gamma", "Γ" }, { "gamma", "γ" }, { "Gammad", "Ϝ" }, { "gammad", "ϝ" }, { "iota", "ι" }, { "kappa", "κ" }, { "kappav", "ϰ" }, { "Lambda", "Λ" }, { "lambda", "λ" }, { "mu", "μ" }, { "nu", "ν" }, { "Omega", "Ω" }, { "omega", "ω" }, { "Phi", "Φ" }, { "phi", "ϕ" }, { "phiv", "φ" }, { "Pi", "Π" }, { "pi", "π" }, { "piv", "ϖ" }, { "Psi", "Ψ" }, { "psi", "ψ" }, { "rho", "ρ" }, { "rhov", "ϱ" }, { "Sigma", "Σ" }, { "sigma", "σ" }, { "sigmav", "ς" }, { "tau", "τ" }, { "Theta", "Θ" }, { "theta", "θ" }, { "thetav", "ϑ" }, { "Upsi", "ϒ" }, { "upsi", "υ" }, { "Xi", "Ξ" }, { "xi", "ξ" }, { "zeta", "ζ" }, { "Cfr", "ℭ" }, { "Hfr", "ℌ" }, { "Ifr", "ℑ" }, { "Rfr", "ℜ" }, { "Zfr", "ℨ" }, { "Copf", "ℂ" }, { "Hopf", "ℍ" }, { "Nopf", "ℕ" }, { "Popf", "ℙ" }, { "Qopf", "ℚ" }, { "Ropf", "ℝ" }, { "Zopf", "ℤ" }, { "Bscr", "ℬ" }, { "Escr", "ℰ" }, { "escr", "ℯ" }, { "Fscr", "ℱ" }, { "gscr", "ℊ" }, { "Hscr", "ℋ" }, { "Iscr", "ℐ" }, { "Lscr", "ℒ" }, { "Mscr", "ℳ" }, { "oscr", "ℴ" }, { "pscr", "𝓅" }, { "Rscr", "ℛ" }, { "acd", "∿" }, { "aleph", "ℵ" }, { "And", "⩓" }, { "and", "∧" }, { "andand", "⩕" }, { "andd", "⩜" }, { "andslope", "⩘" }, { "andv", "⩚" }, { "angrt", "∟" }, { "angsph", "∢" }, { "angst", "Å" }, { "ap", "≈" }, { "apacir", "⩯" }, { "awconint", "∳" }, { "awint", "⨑" }, { "becaus", "∵" }, { "bernou", "ℬ" }, { "bne", "=⃥" }, { "bnequiv", "≡⃥" }, { "bNot", "⫭" }, { "bnot", "⌐" }, { "bottom", "⊥" }, { "cap", "∩" }, { "Cconint", "∰" }, { "cirfnint", "⨐" }, { "compfn", "∘" }, { "cong", "≅" }, { "Conint", "∯" }, { "conint", "∮" }, { "ctdot", "⋯" }, { "cup", "∪" }, { "cwconint", "∲" }, { "cwint", "∱" }, { "cylcty", "⌭" }, { "disin", "⋲" }, { "Dot", "¨" }, { "DotDot", "⃜" }, { "dsol", "⧶" }, { "dtdot", "⋱" }, { "dwangle", "⦦" }, { "epar", "⋕" }, { "eparsl", "⧣" }, { "equiv", "≡" }, { "eqvparsl", "⧥" }, { "exist", "∃" }, { "fnof", "ƒ" }, { "forall", "∀" }, { "fpartint", "⨍" }, { "ge", "≥" }, { "hamilt", "ℋ" }, { "iff", "⇔" }, { "iinfin", "⧜" }, { "infin", "∞" }, { "Int", "∬" }, { "int", "∫" }, { "intlarhk", "⨗" }, { "isin", "∈" }, { "isindot", "⋵" }, { "isinE", "⋹" }, { "isins", "⋴" }, { "isinsv", "⋳" }, { "isinv", "∈" }, { "lagran", "ℒ" }, { "Lang", "《" }, { "lang", "〈" }, { "lArr", "⇐" }, { "lbbrk", "〔" }, { "le", "≤" }, { "loang", "〘" }, { "lobrk", "〚" }, { "lopar", "⦅" }, { "lowast", "∗" }, { "minus", "−" }, { "mnplus", "∓" }, { "nabla", "∇" }, { "ne", "≠" }, { "nedot", "≐̸" }, { "nhpar", "⫲" }, { "ni", "∋" }, { "nis", "⋼" }, { "nisd", "⋺" }, { "niv", "∋" }, { "Not", "⫬" }, { "notin", "∉" }, { "notindot", "⋵̸" }, { "notinva", "∉" }, { "notinvb", "⋷" }, { "notinvc", "⋶" }, { "notni", "∌" }, { "notniva", "∌" }, { "notnivb", "⋾" }, { "notnivc", "⋽" }, { "nparsl", "⫽⃥" }, { "npart", "∂̸" }, { "npolint", "⨔" }, { "nvinfin", "⧞" }, { "olcross", "⦻" }, { "Or", "⩔" }, { "or", "∨" }, { "ord", "⩝" }, { "order", "ℴ" }, { "oror", "⩖" }, { "orslope", "⩗" }, { "orv", "⩛" }, { "par", "∥" }, { "parsl", "⫽" }, { "part", "∂" }, { "permil", "‰" }, { "perp", "⊥" }, { "pertenk", "‱" }, { "phmmat", "ℳ" }, { "pointint", "⨕" }, { "Prime", "″" }, { "prime", "′" }, { "profalar", "⌮" }, { "profline", "⌒" }, { "profsurf", "⌓" }, { "prop", "∝" }, { "qint", "⨌" }, { "qprime", "⁗" }, { "quatint", "⨖" }, { "radic", "√" }, { "Rang", "》" }, { "rang", "〉" }, { "rArr", "⇒" }, { "rbbrk", "〕" }, { "roang", "〙" }, { "robrk", "〛" }, { "ropar", "⦆" }, { "rppolint", "⨒" }, { "scpolint", "⨓" }, { "sim", "∼" }, { "simdot", "⩪" }, { "sime", "≃" }, { "smeparsl", "⧤" }, { "square", "□" }, { "squarf", "▪" }, { "sub", "⊂" }, { "sube", "⊆" }, { "sup", "⊃" }, { "supe", "⊇" }, { "tdot", "⃛" }, { "there4", "∴" }, { "tint", "∭" }, { "top", "⊤" }, { "topbot", "⌶" }, { "topcir", "⫱" }, { "tprime", "‴" }, { "utdot", "⋰" }, { "uwangle", "⦧" }, { "vangrt", "⦜" }, { "veeeq", "≚" }, { "Verbar", "‖" }, { "wedgeq", "≙" }, { "xnis", "⋻" }, { "boxDL", "╗" }, { "boxDl", "╖" }, { "boxdL", "╕" }, { "boxdl", "┐" }, { "boxDR", "╔" }, { "boxDr", "╓" }, { "boxdR", "╒" }, { "boxdr", "┌" }, { "boxH", "═" }, { "boxh", "─" }, { "boxHD", "╦" }, { "boxHd", "╤" }, { "boxhD", "╥" }, { "boxhd", "┬" }, { "boxHU", "╩" }, { "boxHu", "╧" }, { "boxhU", "╨" }, { "boxhu", "┴" }, { "boxUL", "╝" }, { "boxUl", "╜" }, { "boxuL", "╛" }, { "boxul", "┘" }, { "boxUR", "╚" }, { "boxUr", "╙" }, { "boxuR", "╘" }, { "boxur", "└" }, { "boxV", "║" }, { "boxv", "│" }, { "boxVH", "╬" }, { "boxVh", "╫" }, { "boxvH", "╪" }, { "boxvh", "┼" }, { "boxVL", "╣" }, { "boxVl", "╢" }, { "boxvL", "╡" }, { "boxvl", "┤" }, { "boxVR", "╠" }, { "boxVr", "╟" }, { "boxvR", "╞" }, { "boxvr", "├" }, { "Acy", "А" }, { "acy", "а" }, { "Bcy", "Б" }, { "bcy", "б" }, { "CHcy", "Ч" }, { "chcy", "ч" }, { "Dcy", "Д" }, { "dcy", "д" }, { "Ecy", "Э" }, { "ecy", "э" }, { "Fcy", "Ф" }, { "fcy", "ф" }, { "Gcy", "Г" }, { "gcy", "г" }, { "HARDcy", "Ъ" }, { "hardcy", "ъ" }, { "Icy", "И" }, { "icy", "и" }, { "IEcy", "Е" }, { "iecy", "е" }, { "IOcy", "Ё" }, { "iocy", "ё" }, { "Jcy", "Й" }, { "jcy", "й" }, { "Kcy", "К" }, { "kcy", "к" }, { "KHcy", "Х" }, { "khcy", "х" }, { "Lcy", "Л" }, { "lcy", "л" }, { "Mcy", "М" }, { "mcy", "м" }, { "Ncy", "Н" }, { "ncy", "н" }, { "numero", "№" }, { "Ocy", "О" }, { "ocy", "о" }, { "Pcy", "П" }, { "pcy", "п" }, { "Rcy", "Р" }, { "rcy", "р" }, { "Scy", "С" }, { "scy", "с" }, { "SHCHcy", "Щ" }, { "shchcy", "щ" }, { "SHcy", "Ш" }, { "shcy", "ш" }, { "SOFTcy", "Ь" }, { "softcy", "ь" }, { "Tcy", "Т" }, { "tcy", "т" }, { "TScy", "Ц" }, { "tscy", "ц" }, { "Ucy", "У" }, { "ucy", "у" }, { "Vcy", "В" }, { "vcy", "в" }, { "YAcy", "Я" }, { "yacy", "я" }, { "Ycy", "Ы" }, { "ycy", "ы" }, { "YUcy", "Ю" }, { "yucy", "ю" }, { "Zcy", "З" }, { "zcy", "з" }, { "ZHcy", "Ж" }, { "zhcy", "ж" }, { "DJcy", "Ђ" }, { "djcy", "ђ" }, { "DScy", "Ѕ" }, { "dscy", "ѕ" }, { "DZcy", "Џ" }, { "dzcy", "џ" }, { "GJcy", "Ѓ" }, { "gjcy", "ѓ" }, { "Iukcy", "І" }, { "iukcy", "і" }, { "Jsercy", "Ј" }, { "jsercy", "ј" }, { "Jukcy", "Є" }, { "jukcy", "є" }, { "KJcy", "Ќ" }, { "kjcy", "ќ" }, { "LJcy", "Љ" }, { "ljcy", "љ" }, { "NJcy", "Њ" }, { "njcy", "њ" }, { "TSHcy", "Ћ" }, { "tshcy", "ћ" }, { "Ubrcy", "Ў" }, { "ubrcy", "ў" }, { "YIcy", "Ї" }, { "yicy", "ї" }, { "acute", "´" }, { "breve", "˘" }, { "caron", "ˇ" }, { "cedil", "¸" }, { "circ", "ˆ" }, { "dblac", "˝" }, { "die", "¨" }, { "dot", "˙" }, { "grave", "`" }, { "macr", "¯" }, { "ogon", "˛" }, { "ring", "˚" }, { "tilde", "˜" }, { "uml", "¨" }, { "Aacute", "Á" }, { "aacute", "á" }, { "Acirc", "Â" }, { "acirc", "â" }, { "AElig", "Æ" }, { "aelig", "æ" }, { "Agrave", "À" }, { "agrave", "à" }, { "Aring", "Å" }, { "aring", "å" }, { "Atilde", "Ã" }, { "atilde", "ã" }, { "Auml", "Ä" }, { "auml", "ä" }, { "Ccedil", "Ç" }, { "ccedil", "ç" }, { "Eacute", "É" }, { "eacute", "é" }, { "Ecirc", "Ê" }, { "ecirc", "ê" }, { "Egrave", "È" }, { "egrave", "è" }, { "ETH", "Ð" }, { "eth", "ð" }, { "Euml", "Ë" }, { "euml", "ë" }, { "Iacute", "Í" }, { "iacute", "í" }, { "Icirc", "Î" }, { "icirc", "î" }, { "Igrave", "Ì" }, { "igrave", "ì" }, { "Iuml", "Ï" }, { "iuml", "ï" }, { "Ntilde", "Ñ" }, { "ntilde", "ñ" }, { "Oacute", "Ó" }, { "oacute", "ó" }, { "Ocirc", "Ô" }, { "ocirc", "ô" }, { "Ograve", "Ò" }, { "ograve", "ò" }, { "Oslash", "Ø" }, { "oslash", "ø" }, { "Otilde", "Õ" }, { "otilde", "õ" }, { "Ouml", "Ö" }, { "ouml", "ö" }, { "szlig", "ß" }, { "THORN", "Þ" }, { "thorn", "þ" }, { "Uacute", "Ú" }, { "uacute", "ú" }, { "Ucirc", "Û" }, { "ucirc", "û" }, { "Ugrave", "Ù" }, { "ugrave", "ù" }, { "Uuml", "Ü" }, { "uuml", "ü" }, { "Yacute", "Ý" }, { "yacute", "ý" }, { "yuml", "ÿ" }, { "Abreve", "Ă" }, { "abreve", "ă" }, { "Amacr", "Ā" }, { "amacr", "ā" }, { "Aogon", "Ą" }, { "aogon", "ą" }, { "Cacute", "Ć" }, { "cacute", "ć" }, { "Ccaron", "Č" }, { "ccaron", "č" }, { "Ccirc", "Ĉ" }, { "ccirc", "ĉ" }, { "Cdot", "Ċ" }, { "cdot", "ċ" }, { "Dcaron", "Ď" }, { "dcaron", "ď" }, { "Dstrok", "Đ" }, { "dstrok", "đ" }, { "Ecaron", "Ě" }, { "ecaron", "ě" }, { "Edot", "Ė" }, { "edot", "ė" }, { "Emacr", "Ē" }, { "emacr", "ē" }, { "ENG", "Ŋ" }, { "eng", "ŋ" }, { "Eogon", "Ę" }, { "eogon", "ę" }, { "gacute", "ǵ" }, { "Gbreve", "Ğ" }, { "gbreve", "ğ" }, { "Gcedil", "Ģ" }, { "Gcirc", "Ĝ" }, { "gcirc", "ĝ" }, { "Gdot", "Ġ" }, { "gdot", "ġ" }, { "Hcirc", "Ĥ" }, { "hcirc", "ĥ" }, { "Hstrok", "Ħ" }, { "hstrok", "ħ" }, { "Idot", "İ" }, { "IJlig", "IJ" }, { "ijlig", "ij" }, { "Imacr", "Ī" }, { "imacr", "ī" }, { "inodot", "ı" }, { "Iogon", "Į" }, { "iogon", "į" }, { "Itilde", "Ĩ" }, { "itilde", "ĩ" }, { "Jcirc", "Ĵ" }, { "jcirc", "ĵ" }, { "Kcedil", "Ķ" }, { "kcedil", "ķ" }, { "kgreen", "ĸ" }, { "Lacute", "Ĺ" }, { "lacute", "ĺ" }, { "Lcaron", "Ľ" }, { "lcaron", "ľ" }, { "Lcedil", "Ļ" }, { "lcedil", "ļ" }, { "Lmidot", "Ŀ" }, { "lmidot", "ŀ" }, { "Lstrok", "Ł" }, { "lstrok", "ł" }, { "Nacute", "Ń" }, { "nacute", "ń" }, { "napos", "ʼn" }, { "Ncaron", "Ň" }, { "ncaron", "ň" }, { "Ncedil", "Ņ" }, { "ncedil", "ņ" }, { "Odblac", "Ő" }, { "odblac", "ő" }, { "OElig", "Œ" }, { "oelig", "œ" }, { "Omacr", "Ō" }, { "omacr", "ō" }, { "Racute", "Ŕ" }, { "racute", "ŕ" }, { "Rcaron", "Ř" }, { "rcaron", "ř" }, { "Rcedil", "Ŗ" }, { "rcedil", "ŗ" }, { "Sacute", "Ś" }, { "sacute", "ś" }, { "Scaron", "Š" }, { "scaron", "š" }, { "Scedil", "Ş" }, { "scedil", "ş" }, { "Scirc", "Ŝ" }, { "scirc", "ŝ" }, { "Tcaron", "Ť" }, { "tcaron", "ť" }, { "Tcedil", "Ţ" }, { "tcedil", "ţ" }, { "Tstrok", "Ŧ" }, { "tstrok", "ŧ" }, { "Ubreve", "Ŭ" }, { "ubreve", "ŭ" }, { "Udblac", "Ű" }, { "udblac", "ű" }, { "Umacr", "Ū" }, { "umacr", "ū" }, { "Uogon", "Ų" }, { "uogon", "ų" }, { "Uring", "Ů" }, { "uring", "ů" }, { "Utilde", "Ũ" }, { "utilde", "ũ" }, { "Wcirc", "Ŵ" }, { "wcirc", "ŵ" }, { "Ycirc", "Ŷ" }, { "ycirc", "ŷ" }, { "Yuml", "Ÿ" }, { "Zacute", "Ź" }, { "zacute", "ź" }, { "Zcaron", "Ž" }, { "zcaron", "ž" }, { "Zdot", "Ż" }, { "zdot", "ż" }, { "apos", "'" }, { "ast", "*" }, { "brvbar", "¦" }, { "bsol", "\" }, { "cent", "¢" }, { "colon", ":" }, { "comma", "," }, { "commat", "@" }, { "copy", "©" }, { "curren", "¤" }, { "darr", "↓" }, { "deg", "°" }, { "divide", "÷" }, { "dollar", "$" }, { "equals", "=" }, { "excl", "!" }, { "frac12", "½" }, { "frac14", "¼" }, { "frac18", "⅛" }, { "frac34", "¾" }, { "frac38", "⅜" }, { "frac58", "⅝" }, { "frac78", "⅞" }, { "gt", ">" }, { "half", "½" }, { "horbar", "―" }, { "hyphen", "‐" }, { "iexcl", "¡" }, { "iquest", "¿" }, { "laquo", "«" }, { "larr", "←" }, { "lcub", "{" }, { "ldquo", "“" }, { "lowbar", "_" }, { "lpar", "(" }, { "lsqb", "[" }, { "lsquo", "‘" }, { "lt", "<" }, { "micro", "µ" }, { "middot", "·" }, { "nbsp", " " }, { "not", "¬" }, { "num", "#" }, { "ohm", "Ω" }, { "ordf", "ª" }, { "ordm", "º" }, { "para", "¶" }, { "percnt", "%" }, { "period", "." }, { "plus", "+" }, { "plusmn", "±" }, { "pound", "£" }, { "quest", "?" }, { "quot", """ }, { "raquo", "»" }, { "rarr", "→" }, { "rcub", "}" }, { "rdquo", "”" }, { "reg", "®" }, { "rpar", ")" }, { "rsqb", "]" }, { "rsquo", "’" }, { "sect", "§" }, { "semi", ";" }, { "shy", "­" }, { "sol", "/" }, { "sung", "♪" }, { "sup1", "¹" }, { "sup2", "²" }, { "sup3", "³" }, { "times", "×" }, { "trade", "™" }, { "uarr", "↑" }, { "verbar", "|" }, { "yen", "¥" }, { "blank", "␣" }, { "blk12", "▒" }, { "blk14", "░" }, { "blk34", "▓" }, { "block", "█" }, { "bull", "•" }, { "caret", "⁁" }, { "check", "✓" }, { "cir", "○" }, { "clubs", "♣" }, { "copysr", "℗" }, { "cross", "✗" }, { "Dagger", "‡" }, { "dagger", "†" }, { "dash", "‐" }, { "diams", "♦" }, { "dlcrop", "⌍" }, { "drcrop", "⌌" }, { "dtri", "▿" }, { "dtrif", "▾" }, { "emsp", " " }, { "emsp13", " " }, { "emsp14", " " }, { "ensp", " " }, { "female", "♀" }, { "ffilig", "ffi" }, { "fflig", "ff" }, { "ffllig", "ffl" }, { "filig", "fi" }, { "flat", "♭" }, { "fllig", "fl" }, { "frac13", "⅓" }, { "frac15", "⅕" }, { "frac16", "⅙" }, { "frac23", "⅔" }, { "frac25", "⅖" }, { "frac35", "⅗" }, { "frac45", "⅘" }, { "frac56", "⅚" }, { "hairsp", " " }, { "hearts", "♥" }, { "hellip", "…" }, { "hybull", "⁃" }, { "incare", "℅" }, { "ldquor", "„" }, { "lhblk", "▄" }, { "loz", "◊" }, { "lozf", "⧫" }, { "lsquor", "‚" }, { "ltri", "◃" }, { "ltrif", "◂" }, { "male", "♂" }, { "malt", "✠" }, { "marker", "▮" }, { "mdash", "—" }, { "mldr", "…" }, { "natur", "♮" }, { "ndash", "–" }, { "nldr", "‥" }, { "numsp", " " }, { "phone", "☎" }, { "puncsp", " " }, { "rdquor", "”" }, { "rect", "▭" }, { "rsquor", "’" }, { "rtri", "▹" }, { "rtrif", "▸" }, { "rx", "℞" }, { "sext", "✶" }, { "sharp", "♯" }, { "spades", "♠" }, { "squ", "□" }, { "squf", "▪" }, { "star", "☆" }, { "starf", "★" }, { "target", "⌖" }, { "telrec", "⌕" }, { "thinsp", " " }, { "uhblk", "▀" }, { "ulcrop", "⌏" }, { "urcrop", "⌎" }, { "utri", "▵" }, { "utrif", "▴" }, { "vellip", "⋮" }, { "af", "⁡" }, { "asympeq", "≍" }, { "Cross", "⨯" }, { "DD", "ⅅ" }, { "dd", "ⅆ" }, { "DownArrowBar", "⤓" }, { "DownBreve", "̑" }, { "DownLeftRightVector", "⥐" }, { "DownLeftTeeVector", "⥞" }, { "DownLeftVectorBar", "⥖" }, { "DownRightTeeVector", "⥟" }, { "DownRightVectorBar", "⥗" }, { "ee", "ⅇ" }, { "EmptySmallSquare", "◻" }, { "EmptyVerySmallSquare", "▫" }, { "Equal", "⩵" }, { "FilledSmallSquare", "◼" }, { "FilledVerySmallSquare", "▪" }, { "GreaterGreater", "⪢" }, { "Hat", "^" }, { "HorizontalLine", "─" }, { "ic", "⁣" }, { "ii", "ⅈ" }, { "it", "⁢" }, { "larrb", "⇤" }, { "LeftDownTeeVector", "⥡" }, { "LeftDownVectorBar", "⥙" }, { "LeftRightVector", "⥎" }, { "LeftTeeVector", "⥚" }, { "LeftTriangleBar", "⧏" }, { "LeftUpDownVector", "⥑" }, { "LeftUpTeeVector", "⥠" }, { "LeftUpVectorBar", "⥘" }, { "LeftVectorBar", "⥒" }, { "LessLess", "⪡" }, { "mapstodown", "↧" }, { "mapstoleft", "↤" }, { "mapstoup", "↥" }, { "MediumSpace", " " }, { "nbump", "≎̸" }, { "nbumpe", "≏̸" }, { "nesim", "≂̸" }, { "NewLine", " " }, { "NoBreak", "⁠" }, { "NotCupCap", "≭" }, { "NotHumpEqual", "≏̸" }, { "NotLeftTriangleBar", "⧏̸" }, { "NotNestedGreaterGreater", "⪢̸" }, { "NotNestedLessLess", "⪡̸" }, { "NotRightTriangleBar", "⧐̸" }, { "NotSquareSubset", "⊏̸" }, { "NotSquareSuperset", "⊐̸" }, { "NotSucceedsTilde", "≿̸" }, { "OverBar", "¯" }, { "OverBrace", "︷" }, { "OverBracket", "⎴" }, { "OverParenthesis", "︵" }, { "planckh", "ℎ" }, { "Product", "∏" }, { "rarrb", "⇥" }, { "RightDownTeeVector", "⥝" }, { "RightDownVectorBar", "⥕" }, { "RightTeeVector", "⥛" }, { "RightTriangleBar", "⧐" }, { "RightUpDownVector", "⥏" }, { "RightUpTeeVector", "⥜" }, { "RightUpVectorBar", "⥔" }, { "RightVectorBar", "⥓" }, { "RoundImplies", "⥰" }, { "RuleDelayed", "⧴" }, { "Tab", " " }, { "ThickSpace", "   " }, { "UnderBar", "̲" }, { "UnderBrace", "︸" }, { "UnderBracket", "⎵" }, { "UnderParenthesis", "︶" }, { "UpArrowBar", "⤒" }, { "Upsilon", "Υ" }, { "VerticalLine", "|" }, { "VerticalSeparator", "❘" }, { "ZeroWidthSpace", "​" }, { "angle", "∠" }, { "ApplyFunction", "⁡" }, { "approx", "≈" }, { "approxeq", "≊" }, { "Assign", "≔" }, { "backcong", "≌" }, { "backepsilon", "϶" }, { "backprime", "‵" }, { "backsim", "∽" }, { "backsimeq", "⋍" }, { "Backslash", "∖" }, { "barwedge", "⌅" }, { "Because", "∵" }, { "because", "∵" }, { "Bernoullis", "ℬ" }, { "between", "≬" }, { "bigcap", "⋂" }, { "bigcirc", "◯" }, { "bigcup", "⋃" }, { "bigodot", "⨀" }, { "bigoplus", "⨁" }, { "bigotimes", "⨂" }, { "bigsqcup", "⨆" }, { "bigstar", "★" }, { "bigtriangledown", "▽" }, { "bigtriangleup", "△" }, { "biguplus", "⨄" }, { "bigvee", "⋁" }, { "bigwedge", "⋀" }, { "bkarow", "⤍" }, { "blacklozenge", "⧫" }, { "blacksquare", "▪" }, { "blacktriangle", "▴" }, { "blacktriangledown", "▾" }, { "blacktriangleleft", "◂" }, { "blacktriangleright", "▸" }, { "bot", "⊥" }, { "boxminus", "⊟" }, { "boxplus", "⊞" }, { "boxtimes", "⊠" }, { "Breve", "˘" }, { "bullet", "•" }, { "Bumpeq", "≎" }, { "bumpeq", "≏" }, { "CapitalDifferentialD", "ⅅ" }, { "Cayleys", "ℭ" }, { "Cedilla", "¸" }, { "CenterDot", "·" }, { "centerdot", "·" }, { "checkmark", "✓" }, { "circeq", "≗" }, { "circlearrowleft", "↺" }, { "circlearrowright", "↻" }, { "circledast", "⊛" }, { "circledcirc", "⊚" }, { "circleddash", "⊝" }, { "CircleDot", "⊙" }, { "circledR", "®" }, { "circledS", "Ⓢ" }, { "CircleMinus", "⊖" }, { "CirclePlus", "⊕" }, { "CircleTimes", "⊗" }, { "ClockwiseContourIntegral", "∲" }, { "CloseCurlyDoubleQuote", "”" }, { "CloseCurlyQuote", "’" }, { "clubsuit", "♣" }, { "coloneq", "≔" }, { "complement", "∁" }, { "complexes", "ℂ" }, { "Congruent", "≡" }, { "ContourIntegral", "∮" }, { "Coproduct", "∐" }, { "CounterClockwiseContourIntegral", "∳" }, { "CupCap", "≍" }, { "curlyeqprec", "⋞" }, { "curlyeqsucc", "⋟" }, { "curlyvee", "⋎" }, { "curlywedge", "⋏" }, { "curvearrowleft", "↶" }, { "curvearrowright", "↷" }, { "dbkarow", "⤏" }, { "ddagger", "‡" }, { "ddotseq", "⩷" }, { "Del", "∇" }, { "DiacriticalAcute", "´" }, { "DiacriticalDot", "˙" }, { "DiacriticalDoubleAcute", "˝" }, { "DiacriticalGrave", "`" }, { "DiacriticalTilde", "˜" }, { "Diamond", "⋄" }, { "diamond", "⋄" }, { "diamondsuit", "♦" }, { "DifferentialD", "ⅆ" }, { "digamma", "ϝ" }, { "div", "÷" }, { "divideontimes", "⋇" }, { "doteq", "≐" }, { "doteqdot", "≑" }, { "DotEqual", "≐" }, { "dotminus", "∸" }, { "dotplus", "∔" }, { "dotsquare", "⊡" }, { "doublebarwedge", "⌆" }, { "DoubleContourIntegral", "∯" }, { "DoubleDot", "¨" }, { "DoubleDownArrow", "⇓" }, { "DoubleLeftArrow", "⇐" }, { "DoubleLeftRightArrow", "⇔" }, { "DoubleLeftTee", "⫤" }, { "DoubleLongLeftArrow", "⟸" }, { "DoubleLongLeftRightArrow", "⟺" }, { "DoubleLongRightArrow", "⟹" }, { "DoubleRightArrow", "⇒" }, { "DoubleRightTee", "⊨" }, { "DoubleUpArrow", "⇑" }, { "DoubleUpDownArrow", "⇕" }, { "DoubleVerticalBar", "∥" }, { "DownArrow", "↓" }, { "Downarrow", "⇓" }, { "downarrow", "↓" }, { "DownArrowUpArrow", "⇵" }, { "downdownarrows", "⇊" }, { "downharpoonleft", "⇃" }, { "downharpoonright", "⇂" }, { "DownLeftVector", "↽" }, { "DownRightVector", "⇁" }, { "DownTee", "⊤" }, { "DownTeeArrow", "↧" }, { "drbkarow", "⤐" }, { "Element", "∈" }, { "emptyset", "∅" }, { "eqcirc", "≖" }, { "eqcolon", "≕" }, { "eqsim", "≂" }, { "eqslantgtr", "⪖" }, { "eqslantless", "⪕" }, { "EqualTilde", "≂" }, { "Equilibrium", "⇌" }, { "Exists", "∃" }, { "expectation", "ℰ" }, { "ExponentialE", "ⅇ" }, { "exponentiale", "ⅇ" }, { "fallingdotseq", "≒" }, { "ForAll", "∀" }, { "Fouriertrf", "ℱ" }, { "geq", "≥" }, { "geqq", "≧" }, { "geqslant", "⩾" }, { "gg", "≫" }, { "ggg", "⋙" }, { "gnapprox", "⪊" }, { "gneq", "⪈" }, { "gneqq", "≩" }, { "GreaterEqual", "≥" }, { "GreaterEqualLess", "⋛" }, { "GreaterFullEqual", "≧" }, { "GreaterLess", "≷" }, { "GreaterSlantEqual", "⩾" }, { "GreaterTilde", "≳" }, { "gtrapprox", "⪆" }, { "gtrdot", "⋗" }, { "gtreqless", "⋛" }, { "gtreqqless", "⪌" }, { "gtrless", "≷" }, { "gtrsim", "≳" }, { "gvertneqq", "≩︀" }, { "Hacek", "ˇ" }, { "hbar", "ℏ" }, { "heartsuit", "♥" }, { "HilbertSpace", "ℋ" }, { "hksearow", "⤥" }, { "hkswarow", "⤦" }, { "hookleftarrow", "↩" }, { "hookrightarrow", "↪" }, { "hslash", "ℏ" }, { "HumpDownHump", "≎" }, { "HumpEqual", "≏" }, { "iiiint", "⨌" }, { "iiint", "∭" }, { "Im", "ℑ" }, { "ImaginaryI", "ⅈ" }, { "imagline", "ℐ" }, { "imagpart", "ℑ" }, { "Implies", "⇒" }, { "in", "∈" }, { "integers", "ℤ" }, { "Integral", "∫" }, { "intercal", "⊺" }, { "Intersection", "⋂" }, { "intprod", "⨼" }, { "InvisibleComma", "⁣" }, { "InvisibleTimes", "⁢" }, { "langle", "〈" }, { "Laplacetrf", "ℒ" }, { "lbrace", "{" }, { "lbrack", "[" }, { "LeftAngleBracket", "〈" }, { "LeftArrow", "←" }, { "Leftarrow", "⇐" }, { "leftarrow", "←" }, { "LeftArrowBar", "⇤" }, { "LeftArrowRightArrow", "⇆" }, { "leftarrowtail", "↢" }, { "LeftCeiling", "⌈" }, { "LeftDoubleBracket", "〚" }, { "LeftDownVector", "⇃" }, { "LeftFloor", "⌊" }, { "leftharpoondown", "↽" }, { "leftharpoonup", "↼" }, { "leftleftarrows", "⇇" }, { "LeftRightArrow", "↔" }, { "Leftrightarrow", "⇔" }, { "leftrightarrow", "↔" }, { "leftrightarrows", "⇆" }, { "leftrightharpoons", "⇋" }, { "leftrightsquigarrow", "↭" }, { "LeftTee", "⊣" }, { "LeftTeeArrow", "↤" }, { "leftthreetimes", "⋋" }, { "LeftTriangle", "⊲" }, { "LeftTriangleEqual", "⊴" }, { "LeftUpVector", "↿" }, { "LeftVector", "↼" }, { "leq", "≤" }, { "leqq", "≦" }, { "leqslant", "⩽" }, { "lessapprox", "⪅" }, { "lessdot", "⋖" }, { "lesseqgtr", "⋚" }, { "lesseqqgtr", "⪋" }, { "LessEqualGreater", "⋚" }, { "LessFullEqual", "≦" }, { "LessGreater", "≶" }, { "lessgtr", "≶" }, { "lesssim", "≲" }, { "LessSlantEqual", "⩽" }, { "LessTilde", "≲" }, { "ll", "≪" }, { "llcorner", "⌞" }, { "Lleftarrow", "⇚" }, { "lmoustache", "⎰" }, { "lnapprox", "⪉" }, { "lneq", "⪇" }, { "lneqq", "≨" }, { "LongLeftArrow", "⟵" }, { "Longleftarrow", "⟸" }, { "longleftarrow", "⟵" }, { "LongLeftRightArrow", "⟷" }, { "Longleftrightarrow", "⟺" }, { "longleftrightarrow", "⟷" }, { "longmapsto", "⟼" }, { "LongRightArrow", "⟶" }, { "Longrightarrow", "⟹" }, { "longrightarrow", "⟶" }, { "looparrowleft", "↫" }, { "looparrowright", "↬" }, { "LowerLeftArrow", "↙" }, { "LowerRightArrow", "↘" }, { "lozenge", "◊" }, { "lrcorner", "⌟" }, { "Lsh", "↰" }, { "lvertneqq", "≨︀" }, { "maltese", "✠" }, { "mapsto", "↦" }, { "measuredangle", "∡" }, { "Mellintrf", "ℳ" }, { "MinusPlus", "∓" }, { "mp", "∓" }, { "multimap", "⊸" }, { "napprox", "≉" }, { "natural", "♮" }, { "naturals", "ℕ" }, { "nearrow", "↗" }, { "NegativeMediumSpace", "​" }, { "NegativeThickSpace", "​" }, { "NegativeThinSpace", "​" }, { "NegativeVeryThinSpace", "​" }, { "NestedGreaterGreater", "≫" }, { "NestedLessLess", "≪" }, { "nexists", "∄" }, { "ngeq", "≱" }, { "ngeqq", "≧̸" }, { "ngeqslant", "⩾̸" }, { "ngtr", "≯" }, { "nLeftarrow", "⇍" }, { "nleftarrow", "↚" }, { "nLeftrightarrow", "⇎" }, { "nleftrightarrow", "↮" }, { "nleq", "≰" }, { "nleqq", "≦̸" }, { "nleqslant", "⩽̸" }, { "nless", "≮" }, { "NonBreakingSpace", " " }, { "NotCongruent", "≢" }, { "NotDoubleVerticalBar", "∦" }, { "NotElement", "∉" }, { "NotEqual", "≠" }, { "NotEqualTilde", "≂̸" }, { "NotExists", "∄" }, { "NotGreater", "≯" }, { "NotGreaterEqual", "≱" }, { "NotGreaterFullEqual", "≦̸" }, { "NotGreaterGreater", "≫̸" }, { "NotGreaterLess", "≹" }, { "NotGreaterSlantEqual", "⩾̸" }, { "NotGreaterTilde", "≵" }, { "NotHumpDownHump", "≎̸" }, { "NotLeftTriangle", "⋪" }, { "NotLeftTriangleEqual", "⋬" }, { "NotLess", "≮" }, { "NotLessEqual", "≰" }, { "NotLessGreater", "≸" }, { "NotLessLess", "≪̸" }, { "NotLessSlantEqual", "⩽̸" }, { "NotLessTilde", "≴" }, { "NotPrecedes", "⊀" }, { "NotPrecedesEqual", "⪯̸" }, { "NotPrecedesSlantEqual", "⋠" }, { "NotReverseElement", "∌" }, { "NotRightTriangle", "⋫" }, { "NotRightTriangleEqual", "⋭" }, { "NotSquareSubsetEqual", "⋢" }, { "NotSquareSupersetEqual", "⋣" }, { "NotSubset", "⊂⃒" }, { "NotSubsetEqual", "⊈" }, { "NotSucceeds", "⊁" }, { "NotSucceedsEqual", "⪰̸" }, { "NotSucceedsSlantEqual", "⋡" }, { "NotSuperset", "⊃⃒" }, { "NotSupersetEqual", "⊉" }, { "NotTilde", "≁" }, { "NotTildeEqual", "≄" }, { "NotTildeFullEqual", "≇" }, { "NotTildeTilde", "≉" }, { "NotVerticalBar", "∤" }, { "nparallel", "∦" }, { "nprec", "⊀" }, { "npreceq", "⪯̸" }, { "nRightarrow", "⇏" }, { "nrightarrow", "↛" }, { "nshortmid", "∤" }, { "nshortparallel", "∦" }, { "nsimeq", "≄" }, { "nsubset", "⊂⃒" }, { "nsubseteq", "⊈" }, { "nsubseteqq", "⫅̸" }, { "nsucc", "⊁" }, { "nsucceq", "⪰̸" }, { "nsupset", "⊃⃒" }, { "nsupseteq", "⊉" }, { "nsupseteqq", "⫆̸" }, { "ntriangleleft", "⋪" }, { "ntrianglelefteq", "⋬" }, { "ntriangleright", "⋫" }, { "ntrianglerighteq", "⋭" }, { "nwarrow", "↖" }, { "oint", "∮" }, { "OpenCurlyDoubleQuote", "“" }, { "OpenCurlyQuote", "‘" }, { "orderof", "ℴ" }, { "parallel", "∥" }, { "PartialD", "∂" }, { "pitchfork", "⋔" }, { "PlusMinus", "±" }, { "pm", "±" }, { "Poincareplane", "ℌ" }, { "prec", "≺" }, { "precapprox", "⪷" }, { "preccurlyeq", "≼" }, { "Precedes", "≺" }, { "PrecedesEqual", "⪯" }, { "PrecedesSlantEqual", "≼" }, { "PrecedesTilde", "≾" }, { "preceq", "⪯" }, { "precnapprox", "⪹" }, { "precneqq", "⪵" }, { "precnsim", "⋨" }, { "precsim", "≾" }, { "primes", "ℙ" }, { "Proportion", "∷" }, { "Proportional", "∝" }, { "propto", "∝" }, { "quaternions", "ℍ" }, { "questeq", "≟" }, { "rangle", "〉" }, { "rationals", "ℚ" }, { "rbrace", "}" }, { "rbrack", "]" }, { "Re", "ℜ" }, { "realine", "ℛ" }, { "realpart", "ℜ" }, { "reals", "ℝ" }, { "ReverseElement", "∋" }, { "ReverseEquilibrium", "⇋" }, { "ReverseUpEquilibrium", "⥯" }, { "RightAngleBracket", "〉" }, { "RightArrow", "→" }, { "Rightarrow", "⇒" }, { "rightarrow", "→" }, { "RightArrowBar", "⇥" }, { "RightArrowLeftArrow", "⇄" }, { "rightarrowtail", "↣" }, { "RightCeiling", "⌉" }, { "RightDoubleBracket", "〛" }, { "RightDownVector", "⇂" }, { "RightFloor", "⌋" }, { "rightharpoondown", "⇁" }, { "rightharpoonup", "⇀" }, { "rightleftarrows", "⇄" }, { "rightleftharpoons", "⇌" }, { "rightrightarrows", "⇉" }, { "rightsquigarrow", "↝" }, { "RightTee", "⊢" }, { "RightTeeArrow", "↦" }, { "rightthreetimes", "⋌" }, { "RightTriangle", "⊳" }, { "RightTriangleEqual", "⊵" }, { "RightUpVector", "↾" }, { "RightVector", "⇀" }, { "risingdotseq", "≓" }, { "rmoustache", "⎱" }, { "Rrightarrow", "⇛" }, { "Rsh", "↱" }, { "searrow", "↘" }, { "setminus", "∖" }, { "ShortDownArrow", "↓" }, { "ShortLeftArrow", "←" }, { "shortmid", "∣" }, { "shortparallel", "∥" }, { "ShortRightArrow", "→" }, { "ShortUpArrow", "↑" }, { "simeq", "≃" }, { "SmallCircle", "∘" }, { "smallsetminus", "∖" }, { "spadesuit", "♠" }, { "Sqrt", "√" }, { "sqsubset", "⊏" }, { "sqsubseteq", "⊑" }, { "sqsupset", "⊐" }, { "sqsupseteq", "⊒" }, { "Square", "□" }, { "SquareIntersection", "⊓" }, { "SquareSubset", "⊏" }, { "SquareSubsetEqual", "⊑" }, { "SquareSuperset", "⊐" }, { "SquareSupersetEqual", "⊒" }, { "SquareUnion", "⊔" }, { "Star", "⋆" }, { "straightepsilon", "ε" }, { "straightphi", "ϕ" }, { "Subset", "⋐" }, { "subset", "⊂" }, { "subseteq", "⊆" }, { "subseteqq", "⫅" }, { "SubsetEqual", "⊆" }, { "subsetneq", "⊊" }, { "subsetneqq", "⫋" }, { "succ", "≻" }, { "succapprox", "⪸" }, { "succcurlyeq", "≽" }, { "Succeeds", "≻" }, { "SucceedsEqual", "⪰" }, { "SucceedsSlantEqual", "≽" }, { "SucceedsTilde", "≿" }, { "succeq", "⪰" }, { "succnapprox", "⪺" }, { "succneqq", "⪶" }, { "succnsim", "⋩" }, { "succsim", "≿" }, { "SuchThat", "∋" }, { "Sum", "∑" }, { "Superset", "⊃" }, { "SupersetEqual", "⊇" }, { "Supset", "⋑" }, { "supset", "⊃" }, { "supseteq", "⊇" }, { "supseteqq", "⫆" }, { "supsetneq", "⊋" }, { "supsetneqq", "⫌" }, { "swarrow", "↙" }, { "Therefore", "∴" }, { "therefore", "∴" }, { "thickapprox", "≈" }, { "thicksim", "∼" }, { "ThinSpace", " " }, { "Tilde", "∼" }, { "TildeEqual", "≃" }, { "TildeFullEqual", "≅" }, { "TildeTilde", "≈" }, { "toea", "⤨" }, { "tosa", "⤩" }, { "triangle", "▵" }, { "triangledown", "▿" }, { "triangleleft", "◃" }, { "trianglelefteq", "⊴" }, { "triangleq", "≜" }, { "triangleright", "▹" }, { "trianglerighteq", "⊵" }, { "TripleDot", "⃛" }, { "twoheadleftarrow", "↞" }, { "twoheadrightarrow", "↠" }, { "ulcorner", "⌜" }, { "Union", "⋃" }, { "UnionPlus", "⊎" }, { "UpArrow", "↑" }, { "Uparrow", "⇑" }, { "uparrow", "↑" }, { "UpArrowDownArrow", "⇅" }, { "UpDownArrow", "↕" }, { "Updownarrow", "⇕" }, { "updownarrow", "↕" }, { "UpEquilibrium", "⥮" }, { "upharpoonleft", "↿" }, { "upharpoonright", "↾" }, { "UpperLeftArrow", "↖" }, { "UpperRightArrow", "↗" }, { "upsilon", "υ" }, { "UpTee", "⊥" }, { "UpTeeArrow", "↥" }, { "upuparrows", "⇈" }, { "urcorner", "⌝" }, { "varepsilon", "ɛ" }, { "varkappa", "ϰ" }, { "varnothing", "∅" }, { "varphi", "φ" }, { "varpi", "ϖ" }, { "varpropto", "∝" }, { "varrho", "ϱ" }, { "varsigma", "ς" }, { "varsubsetneq", "⊊︀" }, { "varsubsetneqq", "⫋︀" }, { "varsupsetneq", "⊋︀" }, { "varsupsetneqq", "⫌︀" }, { "vartheta", "ϑ" }, { "vartriangleleft", "⊲" }, { "vartriangleright", "⊳" }, { "Vee", "⋁" }, { "vee", "∨" }, { "Vert", "‖" }, { "vert", "|" }, { "VerticalBar", "∣" }, { "VerticalTilde", "≀" }, { "VeryThinSpace", " " }, { "Wedge", "⋀" }, { "wedge", "∧" }, { "wp", "℘" }, { "wr", "≀" }, { "zeetrf", "ℨ" }, { 0, 0 } }; // ******************************************************************* // MmlDocument // ******************************************************************* QString MmlDocument::fontName(QtMmlWidget::MmlFont type) const { switch (type) { case QtMmlWidget::NormalFont: return m_normal_font_name; case QtMmlWidget::FrakturFont: return m_fraktur_font_name; case QtMmlWidget::SansSerifFont: return m_sans_serif_font_name; case QtMmlWidget::ScriptFont: return m_script_font_name; case QtMmlWidget::MonospaceFont: return m_monospace_font_name; case QtMmlWidget::DoublestruckFont: return m_doublestruck_font_name; }; return QString::null; } void MmlDocument::setFontName(QtMmlWidget::MmlFont type, const QString &name) { switch (type) { case QtMmlWidget::NormalFont: m_normal_font_name = name; break; case QtMmlWidget::FrakturFont: m_fraktur_font_name = name; break; case QtMmlWidget::SansSerifFont: m_sans_serif_font_name = name; break; case QtMmlWidget::ScriptFont: m_script_font_name = name; break; case QtMmlWidget::MonospaceFont: m_monospace_font_name = name; break; case QtMmlWidget::DoublestruckFont: m_doublestruck_font_name = name; break; }; } Mml::NodeType domToMmlNodeType(const QDomNode &dom_node) { Mml::NodeType mml_type = Mml::NoNode; switch (dom_node.nodeType()) { case QDomNode::ElementNode: { QString tag = dom_node.nodeName(); const NodeSpec *spec = mmlFindNodeSpec(tag); // treat urecognised tags as mrow if (spec == 0) mml_type = Mml::UnknownNode; else mml_type = spec->type; break; } case QDomNode::TextNode: mml_type = Mml::TextNode; break; case QDomNode::DocumentNode: mml_type = Mml::MrowNode; break; case QDomNode::EntityReferenceNode: // qWarning("EntityReferenceNode: name=\"" + dom_node.nodeName() + "\" value=\"" + dom_node.nodeValue() + "\""); break; case QDomNode::AttributeNode: case QDomNode::CDATASectionNode: case QDomNode::EntityNode: case QDomNode::ProcessingInstructionNode: case QDomNode::CommentNode: case QDomNode::DocumentTypeNode: case QDomNode::DocumentFragmentNode: case QDomNode::NotationNode: case QDomNode::BaseNode: case QDomNode::CharacterDataNode: break; } return mml_type; } MmlDocument::MmlDocument() { m_root_node = 0; // Some defaults which happen to work on my computer, // but probably won't work on other's #if defined(Q_WS_WIN) m_normal_font_name = "Times New Roman"; #else m_normal_font_name = "Century Schoolbook L"; #endif m_fraktur_font_name = "Fraktur"; m_sans_serif_font_name = "Luxi Sans"; m_script_font_name = "Urw Chancery L"; m_monospace_font_name = "Luxi Mono"; m_doublestruck_font_name = "Doublestruck"; m_base_font_point_size = 16; m_foreground_color = Qt::black; m_background_color = Qt::white; } MmlDocument::~MmlDocument() { clear(); } void MmlDocument::clear() { delete m_root_node; m_root_node = 0; } void MmlDocument::dump() const { if (m_root_node == 0) return; QString indent; _dump(m_root_node, indent); } void MmlDocument::_dump(const MmlNode *node, QString &indent) const { if (node == 0) return; qWarning("%s", (indent + node->toStr()).toLatin1().data()); indent += " "; const MmlNode *child = node->firstChild(); for (; child != 0; child = child->nextSibling()) _dump(child, indent); indent.truncate(indent.length() - 2); } bool MmlDocument::setContent(QString text, QString *errorMsg, int *errorLine, int *errorColumn) { clear(); QString prefix = "\n"; prefix.append(entityDeclarations()); uint prefix_lines = 0; for (int i = 0; i < prefix.length(); ++i) { if (prefix.at(i) == '\n') ++prefix_lines; } QDomDocument dom; if (!dom.setContent(prefix + text, false, errorMsg, errorLine, errorColumn)) { if (errorLine != 0) *errorLine -= prefix_lines; return false; } // we don't have access to line info from now on if (errorLine != 0) *errorLine = -1; if (errorColumn != 0) *errorColumn = -1; bool ok; MmlNode *root_node = domToMml(dom, &ok, errorMsg); if (!ok) return false; if (root_node == 0) { if (errorMsg != 0) *errorMsg = "empty document"; return false; } insertChild(0, root_node, 0); layout(); /* QFile of("/tmp/dump.xml"); of.open(IO_WriteOnly); QTextStream os(&of); os.setEncoding(QTextStream::UnicodeUTF8); os << dom.toString(); */ return true; } void MmlDocument::layout() { if (m_root_node == 0) return; m_root_node->layout(); m_root_node->stretch(); // dump(); } bool MmlDocument::insertChild(MmlNode *parent, MmlNode *new_node, QString *errorMsg) { if (new_node == 0) return true; Q_ASSERT(new_node->parent() == 0 && new_node->nextSibling() == 0 && new_node->previousSibling() == 0); if (parent != 0) { if (!mmlCheckChildType(parent->nodeType(), new_node->nodeType(), errorMsg)) return false; } if (parent == 0) { if (m_root_node == 0) m_root_node = new_node; else { MmlNode *n = m_root_node->lastSibling(); n->m_next_sibling = new_node; new_node->m_previous_sibling = n; } } else { new_node->m_parent = parent; if (parent->hasChildNodes()) { MmlNode *n = parent->firstChild()->lastSibling(); n->m_next_sibling = new_node; new_node->m_previous_sibling = n; } else parent->m_first_child = new_node; } return true; } MmlNode *MmlDocument::createNode(NodeType type, const MmlAttributeMap &mml_attr, const QString &mml_value, QString *errorMsg) { Q_ASSERT(type != NoNode); MmlNode *mml_node = 0; if (!mmlCheckAttributes(type, mml_attr, errorMsg)) return 0; switch (type) { case MiNode: mml_node = new MmlMiNode(this, mml_attr); break; case MnNode: mml_node = new MmlMnNode(this, mml_attr); break; case MfracNode: mml_node = new MmlMfracNode(this, mml_attr); break; case MrowNode: mml_node = new MmlMrowNode(this, mml_attr); break; case MsqrtNode: mml_node = new MmlMsqrtNode(this, mml_attr); break; case MrootNode: mml_node = new MmlMrootNode(this, mml_attr); break; case MsupNode: mml_node = new MmlMsupNode(this, mml_attr); break; case MsubNode: mml_node = new MmlMsubNode(this, mml_attr); break; case MsubsupNode: mml_node = new MmlMsubsupNode(this, mml_attr); break; case MoNode: mml_node = new MmlMoNode(this, mml_attr); break; case MstyleNode: mml_node = new MmlMstyleNode(this, mml_attr); break; case TextNode: mml_node = new MmlTextNode(mml_value, this); break; case MphantomNode: mml_node = new MmlMphantomNode(this, mml_attr); break; case MfencedNode: mml_node = new MmlMfencedNode(this, mml_attr); break; case MtableNode: mml_node = new MmlMtableNode(this, mml_attr); break; case MtrNode: mml_node = new MmlMtrNode(this, mml_attr); break; case MtdNode: mml_node = new MmlMtdNode(this, mml_attr); break; case MoverNode: mml_node = new MmlMoverNode(this, mml_attr); break; case MunderNode: mml_node = new MmlMunderNode(this, mml_attr); break; case MunderoverNode: mml_node = new MmlMunderoverNode(this, mml_attr); break; case MalignMarkNode: mml_node = new MmlMalignMarkNode(this); break; case MerrorNode: mml_node = new MmlMerrorNode(this, mml_attr); break; case MtextNode: mml_node = new MmlMtextNode(this, mml_attr); break; case MpaddedNode: mml_node = new MmlMpaddedNode(this, mml_attr); break; case MspaceNode: mml_node = new MmlMspaceNode(this, mml_attr); break; case UnknownNode: mml_node = new MmlUnknownNode(this, mml_attr); break; case NoNode: mml_node = 0; break; } return mml_node; } void MmlDocument::insertOperator(MmlNode *node, const QString &text) { MmlNode *text_node = createNode(TextNode, MmlAttributeMap(), text, 0); MmlNode *mo_node = createNode(MoNode, MmlAttributeMap(), QString::null, 0); bool ok = insertChild(node, mo_node, 0); Q_ASSERT( ok ); ok = insertChild(mo_node, text_node, 0); Q_ASSERT( ok ); } MmlNode *MmlDocument::domToMml(const QDomNode &dom_node, bool *ok, QString *errorMsg) { // create the node Q_ASSERT(ok != 0); NodeType mml_type = domToMmlNodeType(dom_node); if (mml_type == NoNode) { *ok = true; return 0; } QDomNamedNodeMap dom_attr = dom_node.attributes(); MmlAttributeMap mml_attr; for (int i = 0; i < int(dom_attr.length()); ++i) { QDomNode attr_node = dom_attr.item(i); Q_ASSERT(!attr_node.nodeName().isNull()); Q_ASSERT(!attr_node.nodeValue().isNull()); mml_attr[attr_node.nodeName()] = attr_node.nodeValue(); } QString mml_value; if (mml_type == TextNode) mml_value = dom_node.nodeValue(); MmlNode *mml_node = createNode(mml_type, mml_attr, mml_value, errorMsg); if (mml_node == 0) { *ok = false; return 0; } // create the node's children according to the child_spec const NodeSpec *spec = mmlFindNodeSpec(mml_type); QDomNodeList dom_child_list = dom_node.childNodes(); int child_cnt = dom_child_list.count(); MmlNode *mml_child = 0; QString separator_list; if (mml_type == MfencedNode) separator_list = mml_node->explicitAttribute("separators", ","); switch (spec->child_spec) { case NodeSpec::ChildIgnore: break; case NodeSpec::ImplicitMrow: if (child_cnt > 0) { mml_child = createImplicitMrowNode(dom_node, ok, errorMsg); if (!*ok) { delete mml_node; return 0; } if (!insertChild(mml_node, mml_child, errorMsg)) { delete mml_node; delete mml_child; *ok = false; return 0; } } break; default: // exact ammount of children specified - check... if (spec->child_spec != child_cnt) { if (errorMsg != 0) *errorMsg = QString("element ") + spec->tag + " requires exactly " + QString::number(spec->child_spec) + " arguments, got " + QString::number(child_cnt); delete mml_node; *ok = false; return 0; } // ...and continue just as in ChildAny case NodeSpec::ChildAny: if (mml_type == MfencedNode) insertOperator(mml_node, mml_node->explicitAttribute("open", "(")); for (int i = 0; i < child_cnt; ++i) { QDomNode dom_child = dom_child_list.item(i); MmlNode *mml_child = domToMml(dom_child, ok, errorMsg); if (!*ok) { delete mml_node; return 0; } if (mml_type == MtableNode && mml_child->nodeType() != MtrNode) { MmlNode *mtr_node = createNode(MtrNode, MmlAttributeMap(), QString::null, 0); insertChild(mml_node, mtr_node, 0); if (!insertChild(mtr_node, mml_child, errorMsg)) { delete mml_node; delete mml_child; *ok = false; return 0; } } else if (mml_type == MtrNode && mml_child->nodeType() != MtdNode) { MmlNode *mtd_node = createNode(MtdNode, MmlAttributeMap(), QString::null, 0); insertChild(mml_node, mtd_node, 0); if (!insertChild(mtd_node, mml_child, errorMsg)) { delete mml_node; delete mml_child; *ok = false; return 0; } } else { if (!insertChild(mml_node, mml_child, errorMsg)) { delete mml_node; delete mml_child; *ok = false; return 0; } } if (i < child_cnt - 1 && mml_type == MfencedNode && !separator_list.isEmpty()) { QChar separator; if (i >= (int)separator_list.length()) separator = separator_list.at(separator_list.length() - 1); else separator = separator_list[i]; insertOperator(mml_node, QString(separator)); } } if (mml_type == MfencedNode) insertOperator(mml_node, mml_node->explicitAttribute("close", ")")); break; } *ok = true; return mml_node; } MmlNode *MmlDocument::createImplicitMrowNode(const QDomNode &dom_node, bool *ok, QString *errorMsg) { QDomNodeList dom_child_list = dom_node.childNodes(); int child_cnt = dom_child_list.count(); if (child_cnt == 0) { *ok = true; return 0; } if (child_cnt == 1) return domToMml(dom_child_list.item(0), ok, errorMsg); MmlNode *mml_node = createNode(MrowNode, MmlAttributeMap(), QString::null, errorMsg); Q_ASSERT(mml_node != 0); // there is no reason in heaven or hell for this to fail for (int i = 0; i < child_cnt; ++i) { QDomNode dom_child = dom_child_list.item(i); MmlNode *mml_child = domToMml(dom_child, ok, errorMsg); if (!*ok) { delete mml_node; return 0; } if (!insertChild(mml_node, mml_child, errorMsg)) { delete mml_node; delete mml_child; *ok = false; return 0; } } return mml_node; } void MmlDocument::paint(QPainter *p, const QPoint &pos) const { if (m_root_node == 0) return; /* p->save(); p->setPen(Qt::blue); p->drawLine(pos.x() - 5, pos.y(), pos.x() + 5, pos.y()); p->drawLine(pos.x(), pos.y() - 5, pos.x(), pos.y() + 5); p->restore(); */ QRect mr = m_root_node->myRect(); m_root_node->setRelOrigin(pos - mr.topLeft()); m_root_node->paint(p); } QSize MmlDocument::size() const { if (m_root_node == 0) return QSize(0, 0); return m_root_node->deviceRect().size(); } // ******************************************************************* // MmlNode // ******************************************************************* MmlNode::MmlNode(NodeType type, MmlDocument *document, const MmlAttributeMap &attribute_map) { m_parent = 0; m_first_child = 0; m_next_sibling = 0; m_previous_sibling = 0; m_node_type = type; m_document = document; m_attribute_map = attribute_map; m_my_rect = m_parent_rect = QRect(0, 0, 0, 0); m_rel_origin = QPoint(0, 0); m_stretched = false; } MmlNode::~MmlNode() { MmlNode *n = firstChild(); while (n != 0) { MmlNode *tmp = n->nextSibling(); delete n; n = tmp; } } static QString rectToStr(const QRect &rect) { return QString("[(%1, %2), %3x%4]") .arg(rect.x()) .arg(rect.y()) .arg(rect.width()) .arg(rect.height()); } QString MmlNode::toStr() const { const NodeSpec *spec = mmlFindNodeSpec(nodeType()); Q_ASSERT(spec != 0); return QString("%1 %2 mr=%3 pr=%4 dr=%5 ro=(%7, %8) str=%9") .arg(spec->type_str) .arg((unsigned long)this, 0, 16) .arg(rectToStr(myRect())) .arg(rectToStr(parentRect())) .arg(rectToStr(deviceRect())) .arg(m_rel_origin.x()) .arg(m_rel_origin.y()) .arg((int)isStretched()); } int MmlNode::interpretSpacing(const QString &value, bool *ok) const { return ::interpretSpacing(value, em(), ex(), ok); } int MmlNode::basePos() const { QFontMetrics fm(font()); return fm.strikeOutPos(); } int MmlNode::underlinePos() const { QFontMetrics fm(font()); return basePos() + fm.underlinePos(); } int MmlNode::overlinePos() const { QFontMetrics fm(font()); return basePos() - fm.overlinePos(); } MmlNode *MmlNode::lastSibling() const { const MmlNode *n = this; while (!n->isLastSibling()) n = n->nextSibling(); return const_cast(n); } MmlNode *MmlNode::firstSibling() const { const MmlNode *n = this; while (!n->isFirstSibling()) n = n->previousSibling(); return const_cast(n); } int MmlNode::em() const { return QFontMetrics(font()).boundingRect('m').width(); } int MmlNode::ex() const { return QFontMetrics(font()).boundingRect('x').height(); } int MmlNode::scriptlevel(const MmlNode *) const { int parent_sl; const MmlNode *p = parent(); if (p == 0) parent_sl = 0; else parent_sl = p->scriptlevel(this); QString expl_sl_str = explicitAttribute("scriptlevel"); if (expl_sl_str.isNull()) return parent_sl; if (expl_sl_str.startsWith("+") || expl_sl_str.startsWith("-")) { bool ok; int expl_sl = expl_sl_str.toInt(&ok); if (ok) { return parent_sl + expl_sl; } else { qWarning("MmlNode::scriptlevel(): bad value %s", expl_sl_str.toLatin1().data()); return parent_sl; } } bool ok; int expl_sl = expl_sl_str.toInt(&ok); if (ok) return expl_sl; if (expl_sl_str == "+") return parent_sl + 1; else if (expl_sl_str == "-") return parent_sl - 1; else { qWarning("MmlNode::scriptlevel(): could not parse value: \"%s\"", expl_sl_str.toLatin1().data()); return parent_sl; } } QPoint MmlNode::devicePoint(const QPoint &p) const { QRect mr = myRect(); QRect dr = deviceRect(); if (isStretched()) return dr.topLeft() + QPoint((p.x() - mr.left())*dr.width()/mr.width(), (p.y() - mr.top())*dr.height()/mr.height()); else return dr.topLeft() + p - mr.topLeft(); } QString MmlNode::inheritAttributeFromMrow(const QString &name, const QString &def) const { const MmlNode *p = this; for (; p != 0; p = p->parent()) { if (p == this || p->nodeType() == MstyleNode) { QString value = p->explicitAttribute(name); if (!value.isNull()) return value; } } return def; } QColor MmlNode::color() const { // If we are child of return red const MmlNode *p = this; for (; p != 0; p = p->parent()) { if (p->nodeType() == MerrorNode) return QColor("red"); } QString value_str = inheritAttributeFromMrow("mathcolor"); if (value_str.isNull()) value_str = inheritAttributeFromMrow("color"); if (value_str.isNull()) return QColor(); return QColor(value_str); } QColor MmlNode::background() const { QString value_str = inheritAttributeFromMrow("mathbackground"); if (value_str.isNull()) value_str = inheritAttributeFromMrow("background"); if (value_str.isNull()) return QColor(); return QColor(value_str); } static void updateFontAttr(MmlAttributeMap &font_attr, const MmlNode *n, const QString &name, const QString &preferred_name = QString::null) { if (font_attr.contains(preferred_name) || font_attr.contains(name)) return; QString value = n->explicitAttribute(name); if (!value.isNull()) font_attr[name] = value; } static MmlAttributeMap collectFontAttributes(const MmlNode *node) { MmlAttributeMap font_attr; for (const MmlNode *n = node; n != 0; n = n->parent()) { if (n == node || n->nodeType() == Mml::MstyleNode) { updateFontAttr(font_attr, n, "mathvariant"); updateFontAttr(font_attr, n, "mathsize"); // depreciated attributes updateFontAttr(font_attr, n, "fontsize", "mathsize"); updateFontAttr(font_attr, n, "fontweight", "mathvariant"); updateFontAttr(font_attr, n, "fontstyle", "mathvariant"); updateFontAttr(font_attr, n, "fontfamily", "mathvariant"); } } return font_attr; } QFont MmlNode::font() const { QFont fn(document()->fontName(QtMmlWidget::NormalFont), document()->baseFontPointSize()); int ps = fn.pointSize(); int sl = scriptlevel(); if (sl >= 0) { for (int i = 0; i < sl; ++i) ps = (int)(ps*g_script_size_multiplier); } else { for (int i = 0; i > sl; --i) ps = (int)(ps/g_script_size_multiplier); } if (ps < g_min_font_point_size) ps = g_min_font_point_size; fn.setPointSize(ps); int em = QFontMetrics(fn).boundingRect('m').width(); int ex = QFontMetrics(fn).boundingRect('x').height(); MmlAttributeMap font_attr = collectFontAttributes(this); if (font_attr.contains("mathvariant")) { QString value = font_attr["mathvariant"]; bool ok; uint mv = interpretMathVariant(value, &ok); if (ok) { if (mv & ScriptMV) fn.setFamily(document()->fontName(QtMmlWidget::ScriptFont)); if (mv & FrakturMV) fn.setFamily(document()->fontName(QtMmlWidget::FrakturFont)); if (mv & SansSerifMV) fn.setFamily(document()->fontName(QtMmlWidget::SansSerifFont)); if (mv & MonospaceMV) fn.setFamily(document()->fontName(QtMmlWidget::MonospaceFont)); if (mv & DoubleStruckMV) fn.setFamily(document()->fontName(QtMmlWidget::DoublestruckFont)); if (mv & BoldMV) fn.setBold(true); if (mv & ItalicMV) fn.setItalic(true); } } if (font_attr.contains("mathsize")) { QString value = font_attr["mathsize"]; fn = interpretMathSize(value, fn, em, ex, 0); } fn = interpretDepreciatedFontAttr(font_attr, fn, em, ex); if (nodeType() == MiNode && !font_attr.contains("mathvariant") && !font_attr.contains("fontstyle")) { const MmlMiNode *mi_node = (const MmlMiNode*) this; if (mi_node->text().length() == 1) fn.setItalic(true); } if (nodeType() == MoNode) { fn.setItalic(false); fn.setBold(false); } return fn; } QString MmlNode::explicitAttribute(const QString &name, const QString &def) const { MmlAttributeMap::const_iterator it = m_attribute_map.find(name); if (it != m_attribute_map.end()) return *it; return def; } QRect MmlNode::parentRect() const { if (isStretched()) return m_parent_rect; QRect mr = myRect(); QPoint ro = relOrigin(); return QRect(ro + mr.topLeft(), mr.size()); } void MmlNode::stretchTo(const QRect &rect) { m_parent_rect = rect; m_stretched = true; } void MmlNode::setRelOrigin(const QPoint &rel_origin) { m_rel_origin = rel_origin + QPoint(-myRect().left(), 0); m_stretched = false; } void MmlNode::updateMyRect() { m_my_rect = symbolRect(); MmlNode *child = firstChild(); for (; child != 0; child = child->nextSibling()) m_my_rect |= child->parentRect(); } void MmlNode::layout() { m_parent_rect = QRect(0, 0, 0, 0); m_stretched = false; m_rel_origin = QPoint(0, 0); MmlNode *child = firstChild(); for (; child != 0; child = child->nextSibling()) child->layout(); layoutSymbol(); updateMyRect(); if (parent() == 0) m_rel_origin = QPoint(0, 0); } QRect MmlNode::deviceRect() const { if (parent() == 0) return QRect(relOrigin() + myRect().topLeft(), myRect().size()); /* if (!isStretched()) { QRect pdr = parent()->deviceRect(); QRect pmr = parent()->myRect(); QRect pr = parentRect(); QRect mr = myRect(); return QRect(pdr.left() + pr.left() - pmr.left(), pdr.top() + pr.top() - pmr.top(), mr.width(), mr.height()); } */ QRect pdr = parent()->deviceRect(); QRect pr = parentRect(); QRect pmr = parent()->myRect(); float scale_w = 0; if (pmr.width() != 0) scale_w = (float)pdr.width()/pmr.width(); float scale_h = 0; if (pmr.height() != 0) scale_h = (float)pdr.height()/pmr.height(); return QRect(pdr.left() + ROUND((pr.left() - pmr.left())*scale_w), pdr.top() + ROUND((pr.top() - pmr.top())*scale_h), ROUND((pr.width()*scale_w)), ROUND((pr.height()*scale_h))); } void MmlNode::layoutSymbol() { // default behaves like an mrow // now lay them out in a neat row, aligning their origins to my origin int w = 0; MmlNode *child = firstChild(); for (; child != 0; child = child->nextSibling()) { child->setRelOrigin(QPoint(w, 0)); w += child->parentRect().width() + 1; } } void MmlNode::paint(QPainter *p) { if (!myRect().isValid()) return; p->save(); p->setViewport(deviceRect()); p->setWindow(myRect()); QColor fg = color(); QColor bg = background(); if (bg.isValid()) p->fillRect(myRect(), bg); if (fg.isValid()) p->setPen(color()); MmlNode *child = firstChild(); for (; child != 0; child = child->nextSibling()) child->paint(p); paintSymbol(p); p->restore(); } void MmlNode::paintSymbol(QPainter *p) const { if (g_draw_frames && myRect().isValid()) { p->save(); p->setPen(Qt::red); p->drawRect(m_my_rect); QPen pen = p->pen(); pen.setStyle(Qt::DotLine); p->setPen(pen); p->drawLine(myRect().left(), 0, myRect().right(), 0); p->restore(); } } void MmlNode::stretch() { MmlNode *child = firstChild(); for (; child != 0; child = child->nextSibling()) child->stretch(); } QString MmlTokenNode::text() const { QString result; const MmlNode *child = firstChild(); for (; child != 0; child = child->nextSibling()) { if (child->nodeType() != TextNode) continue; if (!result.isEmpty()) result += ' '; result += ((MmlTextNode*)child)->text(); } return result; } MmlNode *MmlMfracNode::numerator() const { MmlNode *node = firstChild(); Q_ASSERT(node != 0); return node; } MmlNode *MmlMfracNode::denominator() const { MmlNode *node = numerator()->nextSibling(); Q_ASSERT(node != 0); return node; } QRect MmlMfracNode::symbolRect() const { int num_width = numerator()->myRect().width(); int denom_width = denominator()->myRect().width(); int my_width = qMax(num_width, denom_width) + 4; return QRect(-my_width/2, 0, my_width, 1); } void MmlMfracNode::layoutSymbol() { MmlNode *num = numerator(); MmlNode *denom = denominator(); QRect num_rect = num->myRect(); QRect denom_rect = denom->myRect(); int spacing = (int)(g_mfrac_spacing*(num_rect.height() + denom_rect.height())); num->setRelOrigin(QPoint(-num_rect.width()/2, - spacing - num_rect.bottom())); denom->setRelOrigin(QPoint(-denom_rect.width()/2, spacing - denom_rect.top())); } static bool zeroLineThickness(const QString &s) { if (s.length() == 0 || !s[0].isDigit()) return false; for (int i = 0; i < s.length(); ++i) { QChar c = s.at(i); if (c.isDigit() && c != '0') return false; } return true; } void MmlMfracNode::paintSymbol(QPainter *p) const { QString linethickness_str = inheritAttributeFromMrow("linethickness", "1"); /* InterpretSpacing returns an int, which might be 0 even if the thickness is > 0, though very very small. That's ok, because the painter then paints a line of thickness 1. However, we have to run this check if the line thickness really is zero */ if (!zeroLineThickness(linethickness_str)) { bool ok; int linethickness = interpretSpacing(linethickness_str, &ok); if (!ok) linethickness = 1; p->save(); QPen pen = p->pen(); pen.setWidth(linethickness); p->setPen(pen); QSize s = myRect().size(); p->drawLine(-s.width()/2, 0, s.width()/2, 0); p->restore(); } } MmlNode *MmlRootBaseNode::base() const { MmlNode *node = firstChild(); // Q_ASSERT(node != 0); return node; } MmlNode *MmlRootBaseNode::index() const { MmlNode *b = base(); if (b == 0) return 0; return b->nextSibling(); } int MmlRootBaseNode::scriptlevel(const MmlNode *child) const { int sl = MmlNode::scriptlevel(); MmlNode *i = index(); if (child != 0 && child == i) return sl + 1; else return sl; } QRect MmlRootBaseNode::symbolRect() const { MmlNode *b = base(); QRect base_rect; if (b == 0) base_rect = QRect(0, 0, 1, 1); else base_rect = base()->myRect(); int margin = (int)(g_mroot_base_margin*base_rect.height()); int tw = tailWidth(); return QRect(-tw, base_rect.top() - margin, tw, base_rect.height() + 2*margin); } int MmlRootBaseNode::tailWidth() const { QFontMetrics fm(font()); return fm.boundingRect(g_radical_char).width(); } void MmlRootBaseNode::layoutSymbol() { MmlNode *b = base(); QSize base_size; if (b != 0) { b->setRelOrigin(QPoint(0, 0)); base_size = base()->myRect().size(); } else base_size = QSize(1, 1); MmlNode *i = index(); if (i != 0) { int tw = tailWidth(); QRect i_rect = i->myRect(); i->setRelOrigin(QPoint(-tw/2 - i_rect.width(), -i_rect.bottom() - 4)); } } void MmlRootBaseNode::paintSymbol(QPainter *p) const { QFont fn = font(); p->save(); QRect sr = symbolRect(); QRect r = sr; r.moveTopLeft(devicePoint(sr.topLeft())); p->setViewport(r); p->setWindow(QFontMetrics(fn).boundingRect(g_radical_char)); p->setFont(font()); p->drawText(0, 0, QString(g_radical_char)); p->restore(); p->drawLine(sr.right(), sr.top(), myRect().right(), sr.top()); } MmlTextNode::MmlTextNode(const QString &text, MmlDocument *document) : MmlNode(TextNode, document, MmlAttributeMap()) { m_text = text; // Trim whitespace from ends, but keep nbsp and thinsp m_text.remove(QRegExp("^[^\\S\\x00a0\\x2009]+")); m_text.remove(QRegExp("[^\\S\\x00a0\\x2009]+$")); if (m_text == QString(QChar(0x62, 0x20)) // ⁢ || m_text == QString(QChar(0x63, 0x20)) // ⁣ || m_text == QString(QChar(0x61, 0x20))) // ⁡ m_text = ""; } QString MmlTextNode::toStr() const { return MmlNode::toStr() + ", text=\"" + m_text + "\""; } void MmlTextNode::paintSymbol(QPainter *p) const { MmlNode::paintSymbol(p); QFont fn = font(); QFontInfo fi(fn); // qWarning("MmlTextNode::paintSymbol(): requested: %s, used: %s, size=%d, italic=%d, bold=%d, text=\"%s\" sl=%d", // fn.family().latin1(), fi.family().latin1(), fi.pointSize(), (int)fi.italic(), (int)fi.bold(), m_text.latin1(), scriptlevel()); QFontMetrics fm(fn); p->save(); p->setFont(fn); p->drawText(0, fm.strikeOutPos(), m_text); p->restore(); } QRect MmlTextNode::symbolRect() const { QFontMetrics fm(font()); QRect br = fm.tightBoundingRect(m_text); br.translate(0, fm.strikeOutPos()); return br; } MmlNode *MmlSubsupBaseNode::base() const { MmlNode *b = firstChild(); Q_ASSERT(b != 0); return b; } MmlNode *MmlSubsupBaseNode::sscript() const { MmlNode *s = base()->nextSibling(); Q_ASSERT(s != 0); return s; } int MmlSubsupBaseNode::scriptlevel(const MmlNode *child) const { int sl = MmlNode::scriptlevel(); MmlNode *s = sscript(); if (child != 0 && child == s) return sl + 1; else return sl; } void MmlMsupNode::layoutSymbol() { MmlNode *b = base(); MmlNode *s = sscript(); b->setRelOrigin(QPoint(-b->myRect().width(), 0)); s->setRelOrigin(QPoint(0, b->myRect().top())); } void MmlMsubNode::layoutSymbol() { MmlNode *b = base(); MmlNode *s = sscript(); b->setRelOrigin(QPoint(-b->myRect().width(), 0)); s->setRelOrigin(QPoint(0, b->myRect().bottom())); } MmlNode *MmlMsubsupNode::base() const { MmlNode *b = firstChild(); Q_ASSERT(b != 0); return b; } MmlNode *MmlMsubsupNode::subscript() const { MmlNode *sub = base()->nextSibling(); Q_ASSERT(sub != 0); return sub; } MmlNode *MmlMsubsupNode::superscript() const { MmlNode *sup = subscript()->nextSibling(); Q_ASSERT(sup != 0); return sup; } void MmlMsubsupNode::layoutSymbol() { MmlNode *b = base(); MmlNode *sub = subscript(); MmlNode *sup = superscript(); b->setRelOrigin(QPoint(-b->myRect().width(), 0)); sub->setRelOrigin(QPoint(0, b->myRect().bottom())); sup->setRelOrigin(QPoint(0, b->myRect().top())); } int MmlMsubsupNode::scriptlevel(const MmlNode *child) const { int sl = MmlNode::scriptlevel(); MmlNode *sub = subscript(); MmlNode *sup = superscript(); if (child != 0 && (child == sup || child == sub)) return sl + 1; else return sl; } QString MmlMoNode::toStr() const { return MmlNode::toStr() + QString(" form=%1").arg((int)form()); } void MmlMoNode::layoutSymbol() { MmlNode *child = firstChild(); if (child == 0) return; child->setRelOrigin(QPoint(0, 0)); if (m_oper_spec == 0) m_oper_spec = mmlFindOperSpec(text(), form()); } MmlMoNode::MmlMoNode(MmlDocument *document, const MmlAttributeMap &attribute_map) : MmlTokenNode(MoNode, document, attribute_map) { m_oper_spec = 0; } QString MmlMoNode::dictionaryAttribute(const QString &name) const { const MmlNode *p = this; for (; p != 0; p = p->parent()) { if (p == this || p->nodeType() == MstyleNode) { QString expl_attr = p->explicitAttribute(name); if (!expl_attr.isNull()) return expl_attr; } } return mmlDictAttribute(name, m_oper_spec); } Mml::FormType MmlMoNode::form() const { QString value_str = inheritAttributeFromMrow("form"); if (!value_str.isNull()) { bool ok; FormType value = interpretForm(value_str, &ok); if (ok) return value; else qWarning("Could not convert %s to form", value_str.toLatin1().data()); } // Default heuristic. if (firstSibling() == (MmlNode*)this && lastSibling() != (MmlNode*)this) return PrefixForm; else if (lastSibling() == (MmlNode*)this && firstSibling() != (MmlNode*)this) return PostfixForm; else return InfixForm; } void MmlMoNode::stretch() { if (parent() == 0) return; if (m_oper_spec == 0) return; if (m_oper_spec->stretch_dir == OperSpec::HStretch && parent()->nodeType() == MrowNode && (nextSibling() != 0 || previousSibling() != 0)) return; QRect pmr = parent()->myRect(); QRect pr = parentRect(); switch (m_oper_spec->stretch_dir) { case OperSpec::VStretch: stretchTo(QRect(pr.left(), pmr.top(), pr.width(), pmr.height())); break; case OperSpec::HStretch: stretchTo(QRect(pmr.left(), pr.top(), pmr.width(), pr.height())); break; case OperSpec::HVStretch: stretchTo(pmr); break; case OperSpec::NoStretch: break; } } int MmlMoNode::lspace() const { Q_ASSERT(m_oper_spec != 0); if (parent() == 0 || (parent()->nodeType() != MrowNode && parent()->nodeType() != MfencedNode && parent()->nodeType() != UnknownNode) || (previousSibling() == 0 && nextSibling() == 0)) return 0; else return interpretSpacing(dictionaryAttribute("lspace"), 0); } int MmlMoNode::rspace() const { Q_ASSERT(m_oper_spec != 0); if (parent() == 0 || (parent()->nodeType() != MrowNode && parent()->nodeType() != MfencedNode && parent()->nodeType() != UnknownNode) || (previousSibling() == 0 && nextSibling() == 0)) return 0; else return interpretSpacing(dictionaryAttribute("rspace"), 0); } QRect MmlMoNode::symbolRect() const { const MmlNode *child = firstChild(); if (child == 0) return QRect(0, 0, 0, 0); QRect cmr = child->myRect(); return QRect(-lspace(), cmr.top(), cmr.width() + lspace() + rspace(), cmr.height()); } int MmlMtableNode::rowspacing() const { QString value = explicitAttribute("rowspacing"); if (value.isNull()) return ex(); bool ok; int r = interpretSpacing(value, &ok); if (ok) return r; else return ex(); } int MmlMtableNode::columnspacing() const { QString value = explicitAttribute("columnspacing"); if (value.isNull()) return (int)(0.8*em()); bool ok; int r = interpretSpacing(value, &ok); if (ok) return r; else return (int)(0.8*em()); } uint MmlMtableNode::CellSizeData::colWidthSum() const { uint w = 0; for (int i = 0; i < col_widths.count(); ++i) w += col_widths[i]; return w; } uint MmlMtableNode::CellSizeData::rowHeightSum() const { uint h = 0; for (int i = 0; i < row_heights.count(); ++i) h += row_heights[i]; return h; } void MmlMtableNode::CellSizeData::init(const MmlNode *first_row) { col_widths.clear(); row_heights.clear(); const MmlNode *mtr = first_row; for (; mtr != 0; mtr = mtr->nextSibling()) { Q_ASSERT(mtr->nodeType() == MtrNode); int col_cnt = 0; const MmlNode *mtd = mtr->firstChild(); for (; mtd != 0; mtd = mtd->nextSibling(), ++col_cnt) { Q_ASSERT(mtd->nodeType() == MtdNode); QRect mtdmr = mtd->myRect(); if (col_cnt == col_widths.count()) col_widths.append(mtdmr.width()); else col_widths[col_cnt] = qMax(col_widths[col_cnt], mtdmr.width()); } row_heights.append(mtr->myRect().height()); } } void MmlMtableNode::layoutSymbol() { // Obtain natural widths of columns m_cell_size_data.init(firstChild()); int col_spc = columnspacing(); int row_spc = rowspacing(); int frame_spc_hor = framespacing_hor(); QString columnwidth_attr = explicitAttribute("columnwidth", "auto"); // Is table width set by user? If so, set col_width_sum and never ever change it. int col_width_sum = m_cell_size_data.colWidthSum(); bool width_set_by_user = false; QString width_str = explicitAttribute("width", "auto"); if (width_str != "auto") { bool ok; int w = interpretSpacing(width_str, &ok); if (ok) { col_width_sum = w - col_spc*(m_cell_size_data.numCols() - 1) - frame_spc_hor*2; width_set_by_user = true; } } // Find out what kind of columns we are dealing with and set the widths of // statically sized columns. int fixed_width_sum = 0; // sum of widths of statically sized set columns int auto_width_sum = 0; // sum of natural widths of auto sized columns int relative_width_sum = 0; // sum of natural widths of relatively sized columns double relative_fraction_sum = 0; // total fraction of width taken by relatively // sized columns int i; for (i = 0; i < m_cell_size_data.numCols(); ++i) { QString value = interpretListAttr(columnwidth_attr, i, "auto"); // Is it an auto sized column? if (value == "auto" || value == "fit") { auto_width_sum += m_cell_size_data.col_widths[i]; continue; } // Is it a statically sized column? bool ok; int w = interpretSpacing(value, &ok); if (ok) { // Yup, sets its width to the user specified value m_cell_size_data.col_widths[i] = w; fixed_width_sum += w; continue; } // Is it a relatively sized column? if (value.endsWith("%")) { value.truncate(value.length() - 1); double factor = value.toFloat(&ok); if (ok && !value.isEmpty()) { factor /= 100.0; relative_width_sum += m_cell_size_data.col_widths[i]; relative_fraction_sum += factor; if (!width_set_by_user) { // If the table width was not set by the user, we are free to increase // it so that the width of this column will be >= than its natural width int min_col_width_sum = ROUND(m_cell_size_data.col_widths[i]/factor); if (min_col_width_sum > col_width_sum) col_width_sum = min_col_width_sum; } continue; } else qWarning("MmlMtableNode::layoutSymbol(): could not parse value %s%%", value.toLatin1().data()); } // Relatively sized column, but we failed to parse the factor. Treat is like an auto // column. auto_width_sum += m_cell_size_data.col_widths[i]; } // Work out how much space remains for the auto olumns, after allocating // the statically sized and the relatively sized columns. int required_auto_width_sum = col_width_sum - ROUND(relative_fraction_sum*col_width_sum) - fixed_width_sum; if (!width_set_by_user && required_auto_width_sum < auto_width_sum) { if (relative_fraction_sum < 1) col_width_sum = ROUND((fixed_width_sum + auto_width_sum)/(1 - relative_fraction_sum)); else col_width_sum = fixed_width_sum + auto_width_sum + relative_width_sum; required_auto_width_sum = auto_width_sum; } // Ratio by which we have to shring/grow all auto sized columns to make it all fit double auto_width_scale = 1; if (auto_width_sum > 0) auto_width_scale = (float)required_auto_width_sum/auto_width_sum; // Set correct sizes for the auto sized and the relatively sized columns. for (i = 0; i < m_cell_size_data.numCols(); ++i) { QString value = interpretListAttr(columnwidth_attr, i, "auto"); // Is it a relatively sized column? if (value.endsWith("%")) { bool ok; int w = interpretPercentSpacing(value, col_width_sum, &ok); if (ok) m_cell_size_data.col_widths[i] = w; else // We're treating parsing errors here as auto sized columns m_cell_size_data.col_widths[i] = ROUND(auto_width_scale*m_cell_size_data.col_widths[i]); } // Is it an auto sized column? else if (value == "auto") { m_cell_size_data.col_widths[i] = ROUND(auto_width_scale*m_cell_size_data.col_widths[i]); } } QString s; QList &col_widths = m_cell_size_data.col_widths; for (i = 0; i < col_widths.count(); ++i) { s += QString("[w=%1 %2%%]") .arg(col_widths[i]) .arg(100*col_widths[i]/m_cell_size_data.colWidthSum()); } // qWarning(s); m_content_width = m_cell_size_data.colWidthSum() + col_spc*(m_cell_size_data.numCols() - 1); m_content_height = m_cell_size_data.rowHeightSum() + row_spc*(m_cell_size_data.numRows() - 1); int bottom = -m_content_height/2; MmlNode *child = firstChild(); for (; child != 0; child = child->nextSibling()) { Q_ASSERT(child->nodeType() == MtrNode); MmlMtrNode *row = (MmlMtrNode*) child; row->layoutCells(m_cell_size_data.col_widths, col_spc); QRect rmr = row->myRect(); row->setRelOrigin(QPoint(0, bottom - rmr.top())); bottom += rmr.height() + row_spc; } } QRect MmlMtableNode::symbolRect() const { int frame_spc_hor = framespacing_hor(); int frame_spc_ver = framespacing_ver(); return QRect(-frame_spc_hor, -m_content_height/2 - frame_spc_ver, m_content_width + 2*frame_spc_hor, m_content_height + 2*frame_spc_ver); } Mml::FrameType MmlMtableNode::frame() const { QString value = explicitAttribute("frame", "none"); return interpretFrameType(value, 0, 0); } Mml::FrameType MmlMtableNode::columnlines(int idx) const { QString value = explicitAttribute("columnlines", "none"); return interpretFrameType(value, idx, 0); } Mml::FrameType MmlMtableNode::rowlines(int idx) const { QString value = explicitAttribute("rowlines", "none"); return interpretFrameType(value, idx, 0); } void MmlMtableNode::paintSymbol(QPainter *p) const { FrameType f = frame(); if (f != FrameNone) { p->save(); QPen pen = p->pen(); if (f == FrameDashed) pen.setStyle(Qt::DashLine); else pen.setStyle(Qt::SolidLine); p->setPen(pen); p->drawRect(myRect()); p->restore(); } p->save(); int col_spc = columnspacing(); int row_spc = rowspacing(); QPen pen = p->pen(); int col_offset = 0; int i; for (i = 0; i < m_cell_size_data.numCols() - 1; ++i) { FrameType f = columnlines(i); col_offset += m_cell_size_data.col_widths[i]; if (f != FrameNone) { if (f == FrameDashed) pen.setStyle(Qt::DashLine); else if (f == FrameSolid) pen.setStyle(Qt::SolidLine); p->setPen(pen); int x = col_offset + col_spc/2; p->drawLine(x, -m_content_height/2, x, m_content_height/2 ); } col_offset += col_spc; } int row_offset = 0; for (i = 0; i < m_cell_size_data.numRows() - 1; ++i) { FrameType f = rowlines(i); row_offset += m_cell_size_data.row_heights[i]; if (f != FrameNone) { if (f == FrameDashed) pen.setStyle(Qt::DashLine); else if (f == FrameSolid) pen.setStyle(Qt::SolidLine); p->setPen(pen); int y = row_offset + row_spc/2 - m_content_height/2; p->drawLine(0, y, m_content_width, y); } row_offset += row_spc; } p->restore(); } int MmlMtableNode::framespacing_ver() const { if (frame() == FrameNone) return (int)(0.2*em()); QString value = explicitAttribute("framespacing", "0.4em 0.5ex"); bool ok; FrameSpacing fs = interpretFrameSpacing(value, em(), ex(), &ok); if (ok) return fs.m_ver; else return (int)(0.5*ex()); } int MmlMtableNode::framespacing_hor() const { if (frame() == FrameNone) return (int)(0.2*em()); QString value = explicitAttribute("framespacing", "0.4em 0.5ex"); bool ok; FrameSpacing fs = interpretFrameSpacing(value, em(), ex(), &ok); if (ok) return fs.m_hor; else return (int)(0.4*em()); } void MmlMtrNode::layoutCells(const QList &col_widths, int col_spc) { QRect mr = myRect(); MmlNode *child = firstChild(); int col_offset = 0; uint colnum = 0; for (; child != 0; child = child->nextSibling(), ++colnum) { Q_ASSERT(child->nodeType() == MtdNode); MmlMtdNode *mtd = (MmlMtdNode*) child; QRect r = QRect(0, mr.top(), col_widths[colnum], mr.height()); mtd->setMyRect(r); mtd->setRelOrigin(QPoint(col_offset, 0)); col_offset += col_widths[colnum] + col_spc; } updateMyRect(); } int MmlMtdNode::scriptlevel(const MmlNode *child) const { int sl = MmlNode::scriptlevel(); if (child != 0 && child == firstChild()) return sl + m_scriptlevel_adjust; else return sl; } void MmlMtdNode::setMyRect(const QRect &rect) { MmlNode::setMyRect(rect); MmlNode *child = firstChild(); if (child == 0) return; if (rect.width() < child->myRect().width()) { while (rect.width() < child->myRect().width() && child->font().pointSize() > g_min_font_point_size) { // qWarning("MmlMtdNode::setMyRect(): rect.width()=%d, child()->myRect().width=%d sl=%d", // rect.width(), child->myRect().width(), m_scriptlevel_adjust); ++m_scriptlevel_adjust; child->layout(); } // qWarning("MmlMtdNode::setMyRect(): rect.width()=%d, child()->myRect().width=%d sl=%d", // rect.width(), child->myRect().width(), m_scriptlevel_adjust); } QRect mr = myRect(); QRect cmr = child->myRect(); QPoint child_rel_origin; switch (columnalign()) { case ColAlignLeft: child_rel_origin.setX(0); break; case ColAlignCenter: child_rel_origin.setX(mr.left() + (mr.width() - cmr.width())/2); break; case ColAlignRight: child_rel_origin.setX(mr.right() - cmr.width()); break; } switch (rowalign()) { case RowAlignTop: child_rel_origin.setY(mr.top() - cmr.top()); break; case RowAlignCenter: case RowAlignBaseline: child_rel_origin.setY(mr.top() -cmr.top() + (mr.height() - cmr.height())/2); break; case RowAlignBottom: child_rel_origin.setY(mr.bottom() - cmr.bottom()); break; case RowAlignAxis: child_rel_origin.setY(0); break; } child->setRelOrigin(child_rel_origin); } uint MmlMtdNode::colNum() { MmlNode *syb = previousSibling(); uint i = 0; for (; syb != 0; syb = syb->previousSibling()) ++i; return i; } uint MmlMtdNode::rowNum() { MmlNode *row = parent()->previousSibling(); uint i = 0; for (; row != 0; row = row->previousSibling()) ++i; return i; } MmlMtdNode::ColAlign MmlMtdNode::columnalign() { QString val = explicitAttribute("columnalign"); if (!val.isNull()) return interpretColAlign(val, 0, 0); MmlNode *node = parent(); // if (node == 0) return ColAlignCenter; uint colnum = colNum(); val = node->explicitAttribute("columnalign"); if (!val.isNull()) return interpretColAlign(val, colnum, 0); node = node->parent(); // if (node == 0) return ColAlignCenter; val = node->explicitAttribute("columnalign"); if (!val.isNull()) return interpretColAlign(val, colnum, 0); return ColAlignCenter; } MmlMtdNode::RowAlign MmlMtdNode::rowalign() { QString val = explicitAttribute("rowalign"); if (!val.isNull()) return interpretRowAlign(val, 0, 0); MmlNode *node = parent(); // if (node == 0) return RowAlignAxis; uint rownum = rowNum(); val = node->explicitAttribute("rowalign"); if (!val.isNull()) return interpretRowAlign(val, rownum, 0); node = node->parent(); // if (node == 0) return RowAlignAxis; val = node->explicitAttribute("rowalign"); if (!val.isNull()) return interpretRowAlign(val, rownum, 0); return RowAlignAxis; } void MmlMoverNode::layoutSymbol() { MmlNode *base = firstChild(); Q_ASSERT(base != 0); MmlNode *over = base->nextSibling(); Q_ASSERT(over != 0); QRect base_rect = base->myRect(); QRect over_rect = over->myRect(); int spacing = (int)(g_mfrac_spacing*(over_rect.height() + base_rect.height())); base->setRelOrigin(QPoint(-base_rect.width()/2, 0)); over->setRelOrigin(QPoint(-over_rect.width()/2, base_rect.top() - spacing - over_rect.bottom())); } int MmlMoverNode::scriptlevel(const MmlNode *node) const { MmlNode *base = firstChild(); Q_ASSERT(base != 0); MmlNode *over = base->nextSibling(); Q_ASSERT(over != 0); int sl = MmlNode::scriptlevel(); if (node != 0 && node == over) return sl + 1; else return sl; } void MmlMunderNode::layoutSymbol() { MmlNode *base = firstChild(); Q_ASSERT(base != 0); MmlNode *under = base->nextSibling(); Q_ASSERT(under != 0); QRect base_rect = base->myRect(); QRect under_rect = under->myRect(); int spacing = (int)(g_mfrac_spacing*(under_rect.height() + base_rect.height())); base->setRelOrigin(QPoint(-base_rect.width()/2, 0)); under->setRelOrigin(QPoint(-under_rect.width()/2, base_rect.bottom() + spacing - under_rect.top())); } int MmlMunderNode::scriptlevel(const MmlNode *node) const { MmlNode *base = firstChild(); Q_ASSERT(base != 0); MmlNode *under = base->nextSibling(); Q_ASSERT(under != 0); int sl = MmlNode::scriptlevel(); if (node != 0 && node == under) return sl + 1; else return sl; } void MmlMunderoverNode::layoutSymbol() { MmlNode *base = firstChild(); Q_ASSERT(base != 0); MmlNode *under = base->nextSibling(); Q_ASSERT(under != 0); MmlNode *over = under->nextSibling(); Q_ASSERT(over != 0); QRect base_rect = base->myRect(); QRect under_rect = under->myRect(); QRect over_rect = over->myRect(); int spacing = (int)(g_mfrac_spacing*( base_rect.height() + under_rect.height() + over_rect.height()) ); base->setRelOrigin(QPoint(-base_rect.width()/2, 0)); under->setRelOrigin(QPoint(-under_rect.width()/2, base_rect.bottom() + spacing - under_rect.top())); over->setRelOrigin(QPoint(-over_rect.width()/2, base_rect.top() - spacing - under_rect.bottom())); } int MmlMunderoverNode::scriptlevel(const MmlNode *node) const { MmlNode *base = firstChild(); Q_ASSERT(base != 0); MmlNode *under = base->nextSibling(); Q_ASSERT(under != 0); MmlNode *over = under->nextSibling(); Q_ASSERT(over != 0); int sl = MmlNode::scriptlevel(); if (node != 0 && (node == under || node == over)) return sl + 1; else return sl; } int MmlMpaddedNode::interpretSpacing(QString value, int base_value, bool *ok) const { if (ok != 0) *ok = false; value.replace(' ', ""); QString sign, factor_str, pseudo_unit; bool percent = false; // extract the sign int idx = 0; if (idx < value.length() && (value.at(idx) == '+' || value.at(idx) == '-')) sign = value.at(idx++); // extract the factor while (idx < value.length() && (value.at(idx).isDigit() || value.at(idx) == '.')) factor_str.append(value.at(idx++)); // extract the % sign if (idx < value.length() && value.at(idx) == '%') { percent = true; ++idx; } // extract the pseudo-unit pseudo_unit = value.mid(idx); bool float_ok; double factor = factor_str.toFloat(&float_ok); if (!float_ok || factor < 0) { qWarning("MmlMpaddedNode::interpretSpacing(): could not parse \"%s\"", value.toLatin1().data()); return 0; } if (percent) factor /= 100.0; QRect cr; if (firstChild() == 0) cr = QRect(0, 0, 0, 0); else cr = firstChild()->myRect(); int unit_size; if (pseudo_unit.isEmpty()) unit_size = base_value; else if (pseudo_unit == "width") unit_size = cr.width(); else if (pseudo_unit == "height") unit_size = -cr.top(); else if (pseudo_unit == "depth") unit_size = cr.bottom(); else { bool unit_ok; unit_size = MmlNode::interpretSpacing("1" + pseudo_unit, &unit_ok); if (!unit_ok) { qWarning("MmlMpaddedNode::interpretSpacing(): could not parse \"%s\"", value.toLatin1().data()); return 0; } } if (ok != 0) *ok = true; if (sign.isNull()) return (int)(factor*unit_size); else if (sign == "+") return base_value + (int)(factor*unit_size); else // sign == "-" return base_value - (int)(factor*unit_size); } int MmlMpaddedNode::lspace() const { QString value = explicitAttribute("lspace"); if (value.isNull()) return 0; bool ok; int lspace = interpretSpacing(value, 0, &ok); if (ok) return lspace; return 0; } int MmlMpaddedNode::width() const { int child_width = 0; if (firstChild() != 0) child_width = firstChild()->myRect().width(); QString value = explicitAttribute("width"); if (value.isNull()) return child_width; bool ok; int w = interpretSpacing(value, child_width, &ok); if (ok) return w; return child_width; } int MmlMpaddedNode::height() const { QRect cr; if (firstChild() == 0) cr = QRect(0, 0, 0, 0); else cr = firstChild()->myRect(); QString value = explicitAttribute("height"); if (value.isNull()) return -cr.top(); bool ok; int h = interpretSpacing(value, -cr.top(), &ok); if (ok) return h; return -cr.top(); } int MmlMpaddedNode::depth() const { QRect cr; if (firstChild() == 0) cr = QRect(0, 0, 0, 0); else cr = firstChild()->myRect(); QString value = explicitAttribute("depth"); if (value.isNull()) return cr.bottom(); bool ok; int h = interpretSpacing(value, cr.bottom(), &ok); if (ok) return h; return cr.bottom(); } void MmlMpaddedNode::layoutSymbol() { MmlNode *child = firstChild(); if (child == 0) return; child->setRelOrigin(QPoint(0, 0)); } QRect MmlMpaddedNode::symbolRect() const { return QRect(-lspace(), -height(), lspace() + width(), height() + depth()); } // ******************************************************************* // QtMmlWidget // ******************************************************************* /*! \class QtMmlWidget \brief The QtMmlWidget class renders mathematical formulas written in MathML 2.0. QtMmlWidget implements the Presentation Markup subset of the MathML 2.0 specification, with a few \link overview.html exceptions\endlink. \code QtMmlWidget *mmlWidget = new QtMmlWidget(this); QString errorMsg; int errorLine; int errorColumn; bool ok = mmlWidget->setContent(mmlText, &errorMsg, &errorLine, &errorColumn); if (!ok) { qWarning("MathML error: %s, Line: %d, Column: %d", errorMsg.latin1(), errorLine, errorColumn); } \endcode */ /*! \enum QtMmlWidget::MmlFont This ennumerated type is used in fontName() and setFontName() to specify a font type. \value NormalFont The default font type, used to render expressions for which no mathvariant or fontfamily is specified, or for which the "normal" mathvariant is specified. \value FrakturFont The font type used to render expressions for which the "fraktur" mathvariant is specified. \value SansSerifFont The font type used to render expressions for which the "sans-serif" mathvariant is specified. \value ScriptFont The font type used to render expressions for which the "script" mathvariant is specified. \value MonospaceFont The font type used to render expressions for which the "monospace" mathvariant is specified. \value DoublestruckFont The font type used to render expressions for which the "doublestruck" mathvariant is specified. \sa setFontName() fontName() setBaseFontPointSize() baseFontPointSize() */ /*! Constructs a QtMmlWidget object. The \a parent parameter is passed to QFrame's constructor. */ QtMmlWidget::QtMmlWidget(QWidget *parent) : QFrame(parent) { m_doc = new MmlDocument; } /*! Destructs a QtMmlWidget object. */ QtMmlWidget::~QtMmlWidget() { delete m_doc; } /*! Returns the name of the font used to render the font \a type. \sa setFontName() setBaseFontPointSize() baseFontPointSize() QtMmlWidget::MmlFont */ QString QtMmlWidget::fontName(MmlFont type) const { return m_doc->fontName(type); } /*! Sets the name of the font used to render the font \a type to \a name. \sa fontName() setBaseFontPointSize() baseFontPointSize() QtMmlWidget::MmlFont */ void QtMmlWidget::setFontName(MmlFont type, const QString &name) { m_doc->setFontName(type, name); m_doc->layout(); update(); } /*! If \a b is true, draws a red bounding rectangle around each expression; if \a b is false, no such rectangle is drawn. This is mostly useful for debugging MathML expressions. \sa drawFrames() */ void QtMmlWidget::setDrawFrames(bool b) { g_draw_frames = b; update(); } /*! Returns true if each expression should be drawn with a red bounding rectangle; otherwise returns false. This is mostly useful for debugging MathML expressions. \sa setDrawFrames() */ bool QtMmlWidget::drawFrames() const { return g_draw_frames; } /*! Clears the contents of this widget. */ void QtMmlWidget::clear() { m_doc->clear(); } /*! Returns the point size of the font used to render expressions whose scriptlevel is 0. \sa setBaseFontPointSize() fontName() setFontName() */ int QtMmlWidget::baseFontPointSize() const { return m_doc->baseFontPointSize(); } /*! Sets the point \a size of the font used to render expressions whose scriptlevel is 0. \sa baseFontPointSize() fontName() setFontName() */ void QtMmlWidget::setBaseFontPointSize(int size) { if (size < g_min_font_point_size) return; m_doc->setBaseFontPointSize(size); m_doc->layout(); update(); } /*! Returns the size of the formula in pixels. */ QSize QtMmlWidget::sizeHint() const { QSize size = m_doc->size(); if (size == QSize(0, 0)) return QSize(100, 50); return m_doc->size(); } /*! Sets the MathML expression to be rendered. The expression is given in the string \a text. If the expression is successfully parsed, this method returns true; otherwise it returns false. If an error occured \a errorMsg is set to a diagnostic message, while \a errorLine and \a errorColumn contain the location of the error. Any of \a errorMsg, \a errorLine and \a errorColumn may be 0, in which case they are not set. \a text should contain MathML 2.0 presentation markup elements enclosed in a element. */ bool QtMmlWidget::setContent(const QString &text, QString *errorMsg, int *errorLine, int *errorColumn) { bool result = m_doc->setContent(text, errorMsg, errorLine, errorColumn); if (result) update(); return result; } /*! \internal */ void QtMmlWidget::paintEvent(QPaintEvent *e) { QFrame::paintEvent(e); QPainter p(this); if (e->rect().intersects(contentsRect())) p.setClipRegion(e->region().intersected(contentsRect())); QSize s = m_doc->size(); int x = (width() - s.width())/2; int y = (height() - s.height())/2; m_doc->paint(&p, QPoint(x, y)); } /*! \internal */ void QtMmlWidget::dump() const { m_doc->dump(); } // ******************************************************************* // Static helper functions // ******************************************************************* static QString entityDeclarations() { QString result = "name != 0; ++ent) { result += "\tname) + " \"" + ent->value + "\">\n"; } result += "]>\n"; return result; } static int interpretSpacing(QString value, int em, int ex, bool *ok) { if (ok != 0) *ok = true; if (value == "thin") return 1; if (value == "medium") return 2; if (value == "thick") return 3; struct HSpacingValue { const char *name; float factor; }; static const HSpacingValue g_h_spacing_data[] = { { "veryverythinmathspace", (float) 0.0555556 }, { "verythinmathspace", (float) 0.111111 }, { "thinmathspace", (float) 0.166667 }, { "mediummathspace", (float) 0.222222 }, { "thickmathspace", (float) 0.277778 }, { "verythickmathspace", (float) 0.333333 }, { "veryverythickmathspace", (float) 0.388889 }, { 0, (float) 0 } }; const HSpacingValue *v = g_h_spacing_data; for (; v->name != 0; ++v) { if (value == v->name) return (int)(em*v->factor); } if (value.endsWith("em")) { value.truncate(value.length() - 2); bool float_ok; float factor = value.toFloat(&float_ok); if (float_ok && factor >= 0) return (int)(em*factor); else { qWarning("interpretSpacing(): could not parse \"%sem\"", value.toLatin1().data()); if (ok != 0) *ok = false; return 0; } } if (value.endsWith("ex")) { value.truncate(value.length() - 2); bool float_ok; float factor = value.toFloat(&float_ok); if (float_ok && factor >= 0) return (int)(ex*factor); else { qWarning("interpretSpacing(): could not parse \"%sex\"", value.toLatin1().data()); if (ok != 0) *ok = false; return 0; } } if (value.endsWith("cm")) { value.truncate(value.length() - 2); bool float_ok; float factor = value.toFloat(&float_ok); if (float_ok && factor >= 0) { Q_ASSERT(qApp->desktop() != 0); QDesktopWidget *dw = qApp->desktop(); Q_ASSERT(dw->width() != 0); Q_ASSERT(dw->widthMM() != 0); return (int)(factor*10*dw->width()/dw->widthMM()); } else { qWarning("interpretSpacing(): could not parse \"%scm\"", value.toLatin1().data()); if (ok != 0) *ok = false; return 0; } } if (value.endsWith("mm")) { value.truncate(value.length() - 2); bool float_ok; float factor = value.toFloat(&float_ok); if (float_ok && factor >= 0) { Q_ASSERT(qApp->desktop() != 0); QDesktopWidget *dw = qApp->desktop(); Q_ASSERT(dw->width() != 0); Q_ASSERT(dw->widthMM() != 0); return (int)(factor*dw->width()/dw->widthMM()); } else { qWarning("interpretSpacing(): could not parse \"%smm\"", value.toLatin1().data()); if (ok != 0) *ok = false; return 0; } } if (value.endsWith("in")) { value.truncate(value.length() - 2); bool float_ok; float factor = value.toFloat(&float_ok); if (float_ok && factor >= 0) { Q_ASSERT(qApp->desktop() != 0); QDesktopWidget *dw = qApp->desktop(); Q_ASSERT(dw->width() != 0); Q_ASSERT(dw->widthMM() != 0); return (int)(factor*10*dw->width()/(2.54*dw->widthMM())); } else { qWarning("interpretSpacing(): could not parse \"%sin\"", value.toLatin1().data()); if (ok != 0) *ok = false; return 0; } } if (value.endsWith("px")) { value.truncate(value.length() - 2); bool float_ok; int i = (int) value.toFloat(&float_ok); if (float_ok && i >= 0) return i; else { qWarning("interpretSpacing(): could not parse \"%spx\"", value.toLatin1().data()); if (ok != 0) *ok = false; return 0; } } bool float_ok; int i = (int)value.toFloat(&float_ok); if (float_ok && i >= 0) return i; qWarning("interpretSpacing(): could not parse \"%s\"", value.toLatin1().data()); if (ok != 0) *ok = false; return 0; } static int interpretPercentSpacing(QString value, int base, bool *ok) { if (!value.endsWith("%")) { if (ok != 0) *ok = false; return 0; } value.truncate(value.length() - 1); bool float_ok; float factor = value.toFloat(&float_ok); if (float_ok && factor >= 0) { if (ok != 0) *ok = true; return (int)(base*factor/100.0); } qWarning("interpretPercentSpacing(): could not parse \"%s%%\"", value.toLatin1().data()); if (ok != 0) *ok = false; return 0; } static int interpretPointSize(QString value, bool *ok) { if (!value.endsWith("pt")) { if (ok != 0) *ok = false; return 0; } value.truncate(value.length() - 2); bool float_ok; int pt_size = (int) value.toFloat(&float_ok); if (float_ok && pt_size > 0) { if (ok != 0) *ok = true; return pt_size; } qWarning("interpretPointSize(): could not parse \"%spt\"", value.toLatin1().data()); if (ok != 0) *ok = false; return 0; } static const NodeSpec *mmlFindNodeSpec(Mml::NodeType type) { const NodeSpec *spec = g_node_spec_data; for (; spec->type != Mml::NoNode; ++spec) { if (type == spec->type) return spec; } return 0; } static const NodeSpec *mmlFindNodeSpec(const QString &tag) { const NodeSpec *spec = g_node_spec_data; for (; spec->type != Mml::NoNode; ++spec) { if (tag == spec->tag) return spec; } return 0; } static bool mmlCheckChildType(Mml::NodeType parent_type, Mml::NodeType child_type, QString *error_str) { if (parent_type == Mml::UnknownNode || child_type == Mml::UnknownNode) return true; const NodeSpec *child_spec = mmlFindNodeSpec(child_type); const NodeSpec *parent_spec = mmlFindNodeSpec(parent_type); Q_ASSERT(parent_spec != 0); Q_ASSERT(child_spec != 0); QString allowed_child_types(parent_spec->child_types); // null list means any child type is valid if (allowed_child_types.isNull()) return true; QString child_type_str = QString(" ") + child_spec->type_str + " "; if (!allowed_child_types.contains(child_type_str)) { if (error_str != 0) *error_str = QString("illegal child ") + child_spec->type_str + " for parent " + parent_spec->type_str; return false; } return true; } static bool mmlCheckAttributes(Mml::NodeType child_type, const MmlAttributeMap &attr, QString *error_str) { const NodeSpec *spec = mmlFindNodeSpec(child_type); Q_ASSERT(spec != 0); QString allowed_attr(spec->attributes); // empty list means any attr is valid if (allowed_attr.isEmpty()) return true; MmlAttributeMap::const_iterator it = attr.begin(), end = attr.end(); for (; it != end; ++it) { QString name = it.key(); if (name.indexOf(':') != -1) continue; QString padded_name = " " + name + " "; if (!allowed_attr.contains(padded_name)) { if (error_str != 0) *error_str = QString("illegal attribute ") + name + " in " + spec->type_str; return false; } } return true; } static int attributeIndex(const QString &name) { for (unsigned i = 0; i < g_oper_spec_rows; ++i) { if (name == g_oper_spec_names[i]) return i; } return -1; } static QString decodeEntityValue(QString literal) { QString result; while (!literal.isEmpty()) { if (!literal.startsWith("&#")) { qWarning("decodeEntityValue(): bad entity literal: \"%s\"", literal.toLatin1().data()); return QString::null; } literal = literal.right(literal.length() - 2); int i = literal.indexOf(';'); if (i == -1) { qWarning("decodeEntityValue(): bad entity literal: \"%s\"", literal.toLatin1().data()); return QString::null; } QString char_code = literal.left(i); literal = literal.right(literal.length() - i - 1); if (char_code.isEmpty()) { qWarning("decodeEntityValue(): bad entity literal: \"%s\"", literal.toLatin1().data()); return QString::null; } if (char_code.at(0) == 'x') { char_code = char_code.right(char_code.length() - 1); bool ok; unsigned c = char_code.toUInt(&ok, 16); if (!ok) { qWarning("decodeEntityValue(): bad entity literal: \"%s\"", literal.toLatin1().data()); return QString::null; } result += QChar(c); } else { bool ok; unsigned c = char_code.toUInt(&ok, 10); if (!ok) { qWarning("decodeEntityValue(): bad entity literal: \"%s\"", literal.toLatin1().data()); return QString::null; } result += QChar(c); } } return result; } static const EntitySpec *searchEntitySpecData(const QString &value, const EntitySpec *from = 0) { const EntitySpec *ent = from; if (ent == 0) ent = g_xml_entity_data; for (; ent->name != 0; ++ent) { QString ent_value = decodeEntityValue(ent->value); if (value == ent_value) return ent; } return 0; } struct OperSpecSearchResult { OperSpecSearchResult() { prefix_form = infix_form = postfix_form = 0; } const OperSpec *prefix_form, *infix_form, *postfix_form; const OperSpec *&getForm(Mml::FormType f); bool haveForm(Mml::FormType f) { return getForm(f) != 0; } void addForm(const OperSpec *spec) { getForm(spec->form) = spec; } }; const OperSpec *&OperSpecSearchResult::getForm(Mml::FormType f) { switch (f) { case Mml::PrefixForm: return prefix_form; case Mml::InfixForm: return infix_form; case Mml::PostfixForm: return postfix_form; } return postfix_form; // just to avoid warning } /* Searches g_oper_spec_data and returns any instance of operator name. There may be more instances, but since the list is sorted, they will be next to each other. */ static const OperSpec *searchOperSpecData(const QString &name) { const char *name_latin1 = name.toLatin1().data(); // binary search // establish invariant g_oper_spec_data[begin].name < name < g_oper_spec_data[end].name int cmp = qstrcmp(name_latin1, g_oper_spec_data[0].name); if (cmp < 0) return 0; if (cmp == 0) return g_oper_spec_data; uint begin = 0; uint end = g_oper_spec_count; // invariant holds while (end - begin > 1) { uint mid = (begin + end)/2; const OperSpec *spec = g_oper_spec_data + mid; int cmp = qstrcmp(name_latin1, spec->name); if (cmp < 0) end = mid; else if (cmp > 0) begin = mid; else return spec; } return 0; } /* This searches g_oper_spec_data until at least one name in name_list is found with FormType form, or until name_list is exhausted. The idea here is that if we don't find the operator in the specified form, we still want to use some other available form of that operator. */ static OperSpecSearchResult _mmlFindOperSpec(const QStringList &name_list, Mml::FormType form) { OperSpecSearchResult result; QStringList::const_iterator it = name_list.begin(); for (; it != name_list.end(); ++it) { const QString &name = *it; const OperSpec *spec = searchOperSpecData(name); if (spec == 0) continue; const char *name_latin1 = name.toLatin1().data(); // backtrack to the first instance of name while (spec > g_oper_spec_data && qstrcmp((spec - 1)->name, name_latin1) == 0) --spec; // iterate over instances of name until the instances are exhausted or until we // find an instance in the specified form. do { result.addForm(spec++); if (result.haveForm(form)) break; } while (qstrcmp(spec->name, name_latin1) == 0); if (result.haveForm(form)) break; } return result; } /* text is a string between and . It can be a character ('+'), an entity reference ("∞") or a character reference ("∞"). Our job is to find an operator spec in the operator dictionary (g_oper_spec_data) that matches text. Things are further complicated by the fact, that many operators come in several forms (prefix, infix, postfix). If available, this function returns an operator spec matching text in the specified form. If such operator is not available, returns an operator spec that matches text, but of some other form in the preference order specified by the MathML spec. If that's not available either, returns the default operator spec. */ static const OperSpec *mmlFindOperSpec(const QString &text, Mml::FormType form) { QStringList name_list; name_list.append(text); // First, just try to find text in the operator dictionary. OperSpecSearchResult result = _mmlFindOperSpec(name_list, form); if (!result.haveForm(form)) { // Try to find other names for the operator represented by text. const EntitySpec *ent = 0; for (;;) { ent = searchEntitySpecData(text, ent); if (ent == 0) break; name_list.append('&' + QString(ent->name) + ';'); ++ent; } result = _mmlFindOperSpec(name_list, form); } const OperSpec *spec = result.getForm(form); if (spec != 0) return spec; spec = result.getForm(Mml::InfixForm); if (spec != 0) return spec; spec = result.getForm(Mml::PostfixForm); if (spec != 0) return spec; spec = result.getForm(Mml::PrefixForm); if (spec != 0) return spec; return &g_oper_spec_defaults; } static QString mmlDictAttribute(const QString &name, const OperSpec *spec) { int i = attributeIndex(name); if (i == -1) return QString::null; else return spec->attributes[i]; } static uint interpretMathVariant(const QString &value, bool *ok) { struct MathVariantValue { const char *value; uint mv; }; static const MathVariantValue g_mv_data[] = { { "normal", Mml::NormalMV }, { "bold", Mml::BoldMV }, { "italic", Mml::ItalicMV }, { "bold-italic", Mml::BoldMV | Mml::ItalicMV }, { "double-struck", Mml::DoubleStruckMV }, { "bold-fraktur", Mml::BoldMV | Mml::FrakturMV }, { "script", Mml::ScriptMV }, { "bold-script", Mml::BoldMV | Mml::ScriptMV }, { "fraktur", Mml::FrakturMV }, { "sans-serif", Mml::SansSerifMV }, { "bold-sans-serif", Mml::BoldMV | Mml::SansSerifMV }, { "sans-serif-italic", Mml::SansSerifMV | Mml::ItalicMV }, { "sans-serif-bold-italic", Mml::SansSerifMV | Mml::ItalicMV | Mml::BoldMV }, { "monospace", Mml::MonospaceMV }, { 0, 0 } }; const MathVariantValue *v = g_mv_data; for (; v->value != 0; ++v) { if (value == v->value) { if (ok != 0) *ok = true; return v->mv; } } if (ok != 0) *ok = false; qWarning("interpretMathVariant(): could not parse value: \"%s\"", value.toLatin1().data()); return Mml::NormalMV; } static Mml::FormType interpretForm(const QString &value, bool *ok) { if (ok != 0) *ok = true; if (value == "prefix") return Mml::PrefixForm; if (value == "infix") return Mml::InfixForm; if (value == "postfix") return Mml::PostfixForm; if (ok != 0) *ok = false; qWarning("interpretForm(): could not parse value \"%s\"", value.toLatin1().data()); return Mml::InfixForm; } static Mml::ColAlign interpretColAlign(const QString &value_list, uint colnum, bool *ok) { QString value = interpretListAttr(value_list, colnum, "center"); if (ok != 0) *ok = true; if (value == "left") return Mml::ColAlignLeft; if (value == "right") return Mml::ColAlignRight; if (value == "center") return Mml::ColAlignCenter; if (ok != 0) *ok = false; qWarning("interpretColAlign(): could not parse value \"%s\"", value.toLatin1().data()); return Mml::ColAlignCenter; } static Mml::RowAlign interpretRowAlign(const QString &value_list, uint rownum, bool *ok) { QString value = interpretListAttr(value_list, rownum, "axis"); if (ok != 0) *ok = true; if (value == "top") return Mml::RowAlignTop; if (value == "center") return Mml::RowAlignCenter; if (value == "bottom") return Mml::RowAlignBottom; if (value == "baseline") return Mml::RowAlignBaseline; if (value == "axis") return Mml::RowAlignAxis; if (ok != 0) *ok = false; qWarning("interpretRowAlign(): could not parse value \"%s\"", value.toLatin1().data()); return Mml::RowAlignAxis; } static QString interpretListAttr(const QString &value_list, int idx, const QString &def) { QStringList l = value_list.split(' '); if (l.count() == 0) return def; if (l.count() <= idx) return l[l.count() - 1]; else return l[idx]; } static Mml::FrameType interpretFrameType(const QString &value_list, uint idx, bool *ok) { if (ok != 0) *ok = true; QString value = interpretListAttr(value_list, idx, "none"); if (value == "none") return Mml::FrameNone; if (value == "solid") return Mml::FrameSolid; if (value == "dashed") return Mml::FrameDashed; if (ok != 0) *ok = false; qWarning("interpretFrameType(): could not parse value \"%s\"", value.toLatin1().data()); return Mml::FrameNone; } static Mml::FrameSpacing interpretFrameSpacing(const QString &value_list, int em, int ex, bool *ok) { Mml::FrameSpacing fs; QStringList l = value_list.split(' '); if (l.count() != 2) { qWarning("interpretFrameSpacing: could not parse value \"%s\"", value_list.toLatin1().data()); if (ok != 0) *ok = false; return Mml::FrameSpacing((int)(0.4*em), (int)(0.5*ex)); } bool hor_ok, ver_ok; fs.m_hor = interpretSpacing(l[0], em, ex, &hor_ok); fs.m_ver = interpretSpacing(l[1], em, ex, &ver_ok); if (ok != 0) *ok = hor_ok && ver_ok; return fs; } static QFont interpretDepreciatedFontAttr(const MmlAttributeMap &font_attr, QFont &fn, int em, int ex) { if (font_attr.contains("fontsize")) { QString value = font_attr["fontsize"]; for (;;) { bool ok; int ptsize = interpretPointSize(value, &ok); if (ok) { fn.setPointSize(ptsize); break; } ptsize = interpretPercentSpacing(value, fn.pointSize(), &ok); if (ok) { fn.setPointSize(ptsize); break; } int size = interpretSpacing(value, em, ex, &ok); if (ok) { fn.setPixelSize(size); break; } break; } } if (font_attr.contains("fontweight")) { QString value = font_attr["fontweight"]; if (value == "normal") fn.setBold(false); else if (value == "bold") fn.setBold(true); else qWarning("interpretDepreciatedFontAttr(): could not parse fontweight \"%s\"", value.toLatin1().data()); } if (font_attr.contains("fontstyle")) { QString value = font_attr["fontstyle"]; if (value == "normal") fn.setItalic(false); else if (value == "italic") fn.setItalic(true); else qWarning("interpretDepreciatedFontAttr(): could not parse fontstyle \"%s\"", value.toLatin1().data()); } if (font_attr.contains("fontfamily")) { QString value = font_attr["fontfamily"]; fn.setFamily(value); } return fn; } static QFont interpretMathSize(QString value, QFont &fn, int em, int ex, bool *ok) { if (ok != 0) *ok = true; if (value == "small") { fn.setPointSize((int)(fn.pointSize()*0.7)); return fn; } if (value == "normal") return fn; if (value == "big") { fn.setPointSize((int)(fn.pointSize()*1.5)); return fn; } bool size_ok; int ptsize = interpretPointSize(value, &size_ok); if (size_ok) { fn.setPointSize(ptsize); return fn; } int size = interpretSpacing(value, em, ex, &size_ok); if (size_ok) { fn.setPixelSize(size); return fn; } if (ok != 0) *ok = false; qWarning("interpretMathSize(): could not parse mathsize \"%s\"", value.toLatin1().data()); return fn; } /*! \class QtMmlDocument \brief The QtMmlDocument class renders mathematical formulas written in MathML 2.0. This class provides a direct API to the rendering engine used by QtMmlWidget. It can be used to paint MathML inside other widgets. All methods work the same as the corresponding methods in QtMmlWidget. */ /*! Constructs an empty MML document. */ QtMmlDocument::QtMmlDocument() { m_doc = new MmlDocument; } /*! Destroys the MML document. */ QtMmlDocument::~QtMmlDocument() { delete m_doc; } /*! Clears the contents of this MML document. */ void QtMmlDocument::clear() { m_doc->clear(); } /*! Sets the MathML expression to be rendered. The expression is given in the string \a text. If the expression is successfully parsed, this method returns true; otherwise it returns false. If an error occured \a errorMsg is set to a diagnostic message, while \a errorLine and \a errorColumn contain the location of the error. Any of \a errorMsg, \a errorLine and \a errorColumn may be 0, in which case they are not set. \a text should contain MathML 2.0 presentation markup elements enclosed in a element. */ bool QtMmlDocument::setContent(QString text, QString *errorMsg, int *errorLine, int *errorColumn) { return m_doc->setContent(text, errorMsg, errorLine, errorColumn); } /*! Renders this MML document with the painter \a p at position \a pos. */ void QtMmlDocument::paint(QPainter *p, const QPoint &pos) const { m_doc->paint(p, pos); } /*! Returns the size of this MML document, as rendered, in pixels. */ QSize QtMmlDocument::size() const { return m_doc->size(); } /*! Returns the name of the font used to render the font \a type. \sa setFontName() setBaseFontPointSize() baseFontPointSize() QtMmlWidget::MmlFont */ QString QtMmlDocument::fontName(QtMmlWidget::MmlFont type) const { return m_doc->fontName(type); } /*! Sets the name of the font used to render the font \a type to \a name. \sa fontName() setBaseFontPointSize() baseFontPointSize() QtMmlWidget::MmlFont */ void QtMmlDocument::setFontName(QtMmlWidget::MmlFont type, const QString &name) { m_doc->setFontName(type, name); m_doc->layout(); } /*! Returns the point size of the font used to render expressions whose scriptlevel is 0. \sa setBaseFontPointSize() fontName() setFontName() */ int QtMmlDocument::baseFontPointSize() const { return m_doc->baseFontPointSize(); } /*! Sets the point \a size of the font used to render expressions whose scriptlevel is 0. \sa baseFontPointSize() fontName() setFontName() */ void QtMmlDocument::setBaseFontPointSize(int size) { m_doc->setBaseFontPointSize(size); m_doc->layout(); } ����������������������������������������������������������������������������������������������������������������veusz-3.0.1/veusz/helpers/src/qtmml/QtMmlWidget�����������������������������������������������������0000664�0001750�0001750�00000000031�13161413406�021162� 0����������������������������������������������������������������������������������������������������ustar �jss�����������������������������jss�����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#include "qtmmlwidget.h" �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������veusz-3.0.1/veusz/helpers/src/nc_cntr/��������������������������������������������������������������0000775�0001750�0001750�00000000000�13325026670�017350� 5����������������������������������������������������������������������������������������������������ustar �jss�����������������������������jss�����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������veusz-3.0.1/veusz/helpers/src/nc_cntr/_nc_cntr.c����������������������������������������������������0000664�0001750�0001750�00000166764�13161413406�021320� 0����������������������������������������������������������������������������������������������������ustar �jss�����������������������������jss�����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* cntr.c General purpose contour tracer for quadrilateral meshes. Handles single level contours, or region between a pair of levels. The routines that do all the work, as well as the explanatory comments, came from gcntr.c, part of the GIST package. The original mpl interface was also based on GIST. The present interface uses parts of the original, but places them in the entirely different framework of a Python type. It was written by following the Python "Extending and Embedding" tutorial. */ /* LICENSE AGREEMENT FOR MATPLOTLIB 0.84 -------------------------------------- 1. This LICENSE AGREEMENT is between John D. Hunter ("JDH"), and the Individual or Organization ("Licensee") accessing and otherwise using matplotlib software in source or binary form and its associated documentation. 2. Subject to the terms and conditions of this License Agreement, JDH hereby grants Licensee a nonexclusive, royalty-free, world-wide license to reproduce, analyze, test, perform and/or display publicly, prepare derivative works, distribute, and otherwise use matplotlib 0.84 alone or in any derivative version, provided, however, that JDH's License Agreement and JDH's notice of copyright, i.e., "Copyright (c) 2002-2005 John D. Hunter; All Rights Reserved" are retained in matplotlib 0.84 alone or in any derivative version prepared by Licensee. 3. In the event Licensee prepares a derivative work that is based on or incorporates matplotlib 0.84 or any part thereof, and wants to make the derivative work available to others as provided herein, then Licensee hereby agrees to include in any such work a brief summary of the changes made to matplotlib 0.84. 4. JDH is making matplotlib 0.84 available to Licensee on an "AS IS" basis. JDH MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, JDH MAKES NO AND DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF MATPLOTLIB 0.84 WILL NOT INFRINGE ANY THIRD PARTY RIGHTS. 5. JDH SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF MATPLOTLIB 0.84 FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING MATPLOTLIB 0.84, OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF. 6. This License Agreement will automatically terminate upon a material breach of its terms and conditions. 7. Nothing in this License Agreement shall be deemed to create any relationship of agency, partnership, or joint venture between JDH and Licensee. This License Agreement does not grant permission to use JDH trademarks or trade name in a trademark sense to endorse or promote products or services of Licensee, or any third party. 8. By copying, installing or otherwise using matplotlib 0.84, Licensee agrees to be bound by the terms and conditions of this License Agreement. */ #include #include "structmember.h" #include #include #define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION #include "numpy/arrayobject.h" /* Note that all arrays in these routines are Fortran-style, in the sense that the "i" index varies fastest; the dimensions of the corresponding C array are z[jmax][imax] in the notation used here. We can identify i and j with the x and y dimensions, respectively. */ /* What is a contour? * * Given a quadrilateral mesh (x,y), and values of a z at the points * of that mesh, we seek a set of polylines connecting points at a * particular value of z. Each point on such a contour curve lies * on an edge of the mesh, at a point linearly interpolated to the * contour level z0 between the given values of z at the endpoints * of the edge. * * Identifying these points is easy. Figuring out how to connect them * into a curve -- or possibly a set of disjoint curves -- is difficult. * Each disjoint curve may be either a closed circuit, or it may begin * and end on a mesh boundary. * * One of the problems with a quadrilateral mesh is that when the z * values at one pair of diagonally opposite points lie below z0, and * the values at the other diagonal pair of the same zone lie above z0, * all four edges of the zone are cut, and there is an ambiguity in * how we should connect the points. I call this a saddle zone. * The problem is that two disjoint curves cut through a saddle zone * (I reject the alternative of connecting the opposite points to make * a single self-intersecting curve, since those make ugly contour plots * -- I've tried it). The real problem with saddle zones is that you * need to communicate the connectivity decision you make back to the * calling routine, since for the next contour level, we need to tell * the contour tracer to make the same decision as on the previous * level. The input/output triangulation array is the solution to this * nasty problem. * * Another complicating factor is that there may be logical holes in * the mesh -- zones which do not exist. We want our contours to stop * if they hit the edge of such a zone, just as if they'd hit the edge * of the whole mesh. The input region array addresses this issue. * * Yet another complication: We may want a list of closed polygons which * outline the region between two contour levels z0 and z1. These may * include sections of the mesh boundary (including edges of logical * holes defined by the region array), in addition to sections of the * contour curves at one or both levels. This introduces a huge * topological problem -- if one of the closed contours (possibly * including an interior logical hole in the mesh, but not any part of * the boundary of the whole mesh) encloses a region which is not * between z0 and z1, that curve must be connected by a slit (or "branch * cut") to the enclosing curve, so that the list of disjoint polygons * we return is each simply connected. * * Okay, one final stunning difficulty: For the two level case, no * individual polygon should have more than a few thousand sides, since * huge filled polygons place an inordinate load on rendering software, * which needs an amount of scratch space proportional to the number * of sides it needs to fill. So in the two level case, we want to * chunk the mesh into rectangular pieces of no more than, say, 30x30 * zones, which keeps each returned polygon to less than a few thousand * sides (the worst case is very very bad -- you can easily write down * a function and two level values which produce a polygon that cuts * every edge of the mesh twice). */ /* * Here is the numbering scheme for points, edges, and zones in * the mesh -- note that each ij corresponds to one point, one zone, * one i-edge (i=constant edge) and one j-edge (j=constant edge): * * (ij-1)-------(ij)-------(ij) * | | * | | * | | * (ij-1) (ij) (ij) * | | * | | * | | * (ij-iX-1)----(ij-iX)----(ij-iX) * * At each point, the function value is either 0, 1, or 2, depending * on whether it is below z0, between z0 and z1, or above z1. * Each zone either exists (1) or not (0). * From these three bits of data, all of the curve connectivity follows. * * The tracing algorithm is naturally edge-based: Either you are at a * point where a level cuts an edge, ready to step across a zone to * another edge, or you are drawing the edge itself, if it happens to * be a boundary with at least one section between z0 and z1. * * In either case, the edge is a directed edge -- either the zone * you are advancing into is to its left or right, or you are actually * drawing it. I always trace curves keeping the region between z0 and * z1 to the left of the curve. If I'm tracing a boundary, I'm always * moving CCW (counter clockwise) around the zone that exists. And if * I'm about to cross a zone, I'll make the direction of the edge I'm * sitting on be such that the zone I'm crossing is to its left. * * I start tracing each curve near its lower left corner (mesh oriented * as above), which is the first point I encounter scanning through the * mesh in order. When I figure the 012 z values and zonal existence, * I also mark the potential starting points: Each edge may harbor a * potential starting point corresponding to either direction, so there * are four start possibilities at each ij point. Only the following * possibilities need to be marked as potential starting edges: * * +-+-+-+ * | | | | * A-0-C-+ One or both levels cut E and have z=1 above them, and * | EZ| | 0A is cut and either 0C is cut or CD is cut. * +-B-D-+ Or, one or both levels cut E and E is a boundary edge. * | | | | (and Z exists) * +-+-+-+ * * +-+-+-+ * | | | | * +-A-0-C One or both levels cut E and have z=1 below them, and * | |ZE | 0A is cut and either 0C is cut or CD is cut. * +-+-B-D Or, one or both levels cut E and E is a boundary edge. * | | | | (and Z exists) * +-+-+-+ * * +-+-+-+ * | | | | * +-+-+-+ E is a boundary edge, Z exists, at some point on E * | |Z| | lies between the levels. * +-+E+-+ * | | | | * +-+-+-+ * * +-+-+-+ * | | | | * +-+E+-+ E is a boundary edge, Z exists, at some point on E * | |Z| | lies between the levels. * +-+-+-+ * | | | | * +-+-+-+ * * During the first tracing pass, the start mark is erased whenever * any non-starting edge is encountered, reducing the number of points * that need to be considered for the second pass. The first pass * makes the basic connectivity decisions. It figures out how many * disjoint curves there will be, and identifies slits for the two level * case or open contours for the single level case, and removes all but * the actual start markers. A second tracing pass can perform the * actual final trace. */ /* ------------------------------------------------------------------------ */ /* the data about edges, zones, and points -- boundary or not, exists * or not, z value 0, 1, or 2 -- is kept in a mesh sized data array */ typedef short Cdata; /* here is the minimum structure required to tell where we are in the * mesh sized data array */ typedef struct Csite Csite; struct Csite { long edge; /* ij of current edge */ long left; /* +-1 or +-imax as the zone is to right, left, below, * or above the edge */ long imax; /* imax for the mesh */ long jmax; /* jmax for the mesh */ long n; /* number of points marked on this curve so far */ long count; /* count of start markers visited */ double zlevel[2]; /* contour levels, zlevel[1]<=zlevel[0] * signals single level case */ short *triangle; /* triangulation array for the mesh */ char *reg; /* region array for the mesh (was int) */ Cdata *data; /* added by EF */ long edge0, left0; /* starting site on this curve for closure */ int level0; /* starting level for closure */ long edge00; /* site needing START_ROW mark */ /* making the actual marks requires a bunch of other stuff */ const double *x, *y, *z; /* mesh coordinates and function values */ double *xcp, *ycp; /* output contour points */ }; #if 0 static void print_Csite(Csite *Csite) { Cdata *data = Csite->data; int i, j, ij; int nd = Csite->imax * (Csite->jmax + 1) + 1; printf("zlevels: %8.2lg %8.2lg\n", Csite->zlevel[0], Csite->zlevel[1]); printf("edge %ld, left %ld, n %ld, count %ld, edge0 %ld, left0 %ld\n", Csite->edge, Csite->left, Csite->n, Csite->count, Csite->edge0, Csite->left0); printf(" level0 %d, edge00 %ld\n", Csite->level0, Csite->edge00); printf("%04x\n", data[nd-1]); for (j = Csite->jmax; j >= 0; j--) { for (i=0; i < Csite->imax; i++) { ij = i + j * Csite->imax; printf("%04x ", data[ij]); } printf("\n"); } printf("\n"); } #endif /* triangle only takes values of -1, 0, 1, so it could be a signed char. */ /* most or all of the longs probably could be converted to ints with no loss */ /* the Cdata array consists of the following bits: * Z_VALUE (2 bits) 0, 1, or 2 function value at point * ZONE_EX 1 zone exists, 0 zone doesn't exist * I_BNDY this i-edge (i=constant edge) is a mesh boundary * J_BNDY this j-edge (i=constant edge) is a mesh boundary * I0_START this i-edge is a start point into zone to left * I1_START this i-edge is a start point into zone to right * J0_START this j-edge is a start point into zone below * J1_START this j-edge is a start point into zone above * START_ROW next start point is in current row (accelerates 2nd pass) * SLIT_UP marks this i-edge as the beginning of a slit upstroke * SLIT_DN marks this i-edge as the beginning of a slit downstroke * OPEN_END marks an i-edge start point whose other endpoint is * on a boundary for the single level case * ALL_DONE marks final start point */ #define Z_VALUE 0x0003 #define ZONE_EX 0x0004 #define I_BNDY 0x0008 #define J_BNDY 0x0010 #define I0_START 0x0020 #define I1_START 0x0040 #define J0_START 0x0080 #define J1_START 0x0100 #define START_ROW 0x0200 #define SLIT_UP 0x0400 #define SLIT_DN 0x0800 #define OPEN_END 0x1000 #define ALL_DONE 0x2000 /* some helpful macros to find points relative to a given directed * edge -- points are designated 0, 1, 2, 3 CCW around zone with 0 and * 1 the endpoints of the current edge */ #define FORWARD(left,ix) ((left)>0?((left)>1?1:-(ix)):((left)<-1?-1:(ix))) #define POINT0(edge,fwd) ((edge)-((fwd)>0?fwd:0)) #define POINT1(edge,fwd) ((edge)+((fwd)<0?fwd:0)) #define IS_JEDGE(edge,left) ((left)>0?((left)>1?1:0):((left)<-1?1:0)) #define ANY_START (I0_START|I1_START|J0_START|J1_START) #define START_MARK(left) \ ((left)>0?((left)>1?J1_START:I1_START):((left)<-1?J0_START:I0_START)) /* ------------------------------------------------------------------------ */ /* these actually mark points */ static int zone_crosser (Csite * site, int level, int pass2); static int edge_walker (Csite * site, int pass2); static int slit_cutter (Csite * site, int up, int pass2); /* this calls the first three to trace the next disjoint curve * -- return value is number of points on this curve, or * 0 if there are no more curves this pass * -(number of points) on first pass if: * this is two level case, and the curve closed on a hole * this is single level case, curve is open, and will start from * a different point on the second pass * -- in both cases, this curve will be combined with another * on the second pass */ static long curve_tracer (Csite * site, int pass2); /* this initializes the data array for curve_tracer */ static void data_init (Csite * site, int region, long nchunk); /* ------------------------------------------------------------------------ */ /* zone_crosser assumes you are sitting at a cut edge about to cross * the current zone. It always marks the initial point, crosses at * least one zone, and marks the final point. On non-boundary i-edges, * it is responsible for removing start markers on the first pass. */ static int zone_crosser (Csite * site, int level, int pass2) { Cdata * data = site->data; long edge = site->edge; long left = site->left; long n = site->n; long fwd = FORWARD (left, site->imax); long p0, p1; int jedge = IS_JEDGE (edge, left); long edge0 = site->edge0; long left0 = site->left0; int level0 = site->level0 == level; int two_levels = site->zlevel[1] > site->zlevel[0]; short *triangle = site->triangle; const double *x = pass2 ? site->x : 0; const double *y = pass2 ? site->y : 0; const double *z = pass2 ? site->z : 0; double zlevel = pass2 ? site->zlevel[level] : 0.0; double *xcp = pass2 ? site->xcp : 0; double *ycp = pass2 ? site->ycp : 0; int z0, z1, z2, z3; int keep_left = 0; /* flag to try to minimize curvature in saddles */ int done = 0; if (level) level = 2; for (;;) { /* set edge endpoints */ p0 = POINT0 (edge, fwd); p1 = POINT1 (edge, fwd); /* always mark cut on current edge */ if (pass2) { /* second pass actually computes and stores the point */ double zcp = (zlevel - z[p0]) / (z[p1] - z[p0]); xcp[n] = zcp * (x[p1] - x[p0]) + x[p0]; ycp[n] = zcp * (y[p1] - y[p0]) + y[p0]; } if (!done && !jedge) { if (n) { /* if this is not the first point on the curve, and we're * not done, and this is an i-edge, check several things */ if (!two_levels && !pass2 && (data[edge] & OPEN_END)) { /* reached an OPEN_END mark, skip the n++ */ done = 4; /* same return value 4 used below */ break; } /* check for curve closure -- if not, erase any start mark */ if (edge == edge0 && left == left0) { /* may signal closure on a downstroke */ if (level0) done = (!pass2 && two_levels && left < 0) ? 5 : 3; } else if (!pass2) { Cdata start = data[edge] & (fwd > 0 ? I0_START : I1_START); if (start) { data[edge] &= ~start; site->count--; } if (!two_levels) { start = data[edge] & (fwd > 0 ? I1_START : I0_START); if (start) { data[edge] &= ~start; site->count--; } } } } } n++; if (done) break; /* cross current zone to another cut edge */ z0 = (data[p0] & Z_VALUE) != level; /* 1 if fill toward p0 */ z1 = !z0; /* know level cuts edge */ z2 = (data[p1 + left] & Z_VALUE) != level; z3 = (data[p0 + left] & Z_VALUE) != level; if (z0 == z2) { if (z1 == z3) { /* this is a saddle zone, need triangle to decide * -- set triangle if not already decided for this zone */ long zone = edge + (left > 0 ? left : 0); if (triangle) { if (!triangle[zone]) { if (keep_left) triangle[zone] = jedge ? -1 : 1; else triangle[zone] = jedge ? 1 : -1; } if (triangle[zone] > 0 ? !jedge : jedge) goto bkwd; } else { if (keep_left) goto bkwd; } } /* bend forward (right along curve) */ keep_left = 1; jedge = !jedge; edge = p1 + (left > 0 ? left : 0); { long tmp = fwd; fwd = -left; left = tmp; } } else if (z1 == z3) { bkwd: /* bend backward (left along curve) */ keep_left = 0; jedge = !jedge; edge = p0 + (left > 0 ? left : 0); { long tmp = fwd; fwd = left; left = -tmp; } } else { /* straight across to opposite edge */ edge += left; } /* after crossing zone, edge/left/fwd is oriented CCW relative to * the next zone, assuming we will step there */ /* now that we've taken a step, check for the downstroke * of a slit on the second pass (upstroke checked above) * -- taking step first avoids a race condition */ if (pass2 && two_levels && !jedge) { if (left > 0) { if (data[edge] & SLIT_UP) done = 6; } else { if (data[edge] & SLIT_DN) done = 5; } } if (!done) { /* finally, check if we are on a boundary */ if (data[edge] & (jedge ? J_BNDY : I_BNDY)) { done = two_levels ? 2 : 4; /* flip back into the zone that exists */ left = -left; fwd = -fwd; if (!pass2 && (edge != edge0 || left != left0)) { Cdata start = data[edge] & START_MARK (left); if (start) { data[edge] &= ~start; site->count--; } } } } } site->edge = edge; site->n = n; site->left = left; return done > 4 ? slit_cutter (site, done - 5, pass2) : done; } /* edge_walker assumes that the current edge is being drawn CCW * around the current zone. Since only boundary edges are drawn * and we always walk around with the filled region to the left, * no edge is ever drawn CW. We attempt to advance to the next * edge on this boundary, but if current second endpoint is not * between the two contour levels, we exit back to zone_crosser. * Note that we may wind up marking no points. * -- edge_walker is never called for single level case */ static int edge_walker (Csite * site, int pass2) { Cdata * data = site->data; long edge = site->edge; long left = site->left; long n = site->n; long fwd = FORWARD (left, site->imax); long p0 = POINT0 (edge, fwd); long p1 = POINT1 (edge, fwd); int jedge = IS_JEDGE (edge, left); long edge0 = site->edge0; long left0 = site->left0; int level0 = site->level0 == 2; int marked; const double *x = pass2 ? site->x : 0; const double *y = pass2 ? site->y : 0; double *xcp = pass2 ? site->xcp : 0; double *ycp = pass2 ? site->ycp : 0; int z0, z1, heads_up = 0; for (;;) { /* mark endpoint 0 only if value is 1 there, and this is a * two level task */ z0 = data[p0] & Z_VALUE; z1 = data[p1] & Z_VALUE; marked = 0; if (z0 == 1) { /* mark current boundary point */ if (pass2) { xcp[n] = x[p0]; ycp[n] = y[p0]; } marked = 1; } else if (!n) { /* if this is the first point is not between the levels * must do the job of the zone_crosser and mark the first cut here, * so that it will be marked again by zone_crosser as it closes */ if (pass2) { double zcp = site->zlevel[(z0 != 0)]; zcp = (zcp - site->z[p0]) / (site->z[p1] - site->z[p0]); xcp[n] = zcp * (x[p1] - x[p0]) + x[p0]; ycp[n] = zcp * (y[p1] - y[p0]) + y[p0]; } marked = 1; } if (n) { /* check for closure */ if (level0 && edge == edge0 && left == left0) { site->edge = edge; site->left = left; site->n = n + marked; /* if the curve is closing on a hole, need to make a downslit */ if (fwd < 0 && !(data[edge] & (jedge ? J_BNDY : I_BNDY))) return slit_cutter (site, 0, pass2); return 3; } else if (pass2) { if (heads_up || (fwd < 0 && (data[edge] & SLIT_DN))) { site->edge = edge; site->left = left; site->n = n + marked; return slit_cutter (site, heads_up, pass2); } } else { /* if this is not first point, clear start mark for this edge */ Cdata start = data[edge] & START_MARK (left); if (start) { data[edge] &= ~start; site->count--; } } } if (marked) n++; /* if next endpoint not between levels, need to exit to zone_crosser */ if (z1 != 1) { site->edge = edge; site->left = left; site->n = n; return (z1 != 0); /* return level closest to p1 */ } /* step to p1 and find next edge * -- turn left if possible, else straight, else right * -- check for upward slit beginning at same time */ edge = p1 + (left > 0 ? left : 0); if (pass2 && jedge && fwd > 0 && (data[edge] & SLIT_UP)) { jedge = !jedge; heads_up = 1; } else if (data[edge] & (jedge ? I_BNDY : J_BNDY)) { long tmp = fwd; fwd = left; left = -tmp; jedge = !jedge; } else { edge = p1 + (fwd > 0 ? fwd : 0); if (pass2 && !jedge && fwd > 0 && (data[edge] & SLIT_UP)) { heads_up = 1; } else if (!(data[edge] & (jedge ? J_BNDY : I_BNDY))) { edge = p1 - (left < 0 ? left : 0); jedge = !jedge; { long tmp = fwd; fwd = -left; left = tmp; } } } p0 = p1; p1 = POINT1 (edge, fwd); } } /* -- slit_cutter is never called for single level case */ static int slit_cutter (Csite * site, int up, int pass2) { Cdata * data = site->data; long imax = site->imax; long n = site->n; const double *x = pass2 ? site->x : 0; const double *y = pass2 ? site->y : 0; double *xcp = pass2 ? site->xcp : 0; double *ycp = pass2 ? site->ycp : 0; if (up) { /* upward stroke of slit proceeds up left side of slit until * it hits a boundary or a point not between the contour levels * -- this never happens on the first pass */ long p1 = site->edge; int z1; for (;;) { z1 = data[p1] & Z_VALUE; if (z1 != 1) { site->edge = p1; site->left = -1; site->n = n; return (z1 != 0); } else if (data[p1] & J_BNDY) { /* this is very unusual case of closing on a mesh hole */ site->edge = p1; site->left = -imax; site->n = n; return 2; } xcp[n] = x[p1]; ycp[n] = y[p1]; n++; p1 += imax; } } else { /* downward stroke proceeds down right side of slit until it * hits a boundary or point not between the contour levels */ long p0 = site->edge; int z0; /* at beginning of first pass, mark first i-edge with SLIT_DN */ data[p0] |= SLIT_DN; p0 -= imax; for (;;) { z0 = data[p0] & Z_VALUE; if (!pass2) { if (z0 != 1 || (data[p0] & I_BNDY) || (data[p0 + 1] & J_BNDY)) { /* at end of first pass, mark final i-edge with SLIT_UP */ data[p0 + imax] |= SLIT_UP; /* one extra count for splicing at outer curve */ site->n = n + 1; return 4; /* return same special value as for OPEN_END */ } } else { if (z0 != 1) { site->edge = p0 + imax; site->left = 1; site->n = n; return (z0 != 0); } else if (data[p0 + 1] & J_BNDY) { site->edge = p0 + 1; site->left = imax; site->n = n; return 2; } else if (data[p0] & I_BNDY) { site->edge = p0; site->left = 1; site->n = n; return 2; } } if (pass2) { xcp[n] = x[p0]; ycp[n] = y[p0]; n++; } else { /* on first pass need to count for upstroke as well */ n += 2; } p0 -= imax; } } } /* ------------------------------------------------------------------------ */ /* curve_tracer finds the next starting point, then traces the curve, * returning the number of points on this curve * -- in a two level trace, the return value is negative on the * first pass if the curve closed on a hole * -- in a single level trace, the return value is negative on the * first pass if the curve is an incomplete open curve * -- a return value of 0 indicates no more curves */ static long curve_tracer (Csite * site, int pass2) { Cdata * data = site->data; long imax = site->imax; long edge0 = site->edge0; long left0 = site->left0; long edge00 = site->edge00; int two_levels = site->zlevel[1] > site->zlevel[0]; int level, level0, mark_row; long n; /* it is possible for a single i-edge to serve as two actual start * points, one to the right and one to the left * -- for the two level case, this happens on the first pass for * a doubly cut edge, or on a chunking boundary * -- for single level case, this is impossible, but a similar * situation involving open curves is handled below * a second two start possibility is when the edge0 zone does not * exist and both the i-edge and j-edge boundaries are cut * yet another possibility is three start points at a junction * of chunk cuts * -- sigh, several other rare possibilities, * allow for general case, just go in order i1, i0, j1, j0 */ int two_starts; /* printf("curve_tracer pass %d\n", pass2); */ /* print_Csite(site); */ if (left0 == 1) two_starts = data[edge0] & (I0_START | J1_START | J0_START); else if (left0 == -1) two_starts = data[edge0] & (J1_START | J0_START); else if (left0 == imax) two_starts = data[edge0] & J0_START; else two_starts = 0; if (pass2 || edge0 == 0) { /* zip up to row marked on first pass (or by data_init if edge0==0) * -- but not for double start case */ if (!two_starts) { /* final start point marked by ALL_DONE marker */ int first = (edge0 == 0 && !pass2); long e0 = edge0; if (data[edge0] & ALL_DONE) return 0; while (!(data[edge0] & START_ROW)) edge0 += imax; if (e0 == edge0) edge0++; /* two starts handled specially */ if (first) /* if this is the very first start point, we want to remove * the START_ROW marker placed by data_init */ data[edge0 - edge0 % imax] &= ~START_ROW; } } else { /* first pass ends when all potential start points visited */ if (site->count <= 0) { /* place ALL_DONE marker for second pass */ data[edge00] |= ALL_DONE; /* reset initial site for second pass */ site->edge0 = site->edge00 = site->left0 = 0; return 0; } if (!two_starts) edge0++; } if (two_starts) { /* trace second curve with this start immediately */ if (left0 == 1 && (data[edge0] & I0_START)) { left0 = -1; level = (data[edge0] & I_BNDY) ? 2 : 0; } else if ((left0 == 1 || left0 == -1) && (data[edge0] & J1_START)) { left0 = imax; level = 2; } else { left0 = -imax; level = 2; } } else { /* usual case is to scan for next start marker * -- on second pass, this is at most one row of mesh, but first * pass hits nearly every point of the mesh, since it can't * know in advance which potential start marks removed */ while (!(data[edge0] & ANY_START)) edge0++; if (data[edge0] & I1_START) left0 = 1; else if (data[edge0] & I0_START) left0 = -1; else if (data[edge0] & J1_START) left0 = imax; else /*data[edge0]&J0_START */ left0 = -imax; if (data[edge0] & (I1_START | I0_START)) level = (data[edge0] & I_BNDY) ? 2 : 0; else level = 2; } /* this start marker will not be unmarked, but it has been visited */ if (!pass2) site->count--; /* if this curve starts on a non-boundary i-edge, we need to * determine the level */ if (!level && two_levels) level = left0 > 0 ? ((data[edge0 - imax] & Z_VALUE) != 0) : ((data[edge0] & Z_VALUE) != 0); /* initialize site for this curve */ site->edge = site->edge0 = edge0; site->left = site->left0 = left0; site->level0 = level0 = level; /* for open curve detection only */ /* single level case just uses zone_crosser */ if (!two_levels) level = 0; /* to generate the curve, alternate between zone_crosser and * edge_walker until closure or first call to edge_walker in * single level case */ site->n = 0; for (;;) { if (level < 2) level = zone_crosser (site, level, pass2); else if (level < 3) level = edge_walker (site, pass2); else break; } n = site->n; /* single level case may have ended at a boundary rather than closing * -- need to recognize this case here in order to place the * OPEN_END mark for zone_crosser, remove this start marker, * and be sure not to make a START_ROW mark for this case * two level case may close with slit_cutter, in which case start * must also be removed and no START_ROW mark made * -- change sign of return n to inform caller */ if (!pass2 && level > 3 && (two_levels || level0 == 0)) { if (!two_levels) data[edge0] |= OPEN_END; data[edge0] &= ~(left0 > 0 ? I1_START : I0_START); mark_row = 0; /* do not mark START_ROW */ n = -n; } else { if (two_levels) mark_row = !two_starts; else mark_row = 1; } /* on first pass, must apply START_ROW mark in column above previous * start marker * -- but skip if we just did second of two start case */ if (!pass2 && mark_row) { data[edge0 - (edge0 - edge00) % imax] |= START_ROW; site->edge00 = edge0; } return n; } /* ------------------------------------------------------------------------ */ /* The sole function of the "region" argument is to specify the value in Csite.reg that denotes a missing zone. We always use zero. */ static void data_init (Csite * site, int region, long nchunk) { Cdata * data = site->data; long imax = site->imax; long jmax = site->jmax; long ijmax = imax * jmax; const double *z = site->z; double zlev0 = site->zlevel[0]; double zlev1 = site->zlevel[1]; int two_levels = zlev1 > zlev0; char *reg = site->reg; long count = 0; int started = 0; int ibndy, jbndy, i_was_chunk; long icsize = imax - 1; long jcsize = jmax - 1; long ichunk, jchunk, irem, jrem, i, j, ij; if (nchunk && two_levels) { /* figure out chunk sizes * -- input nchunk is square root of maximum allowed zones per chunk * -- start points for single level case are wrong, so don't try it */ long inum = (nchunk * nchunk) / (jmax - 1); long jnum = (nchunk * nchunk) / (imax - 1); if (inum < nchunk) inum = nchunk; if (jnum < nchunk) jnum = nchunk; /* ijnum= actual number of chunks, * ijrem= number of those chunks needing one more zone (ijcsize+1) */ inum = (imax - 2) / inum + 1; icsize = (imax - 1) / inum; irem = (imax - 1) % inum; jnum = (jmax - 2) / jnum + 1; jcsize = (jmax - 1) / jnum; jrem = (jmax - 1) % jnum; /* convert ijrem into value of i or j at which to begin adding an * extra zone */ irem = (inum - irem) * icsize; jrem = (jnum - jrem) * jcsize; } else { irem = imax; jrem = jmax; } /* do everything in a single pass through the data array to * minimize cache faulting (z, reg, and data are potentially * very large arrays) * access to the z and reg arrays is strictly sequential, * but we need two rows (+-imax) of the data array at a time */ if (z[0] > zlev0) data[0] = (two_levels && z[0] > zlev1) ? 2 : 1; else data[0] = 0; jchunk = 0; for (j = ij = 0; j < jmax; j++) { ichunk = i_was_chunk = 0; for (i = 0; i < imax; i++, ij++) { /* transfer zonal existence from reg to data array * -- get these for next row so we can figure existence of * points and j-edges for this row */ data[ij + imax + 1] = 0; if (reg) { if (region ? (reg[ij + imax + 1] == region) : (reg[ij + imax + 1] != 0)) data[ij + imax + 1] = ZONE_EX; } else { if (i < imax - 1 && j < jmax - 1) data[ij + imax + 1] = ZONE_EX; } /* translate z values to 0, 1, 2 flags */ if (ij < imax) data[ij + 1] = 0; if (ij < ijmax - 1 && z[ij + 1] > zlev0) data[ij + 1] |= (two_levels && z[ij + 1] > zlev1) ? 2 : 1; /* apply edge boundary marks */ ibndy = i == ichunk || (data[ij] & ZONE_EX) != (data[ij + 1] & ZONE_EX); jbndy = j == jchunk || (data[ij] & ZONE_EX) != (data[ij + imax] & ZONE_EX); if (ibndy) data[ij] |= I_BNDY; if (jbndy) data[ij] |= J_BNDY; /* apply i-edge start marks * -- i-edges are only marked when actually cut * -- no mark is necessary if one of the j-edges which share * the lower endpoint is also cut * -- no I0 mark necessary unless filled region below some cut, * no I1 mark necessary unless filled region above some cut */ if (j) { int v0 = (data[ij] & Z_VALUE); int vb = (data[ij - imax] & Z_VALUE); if (v0 != vb) { /* i-edge is cut */ if (ibndy) { if (data[ij] & ZONE_EX) { data[ij] |= I0_START; count++; } if (data[ij + 1] & ZONE_EX) { data[ij] |= I1_START; count++; } } else { int va = (data[ij - 1] & Z_VALUE); int vc = (data[ij + 1] & Z_VALUE); int vd = (data[ij - imax + 1] & Z_VALUE); if (v0 != 1 && va != v0 && (vc != v0 || vd != v0) && (data[ij] & ZONE_EX)) { data[ij] |= I0_START; count++; } if (vb != 1 && va == vb && (vc == vb || vd == vb) && (data[ij + 1] & ZONE_EX)) { data[ij] |= I1_START; count++; } } } } /* apply j-edge start marks * -- j-edges are only marked when they are boundaries * -- all cut boundary edges marked * -- for two level case, a few uncut edges must be marked */ if (i && jbndy) { int v0 = (data[ij] & Z_VALUE); int vb = (data[ij - 1] & Z_VALUE); if (v0 != vb) { if (data[ij] & ZONE_EX) { data[ij] |= J0_START; count++; } if (data[ij + imax] & ZONE_EX) { data[ij] |= J1_START; count++; } } else if (two_levels && v0 == 1) { if (data[ij + imax] & ZONE_EX) { if (i_was_chunk || !(data[ij + imax - 1] & ZONE_EX)) { /* lower left is a drawn part of boundary */ data[ij] |= J1_START; count++; } } else if (data[ij] & ZONE_EX) { if (data[ij + imax - 1] & ZONE_EX) { /* weird case of open hole at lower left */ data[ij] |= J0_START; count++; } } } } i_was_chunk = (i == ichunk); if (i_was_chunk) ichunk += icsize + (ichunk >= irem); } if (j == jchunk) jchunk += jcsize + (jchunk >= jrem); /* place first START_ROW marker */ if (count && !started) { data[ij - imax] |= START_ROW; started = 1; } } /* place immediate stop mark if nothing found */ if (!count) data[0] |= ALL_DONE; /* initialize site */ site->edge0 = site->edge00 = site->edge = 0; site->left0 = site->left = 0; site->n = 0; site->count = count; } /* ------------------------------------------------------------------------ Original (slightly modified) core contour generation routines are above; below are new routines for interfacing to mpl. ------------------------------------------------------------------------ */ /* Note: index order gets switched in the Python interface; python Z[i,j] -> C z[j,i] so if the array has shape Mi, Nj in python, we have iMax = Nj, jMax = Mi in gcntr.c. On the Python side: Ny, Nx = shape(z), so in C, the x-dimension is the first index, the y-dimension the second. */ /* reg should have the same dimensions as data, which has an extra iMax + 1 points relative to Z. It differs from mask in being the opposite (True where a region exists, versus the mask, which is True where a data point is bad), and in that it marks zones, not points. All four zones sharing a bad point must be marked as not existing. */ static void mask_zones (long iMax, long jMax, char *mask, char *reg) { long i, j, ij; long nreg = iMax * jMax + iMax + 1; for (ij = iMax+1; ij < iMax*jMax; ij++) { reg[ij] = 1; } ij = 0; for (j = 0; j < jMax; j++) { for (i = 0; i < iMax; i++, ij++) { if (i == 0 || j == 0) reg[ij] = 0; if (mask[ij] != 0) { reg[ij] = 0; reg[ij + 1] = 0; reg[ij + iMax] = 0; reg[ij + iMax + 1] = 0; } } } for (; ij < nreg; ij++) { reg[ij] = 0; } } static Csite * cntr_new(void) { Csite *site; site = (Csite *) PyMem_Malloc(sizeof(Csite)); if (site == NULL) return NULL; site->data = NULL; site->reg = NULL; site->triangle = NULL; site->xcp = NULL; site->ycp = NULL; site->x = NULL; site->y = NULL; site->z = NULL; return site; } static int cntr_init(Csite *site, long iMax, long jMax, double *x, double *y, double *z, char *mask) { long ijmax = iMax * jMax; long nreg = iMax * jMax + iMax + 1; long i; site->imax = iMax; site->jmax = jMax; site->data = (Cdata *) PyMem_Malloc(sizeof(Cdata) * nreg); if (site->data == NULL) { PyMem_Free(site); return -1; } site->triangle = (short *) PyMem_Malloc(sizeof(short) * ijmax); if (site->triangle == NULL) { PyMem_Free(site->data); PyMem_Free(site); return -1; } for (i = 0; i < ijmax; i++) site->triangle[i] = 0; site->reg = NULL; if (mask != NULL) { site->reg = (char *) PyMem_Malloc(sizeof(char) * nreg); if (site->reg == NULL) { PyMem_Free(site->triangle); PyMem_Free(site->data); PyMem_Free(site); return -1; } mask_zones(iMax, jMax, mask, site->reg); } /* I don't think we need to initialize site->data. */ site->x = x; site->y = y; site->z = z; site->xcp = NULL; site->ycp = NULL; return 0; } static void cntr_del(Csite *site) { PyMem_Free(site->triangle); PyMem_Free(site->reg); PyMem_Free(site->data); PyMem_Free(site); site = NULL; } /* Build a list of lists of points, where each point is an (x,y) tuple. */ static PyObject * build_cntr_list_p(long *np, double *xp, double *yp, int nparts, long ntotal) { PyObject *point, *contourList, *all_contours; int start = 0, end = 0; int i, j, k; all_contours = PyList_New(nparts); if (all_contours == NULL) return NULL; for (i = 0; i < nparts; i++) { start = end; end += np[i]; contourList = PyList_New(np[i]); if (contourList == NULL) goto error; for (k = 0, j = start; j < end; j++, k++) { point = Py_BuildValue("(dd)", xp[j], yp[j]); if (PyList_SetItem(contourList, k, point)) goto error; } if (PyList_SetItem(all_contours, i, contourList)) goto error; } return all_contours; error: Py_XDECREF(all_contours); return NULL; } /* Build a list of tuples (X, Y), where X and Y are 1-D arrays. */ #if 0 static PyObject * build_cntr_list_v(long *np, double *xp, double *yp, int nparts, long ntotal) { PyObject *point, *all_contours; PyArrayObject *xv, *yv; npy_intp dims[1]; int i; long j, k; all_contours = PyList_New(nparts); k = 0; for (i = 0; i < nparts; i++) { dims[0] = np[i]; xv = (PyArrayObject *) PyArray_SimpleNew(1, dims, NPY_DOUBLE); yv = (PyArrayObject *) PyArray_SimpleNew(1, dims, NPY_DOUBLE); if (xv == NULL || yv == NULL) goto error; for (j = 0; j < dims[0]; j++) { ((double *)xv->data)[j] = xp[k]; ((double *)yv->data)[j] = yp[k]; k++; } point = Py_BuildValue("(NN)", xv, yv); /* "O" increments ref count; "N" does not. */ if (PyList_SetItem(all_contours, i, point)) goto error; } return all_contours; error: Py_XDECREF(all_contours); return NULL; } #endif /* Build a list of XY 2-D arrays, shape (N,2) */ static PyObject * build_cntr_list_v2(long *np, double *xp, double *yp, int nparts, long ntotal) { PyObject *all_contours; PyArrayObject *xyv; npy_intp dims[2]; int i; long j, k; all_contours = PyList_New(nparts); if (all_contours == NULL) return NULL; k = 0; for (i = 0; i < nparts; i++) { double *data; dims[0] = np[i]; dims[1] = 2; xyv = (PyArrayObject *) PyArray_SimpleNew(2, dims, NPY_DOUBLE); if (xyv == NULL) goto error; data = (double*)PyArray_DATA(xyv); for (j = 0; j < dims[0]; j++) { data[2*j] = xp[k]; data[2*j+1] = yp[k]; k++; } if (PyList_SetItem(all_contours, i, (PyObject *)xyv)) goto error; } return all_contours; error: Py_XDECREF(all_contours); return NULL; } /* cntr_trace is called once per contour level or level pair. If nlevels is 1, a set of contour lines will be returned; if nlevels is 2, the set of polygons bounded by the levels will be returned. If points is True, the lines will be returned as a list of list of points; otherwise, as a list of tuples of vectors. */ static PyObject * cntr_trace(Csite *site, double levels[], int nlevels, int points, long nchunk) { PyObject *c_list; double *xp0; double *yp0; long *nseg0; int iseg; /* long nchunk = 30; was hardwired */ long n; long nparts = 0; long ntotal = 0; long nparts2 = 0; long ntotal2 = 0; site->zlevel[0] = levels[0]; site->zlevel[1] = levels[0]; if (nlevels == 2) { site->zlevel[1] = levels[1]; } site->n = site->count = 0; data_init (site, 0, nchunk); /* make first pass to compute required sizes for second pass */ for (;;) { n = curve_tracer (site, 0); if (!n) break; if (n > 0) { nparts++; ntotal += n; } else { ntotal -= n; } } xp0 = (double *) PyMem_Malloc(ntotal * sizeof(double)); yp0 = (double *) PyMem_Malloc(ntotal * sizeof(double)); nseg0 = (long *) PyMem_Malloc(nparts * sizeof(long)); if (xp0 == NULL || yp0 == NULL || nseg0 == NULL) { PyErr_NoMemory(); goto error; } /* second pass */ site->xcp = xp0; site->ycp = yp0; iseg = 0; for (;;iseg++) { n = curve_tracer (site, 1); if (ntotal2 + n > ntotal) { PyErr_SetString(PyExc_RuntimeError, "curve_tracer: ntotal2, pass 2 exceeds ntotal, pass 1"); goto error; } if (n == 0) break; if (n > 0) { /* could add array bounds checking */ nseg0[iseg] = n; site->xcp += n; site->ycp += n; ntotal2 += n; nparts2++; } else { PyErr_SetString(PyExc_RuntimeError, "Negative n from curve_tracer in pass 2"); goto error; } } if (points) { c_list = build_cntr_list_p(nseg0, xp0, yp0, nparts, ntotal); } else { c_list = build_cntr_list_v2(nseg0, xp0, yp0, nparts, ntotal); } PyMem_Free(xp0); PyMem_Free(yp0); PyMem_Free(nseg0); site->xcp = NULL; site->ycp = NULL; return c_list; error: PyMem_Free(xp0); PyMem_Free(yp0); PyMem_Free(nseg0); site->xcp = NULL; site->ycp = NULL; return NULL; } /******* Make an extension type. Based on the tutorial.************/ /* site points to the data arrays in the arrays pointed to by xpa, ypa, zpa, and mpa, so we include them in the structure so we can ensure they are not deleted until we have finished using them. */ typedef struct { PyObject_HEAD PyArrayObject *xpa, *ypa, *zpa, *mpa; Csite *site; } Cntr; static int Cntr_clear(Cntr* self) { PyArrayObject *tmp; cntr_del(self->site); tmp = self->xpa; self->xpa = NULL; Py_XDECREF(tmp); tmp = self->ypa; self->ypa = NULL; Py_XDECREF(tmp); tmp = self->zpa; self->zpa = NULL; Py_XDECREF(tmp); tmp = self->mpa; self->mpa = NULL; Py_XDECREF(tmp); return 0; } static void Cntr_dealloc(Cntr* self) { Cntr_clear(self); Py_TYPE(self)->tp_free((PyObject*)self); } static PyObject * Cntr_new(PyTypeObject *type, PyObject *args, PyObject *kwds) { Cntr *self; self = (Cntr *)type->tp_alloc(type, 0); if (self != NULL) { self->site = cntr_new(); if (self->site == NULL) { PyErr_SetString(PyExc_MemoryError, "Memory allocation failed in cntr_new."); Py_XDECREF(self); return NULL; } self->xpa = NULL; self->ypa = NULL; self->zpa = NULL; self->mpa = NULL; } return (PyObject *)self; } static int Cntr_init(Cntr *self, PyObject *args, PyObject *kwds) { static char *kwlist[] = {"x", "y", "z", "mask", NULL}; PyObject *xarg, *yarg, *zarg, *marg; PyArrayObject *xpa, *ypa, *zpa, *mpa; long iMax, jMax; char *mask; marg = NULL; if (! PyArg_ParseTupleAndKeywords(args, kwds, "OOO|O", kwlist, &xarg, &yarg, &zarg, &marg)) return -1; if (marg == Py_None) marg = NULL; if (!PyArray_Check(xarg) || !PyArray_Check(yarg) || !PyArray_Check(zarg) || (marg && !PyArray_Check(marg))) { PyErr_SetString(PyExc_TypeError, "Arguments x, y, z, (optional) mask must be arrays."); return -1; } xpa = (PyArrayObject *) PyArray_ContiguousFromObject(xarg, NPY_DOUBLE, 2, 2); ypa = (PyArrayObject *) PyArray_ContiguousFromObject(yarg, NPY_DOUBLE, 2, 2); zpa = (PyArrayObject *) PyArray_ContiguousFromObject(zarg, NPY_DOUBLE, 2, 2); if (marg) mpa = (PyArrayObject *) PyArray_ContiguousFromObject(marg, NPY_BYTE, 2, 2); else mpa = NULL; if (xpa == NULL || ypa == NULL || zpa == NULL || (marg && mpa == NULL)) { PyErr_SetString(PyExc_ValueError, "Arguments x, y, z, mask (if present) must be 2D arrays."); goto error; } iMax = PyArray_DIMS(zpa)[1]; jMax = PyArray_DIMS(zpa)[0]; if (PyArray_DIMS(xpa)[0] != jMax || PyArray_DIMS(xpa)[1] != iMax || PyArray_DIMS(ypa)[0] != jMax || PyArray_DIMS(ypa)[1] != iMax || (mpa && (PyArray_DIMS(mpa)[0] != jMax || PyArray_DIMS(mpa)[1] != iMax))) { PyErr_SetString(PyExc_ValueError, "Arguments x, y, z, mask (if present)" " must have the same dimensions."); goto error; } if (mpa) mask = PyArray_DATA(mpa); else mask = NULL; if ( cntr_init(self->site, iMax, jMax, (double *)PyArray_DATA(xpa), (double *)PyArray_DATA(ypa), (double *)PyArray_DATA(zpa), mask)) { PyErr_SetString(PyExc_MemoryError, "Memory allocation failure in cntr_init"); goto error; } self->xpa = xpa; self->ypa = ypa; self->zpa = zpa; self->mpa = mpa; return 0; error: Py_XDECREF(xpa); Py_XDECREF(ypa); Py_XDECREF(zpa); Py_XDECREF(mpa); return -1; } static PyObject * Cntr_trace(Cntr *self, PyObject *args, PyObject *kwds) { double levels[2] = {0.0, -1e100}; int nlevels = 2; int points = 0; long nchunk = 0L; static char *kwlist[] = {"level0", "level1", "points", "nchunk", NULL}; if (! PyArg_ParseTupleAndKeywords(args, kwds, "d|dil", kwlist, levels, levels+1, &points, &nchunk)) { return NULL; } if (levels[1] == -1e100 || levels[1] <= levels[0]) nlevels = 1; return cntr_trace(self->site, levels, nlevels, points, nchunk); } static PyMethodDef Cntr_methods[] = { {"trace", (PyCFunction)Cntr_trace, METH_VARARGS | METH_KEYWORDS, "Return a list of contour line segments or polygons.\n\n" " Required argument: level0, a contour level\n" " Optional argument: level1; if given, and if level1 > level0,\n" " then the contours will be polygons surrounding areas between\n" " the levels.\n" " Optional argument: points; if 0 (default), return a list of\n" " vector pairs; otherwise, return a list of lists of points.\n" " Optional argument: nchunk; approximate number of grid points\n" " per chunk. 0 (default) for no chunking.\n" }, {NULL} /* Sentinel */ }; static PyTypeObject CntrType = { PyVarObject_HEAD_INIT(NULL, 0) "_nc_cntr.Cntr", /*tp_name*/ sizeof(Cntr), /*tp_basicsize*/ 0, /*tp_itemsize*/ (destructor)Cntr_dealloc, /*tp_dealloc*/ 0, /*tp_print*/ 0, /*tp_getattr*/ 0, /*tp_setattr*/ 0, /*tp_compare*/ 0, /*tp_repr*/ 0, /*tp_as_number*/ 0, /*tp_as_sequence*/ 0, /*tp_as_mapping*/ 0, /*tp_hash */ 0, /*tp_call*/ 0, /*tp_str*/ 0, /*tp_getattro*/ 0, /*tp_setattro*/ 0, /*tp_as_buffer*/ Py_TPFLAGS_DEFAULT, /*tp_flags*/ "Contour engine", /* tp_doc */ 0, /* tp_traverse */ (inquiry)Cntr_clear, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ 0, /* tp_iter */ 0, /* tp_iternext */ Cntr_methods, /* tp_methods */ 0, /* tp_members */ 0, /* tp_getset */ 0, /* tp_base */ 0, /* tp_dict */ 0, /* tp_descr_get */ 0, /* tp_descr_set */ 0, /* tp_dictoffset */ (initproc)Cntr_init, /* tp_init */ 0, /* tp_alloc */ Cntr_new, /* tp_new */ }; static PyMethodDef module_methods[] = { {NULL} /* Sentinel */ }; /* see http://python3porting.com/cextensions.html */ #if PY_MAJOR_VERSION >= 3 # define MOD_ERROR_VAL NULL # define MOD_SUCCESS_VAL(val) val # define MOD_INIT(name) PyMODINIT_FUNC PyInit_##name(void) # define MOD_DEF(ob, name, doc, methods) \ { \ static struct PyModuleDef moduledef = { \ PyModuleDef_HEAD_INIT, name, doc, -1, methods, }; \ ob = PyModule_Create(&moduledef); \ } #else # define MOD_ERROR_VAL # define MOD_SUCCESS_VAL(val) # define MOD_INIT(name) void init##name(void) # define MOD_DEF(ob, name, doc, methods) \ ob = Py_InitModule3(name, methods, doc) #endif /* returns different type depending on python version */ #if PY_MAJOR_VERSION >= 3 static void* donumpyinit(void) { import_array(); return NULL; } #else static void donumpyinit(void) { import_array(); } #endif MOD_INIT(_nc_cntr) { PyObject *m; if( PyType_Ready(&CntrType) < 0 ) return MOD_ERROR_VAL; MOD_DEF(m, "_nc_cntr", "Contour 2D data", module_methods); if( m == NULL ) return MOD_ERROR_VAL; donumpyinit(); Py_INCREF(&CntrType); PyModule_AddObject(m, "Cntr", (PyObject *)&CntrType); return MOD_SUCCESS_VAL(m); } ������������veusz-3.0.1/veusz/helpers/src/nc_cntr/README��������������������������������������������������������0000664�0001750�0001750�00000000357�13161413406�020230� 0����������������������������������������������������������������������������������������������������ustar �jss�����������������������������jss�����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������_na_cntr.c is taken from matplotlib (currently version 0.84). The licence of this file is in the file LICENSE_MATPLOTLIB or can be read at http://matplotlib.sourceforge.net/license.html Thanks for the matplotlib guys for this nice code. ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������veusz-3.0.1/veusz/helpers/src/nc_cntr/LICENSE_MATPLOTLIB��������������������������������������������0000664�0001750�0001750�00000004544�13161413406�022106� 0����������������������������������������������������������������������������������������������������ustar �jss�����������������������������jss�����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������LICENSE AGREEMENT FOR MATPLOTLIB 0.84 -------------------------------------- 1. This LICENSE AGREEMENT is between John D. Hunter ("JDH"), and the Individual or Organization ("Licensee") accessing and otherwise using matplotlib software in source or binary form and its associated documentation. 2. Subject to the terms and conditions of this License Agreement, JDH hereby grants Licensee a nonexclusive, royalty-free, world-wide license to reproduce, analyze, test, perform and/or display publicly, prepare derivative works, distribute, and otherwise use matplotlib 0.84 alone or in any derivative version, provided, however, that JDH's License Agreement and JDH's notice of copyright, i.e., "Copyright (c) 2002-2005 John D. Hunter; All Rights Reserved" are retained in matplotlib 0.84 alone or in any derivative version prepared by Licensee. 3. In the event Licensee prepares a derivative work that is based on or incorporates matplotlib 0.84 or any part thereof, and wants to make the derivative work available to others as provided herein, then Licensee hereby agrees to include in any such work a brief summary of the changes made to matplotlib 0.84. 4. JDH is making matplotlib 0.84 available to Licensee on an "AS IS" basis. JDH MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, JDH MAKES NO AND DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF MATPLOTLIB 0.84 WILL NOT INFRINGE ANY THIRD PARTY RIGHTS. 5. JDH SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF MATPLOTLIB 0.84 FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING MATPLOTLIB 0.84, OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF. 6. This License Agreement will automatically terminate upon a material breach of its terms and conditions. 7. Nothing in this License Agreement shall be deemed to create any relationship of agency, partnership, or joint venture between JDH and Licensee. This License Agreement does not grant permission to use JDH trademarks or trade name in a trademark sense to endorse or promote products or services of Licensee, or any third party. 8. By copying, installing or otherwise using matplotlib 0.84, Licensee agrees to be bound by the terms and conditions of this License Agreement. ������������������������������������������������������������������������������������������������������������������������������������������������������������veusz-3.0.1/veusz/helpers/src/threed/���������������������������������������������������������������0000775�0001750�0001750�00000000000�13325026670�017175� 5����������������������������������������������������������������������������������������������������ustar �jss�����������������������������jss�����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������veusz-3.0.1/veusz/helpers/src/threed/mmaths.cpp�����������������������������������������������������0000664�0001750�0001750�00000003214�13306763070�021173� 0����������������������������������������������������������������������������������������������������ustar �jss�����������������������������jss�����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright (C) 2015 Jeremy S. Sanders // Email: Jeremy Sanders // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program 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 General Public License for more details. // // You should have received a copy of the GNU General Public License along // with this program; if not, write to the Free Software Foundation, Inc., // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ///////////////////////////////////////////////////////////////////////////// #include #include "mmaths.h" Mat4 rotateM4(double angle, Vec3 vec) { double c = std::cos(angle); double s = std::sin(angle); Vec3 a(vec); a.normalise(); Vec3 t(a*(1-c)); Mat4 m; m(0,0) = c+t(0)*a(0); m(0,1) = 0+t(1)*a(0)-s*a(2); m(0,2) = 0+t(2)*a(0)+s*a(1); m(0,3) = 0; m(1,0) = 0+t(0)*a(1)+s*a(2); m(1,1) = c+t(1)*a(1); m(1,2) = 0+t(2)*a(1)-s*a(0); m(1,3) = 0; m(2,0) = 0+t(0)*a(2)-s*a(1); m(2,1) = 0+t(1)*a(2)+s*a(0); m(2,2) = c+t(2)*a(2); m(2,3) = 0; m(3,0) = 0; m(3,1) = 0; m(3,2) = 0; m(3,3) = 1; return m; } Mat4 translationM4(Vec3 vec) { Mat4 m; m(0,0) = 1; m(0,3) = vec(0); m(1,1) = 1; m(1,3) = vec(1); m(2,2) = 1; m(2,3) = vec(2); m(3,3) = 1; return m; } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������veusz-3.0.1/veusz/helpers/src/threed/camera.h�������������������������������������������������������0000664�0001750�0001750�00000003124�13302252613�020567� 0����������������������������������������������������������������������������������������������������ustar �jss�����������������������������jss�����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// -*-c++-*- // Copyright (C) 2015 Jeremy S. Sanders // Email: Jeremy Sanders // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program 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 General Public License for more details. // // You should have received a copy of the GNU General Public License along // with this program; if not, write to the Free Software Foundation, Inc., // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ///////////////////////////////////////////////////////////////////////////// #ifndef CAMERA_H #define CAMERA_H #include "mmaths.h" class Camera { public: Camera(); // Look at target position from eye, given up vector. // See glm code lookAt void setPointing(const Vec3 &eye, const Vec3 &target, const Vec3 &up); // fovy_degrees: total field of view in degrees // znear: clip things nearer than this (should be as big as // possible for precision) // zfar: far clipping plane. void setPerspective(double fov_degrees=90, double znear=0.1, double zfar=100); public: Mat4 viewM; // view matrix Mat4 perspM; // perspective matrix Mat4 combM; // combined matrix Vec3 eye; // location of eye }; #endif ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������veusz-3.0.1/veusz/helpers/src/threed/bsp.h����������������������������������������������������������0000664�0001750�0001750�00000004550�13302252613�020127� 0����������������������������������������������������������������������������������������������������ustar �jss�����������������������������jss�����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// -*-c++-*- // Copyright (C) 2015 Jeremy S. Sanders // Email: Jeremy Sanders // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program 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 General Public License for more details. // // You should have received a copy of the GNU General Public License along // with this program; if not, write to the Free Software Foundation, Inc., // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ///////////////////////////////////////////////////////////////////////////// #ifndef BSP_H #define BSP_H #include #include "fragment.h" typedef std::vector IdxVector; #define EMPTY_BSP_IDX (std::numeric_limits::max()) struct BSPRecord { BSPRecord() : minfragidxidx(0), nfrags(0), frontidx(EMPTY_BSP_IDX), backidx(EMPTY_BSP_IDX) { } // fragments stored in this node, in terms of the index to an array // of indexes, frag_idxs unsigned minfragidxidx, nfrags; // indices in bsp_recs to the BSPRecord items in front and behind unsigned frontidx, backidx; }; // This class defines a specialised Binary Space Paritioning (BSP) // buliding routine. 3D space is split recursively by planes to // separate objects into front and back entries. The idea is to only // use the BSP tree _once_, which is unlike normal uses of BSP. It is // used to create a robust back->front ordering for a particular // viewing direction. To avoid lots of dynamic memory allocation and // to reduce overheads, the nodes in the BSP tree are stored in a // vector. class BSPBuilder { public: // construct the BSP tree from the fragments given and a particular // viewing direction BSPBuilder(FragmentVector& fragvec, Vec3 viewdirn); // return a vector of fragment indexes in drawing order IdxVector getFragmentIdxs(const FragmentVector& fragvec) const; // the nodes in the tree std::vector bsp_recs; // vector of indices to the fragments vector IdxVector frag_idxs; }; #endif ��������������������������������������������������������������������������������������������������������������������������������������������������������veusz-3.0.1/veusz/helpers/src/threed/camera.cpp�����������������������������������������������������0000664�0001750�0001750�00000004632�13302252613�021127� 0����������������������������������������������������������������������������������������������������ustar �jss�����������������������������jss�����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright (C) 2015 Jeremy S. Sanders // Email: Jeremy Sanders // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program 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 General Public License for more details. // // You should have received a copy of the GNU General Public License along // with this program; if not, write to the Free Software Foundation, Inc., // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ///////////////////////////////////////////////////////////////////////////// #include #include "camera.h" Camera::Camera() { setPointing(Vec3(0,0,0), Vec3(0,0,1), Vec3(0,1,0)); setPerspective(); } void Camera::setPointing(const Vec3 &_eye, const Vec3 &target, const Vec3 &up) { // is it this one or the one below? // http://3dgep.com/?p=1700 eye = _eye; Vec3 f = target - eye; f.normalise(); Vec3 u = up; u.normalise(); Vec3 s = cross(f, u); s.normalise(); u = cross(s, f); viewM(0,0) = s(0); viewM(0,1) = s(1); viewM(0,2) = s(2); viewM(0,3) = -dot(s, eye); viewM(1,0) = u(0); viewM(1,1) = u(1); viewM(1,2) = u(2); viewM(1,3) = -dot(u, eye); viewM(2,0) = -f(0); viewM(2,1) = -f(1); viewM(2,2) = -f(2); viewM(2,3) = dot(f, eye); viewM(3,0) = 0; viewM(3,1) = 0; viewM(3,2) = 0; viewM(3,3) = 1; combM = perspM * viewM; } void Camera::setPerspective(double fov_degrees, double znear, double zfar) { // matrix from Scratchapixel 2 // https://www.scratchapixel.com/lessons/3d-basic-rendering/perspective-and-orthographic-projection-matrix/building-basic-perspective-projection-matrix double scale = 1/std::tan(fov_degrees*(PI/180/2)); perspM(0,0) = scale; perspM(1,0) = 0; perspM(2,0) = 0; perspM(3,0) = 0; perspM(0,1) = 0; perspM(1,1) = scale; perspM(2,1) = 0; perspM(3,1) = 0; perspM(0,2) = 0; perspM(1,2) = 0; perspM(2,2) = -zfar/(zfar-znear); perspM(3,2) = -1; perspM(0,3) = 0; perspM(1,3) = 0; perspM(2,3) = -zfar*znear/(zfar-znear); perspM(3,3) = 0; combM = perspM * viewM; } ������������������������������������������������������������������������������������������������������veusz-3.0.1/veusz/helpers/src/threed/fragment.cpp���������������������������������������������������0000664�0001750�0001750�00000002303�13302252613�021473� 0����������������������������������������������������������������������������������������������������ustar �jss�����������������������������jss�����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright (C) 2015 Jeremy S. Sanders // Email: Jeremy Sanders // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program 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 General Public License for more details. // // You should have received a copy of the GNU General Public License along // with this program; if not, write to the Free Software Foundation, Inc., // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ///////////////////////////////////////////////////////////////////////////// #include "fragment.h" FragmentParameters::~FragmentParameters() { } void FragmentPathParameters::callback(QPainter* painter, QPointF pt1, QPointF pt2, QPointF pt3, int index, double scale, double linescale) { } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������veusz-3.0.1/veusz/helpers/src/threed/fixedvector.h��������������������������������������������������0000664�0001750�0001750�00000004150�13302252613�021661� 0����������������������������������������������������������������������������������������������������ustar �jss�����������������������������jss�����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// -*-c++-*- // Copyright (C) 2015 Jeremy S. Sanders // Email: Jeremy Sanders // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program 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 General Public License for more details. // // You should have received a copy of the GNU General Public License along // with this program; if not, write to the Free Software Foundation, Inc., // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ///////////////////////////////////////////////////////////////////////////// #ifndef FIXEDVECTOR_H #define FIXEDVECTOR_H // fixed sized vector with no dynamic allocations // warning: no checks for invalid indices! // class T needs a default constructor template class FixedVector { public: typedef T* iterator; typedef const T* const_iterator; FixedVector() : _size(0) {} void push_back(const T& v) { _data[_size++] = v; } unsigned short size() const { return _size; } bool empty() const { return _size==0; } unsigned short max_size() const { return N; } const T& operator[](unsigned short idx) const { return _data[idx]; } T& operator[](unsigned short idx) { return _data[idx]; } iterator begin() { return &_data[0]; } const_iterator begin() const { return &_data[0]; } const_iterator cbegin() const { return &_data[0]; } iterator end() { return &_data[_size]; } const_iterator end() const { return &_data[_size]; } const_iterator cend() const { return &_data[_size]; } T& front() { return _data[0]; } const T& front() const { return _data[0]; } T& back() { return _data[_size-1]; } const T& back() const { return _data[_size-1]; } private: unsigned short _size; T _data[N]; }; #endif ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������veusz-3.0.1/veusz/helpers/src/threed/scene.h��������������������������������������������������������0000664�0001750�0001750�00000006765�13306704305�020456� 0����������������������������������������������������������������������������������������������������ustar �jss�����������������������������jss�����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// -*-c++-*- // Copyright (C) 2015 Jeremy S. Sanders // Email: Jeremy Sanders // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program 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 General Public License for more details. // // You should have received a copy of the GNU General Public License along // with this program; if not, write to the Free Software Foundation, Inc., // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ///////////////////////////////////////////////////////////////////////////// #ifndef SCENE_H #define SCENE_H #include #include #include "mmaths.h" #include "objects.h" #include "camera.h" class Scene { public: enum RenderMode {RENDER_PAINTERS, RENDER_BSP}; private: // internal light color and position struct Light { Vec3 posn; double r, g, b; }; // if passed to drawing routine, this is called after drawing each // fragment class DrawCallback { public: DrawCallback() {}; virtual void drawnFragment(const Fragment& frag) = 0; virtual ~DrawCallback(); }; public: Scene(RenderMode _mode) : mode(_mode) { } // add a light to a list void addLight(Vec3 posn, QColor col, double intensity); // render scene to painter in coordinate range given // (if scale<=0 then automatic scaling) void render(Object* root, QPainter* painter, const Camera& cam, double x1, double y1, double x2, double y2, double scale); // find widget id of pixel painted by drawing scene at (x, y) long idPixel(Object* root, QPainter* painter, const Camera& cam, double x1, double y1, double x2, double y2, double scale, double scaling, int x, int y); public: // last screen matrix Mat3 screenM; private: // calculate lighting norms for triangles void calcLighting(); void calcLightingTriangle(Fragment& frag); void calcLightingLine(Fragment& frag); // compute projected coordinates void projectFragments(const Camera& cam); void doDrawing(QPainter* painter, const Mat3& screenM, double linescale, const Camera& cam, DrawCallback* callback=0); void drawPath(QPainter* painter, const Fragment& frag, QPointF pt1, QPointF pt2, QPointF pt3, double linescale, double distscale); // different rendering modes void renderPainters(const Camera& cam); void renderBSP(const Camera& cam); // render scene to painter in coordinate range given // (if scale<=0 then automatic scaling) void render_internal(Object* root, QPainter* painter, const Camera& cam, double x1, double y1, double x2, double y2, double scale, DrawCallback* callback=0); // create pens/brushes QPen lineProp2QPen(const Fragment& frag, double linescale) const; QColor surfaceProp2QColor(const Fragment& frag) const; QBrush surfaceProp2QBrush(const Fragment& frag) const; QPen surfaceProp2QPen(const Fragment& frag) const; private: RenderMode mode; FragmentVector fragments; std::vector draworder; std::vector lights; }; #endif �����������veusz-3.0.1/veusz/helpers/src/threed/scene.cpp������������������������������������������������������0000664�0001750�0001750�00000042661�13306700142�020777� 0����������������������������������������������������������������������������������������������������ustar �jss�����������������������������jss�����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright (C) 2015 Jeremy S. Sanders // Email: Jeremy Sanders // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program 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 General Public License for more details. // // You should have received a copy of the GNU General Public License along // with this program; if not, write to the Free Software Foundation, Inc., // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ///////////////////////////////////////////////////////////////////////////// #include #include #include #include #include #include #include #include #include #include #include "scene.h" #include "fragment.h" #include "bsp.h" namespace { // Make scaling matrix to move points to correct output range Mat3 makeScreenM(const FragmentVector& frags, double x1, double y1, double x2, double y2) { // get range of projected points in x and y double minx, miny, maxx, maxy; minx = miny = std::numeric_limits::infinity(); maxx = maxy = -std::numeric_limits::infinity(); for(auto const& f : frags) { for(unsigned p=0, np=f.nPointsVisible(); px2,y1->y2 double minscale = std::min((x2-x1)/(maxx-minx), (y2-y1)/(maxy-miny)); return translateM3(0.5*(x1+x2), 0.5*(y1+y2)) * scaleM3(minscale) * translateM3(-0.5*(minx+maxx), -0.5*(miny+maxy)); } // screen matrix for a fixed view Mat3 makeScreenMFixed(double x1, double y1, double x2, double y2, double scale) { double scaling = 0.5*std::min(x2-x1, y2-y1)*scale; return translateM3(0.5*(x1+x2), 0.5*(y1+y2)) * scaleM3(scaling); } template T clip(const T& val, const T& minval, const T& maxval) { return std::min(std::max(val, minval), maxval); } unsigned init_fragments_size = 512; // This is a bit of a hack to avoid problems with the painter's // algorithm. This idea is to just break up lines with a length over // the maximum into pieces smaller than maxlen. void breakLongLines(FragmentVector& fragments, double maxlen) { const double maxlen2 = maxlen*maxlen; const int size = fragments.size(); for(int ifrag=0; ifrag maxlen2) { const int nbits = int(std::sqrt(len2/maxlen2))+1; const Vec3 delta = (f.points[1]-f.points[0])*(1./nbits); // set original to be first segment f.points[1] = f.points[0]+delta; // add nbits-1 copies for next segments Fragment tempf(f); for(int ic=1; ichide) return QPen(Qt::NoPen); QColor col; if(frag.usecalccolor) col = QColor::fromRgba(frag.calccolor); else col = p->color(frag.index); QPen pen( QPen(QBrush(col), p->width*linescale, p->style) ); if(!p->dashpattern.empty()) pen.setDashPattern(p->dashpattern); return pen; } // calculate color, including reflection QColor Scene::surfaceProp2QColor(const Fragment& frag) const { if(frag.usecalccolor) return QColor::fromRgba(frag.calccolor); return frag.surfaceprop->color(frag.index); } QBrush Scene::surfaceProp2QBrush(const Fragment& frag) const { if(frag.surfaceprop==0 || frag.surfaceprop->hide) return QBrush(); else return QBrush(surfaceProp2QColor(frag)); } QPen Scene::surfaceProp2QPen(const Fragment& frag) const { if(frag.surfaceprop==0 || frag.surfaceprop->hide) return QPen(Qt::NoPen); else return QPen(surfaceProp2QColor(frag)); } void Scene::drawPath(QPainter* painter, const Fragment& frag, QPointF pt1, QPointF pt2, QPointF pt3, double linescale, double distscale) { FragmentPathParameters* pars = static_cast(frag.params); double scale = frag.pathsize*linescale; if(pars->scalepersp) scale *= distscale; // hook into drawing routine if(pars->runcallback) { pars->callback(painter, pt1, pt2, pt3, frag.index, scale, linescale); return; } if(pars->scaleline) { painter->save(); painter->translate(pt1.x(), pt1.y()); painter->scale(scale, scale); painter->drawPath(*(pars->path)); painter->restore(); } else { // scale point and relocate QPainterPath path(*(pars->path)); int elementct = path.elementCount(); for(int i=0; idrawPath(path); } } void Scene::doDrawing(QPainter* painter, const Mat3& screenM, double linescale, const Camera& cam, Scene::DrawCallback* callback) { // draw fragments LineProp const* lline = 0; SurfaceProp const* lsurf = 0; Fragment::FragmentType ltype = Fragment::FR_NONE; // distance to centre of plot const double dist0 = vec4to3(cam.viewM*Vec4(0,0,0)).rad(); QPen no_pen(Qt::NoPen); QBrush no_brush(Qt::NoBrush); painter->setPen(no_pen); painter->setBrush(no_brush); QPointF projpts[3]; for(unsigned i=0, s=draworder.size(); ihide) { if(ltype != frag.type || lsurf != frag.surfaceprop || (frag.surfaceprop!=0 && (frag.surfaceprop->hasRGBs() || frag.usecalccolor))) { lsurf = frag.surfaceprop; painter->setBrush(surfaceProp2QBrush(frag)); // use a pen if the surface is not transparent, to // fill up the gaps between triangles when there is // anti-aliasing if(frag.surfaceprop->trans == 0) painter->setPen(surfaceProp2QPen(frag)); else painter->setPen(no_pen); } painter->drawPolygon(projpts, 3); } break; case Fragment::FR_LINESEG: if(frag.lineprop != 0 && !frag.lineprop->hide) { if(ltype != frag.type || lsurf != 0) { painter->setBrush(no_brush); lsurf = 0; } if(ltype != frag.type || lline != frag.lineprop || (frag.lineprop!=0 && (frag.lineprop->hasRGBs() || frag.usecalccolor))) { lline = frag.lineprop; painter->setPen(lineProp2QPen(frag, linescale)); } painter->drawLine(projpts[0], projpts[1]); } break; case Fragment::FR_PATH: { if(ltype != frag.type || lline != frag.lineprop || ((frag.lineprop!=0 && frag.lineprop->hasRGBs()))) { lline = frag.lineprop; painter->setPen(lineProp2QPen(frag, linescale)); } if(ltype != frag.type || lsurf != frag.surfaceprop || (frag.surfaceprop!=0 && (frag.surfaceprop->hasRGBs() || frag.usecalccolor))) { lsurf = frag.surfaceprop; painter->setBrush(surfaceProp2QBrush(frag)); } // ratio of distance for size scaling const double distinvratio = dist0 / frag.points[0].rad(); drawPath(painter, frag, projpts[0], projpts[1], projpts[2], linescale, distinvratio); } break; default: break; } if(callback != 0) callback->drawnFragment(frag); ltype = frag.type; } } void Scene::calcLightingTriangle(Fragment& frag) { // Calculate triangle norm. Make sure norm points towards // the viewer @ (0,0,0) Vec3 tripos = (frag.points[0] + frag.points[1] + frag.points[2]) * (1./3.); Vec3 norm = cross(frag.points[1] - frag.points[0], frag.points[2] - frag.points[0]); if(dot(tripos, norm)<0) norm = -norm; norm.normalise(); // get color of surface const SurfaceProp* prop = frag.surfaceprop; if(prop->refl==0.) return; double r, g, b, a; if(prop->hasRGBs()) { QRgb rgb = prop-> rgbs[std::min(frag.index, unsigned(prop->rgbs.size())-1)]; r=qRed(rgb)*(1./255.); g=qGreen(rgb)*(1./255.); b=qBlue(rgb)*(1./255.); a=qAlpha(rgb)*(1./255.); } else { r=prop->r; g=prop->g; b=prop->b; a=1-prop->trans; } // add lighting contributions for(auto const& light : lights) { // Now dot vector from light source to triangle with norm Vec3 light2tri = tripos-light.posn; light2tri.normalise(); // add new lighting index double dotprod = std::max(0., dot(light2tri, norm)); double delta = prop->refl * dotprod; r += delta*light.r; g += delta*light.g; b += delta*light.b; } frag.calccolor = qRgba( clip(int(r*255), 0, 255), clip(int(g*255), 0, 255), clip(int(b*255), 0, 255), clip(int(a*255), 0, 255) ); frag.usecalccolor = 1; } void Scene::calcLightingLine(Fragment& frag) { const LineProp* prop = frag.lineprop; if(prop->refl==0.) return; double r, g, b, a; if(prop->hasRGBs()) { QRgb rgb = prop-> rgbs[std::min(frag.index, unsigned(prop->rgbs.size())-1)]; r=qRed(rgb)*(1./255.); g=qGreen(rgb)*(1./255.); b=qBlue(rgb)*(1./255.); a=qAlpha(rgb)*(1./255.); } else { r=prop->r; g=prop->g; b=prop->b; a=1-prop->trans; } Vec3 pmid = (frag.points[0]+frag.points[1])*0.5; Vec3 linevec(frag.points[1]-frag.points[0]); linevec.normalise(); // add lighting contributions for(auto const& light : lights) { Vec3 light_to_pmid(light.posn-pmid); light_to_pmid.normalise(); // this is sin of angle between line segment and light double sintheta = cross(linevec, light_to_pmid).rad(); double delta = prop->refl * sintheta; r += delta*light.r; g += delta*light.g; b += delta*light.b; } frag.calccolor = qRgba( clip(int(r*255), 0, 255), clip(int(g*255), 0, 255), clip(int(b*255), 0, 255), clip(int(a*255), 0, 255) ); frag.usecalccolor = 1; } void Scene::calcLighting() { // lighting is full on if(lights.empty()) return; for(auto &frag : fragments) { switch(frag.type) { case Fragment::FR_TRIANGLE: if(frag.surfaceprop != 0) calcLightingTriangle(frag); break; case Fragment::FR_LINESEG: if(frag.lineprop != 0) calcLightingLine(frag); break; default: break; } } } void Scene::projectFragments(const Camera& cam) { // convert 3d to 2d coordinates using the Camera for(auto& f : fragments) for(unsigned pi=0, np=f.nPointsTotal(); pi fragments[j].maxDepth(); } ); } void Scene::renderBSP(const Camera& cam) { calcLighting(); //std::cout << "\nFragment size 1 " << fragments.size() << '\n'; // This is a hack to force lines to be rendered in front of // triangles and paths to be rendered in front of lines. Suggestions // to fix this are welcome. for(auto& f : fragments) { switch(f.type) { case Fragment::FR_LINESEG: f.points[0](2) += LINE_DELTA_DEPTH; f.points[1](2) += LINE_DELTA_DEPTH; break; case Fragment::FR_PATH: f.points[0](2) += 2*LINE_DELTA_DEPTH; f.points[1](2) += 2*LINE_DELTA_DEPTH; break; default: break; } } BSPBuilder bsp(fragments, Vec3(0,0,1)); draworder = bsp.getFragmentIdxs(fragments); //std::cout << "BSP recs size " << bsp.bsp_recs.size() << '\n'; //std::cout << "Fragment size 2 " << fragments.size() << '\n'; projectFragments(cam); } void Scene::render(Object* root, QPainter* painter, const Camera& cam, double x1, double y1, double x2, double y2, double scale) { render_internal(root, painter, cam, x1, y1, x2, y2, scale); } Scene::DrawCallback::~DrawCallback() { } long Scene::idPixel(Object* root, QPainter* painter, const Camera& cam, double x1, double y1, double x2, double y2, double scale, double scaling, int x, int y) { constexpr int box = 3; // class to keep a small pixmap of the image and keep looking for // changes class IdDrawCallback : public Scene::DrawCallback { public: IdDrawCallback() : lastwidgetid(-1), pixrender(2*box+1,2*box+1) { pixrender.fill(QColor(254,254,254)); lastimage = pixrender.toImage(); } void drawnFragment(const Fragment& frag) { // has the image changed since the last time? QImage image = pixrender.toImage(); // Should only be a relatively small number of // comparisons. Alternatively, it could use a checksum. if(image != lastimage) { if(frag.object != 0) lastwidgetid = frag.object->widgetid; lastimage = image; } } long lastwidgetid; QPixmap pixrender; QImage lastimage; }; IdDrawCallback callback; painter->begin(&callback.pixrender); painter->scale(scaling, scaling); painter->setWindow(x-box, y-box, box*2+1, box*2+1); render_internal(root, painter, cam, x1, y1, x2, y2, scale, &callback); painter->end(); return callback.lastwidgetid; } void Scene::render_internal(Object* root, QPainter* painter, const Camera& cam, double x1, double y1, double x2, double y2, double scale, Scene::DrawCallback* callback) { fragments.reserve(init_fragments_size); fragments.resize(0); draworder.resize(0); // get fragments for whole scene root->getFragments(cam.perspM, cam.viewM, fragments); switch(mode) { case RENDER_BSP: renderBSP(cam); break; case RENDER_PAINTERS: renderPainters(cam); break; default: break; } // how to transform projected points to screen (screenM is member) screenM = scale<=0 ? makeScreenM(fragments, x1, y1, x2, y2) : makeScreenMFixed(x1, y1, x2, y2, scale); double linescale = std::max(std::abs(x2-x1), std::abs(y2-y1)) * (1./1000); // finally draw items doDrawing(painter, screenM, linescale, cam, callback); // don't decrease size of fragments unnecessarily, unless it is large init_fragments_size = fragments.size(); if(init_fragments_size > 65536) init_fragments_size /= 2; } �������������������������������������������������������������������������������veusz-3.0.1/veusz/helpers/src/threed/clipcontainer.h������������������������������������������������0000664�0001750�0001750�00000003027�13302252613�022173� 0����������������������������������������������������������������������������������������������������ustar �jss�����������������������������jss�����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// -*-c++-*- // Copyright (C) 2015 Jeremy S. Sanders // Email: Jeremy Sanders // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program 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 General Public License for more details. // // You should have received a copy of the GNU General Public License along // with this program; if not, write to the Free Software Foundation, Inc., // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ///////////////////////////////////////////////////////////////////////////// #ifndef CLIPCONTAINER_H #define CLIPCONTAINER_H #include "objects.h" #include "fragment.h" // container which clips children in a 3D box class ClipContainer : public ObjectContainer { public: ClipContainer(Vec3 _minpt, Vec3 _maxpt) : ObjectContainer(), minpt(_minpt), maxpt(_maxpt) { } void getFragments(const Mat4& perspM, const Mat4& outerM, FragmentVector& v); bool pointInBounds(Vec3 pt) const { return (pt(0) >= minpt(0) && pt(1) >= minpt(1) && pt(2) >= minpt(2) && pt(0) <= maxpt(0) && pt(1) <= maxpt(1) && pt(2) <= maxpt(2)); } public: Vec3 minpt, maxpt; }; #endif ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������veusz-3.0.1/veusz/helpers/src/threed/objects.cpp����������������������������������������������������0000664�0001750�0001750�00000066614�13302276541�021346� 0����������������������������������������������������������������������������������������������������ustar �jss�����������������������������jss�����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright (C) 2015 Jeremy S. Sanders // Email: Jeremy Sanders // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program 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 General Public License for more details. // // You should have received a copy of the GNU General Public License along // with this program; if not, write to the Free Software Foundation, Inc., // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ///////////////////////////////////////////////////////////////////////////// #include #include #include #include #include "objects.h" #include "twod.h" Object::~Object() { } void Object::getFragments(const Mat4& perspM, const Mat4& outerM, FragmentVector& v) { } void Object::assignWidgetId(long id) { widgetid = id; } // Triangle /////////// void Triangle::getFragments(const Mat4& perspM, const Mat4& outerM, FragmentVector& v) { Fragment f; f.type = Fragment::FR_TRIANGLE; f.surfaceprop = surfaceprop.ptr(); f.lineprop = 0; for(unsigned i=0; i<3; ++i) f.points[i] = vec4to3(outerM*vec3to4(points[i])); f.object = this; v.push_back(f); } // PolyLine /////////// void PolyLine::addPoints(const ValVector& x, const ValVector& y, const ValVector& z) { unsigned size = std::min(x.size(), std::min(y.size(), z.size())); points.reserve(points.size()+size); for(unsigned i=0; i 0 && (f.points[0]+f.points[1]).isfinite()) v.push_back(f); } } // LineSegments /////////////// LineSegments::LineSegments(const ValVector& x1, const ValVector& y1, const ValVector& z1, const ValVector& x2, const ValVector& y2, const ValVector& z2, const LineProp* prop) : Object(), lineprop(prop) { unsigned size = std::min( std::min(x1.size(), std::min(y1.size(), z1.size())), std::min(x2.size(), std::min(y2.size(), z2.size())) ); points.reserve(size*2); for(unsigned i=0; i 0 && (fl.points[0]+fl.points[1]).isfinite()) v.push_back(fl); ++fl.index; } } } } void Mesh::getSurfaceFragments(const Mat4& perspM, const Mat4& outerM, FragmentVector& v) { if(surfaceprop.ptr() == 0) return; unsigned vidx_h, vidx_1, vidx_2; getVecIdxs(vidx_h, vidx_1, vidx_2); Fragment fs; fs.type = Fragment::FR_TRIANGLE; fs.surfaceprop = surfaceprop.ptr(); fs.lineprop = 0; fs.object = this; // for each grid point we alternatively draw one of two sets of // triangles, to make a symmetric diamond pattern, which looks // better when striped static const unsigned tidxs[2][2][3] = { {{0,1,2},{3,1,2}}, {{1,0,3},{2,0,3}} }; const unsigned n1 = pos1.size(); const unsigned n2 = pos2.size(); Vec4 p[4]; Vec3 pproj[4]; p[0](3) = p[1](3) = p[2](3) = p[3](3) = 1; for(unsigned i1=0; (i1+1) data; }; }; void DataMesh::getFragments(const Mat4& perspM, const Mat4& outerM, FragmentVector& v) { // check indices bool found[3] = {0, 0, 0}; unsigned idxs[3] = {idxval, idxedge1, idxedge2}; for(unsigned i=0; i<3; ++i) if(idxs[i]<=2) found[idxs[i]]=1; if(!found[0] || !found[1] || !found[2]) { std::fprintf(stderr, "DataMesh: invalid indices\n"); return; } // check that data sizes agree if( (int(edges1.size())-1)*(int(edges2.size())-1) != int(vals.size()) ) { std::fprintf(stderr, "DataMesh: invalid size\n"); return; } // nothing to draw if( lineprop.ptr()==0 && surfaceprop.ptr()==0 ) return; // used to draw the grid and surface Fragment ft; ft.type = Fragment::FR_TRIANGLE; ft.surfaceprop = surfaceprop.ptr(); ft.lineprop = 0; ft.object = this; Fragment fl; fl.type = Fragment::FR_LINESEG; fl.surfaceprop = 0; fl.lineprop = lineprop.ptr(); fl.object = this; // these are the corner indices used for drawing low and high resolution surfaces static const unsigned trilist_highres[8][3] = { {8,0,1},{8,1,2},{8,2,3},{8,3,4},{8,4,5},{8,5,6},{8,6,7},{8,7,0}}; // there are two low resolution triangle lists, as we want to // alternate them in each grid point to make a symmetric pattern static const unsigned trilist_lowres1[2][3] = {{0,2,4},{0,6,4}}; static const unsigned trilist_lowres2[2][3] = {{2,0,6},{2,4,6}}; static const unsigned linelist_lowres[4][2] = { {0,2},{0,6},{4,2},{4,6}}; static const unsigned linelist_highres[8][2] = { {0,1},{1,2},{2,3},{3,4},{4,5},{5,6},{6,7},{7,0}}; // This is to avoid double-drawing lines. Lines are given an x/yindex to say which // side of the grid cell is being drawn and a lineidx which is unique for sub-lines // xidx, yidx, lineidx static const unsigned linecell_lowres[4][3] = { {0,0,0}, {0,0,1}, {0,1,0}, {1,0,1} }; static const unsigned linecell_highres[8][3] = { {0,0,0}, {0,0,1}, {1,0,2}, {1,0,3}, {0,1,1}, {0,1,0}, {0,0,3}, {0,0,2} }; // whether lines are vertical or horizontal static const unsigned dirn_lowres[4] = {0, 1, 0, 1}; static const unsigned dirn_highres[8] = {1, 1, 0, 0, 1, 1, 0, 0}; // select list above depending on high or low resolution const unsigned (*lines)[2] = highres ? linelist_highres : linelist_lowres; const unsigned (*linecells)[3] = highres ? linecell_highres : linecell_lowres; const unsigned *linedirn = highres ? dirn_highres : dirn_lowres; const unsigned ntris = highres ? 8 : 2; const unsigned nlines = highres ? 8 : 4; // store corners and neighbouring cell values double neigh[9]; Vec4 corners[9]; // 4d corners for(unsigned i=0; i<9; ++i) corners[i](3) = 1; Vec3 corners3[9]; // 3d version of above // don't draw lines twice by keeping track if which edges of which // cells have been drawn already LineCellTracker linetracker(edges1.size(), edges2.size()); const int n1=int(edges1.size())-1; const int n2=int(edges2.size())-1; // loop over 2d array for(int i1=0; i1hide) { ft.index = i; // iterate over triangles in cube for(int tri=0; tri<12; ++tri) { // points for triangle for(int pt=0; pt<3; ++pt) { ft.points[pt] = vec4to3(outerM* Vec4(x[triidx[tri][pt][0]], y[triidx[tri][pt][1]], z[triidx[tri][pt][2]])); } if(ft.isVisible()) v.push_back(ft); } } if(fl.lineprop !=0 && !fl.lineprop->hide) { fl.index = i; // iterate over edges for(int edge=0; edge<12; ++edge) { // points for line for(int pt=0; pt<2; ++pt) { fl.points[pt] = vec4to3(outerM* Vec4(x[edgeidx[edge][pt][0]], y[edgeidx[edge][pt][1]], z[edgeidx[edge][pt][2]])); } if(fl.isVisible()) v.push_back(fl); } } } // loop over cuboids } // Points ///////// void Points::getFragments(const Mat4& perspM, const Mat4& outerM, FragmentVector& v) { fragparams.path = &path; fragparams.scaleline = scaleline; fragparams.scalepersp = scalepersp; fragparams.runcallback = false; Fragment fp; fp.type = Fragment::FR_PATH; fp.object = this; fp.params = &fragparams; fp.surfaceprop = surfacefill.ptr(); fp.lineprop = lineedge.ptr(); fp.pathsize = 1; unsigned size = std::min(x.size(), std::min(y.size(), z.size())); bool hassizes = !sizes.empty(); if(hassizes) size = std::min(size, unsigned(sizes.size())); for(unsigned i=0; idraw(painter, pt1, pt2, pt3, index, scale, linescale); } void Text::getFragments(const Mat4& perspM, const Mat4& outerM, FragmentVector& v) { Fragment fp; fp.type = Fragment::FR_PATH; fp.object = this; fp.params = &fragparams; fp.surfaceprop = 0; fp.lineprop = 0; fp.pathsize = 1; unsigned numitems = std::min(pos1.size(), pos2.size()) / 3; for(unsigned i=0; i torigin(2)) Triangle::getFragments(perspM, outerM, v); } // ObjectContainer ////////////////// ObjectContainer::~ObjectContainer() { for(unsigned i=0, s=objects.size(); igetFragments(perspM, totM, v); } void ObjectContainer::assignWidgetId(long id) { for(auto &object : objects) object->assignWidgetId(id); } // FacingContainer void FacingContainer::getFragments(const Mat4& perspM, const Mat4& outerM, FragmentVector& v) { const Vec3 origin = vec4to3(outerM*Vec4(0,0,0,1)); const Vec3 tnorm = vec4to3(outerM*vec3to4(norm)); // norm points towards +z if(tnorm(2) > origin(2)) ObjectContainer::getFragments(perspM, outerM, v); } // AxisLabels AxisLabels::AxisLabels(const Vec3& _box1, const Vec3& _box2, const ValVector& _tickfracs, double _labelfrac) : box1(_box1), box2(_box2), tickfracs(_tickfracs), labelfrac(_labelfrac) { } void AxisLabels::addAxisChoice(const Vec3& _start, const Vec3& _end) { starts.push_back(_start); ends.push_back(_end); } void AxisLabels::PathParameters::callback(QPainter* painter, QPointF pt, QPointF ax1, QPointF ax2, int index, double scale, double linescale) { painter->save(); tl->drawLabel(painter, index, pt, ax1, ax2, axangle); painter->restore(); } void AxisLabels::drawLabel(QPainter* painter, int index, QPointF pt, QPointF ax1, QPointF ax2, double axangle) { } void AxisLabels::getFragments(const Mat4& perspM, const Mat4& outerM, FragmentVector& fragvec) { // algorithm: // Take possible axis positions // Find those which do not overlap on the screen with body of cube // - make cube faces // - look for endpoints which are somewhere on a face (not edge) // Prefer those axes to bottom left // Determine from faces, which side of the axis is inside and which outside // Setup drawLabel for the right axis const unsigned numentries = std::min(starts.size(), ends.size()); if(numentries == 0) return; const Vec3 boxpts[2] = {box1, box2}; // compute corners of cube in projected coordinates // (0,0,0),(0,0,1),(0,1,0),(0,1,1),(1,0,0),(1,0,1),(1,1,0),(1,1,1) Vec3 proj_corners[8]; for(unsigned i0=0; i0<2; ++i0) for(unsigned i1=0; i1<2; ++i1) for(unsigned i2=0; i2<2; ++i2) { const Vec3 pt(boxpts[i0](0), boxpts[i1](1), boxpts[i2](2)); proj_corners[i2+i1*2+i0*4] = calcProjVec(perspM, outerM*vec3to4(pt)); } // point indices for faces of cube static const unsigned faces[6][4] = { {0,1,3,2} /* x==0 */, {4,5,7,6} /* x==1 */, {0,1,5,4} /* y==0 */, {2,3,7,6} /* y==1 */, {0,4,6,2} /* z==0 */, {1,5,7,3} /* z==1 */ }; // scene and projected coords of axis ends std::vector proj_starts, proj_ends; for(unsigned axis=0; axis!=numentries; ++axis) { // ends shifted slightly inwards for overlap checks // (fixes issues in ends exactly overlapping with // face edges causing overlap to fail) const Vec3 delta(ends[axis]-starts[axis]); const Vec4 start_in = vec3to4(starts[axis]+delta*0.001); const Vec4 end_in = vec3to4(starts[axis]+delta*0.999); proj_starts.push_back(calcProjVec(perspM, outerM*start_in)); proj_ends.push_back(calcProjVec(perspM, outerM*end_in)); } // find axes which don't overlap with faces in 2D std::vector axchoices; std::vector facepts; for(unsigned axis=0; axis!=numentries; ++axis) { const Vec2 linept1 = vec3to2(proj_starts[axis]); const Vec2 linept2 = vec3to2(proj_ends[axis]); bool overlap=0; // does this overlap with any faces? for(unsigned face=0; face<6 && !overlap; ++face) { facepts.resize(0); for(unsigned i=0; i<4; ++i) facepts.push_back(vec3to2(proj_corners[faces[face][i]])); twodPolyMakeClockwise(&facepts); if( twodLineIntersectPolygon(linept1, linept2, facepts) ) overlap=1; } if(!overlap) axchoices.push_back(axis); } // if none are suitable, prefer all if(axchoices.empty()) { for(unsigned axis=0; axis!=numentries; ++axis) axchoices.push_back(axis); } // get projected cube centre const Vec3 proj_cent(calcProjVec(perspM,outerM*vec3to4((box1+box2)*0.5))); // currently-prefered axis number unsigned bestaxis = 0; // axes are scored to prefer front, bottom, left axes int bestscore = -1; for(unsigned choice : axchoices) { const Vec3 av((proj_starts[choice]+proj_ends[choice])*0.5); // score is weighted towards front, then bottom, then left const int score = ((av(0) <= proj_cent(0))*10 + (av(1) > proj_cent(1))*11 + (av(2) < proj_cent(2))*12 ); if(score > bestscore) { bestscore = score; bestaxis = choice; } } // initialise PathParameters with best axis fragparams.tl = this; fragparams.path = 0; fragparams.scaleline = false; fragparams.scalepersp = false; fragparams.runcallback = true; fragparams.axangle = (180/PI) * std::atan2 ((proj_starts[bestaxis](1)+proj_ends[bestaxis](1))*0.5 - proj_cent(1), (proj_starts[bestaxis](0)+proj_ends[bestaxis](0))*0.5 - proj_cent(0)); const Vec3 axstart = starts[bestaxis]; const Vec3 delta = ends[bestaxis]-axstart; // scene coordinates of axis ends const Vec3 axstart_scene = vec4to3(outerM*vec3to4(axstart)); const Vec3 axend_scene = vec4to3(outerM*vec3to4(ends[bestaxis])); // ok, now we add the number fragments for the best choice of axis Fragment fp; fp.type = Fragment::FR_PATH; fp.object = this; fp.params = &fragparams; fp.surfaceprop = 0; fp.lineprop = 0; fp.pathsize = 1; fp.points[1] = axstart_scene; fp.points[2] = axend_scene; // add tick labels for(unsigned i=0; i= 0) { fp.index = -1; const Vec3 pt = axstart + delta*labelfrac; fp.points[0] = vec4to3(outerM*vec3to4(pt)); fragvec.push_back(fp); } } ��������������������������������������������������������������������������������������������������������������������veusz-3.0.1/veusz/helpers/src/threed/threed.sip�����������������������������������������������������0000664�0001750�0001750�00000027553�13306763512�021203� 0����������������������������������������������������������������������������������������������������ustar �jss�����������������������������jss�����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// -*- mode: C++; -*- // Copyright (C) 2014 Jeremy S. Sanders // Email: Jeremy Sanders // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program 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 General Public License for more details. // // You should have received a copy of the GNU General Public License along // with this program; if not, write to the Free Software Foundation, Inc., // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ///////////////////////////////////////////////////////////////////////////// %Module(name=threed) %Import(name=QtCore/QtCoremod.sip) %Import(name=QtGui/QtGuimod.sip) %ModuleHeaderCode #include %End %PostInitialisationCode doNumpyInitPackage(); %End struct Vec4 { %TypeHeaderCode #include %End Vec4(); Vec4(double, double, double, double); void set(unsigned, double); %MethodCode if(a0 > 3) { sipIsErr = 1; PyErr_SetString(PyExc_ValueError, "Index should be 0<=i<=3"); } else sipCpp->operator()(a0) = a1; %End double get(unsigned) const; %MethodCode if(a0 > 3) { sipIsErr = 1; PyErr_SetString(PyExc_ValueError, "Index should be 0<=i<=3"); } else sipRes = sipCpp->operator()(a0); %End void operator*=(double); Vec4 operator+(const Vec4&) const; Vec4 operator-(const Vec4&) const; Vec4 operator*(double) const; bool operator==(const Vec4&) const; bool operator!=(const Vec4&) const; double rad2() const; double rad() const; void normalise(); }; struct Vec3 { %TypeHeaderCode #include %End Vec3(); Vec3(double, double, double); void set(unsigned, double); %MethodCode if(a0 > 2) { sipIsErr = 1; PyErr_SetString(PyExc_ValueError, "Index should be 0<=i<=2"); } else sipCpp->operator()(a0) = a1; %End double get(unsigned) const; %MethodCode if(a0 > 2) { sipIsErr = 1; PyErr_SetString(PyExc_ValueError, "Index should be 0<=i<=2"); } else sipRes = sipCpp->operator()(a0); %End void operator*=(double); Vec3 operator+(const Vec3&) const; Vec3 operator-(const Vec3&) const; Vec3 operator*(double) const; bool operator==(const Vec3&) const; bool operator!=(const Vec3&) const; double rad2() const; double rad() const; void normalise(); }; struct Vec2 { %TypeHeaderCode #include %End Vec2(); Vec2(double, double); void set(unsigned, double); %MethodCode if(a0 > 1) { sipIsErr = 1; PyErr_SetString(PyExc_ValueError, "Index should be 0<=i<=1"); } else sipCpp->operator()(a0) = a1; %End double get(unsigned) const; %MethodCode if(a0 > 1) { sipIsErr = 1; PyErr_SetString(PyExc_ValueError, "Index should be 0<=i<=1"); } else sipRes = sipCpp->operator()(a0); %End void operator*=(double); Vec2 operator+(const Vec2&) const; Vec2 operator-(const Vec2&) const; Vec2 operator*(double) const; bool operator==(const Vec2&) const; bool operator!=(const Vec2&) const; double rad2() const; double rad() const; void normalise(); }; struct Mat4 { %TypeHeaderCode #include %End Mat4(); void set(unsigned, unsigned, double); %MethodCode if(a0 > 3 || a1 > 3) { sipIsErr = 1; PyErr_SetString(PyExc_ValueError, "Index should be 0<=i<=3"); } else sipCpp->operator()(a0, a1) = a2; %End double get(unsigned, unsigned) const; %MethodCode if(a0 > 3 || a1 > 3) { sipIsErr = 1; PyErr_SetString(PyExc_ValueError, "Index should be 0<=i<=3"); } else sipRes = sipCpp->operator()(a0, a1); %End Mat4 operator*(const Mat4& o) const; Vec4 operator*(const Vec4& v) const; Mat4 transpose() const; }; Vec4 operator*(const Vec4& v, const Mat4& o); Mat4 identityM4(); Mat4 rotateM4(double angle, Vec3 vec); Mat4 rotate3M4(double ax, double ay, double az); Mat4 translationM4(Vec3 vec); Mat4 scaleM4(Vec3 scalevec); struct Mat3 { %TypeHeaderCode #include %End Mat3(); void set(unsigned, unsigned, double); %MethodCode if(a0 > 2 || a1 > 2) { sipIsErr = 1; PyErr_SetString(PyExc_ValueError, "Index should be 0<=i<=2"); } else sipCpp->operator()(a0, a1) = a2; %End double get(unsigned, unsigned) const; %MethodCode if(a0 > 2 || a1 > 2) { sipIsErr = 1; PyErr_SetString(PyExc_ValueError, "Index should be 0<=i<=2"); } else sipRes = sipCpp->operator()(a0, a1); %End Mat3 operator*(const Mat3& o) const; Vec3 operator*(const Vec3& v) const; Mat3 transpose() const; }; Vec3 operator*(const Vec3& v, const Mat3& o); Mat3 identityM3(); // this is actually std::vector class ValVector { %TypeHeaderCode #include %End public: ValVector(); ValVector(SIP_PYOBJECT obj); %MethodCode try { sipCpp = new ValVector(numpyToValVector(a0)); } catch(const char *msg) { sipIsErr = 1; PyErr_SetString(PyExc_TypeError, msg); } %End void push_back(double d); double operator[](unsigned i) const; %MethodCode if(a0 >= sipCpp->size()) { sipIsErr = 1; PyErr_SetString(PyExc_ValueError, "Index out of range"); } else sipRes = sipCpp->operator[](a0); %End unsigned size() const; bool empty() const; }; Vec3 calcProjVec(const Mat4& projM, const Vec3& v); Vec3 calcProjVec(const Mat4& projM, const Vec4& v); Vec2 projVecToScreen(const Mat3& screenM, const Vec3& vec); ///////////////////////////////////////////////// // Properties struct SurfaceProp /NoDefaultCtors/ { %TypeHeaderCode #include %End SurfaceProp(double r=0.5, double g=0.5, double b=0.5, double refl=0.5, double trans=0, bool hide=0) /KeywordArgs="All"/; void setRGBs(const QImage& img); double r; double g; double b; double refl; double trans; bool hide; }; struct LineProp /NoDefaultCtors/ { %TypeHeaderCode #include %End LineProp(double r=0, double g=0, double b=0, double trans=0, double refl=0, double width=1, bool hide=0, Qt::PenStyle style=Qt::SolidLine) /KeywordArgs="All"/; void setRGBs(const QImage& img); void setDashPattern(const ValVector& vec); double r; double g; double b; double trans; double refl; double width; bool hide; }; /////////////////////////////////////////////////////////////// // Objects class Object /NoDefaultCtors/ { %TypeHeaderCode #include %End public: virtual ~Object(); virtual void assignWidgetId(long id); long widgetid; }; class Triangle : public Object /NoDefaultCtors/ { %TypeHeaderCode #include %End public: Triangle(const Vec3&, const Vec3&, const Vec3&, const SurfaceProp* surfaceprop /Transfer/); }; class PolyLine : public Object /NoDefaultCtors/ { %TypeHeaderCode #include %End public: PolyLine(const LineProp* prop /Transfer/); void addPoint(const Vec3& v); void addPoints(const ValVector& x, const ValVector& y, const ValVector& z); }; class LineSegments : public Object { %TypeHeaderCode #include %End public: LineSegments(const ValVector& x1, const ValVector& y1, const ValVector& z1, const ValVector& x2, const ValVector& y2, const ValVector& z2, const LineProp* prop /Transfer/); LineSegments(const ValVector& pts1, const ValVector& pts2, const LineProp* prop /Transfer/); }; class Mesh : public Object /NoDefaultCtors/ { %TypeHeaderCode #include %End public: enum Direction {X_DIRN, Y_DIRN, Z_DIRN}; Mesh(const ValVector& pos1, const ValVector& pos2, const ValVector& heights, Direction dir, const LineProp* lprop /Transfer/, const SurfaceProp* sprop /Transfer/, bool hidehorzline=0, bool hidevertline=0); }; class DataMesh : public Object /NoDefaultCtors/ { %TypeHeaderCode #include %End public: DataMesh(const ValVector& edges1, const ValVector& edges2, const ValVector& vals, unsigned idxval, unsigned idxedge1, unsigned idxedge2, bool highres, const LineProp* lprop /Transfer/, const SurfaceProp* sprop /Transfer/, bool hidehorzline=0, bool hidevertline=0); }; class MultiCuboid : public Object { %TypeHeaderCode #include %End public: MultiCuboid(const ValVector& _xmin, const ValVector& _xmax, const ValVector& _ymin, const ValVector& _ymax, const ValVector& _zmin, const ValVector& _zmax, const LineProp* lprop /Transfer/, const SurfaceProp* sprop /Transfer/); }; class Points : public Object /NoDefaultCtors/ { %TypeHeaderCode #include %End public: Points(const ValVector& px, const ValVector& py, const ValVector& pz, QPainterPath pp, const LineProp* pointedge /Transfer/, const SurfaceProp* pointfill /Transfer/); void setSizes(const ValVector& _sizes); bool scaleline; bool scalepersp; }; // a "text" class which calls back draw() when drawing is requested class Text : public Object { %TypeHeaderCode #include %End public: // pos1 and pos2 contain a list of x,y,z values Text(const ValVector& _pos1, const ValVector& _pos2); virtual void draw(QPainter* painter, QPointF pt1, QPointF pt2, QPointF pt3, unsigned index, double scale, double linescale); }; class ObjectContainer : public Object { %TypeHeaderCode #include %End public: ObjectContainer(); void addObject(Object* obj /Transfer/); void assignWidgetId(long id); Mat4 objM; }; class TriangleFacing : public Triangle { %TypeHeaderCode #include %End public: TriangleFacing(const Vec3& a, const Vec3& b, const Vec3& c, const SurfaceProp* prop /Transfer/); }; class FacingContainer : public ObjectContainer { %TypeHeaderCode #include %End public: FacingContainer(Vec3 _norm); Vec3 norm; }; class AxisLabels : public Object { %TypeHeaderCode #include %End public: AxisLabels(const Vec3& _box1, const Vec3& _box2, const ValVector& _tickfracs, double _labelfrac); void addAxisChoice(const Vec3& start, const Vec3& end); virtual void drawLabel(QPainter* painter, int index, QPointF pt, QPointF ax1, QPointF ax2, double axangle); }; class ClipContainer : public ObjectContainer { %TypeHeaderCode #include %End public: ClipContainer(Vec3 minpt, Vec3 maxpt); }; //////////////////////////////////////////////////////////////// // Camera class Camera { %TypeHeaderCode #include %End public: Camera(); void setPointing(const Vec3 &eye, const Vec3 &target, const Vec3 &up); void setPerspective(double fov_degrees=45, double znear=0.1, double zfar=100.); public: Mat4 viewM; // view matrix Mat4 perspM; // perspective matrix Mat4 combM; // combined matrix Vec3 eye; // location of eye }; //////////////////////////////////////////////////////////////// // Scene class Scene { %TypeHeaderCode #include %End public: enum RenderMode {RENDER_PAINTERS, RENDER_BSP}; public: Scene(RenderMode mode); void addLight(Vec3 posn, QColor col, double intensity); void render(Object* root, QPainter* painter, const Camera& cam, double x1, double y1, double x2, double y2, double scale); long idPixel(Object* root, QPainter* painter, const Camera& cam, double x1, double y1, double x2, double y2, double scale, double scaling, int x, int y); public: Mat3 screenM; }; �����������������������������������������������������������������������������������������������������������������������������������������������������veusz-3.0.1/veusz/helpers/src/threed/numpy_helpers.cpp����������������������������������������������0000664�0001750�0001750�00000003501�13302252613�022563� 0����������������������������������������������������������������������������������������������������ustar �jss�����������������������������jss�����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright (C) 2015 Jeremy S. Sanders // Email: Jeremy Sanders // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program 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 General Public License for more details. // // You should have received a copy of the GNU General Public License along // with this program; if not, write to the Free Software Foundation, Inc., // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ///////////////////////////////////////////////////////////////////////////// #include "numpy_helpers.h" #define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION #include "numpy/arrayobject.h" namespace { // python3 numpy import_array is a macro with a return (how stupid), // so we have to wrap it up to get it to portably compile #if PY_MAJOR_VERSION >= 3 void* doImport() { import_array(); return 0; } #else void doImport() { import_array(); } #endif } void doNumpyInitPackage() { doImport(); } ValVector numpyToValVector(PyObject* obj) { PyArrayObject *arrayobj = (PyArrayObject*) PyArray_ContiguousFromAny(obj, NPY_DOUBLE, 1, 1); if(arrayobj == NULL) { throw "Cannot covert item to 1D numpy array"; } const double* data = (double*)PyArray_DATA(arrayobj); unsigned dim = PyArray_DIMS(arrayobj)[0]; ValVector out; out.reserve(dim); for(unsigned i=0; i // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program 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 General Public License for more details. // // You should have received a copy of the GNU General Public License along // with this program; if not, write to the Free Software Foundation, Inc., // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ///////////////////////////////////////////////////////////////////////////// #ifndef NUMPY_HELPERS_H #define NUMPY_HELPERS_H #include "Python.h" #include "mmaths.h" void doNumpyInitPackage(); ValVector numpyToValVector(PyObject* obj); #endif ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������veusz-3.0.1/veusz/helpers/src/threed/fragment.h�����������������������������������������������������0000664�0001750�0001750�00000011723�13302252613�021146� 0����������������������������������������������������������������������������������������������������ustar �jss�����������������������������jss�����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// -*-c++-*- // Copyright (C) 2015 Jeremy S. Sanders // Email: Jeremy Sanders // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program 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 General Public License for more details. // // You should have received a copy of the GNU General Public License along // with this program; if not, write to the Free Software Foundation, Inc., // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ///////////////////////////////////////////////////////////////////////////// #ifndef FRAGMENT_H #define FRAGMENT_H #include #include #include #include #include #include "properties.h" #include "mmaths.h" #define LINE_DELTA_DEPTH 1e-6 // from objects.h class Object; // this is passed to the renderer to get the parameters for painting // the path struct FragmentParameters { virtual ~FragmentParameters(); }; struct FragmentPathParameters : public FragmentParameters { QPainterPath* path; bool scaleline; bool scalepersp; bool runcallback; // optional callback function if runcallback is set virtual void callback(QPainter* painter, QPointF pt1, QPointF pt2, QPointF pt3, int index, double scale, double linescale); }; // created by drawing Objects to draw to screen struct Fragment { enum FragmentType {FR_NONE, FR_TRIANGLE, FR_LINESEG, FR_PATH}; // 3D points Vec3 points[3]; // projected points associated with fragment Vec3 proj[3]; // pointer to object, to avoid self-comparison. Object* object; // optional pointer to a parameters object FragmentParameters* params; // drawing style SurfaceProp const* surfaceprop; LineProp const* lineprop; // for path float pathsize; // calculated color from lighting QRgb calccolor; // number of times this has been split unsigned splitcount; // passed to path plotting or as index to color bar unsigned index; // type of fragment FragmentType type; // use calculated color bool usecalccolor; // zero on creation Fragment() : object(0), params(0), surfaceprop(0), lineprop(0), pathsize(0), calccolor(0), splitcount(0), index(0), type(FR_NONE), usecalccolor(0) { } // number of (visible) points used by fragment type unsigned nPointsVisible() const { switch(type) { case FR_TRIANGLE: return 3; case FR_LINESEG: return 2; case FR_PATH: return 1; default: return 0; } } // number of points used by fragment, including hidden ones // FR_PATH has an optional 2nd point for keeping track of a baseline unsigned nPointsTotal() const { switch(type) { case FR_TRIANGLE: return 3; case FR_LINESEG: return 2; case FR_PATH: return 3; default: return 0; } } double minDepth() const { switch(type) { case FR_TRIANGLE: return std::min(proj[0](2), std::min(proj[1](2), proj[2](2))); case FR_LINESEG: return std::min(proj[0](2), proj[1](2)) - LINE_DELTA_DEPTH; case FR_PATH: return proj[0](2) - 2*LINE_DELTA_DEPTH; default: return std::numeric_limits::infinity(); } } double maxDepth() const { switch(type) { case FR_TRIANGLE: return std::max(proj[0](2), std::max(proj[1](2), proj[2](2))); case FR_LINESEG: return std::max(proj[0](2), proj[1](2)) - LINE_DELTA_DEPTH; case FR_PATH: return proj[0](2) - 2*LINE_DELTA_DEPTH; default: return std::numeric_limits::infinity(); } } double meanDepth() const { switch(type) { case FR_TRIANGLE: return (proj[0](2) + proj[1](2) + proj[2](2))*(1/3.f); case FR_LINESEG: return (proj[0](2) + proj[1](2))*0.5f - LINE_DELTA_DEPTH; case FR_PATH: return proj[0](2) - 2*LINE_DELTA_DEPTH; default: return std::numeric_limits::infinity(); } } // recalculate projected coordinates void updateProjCoords(const Mat4& projM) { unsigned n=nPointsTotal(); for(unsigned i=0; icolor(index).alpha() > 0) vis = true; } if((type==FR_LINESEG || type==FR_PATH) && lineprop!=0) { if(lineprop->color(index).alpha() > 0) vis = true; } return vis; } }; typedef std::vector FragmentVector; #endif ���������������������������������������������veusz-3.0.1/veusz/helpers/src/threed/twod.cpp�������������������������������������������������������0000664�0001750�0001750�00000012547�13302252613�020660� 0����������������������������������������������������������������������������������������������������ustar �jss�����������������������������jss�����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright (C) 2015 Jeremy S. Sanders // Email: Jeremy Sanders // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program 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 General Public License for more details. // // You should have received a copy of the GNU General Public License along // with this program; if not, write to the Free Software Foundation, Inc., // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ///////////////////////////////////////////////////////////////////////////// #include #include #include "twod.h" #define EPS 1e-8 ISect twodLineIntersect(Vec2 p1, Vec2 p2, Vec2 q1, Vec2 q2, Vec2* posn, Vec2* posn2) { Vec2 dp = p2-p1; Vec2 dq = q2-q1; Vec2 dpq = p1-q1; double denom = cross(dp, dq); // parallel vectors or points below if(std::abs(denom) < EPS) { if( std::abs(cross(dp, dpq)) > EPS || std::abs(cross(dq, dpq)) > EPS ) return LINE_NOOVERLAP; // colinear segments - do they overlap? double u0, u1; Vec2 dpq2 = p2-q1; if(std::abs(dq(0)) > std::abs(dq(1))) { u0 = dpq(0)*(1/dq(0)); u1 = dpq2(0)*(1/dq(0)); } else { u0 = dpq(1)*(1/dq(1)); u1 = dpq2(1)*(1/dq(1)); } if(u0 > u1) std::swap(u0, u1); if( u0>(1+EPS) || u1<-EPS ) return LINE_NOOVERLAP; u0 = std::max(u0, 0.); u1 = std::min(u1, 1.); if(posn != 0) *posn = q1 + dq*u0; if( std::abs(u0-u1) < EPS ) return LINE_CROSS; if(posn2 != 0) *posn2 = q1 + dq*u1; return LINE_OVERLAP; } double s = cross(dq, dpq)*(1/denom); if(s < -EPS || s > (1+EPS)) return LINE_NOOVERLAP; double t = cross(dp, dpq)*(1/denom); if(t < -EPS || t > (1+EPS)) return LINE_NOOVERLAP; if(posn != 0) *posn = p1 + dp * std::max(std::min(s, 1.), 0.); return LINE_CROSS; } namespace { // is a to the left of p1->p2? inline bool ptInside(Vec2 p, Vec2 cp0, Vec2 cp1) { return (cp1(0)-cp0(0))*(p(1)-cp0(1)) > (cp1(1)-cp0(1))*(p(0)-cp0(0)); }; // version of above with tolerence of points on line // 0: on line, -1: outside, +1: inside inline int ptInsideTol(Vec2 p, Vec2 cp0, Vec2 cp1) { double del = (cp1(0)-cp0(0))*(p(1)-cp0(1)) - (cp1(1)-cp0(1))*(p(0)-cp0(0)); return del>EPS ? 1 : del<-EPS ? -1 : 0; } // return whether lines intersect and return intersection point bool SHlineIntersection(Vec2 a0, Vec2 a1, Vec2 b0, Vec2 b1, Vec2* res) { Vec2 da = a0-a1; Vec2 db = b0-b1; double n1 = cross(a0, a1); double n2 = cross(b0, b1); double denom = cross(da, db); if(denom == 0) return 0; double idenom = 1/denom; *res = db*(n1*idenom) - da*(n2*idenom); return 1; } } // Sutherland–Hodgman algorithm for clipping polygon against // 2nd polygon. Requires clockwise orientation of points. Vec2Vector twodPolyEdgeClip(Vec2Vector inPoly, const Vec2Vector& clipPoly) { if(clipPoly.empty()) return inPoly; Vec2 cp1 = clipPoly[clipPoly.size()-1]; for(unsigned ci=0; ci != clipPoly.size() && !inPoly.empty(); ++ci) { Vec2 cp2 = clipPoly[ci]; Vec2Vector outPoly; Vec2 S = inPoly[inPoly.size()-1]; for(unsigned si=0; si != inPoly.size(); ++si) { Vec2 E = inPoly[si]; if(ptInside(E, cp1, cp2)) { if(!ptInside(S, cp1, cp2)) { Vec2 isect; if(SHlineIntersection(S, E, cp1, cp2, &isect)) outPoly.push_back(isect); } outPoly.push_back(E); } else if(ptInside(S, cp1, cp2)) { Vec2 isect; if(SHlineIntersection(S, E, cp1, cp2, &isect)) outPoly.push_back(isect); } S = E; } inPoly = outPoly; cp1 = cp2; } return inPoly; } double twodPolyArea(const Vec2Vector& poly) { const unsigned s=poly.size(); double tot=0; for(unsigned i=0; ibegin(), poly->end()); } bool twodLineIntersectPolygon(Vec2 p1, Vec2 p2, const Vec2Vector& poly) { const unsigned s=poly.size(); bool inside1=1; bool inside2=1; for(unsigned i=0; i #include #include #include #include "fragment.h" #include "bsp.h" #define EPS 1e-6 namespace { // 2d triangle area squared (only considering X/Y) inline double triAreaSqd2D(const Vec3* pts) { double a = (pts[0](0)*pts[1](1)-pts[0](1)*pts[1](0)+ pts[1](0)*pts[2](1)-pts[1](1)*pts[2](0)+ pts[2](0)*pts[0](1)-pts[2](1)*pts[0](0)); return a*a; } // Find set of three points to define a plane (pts). // Needs to find points which are not the same return true if ok bool findPlane(const IdxVector& idxs, unsigned startidx, const FragmentVector& frags, Vec3* pts) { double maxtriarea2 = -1; unsigned besttri = EMPTY_BSP_IDX; Vec3 temppts[3]; unsigned ptct = 0; double maxptsarea2 = -1; // Algorithm is to find the biggest triangle on the plane of the // image (X/Y), then if none, the set of 3 points with the largest // area. We prefer triangles as splitting them is expensive in // terms of numbers of fragments. // Plane of image triangles are preferred as splitting in the // opposite directions gives a weird viewing order for points // Using larger triangles is a heuristic to prevent lots of split // triangles. Empirically it seems to reduce the number of final // fragments by quite a lot. const unsigned endidx = idxs.size(); for(unsigned i=startidx; i maxtriarea2) { maxtriarea2 = areasqd; besttri = i; } } else if(besttri == EMPTY_BSP_IDX) // only bother looking at other points if we haven't found a // triangle yet { // this is a crude rotating buffer looking for larger triangles // in the plane of the image if(f.type == Fragment::FR_LINESEG || f.type == Fragment::FR_PATH) temppts[(ptct++)%3] = f.points[0]; if(f.type == Fragment::FR_LINESEG) temppts[(ptct++)%3] = f.points[1]; if(ptct >= 3) { double areasqd = triAreaSqd2D(temppts); if(areasqd > maxptsarea2) { for(unsigned j=0; j<3; ++j) pts[j] = temppts[j]; maxptsarea2 = areasqd; } } } } // return triangle if(besttri != EMPTY_BSP_IDX) { for(unsigned i=0; i<3; ++i) pts[i] = frags[idxs[besttri]].points[i]; return 1; } else { // is the returned triangle valid? return maxptsarea2 > EPS; } } // sign of calculated dot inline int dotsign(double dot) { return dot > EPS ? 1 : dot < -EPS ? -1 : 0; } // is path in front, on or behind plane? void handlePath(const Vec3& norm, const Vec3& plane0, FragmentVector& v, unsigned fidx, IdxVector& idxsame, IdxVector& idxfront, IdxVector& idxback) { int sign = dotsign(dot(norm, v[fidx].points[0]-plane0)); switch(sign) { case 1: idxfront.push_back(fidx); break; case -1: idxback.push_back(fidx); break; default: idxsame.push_back(fidx); break; } } // is line in front, on or behind plane void handleLine(const Vec3& norm, const Vec3& plane0, FragmentVector& fragvec, unsigned fidx, IdxVector& idxsame, IdxVector& idxfront, IdxVector& idxback) { Fragment& f = fragvec[fidx]; double dot0 = dot(norm, f.points[0]-plane0); int sign0 = dotsign(dot0); int sign1 = dotsign(dot(norm, f.points[1]-plane0)); int signsum = sign0+sign1; // first cases are that the line is simply on one side if(sign0==0 && sign1==0) idxsame.push_back(fidx); else if(signsum > 0) idxfront.push_back(fidx); else if(signsum < 0) idxback.push_back(fidx); else { // split line. Note: we change original, then push a copy, as // a push invalidates the original reference Vec3 linevec = f.points[1]-f.points[0]; double d = -dot0 / dot(linevec, norm); Vec3 newpt = f.points[0] + linevec*d; Fragment fcpy(f); // overwrite original with +ve part f.points[sign0 < 0 ? 0 : 1] = newpt; idxfront.push_back(fidx); // write copy with -ve part fcpy.points[sign0 < 0 ? 1 : 0] = newpt; idxback.push_back(fragvec.size()); fragvec.push_back(fcpy); } } // is triangle in front, behind or on plane? void handleTriangle(const Vec3& norm, const Vec3& plane0, FragmentVector& fragvec, unsigned fidx, IdxVector& idxsame, IdxVector& idxfront, IdxVector& idxback) { Fragment& f = fragvec[fidx]; double dots[3]; int signs[3]; for(unsigned i=0; i<3; ++i) { dots[i] = dot(norm, f.points[i]-plane0); signs[i] = dotsign(dots[i]); } int signsum = signs[0]+signs[1]+signs[2]; int nzero = (signs[0]==0)+(signs[1]==0)+(signs[2]==0); if(nzero == 3) // all on plane idxsame.push_back(fidx); else if(signsum+nzero == 3) // all +ve or on plane idxfront.push_back(fidx); else if(signsum-nzero == -3) // all -ve or on plane idxback.push_back(fidx); else if(nzero == 1) { // split triangle into two as one point is on the plane and // the other two are either side // index of point on plane unsigned idx0 = signs[0]==0 ? 0 : signs[1]==0 ? 1 : 2; Vec3 linevec = f.points[(idx0+2)%3]-f.points[(idx0+1)%3]; double d = -dots[(idx0+1)%3] / dot(linevec, norm); Vec3 newpt = f.points[(idx0+1)%3] + linevec*d; Fragment fcpy(f); // modify original f.points[(idx0+2)%3] = newpt; (dots[(idx0+1)%3]>0 ? idxfront : idxback).push_back(fidx); // then make a copy for the other side fcpy.points[(idx0+1)%3] = newpt; (dots[(idx0+2)%3]>0 ? idxfront : idxback).push_back(fragvec.size()); fragvec.push_back(fcpy); } else { // nzero==0 // split triangle into three, as no points are on the plane // point index by itself on one side of plane unsigned diffidx = signs[1]==signs[2] ? 0 : signs[0]==signs[2] ? 1 : 2; // new points on plane Vec3 linevec_p1 = f.points[(diffidx+1)%3]-f.points[diffidx]; double d_p1 = -dots[diffidx] / dot(linevec_p1, norm); Vec3 newpt_p1 = f.points[diffidx] + linevec_p1*d_p1; Vec3 linevec_p2 = f.points[(diffidx+2)%3]-f.points[diffidx]; double d_p2 = -dots[diffidx] / dot(linevec_p2, norm); Vec3 newpt_p2 = f.points[diffidx] + linevec_p2*d_p2; // now make one triangle on one side and two on the other Fragment fcpy1(f); Fragment fcpy2(f); // modify original: triangle by itself on one side f.points[(diffidx+1)%3] = newpt_p1; f.points[(diffidx+2)%3] = newpt_p2; (dots[diffidx] > 0 ? idxfront : idxback).push_back(fidx); // then add the other two on the other side fcpy1.points[diffidx] = newpt_p1; fcpy1.points[(diffidx+2)%3] = newpt_p2; (dots[diffidx] < 0 ? idxfront : idxback).push_back(fragvec.size()); fragvec.push_back(fcpy1); fcpy2.points[diffidx] = newpt_p2; (dots[diffidx] < 0 ? idxfront : idxback).push_back(fragvec.size()); fragvec.push_back(fcpy2); } } struct BSPStackItem { BSPStackItem(unsigned _bspidx, unsigned _nidxs) : bspidx(_bspidx), nidxs(_nidxs) {} unsigned bspidx; // BSPRecord we are working on here unsigned nidxs; // Number of fragment indices in to_process }; // get Z component of fragment, nudging points and lines forward // Z decreases away from viewer double fragZ(const Fragment& f) { switch(f.type) { case Fragment::FR_TRIANGLE: return std::min(f.points[0](2), std::min(f.points[1](2), f.points[2](2))); case Fragment::FR_LINESEG: return std::min(f.points[0](2), f.points[1](2)) + 1e-5; case Fragment::FR_PATH: return f.points[0](2) + 2e-5; default: return std::numeric_limits::infinity(); } } struct FragZCompare { FragZCompare(const FragmentVector& _v) : v(_v) {} bool operator()(unsigned a, unsigned b) { return fragZ(v[a]) stack; stack.reserve(128); stack.push_back( BSPStackItem(0, to_process.size()) ); while( !stack.empty() ) { BSPStackItem stackitem(stack.back()); stack.pop_back(); // this is the bsp record with which the items are associated BSPRecord& rec = bsp_recs[stackitem.bspidx]; rec.minfragidxidx = frag_idxs.size(); // where the items get added // if more than item to process then choose a plane, then split if( stackitem.nidxs > 1 && findPlane(to_process, to_process.size()-stackitem.nidxs, fragvec, planepts) ) { // norm of plane (making sure it points to observer) Vec3 norm = cross(planepts[1]-planepts[0], planepts[2]-planepts[0]); if(dot(norm, viewdirn) < 0) norm = -norm; // approximately normalise norm *= 1./(std::abs(norm(0))+std::abs(norm(1))+std::abs(norm(2))); unsigned to_process_size = to_process.size(); for(unsigned i=to_process_size-stackitem.nidxs; i stack; stack.reserve(128); stack.push_back(WalkStackItem(0, 0)); IdxVector temp; while( !stack.empty() ) { WalkStackItem stackitem(stack.back()); stack.pop_back(); const BSPRecord &rec = bsp_recs[stackitem.bsp_idx]; if(stackitem.stage == 0) { if(rec.frontidx != EMPTY_BSP_IDX) stack.push_back( WalkStackItem(rec.frontidx, 0) ); stack.push_back( WalkStackItem(stackitem.bsp_idx, 1) ); if(rec.backidx != EMPTY_BSP_IDX) stack.push_back( WalkStackItem(rec.backidx, 0) ); } else { // Sort images in plane by Z. This is helpful for points // which may overlap. temp.resize(0); temp.insert(temp.end(), frag_idxs.begin()+rec.minfragidxidx, frag_idxs.begin()+(rec.minfragidxidx+rec.nfrags)); std::sort(temp.begin(), temp.end(), FragZCompare(fragvec)); for(int type=Fragment::FR_TRIANGLE; type<=Fragment::FR_PATH; ++type) { for(unsigned i : temp) if(fragvec[i].type == type) retn.push_back(i); } } } return retn; } #if 0 #include int main() { FragmentVector v; for(unsigned i=0; i<10; ++i) { Fragment f; f.type =Fragment::FR_TRIANGLE; for(unsigned j=0;j<3;j++) f.points[j] = Vec3(rand()*1./RAND_MAX, rand()*1./RAND_MAX, rand()*1./RAND_MAX); v.push_back(f); } BSPBuilder builder(v); std::cout << "BSP recs size " << builder.bsp_recs.size() << '\n'; std::cout << "Fragment size " << v.size() << '\n'; IdxVector out = builder.getFragmentIdxs(); for(unsigned i=0; i // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program 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 General Public License for more details. // // You should have received a copy of the GNU General Public License along // with this program; if not, write to the Free Software Foundation, Inc., // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ///////////////////////////////////////////////////////////////////////////// #ifndef PROPERTIES_H #define PROPERTIES_H #include #include #include #include #include #include "mmaths.h" // These classes describe the color and properties of a surface or line // A reference counting scheme (PropSmartPtr) is used to keep track of // when to delete them. PropSmartPtr is an intrusive pointer, which // uses a reference count in the object to keep track of how many // copies are used. typedef std::vector RGBVec; // helper to convert images to list of rgbs inline void _qimage2rgbvec(const QImage& img, RGBVec& vec) { unsigned size=unsigned(img.width()); vec.resize(size); const QRgb* row = (const QRgb*)(img.scanLine(0)); std::copy(row, row+size, &vec[0]); } struct SurfaceProp { SurfaceProp(double _r=0.5, double _g=0.5, double _b=0.5, double _refl=0.5, double _trans=0, bool _hide=0) : r(_r), g(_g), b(_b), refl(_refl), trans(_trans), hide(_hide), _ref_cnt(0) { } bool hasRGBs() const { return !rgbs.empty(); }; void setRGBs(const QImage& img) { _qimage2rgbvec(img, rgbs); } QColor color(unsigned idx) const { if(rgbs.empty()) return QColor(int(r*255), int(g*255), int(b*255), int((1-trans)*255)); else return QColor::fromRgba ( rgbs[std::min(unsigned(rgbs.size())-1,idx)] ); } double r, g, b; double refl, trans; RGBVec rgbs; bool hide; // used to reference count usages by Object() instances mutable unsigned _ref_cnt; }; struct LineProp { LineProp(double _r=0, double _g=0, double _b=0, double _trans=0, double _refl=0, double _width=1, bool _hide=0, Qt::PenStyle _style=Qt::SolidLine) : r(_r), g(_g), b(_b), trans(_trans), refl(_refl), width(_width), hide(_hide), style(_style), _ref_cnt(0) { } bool hasRGBs() const { return !rgbs.empty(); }; void setRGBs(const QImage& img) { _qimage2rgbvec(img, rgbs); } void setDashPattern(const ValVector& vec) { dashpattern.clear(); for(auto v : vec) dashpattern << v; } QColor color(unsigned idx) const { if(rgbs.empty()) return QColor(int(r*255), int(g*255), int(b*255), int((1-trans)*255)); else return QColor::fromRgba ( rgbs[std::min(unsigned(rgbs.size())-1,idx)] ); } double r, g, b; double trans; double refl; double width; RGBVec rgbs; bool hide; Qt::PenStyle style; QVector dashpattern; // used to reference count usages by Object() instances mutable unsigned _ref_cnt; }; //#include // intrusive pointer class is for automatically deleting the // Surface/LineProp instances when the reference count drops back to 0 template class PropSmartPtr { public: PropSmartPtr(T* p) : p_(p) { if(p_ != 0) { ++p_->_ref_cnt; //printf("prop: %p +1 -> %i\n", p_, p_->_ref_cnt); } } ~PropSmartPtr() { if(p_ != 0) { --p_->_ref_cnt; //printf("prop: %p -1 -> %i\n", p_, p_->_ref_cnt); if(p_->_ref_cnt == 0) delete p_; } } PropSmartPtr(const PropSmartPtr &r) : p_(r.p_) { if(p_ != 0) { ++p_->_ref_cnt; //printf("prop: %p +1 -> %i\n", p_, p_->_ref_cnt); } } T* operator->() { return p_; } const T* ptr() const { return p_; } private: T* p_; }; #endif ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������veusz-3.0.1/veusz/helpers/src/threed/mmaths.h�������������������������������������������������������0000664�0001750�0001750�00000032201�13306764717�020647� 0����������������������������������������������������������������������������������������������������ustar �jss�����������������������������jss�����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// -*-c++-*- // Copyright (C) 2015 Jeremy S. Sanders // Email: Jeremy Sanders // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program 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 General Public License for more details. // // You should have received a copy of the GNU General Public License along // with this program; if not, write to the Free Software Foundation, Inc., // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ///////////////////////////////////////////////////////////////////////////// #ifndef MATHS_H #define MATHS_H #include #include #define PI 3.14159265358979323846 ////////////////////////////////////////////////////////////////////////////// // 4-vector struct Vec4 { Vec4() { v[0] = v[1] = v[2] = v[3] = 0; } Vec4(double a, double b, double c, double d=1) { v[0] = a; v[1] = b; v[2] = c; v[3] = d; } inline double& operator()(unsigned i) { return v[i]; } inline double operator()(unsigned i) const { return v[i]; } inline void operator*=(double f) { v[0] *= f; v[1] *= f; v[2] *= f; v[3] *= f; } inline Vec4 operator+(const Vec4& o) const { return Vec4(v[0]+o.v[0], v[1]+o.v[1], v[2]+o.v[2], v[3]+o.v[3]); } inline Vec4 operator-(const Vec4& o) const { return Vec4(v[0]-o.v[0], v[1]-o.v[1], v[2]-o.v[2], v[3]-o.v[3]); } inline Vec4 operator*(double f) const { return Vec4(v[0]*f, v[1]*f, v[2]*f, v[3]*f); } Vec4& operator+=(const Vec4& o) { v[0]+=o.v[0]; v[1]+=o.v[1]; v[2]+=o.v[2]; v[3]+=o.v[3]; return *this; } Vec4& operator-=(const Vec4& o) { v[0]-=o.v[0]; v[1]-=o.v[1]; v[2]-=o.v[2]; v[3]-=o.v[3]; return *this; } inline bool operator==(const Vec4& o) const { return v[0]==o.v[0] && v[1]==o.v[1] && v[2]==o.v[2] && v[3]==o.v[3]; } inline bool operator!=(const Vec4& o) const { return !(operator==(o)); } // radius inline double rad2() const { return v[0]*v[0]+v[1]*v[1]+v[2]*v[2]+v[3]*v[3]; } inline double rad() const { return std::sqrt(rad2()); } inline void normalise() { operator*=(1/rad()); } inline bool isfinite() const { return std::isfinite(v[0]+v[1]+v[2]+v[3]); } private: double v[4]; }; ////////////////////////////////////////////////////////////////////////////// // 3-vector struct Vec3 { Vec3() { v[0] = v[1] = v[2] = 0; } Vec3(double a, double b, double c) { v[0] = a; v[1] = b; v[2] = c; } inline double& operator()(unsigned i) { return v[i]; } inline double operator()(unsigned i) const { return v[i]; } inline void operator*=(double f) { v[0] *= f; v[1] *= f; v[2] *= f; } inline Vec3 operator+(const Vec3& o) const { return Vec3(v[0]+o.v[0], v[1]+o.v[1], v[2]+o.v[2]); } inline Vec3 operator-() const { return Vec3(-v[0], -v[1], -v[2]); } inline Vec3 operator-(const Vec3& o) const { return Vec3(v[0]-o.v[0], v[1]-o.v[1], v[2]-o.v[2]); } inline Vec3 operator*(double f) const { return Vec3(v[0]*f, v[1]*f, v[2]*f); } Vec3& operator+=(const Vec3& o) { v[0]+=o.v[0]; v[1]+=o.v[1]; v[2]+=o.v[2]; return *this; } Vec3& operator-=(const Vec3& o) { v[0]-=o.v[0]; v[1]-=o.v[1]; v[2]-=o.v[2]; return *this; } inline bool operator==(const Vec3& o) const { return v[0]==o.v[0] && v[1]==o.v[1] && v[2]==o.v[2]; } inline bool operator!=(const Vec3& o) const { return !(operator==(o)); } // radius inline double rad2() const { return v[0]*v[0]+v[1]*v[1]+v[2]*v[2]; } inline double rad() const { return std::sqrt(rad2()); } inline void normalise() { operator*=(1/rad()); } inline bool isfinite() const { return std::isfinite(v[0]+v[1]+v[2]); } private: double v[3]; }; inline Vec3 cross(const Vec3& a, const Vec3& b) { return Vec3(a(1)*b(2)-a(2)*b(1), a(2)*b(0)-a(0)*b(2), a(0)*b(1)-a(1)*b(0)); } inline double dot(const Vec3& a, const Vec3& b) { return a(0)*b(0)+a(1)*b(1)+a(2)*b(2); } ////////////////////////////////////////////////////////////////////////////// // 4x4 matrix struct Mat4 { Mat4(bool zero=true) { if(zero) for(unsigned y=0; y<4; ++y) for(unsigned x=0; x<4; ++x) m[y][x] = 0; } inline double& operator()(unsigned y, unsigned x) { return m[y][x]; } inline double operator()(unsigned y, unsigned x) const { return m[y][x]; } // matrix multiply inline Mat4 operator*(const Mat4& o) const { Mat4 ret(false); for(unsigned y=0; y<4; ++y) for(unsigned x=0; x<4; ++x) ret.m[y][x] = m[y][0]*o.m[0][x] + m[y][1]*o.m[1][x] + m[y][2]*o.m[2][x] + m[y][3]*o.m[3][x]; return ret; } inline Vec4 operator*(const Vec4& v) const { return Vec4(v(0)*m[0][0]+v(1)*m[0][1]+v(2)*m[0][2]+v(3)*m[0][3], v(0)*m[1][0]+v(1)*m[1][1]+v(2)*m[1][2]+v(3)*m[1][3], v(0)*m[2][0]+v(1)*m[2][1]+v(2)*m[2][2]+v(3)*m[2][3], v(0)*m[3][0]+v(1)*m[3][1]+v(2)*m[3][2]+v(3)*m[3][3]); } inline Mat4 transpose() const { Mat4 r(false); for(unsigned y=0; y<4; ++y) for(unsigned x=0; y screen coordinates inline Vec2 projVecToScreen(const Mat3& screenM, const Vec3& vec) { Vec3 mult(screenM*Vec3(vec(0), vec(1), 1)); double inv = 1/mult(2); return Vec2(mult(0)*inv, mult(1)*inv); } // do 2d lines overlap? inline bool line2DOverlap(Vec2 A1, Vec2 A2, Vec2 B1, Vec2 B2) { double d = cross(A2-A1, B2-B1); double u = cross(B2-B1, A1-B1); double v = cross(A2-A1, A1-B1); if(d>=0) return 0<=u && u<=d && 0<=v && v<=d; else return 0>=u && u>=d && 0>=v && v>=d; } // drop dimension of vector inline Vec2 dropDim(const Vec3 v) { return Vec2(v(0), v(1)); } ////////////////////////////////////////////////////////////////////////////// // Helper types typedef std::vector Vec2Vector; typedef std::vector Vec3Vector; typedef std::vector Vec4Vector; typedef std::vector ValVector; ////////////////////////////////////////////////////////////////////////////// #if 0 // debug: #include using std::printf; inline void print(const Mat4& m) { printf("% 7.3f % 7.3f % 7.3f % 7.3f\n", m(0,0), m(0,1), m(0,2), m(0,3)); printf("% 7.3f % 7.3f % 7.3f % 7.3f\n", m(1,0), m(1,1), m(1,2), m(1,3)); printf("% 7.3f % 7.3f % 7.3f % 7.3f\n", m(2,0), m(2,1), m(2,2), m(2,3)); printf("% 7.3f % 7.3f % 7.3f % 7.3f\n", m(3,0), m(3,1), m(3,2), m(3,3)); } inline void print(const Mat3& m) { printf("% 7.3f % 7.3f % 7.3f\n", m(0,0), m(0,1), m(0,2)); printf("% 7.3f % 7.3f % 7.3f\n", m(1,0), m(1,1), m(1,2)); printf("% 7.3f % 7.3f % 7.3f\n", m(2,0), m(2,1), m(2,2)); } inline void print(const Vec4& v) { printf("% 7.3f % 7.3f % 7.3f % 7.3f\n", v(0), v(1), v(2), v(3)); } inline void print(const Vec3& v) { printf("% 7.3f % 7.3f % 7.3f\n", v(0), v(1), v(2)); } #endif #endif �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������veusz-3.0.1/veusz/helpers/src/threed/clipcontainer.cpp����������������������������������������������0000664�0001750�0001750�00000013776�13302252613�022542� 0����������������������������������������������������������������������������������������������������ustar �jss�����������������������������jss�����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright (C) 2015 Jeremy S. Sanders // Email: Jeremy Sanders // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program 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 General Public License for more details. // // You should have received a copy of the GNU General Public License along // with this program; if not, write to the Free Software Foundation, Inc., // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ///////////////////////////////////////////////////////////////////////////// #include "clipcontainer.h" #define EPS 1e-8 namespace { // clip line by plane void clipLine(Fragment& f, const Vec3& onplane, const Vec3& normal) { double dot0 = dot(f.points[0]-onplane, normal); bool bad0 = dot0 < -EPS; double dot1 = dot(f.points[1]-onplane, normal); bool bad1 = dot1 < -EPS; if(!bad0 && !bad1) // both points on good side of plane ; else if(bad0 && bad1) // both line points on bad side of plane f.type = Fragment::FR_NONE; else { // clip line to plane (bad1 or bad2, not both) Vec3 linevec = f.points[1]-f.points[0]; double d = -dot0 / dot(linevec, normal); f.points[bad0 ? 0 : 1] = f.points[0] + linevec*d; } } // clip triangle by plane void clipTriangle(FragmentVector& v, unsigned idx, const Vec3& onplane, const Vec3& normal) { Fragment& f = v[idx]; double dotv[3]; unsigned bad[3]; for(unsigned i=0; i<3; ++i) { dotv[i] = dot(f.points[i]-onplane, normal); bad[i] = dotv[i] < -EPS; } unsigned badsum = bad[0]+bad[1]+bad[2]; switch(badsum) { case 0: // all points ok break; case 1: // two points are good, one is bad { unsigned badidx = bad[0] ? 0 : bad[1] ? 1 : 2; // calculate where vectors from good to bad points // intercept plane Vec3 good1 = f.points[(badidx+1)%3]; Vec3 linevec1 = good1 - f.points[badidx]; double d1 = -dotv[badidx] / dot(linevec1, normal); Vec3 icept1 = f.points[badidx] + linevec1*d1; Vec3 good2 = f.points[(badidx+2)%3]; Vec3 linevec2 = good2 - f.points[badidx]; double d2 = -dotv[badidx] / dot(linevec2, normal); Vec3 icept2 = f.points[badidx] + linevec2*d2; // break into two triangles from good points to intercepts // note: the push back invalidates the original, so we have // to make a copy f.points[0] = good2; f.points[1] = icept2; f.points[2] = good1; Fragment fcpy(f); fcpy.points[0] = good1; fcpy.points[1] = icept1; fcpy.points[2] = icept2; v.push_back(fcpy); } break; case 2: // one point is ok, the other two are bad { unsigned goodidx = !bad[0] ? 0 : !bad[1] ? 1 : 2; // work out where vectors from ok point intercept with plane Vec3 linevec1 = f.points[(goodidx+1)%3] - f.points[goodidx]; double d1 = -dotv[goodidx] / dot(linevec1, normal); f.points[(goodidx+1)%3] = f.points[goodidx] + linevec1*d1; Vec3 linevec2 = f.points[(goodidx+2)%3] - f.points[goodidx]; double d2 = -dotv[goodidx] / dot(linevec2, normal); f.points[(goodidx+2)%3] = f.points[goodidx] + linevec2*d2; } break; case 3: // all points are bad f.type = Fragment::FR_NONE; break; } } // clip all fragments to the plane given void clipFragments(FragmentVector& v, unsigned start, const Vec3& onplane, const Vec3& normal) { unsigned nfrags = v.size(); for(unsigned i=start; igetFragments(perspM, outerM, v); // these are the points defining the clipping cube Vec3 pts[8]; pts[0] = minpt; pts[1] = Vec3(minpt(0), minpt(1), maxpt(2)); pts[2] = Vec3(minpt(0), maxpt(1), minpt(2)); pts[3] = Vec3(minpt(0), maxpt(1), maxpt(2)); pts[4] = Vec3(maxpt(0), minpt(1), minpt(2)); pts[5] = Vec3(maxpt(0), minpt(1), maxpt(2)); pts[6] = Vec3(maxpt(0), maxpt(1), minpt(2)); pts[7] = maxpt; // convert cube coordinates to outer coordinates for(unsigned i=0; i<8; ++i) pts[i] = vec4to3(outerM*vec3to4(pts[i])); // clip with plane point and normal // dotting points with plane with these will give all >= 0 if in cube clipFragments(v, fragstart, pts[0], cross(pts[2]-pts[0], pts[1]-pts[0])); clipFragments(v, fragstart, pts[0], cross(pts[1]-pts[0], pts[4]-pts[0])); clipFragments(v, fragstart, pts[0], cross(pts[4]-pts[0], pts[2]-pts[0])); clipFragments(v, fragstart, pts[7], cross(pts[5]-pts[7], pts[3]-pts[7])); clipFragments(v, fragstart, pts[7], cross(pts[3]-pts[7], pts[6]-pts[7])); clipFragments(v, fragstart, pts[7], cross(pts[6]-pts[7], pts[5]-pts[7])); } ��veusz-3.0.1/veusz/helpers/src/threed/objects.h������������������������������������������������������0000664�0001750�0001750�00000023336�13302276644�021011� 0����������������������������������������������������������������������������������������������������ustar �jss�����������������������������jss�����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// -*-c++-*- // Copyright (C) 2015 Jeremy S. Sanders // Email: Jeremy Sanders // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program 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 General Public License for more details. // // You should have received a copy of the GNU General Public License along // with this program; if not, write to the Free Software Foundation, Inc., // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ///////////////////////////////////////////////////////////////////////////// #ifndef SHAPES_H #define SHAPES_H #include #include #include #include #include "mmaths.h" #include "fragment.h" #include "properties.h" class Object { public: Object() : widgetid(-1) {} virtual ~Object(); virtual void getFragments(const Mat4& perspM, const Mat4& outerM, FragmentVector& v); // recursive set id of child objects virtual void assignWidgetId(long id); // id of widget which generated object long widgetid; }; class Triangle : public Object { public: Triangle() : Object(), surfaceprop(0) { } Triangle(const Vec3& a, const Vec3& b, const Vec3& c, const SurfaceProp* prop=0) : surfaceprop(prop) { points[0] = a; points[1] = b; points[2] = c; } void getFragments(const Mat4& perspM, const Mat4& outerM, FragmentVector& v); public: Vec3 points[3]; PropSmartPtr surfaceprop; }; class PolyLine : public Object { public: PolyLine(const LineProp* prop=0) : Object(), lineprop(prop) { } PolyLine(const ValVector& x, const ValVector& y, const ValVector& z, const LineProp* prop=0) : Object(), lineprop(prop) { addPoints(x, y, z); } void addPoint(const Vec3& v) { points.push_back(v); } void addPoints(const ValVector& x, const ValVector& y, const ValVector& z); void getFragments(const Mat4& perspM, const Mat4& outerM, FragmentVector& v); public: Vec3Vector points; PropSmartPtr lineprop; }; class LineSegments : public Object { public: LineSegments(const ValVector& x1, const ValVector& y1, const ValVector& z1, const ValVector& x2, const ValVector& y2, const ValVector& z2, const LineProp* prop); LineSegments(const ValVector& pts1, const ValVector& pts2, const LineProp* prop); void getFragments(const Mat4& perspM, const Mat4& outerM, FragmentVector& v); public: Vec3Vector points; PropSmartPtr lineprop; }; // a grid of height values on a regular mesh with grid points given // heights has M*N elements where M and N are the length of pos1 & pos2 class Mesh : public Object { public: // X_DIRN: heights is X, a is Y, b is Z // Y_DIRN: heights is Y, a is Z. b is X // Z_DIRN: heights is Z, a is X, b is Y enum Direction {X_DIRN, Y_DIRN, Z_DIRN}; public: Mesh(const ValVector& _pos1, const ValVector& _pos2, const ValVector& _heights, Direction _dirn, const LineProp* lprop=0, const SurfaceProp* sprop=0, bool _hidehorzline=0, bool _hidevertline=0) : pos1(_pos1), pos2(_pos2), heights(_heights), dirn(_dirn), lineprop(lprop), surfaceprop(sprop), hidehorzline(_hidehorzline), hidevertline(_hidevertline) { } void getFragments(const Mat4& perspM, const Mat4& outerM, FragmentVector& v); private: void getSurfaceFragments(const Mat4& perspM, const Mat4& outerM, FragmentVector& v); void getLineFragments(const Mat4& perspM, const Mat4& outerM, FragmentVector& v); void getVecIdxs(unsigned &vidx_h, unsigned &vidx_1, unsigned &vidx_2) const; public: ValVector pos1, pos2, heights; Direction dirn; PropSmartPtr lineprop; PropSmartPtr surfaceprop; bool hidehorzline, hidevertline; }; // Grid of data values, where the centres of the bins are specified. // There should be 1 more values along edges than values in array. // idxval, edge1, edge2 give the index of the axis (x=0,y=1,z=2) for // that direction. class DataMesh : public Object { public: DataMesh(const ValVector& _edges1, const ValVector& _edges2, const ValVector& _vals, unsigned _idxval, unsigned _idxedge1, unsigned _idxedge2, bool _highres, const LineProp* lprop=0, const SurfaceProp* sprop=0, bool _hidehorzline=0, bool _hidevertline=0) : edges1(_edges1), edges2(_edges2), vals(_vals), idxval(_idxval), idxedge1(_idxedge1), idxedge2(_idxedge2), highres(_highres), lineprop(lprop), surfaceprop(sprop), hidehorzline(_hidehorzline), hidevertline(_hidevertline) { } void getFragments(const Mat4& perspM, const Mat4& outerM, FragmentVector& v); public: ValVector edges1, edges2, vals; unsigned idxval, idxedge1, idxedge2; bool highres; PropSmartPtr lineprop; PropSmartPtr surfaceprop; bool hidehorzline, hidevertline; }; // multiple cuboids class MultiCuboid : public Object { public: MultiCuboid(const ValVector& _xmin, const ValVector& _xmax, const ValVector& _ymin, const ValVector& _ymax, const ValVector& _zmin, const ValVector& _zmax, const LineProp* lprop=0, const SurfaceProp* sprop=0) : xmin(_xmin), xmax(_xmax), ymin(_ymin), ymax(_ymax), zmin(_zmin), zmax(_zmax), lineprop(lprop), surfaceprop(sprop) { } void getFragments(const Mat4& perspM, const Mat4& outerM, FragmentVector& v); public: ValVector xmin, xmax, ymin, ymax, zmin, zmax; PropSmartPtr lineprop; PropSmartPtr surfaceprop; }; // a set of points to plot class Points : public Object { public: Points(const ValVector& px, const ValVector& py, const ValVector& pz, QPainterPath pp, const LineProp* pointedge=0, const SurfaceProp* pointfill=0) : x(px), y(py), z(pz), path(pp), scaleline(true), scalepersp(true), lineedge(pointedge), surfacefill(pointfill) { } void setSizes(const ValVector& _sizes) { sizes = _sizes; } void getFragments(const Mat4& perspM, const Mat4& outerM, FragmentVector& v); private: FragmentPathParameters fragparams; public: ValVector x, y, z; ValVector sizes; QPainterPath path; bool scaleline, scalepersp; PropSmartPtr lineedge; PropSmartPtr surfacefill; }; // a "text" class which calls back draw() when drawing is requested class Text : public Object { public: // pos1 and pos2 contain a list of x,y,z values Text(const ValVector& _pos1, const ValVector& _pos2); void getFragments(const Mat4& perspM, const Mat4& outerM, FragmentVector& v); virtual void draw(QPainter* painter, QPointF pt1, QPointF pt2, QPointF pt3, unsigned index, double scale, double linescale); private: class TextPathParameters : public FragmentPathParameters { public: void callback(QPainter* painter, QPointF pt1, QPointF pt2, QPointF pt3, int index, double scale, double linescale); Text* text; }; TextPathParameters fragparams; public: ValVector pos1, pos2; }; // A triangle only visible if its norm (translated to viewing space) is +ve class TriangleFacing : public Triangle { public: TriangleFacing(const Vec3& a, const Vec3& b, const Vec3& c, const SurfaceProp* prop=0) : Triangle(a, b, c, prop) {} void getFragments(const Mat4& perspM, const Mat4& outerM, FragmentVector& v); }; // container of objects with transformation matrix of children // Note: object pointers passed to object will be deleted when this // container is deleted class ObjectContainer : public Object { public: ObjectContainer() : objM(identityM4()) {} ~ObjectContainer(); void getFragments(const Mat4& perspM, const Mat4& outerM, FragmentVector& v); void addObject(Object* obj) { objects.push_back(obj); } // recursive set id of child objects void assignWidgetId(long id); public: Mat4 objM; std::vector objects; }; // container which only draws contents if the norm is pointing in +ve // z direction class FacingContainer : public ObjectContainer { public: FacingContainer(Vec3 _norm) : ObjectContainer(), norm(_norm) { } void getFragments(const Mat4& perspM, const Mat4& outerM, FragmentVector& v); public: Vec3 norm; }; // This class draws tick labels with correct choice of axis class AxisLabels : public Object { public: // cube defined to be between these corners AxisLabels(const Vec3& _box1, const Vec3& _box2, const ValVector& _tickfracs, double _labelfrac); void addAxisChoice(const Vec3& start, const Vec3& end); // override this: draw reqested label at origin, with alignment // given // (if index==-1, then draw axis label) virtual void drawLabel(QPainter* painter, int index, QPointF pt, QPointF ax1, QPointF ax2, double axangle); void getFragments(const Mat4& perspM, const Mat4& outerM, FragmentVector& v); private: Vec3 box1, box2; ValVector tickfracs; double labelfrac; std::vector starts, ends; private: struct PathParameters : public FragmentPathParameters { void callback(QPainter* painter, QPointF pt, QPointF ax1, QPointF ax2, int index, double scale, double linescale); AxisLabels* tl; double axangle; }; PathParameters fragparams; }; #endif ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������veusz-3.0.1/veusz/helpers/src/threed/twod.h���������������������������������������������������������0000664�0001750�0001750�00000003737�13302252613�020326� 0����������������������������������������������������������������������������������������������������ustar �jss�����������������������������jss�����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// -*-c++-*- // Copyright (C) 2015 Jeremy S. Sanders // Email: Jeremy Sanders // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program 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 General Public License for more details. // // You should have received a copy of the GNU General Public License along // with this program; if not, write to the Free Software Foundation, Inc., // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ///////////////////////////////////////////////////////////////////////////// #ifndef TWOD_H #define TWOD_H #include "mmaths.h" enum ISect { LINE_NOOVERLAP, LINE_CROSS, LINE_OVERLAP }; // Do the two line segments p1->p2, q1->q2 cross or overlap? // return LINE_NOOVERLAP if no overlap // LINE_CROSS if they cross somewhere // LINE_OVERLAP if they lie on top of each other partially // if posn != 0, return crossing position if LINE_CROSS // if LINE_OVERLAP the two end points of overlap are returned in posn and posn2 // Assumes that the line segments are finite. ISect twodLineIntersect(Vec2 p1, Vec2 p2, Vec2 q1, Vec2 q2, Vec2* posn=0, Vec2* posn2=0); // clip 2D polygon by a 2nd polygon (must be clockwise polygons) Vec2Vector twodPolyEdgeClip(Vec2Vector inPoly, const Vec2Vector& clipPoly); // area of polygon (+ve -> clockwise) double twodPolyArea(const Vec2Vector& poly); // ensure polygon is clockwise void twodPolyMakeClockwise(Vec2Vector* poly); // does line cross polygon? (make sure poly is defined clockwise) bool twodLineIntersectPolygon(Vec2 p1, Vec2 p2, const Vec2Vector& poly); #endif ���������������������������������veusz-3.0.1/veusz/helpers/src/qtloops/��������������������������������������������������������������0000775�0001750�0001750�00000000000�13325026670�017423� 5����������������������������������������������������������������������������������������������������ustar �jss�����������������������������jss�����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������veusz-3.0.1/veusz/helpers/src/qtloops/qtloops_helpers.cpp�������������������������������������������0000664�0001750�0001750�00000007561�13161413406�023356� 0����������������������������������������������������������������������������������������������������ustar �jss�����������������������������jss�����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright (C) 2009 Jeremy S. Sanders // Email: Jeremy Sanders // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program 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 General Public License for more details. // // You should have received a copy of the GNU General Public License along // with this program; if not, write to the Free Software Foundation, Inc., // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ///////////////////////////////////////////////////////////////////////////// #include "Python.h" #define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION #include "numpy/arrayobject.h" #include "qtloops_helpers.h" namespace { // python3 numpy import_array is a macro with a return (how stupid), // so we have to wrap it up to get it to portably compile #if PY_MAJOR_VERSION >= 3 void* do_import() { import_array(); return 0; } #else void do_import() { import_array(); } #endif } void do_numpy_init_package() { do_import(); } Tuple2Ptrs::Tuple2Ptrs(PyObject* tuple) { const size_t numitems = PyTuple_Size(tuple); for(size_t i=0; i != numitems; ++i) { // access python tuple item PyObject* obj = PyTuple_GetItem(tuple, i); // convert to C array (stored in objdata) PyArrayObject *array = (PyArrayObject*) PyArray_ContiguousFromObject(obj, NPY_DOUBLE, 1, 1); if( array == NULL ) { throw "Cannot covert parameter to 1D numpy array"; } data.push_back( (double*)PyArray_DATA(array) ); dims.push_back( PyArray_DIMS(array)[0] ); _arrays.push_back( (PyObject*)array ); } } Tuple2Ptrs::~Tuple2Ptrs() { // delete array objects for(int i=0; i < _arrays.size(); ++i) { Py_DECREF(_arrays[i]); _arrays[i] = 0; data[i] = 0; } } Numpy1DObj::Numpy1DObj(PyObject* array) : data(0), _array(0) { PyArrayObject *arrayobj = (PyArrayObject*) PyArray_ContiguousFromObject(array, NPY_DOUBLE, 1, 1); if( arrayobj == NULL ) { throw "Cannot covert item to 1D numpy array"; } data = (double*)PyArray_DATA(arrayobj); dim = PyArray_DIMS(arrayobj)[0]; _array = (PyObject*)arrayobj; } Numpy1DObj::~Numpy1DObj() { Py_XDECREF(_array); _array = 0; data = 0; } Numpy2DObj::Numpy2DObj(PyObject* array) : data(0), _array(0) { PyArrayObject *arrayobj = (PyArrayObject*) PyArray_ContiguousFromObject(array, NPY_DOUBLE, 2, 2); if( arrayobj == NULL ) { throw "Cannot convert to 2D numpy array"; } data = (double*)PyArray_DATA(arrayobj); dims[0] = PyArray_DIMS(arrayobj)[0]; dims[1] = PyArray_DIMS(arrayobj)[1]; _array = (PyObject*)arrayobj; } Numpy2DObj::~Numpy2DObj() { Py_XDECREF(_array); _array = 0; data = 0; } Numpy2DIntObj::Numpy2DIntObj(PyObject* array) : data(0), _array(0) { PyArrayObject *arrayobj = (PyArrayObject*) PyArray_ContiguousFromObject(array, NPY_INT, 2, 2); if( arrayobj == NULL ) { throw "Cannot convert to 2D numpy integer array. " "Requires numpy.intc argument."; } data = (int*)PyArray_DATA(arrayobj); dims[0] = PyArray_DIMS(arrayobj)[0]; dims[1] = PyArray_DIMS(arrayobj)[1]; _array = (PyObject*)arrayobj; } Numpy2DIntObj::~Numpy2DIntObj() { Py_XDECREF(_array); _array = 0; data = 0; } PyObject* doubleArrayToNumpy(const double* d, int len) { npy_intp dims[1]; dims[0] = len; PyObject* n = PyArray_SimpleNew(1, dims, NPY_DOUBLE); double* pydata = (double*)PyArray_DATA((PyArrayObject*)(n)); for(int i = 0; i < len; ++i) pydata[i] = d[i]; return n; } �����������������������������������������������������������������������������������������������������������������������������������������������veusz-3.0.1/veusz/helpers/src/qtloops/beziers.cpp���������������������������������������������������0000664�0001750�0001750�00000074255�13161413406�021602� 0����������������������������������������������������������������������������������������������������ustar �jss�����������������������������jss�����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#define SP_BEZIERS_C /** \file * Bezier interpolation for inkscape drawing code. */ /* * Original code published in: * An Algorithm for Automatically Fitting Digitized Curves * by Philip J. Schneider * "Graphics Gems", Academic Press, 1990 * * Authors: * Philip J. Schneider * Lauris Kaplinski * Peter Moulder * * Copyright (C) 1990 Philip J. Schneider * Copyright (C) 2001 Lauris Kaplinski * Copyright (C) 2001 Ximian, Inc. * Copyright (C) 2003,2004 Monash University * * Released under GNU GPL, read the file 'COPYING' for more information */ // Modified to be based around QPointF by Jeremy Sanders (2007) #define SP_HUGE 1e5 //#define BEZIER_DEBUG #include #include #include #include #include "beziers.h" #include "isnan.h" #define g_return_val_if_fail(check, val) \ if(!(check)) { \ fprintf(stderr, "Error in check g_return_val_if_fail in " \ __FILE__ "\n"); \ return(val); \ } #define g_return_if_fail(check) \ if(!(check)) { \ fprintf(stderr, "Error in check g_return_if_fail in " \ __FILE__ "\n"); \ return; \ } #define g_assert(check) \ if(!(check)) { \ fprintf(stderr, "Assertion failed in g_assert in " \ __FILE__ "\n"); \ abort(); \ } #define G_N_ELEMENTS(arr) (sizeof (arr) / sizeof ((arr)[0])) static inline bool is_zero(const QPointF& pt) { return pt.isNull(); } static inline QPointF unit_vector(const QPointF& pt) { const double mag = sqrt(pt.x()*pt.x()+pt.y()*pt.y()); return pt / mag; } static inline double dot(const QPointF& a, const QPointF& b) { return a.x()*b.x() + a.y()*b.y(); } /** Compute the L2, or euclidean, norm of \a p. */ static inline double L2(const QPointF &p) { return hypot(p.x(), p.y()); } static inline QPointF rot90(const QPointF& p) { return QPointF(-p.y(), p.x()); } typedef QPointF BezierCurve[]; /* Forward declarations */ static void generate_bezier(QPointF b[], QPointF const d[], double const u[], unsigned len, QPointF const &tHat1, QPointF const &tHat2, double tolerance_sq); static void estimate_lengths(QPointF bezier[], QPointF const data[], double const u[], unsigned len, QPointF const &tHat1, QPointF const &tHat2); static void estimate_bi(QPointF b[4], unsigned ei, QPointF const data[], double const u[], unsigned len); static void reparameterize(QPointF const d[], unsigned len, double u[], BezierCurve const bezCurve); static double NewtonRaphsonRootFind(BezierCurve const Q, QPointF const &P, double u); static QPointF sp_darray_center_tangent(QPointF const d[], unsigned center, unsigned length); static QPointF sp_darray_right_tangent(QPointF const d[], unsigned const len); static unsigned copy_without_nans_or_adjacent_duplicates(QPointF const src[], unsigned src_len, QPointF dest[]); static void chord_length_parameterize(QPointF const d[], double u[], unsigned len); static double compute_max_error_ratio(QPointF const d[], double const u[], unsigned len, BezierCurve const bezCurve, double tolerance, unsigned *splitPoint); static double compute_hook(QPointF const &a, QPointF const &b, double const u, BezierCurve const bezCurve, double const tolerance); static QPointF const unconstrained_tangent(0, 0); /* * B0, B1, B2, B3 : Bezier multipliers */ static inline double B0(double u) { return ( ( 1.0 - u ) * ( 1.0 - u ) * ( 1.0 - u ) ); } static inline double B1(double u) { return ( 3 * u * ( 1.0 - u ) * ( 1.0 - u ) ); } static inline double B2(double u) { return ( 3 * u * u * ( 1.0 - u ) ); } static inline double B3(double u) { return ( u * u * u ); } #ifdef BEZIER_DEBUG # define DOUBLE_ASSERT(x) g_assert( ( (x) > -SP_HUGE ) && ( (x) < SP_HUGE ) ) # define BEZIER_ASSERT(b) do { \ DOUBLE_ASSERT((b)[0].x()); DOUBLE_ASSERT((b)[0].y()); \ DOUBLE_ASSERT((b)[1].x()); DOUBLE_ASSERT((b)[1].y()); \ DOUBLE_ASSERT((b)[2].x()); DOUBLE_ASSERT((b)[2].y()); \ DOUBLE_ASSERT((b)[3].x()); DOUBLE_ASSERT((b)[3].y()); \ } while(0) #else # define DOUBLE_ASSERT(x) do { } while(0) # define BEZIER_ASSERT(b) do { } while(0) #endif /** * Fit a single-segment Bezier curve to a set of digitized points. * * \return Number of segments generated, or -1 on error. */ int sp_bezier_fit_cubic(QPointF *bezier, QPointF const *data, int len, double error) { return sp_bezier_fit_cubic_r(bezier, data, len, error, 1); } /** * Fit a multi-segment Bezier curve to a set of digitized points, with * possible weedout of identical points and NaNs. * * \param max_beziers Maximum number of generated segments * \param Result array, must be large enough for n. segments * 4 elements. * * \return Number of segments generated, or -1 on error. */ int sp_bezier_fit_cubic_r(QPointF bezier[], QPointF const data[], int const len, double const error, unsigned const max_beziers) { g_return_val_if_fail(bezier != NULL, -1); g_return_val_if_fail(data != NULL, -1); g_return_val_if_fail(len > 0, -1); g_return_val_if_fail(max_beziers < (1ul << (31 - 2 - 1 - 3)), -1); QPolygonF uniqued_data(len); unsigned uniqued_len = copy_without_nans_or_adjacent_duplicates(data, len, uniqued_data.data() ); if ( uniqued_len < 2 ) { return 0; } /* Call fit-cubic function with recursion. */ return sp_bezier_fit_cubic_full(bezier, NULL, uniqued_data.data(), uniqued_len, unconstrained_tangent, unconstrained_tangent, error, max_beziers); } /** * Copy points from src to dest, filter out points containing NaN and * adjacent points with equal x and y. * \return length of dest */ static unsigned copy_without_nans_or_adjacent_duplicates(QPointF const src[], unsigned src_len, QPointF dest[]) { unsigned si = 0; for (;;) { if ( si == src_len ) { return 0; } if (!isNaN(src[si].x()) && !isNaN(src[si].y())) { dest[0] = QPointF(src[si]); ++si; break; } } unsigned di = 0; for (; si < src_len; ++si) { QPointF const src_pt = QPointF(src[si]); if ( src_pt != dest[di] && !isNaN(src_pt.x()) && !isNaN(src_pt.y())) { dest[++di] = src_pt; } } unsigned dest_len = di + 1; g_assert( dest_len <= src_len ); return dest_len; } /** * Fit a multi-segment Bezier curve to a set of digitized points, without * possible weedout of identical points and NaNs. * * \pre data is uniqued, i.e. not exist i: data[i] == data[i + 1]. * \param max_beziers Maximum number of generated segments * \param Result array, must be large enough for n. segments * 4 elements. */ int sp_bezier_fit_cubic_full(QPointF bezier[], int split_points[], QPointF const data[], int const len, QPointF const &tHat1, QPointF const &tHat2, double const error, unsigned const max_beziers) { int const maxIterations = 4; /* Max times to try iterating */ g_return_val_if_fail(bezier != NULL, -1); g_return_val_if_fail(data != NULL, -1); g_return_val_if_fail(len > 0, -1); g_return_val_if_fail(max_beziers >= 1, -1); g_return_val_if_fail(error >= 0.0, -1); if ( len < 2 ) return 0; if ( len == 2 ) { /* We have 2 points, which can be fitted trivially. */ bezier[0] = data[0]; bezier[3] = data[len - 1]; double const dist = ( L2( data[len - 1] - data[0] ) *(1./3.) ); if (isNaN(dist)) { /* Numerical problem, fall back to straight line segment. */ bezier[1] = bezier[0]; bezier[2] = bezier[3]; } else { bezier[1] = ( is_zero(tHat1) ? ( 2 * bezier[0] + bezier[3] ) * (1./3.) : bezier[0] + dist * tHat1 ); bezier[2] = ( is_zero(tHat2) ? ( bezier[0] + 2 * bezier[3] ) * (1./3.) : bezier[3] + dist * tHat2 ); } BEZIER_ASSERT(bezier); return 1; } /* Parameterize points, and attempt to fit curve */ unsigned splitPoint; /* Point to split point set at. */ bool is_corner; { QVector u(len); chord_length_parameterize(data, u.data(), len); if ( u[len - 1] == 0.0 ) { /* Zero-length path: every point in data[] is the same. * * (Clients aren't allowed to pass such data; handling the case is defensive * programming.) */ return 0; } generate_bezier(bezier, data, u.data(), len, tHat1, tHat2, error); reparameterize(data, len, u.data(), bezier); /* Find max deviation of points to fitted curve. */ double const tolerance = sqrt(error + 1e-9); double maxErrorRatio = compute_max_error_ratio(data, u.data(), len, bezier, tolerance, &splitPoint); if ( fabs(maxErrorRatio) <= 1.0 ) { BEZIER_ASSERT(bezier); return 1; } /* If error not too large, then try some reparameterization and iteration. */ if ( 0.0 <= maxErrorRatio && maxErrorRatio <= 3.0 ) { for (int i = 0; i < maxIterations; i++) { generate_bezier(bezier, data, u.data(), len, tHat1, tHat2, error); reparameterize(data, len, u.data(), bezier); maxErrorRatio = compute_max_error_ratio(data, u.data(), len, bezier, tolerance, &splitPoint); if ( fabs(maxErrorRatio) <= 1.0 ) { BEZIER_ASSERT(bezier); return 1; } } } is_corner = (maxErrorRatio < 0); } if (is_corner) { g_assert(splitPoint < unsigned(len)); if (splitPoint == 0) { if (is_zero(tHat1)) { /* Got spike even with unconstrained initial tangent. */ ++splitPoint; } else { return sp_bezier_fit_cubic_full(bezier, split_points, data, len, unconstrained_tangent, tHat2, error, max_beziers); } } else if (splitPoint == unsigned(len - 1)) { if (is_zero(tHat2)) { /* Got spike even with unconstrained final tangent. */ --splitPoint; } else { return sp_bezier_fit_cubic_full(bezier, split_points, data, len, tHat1, unconstrained_tangent, error, max_beziers); } } } if ( 1 < max_beziers ) { /* * Fitting failed -- split at max error point and fit recursively */ unsigned const rec_max_beziers1 = max_beziers - 1; QPointF recTHat2, recTHat1; if (is_corner) { g_return_val_if_fail(0 < splitPoint && splitPoint < unsigned(len - 1), -1); recTHat1 = recTHat2 = unconstrained_tangent; } else { /* Unit tangent vector at splitPoint. */ recTHat2 = sp_darray_center_tangent(data, splitPoint, len); recTHat1 = -recTHat2; } int const nsegs1 = sp_bezier_fit_cubic_full(bezier, split_points, data, splitPoint + 1, tHat1, recTHat2, error, rec_max_beziers1); if ( nsegs1 < 0 ) { #ifdef BEZIER_DEBUG fprintf(stderr, "fit_cubic[1]: recursive call failed\n"); #endif return -1; } g_assert( nsegs1 != 0 ); if (split_points != NULL) { split_points[nsegs1 - 1] = splitPoint; } unsigned const rec_max_beziers2 = max_beziers - nsegs1; int const nsegs2 = sp_bezier_fit_cubic_full(bezier + nsegs1*4, ( split_points == NULL ? NULL : split_points + nsegs1 ), data + splitPoint, len - splitPoint, recTHat1, tHat2, error, rec_max_beziers2); if ( nsegs2 < 0 ) { #ifdef BEZIER_DEBUG fprintf(stderr, "fit_cubic[2]: recursive call failed\n"); #endif return -1; } #ifdef BEZIER_DEBUG fprintf(stderr, "fit_cubic: success[nsegs: %d+%d=%d] on max_beziers:%u\n", nsegs1, nsegs2, nsegs1 + nsegs2, max_beziers); #endif return nsegs1 + nsegs2; } else { return -1; } } /** * Fill in \a bezier[] based on the given data and tangent requirements, using * a least-squares fit. * * Each of tHat1 and tHat2 should be either a zero vector or a unit vector. * If it is zero, then bezier[1 or 2] is estimated without constraint; otherwise, * it bezier[1 or 2] is placed in the specified direction from bezier[0 or 3]. * * \param tolerance_sq Used only for an initial guess as to tangent directions * when \a tHat1 or \a tHat2 is zero. */ static void generate_bezier(QPointF bezier[], QPointF const data[], double const u[], unsigned const len, QPointF const &tHat1, QPointF const &tHat2, double const tolerance_sq) { bool const est1 = is_zero(tHat1); bool const est2 = is_zero(tHat2); QPointF est_tHat1( est1 ? sp_darray_left_tangent(data, len, tolerance_sq) : tHat1 ); QPointF est_tHat2( est2 ? sp_darray_right_tangent(data, len, tolerance_sq) : tHat2 ); estimate_lengths(bezier, data, u, len, est_tHat1, est_tHat2); /* We find that sp_darray_right_tangent tends to produce better results for our current freehand tool than full estimation. */ if (est1) { estimate_bi(bezier, 1, data, u, len); if (bezier[1] != bezier[0]) { est_tHat1 = unit_vector(bezier[1] - bezier[0]); } estimate_lengths(bezier, data, u, len, est_tHat1, est_tHat2); } } static void estimate_lengths(QPointF bezier[], QPointF const data[], double const uPrime[], unsigned const len, QPointF const &tHat1, QPointF const &tHat2) { double C[2][2]; /* Matrix C. */ double X[2]; /* Matrix X. */ /* Create the C and X matrices. */ C[0][0] = 0.0; C[0][1] = 0.0; C[1][0] = 0.0; C[1][1] = 0.0; X[0] = 0.0; X[1] = 0.0; /* First and last control points of the Bezier curve are positioned exactly at the first and last data points. */ bezier[0] = data[0]; bezier[3] = data[len - 1]; for (unsigned i = 0; i < len; i++) { /* Bezier control point coefficients. */ double const b0 = B0(uPrime[i]); double const b1 = B1(uPrime[i]); double const b2 = B2(uPrime[i]); double const b3 = B3(uPrime[i]); /* rhs for eqn */ QPointF const a1 = b1 * tHat1; QPointF const a2 = b2 * tHat2; C[0][0] += dot(a1, a1); C[0][1] += dot(a1, a2); C[1][0] = C[0][1]; C[1][1] += dot(a2, a2); /* Additional offset to the data point from the predicted point if we were to set bezier[1] to bezier[0] and bezier[2] to bezier[3]. */ QPointF const shortfall = ( data[i] - ( ( b0 + b1 ) * bezier[0] ) - ( ( b2 + b3 ) * bezier[3] ) ); X[0] += dot(a1, shortfall); X[1] += dot(a2, shortfall); } /* We've constructed a pair of equations in the form of a matrix product C * alpha = X. Now solve for alpha. */ double alpha_l, alpha_r; /* Compute the determinants of C and X. */ double const det_C0_C1 = C[0][0] * C[1][1] - C[1][0] * C[0][1]; if ( det_C0_C1 != 0 ) { /* Apparently Kramer's rule. */ double const det_C0_X = C[0][0] * X[1] - C[0][1] * X[0]; double const det_X_C1 = X[0] * C[1][1] - X[1] * C[0][1]; alpha_l = det_X_C1 / det_C0_C1; alpha_r = det_C0_X / det_C0_C1; } else { /* The matrix is under-determined. Try requiring alpha_l == alpha_r. * * One way of implementing the constraint alpha_l == alpha_r is to treat them as the same * variable in the equations. We can do this by adding the columns of C to form a single * column, to be multiplied by alpha to give the column vector X. * * We try each row in turn. */ double const c0 = C[0][0] + C[0][1]; if (c0 != 0) { alpha_l = alpha_r = X[0] / c0; } else { double const c1 = C[1][0] + C[1][1]; if (c1 != 0) { alpha_l = alpha_r = X[1] / c1; } else { /* Let the below code handle this. */ alpha_l = alpha_r = 0.; } } } /* If alpha negative, use the Wu/Barsky heuristic (see text). (If alpha is 0, you get coincident control points that lead to divide by zero in any subsequent NewtonRaphsonRootFind() call.) */ /// \todo Check whether this special-casing is necessary now that /// NewtonRaphsonRootFind handles non-positive denominator. if ( alpha_l < 1.0e-6 || alpha_r < 1.0e-6 ) { alpha_l = alpha_r = ( L2( data[len - 1] - data[0] ) * (1./3.) ); } /* Control points 1 and 2 are positioned an alpha distance out on the tangent vectors, left and right, respectively. */ bezier[1] = alpha_l * tHat1 + bezier[0]; bezier[2] = alpha_r * tHat2 + bezier[3]; return; } static double lensq(QPointF const p) { return dot(p, p); } static void estimate_bi(QPointF bezier[4], unsigned const ei, QPointF const data[], double const u[], unsigned const len) { g_return_if_fail(1 <= ei && ei <= 2); unsigned const oi = 3 - ei; QPointF num(0., 0.); double den = 0.; for (unsigned i = 0; i < len; ++i) { double const ui = u[i]; double const b[4] = { B0(ui), B1(ui), B2(ui), B3(ui) }; num.rx() += b[ei] * (b[0] * bezier[0].x() + b[oi] * bezier[0].x() + b[3] * bezier[3].x() + - data[i].x()); num.ry() += b[ei] * (b[0] * bezier[0].y() + b[oi] * bezier[0].y() + b[3] * bezier[3].y() + - data[i].y()); den -= b[ei] * b[ei]; } if (den != 0.) { bezier[ei] = num / den; } else { bezier[ei] = ( oi * bezier[0] + ei * bezier[3] ) * (1./3.); } } /** * Given set of points and their parameterization, try to find a better assignment of parameter * values for the points. * * \param d Array of digitized points. * \param u Current parameter values. * \param bezCurve Current fitted curve. * \param len Number of values in both d and u arrays. * Also the size of the array that is allocated for return. */ static void reparameterize(QPointF const d[], unsigned const len, double u[], BezierCurve const bezCurve) { g_assert( 2 <= len ); unsigned const last = len - 1; g_assert( bezCurve[0] == d[0] ); g_assert( bezCurve[3] == d[last] ); g_assert( u[0] == 0.0 ); g_assert( u[last] == 1.0 ); /* Otherwise, consider including 0 and last in the below loop. */ for (unsigned i = 1; i < last; i++) { u[i] = NewtonRaphsonRootFind(bezCurve, d[i], u[i]); } } /** * Use Newton-Raphson iteration to find better root. * * \param Q Current fitted curve * \param P Digitized point * \param u Parameter value for "P" * * \return Improved u */ static double NewtonRaphsonRootFind(BezierCurve const Q, QPointF const &P, double const u) { g_assert( 0.0 <= u ); g_assert( u <= 1.0 ); /* Generate control vertices for Q'. */ QPointF Q1[3]; for (unsigned i = 0; i < 3; i++) { Q1[i] = 3.0 * ( Q[i+1] - Q[i] ); } /* Generate control vertices for Q''. */ QPointF Q2[2]; for (unsigned i = 0; i < 2; i++) { Q2[i] = 2.0 * ( Q1[i+1] - Q1[i] ); } /* Compute Q(u), Q'(u) and Q''(u). */ QPointF const Q_u = bezier_pt(3, Q, u); QPointF const Q1_u = bezier_pt(2, Q1, u); QPointF const Q2_u = bezier_pt(1, Q2, u); /* Compute f(u)/f'(u), where f is the derivative wrt u of distsq(u) = 0.5 * the square of the distance from P to Q(u). Here we're using Newton-Raphson to find a stationary point in the distsq(u), hopefully corresponding to a local minimum in distsq (and hence a local minimum distance from P to Q(u)). */ QPointF const diff = Q_u - P; double numerator = dot(diff, Q1_u); double denominator = dot(Q1_u, Q1_u) + dot(diff, Q2_u); double improved_u; if ( denominator > 0. ) { /* One iteration of Newton-Raphson: improved_u = u - f(u)/f'(u) */ improved_u = u - ( numerator / denominator ); } else { /* Using Newton-Raphson would move in the wrong direction (towards a local maximum rather than local minimum), so we move an arbitrary amount in the right direction. */ if ( numerator > 0. ) { improved_u = u * .98 - .01; } else if ( numerator < 0. ) { /* Deliberately asymmetrical, to reduce the chance of cycling. */ improved_u = .031 + u * .98; } else { improved_u = u; } } if (!isFinite(improved_u)) { improved_u = u; } else if ( improved_u < 0.0 ) { improved_u = 0.0; } else if ( improved_u > 1.0 ) { improved_u = 1.0; } /* Ensure that improved_u isn't actually worse. */ { double const diff_lensq = lensq(diff); for (double proportion = .125; ; proportion += .125) { if ( lensq( bezier_pt(3, Q, improved_u) - P ) > diff_lensq ) { if ( proportion > 1.0 ) { //g_warning("found proportion %g", proportion); improved_u = u; break; } improved_u = ( ( 1 - proportion ) * improved_u + proportion * u ); } else { break; } } } DOUBLE_ASSERT(improved_u); return improved_u; } /** * Evaluate a Bezier curve at parameter value \a t. * * \param degree The degree of the Bezier curve: 3 for cubic, 2 for quadratic etc. * \param V The control points for the Bezier curve. Must have (\a degree+1) * elements. * \param t The "parameter" value, specifying whereabouts along the curve to * evaluate. Typically in the range [0.0, 1.0]. * * Let s = 1 - t. * BezierII(1, V) gives (s, t) * V, i.e. t of the way * from V[0] to V[1]. * BezierII(2, V) gives (s**2, 2*s*t, t**2) * V. * BezierII(3, V) gives (s**3, 3 s**2 t, 3s t**2, t**3) * V. * * The derivative of BezierII(i, V) with respect to t * is i * BezierII(i-1, V'), where for all j, V'[j] = * V[j + 1] - V[j]. */ QPointF bezier_pt(unsigned const degree, QPointF const V[], double const t) { /** Pascal's triangle. */ static int const pascal[4][4] = {{1}, {1, 1}, {1, 2, 1}, {1, 3, 3, 1}}; g_assert( degree < G_N_ELEMENTS(pascal) ); double const s = 1.0 - t; /* Calculate powers of t and s. */ double spow[4]; double tpow[4]; spow[0] = 1.0; spow[1] = s; tpow[0] = 1.0; tpow[1] = t; for (unsigned i = 1; i < degree; ++i) { spow[i + 1] = spow[i] * s; tpow[i + 1] = tpow[i] * t; } QPointF ret = spow[degree] * V[0]; for (unsigned i = 1; i <= degree; ++i) { ret += pascal[degree][i] * spow[degree - i] * tpow[i] * V[i]; } return ret; } /* * ComputeLeftTangent, ComputeRightTangent, ComputeCenterTangent : * Approximate unit tangents at endpoints and "center" of digitized curve */ /** * Estimate the (forward) tangent at point d[first + 0.5]. * * Unlike the center and right versions, this calculates the tangent in * the way one might expect, i.e., wrt increasing index into d. * \pre (2 \<= len) and (d[0] != d[1]). **/ QPointF sp_darray_left_tangent(QPointF const d[], unsigned const len) { g_assert( len >= 2 ); g_assert( d[0] != d[1] ); return unit_vector( d[1] - d[0] ); } /** * Estimates the (backward) tangent at d[last - 0.5]. * * \note The tangent is "backwards", i.e. it is with respect to * decreasing index rather than increasing index. * * \pre 2 \<= len. * \pre d[len - 1] != d[len - 2]. * \pre all[p in d] in_svg_plane(p). */ static QPointF sp_darray_right_tangent(QPointF const d[], unsigned const len) { g_assert( 2 <= len ); unsigned const last = len - 1; unsigned const prev = last - 1; g_assert( d[last] != d[prev] ); return unit_vector( d[prev] - d[last] ); } /** * Estimate the (forward) tangent at point d[0]. * * Unlike the center and right versions, this calculates the tangent in * the way one might expect, i.e., wrt increasing index into d. * * \pre 2 \<= len. * \pre d[0] != d[1]. * \pre all[p in d] in_svg_plane(p). * \post is_unit_vector(ret). **/ QPointF sp_darray_left_tangent(QPointF const d[], unsigned const len, double const tolerance_sq) { g_assert( 2 <= len ); g_assert( 0 <= tolerance_sq ); for (unsigned i = 1;;) { QPointF const pi(d[i]); QPointF const t(pi - d[0]); double const distsq = dot(t, t); if ( tolerance_sq < distsq ) { return unit_vector(t); } ++i; if (i == len) { return ( distsq == 0 ? sp_darray_left_tangent(d, len) : unit_vector(t) ); } } } /** * Estimates the (backward) tangent at d[last]. * * \note The tangent is "backwards", i.e. it is with respect to * decreasing index rather than increasing index. * * \pre 2 \<= len. * \pre d[len - 1] != d[len - 2]. * \pre all[p in d] in_svg_plane(p). */ QPointF sp_darray_right_tangent(QPointF const d[], unsigned const len, double const tolerance_sq) { g_assert( 2 <= len ); g_assert( 0 <= tolerance_sq ); unsigned const last = len - 1; for (unsigned i = last - 1;; i--) { QPointF const pi(d[i]); QPointF const t(pi - d[last]); double const distsq = dot(t, t); if ( tolerance_sq < distsq ) { return unit_vector(t); } if (i == 0) { return ( distsq == 0 ? sp_darray_right_tangent(d, len) : unit_vector(t) ); } } } /** * Estimates the (backward) tangent at d[center], by averaging the two * segments connected to d[center] (and then normalizing the result). * * \note The tangent is "backwards", i.e. it is with respect to * decreasing index rather than increasing index. * * \pre (0 \< center \< len - 1) and d is uniqued (at least in * the immediate vicinity of \a center). */ static QPointF sp_darray_center_tangent(QPointF const d[], unsigned const center, unsigned const len) { g_assert( center != 0 ); g_assert( center < len - 1 ); QPointF ret; if ( d[center + 1] == d[center - 1] ) { /* Rotate 90 degrees in an arbitrary direction. */ QPointF const diff = d[center] - d[center - 1]; ret = rot90(diff); } else { ret = d[center - 1] - d[center + 1]; } return unit_vector(ret); } /** * Assign parameter values to digitized points using relative distances between points. * * \pre Parameter array u must have space for \a len items. */ static void chord_length_parameterize(QPointF const d[], double u[], unsigned const len) { g_return_if_fail( 2 <= len ); /* First let u[i] equal the distance travelled along the path from d[0] to d[i]. */ u[0] = 0.0; for (unsigned i = 1; i < len; i++) { double const dist = L2( d[i] - d[i-1] ); u[i] = u[i-1] + dist; } /* Then scale to [0.0 .. 1.0]. */ double tot_len = u[len - 1]; g_return_if_fail( tot_len != 0 ); if (isFinite(tot_len)) { for (unsigned i = 1; i < len; ++i) { u[i] /= tot_len; } } else { /* We could do better, but this probably never happens anyway. */ for (unsigned i = 1; i < len; ++i) { u[i] = i / (double) ( len - 1 ); } } /** \todo * It's been reported that u[len - 1] can differ from 1.0 on some * systems (amd64), despite it having been calculated as x / x where x * is isFinite and non-zero. */ if (u[len - 1] != 1) { double const diff = u[len - 1] - 1; if (fabs(diff) > 1e-13) { fprintf(stderr, "u[len - 1] = %19g (= 1 + %19g), expecting exactly 1", u[len - 1], diff); } u[len - 1] = 1; } #ifdef BEZIER_DEBUG g_assert( u[0] == 0.0 && u[len - 1] == 1.0 ); for (unsigned i = 1; i < len; i++) { g_assert( u[i] >= u[i-1] ); } #endif } /** * Find the maximum squared distance of digitized points to fitted curve, and (if this maximum * error is non-zero) set \a *splitPoint to the corresponding index. * * \pre 2 \<= len. * \pre u[0] == 0. * \pre u[len - 1] == 1.0. * \post ((ret == 0.0) * || ((*splitPoint \< len - 1) * \&\& (*splitPoint != 0 || ret \< 0.0))). */ static double compute_max_error_ratio(QPointF const d[], double const u[], unsigned const len, BezierCurve const bezCurve, double const tolerance, unsigned *const splitPoint) { g_assert( 2 <= len ); unsigned const last = len - 1; g_assert( bezCurve[0] == d[0] ); g_assert( bezCurve[3] == d[last] ); g_assert( u[0] == 0.0 ); g_assert( u[last] == 1.0 ); /* I.e. assert that the error for the first & last points is zero. * Otherwise we should include those points in the below loop. * The assertion is also necessary to ensure 0 < splitPoint < last. */ double maxDistsq = 0.0; /* Maximum error */ double max_hook_ratio = 0.0; unsigned snap_end = 0; QPointF prev = bezCurve[0]; for (unsigned i = 1; i <= last; i++) { QPointF const curr = bezier_pt(3, bezCurve, u[i]); double const distsq = lensq( curr - d[i] ); if ( distsq > maxDistsq ) { maxDistsq = distsq; *splitPoint = i; } double const hook_ratio = compute_hook(prev, curr, .5 * (u[i - 1] + u[i]), bezCurve, tolerance); if (max_hook_ratio < hook_ratio) { max_hook_ratio = hook_ratio; snap_end = i; } prev = curr; } double const dist_ratio = sqrt(maxDistsq) / tolerance; double ret; if (max_hook_ratio <= dist_ratio) { ret = dist_ratio; } else { g_assert(0 < snap_end); ret = -max_hook_ratio; *splitPoint = snap_end - 1; } g_assert( ret == 0.0 || ( ( *splitPoint < last ) && ( *splitPoint != 0 || ret < 0. ) ) ); return ret; } /** * Whereas compute_max_error_ratio() checks for itself that each data point * is near some point on the curve, this function checks that each point on * the curve is near some data point (or near some point on the polyline * defined by the data points, or something like that: we allow for a * "reasonable curviness" from such a polyline). "Reasonable curviness" * means we draw a circle centred at the midpoint of a..b, of radius * proportional to the length |a - b|, and require that each point on the * segment of bezCurve between the parameters of a and b be within that circle. * If any point P on the bezCurve segment is outside of that allowable * region (circle), then we return some metric that increases with the * distance from P to the circle. * * Given that this is a fairly arbitrary criterion for finding appropriate * places for sharp corners, we test only one point on bezCurve, namely * the point on bezCurve with parameter halfway between our estimated * parameters for a and b. (Alternatives are taking the farthest of a * few parameters between those of a and b, or even using a variant of * NewtonRaphsonFindRoot() for finding the maximum rather than minimum * distance.) */ static double compute_hook(QPointF const &a, QPointF const &b, double const u, BezierCurve const bezCurve, double const tolerance) { QPointF const P = bezier_pt(3, bezCurve, u); QPointF const diff = .5 * (a + b) - P; double const dist = L2(diff); if (dist < tolerance) { return 0; } // factor of 0.1 introduced by JSS to stop more hooks double const allowed = L2(b - a)*0.1 + tolerance; return dist / allowed; /** \todo * effic: Hooks are very rare. We could start by comparing * distsq, only resorting to the more expensive L2 in cases of * uncertainty. */ } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������veusz-3.0.1/veusz/helpers/src/qtloops/polygonclip.h�������������������������������������������������0000664�0001750�0001750�00000002415�13161413406�022130� 0����������������������������������������������������������������������������������������������������ustar �jss�����������������������������jss�����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// -*- mode: C++; -*- // Copyright (C) 2010 Jeremy Sanders // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program 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 General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA // 02110-1301, USA. //////////////////////////////////////////////////////////////////// #ifndef POLYGONCLIP_HH #define POLYGONCLIP_HH #include #include #include #include // clip input polygon to clipping rectangle, filling outpoly void polygonClip(const QPolygonF& inpoly, const QRectF& cliprect, QPolygonF& outpoly); // plot a clipped polygon to painter void plotClippedPolygon(QPainter& painter, QRectF rect, const QPolygonF& inpoly, bool autoexpand = true); #endif ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������veusz-3.0.1/veusz/helpers/src/qtloops/qtloops.sip���������������������������������������������������0000664�0001750�0001750�00000016335�13302252613�021642� 0����������������������������������������������������������������������������������������������������ustar �jss�����������������������������jss�����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright (C) 2009 Jeremy S. Sanders // Email: Jeremy Sanders // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program 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 General Public License for more details. // // You should have received a copy of the GNU General Public License along // with this program; if not, write to the Free Software Foundation, Inc., // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ///////////////////////////////////////////////////////////////////////////// %Module(name=qtloops) %Import(name=QtCore/QtCoremod.sip) %Import(name=QtGui/QtGuimod.sip) %ModuleHeaderCode #include %End %PostInitialisationCode do_numpy_init_package(); %End class QtLoops { %TypeHeaderCode #include #include #include #include #include %End public: QtLoops(); }; void addNumpyToPolygonF(QPolygonF&, ...); %MethodCode { try { Tuple2Ptrs t(a1); addNumpyToPolygonF(*a0, t); } catch( const char *msg ) { sipIsErr = 1; PyErr_SetString(PyExc_TypeError, msg); } } %End void addNumpyPolygonToPath(QPainterPath&, const QRectF*, ...); %MethodCode { try { Tuple2Ptrs t(a2); addNumpyPolygonToPath(*a0, t, a1); } catch( const char *msg ) { sipIsErr = 1; PyErr_SetString(PyExc_TypeError, msg); } } %End void plotPathsToPainter(QPainter&, QPainterPath&, SIP_PYOBJECT, SIP_PYOBJECT, SIP_PYOBJECT, const QRectF* clip=0, const QImage* colorimg=0, bool scaleline=false); %MethodCode { Numpy1DObj* scaling = 0; try { // x and y coordinates Numpy1DObj x(a2); Numpy1DObj y(a3); // a4 is scaling or None if (a4 != Py_None) { scaling = new Numpy1DObj(a4); } plotPathsToPainter(*a0, *a1, x, y, scaling, a5, a6, a7); } catch( const char *msg ) { sipIsErr = 1; PyErr_SetString(PyExc_TypeError, msg); } delete scaling; } %End void plotLinesToPainter(QPainter& painter, SIP_PYOBJECT, SIP_PYOBJECT, SIP_PYOBJECT, SIP_PYOBJECT, const QRectF* clip = 0, bool autoexpand = true); %MethodCode { try { Numpy1DObj x1(a1); Numpy1DObj y1(a2); Numpy1DObj x2(a3); Numpy1DObj y2(a4); plotLinesToPainter(*a0, x1, y1, x2, y2, a5, a6); } catch( const char *msg ) { sipIsErr = 1; PyErr_SetString(PyExc_TypeError, msg); } } %End void plotBoxesToPainter(QPainter& painter, SIP_PYOBJECT, SIP_PYOBJECT, SIP_PYOBJECT, SIP_PYOBJECT, const QRectF* clip = 0, bool autoexpand = true); %MethodCode { try { Numpy1DObj x1(a1); Numpy1DObj y1(a2); Numpy1DObj x2(a3); Numpy1DObj y2(a4); plotBoxesToPainter(*a0, x1, y1, x2, y2, a5, a6); } catch( const char *msg ) { sipIsErr = 1; PyErr_SetString(PyExc_TypeError, msg); } } %End void addCubicsToPainterPath(QPainterPath& path, const QPolygonF& poly); QImage numpyToQImage(SIP_PYOBJECT, SIP_PYOBJECT, bool forcetrans = false); %MethodCode { try { Numpy2DObj data(a0); Numpy2DIntObj colors(a1); QImage *img = new QImage( numpyToQImage(data, colors, a2) ); sipRes = img; } catch( const char *msg ) { sipIsErr = 1; PyErr_SetString(PyExc_TypeError, msg); } } %End void applyImageTransparancy(QImage& img, SIP_PYOBJECT); %MethodCode { try { Numpy2DObj data(a1); applyImageTransparancy(*a0, data); } catch( const char *msg ) { sipIsErr = 1; PyErr_SetString(PyExc_TypeError, msg); } } %End QImage resampleLinearImage(QImage& img, SIP_PYOBJECT, SIP_PYOBJECT); %MethodCode { try { Numpy1DObj xpts(a1); Numpy1DObj ypts(a2); QImage *oimg = new QImage( resampleLinearImage(*a0, xpts, ypts) ); sipRes = oimg; } catch( const char *msg ) { sipIsErr = 1; PyErr_SetString(PyExc_TypeError, msg); } } %End void polygonClip(const QPolygonF& inpoly, const QRectF& cliprect, QPolygonF& outpoly); void plotClippedPolygon(QPainter& painter, QRectF rect, const QPolygonF& inpoly, bool autoexpand = true); // polyline clipping code void plotClippedPolyline(QPainter& painter, QRectF clip, const QPolygonF& poly, bool autoexpand = true); // clip polyline to rectangle and return polylines QVector clipPolyline(QRectF clip, const QPolygonF& poly); // Do the polygons intersect? bool doPolygonsIntersect(const QPolygonF& a, const QPolygonF& b); // storing rotated rectangles struct RotatedRectangle { %TypeHeaderCode #include %End RotatedRectangle(double _cx, double _cy, double _xw, double _yw, double _angle); bool isValid() const; void rotate(double dtheta); void rotateAboutOrigin(double dtheta); void translate(double dx, double dy); QPolygonF makePolygon() const; double cx; double cy; double xw; double yw; double angle; }; // for labelling of sets of contour lines class LineLabeller { %TypeHeaderCode #include %End public: LineLabeller(QRectF cliprect, bool rotatelabels); virtual ~LineLabeller(); // override this to receive the label to draw virtual void drawAt(int idx, RotatedRectangle r); void addLine(const QPolygonF& poly, QSizeF textsize); void process(); int getNumPolySets() const; QVector getPolySet(int i) const; }; class RectangleOverlapTester { %TypeHeaderCode #include %End public: RectangleOverlapTester(); bool willOverlap(const RotatedRectangle& rect) const; void addRect(const RotatedRectangle& rect); void reset(); void debug(QPainter& painter) const; }; // bezier functions QPolygonF bezier_fit_cubic_single(const QPolygonF& data, double error); QPolygonF bezier_fit_cubic_multi(const QPolygonF& data, double error, unsigned max_beziers); SIP_PYOBJECT binData(SIP_PYOBJECT data, int binning, bool average); %MethodCode try { Numpy1DObj d(a0); double* data; int numelem; binData(d, a1, a2, &numelem, &data); sipRes = doubleArrayToNumpy(data, numelem); delete[] data; } catch( const char *msg ) { sipIsErr = 1; PyErr_SetString(PyExc_TypeError, msg); } %End SIP_PYOBJECT rollingAverage(SIP_PYOBJECT data, SIP_PYOBJECT weights, int width); %MethodCode { Numpy1DObj* weightarray = 0; try { Numpy1DObj d(a0); if( a1 != Py_None ) { weightarray = new Numpy1DObj(a1); } double* data; int numelem; rollingAverage(d, weightarray, a2, &numelem, &data); sipRes = doubleArrayToNumpy(data, numelem); delete[] data; } catch( const char *msg ) { sipIsErr = 1; PyErr_SetString(PyExc_TypeError, msg); } delete weightarray; } %End ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������veusz-3.0.1/veusz/helpers/src/qtloops/beziers.h�����������������������������������������������������0000664�0001750�0001750�00000002501�13161413406�021230� 0����������������������������������������������������������������������������������������������������ustar �jss�����������������������������jss�����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// -*- mode: C++; -*- #ifndef SP_BEZIERS_H #define SP_BEZIERS_H /* * An Algorithm for Automatically Fitting Digitized Curves * by Philip J. Schneider * from "Graphics Gems", Academic Press, 1990 * * Authors: * Philip J. Schneider * Lauris Kaplinski * * Copyright (C) 1990 Philip J. Schneider * Copyright (C) 2001 Lauris Kaplinski and Ximian, Inc. * * Released under GNU GPL */ /* Bezier approximation utils */ // Modified to be based around QPointF by Jeremy Sanders (2007) #include QPointF bezier_pt(unsigned const degree, QPointF const V[], double const t); int sp_bezier_fit_cubic(QPointF bezier[], QPointF const *data, int len, double error); int sp_bezier_fit_cubic_r(QPointF bezier[], QPointF const data[], int len, double error, unsigned max_beziers); int sp_bezier_fit_cubic_full(QPointF bezier[], int split_points[], QPointF const data[], int len, QPointF const &tHat1, QPointF const &tHat2, double error, unsigned max_beziers); QPointF sp_darray_left_tangent(QPointF const d[], unsigned const len); QPointF sp_darray_left_tangent(QPointF const d[], unsigned const len, double const tolerance_sq); QPointF sp_darray_right_tangent(QPointF const d[], unsigned const length, double const tolerance_sq); #endif /* SP_BEZIERS_H */ �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������veusz-3.0.1/veusz/helpers/src/qtloops/polygonclip.cpp�����������������������������������������������0000664�0001750�0001750�00000012437�13161413406�022470� 0����������������������������������������������������������������������������������������������������ustar �jss�����������������������������jss�����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright (C) 2010 Jeremy Sanders // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program 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 General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA // 02110-1301, USA. //////////////////////////////////////////////////////////////////// // This code uses the Sutherland Hodgman algorithm to clip a polygon // It is inspired by another version by Sjaak Priester // see http://www.codeguru.com/Cpp/misc/misc/graphics/article.php/c8965/ #include #include "polygonclip.h" using std::abs; // macro to clip point against edge // - edge: name of edge for clipping // - isinside: test whether point is inside edge // - intercept: function to calculate coordinate where line // crosses edge // - next: function to call next to clip point #define CLIPEDGE(edge, isinside, intercept, next) \ void edge##ClipPoint(const QPointF& pt) \ { \ QPointF& lastpt = edge##last; \ if( edge##is1st ) \ { \ /* do nothing */ \ edge##1st = pt; \ edge##is1st = false; \ } \ else \ { \ if( isinside(pt) ) \ { \ if( ! isinside(lastpt) ) \ /* this point inside and last point outside */ \ next( intercept(clip.edge(), pt, lastpt) ); \ next(pt); \ } \ else \ { \ if( isinside(lastpt) ) \ /* this point outside and last point inside */ \ next( intercept(clip.edge(), pt, lastpt) ); \ /* else do nothing if both outside */ \ } \ } \ \ lastpt = pt; \ } // tolerance for points being the same #define TOL 1e-5 namespace { inline QPointF interceptVert(qreal horzval, const QPointF& pt1, const QPointF& pt2) { const qreal gradient = (pt2.y()-pt1.y()) / (pt2.x()-pt1.x()); return QPointF(horzval, (horzval - pt1.x())*gradient + pt1.y()); } inline QPointF interceptHorz(qreal vertval, const QPointF& pt1, const QPointF& pt2) { const qreal gradient = (pt2.x()-pt1.x()) / (pt2.y()-pt1.y()); return QPointF((vertval - pt1.y())*gradient + pt1.x(), vertval); } // greater than or close inline int gtclose(qreal v1, qreal v2) { return v1 > v2 || abs(v1-v2) < TOL; } // less than or close inline int ltclose(qreal v1, qreal v2) { return v1 < v2 || abs(v1-v2) < TOL; } struct State { State(const QRectF& rect, QPolygonF& out) : clip(rect), output(out), leftis1st(true), rightis1st(true), topis1st(true), bottomis1st(true) { } // tests for whether point is inside of outside of each side inline bool insideBottom(const QPointF& pt) const { return ltclose(pt.y(), clip.bottom()); } inline bool insideTop(const QPointF& pt) const { return gtclose(pt.y(), clip.top()); } inline bool insideRight(const QPointF& pt) const { return ltclose(pt.x(), clip.right()); } inline bool insideLeft(const QPointF& pt) const { return gtclose(pt.x(), clip.left()); } // add functions for clipping to each edge CLIPEDGE(bottom, insideBottom, interceptHorz, writeClipPoint) CLIPEDGE(top, insideTop, interceptHorz, bottomClipPoint) CLIPEDGE(right, insideRight, interceptVert, topClipPoint) CLIPEDGE(left, insideLeft, interceptVert, rightClipPoint) // finally writes to output void writeClipPoint(const QPointF& pt) { // don't add the same point if( output.empty() || abs(pt.x() - output.last().x()) > TOL || abs(pt.y() - output.last().y()) > TOL ) output << pt; } /* location of corners of clip rectangle */ QRectF clip; /* output points are added here */ QPolygonF &output; /* last points added */ QPointF leftlast, rightlast, toplast, bottomlast; /* first point for each stage */ QPointF left1st, right1st, top1st, bottom1st; /* whether this is the 1st point through */ bool leftis1st, rightis1st, topis1st, bottomis1st; }; } void polygonClip(const QPolygonF& inpoly, const QRectF& cliprect, QPolygonF& out) { // construct initial state State state(cliprect, out); // do the clipping for(QPolygonF::const_iterator pt = inpoly.begin(); pt != inpoly.end(); ++pt) { state.leftClipPoint(*pt); } // complete state.leftClipPoint(state.left1st); state.rightClipPoint(state.right1st); state.topClipPoint(state.top1st); state.bottomClipPoint(state.bottom1st); } void plotClippedPolygon(QPainter& painter, QRectF rect, const QPolygonF& inpoly, bool autoexpand) { if ( autoexpand ) { const qreal lw = painter.pen().widthF(); if( painter.pen().style() != Qt::NoPen ) rect.adjust(-lw, -lw, lw, lw); } QPolygonF plt; polygonClip(inpoly, rect, plt); painter.drawPolygon(plt); } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������veusz-3.0.1/veusz/helpers/src/qtloops/polylineclip.h������������������������������������������������0000664�0001750�0001750�00000007117�13302252613�022276� 0����������������������������������������������������������������������������������������������������ustar �jss�����������������������������jss�����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// -*- mode: C++; -*- // Copyright (C) 2010 Jeremy Sanders // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program 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 General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA // 02110-1301, USA. //////////////////////////////////////////////////////////////////// #ifndef POLYLINECLIP_HH #define POLYLINECLIP_HH #include #include #include #include #include // clip a line made up of the points given, returning true // if is in region or false if not bool clipLine(const QRectF& clip, QPointF& pt1, QPointF& pt2); // plot a polyline poly on the painter, clipping by the rectangle given // if autoexpand is true, then the rectangle is expanded by the line width void plotClippedPolyline(QPainter& painter, QRectF clip, const QPolygonF& poly, bool autoexpand = true); // clip polyline to rectangle // return list of lines to plot QVector clipPolyline(QRectF clip, const QPolygonF& poly); // Do the polygons intersect? bool doPolygonsIntersect(const QPolygonF& a, const QPolygonF& b); // class for describing a rectangle with a rotation angle struct RotatedRectangle { // a lot of boilerplate so it can go in QVector RotatedRectangle() : cx(0), cy(0), xw(0), yw(0), angle(0) {} RotatedRectangle(double _cx, double _cy, double _xw, double _yw, double _angle) : cx(_cx), cy(_cy), xw(_xw), yw(_yw), angle(_angle) {} RotatedRectangle(const RotatedRectangle& o) : cx(o.cx), cy(o.cy), xw(o.xw), yw(o.yw), angle(o.angle) {} RotatedRectangle &operator=(const RotatedRectangle& o) { cx = o.cx; cy = o.cy; xw=o.xw; yw=o.yw; angle=o.angle; return *this; } bool isValid() const { return xw > 0 && yw > 0; } void rotate(double dtheta) { angle += dtheta; } void rotateAboutOrigin(double dtheta); void translate(double dx, double dy) { cx+=dx; cy+=dy; } QPolygonF makePolygon() const; double cx, cy, xw, yw, angle; }; // for labelling of sets of contour lines class LineLabeller { public: LineLabeller(QRectF cliprect, bool rotatelabels); virtual ~LineLabeller(); // override this to receive the label to draw virtual void drawAt(int idx, RotatedRectangle r); void addLine(const QPolygonF& poly, QSizeF textsize); void process(); int getNumPolySets() const { return _polys.size(); }; QVector getPolySet(int i) const; private: RotatedRectangle findLinePosition(const QPolygonF& poly, double frac, QSizeF size); private: QRectF _cliprect; bool _rotatelabels; QVector< QVector > _polys; QVector _textsizes; }; class RectangleOverlapTester { public: RectangleOverlapTester(); bool willOverlap(const RotatedRectangle& rect) const; void addRect(const RotatedRectangle& rect) { _rects.append(rect); }; void reset() { _rects.clear(); }; // debug by drawing all the rectangles void debug(QPainter& painter) const; private: QVector _rects; }; #endif �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������veusz-3.0.1/veusz/helpers/src/qtloops/numpyfuncs.h��������������������������������������������������0000664�0001750�0001750�00000002635�13161413406�022004� 0����������������������������������������������������������������������������������������������������ustar �jss�����������������������������jss�����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright (C) 2011 Jeremy S. Sanders // Email: Jeremy Sanders // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program 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 General Public License for more details. // // You should have received a copy of the GNU General Public License along // with this program; if not, write to the Free Software Foundation, Inc., // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ///////////////////////////////////////////////////////////////////////////// #ifndef NUMPYFUNCS_HH #define NUMPYFUNCS_HH #include "qtloops_helpers.h" // bin up data given by factor. If average is True, then divide by number // of elements in bins void binData(const Numpy1DObj& indata, int binning, bool average, int* numoutbins, double** outdata); // rolling average calculation // weights is an optional weighting array void rollingAverage(const Numpy1DObj& indata, const Numpy1DObj* weights, int width, int* numoutbins, double** outdata); #endif ���������������������������������������������������������������������������������������������������veusz-3.0.1/veusz/helpers/src/qtloops/beziers_qtwrap.cpp��������������������������������������������0000664�0001750�0001750�00000003220�13161413406�023160� 0����������������������������������������������������������������������������������������������������ustar �jss�����������������������������jss�����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright (C) 2010 Jeremy S. Sanders // Email: Jeremy Sanders // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program 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 General Public License for more details. // // You should have received a copy of the GNU General Public License along // with this program; if not, write to the Free Software Foundation, Inc., // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ///////////////////////////////////////////////////////////////////////////// #include "beziers.h" #include "beziers_qtwrap.h" QPolygonF bezier_fit_cubic_single( const QPolygonF& data, double error ) { QPolygonF out(4); const int retn = sp_bezier_fit_cubic(out.data(), data.data(), data.count(), error); if( retn >= 0 ) return out; else return QPolygonF(); } QPolygonF bezier_fit_cubic_multi( const QPolygonF& data, double error, unsigned max_beziers ) { QPolygonF out(4*max_beziers); const int retn = sp_bezier_fit_cubic_r(out.data(), data.data(), data.count(), error, max_beziers); if( retn >= 0 ) { // get rid of unused points if( retn*4 < out.count() ) out.remove( retn*4, out.count()-retn*4 ); return out; } else return QPolygonF(); } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������veusz-3.0.1/veusz/helpers/src/qtloops/qtloops_helpers.h���������������������������������������������0000664�0001750�0001750�00000005217�13161413406�023017� 0����������������������������������������������������������������������������������������������������ustar �jss�����������������������������jss�����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// -*- mode: C++; -*- #ifndef QTLOOPS_HELPERS_H #define QTLOOPS_HELPERS_H // Copyright (C) 2009 Jeremy S. Sanders // Email: Jeremy Sanders // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program 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 General Public License for more details. // // You should have received a copy of the GNU General Public License along // with this program; if not, write to the Free Software Foundation, Inc., // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ///////////////////////////////////////////////////////////////////////////// #include "Python.h" #include #define DEBUG false void do_numpy_init_package(); // class for converting tuples to objects which clean themselves up // throws const char* if conversion failed class Tuple2Ptrs { public: Tuple2Ptrs(PyObject* tuple); ~Tuple2Ptrs(); // data in tuple are stored here QVector data; QVector dims; private: // these are the python objects made by PyArray_AsCArray QVector _arrays; }; // class for converting numpy array to an array class Numpy1DObj { public: Numpy1DObj(PyObject* array); ~Numpy1DObj(); const double* data; int dim; inline double operator()(const int x) const { if( DEBUG && (x < 0 || x >= dim) ) throw "Invalid index in array"; return data[x]; } private: PyObject* _array; }; // class for converting a 2D numpy array to an array class Numpy2DObj { public: Numpy2DObj(PyObject* array); ~Numpy2DObj(); const double* data; int dims[2]; inline double operator()(const int x, const int y) const { if( DEBUG && (x < 0 || x >= dims[0] || y < 0 || y >= dims[1]) ) throw "Invalid index in array"; return data[x+y*dims[1]]; } private: PyObject* _array; }; // class for converting a 2D numpy array to an integer array class Numpy2DIntObj { public: Numpy2DIntObj(PyObject* array); ~Numpy2DIntObj(); const int* data; int dims[2]; inline int operator()(const int x, const int y) const { if( DEBUG && (x < 0 || x >= dims[0] || y < 0 || y >= dims[1]) ) throw "Invalid index in array"; return data[x+y*dims[1]]; } private: PyObject* _array; }; PyObject* doubleArrayToNumpy(const double* d, int len); #endif ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������veusz-3.0.1/veusz/helpers/src/qtloops/isnan.h�������������������������������������������������������0000664�0001750�0001750�00000004301�13161413406�020675� 0����������������������������������������������������������������������������������������������������ustar �jss�����������������������������jss�����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// -*- mode: C++; -*- #ifndef __ISNAN_H__ #define __ISNAN_H__ /* * Temporary fix for various misdefinitions of isnan(). * isnan() is becoming undef'd in some .h files. * #include this last in your .cpp file to get it right. * * The problem is that isnan and isfinite are part of C99 but aren't part of * the C++ standard (which predates C99). * * Authors: * Inkscape groupies and obsessive-compulsives * * Copyright (C) 2004 authors * * Released under GNU GPL, read the file 'COPYING' for more information * * 2005 modification hereby placed in public domain. Probably supercedes the 2004 copyright * for the code itself. */ #include #include /* You might try changing the above to if you have problems. * Whether you use math.h or cmath, you may need to edit the .cpp file * and/or other .h files to use the same header file. */ #if defined(__isnan) # define isNaN(_a) (__isnan(_a)) /* MacOSX/Darwin definition < 10.4 */ #elif defined(WIN32) || defined(_isnan) || defined(_MSC_VER) # define isNaN(_a) (_isnan(_a)) /* Win32 definition */ #elif defined(isnan) || defined(__FreeBSD__) || defined(__osf__) # define isNaN(_a) (isnan(_a)) /* GNU definition */ #else # define isNaN(_a) (std::isnan(_a)) #endif /* If the above doesn't work, then try (a != a). * Also, please report a bug as per http://www.inkscape.org/report_bugs.php, * giving information about what platform and compiler version you're using. */ #if defined(__isfinite) # define isFinite(_a) (__isfinite(_a)) /* MacOSX/Darwin definition < 10.4 */ #elif defined(WIN32) || defined(_finite) || defined(_MSC_VER) # define isFinite(_a) (_finite(_a)) /* Win32 definition */ #elif defined(__sgi) # define isFinite(_a) (_isfinite(_a)) #elif defined(isfinite) || defined(__FreeBSD__) # define isFinite(_a) (isfinite(_a)) #elif defined(__osf__) # define isFinite(_a) (finite(_a) && !isNaN(_a)) #else # define isFinite(_a) (std::isfinite(_a)) #endif /* If the above doesn't work, then try (finite(_a) && !isNaN(_a)) or (!isNaN((_a) - (_a))). * Also, please report a bug as per http://www.inkscape.org/report_bugs.php, * giving information about what platform and compiler version you're using. */ #endif /* __ISNAN_H__ */ �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������veusz-3.0.1/veusz/helpers/src/qtloops/qtloops.h�����������������������������������������������������0000664�0001750�0001750�00000005145�13161413406�021275� 0����������������������������������������������������������������������������������������������������ustar �jss�����������������������������jss�����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// -*- mode: C++; -*- #ifndef QTLOOPS_H #define QTLOOPS_H // Copyright (C) 2009 Jeremy S. Sanders // Email: Jeremy Sanders // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program 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 General Public License for more details. // // You should have received a copy of the GNU General Public License along // with this program; if not, write to the Free Software Foundation, Inc., // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ///////////////////////////////////////////////////////////////////////////// #include "qtloops_helpers.h" #include #include #include #include #include class QtLoops { public: QtLoops() {}; }; // add sets of points to a QPolygonF void addNumpyToPolygonF(QPolygonF& poly, const Tuple2Ptrs& v); // add sets of polygon points to a path void addNumpyPolygonToPath(QPainterPath &path, const Tuple2Ptrs& d, const QRectF* clip = 0); // plot paths to painter // x and y locations are given in x and y // if scaling is not 0, is an array to scale the data points by // if colorimg is not 0, is a Nx1 image containing color points for path fills // clip is a clipping rectangle if set void plotPathsToPainter(QPainter& painter, QPainterPath& path, const Numpy1DObj& x, const Numpy1DObj& y, const Numpy1DObj* scaling = 0, const QRectF* clip = 0, const QImage* colorimg = 0, bool scaleline = false); void plotLinesToPainter(QPainter& painter, const Numpy1DObj& x1, const Numpy1DObj& y1, const Numpy1DObj& x2, const Numpy1DObj& y2, const QRectF* clip = 0, bool autoexpand = true); void plotBoxesToPainter(QPainter& painter, const Numpy1DObj& x1, const Numpy1DObj& y1, const Numpy1DObj& x2, const Numpy1DObj& y2, const QRectF* clip = 0, bool autoexpand = true); // add polygon to painter path as a cubic void addCubicsToPainterPath(QPainterPath& path, const QPolygonF& poly); QImage numpyToQImage(const Numpy2DObj& data, const Numpy2DIntObj &colors, bool forcetrans = false); void applyImageTransparancy(QImage& img, const Numpy2DObj& data); QImage resampleLinearImage(QImage& img, const Numpy1DObj& xpts, const Numpy1DObj& ypts); #endif ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������veusz-3.0.1/veusz/helpers/src/qtloops/polylineclip.cpp����������������������������������������������0000664�0001750�0001750�00000035366�13305310005�022631� 0����������������������������������������������������������������������������������������������������ustar �jss�����������������������������jss�����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright (C) 2010 Jeremy Sanders // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program 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 General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA // 02110-1301, USA. //////////////////////////////////////////////////////////////////// #include #include #include #include #include #include using std::fabs; // Cohen-Sutherland line clipping algorithm // codes used classify endpoints are combinations of these #define LEFT 1 #define RIGHT 2 #define TOP 4 #define BOTTOM 8 namespace { // compute intersection with horizontal line inline QPointF horzIntersection(qreal yval, const QPointF& pt1, const QPointF& pt2) { if( pt1.y() == pt2.y() ) // line is vertical return QPointF( pt1.x(), yval ); else return QPointF( pt1.x() + (yval-pt1.y()) * (pt2.x()-pt1.x()) / (pt2.y()-pt1.y()), yval ); } // compute intersection with vertical line inline QPointF vertIntersection(qreal xval, const QPointF& pt1, const QPointF& pt2) { if( pt1.x() == pt2.x() ) // line is horizontal return QPointF( xval, pt1.y() ); else return QPointF( xval, pt1.y() + (xval-pt1.x()) * (pt2.y()-pt1.y()) / (pt2.x()-pt1.x()) ); } // is difference between points very small? inline bool smallDelta(const QPointF& pt1, const QPointF& pt2) { return fabs(pt1.x() - pt2.x()) < 0.01 && fabs(pt1.y()- pt2.y()) < 0.01; } template T sqr(T v) { return v*v; } // private class for helping clip class _Clipper { public: _Clipper(const QRectF& cliprect); unsigned computeCode(const QPointF& pt) const; void fixPt(QPointF& pt) const; bool clipLine(QPointF& pt1, QPointF& pt2) const; private: QRectF clip; }; // This class is use to separate out the clipping of polylines // overrite emitPolyline to handle the clipped part of the original // polyline class _PolyClipper { public: _PolyClipper(QRectF clip) : _clipper(clip) {} virtual ~_PolyClipper() {}; // override this to draw the output polylines virtual void emitPolyline(const QPolygonF& poly) = 0; // do clipping on the polyline void clipPolyline(const QPolygonF& poly); private: _Clipper _clipper; }; } //////////////////////////////////////////////////////////////////////// _Clipper::_Clipper(const QRectF& cliprect) : clip(cliprect) { } // compute the Cohen-Sutherland code unsigned _Clipper::computeCode(const QPointF& pt) const { unsigned code = 0; if (pt.x() < clip.left()) code |= LEFT; else if (pt.x() > clip.right()) code |= RIGHT; if (pt.y() < clip.top()) code |= TOP; else if (pt.y() > clip.bottom()) code |= BOTTOM; return code; } // get consistent clipping on different platforms by making line // edges meet clipping box if close void _Clipper::fixPt(QPointF& pt) const { if( fabs(pt.x() - clip.left()) < 1e-4 ) pt.setX(clip.left()); if( fabs(pt.x() - clip.right()) < 1e-4 ) pt.setX(clip.right()); if( fabs(pt.y() - clip.top()) < 1e-4 ) pt.setY(clip.top()); if( fabs(pt.y() - clip.bottom()) < 1e-4 ) pt.setY(clip.bottom()); } // modifies points, returning true if okay to accept bool _Clipper::clipLine(QPointF& pt1, QPointF& pt2) const { // fixup ends to meet clip box if close fixPt(pt1); fixPt(pt2); unsigned code1 = computeCode(pt1); unsigned code2 = computeCode(pt2); // repeat until points are at edge of box // bail out if we repeat too many times (avoid numerical issues) for(unsigned count = 0 ; count < 16 ; count++ ) { if( (code1 | code2) == 0 ) { // no more clipping required - inside return true; } else if( (code1 & code2) != 0 ) { // line should not be drawn - outside return false; } else { // compute intersection // which point to compute for? const unsigned code = (code1 != 0) ? code1 : code2; // get intersection new point and new code for line QPointF pt; if( code & LEFT ) pt = vertIntersection(clip.left(), pt1, pt2); else if( code & RIGHT ) pt = vertIntersection(clip.right(), pt1, pt2); else if( code & TOP ) pt = horzIntersection(clip.top(), pt1, pt2); else if ( code & BOTTOM ) pt = horzIntersection(clip.bottom(), pt1, pt2); // update point as intersection if( code == code1 ) { // changed first point pt1 = pt; code1 = computeCode(pt1); } else { // changed second point pt2 = pt; code2 = computeCode(pt2); } } } // loop return false; } bool clipLine(const QRectF& clip, QPointF& pt1, QPointF& pt2) { _Clipper clipper(clip); return clipper.clipLine(pt1, pt2); } ////////////////////////////////////////////////////////////////////// void _PolyClipper::clipPolyline(const QPolygonF& poly) { // exit if fewer than 2 points in polygon if ( poly.size() < 2 ) return; // output goes here QPolygonF pout; QPolygonF::const_iterator polyiter = poly.begin(); QPointF lastpt = *polyiter; polyiter++; for( ; polyiter != poly.end(); ++polyiter ) { QPointF p1 = lastpt; QPointF p2 = *polyiter; bool plotline = _clipper.clipLine(p1, p2); if( plotline ) { if( pout.isEmpty() ) { // add first line pout << p1; if( ! smallDelta(p1, p2) ) pout << p2; } else { if( p1 == pout.last() ) { if( ! smallDelta(p1, p2) ) // extend polyline pout << p2; } else { // paint existing line if( pout.size() >= 2 ) emitPolyline(pout); // start new line pout.clear(); pout << p1; if( ! smallDelta(p1, p2) ) pout << p2; } } } else { // line isn't in region, so ignore results from clip function // paint existing line if( pout.size() >= 2 ) emitPolyline(pout); // cleanup pout.clear(); } lastpt = *polyiter; } if( pout.size() >= 2 ) emitPolyline(pout); } // class used for drawing clipped polylines class PlotDrawCallback : public _PolyClipper { public: PlotDrawCallback(QRectF clip, QPainter& painter) : _PolyClipper(clip), _painter(painter) {} void emitPolyline(const QPolygonF& poly) { _painter.drawPolyline(poly); } private: QPainter& _painter; }; // take polyline and paint to painter, clipping void plotClippedPolyline(QPainter& painter, QRectF clip, const QPolygonF& poly, bool autoexpand) { // if autoexpand, expand rectangle by line width if ( autoexpand ) { const qreal lw = painter.pen().widthF(); clip.adjust(-lw, -lw, lw, lw); } PlotDrawCallback pcb(clip, painter); pcb.clipPolyline(poly); } ////////////////////////////////////////////////////// // clip polyline and add polyines clipped to a vector class PolyAddCallback : public _PolyClipper { public: PolyAddCallback(QRectF clip) : _PolyClipper(clip) {} void emitPolyline(const QPolygonF& poly) { polys.push_back(poly); } public: QVector polys; }; QVector clipPolyline(QRectF clip, const QPolygonF& poly) { PolyAddCallback pcb(clip); pcb.clipPolyline(poly); return pcb.polys; } ////////////////////////////////////////////////////// typedef QVector PolyVector; // clip polygon, adding clipped parts to output vector of polygons class _LineLabClipper : public _PolyClipper { public: _LineLabClipper(QRectF cliprect, PolyVector& polyvec) : _PolyClipper(cliprect), _polyvec(polyvec) { } void emitPolyline(const QPolygonF& poly) { _polyvec.append(poly); } private: PolyVector& _polyvec; }; /////////////////////////////////////////////////////// // Check whether polygons intersect // http://stackoverflow.com/questions/10962379/how-to-check-intersection-between-2-rotated-rectangles // note: requires clockwise polygons bool doPolygonsIntersect(const QPolygonF& a, const QPolygonF& b) { for(auto const& poly : {a, b}) { QPointF prevpt(poly.last()); for(auto const& currpt : poly) { // normal to line segment const double normx = currpt.y()-prevpt.y(); const double normy = prevpt.x()-currpt.x(); double minA = std::numeric_limits::max(); double maxA = std::numeric_limits::lowest(); for(auto const& pt : a) { const double proj = normx*pt.x() + normy*pt.y(); minA = std::min(minA, proj); maxA = std::max(maxA, proj); } double minB = std::numeric_limits::max(); double maxB = std::numeric_limits::lowest(); for(auto const& pt : b) { const double proj = normx*pt.x() + normy*pt.y(); minB = std::min(minB, proj); maxB = std::max(maxB, proj); } if(maxA= totlength*frac) { // interpolate along edge const double fseg = (totlength*frac - length) / seglength; const double xp = poly[i-1].x()*(1-fseg) + poly[i].x()*fseg; const double yp = poly[i-1].y()*(1-fseg) + poly[i].y()*fseg; const double angle = _rotatelabels ? std::atan2( poly[i].y() - poly[i-1].y(), poly[i].x() - poly[i-1].x() ) : 0.; return RotatedRectangle(xp, yp, size.width(), size.height(), angle); } length += seglength; } // shouldn't get here return RotatedRectangle(); } // these are the positions where labels might be placed namespace { #define NUM_LABEL_POSITIONS 7 const double label_positions[NUM_LABEL_POSITIONS] = { 0.5, 1/3., 2/3., 0.4, 0.6, 0.25, 0.75}; } void LineLabeller::process() { RectangleOverlapTester rtest; // iterate over each set of polylines for(int polyseti = 0; polyseti < _polys.size(); ++polyseti) { const PolyVector& pv = _polys[polyseti]; QSizeF size = _textsizes[polyseti]; for(int polyi = 0; polyi < pv.size(); ++polyi) { for(int posi = 0; posi < NUM_LABEL_POSITIONS; ++posi) { const RotatedRectangle r = findLinePosition(pv[polyi], label_positions[posi], size); if( ! r.isValid() ) break; if( ! rtest.willOverlap(r) ) { drawAt(polyseti, r); rtest.addRect(r); break; // only add label once } } // positions } // polylines in set of polylines } // sets of polylines } QVector LineLabeller::getPolySet(int i) const { if( i >= 0 && i < _polys.size() ) return _polys[i]; return QVector(); } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������veusz-3.0.1/veusz/helpers/src/qtloops/beziers_qtwrap.h����������������������������������������������0000664�0001750�0001750�00000002264�13161413406�022634� 0����������������������������������������������������������������������������������������������������ustar �jss�����������������������������jss�����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// -*- mode: C++; -*- // Copyright (C) 2010 Jeremy S. Sanders // Email: Jeremy Sanders // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program 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 General Public License for more details. // // You should have received a copy of the GNU General Public License along // with this program; if not, write to the Free Software Foundation, Inc., // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ///////////////////////////////////////////////////////////////////////////// #ifndef BEZIERS_QTWRAP_H #define BEZIERS_QTWRAP_H #include QPolygonF bezier_fit_cubic_single(const QPolygonF& data, double error); QPolygonF bezier_fit_cubic_multi(const QPolygonF& data, double error, unsigned max_beziers); #endif ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������veusz-3.0.1/veusz/helpers/src/qtloops/qtloops.cpp���������������������������������������������������0000664�0001750�0001750�00000031314�13161413406�021625� 0����������������������������������������������������������������������������������������������������ustar �jss�����������������������������jss�����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright (C) 2009 Jeremy S. Sanders // Email: Jeremy Sanders // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program 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 General Public License for more details. // // You should have received a copy of the GNU General Public License along // with this program; if not, write to the Free Software Foundation, Inc., // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ///////////////////////////////////////////////////////////////////////////// #include "qtloops.h" #include "isnan.h" #include "polylineclip.h" #include "polygonclip.h" #include #include #include #include #include #include #include namespace { // is difference between points very small? inline bool smallDelta(const QPointF& pt1, const QPointF& pt2) { return fabs(pt1.x() - pt2.x()) < 0.01 && fabs(pt1.y()- pt2.y()) < 0.01; } template inline T min(T a, T b) { return (a inline T min(T a, T b, T c, T d) { return min( min(a, b), min(c, d) ); } template inline T clipval(T val, T minv, T maxv) { if( val < minv ) return minv; if( val > maxv ) return maxv; return val; } } void addNumpyToPolygonF(QPolygonF& poly, const Tuple2Ptrs& d) { // iterate over rows until none left const int numcols = d.data.size(); QPointF lastpt(-1e6, -1e6); for(int row=0 ; ; ++row) { bool ifany = false; // the numcols-1 makes sure we don't get odd numbers of columns for(int col=0; col < (numcols-1); col += 2) { // add point if point in two columns if( row < d.dims[col] && row < d.dims[col+1] ) { const QPointF pt(d.data[col][row], d.data[col+1][row]); if( ! smallDelta(pt, lastpt) ) { poly << pt; lastpt = pt; } ifany = true; } } // exit loop if no more columns if(! ifany ) break; } } void addNumpyPolygonToPath(QPainterPath &path, const Tuple2Ptrs& d, const QRectF* clip) { const int numcols = d.data.size(); for(int row=0 ; ; ++row) { bool ifany = false; // output polygon QPolygonF poly; // the numcols-1 makes sure we don't get odd numbers of columns for(int col=0; col < (numcols-1); col += 2) { // add point if point in two columns if( row < d.dims[col] && row < d.dims[col+1] ) { const QPointF pt(d.data[col][row], d.data[col+1][row]); poly << pt; ifany = true; } } if( ifany ) { if( clip != 0 ) { QPolygonF clippedpoly; polygonClip(poly, *clip, clippedpoly); path.addPolygon(clippedpoly); } else { path.addPolygon(poly); } path.closeSubpath(); } else { // exit loop if no more columns break; } } } namespace { // Scale path by scale given. Puts output in out. void scalePath(const QPainterPath& path, qreal scale, QPainterPath& out) { const int count = path.elementCount(); for(int i=0; igetCoords(&x1, &y1, &x2, &y2); cliprect.setCoords(x1, y1, x2, y2); } QRectF pathbox = path.boundingRect(); cliprect.adjust(pathbox.left(), pathbox.top(), pathbox.bottom(), pathbox.right()); // keep track of duplicate points QPointF lastpt(-1e6, -1e6); // keep original transformation for restoration after each iteration QTransform origtrans(painter.worldTransform()); // number of iterations int size = min(x.dim, y.dim); // if few color points, trim down number of paths if( colorimg != 0 ) size = min(size, colorimg->width()); // too few scaling points if( scaling != 0 ) size = min(size, scaling->dim); // draw each path for(int i = 0; i < size; ++i) { const QPointF pt(x(i), y(i)); if( cliprect.contains(pt) && ! smallDelta(lastpt, pt) ) { painter.translate(pt); if( colorimg != 0 ) { // get color from pixel and create a new brush QBrush b( QColor::fromRgba(colorimg->pixel(i, 0)) ); painter.setBrush(b); } if( scaling == 0 ) { painter.drawPath(path); } else { // scale point if requested const qreal s = (*scaling)(i); if( scaleline ) { painter.scale(s, s); painter.drawPath(path); } else { QPainterPath scaled; scalePath(path, s, scaled); painter.drawPath(scaled); } } painter.setWorldTransform(origtrans); lastpt = pt; } } } void plotLinesToPainter(QPainter& painter, const Numpy1DObj& x1, const Numpy1DObj& y1, const Numpy1DObj& x2, const Numpy1DObj& y2, const QRectF* clip, bool autoexpand) { const int maxsize = min(x1.dim, x2.dim, y1.dim, y2.dim); // if autoexpand, expand rectangle by line width QRectF clipcopy; if ( clip != 0 && autoexpand ) { const qreal lw = painter.pen().widthF(); qreal x1, y1, x2, y2; clip->getCoords(&x1, &y1, &x2, &y2); clipcopy.setCoords(x1, y1, x2, y2); clipcopy.adjust(-lw, -lw, lw, lw); } if( maxsize != 0 ) { QVector lines; for(int i = 0; i < maxsize; ++i) { QPointF pt1(x1(i), y1(i)); QPointF pt2(x2(i), y2(i)); if( clip != 0 ) { if( clipLine(clipcopy, pt1, pt2) ) lines << QLineF(pt1, pt2); } else lines << QLineF(pt1, pt2); } painter.drawLines(lines); } } void plotBoxesToPainter(QPainter& painter, const Numpy1DObj& x1, const Numpy1DObj& y1, const Numpy1DObj& x2, const Numpy1DObj& y2, const QRectF* clip, bool autoexpand) { // if autoexpand, expand rectangle by line width QRectF clipcopy(QPointF(-32767,-32767), QPointF(32767,32767)); if ( clip != 0 && autoexpand ) { const qreal lw = painter.pen().widthF(); qreal x1, y1, x2, y2; clip->getCoords(&x1, &y1, &x2, &y2); clipcopy.setCoords(x1, y1, x2, y2); clipcopy.adjust(-lw, -lw, lw, lw); } const int maxsize = min(x1.dim, x2.dim, y1.dim, y2.dim); QVector rects; for(int i = 0; i < maxsize; ++i) { QPointF pt1(x1(i), y1(i)); QPointF pt2(x2(i), y2(i)); const QRectF rect(pt1, pt2); if( clipcopy.intersects(rect) ) { rects << clipcopy.intersected(rect); } } if( ! rects.isEmpty() ) painter.drawRects(rects); } void addCubicsToPainterPath(QPainterPath& path, const QPolygonF& poly) { QPointF lastpt(-999999, -999999); for(int i=0; i(img.scanLine(yw-y-1)); for(int x=0; x= QT_VERSION_CHECK(5, 9, 0) // recent qt version // just change the format to the non-transparent version img.reinterpretAsFormat(QImage::Format_RGB32); #else // do slower conversion of data return img.convertToFormat(QImage::Format_RGB32); #endif } return img; } void applyImageTransparancy(QImage& img, const Numpy2DObj& data) { const int xw = min(data.dims[1], img.width()); const int yw = min(data.dims[0], img.height()); for(int y=0; y(img.scanLine(yw-y-1)); for(int x=0; x xpts(xpts.dim-1); const int revy = ypts(0) > ypts(ypts.dim-1); // get smallest spacing double mindeltax = 1e99; for(int i=0; i<(xpts.dim-1); ++i) { mindeltax = std::min(mindeltax, fabs(xpts(i+1)-xpts(i))); } double mindeltay = 1e99; for(int i=0; i<(ypts.dim-1); ++i) { mindeltay = std::min(mindeltay, fabs(ypts(i+1)-ypts(i))); } // get bounds const double minx = revx ? xpts(xpts.dim-1) : xpts(0); const double maxx = revx ? xpts(0) : xpts(xpts.dim-1); const double miny = revy ? ypts(ypts.dim-1) : ypts(0); const double maxy = revy ? ypts(0) : ypts(ypts.dim-1); // output size (trimmed to 1024) const int sizex = std::min(1024, int((maxx-minx) / (mindeltax*0.25) + 0.01) ); const int sizey = std::min(1024, int((maxy-miny) / (mindeltay*0.25) + 0.01) ); const double deltax = (maxx-minx) / sizex; const double deltay = (maxy-miny) / sizey; QImage outimg(sizex, sizey, img.format()); // need to account for reverse direction, so count backwards const int xptsbase = revx ? xpts.dim-1 : 0; const int xptsdir = revx ? -1 : 1; const int yptsbase = revy ? ypts.dim-1 : 0; const int yptsdir = revy ? -1 : 1; int iy = 0; for(int oy = 0; oy < sizey; ++oy) { // do we move to the next pixel in y? while( miny+(oy+0.5)*deltay > ypts(yptsbase+yptsdir*(iy+1)) && iy < ypts.dim-2 ) ++iy; QRgb* iscanline = reinterpret_cast(img.scanLine(iy)); QRgb* oscanline = reinterpret_cast(outimg.scanLine(oy)); int ix = 0; for(int ox = 0; ox < sizex; ++ox) { // do we move to the next pixel in x? while( minx+(ox+0.5)*deltax > xpts(xptsbase+xptsdir*(ix+1)) && ix < xpts.dim-2 ) ++ix; // copy pixel *(oscanline+ox) = *(iscanline+ix); } } return outimg; } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������veusz-3.0.1/veusz/helpers/src/qtloops/numpyfuncs.cpp������������������������������������������������0000664�0001750�0001750�00000005571�13161413406�022341� 0����������������������������������������������������������������������������������������������������ustar �jss�����������������������������jss�����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright (C) 2011 Jeremy S. Sanders // Email: Jeremy Sanders // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program 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 General Public License for more details. // // You should have received a copy of the GNU General Public License along // with this program; if not, write to the Free Software Foundation, Inc., // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ///////////////////////////////////////////////////////////////////////////// #include "numpyfuncs.h" #include #include "isnan.h" namespace { template inline T min(T a, T b) { return (a::quiet_NaN(); } else { if( average ) out[i / binning] = sum / ct; else out[i / binning] = sum; } sum = 0; ct = 0; } } } void rollingAverage(const Numpy1DObj& indata, const Numpy1DObj* weights, int width, int* numoutbins, double** outdata) { // round up output size int size = indata.dim; if( weights != 0 ) size = min( weights->dim, size ); // create output array *numoutbins = size; double *out = new double[size]; *outdata = out; for(int i = 0 ; i < size; ++i) { double ct = 0.; double sum = 0.; // iterate over rolling width for(int di = -width; di <= width; ++di) { const int ri = di+i; if ( ri >= 0 && ri < size && isFinite(indata(ri)) ) { if( weights != 0 ) { // weighted average const double w = (*weights)(ri); if( isFinite(w) ) { ct += w; sum += w*indata(ri); } } else { // standard average ct += 1; sum += indata(ri); } } } if( ct != 0. ) out[i] = sum / ct; else out[i] = std::numeric_limits::quiet_NaN(); } } ���������������������������������������������������������������������������������������������������������������������������������������veusz-3.0.1/veusz/compat.py�������������������������������������������������������������������������0000664�0001750�0001750�00000011526�13244231256�015331� 0����������������������������������������������������������������������������������������������������ustar �jss�����������������������������jss�����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2013 Jeremy S. Sanders # Email: Jeremy Sanders # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ############################################################################### """ six-like compatibility module between python2 and python3 Rolled own, because I can control the naming better (everying starts with a 'c') """ import itertools import locale import os import sys cpy3 = sys.version_info[0] == 3 if cpy3: # py3 # builtins import builtins as cbuiltins from io import StringIO as CStringIO, BytesIO as CBytesIO import urllib.request as curlrequest from urllib.parse import urlencode as curlencode # imports import pickle # range function crange = range # zip function czip = zip # function to create user strings cstr = str # base string type cbasestr = str # bytes-like object cbytes = bytes # unicode-like object cunicode = str # iterate over dict def citems(d): return d.items() def ckeys(d): return d.keys() def cvalues(d): return d.values() # next iterator cnext = next # python3 compatible iterator CIterator = object # python3 object with __bool__/__nonzero__ class CBool(object): def __bool__(self): return self.cbool() # exec function cexec = getattr(cbuiltins, 'exec') # execfile def cexecfile(filename, globaldict): with open(filename) as f: code = compile(f.read(), filename, 'exec') cexec(code, globaldict) # convert strerror exception to string def cstrerror(ex): return ex.strerror # text repr (compatible with python2) def crepr(v): if isinstance(v, str): return 'u' + repr(v) return repr(v) # convert exception to a user string def cexceptionuser(ex): return str(ex) # get current directory (as unicode) cgetcwd = os.getcwd else: # py2 # builtins import __builtin__ as cbuiltins # imports import cPickle as pickle from StringIO import StringIO as CStringIO from io import BytesIO as CBytesIO import urllib2 as curlrequest from urllib import urlencode as curlencode # range function crange = xrange # zip function czip = itertools.izip # function to create user strings cstr = unicode # base string cbasestr = basestring # bytes-like object cbytes = str # unicode-like string cunicode = unicode # iterate over dict def citems(d): return d.iteritems() def ckeys(d): return d.iterkeys() def cvalues(d): return d.itervalues() # next iterator def cnext(i): return i.next() # python3 compatible iterator class CIterator(object): def next(self): return type(self).__next__(self) # python3 object with __bool__/__nonzero__ class CBool(object): def __nonzero__(self): return self.cbool() # exec function def cexec(text, globdict): """An exec-like function. As veusz always supplies a globals and no locals, we simplify this.""" # this is done like this to avoid a compile-time error in py3 code = 'exec text in globdict' exec(code) # execfile def cexecfile(filename, globaldict): execfile(filename, globaldict) # convert strerror exception to string def cstrerror(ex): if isinstance(ex.strerror, str): deflocale = locale.getdefaultlocale()[1] or 'ascii' return ex.strerror.decode(deflocale) else: return ex.strerror # sometimes exceptions come as unicode, sometimes as strings # encoded in ascii, so we have to decode def cexceptionuser(ex): if hasattr(ex, 'strerror') and isinstance(ex.strerror, str): # comes from operating system as encoded deflocale = locale.getdefaultlocale()[1] or 'ascii' return str(ex).decode(deflocale) else: # let's hope this works return unicode(ex) # py2/3 repr crepr = repr # unicode getcwd cgetcwd = os.getcwdu ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������veusz-3.0.1/veusz/datasets/�������������������������������������������������������������������������0000775�0001750�0001750�00000000000�13325026670�015301� 5����������������������������������������������������������������������������������������������������ustar �jss�����������������������������jss�����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������veusz-3.0.1/veusz/datasets/plugin.py����������������������������������������������������������������0000664�0001750�0001750�00000015162�13173073033�017152� 0����������������������������������������������������������������������������������������������������ustar �jss�����������������������������jss�����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2016 Jeremy S. Sanders # Email: Jeremy Sanders # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ############################################################################### from ..compat import cstr, citems from .oned import Dataset1DBase, Dataset from .twod import Dataset2DBase, Dataset2D from .nd import DatasetNDBase, DatasetND from .text import DatasetText from .date import DatasetDateTimeBase, DatasetDateTime class _DatasetPlugin(object): """Shared methods for dataset plugins.""" def __init__(self, manager, ds): self.pluginmanager = manager self.pluginds = ds def getPluginData(self, attr): self.pluginmanager.update() return getattr(self.pluginds, attr) def linkedInformation(self): """Return information about how this dataset was created.""" fields = [] for name, val in citems(self.pluginmanager.fields): fields.append('%s: %s' % (cstr(name), cstr(val))) try: shape = [str(x) for x in self.data.shape] except AttributeError: shape = [str(len(self.data))] shape = u'\u00d7'.join(shape) return '%s plugin dataset (fields %s), size %s' % ( self.pluginmanager.plugin.name, ', '.join(fields), shape) def canUnlink(self): """Can relationship be unlinked?""" return True def deleteRows(self, row, numrows): pass def insertRows(self, row, numrows, rowdata): pass def saveDataRelationToText(self, fileobj, name): """Save plugin to file, if this is the first one.""" # only try to save if this is the 1st dataset of this plugin # manager in the document, so that we don't save more than once docdatasets = set( self.document.data.values() ) for ds in self.pluginmanager.veuszdatasets: if ds in docdatasets: if ds is self: # is 1st dataset self.pluginmanager.saveToFile(fileobj) return def saveDataDumpToText(self, fileobj, name): """Save data to text: not used.""" def saveDataDumpToHDF5(self, group, name): """Save data to HDF5: not used.""" @property def dstype(self): """Return type of plugin.""" return self.pluginmanager.plugin.name class Dataset1DPlugin(_DatasetPlugin, Dataset1DBase): """Return 1D dataset from a plugin.""" def __init__(self, manager, ds): _DatasetPlugin.__init__(self, manager, ds) Dataset1DBase.__init__(self) def userSize(self): """Size of dataset.""" return str( self.data.shape[0] ) def __getitem__(self, key): """Return a dataset based on this dataset We override this from DatasetConcreteBase as it would return a DatsetExpression otherwise, not chopped sets of data. """ return Dataset(**self._getItemHelper(key)) # parent class sets these attributes, so override setattr to do nothing data = property( lambda self: self.getPluginData('data'), lambda self, val: None ) serr = property( lambda self: self.getPluginData('serr'), lambda self, val: None ) nerr = property( lambda self: self.getPluginData('nerr'), lambda self, val: None ) perr = property( lambda self: self.getPluginData('perr'), lambda self, val: None ) class Dataset2DPlugin(_DatasetPlugin, Dataset2DBase): """Return 2D dataset from a plugin.""" def __init__(self, manager, ds): _DatasetPlugin.__init__(self, manager, ds) Dataset2DBase.__init__(self) def __getitem__(self, key): return Dataset2D(self.data[key], xrange=self.xrange, yrange=self.yrange, xedge=self.xedge, yedge=self.yedge, xcent=self.xcent, ycent=self.ycent) data = property( lambda self: self.getPluginData('data'), lambda self, val: None ) xrange = property( lambda self: self.getPluginData('rangex'), lambda self, val: None ) yrange = property( lambda self: self.getPluginData('rangey'), lambda self, val: None ) xedge = property( lambda self: self.getPluginData('xedge'), lambda self, val: None ) yedge = property( lambda self: self.getPluginData('yedge'), lambda self, val: None ) xcent = property( lambda self: self.getPluginData('xcent'), lambda self, val: None ) ycent = property( lambda self: self.getPluginData('ycent'), lambda self, val: None ) class DatasetNDPlugin(_DatasetPlugin, DatasetNDBase): """Return N-dimensional dataset from plugin.""" def __init__(self, manager, ds): _DatasetPlugin.__init__(self, manager, ds) DatasetNDBase.__init__(self) def __getitem__(self, key): return DatasetND(self.data[key]) data = property( lambda self: self.getPluginData('data'), lambda self, val: None ) class DatasetTextPlugin(_DatasetPlugin, DatasetText): """Return text dataset from a plugin.""" def __init__(self, manager, ds): _DatasetPlugin.__init__(self, manager, ds) DatasetText.__init__(self, []) def __getitem__(self, key): return DatasetText(self.data[key]) data = property( lambda self: self.getPluginData('data'), lambda self, val: None ) class DatasetDateTimePlugin(_DatasetPlugin, DatasetDateTimeBase): """Return date dataset from plugin.""" def __init__(self, manager, ds): _DatasetPlugin.__init__(self, manager, ds) DatasetDateTimeBase.__init__(self) self.serr = self.perr = self.nerr = None def __getitem__(self, key): return DatasetDateTime(self.data[key]) data = property( lambda self: self.getPluginData('data'), lambda self, val: None ) ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������veusz-3.0.1/veusz/datasets/histo.py�����������������������������������������������������������������0000664�0001750�0001750�00000023252�13212733513�017001� 0����������������������������������������������������������������������������������������������������ustar �jss�����������������������������jss�����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2010 Jeremy S. Sanders # Email: Jeremy Sanders # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ############################################################################### from __future__ import division import numpy as N from ..compat import citems from .. import utils from .commonfn import _ from .oned import Dataset1DBase from .expression import evalDatasetExpression class DatasetHistoGenerator(object): def __init__(self, document, inexpr, binmanual = None, binparams = None, method = 'counts', cumulative = 'none', errors=False): """ inexpr = ds expression binmanual = None / [1,2,3,4,5] binparams = None / (num, minval, maxval, islog) method = ('counts', 'density', or 'fractions') cumulative = ('none', 'smalltolarge', 'largetosmall') errors = True/False """ self.changeset = -1 self.document = document self.inexpr = inexpr self.binmanual = binmanual if binparams is None: self.binparams = (10, 'Auto', 'Auto', False) else: self.binparams = binparams self.method = method self.cumulative = cumulative self.errors = errors self.bindataset = self.valuedataset = None def getData(self): """Get data from input expression, caching result.""" if self.document.changeset != self.changeset: d = evalDatasetExpression(self.document, self.inexpr) if d is not None: d = d.data # only use finite data d = d[N.isfinite(d)] if len(d) == 0: d = None self._cacheddata = d self.changeset = self.document.changeset return self._cacheddata def binLocations(self): """Compute locations of bins edges, giving N+1 items.""" if self.binmanual is not None: return N.array(self.binmanual) else: numbins, minval, maxval, islog = self.binparams if minval == 'Auto' or maxval == 'Auto': data = self.getData() if data is None: return N.array([]) if minval == 'Auto': minval = N.min(data) if maxval == 'Auto': maxval = N.max(data) if not islog: delta = (maxval - minval) / numbins return N.arange(numbins+1)*delta + minval else: if minval <= 0: minval = 1e-99 if maxval <= 0: maxval = 1e99 lmin, lmax = N.log(minval), N.log(maxval) delta = (lmax - lmin) / numbins return N.exp( N.arange(numbins+1)*delta + lmin ) def getBinLocations(self): """Return bin centre, -ve bin width, +ve bin width.""" if self.getData() is None: return (N.array([]), None, None) binlocs = self.binLocations() if self.binparams and self.binparams[3]: # log bins lbin = N.log(binlocs) data = N.exp( 0.5*(lbin[:-1] + lbin[1:]) ) else: # otherwise linear bins data = 0.5*(binlocs[:-1] + binlocs[1:]) # error bars nerr = binlocs[:-1] - data perr = binlocs[1:] - data return data, nerr, perr def getErrors(self, data, binlocs): """Compute error bars if requried.""" hist, edges = N.histogram(data, bins=binlocs) hist = hist.astype(N.float64) # integers can break plots (github#49) # calculate scaling values for error bars if self.method == 'density': ratio = 1. / (hist.size*(edges[1]-edges[0])) elif self.method == 'fractions': ratio = 1. / data.size else: ratio = 1. # compute cumulative values (errors correlated) if self.cumulative == 'smalltolarge': hist = N.cumsum(hist) elif self.cumulative == 'largetosmall': hist = N.cumsum(hist[::-1])[::-1] # Gehrels 1986 ApJ 303 336 perr = 1. + N.sqrt(hist + 0.75) nerr = N.where(hist > 0, N.sqrt(hist - 0.25), 0.) return -nerr*ratio, perr*ratio def getBinVals(self): """Return results for each bin.""" data = self.getData() if data is None: return (N.array([]), None, None) density = self.method == 'density' binlocs = self.binLocations() hist, edges = N.histogram(data, bins=binlocs, density=density) hist = hist.astype(N.float64) # integers can break plots (github#49) if self.method == 'fractions': hist = hist * (1./data.size) # if cumulative wanted if self.cumulative == 'smalltolarge': hist = N.cumsum(hist) elif self.cumulative == 'largetosmall': hist = N.cumsum(hist[::-1])[::-1] if self.errors: nerr, perr = self.getErrors(data, binlocs) else: nerr, perr = None, None return hist, nerr, perr def generateBinDataset(self): self.bindataset = DatasetHistoBins(self, self.document) return self.bindataset def generateValueDataset(self): self.valuedataset = DatasetHistoValues(self, self.document) return self.valuedataset def saveToFile(self, fileobj): """Save two datasets to file.""" # lookup names of datasets in document bindsname = valuedsname = '' for name, ds in citems(self.document.data): if ds is self.bindataset: bindsname = name elif ds is self.valuedataset: valuedsname = name fileobj.write( "CreateHistogram(%s, %s, %s, binparams=%s, " "binmanual=%s, method=%s, " "cumulative=%s, errors=%s)\n" % ( utils.rrepr(self.inexpr), utils.rrepr(bindsname), utils.rrepr(valuedsname), utils.rrepr(self.binparams), utils.rrepr(self.binmanual), utils.rrepr(self.method), utils.rrepr(self.cumulative), utils.rrepr(self.errors)) ) def linkedInformation(self): """Informating about linking.""" if self.binmanual is not None: bins = _('manual bins') else: bins = _('%i bins from %s to %s') % (self.binparams[0], self.binparams[1], self.binparams[2]) return _("Histogram of '%s' with %s") % (self.inexpr, bins) class DatasetHistoBins(Dataset1DBase): """A dataset for getting the bin positions for the histogram.""" dstype = _('Histogram') def __init__(self, generator, document): Dataset1DBase.__init__(self) self.generator = generator self.document = document self.linked = None self._invalidpoints = None self.changeset = -1 def getData(self): """Get bin positions, caching results.""" if self.changeset != self.generator.document.changeset: self.datacache = self.generator.getBinLocations() self.changeset = self.generator.document.changeset return self.datacache def linkedInformation(self): """Informating about linking.""" return self.generator.linkedInformation() + _(" (bin positions)") def saveDataDumpToText(self, fileobj, name): pass def saveDataDumpToHDF5(self, group, name): pass data = property(lambda self: self.getData()[0]) nerr = property(lambda self: self.getData()[1]) perr = property(lambda self: self.getData()[2]) serr = None class DatasetHistoValues(Dataset1DBase): """A dataset for getting the height of the bins in a histogram.""" dstype = _('Histogram') def __init__(self, generator, document): Dataset1DBase.__init__(self) self.generator = generator self.document = document self.linked = None self._invalidpoints = None self.changeset = -1 def getData(self): """Get bin heights, caching results.""" if self.changeset != self.generator.document.changeset: self.datacache = self.generator.getBinVals() self.changeset = self.generator.document.changeset return self.datacache def saveDataRelationToText(self, fileobj, name): """Save dataset and its counterpart to a file.""" self.generator.saveToFile(fileobj) def saveDataDumpToText(self, fileobj, name): pass def saveDataDumpToHDF5(self, group, name): pass def linkedInformation(self): """Informating about linking.""" return self.generator.linkedInformation() + _(" (bin values)") data = property(lambda self: self.getData()[0]) nerr = property(lambda self: self.getData()[1]) perr = property(lambda self: self.getData()[2]) serr = None ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������veusz-3.0.1/veusz/datasets/filtered.py��������������������������������������������������������������0000664�0001750�0001750�00000016645�13161413406�017460� 0����������������������������������������������������������������������������������������������������ustar �jss�����������������������������jss�����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2015 Jeremy S. Sanders # Email: Jeremy Sanders # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ############################################################################### from __future__ import division, print_function import numpy as N from ..compat import czip, crepr from .commonfn import _ from .base import DatasetBase from .oned import Dataset from .expression import evalDatasetExpression class DatasetFilterGenerator(object): """This object is shared by all DatasetFiltered datasets, to calculate the filter expression.""" def __init__(self, inexpr, indatasets, prefix="", suffix="", invert=False, replaceblanks=False): """ inexpr = filter expression indatasets = list of input datasets prefix = output prefix suffix = output suffix invert = invert filter replaceblanks = replace filtered values by nans """ self.changeset = -1 self.inexpr = inexpr self.indatasets = indatasets self.prefix = prefix self.suffix = suffix self.invert = invert self.replaceblanks = replaceblanks self.outdatasets = {} def filterNumeric(self, ds, filterarr): """Filter a numeric dataset.""" outdata = {} minlen = len(filterarr) for attr in ds.columns: data = getattr(ds, attr) if data is None: filtered = None else: filtered = N.array(data[:minlen]) if self.replaceblanks: filtered[N.logical_not(filterarr)] = N.nan else: filtered = filtered[filterarr] outdata[attr] = filtered return ds.returnCopyWithNewData(**outdata) def filterText(self, ds, filterarr): """Filter a text dataset.""" data = ds.data if self.replaceblanks: filtered = [(d if f else "") for f, d in czip(filterarr, data)] else: filtered = [d for f, d in czip(filterarr, data) if f] return ds.returnCopyWithNewData(data=filtered) def checkUpdate(self, doc): """Check whether datasets need to be updated.""" if doc.changeset != self.changeset: self.changeset = doc.changeset log = self.evaluateFilter(doc) if log: doc.log('\n'.join(log)+'\n') def evaluateFilter(self, doc): """Update filtering calculation if doc changed. Returns log of errors """ # this is populated by output self.outdatasets = {} # evaluate filter expression d = evalDatasetExpression(doc, self.inexpr) if d is None: return ["Invalid filter expression: '%s'" % self.inexpr] if d.dimensions != 1: return [ _("Invalid number of dimensions in filter expression '%s'") % self.inexpr] if d.datatype != "numeric": return [ _("Input filter expression non-numeric: '%s'") % self.inexpr] filterarr = d.data.astype(N.bool) if self.invert: filterarr = N.logical_not(filterarr) # do filtering of datasets log = [] for name in self.indatasets: ds = doc.data.get(name) if ds is None: continue if ds.dimensions != 1: log.append( _("Filtered dataset '%s' has more than 1 dimension") % name) continue minlen = min(len(ds.data), len(filterarr)) filterarrchop = filterarr[:minlen] if ds.datatype == "numeric": filtered = self.filterNumeric(ds, filterarrchop) elif ds.datatype == "text": filtered = self.filterText(ds, filterarrchop) else: log.append(_("Could not filter dataset '%s'") % name) continue self.outdatasets[name] = filtered return log def saveToFile(self, doc, fileobj): """Save datasets to file.""" # find current datasets in document which use this generator # (some may have been deleted) names = [] for name, ds in sorted(doc.data.items()): if getattr(ds, "generator", None) is self: names.append(ds.namein) args = [ crepr(self.inexpr), crepr(names), ] if self.prefix: args.append("prefix="+crepr(self.prefix)) if self.suffix: args.append("suffix="+crepr(self.suffix)) if self.invert: args.append("invert=True") if self.replaceblanks: args.append("replaceblanks=True") fileobj.write("FilterDatasets(%s)\n" % ", ".join(args)) class DatasetFiltered(DatasetBase): """A dataset which is another dataset filtered by an expression.""" dstype = "Filtered" editable = False def __init__(self, gen, name, doc): DatasetBase.__init__(self) self.generator = gen self.namein = name self.document = doc self.changeset = -1 self._internalds = None self.tags = set() def _checkUpdate(self): """Recalculate if document has changed.""" if self.document.changeset != self.changeset: self.generator.checkUpdate(self.document) self.changeset = self.document.changeset ds = self.generator.outdatasets.get(self.namein) if ds is None: self._internalds = Dataset(data=[]) else: self._internalds = ds def linkedInformation(self): return _("Filtered '%s' using '%s'") % ( self.namein, self.generator.inexpr) def canUnlink(self): return True def saveToFile(self, fileobj, name, **args): """Save plugin to file, if this is the first one.""" # Am I the first dataset in the document with this generator? am1st = False for ds in sorted(self.document.data): data = self.document.data[ds] if data is self: am1st = True break elif getattr(data, "generator", None) is self.generator: # not 1st break if am1st: self.generator.saveToFile(self.document, fileobj) def __getattr__(self, attr): """Lookup attribute from internal dataset.""" self._checkUpdate() return getattr(self._internalds, attr) # these have to be overridden manually def __getitem__(self, key): self._checkUpdate() return self._internalds[key] def __len__(self): self._checkUpdate() return len(self._internalds) �������������������������������������������������������������������������������������������veusz-3.0.1/veusz/datasets/twod.py������������������������������������������������������������������0000664�0001750�0001750�00000030625�13161413406�016631� 0����������������������������������������������������������������������������������������������������ustar �jss�����������������������������jss�����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- coding: utf-8 -*- # Copyright (C) 2016 Jeremy S. Sanders # Email: Jeremy Sanders # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ############################################################################### """Two dimensional datasets.""" from __future__ import division import numpy as N from ..compat import crepr, cstr from .. import utils from .commonfn import _, dsPreviewHelper, convertNumpy from .base import ( DatasetConcreteBase, DatasetException, DatasetExpressionException) def regularGrid(vals): '''Are the values equally spaced?''' if len(vals) < 2: return False vals = N.array(vals) deltas = vals[1:] - vals[:-1] return N.all(N.abs(deltas - deltas[0]) < (deltas[0]*1e-5)) class Dataset2DBase(DatasetConcreteBase): """Ancestor for 2D datasets.""" # number of dimensions the dataset holds dimensions = 2 # dataset type dstype = _('2D') # subclasses must define data, x/yrange, x/yedge, x/ycent as # attributes or properties def isLinearImage(self): """Are these simple linear pixels?""" return ( self.xedge is None and self.yedge is None and self.xcent is None and self.ycent is None ) def getPixelEdges(self, scalefnx=None, scalefny=None): """Return edges for x and y pixels. scalefnx/y: function to convert values to plotted pixel scale (used to calculate edges from centres on screen) """ def fromcentres(vals, scalefn): """Calculate edges from centres.""" if scalefn: vals = scalefn(vals) if len(vals) == 0: e = [] elif len(vals) == 1: if vals[0] != 0: e = [0, vals[0]*2] else: e = [0, 1] else: e = N.concatenate(( [vals[0] - 0.5*(vals[1]-vals[0])], 0.5*(vals[:-1] + vals[1:]), [vals[-1] + 0.5*(vals[-1]-vals[-2])] )) return N.array(e) if self.xedge is not None: xg = self.xedge if scalefnx: xg = scalefnx(xg) elif self.xcent is not None: xg = fromcentres(self.xcent, scalefnx) else: xg = N.linspace(self.xrange[0], self.xrange[1], self.data.shape[1]+1) if scalefnx: xg = scalefnx(xg) if self.yedge is not None: yg = self.yedge if scalefny: yg = scalefny(yg) elif self.ycent is not None: yg = fromcentres(self.ycent, scalefny) else: yg = N.linspace(self.yrange[0], self.yrange[1], self.data.shape[0]+1) if scalefny: yg = scalefny(yg) return xg, yg def getPixelCentres(self): """Return lists of pixel centres in x and y.""" yw, xw = self.data.shape if self.xcent is not None: xc = self.xcent elif self.xedge is not None: xc = 0.5*(self.xedge[:-1]+self.xedge[1:]) else: xc = (N.arange(xw) + 0.5) * ( (self.xrange[1]-self.xrange[0])/xw) + self.xrange[0] if self.ycent is not None: yc = self.ycent elif self.yedge is not None: yc = 0.5*(self.yedge[:-1]+self.yedge[1:]) else: yc = (N.arange(yw) + 0.5) * ( (self.yrange[1]-self.yrange[0])/yw) + self.yrange[0] return xc, yc def getDataRanges(self): """Return ranges of x and y data (as tuples).""" xe, ye = self.getPixelEdges() return (xe[0], xe[-1]), (ye[0], ye[-1]) def datasetAsText(self, fmt='%g', join='\t'): """Return dataset as text. fmt is the format specifier to use join is the string to separate the items """ format = ((fmt+join) * (self.data.shape[1]-1)) + fmt + '\n' # write rows backwards, so lowest y comes first lines = [] for row in self.data[::-1]: line = format % tuple(row) lines.append(line) return ''.join(lines) def userSize(self): """Return dimensions of dataset for user.""" return u'%i×%i' % self.data.shape def userPreview(self): """Return preview of data.""" return dsPreviewHelper(self.data.flatten()) def description(self): """Get description of dataset.""" xr, yr = self.getDataRanges() text = _(u"2D (%i×%i), numeric, x=%.4g->%.4g, y=%.4g->%.4g") % ( self.data.shape[0], self.data.shape[1], xr[0], xr[1], yr[0], yr[1]) return text def returnCopy(self): return Dataset2D( N.array(self.data), xrange=self.xrange, yrange=self.yrange, xedge=self.xedge, yedge=self.yedge, xcent=self.xcent, ycent=self.ycent ) def returnCopyWithNewData(self, **args): return Dataset2D(**args) class Dataset2D(Dataset2DBase): '''Represents a two-dimensional dataset.''' editable = True def __init__(self, data=None, xrange=None, yrange=None, xedge=None, yedge=None, xcent=None, ycent=None): '''Create a two dimensional dataset based on data. data: 2d numpy of imaging data Range specfied by: xrange: a tuple of (start, end) coordinates for x yrange: a tuple of (start, end) coordinates for y _or_ xedge: list of values start..end (npix+1 values) yedge: list of values start..end (npix+1 values) _or_ xcent: list of values (npix values) ycent: list of values (npix values) ''' Dataset2DBase.__init__(self) self.data = convertNumpy(data, dims=2) # try to regularise data if possible # by converting regular grids to ranges if xedge is not None and regularGrid(xedge): xrange = (xedge[0], xedge[-1]) xedge = None if yedge is not None and regularGrid(yedge): yrange = (yedge[0], yedge[-1]) yedge = None if xcent is not None and regularGrid(xcent): delta = 0.5*(xcent[1]-xcent[0]) xrange = (xcent[0]-delta, xcent[-1]+delta) xcent = None if ycent is not None and regularGrid(ycent): delta = 0.5*(ycent[1]-ycent[0]) yrange = (ycent[0]-delta, ycent[-1]+delta) ycent = None self.xrange = self.yrange = None self.xedge = self.yedge = self.xcent = self.ycent = None if xrange is not None: self.xrange = tuple(xrange) elif xedge is not None: self.xedge = N.array(xedge) elif xcent is not None: self.xcent = N.array(xcent) elif self.data is not None: self.xrange = (0, self.data.shape[1]) else: self.xrange = (0., 1.) if yrange is not None: self.yrange = tuple(yrange) elif yedge is not None: self.yedge = N.array(yedge) elif ycent is not None: self.ycent = N.array(ycent) elif self.data is not None: self.yrange = (0, self.data.shape[0]) else: self.yrange = (0., 1.) def saveDataDumpToText(self, fileobj, name): """Write the 2d dataset to the file given.""" fileobj.write("ImportString2D(%s, '''\n" % crepr(name)) if self.xcent is not None: fileobj.write("xcent %s\n" % " ".join(("%e" % v for v in self.xcent)) ) elif self.xedge is not None: fileobj.write("xedge %s\n" % " ".join(("%e" % v for v in self.xedge)) ) else: fileobj.write("xrange %e %e\n" % tuple(self.xrange)) if self.ycent is not None: fileobj.write("ycent %s\n" % " ".join(("%e" % v for v in self.ycent)) ) elif self.yedge is not None: fileobj.write("yedge %s\n" % " ".join(("%e" % v for v in self.yedge)) ) else: fileobj.write("yrange %e %e\n" % tuple(self.yrange)) fileobj.write(self.datasetAsText(fmt='%e', join=' ')) fileobj.write("''')\n") def saveDataDumpToHDF5(self, group, name): """Save 2D data in hdf5 file.""" tdgrp = group.create_group(utils.escapeHDFDataName(name)) tdgrp.attrs['vsz_datatype'] = '2d' for v in ('data', 'xcent', 'xedge', 'ycent', 'yedge', 'xrange', 'yrange'): if getattr(self, v) is not None: tdgrp[v] = getattr(self, v) # map attributes for importing if v != 'data': tdgrp['data'].attrs['vsz_' + v] = tdgrp[v].ref # unicode text not stored properly unless encoded tdgrp['data'].attrs['vsz_name'] = name.encode('utf-8') class Dataset2DXYFunc(Dataset2DBase): """Given a range of x and y, this is a dataset which is a function of this. """ dstype = _('2D f(x,y)') def __init__(self, xstep, ystep, expr): """Create 2d dataset: xstep: tuple(xmin, xmax, step) ystep: tuple(ymin, ymax, step) expr: expression of x and y """ Dataset2DBase.__init__(self) if xstep is None or ystep is None: raise DatasetException('Steps are not set') self.xstep = xstep self.ystep = ystep self.expr = expr self.xrange = ( self.xstep[0] - self.xstep[2]*0.5, self.xstep[1] + self.xstep[2]*0.5) self.yrange = ( self.ystep[0] - self.ystep[2]*0.5, self.ystep[1] + self.ystep[2]*0.5) self.xedge = self.yedge = self.xcent = self.ycent = None self.cacheddata = None self.lastchangeset = -1 @property def data(self): """Return data, or empty array if error.""" try: return self.evalDataset() except DatasetExpressionException as ex: self.document.log(cstr(ex)) return N.array([[]]) def evalDataset(self): """Evaluate the 2d dataset.""" if self.document.changeset == self.lastchangeset: return self.cacheddata env = self.document.evaluate.context.copy() xarange = N.arange(self.xstep[0], self.xstep[1]+self.xstep[2], self.xstep[2]) yarange = N.arange(self.ystep[0], self.ystep[1]+self.ystep[2], self.ystep[2]) ystep, xstep = N.indices( (len(yarange), len(xarange)) ) xstep = xarange[xstep] ystep = yarange[ystep] env['x'] = xstep env['y'] = ystep try: data = eval(self.expr, env) except Exception as e: raise DatasetExpressionException( _("Error evaluating expression: %s\n" "Error: %s") % (self.expr, str(e)) ) # ensure we get an array out of this (in case expr is scalar) data = data + xstep*0 self.cacheddata = data self.lastchangeset = self.document.changeset return data def saveDataRelationToText(self, fileobj, name): '''Save expressions to file. ''' s = 'SetData2DXYFunc(%s, %s, %s, %s, linked=True)\n' % ( crepr(name), crepr(self.xstep), crepr(self.ystep), crepr(self.expr) ) fileobj.write(s) def canUnlink(self): """Can relationship be unlinked?""" return True def linkedInformation(self): """Return linking information.""" return _('Linked 2D function: x=%g:%g:%g, y=%g:%g:%g, z=%s') % tuple( list(self.xstep) + list(self.ystep) + [self.expr]) �����������������������������������������������������������������������������������������������������������veusz-3.0.1/veusz/datasets/text.py������������������������������������������������������������������0000664�0001750�0001750�00000007622�13212734124�016641� 0����������������������������������������������������������������������������������������������������ustar �jss�����������������������������jss�����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2016 Jeremy S. Sanders # Email: Jeremy Sanders # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ############################################################################### """Text datasets.""" from __future__ import division from .commonfn import _ from .base import DatasetConcreteBase from ..compat import cstr, crepr from .. import utils class DatasetText(DatasetConcreteBase): """Represents a text dataset: holding an array of strings.""" dimensions = 1 datatype = displaytype = 'text' columns = ('data',) column_descriptions = (_('Data'),) dstype = _('Text') editable = True def __init__(self, data=None, linked=None): """Initialise dataset with data given. Data are a list of strings.""" DatasetConcreteBase.__init__(self, linked=linked) self.data = list(data) def description(self): return _('Text (length %i)') % len(self.data) def userSize(self): """Size of dataset.""" return str( len(self.data) ) def changeValues(self, type, vals): if type == 'data': self.data = list(vals) else: raise ValueError('type does not contain an allowed value') self.document.modifiedData(self) def uiConvertToDataItem(self, val): """Return a value cast to this dataset data type.""" return cstr(val) def uiDataItemToData(self, val): """Return val converted to data.""" return val def saveDataDumpToText(self, fileobj, name): '''Save data to file. ''' fileobj.write("SetDataText(%s, [\n" % crepr(name)) for line in self.data: fileobj.write(" %s,\n" % crepr(line)) fileobj.write("])\n") def saveDataDumpToHDF5(self, group, name): """Save text data to hdf5 file.""" tgrp = group.create_group(utils.escapeHDFDataName(name)) tgrp.attrs['vsz_datatype'] = 'text' # make sure data are encoded encdata = [x.encode('utf-8') for x in self.data] tgrp['data'] = encdata tgrp['data'].attrs['vsz_name'] = name.encode('utf-8') def datasetAsText(self, fmt=None, join=None): """Return data as text.""" lines = list(self.data) lines.append('') return '\n'.join(lines) def deleteRows(self, row, numrows): """Delete numrows rows starting from row. Returns deleted rows as a dict of {column:data, ...} """ retn = {'data': self.data[row:row+numrows]} del self.data[row:row+numrows] self.document.modifiedData(self) return retn def insertRows(self, row, numrows, rowdata): """Insert numrows rows starting from row. rowdata is a dict of {column: data}. """ data = rowdata.get('data', []) insdata = data + (['']*(numrows-len(data))) for d in insdata[::-1]: self.data.insert(row, d) self.document.modifiedData(self) def returnCopy(self): """Returns version of dataset with no linking.""" return DatasetText(self.data) def returnCopyWithNewData(self, **args): """Return dataset of same type using the column data given.""" return DatasetText(**args) ��������������������������������������������������������������������������������������������������������������veusz-3.0.1/veusz/datasets/expression.py������������������������������������������������������������0000664�0001750�0001750�00000053362�13304514354�020061� 0����������������������������������������������������������������������������������������������������ustar �jss�����������������������������jss�����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2016 Jeremy S. Sanders # Email: Jeremy Sanders # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ############################################################################### """For evaluating dataset expressions and dataset classes using expressions.""" from __future__ import division import re import numpy as N from .commonfn import _ from .base import DatasetExpressionException from .oned import Dataset1DBase, Dataset from .twod import Dataset2DBase, Dataset2D from .text import DatasetText from ..compat import czip, crange, cstr, crepr from .. import utils # split expression on python operators or quoted `DATASET` dataexpr_split_re = re.compile(r'(`.*?`|[\.+\-*/\(\)\[\],<>=!|%^~& ])') # identify whether string is a quoted identifier dataexpr_quote_re = re.compile(r'^`.*`$') dataexpr_columns = {'data':True, 'serr':True, 'perr':True, 'nerr':True} def substituteDatasets(datasets, expression, thispart): """Substitute the names of datasets with calls to a function which will evaluate them. Returns (new expression, list of substituted datasets) """ # split apart the expression to look for dataset names bits = dataexpr_split_re.split(expression) dslist = [] for i, bit in enumerate(bits): # test whether there's an _data, _serr or such at the end of the name part = thispart if dataexpr_quote_re.match(bit): # quoted text, so remove backtick-"quotes" bit = bit[1:-1] # replace name with a function to call bits[i] = "_DS_(%s, %s)" % (crepr(bit), crepr(part)) dslist.append(bit) else: bitbits = bit.split('_') if len(bitbits) > 1: if bitbits[-1] in dataexpr_columns: part = bitbits.pop(-1) bit = '_'.join(bitbits) if bit in datasets: # replace name with a function to call bits[i] = "_DS_(%s, %s)" % (crepr(bit), crepr(part)) dslist.append(bit) return ''.join(bits), dslist def _evaluateDataset(datasets, dsname, dspart): """Return the dataset given. dsname is the name of the dataset dspart is the part to get (e.g. data, serr) """ if dspart not in dataexpr_columns: raise DatasetExpressionException( 'Internal error - invalid dataset part') if dsname not in datasets: raise DatasetExpressionException( _("Dataset '%s' is not defined") % (dsname, )) val = getattr(datasets[dsname], dspart) if val is None: raise DatasetExpressionException( _("Dataset '%s' does not have part '%s'") % (dsname, dspart)) return val def _returnNumericDataset(doc, vals, dimensions, subdatasets): """Used internally to convert a set of values (which needs to be numeric) into a Dataset. subdatasets is list of datasets substituted into expression """ err = None # try to convert array to a numpy array try: vals = N.array(vals, dtype=N.float64) except ValueError: err = _('Could not convert to array') # if error on first time, try to sanitize input arrays if err and dimensions == 1: try: vals = list(vals) vals[0] = N.array(vals[0]) minlen = len(vals[0]) if len(vals) in (2,3): # expand/convert error bars for i in crange(1, len(vals)): if N.isscalar(vals[i]): # convert to scalar vals[i] = N.zeros(minlen) + vals[i] else: # convert to array vals[i] = N.array(vals[i]) if vals[i].ndim != 1: raise ValueError minlen = min(minlen, len(vals[i])) # chop to minimum length for i in crange(len(vals)): vals[i] = vals[i][:minlen] vals = N.array(vals, dtype=N.float64) err = None except (ValueError, IndexError, TypeError): pass if not err: if dimensions == 1: # see whether data values suitable for a 1d dataset if vals.ndim == 1: # 1d, so ok return Dataset(data=vals) elif vals.ndim == 0: # single value return Dataset(data=[vals]) elif vals.ndim == 2: # 2d, see whether data are error bars if vals.shape[0] == 2: return Dataset( data=vals[0,:], serr=vals[1,:]) elif vals.shape[0] == 3: return Dataset( data=vals[0,:], perr=vals[1,:], nerr=vals[2,:]) else: err = _('Expression has wrong dimensions') elif dimensions == 2 and vals.ndim == 2: # try to use dimensions of first-substituted dataset dsrange = {} for ds in subdatasets: d = doc.data[ds] if d.dimensions == 2: for p in ('xrange', 'yrange', 'xedge', 'yedge', 'xcent', 'ycent'): dsrange[p] = getattr(d, p) break return Dataset2D(vals, **dsrange) else: err = _('Expression has wrong dimensions') raise DatasetExpressionException(err) def evalDatasetExpression(doc, origexpr, datatype='numeric', dimensions=1, part='data'): """Evaluate expression and return an appropriate Dataset. part is 'data', 'serr', 'perr' or 'nerr' - these are the dataset parts which are evaluated by the expression Returns None if error """ if not origexpr: # ignore blank names return None d = doc.data.get(origexpr) if ( d is not None and d.datatype == datatype and d.dimensions == dimensions ): return d # support nD datasets by converting to requested shape if d is not None and d.dimensions == -1 and d.data.ndim == dimensions: if dimensions == 1: return Dataset(d.data) elif dimensions == 2: return Dataset2D(d.data) if utils.id_re.match(origexpr): # if name is a python identifier, then it has to be a dataset # name. As it wasn't there, just return with nothing rather # than print error message. return None # replace dataset names by calls to _DS_(name,part) expr, subdatasets = substituteDatasets(doc.data, origexpr, part) comp = doc.evaluate.compileCheckedExpression(expr, origexpr=origexpr) if comp is None: return # set up environment for evaluation env = doc.evaluate.context.copy() def doeval(dsname, dspart): return _evaluateDataset(doc.data, dsname, dspart) env['_DS_'] = doeval # do evaluation try: evalout = eval(comp, env) except Exception as ex: doc.log(_("Error evaluating '%s': '%s'" % (origexpr, cstr(ex)))) return None # return correct dataset for data type try: if datatype == 'numeric': return _returnNumericDataset(doc, evalout, dimensions, subdatasets) elif datatype == 'text': return DatasetText([cstr(x) for x in evalout]) else: raise RuntimeError('Invalid data type') except DatasetExpressionException as ex: doc.log(_("Error evaluating '%s': %s\n") % (origexpr, cstr(ex))) return None class DatasetExpression(Dataset1DBase): """A dataset which is linked to another dataset by an expression.""" dstype = _('Expression') def __init__(self, data=None, serr=None, nerr=None, perr=None, parametric=None): """Initialise the dataset with the expressions given. parametric is option and can be (minval, maxval, steps) or None """ Dataset1DBase.__init__(self) # store the expressions to use to generate the dataset self.expr = {} self.expr['data'] = data self.expr['serr'] = serr self.expr['nerr'] = nerr self.expr['perr'] = perr self.parametric = parametric self.docchangeset = -1 self.evaluated = {} def evaluateDataset(self, dsname, dspart): """Return the dataset given. dsname is the name of the dataset dspart is the part to get (e.g. data, serr) """ return _evaluateDataset(self.document.data, dsname, dspart) def _evaluatePart(self, expr, part): """Evaluate expression expr for part part. Returns True if succeeded """ # replace dataset names with calls newexpr = substituteDatasets(self.document.data, expr, part)[0] comp = self.document.evaluate.compileCheckedExpression( newexpr, origexpr=expr) if comp is None: return False # set up environment to evaluate expressions in environment = self.document.evaluate.context.copy() # create dataset using parametric expression if self.parametric: p = self.parametric if p[2] >= 2: deltat = (p[1]-p[0]) / (p[2]-1) t = N.arange(p[2])*deltat + p[0] else: t = N.array([p[0]]) environment['t'] = t # this fn gets called to return the value of a dataset environment['_DS_'] = self.evaluateDataset # actually evaluate the expression try: result = eval(comp, environment) evalout = N.array(result, N.float64) if len(evalout.shape) > 1: raise RuntimeError("Number of dimensions is not 1") except Exception as ex: self.document.log( _("Error evaluating expression: %s\n" "Error: %s") % (self.expr[part], cstr(ex)) ) return False # make evaluated error expression have same shape as data if part != 'data': data = self.evaluated['data'] if evalout.shape == (): # zero dimensional - expand to data shape evalout = N.resize(evalout, data.shape) else: # 1-dimensional - make it right size and trim oldsize = evalout.shape[0] evalout = N.resize(evalout, data.shape) evalout[oldsize:] = N.nan else: if evalout.shape == (): # zero dimensional - make a single point evalout = N.resize(evalout, 1) self.evaluated[part] = evalout return True def updateEvaluation(self): """Update evaluation of parts of dataset. Returns False if problem with any evaluation """ ok = True if self.docchangeset != self.document.changeset: # avoid infinite recursion! self.docchangeset = self.document.changeset # zero out previous values for part in self.columns: self.evaluated[part] = None # update all parts for part in self.columns: expr = self.expr[part] if expr is not None and expr.strip() != '': ok = ok and self._evaluatePart(expr, part) return ok def _propValues(self, part): """Check whether expressions need reevaluating, and recalculate if necessary.""" self.updateEvaluation() # catch case where error in setting data, need to return "real" data if self.evaluated['data'] is None: self.evaluated['data'] = N.array([]) return self.evaluated[part] # expose evaluated data as properties # this allows us to recalculate the expressions on the fly data = property(lambda self: self._propValues('data')) serr = property(lambda self: self._propValues('serr')) perr = property(lambda self: self._propValues('perr')) nerr = property(lambda self: self._propValues('nerr')) def saveDataRelationToText(self, fileobj, name): '''Save data to file. ''' parts = [crepr(name), crepr(self.expr['data'])] if self.expr['serr']: parts.append('symerr=%s' % crepr(self.expr['serr'])) if self.expr['nerr']: parts.append('negerr=%s' % crepr(self.expr['nerr'])) if self.expr['perr']: parts.append('poserr=%s' % crepr(self.expr['perr'])) if self.parametric is not None: parts.append('parametric=%s' % crepr(self.parametric)) parts.append('linked=True') s = 'SetDataExpression(%s)\n' % ', '.join(parts) fileobj.write(s) def __getitem__(self, key): """Return a dataset based on this dataset We override this from DatasetConcreteBase as it would return a DatsetExpression otherwise, not chopped sets of data. """ return Dataset(**self._getItemHelper(key)) def canUnlink(self): """Whether dataset can be unlinked.""" return True def linkedInformation(self): """Return information about linking.""" text = [] if self.parametric: text.append(_('Linked parametric dataset')) else: text.append(_('Linked expression dataset')) for label, part in czip(self.column_descriptions, self.columns): if self.expr[part]: text.append('%s: %s' % (label, self.expr[part])) if self.parametric: text.append(_("where t goes from %g:%g in %i steps") % self.parametric) return '\n'.join(text) def getSpacing(data): """Given a set of values, get minimum, maximum, step size and number of steps. Function allows that values may be missing Function assumes that at least one of the steps is the minimum step size (i.e. steps are not all multiples of some mininimum) """ try: data = N.array(data) + 0 except ValueError: raise DatasetExpressionException('Expression is not an array') if len(data.shape) != 1: raise DatasetExpressionException('Array is not 1D') if len(data) < 2: raise DatasetExpressionException('Two values required to convert to 2D') uniquesorted = N.unique(data) sigfactor = (uniquesorted[-1]-uniquesorted[0])*1e-13 # differences between elements deltas = N.unique( N.ediff1d(uniquesorted) ) mindelta = None for delta in deltas: if delta > sigfactor: if mindelta is None: # first delta mindelta = delta elif N.fabs(mindelta-delta) > sigfactor: # new delta - check is multiple of old delta ratio = delta/mindelta if N.fabs(int(ratio)-ratio) > 1e-3: raise DatasetExpressionException( 'Variable spacings not yet supported ' 'in constructing 2D datasets') if mindelta is None or mindelta == 0: raise DatasetExpressionException('Could not identify delta') return (uniquesorted[0], uniquesorted[-1], mindelta, int((uniquesorted[-1]-uniquesorted[0])/mindelta)+1) class Dataset2DXYZExpression(Dataset2DBase): '''A 2d dataset with expressions for x, y and z.''' dstype = _('2D XYZ') def __init__(self, exprx, expry, exprz): """Initialise dataset. Parameters are mathematical expressions based on datasets.""" Dataset2DBase.__init__(self) self.lastchangeset = -1 self.cacheddata = None self.xedge = self.yedge = self.xcent = self.ycent = None # copy parameters self.exprx = exprx self.expry = expry self.exprz = exprz def evaluateDataset(self, dsname, dspart): """Return the dataset given. dsname is the name of the dataset dspart is the part to get (e.g. data, serr) """ return _evaluateDataset(self.document.data, dsname, dspart) def evalDataset(self): """Return the evaluated dataset.""" # FIXME: handle irregular grids # return cached data if document unchanged if self.document.changeset == self.lastchangeset: return self.cacheddata self.lastchangeset = self.document.changeset self.cacheddata = None evaluated = {} environment = self.document.evaluate.context.copy() environment['_DS_'] = self.evaluateDataset # evaluate the x, y and z expressions for name in ('exprx', 'expry', 'exprz'): origexpr = getattr(self, name) expr = substituteDatasets(self.document.data, origexpr, 'data')[0] comp = self.document.evaluate.compileCheckedExpression( expr, origexpr=origexpr) if comp is None: return None try: evaluated[name] = eval(comp, environment) except Exception as e: self.document.log(_("Error evaluating expression: %s\n" "Error: %s") % (expr, cstr(e)) ) return None minx, maxx, stepx, stepsx = getSpacing(evaluated['exprx']) miny, maxy, stepy, stepsy = getSpacing(evaluated['expry']) # update cached x and y ranges self._xrange = (minx-stepx*0.5, maxx+stepx*0.5) self._yrange = (miny-stepy*0.5, maxy+stepy*0.5) self.cacheddata = N.empty( (stepsy, stepsx) ) self.cacheddata[:,:] = N.nan xpts = ((1./stepx)*(evaluated['exprx']-minx)).astype('int32') ypts = ((1./stepy)*(evaluated['expry']-miny)).astype('int32') # this is ugly - is this really the way to do it? try: self.cacheddata.flat [ xpts + ypts*stepsx ] = evaluated['exprz'] except Exception as e: self.document.log(_("Shape mismatch when constructing dataset\n" "Error: %s") % cstr(e) ) return None return self.cacheddata @property def xrange(self): """Get x range of data as a tuple (min, max).""" return self.getDataRanges()[0] @property def yrange(self): """Get y range of data as a tuple (min, max).""" return self.getDataRanges()[1] def getDataRanges(self): """Get both ranges of axis.""" ds = self.evalDataset() if ds is None: return ( (0., 1.), (0., 1.) ) return (self._xrange, self._yrange) @property def data(self): """Get data, or none if error.""" ds = self.evalDataset() if ds is None: return N.array( [[]] ) return ds def saveDataRelationToText(self, fileobj, name): '''Save expressions to file. ''' s = 'SetData2DExpressionXYZ(%s, %s, %s, %s, linked=True)\n' % ( crepr(name), crepr(self.exprx), crepr(self.expry), crepr(self.exprz) ) fileobj.write(s) def canUnlink(self): """Can relationship be unlinked?""" return True def linkedInformation(self): """Return linking information.""" return _('Linked 2D function: x=%s, y=%s, z=%s') % ( self.exprx, self.expry, self.exprz) class Dataset2DExpression(Dataset2DBase): """Evaluate an expression of 2d datasets.""" dstype = _('2D Expr') def __init__(self, expr): """Create 2d expression dataset.""" Dataset2DBase.__init__(self) self.expr = expr self.lastchangeset = -1 @property def data(self): """Return data, or empty array if error.""" ds = self.evalDataset() return ds.data if ds is not None else N.array([[]]) @property def xrange(self): """Return x range.""" ds = self.evalDataset() return ds.xrange if ds is not None else [0., 1.] @property def yrange(self): """Return y range.""" ds = self.evalDataset() return ds.yrange if ds is not None else [0., 1.] @property def xedge(self): """Return x grid points.""" ds = self.evalDataset() return ds.xedge if ds is not None else None @property def yedge(self): """Return y grid points.""" ds = self.evalDataset() return ds.yedge if ds is not None else None @property def xcent(self): """Return x cent points.""" ds = self.evalDataset() return ds.xcent if ds is not None else None @property def ycent(self): """Return y cent points.""" ds = self.evalDataset() return ds.ycent if ds is not None else None def evalDataset(self): """Do actual evaluation.""" return evalDatasetExpression(self.document, self.expr, dimensions=2) def saveDataRelationToText(self, fileobj, name): '''Save expression to file.''' s = 'SetData2DExpression(%s, %s, linked=True)\n' % ( crepr(name), crepr(self.expr) ) fileobj.write(s) def canUnlink(self): """Can relationship be unlinked?""" return True def linkedInformation(self): """Return linking information.""" return _('Linked 2D expression: %s') % self.expr ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������veusz-3.0.1/veusz/datasets/commonfn.py��������������������������������������������������������������0000664�0001750�0001750�00000006367�13161413406�017476� 0����������������������������������������������������������������������������������������������������ustar �jss�����������������������������jss�����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2016 Jeremy S. Sanders # Email: Jeremy Sanders # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ############################################################################### from __future__ import division import re import numpy as N from .. import qtall as qt4 def _(text, disambiguation=None, context="Datasets"): """Translate text.""" return qt4.QCoreApplication.translate(context, text, disambiguation) def convertNumpy(a, dims=1): """Convert to a numpy double if possible. dims is number of dimensions to check for """ if a is None: # leave as None return None elif isinstance(a, N.ndarray): # make conversion if numpy type is not correct if a.dtype != N.float64: a = a.astype(N.float64) else: # convert to numpy array a = N.array(a, dtype=N.float64) if a.ndim != dims: if a.ndim == 0: if dims == 1: a = a.reshape((1,)) elif dims == 2: a = a.reshape((1,1)) else: raise RuntimeError() else: raise ValueError("Only %i-dimensional arrays or lists allowed" % dims) return a def convertNumpyAbs(a): """Convert to numpy 64 bit positive values, if possible.""" if a is None: return None else: return N.abs( convertNumpy(a) ) def convertNumpyNegAbs(a): """Convert to numpy 64 bit negative values, if possible.""" if a is None: return None else: return -N.abs( convertNumpy(a) ) def copyOrNone(a): """Return a copy if not None, or None.""" if a is None: return None elif isinstance(a, N.ndarray): return N.array(a) elif isinstance(a, list): return list(a) def datasetNameToDescriptorName(name): """Return descriptor name for dataset.""" if re.match('^[0-9A-Za-z_]+$', name): return name else: return '`%s`' % name def dsPreviewHelper(d): """Get preview of numpy data d.""" if d.shape[0] <= 6: line1 = ', '.join( ['%.3g' % x for x in d] ) else: line1 = ', '.join( ['%.3g' % x for x in d[:3]] + [ '...' ] + ['%.3g' % x for x in d[-3:]] ) try: line2 = _('mean: %.3g, min: %.3g, max: %.3g') % ( N.nansum(d) / N.isfinite(d).sum(), N.nanmin(d), N.nanmax(d)) except (ValueError, ZeroDivisionError): # nanXXX returns error if no valid data points return line1 return line1 + '\n' + line2 �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������veusz-3.0.1/veusz/datasets/__init__.py��������������������������������������������������������������0000664�0001750�0001750�00000002227�13161413406�017410� 0����������������������������������������������������������������������������������������������������ustar �jss�����������������������������jss�����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2016 Jeremy S. Sanders # Email: Jeremy Sanders # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ############################################################################### from .base import * from .oned import * from .twod import * from .nd import * from .text import * from .date import * from .filtered import * from .histo import * from .expression import * from .plugin import * from .commonfn import * from .helpers import * �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������veusz-3.0.1/veusz/datasets/base.py������������������������������������������������������������������0000664�0001750�0001750�00000014145�13173073033�016566� 0����������������������������������������������������������������������������������������������������ustar �jss�����������������������������jss�����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2016 Jeremy S. Sanders # Email: Jeremy Sanders # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ############################################################################### """Base class for all Datasets.""" from __future__ import division from ..compat import cbasestr from .commonfn import _ class DatasetException(Exception): """Raised with dataset errors.""" pass class DatasetExpressionException(DatasetException): """Raised if there is an error evaluating a dataset expression.""" pass class DatasetBase(object): """Base class for all datasets.""" class DatasetConcreteBase(DatasetBase): """A base dataset class for datasets which are real, and not proxies, etc.""" # number of dimensions the dataset holds dimensions = 0 # datatype is fundamental type of data # displaytype is formatting suggestion for data datatype = displaytype = 'numeric' # dataset type to show to user dstype = 'Dataset' # list of columns in dataset (if any) columns = () # use descriptions for columns column_descriptions = () # can values be edited editable = False # class for representing part of this dataset subsetclass = None def __init__(self, linked=None): """Initialise commonfn members.""" # document member set when this dataset is set in document self.document = None # file this dataset is linked to self.linked = linked # tags applied to dataset self.tags = set() def saveLinksToSavedDoc(self, fileobj, savedlinks, relpath=None): '''Save the link to the saved document, if this dataset is linked. savedlinks is a dict containing any linked files which have already been written relpath is a directory to save linked files relative to ''' # links should only be saved once if self.linked is not None and self.linked not in savedlinks: savedlinks[self.linked] = True self.linked.saveToFile(fileobj, relpath=relpath) def saveToFile(self, fileobj, name, mode='text', hdfgroup=None): """Save dataset to file.""" self.saveDataRelationToText(fileobj, name) if self.linked is None: if mode == 'text': self.saveDataDumpToText(fileobj, name) elif mode == 'hdf5': self.saveDataDumpToHDF5(hdfgroup, name) def saveDataRelationToText(self, fileobj, name): """Save a dataset relation to a text stream fileobj. Not for datasets which are raw data.""" def saveDataDumpToText(self, fileobj, name): """Save dataset to file if file is text and data is actually a set of data and not a relation """ def saveDataDumpToHDF5(self, group, name): """Save dumped dataset to HDF5. group is the group to save it in (h5py group) """ def userSize(self): """Return dimensions of dataset for user.""" return "" def userPreview(self): """Return a small preview of the dataset for the user, e.g. 1, 2, 3, ..., 4, 5, 6.""" return None def description(self): """Get description of dataset.""" return "" def uiConvertToDataItem(self, val): """Return a value cast to this dataset data type. We assume here it is a float, so override if not """ from .. import setting if isinstance(val, cbasestr): val, ok = setting.uilocale.toDouble(val) if ok: return val raise ValueError("Invalid floating point number") return float(val) def uiDataItemToData(self, val): """Return val converted to data.""" return float(val) def _getItemHelper(self, key): """Help get arguments to constructor.""" args = {} for col in self.columns: array = getattr(self, col) if array is not None: args[col] = array[key] return args def __getitem__(self, key): """Return a dataset based on this dataset e.g. dataset[5:100] - make a dataset based on items 5 to 99 inclusive """ return self.returnCopyWithNewData(**self._getItemHelper(key)) def __len__(self): """Return length of dataset.""" return len(self.data) def deleteRows(self, row, numrows): """Delete numrows rows starting from row. Returns deleted rows as a dict of {column:data, ...} """ pass def insertRows(self, row, numrows, rowdata): """Insert numrows rows starting from row. rowdata is a dict of {column: data}. """ pass def canUnlink(self): """Can dataset be unlinked?""" return self.linked is not None def linkedInformation(self): """Return information about any linking for the user.""" if self.linked is None: return _('Linked file: None') else: return _('Linked file: %s') % self.linked.filename def returnCopy(self): """Return an unlinked copy of self.""" pass def returnCopyWithNewData(self, **args): """Return copy with new data given.""" pass def renameable(self): """Is it possible to rename this dataset?""" return self.linked is None def datasetAsText(self, fmt='%g', join='\t'): """Return dataset as text (for use by user).""" return '' ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������veusz-3.0.1/veusz/datasets/oned.py������������������������������������������������������������������0000664�0001750�0001750�00000025433�13173073033�016603� 0����������������������������������������������������������������������������������������������������ustar �jss�����������������������������jss�����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2016 Jeremy S. Sanders # Email: Jeremy Sanders # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ############################################################################### """One dimensional datasets.""" import numpy as N from .commonfn import ( _, dsPreviewHelper, copyOrNone, convertNumpy, convertNumpyAbs, convertNumpyNegAbs, datasetNameToDescriptorName) from .base import DatasetConcreteBase, DatasetException from ..compat import czip, crepr from .. import utils class Dataset1DBase(DatasetConcreteBase): """Base for 1D datasets.""" # number of dimensions the dataset holds dimensions = 1 columns = ('data', 'serr', 'nerr', 'perr') column_descriptions = (_('Data'), _('Sym. errors'), _('Neg. errors'), _('Pos. errors') ) dstype = _('1D') # subclasses must define .data, .serr, .perr, .nerr def userSize(self): """Size of dataset.""" return str( self.data.shape[0] ) def userPreview(self): """Preview of data.""" return dsPreviewHelper(self.data) def description(self): """Get description of dataset.""" if self.serr is not None: templ = _("1D (length %i, symmetric errors)") elif self.perr is not None or self.nerr is not None: templ = _("1D (length %i, asymmetric errors)") else: templ = _("1D (length %i)") return templ % len(self.data) def invalidDataPoints(self): """Return a numpy bool detailing which datapoints are invalid.""" valid = N.isfinite(self.data) for error in self.serr, self.perr, self.nerr: if error is not None: valid = N.logical_and(valid, N.isfinite(error)) return N.logical_not(valid) def hasErrors(self): '''Whether errors on dataset''' return (self.serr is not None or self.nerr is not None or self.perr is not None) def getPointRanges(self): '''Get range of coordinates for each point in the form (minima, maxima).''' minvals = self.data.copy() maxvals = self.data.copy() if self.serr is not None: minvals -= self.serr maxvals += self.serr if self.nerr is not None: minvals += self.nerr if self.perr is not None: maxvals += self.perr return ( minvals[N.isfinite(minvals)], maxvals[N.isfinite(maxvals)] ) def getRange(self): '''Get total range of coordinates. Returns None if empty.''' minvals, maxvals = self.getPointRanges() if len(minvals) > 0 and len(maxvals) > 0: return ( minvals.min(), maxvals.max() ) else: return None def rangeVisit(self, fn): '''Call fn on data points and error values, in order to get range.''' fn(self.data) if self.serr is not None: fn(self.data - self.serr) fn(self.data + self.serr) if self.nerr is not None: fn(self.data + self.nerr) if self.perr is not None: fn(self.data + self.perr) def empty(self): '''Is the data defined?''' return self.data is None or len(self.data) == 0 def datasetAsText(self, fmt='%g', join='\t'): """Return data as text.""" # work out which columns to write cols = [] for c in (self.data, self.serr, self.perr, self.nerr): if c is not None: cols.append(c) # format statement format = (fmt + join) * (len(cols)-1) + fmt + '\n' # do the conversion lines = [] for line in czip(*cols): lines.append( format % line ) return ''.join(lines) def returnCopy(self): """Return version of dataset with no linking.""" return Dataset(data = copyOrNone(self.data), serr = copyOrNone(self.serr), perr = copyOrNone(self.perr), nerr = copyOrNone(self.nerr)) def returnCopyWithNewData(self, **args): """Return dataset of same type using the column data given.""" return Dataset(**args) class Dataset(Dataset1DBase): '''Represents a dataset.''' editable = True def __init__(self, data = None, serr = None, nerr = None, perr = None, linked = None): '''Initialise dataset with the sets of values given. The values can be given as numpy 1d arrays or lists of numbers linked optionally specifies a LinkedFile to link the dataset to ''' Dataset1DBase.__init__(self, linked=linked) # convert data to numpy arrays self.data = convertNumpy(data) self.serr = convertNumpyAbs(serr) self.perr = convertNumpyAbs(perr) self.nerr = convertNumpyNegAbs(nerr) # check the sizes of things match up s = self.data.shape for x in self.serr, self.nerr, self.perr: if x is not None and x.shape != s: raise DatasetException('Lengths of error data do not match data') def changeValues(self, thetype, vals): """Change the requested part of the dataset to vals. thetype == data | serr | perr | nerr """ if thetype in self.columns: setattr(self, thetype, vals) else: raise ValueError('thetype does not contain an allowed value') # just a check... s = self.data.shape for x in (self.serr, self.nerr, self.perr): assert x is None or x.shape == s # tell the document that we've changed self.document.modifiedData(self) def saveDataDumpToText(self, fileobj, name): '''Save data to file. ''' # build up descriptor descriptor = datasetNameToDescriptorName(name) + '(numeric)' if self.serr is not None: descriptor += ',+-' if self.perr is not None: descriptor += ',+' if self.nerr is not None: descriptor += ',-' fileobj.write( "ImportString(%s,'''\n" % crepr(descriptor) ) fileobj.write( self.datasetAsText(fmt='%e', join=' ') ) fileobj.write( "''')\n" ) def saveDataDumpToHDF5(self, group, name): """Save dataset to HDF5.""" # store as a group to simplify things odgrp = group.create_group(utils.escapeHDFDataName(name)) odgrp.attrs['vsz_datatype'] = '1d' for key, suffix in ( ('data', ''), ('serr', ' (+-)'), ('perr', ' (+)'), ('nerr', ' (-)')): if getattr(self, key) is not None: odgrp[key] = getattr(self, key) odgrp[key].attrs['vsz_name'] = (name + suffix).encode('utf-8') def deleteRows(self, row, numrows): """Delete numrows rows starting from row. Returns deleted rows as a dict of {column:data, ...} """ retn = {} for col in self.columns: coldata = getattr(self, col) if coldata is not None: retn[col] = coldata[row:row+numrows] setattr(self, col, N.delete( coldata, N.s_[row:row+numrows] )) self.document.modifiedData(self) return retn def insertRows(self, row, numrows, rowdata): """Insert numrows rows starting from row. rowdata is a dict of {column: data}. """ for col in self.columns: coldata = getattr(self, col) data = N.zeros(numrows) if col in rowdata: data[:len(rowdata[col])] = N.array(rowdata[col]) if coldata is not None: newdata = N.insert(coldata, [row]*numrows, data) setattr(self, col, newdata) self.document.modifiedData(self) class DatasetRange(Dataset1DBase): """Dataset consisting of a range of values e.g. 1 to 10 in 10 steps.""" dstype = _('Range') def __init__(self, numsteps, data, serr=None, perr=None, nerr=None): """Construct dataset. numsteps: number of steps in range data, serr, perr and nerr are tuples containing (start, stop) values.""" Dataset1DBase.__init__(self) self.range_data = data self.range_serr = serr self.range_perr = perr self.range_nerr = nerr self.numsteps = numsteps for name in ('data', 'serr', 'perr', 'nerr'): val = getattr(self, 'range_%s' % name) if val is not None: minval, maxval = val if numsteps == 1: vals = N.array( [minval] ) else: delta = (maxval - minval) / (numsteps-1) vals = N.arange(numsteps)*delta + minval else: vals = None setattr(self, name, vals) def __getitem__(self, key): """Return a dataset based on this dataset We override this from DatasetConcreteBase as it would return a DatsetExpression otherwise, not chopped sets of data. """ return Dataset(**self._getItemHelper(key)) def userSize(self): """Size of dataset.""" return str( self.numsteps ) def saveDataRelationToText(self, fileobj, name): """Save dataset to file.""" parts = [crepr(name), crepr(self.numsteps), crepr(self.range_data)] if self.range_serr is not None: parts.append('symerr=%s' % crepr(self.range_serr)) if self.range_perr is not None: parts.append('poserr=%s' % crepr(self.range_perr)) if self.range_nerr is not None: parts.append('negerr=%s' % crepr(self.range_nerr)) parts.append('linked=True') s = 'SetDataRange(%s)\n' % ', '.join(parts) fileobj.write(s) def canUnlink(self): return True def linkedInformation(self): """Return information about linking.""" text = [_('Linked range dataset')] for label, part in czip(self.column_descriptions, self.columns): val = getattr(self, 'range_%s' % part) if val: text.append('%s: %g:%g' % (label, val[0], val[1])) return '\n'.join(text) �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������veusz-3.0.1/veusz/datasets/date.py������������������������������������������������������������������0000664�0001750�0001750�00000010740�13173073033�016566� 0����������������������������������������������������������������������������������������������������ustar �jss�����������������������������jss�����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2016 Jeremy S. Sanders # Email: Jeremy Sanders # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ############################################################################### """Date time datasets.""" from __future__ import division import numpy as N from .. import utils from ..compat import cbasestr, cstr, crepr from .commonfn import _, convertNumpy, datasetNameToDescriptorName from .oned import Dataset1DBase class DatasetDateTimeBase(Dataset1DBase): """Dataset holding dates and times.""" columns = ('data',) column_descriptions = (_('Data'),) dstype = _('Date') displaytype = 'date' def description(self): return _('Date/time (length %i)') % len(self.data) def returnCopy(self): """Returns version of dataset with no linking.""" return DatasetDateTime(data=N.array(self.data)) def returnCopyWithNewData(self, **args): """Return dataset of same type using the column data given.""" return DatasetDateTime(**args) def uiConvertToDataItem(self, val): """Return a value cast to this dataset data type.""" if isinstance(val, cbasestr): v = utils.dateStringToDate( cstr(val) ) if not N.isfinite(v): try: v = float(val) except ValueError: pass return v else: return N.nan def uiDataItemToData(self, val): """Return val converted to data.""" return utils.dateFloatToString(val) def datasetAsText(self, fmt=None, join=None): """Return data as text.""" lines = [ utils.dateFloatToString(val) for val in self.data ] lines.append('') return '\n'.join(lines) class DatasetDateTime(DatasetDateTimeBase): """Standard date/time class for use by humans.""" editable = True def __init__(self, data=None, linked=None): DatasetDateTimeBase.__init__(self, linked=linked) self.data = convertNumpy(data) self.perr = self.nerr = self.serr = None def saveDataDumpToText(self, fileobj, name): '''Save data to file. ''' descriptor = datasetNameToDescriptorName(name) + '(date)' fileobj.write( "ImportString(%s,'''\n" % crepr(descriptor) ) fileobj.write( self.datasetAsText() ) fileobj.write( "''')\n" ) def saveDataDumpToHDF5(self, group, name): """Save date data to hdf5 file.""" dgrp = group.create_group(utils.escapeHDFDataName(name)) dgrp.attrs['vsz_datatype'] = 'date' dgrp['data'] = self.data data = dgrp['data'] data.attrs['vsz_convert_datetime'] = 1 data.attrs['vsz_name'] = name.encode('utf-8') def deleteRows(self, row, numrows): """Delete numrows rows starting from row. Returns deleted rows as a dict of {column:data, ...} """ retn = { 'data': self.data[row:row+numrows], } self.data = N.delete(self.data, N.s_[row:row+numrows]) self.document.modifiedData(self) return retn def insertRows(self, row, numrows, rowdata): """Insert numrows rows starting from row. rowdata is a dict of {column: data}. """ data = N.zeros(numrows) if 'data' in rowdata: data[:len(rowdata['data'])] = N.array(rowdata['data']) self.data = N.insert(self.data, [row]*numrows, data) self.document.modifiedData(self) def changeValues(self, thetype, vals): """Change the requested part of the dataset to vals. thetype == data """ if thetype != 'data': raise ValueError('invalid column %s' % thetype) self.data = N.array(vals) # tell the document that we've changed self.document.modifiedData(self) ��������������������������������veusz-3.0.1/veusz/datasets/helpers.py���������������������������������������������������������������0000664�0001750�0001750�00000007153�13304514354�017321� 0����������������������������������������������������������������������������������������������������ustar �jss�����������������������������jss�����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2004 Jeremy S. Sanders # Email: Jeremy Sanders # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ############################################################################## """Functions to be used by module users as helpers.""" from __future__ import division import numpy as N from .base import DatasetBase from .oned import Dataset from .twod import Dataset2D from .text import DatasetText from ..compat import cstr def valsToDataset(vals, datatype, dimensions): """Return a dataset given a numpy array of values.""" if datatype == 'numeric': try: nvals = N.array(vals, dtype=N.float64) if nvals.ndim == dimensions: if nvals.ndim == 1: return Dataset(data=nvals) elif nvals.ndim == 2: return Dataset2D(nvals) except ValueError: pass elif datatype == 'text': try: return DatasetText([cstr(x) for x in vals]) except ValueError: pass raise RuntimeError('Invalid array') def generateValidDatasetParts(datasets, breakds=True): """Generator to return array of valid parts of datasets. if breakds is True: Yields new datasets between rows which are invalid else: Yields single, filtered dataset """ # find NaNs and INFs in input dataset invalid = datasets[0].invalidDataPoints() minlen = invalid.shape[0] for ds in datasets[1:]: if isinstance(ds, DatasetBase) and not ds.empty(): nextinvalid = ds.invalidDataPoints() minlen = min(nextinvalid.shape[0], minlen) invalid = N.logical_or(invalid[:minlen], nextinvalid[:minlen]) if breakds: # return multiple datasets, breaking at invalid values # get indexes of invalid points indexes = invalid.nonzero()[0].tolist() # no bad points: optimisation if not indexes: yield datasets return # add on shortest length of datasets indexes.append(minlen) lastindex = 0 for index in indexes: if index != lastindex: retn = [] for ds in datasets: if ds is not None and ( not isinstance(ds, DatasetBase) or not ds.empty()): retn.append(ds[lastindex:index]) else: retn.append(None) yield retn lastindex = index+1 else: # in this mode we return single datasets where the invalid # values are masked out if not N.any(invalid): yield datasets return valid = N.logical_not(invalid) retn = [] for ds in datasets: if ds is None: retn.append(None) else: retn.append(ds[valid]) yield retn ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������veusz-3.0.1/veusz/datasets/nd.py��������������������������������������������������������������������0000664�0001750�0001750�00000006740�13161413406�016256� 0����������������������������������������������������������������������������������������������������ustar �jss�����������������������������jss�����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- coding: utf-8 -*- # Copyright (C) 2016 Jeremy S. Sanders # Email: Jeremy Sanders # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ############################################################################### """N dimensional datasets.""" import numpy as N from ..compat import crepr from .. import utils from .commonfn import _, dsPreviewHelper from .base import DatasetConcreteBase class DatasetNDBase(DatasetConcreteBase): """N-dimensional datasets.""" dimensions = -1 dstype = _('nD') editable = False def userSize(self): return u'×'.join(str(x) for x in self.data.shape) def userPreview(self): return dsPreviewHelper(N.ravel(self.data)) def description(self): return _('ND (%s), numeric') % self.userSize() def returnCopy(self): return DatasetND(data=self.data) def returnCopyWithNewData(self, **args): return DatasetND(**args) def empty(self): """Is the data defined?""" return len(self.data) == 0 def datasetAsText(self, fmt='%g', join='\t'): """Dataset as text for copy, paste, etc.""" def fmtrecurse(arr): if arr.ndim == 0: return fmt % arr + '\n' elif arr.ndim == 1: out = [fmt % v for v in arr] return join.join(out) else: out = [] for v in arr: out.append(fmtrecurse(v)) out.append('') return '\n'.join(out) return fmtrecurse(self.data) class DatasetND(DatasetNDBase): def __init__(self, data=None): """data is a numpy array of N dimensions.""" DatasetNDBase.__init__(self) if isinstance(data, N.ndarray): self.data = data.astype(N.float64) elif isinstance(data, list) or isinstance(data, tuple): self.data = N.array(dtype=N.float64) else: raise ValueError("Could not convert data to nD numpy array.") def saveDataDumpToText(self, fileobj, name): """Save data to vsz in form of text.""" fileobj.write("ImportStringND(%s, '''\n" % crepr(name)) if self.data.shape[0] == 1: # unfortunately it's hard to decode a single dimension # here so we record this unambiguously shape = ' '.join((str(d) for d in self.data.shape)) fileobj.write("shape %s\n" % shape) fileobj.write(self.datasetAsText(fmt='%e', join=' ')) fileobj.write("''')\n") def saveDataDumpToHDF5(self, group, name): """Save dataset to VSZ HDF5 format.""" escname = utils.escapeHDFDataName(name) group[escname] = self.data group[escname].attrs['vsz_datatype'] = 'nd' group[escname].attrs['vsz_name'] = name.encode('utf-8') ��������������������������������veusz-3.0.1/veusz/document/�������������������������������������������������������������������������0000775�0001750�0001750�00000000000�13325026670�015307� 5����������������������������������������������������������������������������������������������������ustar �jss�����������������������������jss�����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������veusz-3.0.1/veusz/document/colors.py����������������������������������������������������������������0000664�0001750�0001750�00000032444�13161413406�017164� 0����������������������������������������������������������������������������������������������������ustar �jss�����������������������������jss�����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2017 Jeremy S. Sanders # Email: Jeremy Sanders # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ############################################################################## """Store document colors.""" from __future__ import division import re from .. import qtall as qt4 from ..compat import crange # match name of color themecolor_re = re.compile(r'^theme([1-9][0-9]*)$') # match extended color extendedcolor_re = re.compile('^#[0-9A-Fa-f]{8}$') def makeColor(name): """Make a new color, allowing extended hex format with extra two digits.""" m = extendedcolor_re.match(name) if m: col = qt4.QColor(name[:7]) col.setAlpha( int(name[7:], 16) ) return col else: return qt4.QColor(name) # Default color themes colorthemes = { # backward compatibility with old documents 'black': [ 'black', ], # black and colorbrewer sets 1 and 2 (minus yellow, as it doesn't show) 'default1': [ '#e41a1c', '#377eb8', '#4daf4a', '#984ea3', '#ff7f00', #'#ffff33', '#a65628', '#f781bf', '#999999', '#66c2a5', '#fc8d62', '#8da0cb', '#e78ac3', '#a6d854', '#ffd92f', '#e5c494', '#b3b3b3', ], # colorbrewer set 1 'colorbrewer1': [ '#e41a1c', '#377eb8', '#4daf4a', '#984ea3', '#ff7f00', '#ffff33', '#a65628', '#f781bf', '#999999', ], # colorbrewer set 2 'colorbrewer2': [ '#66c2a5', '#fc8d62', '#8da0cb', '#e78ac3', '#a6d854', '#ffd92f', '#e5c494', '#b3b3b3', ], # rgb 'rgb6': [ '#ff0000', '#00ff00', '#0000ff', '#ffff00', '#00ffff', '#ff00ff', ], # maximum dissimilar colors # taken from http://stackoverflow.com/questions/33295120/how-to-generate-gif-256-colors-palette 'max128': [ "#000000", "#FFFF00", "#1CE6FF", "#FF34FF", "#FF4A46", "#008941", "#006FA6", "#A30059", "#FFDBE5", "#7A4900", "#0000A6", "#63FFAC", "#B79762", "#004D43", "#8FB0FF", "#997D87", "#5A0007", "#809693", "#FEFFE6", "#1B4400", "#4FC601", "#3B5DFF", "#4A3B53", "#FF2F80", "#61615A", "#BA0900", "#6B7900", "#00C2A0", "#FFAA92", "#FF90C9", "#B903AA", "#D16100", "#DDEFFF", "#000035", "#7B4F4B", "#A1C299", "#300018", "#0AA6D8", "#013349", "#00846F", "#372101", "#FFB500", "#C2FFED", "#A079BF", "#CC0744", "#C0B9B2", "#C2FF99", "#001E09", "#00489C", "#6F0062", "#0CBD66", "#EEC3FF", "#456D75", "#B77B68", "#7A87A1", "#788D66", "#885578", "#FAD09F", "#FF8A9A", "#D157A0", "#BEC459", "#456648", "#0086ED", "#886F4C", "#34362D", "#B4A8BD", "#00A6AA", "#452C2C", "#636375", "#A3C8C9", "#FF913F", "#938A81", "#575329", "#00FECF", "#B05B6F", "#8CD0FF", "#3B9700", "#04F757", "#C8A1A1", "#1E6E00", "#7900D7", "#A77500", "#6367A9", "#A05837", "#6B002C", "#772600", "#D790FF", "#9B9700", "#549E79", "#FFF69F", "#201625", "#72418F", "#BC23FF", "#99ADC0", "#3A2465", "#922329", "#5B4534", "#FDE8DC", "#404E55", "#0089A3", "#CB7E98", "#A4E804", "#324E72", "#6A3A4C", "#83AB58", "#001C1E", "#D1F7CE", "#004B28", "#C8D0F6", "#A3A489", "#806C66", "#222800", "#BF5650", "#E83000", "#66796D", "#DA007C", "#FF1A59", "#8ADBB4", "#1E0200", "#5B4E51", "#C895C5", "#320033", "#FF6832", "#66E1D3", "#CFCDAC", "#D0AC94", "#7ED379", "#012C58", ], } # most up-to-date theme colorthemes['default-latest'] = colorthemes['default1'] class Colors(qt4.QObject): """Document colors.""" sigColorsModified = qt4.pyqtSignal() def __init__(self): qt4.QObject.__init__(self) self.defaultnames = [ 'auto', 'foreground', 'background', 'transparent', 'white', 'black', 'red', 'green', 'blue', 'cyan', 'magenta', 'yellow', 'grey', 'darkred', 'darkgreen', 'darkblue', 'darkcyan', 'darkmagenta' ] # model for UI for choosing colors self.model = None # maximum index of colors in the current theme self.maxthemeidx = 1 # current theme self.colortheme = 'black' # names of colors in theme self.themenames = [] # setup default colors from theme self.wipe() # model for colors to use in qt widgets self.model = ColorModel(self, self) def wipe(self): self.colors = { # line and background colors 'foreground': '#000000', 'background': '#ffffff', 'transparent': '#ffffff00', # this is a special color with a special (fake) value 'auto': '#31323334', } # maximum color index used self.maxthemeidx = 1 # colors defined by user in custom definition self.definednames = [] # update the theme self.setColorTheme(self.colortheme) def addColor(self, color, val): """Add color to defined list.""" self.colors[color] = val self.definednames.append(color) # keep track of maximum theme index m = themecolor_re.match(color) if m: self.maxthemeidx = max(self.maxthemeidx, int(m.group(1))) def setColorTheme(self, theme): """Set color theme to name given. """ try: themecolors = colorthemes[theme] except KeyError: raise ValueError('Unknown color theme') self.colortheme = theme # delete old theme colors from dict defnset = set(self.definednames) for col in list(self.colors): if themecolor_re.match(col) and col not in defnset: del self.colors[col] # now add colors from theme (excluding defined colors) self.themenames = [] for i, col in enumerate(themecolors): key = 'theme%i' % (i+1) if key not in defnset: self.colors[key] = col self.themenames.append(key) # keep track of maximum theme index self.maxthemeidx = len(themecolors) for color in self.definednames: m = themecolor_re.match(color) if m: self.maxthemeidx = max(self.maxthemeidx, int(m.group(1))) self.updateModel() def updateModel(self): """Update user color model. Call after using addColor.""" if self.model is not None: self.model.updateColorList() def get(self, name): """Get QColor given name.""" if name in self.colors: name = self.colors[name] # special colors themeXXX, where XXX is a number from 1 # requires wrapping number according to the maximum definition m = themecolor_re.match(name) if m: idx = int(m.group(1)) name = self.getIndex(idx) # standard colors return makeColor(name) def getIndex(self, idx): """Get name of color by index given.""" try: # wrap index to maximum number of colors defined in theme wrapidx = (idx-1) % self.maxthemeidx + 1 return self.colors['theme%i' % wrapidx] except (ZeroDivisionError, KeyError): return 'foreground' class ColorModel(qt4.QAbstractListModel): """This is a Qt model to get access to the complete list of colors.""" def __init__(self, parent, colors): qt4.QAbstractListModel.__init__(self, parent) self.colors = colors # cache of icons for colors indexed by rgba value self.iconcache = {} # list of extra colors added during operation by user self.xtranames = [] # initialise list of colors self.colorlist = [] self.updateColorList() def rowCount(self, index): if index.isValid(): return 0 return len(self.colorlist) def makeIcon(self, color): """Make icon for color in cache.""" xw, yw = 16, 12 qcolor = self.colors.get(color) if color.lower() in ('auto', 'transparent'): # make a checkerboard pattern for special colors image = qt4.QImage(xw, yw, qt4.QImage.Format_RGB32) if color.lower() == 'auto': cnames = ['orange', 'skyblue', 'green'] else: cnames = ['lightgrey', 'darkgrey'] cols = [qt4.QColor(c).rgba() for c in cnames] for x in crange(xw): for y in crange(yw): idx = (x//4 + y//4) % len(cols) image.setPixel(x, y, cols[idx]) pixmap = qt4.QPixmap.fromImage(image) else: # solid color pixmap = qt4.QPixmap(xw, yw) pixmap.fill(qcolor) icon = qt4.QIcon(pixmap) self.iconcache[qcolor.rgba()] = icon def data(self, index, role): row = index.row() if row<0 or row>=len(self.colorlist): return None color = self.colorlist[index.row()] if role == qt4.Qt.DisplayRole or role == qt4.Qt.EditRole: return color elif role == qt4.Qt.DecorationRole: # icons are cached using rgba as index rgba = self.colors.get(color).rgba() if rgba not in self.iconcache: self.makeIcon(color) return self.iconcache[rgba] return None def flags(self, index): if not index.isValid(): return qt4.Qt.ItemIsEnabled return ( qt4.QAbstractListModel.flags(self, index) | qt4.Qt.ItemIsEditable) def setData(self, index, value, role): if role == qt4.Qt.EditRole or role == qt4.Qt.DisplayRole: row = index.row() if row>=0 and rowlen(self.colorlist): return False self.beginInsertRows(qt4.QModelIndex(), row, row+count-1) self.colorlist = ( self.colorlist[:row] + ['']*count + self.colorlist[row:]) self.endInsertRows() return True def removeRows(self, row, count, parent): if count<=0 or row<0 or (row+count)>len(self.colorlist): return False self.beginRemoveRows(qt4.QModelIndex(), row, row+count-1) self.colorlist = ( self.colorlist[:row] + self.colorlist[row+count:]) self.endRemoveRows() return True def updateColorList(self): """Update internal set of colors with updated set from Colors.""" curcols = self.colorlist oldset = set(curcols) # make list + set of new colors newcols = ( self.colors.defaultnames + self.colors.definednames + self.colors.themenames + self.xtranames) newset = set(newcols) # prune any duplicates if len(newcols) > len(newset): seen = set() out = [] for c in newcols: if c not in seen: out.append(c) seen.add(c) newcols = out # delete missing entries first i = 0 while i < len(curcols): col = curcols[i] if col not in newset: self.beginRemoveRows(qt4.QModelIndex(), i, i) del curcols[i] self.endRemoveRows() else: i += 1 # add new entries for i, ncol in enumerate(newcols): if i == len(curcols) or curcols[i] != ncol: # maybe swap if i # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ############################################################################## """A paint engine for doing self-tests.""" from __future__ import division from . import svg_export class SelfTestPaintEngine(svg_export.SVGPaintEngine): """Paint engine class for self testing output.""" def __init__(self): svg_export.SVGPaintEngine.__init__(self) # ppm images are simple and should be same on all platforms self.imageformat = 'ppm' def drawTextItem(self, pt, textitem): """Write text directly in self test mode.""" text = textitem.text().encode('ascii', 'xmlcharrefreplace').decode( 'ascii') svg_export.SVGElement( self.celement, 'text', 'x="%s" y="%s" font-size="%gpt" fill="%s"' % ( svg_export.fltStr(pt.x()), svg_export.fltStr(pt.y()), textitem.font().pointSize(), self.pen.color().name() ), text=text ) class SelfTestPaintDevice(svg_export.SVGPaintDevice): """Paint device for SVG paint engine. Note: this device is different to SVGPaintDevice because it switches scaling to 1 by default. """ def __init__(self, fileobj, width_in, height_in, dpi=90): """Initialise with output file, and dimensions in inches.""" svg_export.SVGPaintDevice.__init__( self, fileobj, width_in, height_in, dpi=dpi, scale=1) self.engine = SelfTestPaintEngine() ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������veusz-3.0.1/veusz/document/svg_export.py������������������������������������������������������������0000664�0001750�0001750�00000051576�13322660165�020077� 0����������������������������������������������������������������������������������������������������ustar �jss�����������������������������jss�����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2010 Jeremy S. Sanders # Email: Jeremy Sanders # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ############################################################################## """A home-brewed SVG paint engine for doing svg with clipping and exporting text as paths for WYSIWYG.""" from __future__ import division, print_function import re from ..compat import crange, cbytes from .. import qtall as qt # physical sizes inch_mm = 25.4 inch_pt = 72.0 def printpath(path): """Debugging print path.""" print("Contents of", path) for i in crange(path.elementCount()): el = path.elementAt(i) print(" ", el.type, el.x, el.y) def fltStr(v, prec=2): """Change a float to a string, using a maximum number of decimal places but removing trailing zeros.""" # ensures consistent rounding behaviour on different platforms v = round(v, prec+2) val = ('% 20.10f' % v)[:10+prec] # drop any trailing zeros val = val.rstrip('0').lstrip(' ').rstrip('.') # get rid of -0s (platform differences here) if val == '-0': val = '0' return val def escapeXML(text): """Escape special characters in XML.""" # we have swap & with an unused character, so we can replace it later text = text.replace('&', u'\ue001') text = text.replace('<', '<') text = text.replace('>', '>') text = text.replace('"', '"') text = text.replace("'", ''') text = text.replace(u'\ue001', '&') return text def createPath(path, scale): """Convert qt path to svg path. We use relative coordinates to make the file size smaller and help compression """ p = [] count = path.elementCount() i = 0 ox, oy = 0, 0 while i < count: e = path.elementAt(i) nx, ny = e.x*scale, e.y*scale if e.type == qt.QPainterPath.MoveToElement: p.append( 'm%s,%s' % (fltStr(nx-ox), fltStr(ny-oy)) ) ox, oy = nx, ny elif e.type == qt.QPainterPath.LineToElement: p.append( 'l%s,%s' % (fltStr(nx-ox), fltStr(ny-oy)) ) ox, oy = nx, ny elif e.type == qt.QPainterPath.CurveToElement: e1 = path.elementAt(i+1) e2 = path.elementAt(i+2) p.append( 'c%s,%s,%s,%s,%s,%s' % ( fltStr(nx-ox), fltStr(ny-oy), fltStr(e1.x*scale-ox), fltStr(e1.y*scale-oy), fltStr(e2.x*scale-ox), fltStr(e2.y*scale-oy)) ) ox, oy = e2.x*scale, e2.y*scale i += 2 else: assert False i += 1 return ''.join(p) class SVGElement(object): """SVG element in output. This represents the XML tree in memory """ def __init__(self, parent, eltype, attrb, text=None): """Intialise element. parent: parent element or None eltype: type (e.g. 'polyline') attrb: attribute string appended to output text: text to output between this and closing element. """ self.eltype = eltype self.attrb = attrb self.children = [] self.parent = parent self.text = text if parent: parent.children.append(self) def write(self, fileobj): """Write element and its children to the output file.""" fileobj.write('<%s' % self.eltype) if self.attrb: fileobj.write(' ' + self.attrb) if self.text: fileobj.write('>%s\n' % (self.text, self.eltype)) elif self.children: fileobj.write('>\n') for c in self.children: c.write(fileobj) fileobj.write('\n' % self.eltype) else: # simple close tag if not children or text fileobj.write('/>\n') class SVGPaintEngine(qt.QPaintEngine): """Paint engine class for writing to svg files.""" def __init__(self, writetextastext=False): qt.QPaintEngine.__init__( self, qt.QPaintEngine.Antialiasing | qt.QPaintEngine.PainterPaths | qt.QPaintEngine.PrimitiveTransform | qt.QPaintEngine.PaintOutsidePaintEvent | qt.QPaintEngine.PixmapTransform | qt.QPaintEngine.AlphaBlend ) self.imageformat = 'png' self.writetextastext = writetextastext def begin(self, paintdevice): """Start painting.""" self.device = paintdevice self.scale = paintdevice.scale self.pen = qt.QPen() self.brush = qt.QBrush() self.clippath = None self.clipnum = 0 self.existingclips = {} self.transform = qt.QTransform() # svg root element for qt defaults self.rootelement = SVGElement( None, 'svg', ('width="%spx" height="%spx" version="1.1"\n' ' xmlns="http://www.w3.org/2000/svg"\n' ' xmlns:xlink="http://www.w3.org/1999/xlink"') % ( fltStr(self.device.width*self.device.sdpi*self.scale), fltStr(self.device.height*self.device.sdpi*self.scale)) ) SVGElement(self.rootelement, 'desc', '', 'Veusz output document') # definitions, for clips, etc. self.defs = SVGElement(self.rootelement, 'defs', '') # this is where all the drawing goes self.celement = SVGElement( self.rootelement, 'g', 'stroke-linejoin="bevel" stroke-linecap="square" ' 'stroke="#000000" fill-rule="evenodd"') # previous transform, stroke and clip states self.oldstate = [None, None, None] # cache paths to avoid duplication self.pathcache = {} self.pathcacheidx = 0 return True def pruneEmptyGroups(self): """Take the element tree and remove any empty group entries.""" def recursive(root): children = list(root.children) # remove any empty children first for c in children: recursive(c) if root.eltype == 'g' and len(root.children) == 0: # safe to remove index = root.parent.children.index(root) del root.parent.children[index] # merge equal groups last = None i = 0 while i < len(root.children): this = root.children[i] if ( last is not None and last.eltype == this.eltype and last.attrb == this.attrb and last.text == this.text ): last.children += this.children del root.children[i] else: last = this i += 1 recursive(self.rootelement) def end(self): self.pruneEmptyGroups() fileobj = self.device.fileobj fileobj.write('\n' '\n') # write all the elements self.rootelement.write(fileobj) return True def _updateClipPath(self, clippath, clipoperation): """Update clip path given state change.""" clippath = self.transform.map(clippath) if clipoperation == qt.Qt.NoClip: self.clippath = None elif clipoperation == qt.Qt.ReplaceClip: self.clippath = clippath elif clipoperation == qt.Qt.IntersectClip: self.clippath = self.clippath.intersected(clippath) elif clipoperation == qt.Qt.UniteClip: self.clippath = self.clippath.united(clippath) else: assert False def updateState(self, state): """Examine what has changed in state and call apropriate function.""" ss = state.state() # state is a list of transform, stroke/fill and clip states statevec = list(self.oldstate) if ss & qt.QPaintEngine.DirtyTransform: self.transform = state.transform() statevec[0] = self.transformState() if ss & qt.QPaintEngine.DirtyPen: self.pen = state.pen() statevec[1] = self.strokeFillState() if ss & qt.QPaintEngine.DirtyBrush: self.brush = state.brush() statevec[1] = self.strokeFillState() if ss & qt.QPaintEngine.DirtyClipPath: self._updateClipPath(state.clipPath(), state.clipOperation()) statevec[2] = self.clipState() if ss & qt.QPaintEngine.DirtyClipRegion: path = qt.QPainterPath() path.addRegion(state.clipRegion()) self._updateClipPath(path, state.clipOperation()) statevec[2] = self.clipState() # work out which state differs first pop = 0 for i in crange(2, -1, -1): if statevec[i] != self.oldstate[i]: pop = i+1 break # go back up the tree the required number of times for i in crange(pop): if self.oldstate[i]: self.celement = self.celement.parent # create new elements for changed states for i in crange(pop-1, -1, -1): if statevec[i]: self.celement = SVGElement( self.celement, 'g', ' '.join(statevec[i])) self.oldstate = statevec def clipState(self): """Get SVG clipping state. This is in the form of an svg group""" if self.clippath is None: return () path = createPath(self.clippath, self.scale) if path in self.existingclips: url = 'url(#c%i)' % self.existingclips[path] else: clippath = SVGElement(self.defs, 'clipPath', 'id="c%i"' % self.clipnum) SVGElement(clippath, 'path', 'd="%s"' % path) url = 'url(#c%i)' % self.clipnum self.existingclips[path] = self.clipnum self.clipnum += 1 return ('clip-path="%s"' % url,) def strokeFillState(self): """Return stroke-fill state.""" vals = {} p = self.pen # - color color = p.color().name() if color != '#000000': vals['stroke'] = p.color().name() # - opacity if p.color().alphaF() != 1.: vals['stroke-opacity'] = '%.3g' % p.color().alphaF() # - join style if p.joinStyle() != qt.Qt.BevelJoin: vals['stroke-linejoin'] = { qt.Qt.MiterJoin: 'miter', qt.Qt.SvgMiterJoin: 'miter', qt.Qt.RoundJoin: 'round', qt.Qt.BevelJoin: 'bevel' }[p.joinStyle()] # - cap style if p.capStyle() != qt.Qt.SquareCap: vals['stroke-linecap'] = { qt.Qt.FlatCap: 'butt', qt.Qt.SquareCap: 'square', qt.Qt.RoundCap: 'round' }[p.capStyle()] # - width w = p.widthF() # width 0 is device width for qt if w == 0.: w = 1./self.scale vals['stroke-width'] = fltStr(w*self.scale) # - line style if p.style() == qt.Qt.NoPen: vals['stroke'] = 'none' elif p.style() not in (qt.Qt.SolidLine, qt.Qt.NoPen): # convert from pen width fractions to pts nums = [fltStr(self.scale*w*x) for x in p.dashPattern()] vals['stroke-dasharray'] = ','.join(nums) # BRUSH STYLES b = self.brush if b.style() == qt.Qt.NoBrush: vals['fill'] = 'none' else: vals['fill'] = b.color().name() if b.color().alphaF() != 1.0: vals['fill-opacity'] = '%.3g' % b.color().alphaF() items = ['%s="%s"' % x for x in sorted(vals.items())] return tuple(items) def transformState(self): if not self.transform.isIdentity(): m = self.transform dx, dy = m.dx(), m.dy() if (m.m11(), m.m12(), m.m21(), m.m22()) == (1., 0., 0., 1): out = ('transform="translate(%s,%s)"' % ( fltStr(dx*self.scale), fltStr(dy*self.scale)) ,) else: out = ('transform="matrix(%s %s %s %s %s %s)"' % ( fltStr(m.m11(), 4), fltStr(m.m12(), 4), fltStr(m.m21(), 4), fltStr(m.m22(), 4), fltStr(dx*self.scale), fltStr(dy*self.scale) ),) else: out = () return out def drawPath(self, path): """Draw a path on the output.""" p = createPath(path, self.scale) attrb = 'd="%s"' % p if path.fillRule() == qt.Qt.WindingFill: attrb += ' fill-rule="nonzero"' if attrb in self.pathcache: element, num = self.pathcache[attrb] if num is None: # this is the first time an element has been referenced again # assign it an id for use below num = self.pathcacheidx self.pathcacheidx += 1 self.pathcache[attrb] = element, num # add an id attribute element.attrb += ' id="p%i"' % num # if the parent is a translation, swallow this into the use element m = re.match('transform="translate\(([-0-9.]+),([-0-9.]+)\)"', self.celement.attrb) if m: SVGElement(self.celement.parent, 'use', 'xlink:href="#p%i" x="%s" y="%s"' % ( num, m.group(1), m.group(2))) else: SVGElement(self.celement, 'use', 'xlink:href="#p%i"' % num) else: pathel = SVGElement(self.celement, 'path', attrb) self.pathcache[attrb] = [pathel, None] def drawTextItem(self, pt, textitem): """Convert text to a path and draw it. """ if self.writetextastext: # size f = textitem.font() if f.pixelSize() > 0: size = f.pixelSize()*self.scale else: size = f.pointSizeF()*self.scale*self.device.sdpi/inch_pt font = textitem.font() grpattrb = [ 'stroke="none"', 'fill="%s"' % self.pen.color().name(), 'fill-opacity="%.3g"' % self.pen.color().alphaF(), 'font-family="%s"' % escapeXML(font.family()), 'font-size="%s"' % size, ] if font.italic(): grpattrb.append('font-style="italic"') if font.bold(): grpattrb.append('font-weight="bold"') grp = SVGElement( self.celement, 'g', ' '.join(grpattrb) ) text = escapeXML( textitem.text() ) textattrb = [ 'x="%s"' % fltStr(pt.x()*self.scale), 'y="%s"' % fltStr(pt.y()*self.scale), 'textLength="%s"' % fltStr(textitem.width()*self.scale), ] # spaces get lost without this if text.find(' ') >= 0 or text[:1] == ' ' or text[-1:] == ' ': textattrb.append('xml:space="preserve"') # write as an SVG text element SVGElement( grp, 'text', ' '.join(textattrb), text=text ) else: # convert to a path path = qt.QPainterPath() path.addText(pt, textitem.font(), textitem.text()) p = createPath(path, self.scale) SVGElement( self.celement, 'path', 'd="%s" fill="%s" stroke="none" fill-opacity="%.3g"' % ( p, self.pen.color().name(), self.pen.color().alphaF()) ) def drawLines(self, lines): """Draw multiple lines.""" paths = [] for line in lines: path = 'M%s,%sl%s,%s' % ( fltStr(line.x1()*self.scale), fltStr(line.y1()*self.scale), fltStr((line.x2()-line.x1())*self.scale), fltStr((line.y2()-line.y1())*self.scale)) paths.append(path) SVGElement(self.celement, 'path', 'd="%s"' % ''.join(paths)) def drawPolygon(self, points, mode): """Draw polygon on output.""" pts = [] for p in points: pts.append( '%s,%s' % (fltStr(p.x()*self.scale), fltStr(p.y()*self.scale)) ) if mode == qt.QPaintEngine.PolylineMode: SVGElement(self.celement, 'polyline', 'fill="none" points="%s"' % ' '.join(pts)) else: attrb = 'points="%s"' % ' '.join(pts) if mode == qt.Qt.WindingFill: attrb += ' fill-rule="nonzero"' SVGElement(self.celement, 'polygon', attrb) def drawEllipse(self, rect): """Draw an ellipse to the svg file.""" SVGElement(self.celement, 'ellipse', 'cx="%s" cy="%s" rx="%s" ry="%s"' % (fltStr(rect.center().x()*self.scale), fltStr(rect.center().y()*self.scale), fltStr(rect.width()*0.5*self.scale), fltStr(rect.height()*0.5*self.scale))) def drawPoints(self, points): """Draw points.""" for pt in points: x, y = fltStr(pt.x()*self.scale), fltStr(pt.y()*self.scale) SVGElement(self.celement, 'line', ('x1="%s" y1="%s" x2="%s" y2="%s" ' 'stroke-linecap="round"') % (x, y, x, y)) def drawImage(self, r, img, sr, flags): """Draw image. As the pixmap method uses the same code, just call this.""" self.drawPixmap(r, img, sr) def drawPixmap(self, r, pixmap, sr): """Draw pixmap svg item. This is converted to a bitmap and embedded in the output """ # convert pixmap to textual data data = qt.QByteArray() buf = qt.QBuffer(data) buf.open(qt.QBuffer.ReadWrite) pixmap.save(buf, self.imageformat.upper(), 0) buf.close() attrb = [ 'x="%s" y="%s" ' % (fltStr(r.x()*self.scale), fltStr(r.y()*self.scale)), 'width="%s" ' % fltStr(r.width()*self.scale), 'height="%s" ' % fltStr(r.height()*self.scale), 'xlink:href="data:image/%s;base64,' % self.imageformat, cbytes(data.toBase64()).decode('ascii'), '" preserveAspectRatio="none"' ] SVGElement(self.celement, 'image', ''.join(attrb)) def type(self): """A random number for the engine.""" return qt.QPaintEngine.User + 11 class SVGPaintDevice(qt.QPaintDevice): """Paint device for SVG paint engine. dpi is the real output DPI (unscaled) scale is a scaling value to apply to outputted values """ def __init__(self, fileobj, width_in, height_in, writetextastext=False, dpi=90, scale=0.1): qt.QPaintDevice.__init__(self) self.fileobj = fileobj self.width = width_in self.height = height_in self.scale = scale self.sdpi = dpi/scale self.engine = SVGPaintEngine(writetextastext=writetextastext) def paintEngine(self): return self.engine def metric(self, m): """Return the metrics of the painter.""" if m == qt.QPaintDevice.PdmWidth: return int(self.width*self.sdpi) elif m == qt.QPaintDevice.PdmHeight: return int(self.height*self.sdpi) elif m == qt.QPaintDevice.PdmWidthMM: return int(self.engine.width*inch_mm) elif m == qt.QPaintDevice.PdmHeightMM: return int(self.engine.height*inch_mm) elif m == qt.QPaintDevice.PdmNumColors: return 2147483647 elif m == qt.QPaintDevice.PdmDepth: return 24 elif m == qt.QPaintDevice.PdmDpiX: return int(self.sdpi) elif m == qt.QPaintDevice.PdmDpiY: return int(self.sdpi) elif m == qt.QPaintDevice.PdmPhysicalDpiX: return int(self.sdpi) elif m == qt.QPaintDevice.PdmPhysicalDpiY: return int(self.sdpi) elif m == qt.QPaintDevice.PdmDevicePixelRatio: return 1 # Qt >= 5.6 elif m == getattr(qt.QPaintDevice, 'PdmDevicePixelRatioScaled', -1): return 1 else: # fall back return qt.QPaintDevice.metric(self, m) ����������������������������������������������������������������������������������������������������������������������������������veusz-3.0.1/veusz/document/evaluate.py��������������������������������������������������������������0000664�0001750�0001750�00000035703�13262200021�017457� 0����������������������������������������������������������������������������������������������������ustar �jss�����������������������������jss�����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2016 Jeremy S. Sanders # Email: Jeremy Sanders # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ############################################################################## from __future__ import division from collections import defaultdict import os.path import re import datetime import numpy as N from . import colors from ..compat import citems, cstr, cexec from .. import setting from .. import utils from .. import datasets from .. import qtall as qt # python identifier identifier_re = re.compile(r'^[A-Za-z_][A-Za-z0-9_]*$') # for splitting identifier_split_re = re.compile(r'[A-Za-z_][A-Za-z0-9_]*') # python module module_re = re.compile(r'^[A-Za-z_\.]+$') # function(arg1, arg2...) for custom functions # not quite correct as doesn't check for commas in correct places function_re = re.compile(r''' ^([A-Za-z_][A-Za-z0-9_]*)[ ]* # identifier \(( # begin args (?: [ ]* ,? [ ]* [A-Za-z_][A-Za-z0-9_]* )* # named args (?: [ ]* ,? [ ]* \*[A-Za-z_][A-Za-z0-9_]* )? # *args (?: [ ]* ,? [ ]* \*\*[A-Za-z_][A-Za-z0-9_]* )? # **kwargs )\)$ # endargs''', re.VERBOSE) def _(text, disambiguation=None, context="Evaluate"): """Translate text.""" return qt.QCoreApplication.translate(context, text, disambiguation) class Evaluate: """Class to manage evaluation of expressions in a special environment.""" def __init__(self, doc): self.doc = doc # directories to examine when importing self.importpath = [] self.wipe() def wipe(self): """Clear current customs.""" # store custom functions and constants # consists of tuples of (name, type, value) # type is constant or function # we use this format to preserve evaluation order self.def_imports = [] self.def_definitions = [] self.def_colors = [] self.def_colormaps = [] #self.customs = [] # this is the context used to evaluate expressions self.context = {} # copy default colormaps self.colormaps = utils.ColorMaps() self.colors = colors.Colors() self.update() # copies of validated compiled expressions self.compiled = {} self.compfailed = set() self.compfailedchangeset = -1 # cached expressions which have been already evaluated as datasets self.exprdscache = {} self.exprdscachechangeset = None def update(self): """To be called after custom constants or functions are changed. This sets up a safe environment where things can be evaluated """ c = self.context c.clear() # add numpy things # we try to avoid various bits and pieces for safety for name, val in citems(N.__dict__): if ( (callable(val) or type(val)==float) and name not in __builtins__ and name[:1] != '_' and name[-1:] != '_' ): c[name] = val # safe functions c['os_path_join'] = os.path.join c['os_path_dirname'] = os.path.dirname c['veusz_markercodes'] = tuple(utils.MarkerCodes) # helpful functions for expansion c['ENVIRON'] = dict(os.environ) c['DATE'] = self._evalformatdate c['TIME'] = self._evalformattime c['DATA'] = self._evaldata c['FILENAME'] = self._evalfilename c['BASENAME'] = self._evalbasename c['ESCAPE'] = utils.latexEscape c['SETTING'] = self._evalsetting c['LANG'] = self._evallang for name, val in self.def_imports: self._updateImport(name, val) for name, val in self.def_definitions: self._updateDefinition(name, val) self.colors.wipe() for name, val in self.def_colors: self.colors.addColor(name, val) self.colors.updateModel() self.colormaps.wipe() for name, val in self.def_colormaps: self._updateColormap(name, val) def _updateImport(self, module, val): """Add an import statement to the eval function context.""" if module_re.match(module): # work out what is safe to import symbols = identifier_split_re.findall(val) toimport = self._processSafeImports(module, symbols) if toimport: defn = 'from %s import %s' % ( module, ', '.join(toimport)) try: cexec(defn, self.context) except Exception: self.doc.log(_( "Failed to import '%s' from module '%s'") % ( ', '.join(toimport), module)) return delta = set(symbols)-set(toimport) if delta: self.doc.log(_( "Did not import '%s' from module '%s'") % ( ', '.join(list(delta)), module)) else: self.doc.log( _("Invalid module name '%s'") % module ) def validateProcessColormap(self, colormap): """Validate and process a colormap value. Returns a list of B,G,R,alpha tuples or raises ValueError if a problem.""" try: if len(colormap) < 2: raise ValueError( _("Need at least two entries in colormap") ) except TypeError: raise ValueError( _("Invalid type for colormap") ) out = [] for entry in colormap: if entry == (-1,0,0,0): out.append(entry) continue for v in entry: try: v - 0 except TypeError: raise ValueError( _("Colormap entries should be numerical") ) if v < 0 or v > 255: raise ValueError( _("Colormap entries should be between 0 and 255") ) if len(entry) == 3: out.append( (int(entry[2]), int(entry[1]), int(entry[0]), 255) ) elif len(entry) == 4: out.append( (int(entry[2]), int(entry[1]), int(entry[0]), int(entry[3])) ) else: raise ValueError( _("Each colormap entry consists of R,G,B " "and optionally alpha values") ) return tuple(out) def _updateColormap(self, name, val): """Add a colormap entry.""" try: cmap = self.validateProcessColormap(val) except ValueError as e: self.doc.log( cstr(e) ) else: self.colormaps[ cstr(name) ] = cmap def _updateDefinition(self, name, val): """Update a function or constant in eval function context.""" if identifier_re.match(name): defn = val else: m = function_re.match(name) if not m: self.doc.log( _("Invalid function or constant specification '%s'") % name) return name = m.group(1) args = m.group(2) defn = 'lambda %s: %s' % (args, val) # evaluate, but we ignore any unsafe commands or exceptions comp = self.compileCheckedExpression(defn) if comp is None: return try: self.context[name] = eval(comp, self.context) except Exception as e: self.doc.log( _( "Error evaluating '%s': '%s'") % (name, cstr(e)) ) def compileCheckedExpression(self, expr, origexpr=None, log=True): """Compile expression and check for errors. origexpr is an expression to show in error messages. This is used if replacements have been done, etc. """ try: return self.compiled[expr] except KeyError: pass # track failed compilations, so we only print them once if self.compfailedchangeset != self.doc.changeset: self.compfailedchangeset = self.doc.changeset self.compfailed.clear() elif expr in self.compfailed: return None if origexpr is None: origexpr = expr try: checked = utils.compileChecked( expr, ignoresecurity=setting.transient_settings['unsafe_mode']) except utils.SafeEvalException as e: if log: self.doc.log( _("Unsafe expression '%s': %s") % (origexpr, cstr(e))) self.compfailed.add(expr) return None except Exception as e: if log: self.doc.log( _("Error in expression '%s': %s") % (origexpr, cstr(e))) return None else: self.compiled[expr] = checked return checked @staticmethod def _evalformatdate(fmt=None): """DATE() eval: return date with optional format.""" d = datetime.date.today() return d.isoformat() if fmt is None else d.strftime(fmt) @staticmethod def _evalformattime(fmt=None): """TIME() eval: return time with optional format.""" t = datetime.datetime.now() return t.isoformat() if fmt is None else t.strftime(fmt) def _evaldata(self, name, part='data'): """DATA(name, [part]) eval: return dataset as array.""" if part not in ('data', 'perr', 'serr', 'nerr'): raise RuntimeError("Invalid dataset part '%s'" % part) if name not in self.doc.data: raise RuntimeError("Dataset '%s' does not exist" % name) data = getattr(self.doc.data[name], part) if isinstance(data, N.ndarray): return N.array(data) elif isinstance(data, list): return list(data) return data def _evalfilename(self): """FILENAME() eval: returns filename.""" return utils.latexEscape(self.doc.filename) def _evalbasename(self): """BASENAME() eval: returns base filename.""" return utils.latexEscape(os.path.basename(self.doc.filename)) def _evalsetting(self, path): """SETTING() eval: return setting given full path.""" return self.doc.resolveSettingPath(None, path).get() @staticmethod def _evallang(opts): lang = qt.QLocale().name() if lang in opts: return opts[lang] majorl = lang.split('_')[0] if majorl in opts: return opts[majorl] if 'default' in opts: return opts['default'] return utils.latexEscape('NOLANG:%s' % str(lang)) def evalDatasetExpression(self, expr, part='data', datatype='numeric', dimensions=1): """Return dataset after evaluating a dataset expression. part is 'data', 'serr', 'perr' or 'nerr' - these are the dataset parts which are evaluated by the expression None is returned on error """ key = (expr, part, datatype, dimensions) if self.exprdscachechangeset != self.doc.changeset: self.exprdscachechangeset = self.doc.changeset self.exprdscache.clear() elif key in self.exprdscache: return self.exprdscache[key] self.exprdscache[key] = ds = datasets.evalDatasetExpression( self.doc, expr, part=part, datatype=datatype, dimensions=dimensions) return ds def _processSafeImports(self, module, symbols): """Check what symbols are safe to import.""" # empty list if not symbols: return symbols # do import anyway if setting.transient_settings['unsafe_mode']: return symbols # two-pass to ask user whether they want to import symbol for thepass in range(2): # remembered during session a = 'import_allowed' if a not in setting.transient_settings: setting.transient_settings[a] = defaultdict(set) allowed = setting.transient_settings[a][module] # not allowed during session a = 'import_notallowed' if a not in setting.transient_settings: setting.transient_settings[a] = defaultdict(set) notallowed = setting.transient_settings[a][module] # remembered in setting file a = 'import_allowed' if a not in setting.settingdb: setting.settingdb[a] = {} if module not in setting.settingdb[a]: setting.settingdb[a][module] = {} allowed_always = setting.settingdb[a][module] # collect up toimport = [] possibleimport = [] for symbol in symbols: if symbol in allowed or symbol in allowed_always: toimport.append(symbol) elif symbol not in notallowed: possibleimport.append(symbol) # nothing to do, so leave if not possibleimport: break # only ask the user the first time if thepass == 0: self.doc.sigAllowedImports.emit(module, possibleimport) return toimport def getColormap(self, name, invert): """Get colormap with name given (returning grey if does not exist).""" cmap = self.colormaps.get(name, self.colormaps['grey']) if invert: if cmap[0][0] >= 0: return cmap[::-1] else: # ignore marker at beginning for stepped maps return tuple([cmap[0]] + list(cmap[-1:0:-1])) return cmap def saveCustomDefinitions(self, fileobj): """Save custom constants and functions.""" for ctype, defns in ( ('import', self.def_imports), ('definition', self.def_definitions), ('color', self.def_colors), ('colormap', self.def_colormaps)): for val in defns: fileobj.write( 'AddCustom(%s, %s, %s)\n' % ( utils.rrepr(ctype), utils.rrepr(val[0]), utils.rrepr(val[1]))) def saveCustomFile(self, fileobj): """Export the custom settings to a file.""" self.doc._writeFileHeader(fileobj, 'custom definitions') self.saveCustomDefinitions(fileobj) �������������������������������������������������������������veusz-3.0.1/veusz/document/mime.py������������������������������������������������������������������0000664�0001750�0001750�00000022071�13164720523�016611� 0����������������������������������������������������������������������������������������������������ustar �jss�����������������������������jss�����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2010 Jeremy S. Sanders # Email: Jeremy Sanders # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ############################################################################### from __future__ import division from itertools import count from ..compat import czip, CStringIO from .. import qtall as qt4 from . import doc from . import operations from . import widgetfactory # mime type for copy and paste widgetmime = 'text/x-vnd.veusz-widget-3' # dataset mime datamime = 'text/x-vnd.veusz-data-1' def generateWidgetsMime(widgets): """Create mime data describing widget and children. format is: numberofwidgets widgettype1 widgetname1 widgetpath1 numberoflines1 ... texttoreproducewidget """ header = [str(len(widgets))] savetext = [] for widget in widgets: header.append(widget.typename) header.append(repr(widget.name)) header.append(repr(widget.path)) save = widget.getSaveText() header.append( str(save.count('\n')) ) savetext.append(save) header.append('') text = ('\n'.join(header) + ''.join(savetext)).encode('utf-8') mimedata = qt4.QMimeData() mimedata.setData(widgetmime, qt4.QByteArray(text)) return mimedata def generateDatasetsMime(datasets, document): """Generate mime for the list of dataset names given in the document. Format is: repr of names text to recreate dataset 1 ... """ mimedata = qt4.QMimeData() # just plain text format output = [] for name in datasets: output.append( document.data[name].datasetAsText() ) text = ('\n'.join(output)).encode('utf-8') mimedata.setData('text/plain', qt4.QByteArray(text)) textfile = CStringIO() for name in datasets: # get unlinked copy of dataset ds = document.data[name].returnCopy() # write into a string file ds.saveToFile(textfile, name) rawdata = textfile.getvalue().encode('utf-8') mimedata.setData(datamime, rawdata) return mimedata def isClipboardDataMime(): """Returns whether data available on clipboard.""" mimedata = qt4.QApplication.clipboard().mimeData() return datamime in mimedata.formats() def getWidgetMime(mimedata): """Given mime data, return decoded python string.""" if widgetmime in mimedata.formats(): return mimedata.data(widgetmime).data().decode('utf-8') else: return None def getClipboardWidgetMime(): """Returns widget mime data if mimedata contains correct mimetype or None If mimedata is set, use this rather than clipboard directly """ return getWidgetMime(qt4.QApplication.clipboard().mimeData()) def getMimeWidgetTypes(data): """Get list of widget types in the mime data.""" lines = data.split('\n') try: numwidgets = int(lines[0]) except ValueError: return [] types = lines[1:1+4*numwidgets:4] return types def getMimeWidgetPaths(data): """Get list of widget paths in the mime data.""" lines = data.split('\n') numwidgets = int(lines[0]) paths = [eval(x) for x in lines[3:3+4*numwidgets:4]] return paths def isWidgetMimePastable(parentwidget, mimedata): """Is widget mime data suitable to paste at parentwidget?""" if mimedata is None: return False types = getMimeWidgetTypes(mimedata) for type in types: if doc.getSuitableParent(type, parentwidget) is None: return False return True def isMimeDropable(parentwidget, mimedata): """Can parent have this data pasted directly inside?""" if mimedata is None or parentwidget is None: return False types = getMimeWidgetTypes(mimedata) for type in types: wc = widgetfactory.thefactory.getWidgetClass(type) if not wc.willAllowParent(parentwidget): return False return True def getMimeWidgetCount(mimedata): """Get number of widgets in mimedata.""" return int( mimedata[:mimedata.find('\n')] ) class OperationWidgetPaste(operations.OperationMultiple): """Paste a widget from mime data.""" descr= 'paste widget' def __init__(self, parent, mimedata, index=-1, newnames=None): """Paste widget into parent widget from mimedata. newnames is a list of new names for pasting, if given.""" operations.OperationMultiple.__init__(self, [], descr=None) self.parentpath = parent.path self.mimedata = mimedata self.index = index self.newnames = newnames def do(self, document): """Do the import.""" from . import commandinterpreter index = self.index # get document to keep track of changes for undo/redo document.batchHistory(self) # fire up interpreter to read file interpreter = commandinterpreter.CommandInterpreter(document) parentwidget = document.resolveWidgetPath(None, self.parentpath) lines = self.mimedata.split('\n') numwidgets = int(lines[0]) # get types, names and number of lines for widgets types = lines[1:1+4*numwidgets:4] names = lines[2:2+4*numwidgets:4] names = [eval(name) for name in names] if self.newnames is not None: names = self.newnames # paths = lines[3:3+4*numwidgets:4] (not required here) widgetslines = lines[4:4+4*numwidgets:4] widgetslines = [int(x) for x in widgetslines] newwidgets = [] widgetline = 1+4*numwidgets try: for wtype, name, numline in czip(types, names, widgetslines): thisparent = doc.getSuitableParent(wtype, parentwidget) if thisparent is None: raise RuntimeError("Cannot find suitable parent for pasting") # override name if it exists already if name in thisparent.childnames: name = None # make new widget widget = document.applyOperation( operations.OperationWidgetAdd( thisparent, wtype, autoadd=False, name=name, index=index) ) newwidgets.append(widget) # run generating commands interpreter.interface.currentwidget = widget for line in lines[widgetline:widgetline+numline]: interpreter.run(line) if index >= 0: index += 1 # move to next widget widgetline += numline except Exception: document.batchHistory(None) raise # stop batching changes document.batchHistory(None) return newwidgets class OperationWidgetClone(OperationWidgetPaste): """Clone a widget.""" descr = 'clone widget' def __init__(self, widget, newparent, newname): mime = generateWidgetsMime([widget]) mimedec = mime.data(widgetmime).data().decode('utf-8') OperationWidgetPaste.__init__( self, newparent, mimedec, newnames=[newname]) def do(self, document): """Do the import.""" widgets = OperationWidgetPaste.do(self, document) return widgets[0] class OperationDataPaste(operations.Operation): """Paste dataset from mime data.""" descr = 'paste data' def __init__(self, mimedata): """Paste datasets into document.""" self.data = mimedata.data(datamime).data().decode('utf-8') def do(self, thisdoc): """Do the data paste.""" from . import commandinterpreter # write data into a temporary document tempdoc = doc.Document() # interpreter to create datasets interpreter = commandinterpreter.CommandInterpreter(tempdoc) interpreter.runFile(CStringIO(self.data)) # list of pasted datasets self.newds = [] # now transfer datasets to existing document for name, ds in sorted(tempdoc.data.items()): # get new name if name not in thisdoc.data: newname = name else: for idx in count(2): newname = '%s_%s' % (name, idx) if newname not in thisdoc.data: break thisdoc.setData(newname, ds) self.newds.append(newname) def undo(self, thisdoc): """Undo pasting datasets.""" for n in self.newds: thisdoc.deleteData(n) �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������veusz-3.0.1/veusz/document/__init__.py��������������������������������������������������������������0000664�0001750�0001750�00000002376�13161413406�017423� 0����������������������������������������������������������������������������������������������������ustar �jss�����������������������������jss�����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# document __init__.py # Copyright (C) 2004 Jeremy S. Sanders # Email: Jeremy Sanders # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ############################################################################## from .widgetfactory import * from .doc import * from .evaluate import * from .commandinterface import * from .commandinterpreter import * from .operations import * from .mime import * from .painthelper import * from .export import Export, printDialog from .dbusinterface import * from .loader import loadDocument, executeScript, LoadError ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������veusz-3.0.1/veusz/document/widgetfactory.py���������������������������������������������������������0000664�0001750�0001750�00000005241�13165472551�020543� 0����������������������������������������������������������������������������������������������������ustar �jss�����������������������������jss�����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# widget factory # tools to generate any type of plot widget you want # Copyright (C) 2004 Jeremy S. Sanders # Email: Jeremy Sanders # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ############################################################################## from __future__ import division from ..compat import citems class WidgetFactory(object): """Class to help produce any type of widget you want by name.""" def __init__(self): """Initialise the class.""" self.regwidgets = {} def register(self, classobj): """Register a class with the factory.""" self.regwidgets[classobj.typename] = classobj def makeWidget(self, widgettype, parent, document, name=None, autoadd=True, index=-1, **optargs): """Make a new widget of the appropriate type.""" # check for / in name of widget if name is not None and name.find('/') != -1: raise ValueError('name cannot contain "/"') w = self.regwidgets[widgettype](parent, name=name) w.document = document w.linkToStylesheet() # set all the passed default settings for name, val in citems(optargs): # allow subsettings to be set using __ -> syntax name = name.replace('__', '/') document.resolveSettingPath(w, name).set(val) if autoadd: w.addDefaultSubWidgets() # move around child afterwards, yuck if index != -1: del parent.children[-1] parent.children.insert(index, w) return w def getWidgetClass(self, name): """Get the class for the widget.""" return self.regwidgets[name] def listWidgets(self): """Return an array of the widgets the factory can make.""" return sorted(self.regwidgets) def listWidgetClasses(self): """Return list of allowed classes.""" return list(self.regwidgets.values()) # singleton thefactory = WidgetFactory() ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������veusz-3.0.1/veusz/document/doc.py�������������������������������������������������������������������0000664�0001750�0001750�00000053030�13316734155�016433� 0����������������������������������������������������������������������������������������������������ustar �jss�����������������������������jss�����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# document.py # A module to handle documents # Copyright (C) 2004 Jeremy S. Sanders # Email: Jeremy Sanders # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ############################################################################## """A class to represent Veusz documents, with dataset classes.""" from __future__ import division, print_function, absolute_import import codecs import os.path import traceback import datetime from collections import defaultdict try: import h5py except ImportError: h5py = None from ..compat import citems, cvalues, cstr, CStringIO, cexecfile from .. import qtall as qt4 from . import widgetfactory from . import painthelper from . import evaluate from .. import datasets from .. import utils from .. import setting def _(text, disambiguation=None, context="Document"): """Translate text.""" return qt4.QCoreApplication.translate(context, text, disambiguation) def getSuitableParent(widgettype, initialwidget): """Find the nearest relevant parent for the widgettype given.""" # find the parent to add the child to, we go up the tree looking # for possible parents parent = initialwidget wc = widgetfactory.thefactory.getWidgetClass(widgettype) while parent is not None and not wc.willAllowParent(parent): parent = parent.parent return parent class DocSuspend(object): """Handle document updates/suspensions.""" def __init__(self, doc): self.doc = doc def __enter__(self): self.doc.suspendUpdates() return self def __exit__(self, type, value, traceback): self.doc.enableUpdates() class Document(qt4.QObject): """Document class for holding the graph data. """ pluginsloaded = False # this is emitted when the document is modified signalModified = qt4.pyqtSignal(int) # emited to log a message sigLog = qt4.pyqtSignal(cstr) # emitted when document wiped sigWiped = qt4.pyqtSignal() # to ask whether the import is allowed (module name and symbol list) sigAllowedImports = qt4.pyqtSignal(cstr, list) def __init__(self): """Initialise the document.""" qt4.QObject.__init__( self ) if not Document.pluginsloaded: Document.loadPlugins() Document.pluginsloaded = True # change tracking of document as a whole self.changeset = 0 # increased when the document changes # map tags to dataset names self.datasettags = defaultdict(list) # if set, do not notify listeners of updates # wait under enableUpdates self.suspendupdates = [] # default document locale self.locale = qt4.QLocale() # evaluation context self.evaluate = evaluate.Evaluate(self) self.clearHistory() self.wipe() def wipe(self): """Wipe out any stored data.""" self.data = {} self.basewidget = widgetfactory.thefactory.makeWidget( 'document', None, self) self.setModified(False) self.filename = "" self.evaluate.wipe() self.sigWiped.emit() def clearHistory(self): """Clear any history.""" self.historybatch = [] self.historyundo = [] self.historyredo = [] def suspendUpdates(self): """Holds sending update messages. This speeds up modification of the document and prevents the document from being updated on the screen.""" self.suspendupdates.append(self.changeset) def enableUpdates(self): """Reenables document updates.""" changeset = self.suspendupdates.pop() if not self.suspendupdates and changeset != self.changeset: # bump this up as some watchers might ignore this otherwise self.changeset += 1 self.setModified() def suspend(self): """Return context manager for suspending updates.""" return DocSuspend(self) def makeDefaultDoc(self, mode='graph'): """Add default widgets to create document. mode == 'graph', 'polar', 'ternary' or 'graph3d' """ page = widgetfactory.thefactory.makeWidget( 'page', self.basewidget, self) if mode == 'graph3d': scene = widgetfactory.thefactory.makeWidget('scene3d', page, self) widgetfactory.thefactory.makeWidget('graph3d', scene, self) else: assert mode in ('graph', 'polar', 'ternary') widgetfactory.thefactory.makeWidget(mode, page, self) self.setModified() self.setModified(False) self.changeset = 0 def log(self, message): """Log a message - this is emitted as a signal.""" self.sigLog.emit(message) def applyOperation(self, operation, redoing=False): """Apply operation to the document. Operations represent atomic actions which can be done to the document and undone. Updates are suspended during the operation. If redoing is not True, the redo stack is cleared """ with DocSuspend(self): retn = operation.do(self) self.changeset += 1 if self.historybatch: # in batch mode, create an OperationMultiple for all changes self.historybatch[-1].addOperation(operation) else: # standard mode self.historyundo = self.historyundo[-9:] + [operation] if not redoing: self.historyredo = [] return retn def batchHistory(self, batch): """Enable/disable batch history mode. In this mode further operations are added to the OperationMultiple specified, until batchHistory is called with None. The objects are pushed into a list and popped off This allows multiple operations to be batched up for simple undo. """ if batch: self.historybatch.append(batch) else: self.historybatch.pop() def undoOperation(self): """Undo the previous operation.""" operation = self.historyundo.pop() with DocSuspend(self): operation.undo(self) self.changeset += 1 self.historyredo.append(operation) def canUndo(self): """Returns True if previous operation can be removed.""" return len(self.historyundo) != 0 def redoOperation(self): """Redo undone operations.""" operation = self.historyredo.pop() return self.applyOperation(operation, redoing=True) def canRedo(self): """Returns True if previous operation can be redone.""" return len(self.historyredo) != 0 def isBlank(self): """Is the document unchanged?""" return self.changeset == 0 def setData(self, name, dataset): """Set dataset in document.""" self.data[name] = dataset dataset.document = self # update the change tracking self.setModified() def deleteData(self, name): """Remove a dataset""" del self.data[name] self.setModified() def modifiedData(self, dataset): """Notify dataset was modified""" assert dataset in self.data.values() self.setModified() def getLinkedFiles(self, filenames=None): """Get a list of LinkedFile objects used by the document. if filenames is a set, only get the objects with filenames given """ links = set() for ds in cvalues(self.data): if ds.linked and (filenames is None or ds.linked.filename in filenames): links.add(ds.linked) return list(links) def reloadLinkedDatasets(self, filenames=None): """Reload linked datasets from their files. If filenames is a set(), only reload from these filenames Returns a tuple of - List of datasets read - Dict of tuples containing dataset names and number of errors """ links = self.getLinkedFiles(filenames=filenames) read = [] errors = {} # load in the files, merging the vars read and errors if links: with self.suspend(): for lf in links: nread, nerrors = lf.reloadLinks(self) read += nread errors.update(nerrors) self.setModified() read.sort() return (read, errors) def datasetName(self, dataset): """Find name for given dataset, raising ValueError if missing.""" for name, ds in citems(self.data): if ds is dataset: return name raise ValueError("Cannot find dataset") def renameDataset(self, oldname, newname): """Rename the dataset.""" d = self.data[oldname] del self.data[oldname] self.data[newname] = d self.setModified() def getData(self, name): """Get data with name""" return self.data[name] def setModified(self, ismodified=True): """Set the modified flag on the data, and inform views.""" # useful for tracking back modifications # import traceback # traceback.print_stack() self.modified = ismodified self.changeset += 1 if len(self.suspendupdates) == 0: self.signalModified.emit(ismodified) def isModified(self): """Return whether modified flag set.""" return self.modified @classmethod def loadPlugins(kls, pluginlist=None): """Load plugins and catch exceptions.""" if pluginlist is None: pluginlist = setting.settingdb.get('plugins', []) for plugin in pluginlist: try: cexecfile(plugin, {}) except Exception: err = _('Error loading plugin %s\n\n%s') % ( plugin, traceback.format_exc()) raise RuntimeError(err) def paintTo(self, painthelper, page): """Paint page specified to the paint helper.""" self.basewidget.draw(painthelper, page) def getNumberPages(self): """Return the number of pages in the document.""" return len(self.basewidget.children) def getPage(self, pagenumber): """Return widget for page.""" return self.basewidget.children[pagenumber] def datasetTags(self): """Get list of all tags in datasets.""" tags = set() for dataset in cvalues(self.data): tags.update(dataset.tags) return sorted(tags) def _writeFileHeader(self, fileobj, type): """Write a header to a saved file of type.""" fileobj.write('# Veusz %s (version %s)\n' % (type, utils.version())) fileobj.write('# Saved at %s\n\n' % datetime.datetime.utcnow().isoformat()) def saveDatasetTags(self, fileobj): """Write dataset tags to output file""" # get a list of all tags and which datasets have them bytag = defaultdict(list) for name, dataset in sorted(self.data.items()): for t in dataset.tags: bytag[t].append(name) # write out tags for tag, val in sorted(bytag.items()): fileobj.write( 'TagDatasets(%s, %s)\n' % (utils.rrepr(tag), utils.rrepr(val))) def saveToFile(self, fileobj): """Save the text representing a document to a file. The ordering can be important, as some things override previous steps: - Tagging doesn't work if the dataset isn't already defined. - Loading from files may bring in new datasets which override defined datasets, so save links first """ self._writeFileHeader(fileobj, 'saved document') # add file directory to import path if we know it reldirname = None if getattr(fileobj, 'name', False): reldirname = os.path.dirname( os.path.abspath(fileobj.name) ) fileobj.write('AddImportPath(%s)\n' % utils.rrepr(reldirname)) # save those datasets which are linked # we do this first in case the datasets are overridden below savedlinks = {} for name, dataset in sorted(self.data.items()): dataset.saveLinksToSavedDoc(fileobj, savedlinks, relpath=reldirname) # save the remaining datasets for name, dataset in sorted(self.data.items()): dataset.saveToFile(fileobj, name) # save tags of datasets self.saveDatasetTags(fileobj) # add custom definitions self.evaluate.saveCustomDefinitions(fileobj) # save the actual tree structure fileobj.write(self.basewidget.getSaveText()) self.setModified(False) def saveToHDF5File(self, fileobj): """Save to HDF5 (h5py) output file given.""" # groups in output hdf5 vszgrp = fileobj.create_group('Veusz') vszgrp.attrs['vsz_version'] = utils.version() vszgrp.attrs['vsz_saved_at'] = datetime.datetime.utcnow().isoformat() vszgrp.attrs['vsz_format'] = 1 # version number (currently unused) datagrp = vszgrp.create_group('Data') docgrp = vszgrp.create_group('Document') textstream = CStringIO() self._writeFileHeader(textstream, 'saved document') # add file directory to import path if we know it reldirname = None if getattr(fileobj, 'filename', False): reldirname = os.path.dirname( os.path.abspath(fileobj.filename) ) textstream.write('AddImportPath(%s)\n' % utils.rrepr(reldirname)) # add custom definitions self.evaluate.saveCustomDefinitions(textstream) # save those datasets which are linked # we do this first in case the datasets are overridden below savedlinks = {} for name, dataset in sorted(self.data.items()): dataset.saveLinksToSavedDoc(textstream, savedlinks, relpath=reldirname) # save the remaining datasets for name, dataset in sorted(self.data.items()): dataset.saveToFile(textstream, name, mode='hdf5', hdfgroup=datagrp) # handle tagging # get a list of all tags and which datasets have them bytag = defaultdict(list) for name, dataset in sorted(self.data.items()): for t in dataset.tags: bytag[t].append(name) # write out tags as datasets tagsgrp = docgrp.create_group('Tags') for tag, dsnames in sorted(bytag.items()): tagsgrp[tag] = [v.encode('utf-8') for v in sorted(dsnames)] # save the actual tree structure textstream.write(self.basewidget.getSaveText()) # create single dataset contains document docgrp['document'] = [ textstream.getvalue().encode('utf-8') ] self.setModified(False) def save(self, filename, mode='vsz'): """Save to output file. mode is 'vsz' or 'hdf5' """ if mode == 'vsz': with codecs.open(filename, 'w', 'utf-8') as f: self.saveToFile(f) elif mode == 'hdf5': if h5py is None: raise RuntimeError('Missing h5py module') with h5py.File(filename, 'w') as f: self.saveToHDF5File(f) else: raise RuntimeError('Invalid save mode') self.filename = filename def load(self, filename, mode='vsz', callbackunsafe=None, callbackimporterror=None): """Load document from file. mode is 'vsz' or 'hdf5' """ from . import loader loader.loadDocument( self, filename, mode=mode, callbackunsafe=callbackunsafe, callbackimporterror=callbackimporterror) def exportStyleSheet(self, fileobj): """Export the StyleSheet to a file.""" self._writeFileHeader(fileobj, 'exported stylesheet') stylesheet = self.basewidget.settings.StyleSheet fileobj.write( stylesheet.saveText(True, rootname='') ) def _pagedocsize(self, widget, dpi, scaling, integer): """Helper for page or doc size.""" if dpi is None: p = qt4.QPixmap(1, 1) dpi = (p.logicalDpiX(), p.logicalDpiY()) helper = painthelper.PaintHelper(self, (1,1), dpi=dpi) w = widget.settings.get('width').convert(helper) * scaling h = widget.settings.get('height').convert(helper) * scaling if integer: return int(w), int(h) else: return w, h def pageSize(self, pagenum, dpi=None, scaling=1., integer=True): """Get the size of a particular page in pixels. If dpi is None, use the default Qt screen dpi Use dpi if given.""" page = self.basewidget.getPage(pagenum) if page is None: return self.docSize(dpi=dpi, scaling=scaling, integer=integer) return self._pagedocsize( page, dpi=dpi, scaling=scaling, integer=integer) def docSize(self, dpi=None, scaling=1., integer=True): """Get size for document.""" return self._pagedocsize( self.basewidget, dpi=dpi, scaling=scaling, integer=integer) def resolvePath(self, fromobj, path): """Resolve item relative to fromobj. If fromobj is None, then an absolute path is assumed. Returns a widget, setting or settings as appropriate. """ # where to search from obj = self.basewidget if (path[:1]=='/' or fromobj is None) else fromobj # iterate over path parts for p in path.split('/'): if p == '..': p = obj.parent if p is None: raise ValueError("Base graph has no parent") obj = p elif p == '.' or len(p) == 0: pass elif obj.iswidget: if p in obj.settings: obj = obj.settings.get(p) else: widget = obj.getChild(p) if widget is None: raise ValueError("Widget has no child %s" % p) obj = widget elif obj.issettings: try: obj = obj.get(p) except KeyError: raise ValueError("Settings has no child %s" % p) else: raise ValueError("Item has no children") return obj def resolveWidgetPath(self, fromobj, path): """Resolve path to Widget. If fromobj is None, then is resolved to root widget. Raises ValueError if path is invalid or not to widget. """ obj = self.resolvePath(fromobj, path) if not obj.iswidget: raise ValueError("Not path to widget") return obj def resolveSettingPath(self, fromobj, path): """Resolve path to Setting. If fromobj is None, then is resolved to root widget. Raises ValueError if path is invalid or not to Setting. """ obj = self.resolvePath(fromobj, path) if not obj.issetting: raise ValueError("Not path to setting") return obj def walkNodes(self, tocall, root=None, nodetypes=('widget', 'setting', 'settings'), _path=None): """Walk the widget/settings/setting nodes in the document. For each one call tocall(path, node). nodetypes is tuple of possible node types """ if root is None: root = self.basewidget if _path is None: _path = root.path if root.nodetype in nodetypes: tocall(_path, root) if root.nodetype == 'widget': # get rid of // at start of path if _path == '/': _path = '' # do the widget's children for w in root.children: self.walkNodes(tocall, root=w, nodetypes=nodetypes, _path = _path + '/' + w.name) # then do the widget's settings self.walkNodes(tocall, root=root.settings, nodetypes=nodetypes, _path=_path) elif root.nodetype == 'settings': # do the settings of the settings for name, s in sorted(root.setdict.items()): self.walkNodes(tocall, root=s, nodetypes=nodetypes, _path = _path + '/' + s.name) # elif root.nodetype == 'setting': pass def formatValsWithDatatypeToText(self, vals, displaydatatype): """Given a set of values, datatype, return a list of strings corresponding to these data.""" if displaydatatype == 'text': return vals elif displaydatatype == 'numeric': return [ utils.formatNumber(val, '%Vg', locale=self.locale) for val in vals ] elif displaydatatype == 'date': return [ utils.dateFloatToString(val) for val in vals ] else: raise RuntimeError('Invalid data type') ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������veusz-3.0.1/veusz/document/commandinterpreter.py����������������������������������������������������0000664�0001750�0001750�00000021401�13166117204�021556� 0����������������������������������������������������������������������������������������������������ustar �jss�����������������������������jss�����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# commandinterpreter.py # this module handles the command line interface interpreter # Copyright (C) 2004 Jeremy S. Sanders # Email: Jeremy Sanders # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ############################################################################## """ A module for the execution of user 'macro' code inside a special environment. That way the commandline can be used to interact with the app without worrying about the app internals. The way this works is to create an evironment specific to the class consisting of globals & locals. Commands can be run inside the environment. Python errors are trapped and dumped out to stderr. stderr and stdout can be reassigned in the environment to point to an alternative interface. They should point to objects providing a write() interface. This class is modelled on the one described in 'GUI Programming in Python: QT Edition' (Boudewijn Rempt) """ from __future__ import division, print_function # get globals before things are imported _globals = globals() import sys import traceback import io import os.path from ..compat import pickle, cexec from .commandinterface import CommandInterface from .. import utils class CommandInterpreter(object): """Class for executing commands in the Veusz command line language.""" def __init__(self, document): """ Initialise object with the document it interfaces.""" self.document = document # set up interface to document self.interface = CommandInterface(document) # initialise environment (make a copy from inital globals) self.globals = _globals.copy() # save the stdout & stderr self.write_stdout = sys.stdout self.write_stderr = sys.stderr self.read_stdin = sys.stdin # import numpy into the environment cexec("from numpy import *", self.globals) # define root object self.globals['Root'] = self.interface.Root # shortcut ifc = self.interface # define commands for interface self.cmds = {} for cmd in ( CommandInterface.safe_commands + CommandInterface.unsafe_commands + CommandInterface.import_commands): self.cmds[cmd] = getattr(ifc, cmd) self.cmds['GPL'] = self.GPL self.cmds['Load'] = self.Load self.globals.update( self.cmds ) def addCommand(self, name, command): """Add the given command to the list of available commands.""" self.cmds[name] = command self.globals[name] = command def setFiles(self, stdout, stderr, stdin): """Assign the environment input/output files.""" self.write_stdout = stdout self.write_stderr = stderr self.read_stdin = stdin def _pythonise(self, text): """Internal routine to convert commands in the form Cmd a b c into Cmd(a,b,c).""" out = '' # iterate over lines for line in text.split('\n'): parts = line.split() # turn Cmd a b c into Cmd(a,b,c) if len(parts) != 0 and parts[0] in self.cmds: line = utils.pythonise(line) out += line + '\n' return out def run(self, inputcmds, filename = None): """ Run a set of commands inside the preserved environment. inputcmds: a string with the commands to run filename: a filename to report if there are errors """ if filename is None: filename = '' # pythonise! inputcmds = self._pythonise(inputcmds) # ignore if blank if len(inputcmds.strip()) == 0: return # preserve output streams saved = sys.stdout, sys.stderr, sys.stdin sys.stdout, sys.stderr, sys.stdin = ( self.write_stdout, self.write_stderr, self.read_stdin) # count number of newlines in expression # If it's 2, then execute as a single statement (print out result) if inputcmds.count('\n') == 2: stattype = 'single' else: stattype = 'exec' # first compile the code to check for syntax errors try: c = compile(inputcmds, filename, stattype) except (OverflowError, ValueError, SyntaxError): info = sys.exc_info() backtrace = traceback.format_exception(*info) for line in backtrace: sys.stderr.write(line) else: # block update signals from document while updating with self.document.suspend(): try: # execute the code cexec(c, self.globals) except: # print out the backtrace to stderr info = sys.exc_info() backtrace = traceback.format_exception(*info) for line in backtrace: sys.stderr.write(line) # return output streams sys.stdout, sys.stderr, sys.stdin = saved def Load(self, filename): """Replace the document with a new one from the filename.""" with io.open(filename, 'rU', encoding='utf8') as f: self.document.wipe() self.interface.To('/') oldfile = self.globals['__file__'] self.globals['__file__'] = os.path.abspath(filename) self.interface.importpath.append( os.path.dirname(os.path.abspath(filename))) self.runFile(f) self.interface.importpath.pop() self.globals['__file__'] = oldfile self.document.setModified() self.document.setModified(False) self.document.clearHistory() def runFile(self, fileobject): """ Run a file in the preserved environment.""" # preserve output streams temp_stdout = sys.stdout temp_stderr = sys.stderr sys.stdout = self.write_stdout sys.stderr = self.write_stderr with self.document.suspend(): # actually run the code try: cexec(fileobject.read(), self.globals) except Exception: # print out the backtrace to stderr info = sys.exc_info() backtrace = traceback.format_exception(*info) for line in backtrace: sys.stderr.write(line) # return output streams sys.stdout = temp_stdout sys.stderr = temp_stderr def evaluate(self, expression): """Evaluate an expression in the environment.""" # preserve output streams temp_stdout = sys.stdout temp_stderr = sys.stderr sys.stdout = self.write_stdout sys.stderr = self.write_stderr # actually run the code try: retn = eval(expression, self.globals) except Exception: # print out the backtrace to stderr info = sys.exc_info() backtrace = traceback.format_exception(*info) for line in backtrace: sys.stderr.write(line) retn = None # return output streams sys.stdout = temp_stdout sys.stderr = temp_stderr return retn def GPL(self): """Write the GPL to the console window.""" sys.stdout.write( utils.getLicense() ) def runPickle(self, command): """Run a pickled command given as arguments. command should consist of following: dumps( (name, args, namedargs) ) name is name of function to execute in environment args are the arguments (list) namedargs are the named arguments (dict). """ name, args, namedargs = pickle.loads(command) self.globals['_tmp_args0'] = args self.globals['_tmp_args1'] = namedargs #print(name, args, namedargs) try: retn = eval('%s(*_tmp_args0, **_tmp_args1)' % name) except Exception as e: # return exception picked if exception retn = e del self.globals['_tmp_args0'] del self.globals['_tmp_args1'] return pickle.dumps(retn) ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������veusz-3.0.1/veusz/document/emf_export.py������������������������������������������������������������0000664�0001750�0001750�00000035563�13260412451�020037� 0����������������������������������������������������������������������������������������������������ustar �jss�����������������������������jss�����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2009 Jeremy S. Sanders # Email: Jeremy Sanders # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ############################################################################## """A paint engine to produce EMF exports. Requires: PyQt-x11-gpl-4.6-snapshot-20090906.tar.gz sip-4.9-snapshot-20090906.tar.gz pyemf """ from __future__ import division, absolute_import import struct import pyemf from .. import qtall as qt4 from ..compat import cbytes inch_mm = 25.4 scale = 100 def isStockObject(obj): """Is this a stock windows object.""" return (obj & 0x80000000) != 0 class _EXTCREATEPEN(pyemf._EMR._EXTCREATEPEN): """Extended pen creation record with custom line style.""" typedef = [ ('i','handle',0), ('i','offBmi',0), ('i','cbBmi',0), ('i','offBits',0), ('i','cbBits',0), ('i','style'), ('i','penwidth'), ('i','brushstyle'), ('i','color'), ('i','brushhatch',0), ('i','numstyleentries')] def __init__(self, style=pyemf.PS_SOLID, width=1, color=0, styleentries=[]): """Create pen. styleentries is a list of dash and space lengths.""" pyemf._EMR._EXTCREATEPEN.__init__(self) self.style = style self.penwidth = width self.color = pyemf._normalizeColor(color) self.brushstyle = 0x0 # solid if style & pyemf.PS_STYLE_MASK != pyemf.PS_USERSTYLE: styleentries = [] self.numstyleentries = len(styleentries) if styleentries: self.unhandleddata = struct.pack( "i"*self.numstyleentries, *styleentries) def hasHandle(self): return True class EMFPaintEngine(qt4.QPaintEngine): """Custom EMF paint engine.""" def __init__(self, width_in, height_in, dpi=75): qt4.QPaintEngine.__init__(self, qt4.QPaintEngine.Antialiasing | qt4.QPaintEngine.PainterPaths | qt4.QPaintEngine.PrimitiveTransform | qt4.QPaintEngine.PaintOutsidePaintEvent | qt4.QPaintEngine.PatternBrush ) self.width = width_in self.height = height_in self.dpi = dpi def begin(self, paintdevice): self.emf = pyemf.EMF(self.width, self.height, int(self.dpi*scale)) self.pen = self.emf.GetStockObject(pyemf.BLACK_PEN) self.pencolor = (0, 0, 0) self.brush = self.emf.GetStockObject(pyemf.NULL_BRUSH) self.paintdevice = paintdevice return True def drawLines(self, lines): """Draw lines to emf output.""" for line in lines: self.emf.Polyline( [ (int(line.x1()*scale), int(line.y1()*scale)), (int(line.x2()*scale), int(line.y2()*scale)) ] ) def drawPolygon(self, points, mode): """Draw polygon on output.""" # print "Polygon" pts = [(int(p.x()*scale), int(p.y()*scale)) for p in points] if mode == qt4.QPaintEngine.PolylineMode: self.emf.Polyline(pts) else: self.emf.SetPolyFillMode( {qt4.QPaintEngine.WindingMode: pyemf.WINDING, qt4.QPaintEngine.OddEvenMode: pyemf.ALTERNATE, qt4.QPaintEngine.ConvexMode: pyemf.WINDING} ) self.emf.Polygon(pts) def drawEllipse(self, rect): """Draw an ellipse.""" # print "ellipse" args = ( int(rect.left()*scale), int(rect.top()*scale), int(rect.right()*scale), int(rect.bottom()*scale), int(rect.left()*scale), int(rect.top()*scale), int(rect.left()*scale), int(rect.top()*scale) ) self.emf.Pie(*args) self.emf.Arc(*args) def drawPoints(self, points): """Draw points.""" # print "points" for pt in points: x, y = (pt.x()-0.5)*scale, (pt.y()-0.5)*scale self.emf.Pie( int(x), int(y), int((pt.x()+0.5)*scale), int((pt.y()+0.5)*scale), int(x), int(y), int(x), int(y) ) def drawPixmap(self, r, pixmap, sr): """Draw pixmap to display.""" # convert pixmap to BMP format bytearr = qt4.QByteArray() buf = qt4.QBuffer(bytearr) buf.open(qt4.QIODevice.WriteOnly) pixmap.save(buf, "BMP") # chop off bmp header to get DIB bmp = cbytes(buf.data()) dib = bmp[0xe:] hdrsize, = struct.unpack('= 5.6 elif m == getattr(qt4.QPaintDevice, 'PdmDevicePixelRatioScaled', -1): return 1 else: # fall back return qt4.QPaintDevice.metric(self, m) ���������������������������������������������������������������������������������������������������������������������������������������������veusz-3.0.1/veusz/document/loader.py����������������������������������������������������������������0000664�0001750�0001750�00000024052�13166140370�017127� 0����������������������������������������������������������������������������������������������������ustar �jss�����������������������������jss�����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2014 Jeremy S. Sanders # Email: Jeremy Sanders # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ############################################################################## # note: no future statements here for backward compatibility import sys import os.path import traceback import io import numpy as N from .. import qtall as qt4 from .. import setting from .. import utils from ..compat import cexec, cstrerror, cbytes, cexceptionuser from .commandinterface import CommandInterface from . import datasets # loaded lazily h5py = None def _(text, disambiguation=None, context='DocumentLoader'): """Translate text.""" return qt4.QCoreApplication.translate(context, text, disambiguation) class LoadError(RuntimeError): """Error when loading document.""" def __init__(self, text, backtrace=''): RuntimeError.__init__(self, text) self.backtrace = backtrace def bconv(s): """Sometimes h5py returns non-unicode strings, so hack to decode strings if in wrong format.""" if isinstance(s, cbytes): return s.decode('utf-8') return s def _importcaller(interface, name, callbackimporterror): """Wrap an import statement to check for IOError.""" def wrapped(*args, **argsk): while True: try: getattr(interface, name)(*args, **argsk) except IOError as e: errmsg = cexceptionuser(e) fnameidx = interface.import_filenamearg[name] assert fnameidx >= 0 filename = args[fnameidx] raiseerror = True if callbackimporterror: # used by mainwindow to show dialog and get new filename fname = callbackimporterror(filename, errmsg) if fname: # put new filename into function argument list args = list(args) args[fnameidx] = fname raiseerror = False if raiseerror: # send error message back to UI raise LoadError( _("Error reading file '%s':\n\n%s") % (filename, errmsg)) else: # imported ok break return wrapped def executeScript(thedoc, filename, script, callbackunsafe=None, callbackimporterror=None): """Execute a script for the document. This handles setting up the environment and checking for unsafe commands in the execution. filename: filename to supply in __filename__ script: text to execute callbackunsafe: should be set to a function to ask the user whether it is ok to execute any unsafe commands found. Return True if ok. callbackimporterror(filename, error): should be set to function to return new filename in case of import error, or False if none User should wipe docment before calling this. """ def genexception(exc): info = sys.exc_info() backtrace = ''.join(traceback.format_exception(*info)) return LoadError(cexceptionuser(exc), backtrace=backtrace) # compile script and check for security (if reqd) unsafe = [setting.transient_settings['unsafe_mode']] while True: try: compiled = utils.compileChecked( script, mode='exec', filename=filename, ignoresecurity=unsafe[0]) break except utils.SafeEvalException: if callbackunsafe is None or not callbackunsafe(): raise LoadError(_("Unsafe command in script")) # repeat with unsafe mode switched on unsafe[0] = True except Exception as e: raise genexception(e) env = thedoc.evaluate.context.copy() interface = CommandInterface(thedoc) # allow safe commands as-is for cmd in interface.safe_commands: env[cmd] = getattr(interface, cmd) # define root node env['Root'] = interface.Root # wrap unsafe calls with a function to check whether ok def _unsafecaller(func): def wrapped(*args, **argsk): if not unsafe[0]: if callbackunsafe is None or not callbackunsafe(): raise LoadError(_("Unsafe command in script")) unsafe[0] = True func(*args, **argsk) return wrapped for name in interface.unsafe_commands: env[name] = _unsafecaller(getattr(interface, name)) # override import commands with wrapper for name in interface.import_commands: env[name] = _importcaller(interface, name, callbackimporterror) # get ready for loading document env['__file__'] = filename # allow import to happen relative to loaded file interface.AddImportPath( os.path.dirname(os.path.abspath(filename)) ) with thedoc.suspend(): try: # actually run script text cexec(compiled, env) except LoadError: raise except Exception as e: raise genexception(e) def loadHDF5Dataset1D(datagrp): args = {} # this weird usage of sets is to work around some sort of weird # error where h5py gives an error when doing 'a' in datagrp # this gives error: 'perr' in datagrp parts = set(datagrp) & set(('data', 'serr', 'perr', 'nerr')) for v in parts: args[v] = N.array(datagrp[v]) return datasets.Dataset(**args) def loadHDF5Dataset2D(datagrp): args = {} parts = set(datagrp) & set( ('data', 'xcent', 'xedge', 'ycent', 'yedge', 'xrange', 'yrange')) for v in parts: args[v] = N.array(datagrp[v]) return datasets.Dataset2D(**args) def loadHDF5DatasetDate(datagrp): return datasets.DatasetDateTime(data=datagrp['data']) def loadHDF5DatasetText(datagrp): data = [d.decode('utf-8') for d in datagrp['data']] return datasets.DatasetText(data=data) def loadHDF5Datasets(thedoc, hdffile): """Load all the Veusz datasets in the HDF5 file.""" alldatagrp = hdffile['Veusz']['Data'] datafuncs = { '1d': loadHDF5Dataset1D, '2d': loadHDF5Dataset2D, 'date': loadHDF5DatasetDate, 'text': loadHDF5DatasetText, } for name in alldatagrp: datagrp = alldatagrp[name] datatype = bconv(datagrp.attrs['vsz_datatype']) veuszname = utils.unescapeHDFDataName(bconv(name)) dataset = datafuncs[datatype](datagrp) thedoc.setData(veuszname, dataset) def tagHDF5Datasets(thedoc, hdffile): """Tag datasets loaded from HDF5 file.""" tags = hdffile['Veusz']['Document']['Tags'] for tag in tags: vsztag = bconv(tag) datasets = tags[tag] for name in datasets: dsname = name.decode('utf-8') thedoc.data[dsname].tags.add(vsztag) def loadHDF5Doc(thedoc, filename, callbackunsafe=None, callbackimporterror=None): """Load an HDF5 of the name given.""" try: global h5py import h5py except ImportError: raise LoadError(_("No HDF5 support as h5py module is missing")) with thedoc.suspend(): thedoc.wipe() thedoc.filename = filename hdffile = h5py.File(filename, 'r') try: vszformat = hdffile['Veusz'].attrs['vsz_format'] vszversion = hdffile['Veusz'].attrs['vsz_version'] except KeyError: raise LoadError( _("HDF5 file '%s' is not a Veusz saved document") % os.path.basename(filename)) maxformat = 1 if vszformat > maxformat: raise LoadError( _("This document version (%i) is not supported. " "It was written by Veusz %s.\n" "This Veusz only supports document version %i.") % (vszformat, vszversion, maxformat)) # load document script = hdffile['Veusz']['Document']['document'][0].decode('utf-8') executeScript( thedoc, filename, script, callbackunsafe=callbackunsafe, callbackimporterror=callbackimporterror) # then load datasets loadHDF5Datasets(thedoc, hdffile) # and then tag tagHDF5Datasets(thedoc, hdffile) hdffile.close() def loadDocument(thedoc, filename, mode='vsz', callbackunsafe=None, callbackimporterror=None): """Load document from file. mode is 'vsz' or 'hdf5' """ if mode == 'vsz': try: with io.open(filename, 'rU', encoding='utf-8') as f: script = f.read() except EnvironmentError as e: raise LoadError( _("Cannot open document '%s'\n\n%s") % (os.path.basename(filename), cstrerror(e)) ) except UnicodeDecodeError: raise LoadError( _("File '%s' is not a valid Veusz document") % os.path.basename(filename) ) thedoc.wipe() thedoc.filename = filename executeScript( thedoc, filename, script, callbackunsafe=callbackunsafe, callbackimporterror=callbackimporterror) elif mode == 'hdf5': loadHDF5Doc( thedoc, filename, callbackunsafe=callbackunsafe, callbackimporterror=callbackimporterror) else: raise RuntimeError('Invalid load mode') thedoc.setModified(False) thedoc.clearHistory() ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������veusz-3.0.1/veusz/document/commandinterface.py������������������������������������������������������0000664�0001750�0001750�00000076651�13322660165�021177� 0����������������������������������������������������������������������������������������������������ustar �jss�����������������������������jss�����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# commandinterface.py # this module supplies the command line interface for plotting # Copyright (C) 2004 Jeremy S. Sanders # Email: Jeremy Sanders # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ############################################################################## """ Module supplies the command interface used in the program, and for external programs. """ from __future__ import division, print_function import os.path import numpy as N from ..compat import cbasestr from .. import qtall as qt4 from .. import setting from .. import embed from .. import plugins from .. import utils from .. import datasets from . import operations from . import mime from . import export def _(text, disambiguation=None, context='CommandInterface'): """Translate text.""" return qt4.QCoreApplication.translate(context, text, disambiguation) def registerImportCommand(name, method, filenamearg=0): """Add command to command interface.""" setattr(CommandInterface, name, method) CommandInterface.import_commands.append(name) CommandInterface.import_filenamearg[name] = filenamearg class CommandInterface(qt4.QObject): """Class provides command interface.""" # commands which are safe in any script (excluding import commands) safe_commands = [ 'Action', 'Add', 'AddCustom', 'AddImportPath', 'CloneWidget', 'CreateHistogram', 'DatasetPlugin', 'FilterDatasets', 'Get', 'GetChildren', 'GetColormap', 'GetData', 'GetDataType', 'GetDatasets', 'ImportFITSFile', 'List', 'NodeChildren', 'NodeType', 'ReloadData', 'Remove', 'RemoveCustom', 'Rename', 'ResolveReference', 'Set', 'SetData', 'SetData2D', 'SetData2DExpression', 'SetData2DExpressionXYZ', 'SetData2DXYFunc', 'SetDataDateTime', 'SetDataExpression', 'SetDataND', 'SetDataRange', 'SetDataText', 'SetToReference', 'SetVerbose', 'SettingType', 'TagDatasets', 'To', 'WidgetType', ] # commands for importing data import_commands = [] # number of argument which contains filename import_filenamearg = {} # commands which can modify disk, etc unsafe_commands = [ 'Export', 'Print', 'Save', ] def __init__(self, document): """Initialise the interface.""" qt4.QObject.__init__(self) self.document = document self.currentwidget = self.document.basewidget self.verbose = False self.importpath = [] self.document.sigWiped.connect(self.slotWipedDoc) self.Root = embed.WidgetNode(self, 'widget', '/') @qt4.pyqtSlot() def slotWipedDoc(self): """When the document is wiped, we change to the root widget.""" self.To('/') def findFileOnImportPath(self, filename): """Find file on path, returning filename, or original if not found.""" for path in self.importpath: fname = os.path.join(path, filename) try: # try to open file to make sure we have access to it and # it exists opened = open(fname) opened.close() return fname except EnvironmentError: pass return filename def SetVerbose(self, v=True): """Specify whether we want verbose output after operations.""" self.verbose = v def Add(self, widgettype, **args_opt): """Add a widget to the widget with the type given. optional arguments: widget: widget path to place widget in autoadd: if True (default), any subwidgets associated with widget are created automatically setting names, e.g. leftMargin='1cm', Border_color='red' """ at = self.currentwidget if 'widget' in args_opt: at = self.document.resolveWidgetPath( self.currentwidget, args_opt['widget']) del args_opt['widget'] op = operations.OperationWidgetAdd(at, widgettype, **args_opt) w = self.document.applyOperation(op) if self.verbose: print(_("Added a widget of type '%s' (%s)") % (type, w.userdescription)) return w.name def AddCustom(self, ctype, name, val, mode='appendalways'): """Add a custom definition for evaluation of expressions. This can define a constant (can be in terms of other constants), a function of 1 or more variables, or a function imported from an external python module. ctype is "constant", "function", "definition" (either constant or function), "import", "color" or "colormap". name is name of constant, color or colormap, "function(x, y, ...)" or module name. val is definition for constant or function (both are _strings_), or is a list of symbols for a module (comma separated items in a string). For a colormap, val is a list of 4-item tuples containing R,G,B,alpha values from 0 to 255. For a color this is a string with the format '#RRGGBB' or '#RRGGBBAA'. if mode is 'appendalways', the custom value is appended to the end of the list even if there is one with the same name. If mode is 'replace', it replaces any existing definition in the same place in the list or is appended otherwise. If mode is 'append', then an existing definition is deleted, and the new one appended to the end. """ if ctype == 'colormap': self.document.evaluate.validateProcessColormap(val) else: if not isinstance(val, cbasestr): raise RuntimeError('Value should be string') if mode not in ('appendalways', 'append', 'replace'): raise RuntimeError('Invalid mode') try: attr = operations.OperationSetCustom.type_to_attr[ctype] except KeyError: raise RuntimeError('Invalid type') vals = list(getattr(self.document.evaluate, attr)) item = [name.strip(), val] if mode == 'appendalways': vals.append(item) else: # find any existing item for i, (n, v) in enumerate(vals): if n == name: if mode == 'append': del vals[i] vals.append(item) else: # replace vals[i] = item break else: # no existing item, so append vals.append(item) op = operations.OperationSetCustom(ctype, vals) self.document.applyOperation(op) def AddImportPath(self, directory): """Add directory to import file path.""" assert isinstance(directory, cbasestr) self.importpath.append(directory) def CloneWidget(self, widget, newparent, newname=None): """Clone the widget given, placing the copy in newparent and the name given. newname is an optional new name to give it Returns new widget path """ widget = self.document.resolveWidgetPath(self.currentwidget, widget) newparent = self.document.resolveWidgetPath(self.currentwidget, newparent) op = mime.OperationWidgetClone(widget, newparent, newname) w = self.document.applyOperation(op) return w.path def CreateHistogram(self, inexpr, outbinsds, outvalsds, binparams=None, binmanual=None, method='counts', cumulative = 'none', errors=False): """Histogram an input expression. inexpr is input expression outbinds is the name of the dataset to create giving bin positions outvalsds is name of dataset for bin values binparams is None or (numbins, minval, maxval, islogbins) binmanual is None or a list of bin values method is 'counts', 'density', or 'fractions' cumulative is to calculate cumulative distributions which is 'none', 'smalltolarge' or 'largetosmall' errors is to calculate Poisson error bars """ op = operations.OperationDatasetHistogram( inexpr, outbinsds, outvalsds, binparams=binparams, binmanual=binmanual, method=method, cumulative=cumulative, errors=errors) self.document.applyOperation(op) if self.verbose: print(_('Constructed histogram of "%s", creating datasets' ' "%s" and "%s"') % (inexpr, outbinsds, outvalsds)) def DatasetPlugin(self, pluginname, fields, datasetnames={}): """Use a dataset plugin. pluginname: name of plugin to use fields: dict of input values to plugin datasetnames: dict mapping old names to new names of datasets if they are renamed. The new name None means dataset is deleted.""" # lookup plugin (urgh) plugin = None for pkls in plugins.datasetpluginregistry: if pkls.name == pluginname: plugin = pkls() break if plugin is None: raise RuntimeError("Cannot find dataset plugin '%s'" % pluginname) # do the work op = operations.OperationDatasetPlugin(plugin, fields, datasetnames=datasetnames) outdatasets = self.document.applyOperation(op) if self.verbose: print(_("Used dataset plugin %s to make datasets %s") % ( pluginname, ', '.join(outdatasets))) def Remove(self, name): """Remove a widget from the dataset.""" w = self.document.resolveWidgetPath(self.currentwidget, name) op = operations.OperationWidgetDelete(w) self.document.applyOperation(op) if self.verbose: print(_("Removed widget '%s'") % name) def RemoveCustom(self, name): """Removes a custom-defined constant, function or import.""" # look for definiton and delete if found for ctype, attr in ( ('import', 'def_imports'), ('definition', 'def_definitions'), ('color', 'def_colors'), ('colormap', 'def_colormaps')): vals = list(getattr(self.document.evaluate, attr)) for i, (cname, cval) in enumerate(vals): if name == cname: del vals[i] op = operations.OperationSetCustom(ctype, vals) self.document.applyOperation(op) return else: raise ValueError('Custom variable not defined') def To(self, where): """Change to a widget within the current widget. where is a path to the widget relative to the current widget """ self.currentwidget = self.document.resolveWidgetPath( self.currentwidget, where) if self.verbose: print(_("Changed to widget '%s'") % self.currentwidget.path) def List(self, where='.'): """List the contents of a widget, by default the current widget.""" widget = self.document.resolveWidgetPath(self.currentwidget, where) children = widget.childnames if len(children) == 0: print('%30s' % _('No children found')) else: # output format name, type for name in children: w = widget.getChild(name) print('%10s %10s %30s' % (name, w.typename, w.userdescription)) def Get(self, var): """Get the value of a setting.""" return self.document.resolveSettingPath(self.currentwidget, var).val def GetChildren(self, where='.'): """Return a list of widgets which are children of the widget of the path given.""" return list( self.document.resolveWidgetPath(self.currentwidget, where).childnames ) def GetColormap(self, name, invert=False, nvals=256): """Return an array of [red,green,blue,alpha] values representing the colormap with the name given. Each return value is between 0 and 255. The number of values to return is given by nvals """ cmap = self.document.evaluate.getColormap(name, invert) return utils.getColormapArray(cmap, nvals) def GetDatasets(self): """Return a list of names of datasets.""" return sorted(self.document.data) def ResolveReference(self, setn): """If the setting is set to a reference, follow the chain of references to return the absolute path to the real setting. If it is not a reference return None. """ setn = self.document.resolveSettingPath(self.currentwidget, setn) if setn.isReference(): real = setn.getReference().resolve(setn) return real.path else: return None def Save(self, filename, mode='vsz'): """Save the state to a file. mode can be: 'vsz': standard veusz text format 'hdf5': HDF5 format """ self.document.save(filename, mode) def Set(self, setting_path, val): """Set the value of a setting.""" setn = self.document.resolveSettingPath(self.currentwidget, setting_path) op = operations.OperationSettingSet(setn, val) self.document.applyOperation(op) if self.verbose: print( _("Set setting '%s' to %s") % (setting_path, repr(setn.get())) ) def SetToReference(self, setting_path, val): """Set setting to a reference value.""" setn = self.document.resolveSettingPath(self.currentwidget, setting_path) op = operations.OperationSettingSet(setn, setting.Reference(val)) self.document.applyOperation(op) if self.verbose: print( _( "Set setting '%s' to %s") % (setting_path, repr(setn.get())) ) def SetData(self, name, val, symerr=None, negerr=None, poserr=None): """Create/set dataset name with values (and optionally errors).""" data = datasets.Dataset(val, symerr, negerr, poserr) op = operations.OperationDatasetSet(name, data) self.document.applyOperation(op) if self.verbose: print( _("Set dataset '%s':\n" " Values = %s\n" " Symmetric errors = %s\n" " Negative errors = %s\n" " Positive errors = %s") % ( name, str(data.data), str(data.serr), str(data.nerr), str(data.perr)) ) def SetDataDateTime(self, name, vals): """Set datetime dataset to be values given. vals is a list of python datetime objects """ v = [utils.datetimeToFloat(x) for x in vals] ds = datasets.DatasetDateTime(v) op = operations.OperationDatasetSet(name, ds) self.document.applyOperation(op) if self.verbose: print( _("Set dataset '%s':\n" " Values = %s") % ( name, str(ds.data)) ) def SetDataExpression(self, name, val, symerr=None, negerr=None, poserr=None, linked=False, parametric=None): """Create a dataset based on text expressions. Expressions are functions of existing datasets. If evaluating the expression 'y*10' in negerr, then the negerrs of dataset y are used, and so on. To access a specific part of the dataset y, the suffixes _data, _serr, _perr, and _nerr can be appended. If linked is True then the expressions are reevaluated if the document is modified parametric: tuple of (minval, maxval, numitems) for creating parametric datasets. t set to this range when evaluating expressions. """ expr = {'data': val, 'serr': symerr, 'nerr': negerr, 'perr': poserr} op = operations.OperationDatasetCreateExpression(name, expr, linked, parametric=parametric) data = self.document.applyOperation(op) if self.verbose: print( _("Set dataset '%s' based on expression:\n" " Values = %s\n" " Symmetric errors = %s\n" " Negative errors = %s\n" " Positive errors = %s") % ( name, str(data.data), str(data.serr), str(data.nerr), str(data.perr)) ) if parametric: print(_(" Where t goes form %g:%g in %i steps") % parametric) print(_(" linked to expression = %s") % repr(linked)) def SetDataND(self, name, val): """Set n-dimensional dataset name with values.""" data = datasets.DatasetND(val) op = operations.OperationDatasetSet(name, data) self.document.applyOperation(op) if self.verbose: print( _("Set dataset (nD) '%s':\n" " Values = %s\n") % ( name, str(data.data)) ) def SetDataRange(self, name, numsteps, val, symerr=None, negerr=None, poserr=None, linked=False): """Create dataset based on ranges of values, e.g. 1 to 10 in 10 steps name: name of dataset numsteps: number of steps to create val: range in form of tuple (minval, maxval) symerr, negerr & poserr: ranges for errors (optional) """ parts = {'data': val, 'serr': symerr, 'nerr': negerr, 'perr': poserr} op = operations.OperationDatasetCreateRange(name, numsteps, parts, linked) self.document.applyOperation(op) if self.verbose: print( _("Set dataset '%s' based on range:\n" " Number of steps = %i\n" " Range of data = %s\n" " Range of symmetric error = %s\n" " Range of positive error = %s\n" " Range of negative error = %s") % ( name, numsteps, repr(val), repr(symerr), repr(poserr), repr(negerr)) ) def SetData2DExpression(self, name, expr, linked=False): """Create a 2D dataset based on expressions name is the new dataset name expr is an expression which should return a 2D array linked specifies whether to permanently link the dataset to the expressions """ op = operations.OperationDataset2DCreateExpression(name, expr, linked) data = self.document.applyOperation(op) if self.verbose: print( _("Set 2D dataset '%s' based on expressions\n" " expression = %s\n" " linked to expression = %s\n" " Made a dataset (%i x %i)") % ( name, repr(expr), repr(linked), data.data.shape[0], data.data.shape[1]) ) def SetData2DExpressionXYZ(self, name, xexpr, yexpr, zexpr, linked=False): """Create a 2D dataset based on expressions in x, y and z xexpr is an expression which expands to an equally-spaced grid of x coordinates yexpr expands to equally spaced y coordinates zexpr expands to z coordinates. linked specifies whether to permanently link the dataset to the expressions """ op = operations.OperationDataset2DCreateExpressionXYZ(name, xexpr, yexpr, zexpr, linked) data = self.document.applyOperation(op) if self.verbose: print( _("Made 2D dataset '%s' based on expressions:\n" " X expression = %s\n" " Y expression = %s\n" " Z expression = %s\n" " is linked to expression = %s\n" " Shape (%i x %i)") % ( name, repr(xexpr), repr(yexpr), repr(zexpr), repr(linked), data.data.shape[0], data.data.shape[1]) ) def SetData2DXYFunc(self, name, xstep, ystep, expr, linked=False): """Create a 2D dataset based on expressions of a range of x and y xstep is a tuple(min, max, step) ystep is a tuple(min, max, step) expr is an expression of x and y linked specifies whether to permanently link the dataset to the expressions """ op = operations.OperationDataset2DXYFunc(name, xstep, ystep, expr, linked) data = self.document.applyOperation(op) if self.verbose: print( _("Set 2D dataset '%s' based on function of x and y\n" " X steps = %s\n" " Y steps = %s\n" " Expression = %s\n" " linked to expression = %s\n" " Made a dataset (%i x %i)") % ( name, repr(xstep), repr(ystep), repr(expr), repr(linked), data.data.shape[0], data.data.shape[1]) ) def SetData2D(self, name, data, xrange=None, yrange=None, xedge=None, yedge=None, xcent=None, ycent=None): """Create a 2D dataset. name: name of dataset data: 2d array xrange: optional tuple with X range of data (min, max) yrange: optional tuple with Y range of data (min, max) xedge: x values for grid (instead of rangex) yedge: y values for grid (instead of rangey) xcent: x values for pixel centres (instead of rangex) ycent: y values for pixel centres (instead of rangey) """ data = N.array(data) if ( (xedge is not None and not utils.checkAscending(xedge)) or (yedge is not None and not utils.checkAscending(yedge)) ): raise ValueError("xedge and yedge must be ascending, if given") if ( (xcent is not None and not utils.checkAscending(xcent)) or (ycent is not None and not utils.checkAscending(ycent)) ): raise ValueError("xcent and ycent must be ascending, if given") if ( (xedge is not None and len(xedge) != data.shape[1]+1) or (yedge is not None and len(yedge) != data.shape[0]+1) ): raise ValueError("xedge and yedge lengths must be data shape+1") if ( (xcent is not None and len(xcent) != data.shape[1]) or (ycent is not None and len(ycent) != data.shape[0]) ): raise ValueError("xcent and ycent lengths must be data shape") data = datasets.Dataset2D(data, xrange=xrange, yrange=yrange, xedge=xedge, yedge=yedge, xcent=xcent, ycent=ycent) op = operations.OperationDatasetSet(name, data) self.document.applyOperation(op) if self.verbose: print(_("Set 2d dataset '%s'") % name) def SetDataText(self, name, val): """Create a text dataset.""" data = datasets.DatasetText(val) op = operations.OperationDatasetSet(name, data) self.document.applyOperation(op) if self.verbose: print( _("Set text dataset '%s'\n" "Values = %s") % ( name, repr(data.data)) ) def GetData(self, name): """Return the data with the name. For a 1D dataset, returns a tuple (None if not defined) (data, serr, nerr, perr) For a 2D dataset, returns (data, xrange, yrange) For an nD dataset returns data array For a text dataset, return a list of text For a date dataset, return a list of python datetime objects Return copies, so that the original data can't be indirectly modified """ d = self.document.getData(name) if d.displaytype == 'text': return d.data[:] elif d.displaytype == 'date': return [utils.floatToDateTime(x) for x in d.data] elif d.dimensions == 2: return (d.data.copy(), d.xrange, d.yrange) elif d.dimensions == -1: return d.data.copy() else: data = serr = nerr = perr = None if d.data is not None: data = d.data.copy() if d.serr is not None: serr = d.serr.copy() if d.nerr is not None: nerr = d.nerr.copy() if d.perr is not None: perr = d.perr.copy() return (data, serr, nerr, perr) def GetDataType(self, name): """Return the type of dataset. Returns None if no dataset. For a 1D dataset, returns '1d' For a 2D dataset, returns '2d' For a text dataset, returns 'text' For a datetime dataset, returns 'datetime' """ try: d = self.document.getData(name) except KeyError: return None if d.displaytype == 'text': return 'text' elif d.displaytype == 'date': return 'datetime' elif d.dimensions == 2: return '2d' else: return '1d' def ReloadData(self): """Reload any linked datasets. Returned is a tuple (datasets, errors) where datasets is a list of datasets read errors is a dict of the datasets with the number of errors while converting the data """ return self.document.reloadLinkedDatasets() def Action(self, action, widget='.'): """Performs action on current widget.""" w = self.document.resolveWidgetPath(self.currentwidget, widget) # run action w.getAction(action).function() def Print(self): """Print document.""" export.printDialog(None, self.document) def Export(self, filename, color=True, page=[0], dpi=100, antialias=True, quality=85, backcolor='#ffffff00', pdfdpi=150, svgdpi=96, svgtextastext=False): """Export plot to filename. color is True or False if color is requested in output file page is a list of page numbers to export dpi is the number of dots per inch for bitmap output files antialias antialiases output if True quality is a quality parameter for jpeg output backcolor is the background color for bitmap files, which is a name or a #RRGGBBAA value (red, green, blue, alpha) pdfdpi is the dpi to use when exporting eps or pdf files svgdpi is the dpi to use when exporting svg files svgtextastext: write text in SVG as text, rather than curves """ # compatibility where page was a single number try: pages = [p for p in page] except TypeError: pages = [page] e = export.Export( self.document, filename, pages, color=color, bitmapdpi=dpi, antialias=antialias, quality=quality, backcolor=backcolor, pdfdpi=pdfdpi, svgdpi=svgdpi, svgtextastext=svgtextastext) e.export() def Rename(self, widget, newname): """Rename the widget with the path given to the new name. eg Rename('graph1/xy1', 'scatter') This function does not move widgets.""" w = self.document.resolveWidgetPath(self.currentwidget, widget) op = operations.OperationWidgetRename(w, newname) self.document.applyOperation(op) def NodeType(self, path): """This function treats the set of objects in the widget and setting tree as a set of nodes. Returns type of node given. Return values are: 'widget', 'settings' or 'setting' """ item = self.document.resolvePath(self.currentwidget, path) if item.iswidget: return 'widget' elif item.issettings: return 'settinggroup' else: return 'setting' def NodeChildren(self, path, types='all'): """This function treats the set of objects in the widget and setting tree as a set of nodes. Returns a list of the names of the children of this node.""" item = self.document.resolvePath(self.currentwidget, path) out = [] if item.iswidget: if types == 'all' or types == 'widget': out += item.childnames if types == 'all' or types == 'settinggroup': out += [s.name for s in item.settings.getSettingsList()] if types == 'all' or types == 'setting': out += [s.name for s in item.settings.getSettingList()] elif item.issettings: if types == 'all' or types == 'settinggroup': out += [s.name for s in item.getSettingsList()] if types == 'all' or types == 'setting': out += [s.name for s in item.getSettingList()] return out def WidgetType(self, path): """Get the Veusz widget type for a widget with path given. Raises a ValueError if the path doesn't point to a widget.""" item = self.document.resolvePath(self.currentwidget, path) if item.iswidget: return item.typename else: raise ValueError("Path '%s' is not a widget" % path) def SettingType(self, setting_path): """Get the type of setting (a string) for the path given. Raise a ValueError if path is not a setting """ setn = self.document.resolveSettingPath(self.currentwidget, setting_path) return setn.typename def TagDatasets(self, tag, datasets): """Apply tag to list of datasets.""" op = operations.OperationDataTag(tag, datasets) self.document.applyOperation(op) if self.verbose: print(_("Applied tag %s to datasets %s") % ( tag, ' '.join(datasets))) def FilterDatasets(self, filterexpr, dataset_list, prefix="", suffix="", invert=False, replaceblanks=False): """Apply filter expression to list of datasets. filterexpr: input filter expression dataset_list: list of input dataset names prefix, suffix: output prefix/suffix to add to names (one must be set) invert: invert results of filter expression replaceblanks: replace filtered values with nan/blank in output. """ op = operations.OperationDatasetsFilter( filterexpr, dataset_list, prefix=prefix, suffix=suffix, invert=invert, replaceblanks=replaceblanks) self.document.applyOperation(op) if self.verbose: print( _('Filtered datasets %s using expression %s. ' 'Output prefix=%s, suffix=%s') % ( dataset_list, filterexpr, prefix, suffix) ) ���������������������������������������������������������������������������������������veusz-3.0.1/veusz/document/painthelper.py�����������������������������������������������������������0000664�0001750�0001750�00000030003�13302477313�020167� 0����������������������������������������������������������������������������������������������������ustar �jss�����������������������������jss�����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2011 Jeremy S. Sanders # Email: Jeremy Sanders # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ############################################################################## """Helper for doing the plotting of the document. """ from __future__ import division from .. import qtall as qt4 from .. import setting from .. import utils try: from ..helpers.recordpaint import RecordPaintDevice except ImportError: # fallback to this if we don't get the native recorded def RecordPaintDevice(width, height, dpix, dpiy): return qt4.QPicture() class DrawState(object): """Each widget plotted has a recorded state in this object.""" def __init__(self, widget, bounds, clip, helper): """Initialise state for widget. bounds: tuple of (x1, y1, x2, y2) clip: if clipping should be done, another tuple.""" self.widget = widget self.record = RecordPaintDevice( helper.pagesize[0], helper.pagesize[1], helper.dpi[0], helper.dpi[1]) self.bounds = bounds self.clip = clip # controlgraphs belonging to widget self.cgis = [] # list of child widgets states self.children = [] class PainterRoot(qt4.QPainter): """Base class for painting of widgets.""" def updateMetaData(self, helper): """Update metadeta from helper These values are used during plotting.""" self.helper = helper self.document = helper.document self.colors = self.document.evaluate.colors self.scaling = helper.scaling self.pixperpt = helper.pixperpt self.dpi = helper.dpi[1] self.pagesize = helper.pagesize self.maxdim = max(*self.pagesize) self.textrects = helper.textrects def docColor(self, name): """Return color from document.""" return self.colors.get(name) def docColorAuto(self, index): """Return automatic doc color given index.""" return self.colors.getIndex(index+1) def __enter__(self): pass def __exit__(self, exc_type, exc_value, traceback): pass class DirectPainter(PainterRoot): """Painter class for direct painting with PaintHelper below. """ class RecordPainter(PainterRoot): """This is the painter subclass for rendering in Veusz, which keeps track of which widget is being painted.""" def __init__(self, widget, outdev): PainterRoot.__init__(self, outdev) self.widget = widget def __enter__(self): #print ' '*len(self.helper.widgetstack), self.widget self.helper.widgetstack.append(self.widget) def __exit__(self, exc_type, exc_value, traceback): self.helper.widgetstack.pop() class PaintHelper(object): """Helper used when painting widgets. Designed to be used for a particular page. Provides a QPainter/RecordPainter to each widget for plotting. Records the controlgraphs for each widget. Holds the scaling, dpi and size of the page. """ def __init__(self, document, pagesize, scaling=1, devicepixelratio=1, dpi=(100, 100), directpaint=None): """ pagesize: tuple (pixelw, pixelh), which can be float. This is the page size in the coordinates presented to graph drawing. scaling: scaling from graph coordinates to native coordinates devicepixelratio: high DPI scaling factor from logical to native pixels dpi: tuple of X and Y dpi for graph coordinates directpaint: use this painter directly, rather than using RecordPainter to store each widget painting """ self.document = document self.dpi = dpi self.scaling = scaling # scaling factor, excluding high-DPI factor (for controlgraphs) self.cgscale = scaling / devicepixelratio self.devicepixelratio = devicepixelratio self.pixperpt = self.dpi[1] / 72. # page size in native pixels (without default zoom) self.rawpagesize = max(pagesize[0], 1), max(pagesize[1], 1) # page size in graph pixels self.pagesize = self.rawpagesize[0]/scaling, self.rawpagesize[1]/scaling # keep track of states of all widgets # maps (widget, layer) to DrawState self.states = {} # axis to plotter mappings self.axisplottermap = {} self.plotteraxismap = {} # whether to directly render to a painter or make new layers self.directpaint = directpaint # state for root widget self.rootstate = None # keep track of last widget being plotted self.widgetstack = [] # current index for each plotter (if wanting automatic colors) self.autoplottercount = 0 self.autoplottermap = {} # to avoid overlapping text self.textrects = utils.RectangleOverlapTester() @property def maxdim(self): """Return maximum page dimension (using PaintHelper's DPI).""" return max(*self.pagesize) def sizeAtDpi(self, dpi): """Return a tuple size for the page given an output device dpi.""" return ( int(self.pagesize[0]/self.dpi[0] * dpi), int(self.pagesize[1]/self.dpi[1] * dpi) ) def painter(self, widget, bounds, clip=None, layer=None): """Return a painter for use when drawing the widget. widget: widget object bounds: tuple (x1, y1, x2, y2) of widget bounds clip: a QRectF, if set layer: layer to plot widget, or None to get next automatically """ # automatically add a layer if not given if layer is None: layer = 0 while (widget, layer) in self.states: layer += 1 s = self.states[(widget, layer)] = DrawState(widget, bounds, clip, self) if self.widgetstack: self.states[(self.widgetstack[-1], 0)].children.append(s) else: self.rootstate = s if self.directpaint is None: # save to multiple recorded layers p = RecordPainter(widget, s.record) else: # only paint to one output painter p = self.directpaint # make sure we get the same state each time p.restore() p.save() if clip is not None: # have to clip before scaling, avoiding a qt bug where the clipping # seems to happen in the wrong place p.setClipRect(qt4.QRectF( clip.topLeft()*self.scaling, clip.bottomRight()*self.scaling)) # scale (used for zooming) if self.scaling != 1: p.scale(self.scaling, self.scaling) p.updateMetaData(self) return p def setControlGraph(self, widget, cgis): """Records the control graph list for the widget given.""" self.states[(widget,0)].cgis = cgis def getControlGraph(self, widget): """Return control graph for widget (or None).""" try: return self.states[(widget,0)].cgis except KeyError: return None def renderToPainter(self, painter): """Render saved output to painter. """ self._renderState(self.rootstate, painter) def _renderState(self, state, painter, indent=0): """Render state to painter.""" painter.save() state.record.play(painter) painter.restore() for child in state.children: #print ' '*indent, child.widget self._renderState(child, painter, indent=indent+1) def identifyWidgetAtPoint(self, x, y, antialias=True): """What widget has drawn at the point x,y? Returns the widget drawn last on the point, or None if it is an empty part of the page. root is the root widget to recurse from if antialias is true, do test for antialiased drawing """ # reset text rectangles for painting so not to lose text self.textrects.reset() # convert screen to bitmap coordinates x *= self.devicepixelratio y *= self.devicepixelratio # make a small image filled with a specific color box = 3 specialcolor = qt4.QColor(254, 255, 254) origpix = qt4.QPixmap(2*box+1, 2*box+1) origpix.fill(specialcolor) origimg = origpix.toImage() # store most recent widget here lastwidget = [None] def rendernextstate(state): """Recursively draw painter. Checks whether drawing a widgetchanges the small image around the point given. """ pixmap = qt4.QPixmap(origpix) painter = qt4.QPainter(pixmap) painter.setRenderHint(qt4.QPainter.Antialiasing, antialias) painter.setRenderHint(qt4.QPainter.TextAntialiasing, antialias) # this makes the small image draw from x-box->x+box, y-box->y+box # translate would get overriden by coordinate system playback painter.setWindow(x-box,y-box,box*2+1,box*2+1) state.record.play(painter) painter.end() newimg = pixmap.toImage() if newimg != origimg: lastwidget[0] = state.widget for child in state.children: rendernextstate(child) rendernextstate(self.rootstate) widget = lastwidget[0] # need to re-render 3d scene to look for clicks if widget and widget.typename == 'scene3d': bounds = [0,0,100,100] for w in self.states: if w[0] is widget: bounds = self.states[w].bounds return widget.identifyWidgetAtPoint(self, bounds, self.scaling, x, y) else: return widget def pointInWidgetBounds(self, x, y, widgettype): """Which graph widget plots at point x,y? Recurse from widget root widgettype is the class of widget to get """ widget = [None] def recursestate(state): if isinstance(state.widget, widgettype): b = state.bounds if x >= b[0] and y >= b[1] and x <= b[2] and y <= b[3]: # most recent widget drawing on point widget[0] = state.widget for child in state.children: recursestate(child) recursestate(self.rootstate) return widget[0] def widgetBounds(self, widget): """Return bounds of widget.""" return self.states[(widget,0)].bounds def widgetBoundsIterator(self, widgettype=None): """Returns bounds for each widget. Set widgettype to be a widget type to filter returns Yields (widget, bounds) """ # this is a recursive algorithm turned into an iterative one # which makes creation of a generator easier stack = [self.rootstate] while stack: state = stack[0] if widgettype is None or isinstance(state.widget, widgettype): yield state.widget, state.bounds # remove the widget itself from the stack and insert children stack = state.children + stack[1:] def autoColorIndex(self, key): """Return automatic color index for key given.""" if key not in self.autoplottermap: self.autoplottermap[key] = self.autoplottercount self.autoplottercount += 1 return self.autoplottermap[key] �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������veusz-3.0.1/veusz/document/export.py����������������������������������������������������������������0000664�0001750�0001750�00000037614�13322660165�017215� 0����������������������������������������������������������������������������������������������������ustar �jss�����������������������������jss�����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2011 Jeremy S. Sanders # Email: Jeremy Sanders # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ############################################################################## """Routines to export the document.""" from __future__ import division import os.path import random import math import codecs import re import sys import subprocess from ..compat import crange from .. import qtall as qt4 from .. import setting from .. import utils try: from . import emf_export hasemf = True except ImportError: hasemf = False from . import svg_export from . import selftest_export from . import painthelper # 1m in inch m_inch = 39.370079 def _(text, disambiguation=None, context="Export"): """Translate text.""" return qt4.QCoreApplication.translate(context, text, disambiguation) def printPages(doc, printer, pages, scaling=1., antialias=False, setsizes=False): """Print onto printing device. Returns list of page sizes setsizes: Set page size on printer to page sizes """ if not pages: return dpi = (printer.logicalDpiX(), printer.logicalDpiY()) def getUpdateSize(page): size = doc.pageSize(page, dpi=dpi, integer=False) if setsizes: # update paper size on printer sizeinchx, sizeinchy = size[0]/dpi[0], size[1]/dpi[1] pagesize = qt4.QPageSize( qt4.QSizeF(sizeinchx, sizeinchy), qt4.QPageSize.Inch) layout = qt4.QPageLayout( pagesize, qt4.QPageLayout.Portrait, qt4.QMarginsF()) printer.setPageLayout(layout) return size size = getUpdateSize(pages[0]) painter = painthelper.DirectPainter(printer) if antialias: painter.setRenderHint(qt4.QPainter.Antialiasing, True) painter.setRenderHint(qt4.QPainter.TextAntialiasing, True) # This all assumes that only pages can go into the root widget for count, page in enumerate(pages): painter.save() painter.setClipRect(qt4.QRectF( qt4.QPointF(0,0), qt4.QPointF(*size))) helper = painthelper.PaintHelper( doc, size, dpi=dpi, directpaint=painter) doc.paintTo(helper, page) painter.restore() # start new pages between each page if count < len(pages)-1: # set page size before newPage! size = getUpdateSize(pages[count+1]) printer.newPage() painter.end() class Export(object): """Class to do the document exporting. This is split from document to make that class cleaner. """ # whether ghostscript has been searched for gs_searched = False # its path if it exists gs_exe = None # map extensions to ghostscript devices gs_dev = None @classmethod def searchGhostscript(klass): """Find location of Ghostscript executable.""" if not klass.gs_searched: # test for existence of ghostscript gs_exe = None gs = setting.settingdb["external_ghostscript"] if gs: if os.path.isfile(gs) and os.access(gs, os.X_OK): gs_exe = gs else: if sys.platform == "win32": # look for ghostscript as 64 and 32 bit versions gs_exe = utils.findOnPath("gswin64c.exe") if not gs_exe: gs_exe = utils.findOnPath("gswin32c.exe") else: # unix tends to call it just gs gs_exe = utils.findOnPath("gs") klass.gs_dev = dev = {} if gs_exe: try: # check output devices contain # ps2write/eps2write or pswrite/epswrite popen = subprocess.Popen( [gs_exe, '-h'], stdout=subprocess.PIPE, universal_newlines=True) text = popen.stdout.read() if re.search(r'\beps2write\b', text): dev['.eps'] = 'eps2write' elif re.search(r'\bepswrite\b', text): dev['.eps'] = 'epswrite' if re.search(r'\bps2write\b', text): dev['.ps'] = 'ps2write' elif re.search(r'\bpswrite\b', text): dev['.ps'] = 'pswrite' except Exception as e: pass else: klass.gs_exe = gs_exe klass.gs_searched = True @classmethod def getFormats(klass): """Return list of formats in form of tuples of extension and description.""" formats = [ (["bmp"], _("Windows bitmap")), (["jpg"], _("Jpeg bitmap")), (["pdf"], _("Portable Document Format")), (["png"], _("Portable Network Graphics")), (["svg"], _("Scalable Vector Graphics")), (["tiff"], _("Tagged Image File Format bitmap")), (["xpm"], _("X Pixmap")), ] if hasemf: formats.append( (["emf"], _("Windows Enhanced Metafile")) ) klass.searchGhostscript() if '.eps' in klass.gs_dev: formats.append((["eps"], _("Encapsulated Postscript"))) if '.ps' in klass.gs_dev: formats.append((["ps"], _("Postscript"))) formats.sort() return formats def __init__(self, doc, filename, pagenumbers, color=True, bitmapdpi=100, antialias=True, quality=85, backcolor='#ffffff00', pdfdpi=150, svgdpi=96, svgtextastext=False): """Initialise export class. Parameters are: doc: document to write filename: output filename pagenumbers: list of pages to export color: use color or try to use monochrome bitmapdpi: assume this dpi value when writing images antialias: antialias text and lines when writing bitmaps quality: compression factor for bitmaps backcolor: background color default for bitmaps (default transparent). pdfdpi: dpi for pdf and eps files svgdpi: dpi for svg files svgtextastext: write text in SVG as text, rather than curves """ self.doc = doc self.filename = filename self.pagenumbers = pagenumbers self.color = color self.bitmapdpi = bitmapdpi self.antialias = antialias self.quality = quality self.backcolor = backcolor self.pdfdpi = pdfdpi self.svgdpi = svgdpi self.svgtextastext = svgtextastext def export(self): """Export the figure to the filename.""" ext = os.path.splitext(self.filename)[1].lower() if ext == '.pdf': self.exportPDF(self.filename) elif ext in ('.eps', '.ps'): self.exportPS(self.filename, ext) elif ext in ('.png', '.jpg', '.jpeg', '.bmp', '.tiff', '.xpm'): self.exportBitmap(self.filename, ext) elif ext == '.svg': self.exportSVG(self.filename) elif ext == '.selftest': self.exportSelfTest(self.filename) elif ext == '.pic': self.exportPIC(self.filename) elif ext == '.emf' and hasemf: self.exportEMF(self.filename) else: raise RuntimeError("File type '%s' not supported" % ext) def renderPage(self, page, size, dpi, painter): """Render page using paint helper to painter. This first renders to the helper, then to the painter """ helper = painthelper.PaintHelper(self.doc, size, dpi=dpi, directpaint=painter) painter.setClipRect( qt4.QRectF( qt4.QPointF(0,0), qt4.QPointF(*size)) ) painter.save() self.doc.paintTo(helper, page) painter.restore() painter.end() def getSinglePage(self): """Check single number of pages or throw exception, else return page number.""" if len(self.pagenumbers) != 1: raise RuntimeError( 'Can only export a single page in this format') return self.pagenumbers[0] def exportBitmap(self, filename, ext): """Export to a bitmap format.""" fmt = ext.lstrip('.') # setFormat() doesn't want the leading '.' if fmt == 'jpeg': fmt = 'jpg' page = self.getSinglePage() # get size for bitmap's dpi dpi = self.bitmapdpi size = self.doc.pageSize(page, dpi=(dpi,dpi)) # create real output image backqcolor = self.doc.evaluate.colors.get(self.backcolor) if fmt == 'png': # transparent output image = qt4.QImage(size[0], size[1], qt4.QImage.Format_ARGB32_Premultiplied) else: # non transparent output image = qt4.QImage(size[0], size[1], qt4.QImage.Format_RGB32) backqcolor.setAlpha(255) image.setDotsPerMeterX(dpi*m_inch) image.setDotsPerMeterY(dpi*m_inch) if backqcolor.alpha() == 0: image.fill(qt4.qRgba(0,0,0,0)) else: image.fill(backqcolor.rgb()) # paint to the image painter = painthelper.DirectPainter(image) painter.setRenderHint(qt4.QPainter.Antialiasing, self.antialias) painter.setRenderHint(qt4.QPainter.TextAntialiasing, self.antialias) self.renderPage(page, size, (dpi,dpi), painter) # write image to disk writer = qt4.QImageWriter() writer.setFormat(fmt.encode('ascii')) writer.setFileName(filename) # enable LZW compression for TIFFs writer.setCompression(1) try: # try to enable optimal JPEG compression using new # options added in Qt 5.5 writer.setOptimizedWrite(True) writer.setProgressiveScanWrite(True) except AttributeError: pass if fmt == 'png': # min quality for png as it makes no difference to output # and makes file size smaller writer.setQuality(0) else: writer.setQuality(self.quality) writer.write(image) def exportPDF(self, filename): """Export to PDF format.""" # setup printer with requested parameters printer = qt4.QPrinter() printer.setResolution(self.pdfdpi) printer.setFullPage(True) printer.setColorMode( qt4.QPrinter.Color if self.color else qt4.QPrinter.GrayScale) printer.setOutputFormat(qt4.QPrinter.PdfFormat) printer.setOutputFileName(filename) printer.setCreator('Veusz %s' % utils.version()) printPages(self.doc, printer, self.pagenumbers, setsizes=True) def exportPS(self, filename, ext): """Export to PS/EPS via conversion with Ghostscript. ext == '.eps' or '.ps' """ if len(self.pagenumbers) != 1 and ext == '.eps': raise RuntimeError( 'Only single pages allowed for .eps. Use .ps instead.') self.searchGhostscript() if not self.gs_exe: raise RuntimeError("Cannot write Postscript with Ghostscript") # write to pdf file first tmpfilepdf = "%s.tmp.%i.pdf" % ( filename, random.randint(0,1000000)) tmpfileps = "%s.tmp.%i%s" % ( filename, random.randint(0,1000000), ext) self.exportPDF(tmpfilepdf) # run ghostscript to covert from pdf to postscript cmd = [ self.gs_exe, '-q', '-dNOCACHE', '-dNOPAUSE', '-dBATCH', '-dSAFER', '-sDEVICE=%s' % self.gs_dev[ext], '-sOutputFile=%s' % tmpfileps, tmpfilepdf ] try: subprocess.check_call(cmd) except Exception as e: raise RuntimeError("Could not run ghostscript: %s" % str(e)) if not os.path.isfile(tmpfileps): raise RuntimeError("Ghostscript failed to create %s" % tmpfileps) os.remove(tmpfilepdf) try: os.remove(filename) except OSError: pass os.rename(tmpfileps, filename) def exportSVG(self, filename): """Export document as SVG""" page = self.getSinglePage() scale = 0.1 sdpi = self.svgdpi/scale size = self.doc.pageSize( page, dpi=(sdpi,sdpi), integer=False) with codecs.open(filename, 'w', 'utf-8') as f: paintdev = svg_export.SVGPaintDevice( f, size[0]/sdpi, size[1]/sdpi, writetextastext=self.svgtextastext, dpi=self.svgdpi, scale=scale) painter = painthelper.DirectPainter(paintdev) self.renderPage(page, size, (sdpi,sdpi), painter) def exportSelfTest(self, filename): """Export document for testing""" page = self.getSinglePage() dpi = 90 size = width, height = self.doc.pageSize( page, dpi=(dpi,dpi), integer=False) with open(filename, 'w') as fout: paintdev = selftest_export.SelfTestPaintDevice( fout, width/dpi, height/dpi, dpi=dpi) painter = painthelper.DirectPainter(paintdev) self.renderPage(page, size, (dpi,dpi), painter) def exportPIC(self, filename): """Export document as Qt PIC""" page = self.getSinglePage() pic = qt4.QPicture() painter = painthelper.DirectPainter(pic) dpi = (pic.logicalDpiX(), pic.logicalDpiY()) size = self.doc.pageSize(page, dpi=dpi) self.renderPage(page, size, dpi, painter) pic.save(filename) def exportEMF(self, filename): """Export document as EMF.""" page = self.getSinglePage() dpi = 90. size = self.doc.pageSize(page, dpi=(dpi,dpi), integer=False) paintdev = emf_export.EMFPaintDevice(size[0]/dpi, size[1]/dpi, dpi=dpi) painter = painthelper.DirectPainter(paintdev) self.renderPage(page, size, (dpi,dpi), painter) paintdev.paintEngine().saveFile(filename) def printDialog(parentwindow, document, filename=None): """Open a print dialog and print document.""" if document.getNumberPages() == 0: qt4.QMessageBox.warning( parentwindow, _("Error - Veusz"), _("No pages to print")) return prnt = qt4.QPrinter(qt4.QPrinter.HighResolution) prnt.setColorMode(qt4.QPrinter.Color) prnt.setCreator(_('Veusz %s') % utils.version()) if filename: prnt.setDocName(filename) dialog = qt4.QPrintDialog(prnt, parentwindow) dialog.setMinMax(1, document.getNumberPages()) if dialog.exec_(): # get page range if dialog.printRange() == qt4.QAbstractPrintDialog.PageRange: # page range minval, maxval = dialog.fromPage(), dialog.toPage() else: # all pages minval, maxval = 1, document.getNumberPages() # pages are relative to zero minval -= 1 maxval -= 1 # reverse or forward order if prnt.pageOrder() == qt4.QPrinter.FirstPageFirst: pages = list(crange(minval, maxval+1)) else: pages = list(crange(maxval, minval-1, -1)) # if more copies are requested pages *= prnt.copyCount() # do the printing printPages(document, prnt, pages) ��������������������������������������������������������������������������������������������������������������������veusz-3.0.1/veusz/document/operations.py������������������������������������������������������������0000664�0001750�0001750�00000122447�13165473625�020066� 0����������������������������������������������������������������������������������������������������ustar �jss�����������������������������jss�����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2006 Jeremy S. Sanders # Email: Jeremy Sanders # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ############################################################################### """Represents atomic operations to take place on a document which can be undone. Rather than the document modified directly, this interface should be used. Operations should be passed to the document to be enacted with applyOperation Each operation provides do(document) and undo(document) methods. Operations store paths to objects to be modified rather than object references because some operations cannot restore references (e.g. add object) """ from __future__ import division, print_function import os.path import io import numpy as N from ..compat import czip, crange, citems, cbasestr from . import widgetfactory from .. import datasets from .. import plugins from .. import qtall as qt4 def _(text, disambiguation=None, context="Operations"): """Translate text.""" return qt4.QCoreApplication.translate(context, text, disambiguation) ############################################################################### # Setting operations class Operation(object): """Root class for operations.""" descr = 'REPLACE THIS' def do(self, document): """Apply operation to document.""" def undo(self, document): """Undo operation.""" class OperationSettingSet(Operation): """Set a variable to a value.""" descr = _('change setting') def __init__(self, setting, value): """Set the setting to value. Setting may be a widget path """ if isinstance(setting, cbasestr): self.settingpath = setting else: self.settingpath = setting.path self.value = value def do(self, document): """Apply setting variable.""" setting = document.resolveSettingPath(None, self.settingpath) if setting.isReference(): self.oldvalue = setting.getReference() else: self.oldvalue = setting.get() setting.set(self.value) def undo(self, document): """Return old value back...""" setting = document.resolveSettingPath(None, self.settingpath) setting.set(self.oldvalue) class OperationSettingPropagate(Operation): """Propagate setting to other widgets.""" descr = _('propagate setting') def __init__(self, setting, widgetname = None, root = None, maxlevels = -1): """Take the setting given, and propagate it to other widgets, according to the parameters here. If widgetname is given then only propagate it to widgets with the name given. widgets are located from the widget given (root if not set) Up to maxlevels levels of widgets are changed (<0 means infinite) """ self.val = setting.val self.widgetname = widgetname if root: self.rootpath = root.path else: self.rootpath = None self.maxlevels = maxlevels # work out path of setting relative to widget path = [] s = setting while not s.iswidget: path.insert(0, s.name) s = s.parent self.setpath = path[1:] self.widgettype = s.typename def do(self, document): """Apply the setting propagation.""" # default is root widget if not self.rootpath: root = document.basewidget else: root = document.resolveWidgetPath(None, self.rootpath) # get a list of matching widgets widgetlist = [] self._recursiveGet(root, self.widgetname, self.widgettype, widgetlist, self.maxlevels) self.restorevals = {} # set the settings for the widgets for w in widgetlist: # lookup the setting s = w.settings for i in self.setpath: s = s.get(i) self.restorevals[s.path] = s.val s.set(self.val) def undo(self, document): """Undo all those changes.""" for setpath, setval in citems(self.restorevals): setting = document.resolveSettingPath(None, setpath) setting.set(setval) @staticmethod def _recursiveGet(root, name, typename, outlist, maxlevels): """Add those widgets in root with name and type to outlist. If name or typename are None, then ignore the criterion. maxlevels is the maximum number of levels to check """ if maxlevels != 0: # if levels is not zero, add the children of this root newmaxlevels = maxlevels - 1 for w in root.children: if ( (w.name == name or name is None) and (w.typename == typename or typename is None) ): outlist.append(w) OperationSettingPropagate._recursiveGet( w, name, typename, outlist, newmaxlevels) ############################################################################### # Widget operations class OperationWidgetRename(Operation): """Rename widget.""" descr = _('rename') def __init__(self, widget, newname): """Rename the widget to newname.""" self.widgetpath = widget.path self.newname = newname def do(self, document): """Rename widget.""" widget = document.resolveWidgetPath(None, self.widgetpath) self.oldname = widget.name widget.rename(self.newname) self.newpath = widget.path def undo(self, document): """Undo rename.""" widget = document.resolveWidgetPath(None, self.newpath) widget.rename(self.oldname) class OperationWidgetDelete(Operation): """Delete widget.""" descr = _('delete') def __init__(self, widget): """Delete the widget.""" self.widgetpath = widget.path def do(self, document): """Delete widget.""" self.oldwidget = document.resolveWidgetPath(None, self.widgetpath) oldparent = self.oldwidget.parent self.oldwidget.parent = None self.oldparentpath = oldparent.path self.oldindex = oldparent.children.index(self.oldwidget) oldparent.removeChild(self.oldwidget.name) def undo(self, document): """Restore deleted widget.""" oldparent = document.resolveWidgetPath(None, self.oldparentpath) self.oldwidget.parent = oldparent oldparent.addChild(self.oldwidget, index=self.oldindex) class OperationWidgetsDelete(Operation): """Delete mutliple widget.""" descr = _('delete') def __init__(self, widgets): """Delete the widget.""" self.widgetpaths = [w.path for w in widgets] def do(self, document): """Delete widget.""" # ignore widgets which share ancestry # as deleting the parent deletes the child widgetpaths = list(self.widgetpaths) widgetpaths.sort(key=len) i = 0 while i < len(widgetpaths): wp = widgetpaths[i] for j in crange(i): if wp[:len(widgetpaths[j])+1] == widgetpaths[j]+'/': del widgetpaths[i] break else: i += 1 self.oldwidgets = [] self.oldparentpaths = [] self.oldindexes = [] # delete each widget keeping track of details for path in widgetpaths: self.oldwidgets.append( document.resolveWidgetPath(None, path) ) oldparent = self.oldwidgets[-1].parent self.oldparentpaths.append( oldparent.path ) self.oldindexes.append( oldparent.children.index(self.oldwidgets[-1]) ) oldparent.removeChild(self.oldwidgets[-1].name) def undo(self, document): """Restore deleted widget.""" # put back widgets in reverse order so that indexes are corrent for i in crange(len(self.oldwidgets)-1,-1,-1): oldparent = document.resolveWidgetPath(None, self.oldparentpaths[i]) oldparent.addChild(self.oldwidgets[i], index=self.oldindexes[i]) class OperationWidgetMoveUpDown(Operation): """Move a widget up or down in the hierarchy.""" descr = _('move') def __init__(self, widget, direction): """Move the widget specified up or down in the hierarchy. direction is -1 for 'up' or +1 for 'down' """ self.widgetpath = widget.path self.direction = direction def do(self, document): """Move the widget.""" widget = document.resolveWidgetPath(None, self.widgetpath) parent = widget.parent self.suceeded = parent.moveChild(widget, self.direction) self.newpath = widget.path def undo(self, document): """Move it back.""" if self.suceeded: widget = document.resolveWidgetPath(None, self.newpath) parent = widget.parent parent.moveChild(widget, -self.direction) class OperationWidgetMove(Operation): """Move a widget arbitrarily in the hierarchy.""" descr = _('move') def __init__(self, oldchildpath, newparentpath, newindex): """Move widget with path oldchildpath to be a child of newparentpath and with index newindex.""" self.oldchildpath = oldchildpath self.newparentpath = newparentpath self.newindex = newindex def do(self, document): """Move widget.""" child = document.resolveWidgetPath(None, self.oldchildpath) oldparent = child.parent newparent = document.resolveWidgetPath(None, self.newparentpath) self.oldchildindex = oldparent.children.index(child) self.oldparentpath = oldparent.path self.oldname = None if self.newindex < 0: # convert negative index to normal index self.newindex = len(newparent.children) if oldparent is newparent: # moving within same parent self.movemode = 'sameparent' del oldparent.children[self.oldchildindex] if self.newindex > self.oldchildindex: self.newindex -= 1 oldparent.children.insert(self.newindex, child) else: # moving to different parent self.movemode = 'differentparent' # remove from old parent del oldparent.children[self.oldchildindex] # current names of children childnames = newparent.childnames # record previous parent and position newparent.children.insert(self.newindex, child) child.parent = newparent # set a new name, if required if child.name in childnames: self.oldname = child.name child.name = child.chooseName() self.newchildpath = child.path def undo(self, document): """Undo move.""" newparent = document.resolveWidgetPath(None, self.newparentpath) child = document.resolveWidgetPath(None, self.newchildpath) oldparent = document.resolveWidgetPath(None, self.oldparentpath) # remove from new parent del newparent.children[self.newindex] # restore parent oldparent.children.insert(self.oldchildindex, child) child.parent = oldparent # restore name if self.oldname is not None: child.name = self.oldname class OperationWidgetAdd(Operation): """Add a widget of specified type to parent.""" descr = _('add') def __init__(self, parent, type, autoadd=True, name=None, index=-1, **defaultvals): """Add a widget of type given parent is the parent widget type is the type to add (string) autoadd adds children automatically for some widgets name is the (optional) name of the new widget index is position in parent to add the widget settings can be passed to the created widgets as optional arguments """ self.parentpath = parent.path self.wtype = type self.autoadd = autoadd self.name = name self.index = index self.defaultvals = defaultvals def do(self, document): """Create the new widget. Returns the new widget """ parent = document.resolveWidgetPath(None, self.parentpath) w = widgetfactory.thefactory.makeWidget( self.wtype, parent, document, autoadd=self.autoadd, name=self.name, index=self.index, **self.defaultvals) self.createdname = w.name return w def undo(self, document): """Remove the added widget.""" parent = document.resolveWidgetPath(None, self.parentpath) parent.removeChild(self.createdname) ############################################################################### # Dataset operations class OperationDatasetSet(Operation): """Set a dataset to that specified.""" descr = _('set dataset') def __init__(self, datasetname, dataset): self.datasetname = datasetname self.dataset = dataset def do(self, document): """Set dataset, backing up existing one.""" self.olddata = document.data.get(self.datasetname) document.setData(self.datasetname, self.dataset) def undo(self, document): """Undo the data setting.""" if self.olddata is None: document.deleteData(self.datasetname) else: document.setData(self.datasetname, self.olddata) class OperationDatasetDelete(Operation): """Delete a dateset.""" descr = _('delete dataset') def __init__(self, datasetname): self.datasetname = datasetname def do(self, document): """Remove dataset from document, but preserve for undo.""" self.olddata = document.data[self.datasetname] document.deleteData(self.datasetname) def undo(self, document): """Put dataset back""" document.setData(self.datasetname, self.olddata) class OperationDatasetRename(Operation): """Rename the dataset. Assumes newname doesn't already exist """ descr = _('rename dataset') def __init__(self, oldname, newname): self.oldname = oldname self.newname = newname def do(self, document): """Rename dataset from oldname to newname.""" ds = document.data[self.oldname] self.origname = self.origrename = None if ds.linked: p = ds.linked.params if p.renames is None: p.renames = {} # dataset might have been renamed before, so we have to # remove that entry and remember how to put it back origname = self.oldname for o, n in list(citems(p.renames)): if n == self.oldname: origname = o # store in case of undo self.origrename = (o, n) break p.renames[origname] = self.newname self.origname = origname document.renameDataset(self.oldname, self.newname) def undo(self, document): """Change name back.""" ds = document.data[self.newname] if ds.linked: p = ds.linked.params del p.renames[self.origname] if self.origrename: p.renames[self.origrename[0]] = self.origrename[1] document.renameDataset(self.newname, self.oldname) class OperationDatasetDuplicate(Operation): """Duplicate a dataset. Assumes duplicate name doesn't already exist """ descr = _('duplicate dataset') def __init__(self, origname, duplname): self.origname = origname self.duplname = duplname def do(self, document): """Make the duplicate""" self.olddata = document.data.get(self.duplname) dataset = document.data[self.origname] duplicate = dataset.returnCopy() document.setData(self.duplname, duplicate) def undo(self, document): """Delete the duplicate""" if self.olddata is None: document.deleteData(self.duplname) else: document.setData(self.duplname, self.olddata) class OperationDatasetUnlinkFile(Operation): """Remove association between dataset and file.""" descr = _('unlink dataset') def __init__(self, datasetname): self.datasetname = datasetname def do(self, document): dataset = document.data[self.datasetname] self.oldfilelink = dataset.linked dataset.linked = None def undo(self, document): dataset = document.data[self.datasetname] dataset.linked = self.oldfilelink class OperationDatasetUnlinkRelation(Operation): """Remove association between dataset and another dataset. """ descr = _('unlink dataset') def __init__(self, datasetname): self.datasetname = datasetname def do(self, document): dataset = document.data[self.datasetname] self.olddataset = dataset ds = dataset.returnCopy() document.setData(self.datasetname, ds) def undo(self, document): document.setData(self.datasetname, self.olddataset) class OperationDatasetCreate(Operation): """Create dataset base class.""" def __init__(self, datasetname): self.datasetname = datasetname self.parts = {} def setPart(self, part, val): self.parts[part] = val def do(self, document): """Record old dataset if it exists.""" self.olddataset = document.data.get(self.datasetname) def undo(self, document): """Delete the created dataset.""" document.deleteData(self.datasetname) if self.olddataset is not None: document.setData(self.datasetname, self.olddataset) class OperationDatasetCreateRange(OperationDatasetCreate): """Create a dataset in a specfied range.""" descr = _('create dataset from range') def __init__(self, datasetname, numsteps, parts, linked=False): """Create a dataset with numsteps values. parts is a dict containing keys 'data', 'serr', 'perr' and/or 'nerr'. The values are tuples with (start, stop) values for each range. """ OperationDatasetCreate.__init__(self, datasetname) self.numsteps = numsteps self.parts = parts self.linked = linked def do(self, document): """Create dataset using range.""" OperationDatasetCreate.do(self, document) data = self.parts['data'] serr = self.parts.get('serr', None) perr = self.parts.get('perr', None) nerr = self.parts.get('nerr', None) ds = datasets.DatasetRange(self.numsteps, data, serr=serr, perr=perr, nerr=nerr) if not self.linked: # copy these values if we don't want to link ds = datasets.Dataset(data=ds.data, serr=ds.serr, perr=ds.perr, nerr=ds.nerr) document.setData(self.datasetname, ds) return ds class CreateDatasetException(Exception): """Thrown by dataset creation routines.""" pass class OperationDatasetCreateParameteric(OperationDatasetCreate): """Create a dataset using expressions dependent on t.""" descr = _('create parametric dataset') def __init__(self, datasetname, t0, t1, numsteps, parts, linked=False): """Create a parametric dataset. Variable t goes from t0 to t1 in numsteps. parts is a dict with keys 'data', 'serr', 'perr' and/or 'nerr' The values are expressions for evaluating.""" OperationDatasetCreate.__init__(self, datasetname) self.numsteps = numsteps self.t0 = t0 self.t1 = t1 self.parts = parts self.linked = linked def do(self, document): """Create the dataset.""" OperationDatasetCreate.do(self, document) p = self.parts.copy() p['parametric'] = (self.t0, self.t1, self.numsteps) ds = datasets.DatasetExpression(**p) ds.document = document if not self.linked: # copy these values if we don't want to link ds = datasets.Dataset(data=ds.data, serr=ds.serr, perr=ds.perr, nerr=ds.nerr) document.setData(self.datasetname, ds) return ds class OperationDatasetCreateExpression(OperationDatasetCreate): descr = _('create dataset from expression') def __init__(self, datasetname, parts, link, parametric=None): """Create a dataset from existing dataset using expressions. parts is a dict with keys 'data', 'serr', 'perr' and/or 'nerr' The values are expressions for evaluating. If link is True, then the dataset is linked to the expressions Parametric is a tuple (min, max, numitems) if creating parametric datasets. """ OperationDatasetCreate.__init__(self, datasetname) self.parts = parts self.link = link self.parametric = parametric def validateExpression(self, document): """Validate the expression is okay. Returns True if ok """ p = self.parts.copy() p['parametric'] = self.parametric ds = datasets.DatasetExpression(**p) ds.document = document return ds.updateEvaluation() def do(self, document): """Create the dataset.""" OperationDatasetCreate.do(self, document) p = self.parts.copy() p['parametric'] = self.parametric ds = datasets.DatasetExpression(**p) ds.document = document if not self.link: # copy these values if we don't want to link ds = datasets.Dataset(data=ds.data, serr=ds.serr, perr=ds.perr, nerr=ds.nerr) document.setData(self.datasetname, ds) return ds class OperationDatasetsFilter(Operation): """Operation to filter datasets.""" descr = _("filter datasets") def __init__(self, inexpr, indatasets, prefix="", suffix="", invert=False, replaceblanks=False): """Initialise operation: inexpr: input expression indatasets: list of dataset names prefix, suffix: output prefix/suffix invert: invert filter expression replaceblanks: replace output with blank/nan values. """ if not prefix and not suffix: raise ValueError("Prefix and/or suffix must be given") self.inexpr = inexpr self.indatasets = indatasets self.prefix = prefix self.suffix = suffix self.invert = invert self.replaceblanks = replaceblanks def makeGen(self): """Return generator object.""" return datasets.DatasetFilterGenerator( self.inexpr, self.indatasets, prefix=self.prefix, suffix=self.suffix, invert=self.invert, replaceblanks=self.replaceblanks) def check(self, doc): """Check the filter is ok. Return (ok, [list of errors]) """ log = self.makeGen().evaluateFilter(doc) if log: return (False, log) return (True, []) def do(self, doc): """Do the operation.""" gen = self.makeGen() self.olddatasets = {} for name in self.indatasets: outname = self.prefix + name + self.suffix self.olddatasets[outname] = doc.data.get(outname) doc.setData(outname, datasets.DatasetFiltered(gen, name, doc)) def undo(self, doc): """Undo operation.""" for name, val in citems(self.olddatasets): if val is None: doc.deleteData(name) else: doc.setData(name, val) class OperationDataset2DBase(Operation): """Operation as base for 2D dataset creation operations.""" def __init__(self, name, link): """Setup operation.""" self.datasetname = name self.link = link def validateExpression(self, document): """Validate expression is okay.""" ds = self.makeDSClass() ds.document = document ds.evalDataset() if 0 in ds.data.shape: raise CreateDatasetException() def do(self, document): """Make new dataset.""" # keep backup of old if exists self.olddataset = document.data.get(self.datasetname, None) # make new dataset ds = self.makeDSClass() ds.document = document if not self.link: # unlink if necessary ds = datasets.Dataset2D(ds.data, xrange=ds.xrange, yrange=ds.yrange, xedge=ds.xedge, yedge=ds.yedge, xcent=ds.xcent, ycent=ds.ycent) document.setData(self.datasetname, ds) return ds def undo(self, document): """Undo dataset creation.""" document.deleteData(self.datasetname) if self.olddataset: document.setData(self.datasetname, self.olddataset) class OperationDataset2DCreateExpressionXYZ(OperationDataset2DBase): descr = _('create 2D dataset from x, y and z expressions') def __init__(self, datasetname, xexpr, yexpr, zexpr, link): OperationDataset2DBase.__init__(self, datasetname, link) self.xexpr = xexpr self.yexpr = yexpr self.zexpr = zexpr def makeDSClass(self): return datasets.Dataset2DXYZExpression( self.xexpr, self.yexpr, self.zexpr) class OperationDataset2DCreateExpression(OperationDataset2DBase): descr = _('create 2D dataset from expression') def __init__(self, datasetname, expr, link): OperationDataset2DBase.__init__(self, datasetname, link) self.expr = expr def makeDSClass(self): return datasets.Dataset2DExpression(self.expr) class OperationDataset2DXYFunc(OperationDataset2DBase): descr = _('create 2D dataset from function of x and y') def __init__(self, datasetname, xstep, ystep, expr, link): """Create 2d dataset: xstep: tuple(xmin, xmax, step) ystep: tuple(ymin, ymax, step) expr: expression of x and y link: whether to link to this expression """ OperationDataset2DBase.__init__(self, datasetname, link) self.xstep = xstep self.ystep = ystep self.expr = expr def makeDSClass(self): return datasets.Dataset2DXYFunc(self.xstep, self.ystep, self.expr) class OperationDatasetUnlinkByFile(Operation): """Unlink all datasets associated with file.""" descr = _('unlink datasets') def __init__(self, filename): """Unlink all datasets associated with filename.""" self.filename = filename def do(self, document): """Remove links.""" self.oldlinks = {} for name, ds in citems(document.data): if ds.linked is not None and ds.linked.filename == self.filename: self.oldlinks[name] = ds.linked ds.linked = None def undo(self, document): """Restore links.""" for name, link in citems(self.oldlinks): try: document.data[name].linked = link except KeyError: pass class OperationDatasetDeleteByFile(Operation): """Delete all datasets associated with file.""" descr = _('delete datasets') def __init__(self, filename): """Delete all datasets associated with filename.""" self.filename = filename def do(self, document): """Remove datasets.""" self.olddatasets = {} for name, ds in list(document.data.items()): if ds.linked is not None and ds.linked.filename == self.filename: self.olddatasets[name] = ds document.deleteData(name) def undo(self, document): """Restore datasets.""" for name, ds in citems(self.olddatasets): document.setData(name, ds) ############################################################################### # Import datasets class OperationDataTag(Operation): """Add a tag to a list of datasets.""" descr = _('add dataset tags') def __init__(self, tag, datasetnames): """Add tag to datasets listed.""" self.tag = tag self.datasetnames = datasetnames def do(self, document): """Add new tags, if required.""" self.removetags = [] for name in self.datasetnames: existing = document.data[name].tags if self.tag not in existing: existing.add(self.tag) self.removetags.append(name) def undo(self, document): """Remove tags, if not previously present.""" for name in self.removetags: document.data[name].tags.remove(self.tag) class OperationDataUntag(Operation): """Add a tag to a list of datasets.""" descr = _('remove dataset tags') def __init__(self, tag, datasetnames): """Remove tag to datasets listed.""" self.tag = tag self.datasetnames = datasetnames def do(self, document): """Add new tags, if required.""" for name in self.datasetnames: document.data[name].tags.remove(self.tag) def undo(self, document): """Remove tags, if not previously present.""" for name in self.datasetnames: document.data[name].tags.add(self.tag) ############################################################################### # Alter dataset class OperationDatasetAddColumn(Operation): """Add a column to a dataset, blanked to zero.""" descr = _('add dataset column') def __init__(self, datasetname, columnname): """Initialise column columnname in datasetname. columnname can be one of 'data', 'serr', 'perr' or 'nerr' """ self.datasetname = datasetname self.columnname = columnname def do(self, document): """Zero the column.""" ds = document.data[self.datasetname] datacol = ds.data try: setattr(ds, self.columnname, N.zeros(datacol.shape, dtype='float64')) except AttributeError: raise RuntimeError("Invalid column name for dataset") document.setData(self.datasetname, ds) def undo(self, document): """Remove the column.""" ds = document.data[self.datasetname] setattr(ds, self.columnname, None) document.setData(self.datasetname, ds) class OperationDatasetSetVal(Operation): """Set a value in the dataset.""" descr = _('change dataset value') def __init__(self, datasetname, columnname, row, val): """Set row in column columnname to val.""" self.datasetname = datasetname self.columnname = columnname self.row = row self.val = val def do(self, document): """Set the value.""" ds = document.data[self.datasetname] datacol = getattr(ds, self.columnname) self.oldval = datacol[self.row] datacol[self.row] = self.val ds.changeValues(self.columnname, datacol) def undo(self, document): """Restore the value.""" ds = document.data[self.datasetname] datacol = getattr(ds, self.columnname) datacol[self.row] = self.oldval ds.changeValues(self.columnname, datacol) class OperationDatasetSetVal2D(Operation): """Set a value in a 2D dataset.""" descr = _('change 2D dataset value') def __init__(self, datasetname, row, col, val): """Set row in column columnname to val.""" self.datasetname = datasetname self.row = row self.col = col self.val = val def do(self, document): """Set the value.""" ds = document.data[self.datasetname] self.oldval = ds.data[self.row, self.col] ds.data[self.row, self.col] = self.val document.modifiedData(ds) def undo(self, document): """Restore the value.""" ds = document.data[self.datasetname] ds.data[self.row, self.col] = self.oldval document.modifiedData(ds) class OperationDatasetDeleteRow(Operation): """Delete a row or several in the dataset.""" descr = _('delete dataset row') def __init__(self, datasetname, row, numrows=1): """Delete a row in a dataset.""" self.datasetname = datasetname self.row = row self.numrows = numrows def do(self, document): """Set the value.""" ds = document.data[self.datasetname] self.saveddata = ds.deleteRows(self.row, self.numrows) def undo(self, document): """Restore the value.""" ds = document.data[self.datasetname] ds.insertRows(self.row, self.numrows, self.saveddata) class OperationDatasetInsertRow(Operation): """Insert a row or several in the dataset.""" descr = _('insert dataset row') def __init__(self, datasetname, row, numrows=1): """Delete a row in a dataset.""" self.datasetname = datasetname self.row = row self.numrows = numrows def do(self, document): """Set the value.""" ds = document.data[self.datasetname] ds.insertRows(self.row, self.numrows, {}) def undo(self, document): """Restore the value.""" ds = document.data[self.datasetname] ds.deleteRows(self.row, self.numrows) ############################################################################### # Custom setting operations class OperationSetCustom(Operation): """Set custom objects, such as constants.""" descr = _('set a custom definition') # translate ctype below into attribute of evaluate type_to_attr = { 'definition': 'def_definitions', 'function': 'def_definitions', 'constant': 'def_definitions', 'import': 'def_imports', 'color': 'def_colors', 'colormap': 'def_colormaps', } def __init__(self, ctype, vals): """Set custom values to be the list given. ctype is one of 'definition', 'function', 'constant', 'import', 'color' or 'colormap' """ self.ctype = ctype self.customvals = list(vals) def _getlist(self, document): return getattr(document.evaluate, self.type_to_attr[self.ctype]) def do(self, document): """Set the custom object.""" lst = self._getlist(document) self.oldval = list(lst) lst[:] = self.customvals document.evaluate.update() def undo(self, document): """Restore custom object.""" self._getlist(document)[:] = self.oldval document.evaluate.update() ############################################################################### # Misc operations class OperationMultiple(Operation): """Multiple operations batched into one.""" def __init__(self, operations, descr='change'): """A batch operation made up of the operations in list. Optional argument descr gives a description of the combined operation """ self.operations = operations if descr: self.descr = descr def addOperation(self, op): """Add an operation to the list of operations.""" self.operations.append(op) def do(self, document): """Do the multiple operations.""" for op in self.operations: op.do(document) def undo(self, document): """Undo the multiple operations.""" # operations need to undone in reverse order for op in self.operations[::-1]: op.undo(document) class OperationLoadStyleSheet(OperationMultiple): """An operation to load a stylesheet.""" descr = _('load stylesheet') def __init__(self, filename): """Load stylesheet with filename.""" OperationMultiple.__init__(self, [], descr=None) self.filename = os.path.abspath(filename) def do(self, document): """Do the import.""" from . import commandinterpreter # get document to keep track of changes for undo/redo document.batchHistory(self) # fire up interpreter to read file interpreter = commandinterpreter.CommandInterpreter(document) try: interpreter.runFile( io.open(self.filename, 'rU', encoding='utf8') ) except: document.batchHistory(None) raise document.batchHistory(None) class OperationLoadCustom(OperationLoadStyleSheet): descr = _('load custom definitions') class OperationToolsPlugin(OperationMultiple): """An operation to represent what a tools plugin does.""" def __init__(self, plugin, fields): """Use tools plugin, passing fields.""" OperationMultiple.__init__(self, [], descr=None) self.plugin = plugin self.fields = fields self.descr = plugin.name def do(self, document): """Use the plugin.""" from . import commandinterface # get document to keep track of changes for undo/redo document.batchHistory(self) # fire up interpreter to read file ifc = commandinterface.CommandInterface(document) try: self.plugin.apply(ifc, self.fields) except: document.batchHistory(None) raise document.batchHistory(None) class OperationDatasetPlugin(Operation): """An operation to activate a dataset plugin.""" def __init__(self, plugin, fields, datasetnames={}): """Use dataset plugin, passing fields.""" self.plugin = plugin self.fields = fields self.descr = plugin.name self.names = datasetnames def do(self, document): """Use the plugin. """ self.datasetnames = [] self.olddata = {} manager = self.manager = plugins.DatasetPluginManager( self.plugin, document, self.fields) names = self.datasetnames = list(manager.datasetnames) # rename if requested for i in crange(len(names)): if names[i] in self.names: names[i] = self.names[names[i]] # preserve old datasets for name in names: if name in document.data: self.olddata[name] = document.data[name] # add new datasets to document for name, ds in czip(names, manager.veuszdatasets): if name is not None: document.setData(name, ds) return names def validate(self): """Check that the plugin works the first time.""" self.manager.update(raiseerrors=True) def undo(self, document): """Undo dataset plugin.""" # delete datasets which were created for name in self.datasetnames: if name is not None: document.deleteData(name) # put back old datasets for name, ds in citems(self.olddata): document.setData(name, ds) class OperationDatasetHistogram(Operation): """Operation to make histogram from data.""" descr = _("make histogram") def __init__(self, expr, outposns, outvalues, binparams=None, binmanual=None, method='counts', cumulative = 'none', errors=False): """ inexpr = input dataset expression outposns = name of dataset for bin positions outvalues = name of dataset for bin values binparams = None / (num, minval, maxval, islog) binmanual = None / [1,2,3,4,5] method = ('counts', 'density', or 'fractions') cumulative = ('none', 'smalltolarge', 'largetosmall') errors = True/False """ self.expr = expr self.outposns = outposns self.outvalues = outvalues self.binparams = binparams self.binmanual = binmanual self.method = method self.cumulative = cumulative self.errors = errors def do(self, document): """Create histogram datasets.""" gen = datasets.DatasetHistoGenerator( document, self.expr, binparams=self.binparams, binmanual=self.binmanual, method=self.method, cumulative=self.cumulative, errors=self.errors) self.oldposnsds = self.oldvaluesds = None if self.outvalues != '': self.oldvaluesds = document.data.get(self.outvalues, None) document.setData(self.outvalues, gen.generateValueDataset()) if self.outposns != '': self.oldposnsds = document.data.get(self.outposns, None) document.setData(self.outposns, gen.generateBinDataset()) def undo(self, document): """Undo creation of datasets.""" if self.oldposnsds is not None: if self.outposns != '': document.setData(self.outposns, self.oldposnsds) else: document.deleteData(self.outposns) if self.oldvaluesds is not None: if self.outvalues != '': document.setData(self.outvalues, self.oldvaluesds) else: document.deleteData(self.outvalues) �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������veusz-3.0.1/veusz/document/dbusinterface.py���������������������������������������������������������0000664�0001750�0001750�00000025016�13161413406�020476� 0����������������������������������������������������������������������������������������������������ustar �jss�����������������������������jss�����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2011 Jeremy S. Sanders # Email: Jeremy Sanders # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ############################################################################## """DBus interface to Veusz document.""" from __future__ import division import numpy as N from ..compat import cstr from ..utils import vzdbus from . import commandinterpreter class DBusInterface(vzdbus.Object): """DBus interface to Veusz document command interface.""" _ctr = 1 interface = 'org.veusz.document' def __init__(self, doc): root = '/Windows/%i/Document' % DBusInterface._ctr # possible exception in dbus means we have to check sessionbus if vzdbus.sessionbus is not None: vzdbus.Object.__init__(self, vzdbus.sessionbus, root) self.index = DBusInterface._ctr DBusInterface._ctr += 1 self.cmdinter = commandinterpreter.CommandInterpreter(doc) self.ci = self.cmdinter.interface @vzdbus.method(dbus_interface=interface, in_signature='s') def RunPython(self, cmdstr): return self.cmdinter.run(cmdstr) @vzdbus.method(dbus_interface=interface, in_signature='sa{sv}') def Action(self, action, optargs): return self.ci.Action(action, **optargs) @vzdbus.method(dbus_interface=interface, in_signature='sa{sv}', out_signature='s') def Add(self, wtype, optargs): return self.ci.Add(wtype, **optargs) @vzdbus.method(dbus_interface=interface, in_signature='sssa{sv}') def AddCustom(self, thetype, name, val, argsv): self.ci.AddCustom(thetype, name, val, **argsv) @vzdbus.method(dbus_interface=interface, in_signature='s') def AddImportPath(self, dirname): self.ci.AddImportPath(cstr(dirname)) @vzdbus.method(dbus_interface=interface, in_signature='ssa{sv}', out_signature='s') def CloneWidget(self, widget, newparent, optargs): return self.ci.CloneWidget(cstr(widget), cstr(newparent), **optargs) @vzdbus.method(dbus_interface=interface, in_signature='sssa{sv}') def CreateHistogram(self, inexpr, outbinsds, outvalsds, optargs): self.ci.CreateHistogram(cstr(inexpr), cstr(outbinsds), cstr(outvalsds), **optargs) @vzdbus.method(dbus_interface=interface, in_signature='sa{sv}a{sv}') def DatasetPlugin(self, pluginname, fields, datasetnames): self.ci.DatasetPlugin(cstr(pluginname), fields, datasetnames) @vzdbus.method(dbus_interface=interface, in_signature='sa{sv}') def Export(self, filename, optargs): self.ci.Export(filename, **optargs) @vzdbus.method(dbus_interface=interface, in_signature='s', out_signature='v') def Get(self, val): return self.ci.Get(val) @vzdbus.method(dbus_interface=interface, in_signature='s', out_signature='as') def GetChildren(self, where): return self.ci.GetChildren(where=where) @vzdbus.method(dbus_interface=interface, in_signature='s', out_signature='adadadad') def GetData1D(self, datasetname): """Get a numeric dataset. Returns lists of numeric values for data, symmetric error, negative error and positive error.""" def lornull(l): """Get blank list if None or convert to list otherwise.""" if l is None: return [] return list(l) data, serr, nerr, perr = self.ci.GetData(cstr(datasetname)) return lornull(data), lornull(serr), lornull(nerr), lornull(perr) @vzdbus.method(dbus_interface=interface, in_signature='s', out_signature='iiddddad') def GetData2D(self, datasetname): """Get a 2D dataset. Returns (X dim, Y dim, rangex min, rangex max, rangey min, rangey max, data (as 1d numeric array)) """ data = self.ci.GetData(cstr(datasetname)) return ( data[0].shape[1], data[0].shape[0], data[1][0], data[1][1], data[2][0], data[2][1], list(data[0].flat) ) @vzdbus.method(dbus_interface=interface, in_signature='s', out_signature='as') def GetDataText(self, datasetname): """Get a text dataset as an array of strings.""" return self.ci.GetData(cstr(datasetname)) @vzdbus.method(dbus_interface=interface, out_signature='as') def GetDatasets(self): return self.ci.GetDatasets() @vzdbus.method(dbus_interface=interface, in_signature='ssa{sv}') def ImportFile(self, filename, descriptor, optargs): self.ci.ImportFile(filename, descriptor, **optargs) @vzdbus.method(dbus_interface=interface, in_signature='sasa{sv}') def ImportFile2D(self, filename, datasetnames, optargs): self.ci.ImportFile2D(filename, datasetnames, **optargs) @vzdbus.method(dbus_interface=interface, in_signature='sa{sv}') def ImportFileCSV(self, filename, optargs): self.ci.ImportFileCSV(filename, **optargs) @vzdbus.method(dbus_interface=interface, in_signature='sssa{sv}') def ImportFITSFile(self, dsname, filename, hdu, optargs): self.ci.ImportFITSFile(filename, **optargs) @vzdbus.method(dbus_interface=interface, in_signature='ssa{sv}') def ImportFilePlugin(self, plugin, filename, optargs): self.ci.ImportFilePlugin(plugin, filename, **optargs) @vzdbus.method(dbus_interface=interface, in_signature='ssa{sv}') def ImportString(self, descriptor, string, optargs): self.ci.ImportString(cstr(descriptor), cstr(string), **optargs) @vzdbus.method(dbus_interface=interface, in_signature='s') def Load(self, filename): self.cmdinter.Load(filename) @vzdbus.method(dbus_interface=interface) def Print(self): self.ci.Print() @vzdbus.method(dbus_interface=interface) def ReloadData(self): self.ci.ReloadData() @vzdbus.method(dbus_interface=interface, in_signature='ss') def Rename(self, widget, newname): self.ci.Rename( cstr(widget), cstr(newname) ) @vzdbus.method(dbus_interface=interface, in_signature='s') def Remove(self, name): self.ci.Remove(cstr(name)) @vzdbus.method(dbus_interface=interface, in_signature='s') def RemoveCustom(self, name): self.ci.RemoveCustom(cstr(name)) @vzdbus.method(dbus_interface=interface, in_signature='s', out_signature='s') def ResolveReference(self, name): return self.ci.ResolveReference(cstr(name)) @vzdbus.method(dbus_interface=interface, in_signature='s') def Save(self, filename): self.ci.Save(cstr(filename)) @vzdbus.method(dbus_interface=interface, in_signature='sv') def Set(self, name, val): return self.ci.Set(cstr(name), val) @vzdbus.method(dbus_interface=interface, in_signature='ss') def SetToReference(self, name, val): return self.ci.SetToReference(cstr(name), cstr(val)) @vzdbus.method(dbus_interface=interface, in_signature='sadadadad') def SetData(self, name, data, symerr, negerr, poserr): if not symerr: symerr = None if not negerr: negerr = None if not poserr: poserr = None self.ci.SetData(cstr(name), data, symerr, negerr, poserr) @vzdbus.method(dbus_interface=interface, in_signature='ssssa{sv}') def SetData2DExpressionXYZ(self, name, xexpr, yexpr, zexpr, optargs): self.ci.SetData2DExpressionXYZ(cstr(name), xexpr, yexpr, zexpr, **optargs) @vzdbus.method(dbus_interface=interface, in_signature='s(ddd)(ddd)sa{sv}') def SetData2DXYFunc(self, name, xstep, ystep, expr, optargs): self.ci.SetData2DXYFunc(cstr(name), xstep, ystep, expr, **optargs) @vzdbus.method(dbus_interface=interface, in_signature='sadii(dd)(dd)') def SetData2D(self, name, data, nx, ny, xrange, yrange): data = N.array(data).reshape(nx, ny) self.ci.SetData2D(cstr(name), data, xrange=xrange, yrange=yrange) @vzdbus.method(dbus_interface=interface, in_signature='ssa{sv}') def SetDataExpression(self, name, val, optargs): self.ci.SetDataExpression(cstr(name), val, **optargs) @vzdbus.method(dbus_interface=interface, in_signature='sas') def SetDataText(self, name, val): val = [cstr(x) for x in val] self.ci.SetDataText(cstr(name), val) @vzdbus.method(dbus_interface=interface, in_signature='sas') def TagDatasets(self, tag, datasets): self.ci.TagDatasets(cstr(tag), datasets) @vzdbus.method(dbus_interface=interface, in_signature='s') def To(self, path): self.ci.To(path) # node interface @vzdbus.method(dbus_interface=interface, in_signature='ss', out_signature='as') def NodeChildren(self, path, types): return self.ci.NodeChildren(path, types) @vzdbus.method(dbus_interface=interface, in_signature='s', out_signature='s') def NodeType(self, path): return self.ci.NodeType(path) @vzdbus.method(dbus_interface=interface, in_signature='s', out_signature='s') def SettingType(self, path): return self.ci.SettingType(path) @vzdbus.method(dbus_interface=interface, in_signature='s', out_signature='s') def WidgetType(self, path): return self.ci.WidgetType(path) ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������veusz-3.0.1/veusz/windows/��������������������������������������������������������������������������0000775�0001750�0001750�00000000000�13325026670�015163� 5����������������������������������������������������������������������������������������������������ustar �jss�����������������������������jss�����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������veusz-3.0.1/veusz/windows/consolewindow.py����������������������������������������������������������0000664�0001750�0001750�00000026334�13242310603�020425� 0����������������������������������������������������������������������������������������������������ustar �jss�����������������������������jss�����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# consolewindow.py # a python-like qt console # Copyright (C) 2003 Jeremy S. Sanders # Email: Jeremy Sanders # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ############################################################################## from __future__ import division import codeop import traceback import sys from ..compat import cstr from .. import qtall as qt4 from .. import document from .. import utils from .. import setting # TODO - command line completion def _(text, disambiguation=None, context='ConsoleWindow'): """Translate text.""" return qt4.QCoreApplication.translate(context, text, disambiguation) class _Writer(object): """ Class to behave like an output stream. Pipes input back to the specified function.""" def __init__(self, function): """Set the function output is sent to.""" self.function = function def write(self, text): """Send text to the output function.""" self.function(text) def flush(self): """Does nothing as yet.""" pass class _Reader(object): """Fake reading input stream object.""" def read(self, *args): raise IOError('Interactive input not supported') def readline(self, *args): raise IOError('Interactive input not supported') class _CommandEdit(qt4.QLineEdit): """ A special class to allow entering of the command line. emits sigEnter if the return key is pressed, and returns command The edit control has a history (press up and down keys to access) """ sigEnter = qt4.pyqtSignal(cstr) def __init__(self, *args): qt4.QLineEdit.__init__(self, *args) self.history = [] self.history_posn = 0 self.entered_text = '' self.returnPressed.connect(self.slotReturnPressed) self.setToolTip(_("Input a python expression here and press enter")) def slotReturnPressed(self): """ Called if the return key is pressed in the edit control.""" # retrieve the text command = self.text() self.setText("") # keep the command for history self.history.append(command) self.history_posn = len(self.history) self.entered_text = '' # tell the console we have a command self.sigEnter.emit(command) historykeys = (qt4.Qt.Key_Up, qt4.Qt.Key_Down) def keyPressEvent(self, key): """ Overridden to handle history. """ qt4.QLineEdit.keyPressEvent(self, key) code = key.key() # check whether one of the "history keys" has been pressed if code in _CommandEdit.historykeys: # look for the next or previous history item which our current text # is a prefix of if self.isModified(): text = self.text() self.history_posn = len(self.history) else: text = self.entered_text if code == qt4.Qt.Key_Up: step = -1 elif code == qt4.Qt.Key_Down: step = 1 newpos = self.history_posn + step while True: if newpos >= len(self.history): break if newpos < 0: return if self.history[newpos].startswith(text): break newpos += step if newpos >= len(self.history): # go back to whatever the user had typed in self.history_posn = len(self.history) self.setText(self.entered_text) return # found a relevant history item self.history_posn = newpos # user has modified text since last set if self.isModified(): self.entered_text = text # replace the text in the control text = self.history[ self.history_posn ] self.setText(text) introtext=_(u'''Welcome to Veusz %s --- a scientific plotting application.
        Copyright \u00a9 2003-2018 Jeremy Sanders <jeremy@jeremysanders.net> and contributors.
        Veusz comes with ABSOLUTELY NO WARRANTY. Veusz is Free Software, and you are
        welcome to redistribute it under certain conditions. Enter "GPL()" for details.
        This window is a Python command line console and acts as a calculator.
        ''') % utils.version() class ConsoleWindow(qt4.QDockWidget): """ A python-like qt console.""" def __init__(self, thedocument, *args): qt4.QDockWidget.__init__(self, *args) self.setWindowTitle(_("Console - Veusz")) self.setObjectName("veuszconsolewindow") # arrange sub-widgets in a vbox self.vbox = qt4.QWidget() self.setWidget(self.vbox) vlayout = qt4.QVBoxLayout(self.vbox) s = vlayout.contentsMargins().left()//4 vlayout.setContentsMargins(s,s,s,s) vlayout.setSpacing(s) # start an interpreter instance to the document self.interpreter = document.CommandInterpreter(thedocument) self.document = thedocument # streams the output/input goes to/from self.con_stdout = _Writer(self.output_stdout) self.con_stderr = _Writer(self.output_stderr) self.con_stdin = _Reader() self.interpreter.setFiles( self.con_stdout, self.con_stderr, self.con_stdin) self.stdoutbuffer = "" self.stderrbuffer = "" # (mostly) hidden notification self._hiddennotify = qt4.QLabel() vlayout.addWidget(self._hiddennotify) self._hiddennotify.hide() # the output from the console goes here self._outputdisplay = qt4.QTextEdit() self._outputdisplay.setReadOnly(True) self._outputdisplay.insertHtml( introtext ) vlayout.addWidget(self._outputdisplay) self._hbox = qt4.QWidget() hlayout = qt4.QHBoxLayout(self._hbox) hlayout.setContentsMargins(0,0,0,0) vlayout.addWidget(self._hbox) self._prompt = qt4.QLabel(">>>") hlayout.addWidget(self._prompt) # where commands are typed in self._inputedit = _CommandEdit() hlayout.addWidget(self._inputedit) self._inputedit.setFocus() # keep track of multiple line commands self.command_build = '' # get called if enter is pressed in the input control self._inputedit.sigEnter.connect(self.slotEnter) # called if document logs something thedocument.sigLog.connect(self.slotDocumentLog) def _makeTextFormat(self, cursor, color): fmt = cursor.charFormat() if color is not None: brush = qt4.QBrush(color) fmt.setForeground(brush) else: # use the default foreground color fmt.clearForeground() return fmt def appendOutput(self, text, style): """Add text to the tail of the error log, with a specified style""" if style == 'error': color = setting.settingdb.color('error') elif style == 'command': color = setting.settingdb.color('command') else: color = None cursor = self._outputdisplay.textCursor() cursor.movePosition(qt4.QTextCursor.End) cursor.insertText(text, self._makeTextFormat(cursor, color)) self._outputdisplay.setTextCursor(cursor) self._outputdisplay.ensureCursorVisible() def runFunction(self, func): """Execute the function within the console window, trapping exceptions.""" # preserve output streams saved = sys.stdout, sys.stderr, sys.stdin sys.stdout, sys.stderr, sys.stdin = ( self.con_stdout, self.con_stderr, self.con_stdin) # catch any exceptions, printing problems to stderr with self.document.suspend(): try: func() except: # print out the backtrace to stderr info = sys.exc_info() backtrace = traceback.format_exception(*info) for line in backtrace: sys.stderr.write(line) # return output streams sys.stdout, sys.stderr, sys.stdin = saved def checkVisible(self): """If this window is hidden, show it, then hide it again in a few seconds.""" if self.isHidden(): self._hiddennotify.setText(_("This window will shortly disappear. " "You can bring it back by selecting " "View, Windows, Console Window on the " "menu.")) qt4.QTimer.singleShot(5000, self.hideConsole) self.show() self._hiddennotify.show() def hideConsole(self): """Hide window and notification widget.""" self._hiddennotify.hide() self.hide() def output_stdout(self, text): """ Write text in stdout font to the log.""" self.checkVisible() self.appendOutput(text, 'normal') def output_stderr(self, text): """ Write text in stderr font to the log.""" self.checkVisible() self.appendOutput(text, 'error') def insertTextInOutput(self, text): """ Inserts the text into the log.""" self.appendOutput(text, 'normal') def slotEnter(self, command): """ Called if the return key is pressed in the edit control.""" newc = self.command_build + '\n' + command # check whether command can be compiled # c set to None if incomplete try: comp = codeop.compile_command(newc) except Exception: # we want errors to be caught by self.interpreter.run below comp = 1 # which prompt? prompt = '>>>' if self.command_build != '': prompt = '...' # output the command in the log pane self.appendOutput('%s %s\n' % (prompt, command), 'command') # are we ready to run this? if comp is None or ( len(command) != 0 and len(self.command_build) != 0 and (command[0] == ' ' or command[0] == '\t')): # build up the expression self.command_build = newc # modify the prompt self._prompt.setText('...') else: # actually execute the command self.interpreter.run(newc) self.command_build = '' # modify the prompt self._prompt.setText('>>>') def slotDocumentLog(self, text): """Output information if the document logs something.""" self.output_stderr(text + '\n') veusz-3.0.1/veusz/windows/treeeditwindow.py0000664000175000017500000014730413304514354020601 0ustar jssjss00000000000000# -*- coding: utf-8 -*- # Copyright (C) 2004 Jeremy S. Sanders # Email: Jeremy Sanders # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ############################################################################### """Window to edit the document using a tree, widget properties and formatting properties.""" from __future__ import division from ..compat import crange, citems from .. import qtall as qt4 from .. import widgets from .. import utils from .. import document from .. import setting from .widgettree import WidgetTreeModel, WidgetTreeView def _(text, disambiguation=None, context='TreeEditWindow'): """Translate text.""" return qt4.QCoreApplication.translate(context, text, disambiguation) class SettingsProxy(object): """Object to handle communication between widget/settings or sets of widgets/settings.""" def childProxyList(self): """Return a list settings and setting variables proxified.""" def settingsProxyList(self): """Return list of SettingsProxy objects for sub Settings.""" def settingList(self): """Return list of Setting objects.""" def actionsList(self): """Return list of Action objects.""" def onSettingChanged(self, control, setting, val): """Called when a setting has been modified.""" def onAction(self, action, console): """Called if action pressed. Console window is given.""" def name(self): """Return name of Settings.""" def pixmap(self): """Return pixmap for Settings.""" def usertext(self): """Return text for user.""" def setnsmode(self): """Return setnsmode of Settings.""" def multivalued(self, name): """Is setting with name multivalued?""" return False def resetToDefault(self, name): """Reset setting to default.""" class SettingsProxySingle(SettingsProxy): """A proxy wrapping settings for a single widget.""" def __init__(self, document, settings, actions=None): """Initialise settings proxy. settings is the widget settings, actions is its actions.""" self.document = document self.settings = settings self.actions = actions def childProxyList(self): """Return a list settings and setting variables proxified.""" retn = [] s = self.settings for n in s.getNames(): o = s.get(n) if isinstance(o, setting.Settings): retn.append( SettingsProxySingle(self.document, o) ) else: retn.append(o) return retn def settingsProxyList(self): """Return list of SettingsProxy objects.""" return [ SettingsProxySingle(self.document, s) for s in self.settings.getSettingsList() ] def settingList(self): """Return list of Setting objects.""" return self.settings.getSettingList() def actionsList(self): """Return list of actions.""" return self.actions def onSettingChanged(self, control, setting, val): """Change setting in document.""" if setting.val != val: self.document.applyOperation( document.OperationSettingSet(setting, val)) def onAction(self, action, console): """Run action on console.""" console.runFunction(action.function) def name(self): """Return name.""" return self.settings.name def pixmap(self): """Return pixmap.""" return self.settings.pixmap def usertext(self): """Return text for user.""" return self.settings.usertext def setnsmode(self): """Return setnsmode of Settings.""" return self.settings.setnsmode def resetToDefault(self, name): """Reset setting to default.""" setn = self.settings.get(name) self.document.applyOperation( document.OperationSettingSet(setn, setn.default)) class SettingsProxyMulti(SettingsProxy): """A proxy wrapping settings for multiple widgets.""" def __init__(self, document, widgets, _root=''): """Initialise settings proxy. widgets is a list of widgets to proxy for.""" self.document = document self.widgets = widgets self._root = _root self._settingsatlevel = self._getSettingsAtLevel() self._cachesettings = self._cachesetting = self._cachechild = None def _getSettingsAtLevel(self): """Return settings of widgets at level given.""" if self._root: levels = self._root.split('/') else: levels = [] setns = [] for w in self.widgets: s = w.settings for lev in levels: s = s.get(lev) setns.append(s) return setns def _objList(self, filterclasses): """Return a list of objects with the type in filterclasses.""" setns = self._settingsatlevel # get list of names with appropriate class names = [] for n in setns[0].getNames(): o = setns[0].get(n) for c in filterclasses: if isinstance(o, c): names.append(n) break sset = set(names) for s in setns[1:]: sset &= set(s.getNames()) names = [n for n in names if n in sset] proxylist = [] for n in names: o = setns[0].get(n) if isinstance(o, setting.Settings): # construct new proxy settings (adding on name of root) newroot = n if self._root: newroot = self._root + '/' + newroot v = SettingsProxyMulti(self.document, self.widgets, _root=newroot) else: # use setting from first settings as template v = o proxylist.append(v) return proxylist def childProxyList(self): """Make a list of proxy settings.""" if self._cachechild is None: self._cachechild = self._objList( (setting.Settings, setting.Setting) ) return self._cachechild def settingsProxyList(self): """Get list of settings proxy.""" if self._cachesettings is None: self._cachesettings = self._objList( (setting.Settings,) ) return self._cachesettings def settingList(self): """Set list of common Setting objects for each widget.""" if self._cachesetting is None: self._cachesetting = self._objList( (setting.Setting,) ) return self._cachesetting def actionsList(self): """Get list of common actions.""" anames = None for widget in self.widgets: a = set([a.name for a in widget.actions]) if anames is None: anames = a else: anames &= a actions = [a for a in self.widgets[0].actions if a.name in anames] return actions def onSettingChanged(self, control, setting, val): """Change setting in document.""" # construct list of operations to change each setting ops = [] sname = setting.name if self._root: sname = self._root + '/' + sname for w in self.widgets: s = self.document.resolveSettingPath(None, w.path+'/'+sname) if s.val != val: ops.append(document.OperationSettingSet(s, val)) # apply all operations if ops: self.document.applyOperation( document.OperationMultiple(ops, descr=_('change settings'))) def onAction(self, action, console): """Run actions with same name.""" aname = action.name for w in self.widgets: for a in w.actions: if a.name == aname: console.runFunction(a.function) def name(self): return self._settingsatlevel[0].name def pixmap(self): """Return pixmap.""" return self._settingsatlevel[0].pixmap def usertext(self): """Return text for user.""" return self._settingsatlevel[0].usertext def setnsmode(self): """Return setnsmode.""" return self._settingsatlevel[0].setnsmode def multivalued(self, name): """Is setting multivalued?""" slist = [s.get(name) for s in self._settingsatlevel] first = slist[0].get() for s in slist[1:]: if s.get() != first: return True return False def resetToDefault(self, name): """Reset settings to default.""" ops = [] for s in self._settingsatlevel: setn = s.get(name) ops.append(document.OperationSettingSet(setn, setn.default)) self.document.applyOperation( document.OperationMultiple(ops, descr=_("reset to default"))) class PropertyList(qt4.QWidget): """Edit the widget properties using a set of controls.""" def __init__(self, document, showformatsettings=True, *args): qt4.QWidget.__init__(self, *args) self.document = document self.showformatsettings = showformatsettings self.layout = qt4.QGridLayout(self) self.layout.setSpacing( self.layout.spacing()//2 ) self.layout.setContentsMargins(4,4,4,4) self.childlist = [] self.setncntrls = {} # map setting name to controls def getConsole(self): """Find console window. This is horrible: HACK.""" win = self.parent() while not hasattr(win, 'console'): win = win.parent() return win.console def _addActions(self, setnsproxy, row): """Add a list of actions.""" for action in setnsproxy.actionsList(): text = action.name if action.usertext: text = action.usertext lab = qt4.QLabel(text) self.layout.addWidget(lab, row, 0) self.childlist.append(lab) button = qt4.QPushButton(text) button.setToolTip(action.descr) button.clicked.connect( lambda checked=True, a=action: setnsproxy.onAction(a, self.getConsole())) self.layout.addWidget(button, row, 1) self.childlist.append(button) row += 1 return row def _addControl(self, setnsproxy, setn, row): """Add a control for a setting.""" cntrl = setn.makeControl(None) if cntrl: lab = SettingLabel(self.document, setn, setnsproxy) self.layout.addWidget(lab, row, 0) self.childlist.append(lab) cntrl.sigSettingChanged.connect(setnsproxy.onSettingChanged) self.layout.addWidget(cntrl, row, 1) self.childlist.append(cntrl) self.setncntrls[setn.name] = (lab, cntrl) row += 1 return row def _addGroupedSettingsControl(self, grpdsetting, row): """Add a control for a set of grouped settings.""" slist = grpdsetting.settingList() # make first widget with expandable button # this is a label with a + button by this side setnlab = SettingLabel(self.document, slist[0], grpdsetting) expandbutton = qt4.QPushButton("+", checkable=True, flat=True, maximumWidth=16) l = qt4.QHBoxLayout(spacing=0) l.setContentsMargins(0,0,0,0) l.addWidget( expandbutton ) l.addWidget( setnlab ) lw = qt4.QWidget() lw.setLayout(l) self.layout.addWidget(lw, row, 0) self.childlist.append(lw) # make main control cntrl = slist[0].makeControl(None) cntrl.sigSettingChanged.connect(grpdsetting.onSettingChanged) self.layout.addWidget(cntrl, row, 1) self.childlist.append(cntrl) row += 1 # set of controls for remaining settings l = qt4.QGridLayout() grp_row = 0 for setn in slist[1:]: cntrl = setn.makeControl(None) if cntrl: lab = SettingLabel(self.document, setn, grpdsetting) l.addWidget(lab, grp_row, 0) cntrl.sigSettingChanged.connect(grpdsetting.onSettingChanged) l.addWidget(cntrl, grp_row, 1) grp_row += 1 grpwidget = qt4.QFrame( frameShape = qt4.QFrame.Panel, frameShadow = qt4.QFrame.Raised, visible=False ) grpwidget.setLayout(l) def ontoggle(checked): """Toggle button text and make grp visible/invisible.""" expandbutton.setText( ("+","-")[checked] ) grpwidget.setVisible( checked ) expandbutton.toggled.connect(ontoggle) # add group to standard layout self.layout.addWidget(grpwidget, row, 0, 1, -1) self.childlist.append(grpwidget) row += 1 return row def updateProperties(self, setnsproxy, title=None, showformatting=True, onlyformatting=False): """Update the list of controls with new ones for the SettingsProxy.""" # keep a reference to keep it alive self._setnsproxy = setnsproxy # delete all child widgets self.setUpdatesEnabled(False) while len(self.childlist) > 0: c = self.childlist.pop() self.layout.removeWidget(c) c.deleteLater() del c if setnsproxy is None: self.setUpdatesEnabled(True) return row = 0 self.setncntrls = {} self.layout.setEnabled(False) # add a title if requested if title is not None: lab = qt4.QLabel(title[0], frameShape=qt4.QFrame.Panel, frameShadow=qt4.QFrame.Sunken, toolTip=title[1]) self.layout.addWidget(lab, row, 0, 1, -1) row += 1 # add actions if parent is widget if setnsproxy.actionsList() and not showformatting: row = self._addActions(setnsproxy, row) if setnsproxy.settingsProxyList() and self.showformatsettings: # if we have subsettings, use tabs tabbed = TabbedFormatting(self.document, setnsproxy) self.layout.addWidget(tabbed, row, 1, 1, 2) row += 1 self.childlist.append(tabbed) else: # else add settings proper as a list for setn in setnsproxy.childProxyList(): # add setting # only add if formatting setting and formatting allowed # and not formatting and not formatting not allowed if ( isinstance(setn, setting.Setting) and ( (setn.formatting and (showformatting or onlyformatting)) or (not setn.formatting and not onlyformatting)) and not setn.hidden ): row = self._addControl(setnsproxy, setn, row) elif ( isinstance(setn, SettingsProxy) and setn.setnsmode() == 'groupedsetting' and not onlyformatting ): row = self._addGroupedSettingsControl(setn, row) # add empty widget to take rest of space w = qt4.QWidget( sizePolicy=qt4.QSizePolicy( qt4.QSizePolicy.Maximum, qt4.QSizePolicy.MinimumExpanding) ) self.layout.addWidget(w, row, 0) self.childlist.append(w) self.setUpdatesEnabled(True) self.layout.setEnabled(True) def showHideSettings(self, setnshow, setnhide): """Show or hide controls for settings.""" for vis, setns in ( (True, setnshow), (False, setnhide) ): for setn in setns: if setn in self.setncntrls: for cntrl in self.setncntrls[setn]: cntrl.setVisible(vis) class TabbedFormatting(qt4.QTabWidget): """Class to have tabbed set of settings.""" def __init__(self, document, setnsproxy, shownames=False): qt4.QTabWidget.__init__(self) self.setUsesScrollButtons(True) self.document = document if setnsproxy is None: return # get list of settings self.setnsproxy = setnsproxy setnslist = setnsproxy.settingsProxyList() # add formatting settings if necessary numformat = len( [setn for setn in setnsproxy.settingList() if setn.formatting] ) if numformat > 0: # add on a formatting tab setnslist.insert(0, setnsproxy) self.currentChanged.connect(self.slotCurrentChanged) # subsettings for tabs self.tabsubsetns = [] # collected titles and tooltips for tabs self.tabtitles = [] self.tabtooltips = [] # tabs which have been initialized self.tabinit = set() # add tab for each subsettings for subset in setnslist: if subset.setnsmode() not in ('formatting', 'widgetsettings'): continue self.tabsubsetns.append(subset) # details of tab if subset is setnsproxy: # main tab formatting, so this is special pixmap = 'settings_main' tabname = title = _('Main') tooltip = _('Main formatting') else: # others if hasattr(subset, 'pixmap'): pixmap = subset.pixmap() else: pixmap = None tabname = subset.name() tooltip = title = subset.usertext() # hide name in tab if not shownames: tabname = '' self.tabtitles.append(title) self.tabtooltips.append(tooltip) # create tab indx = self.addTab(qt4.QWidget(), utils.getIcon(pixmap), tabname) self.setTabToolTip(indx, tooltip) def slotCurrentChanged(self, tab): """Lazy loading of tab when displayed.""" if tab in self.tabinit: # already initialized return self.tabinit.add(tab) # settings to show subsetn = self.tabsubsetns[tab] # whether these are the main settings mainsettings = subsetn is self.setnsproxy # add this property list to the scroll widget for tab plist = PropertyList(self.document, showformatsettings=not mainsettings) plist.updateProperties(subsetn, title=(self.tabtitles[tab], self.tabtooltips[tab]), onlyformatting=mainsettings) # create scrollable area scroll = qt4.QScrollArea() scroll.setWidgetResizable(True) scroll.setWidget(plist) # layout for tab widget layout = qt4.QVBoxLayout() layout.setContentsMargins(2,2,2,2) layout.addWidget(scroll) # finally use layout containing items for tab self.widget(tab).setLayout(layout) class FormatDock(qt4.QDockWidget): """A window for formatting the current widget. Provides tabbed formatting properties """ def __init__(self, document, treeedit, *args): qt4.QDockWidget.__init__(self, *args) self.setWindowTitle(_("Formatting - Veusz")) self.setObjectName("veuszformattingdock") self.document = document self.tabwidget = None # update our view when the tree edit window selection changes treeedit.widgetsSelected.connect(self.selectedWidgets) def selectedWidgets(self, widgets, setnsproxy): """Created tabbed widgets for formatting for each subsettings.""" # get current tab (so we can set it afterwards) if self.tabwidget: tab = self.tabwidget.currentIndex() else: tab = 0 # delete old tabwidget if self.tabwidget: self.tabwidget.deleteLater() self.tabwidget = None self.tabwidget = TabbedFormatting(self.document, setnsproxy) self.setWidget(self.tabwidget) # wrap tab from zero to max number tab = max( min(self.tabwidget.count()-1, tab), 0 ) self.tabwidget.setCurrentIndex(tab) class PropertiesDock(qt4.QDockWidget): """A window for editing properties for widgets.""" def __init__(self, document, treeedit, *args): qt4.QDockWidget.__init__(self, *args) self.setWindowTitle(_("Properties - Veusz")) self.setObjectName("veuszpropertiesdock") self.document = document # update our view when the tree edit window selection changes treeedit.widgetsSelected.connect(self.slotWidgetsSelected) # construct scrollable area self.scroll = qt4.QScrollArea() self.scroll.setWidgetResizable(True) self.setWidget(self.scroll) # construct properties list in scrollable area self.proplist = PropertyList(document, showformatsettings=False) self.scroll.setWidget(self.proplist) def slotWidgetsSelected(self, widgets, setnsproxy): """Update properties when selected widgets change.""" self.proplist.updateProperties(setnsproxy, showformatting=False) class TreeEditDock(qt4.QDockWidget): """A dock window presenting widgets as a tree.""" widgetsSelected = qt4.pyqtSignal(list, object) sigPageChanged = qt4.pyqtSignal(int) def __init__(self, document, parentwin): """Initialise dock given document and parent widget.""" qt4.QDockWidget.__init__(self, parentwin) self.parentwin = parentwin self.setWindowTitle(_("Editing - Veusz")) self.setObjectName("veuszeditingwindow") self.selwidgets = [] self.document = document # construct tree self.treemodel = WidgetTreeModel(document) self.treeview = WidgetTreeView(self.treemodel) self.document.sigWiped.connect(self.slotDocumentWiped) # receive change in selection self.treeview.selectionModel().selectionChanged.connect( self.slotTreeItemsSelected) # set tree as main widget self.setWidget(self.treeview) # toolbar to create widgets self.addtoolbar = qt4.QToolBar(_("Insert toolbar - Veusz"), parentwin) # note wrong description!: backwards compatibility self.addtoolbar.setObjectName("veuszeditingtoolbar") # toolbar for editting widgets self.edittoolbar = qt4.QToolBar(_("Edit toolbar - Veusz"), parentwin) self.edittoolbar.setObjectName("veuszedittoolbar") self._constructToolbarMenu() parentwin.addToolBarBreak(qt4.Qt.TopToolBarArea) parentwin.addToolBar(qt4.Qt.TopToolBarArea, self.addtoolbar) parentwin.addToolBar(qt4.Qt.TopToolBarArea, self.edittoolbar) # this sets various things up self.selectWidget(document.basewidget) # update paste button when clipboard changes qt4.QApplication.clipboard().dataChanged.connect( self.updatePasteButton) self.updatePasteButton() def slotDocumentWiped(self): """If the document is wiped, reselect root widget.""" self.selectWidget(self.document.basewidget) def slotTreeItemsSelected(self, current, previous): """New item selected in tree. This updates the list of properties """ # get selected widgets self.selwidgets = swidget = [ self.treemodel.getWidget(idx) for idx in self.treeview.selectionModel().selectedRows() ] if len(swidget) == 0: setnsproxy = None elif len(swidget) == 1: setnsproxy = SettingsProxySingle(self.document, swidget[0].settings, actions=swidget[0].actions) else: setnsproxy = SettingsProxyMulti(self.document, swidget) self._enableCorrectButtons() self._checkPageChange() self.widgetsSelected.emit(swidget, setnsproxy) def contextMenuEvent(self, event): """Bring up context menu.""" # no widgets selected if not self.selwidgets: return m = qt4.QMenu(self) # selection m.addMenu(self.parentwin.menus['edit.select']) m.addSeparator() # actions on widget(s) for act in ('edit.cut', 'edit.copy', 'edit.paste', 'edit.moveup', 'edit.movedown', 'edit.delete', 'edit.rename'): m.addAction(self.vzactions[act]) # allow show or hides of selected widget anyhide = False anyshow = False for w in self.selwidgets: if 'hide' in w.settings: if w.settings.hide: anyshow = True else: anyhide = True for (enabled, menutext, showhide) in ( (anyhide, 'Hide', True), (anyshow, 'Show', False) ): if enabled: m.addSeparator() act = qt4.QAction(menutext, self) def trigfn(showorhide): return lambda: self.slotWidgetHideShow( self.selwidgets, showorhide) act.triggered.connect(trigfn(showhide)) m.addAction(act) m.exec_(self.mapToGlobal(event.pos())) event.accept() def _checkPageChange(self): """Check to see whether page has changed.""" w = None if self.selwidgets: w = self.selwidgets[0] while w is not None and not isinstance(w, widgets.Page): w = w.parent if w is not None: # have page, so check what number we are in basewidget children try: i = self.document.basewidget.children.index(w) self.sigPageChanged.emit(i) except ValueError: pass def _enableCorrectButtons(self): """Make sure the create graph buttons are correctly enabled.""" selw = None if self.selwidgets: selw = self.selwidgets[0] # has to be visible if is to be enabled (yuck) self.vzactions['add.nonorthpoint'].setVisible(True) self.vzactions['add.point3d'].setVisible(True) # check whether each button can have this widget # (or a parent) as parent for wc, action in citems(self.addslots): w = selw while w is not None and not wc.willAllowParent(w): w = w.parent self.vzactions['add.%s' % wc.typename].setEnabled(w is not None) self.vzactions['add.axismenu'].setEnabled( self.vzactions['add.axis'].isEnabled()) # exclusive widgets nonorth = self.vzactions['add.nonorthpoint'].isEnabled() in3dgraph = self.vzactions['add.point3d'].isEnabled() self.vzactions['add.nonorthpoint'].setVisible(nonorth) self.vzactions['add.point3d'].setVisible(in3dgraph) self.vzactions['add.xy'].setVisible(not nonorth and not in3dgraph) self.vzactions['add.nonorthfunc'].setVisible(nonorth) self.vzactions['add.function'].setVisible(not nonorth and not in3dgraph) self.vzactions['add.function3d'].setVisible(in3dgraph) self.vzactions['add.axismenu'].setVisible(not in3dgraph) self.vzactions['add.axis3d'].setVisible(in3dgraph) self.vzactions['add.image'].setVisible(not in3dgraph) self.vzactions['add.surface3d'].setVisible(in3dgraph) self.vzactions['add.contour'].setVisible(not in3dgraph) self.vzactions['add.volume3d'].setVisible(in3dgraph) # certain actions shouldn't work on root isnotroot = not any([isinstance(w, widgets.Root) for w in self.selwidgets]) for act in ('edit.cut', 'edit.copy', 'edit.delete', 'edit.moveup', 'edit.movedown', 'edit.rename'): self.vzactions[act].setEnabled(isnotroot) self.updatePasteButton() def _constructToolbarMenu(self): """Add items to edit/add graph toolbar and menu.""" def slotklass(klass): return lambda: self.slotMakeWidgetButton(klass) iconsize = setting.settingdb['toolbar_size'] self.addtoolbar.setIconSize( qt4.QSize(iconsize, iconsize) ) self.edittoolbar.setIconSize( qt4.QSize(iconsize, iconsize) ) self.addslots = {} self.vzactions = actions = self.parentwin.vzactions for widgettype in ('page', 'grid', 'graph', 'axis', 'axis-broken', 'axis-function', 'xy', 'bar', 'fit', 'function', 'boxplot', 'image', 'contour', 'vectorfield', 'key', 'label', 'colorbar', 'rect', 'ellipse', 'imagefile', 'line', 'polygon', 'polar', 'ternary', 'nonorthpoint', 'nonorthfunc', 'covariance', 'scene3d', 'graph3d', 'function3d', 'point3d', 'axis3d', 'surface3d', 'volume3d'): wc = document.thefactory.getWidgetClass(widgettype) slot = slotklass(wc) self.addslots[wc] = slot actionname = 'add.' + widgettype actions[actionname] = utils.makeAction( self, wc.description, _('Add %s') % widgettype, slot, icon='button_%s' % widgettype) a = utils.makeAction actions.update({ 'edit.cut': a(self, _('Cut the selected widget'), _('Cu&t'), self.slotWidgetCut, icon='veusz-edit-cut', key='Ctrl+X'), 'edit.copy': a(self, _('Copy the selected widget'), _('&Copy'), self.slotWidgetCopy, icon='kde-edit-copy', key='Ctrl+C'), 'edit.paste': a(self, _('Paste widget from the clipboard'), _('&Paste'), self.slotWidgetPaste, icon='kde-edit-paste', key='Ctrl+V'), 'edit.moveup': a(self, _('Move the selected widget up'), _('Move &up'), lambda: self.slotWidgetMove(-1), icon='kde-go-up'), 'edit.movedown': a(self, _('Move the selected widget down'), _('Move d&own'), lambda: self.slotWidgetMove(1), icon='kde-go-down'), 'edit.delete': a(self, _('Remove the selected widget'), _('&Delete'), self.slotWidgetDelete, icon='kde-edit-delete'), 'edit.rename': a(self, _('Renames the selected widget'), _('&Rename'), self.slotWidgetRename, icon='kde-edit-rename'), 'add.shapemenu': a(self, _('Add a shape to the plot'), _('Shape'), self.slotShowShapeMenu, icon='veusz-shape-menu'), 'add.axismenu': a(self, _('Add an axis to the plot'), _('Axis'), None, icon='button_axis'), }) # list of widget-generating actions for menu and toolbar widgetactions = ( 'add.page', 'add.grid', 'add.graph', 'add.axismenu', 'add.axis3d', 'add.xy', 'add.nonorthpoint', 'add.point3d', 'add.bar', 'add.fit', 'add.function', 'add.nonorthfunc', 'add.function3d', 'add.boxplot', 'add.image', 'add.surface3d', 'add.contour', 'add.volume3d', 'add.vectorfield', 'add.key', 'add.label', 'add.colorbar', 'add.polar', 'add.ternary', 'add.scene3d', 'add.graph3d', 'add.covariance', 'add.shapemenu', ) # separate menus for adding shapes and axis types shapemenu = qt4.QMenu() shapemenu.addActions( [actions[act] for act in ( 'add.rect', 'add.ellipse', 'add.line', 'add.imagefile', 'add.polygon', )]) actions['add.shapemenu'].setMenu(shapemenu) axismenu = qt4.QMenu() axismenu.addActions( [actions[act] for act in ( 'add.axis', 'add.axis-broken', 'add.axis-function', )]) actions['add.axismenu'].setMenu(axismenu) actions['add.axismenu'].triggered.connect(actions['add.axis'].trigger) menuitems = ( ('insert', '', widgetactions), ('edit', '', ( 'edit.cut', 'edit.copy', 'edit.paste', 'edit.moveup', 'edit.movedown', 'edit.delete', 'edit.rename' )), ) utils.constructMenus( self.parentwin.menuBar(), self.parentwin.menus, menuitems, actions ) # add actions to toolbar to create widgets utils.addToolbarActions(self.addtoolbar, actions, widgetactions) # add action to toolbar for editing utils.addToolbarActions(self.edittoolbar, actions, ('edit.cut', 'edit.copy', 'edit.paste', 'edit.moveup', 'edit.movedown', 'edit.delete', 'edit.rename')) self.parentwin.menus['edit.select'].aboutToShow.connect( self.updateSelectMenu) def slotMakeWidgetButton(self, wc): """User clicks button to make widget.""" self.makeWidget(wc.typename) def slotShowShapeMenu(self): a = self.vzactions['add.shapemenu'] a.menu().popup( qt4.QCursor.pos() ) def makeWidget(self, widgettype, autoadd=True, name=None): """Called when an add widget button is clicked. widgettype is the type of widget autoadd specifies whether to add default children if name is set this name is used if possible (ie no other children have it) """ # if no widget selected, bomb out if not self.selwidgets: return parent = document.getSuitableParent(widgettype, self.selwidgets[0]) assert parent is not None if name in parent.childnames: name = None # make the new widget and update the document w = self.document.applyOperation( document.OperationWidgetAdd(parent, widgettype, autoadd=autoadd, name=name) ) # select the widget self.selectWidget(w) # bump the feedback statistics utils.feedback.widgetcts[widgettype] += 1 def slotWidgetCut(self): """Cut the selected widget""" self.slotWidgetCopy() self.slotWidgetDelete() def slotWidgetCopy(self): """Copy selected widget to the clipboard.""" if self.selwidgets: mimedata = document.generateWidgetsMime(self.selwidgets) clipboard = qt4.QApplication.clipboard() clipboard.setMimeData(mimedata) def updatePasteButton(self): """Is the data on the clipboard a valid paste at the currently selected widget? If so, enable paste button""" data = document.getClipboardWidgetMime() if len(self.selwidgets) == 0: show = False else: show = document.isWidgetMimePastable(self.selwidgets[0], data) self.vzactions['edit.paste'].setEnabled(show) def doInitialWidgetSelect(self): """Select a sensible initial widget.""" w = self.document.basewidget for i in crange(2): try: c = w.children[0] except IndexError: break if c: w = c self.selectWidget(w) def slotWidgetPaste(self): """Paste something from the clipboard""" data = document.getClipboardWidgetMime() if data: op = document.OperationWidgetPaste(self.selwidgets[0], data) widgets = self.document.applyOperation(op) if widgets: self.selectWidget(widgets[0]) def slotWidgetDelete(self): """Delete the widget selected.""" widgets = self.selwidgets # if no item selected, leave if not widgets: return # get list of widgets in order widgetlist = [] self.document.basewidget.buildFlatWidgetList(widgetlist) # find indices of widgets to be deleted - find one to select after indexes = [widgetlist.index(w) for w in widgets] if -1 in indexes: raise RuntimeError("Invalid widget in list of selected widgets") minindex = min(indexes) # delete selected widget self.document.applyOperation( document.OperationWidgetsDelete(widgets)) # rebuild list widgetlist = [] self.document.basewidget.buildFlatWidgetList(widgetlist) # find next to select if minindex < len(widgetlist): nextwidget = widgetlist[minindex] else: nextwidget = widgetlist[-1] # select the next widget (we have to select root first!) self.selectWidget(self.document.basewidget) self.selectWidget(nextwidget) def slotWidgetRename(self): """Allows the user to rename the selected widget.""" selected = self.treeview.selectedIndexes() if len(selected) != 0: self.treeview.edit(selected[0]) def selectWidget(self, widget, mode='new'): """Select the associated listviewitem for the widget w in the listview. mode: 'new': new selection 'add': add to selection 'toggle': toggle selection """ index = self.treemodel.getWidgetIndex(widget) if index is not None: self.treeview.scrollTo(index) flags = qt4.QItemSelectionModel.Rows | { 'new': ( qt4.QItemSelectionModel.ClearAndSelect | qt4.QItemSelectionModel.Current), 'add': qt4.QItemSelectionModel.Select, 'toggle': qt4.QItemSelectionModel.Toggle, }[mode] self.treeview.selectionModel().select(index, flags) def slotWidgetMove(self, direction): """Move the selected widget up/down in the hierarchy. a is the action (unused) direction is -1 for 'up' and +1 for 'down' """ if not self.selwidgets: return # widget to move w = self.selwidgets[0] # actually move the widget self.document.applyOperation( document.OperationWidgetMoveUpDown(w, direction) ) # re-highlight moved widget self.selectWidget(w) def slotWidgetHideShow(self, widgets, hideshow): """Hide or show selected widgets. hideshow is True for hiding, False for showing """ ops = [ document.OperationSettingSet(w.settings.get('hide'), hideshow) for w in widgets if 'hide' in w.settings ] descr = ('show', 'hide')[hideshow] self.document.applyOperation( document.OperationMultiple(ops, descr=descr)) def checkWidgetSelected(self): """Check widget is selected.""" if len(self.treeview.selectionModel().selectedRows()) == 0: self.selectWidget(self.document.basewidget) def _selectWidgetsTypeAndOrName(self, wtype, wname, root=None): """Select widgets with type or name given. Give None if you don't care for either.""" def selectwidget(path, w): """Select widget if of type or name given.""" if ( (wtype is None or w.typename == wtype) and (wname is None or w.name == wname) ): idx = self.treemodel.getWidgetIndex(w) self.treeview.selectionModel().select( idx, qt4.QItemSelectionModel.Select | qt4.QItemSelectionModel.Rows) self.document.walkNodes(selectwidget, nodetypes=('widget',), root=root) def _selectWidgetSiblings(self, w, wtype): """Select siblings of widget given with type.""" if w.parent is None: return for c in w.parent.children: if c is not w and c.typename == wtype: idx = self.treemodel.getWidgetIndex(c) self.treeview.selectionModel().select( idx, qt4.QItemSelectionModel.Select | qt4.QItemSelectionModel.Rows) def updateSelectMenu(self): """Update edit.select menu.""" menu = self.parentwin.menus['edit.select'] menu.clear() if len(self.selwidgets) == 0: return widget = self.selwidgets[0] wtype = widget.typename name = widget.name # get page widget for selecting on page page = widget while page is not None and page.typename != 'page': page = page.parent menu.addAction( _("All '%s' widgets") % wtype, lambda: self._selectWidgetsTypeAndOrName(wtype, None)) menu.addAction( _("Siblings of '%s' with type '%s'") % (name, wtype), lambda: self._selectWidgetSiblings(widget, wtype)) menu.addAction( _("All '%s' widgets called '%s'") % (wtype, name), lambda: self._selectWidgetsTypeAndOrName(wtype, name)) menu.addAction( _("All widgets called '%s'") % name, lambda: self._selectWidgetsTypeAndOrName(None, name)) if page and page is not widget: menu.addAction( _("All widgets called '%s' on page '%s'") % (name, page.name), lambda: self._selectWidgetsTypeAndOrName( None, name, root=page)) class SettingLabel(qt4.QWidget): """A label to describe a setting. This widget shows the name, a tooltip description, and gives access to the context menu """ # this is emitted when widget is clicked signalClicked = qt4.pyqtSignal(qt4.QPoint) def __init__(self, document, setting, setnsproxy): """Initialise button, passing document, setting, and parent widget.""" qt4.QWidget.__init__(self) self.setFocusPolicy(qt4.Qt.StrongFocus) self.document = document document.signalModified.connect(self.slotDocModified) self.setting = setting self.setnsproxy = setnsproxy self.layout = qt4.QHBoxLayout(self) self.layout.setContentsMargins(2,2,2,2) if setting.usertext: text = setting.usertext else: text = setting.name self.labelicon = qt4.QLabel(text) self.layout.addWidget(self.labelicon) self.iconlabel = qt4.QLabel() self.layout.addWidget(self.iconlabel) self.signalClicked.connect(self.settingMenu) self.infocus = False self.inmouse = False self.inmenu = False # initialise settings self.slotDocModified(True) def mouseReleaseEvent(self, event): """Emit signalClicked(pos) on mouse release.""" self.signalClicked.emit( self.mapToGlobal(event.pos()) ) return qt4.QWidget.mouseReleaseEvent(self, event) def keyReleaseEvent(self, event): """Emit signalClicked(pos) on key release.""" if event.key() == qt4.Qt.Key_Space: self.signalClicked.emit( self.mapToGlobal(self.iconlabel.pos()) ) event.accept() else: return qt4.QWidget.keyReleaseEvent(self, event) # Mark as a qt slot. This fixes a bug where you get C/C++ object # deleted messages when the document emits signalModified but this # widget has been deleted. This can be reproduced by dragging a # widget between two windows, then undoing. @qt4.pyqtSlot(int) def slotDocModified(self, ismodified): """If the document has been modified.""" # update pixmap (e.g. link added/removed) self.updateHighlight() # update tooltip tooltip = self.setting.descr if self.setting.isReference(): paths = self.setting.getReference().getPaths() tooltip += _('\nLinked to: %s') % ', '.join(paths) self.setToolTip(tooltip) # if not default, make label bold f = qt4.QFont(self.labelicon.font()) multivalued = self.setnsproxy.multivalued(self.setting.name) f.setBold( (not self.setting.isDefault()) or multivalued ) f.setItalic( multivalued ) self.labelicon.setFont(f) def updateHighlight(self): """Show drop down arrow if item has focus.""" if self.inmouse or self.infocus or self.inmenu: pixmap = 'downarrow.png' else: if self.setting.isReference() and not self.setting.isDefault(): pixmap = 'link.png' else: pixmap = 'downarrow_blank.png' self.iconlabel.setPixmap(utils.getPixmap(pixmap)) def enterEvent(self, event): """Focus on mouse enter.""" self.inmouse = True self.updateHighlight() return qt4.QWidget.enterEvent(self, event) def leaveEvent(self, event): """Clear focus on mouse leaving.""" self.inmouse = False self.updateHighlight() return qt4.QWidget.leaveEvent(self, event) def focusInEvent(self, event): """Focus if widgets gets focus.""" self.infocus = True self.updateHighlight() return qt4.QWidget.focusInEvent(self, event) def focusOutEvent(self, event): """Lose focus if widget loses focus.""" self.infocus = False self.updateHighlight() return qt4.QWidget.focusOutEvent(self, event) def addCopyToWidgets(self, menu): """Make a menu with list of other widgets in it.""" def getWidgetsOfType(widget, widgettype, widgets=[]): """Recursively build up a list of widgets of the type given.""" for w in widget.children: if w.typename == widgettype: widgets.append(w) getWidgetsOfType(w, widgettype, widgets) # get list of widget paths to copy setting to # this is all widgets of same type widgets = [] setwidget = self.setting.getWidget() if setwidget is None: return getWidgetsOfType(self.document.basewidget, setwidget.typename, widgets) widgets = [w.path for w in widgets if w != setwidget] widgets.sort() # chop off widget part of setting path # this is so we can add on a different widget path # note setpath needs to include Settings part of path too setpath = self.setting.path wpath = self.setting.getWidget().path setpath = setpath[len(wpath):] # includes / def modifyfn(widget): def modify(): """Modify the setting for the widget given.""" wpath = widget + setpath self.document.applyOperation( document.OperationSettingSet(wpath, self.setting.get())) return modify for widget in widgets: action = menu.addAction(widget) action.triggered.connect(modifyfn(widget)) @qt4.pyqtSlot(qt4.QPoint) def settingMenu(self, pos): """Pop up menu for each setting.""" # forces settings to be updated self.parentWidget().setFocus() # get it back straight away self.setFocus() # get widget, with its type and name widget = self.setting.parent while widget is not None and not isinstance(widget, widgets.Widget): widget = widget.parent if widget is None: return self._clickwidget = widget wtype = widget.typename name = widget.name popup = qt4.QMenu(self) popup.addAction( _('Reset to default'), self.actionResetDefault) if self.setting.path[:12] != '/StyleSheet/': # settings not relevant for style sheet items copyto = popup.addMenu(_('Copy to')) copyto.addAction( _("all '%s' widgets") % wtype, self.actionCopyTypedWidgets) copyto.addAction( _("'%s' siblings") % wtype, self.actionCopyTypedSiblings) copyto.addAction( _("'%s' widgets called '%s'") % (wtype, name), self.actionCopyTypedNamedWidgets) copyto.addSeparator() self.addCopyToWidgets(copyto) popup.addAction( _('Use as default style'), self.actionSetStyleSheet) # special actions for references if self.setting.isReference(): popup.addSeparator() popup.addAction( _('Unlink setting'), self.actionUnlinkSetting) self.inmenu = True self.updateHighlight() popup.exec_(pos) self.inmenu = False self.updateHighlight() def actionResetDefault(self): """Reset setting to default.""" self.setnsproxy.resetToDefault(self.setting.name) def actionCopyTypedWidgets(self): """Copy setting to widgets of same type.""" self.document.applyOperation( document.OperationSettingPropagate(self.setting) ) def actionCopyTypedSiblings(self): """Copy setting to siblings of the same type.""" self.document.applyOperation( document.OperationSettingPropagate(self.setting, root=self._clickwidget.parent, maxlevels=1) ) def actionCopyTypedNamedWidgets(self): """Copy setting to widgets with the same name and type.""" self.document.applyOperation( document.OperationSettingPropagate(self.setting, widgetname= self._clickwidget.name) ) def actionUnlinkSetting(self): """Unlink the setting if it is a reference.""" self.document.applyOperation( document.OperationSettingSet(self.setting, self.setting.get()) ) def actionSetStyleSheet(self): """Use the setting as the default in the stylesheet.""" # get name of stylesheet setting sslink = self.setting.getStylesheetLink() # apply operation to change it self.document.applyOperation( document.OperationMultiple( [ document.OperationSettingSet(sslink, self.setting.get()), document.OperationSettingSet(self.setting, self.setting.default) ], descr=_("make default style")) ) veusz-3.0.1/veusz/windows/mainwindow.py0000664000175000017500000015033513302252613017711 0ustar jssjss00000000000000# -*- coding: utf-8 -*- # Copyright (C) 2003 Jeremy S. Sanders # Email: Jeremy Sanders # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ############################################################################## """Implements the main window of the application.""" from __future__ import division, print_function import os import os.path import sys import glob import re import datetime try: import h5py except ImportError: h5py = None from ..compat import cstr, cstrerror, cgetcwd, cbytes from .. import qtall as qt4 from .. import document from .. import utils from ..utils import vzdbus from .. import setting from .. import plugins from . import consolewindow from . import plotwindow from . import treeeditwindow from .datanavigator import DataNavigatorWindow def _(text, disambiguation=None, context='MainWindow'): """Translate text.""" return qt4.QCoreApplication.translate(context, text, disambiguation) # shortcut to this setdb = setting.settingdb class DBusWinInterface(vzdbus.Object): """Simple DBus interface to window for triggering actions.""" interface = 'org.veusz.actions' def __init__(self, actions, index): prefix = '/Windows/%i/Actions' % index # possible exception in dbus means we have to check sessionbus if vzdbus.sessionbus is not None: vzdbus.Object.__init__(self, vzdbus.sessionbus, prefix) self.actions = actions @vzdbus.method(dbus_interface=interface, out_signature='as') def GetActions(self): """Get list of actions which can be activated.""" return sorted(self.actions) @vzdbus.method(dbus_interface=interface, in_signature='s') def TriggerAction(self, action): """Activate action given.""" self.actions[action].trigger() class MainWindow(qt4.QMainWindow): """ The main window class for the application.""" # this is emitted when a dialog is opened by the main window dialogShown = qt4.pyqtSignal(qt4.QWidget) # emitted when a document is opened documentOpened = qt4.pyqtSignal() windows = [] @classmethod def CreateWindow(cls, filename=None, mode='graph'): """Window factory function. If filename is given then that file is loaded into the window. Returns window created """ # create the window, and optionally load a saved file win = cls() win.show() if filename: # load document win.openFileInWindow(filename) else: win.setupDefaultDoc(mode) # try to select first graph of first page win.treeedit.doInitialWidgetSelect() cls.windows.append(win) # check if tutorial wanted (only for graph mode) if not setting.settingdb['ask_tutorial'] and mode=='graph': win.askTutorial() # don't ask again setting.settingdb['ask_tutorial'] = True # check if version check is ok win.askVersionCheck() # periodically do the check win.doVersionCheck() # is it ok to do feedback? win.askFeedbackCheck() # periodically send feedback win.doFeedback() return win def __init__(self, *args): qt4.QMainWindow.__init__(self, *args) self.setAcceptDrops(True) # icon and different size variations self.setWindowIcon( utils.getIcon('veusz') ) # master documenent self.document = document.Document() # filename for document and update titlebar self.filename = '' self.updateTitlebar() # keep a list of references to dialogs self.dialogs = [] # construct menus and toolbars self._defineMenus() # make plot window self.plot = plotwindow.PlotWindow(self.document, self, menu = self.menus['view']) self.setCentralWidget(self.plot) self.plot.showToolbar() # likewise with the tree-editing window self.treeedit = treeeditwindow.TreeEditDock(self.document, self) self.addDockWidget(qt4.Qt.LeftDockWidgetArea, self.treeedit) self.propdock = treeeditwindow.PropertiesDock(self.document, self.treeedit, self) self.addDockWidget(qt4.Qt.LeftDockWidgetArea, self.propdock) self.formatdock = treeeditwindow.FormatDock(self.document, self.treeedit, self) self.addDockWidget(qt4.Qt.LeftDockWidgetArea, self.formatdock) self.datadock = DataNavigatorWindow(self.document, self, self) self.addDockWidget(qt4.Qt.RightDockWidgetArea, self.datadock) # make the console window a dock self.console = consolewindow.ConsoleWindow(self.document, self) self.console.hide() self.interpreter = self.console.interpreter self.addDockWidget(qt4.Qt.BottomDockWidgetArea, self.console) # assemble the statusbar statusbar = self.statusbar = qt4.QStatusBar(self) self.setStatusBar(statusbar) self.updateStatusbar(_('Ready')) # a label for the picker readout self.pickerlabel = qt4.QLabel(statusbar) self._setPickerFont(self.pickerlabel) statusbar.addPermanentWidget(self.pickerlabel) self.pickerlabel.hide() # plot queue - how many plots are currently being drawn self.plotqueuecount = 0 self.plot.sigQueueChange.connect(self.plotQueueChanged) self.plotqueuelabel = qt4.QLabel() self.plotqueuelabel.setToolTip(_("Number of rendering jobs remaining")) statusbar.addWidget(self.plotqueuelabel) self.plotqueuelabel.show() # a label for the cursor position readout self.axisvalueslabel = qt4.QLabel(statusbar) statusbar.addPermanentWidget(self.axisvalueslabel) self.axisvalueslabel.show() self.slotUpdateAxisValues(None) # a label for the page number readout self.pagelabel = qt4.QLabel(statusbar) statusbar.addPermanentWidget(self.pagelabel) self.pagelabel.show() # working directory - use previous one self.dirname = setdb.get('dirname', qt4.QDir.homePath()) if setdb['dirname_usecwd']: self.dirname = cgetcwd() # connect plot signals to main window self.plot.sigUpdatePage.connect(self.slotUpdatePage) self.plot.sigAxisValuesFromMouse.connect(self.slotUpdateAxisValues) self.plot.sigPickerEnabled.connect(self.slotPickerEnabled) self.plot.sigPointPicked.connect(self.slotUpdatePickerLabel) # disable save if already saved self.document.signalModified.connect(self.slotModifiedDoc) # if the treeeditwindow changes the page, change the plot window self.treeedit.sigPageChanged.connect(self.plot.setPageNumber) # if a widget in the plot window is clicked by the user self.plot.sigWidgetClicked.connect(self.treeedit.selectWidget) self.treeedit.widgetsSelected.connect(self.plot.selectedWidgets) # enable/disable undo/redo self.menus['edit'].aboutToShow.connect(self.slotAboutToShowEdit) #Get the list of recently opened files self.populateRecentFiles() self.setupWindowGeometry() self.defineViewWindowMenu() # if document requests it, ask whether an allowed import self.document.sigAllowedImports.connect(self.slotAllowedImportsDoc) # add on dbus interface self.dbusdocinterface = document.DBusInterface(self.document) self.dbuswininterface = DBusWinInterface( self.vzactions, self.dbusdocinterface.index) # has the document already been setup self.documentsetup = False def updateStatusbar(self, text): '''Display text for a set period.''' self.statusBar().showMessage(text, 2000) def dragEnterEvent(self, event): """Check whether event is valid to be dropped.""" if (event.mimeData().hasUrls() and self._getVeuszDropFiles(event)): event.acceptProposedAction() def dropEvent(self, event): """Respond to a drop event on the current window""" if event.mimeData().hasUrls(): files = self._getVeuszDropFiles(event) if files: if self.document.isBlank(): self.openFileInWindow(files[0]) else: self.CreateWindow(files[0]) for filename in files[1:]: self.CreateWindow(filename) def _getVeuszDropFiles(self, event): """Return a list of veusz files from a drag/drop event containing a text/uri-list""" mime = event.mimeData() if not mime.hasUrls(): return [] else: # get list of vsz files dropped urls = [u.toLocalFile() for u in mime.urls()] urls = [u for u in urls if os.path.splitext(u)[1] == '.vsz'] return urls def setupDefaultDoc(self, mode): """Setup default document.""" if not self.documentsetup: # add page and default graph self.document.makeDefaultDoc(mode) # set color theme self.document.basewidget.settings.get( 'colorTheme').set(setting.settingdb['colortheme_default']) # load defaults if set self.loadDefaultStylesheet() self.loadDefaultCustomDefinitions() # done setup self.documentsetup = True def loadDefaultStylesheet(self): """Loads the default stylesheet for the new document.""" filename = setdb['stylesheet_default'] if filename: try: self.document.applyOperation( document.OperationLoadStyleSheet(filename) ) except EnvironmentError as e: qt4.QMessageBox.warning( self, _("Error - Veusz"), _("Unable to load default stylesheet '%s'\n\n%s") % (filename, cstrerror(e))) else: # reset any modified flag self.document.setModified(False) self.document.changeset = 0 def loadDefaultCustomDefinitions(self): """Loads the custom definitions for the new document.""" filename = setdb['custom_default'] if filename: try: self.document.applyOperation( document.OperationLoadCustom(filename) ) except EnvironmentError as e: qt4.QMessageBox.warning( self, _("Error - Veusz"), _("Unable to load custom definitions '%s'\n\n%s") % (filename, cstrerror(e))) else: # reset any modified flag self.document.setModified(False) self.document.changeset = 0 def slotAboutToShowEdit(self): """Enable/disable undo/redo menu items.""" # enable distable, and add appropriate text to describe # the operation being undone/redone canundo = self.document.canUndo() undotext = _('Undo') if canundo: undotext = "%s %s" % (undotext, self.document.historyundo[-1].descr) self.vzactions['edit.undo'].setText(undotext) self.vzactions['edit.undo'].setEnabled(canundo) canredo = self.document.canRedo() redotext = _('Redo') if canredo: redotext = "%s %s" % (redotext, self.document.historyredo[-1].descr) self.vzactions['edit.redo'].setText(redotext) self.vzactions['edit.redo'].setEnabled(canredo) def slotEditUndo(self): """Undo the previous operation""" if self.document.canUndo(): self.document.undoOperation() self.treeedit.checkWidgetSelected() def slotEditRedo(self): """Redo the previous operation""" if self.document.canRedo(): self.document.redoOperation() def slotEditPreferences(self): from ..dialogs.preferences import PreferencesDialog dialog = PreferencesDialog(self) dialog.exec_() def slotEditStylesheet(self): from ..dialogs.stylesheet import StylesheetDialog dialog = StylesheetDialog(self, self.document) self.showDialog(dialog) return dialog def slotEditCustom(self): from ..dialogs.custom import CustomDialog dialog = CustomDialog(self, self.document) self.showDialog(dialog) return dialog def definePlugins(self, pluginlist, actions, menuname): """Create menu items and actions for plugins. pluginlist: list of plugin classes actions: dict of actions to add new actions to menuname: string giving prefix for new menu entries (inside actions) """ def getLoadDialog(pluginkls): def _loadPlugin(): from ..dialogs.plugin import handlePlugin handlePlugin(self, self.document, pluginkls) return _loadPlugin menu = [] for pluginkls in pluginlist: actname = menuname + '.' + '.'.join(pluginkls.menu) text = pluginkls.menu[-1] if pluginkls.has_parameters: text += '...' actions[actname] = utils.makeAction( self, pluginkls.description_short, text, getLoadDialog(pluginkls)) # build up menu from tuple of names menulook = menu namebuild = [menuname] for cmpt in pluginkls.menu[:-1]: namebuild.append(cmpt) name = '.'.join(namebuild) for c in menulook: if c[0] == name: menulook = c[2] break else: menulook.append( [name, cmpt, []] ) menulook = menulook[-1][2] menulook.append(actname) return menu def _defineMenus(self): """Initialise the menus and toolbar.""" # these are actions for main menu toolbars and menus a = utils.makeAction self.vzactions = { 'file.new.menu': a(self, _('New document'), _('New'), None, icon='kde-document-new'), 'file.new.graph': a(self, _('New graph document'), _('&New graph document'), self.slotFileNewGraph, icon='kde-document-new-graph', key='Ctrl+N'), 'file.new.polar': a(self, _('New polar plot document'), _('New polar document'), self.slotFileNewPolar, icon='kde-document-new-polar'), 'file.new.ternary': a(self, _('New ternary plot document'), _('New ternary document'), self.slotFileNewTernary, icon='kde-document-new-ternary'), 'file.new.graph3d': a(self, _('New 3D plot document'), _('New 3D document'), self.slotFileNewGraph3D, icon='kde-document-new-graph3d'), 'file.open': a(self, _('Open a document'), _('&Open...'), self.slotFileOpen, icon='kde-document-open', key='Ctrl+O'), 'file.reload': a(self, _('Reload document from saved version'), _('Reload...'), self.slotFileReload), 'file.save': a(self, _('Save the document'), _('&Save'), self.slotFileSave, icon='kde-document-save', key='Ctrl+S'), 'file.saveas': a(self, _('Save the current document under a new name'), _('Save &As...'), self.slotFileSaveAs, icon='kde-document-save-as'), 'file.print': a(self, _('Print the document'), _('&Print...'), self.slotFilePrint, icon='kde-document-print', key='Ctrl+P'), 'file.export': a(self, _('Export to graphics formats'), _('&Export...'), self.slotFileExport, icon='kde-document-export'), 'file.close': a(self, _('Close current window'), _('Close Window'), self.slotFileClose, icon='kde-window-close', key='Ctrl+W'), 'file.quit': a(self, _('Exit the program'), _('&Quit'), self.slotFileQuit, icon='kde-application-exit', key='Ctrl+Q'), 'edit.undo': a(self, _('Undo the previous operation'), _('Undo'), self.slotEditUndo, icon='kde-edit-undo', key='Ctrl+Z'), 'edit.redo': a(self, _('Redo the previous operation'), _('Redo'), self.slotEditRedo, icon='kde-edit-redo', key='Ctrl+Shift+Z'), 'edit.prefs': a(self, _('Edit preferences'), _('Preferences...'), self.slotEditPreferences, icon='veusz-edit-prefs'), 'edit.custom': a(self, _('Edit custom functions, constants, colors and colormaps'), _('Custom definitions...'), self.slotEditCustom, icon='veusz-edit-custom'), 'edit.stylesheet': a(self, _('Edit stylesheet to change default widget settings'), _('Default styles...'), self.slotEditStylesheet, icon='settings_stylesheet'), 'view.edit': a(self, _('Show or hide edit window'), _('Edit window'), None, checkable=True), 'view.props': a(self, _('Show or hide property window'), _('Properties window'), None, checkable=True), 'view.format': a(self, _('Show or hide formatting window'), _('Formatting window'), None, checkable=True), 'view.console': a(self, _('Show or hide console window'), _('Console window'), None, checkable=True), 'view.datanav': a(self, _('Show or hide data navigator window'), _('Data navigator window'), None, checkable=True), 'view.maintool': a(self, _('Show or hide main toolbar'), _('Main toolbar'), None, checkable=True), 'view.datatool': a(self, _('Show or hide data toolbar'), _('Data toolbar'), None, checkable=True), 'view.viewtool': a(self, _('Show or hide view toolbar'), _('View toolbar'), None, checkable=True), 'view.edittool': a(self, _('Show or hide editing toolbar'), _('Editing toolbar'), None, checkable=True), 'view.addtool': a(self, _('Show or hide insert toolbar'), _('Insert toolbar'), None, checkable=True), 'data.import': a(self, _('Import data into Veusz'), _('&Import...'), self.slotDataImport, icon='kde-vzdata-import'), 'data.edit': a(self, _('Edit and enter new datasets'), _('&Editor...'), lambda: self.slotDataEdit(), icon='kde-edit-veuszedit'), 'data.create': a(self, _('Create new datasets using ranges, parametrically or as functions of existing datasets'), _('&Create...'), self.slotDataCreate, icon='kde-dataset-new-veuszedit'), 'data.create2d': a(self, _('Create new 2D datasets from existing datasets, or as a function of x and y'), _('Create &2D...'), self.slotDataCreate2D, icon='kde-dataset2d-new-veuszedit'), 'data.capture': a(self, _('Capture remote data'), _('Ca&pture...'), self.slotDataCapture, icon='veusz-capture-data'), 'data.filter': a(self, _('Filter data'), _('&Filter...'), self.slotDataFilter, icon='kde-filter'), 'data.histogram': a(self, _('Histogram data'), _('&Histogram...'), self.slotDataHistogram, icon='button_bar'), 'data.reload': a(self, _('Reload linked datasets'), _('&Reload'), self.slotDataReload, icon='kde-view-refresh'), 'help.home': a(self, _('Go to the Veusz home page on the internet'), _('Home page'), self.slotHelpHomepage), 'help.bug': a(self, _('Report a bug on the internet'), _('Suggestions and bugs'), self.slotHelpBug), 'help.update': a(self, _('Download latest version'), _('Download latest version'), self.slotHelpUpdate), 'help.tutorial': a(self, _('An interactive Veusz tutorial'), _('Tutorial'), self.slotHelpTutorial), 'help.about': a(self, _('Displays information about the program'), _('About...'), self.slotHelpAbout, icon='veusz') } # create main toolbar tb = self.maintoolbar = qt4.QToolBar(_("Main toolbar - Veusz"), self) iconsize = setdb['toolbar_size'] tb.setIconSize(qt4.QSize(iconsize, iconsize)) tb.setObjectName('veuszmaintoolbar') self.addToolBar(qt4.Qt.TopToolBarArea, tb) utils.makeMenuGroupSaved( 'file.new.menu', self, self.vzactions, ( 'file.new.graph', 'file.new.graph3d', 'file.new.polar', 'file.new.ternary', ) ) utils.addToolbarActions( tb, self.vzactions, ('file.new.menu', 'file.open', 'file.save', 'file.print', 'file.export')) # data toolbar tb = self.datatoolbar = qt4.QToolBar(_("Data toolbar - Veusz"), self) tb.setIconSize(qt4.QSize(iconsize, iconsize)) tb.setObjectName('veuszdatatoolbar') self.addToolBar(qt4.Qt.TopToolBarArea, tb) utils.addToolbarActions( tb, self.vzactions, ('data.import', 'data.edit', 'data.create', 'data.capture', 'data.filter', 'data.reload')) # menu structure filemenu = [ ['file.new', _('New'), ['file.new.graph', 'file.new.graph3d', 'file.new.polar', 'file.new.ternary']], 'file.open', ['file.filerecent', _('Open &Recent'), []], 'file.reload', '', 'file.save', 'file.saveas', '', 'file.print', 'file.export', '', 'file.close', 'file.quit' ] editmenu = [ 'edit.undo', 'edit.redo', '', ['edit.select', _('&Select'), []], '', 'edit.prefs', 'edit.stylesheet', 'edit.custom', '' ] viewwindowsmenu = [ 'view.edit', 'view.props', 'view.format', 'view.console', 'view.datanav', '', 'view.maintool', 'view.viewtool', 'view.addtool', 'view.edittool' ] viewmenu = [ ['view.viewwindows', _('&Windows'), viewwindowsmenu], '' ] insertmenu = [ ] # load dataset plugins and create menu datapluginsmenu = self.definePlugins( plugins.datasetpluginregistry, self.vzactions, 'data.ops' ) datamenu = [ ['data.ops', _('&Operations'), datapluginsmenu], 'data.import', 'data.edit', 'data.create', 'data.create2d', 'data.capture', 'data.filter', 'data.histogram', 'data.reload', ] helpmenu = [ 'help.home', 'help.bug', 'help.update', '', 'help.tutorial', '', ['help.examples', _('&Example documents'), []], '', 'help.about' ] # load tools plugins and create menu toolsmenu = self.definePlugins( plugins.toolspluginregistry, self.vzactions, 'tools' ) menus = [ ['file', _('&File'), filemenu], ['edit', _('&Edit'), editmenu], ['view', _('&View'), viewmenu], ['insert', _('&Insert'), insertmenu], ['data', _('&Data'), datamenu], ['tools', _('&Tools'), toolsmenu], ['help', _('&Help'), helpmenu], ] self.menus = {} utils.constructMenus(self.menuBar(), self.menus, menus, self.vzactions) # set icon for File->New self.menus['file.new'].setIcon(utils.getIcon('kde-document-new')) self.populateExamplesMenu() def _setPickerFont(self, label): f = label.font() f.setBold(True) f.setPointSizeF(f.pointSizeF() * 1.2) label.setFont(f) def populateExamplesMenu(self): """Add examples to help menu.""" # not cstr here forces to unicode for Python 2, getting # filenames in unicode examples = [ os.path.join(utils.exampleDirectory, f) for f in os.listdir(cstr(utils.exampleDirectory)) if os.path.splitext(f)[1] == ".vsz" ] menu = self.menus["help.examples"] for ex in sorted(examples): name = os.path.splitext(os.path.basename(ex))[0] def _openexample(ex=ex): MainWindow.CreateWindow(ex) a = menu.addAction(name, _openexample) a.setStatusTip(_("Open %s example document") % name) def defineViewWindowMenu(self): """Setup View -> Window menu.""" def viewHideWindow(window): """Toggle window visibility.""" w = window def f(): w.setVisible(not w.isVisible()) return f # set whether windows are visible and connect up to toggle windows self.viewwinfns = [] for win, act in ((self.treeedit, 'view.edit'), (self.propdock, 'view.props'), (self.formatdock, 'view.format'), (self.console, 'view.console'), (self.datadock, 'view.datanav'), (self.maintoolbar, 'view.maintool'), (self.datatoolbar, 'view.datatool'), (self.treeedit.edittoolbar, 'view.edittool'), (self.treeedit.addtoolbar, 'view.addtool'), (self.plot.viewtoolbar, 'view.viewtool')): a = self.vzactions[act] fn = viewHideWindow(win) self.viewwinfns.append( (win, a, fn) ) a.triggered.connect(fn) # needs to update state every time menu is shown self.menus['view.viewwindows'].aboutToShow.connect( self.slotAboutToShowViewWindow) def slotAboutToShowViewWindow(self): """Enable/disable View->Window item check boxes.""" for win, act, fn in self.viewwinfns: act.setChecked(not win.isHidden()) def showDialog(self, dialog): """Show dialog given.""" dialog.dialogFinished.connect(self.deleteDialog) self.dialogs.append(dialog) dialog.show() self.dialogShown.emit(dialog) def deleteDialog(self, dialog): """Remove dialog from list of dialogs.""" try: idx = self.dialogs.index(dialog) del self.dialogs[idx] except ValueError: pass def slotDataImport(self): """Display the import data dialog.""" from ..dialogs import importdialog dialog = importdialog.ImportDialog(self, self.document) self.showDialog(dialog) return dialog def slotDataEdit(self, editdataset=None): """Edit existing datasets. If editdataset is set to a dataset name, edit this dataset """ from ..dialogs import dataeditdialog dialog = dataeditdialog.DataEditDialog(self, self.document) self.showDialog(dialog) if editdataset is not None: dialog.selectDataset(editdataset) return dialog def slotDataCreate(self): """Create new datasets.""" from ..dialogs.datacreate import DataCreateDialog dialog = DataCreateDialog(self, self.document) self.showDialog(dialog) return dialog def slotDataCreate2D(self): """Create new datasets.""" from ..dialogs.datacreate2d import DataCreate2DDialog dialog = DataCreate2DDialog(self, self.document) self.showDialog(dialog) return dialog def slotDataCapture(self): """Capture remote data.""" from ..dialogs.capturedialog import CaptureDialog dialog = CaptureDialog(self.document, self) self.showDialog(dialog) return dialog def slotDataFilter(self): """Filter datasets.""" from ..dialogs.filterdialog import FilterDialog dialog = FilterDialog(self, self.document) self.showDialog(dialog) return dialog def slotDataHistogram(self): """Histogram data.""" from ..dialogs.histodata import HistoDataDialog dialog = HistoDataDialog(self, self.document) self.showDialog(dialog) return dialog def slotDataReload(self): """Reload linked datasets.""" from ..dialogs.reloaddata import ReloadData dialog = ReloadData(self.document, self) self.showDialog(dialog) return dialog def slotHelpHomepage(self): """Go to the veusz homepage.""" qt4.QDesktopServices.openUrl(qt4.QUrl('https://veusz.github.io/')) def slotHelpBug(self): """Go to the veusz bug page.""" qt4.QDesktopServices.openUrl( qt4.QUrl('https://github.com/veusz/veusz/issues') ) def askTutorial(self): """Ask if tutorial wanted.""" retn = qt4.QMessageBox.question( self, _("Veusz Tutorial"), _("Veusz includes a tutorial to help get you started.\n" "Would you like to start the tutorial now?\n" "If not, you can access it later through the Help menu."), qt4.QMessageBox.Yes | qt4.QMessageBox.No ) if retn == qt4.QMessageBox.Yes: self.slotHelpTutorial() def slotHelpTutorial(self): """Show a Veusz tutorial.""" if self.document.isBlank(): # run the tutorial from .tutorial import TutorialDock tutdock = TutorialDock(self.document, self, self) self.addDockWidget(qt4.Qt.RightDockWidgetArea, tutdock) tutdock.show() else: # open up a blank window for tutorial win = self.CreateWindow() win.slotHelpTutorial() def slotHelpAbout(self): """Show about dialog.""" from ..dialogs.aboutdialog import AboutDialog AboutDialog(self).exec_() def askVersionCheck(self, mininterval=2): """Check with user whether to do version checks. This is only done after the user has been using the program for mininterval days """ dayssinceinstall = ( datetime.date.today() - datetime.date(*setting.settingdb['install_date'])).days if (dayssinceinstall # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ############################################################################### """Contains a model and view for handling a tree of widgets.""" from __future__ import division, print_function from ..compat import crange from .. import qtall as qt4 from .. import utils from .. import document def _(text, disambiguation=None, context="WidgetTree"): """Translate text.""" return qt4.QCoreApplication.translate(context, text, disambiguation) class _WidgetNode: """Class to represent widgets in WidgetTreeModel. parent: parent _WidgetNode widget: document widget node is representing children: child nodes data: tuple of data items, so we can see whether the node should be refreshed """ def __init__(self, parent, widget): self.parent = parent self.widget = widget self.children = [] self.data = self.getData() def getData(self): """Get the latest version of the data.""" w = self.widget parenthidden = False if self.parent is None else self.parent.data[3] hidden = parenthidden or ("hide" in w.settings and w.settings.hide) return ( w.name, w.typename, w.userdescription, hidden, ) def __repr__(self): return "<_WidgetNode widget:%s>" % repr(self.widget) class WidgetTreeModel(qt4.QAbstractItemModel): """A model representing the widget tree structure. We hide the actual widgets behind a tree of _WidgetNode objects. The syncTree method synchronises the tree in the model to the tree in the document. It works out which nodes to be deleted, which to be added and which to be moved around. It informs the view that the data are being changed using the standard begin... and end... functions. The synchronisation code is a bit hairy and is hopefully correct. This extra layer is necessary as the model requires that the document underneath it can't be changed until the view knows its about to be changed. """ def __init__(self, document, parent=None): """Initialise using document.""" qt4.QAbstractItemModel.__init__(self, parent) self.document = document document.signalModified.connect(self.slotDocumentModified) document.sigWiped.connect(self.deleteTree) # root node of document self.rootnode = _WidgetNode(None, document.basewidget) # map of widgets to nodes self.widgetnodemap = {self.rootnode.widget: self.rootnode} self.syncTree() def deleteTree(self): """Reset tree contents (for loading docs, etc).""" self.beginRemoveRows( self.nodeIndex(self.rootnode), 0, len(self.rootnode.children)) self.rootnode.widget = self.document.basewidget del self.rootnode.children[:] self.widgetnodemap = {self.rootnode.widget: self.rootnode} self.endRemoveRows() def slotDocumentModified(self): """The document has been changed.""" self.syncTree() def syncTree(self): """Synchronise tree to document.""" docwidgets = set() def recursecollect(widget): """Recursively collect widgets in document.""" docwidgets.add(widget) for child in widget.children: recursecollect(child) recursecollect(self.rootnode.widget) self._recursiveupdate(self.rootnode.widget, docwidgets) def _recursiveupdate(self, widget, docwidgets): """Recursively remove, add and move nodes to correct place. widget: widget to operate below docwidgets: all widgets used in the document """ #print('recurse', widget) node = self.widgetnodemap[widget] # delete non existent child nodes recursively for nch in node.children[::-1]: if nch.widget not in docwidgets: self._recursivedelete(nch) # now iterate over children to see whether anything has # changed for i in crange(len(widget.children)): c = widget.children[i] add = False if c not in self.widgetnodemap: # need to add widget as not in doc #print('add', c, i, node) self.beginInsertRows(self.nodeIndex(node), i, i) self.widgetnodemap[c] = cnode = _WidgetNode(node, c) node.children.insert(i, cnode) self.endInsertRows() add = True elif (i >= len(node.children) or c is not node.children[i].widget or c.parent is not node.children[i].parent.widget): # need to move widget cnode = self.widgetnodemap[c] oldparent = cnode.parent oldrow = oldparent.children.index(cnode) # this code works because when moving, we're always # moving a widget to this position in the list (and # never backwards within this list) oldidx = self.nodeIndex(oldparent) newidx = oldidx if oldparent is node else self.nodeIndex(node) #print('move', oldparent, oldrow, node, i) self.beginMoveRows(oldidx, oldrow, oldrow, newidx, i) del oldparent.children[oldrow] node.children.insert(i, cnode) cnode.parent = node self.endMoveRows() if not add: # update data if changed cnode = self.widgetnodemap[c] data = cnode.getData() if cnode.data != data: index = self.nodeIndex(cnode) cnode.data = data #print('changed', c, data) self.dataChanged.emit(index, index) self._recursiveupdate(c, docwidgets) #print('rec retn') def _recursivedelete(self, node): """Recursively delete node and its children.""" for cnode in node.children[::-1]: self._recursivedelete(cnode) parentnode = node.parent if parentnode is not None: #print('delete', node.widget) row = parentnode.children.index(node) self.beginRemoveRows(self.nodeIndex(parentnode), row, row) del parentnode.children[row] del self.widgetnodemap[node.widget] self.endRemoveRows() def columnCount(self, parent): """Return number of columns of data.""" return 2 def rowCount(self, index): """Return number of rows of children of index.""" if index.isValid(): return len(index.internalPointer().children) else: # always 1 root node return 1 def data(self, index, role): """Return data for the index given. Uses the data from the _WidgetNode class. """ if not index.isValid(): return None column = index.column() data = index.internalPointer().data if role in (qt4.Qt.DisplayRole, qt4.Qt.EditRole): # return text for columns if column == 0: return data[0] elif column == 1: return data[1] elif role == qt4.Qt.DecorationRole: # return icon for first column if column == 0: filename = 'button_%s' % data[1] return utils.getIcon(filename) elif role == qt4.Qt.ToolTipRole: # provide tool tip showing description return data[2] elif role == qt4.Qt.TextColorRole: # show disabled looking text if object or any parent is hidden # return brush for hidden widget text, based on disabled text if data[3]: return qt4.QPalette().brush( qt4.QPalette.Disabled, qt4.QPalette.Text) # return nothing return None def setData(self, index, name, role): """User renames object. This renames the widget.""" if not index.isValid(): return False widget = index.internalPointer().widget # check symbols in name if not utils.validateWidgetName(name): return False # check name not already used if widget.parent.hasChild(name): return False # actually rename the widget self.document.applyOperation( document.OperationWidgetRename(widget, name)) self.dataChanged.emit(index, index) return True def flags(self, index): """What we can do with the item.""" if not index.isValid(): return qt4.Qt.ItemIsEnabled flags = ( qt4.Qt.ItemIsEnabled | qt4.Qt.ItemIsSelectable | qt4.Qt.ItemIsDropEnabled ) if ( index.internalPointer().parent is not None and index.column() == 0 ): # allow items other than root to be edited and dragged flags |= qt4.Qt.ItemIsEditable | qt4.Qt.ItemIsDragEnabled return flags def headerData(self, section, orientation, role): """Return the header of the tree.""" if orientation == qt4.Qt.Horizontal and role == qt4.Qt.DisplayRole: val = ('Name', 'Type')[section] return val return None def nodeIndex(self, node): row = 0 if node.parent is None else node.parent.children.index(node) return self.createIndex(row, 0, node) def index(self, row, column, parent): """Construct an index for a child of parent.""" if parent.isValid(): # normal widget try: child = parent.internalPointer().children[row] except IndexError: return qt4.QModelIndex() else: # root widget child = self.rootnode return self.createIndex(row, column, child) def getWidgetIndex(self, widget): """Returns index for widget specified.""" if widget not in self.widgetnodemap: return None node = self.widgetnodemap[widget] parent = node.parent row = 0 if parent is None else parent.children.index(node) return self.createIndex(row, 0, node) def parent(self, index): """Find the parent of the index given.""" if not index.isValid(): return qt4.QModelIndex() parent = index.internalPointer().parent if parent is None: return qt4.QModelIndex() else: gparent = parent.parent row = 0 if gparent is None else gparent.children.index(parent) return self.createIndex(row, 0, parent) def getSettings(self, index): """Return the settings for the index selected.""" return index.internalPointer().widget.settings def getWidget(self, index): """Get associated widget for index selected.""" return index.internalPointer().widget def removeRows(self, row, count, parentindex): """Remove widgets from parent. This is used by the mime dragging and dropping """ if not parentindex.isValid(): return parent = self.getWidget(parentindex) # make an operation to delete the rows deleteops = [] for w in parent.children[row:row+count]: deleteops.append( document.OperationWidgetDelete(w) ) op = document.OperationMultiple(deleteops, descr=_("remove widget(s)")) self.document.applyOperation(op) return True def supportedDropActions(self): """Supported drag and drop actions.""" return qt4.Qt.MoveAction | qt4.Qt.CopyAction def mimeData(self, indexes): """Get mime data for indexes.""" widgets = [idx.internalPointer().widget for idx in indexes] return document.generateWidgetsMime(widgets) def mimeTypes(self): """Accepted mime types.""" return [document.widgetmime] def dropMimeData(self, mimedata, action, row, column, parentindex): """User drags and drops widget.""" if action == qt4.Qt.IgnoreAction: return True data = document.getWidgetMime(mimedata) if data is None: return False if parentindex.isValid(): parent = self.getWidget(parentindex) else: parent = self.document.basewidget # check parent supports child if not document.isMimeDropable(parent, data): return False # work out where row will be pasted startrow = row if row == -1: startrow = len(parent.children) op = document.OperationWidgetPaste(parent, data, index=startrow) self.document.applyOperation(op) return True class WidgetTreeView(qt4.QTreeView): """A model view for viewing the widgets.""" def __init__(self, model, *args): qt4.QTreeView.__init__(self, *args) self.setModel(model) self.expandAll() # stretch header hdr = self.header() hdr.setStretchLastSection(False) hdr.setSectionResizeMode(0, qt4.QHeaderView.Stretch) hdr.setSectionResizeMode(1, qt4.QHeaderView.Custom) # setup drag and drop self.setSelectionMode(qt4.QAbstractItemView.ExtendedSelection) self.setDragEnabled(True) self.viewport().setAcceptDrops(True) self.setDropIndicatorShown(True) def testModifier(self, e): """Look for keyboard modifier for copy or move.""" if e.keyboardModifiers() & qt4.Qt.ControlModifier: e.setDropAction(qt4.Qt.CopyAction) else: e.setDropAction(qt4.Qt.MoveAction) def handleInternalMove(self, event): """Handle a move inside treeview.""" # make sure qt doesn't handle this event.setDropAction(qt4.Qt.IgnoreAction) event.ignore() if not self.viewport().rect().contains(event.pos()): return # get widget at event position index = self.indexAt(event.pos()) if not index.isValid(): index = self.rootIndex() # adjust according to drop indicator position row = -1 posn = self.dropIndicatorPosition() if posn == qt4.QAbstractItemView.AboveItem: row = index.row() index = index.parent() elif posn == qt4.QAbstractItemView.BelowItem: row = index.row() + 1 index = index.parent() if index.isValid(): parent = self.model().getWidget(index) data = document.getWidgetMime(event.mimeData()) if document.isMimeDropable(parent, data): # move the widget! parentpath = parent.path widgetpaths = document.getMimeWidgetPaths(data) ops = [] r = row for path in widgetpaths: ops.append( document.OperationWidgetMove(path, parentpath, r) ) if r >= 0: r += 1 self.model().document.applyOperation( document.OperationMultiple(ops, descr='move')) event.ignore() def dropEvent(self, e): """When an object is dropped on the view.""" self.testModifier(e) if e.source() is self and e.dropAction() == qt4.Qt.MoveAction: self.handleInternalMove(e) qt4.QTreeView.dropEvent(self, e) def dragMoveEvent(self, e): """Make items move by default and copy if Ctrl is held down.""" self.testModifier(e) qt4.QTreeView.dragMoveEvent(self, e) veusz-3.0.1/veusz/windows/__init__.py0000664000175000017500000000165713161413406017300 0ustar jssjss00000000000000# Copyright (C) 2008 Jeremy S. Sanders # Email: Jeremy Sanders # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ############################################################################## """Veusz windows module.""" veusz-3.0.1/veusz/windows/simplewindow.py0000664000175000017500000000513213161413406020252 0ustar jssjss00000000000000# Copyright (C) 2005 Jeremy S. Sanders # Email: Jeremy Sanders # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ############################################################################## from __future__ import division from .. import qtall as qt4 from .. import document from .. import dataimport from . import plotwindow """ A simple window class for wrapping a plotwindow """ class SimpleWindow(qt4.QMainWindow): """ The main window class for the application.""" def __init__(self, title, doc=None): qt4.QMainWindow.__init__(self) self.setWindowTitle(title) self.document = doc if not doc: self.document = document.Document() self.plot = plotwindow.PlotWindow(self.document, self) self.toolbar = None self.setCentralWidget( self.plot ) def enableToolbar(self, enable=True): """Enable or disable the zoom toolbar in this window.""" if self.toolbar is None and enable: self.toolbar = self.plot.createToolbar(self, None) self.toolbar.show() if self.toolbar is not None and not enable: self.toolbar.close() self.toolbar = None def setZoom(self, zoom): """Zoom(zoom) Set the plot zoom level: This is a number to for the zoom from 1:1 or 'page': zoom to page 'width': zoom to fit width 'height': zoom to fit height """ if zoom == 'page': self.plot.slotViewZoomPage() elif zoom == 'width': self.plot.slotViewZoomWidth() elif zoom == 'height': self.plot.slotViewZoomHeight() else: self.plot.setZoomFactor(zoom) def setAntiAliasing(self, ison): """AntiAliasing(ison) Switches on or off anti aliasing in the plot.""" self.plot.antialias = ison self.plot.actionForceUpdate() veusz-3.0.1/veusz/windows/tutorial.py0000664000175000017500000007101013165473120017375 0ustar jssjss00000000000000# -*- coding: utf-8 -*- # Copyright (C) 2011 Jeremy S. Sanders # Email: Jeremy Sanders # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ############################################################################### from __future__ import division import os.path import sip from .. import qtall as qt4 from .. import utils from .. import setting def _(text, disambiguation=None, context="Tutorial"): """Translate text.""" return qt4.QCoreApplication.translate(context, text, disambiguation) class TutorialStep(qt4.QObject): nextStep = qt4.pyqtSignal() def __init__(self, text, mainwin, nextstep=None, flash=None, disablenext=False, closestep=False, nextonsetting=None, nextonselected=None): """ nextstep is class next TutorialStep class to use If flash is set, flash widget disablenext: wait until nextStep is emitted before going to next slide closestep: add a close button nextonsetting: (setnpath, lambda val: ok) - check setting to go to next slide nextonselected: go to next if widget with name is selected """ qt4.QObject.__init__(self) self.text = text self.nextstep = nextstep self.flash = flash self.disablenext = disablenext self.closestep = closestep self.mainwin = mainwin self.nextonsetting = nextonsetting if nextonsetting is not None: mainwin.document.signalModified.connect(self.slotNextSetting) self.nextonselected = nextonselected if nextonselected is not None: mainwin.treeedit.widgetsSelected.connect(self.slotWidgetsSelected) def slotNextSetting(self, *args): """Check setting to emit next.""" try: setn = self.mainwin.document.resolveSettingPath( None, self.nextonsetting[0]).get() if self.nextonsetting[1](setn): self.nextStep.emit() except ValueError: pass def slotWidgetsSelected(self, widgets, *args): """Go to next page if widget selected.""" if len(widgets) == 1 and widgets[0].name == self.nextonselected: self.nextStep.emit() ########################## ## Introduction to widgets class StepIntro(TutorialStep): def __init__(self, mainwin): TutorialStep.__init__( self, _('''

        Welcome to Veusz!

        This tutorial aims to get you working with Veusz as quickly as possible.

        You can close this tutorial at any time using the close button to the top-right of this panel. The tutorial can be replayed in the help menu.

        Press Next to go to the next step

        '''), mainwin, nextstep=StepWidgets1) class StepWidgets1(TutorialStep): def __init__(self, mainwin): TutorialStep.__init__( self, _('''

        Widgets

        Plots in Veusz are constructed from widgets. Different types of widgets are used to make different parts of a plot. For example, there are widgets for axes, for a graph, for plotting data and for plotting functions.

        There are also special widgets. The grid widget arranges graphs inside it in a grid arrangement.

        '''), mainwin, nextstep=StepWidgets2) class StepWidgets2(TutorialStep): def __init__(self, mainwin): TutorialStep.__init__( self, _('''

        Widgets can often be placed inside each other. For instance, a graph widget is placed in a page widget or a grid widget. Plotting widgets are placed in graph widget.

        You can have multiple widgets of different types. For example, you can have several graphs on the page, optionally arranged in a grid. Several plotting widgets and axis widgets can be put in a graph.

        '''), mainwin, nextstep=StepWidgetWin) class StepWidgetWin(TutorialStep): def __init__(self, mainwin): t = mainwin.treeedit TutorialStep.__init__( self, _('''

        Widget editing

        The flashing window is the Editing window, which shows the widgets currently in the plot in a hierarchical tree. Each widget has a name (the left column) and a type (the right column).

        Press Next to continue.

        '''), mainwin, nextstep=StepWidgetWinExpand, flash=t) class StepWidgetWinExpand(TutorialStep): def __init__(self, mainwin): TutorialStep.__init__( self, _('''

        The graph widget is the currently selected widget.

        Expand the graph widget - click the arrow or plus to its left in the editing window - and select the x axis widget.

        '''), mainwin, disablenext=True, nextonselected='x', nextstep=StepPropertiesWin) class StepPropertiesWin(TutorialStep): def __init__(self, mainwin): TutorialStep.__init__( self, _('''

        Widget properties

        This window shows the properties of the currently selected widget, the x axis widget of the graph.

        Enter a new label for the widget, by clicking in the text edit box to the right of "Label", typing some text and press the Enter key.

        '''), mainwin, flash = mainwin.propdock, disablenext = True, nextonsetting = ('/page1/graph1/x/label', lambda val: val != ''), nextstep = StepPropertiesWin2) class StepPropertiesWin2(TutorialStep): def __init__(self, mainwin): TutorialStep.__init__( self, _('''

        Notice that the x axis label of your plot has now been updated. Veusz supports LaTeX style formatting for labels, so you could include superscripts, subscripts and fractions.

        Other important axis properties include the minimum, maximum values of the axis and whether the axis is logarithmic.

        Click Next to continue.

        '''), mainwin, nextstep=WidgetAdd) class WidgetAdd(TutorialStep): def __init__(self, mainwin): TutorialStep.__init__( self, _('''

        Adding widgets

        The flashing Add Widget toolbar and the Insert menu add widgets to the document. New widgets are inserted in the currently selected widget, if possible, or its parents.

        Hold your mouse pointer over one of the toolbar buttons to see a description of a widget type.

        Press Next to continue.

        '''), mainwin, flash=mainwin.treeedit.addtoolbar, nextstep=FunctionAdd ) class FunctionAdd(TutorialStep): def __init__(self, mainwin): TutorialStep.__init__( self, _('''

        Add a function

        We will now add a function plotting widget to the current graph.

        Click on the flashing icon, or go to the Insert menu and choosing "Add function".

        '''), mainwin, flash=mainwin.treeedit.addtoolbar.widgetForAction( mainwin.vzactions['add.function']), disablenext=True, nextonsetting = ('/page1/graph1/function1/function', lambda val: val != ''), nextstep=FunctionSet) class FunctionSet(TutorialStep): def __init__(self, mainwin): TutorialStep.__init__( self, _('''

        You have now added a function widget to the graph widget. By default function widgets plot y=x.

        Go to the Function property and change the function to be x**2, plotting x squared.

        (Veusz uses Python syntax for its functions, so the power operator is **, rather than ^)

        '''), mainwin, nextonsetting = ('/page1/graph1/function1/function', lambda val: val.strip() == 'x**2'), disablenext = True, nextstep=FunctionFormatting) class FunctionFormatting(TutorialStep): def __init__(self, mainwin): TutorialStep.__init__( self, _('''

        Formatting

        Widgets have a number of formatting options. The Formatting window (flashing) shows the options for the currently selected widget, here the function widget.

        Press Next to continue

        '''), mainwin, flash=mainwin.formatdock, nextstep=FunctionFormatLine) class FunctionFormatLine(TutorialStep): def __init__(self, mainwin): tb = mainwin.formatdock.tabwidget.tabBar() label = qt4.QLabel(" ", tb) tb.setTabButton(1, qt4.QTabBar.LeftSide, label) TutorialStep.__init__( self, _('''

        Different types of formatting properties are grouped under separate tables. The options for drawing the function line are grouped under the flashing Line tab (%s).

        Click on the Line tab to continue.

        ''') % utils.pixmapAsHtml(utils.getPixmap('settings_plotline.png')), mainwin, flash=label, disablenext=True, nextstep=FunctionLineFormatting) tb.currentChanged[int].connect(self.slotCurrentChanged) def slotCurrentChanged(self, idx): if idx == 1: self.nextStep.emit() class FunctionLineFormatting(TutorialStep): def __init__(self, mainwin): TutorialStep.__init__( self, _('''

        Veusz lets you choose a line style, thickness and color for the function line.

        Choose a new line color for the line.

        '''), mainwin, disablenext=True, nextonsetting = ('/page1/graph1/function1/Line/color', lambda val: val.strip() != 'black'), nextstep=DataStart) ########### ## Datasets class DataStart(TutorialStep): def __init__(self, mainwin): TutorialStep.__init__( self, _('''

        Datasets

        Many widgets in Veusz plot datasets. Datasets can be imported from files, entered manually or created from existing datasets using operations or expressions.

        Imported data can be linked to an external file or embedded in the document.

        Press Next to continue

        '''), mainwin, nextstep=DataImport) class DataImport(TutorialStep): def __init__(self, mainwin): TutorialStep.__init__( self, _('''

        Importing data

        Let us start by importing data.

        Click the flashing Data Import icon, or choose "Import..." From the Data menu.

        '''), mainwin, flash=mainwin.datatoolbar.widgetForAction( mainwin.vzactions['data.import']), disablenext=True, nextstep=DataImportDialog) # make sure we have the default delimiters for k in ( 'importdialog_csvdelimitercombo_HistoryCombo', 'importdialog_csvtextdelimitercombo_HistoryCombo' ): if k in setting.settingdb: del setting.settingdb[k] mainwin.dialogShown.connect(self.slotDialogShown) def slotDialogShown(self, dialog): """Called when a dialog is opened in the main window.""" from ..dialogs.importdialog import ImportDialog if isinstance(dialog, ImportDialog): # make life easy by sticking in filename dialog.slotReset() dialog.filenameedit.setText( os.path.join(utils.exampleDirectory, 'tutorialdata.csv')) # and choosing tab dialog.guessImportTab() # get rid of existing values self.nextStep.emit() class DataImportDialog(TutorialStep): def __init__(self, mainwin): TutorialStep.__init__( self, _('''

        This is the data import dialog. In this tutorial, we have selected an example CSV (comma separated value) file for you, but you would normally browse to find your data file.

        This example file defines three datasets, alpha, beta and gamma, entered as columns in the CSV file.

        Press Next to continue

        '''), mainwin, nextstep=DataImportDialog2) class DataImportDialog2(TutorialStep): def __init__(self, mainwin): TutorialStep.__init__( self, _('''

        Veusz will try to guess the datatype - numeric, text or date - from the data in the file or you can specify it manually.

        Several different data formats are supported in Veusz and plugins can be defined to import any data format. The Link option links data to the original file.

        Click the Import button in the dialog.

        '''), mainwin, nextstep=DataImportDialog3, disablenext=True) mainwin.document.signalModified.connect(self.slotDocModified) def slotDocModified(self): if 'alpha' in self.mainwin.document.data: self.nextStep.emit() class DataImportDialog3(TutorialStep): def __init__(self, mainwin): TutorialStep.__init__( self, _('''

        Notice how Veusz has loaded the three different datasets from the file. You could carry on importing new datasets from the Import dialog box or reopen it later.

        Close the Import dialog box.

        '''), mainwin, disablenext=True, nextstep=DataImportDialog4) self.timer = qt4.QTimer() self.timer.timeout.connect(self.slotTimeout) self.timer.start(200) def slotTimeout(self): from ..dialogs.importdialog import ImportDialog closed = True for dialog in self.mainwin.dialogs: if isinstance(dialog, ImportDialog): closed = False if closed: # move forward if no import dialog open self.nextStep.emit() class DataImportDialog4(TutorialStep): def __init__(self, mainwin): TutorialStep.__init__( self, _('''

        The Data viewing window (flashing) shows the currently loaded datasets in the document.

        Hover your mouse over datasets to get information about them. You can see datasets in more detail in the Data Edit dialog box.

        Click Next to continue

        '''), mainwin, flash=mainwin.datadock, nextstep=AddXYPlotter) ############## ## XY plotting class AddXYPlotter(TutorialStep): def __init__(self, mainwin): TutorialStep.__init__( self, _('''

        Plotting data

        The point plotting widget plots datasets loaded in Veusz.

        The flashing icon adds a point plotting (xy) widget. Click on this, or go to the Insert menu and choose "Add xy".

        '''), mainwin, flash=mainwin.treeedit.addtoolbar.widgetForAction( mainwin.vzactions['add.xy']), disablenext=True, nextonsetting = ('/page1/graph1/xy1/xData', lambda val: val != ''), nextstep=SetXY_X) class SetXY_X(TutorialStep): def __init__(self, mainwin): TutorialStep.__init__( self, _('''

        The datasets to be plotted are in the widget's properties.

        Change the "X data" setting to be the alpha dataset. You can choose this from the drop down menu or type it.

        '''), mainwin, disablenext=True, nextonsetting = ('/page1/graph1/xy1/xData', lambda val: val == 'alpha'), nextstep=SetXY_Y) class SetXY_Y(TutorialStep): def __init__(self, mainwin): TutorialStep.__init__( self, _('''

        Change the "Y data" setting to be the beta dataset.

        '''), mainwin, disablenext=True, nextonsetting = ('/page1/graph1/xy1/yData', lambda val: val == 'beta'), nextstep=SetXYLine) class SetXYLine(TutorialStep): def __init__(self, mainwin): TutorialStep.__init__( self, _('''

        Veusz has now plotted the data on the graph. You can manipulate how the data are shown using the formatting settings.

        Make sure that the line Formatting tab (%s) for the widget is selected.

        Click on the check box next to the Hide option at the bottom, to hide the line plotted between the data points.

        ''') % utils.pixmapAsHtml(utils.getPixmap('settings_plotline.png')), mainwin, disablenext=True, nextonsetting = ('/page1/graph1/xy1/PlotLine/hide', lambda val: val), nextstep=SetXYFill) class SetXYFill(TutorialStep): def __init__(self, mainwin): TutorialStep.__init__( self, _('''

        Now we will change the point color.

        Click on the "Marker fill (%s)" formatting tab. Change the fill color of the plotted data.

        ''') % utils.pixmapAsHtml(utils.getPixmap('settings_plotmarkerfill.png')), mainwin, disablenext=True, nextonsetting = ('/page1/graph1/xy1/MarkerFill/color', lambda val: val != 'black'), nextstep=AddXY2nd) class AddXY2nd(TutorialStep): def __init__(self, mainwin): TutorialStep.__init__( self, _('''

        Adding a second dataset

        We will now plot dataset alpha against gamma on the same graph.

        Add a second point plotting (xy) widget using the flashing icon, or go to the Insert menu and choose "Add xy".

        '''), mainwin, flash=mainwin.treeedit.addtoolbar.widgetForAction( mainwin.vzactions['add.xy']), disablenext=True, nextonsetting = ('/page1/graph1/xy2/xData', lambda val: val != ''), nextstep=AddXY2nd_2) class AddXY2nd_2(TutorialStep): def __init__(self, mainwin): TutorialStep.__init__( self, _('''

        Change the "X data" setting to be the alpha dataset.

        '''), mainwin, disablenext=True, nextonsetting = ('/page1/graph1/xy2/xData', lambda val: val == 'alpha'), nextstep=AddXY2nd_3) class AddXY2nd_3(TutorialStep): def __init__(self, mainwin): TutorialStep.__init__( self, _('''

        Next, change the "Y data" setting to be the gamma dataset.

        '''), mainwin, disablenext=True, nextonsetting = ('/page1/graph1/xy2/yData', lambda val: val == 'gamma'), nextstep=AddXY2nd_4) class AddXY2nd_4(TutorialStep): def __init__(self, mainwin): TutorialStep.__init__( self, _('''

        We can fill regions under plots using the Fill Below Formatting tab (%s).

        Go to this tab, and unselect the "Hide edge fill" option.

        ''') % utils.pixmapAsHtml(utils.getPixmap('settings_plotfillbelow.png')), mainwin, disablenext=True, nextonsetting = ('/page1/graph1/xy2/FillBelow/hide', lambda val: not val), nextstep=File1) class File1(TutorialStep): def __init__(self, mainwin): TutorialStep.__init__( self, _('''

        Saving

        The document can be saved under the File menu, choosing "Save as...", or by clicking on the Save icon (flashing).

        Veusz documents are simple text files which can be easily modified outside the program.

        Click Next to continue

        '''), mainwin, flash=mainwin.maintoolbar.widgetForAction( mainwin.vzactions['file.save']), nextstep=File2) class File2(TutorialStep): def __init__(self, mainwin): TutorialStep.__init__( self, _('''

        Exporting

        The document can be exported in scalable (EPS, PDF, SVG and EMF) or bitmap formats.

        The "Export..." command under the File menu exports the selected page. Alternatively, click on the Export icon (flashing).

        Click Next to continue

        '''), mainwin, flash=mainwin.maintoolbar.widgetForAction( mainwin.vzactions['file.export']), nextstep=Cut1, ) class Cut1(TutorialStep): def __init__(self, mainwin): TutorialStep.__init__( self, _('''

        Cut and paste

        Widgets can be cut and pasted to manipulate the document.

        Select the "graph1" widget in the Editing window.

        '''), mainwin, disablenext=True, nextonselected='graph1', nextstep=Cut2) class Cut2(TutorialStep): def __init__(self, mainwin): TutorialStep.__init__( self, _('''

        Now click the Cut icon (flashing) or choose "Cut" from the Edit menu.

        This copies the currently selected widget to the clipboard and deletes it from the document.

        '''), mainwin, disablenext=True, flash=mainwin.treeedit.edittoolbar.widgetForAction( mainwin.vzactions['edit.cut']), nextstep=AddGrid) mainwin.document.signalModified.connect(self.slotCheckDelete) def slotCheckDelete(self, *args): d = self.mainwin.document try: d.resolveWidgetPath(None, '/page1/graph1') except ValueError: # success! self.nextStep.emit() class AddGrid(TutorialStep): def __init__(self, mainwin): TutorialStep.__init__( self, _('''

        Adding a grid

        Now we will add a grid widget to paste the graph back into.

        Click on the flashing Grid widget icon, or choose "Add grid" from the Insert menu.

        '''), mainwin, flash=mainwin.treeedit.addtoolbar.widgetForAction( mainwin.vzactions['add.grid']), disablenext=True, nextonsetting = ('/page1/grid1/rows', lambda val: val != ''), nextstep=Paste1) class Paste1(TutorialStep): def __init__(self, mainwin): TutorialStep.__init__( self, _('''

        Now click the Paste icon (flashing) or choose "Paste" from the Edit menu.

        This pastes back the widget from the clipboard.

        '''), mainwin, disablenext=True, flash=mainwin.treeedit.edittoolbar.widgetForAction( mainwin.vzactions['edit.paste']), nextonsetting = ('/page1/grid1/graph1/leftMargin', lambda val: val != ''), nextstep=Paste2) class Paste2(TutorialStep): def __init__(self, mainwin): TutorialStep.__init__( self, _('''

        For a second time, click the Paste icon (flashing) or choose "Paste" from the Edit menu.

        This adds a second copy of the original graph to the grid.

        '''), mainwin, disablenext=True, flash=mainwin.treeedit.edittoolbar.widgetForAction( mainwin.vzactions['edit.paste']), nextonsetting = ('/page1/grid1/graph2/leftMargin', lambda val: val != ''), nextstep=Paste3) class Paste3(TutorialStep): def __init__(self, mainwin): TutorialStep.__init__( self, _('''

        Having the graphs side-by-side looks a bit messy. We would like to change the graphs to be arranged in rows.

        Navigate to the grid1 widget properties. Change the number of columns to 1.

        '''), mainwin, disablenext=True, nextonsetting = ('/page1/grid1/columns', lambda val: val == 1), nextstep=Paste4) class Paste4(TutorialStep): def __init__(self, mainwin): TutorialStep.__init__( self, _('''

        We could now adjust the margins of the graphs and the grid.

        Axes can also be shared by the graphs of the grid by moving them into the grid widget. This shares the same axis scale for graphs.

        Click Next to continue

        '''), mainwin, nextstep=EndStep) class EndStep(TutorialStep): def __init__(self, mainwin): TutorialStep.__init__( self, _('''

        The End

        Thank you for working through this Veusz tutorial. We hope you enjoy using Veusz!

        Please send comments, bug reports and suggestions to the developers via the mailing list.

        You can try this tutorial again from the Help menu.

        '''), mainwin, closestep=True, disablenext=True) class TutorialDock(qt4.QDockWidget): '''A dock tutorial window.''' def __init__(self, document, mainwin, *args): qt4.QDockWidget.__init__(self, *args) self.setAttribute(qt4.Qt.WA_DeleteOnClose) self.setMinimumHeight(300) self.setWindowTitle('Tutorial - Veusz') self.setObjectName('veusztutorialwindow') self.setStyleSheet('background: lightyellow;') self.document = document self.mainwin = mainwin self.layout = l = qt4.QVBoxLayout() txtdoc = qt4.QTextDocument(self) txtdoc.setDefaultStyleSheet( "p.usercmd { color: blue; } " "h1 { font-size: x-large;} " "code { color: green;} " ) self.textedit = qt4.QTextEdit(readOnly=True) self.textedit.setDocument(txtdoc) l.addWidget(self.textedit) self.buttonbox = qt4.QDialogButtonBox() self.nextb = self.buttonbox.addButton( 'Next', qt4.QDialogButtonBox.ActionRole) self.nextb.clicked.connect(self.slotNext) l.addWidget(self.buttonbox) # have to use a separate widget as dialog already has layout self.widget = qt4.QWidget() self.widget.setLayout(l) self.setWidget(self.widget) # timer for controlling flashing self.flashtimer = qt4.QTimer(self) self.flashtimer.timeout.connect(self.slotFlashTimeout) self.flash = self.oldflash = None self.flashon = False self.flashct = 0 self.flashtimer.start(500) self.changeStep(StepIntro) def ensureShowFlashWidgets(self): '''Ensure we can see the widgets flashing.''' w = self.flash while w is not None: w.show() w = w.parent() def changeStep(self, stepklass): '''Apply the next step.''' # this is the current text self.step = stepklass(self.mainwin) # listen to step for next step self.step.nextStep.connect(self.slotNext) # update text self.textedit.setHtml(self.step.text) # handle requests for flashing self.flashct = 20 self.flashon = True self.flash = self.step.flash if self.flash is not None: self.ensureShowFlashWidgets() # enable/disable next button self.nextb.setEnabled(not self.step.disablenext) # add a close button if requested if self.step.closestep: closeb = self.buttonbox.addButton( 'Close', qt4.QDialogButtonBox.ActionRole) closeb.clicked.connect(self.close) # work around C/C++ object deleted @qt4.pyqtSlot() def slotFlashTimeout(self): '''Handle flashing of UI components.''' # because we're flashing random UI components, the C++ object # might be deleted, so we have to check before doing things to # it: hence the sip.isdeleted if ( self.flash is not self.oldflash and self.oldflash is not None and not sip.isdeleted(self.oldflash) ): # clear any flashing on previous widget self.oldflash.setStyleSheet('') self.oldflash = None if self.flash is not None and not sip.isdeleted(self.flash): # set flash state and toggle variable if self.flashon: self.flash.setStyleSheet('background: yellow;') else: self.flash.setStyleSheet('') self.flashon = not self.flashon self.oldflash = self.flash # stop flashing after N iterations self.flashct -= 1 if self.flashct == 0: self.flash = None def slotNext(self): """Move to the next page of the tutorial.""" nextstepklass = self.step.nextstep if nextstepklass is not None: self.changeStep( nextstepklass ) veusz-3.0.1/veusz/windows/plotwindow.py0000664000175000017500000013525313302252613017745 0ustar jssjss00000000000000# plotwindow.py # the main window for showing plots # Copyright (C) 2004 Jeremy S. Sanders # Email: Jeremy Sanders # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ############################################################################## from __future__ import division import sys import traceback from ..compat import crange from .. import qtall as qt4 import numpy as N from .. import setting from ..dialogs import exceptiondialog from .. import document from .. import utils from .. import widgets def _(text, disambiguation=None, context='PlotWindow'): """Translate text.""" return qt4.QCoreApplication.translate(context, text, disambiguation) class PickerCrosshairItem( qt4.QGraphicsPathItem ): """The picker cross widget: it moves from point to point and curve to curve with the arrow keys, and hides itself when it looses focus""" def __init__(self, parent=None): path = qt4.QPainterPath() path.addRect(-4, -4, 8, 8) path.addRect(-5, -5, 10, 10) path.moveTo(-8, 0) path.lineTo(8, 0) path.moveTo(0, -8) path.lineTo(0, 8) qt4.QGraphicsPathItem.__init__(self, path, parent) self.setBrush(qt4.QBrush(qt4.Qt.black)) self.setFlags(self.flags() | qt4.QGraphicsItem.ItemIsFocusable) def paint(self, painter, option, widget): """Override this to enforce the global antialiasing setting""" aa = setting.settingdb['plot_antialias'] painter.save() painter.setRenderHint(qt4.QPainter.Antialiasing, aa) qt4.QGraphicsPathItem.paint(self, painter, option, widget) painter.restore() def focusOutEvent(self, event): qt4.QGraphicsPathItem.focusOutEvent(self, event) self.hide() class RenderControl(qt4.QObject): """Object for rendering plots in a separate thread.""" # emitted when new item on plot queue sigQueueChange = qt4.pyqtSignal(int) # when a rendering job is finished signalRenderFinished = qt4.pyqtSignal( int, qt4.QImage, document.PaintHelper) def __init__(self, plotwindow): """Start up numthreads rendering threads.""" qt4.QObject.__init__(self) self.sem = qt4.QSemaphore() self.mutex = qt4.QMutex() self.threads = [] self.exit = False self.latestjobs = [] self.latestaddedjob = -1 self.latestdrawnjob = -1 self.plotwindow = plotwindow self.updateNumberThreads() def updateNumberThreads(self, num=None): """Changes the number of rendering threads.""" if num is None: if qt4.QFontDatabase.supportsThreadedFontRendering(): # use number of threads in preference num = setting.settingdb['plot_numthreads'] else: # disable threads num = 0 if self.threads: # delete old ones self.exit = True self.sem.release(len(self.threads)) for t in self.threads: t.wait() del self.threads[:] self.exit = False # start new ones for i in crange(num): t = RenderThread(self) t.start() self.threads.append(t) def exitThreads(self): """Exit threads started.""" self.updateNumberThreads(num=0) def processNextJob(self): """Take a job from the queue and process it. emits renderfinished(jobid, img, painthelper) when done, if job has not been superseded """ self.mutex.lock() jobid, helper = self.latestjobs[-1] del self.latestjobs[-1] lastadded = self.latestaddedjob self.mutex.unlock() # don't process jobs which have been superseded if lastadded == jobid: img = qt4.QImage( int(helper.rawpagesize[0]), int(helper.rawpagesize[1]), qt4.QImage.Format_ARGB32_Premultiplied) img.fill( setting.settingdb.color('page').rgb() ) painter = qt4.QPainter(img) aa = self.plotwindow.antialias painter.setRenderHint(qt4.QPainter.Antialiasing, aa) painter.setRenderHint(qt4.QPainter.TextAntialiasing, aa) helper.renderToPainter(painter) painter.end() self.mutex.lock() # just throw away result if it older than the latest one if jobid > self.latestdrawnjob: self.signalRenderFinished.emit(jobid, img, helper) self.latestdrawnjob = jobid self.mutex.unlock() # tell any listeners that a job has been processed self.sigQueueChange.emit(-1) def addJob(self, helper): """Process drawing job in PaintHelper given.""" # indicate that there is a new item to be processed to listeners self.sigQueueChange.emit(1) # add the job to the queue self.mutex.lock() self.latestaddedjob += 1 self.latestjobs.append( (self.latestaddedjob, helper) ) self.mutex.unlock() if self.threads: # tell a thread to process job self.sem.release(1) else: # process job in current thread if multithreading disabled self.processNextJob() class RenderThread( qt4.QThread ): """A thread for processing rendering jobs. This is controlled by a RenderControl object """ def __init__(self, rendercontrol): qt4.QThread.__init__(self) self.rc = rendercontrol def run(self): """Repeat forever until told to exit. If it aquires 1 resource from the semaphore it will process the next job. """ while True: # wait until we can aquire the resources self.rc.sem.acquire(1) if self.rc.exit: break try: self.rc.processNextJob() except Exception: sys.stderr.write(_("Error in rendering thread\n")) traceback.print_exc(file=sys.stderr) class ControlGraphRoot(qt4.QGraphicsItem): """Control graph items are connected to this root item. We don't use a group here as it would swallow parent events.""" def __init__(self): qt4.QGraphicsItem.__init__(self) def paint(self, painter, option, widget=None): pass def boundingRect(self): return qt4.QRectF() class PlotWindow( qt4.QGraphicsView ): """Class to show the plot(s) in a scrollable window.""" # emitted when new item on plot queue sigQueueChange = qt4.pyqtSignal(int) # on drawing a page sigUpdatePage = qt4.pyqtSignal(int) # point picked on plot sigPointPicked = qt4.pyqtSignal(object) # picker enabled sigPickerEnabled = qt4.pyqtSignal(bool) # axis values update from moving mouse sigAxisValuesFromMouse = qt4.pyqtSignal(dict) # gives widget clicked sigWidgetClicked = qt4.pyqtSignal(object, str) # how often the document can update updateintervals = ( (0, _('Disable')), (-1, _('On document change')), (100, _('Every 0.1s')), (250, _('Every 0.25s')), (500, _('Every 0.5s')), (1000, _('Every 1s')), (2000, _('Every 2s')), (5000, _('Every 5s')), (10000, _('Every 10s')), ) def __init__(self, document, parent, menu=None): """Initialise the window. menu gives a menu to add any menu items to """ qt4.QGraphicsView.__init__(self, parent) self.setBackgroundRole(qt4.QPalette.Dark) self.scene = qt4.QGraphicsScene() self.setScene(self.scene) # this graphics scene item is the actual graph pixmap = qt4.QPixmap(1, 1) self.dpi = (pixmap.logicalDpiX(), pixmap.logicalDpiY()) self.pixmapitem = self.scene.addPixmap(pixmap) # whether full screen mode self.isfullscreen = False # set to be parent's actions self.vzactions = None # for controlling plot elements self.controlgraphroot = ControlGraphRoot() self.scene.addItem(self.controlgraphroot) # zoom rectangle for zooming into graph (not shown normally) self.zoomrect = self.scene.addRect( 0, 0, 100, 100, qt4.QPen(qt4.Qt.DotLine)) self.zoomrect.setZValue(2.) self.zoomrect.hide() # picker graphicsitem for marking the picked point self.pickeritem = PickerCrosshairItem() self.scene.addItem(self.pickeritem) self.pickeritem.setZValue(2.) self.pickeritem.hide() # all the widgets that picker key-navigation might cycle through self.pickerwidgets = [] # the picker state self.pickerinfo = widgets.PickInfo() # set up so if document is modified we are notified self.document = document self.docchangeset = -100 self.oldpagenumber = -1 self.document.signalModified.connect(self.slotDocModified) # state of last plot from painthelper self.painthelper = None self.lastwidgetsselected = [] self.oldzoom = -1. self.zoomfactor = 1. self.pagenumber = 0 self.ignoreclick = False # for rendering plots in separate threads self.rendercontrol = RenderControl(self) self.rendercontrol.signalRenderFinished.connect( self.slotRenderFinished) self.rendercontrol.sigQueueChange.connect( self.sigQueueChange) # mode for clicking self.clickmode = 'select' self.currentclickmode = None # wheel zooming/scrolling accumulator self.sumwheeldelta = 0 # set up redrawing timer self.timer = qt4.QTimer(self) self.timer.timeout.connect(self.checkPlotUpdate) # for drag scrolling self.grabpos = None self.scrolltimer = qt4.QTimer(self) self.scrolltimer.setSingleShot(True) # for turning clicking into scrolling after a period self.scrolltimer.timeout.connect(self.slotBecomeScrollClick) # get plot view updating policy # -1: update on document changes # 0: never update automatically # >0: check for updates every x ms self.interval = setting.settingdb['plot_updatepolicy'] # if using a time-based document update checking, start timer if self.interval > 0: self.timer.start(self.interval) # load antialias settings self.antialias = setting.settingdb['plot_antialias'] # allow window to get focus, to allow context menu self.setFocusPolicy(qt4.Qt.StrongFocus) # get mouse move events if mouse is not pressed self.setMouseTracking(True) # create toolbar in main window (urgh) self.createToolbar(parent, menu) def hideEvent(self, event): """Window closing, so exit rendering threads.""" self.rendercontrol.exitThreads() qt4.QGraphicsView.hideEvent(self, event) def sizeHint(self): """Return size hint for window.""" p = self.pixmapitem.pixmap() if p.width() <= 1 and p.height() <= 1: # if the document has been uninitialized, get the doc size return qt4.QSize(*self.document.docSize()) return p.size() def showToolbar(self, show=True): """Show or hide toolbar""" self.viewtoolbar.setVisible(show) def createToolbar(self, parent, menu=None): """Make a view toolbar, and optionally update menu.""" self.viewtoolbar = qt4.QToolBar(_("View toolbar - Veusz"), parent) self.viewtoolbar.setObjectName('veuszviewtoolbar') iconsize = setting.settingdb['toolbar_size'] self.viewtoolbar.setIconSize(qt4.QSize(iconsize, iconsize)) self.viewtoolbar.hide() if parent: parent.addToolBar(qt4.Qt.TopToolBarArea, self.viewtoolbar) if parent and hasattr(parent, 'vzactions'): # share actions with parent if possible # as plot windows can be isolated from mainwindows, we need this self.vzactions = actions = parent.vzactions else: self.vzactions = actions = {} a = utils.makeAction actions.update({ 'view.zoomin': a(self, _('Zoom into the plot'), _('Zoom &In'), self.slotViewZoomIn, icon='kde-zoom-in', key='Ctrl++'), 'view.zoomout': a(self, _('Zoom out of the plot'), _('Zoom &Out'), self.slotViewZoomOut, icon='kde-zoom-out', key='Ctrl+-'), 'view.zoom11': a(self, _('Restore plot to natural size'), _('Zoom 1:1'), self.slotViewZoom11, icon='kde-zoom-1-veuszedit', key='Ctrl+1'), 'view.zoomwidth': a(self, _('Zoom plot to show whole width'), _('Zoom to width'), self.slotViewZoomWidth, icon='kde-zoom-width-veuszedit'), 'view.zoomheight': a(self, _('Zoom plot to show whole height'), _('Zoom to height'), self.slotViewZoomHeight, icon='kde-zoom-height-veuszedit'), 'view.zoompage': a(self, _('Zoom plot to show whole page'), _('Zoom to page'), self.slotViewZoomPage, icon='kde-zoom-page-veuszedit'), 'view.zoommenu': a(self, _('Zoom functions menu'), _('Zoom'), None, icon='kde-zoom-veuszedit'), 'view.prevpage': a(self, _('Move to the previous page'), _('&Previous page'), self.slotViewPreviousPage, icon='kde-go-previous', key='Ctrl+PgUp'), 'view.nextpage': a(self, _('Move to the next page'), _('&Next page'), self.slotViewNextPage, icon='kde-go-next', key='Ctrl+PgDown'), 'view.select': a(self, _('Select items from the graph or scroll'), _('Select items or scroll'), None, icon='kde-mouse-pointer'), 'view.pick': a(self, _('Read data points on the graph'), _('Read data points'), None, icon='veusz-pick-data'), 'view.zoomgraph': a(self, _('Zoom into graph'), _('Zoom graph'), None, icon='veusz-zoom-graph'), 'view.fullscreen': a(self, _('View plot full screen'), _('Full screen'), self.slotFullScreen, icon='veusz-view-fullscreen', key='Ctrl+F11'), }) if menu: # only construct menu if required menuitems = [ ('view', '', [ 'view.zoomin', 'view.zoomout', 'view.zoom11', 'view.zoomwidth', 'view.zoomheight', 'view.zoompage', '', 'view.prevpage', 'view.nextpage', 'view.fullscreen', '', 'view.select', 'view.pick', 'view.zoomgraph', ]), ] utils.constructMenus(menu, {'view': menu}, menuitems, actions) # populate menu on zoom toolbar icon utils.makeMenuGroupSaved( 'view.zoommenu', self, actions, ( 'view.zoomin', 'view.zoomout', 'view.zoom11', 'view.zoomwidth', 'view.zoomheight', 'view.zoompage', )) # add items to toolbar utils.addToolbarActions( self.viewtoolbar, actions, ( 'view.prevpage', 'view.nextpage', 'view.fullscreen', 'view.select', 'view.pick', 'view.zoomgraph', 'view.zoommenu', )) # define action group for various different selection models grp = self.selectactiongrp = qt4.QActionGroup(self) grp.setExclusive(True) for a in ('view.select', 'view.pick', 'view.zoomgraph'): actions[a].setActionGroup(grp) actions[a].setCheckable(True) actions['view.select'].setChecked(True) grp.triggered.connect(self.slotSelectMode) return self.viewtoolbar def doZoomRect(self, endpos): """Take the zoom rectangle drawn by the user and do the zooming. endpos is a QPoint end point This is pretty messy - first we have to work out the graph associated to the first point Then we have to iterate over each of the plotters, identify their axes, and change the range of the axes to match the screen region selected. """ # safety net if self.grabpos is None or endpos is None: return # get points corresponding to corners of rectangle pt1 = self.grabpos pt2 = endpos # work out whether it's worthwhile to zoom: only zoom if there # are >=5 pixels movement if abs((pt2-pt1).x()) < 10 or abs((pt2-pt1).y()) < 10: return # scale into graph coordinates pt1 /= self.painthelper.cgscale pt2 /= self.painthelper.cgscale # try to work out in which widget the first point is in widget = self.painthelper.pointInWidgetBounds( pt1.x(), pt1.y(), widgets.Graph) if widget is None: return # convert points on plotter to points on axis for each axis # we also add a neighbouring pixel for the rounding calculation xpts = N.array( [pt1.x(), pt2.x(), pt1.x()+1, pt2.x()-1] ) ypts = N.array( [pt1.y(), pt2.y(), pt1.y()+1, pt2.y()-1] ) # build up operation list to do zoom operations = [] axes = {} # iterate over children, to look for plotters for c in [i for i in widget.children if isinstance(i, widgets.GenericPlotter)]: # get axes associated with plotter caxes = c.parent.getAxes( (c.settings.xAxis, c.settings.yAxis) ) for a in caxes: if a: axes[a] = True # iterate over each axis, and update the ranges for axis in axes: s = axis.settings if s.direction == 'horizontal': p = xpts else: p = ypts # convert points on plotter to axis coordinates # FIXME: Need To Trap Conversion Errors! try: r = axis.plotterToGraphCoords( self.painthelper.widgetBounds(axis), p) except KeyError: continue # invert if min and max are inverted if r[1] < r[0]: r[1], r[0] = r[0], r[1] r[3], r[2] = r[2], r[3] # build up operations to change axis if s.min != r[0]: operations.append( document.OperationSettingSet( s.get('min'), utils.round2delt(r[0], r[2])) ) if s.max != r[1]: operations.append( document.OperationSettingSet( s.get('max'), utils.round2delt(r[1], r[3])) ) # finally change the axes self.document.applyOperation( document.OperationMultiple(operations,descr=_('zoom axes')) ) def axesForPoint(self, mousepos): """Find all the axes which contain the given mouse position""" if self.painthelper is None: return [] pos = self.mapToScene(mousepos) px = pos.x() / self.painthelper.cgscale py = pos.y() / self.painthelper.cgscale axes = [] for widget, bounds in self.painthelper.widgetBoundsIterator( widgettype=widgets.Axis): # if widget is axis, and point lies within bounds if ( px>=bounds[0] and px<=bounds[2] and py>=bounds[1] and py<=bounds[3] ): # convert correct pointer position if widget.settings.direction == 'horizontal': val = px else: val = py coords=widget.plotterToGraphCoords(bounds, N.array([val])) axes.append( (widget, coords[0]) ) return axes def emitPicked(self, pickinfo): """Report that a new point has been picked""" self.pickerinfo = pickinfo self.pickeritem.setPos( pickinfo.graphpos[0] * self.painthelper.cgscale, pickinfo.graphpos[1] * self.painthelper.cgscale) self.sigPointPicked.emit(pickinfo) def doPick(self, mousepos): """Find the point on any plot-like widget closest to the cursor""" self.pickerwidgets = [] pickinfo = widgets.PickInfo() # get scalable graph coordinates for mouse point pos = self.mapToScene(mousepos) pos /= self.painthelper.cgscale for w, bounds in self.painthelper.widgetBoundsIterator(): # ask the widget for its (visually) closest point to the cursor try: info = w.pickPoint(pos.x(), pos.y(), bounds) except AttributeError: # widget isn't pickable continue if info: # this is a pickable widget, so remember it for future # key navigation self.pickerwidgets.append(w) if info.distance < pickinfo.distance: # and remember the overall closest pickinfo = info if not pickinfo: self.pickeritem.hide() return self.emitPicked(pickinfo) def slotBecomeScrollClick(self): """If the click is still down when this timer is reached then we turn the click into a scrolling click.""" if self.currentclickmode == 'select': qt4.QApplication.setOverrideCursor(qt4.QCursor(qt4.Qt.SizeAllCursor)) self.currentclickmode = 'scroll' def mousePressEvent(self, event): """Allow user to drag window around.""" qt4.QGraphicsView.mousePressEvent(self, event) if self.painthelper is None: return # work out whether user is clicking on a control point items = self.items(event.pos()) self.ignoreclick = ( len(items)==0 or items[0] is not self.pixmapitem or self.painthelper is None ) if event.button() == qt4.Qt.LeftButton and not self.ignoreclick: # need to copy position, otherwise it gets reused! self.winpos = qt4.QPoint(event.pos()) self.grabpos = self.mapToScene(self.winpos) if self.clickmode == 'select': # we set this to true unless the timer runs out (400ms), # then it becomes a scroll click # scroll clicks drag the window around, and selecting clicks # select widgets! self.scrolltimer.start(400) elif self.clickmode == 'pick': self.pickeritem.show() self.pickeritem.setFocus(qt4.Qt.MouseFocusReason) self.doPick(event.pos()) elif self.clickmode == 'scroll': qt4.QApplication.setOverrideCursor( qt4.QCursor(qt4.Qt.SizeAllCursor)) elif self.clickmode == 'graphzoom': self.zoomrect.setRect( self.grabpos.x(), self.grabpos.y(), 0, 0) self.zoomrect.show() #self.label.drawRect(self.grabpos, self.grabpos) # record what mode we were clicked in self.currentclickmode = self.clickmode def mouseMoveEvent(self, event): """Scroll window by how much the mouse has moved since last time.""" qt4.QGraphicsView.mouseMoveEvent(self, event) if self.painthelper is None: return if self.currentclickmode == 'scroll': event.accept() # move scroll bars by amount pos = event.pos() dx = self.winpos.x()-pos.x() scrollx = self.horizontalScrollBar() scrollx.setValue( scrollx.value() + dx ) dy = self.winpos.y()-pos.y() scrolly = self.verticalScrollBar() scrolly.setValue( scrolly.value() + dy ) # need to copy point self.winpos = qt4.QPoint(event.pos()) elif self.currentclickmode == 'graphzoom' and self.grabpos is not None: pos2 = self.mapToScene(event.pos()) self.zoomrect.setRect(qt4.QRectF( qt4.QPointF( min(self.grabpos.x(), pos2.x()), min(self.grabpos.y(), pos2.y())), qt4.QPointF( max(self.grabpos.x(), pos2.x()), max(self.grabpos.y(), pos2.y())), )) elif self.clickmode == 'select' or self.clickmode == 'pick': # find axes which map to this position axes = self.axesForPoint(event.pos()) vals = dict([ (a[0].name, a[1]) for a in axes ]) self.sigAxisValuesFromMouse.emit(vals) if self.currentclickmode == 'pick': # drag the picker around self.doPick(event.pos()) def mouseReleaseEvent(self, event): """If the mouse button is released, check whether the mouse clicked on a widget, and emit a sigWidgetClicked(widget,mode).""" qt4.QGraphicsView.mouseReleaseEvent(self, event) if self.painthelper is None: return if event.button() == qt4.Qt.LeftButton and not self.ignoreclick: event.accept() self.scrolltimer.stop() if self.currentclickmode == 'select': # work out where the mouse clicked and choose widget pos = self.mapToScene(event.pos()) self.identifyAndClickWidget(pos.x(), pos.y(), event.modifiers()) elif self.currentclickmode == 'scroll': # return the cursor to normal after scrolling self.clickmode = 'select' self.currentclickmode = None qt4.QApplication.restoreOverrideCursor() elif self.currentclickmode == 'graphzoom': self.zoomrect.hide() self.doZoomRect(self.mapToScene(event.pos())) self.grabpos = None elif self.currentclickmode == 'viewgetclick': self.clickmode = 'select' elif self.currentclickmode == 'pick': self.currentclickmode = None def keyPressEvent(self, event): """Keypad motion moves the picker if it has focus""" if self.pickeritem.hasFocus(): k = event.key() if k == qt4.Qt.Key_Left or k == qt4.Qt.Key_Right: # navigate to the previous or next point on the curve event.accept() dir = 'right' if k == qt4.Qt.Key_Right else 'left' ix = self.pickerinfo.index pickinfo = self.pickerinfo.widget.pickIndex( ix, dir, self.painthelper.widgetBounds( self.pickerinfo.widget)) if pickinfo: # more points visible in this direction self.emitPicked(pickinfo) return elif k == qt4.Qt.Key_Up or k == qt4.Qt.Key_Down: # navigate to the next plot up or down on the screen event.accept() p = self.pickeritem.pos() oldw = self.pickerinfo.widget pickinfo = widgets.PickInfo() dist = float('inf') for w in self.pickerwidgets: if w == oldw: continue # ask the widgets to pick their point which is closest horizontally # to the last (screen) x value picked pi = w.pickPoint( self.pickerinfo.graphpos[0], p.y(), self.painthelper.widgetBounds(w), distance='horizontal') if not pi: continue dy = p.y() - pi.graphpos[1] # take the new point which is closest vertically to the current # one and either above or below it as appropriate if abs(dy) < dist and ( (k == qt4.Qt.Key_Up and dy > 0) or (k == qt4.Qt.Key_Down and dy < 0) ): pickinfo = pi dist = abs(dy) if pickinfo: oldx = self.pickerinfo.graphpos[0] self.emitPicked(pickinfo) # restore the previous x-position, so that vertical navigation # stays repeatable pickinfo.graphpos = (oldx, pickinfo.graphpos[1]) return # handle up-stream qt4.QGraphicsView.keyPressEvent(self, event) def wheelEvent(self, event): """For zooming in or moving.""" if event.modifiers() & qt4.Qt.ControlModifier: # zoom in/out with ctrl held down d = event.angleDelta() delta = d.x() if d.x() != 0 else d.y() self.sumwheeldelta += delta while self.sumwheeldelta <= -120: self.slotViewZoomOut() self.sumwheeldelta += 120 while self.sumwheeldelta >= 120: self.slotViewZoomIn() self.sumwheeldelta -= 120 event.accept() elif event.modifiers() & qt4.Qt.ShiftModifier: # scroll horizontally if shift is held down d = event.pixelDelta() if d.isNull(): # Fallback mode to angleDelta d = event.angleDelta() delta = d.x() if d.x() != 0 else d.y() scrollx = self.horizontalScrollBar() scrollx.setValue(scrollx.value() + delta) event.accept() else: qt4.QGraphicsView.wheelEvent(self, event) def identifyAndClickWidget(self, x, y, modifier): """Work out which widget was clicked, and if necessary send a sigWidgetClicked(widget) signal.""" if self.document.getNumberPages() == 0: return widget = self.painthelper.identifyWidgetAtPoint( x, y, antialias=self.antialias) if widget is None: # select page if nothing clicked widget = self.document.basewidget.getPage(self.pagenumber) # tell connected objects that widget was clicked if widget is not None: if modifier & qt4.Qt.ControlModifier: mode = 'toggle' elif modifier & qt4.Qt.ShiftModifier: mode = 'add' else: mode = 'new' self.sigWidgetClicked.emit(widget, mode) def setPageNumber(self, pageno): """Move the the selected page.""" # we don't need to do anything if (self.pagenumber == pageno and self.document.changeset == self.docchangeset): return # keep within bounds pageno = min(pageno, self.document.getNumberPages()-1) pageno = max(0, pageno) self.pagenumber = pageno if self.pagenumber != self.oldpagenumber and self.interval != 0: self.checkPlotUpdate() def getPageNumber(self): """Get the the selected page.""" return self.pagenumber @qt4.pyqtSlot(int) def slotDocModified(self, ismodified): """Update plot on document being modified.""" # only update if doc is modified and the update policy is set # to update on document updates if ismodified and self.interval == -1: self.checkPlotUpdate() def getDevicePixelRatio(self): """Get device pixel ratio for window.""" # ugly, as we have to get it from QWindow widget = self while widget: window = widget.windowHandle() if window is not None: try: return window.devicePixelRatio() except AttributeError: return 1 widget = widget.parent() return 1 def checkPlotUpdate(self): """Check whether plot needs updating.""" # no threads, so can't get interrupted here # draw data into background pixmap if modified # is an update required? if ( self.zoomfactor == self.oldzoom and self.document.changeset == self.docchangeset and self.pagenumber == self.oldpagenumber ): return self.pickeritem.hide() # do we need the following line? self.pagenumber = min( self.document.getNumberPages()-1, self.pagenumber) self.oldpagenumber = self.pagenumber if self.pagenumber >= 0: devicepixelratio = self.getDevicePixelRatio() scaling = self.zoomfactor*devicepixelratio size = self.document.pageSize( self.pagenumber, scaling=scaling, integer=False) # draw the data into the buffer # errors cause an exception window to pop up try: phelper = document.PaintHelper( self.document, size, scaling=scaling, dpi=self.dpi, devicepixelratio=devicepixelratio) self.document.paintTo(phelper, self.pagenumber) except Exception: # stop updates this time round and show exception dialog d = exceptiondialog.ExceptionDialog(sys.exc_info(), self) self.oldzoom = self.zoomfactor self.docchangeset = self.document.changeset d.exec_() self.painthelper = phelper self.rendercontrol.addJob(phelper) else: self.painthelper = None self.pagenumber = 0 size = self.document.docSize() pixmap = qt4.QPixmap(*size) pixmap.fill( setting.settingdb.color('page') ) self.setSceneRect(0, 0, *size) self.pixmapitem.setPixmap(pixmap) self.sigUpdatePage.emit(self.pagenumber) self.updatePageToolbar() self.updateControlGraphs(self.lastwidgetsselected) self.oldzoom = self.zoomfactor self.docchangeset = self.document.changeset def slotRenderFinished(self, jobid, img, helper): """Update image on display if rendering (usually in other thread) finished.""" dpr = helper.devicepixelratio bufferpixmap = qt4.QPixmap.fromImage(img) bufferpixmap.setDevicePixelRatio(dpr) self.setSceneRect(0, 0, bufferpixmap.width()/dpr, bufferpixmap.height()/dpr) self.pixmapitem.setPixmap(bufferpixmap) def updatePlotSettings(self): """Update plot window settings from settings.""" self.setTimeout(setting.settingdb['plot_updatepolicy']) self.antialias = setting.settingdb['plot_antialias'] self.rendercontrol.updateNumberThreads() self.actionForceUpdate() def contextMenuEvent(self, event): """Show context menu.""" menu = qt4.QMenu(self) # add some useful entries menu.addAction( self.vzactions['view.zoommenu'] ) menu.addSeparator() menu.addAction( self.vzactions['view.prevpage'] ) menu.addAction( self.vzactions['view.nextpage'] ) menu.addSeparator() # force an update now menu item menu.addAction(_('Force update'), self.actionForceUpdate) if self.isfullscreen: menu.addAction(_('Close full screen'), self.slotFullScreen) else: menu.addAction( self.vzactions['view.fullscreen'] ) # Update policy submenu submenu = menu.addMenu(_('Updates')) intgrp = qt4.QActionGroup(self) # bind interval options to actions for intv, text in self.updateintervals: act = intgrp.addAction(text) act.setCheckable(True) def setfn(interval): return lambda checked: self.actionSetTimeout(interval, checked) act.triggered.connect(setfn(intv)) if intv == self.interval: act.setChecked(True) submenu.addAction(act) # antialias menu.addSeparator() act = menu.addAction(_('Antialias'), self.actionAntialias) act.setCheckable(True) act.setChecked(self.antialias) menu.exec_(qt4.QCursor.pos()) def actionForceUpdate(self): """Force an update for the graph.""" self.docchangeset = -100 self.checkPlotUpdate() def slotFullScreen(self): """Show window full screen or not.""" if not self.isfullscreen: self._fullscreenwindow = FullScreenPlotWindow( self.document, self.pagenumber) else: # cheesy way of closing full screen window p = self while p.parent() is not None: p = p.parent() p.close() def setTimeout(self, interval): """Change timer setting without changing save value.""" self.interval = interval if interval <= 0: # stop updates if self.timer.isActive(): self.timer.stop() else: # change interval to one selected self.timer.setInterval(interval) # start timer if it was stopped if not self.timer.isActive(): self.timer.start() def actionSetTimeout(self, interval, checked): """Called by setting the interval.""" self.setTimeout(interval) # remember changes for next time setting.settingdb['plot_updatepolicy'] = self.interval def actionAntialias(self): """Toggle antialias.""" self.antialias = not self.antialias setting.settingdb['plot_antialias'] = self.antialias self.actionForceUpdate() def setZoomFactor(self, zoomfactor): """Set the zoom factor of the window.""" zoomfactor = max(0.05, min(20, zoomfactor)) self.zoomfactor = float(zoomfactor) self.checkPlotUpdate() def slotViewZoomIn(self): """Zoom into the plot.""" self.setZoomFactor(self.zoomfactor * N.sqrt(2.)) def slotViewZoomOut(self): """Zoom out of the plot.""" self.setZoomFactor(self.zoomfactor / N.sqrt(2.)) def slotViewZoomWidth(self): """Make the zoom factor so that the plot fills the whole width.""" # need to take account of scroll bars when deciding size viewportsize = self.maximumViewportSize() aspectwin = viewportsize.width() / viewportsize.height() r = self.pixmapitem.boundingRect() aspectplot = r.width() / r.height() width = viewportsize.width() if aspectwin > aspectplot: # take account of scroll bar width -= self.verticalScrollBar().width() mult = width / r.width() self.setZoomFactor(self.zoomfactor * mult) def slotViewZoomHeight(self): """Make the zoom factor so that the plot fills the whole width.""" viewportsize = self.maximumViewportSize() pixrect = self.pixmapitem.boundingRect() try: aspectwin = viewportsize.width() / viewportsize.height() aspectplot = pixrect.width() / pixrect.height() except ZeroDivisionError: return height = viewportsize.height() if aspectwin < aspectplot: # take account of scroll bar height -= self.horizontalScrollBar().height() mult = height / pixrect.height() self.setZoomFactor(self.zoomfactor * mult) def slotViewZoomPage(self): """Make the zoom factor correct to show the whole page.""" viewportsize = self.maximumViewportSize() r = self.pixmapitem.boundingRect() if r.width() != 0 and r.height() != 0: multw = viewportsize.width() / r.width() multh = viewportsize.height() / r.height() self.setZoomFactor(self.zoomfactor * min(multw, multh)) def slotViewZoom11(self): """Restore the zoom to 1:1""" self.setZoomFactor(1.) def slotViewPreviousPage(self): """View the previous page.""" self.setPageNumber( self.pagenumber - 1 ) def slotViewNextPage(self): """View the next page.""" self.setPageNumber( self.pagenumber + 1 ) def updatePageToolbar(self): """Update page number when the plot window says so.""" # disable previous and next page actions if self.vzactions is not None: np = self.document.getNumberPages() self.vzactions['view.prevpage'].setEnabled(self.pagenumber != 0) self.vzactions['view.nextpage'].setEnabled(self.pagenumber < np-1) def slotSelectMode(self, action): """Called when the selection mode has changed.""" modecnvt = { self.vzactions['view.select'] : 'select', self.vzactions['view.pick'] : 'pick', self.vzactions['view.zoomgraph'] : 'graphzoom' } # close the current picker self.pickeritem.hide() self.sigPickerEnabled.emit(False) # convert action into clicking mode self.clickmode = modecnvt[action] if self.clickmode == 'select': self.pixmapitem.unsetCursor() elif self.clickmode == 'graphzoom': self.pixmapitem.setCursor(qt4.Qt.CrossCursor) elif self.clickmode == 'pick': self.pixmapitem.setCursor(qt4.Qt.CrossCursor) self.sigPickerEnabled.emit(True) def getClick(self): """Return a click point from the graph.""" # wait for click from user qt4.QApplication.setOverrideCursor(qt4.QCursor(qt4.Qt.CrossCursor)) oldmode = self.clickmode self.clickmode = 'viewgetclick' while self.clickmode == 'viewgetclick': qt4.qApp.processEvents() self.clickmode = oldmode qt4.QApplication.restoreOverrideCursor() # take clicked point and convert to coords of scrollview pt = self.grabpos # try to work out in which widget the first point is in widget = self.painthelper.pointInWidgetBounds( pt.x(), pt.y(), widgets.Graph) if widget is None: return [] # convert points on plotter to points on axis for each axis xpts = N.array( [pt.x()] ) ypts = N.array( [pt.y()] ) axesretn = [] # iterate over children, to look for plotters for c in [i for i in widget.children if isinstance(i, widgets.GenericPlotter)]: # get axes associated with plotter axes = c.parent.getAxes( (c.settings.xAxis, c.settings.yAxis) ) # iterate over each, and update the ranges for axis in [a for a in axes if a is not None]: s = axis.settings if s.direction == 'horizontal': p = xpts else: p = ypts # convert point on plotter to axis coordinate # FIXME: Need To Trap Conversion Errors! r = axis.plotterToGraphCoords( self.painthelper.widgetBounds(axis), p) axesretn.append( (axis.path, r[0]) ) return axesretn def selectedWidgets(self, widgets): """Update control items on screen associated with widget. Called when widgets have been selected in the tree edit window """ self.updateControlGraphs(widgets) self.lastwidgetsselected = widgets def updateControlGraphs(self, widgets): """Add control graphs for the widgets given.""" # delete old items from root for c in list(self.controlgraphroot.childItems()): self.scene.removeItem(c) # add each item to the root if self.painthelper: for widget in widgets: cgis = self.painthelper.getControlGraph(widget) if cgis: for control in cgis: control.createGraphicsItem(self.controlgraphroot) class FullScreenPlotWindow(qt4.QScrollArea): """Window for showing plot in full-screen mode.""" def __init__(self, document, pagenumber): qt4.QScrollArea.__init__(self) self.setFrameShape(qt4.QFrame.NoFrame) self.setWidgetResizable(True) # window which shows plot self.document = document pw = self.plotwin = PlotWindow(document, None) pw.isfullscreen = True pw.pagenumber = pagenumber self.setWidget(pw) pw.setFocus() self.showFullScreen() self.toolbar = qt4.QToolBar(_("Full screen toolbar"), self) self.toolbar.addAction(utils.getIcon("kde-window-close"), _("Close"), self.close) for a in ('view.zoom11', 'view.zoomin', 'view.zoomout', 'view.zoomwidth', 'view.zoomheight', 'view.zoompage', 'view.prevpage', 'view.nextpage'): self.toolbar.addAction( pw.vzactions[a] ) self.toolbar.show() def resizeEvent(self, event): """Make zoom fit screen.""" qt4.QScrollArea.resizeEvent(self, event) # size graph to fill screen pagesize = self.document.pageSize(self.plotwin.pagenumber, dpi=self.plotwin.dpi) screensize = self.plotwin.size() aspectw = screensize.width() / pagesize[0] aspecth = screensize.height() / pagesize[1] self.plotwin.zoomfactor = min(aspectw, aspecth) self.plotwin.checkPlotUpdate() def keyPressEvent(self, event): k = event.key() if k == qt4.Qt.Key_Escape: event.accept() self.close() return qt4.QScrollArea.keyPressEvent(self, event) veusz-3.0.1/veusz/windows/datanavigator.py0000664000175000017500000000311113161413406020350 0ustar jssjss00000000000000# -*- coding: utf-8 -*- # Copyright (C) 2011 Jeremy S. Sanders # Email: Jeremy Sanders # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ############################################################################### from __future__ import division from .. import qtall as qt4 from ..qtwidgets.datasetbrowser import DatasetBrowser def _(text, disambiguation=None, context="DataNavigator"): """Translate text.""" return qt4.QCoreApplication.translate(context, text, disambiguation) class DataNavigatorWindow(qt4.QDockWidget): """A dock window containing a dataset browsing widget.""" def __init__(self, thedocument, mainwin, *args): qt4.QDockWidget.__init__(self, *args) self.setWindowTitle(_("Data - Veusz")) self.setObjectName("veuszdatawindow") self.nav = DatasetBrowser(thedocument, mainwin, self) self.setWidget(self.nav) veusz-3.0.1/veusz/veusz_listen.py0000775000175000017500000001146313161413406016600 0ustar jssjss00000000000000#!/usr/bin/env python # Copyright (C) 2005 Jeremy S. Sanders # Email: Jeremy Sanders # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ############################################################################## """ Veusz interface which listens to stdin, and receives commands. Results are written to stdout All commands in CommandInterface are supported, plus further commands: Quit: exit the listening program Zoom x: Change the zoom factor of the plot to x """ from __future__ import division import sys from . import qtall as qt4 from .compat import cstr from .windows.simplewindow import SimpleWindow from . import document class ReadingThread(qt4.QThread): """Stdin reading thread. Emits newline signals with new data. We could use a QSocketNotifier on Unix, but this doesn't work on Windows as its stdin is a weird object """ newline = qt4.pyqtSignal(cstr) def run(self): """Emit lines read from stdin.""" while True: line = sys.stdin.readline() if line == '': break self.newline.emit(line) class InputListener(qt4.QObject): """Class reads text from stdin, in order to send commands to a document.""" def __init__(self, window): """Initialse the listening object to send commands to the document given by window.""" qt4.QObject.__init__(self) self.window = window self.document = window.document self.plot = window.plot self.pickle = False self.ci = document.CommandInterpreter(self.document) self.ci.addCommand('Quit', self.quitProgram) self.ci.addCommand('Zoom', self.plotZoom) self.ci.addCommand('EnableToolbar', self.enableToolbar) self.ci.addCommand('Pickle', self.enablePickle) self.ci.addCommand('ResizeWindow', self.resizeWindow) self.ci.addCommand('SetUpdateInterval', self.setUpdateInterval) self.ci.addCommand('MoveToPage', self.moveToPage) # reading is done in a separate thread so as not to block self.readthread = ReadingThread(self) self.readthread.newline.connect(self.processLine) self.readthread.start() def resizeWindow(self, width, height): """ResizeWindow(width, height) Resize the window to be width x height pixels.""" self.window.resize(width, height) def setUpdateInterval(self, interval): """SetUpdateInterval(interval) Set graph update interval. interval is in milliseconds (ms) set to zero to disable updates """ self.plot.setTimeout(interval) def moveToPage(self, pagenum): """MoveToPage(pagenum) Tell window to show specified pagenumber (starting from 1). """ self.plot.setPageNumber(pagenum-1) def quitProgram(self): """Exit the program.""" self.window.close() def plotZoom(self, zoomfactor): """Set the plot zoom factor.""" self.window.setZoom(zoomfactor) def enableToolbar(self, enable=True): """Enable plot toolbar.""" self.window.enableToolbar(enable) def enablePickle(self, on=True): """Enable/disable pickling of commands to/data from veusz""" self.pickle = on def processLine(self, line): """Process inputted line.""" if self.pickle: # line is repr form of pickled string get get rid of \n retn = self.ci.runPickle( eval(line.strip()) ) sys.stdout.write('%s\n' % repr(retn)) sys.stdout.flush() else: self.ci.run(line) def openWindow(args, quiet=False): '''Opening listening window. args is a list of arguments to the program ''' global _win global _listen if len(args) > 1: name = args[1] else: name = 'Veusz output' _win = SimpleWindow(name) if not quiet: _win.show() _listen = InputListener(_win) def run(): '''Actually run the program.''' app = qt4.QApplication(sys.argv) openWindow(sys.argv) app.exec_() # if ran as a program if __name__ == '__main__': run() veusz-3.0.1/veusz/__init__.py0000664000175000017500000000165413161413406015603 0ustar jssjss00000000000000# Copyright (C) 2008 Jeremy S. Sanders # Email: Jeremy Sanders # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ############################################################################## """Main veusz module.""" veusz-3.0.1/veusz/embed.py0000664000175000017500000004662513161413406015127 0ustar jssjss00000000000000# A module for embedding Veusz within another python program # Copyright (C) 2005 Jeremy S. Sanders # Email: Jeremy Sanders # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ############################################################################## """This module allows veusz to be embedded within other Python programs. For example: import time import numpy import veusz.embed as veusz g = veusz.Embedded('new win') g.To( g.Add('page') ) g.To( g.Add('graph') ) g.SetData('x', numpy.arange(20)) g.SetData('y', numpy.arange(20)**2) g.Add('xy') g.Zoom(0.5) time.sleep(60) g.Close() More than one embedded window can be opened at once """ from __future__ import division import atexit import sys import os import os.path import struct import socket import subprocess import time import uuid import functools import types # python3 compatibility try: import cPickle as pickle except ImportError: import pickle # check remote process has this API version API_VERSION = 2 def findOnPath(cmd): """Find a command on the system path, or None if does not exist.""" path = os.getenv('PATH', os.path.defpath) pathparts = path.split(os.path.pathsep) for dirname in pathparts: dirname = dirname.strip('"') cmdtry = os.path.join(dirname, cmd) if os.path.isfile(cmdtry) and os.access(cmdtry, os.X_OK): return cmdtry return None class Embedded(object): """An embedded instance of Veusz. This embedded instance supports all the normal veusz functions """ remote = None def __init__(self, name='Veusz', copyof=None, hidden=False): """Initialse the embedded veusz window. name is the name of the window to show. copyof duplicates a view of the document in the Embedded instance given hidden makes a hidden window (useful for batch scripting) """ if not Embedded.remote: Embedded.startRemote() if not copyof: retval = self.sendCommand( (-1, '_NewWindow', (name,), {'hidden': hidden}) ) else: retval = self.sendCommand( (-1, '_NewWindowCopy', (name, copyof.winno), {'hidden': hidden}) ) self.winno, cmds = retval # add methods corresponding to Veusz commands for name, doc in cmds: func = functools.partial(self.runCommand, name) func.__doc__ = doc # set docstring func.__name__ = name # make name match what it calls method = types.MethodType(func, self) setattr(self, name, method) # assign to self # check API version is same try: remotever = self._apiVersion() except AttributeError: remotever = 0 if remotever != API_VERSION: raise RuntimeError("Remote Veusz instance reports version %i of" " API. This embed.py supports version %i." % (remotever, API_VERSION)) # define root object self.Root = WidgetNode(self, 'widget', '/') def StartSecondView(self, name = 'Veusz'): """Provides a second view onto the document of this window. Returns an Embedded instance """ return Embedded(name=name, copyof=self) def WaitForClose(self): """Wait for the window to close.""" # this is messy, polling for closure, but cleaner than doing # it in the remote client while not self.IsClosed(): time.sleep(0.1) @classmethod def makeSockets(cls): """Make socket(s) to communicate with remote process. Returns string to send to remote process """ if ( hasattr(socket, 'AF_UNIX') and hasattr(socket, 'socketpair') ): # convenient interface cls.sockfamily = socket.AF_UNIX sock, socket2 = socket.socketpair(cls.sockfamily, socket.SOCK_STREAM) # socket is closed on popen in Python 3.4+ without this (PEP 446) try: os.set_inheritable(socket2.fileno(), True) except AttributeError: pass sendtext = 'unix %i\n' % socket2.fileno() cls.socket2 = socket2 # prevent socket being destroyed waitaccept = False else: # otherwise mess around with internet sockets # * This is required for windows, which doesn't have AF_UNIX # * It is required where socketpair is not supported cls.sockfamily = socket.AF_INET sock = socket.socket(cls.sockfamily, socket.SOCK_STREAM) sock.bind( ('localhost', 0) ) interface, port = sock.getsockname() sock.listen(1) sendtext = 'internet %s %i\n' % (interface, port) waitaccept = True return (sock, sendtext.encode('ascii'), waitaccept) @classmethod def makeRemoteProcess(cls): """Try to find veusz process for remote program.""" # here's where to look for embed_remote.py thisdir = os.path.dirname(os.path.abspath(__file__)) # build up a list of possible command lines to start the remote veusz if sys.platform == 'win32': # windows is a special case # we need to run embed_remote.py under pythonw.exe, not python.exe # look for the python windows interpreter on path findpython = findOnPath('pythonw.exe') if not findpython: # if it wasn't on the path, use sys.prefix instead findpython = os.path.join(sys.prefix, 'pythonw.exe') # look for veusz executable on path findexe = findOnPath('veusz.exe') if not findexe: try: # add the usual place as a guess :-( findexe = os.path.join(os.environ['ProgramFiles'], 'Veusz', 'veusz.exe') except KeyError: pass # here is the list of commands to try possiblecommands = [ [findpython, os.path.join(thisdir, 'veusz_main.py')], [findexe] ] else: executable = sys.executable # try embed_remote.py in this directory, veusz in this directory # or veusz on the path in order possiblecommands = [ [executable, os.path.join(thisdir, 'veusz_main.py')], [os.path.join(thisdir, 'veusz')], [findOnPath('veusz')] ] # cheat and look for Veusz app for MacOS under the standard application # directory. I don't know how else to find it :-( if sys.platform == 'darwin': findbundle = findOnPath('Veusz.app') if findbundle: possiblecommands += [ [findbundle+'/Contents/MacOS/Veusz'] ] else: possiblecommands += [[ '/Applications/Veusz.app/Contents/MacOS/Veusz' ]] possiblecommands += [[ os.path.expanduser('~/Applications/Veusz.app/Contents/MacOS/Veusz')]] for cmd in possiblecommands: # only try to run commands that exist as error handling # does not work well when interfacing with OS (especially Windows) if ( None not in cmd and False not in [os.path.isfile(c) for c in cmd] ): try: # we don't use stdout below, but works around windows bug # http://bugs.python.org/issue1124861 cls.remote = subprocess.Popen(cmd + ['--embed-remote'], shell=False, bufsize=0, close_fds=False, stdin=subprocess.PIPE, stdout=subprocess.PIPE) return except OSError: pass raise RuntimeError('Unable to find a veusz executable on system path') @classmethod def startRemote(cls): """Start remote process.""" cls.serv_socket, sendtext, waitaccept = cls.makeSockets() cls.makeRemoteProcess() stdin = cls.remote.stdin # send socket number over pipe stdin.write( sendtext ) # accept connection if necessary if waitaccept: cls.serv_socket, address = cls.serv_socket.accept() # Send a secret to the remote program by secure route and # check it comes back. This is to check that no program has # secretly connected on our port, which isn't really useful # for AF_UNIX sockets. secret = (str(uuid.uuid4()) + '\n').encode('ascii') stdin.write(secret) secretback = cls.readLenFromSocket(cls.serv_socket, len(secret)) if secret != secretback: raise RuntimeError("Security between client and server broken") # packet length for command bytes cls.cmdlen = struct.calcsize('" % (self.__class__.__name__, repr(self._path), self._type) def fromPath(self, path): """Return a new Node for the path given.""" wtype = self._ci.NodeType(path) if wtype == 'widget': return WidgetNode(self._ci, wtype, path) elif wtype == 'setting': return SettingNode(self._ci, wtype, path) else: return SettingGroupNode(self._ci, wtype, path) @property def path(self): """Veusz full path to node""" return self._path @property def type(self): """Type of node: 'widget', 'settinggroup', or 'setting'""" return self._type def _joinPath(self, child): """Return new path of child.""" if self._path == '/': return '/' + child else: return self._path + '/' + child def __getitem__(self, key): """Return a child widget, settinggroup or setting.""" if self._type != 'setting': try: return self.fromPath(self._joinPath(key)) except ValueError: pass raise KeyError("%s does not have key or child '%s'" % ( self.__class__.__name__, key)) def __getattr__(self, attr): """Return a child widget, settinggroup or setting.""" if self._type == 'setting': pass elif attr[:2] != '__': try: return self.fromPath(self._joinPath(attr)) except ValueError: pass raise AttributeError("%s does not have attribute or child '%s'" % ( self.__class__.__name__, attr)) # boring ways to get children of nodes @property def children(self): """Generator to get children as Nodes.""" for c in self._ci.NodeChildren(self._path): yield self.fromPath(self._joinPath(c)) @property def children_widgets(self): """Generator to get child widgets as Nodes.""" for c in self._ci.NodeChildren(self._path, types='widget'): yield self.fromPath(self._joinPath(c)) @property def children_settings(self): """Generator to get child settings as Nodes.""" for c in self._ci.NodeChildren(self._path, types='setting'): yield self.fromPath(self._joinPath(c)) @property def children_settinggroups(self): """Generator to get child settingsgroups as Nodes.""" for c in self._ci.NodeChildren(self._path, types='settinggroup'): yield self.fromPath(self._joinPath(c)) @property def childnames(self): """Get names of children.""" return self._ci.NodeChildren(self._path) @property def childnames_widgets(self): """Get names of children widgets.""" return self._ci.NodeChildren(self._path, types='widget') @property def childnames_settings(self): """Get names of child settings.""" return self._ci.NodeChildren(self._path, types='setting') @property def childnames_settinggroups(self): """Get names of child setting groups""" return self._ci.NodeChildren(self._path, types='settinggroup') @property def parent(self): """Return parent of node.""" if self._path == '/': raise TypeError("Cannot get parent node of root node""") p = self._path.split('/')[:-1] if p == ['']: newpath = '/' else: newpath = '/'.join(p) return self.fromPath(newpath) @property def name(self): """Get name of node.""" if self._path == '/': return self._path else: return self._path.split('/')[-1] class SettingNode(Node): """A node which is a setting.""" def _getVal(self): """The value of a setting.""" if self._type == 'setting': return self._ci.Get(self._path) raise TypeError("Cannot get value unless is a setting""") def _setVal(self, val): if self._type == 'setting': self._ci.Set(self._path, val) else: raise TypeError("Cannot set value unless is a setting.""") val = property(_getVal, _setVal) @property def isreference(self): """Is this setting set to a reference to another setting?.""" ref = self._ci.ResolveReference(self._path) return bool(ref) def resolveReference(self): """If this is set to a reference to a setting, return a new SettingNode to the original setting. If there are a chain of references, follow them to the target. Returns None if this setting is not set to a reference. """ real = self._ci.ResolveReference(self._path) if not real: return None return self.fromPath(real) def setToReference(self, othernode): """Make this setting point to another setting, by creating a reference. References can be chained. Note that the absolute path is used to specify a reference, so moving affected widgets around will destroy the link.""" if not isinstance(othernode, SettingNode): raise ValueError("othernode is not a SettingNode") self._ci.SetToReference(self._path, othernode._path) @property def settingtype(self): """Get the type of setting, which is a string.""" return self._ci.SettingType(self._path) class SettingGroupNode(Node): """A node containing a group of settings.""" pass class WidgetNode(Node): """A node pointing to a widget.""" @property def widgettype(self): """Get Veusz type of widget.""" return self._ci.WidgetType(self.path) def WalkWidgets(self, widgettype=None): """Generator to walk widget tree and get this widget and the widgets below this WidgetNode of type given. widgettype is a Veusz widget type name or None to get all widgets.""" if widgettype is None or self._ci.WidgetType(self._path) == widgettype: yield self for child in self.children_widgets: for w in child.WalkWidgets(widgettype=widgettype): yield w def Add(self, widgettype, *args, **args_opt): """Add a widget of the type given, returning the Node instance. """ args_opt['widget'] = self._path name = self._ci.Add(widgettype, *args, **args_opt) return WidgetNode( self._ci, 'widget', self._joinPath(name) ) def Rename(self, newname): """Renames widget to name given.""" if self._path == '/': raise RuntimeError("Cannot rename root widget") self._ci.Rename(self._path, newname) self._path = '/'.join( self._path.split('/')[:-1] + [newname] ) def Action(self, action): """Applies action on widget.""" self._ci.Action(action, widget=self._path) def Remove(self): """Removes a widget and its children.""" self._ci.Remove(self._path) def Clone(self, newparent, newname=None): """Clone widget, placing at newparent. Uses newname if given. Returns new node.""" path = self._ci.CloneWidget(self._path, newparent._path, newname=newname) return WidgetNode( self._ci, 'widget', path ) veusz-3.0.1/veusz/dialogs/0000775000175000017500000000000013325026670015113 5ustar jssjss00000000000000veusz-3.0.1/veusz/dialogs/plugin.py0000664000175000017500000001635513161413406016770 0ustar jssjss00000000000000# Copyright (C) 2010 Jeremy S. Sanders # Email: Jeremy Sanders # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ############################################################################## """Dialog boxes for tools and dataset plugins.""" from __future__ import division import sys from ..compat import czip, cstr from .. import qtall as qt4 from .. import document from .. import plugins from . import exceptiondialog from .veuszdialog import VeuszDialog def _(text, disambiguation=None, context="PluginDialog"): """Translate text.""" return qt4.QCoreApplication.translate(context, text, disambiguation) def handlePlugin(mainwindow, doc, pluginkls): """Show plugin dialog or directly execute (if it takes no parameters).""" plugin = pluginkls() if plugin.has_parameters: d = PluginDialog(mainwindow, doc, plugin, pluginkls) mainwindow.showDialog(d) else: fields = {'currentwidget': '/'} if mainwindow.treeedit.selwidgets: fields = {'currentwidget': mainwindow.treeedit.selwidgets[0].path} runPlugin(mainwindow, doc, plugin, fields) def wordwrap(text, linelength=80): """Wrap on a word boundary.""" out = [] l = 0 for w in text.split(' '): if w.find('\n') >= 0: l = 0 if l + len(w) > linelength: out.append('\n') l = 0 out.append(w) l += len(w) return ' '.join(out) class PluginDialog(VeuszDialog): """Dialog box class for plugins.""" def __init__(self, mainwindow, doc, plugininst, pluginkls): VeuszDialog.__init__(self, mainwindow, 'plugin.ui') reset = self.buttonBox.button(qt4.QDialogButtonBox.Reset) reset.setAutoDefault(False) reset.setDefault(False) reset.clicked.connect( self.slotReset) self.buttonBox.button( qt4.QDialogButtonBox.Apply).clicked.connect(self.slotApply) self.pluginkls = pluginkls self.plugininst = plugininst self.document = doc title = ': '.join(list(plugininst.menu)) self.setWindowTitle(title) descr = plugininst.description_full if plugininst.author: descr += '\n ' + _('Author: %s') % plugininst.author self.descriptionLabel.setText( wordwrap(descr) ) self.fieldcntrls = [] self.fields = [] self.addFields() def addFields(self): """Add any fields, removing existing ones if required.""" layout = self.fieldGroup.layout() for line in self.fieldcntrls: for cntrl in line: layout.removeWidget(cntrl) cntrl.deleteLater() del self.fieldcntrls[:] currentwidget = '/' if self.mainwindow.treeedit.selwidgets: currentwidget = self.mainwindow.treeedit.selwidgets[0].path for row, field in enumerate(self.plugininst.fields): if isinstance(field, list) or isinstance(field, tuple): for c, f in enumerate(field): cntrls = f.makeControl(self.document, currentwidget) layout.addWidget(cntrls[0], row, c*2) layout.addWidget(cntrls[1], row, c*2+1) self.fieldcntrls.append(cntrls) self.fields.append(f) else: cntrls = field.makeControl(self.document, currentwidget) layout.addWidget(cntrls[0], row, 0) layout.addWidget(cntrls[1], row, 1) self.fieldcntrls.append(cntrls) self.fields.append(field) def slotReset(self): """Reset fields to defaults.""" self.addFields() def reEditDataset(self, ds, dsname): """Open up dataset in dialog for editing.""" oldfields = ds.pluginmanager.fields for field, cntrl in czip(self.fields, self.fieldcntrls): field.setControlVal(cntrl, oldfields[field.name]) def slotApply(self): """Use the plugin with the inputted data.""" # default field fields = {'currentwidget': '/'} if self.mainwindow.treeedit.selwidgets: fields = {'currentwidget': self.mainwindow.treeedit.selwidgets[0].path} # read values from controls for field, cntrls in czip(self.fields, self.fieldcntrls): fields[field.name] = field.getControlResults(cntrls) # run plugin plugin = self.pluginkls() statustext = runPlugin(self, self.document, plugin, fields) # show any results self.notifyLabel.setText(statustext) qt4.QTimer.singleShot(3000, self.notifyLabel.clear) def runPlugin(window, doc, plugin, fields): """Execute a plugin. window - parent window doc - veusz document plugin - plugin object.""" if isinstance(plugin, plugins.ToolsPlugin): mode = 'tools' elif isinstance(plugin, plugins.DatasetPlugin): mode = 'dataset' else: raise RuntimeError("Invalid plugin class") # use correct operation class for different plugin types if mode == 'tools': op = document.OperationToolsPlugin(plugin, fields) elif mode == 'dataset': # a bit of a hack as we don't give currentwidget to this plugin del fields['currentwidget'] op = document.OperationDatasetPlugin(plugin, fields) resultstext = '' qt4.QApplication.setOverrideCursor( qt4.QCursor(qt4.Qt.WaitCursor) ) try: results = doc.applyOperation(op) # evaluate datasets using plugin to check it works if mode == 'dataset': op.validate() resultstext = _('Created datasets: ') + ', '.join(results) else: resultstext = _('Done') except (plugins.ToolsPluginException, plugins.DatasetPluginException) as ex: # unwind operations op.undo(doc) qt4.QApplication.restoreOverrideCursor() qt4.QMessageBox.warning( window, _("Error in %s") % plugin.name, cstr(ex)) except Exception: op.undo(doc) qt4.QApplication.restoreOverrideCursor() # show exception dialog exceptiondialog.ExceptionDialog(sys.exc_info(), window).exec_() else: qt4.QApplication.restoreOverrideCursor() return resultstext def recreateDataset(mainwindow, document, dataset, datasetname): """Open dialog to recreate plugin dataset(s).""" # make a new instance of the plugin class kls = dataset.pluginmanager.plugin.__class__ newplugin = kls() dialog = PluginDialog(mainwindow, document, newplugin, kls) mainwindow.showDialog(dialog) dialog.reEditDataset(dataset, datasetname) veusz-3.0.1/veusz/dialogs/filterdialog.py0000664000175000017500000001045613161413406020133 0ustar jssjss00000000000000# Copyright (C) 2015 Jeremy S. Sanders # Email: Jeremy Sanders # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ############################################################################## """Dialog for filtering data.""" from __future__ import division, print_function from .. import qtall as qt4 from .. import document from ..qtwidgets.datasetbrowser import DatasetBrowser from .veuszdialog import VeuszDialog def _(text, disambiguation=None, context="FilterDialog"): """Translate text.""" return qt4.QCoreApplication.translate(context, text, disambiguation) class FilterDialog(VeuszDialog): """Preferences dialog.""" def __init__(self, parent, doc): """Setup dialog.""" VeuszDialog.__init__(self, parent, "filter.ui") self.document = doc self.dsbrowser = DatasetBrowser(doc, parent, None, checkable=True) grplayout = qt4.QVBoxLayout() grplayout.addWidget(self.dsbrowser) self.filtergroup.setLayout(grplayout) self.buttonBox.button(qt4.QDialogButtonBox.Apply).clicked.connect( self.applyClicked) self.buttonBox.button(qt4.QDialogButtonBox.Reset).clicked.connect( self.resetClicked) def updateStatus(self, text): """Show message in dialog.""" qt4.QTimer.singleShot(4000, self.statuslabel.clear) self.statuslabel.setText(text) def applyClicked(self): """Do the filtering.""" prefix = self.prefixcombo.currentText().strip() suffix = self.suffixcombo.currentText().strip() if not prefix and not suffix: self.updateStatus(_("Prefix and/or suffix must be entered")) return expr = self.exprcombo.currentText().strip() if not expr: self.updateStatus(_("Enter a valid filter expression")) return tofilter = self.dsbrowser.checkedDatasets() if not tofilter: self.updateStatus(_("Choose at least one dataset to filter")) return invert = self.invertcheck.isChecked() replaceblanks = self.replaceblankscheck.isChecked() op = document.OperationDatasetsFilter( expr, tofilter, prefix=prefix, suffix=suffix, invert=invert, replaceblanks=replaceblanks) ok, log = op.check(self.document) if not ok: self.updateStatus("\n".join(log)) return self.document.applyOperation(op) self.updateStatus(_("Filtered %i datasets") % len(tofilter)) def resetClicked(self): """Reset controls to defaults.""" for cntrl in self.exprcombo, self.prefixcombo, self.suffixcombo: cntrl.setEditText("") self.dsbrowser.reset() self.invertcheck.setChecked(False) self.replaceblankscheck.setChecked(False) self.updateStatus(_("Dialog reset")) def reEditDialog(self, dataset): """Load controls with settings from dataset.""" gen = dataset.generator self.exprcombo.setEditText(gen.inexpr) self.prefixcombo.setEditText(gen.prefix) self.suffixcombo.setEditText(gen.suffix) self.invertcheck.setChecked(gen.invert) self.replaceblankscheck.setChecked(gen.replaceblanks) datasets = [ d for d in gen.indatasets if d in self.document.data ] self.dsbrowser.setCheckedDatasets(datasets) def recreateDataset(mainwindow, document, dataset, datasetname): """Open dialog to recreate filter.""" dialog = FilterDialog(mainwindow, document) mainwindow.showDialog(dialog) dialog.reEditDialog(dataset) veusz-3.0.1/veusz/dialogs/importdialog.py0000664000175000017500000002732013161413406020156 0ustar jssjss00000000000000# data import dialog # Copyright (C) 2004 Jeremy S. Sanders # Email: Jeremy Sanders # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ############################################################################## """Module for implementing dialog boxes for importing data in Veusz.""" from __future__ import division, print_function import os.path import sys from ..compat import crange from .. import qtall as qt4 from .. import setting from .. import utils from .. import plugins from . import exceptiondialog from .veuszdialog import VeuszDialog def _(text, disambiguation=None, context="ImportDialog"): """Translate text.""" return qt4.QCoreApplication.translate(context, text, disambiguation) class ImportTab(qt4.QWidget): """Tab for a particular import type.""" resource = '' filetypes = () # list of file types handled filefilter = None # name of filter for types for open dialog def __init__(self, importdialog, *args): """Initialise dialog. importdialog is the import dialog itself.""" qt4.QWidget.__init__(self, *args) self.dialog = importdialog self.uiloaded = False def loadUi(self): """Load up UI file.""" qt4.loadUi(os.path.join(utils.resourceDirectory, 'ui', self.resource), self) self.uiloaded = True def reset(self): """Reset controls to initial conditions.""" pass def doPreview(self, filename, encoding): """Update the preview window, returning whether import should be attempted.""" pass def doImport(self, doc, filename, linked, encoding, prefix, suffix, tags): """Do the import iteself.""" pass def okToImport(self): """Secondary check (after preview) for enabling import button.""" return True def isFiletypeSupported(self, ftype): """Is the filetype supported by this tab?""" return ftype in self.filetypes def useFiletype(self, ftype): """If the tab can do something with the selected filetype, update itself.""" pass importtabs = [] def registerImportTab(name, klass): """Register an import tab for the dialog.""" importtabs.append((name, klass)) class ImportDialog(VeuszDialog): """Dialog box for importing data. See ImportTab classes above which actually do the work of importing """ dirname = '.' def __init__(self, parent, document): VeuszDialog.__init__(self, parent, 'import.ui') self.document = document # whether file import looks likely to work self.filepreviewokay = False # tabs loaded currently in dialog self.tabs = {} for tabname, tabclass in importtabs: w = tabclass(self) self.methodtab.addTab(w, tabname) # add promoted plugins for p in plugins.importpluginregistry: if p.promote_tab is not None: from ..dataimport.dialog_plugin import ImportTabPlugins w = ImportTabPlugins(self, promote=p.name) self.methodtab.addTab(w, p.name) self.methodtab.currentChanged.connect(self.slotTabChanged) self.browsebutton.clicked.connect(self.slotBrowseClicked) self.filenameedit.editTextChanged.connect(self.slotUpdatePreview) self.importbutton = self.buttonBox.addButton( _("&Import"), qt4.QDialogButtonBox.ApplyRole) self.importbutton.clicked.connect(self.slotImport) self.buttonBox.button(qt4.QDialogButtonBox.Reset).clicked.connect( self.slotReset) self.encodingcombo.currentIndexChanged.connect(self.slotUpdatePreview) # add completion for filename c = self.filenamecompleter = qt4.QCompleter(self) self.filenameedit.setCompleter(c) # change to tab last used self.methodtab.setCurrentIndex( setting.settingdb.get('import_lasttab', 0)) # defaults for prefix and suffix self.prefixcombo.default = self.suffixcombo.default = ['', '$FILENAME'] # default state for check boxes self.linkcheckbox.default = True # further defaults self.encodingcombo.defaultlist = utils.encodings self.encodingcombo.defaultval = 'utf_8' # load icon for clipboard self.clipbutton.setIcon( utils.getIcon('kde-clipboard') ) qt4.QApplication.clipboard().dataChanged.connect( self.updateClipPreview) self.clipbutton.clicked.connect(self.slotClipButtonClicked) self.updateClipPreview() def slotBrowseClicked(self): """Browse for a data file.""" fd = qt4.QFileDialog(self, _('Browse data file')) fd.setFileMode( qt4.QFileDialog.ExistingFile ) # collect filters from tabs filters = [_('All files (*.*)')] for i in crange(self.methodtab.count()): w = self.methodtab.widget(i) if w.filefilter: ftypes = ' '.join(['*'+t for t in w.filetypes]) f = '%s (%s)' % (w.filefilter, ftypes) filters.append(f) fd.setNameFilters(filters) lastfilt = setting.settingdb.get('import_filterbrowse') if lastfilt in filters: fd.selectNameFilter(lastfilt) # use filename to guess a path if possible filename = self.filenameedit.text() if os.path.isdir(filename): ImportDialog.dirname = filename elif os.path.isdir( os.path.dirname(filename) ): ImportDialog.dirname = os.path.dirname(filename) fd.setDirectory(ImportDialog.dirname) # update filename if changed if fd.exec_() == qt4.QDialog.Accepted: ImportDialog.dirname = fd.directory().absolutePath() self.filenameedit.replaceAndAddHistory( fd.selectedFiles()[0] ) setting.settingdb['import_filterbrowse'] = fd.selectedNameFilter() self.guessImportTab() def guessImportTab(self): """Guess import tab based on filename.""" filename = self.filenameedit.text() ftype = os.path.splitext(filename)[1] # strip off any gz, bz2 extensions to get real extension while ftype.lower() in ('.gz', '.bz2'): ftype = os.path.splitext(filename)[1] ftype = ftype.lower() # examine from left to right # promoted plugins come after plugins idx = -1 for i in crange(self.methodtab.count()): w = self.methodtab.widget(i) if w.isFiletypeSupported(ftype): idx = i if idx >= 0: self.methodtab.setCurrentIndex(idx) self.methodtab.widget(idx).useFiletype(ftype) def slotUpdatePreview(self, *args): """Update preview window when filename or tab changed.""" # save so we can restore later tab = self.methodtab.currentIndex() setting.settingdb['import_lasttab'] = tab filename = self.filenameedit.text() encoding = str(self.encodingcombo.currentText()) importtab = self.methodtab.currentWidget() if encoding == '': return if isinstance(importtab, ImportTab): if not importtab.uiloaded: importtab.loadUi() self.filepreviewokay = importtab.doPreview( filename, encoding) # enable or disable import button self.enableDisableImport() def slotTabChanged(self, tabindex): """Change completer depending on tab.""" self.slotUpdatePreview() w = self.methodtab.widget(tabindex) if w.filetypes is None: filters = ['*.*'] else: filters = ['*'+t for t in w.filetypes] model = qt4.QDirModel(filters, qt4.QDir.AllDirs | qt4.QDir.Files, qt4.QDir.Name) self.filenamecompleter.setModel(model) def enableDisableImport(self, *args): """Disable or enable import button if allowed.""" importtab = self.methodtab.currentWidget() enabled = self.filepreviewokay and importtab.okToImport() # actually enable or disable import button self.importbutton.setEnabled( enabled ) def slotImport(self): """Do the importing""" filename = self.filenameedit.text() linked = self.linkcheckbox.isChecked() encoding = str(self.encodingcombo.currentText()) if filename == '{clipboard}': linked = False else: # normalise filename filename = os.path.abspath(filename) # import according to tab selected importtab = self.methodtab.currentWidget() prefix, suffix = self.getPrefixSuffix(filename) tags = self.tagcombo.currentText().split() try: qt4.QApplication.setOverrideCursor( qt4.QCursor(qt4.Qt.WaitCursor) ) with self.document.suspend(): importtab.doImport(self.document, filename, linked, encoding, prefix, suffix, tags) qt4.QApplication.restoreOverrideCursor() except IOError: qt4.QApplication.restoreOverrideCursor() qt4.QMessageBox.warning( self, _("Veusz"), _("Could not read file")) except Exception: qt4.QApplication.restoreOverrideCursor() # show exception dialog d = exceptiondialog.ExceptionDialog(sys.exc_info(), self) d.exec_() def retnDatasetInfo(self, dsnames, linked, filename): """Return a list of information for the dataset names given.""" lines = [_('Imported data for datasets:')] dsnames.sort() for name in dsnames: ds = self.document.getData(name) # build up description lines.append(_('%s: %s') % (name, ds.description())) # whether the data were linked if linked: lines.append('') lines.append(_('Datasets were linked to file "%s"') % filename) return lines def getPrefixSuffix(self, filename): """Get prefix and suffix values.""" f = utils.cleanDatasetName( os.path.basename(filename) ) prefix = self.prefixcombo.lineEdit().text() prefix = prefix.replace('$FILENAME', f) suffix = self.suffixcombo.lineEdit().text() suffix = suffix.replace('$FILENAME', f) return prefix, suffix def slotReset(self): """Reset input fields.""" self.filenameedit.setText("") self.encodingcombo.setCurrentIndex( self.encodingcombo.findText("utf_8")) self.linkcheckbox.setChecked(True) self.prefixcombo.setEditText("") self.suffixcombo.setEditText("") importtab = self.methodtab.currentWidget() importtab.reset() def slotClipButtonClicked(self): """Clicked clipboard button.""" self.filenameedit.setText("{clipboard}") def updateClipPreview(self): """Clipboard contents changed, so update preview if showing clipboard.""" filename = self.filenameedit.text() if filename == '{clipboard}': self.slotUpdatePreview() veusz-3.0.1/veusz/dialogs/capturedialog.py0000664000175000017500000003000213161413406020276 0ustar jssjss00000000000000# Copyright (C) 2009 Jeremy S. Sanders # Email: Jeremy Sanders # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ############################################################################## """Veusz data capture dialog.""" from __future__ import division from ..compat import citems, cstr, cstrerror from .. import qtall as qt4 from .. import setting from ..dataimport import capture, simpleread from .veuszdialog import VeuszDialog def _(text, disambiguation=None, context="CaptureDialog"): """Translate text.""" return qt4.QCoreApplication.translate(context, text, disambiguation) class CaptureDialog(VeuszDialog): """Capture dialog. This allows the user to set the various capture options.""" def __init__(self, document, mainwindow): VeuszDialog.__init__(self, mainwindow, 'capture.ui') self.document = document # set values of edit controls from previous invocation (if any) d = setting.settingdb # Validate edit controls validator = qt4.QIntValidator(1, 65535, self) self.portEdit.setValidator(validator) validator = qt4.QIntValidator(1, 1000000000, self) self.numLinesStopEdit.setValidator(validator) self.timeStopEdit.setValidator(validator) self.tailEdit.setValidator(validator) # floating point values for interval self.updateIntervalsEdit.setValidator( qt4.QDoubleValidator(1e-2, 10000000, 2, self)) # add completion for filenames c = self.filenamecompleter = qt4.QCompleter(self) model = qt4.QDirModel(c) c.setModel(model) self.filenameEdit.setCompleter(c) # get notification of change of capture method self.methodBG = qt4.QButtonGroup(self) self.methodBG.addButton( self.captureFileButton, 0 ) self.methodBG.addButton( self.captureInternetButton, 1 ) self.methodBG.addButton( self.captureProgramButton, 2 ) self.methodBG.buttonClicked[int].connect(self.slotMethodChanged) # restore previously clicked button self.methodBG.button( d.get('CaptureDialog_method', 0) ).click() # get notification of change of stop method self.stopBG = qt4.QButtonGroup(self) self.stopBG.addButton( self.clickingStopButton, 0 ) self.stopBG.addButton( self.numLinesStopButton, 1 ) self.stopBG.addButton( self.timeStopButton, 2 ) self.stopBG.buttonClicked[int].connect(self.slotStopChanged) self.stopBG.button( d.get('CaptureDialog_stop', 0) ).click() # update interval self.updateIntervalsCheck.toggled.connect( self.updateIntervalsEdit.setEnabled) # tail data self.tailCheck.toggled.connect(self.tailEdit.setEnabled) # user starts capture self.captureButton = self.buttonBox.addButton( _("Ca&pture"), qt4.QDialogButtonBox.ApplyRole ) self.captureButton.clicked.connect(self.slotCaptureClicked) # filename browse button clicked self.browseButton.clicked.connect(self.slotBrowseClicked) def done(self, r): """Dialog is closed.""" VeuszDialog.done(self, r) # record values for next time dialog is opened d = setting.settingdb d['CaptureDialog_method'] = self.methodBG.checkedId() d['CaptureDialog_stop'] = self.stopBG.checkedId() def slotMethodChanged(self, buttonid): """Enable/disable correct controls in methodBG.""" # enable correct buttons fc = buttonid==0 self.filenameEdit.setEnabled(fc) self.browseButton.setEnabled(fc) ic = buttonid==1 self.hostEdit.setEnabled(ic) self.portEdit.setEnabled(ic) xc = buttonid==2 self.commandLineEdit.setEnabled(xc) def slotStopChanged(self, buttonid): """Enable/disable correct controls in stopBG.""" ns = buttonid == 1 self.numLinesStopEdit.setEnabled(ns) ts = buttonid == 2 self.timeStopEdit.setEnabled(ts) def slotBrowseClicked(self): """Browse for a data file.""" fd = qt4.QFileDialog(self, 'Browse data file or socket') fd.setFileMode( qt4.QFileDialog.ExistingFile ) # update filename if changed if fd.exec_() == qt4.QDialog.Accepted: self.filenameEdit.replaceAndAddHistory( fd.selectedFiles()[0] ) def slotCaptureClicked(self): """User requested capture.""" # object to interpret data from stream descriptor = self.descriptorEdit.text() simprd = simpleread.SimpleRead(descriptor) maxlines = None timeout = None updateinterval = None tail = None try: stop = self.stopBG.checkedId() if stop == 1: # number of lines to read before stopping maxlines = int( self.numLinesStopEdit.text() ) elif stop == 2: # maximum time period before stopping timeout = int( self.timeStopEdit.text() ) # whether to do an update periodically if self.updateIntervalsCheck.isChecked(): updateinterval = float( self.updateIntervalsEdit.text() ) # whether to only retain N values if self.tailCheck.isChecked(): tail = int( self.tailEdit.text() ) except ValueError: qt4.QMessageBox.critical(self, _("Invalid number"), _("Invalid number")) return # get method of getting data method = self.methodBG.checkedId() try: # create stream if method == 0: # file/socket stream = capture.FileCaptureStream(self.filenameEdit.text()) elif method == 1: # internet socket stream = capture.SocketCaptureStream( self.hostEdit.text(), int(self.portEdit.text()) ) elif method == 2: # external program stream = capture.CommandCaptureStream( self.commandLineEdit.text()) except EnvironmentError as e: # problem opening stream qt4.QMessageBox.critical(self, _("Cannot open input"), _("Cannot open input:\n" " %s (error %i)") % ( cstrerror(e), e.errno)) return stream.maxlines = maxlines stream.timeout = timeout simprd.tail = tail cd = CapturingDialog(self.document, simprd, stream, self, updateinterval=updateinterval) self.mainwindow.showDialog(cd) ######################################################################## class CapturingDialog(VeuszDialog): """Capturing data dialog. Shows progress to user.""" def __init__(self, document, simprd, stream, parent, updateinterval = None): """Initialse capture dialog: document: document to send data to simprd: object to interpret data stream: capturestream to read data from parent: parent widget updateinterval: if set, interval of seconds to update data in doc """ VeuszDialog.__init__(self, parent, 'capturing.ui') self.document = document self.simpleread = simprd self.stream = stream # connect buttons self.finishButton.clicked.connect(self.slotFinish) self.cancelButton.clicked.connect(self.slotCancel) # timer which governs reading from source self.readtimer = qt4.QTimer(self) self.readtimer.timeout.connect(self.slotReadTimer) # record time capture started self.starttime = qt4.QTime() self.starttime.start() # sort tree by dataset name self.datasetTreeWidget.sortItems(0, qt4.Qt.AscendingOrder) # timer for updating display self.displaytimer = qt4.QTimer(self) self.displaytimer.timeout.connect(self.slotDisplayTimer) self.sourceLabel.setText( self.sourceLabel.text() % stream.name ) self.txt_statusLabel = self.statusLabel.text() self.slotDisplayTimer() # initialise label # timer to update document self.updatetimer = qt4.QTimer(self) self.updateoperation = None if updateinterval: self.updatetimer.timeout.connect(self.slotUpdateTimer) self.updatetimer.start( int(updateinterval*1000) ) # start display and read timers self.displaytimer.start(1000) self.readtimer.start(10) def slotReadTimer(self): """Time to read more data.""" try: self.simpleread.readData(self.stream) except capture.CaptureFinishException as e: # stream tells us it's time to finish self.streamCaptureFinished( cstr(e) ) def slotDisplayTimer(self): """Time to update information about data source.""" self.statusLabel.setText( self.txt_statusLabel % (self.stream.bytesread, self.starttime.elapsed() // 1000) ) tree = self.datasetTreeWidget cts = self.simpleread.getDatasetCounts() # iterate over each dataset for name, length in citems(cts): find = tree.findItems(name, qt4.Qt.MatchExactly, 0) if find: # if already in tree, update number of counts find[0].setText(1, str(length)) else: # add new item tree.addTopLevelItem( qt4.QTreeWidgetItem([name, str(length)])) def slotUpdateTimer(self): """Called to update document while data is being captured.""" # undo any previous update if self.updateoperation: self.updateoperation.undo(self.document) # create new one self.updateoperation = capture.OperationDataCaptureSet( self.simpleread) # apply it (bypass history here - urgh) self.updateoperation.do(self.document) self.document.setModified() def streamCaptureFinished(self, message): """Stop timers, close stream and display message about finished stream.""" # stop reading / displaying self.readtimer.stop() self.displaytimer.stop() self.updatetimer.stop() if self.stream: # update stats self.slotDisplayTimer() # close stream self.stream.close() self.stream = None # show message from stream self.statusLabel.setText(message) def slotFinish(self): """Finish capturing and save the results.""" # close down timers self.streamCaptureFinished('') # undo any in-progress update if self.updateoperation: self.updateoperation.undo(self.document) # apply real document operation update op = capture.OperationDataCaptureSet(self.simpleread) self.document.applyOperation(op) # close dialog self.close() def slotCancel(self): """Cancel capturing.""" # close down timers self.streamCaptureFinished('') # undo any in-progress update if self.updateoperation: self.updateoperation.undo(self.document) self.document.setModified() # close dialog self.close() veusz-3.0.1/veusz/dialogs/veuszdialog.py0000664000175000017500000000443713161413406020024 0ustar jssjss00000000000000# Copyright (C) 2010 Jeremy S. Sanders # Email: Jeremy Sanders # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ############################################################################## """Define a base dialog class cleans up self after being hidden.""" from __future__ import division import os.path from .. import qtall as qt4 from .. import utils # register functions to open up dialogs to recreate a dataset recreate_register = {} class VeuszDialog(qt4.QDialog): """Base dialog class. - Loads self from ui file. - Deletes self on closing. - Emits dialogFinished when dialog is done """ dialogFinished = qt4.pyqtSignal(qt4.QDialog) def __init__(self, mainwindow, uifile, modal=False): """Initialise dialog given Veusz mainwindow and uifile for dialog. If modal is False, base on a top level window instead """ flag = qt4.Qt.Dialog if not modal: flag |= ( qt4.Qt.CustomizeWindowHint | qt4.Qt.WindowMinimizeButtonHint | qt4.Qt.WindowMaximizeButtonHint | qt4.Qt.WindowCloseButtonHint | qt4.Qt.WindowTitleHint | qt4.Qt.WindowSystemMenuHint ) qt4.QDialog.__init__(self, mainwindow, flag) self.setAttribute(qt4.Qt.WA_DeleteOnClose) qt4.loadUi(os.path.join(utils.resourceDirectory, 'ui', uifile), self) self.mainwindow = mainwindow def hideEvent(self, event): """Emits dialogFinished if hidden.""" if not event.spontaneous(): self.dialogFinished.emit(self) return qt4.QDialog.hideEvent(self, event) veusz-3.0.1/veusz/dialogs/datacreate.py0000664000175000017500000002725113161413406017564 0ustar jssjss00000000000000# Copyright (C) 2006 Jeremy S. Sanders # Email: Jeremy Sanders # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ############################################################################## """Dataset creation dialog.""" from ..compat import cstr from .. import qtall as qt4 from .. import utils from .. import document from .. import datasets from .veuszdialog import VeuszDialog def _(text, disambiguation=None, context="DataCreateDialog"): """Translate text.""" return qt4.QCoreApplication.translate(context, text, disambiguation) class _DSException(RuntimeError): """A class to handle errors while trying to create datasets.""" pass class DataCreateDialog(VeuszDialog): """Dialog to create datasets. They can be created from numerical ranges, parametrically or from expressions involving other dataset.""" def __init__(self, parent, document): """Initialise dialog with document.""" VeuszDialog.__init__(self, parent, 'datacreate.ui') self.document = document # create button group to get notification of changes self.methodGroup.radioClicked.connect(self.slotMethodChanged) # connect create button self.createbutton = self.buttonBox.addButton( _("C&reate"), qt4.QDialogButtonBox.ApplyRole ) self.replacebutton = self.buttonBox.addButton( _("&Replace"), qt4.QDialogButtonBox.ApplyRole ) self.buttonBox.button( qt4.QDialogButtonBox.Reset).clicked.connect(self.resetButtonClicked) self.createbutton.clicked.connect(self.createButtonClicked) self.replacebutton.clicked.connect(self.createButtonClicked) # connect notification of document change self.document.signalModified.connect(self.modifiedDocSlot) # set validators for edit controls self.numstepsedit.setValidator( qt4.QIntValidator(1, 99999999, self) ) self.tstartedit.setValidator( qt4.QDoubleValidator(self) ) self.tendedit.setValidator( qt4.QDoubleValidator(self) ) self.tstepsedit.setValidator( qt4.QIntValidator(1, 99999999, self) ) # connect up edit control to update create button status for edit in (self.numstepsedit, self.tstartedit, self.tendedit, self.tstepsedit, self.nameedit, self.valueedit): edit.editTextChanged.connect(self.editsEditSlot) self.nameedit.currentIndexChanged[int].connect(self.datasetSelected) # edit controls for dataset self.dsedits = { 'data': self.valueedit, 'serr': self.symerroredit, 'perr': self.poserroredit, 'nerr': self.negerroredit } # update button state self.editsEditSlot('') def slotMethodChanged(self, button): """Called when a new data creation method is used.""" # enable and disable correct widgets depending on method isvalue = button is self.valueradio self.valuehelperlabel.setVisible(isvalue) self.numstepsedit.setEnabled(isvalue) isparametric = button is self.parametricradio self.parametrichelperlabel.setVisible(isparametric) self.tstartedit.setEnabled(isparametric) self.tendedit.setEnabled(isparametric) self.tstepsedit.setEnabled(isparametric) isfunction = button is self.expressionradio self.expressionhelperlabel.setVisible(isfunction) # enable/disable create button self.editsEditSlot('') def modifiedDocSlot(self): """Update create button if document changes.""" self.editsEditSlot('') def datasetSelected(self, index): """If dataset is selected from drop down box, reload entries for editing.""" if index >= 0: dsname = self.nameedit.text() if dsname in self.document.data: self.reEditDataset(self.document.data[dsname], dsname) def reEditDataset(self, ds, dsname): """Given a dataset name, allow it to be edited again (if it is editable).""" if isinstance(ds, datasets.DatasetExpression): # change selected method if ds.parametric is None: # standard expression self.expressionradio.click() else: # parametric dataset self.parametricradio.click() p = ds.parametric self.tstartedit.setText( '%g' % p[0] ) self.tendedit.setText( '%g' % p[1] ) self.tstepsedit.setText( str(p[2]) ) # make sure name is set self.nameedit.setText(dsname) # set expressions for part in self.dsedits: text = ds.expr[part] if text is None: text = '' self.dsedits[part].setText(text) elif isinstance(ds, datasets.DatasetRange): # change selected method self.valueradio.click() # make sure name is set self.nameedit.setText(dsname) # set expressions for part in self.dsedits: data = getattr(ds, 'range_%s' % part) if data is None: text = '' else: text = '%g:%g' % data self.dsedits[part].setText(text) def editsEditSlot(self, dummytext): """Enable/disable createbutton.""" # dataset name checks dstext = self.nameedit.text() dsvalid = utils.validateDatasetName(dstext) dsexists = dstext in self.document.data # check other edit controls method = self.methodGroup.getRadioChecked() if method is self.valueradio: # value editsokay = self.numstepsedit.hasAcceptableInput() elif method is self.parametricradio: # parametric editsokay = (self.tstartedit.hasAcceptableInput() and self.tendedit.hasAcceptableInput() and self.tstepsedit.hasAcceptableInput()) else: # function editsokay = True # we needs some input on the value if not self.valueedit.text(): editsokay = False # hide / show create button depending whether dataset exists self.createbutton.setVisible(not dsexists) self.replacebutton.setVisible(dsexists) # enable buttons if expressions valid enabled = dsvalid and editsokay self.createbutton.setEnabled(enabled) self.replacebutton.setEnabled(enabled) def resetButtonClicked(self): """Reset button clicked - reset dialog.""" for cntrl in (self.valueedit, self.symerroredit, self.poserroredit, self.negerroredit, self.numstepsedit, self.tstartedit, self.tendedit, self.tstepsedit, self.nameedit): cntrl.setEditText("") self.linkcheckbox.setChecked(True) self.valueradio.click() def createButtonClicked(self): """Create button pressed.""" dsname = self.nameedit.text() dsexists = dsname in self.document.data try: # select function to create dataset with createfn = { self.valueradio: self.createFromRange, self.parametricradio: self.createParametric, self.expressionradio: self.createFromExpression }[ self.methodGroup.getRadioChecked()] # make a new dataset using method op = createfn(dsname) self.document.applyOperation(op) if dsexists: status = _("Replaced dataset '%s'") % dsname else: status = _("Created dataset '%s'") % dsname self.statuslabel.setText(status) except (document.CreateDatasetException, datasets.DatasetException, _DSException) as e: # all bad roads lead here - take exception string and tell user if dsexists: status = _("Replacement failed") else: status = _("Creation failed") if cstr(e) != '': status += ': %s' % cstr(e) self.statuslabel.setText(status) def createFromRange(self, name): """Make dataset from a range or constant. name is the name of the dataset Raises _DSException if error """ numsteps = int(self.numstepsedit.text()) # go over each of the ranges / values vals = {} for key, cntrl in self.dsedits.items(): text = cntrl.text().strip() if not text: continue if text.find(':') != -1: # an actual range parts = text.split(':') if len(parts) != 2: raise _DSException(_("Incorrect range format, use form 1:10")) try: minval, maxval = float(parts[0]), float(parts[1]) except ValueError: raise _DSException(_("Invalid number in range")) else: try: minval = float(text) except ValueError: raise _DSException(_("Invalid number")) maxval = minval vals[key] = (minval, maxval) linked = self.linkcheckbox.checkState() == qt4.Qt.Checked return document.OperationDatasetCreateRange( name, numsteps, vals, linked=linked) def createParametric(self, name): """Use a parametric form to create the dataset. Raises _DSException if error """ t0 = float(self.tstartedit.text()) t1 = float(self.tendedit.text()) numsteps = int(self.tstepsedit.text()) # get expressions vals = {} for key, cntrl in self.dsedits.items(): text = cntrl.text().strip() if text: vals[key] = text linked = self.linkcheckbox.checkState() == qt4.Qt.Checked return document.OperationDatasetCreateParameteric( name, t0, t1, numsteps, vals, linked=linked) def createFromExpression(self, name): """Create a dataset based on the expressions given.""" # get expression for each part of the dataset vals = {} for key, cntrl in self.dsedits.items(): text = cntrl.text().strip() if text: vals[key] = text link = self.linkcheckbox.checkState() == qt4.Qt.Checked op = document.OperationDatasetCreateExpression(name, vals, link) if not op.validateExpression(self.document): raise _DSException() return op def recreateDataset(mainwindow, document, dataset, datasetname): """Open dialog to recreate a DatasetExpression / DatasetRange.""" dialog = DataCreateDialog(mainwindow, document) mainwindow.showDialog(dialog) dialog.reEditDataset(dataset, datasetname) veusz-3.0.1/veusz/dialogs/aboutdialog.py0000664000175000017500000000413513244475663017774 0ustar jssjss00000000000000# about dialog box # aboutdialog.py # Copyright (C) 2006 Jeremy S. Sanders # Email: Jeremy Sanders # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ############################################################################## """About dialog module.""" from __future__ import division import os.path from .. import qtall as qt from .. import utils from .veuszdialog import VeuszDialog class AboutDialog(VeuszDialog): """About dialog.""" def __init__(self, mainwindow): VeuszDialog.__init__(self, mainwindow, 'about.ui', modal=True) # draw logo in dialog logo = utils.SvgWidgetFixedAspect(os.path.join(utils.imagedir, 'logo.svg')) self.logolayout.addWidget(logo) self.logoframe.setBackgroundRole(qt.QPalette.Base) self.logoframe.setAutoFillBackground(True) # add version to copyright text copyrighttext = self.copyrightlabel.text() copyrighttext = copyrighttext % {'version': utils.version()} self.copyrightlabel.setText(copyrighttext) self.licenseButton.clicked.connect(self.licenseClicked) def licenseClicked(self): """Show the license.""" LicenseDialog(self).exec_() class LicenseDialog(VeuszDialog): """About license dialog.""" def __init__(self, parent): VeuszDialog.__init__(self, parent, 'license.ui') self.licenseEdit.setPlainText(utils.getLicense()) veusz-3.0.1/veusz/dialogs/preferences.py0000664000175000017500000002740713302252431017767 0ustar jssjss00000000000000# Copyright (C) 2006 Jeremy S. Sanders # Email: Jeremy Sanders # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ############################################################################## from __future__ import division from .. import qtall as qt4 from .. import setting from .. import utils from .. import document from .veuszdialog import VeuszDialog def _(text, disambiguation=None, context="PrefsDialog"): """Translate text.""" return qt4.QCoreApplication.translate(context, text, disambiguation) # names for display of colors and a longer description color_names = { 'page': (_('Page'), _('Page background color')), 'error': (_('Error'), _('Color for errors')), 'command': (_('Console command'), _('Commands in the console window color')), 'cntrlline': (_('Control line'), _('Color of lines controlling widgets')), 'cntrlcorner': (_('Control corner'), _('Color of corners controlling widgets')), } class PreferencesDialog(VeuszDialog): """Preferences dialog.""" def __init__(self, mainwindow): """Setup dialog.""" VeuszDialog.__init__(self, mainwindow, 'preferences.ui', modal=True) self.plotwindow = mainwindow.plot # for ease of use setdb = setting.settingdb # view settings self.antialiasCheck.setChecked( setdb['plot_antialias'] ) self.englishCheck.setChecked( setdb['ui_english'] ) for intv in self.plotwindow.updateintervals: self.intervalCombo.addItem(intv[1]) index = [i[0] for i in self.plotwindow.updateintervals].index( setdb['plot_updatepolicy']) self.intervalCombo.setCurrentIndex(index) self.threadSpinBox.setValue( setdb['plot_numthreads'] ) self.translationEdit.setText( setdb['translation_file'] ) self.translationBrowseButton.clicked.connect( self.translationBrowseClicked) # disable thread option if not supported if not qt4.QFontDatabase.supportsThreadedFontRendering(): self.threadSpinBox.setEnabled(False) self.threadSpinBox.setToolTip(_("Disabled because of lack of " "threaded drawing support")) # use cwd for file dialogs (self.dirDocCWDRadio if setdb['dirname_usecwd'] else self.dirDocPrevRadio).click() # exporting documents { 'doc': self.dirExportDocRadio, 'cwd': self.dirExportCWDRadio, 'prev': self.dirExportPrevRadio, }[setdb.get('dirname_export_location')].click() # templates when exporting self.exportTemplSingleEdit.setText(setdb['export_template_single']) self.exportTemplMultiEdit.setText(setdb['export_template_multi']) # set icon size self.iconSizeCombo.setCurrentIndex( self.iconSizeCombo.findText( str(setdb['toolbar_size']))) # default stylesheet self.styleLineEdit.setText(setdb['stylesheet_default']) self.styleBrowseButton.clicked.connect(self.styleBrowseClicked) # default custom settings self.customLineEdit.setText(setdb['custom_default']) self.customBrowseButton.clicked.connect(self.customBrowseClicked) # for plugins plugins = list( setdb.get('plugins', []) ) self.pluginmodel = qt4.QStringListModel(plugins) self.pluginList.setModel(self.pluginmodel) self.pluginAddButton.clicked.connect(self.pluginAddClicked) self.pluginRemoveButton.clicked.connect(self.pluginRemoveClicked) # specifics for color tab self.setupColorTab() # for point picker self.pickerToConsoleCheck.setChecked( setdb['picker_to_console'] ) self.pickerToClipboardCheck.setChecked( setdb['picker_to_clipboard'] ) # python path self.externalPythonPath.setText(setdb['external_pythonpath']) self.externalGhostscript.setText(setdb['external_ghostscript']) self.externalGhostscriptBrowse.clicked.connect( self.externalGhostscriptBrowseClicked) self.externalNewVerCheck.setChecked(setdb['vercheck_disabled']) if utils.disableVersionChecks: self.externalNewVerCheck.setEnabled(False) self.externalNewVerCheck.setChecked(setdb['feedback_disabled']) if utils.disableFeedback: self.externalFeedbackCheck.setEnabled(False) def setupColorTab(self): """Initialise color tab this makes a grid of controls for each color consisting of label, isdefault check and change color button.""" setdb = setting.settingdb # theme themes = sorted(list(document.colors.colorthemes)) self.colorThemeDefCombo.addItems(themes) self.colorThemeDefCombo.setCurrentIndex( themes.index(setdb['colortheme_default'])) # UI colors self.chosencolors = {} self.colorbutton = {} self.colordefaultcheck = {} layout = qt4.QGridLayout() for row, colname in enumerate(setdb.colors): isdefault, colval = setting.settingdb['color_%s' % colname] self.chosencolors[colname] = qt4.QColor(colval) # label name, tooltip = color_names[colname] label = qt4.QLabel(name) label.setToolTip(tooltip) layout.addWidget(label, row, 0) # is default check defcheck = qt4.QCheckBox(_("Default")) defcheck.setToolTip( _("Use the default color instead of the one chosen here")) layout.addWidget(defcheck, row, 1) self.colordefaultcheck[colname] = defcheck defcheck.setChecked(isdefault) # connect button to method to change color button = self.colorbutton[colname] = qt4.QPushButton() def getcolclick(cname): # double function to get around colname changing return lambda: self.colorButtonClicked(cname) button.clicked.connect(getcolclick(colname)) layout.addWidget(button, row, 2) self.colorGroup.setLayout(layout) self.updateButtonColors() def colorButtonClicked(self, cname): """Open color dialog if color button clicked.""" retcolor = qt4.QColorDialog.getColor( self.chosencolors[cname], self ) if retcolor.isValid(): self.chosencolors[cname] = retcolor self.updateButtonColors() def updateButtonColors(self): """Update color icons on color buttons.""" for name, val in self.chosencolors.items(): pixmap = qt4.QPixmap(16, 16) pixmap.fill(val) self.colorbutton[name].setIcon( qt4.QIcon(pixmap) ) def accept(self): """Keep settings if okay pressed.""" qt4.QDialog.accept(self) # view settings setdb = setting.settingdb setdb['plot_updatepolicy'] = ( self.plotwindow.updateintervals[self.intervalCombo.currentIndex()][0] ) setdb['plot_antialias'] = self.antialiasCheck.isChecked() setdb['ui_english'] = self.englishCheck.isChecked() setdb['plot_numthreads'] = self.threadSpinBox.value() setdb['translation_file'] = self.translationEdit.text() # use cwd setdb['dirname_usecwd'] = self.dirDocCWDRadio.isChecked() for radio, val in ( (self.dirExportDocRadio, 'doc'), (self.dirExportCWDRadio, 'cwd'), (self.dirExportPrevRadio, 'prev'), ): if radio.isChecked(): setdb['dirname_export_location'] = val # templates for exporting setdb['export_template_single'] = self.exportTemplSingleEdit.text().strip() setdb['export_template_multi'] = self.exportTemplMultiEdit.text().strip() # update icon size if necessary iconsize = int( self.iconSizeCombo.currentText() ) if iconsize != setdb['toolbar_size']: setdb['toolbar_size'] = iconsize for widget in self.parent().children(): # find toolbars if isinstance(widget, qt4.QToolBar): widget.setIconSize( qt4.QSize(iconsize, iconsize) ) # new document settings setdb['stylesheet_default'] = self.styleLineEdit.text() setdb['custom_default'] = self.customLineEdit.text() # color theme setdb['colortheme_default'] = self.colorThemeDefCombo.currentText() # UI colors for name, color in self.chosencolors.items(): isdefault = self.colordefaultcheck[name].isChecked() colorname = color.name() setdb['color_' + name] = (isdefault, colorname) # plugins plugins = self.pluginmodel.stringList() setdb['plugins'] = plugins # picker setdb['picker_to_clipboard'] = self.pickerToClipboardCheck.isChecked() setdb['picker_to_console'] = self.pickerToConsoleCheck.isChecked() # python path setdb['external_pythonpath'] = self.externalPythonPath.text() # where to find ghostscript setdb['external_ghostscript'] = self.externalGhostscript.text() # version updates setdb['vercheck_disabled'] = self.externalNewVerCheck.isChecked() # feedback setdb['feedback_disabled'] = self.externalFeedbackCheck.isChecked() self.plotwindow.updatePlotSettings() # write settings out now, rather than wait until the end setdb.writeSettings() def translationBrowseClicked(self): """Browse for a translation.""" filename = self.parent().fileOpenDialog( [_('Translation file (*.qm)')], _('Choose translation file')) if filename: self.translationEdit.setText(filename) def styleBrowseClicked(self): """Browse for a stylesheet.""" filename = self.parent().fileOpenDialog( [_('Veusz stylesheet (*.vst)')], _('Choose stylesheet')) if filename: self.styleLineEdit.setText(filename) def customBrowseClicked(self): """Browse for a custom definitons.""" filename = self.parent().fileOpenDialog( [_('Veusz document (*.vsz)')], _('Choose custom definitons')) if filename: self.customLineEdit.setText(filename) def pluginAddClicked(self): """Add a new plugin.""" filename = self.parent().fileOpenDialog( [_('Python scripts (*.py)')], _('Choose plugin')) if filename: self.pluginmodel.insertRows(0, 1) self.pluginmodel.setData( self.pluginmodel.index(0), filename ) def pluginRemoveClicked(self): """Remove selected plugin.""" sel = self.pluginList.selectionModel().currentIndex() if sel.isValid(): self.pluginmodel.removeRow( sel.row() ) def externalGhostscriptBrowseClicked(self): """Choose a ghostscript executable.""" filename = self.parent().fileOpenDialog( [_('All files (*)')], _('Choose ghostscript executable')) if filename: self.externalGhostscript.setText(filename) veusz-3.0.1/veusz/dialogs/__init__.py0000664000175000017500000000461113161413406017221 0ustar jssjss00000000000000# Copyright (C) 2008 Jeremy S. Sanders # Email: Jeremy Sanders # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ############################################################################## """Veusz dialogs module.""" # load custom widgets from .. import qtwidgets from .. import datasets from .veuszdialog import recreate_register # lazy loading recreation routines def _lazy_recreate_1d(*args): from .datacreate import recreateDataset recreateDataset(*args) def _lazy_recreate_2d(*args): from .datacreate2d import recreateDataset recreateDataset(*args) def _lazy_recreate_histo(*args): from .histodata import recreateDataset recreateDataset(*args) def _lazy_recreate_filtered(*args): from .filterdialog import recreateDataset recreateDataset(*args) def _lazy_recreate_plugin(*args): from .plugin import recreateDataset recreateDataset(*args) for kls, fn in ( (datasets.DatasetExpression, _lazy_recreate_1d), (datasets.DatasetRange, _lazy_recreate_1d), (datasets.Dataset2DXYZExpression, _lazy_recreate_2d), (datasets.Dataset2DExpression, _lazy_recreate_2d), (datasets.Dataset2DXYFunc, _lazy_recreate_2d), (datasets.DatasetHistoValues, _lazy_recreate_histo), (datasets.DatasetHistoBins, _lazy_recreate_histo), (datasets.DatasetFiltered, _lazy_recreate_filtered), (datasets.Dataset1DPlugin, _lazy_recreate_plugin), (datasets.Dataset2DPlugin, _lazy_recreate_plugin), (datasets.DatasetNDPlugin, _lazy_recreate_plugin), (datasets.DatasetTextPlugin, _lazy_recreate_plugin), (datasets.DatasetDateTimePlugin, _lazy_recreate_plugin), ): recreate_register[kls] = fn veusz-3.0.1/veusz/dialogs/datacreate2d.py0000664000175000017500000002114013161413406020001 0ustar jssjss00000000000000# Copyright (C) 2007 Jeremy S. Sanders # Email: Jeremy Sanders # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ############################################################################## """Dataset creation dialog for 2d data.""" from __future__ import division from ..compat import crange, citems, cstr from .. import qtall as qt4 from .. import utils from .. import document from .. import datasets from .veuszdialog import VeuszDialog def _(text, disambiguation=None, context="DataCreate2D"): """Translate text.""" return qt4.QCoreApplication.translate(context, text, disambiguation) def checkGetStep(text): """Check step syntax is okay. Syntax is min:max:stepsize Returns None if fails """ parts = text.split(':') if len(parts) == 3: try: return tuple([float(x) for x in parts]) except ValueError: pass return None class DataCreate2DDialog(VeuszDialog): def __init__(self, parent, document): """Initialise dialog with document.""" VeuszDialog.__init__(self, parent, 'datacreate2d.ui') self.document = document self.createbutton = self.buttonBox.addButton( _("C&reate"), qt4.QDialogButtonBox.ApplyRole ) self.createbutton.clicked.connect(self.createButtonClickedSlot) self.fromxyfunc.toggled.connect(self.fromxyfuncSlot) self.fromxyzexpr.toggled.connect(self.fromxyzexprSlot) self.from2dexpr.toggled.connect(self.from2dexprSlot) document.signalModified.connect(self.updateDatasetLists) for combo in (self.namecombo, self.xexprcombo, self.yexprcombo, self.zexprcombo): combo.editTextChanged.connect(self.enableDisableCreate) self.fromxyzexpr.toggle() self.enableDisableCreate() # change mode according to radio pressed def fromxyfuncSlot(self, checked): self.mode = 'xyfunc' if checked: self.updateDatasetLists() def fromxyzexprSlot(self, checked): self.mode = 'xyzexpr' if checked: self.updateDatasetLists() def from2dexprSlot(self, checked): self.mode = '2dexpr' if checked: self.updateDatasetLists() def escapeDatasets(self, dsnames): """Escape dataset names if they are not typical python ones.""" for i in crange(len(dsnames)): if not utils.validPythonIdentifier(dsnames[i]): dsnames[i] = '`%s`' % dsnames[i] def updateDatasetLists(self): """Update controls depending on selected mode.""" # get list of 1d and 2d numeric datasets datasets = [[],[]] for name, ds in citems(self.document.data): if ds.datatype == 'numeric': datasets[ds.dimensions-1].append(name) datasets[0].sort() datasets[1].sort() # make sure names are escaped if they have funny characters self.escapeDatasets(datasets[0]) self.escapeDatasets(datasets[1]) # help the user by listing existing datasets utils.populateCombo(self.namecombo, datasets[0]) if self.mode == 'xyzexpr': # enable everything for combo in self.xexprcombo, self.yexprcombo, self.zexprcombo: combo.setDisabled(False) utils.populateCombo(combo, datasets[0]) elif self.mode == '2dexpr': # only enable the z expression button self.xexprcombo.setDisabled(True) self.yexprcombo.setDisabled(True) self.zexprcombo.setDisabled(False) utils.populateCombo(self.zexprcombo, datasets[1]) else: # enable everything for combo in self.xexprcombo, self.yexprcombo, self.zexprcombo: combo.setDisabled(False) # put in some examples to help the the user utils.populateCombo(self.xexprcombo, ['0:10:0.1']) utils.populateCombo(self.yexprcombo, ['0:10:0.1']) utils.populateCombo(self.zexprcombo, ['x+y']) def reEditDataset(self, ds, dsname): """Allow dataset to be edited again.""" self.namecombo.setEditText(dsname) self.linkcheckbox.setChecked(True) if isinstance(ds, datasets.Dataset2DXYZExpression): self.fromxyzexpr.click() self.xexprcombo.setEditText(ds.exprx) self.yexprcombo.setEditText(ds.expry) self.zexprcombo.setEditText(ds.exprz) elif isinstance(ds, datasets.Dataset2DExpression): self.from2dexpr.click() self.xexprcombo.clearEditText() self.yexprcombo.clearEditText() self.zexprcombo.setEditText(ds.expr) elif isinstance(ds, datasets.Dataset2DXYFunc): self.fromxyfunc.click() self.xexprcombo.setEditText('%g:%g:%g' % tuple(ds.xstep)) self.yexprcombo.setEditText('%g:%g:%g' % tuple(ds.ystep)) self.zexprcombo.setEditText(ds.expr) else: raise RuntimeError('Invalid dataset type') def enableDisableCreate(self): """Enable or disable create button.""" # get contents of combo boxes text = {} for name in ('xexpr', 'yexpr', 'zexpr', 'name'): text[name] = getattr(self, name+'combo').currentText().strip() disable = False # need name and zexpr disable = disable or not text['name'] or not text['zexpr'] if self.mode == 'xyzexpr': # need x and yexpr disable = disable or not text['xexpr'] or not text['yexpr'] elif self.mode == '2dexpr': # nothing else pass elif self.mode == 'xyfunc': # need x and yexpr in special step format min:max:step disable = disable or ( checkGetStep(text['xexpr']) is None or checkGetStep(text['yexpr']) is None ) # finally check button self.createbutton.setDisabled(disable) def createButtonClickedSlot(self): """Create button pressed.""" text = {} for name in ('xexpr', 'yexpr', 'zexpr', 'name'): text[name] = getattr(self, name+'combo').currentText().strip() link = self.linkcheckbox.checkState() == qt4.Qt.Checked # create and apply operation, catching evaluation errors try: if self.mode == 'xyzexpr': # build operation op = document.OperationDataset2DCreateExpressionXYZ( text['name'], text['xexpr'], text['yexpr'], text['zexpr'], link) elif self.mode == '2dexpr': op = document.OperationDataset2DCreateExpression( text['name'], text['zexpr'], link) elif self.mode == 'xyfunc': xstep = checkGetStep(text['xexpr']) ystep = checkGetStep(text['yexpr']) # build operation op = document.OperationDataset2DXYFunc( text['name'], xstep, ystep, text['zexpr'], link) # check expression is okay op.validateExpression(self.document) # try to make dataset self.document.applyOperation(op) # forces an evaluation self.document.data[text['name']].data except (document.CreateDatasetException, datasets.DatasetException) as e: msg = _("Failed to create dataset '%s'") % text['name'] s = cstr(e) if s: msg += ' (%s)' % s else: msg = _("Created dataset '%s'") % text['name'] self.notifylabel.setText(msg) qt4.QTimer.singleShot(4000, self.notifylabel.clear) def recreateDataset(mainwindow, document, dataset, datasetname): """Open dialog to recreate a DatasetExpression / DatasetRange.""" dialog = DataCreate2DDialog(mainwindow, document) mainwindow.showDialog(dialog) dialog.reEditDataset(dataset, datasetname) veusz-3.0.1/veusz/dialogs/safetyimport.py0000664000175000017500000000646613161413406020222 0ustar jssjss00000000000000# Copyright (C) 2009 Jeremy S. Sanders # Email: Jeremy Sanders # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ############################################################################## """Ask user whether to import symbols.""" from __future__ import division from .. import qtall as qt4 from .. import setting def _(text, disambiguation=None, context="SafetyImportDialog"): """Translate text.""" return qt4.QCoreApplication.translate(context, text, disambiguation) class SafetyImportDialog(qt4.QMessageBox): def __init__(self, parent, module, names): """Initialise dialog. parent is parent widget module is module to import symbols from names is a list of names to import.""" qt4.QMessageBox.__init__(self, parent) self.names = names self.module = module self.setIcon(qt4.QMessageBox.Warning) self.setWindowTitle(_("Allow Python import?")) self.setText(_("The document has requested that the symbol(s):\n" " %s\nbe loaded from Python module '%s'.\n\n" "This could be unsafe if the document comes from " "an untrusted source.") % ( ', '.join(names), module)) self.allow = self.addButton(_("Allow"), qt4.QMessageBox.YesRole) self.allow.setToolTip(_("Allow use of symbol in module during session")) self.allowalways = self.addButton(_("Allow always"), qt4.QMessageBox.YesRole) self.allowalways.setToolTip(_("Always allow use of symbol in module")) self.notallow = self.addButton(_("Do not allow"), qt4.QMessageBox.NoRole) self.notallow.setToolTip(_("Do allow use of symbol in module in session")) def exec_(self): """Execute dialog.""" # when loading the document the busy cursor is on, this gets # rid of it for a while qt4.qApp.setOverrideCursor(qt4.QCursor(qt4.Qt.ArrowCursor)) qt4.QMessageBox.exec_(self) qt4.qApp.restoreOverrideCursor() b = self.clickedButton() # update lists of variables in settings depending on chosen button if b is self.allow: a = setting.transient_settings['import_allowed'][self.module] a |= set(self.names) elif b is self.allowalways: a = setting.settingdb['import_allowed'][self.module] a.update( [(x, True) for x in self.names] ) elif b is self.notallow: a = setting.transient_settings['import_notallowed'][self.module] a |= set(self.names) veusz-3.0.1/veusz/dialogs/stylesheet.py0000664000175000017500000001203313161413406017650 0ustar jssjss00000000000000# Copyright (C) 2009 Jeremy S. Sanders # Email: Jeremy Sanders # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ############################################################################### from __future__ import division from ..compat import cstrerror from .. import utils from .. import qtall as qt4 from .. import document from ..windows.treeeditwindow import TabbedFormatting, PropertyList, \ SettingsProxySingle from .veuszdialog import VeuszDialog def _(text, disambiguation=None, context="StylesheetDialog"): """Translate text.""" return qt4.QCoreApplication.translate(context, text, disambiguation) class StylesheetDialog(VeuszDialog): """This is a dialog box to edit stylesheets. Most of the work is done elsewhere, so this doesn't do a great deal """ def __init__(self, parent, document): VeuszDialog.__init__(self, parent, 'stylesheet.ui') self.document = document self.stylesheet = document.basewidget.settings.StyleSheet self.stylesListWidget.setMinimumWidth(100) # initial properties widget self.tabformat = None self.properties = None self.fillStyleList() self.stylesListWidget.currentItemChanged.connect( self.slotStyleItemChanged) self.stylesListWidget.setCurrentRow(0) # we disable default buttons as they keep grabbing the enter key close = self.buttonBox.button(qt4.QDialogButtonBox.Close) close.setDefault(False) close.setAutoDefault(False) self.saveButton.clicked.connect(self.slotSaveStyleSheet) self.loadButton.clicked.connect(self.slotLoadStyleSheet) # recent button shows list of recently used files for loading self.recentButton.filechosen.connect(self.loadStyleSheet) self.recentButton.setSetting('stylesheetdialog_recent') def loadStyleSheet(self, filename): """Load the given stylesheet.""" self.document.applyOperation( document.OperationLoadStyleSheet(filename) ) def fillStyleList(self): """Fill list of styles.""" for stns in self.stylesheet.getSettingsList(): item = qt4.QListWidgetItem(utils.getIcon(stns.pixmap), stns.usertext) item.VZsettings = stns self.stylesListWidget.addItem(item) def slotStyleItemChanged(self, current, previous): """Item changed in list of styles.""" if current is None: return if self.tabformat: self.tabformat.deleteLater() if self.properties: self.properties.deleteLater() settings = current.VZsettings # update formatting properties setnsproxy = SettingsProxySingle(self.document, settings) self.tabformat = TabbedFormatting(self.document, setnsproxy) self.formattingGroup.layout().addWidget(self.tabformat) # update properties self.properties = PropertyList(self.document, showformatsettings=False) self.properties.updateProperties(setnsproxy, showformatting=False) self.propertiesScrollArea.setWidget(self.properties) def slotSaveStyleSheet(self): """Save stylesheet as a file.""" filename = self.parent().fileSaveDialog( [_('Veusz stylesheet (*.vst)')], _('Save stylesheet')) if filename: try: f = open(filename, 'w') self.document.exportStyleSheet(f) f.close() self.recentButton.addFile(filename) except EnvironmentError as e: qt4.QMessageBox.critical( self, _("Error - Veusz"), _("Unable to save '%s'\n\n%s") % ( filename, cstrerror(e))) def slotLoadStyleSheet(self): """Load a style sheet.""" filename = self.parent().fileOpenDialog( [_('Veusz stylesheet (*.vst)')], _('Load stylesheet')) if filename: try: self.loadStyleSheet(filename) except EnvironmentError as e: qt4.QMessageBox.critical( self, _("Error - Veusz"), _("Unable to load '%s'\n\n%s") % ( filename, cstrerror(e))) else: # add to recent file list self.recentButton.addFile(filename) veusz-3.0.1/veusz/dialogs/exceptiondialog.py0000664000175000017500000001742113244475576020665 0ustar jssjss00000000000000# Copyright (C) 2006 Jeremy S. Sanders # Email: Jeremy Sanders # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ############################################################################## '''Dialog to pop up if an exception occurs in Veusz. This allows the user to send a bug report.''' from __future__ import division, print_function import sys import time import traceback import re import base64 import numpy import sip from ..compat import citems, curlrequest, cexceptionuser from .. import qtall as qt from .. import utils from .veuszdialog import VeuszDialog def _(text, disambiguation=None, context="ExceptionDialog"): """Translate text.""" return qt.QCoreApplication.translate(context, text, disambiguation) _emailUrl ='https://barmag.net/veusz-mail.php' _reportformat = \ '''Veusz version: %s Python version: %s Python platform: %s Numpy version: %s Qt version: %s PyQt version: %s SIP version: %s Date: %s %s ''' _sendformat = \ '''Email: %s Error report ------------ %s What the user was doing before the crash ---------------------------------------- %s ''' def createReportText(exception): return _reportformat % ( utils.version(), sys.version, sys.platform, numpy.__version__, qt.qVersion(), qt.PYQT_VERSION_STR, sip.SIP_VERSION_STR, time.strftime('%a, %d %b %Y %H:%M:%S +0000', time.gmtime()), cexceptionuser(exception), ) class ExceptionSendDialog(VeuszDialog): """Dialog to send debugging report.""" def __init__(self, exception, parent): VeuszDialog.__init__(self, parent, 'exceptionsend.ui') # debugging report text self.text = createReportText(exception) self.detailstosend.setPlainText(self.text) def accept(self): """Send text.""" # build up the text of the message text = ( _sendformat % ( self.emailedit.text(), self.text, self.detailsedit.toPlainText() )) # send the message as base-64 encoded utf-8 text = base64.b64encode(text.encode('utf8')) try: # send the message curlrequest.urlopen(_emailUrl, b'message='+text) except: # something went wrong... qt.QMessageBox.critical( None, _("Veusz"), _("Failed to connect to error server " "to send report. Is your internet connected?")) return qt.QMessageBox.information( self, _("Submitted"), _("Thank you for submitting an error report")) VeuszDialog.accept(self) def _raiseIgnoreException(): """Ignore this exception to clear out stack frame of previous exception.""" raise utils.IgnoreException() def formatLocals(exception): """Return local variables.""" alreadyself = set() tb = exception[2] outlines = [] while tb: frame = tb.tb_frame tb = tb.tb_next outlines.append('') outlines.append( 'Frame %s (File %s, line %s)' % (frame.f_code.co_name, frame.f_code.co_filename, frame.f_lineno)) # get local variables for frame for key, value in citems(frame.f_locals): # print out variables in frame try: v = repr(value) except: v = '' if len(v) > 128: v = v[:120] + '...' outlines.append(' %s = %s' % (key, v)) # print out attributes if item is self if key == 'self' and id(value) not in alreadyself: alreadyself.add(id(value)) for attr in sorted( dir(value) ): try: v = getattr(value, attr) except: # can sometimes get type error continue if hasattr(v, '__call__'): # skip callables, to cut down output continue try: sv = repr(v) except: sv = '' if len(sv) > 128: sv = sv[:120] + '...' outlines.append(' self.%s = %s' % (attr, sv)) return '\n'.join(outlines) class ExceptionDialog(VeuszDialog): """Choose an exception to send to developers.""" ignore_exceptions = set() def __init__(self, exception, parent): VeuszDialog.__init__(self, parent, 'exceptionlist.ui') # get text for traceback and locals self.fmtexcept = ''.join(traceback.format_exception(*exception)) self.backtrace = self.fmtexcept + formatLocals(exception) self.errortextedit.setPlainText(self.backtrace) # set critical pixmap to left of dialog icon = qt.qApp.style().standardIcon( qt.QStyle.SP_MessageBoxCritical, None, self) self.erroriconlabel.setPixmap(icon.pixmap(32)) self.ignoreSessionButton.clicked.connect(self.ignoreSessionSlot) self.saveButton.clicked.connect(self.saveButtonSlot) self.checkVeuszVersion() if not _emailUrl: self.okButton.hide() def checkVeuszVersion(self): """See whether there is a later version of veusz and inform the user.""" newver = utils.latestVersion() thisver = utils.version() if not newver: msg = _('Could not check the latest Veusz version') else: # convert to tuples for comparison newver_tup = utils.versionToTuple(newver) thisver_tup = utils.versionToTuple(thisver) if thisver_tup == newver_tup: msg = _('You are running the latest released Veusz version') elif thisver_tup > newver_tup: msg = _('You are running an unreleased Veusz version') else: msg = ( _('Your current version of Veusz is old. ' 'Veusz %s is available.') % newver) self.veuszversionlabel.setText(msg) def accept(self): """Accept by opening send dialog.""" d = ExceptionSendDialog(self.backtrace, self) if d.exec_() == qt.QDialog.Accepted: VeuszDialog.accept(self) def ignoreSessionSlot(self): """Ignore exception for session.""" ExceptionDialog.ignore_exceptions.add(self.fmtexcept) self.reject() def saveButtonSlot(self): filename = qt.QFileDialog.getSaveFileName(self, 'Save File') if filename[0]: f = open(filename[0], 'w') f.write(createReportText(self.backtrace)) f.close() self.close() def exec_(self): """Exec dialog if exception is not ignored.""" if self.fmtexcept not in ExceptionDialog.ignore_exceptions: VeuszDialog.exec_(self) # send another exception shortly - this clears out the current one # so the stack frame of the current exception is released qt.QTimer.singleShot(0, _raiseIgnoreException) veusz-3.0.1/veusz/dialogs/histodata.py0000664000175000017500000002562313161413406017450 0ustar jssjss00000000000000# Copyright (C) 2010 Jeremy S. Sanders # Email: Jeremy Sanders # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ############################################################################## from __future__ import division from ..compat import crange, citems, cstr from .. import qtall as qt from .. import utils from .. import datasets from .. import document from .veuszdialog import VeuszDialog def _(text, disambiguation=None, context="HistogramDialog"): """Translate text.""" return qt.QCoreApplication.translate(context, text, disambiguation) def checkValidator(combo): """Is this validator ok?""" valid = combo.validator() state, s, x = valid.validate(combo.currentText(), 0) return state == qt.QValidator.Acceptable class ManualBinModel(qt.QAbstractListModel): """Model to store a list of floating point values in a list.""" def __init__(self, thedata): qt.QAbstractListModel.__init__(self) self.thedata = thedata def data(self, index, role): if role == qt.Qt.DisplayRole and index.isValid(): return float(self.thedata[index.row()]) return None def rowCount(self, parent): return len(self.thedata) def flags(self, index): return ( qt.Qt.ItemIsSelectable | qt.Qt.ItemIsEnabled | qt.Qt.ItemIsEditable ) def setData(self, index, value, role): if role == qt.Qt.EditRole: try: val = float(value) except ValueError: return False self.thedata[ index.row() ] = val self.dataChanged.emit(index, index) return True return False class HistoDataDialog(VeuszDialog): """Preferences dialog.""" def __init__(self, parent, document): """Setup dialog.""" VeuszDialog.__init__(self, parent, 'histodata.ui') self.document = document self.minval.default = self.maxval.default = ['Auto'] regexp = qt.QRegExp("^[-+]?[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)?|Auto$") validator = qt.QRegExpValidator(regexp, self) self.minval.setValidator(validator) self.maxval.setValidator(validator) self.buttonBox.button(qt.QDialogButtonBox.Apply).clicked.connect( self.applyClicked ) self.buttonBox.button(qt.QDialogButtonBox.Reset).clicked.connect( self.resetClicked ) self.bingenerate.clicked.connect(self.generateManualBins) self.binadd.clicked.connect(self.addManualBins) self.binremove.clicked.connect(self.removeManualBins) self.bindata = [] self.binmodel = ManualBinModel(self.bindata) self.binmanuals.setModel(self.binmodel) document.signalModified.connect(self.updateDatasetLists) self.updateDatasetLists() def escapeDatasets(self, dsnames): """Escape dataset names if they are not typical python ones.""" for i in crange(len(dsnames)): if not utils.validPythonIdentifier(dsnames[i]): dsnames[i] = '`%s`' % dsnames[i] def updateDatasetLists(self): """Update list of datasets.""" datasets = [] for name, ds in citems(self.document.data): if ds.datatype == 'numeric' and ds.dimensions == 1: datasets.append(name) datasets.sort() # make sure names are escaped if they have funny characters self.escapeDatasets(datasets) # help the user by listing existing datasets utils.populateCombo(self.indataset, datasets) def datasetExprChanged(self): """Validate expression.""" text = self.indataset.text() datasets.evalDatasetExpression(self.document, text) class Params(object): """Parameters to creation of histogram.""" def __init__(self, dialog): """Initialise parameters from dialog.""" numbins = dialog.numbins.value() if not checkValidator(dialog.minval): raise RuntimeError(_("Invalid minimum value")) minval = dialog.minval.text() if minval != 'Auto': minval = float(minval) if not checkValidator(dialog.maxval): raise RuntimeError(_("Invalid maximum value")) maxval = dialog.maxval.text() if maxval != 'Auto': maxval = float(maxval) islog = dialog.logarithmic.isChecked() self.binparams = (numbins, minval, maxval, islog) self.expr = dialog.indataset.currentText().strip() self.outdataset = dialog.outdataset.currentText().strip() self.outbins = dialog.outbins.currentText().strip() if self.expr == self.outdataset or self.expr == self.outbins: raise RuntimeError(_("Output datasets cannot be the same as input datasets")) self.method = dialog.methodGroup.getRadioChecked().objectName() self.manualbins = list( dialog.bindata ) self.manualbins.sort() if len(self.manualbins) == 0: self.manualbins = None self.errors = dialog.errorBars.isChecked() cuml = dialog.cumlGroup.getRadioChecked().objectName() self.cumulative = 'none' if cuml == 'cumlStoL': self.cumulative = 'smalltolarge' elif cuml == 'cumlLtoS': self.cumulative = 'largetosmall' def getGenerator(self, doc): """Return dataset generator.""" return datasets.DatasetHistoGenerator( doc, self.expr, binparams = self.binparams, binmanual = self.manualbins, method = self.method, cumulative = self.cumulative, errors = self.errors) def getOperation(self): """Get operation to make histogram.""" return document.OperationDatasetHistogram( self.expr, self.outbins, self.outdataset, binparams = self.binparams, binmanual = self.manualbins, method = self.method, cumulative = self.cumulative, errors = self.errors) def generateManualBins(self): """Generate manual bins.""" try: p = HistoDataDialog.Params(self) except RuntimeError as ex: qt.QMessageBox.warning(self, _("Invalid parameters"), cstr(ex)) return self.binmodel.beginRemoveRows(qt.QModelIndex(), 0, len(self.bindata)-1) del self.bindata[:] self.binmodel.endRemoveRows() if p.expr != '': p.manualbins = [] gen = p.getGenerator(self.document) locs = list(gen.binLocations()) self.binmodel.beginInsertRows(qt.QModelIndex(), 0, len(locs)-1) self.bindata += locs self.binmodel.endInsertRows() def addManualBins(self): """Add an extra bin to the manual list.""" self.binmodel.beginInsertRows(qt.QModelIndex(), 0, 0) self.bindata.insert(0, 0.) self.binmodel.endInsertRows() def removeManualBins(self): """Remove selected bins.""" indexes = self.binmanuals.selectionModel().selectedIndexes() if indexes: row = indexes[0].row() self.binmodel.beginRemoveRows(qt.QModelIndex(), row, row) del self.bindata[row] self.binmodel.endRemoveRows() def resetClicked(self): """Reset button clicked.""" for cntrl in (self.indataset, self.outdataset, self.outbins): cntrl.setEditText("") self.numbins.setValue(10) self.minval.setEditText("Auto") self.maxval.setEditText("Auto") self.logarithmic.setChecked(False) self.binmodel.beginRemoveRows(qt.QModelIndex(), 0, len(self.bindata)-1) del self.bindata[:] self.binmodel.endRemoveRows() self.errorBars.setChecked(False) self.counts.click() self.cumlOff.click() def reEditDataset(self, ds, dsname): """Re-edit dataset.""" gen = ds.generator self.indataset.setEditText(gen.inexpr) # need to map backwards to get dataset names revds = dict( (a,b) for b,a in citems(self.document.data) ) self.outdataset.setEditText(revds.get(gen.valuedataset, '')) self.outbins.setEditText(revds.get(gen.bindataset, '')) # if there are parameters if gen.binparams: p = gen.binparams self.numbins.setValue( p[0] ) self.minval.setEditText( cstr(p[1]) ) self.maxval.setEditText( cstr(p[2]) ) self.logarithmic.setChecked( bool(p[3]) ) else: self.numbins.setValue(10) self.minval.setEditText("Auto") self.maxval.setEditText("Auto") self.logarithmic.setChecked(False) # if there is a manual list of bins if gen.binmanual is not None: self.binmodel.beginResetModel() self.bindata[:] = list(gen.binmanual) self.binmodel.endResetModel() # select correct method {'counts': self.counts, 'density': self.density, 'fractions': self.fractions}[gen.method].click() # select if cumulative {'none': self.cumlOff, 'smalltolarge': self.cumlStoL, 'largetosmall': self.cumlLtoS}[gen.cumulative].click() # if error bars self.errorBars.setChecked( bool(gen.errors) ) def applyClicked(self): """Create histogram.""" qt.QTimer.singleShot(4000, self.statuslabel.clear) try: p = HistoDataDialog.Params(self) except RuntimeError as ex: self.statuslabel.setText(_("Invalid parameters: %s") % cstr(ex)) return exprresult = datasets.evalDatasetExpression(self.document, p.expr) if exprresult is None: self.statuslabel.setText(_("Invalid expression")) return op = p.getOperation() self.document.applyOperation(op) self.statuslabel.setText( _('Created datasets "%s" and "%s"') % (p.outbins, p.outdataset)) def recreateDataset(mainwindow, document, dataset, datasetname): """Open dialog to recreate histogram.""" dialog = HistoDataDialog(mainwindow, document) mainwindow.showDialog(dialog) dialog.reEditDataset(dataset, datasetname) veusz-3.0.1/veusz/dialogs/dataeditdialog.py0000664000175000017500000006441113173073033020426 0ustar jssjss00000000000000# data editting dialog # Copyright (C) 2005 Jeremy S. Sanders # Email: Jeremy Sanders # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ############################################################################## """Module for implementing dialog box for viewing/editing data.""" from __future__ import division import numpy as N from ..compat import cstr from .. import qtall as qt4 from .. import document from .. import datasets from .. import setting from ..qtwidgets.datasetbrowser import DatasetBrowser from .veuszdialog import VeuszDialog, recreate_register def _(text, disambiguation=None, context="DataEditDialog"): """Translate text.""" return qt4.QCoreApplication.translate(context, text, disambiguation) class DatasetTableModel1D(qt4.QAbstractTableModel): """Provides access to editing and viewing of datasets.""" def __init__(self, parent, document, datasetname): qt4.QAbstractTableModel.__init__(self, parent) self.document = document self.dsname = datasetname document.signalModified.connect(self.slotDocumentModified) def rowCount(self, parent): """Return number of rows.""" if parent.isValid(): # docs say we should return zero return 0 try: return len(self.document.data[self.dsname].data)+1 except (KeyError, AttributeError): return 0 def slotDocumentModified(self): """Called when document modified.""" self.layoutChanged.emit() def columnCount(self, parent): """Return number of columns.""" if parent.isValid(): return 0 try: ds = self.document.data[self.dsname] except KeyError: return 0 return len( ds.column_descriptions ) def data(self, index, role): """Return data for index.""" # get dataset ds = self.document.data[self.dsname] if ds is not None: # select correct part of dataset data = getattr(ds, ds.columns[index.column()]) if ds is not None and data is not None and role in ( qt4.Qt.DisplayRole, qt4.Qt.EditRole): # blank row at end of data if index.row() == len(data): return None # convert data to data d = data[index.row()] return ds.uiDataItemToData(d) # empty entry return None def headerData(self, section, orientation, role): """Return row numbers or column names.""" try: ds = self.document.data[self.dsname] except KeyError: return None if role == qt4.Qt.DisplayRole: if orientation == qt4.Qt.Horizontal: # column names return ds.column_descriptions[section] else: if section == len(ds.data): return "+" # return row numbers return section+1 return None def flags(self, index): """Update flags to say that items are editable.""" if index.isValid(): f = qt4.QAbstractTableModel.flags(self, index) ds = self.document.data.get(self.dsname) if ds is not None and ds.editable: f |= qt4.Qt.ItemIsEditable return f return qt4.Qt.ItemIsEnabled def removeRows(self, row, count): """Remove rows.""" self.document.applyOperation( document.OperationDatasetDeleteRow(self.dsname, row, count)) def insertRows(self, row, count): """Remove rows.""" self.document.applyOperation( document.OperationDatasetInsertRow(self.dsname, row, count)) def setData(self, index, value, role): """Called to set the data.""" if not index.isValid() or role != qt4.Qt.EditRole: return False row = index.row() column = index.column() ds = self.document.data[self.dsname] data = getattr(ds, ds.columns[index.column()]) # add new column if necessary ops = document.OperationMultiple([], descr=_('set value')) if data is None: ops.addOperation( document.OperationDatasetAddColumn(self.dsname, ds.columns[column])) # add a row if necessary if row == len(ds.data): ops.addOperation( document.OperationDatasetInsertRow(self.dsname, row, 1)) # update if conversion okay try: val = ds.uiConvertToDataItem(value) except ValueError: return False ops.addOperation( document.OperationDatasetSetVal(self.dsname, ds.columns[column], row, val)) try: self.document.applyOperation(ops) except RuntimeError: return False return True class DatasetTableModelMulti(qt4.QAbstractTableModel): """Edit multiple datasets simultaneously with a spreadsheet-like style.""" def __init__(self, parent, document, datasetnames): qt4.QAbstractTableModel.__init__(self, parent) self.document = document self.dsnames = datasetnames document.signalModified.connect(self.slotDocumentModified) self.changeset = -1 self.rows = 0 def updateCounts(self): """Count rows and columns.""" self.changeset = self.document.changeset rows = 0 rowcounts = self.rowcounts = [] colcounts = self.colcounts = [] colattrs = self.colattrs = [] for dsidx, name in enumerate(self.dsnames): if name not in self.document.data: continue dataset = self.document.data[name] if (not hasattr(dataset, 'data') or not hasattr(dataset, 'columns') or dataset.dimensions != 1): continue r = len(dataset.data)+1 rowcounts.append(r) rows = max(rows, r) attr = [] for colidx, col in enumerate(dataset.columns): data = getattr(dataset, col) if data is not None: attr.append( (name, col, dsidx, colidx) ) colcounts.append( len(attr) ) colattrs += attr self.rows = rows def rowCount(self, parent): if parent.isValid(): return 0 if self.changeset != self.document.changeset: self.updateCounts() return self.rows def columnCount(self, parent): if parent.isValid(): return 0 if self.changeset != self.document.changeset: self.updateCounts() return len(self.colattrs) def slotDocumentModified(self): self.updateCounts() self.layoutChanged.emit() def data(self, index, role): """Return data for index.""" dsname, colname, dsidx, colidx = self.colattrs[index.column()] ds = self.document.data[dsname] data = getattr(ds, colname) if role == qt4.Qt.DisplayRole: if index.row() < self.rowcounts[dsidx]-1: # convert data to Data d = data[index.row()] return ds.uiDataItemToData(d) # empty entry return None def headerData(self, section, orientation, role): """Return row numbers or column names.""" if role == qt4.Qt.DisplayRole: if orientation == qt4.Qt.Horizontal: # column names dsname, colname, dsidx, colidx = self.colattrs[section] ds = self.document.data[dsname] descr = ds.column_descriptions[colidx] header = dsname + '\n' + descr return header else: # return row numbers if section == self.rows-1: return "+" return section+1 return None def flags(self, index): """Update flags to say that items are editable.""" if index.isValid(): f = qt4.QAbstractTableModel.flags(self, index) dsname, colname, dsidx, colidx = self.colattrs[index.column()] ds = self.document.data.get(dsname) if ds is not None and ds.editable: f |= qt4.Qt.ItemIsEditable return f return qt4.Qt.ItemIsEnabled def setData(self, index, value, role): """Validate and set data in dataset.""" if not index.isValid() or role != qt4.Qt.EditRole: return False row = index.row() column = index.column() dsname, colname, dsidx, colidx = self.colattrs[column] ds = self.document.data[dsname] ops = document.OperationMultiple([], descr=_('set value')) if row >= self.rowcounts[dsidx]-1: # add number of rows required to add new value below ops.addOperation( document.OperationDatasetInsertRow( dsname, self.rowcounts[dsidx]-1, row+1-self.rowcounts[dsidx]+1)) # convert text to value try: val = ds.uiConvertToDataItem(value) except ValueError: return False ops.addOperation( document.OperationDatasetSetVal(dsname, colname, row, val)) try: self.document.applyOperation(ops) return True except RuntimeError: return False def insertRows(self, row, count): ops = [] for i, name in enumerate(self.dsnames): if self.rowcounts[i]-1 >= row: ops.append( document.OperationDatasetInsertRow(name, row, count)) self.document.applyOperation( document.OperationMultiple(ops, _('insert row(s)'))) def removeRows(self, row, count): ops = [] for i, name in enumerate(self.dsnames): if self.rowcounts[i]-1 >= row: ops.append( document.OperationDatasetDeleteRow(name, row, count)) self.document.applyOperation( document.OperationMultiple(ops, _('delete row(s)'))) class DatasetTableModel2D(qt4.QAbstractTableModel): """A 2D dataset model.""" def __init__(self, parent, document, datasetname): qt4.QAbstractTableModel.__init__(self, parent) self.document = document self.dsname = datasetname self.updatePixelCoords() document.signalModified.connect(self.slotDocumentModified) def updatePixelCoords(self): """Get coordinates at edge of grid.""" self.xedge = self.yedge = self.xcent = self.ycent = [] ds = self.document.data.get(self.dsname) if ds and ds.dimensions==2: self.xcent, self.ycent = ds.getPixelCentres() self.xedge, self.yedge = ds.getPixelEdges() def rowCount(self, parent): if parent.isValid(): return 0 try: data = self.document.data[self.dsname].data except KeyError: return 0 if data is not None and data.ndim==2: return data.shape[0] else: return 0 def columnCount(self, parent): if parent.isValid(): return 0 try: data = self.document.data[self.dsname].data except KeyError: return 0 if data is not None and data.ndim==2: return data.shape[1] else: return 0 def data(self, index, role): if role == qt4.Qt.DisplayRole: # get data (note y is reversed, sigh) try: data = self.document.data[self.dsname].data except KeyError: return None if data is not None and data.ndim==2: try: num = data[data.shape[0]-index.row()-1, index.column()] return float(num) except IndexError: pass return None def headerData(self, section, orientation, role): """Return headers at top.""" ds = self.document.data.get(self.dsname) if ds.dimensions != 2: return None xaxis = orientation == qt4.Qt.Horizontal # note: y coordinates are upside down (high y is at top) if ds is not None and role == qt4.Qt.DisplayRole: v = self.xcent[section] if xaxis else self.ycent[ len(self.ycent)-section-1] return '%i (%s)' % ( len(self.ycent)-section, setting.ui_floattostring(v, maxdp=4)) elif ds is not None and role == qt4.Qt.ToolTipRole: v1 = self.xedge[section] if xaxis else self.yedge[ len(self.yedge)-section-2] v2 = self.xedge[section+1] if xaxis else self.yedge[ len(self.yedge)-section-1] return u'%s\u2013%s' % (setting.ui_floattostring(v1), setting.ui_floattostring(v2)) return None def flags(self, index): """Update flags to say that items are editable.""" if not index.isValid(): return qt4.Qt.ItemIsEnabled else: f = qt4.QAbstractTableModel.flags(self, index) ds = self.document.data.get(self.dsname) if ds is not None and ds.editable: f |= qt4.Qt.ItemIsEditable return f def slotDocumentModified(self): """Called when document modified.""" self.updatePixelCoords() self.layoutChanged.emit() def setData(self, index, value, role): """Called to set the data.""" if not index.isValid() or role != qt4.Qt.EditRole: return False ds = self.document.data[self.dsname] row = ds.data.shape[0]-index.row()-1 col = index.column() # update if conversion okay try: val = ds.uiConvertToDataItem(value) except ValueError: return False op = document.OperationDatasetSetVal2D( self.dsname, row, col, val) self.document.applyOperation(op) return True class DatasetTableModelND(qt4.QAbstractTableModel): """An ND dataset model.""" def __init__(self, parent, document, datasetname): qt4.QAbstractTableModel.__init__(self, parent) self.document = document self.dsname = datasetname document.signalModified.connect(self.slotDocumentModified) def rowCount(self, parent): if parent.isValid(): return 0 try: data = self.document.data[self.dsname].data except KeyError: return 0 return 0 if data is None else data.size def columnCount(self, parent): if parent.isValid(): return 0 try: data = self.document.data[self.dsname].data except KeyError: return 0 return 1 if data is not None else 0 def data(self, index, role): """Items in array.""" if role == qt4.Qt.DisplayRole: try: data = self.document.data[self.dsname].data except KeyError: return None if data is not None: try: num = N.ravel(data)[index.row()] return float(num) except IndexError: pass return None def headerData(self, section, orientation, role): """Return headers at top.""" ds = self.document.data.get(self.dsname) if ds is None: return None if ds is not None and role == qt4.Qt.DisplayRole: if orientation == qt4.Qt.Horizontal: return _('Value') else: idx = N.unravel_index(section, ds.data.shape) txt = ','.join( [str(v+1) for v in idx] ) return txt return None def slotDocumentModified(self): """Called when document modified.""" self.layoutChanged.emit() class ViewDelegate(qt4.QStyledItemDelegate): """Delegate for fixing double editing. Normal editing uses double spin box, which is inappropriate """ def createEditor(self, parent, option, index): if type(index.data()) is float: return qt4.QLineEdit(parent) else: return qt4.QStyledItemDelegate.createEditor( self, parent, option, index) def setEditorData(self, editor, index): """Override setData to use correct formatting.""" if type(index.data()) is float: txt = setting.ui_floattostring(index.data()) editor.setText(txt) else: qt4.QStyledItemDelegate.setEditorData(self, editor, index) class DataEditDialog(VeuszDialog): """Dialog for editing and rearranging data sets.""" def __init__(self, parent, document): VeuszDialog.__init__(self, parent, 'dataedit.ui') self.document = document # set up dataset list self.dsbrowser = DatasetBrowser(document, parent, parent) self.dsbrowser.setToolTip( _('Select multiple datasets to edit simultaneously')) self.splitter.insertWidget(0, self.dsbrowser) self.deligate = ViewDelegate() self.datatableview.setItemDelegate(self.deligate) # actions for data table for text, slot in ( (_('Copy'), self.slotCopy), (_('Delete row'), self.slotDeleteRow), (_('Insert row'), self.slotInsertRow), ): act = qt4.QAction(text, self) act.triggered.connect(slot) self.datatableview.addAction(act) self.datatableview.setContextMenuPolicy( qt4.Qt.ActionsContextMenu ) # layout edit dialog improvement self.splitter.setStretchFactor(0, 3) self.splitter.setStretchFactor(1, 4) # don't want text to look editable or special self.linkedlabel.setFrameShape(qt4.QFrame.NoFrame) self.linkedlabel.viewport().setBackgroundRole(qt4.QPalette.Window) # document changes document.signalModified.connect(self.slotDocumentModified) # select first item, if any or initialise if none if len(self.document.data) > 0: self.selectDataset( sorted(self.document.data)[0] ) else: self.slotDatasetsSelected([]) self.dsbrowser.navtree.selecteddatasets.connect( self.slotDatasetsSelected) # connect buttons for btn, slot in ( (self.deletebutton, self.slotDatasetDelete), (self.unlinkbutton, self.slotDatasetUnlink), (self.duplicatebutton, self.slotDatasetDuplicate), (self.importbutton, self.slotDatasetImport), (self.createbutton, self.slotDatasetCreate), (self.editbutton, self.slotDatasetEdit), ): btn.clicked.connect(slot) # menu for new button self.newmenu = qt4.QMenu() for text, slot in ( (_('Numerical dataset'), self.slotNewNumericalDataset), (_('Text dataset'), self.slotNewTextDataset), (_('Date/time dataset'), self.slotNewDateDataset) ): a = self.newmenu.addAction(text) a.triggered.connect(slot) self.newbutton.setMenu(self.newmenu) def slotDatasetsSelected(self, names): """Called when a new dataset is selected.""" # FIXME: Make readonly models readonly!! model = None if len(names) == 1: # get selected dataset ds = self.document.data[names[0]] # make model for dataset if ds.dimensions == 1: model = DatasetTableModel1D(self, self.document, names[0]) elif ds.dimensions == 2: model = DatasetTableModel2D(self, self.document, names[0]) elif ds.dimensions == -1: model = DatasetTableModelND(self, self.document, names[0]) elif len(names) > 1: model = DatasetTableModelMulti(self, self.document, names) # disable context menu if no menu for a in self.datatableview.actions(): a.setEnabled(model is not None) self.datatableview.setModel(model) self.setUnlinkState() def setUnlinkState(self): """Enable the unlink button correctly.""" linkinfo = [] canunlink = [] canedit = [] names = self.dsbrowser.navtree.getSelectedDatasets() for name in names: ds = self.document.data[name] canunlink.append(ds.canUnlink()) if len(names) > 1: linkinfo.append(name) linkinfo.append(ds.linkedInformation()) canedit.append(type(ds) in recreate_register) self.editbutton.setVisible(any(canedit)) self.unlinkbutton.setEnabled(any(canunlink)) self.linkedlabel.setText('\n'.join(linkinfo)) self.deletebutton.setEnabled(bool(names)) self.duplicatebutton.setEnabled(bool(names)) def slotDocumentModified(self): """Set unlink status when document modified.""" self.setUnlinkState() def selectDataset(self, dsname): """Select dataset with name given.""" self.dsbrowser.navtree.selectDataset(dsname) self.slotDatasetsSelected([dsname]) def slotDatasetDelete(self): """Delete selected dataset.""" dsnames = self.dsbrowser.navtree.getSelectedDatasets() self.document.applyOperation( document.OperationMultiple( [document.OperationDatasetDelete(n) for n in dsnames], descr=_('delete dataset(s)'))) def slotDatasetUnlink(self): """Allow user to remove link to file or other datasets.""" ops = [] for name in self.dsbrowser.navtree.getSelectedDatasets(): d = self.document.data[name] if d.linked is not None: ops.append(document.OperationDatasetUnlinkFile(name)) elif d.canUnlink(): ops.append(document.OperationDatasetUnlinkRelation(name)) if ops: self.document.applyOperation( document.OperationMultiple(ops, _('unlink dataset(s)'))) def slotDatasetDuplicate(self): """Duplicate selected datasets.""" ops = [] for name in self.dsbrowser.navtree.getSelectedDatasets(): # generate new name for dataset newname = name + '_copy' index = 2 while newname in self.document.data: newname = '%s_copy_%i' % (name, index) index += 1 ops.append( document.OperationDatasetDuplicate(name, newname)) if ops: self.document.applyOperation( document.OperationMultiple(ops, _('duplicate dataset(s)'))) def slotDatasetImport(self): """Show import dialog.""" self.mainwindow.slotDataImport() def slotDatasetCreate(self): """Show dataset creation dialog.""" self.mainwindow.slotDataCreate() def slotDatasetEdit(self): """Reload dataset into dataset creation dialog.""" for name in self.dsbrowser.navtree.getSelectedDatasets(): dataset = self.document.data[name] try: recreate_register[type(dataset)](self.mainwindow, self.document, dataset, name) except KeyError: pass def slotCopy(self): """Copy text from selection.""" # get list of selected rows and columns selmodel = self.datatableview.selectionModel() model = self.datatableview.model() indices = [] for index in selmodel.selectedIndexes(): indices.append( (index.row(), index.column()) ) indices.sort() # build up text stream for copying to clipboard lines = [] rowitems = [] lastrow = -1 for row, column in indices: if row != lastrow: if rowitems: # items are tab separated lines.append( '\t'.join(rowitems) ) rowitems = [] lastrow = row rowitems.append( cstr(model.createIndex(row, column).data()) ) if rowitems: lines.append( '\t'.join(rowitems) ) lines.append('') # blank line at end lines = '\n'.join(lines) # put text on clipboard qt4.QApplication.clipboard().setText(lines) def slotDeleteRow(self): """Delete the current row.""" self.datatableview.model().removeRows( self.datatableview.currentIndex().row(), 1) def slotInsertRow(self): """Insert a new row.""" self.datatableview.model().insertRows( self.datatableview.currentIndex().row(), 1) def slotNewNumericalDataset(self): """Add new value dataset.""" self.newDataset( datasets.Dataset(data=[0.]) ) def slotNewTextDataset(self): """Add new text dataset.""" self.newDataset( datasets.DatasetText(data=['']) ) def slotNewDateDataset(self): """Add new date dataset.""" self.newDataset( datasets.DatasetDateTime(data=[]) ) def newDataset(self, ds): """Add new dataset to document.""" # get a name for dataset name = _('new dataset') if name in self.document.data: count = 1 while name in self.document.data: name = _('new dataset %i') % count count += 1 # add new dataset self.document.applyOperation( document.OperationDatasetSet(name, ds)) self.dsbrowser.selectDataset(name) veusz-3.0.1/veusz/dialogs/export.py0000664000175000017500000004373513324144015017013 0ustar jssjss00000000000000# Copyright (C) 2014 Jeremy S. Sanders # Email: Jeremy Sanders # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ############################################################################## from __future__ import division, print_function import os import os.path from .. import qtall as qt4 from .. import setting from .. import utils from .. import document from ..compat import citems, cstrerror, cstr, cgetcwd from .veuszdialog import VeuszDialog def _(text, disambiguation=None, context='ExportDialog'): """Translate text.""" return qt4.QCoreApplication.translate(context, text, disambiguation) # formats which can have multiple pages multipageformats = set(('ps', 'pdf')) bitmapformats = set(('png', 'bmp', 'jpg', 'tiff', 'xpm')) # map formats to names of radio buttons formatradio = ( ('pdf', 'radioFormatPDF'), ('eps', 'radioFormatEPS'), ('ps', 'radioFormatPS' ), ('svg', 'radioFormatSVG'), ('emf', 'radioFormatEMF'), ('png', 'radioFormatPNG'), ('bmp', 'radioFormatBMP'), ('jpg', 'radioFormatJPG'), ('tiff', 'radioFormatTIFF'), ('xpm', 'radioFormatXPM'), ) class ExportDialog(VeuszDialog): """Export dialog.""" def __init__(self, mainwindow, doc, docfilename): """Setup dialog.""" VeuszDialog.__init__(self, mainwindow, 'export.ui') self.document = doc doc.signalModified.connect(self.updatePagePages) self.updatePagePages() # change 'Save' button to 'Export' self.buttonBox.button(qt4.QDialogButtonBox.Save).setText(_('Export')) # these are mappings between filetypes and radio buttons self.fmtradios = dict([(f, getattr(self, r)) for f, r in formatradio]) self.radiofmts = dict([(getattr(self, r), f) for f, r in formatradio]) # get allowed types (some formats are disabled if no helper) docfmts = set() for types, descr in document.Export.getFormats(): docfmts.update(types) # disable type if not allowed for fmt, radio in citems(self.fmtradios): if fmt not in docfmts: radio.setEnabled(False) # connect format radio buttons def fmtclicked(f): return lambda: self.formatClicked(f) for r, f in citems(self.radiofmts): r.clicked.connect(fmtclicked(f)) # connect page radio buttons self.radioPageSingle.clicked.connect(lambda: self.pageClicked('single')) self.radioPageAll.clicked.connect(lambda: self.pageClicked('all')) self.radioPagePages.clicked.connect(lambda: self.pageClicked('pages')) # other controls self.checkMultiPage.clicked.connect(self.updateSingleMulti) self.buttonBrowse.clicked.connect(self.browseClicked) setdb = setting.settingdb eloc = setdb['dirname_export_location'] # where to export file if eloc == 'doc': self.dirname = os.path.dirname(os.path.abspath(docfilename)) elif eloc == 'cwd': self.dirname = cgetcwd() else: # 'prev' self.dirname = setdb.get('dirname_export', qt4.QDir.homePath()) # set default filename ext = setdb.get('export_format', 'pdf') if not docfilename: docfilename = 'export' self.docname = os.path.splitext(os.path.basename(docfilename))[0] self.formatselected = ext self.pageselected = setdb.get('export_page', 'single') self.checkMultiPage.setChecked(setdb.get('export_multipage', True)) self.updateSingleMulti() self.checkOverwrite.setChecked(setdb.get('export_overwrite', False)) self.exportSVGTextAsText.setChecked(setdb['export_SVG_text_as_text']) self.exportAntialias.setChecked(setdb['export_antialias']) self.exportQuality.setValue(setdb['export_quality']) # validate and set DPIs dpis = ('72', '75', '90', '96', '100', '150', '200', '300') for cntrl in self.exportDPI, self.exportDPISVG, self.exportDPIPDF: cntrl.addItems(dpis) cntrl.setValidator(qt4.QIntValidator(10, 10000, self)) self.exportDPI.setEditText(str(setdb['export_DPI'])) self.exportDPISVG.setEditText(str(setdb['export_DPI_SVG'])) self.exportDPIPDF.setEditText(str(setdb['export_DPI_PDF'])) # button to change bitmap background self.exportBackgroundButton.clicked.connect( self.slotExportBackgroundClicked) self.updateExportBackground(setdb['export_background']) # set correct format self.fmtradios[ext].click() # regexp for comma separated ranges valre = qt4.QRegExp( r'^[0-9]+(\s*-\s*[0-9]+)?(\s*,\s*[0-9]+(\s*-\s*[0-9]+)?)*$') valid = qt4.QRegExpValidator(valre, self) self.editPagePages.setValidator(valid) # set page mode { 'range': self.radioPageSingle, # compatibility 'single': self.radioPageSingle, 'all': self.radioPageAll, 'pages': self.radioPagePages, }[self.pageselected].click() # label showing success/failure self.labelStatus.clear() # fix height as widgets are hidden width = self.size().width() self.adjustSize() self.resize(width, self.size().height()) def formatClicked(self, fmt): """If the format is changed.""" setting.settingdb['export_format'] = fmt self.formatselected = fmt self.checkMultiPage.setEnabled(fmt in multipageformats) for c in (self.exportAntialias, self.exportDPI, self.labelDPI, self.exportBackgroundButton, self.labelBackgroundButton): c.setVisible(fmt in bitmapformats) for c in (self.exportDPIPDF, self.labelDPIPDF, self.exportColor, self.labelColor): c.setVisible(fmt in ('pdf', 'ps', 'eps')) for c in (self.exportQuality, self.labelQuality): c.setVisible(fmt == 'jpg') for c in (self.exportSVGTextAsText, self.labelSVGTextAsText, self.exportDPISVG, self.labelDPISVG): c.setVisible(fmt == 'svg') self.updateSingleMulti() filename = os.path.splitext(self.editFileName.text())[0] + '.' + fmt self.editFileName.setText(filename) def pageClicked(self, page): """If page type is set.""" setting.settingdb['export_page'] = page self.pageselected = page self.updateSingleMulti() self.editPagePages.setEnabled(page=='pages') def browseClicked(self): """Browse for file.""" setdb = setting.settingdb # File types we can export to in the form ([extensions], Name) fd = qt4.QFileDialog(self, _('Export page')) filename = self.editFileName.text() dirname = os.path.dirname(self.editFileName.text()) fd.setDirectory(dirname if dirname else self.dirname) fd.setFileMode(qt4.QFileDialog.AnyFile) fd.setAcceptMode(qt4.QFileDialog.AcceptSave) fd.setOptions(qt4.QFileDialog.DontConfirmOverwrite) # Create a mapping between a format string and extensions filtertoext = {} # convert extensions to filter exttofilter = {} filters = [] # a list of extensions which are allowed validextns = [] formats = document.Export.getFormats() for extns, name in formats: extensions = " ".join(["*." + item for item in extns]) # join eveything together to make a filter string filterstr = '%s (%s)' % (name, extensions) filtertoext[filterstr] = extns for e in extns: exttofilter[e] = filterstr filters.append(filterstr) validextns += extns fd.setNameFilters(filters) fd.selectNameFilter(exttofilter[setdb['export_format']]) filename = self.editFileName.text() dirname = os.path.dirname(os.path.abspath(filename)) if os.path.isdir(dirname): fd.selectFile(filename) if fd.exec_() == qt4.QDialog.Accepted: # convert filter to extension filterused = str(fd.selectedNameFilter()) chosenext = filtertoext[filterused][0] filename = fd.selectedFiles()[0] fileext = os.path.splitext(filename)[1][1:] if fileext not in validextns or fileext != chosenext: filename += "." + chosenext self.editFileName.setText(filename) self.fmtradios[chosenext].click() def isMultiFile(self): """Is output going to be multiple pages?""" multifile = self.pageselected != 'single' if (self.formatselected in multipageformats and self.checkMultiPage.isChecked()): multifile = False return multifile def updateSingleMulti(self, _oldmulti=[None]): """Change filename according to selected single or multi button.""" setting.settingdb['export_multipage'] = self.checkMultiPage.isChecked() multifile = self.isMultiFile() if multifile: templ = setting.settingdb['export_template_multi'] else: templ = setting.settingdb['export_template_single'] newfilename = os.path.join( self.dirname, templ.replace('%DOCNAME%', self.docname) + '.' + self.formatselected) # only change if multi format status has changed or is # uninitialised if multifile is not getattr(self, '_oldsinglemulti', None): self.editFileName.setText(newfilename) self._oldsinglemulti = multifile def updatePagePages(self): """Update widgets allowing user to set ranges of pages.""" npages = self.document.getNumberPages() if npages == 0: return text = '%i-%i' % (1, npages) self.editPagePages.setText(text) @qt4.pyqtSlot() def clearLabel(self): """Clear label. Defined as a slot to work around PyQt C++ object deleted bug. """ self.labelStatus.clear() def showMessage(self, text): """Show a message in a label, clearing after a time.""" self.labelStatus.setText(text) qt4.QTimer.singleShot(3000, self.clearLabel) def updateExportBackground(self, colorname): """Update color on export background.""" pixmap = qt4.QPixmap(16, 16) col = self.document.evaluate.colors.get(colorname) pixmap.fill(col) # update button (storing color in button itself - what fun!) self.exportBackgroundButton.setIcon(qt4.QIcon(pixmap)) self.exportBackgroundButton.iconcolor = colorname def slotExportBackgroundClicked(self): """Button clicked to change background.""" qcolor = self.document.evaluate.colors.get( self.exportBackgroundButton.iconcolor) color = qt4.QColorDialog.getColor( qcolor, self, "Choose color", qt4.QColorDialog.ShowAlphaChannel ) if color.isValid(): self.updateExportBackground(utils.extendedColorFromQColor(color)) def getPagePages(self): """Get list of entered pages.""" txt = self.editPagePages.text() parts = txt.split(',') pages = [] for p in parts: p = p.replace(' ', '') try: if p.find('-')>=0: rng = p.split('-') pages += list(range(int(rng[0])-1, int(rng[1]))) else: pages.append(int(p)-1) except ValueError: # convertsion error raise RuntimeError(_('Error: invalid list of pages')) # check in range for pg in pages: if pg<0 or pg>=self.document.getNumberPages(): raise RuntimeError(_('Error: pages out of range')) return pages def accept(self): """Do the export""" if self.document.getNumberPages() == 0: self.showMessage(_('Error: no pages in document')) return filename = self.editFileName.text() if (self.isMultiFile() and '%PAGENAME%' not in filename and '%PAGE%' not in filename and '%PAGE00%' not in filename and '%PAGE000%' not in filename): self.showMessage( _('Error: page name or number must be in filename')) return if self.pageselected == 'single': pages = [self.mainwindow.plot.getPageNumber()] elif self.pageselected == 'all': pages = list(range(self.document.getNumberPages())) elif self.pageselected == 'pages': try: pages = self.getPagePages() except RuntimeError as e: self.showMessage(str(e)) return setdb = setting.settingdb # update settings from controls setdb['export_overwrite'] = self.checkOverwrite.isChecked() setdb['export_antialias'] = self.exportAntialias.isChecked() setdb['export_quality'] = self.exportQuality.value() setdb['export_color'] = self.exportColor.currentIndex() == 0 setdb['export_background'] = self.exportBackgroundButton.iconcolor setdb['export_SVG_text_as_text'] = self.exportSVGTextAsText.isChecked() # update dpi if possible # FIXME: requires some sort of visual notification of validator for cntrl, setn in ( (self.exportDPI, 'export_DPI'), (self.exportDPIPDF, 'export_DPI_PDF'), (self.exportDPISVG, 'export_DPI_SVG')): try: text = cntrl.currentText() valid = cntrl.validator().validate(text, 0)[0] if valid == qt4.QValidator.Acceptable: setdb[setn] = int(text) except ValueError: pass export = document.Export( self.document, '', # filename [0], # page numbers bitmapdpi=setdb['export_DPI'], pdfdpi=setdb['export_DPI_PDF'], antialias=setdb['export_antialias'], color=setdb['export_color'], quality=setdb['export_quality'], backcolor=setdb['export_background'], svgtextastext=setdb['export_SVG_text_as_text'], svgdpi=setdb['export_DPI_SVG'], ) def _overwriteQuestion(filename): """Ask user whether file can be overwritten.""" retn = qt4.QMessageBox.question( self, _("Overwrite file?"), _("The file %s already exists") % os.path.basename(filename), qt4.QMessageBox.Save | qt4.QMessageBox.Cancel, qt4.QMessageBox.Cancel) return retn == qt4.QMessageBox.Save # count exported pages (in list so can be modified in function) pagecount = [0] def _checkAndExport(): """Check whether file exists and export if ok.""" if os.path.exists(export.filename): if not setdb['export_overwrite']: if not _overwriteQuestion(export.filename): return # show busy cursor qt4.QApplication.setOverrideCursor(qt4.QCursor(qt4.Qt.WaitCursor)) # delete file if already exists try: os.unlink(export.filename) except EnvironmentError: pass try: # actually do the export export.export() pagecount[0] += len(export.pagenumbers) except (RuntimeError, EnvironmentError) as e: # errors from the export if isinstance(e, EnvironmentError): msg = cstrerror(e) else: msg = cstr(e) qt4.QApplication.restoreOverrideCursor() qt4.QMessageBox.critical( self, _("Error - Veusz"), _("Error exporting to file '%s'\n\n%s") % (export.filename, msg)) else: qt4.QApplication.restoreOverrideCursor() if self.isMultiFile() or len(pages)==1: # write pages to multiple files for page in pages: pagename = self.document.getPage(page).name export.pagenumbers = [page] pg = page+1 fname = filename.replace('%PAGE%', str(pg)) fname = fname.replace('%PAGE00%', '%02i' % pg) fname = fname.replace('%PAGE000%', '%03i' % pg) fname = fname.replace('%PAGENAME%', pagename) export.filename = fname _checkAndExport() else: # write page/pages to single file fname = filename.replace('%PAGE%', _('none')) fname = fname.replace('%PAGE00%', _('none')) fname = fname.replace('%PAGE000%', _('none')) fname = fname.replace('%PAGENAME%', _('none')) export.pagenumbers = pages export.filename = fname _checkAndExport() dirname = os.path.dirname(filename) if dirname: setting.settingdb['dirname_export'] = dirname # format feedback ext = os.path.splitext(export.filename)[1] if ext: utils.feedback.exportcts[ext] += 1 if pagecount[0] > 0: self.showMessage(_('Exported %i page(s)') % pagecount[0]) veusz-3.0.1/veusz/dialogs/custom.py0000664000175000017500000002624113161413406016777 0ustar jssjss00000000000000# Copyright (C) 2009 Jeremy S. Sanders # Email: Jeremy Sanders # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ############################################################################## from __future__ import division import ast import copy from ..compat import cstrerror from .. import qtall as qt4 from .. import document from .veuszdialog import VeuszDialog def _(text, disambiguation=None, context="CustomDialog"): """Translate text.""" return qt4.QCoreApplication.translate(context, text, disambiguation) class CustomItemModel(qt4.QAbstractTableModel): """A model for editing custom items.""" # headers for type of widget headers = { 'definition': (_('Name'), _('Definition')), 'import': (_('Module'), _('Symbol list')), 'color': (_('Name'), _('Definition')), 'colormap': (_('Name'), _('Definition')), } # tooltips for columns tooltips = { 'definition': ( _('Name for constant, or function name and arguments, e.g. "f(x,y)"'), _('Python expression defining constant or function, e.g. "x+y"')), 'import': ( _('Module to import symbols from, e.g. "scipy.special"'), _('Comma-separated list of symbols to import or "*" to import everything')), 'color': ( _('Name of color'), _('Definition of color ("#RRGGBB", "#RRGGBBAA" or "red")')), 'colormap': ( _('Name of colormap'), _('Definition of colormap, defined as lists of RGB tuples, e.g. "((0,0,0),(255,255,255))"')), } def __init__(self, parent, doc, ctype): """ ctype is 'definition', 'import', 'color' or 'colormap' """ qt4.QAbstractTableModel.__init__(self, parent) self.doc = doc self.ctype = ctype self.attr = document.OperationSetCustom.type_to_attr[ctype] # connect notification of document change doc.signalModified.connect(self.doUpdate) # do not inform qt model has changed on document change self.moddocupignore = False def _getCustoms(self): return getattr(self.doc.evaluate, self.attr) def _getCustomsCopy(self): return copy.deepcopy(self._getCustoms()) def rowCount(self, parent): return 0 if parent.isValid() else len(self._getCustoms())+1 def columnCount(self, parent): return 0 if parent.isValid() else 2 def data(self, index, role): """Lookup data in document.evaluate.customs list.""" if role in (qt4.Qt.DisplayRole, qt4.Qt.EditRole): try: defn = self._getCustoms()[index.row()] except IndexError: # empty row beyond end return '' col = index.column() if self.ctype=='colormap' and col==1: return repr(defn[col]) else: return defn[col] elif role == qt4.Qt.ToolTipRole: # tooltip on row for new entries on last row if index.row() == len(self._getCustoms()): return self.tooltips[self.ctype][index.column()] return None def flags(self, index): """Items are editable""" return ( qt4.Qt.ItemIsSelectable | qt4.Qt.ItemIsEditable | qt4.Qt.ItemIsEnabled ) def headerData(self, section, orientation, role): """Return the headers at the top of the view.""" if role == qt4.Qt.DisplayRole: if orientation == qt4.Qt.Horizontal: # columns defined in headers return self.headers[self.ctype][section] else: # number rows return str(section+1) return None def doUpdate(self): """Document changed.""" if not self.moddocupignore: self.layoutChanged.emit() def validateName(self, val): if self.ctype == 'import': return document.module_re.match(val) elif self.ctype == 'definition': return ( document.identifier_re.match(val) or document.function_re.match(val)) else: # color or colormap return val.strip() != '' def validateDefn(self, value): if self.ctype == 'colormap': try: tmp = ast.literal_eval(value) except (ValueError, SyntaxError): return False return value.strip() != '' def setData(self, index, value, role): """Edit an item.""" if index.isValid() and role == qt4.Qt.EditRole: col = index.column() row = index.row() if col == 0: ok = self.validateName(value) elif col == 1: ok = self.validateDefn(value) if not ok: return False # extend if necessary newcustom = self._getCustomsCopy() while len(newcustom) < row+1: if self.ctype == 'colormap': newcustom.append(['', ((0,0,0),(255,255,255))]) else: newcustom.append(['', '']) if self.ctype=='colormap' and col==1: newcustom[row][col] = eval(value) else: newcustom[row][col] = value self.doc.applyOperation( document.OperationSetCustom(self.ctype, newcustom) ) self.dataChanged.emit(index, index) return True return False def deleteEntry(self, num): """Delete row num. True if success.""" newcustoms = self._getCustomsCopy() if num >= len(newcustoms): return False self.beginRemoveRows(qt4.QModelIndex(), num, num) del newcustoms[num] self.moddocupignore = True self.doc.applyOperation( document.OperationSetCustom(self.ctype, newcustoms)) self.moddocupignore = False self.endRemoveRows() return True def moveUpEntry(self, num): """Move up entry.""" newcustoms = self._getCustomsCopy() if num == 0 or num >= len(newcustoms): return False row = newcustoms[num] del newcustoms[num] newcustoms.insert(num-1, row) self.doc.applyOperation( document.OperationSetCustom(self.ctype, newcustoms)) return True def moveDownEntry(self, num): """Move down entry.""" newcustoms = self._getCustomsCopy() if num >= len(newcustoms)-1: return False row = newcustoms[num] del newcustoms[num] newcustoms.insert(num+1, row) self.doc.applyOperation( document.OperationSetCustom(self.ctype, newcustoms)) return True class CustomDialog(VeuszDialog): """A dialog to create or edit custom constant and function definitions.""" def __init__(self, parent, document): VeuszDialog.__init__(self, parent, 'custom.ui') self.document = document # model/view self.defnModel = CustomItemModel(self, document, 'definition') self.defnView.setModel(self.defnModel) self.importModel = CustomItemModel(self, document, 'import') self.importView.setModel(self.importModel) self.colorModel = CustomItemModel(self, document, 'color') self.colorView.setModel(self.colorModel) self.colormapModel = CustomItemModel(self, document, 'colormap') self.colormapView.setModel(self.colormapModel) # buttons self.removeButton.clicked.connect(self.slotRemove) self.upButton.clicked.connect(self.slotUp) self.downButton.clicked.connect(self.slotDown) self.saveButton.clicked.connect(self.slotSave) self.loadButton.clicked.connect(self.slotLoad) # recent button shows list of recently used files for loading self.recentButton.filechosen.connect(self.loadFile) self.recentButton.setSetting('customdialog_recent') def loadFile(self, filename): """Load the given file.""" self.document.applyOperation( document.OperationLoadCustom(filename) ) def getTabViewAndModel(self): """Get view and model for currently selected tab.""" return { 0: (self.defnView, self.defnModel), 1: (self.importView, self.importModel), 2: (self.colorView, self.colorModel), 3: (self.colormapView, self.colormapModel) }[self.viewsTab.currentIndex()] def slotRemove(self): """Remove an entry.""" view, model = self.getTabViewAndModel() selected = view.selectedIndexes() if selected: model.deleteEntry(selected[0].row()) def slotUp(self): """Move item up list.""" view, model = self.getTabViewAndModel() selected = view.selectedIndexes() if selected: row = selected[0].row() if model.moveUpEntry(row): idx = model.index(row-1, selected[0].column()) view.setCurrentIndex(idx) def slotDown(self): """Move item down list.""" view, model = self.getTabViewAndModel() selected = view.selectedIndexes() if selected: row = selected[0].row() if model.moveDownEntry(row): idx = model.index(row+1, selected[0].column()) view.setCurrentIndex(idx) def slotSave(self): """Save entries.""" filename = self.parent().fileSaveDialog( [_('Veusz document (*.vsz)')], _('Save custom definitions')) if filename: try: with open(filename, 'w') as f: self.document.evaluate.saveCustomFile(f) self.recentButton.addFile(filename) except EnvironmentError as e: qt4.QMessageBox.critical( self, _("Error - Veusz"), _("Unable to save '%s'\n\n%s") % ( filename, cstrerror(e))) def slotLoad(self): """Load entries.""" filename = self.parent().fileOpenDialog( [_('Veusz document (*.vsz)')], _('Load custom definitions')) if filename: try: self.loadFile(filename) except EnvironmentError as e: qt4.QMessageBox.critical( self, _("Error - Veusz"), _("Unable to load '%s'\n\n%s") % ( filename, cstrerror(e))) else: # add to recent file list self.recentButton.addFile(filename) veusz-3.0.1/veusz/dialogs/errorloading.py0000664000175000017500000000337013161413406020152 0ustar jssjss00000000000000# Copyright (C) 2006 Jeremy S. Sanders # Email: Jeremy Sanders # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ############################################################################## """Dialog to show if there is an error loading.""" from __future__ import division from .. import qtall as qt4 from .veuszdialog import VeuszDialog class ErrorLoadingDialog(VeuszDialog): """Dialog when error loading.""" def __init__(self, parent, filename, error, traceback): VeuszDialog.__init__(self, parent, 'errorloading.ui') # insert filename into label text = self.errorlabel.text() text = text % filename self.errorlabel.setText(text) self.errormessagelabel.setText(error) # put backtrace into error edit box self.errortextedit.setPlainText(traceback) # set warning pixmap to left of dialog icon = qt4.qApp.style().standardIcon(qt4.QStyle.SP_MessageBoxWarning, None, self) self.iconlabel.setPixmap(icon.pixmap(32)) veusz-3.0.1/veusz/dialogs/reloaddata.py0000664000175000017500000001210413161413406017556 0ustar jssjss00000000000000# data reload dialog # Copyright (C) 2005 Jeremy S. Sanders # Email: Jeremy Sanders # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ############################################################################## """Dialog for reloading linked data.""" from __future__ import division import os from ..compat import cstr from .. import qtall as qt4 from .. import document from .veuszdialog import VeuszDialog def _(text, disambiguation=None, context="ReloadDialog"): """Translate text.""" return qt4.QCoreApplication.translate(context, text, disambiguation) class ReloadData(VeuszDialog): """Dialog for reloading linked datasets.""" def __init__(self, document, parent, filenames=None): """Initialise the dialog. document: veusz document parent: parent window filenames: if a set() only reload from these filenames """ VeuszDialog.__init__(self, parent, 'reloaddata.ui') self.document = document self.filenames = filenames # update on reloading self.reloadct = 1 # get a record of names, dates and sizes of files linked self.filestats = self.statLinkedFiles() # actually reload the data (and show the user) self.reloadData() # if interval changed or enabled update timer self.intervalCheck.clicked.connect(self.intervalUpdate) self.intervalTime.valueChanged[int].connect(self.intervalUpdate) # timer to reload data self.intervalTimer = qt4.QTimer() self.intervalTimer.timeout.connect(self.reloadIfChanged) # manual reload self.reloadbutton = self.buttonBox.addButton( "&Reload again", qt4.QDialogButtonBox.ApplyRole) self.reloadbutton.clicked.connect(self.reloadData) # close by default, not reload self.buttonBox.button(qt4.QDialogButtonBox.Close).setDefault(True) def statLinkedFiles(self): """Stat linked files. Returns a list of (filename, mtime, size) """ files = [] for lf in self.document.getLinkedFiles(): filename = lf.filename try: s = os.stat(filename) files.append( (filename, s.st_mtime, s.st_size) ) except OSError: pass files.sort() return files def intervalUpdate(self, *args): """Reload at intervals option toggled.""" if self.intervalCheck.isChecked(): self.intervalTimer.start( self.intervalTime.value()*1000 ) else: self.intervalTimer.stop() def reloadIfChanged(self): """Reload linked data if it has changed.""" newstat = self.statLinkedFiles() if newstat != self.filestats: self.filestats = newstat self.reloadData() def reloadData(self): """Reload linked data. Show the user what was done.""" lines = [] datasets = [] errors = {} try: # try to reload the datasets datasets, errors = self.document.reloadLinkedDatasets( self.filenames) except EnvironmentError as e: lines.append(_("Error reading file: %s") % cstr(e)) # header showing count if len(datasets) > 0: lines.append(_("Reloaded (%i)") % self.reloadct) self.reloadct += 1 # show errors in read data for var, count in errors.items(): if count: lines.append( _('%i conversions failed for dataset "%s"') % (count, var) ) # show successes # group datasets by linked file linked = set() for var in datasets: ds = self.document.data[var] linked.add(ds.linked) linked = [(l.filename, l) for l in linked] linked.sort(key=lambda x: x[0]) # list datasets for each linked file for lname, link in linked: lines.append('') lines.append(_('Linked to %s') % lname) for var in sorted(datasets): ds = self.document.data[var] if ds.linked is link: lines.append( ' %s: %s' % ( var, ds.description()) ) if len(datasets) == 0: lines.append(_('Nothing to do. No linked datasets.')) self.outputedit.setPlainText('\n'.join(lines)) veusz-3.0.1/veusz/embed_remote.py0000664000175000017500000002434713165473546016516 0ustar jssjss00000000000000# Copyright (C) 2008 Jeremy S. Sanders # Email: Jeremy Sanders # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ############################################################################## from __future__ import division import sys import struct import socket from .compat import citems, pickle from .windows.simplewindow import SimpleWindow from . import document from . import setting from . import qtall as qt4 """Program to be run by embedding interface to run Veusz commands.""" # embed.py module checks this is the same as its version number API_VERSION = 2 class EmbeddedClient(object): """An object for each instance of embedded window with document.""" def __init__(self, title, doc=None, hidden=False): """Construct window with title given.""" self.window = SimpleWindow(title, doc=doc) if not hidden: self.window.show() self.document = self.window.document self.plot = self.window.plot # use time based checking by default self.plot.setTimeout(250) self.ci = document.CommandInterpreter(self.document) self.ci.addCommand('Close', self.cmdClose) self.ci.addCommand('Zoom', self.cmdZoom) self.ci.addCommand('EnableToolbar', self.cmdEnableToolbar) self.ci.addCommand('ForceUpdate', self.cmdForceUpdate) self.ci.addCommand('GetClick', self.cmdGetClick) self.ci.addCommand('ResizeWindow', self.cmdResizeWindow) self.ci.addCommand('SetUpdateInterval', self.cmdSetUpdateInterval) self.ci.addCommand('MoveToPage', self.cmdMoveToPage) self.ci.addCommand('IsClosed', self.cmdIsClosed) self.ci.addCommand('SetAntiAliasing', self.cmdSetAntiAliasing) self.ci.addCommand('Wipe', self.cmdWipe) self.ci.addCommand('_apiVersion', self.cmd_apiVersion) setting.transient_settings['unsafe_mode'] = True self.document.sigLog.connect(self.logEmitted) def logEmitted(self, msg): """Write anything logged to stderr.""" sys.stderr.write(msg + '\n') def cmdClose(self): """Close() Close this window.""" self.window.close() self.document = None self.window = None self.plot = None self.ci = None def cmdIsClosed(self): """IsClosed() Return whether window is still open.""" return not self.window.isVisible() def cmd_apiVersion(self): """Get internal API version.""" return API_VERSION def cmdZoom(self, zoom): """Zoom(zoom) Set the plot zoom level: This is a number to for the zoom from 1:1 or 'page': zoom to page 'width': zoom to fit width 'height': zoom to fit height """ self.window.setZoom(zoom) def cmdSetAntiAliasing(self, ison): """SetAntiAliasing(zoom) Enables or disables anti aliasing. """ self.window.setAntiAliasing(ison) def cmdEnableToolbar(self, enable=True): """EnableToolbar(enable=True) Enable the toolbar in this plotwindow. if enable is False, disable it. """ self.window.enableToolbar(enable) def cmdForceUpdate(self): """ForceUpdate() Forces an update of the plot window. """ self.plot.actionForceUpdate() def cmdGetClick(self): """GetClick() Return a clicked point. The user can click a point on the graph This returns a list of tuples containing items for each axis in the clicked region: (axisname, valonaxis) where axisname is the full name of an axis valonaxis is value clicked along the axis [] is returned if no axes span the clicked region """ return self.plot.getClick() def cmdResizeWindow(self, width, height): """ResizeWindow(width, height) Resize the window to be width x height pixels.""" self.window.resize(width, height) def cmdSetUpdateInterval(self, interval): """SetUpdateInterval(interval) Set graph update interval. interval is in milliseconds (ms) set to zero to disable updates set to -1 to update when document changes default interval is 250ms """ self.plot.setTimeout(interval) def cmdMoveToPage(self, pagenum): """MoveToPage(pagenum) Tell window to show specified pagenumber (starting from 1). """ self.plot.setPageNumber(pagenum-1) def cmdWipe(self): """Wipe the current document.""" self.document.wipe() class EmbedApplication(qt4.QApplication): """Application to run remote end of embed connection. Commands are sent over stdin, with responses sent to stdout """ # lengths of lengths sent to application cmdlenlen = struct.calcsize('