pax_global_header00006660000000000000000000000064136505273720014524gustar00rootroot0000000000000052 comment=b29fc6f3c8ab9072b5e36acdd85fd56af0b12190 MutatorMath-3.0.1/000077500000000000000000000000001365052737200137725ustar00rootroot00000000000000MutatorMath-3.0.1/.coveragerc000066400000000000000000000016251365052737200161170ustar00rootroot00000000000000[run] # measure 'branch' coverage in addition to 'statement' coverage # See: http://coverage.readthedocs.org/en/coverage-4.0.3/branch.html#branch branch = True # list of directories or packages to measure source = mutatorMath # these are treated as equivalent when combining data [paths] source = Lib/mutatorMath .tox/*/lib/python*/site-packages/mutatorMath .tox/pypy*/site-packages/mutatorMath [report] # Regexes for lines to exclude from consideration exclude_lines = # keywords to use in inline comments to skip coverage pragma: no cover # don't complain if tests don't hit defensive assertion code raise AssertionError raise NotImplementedError # don't complain if non-runnable code isn't run if 0: if __name__ == .__main__.: # ignore source code that can’t be found ignore_errors = True # when running a summary report, show missing lines show_missing = True MutatorMath-3.0.1/.gitignore000066400000000000000000000010411365052737200157560ustar00rootroot00000000000000# Byte-compiled / optimized / DLL files __pycache__/ *.py[cod] *.idea *.log # Distribution / Packaging *.egg-info *.eggs build dist # Unit test / coverage files .tox/ .coverage .coverage.* htmlcov/ Lib/support/axes.py Lib/support/mutatorMath-A.py Lib/support/mutatorMath-B.py Lib/support/mutatorMath-performance.py .coverage .coverage.* Lib/mutatorMath/test/ufo/testData Lib/mutatorMath/test/ufo/data/instances Lib/mutatorMath/test/ufo/data/no_warpmap_test.designspace Lib/mutatorMath/test/ufo/data/warpmap_test.designspace .pytest_cache MutatorMath-3.0.1/.pyup.yml000066400000000000000000000002501365052737200155650ustar00rootroot00000000000000# controls the frequency of updates (undocumented beta feature) schedule: every week # do not pin dependencies unless they have explicit version specifiers pin: False MutatorMath-3.0.1/.travis.yml000066400000000000000000000027011365052737200161030ustar00rootroot00000000000000language: python env: global: - TWINE_USERNAME="anthrotype" - secure: CMCLOo9Y8eKK8BfYZeHfclvwx5uJX7078/nLVFF8qZJssopTA34kuJvIOG3UIGpN0Q/iy+GwKVcEHOA59aOuS+lLUjX9U6O/p9Byxg4hNrYQkqEGUS+2eUvGII9ko03ZzWpX4uGSvhRx6013cCNM3iCGtcoCQhMYsIkxAipVXBEdeo78km9lN4FEvdLy4P9KcRP8rEFWvIOg+zrvQr8i5M8UMLloL5E+AY0q+W+qVCGx6k2hxt5UGqEiffoC99Y36iXlge932ftAxOS3Yf5seh8lN5RlTnJ3fB6cqkXV28ZNi390R1kGLRQSos1bqvk3KPk3ItfSjqVJtzkKemtWbZcu0KfUzdbvjugNudfxSHYxs5rpj3ncPFC8C4Os94ICgr+HrgTODj8PBfwfm1bgKU7RboHzPa0pCPboaO1Hxac/c2BNzQPvtO8xbOw/ehIPSNTzZf/zszhcULB17nx6OUoWZKhwXw217ZUlSCd9Vo/nxx7IYktkmaydAKUbBoMBUXq6b+DgMHGpt4fRtQ/6CqU35HIvdcyiuSfFck/17Wep26GQ35JJunE/w3KNES6bcWlUShyJyk/meZwyCOEqg3n9OCax9GedO28DsQML7WK1L36JFDXk2FWq+noz/gts99u4RgBOekQ/ggRIsMFKeW0XzqYuYTqorHmbLVfYPLE= matrix: include: - python: 2.7 env: TOXENV=py27-cov - python: 3.6 env: TOXENV=py36-cov - python: 3.7 env: - TOXENV=py37-cov - BUILD_DIST=true dist: xenial install: - pip install tox - pip install coveralls script: - tox after_success: - coverage combine - coveralls # if it's a tagged commit, upload distribution packages to pypi - | if [ -n "$TRAVIS_TAG" ] && [ "$TRAVIS_REPO_SLUG" == "LettError/MutatorMath" ] && [ "$BUILD_DIST" == true ]; then pip install --upgrade twine pip setuptools wheel python setup.py sdist pip wheel --no-deps --wheel-dir dist . twine upload dist/*.whl dist/*.zip fi MutatorMath-3.0.1/Docs/000077500000000000000000000000001365052737200146625ustar00rootroot00000000000000MutatorMath-3.0.1/Docs/designSpaceFactors.md000066400000000000000000000050541365052737200207570ustar00rootroot00000000000000DesignSpace Factors =================== In MutatorMath instances are calculated by multiplying all masters with a specific factor and then adding them all up. Determining the factors is then the important job. MutatorMath has to deal with all sorts of edge cases and extrapolation so the code might be a bit cryptic, but the basic idea is not so complex. So here are some images. These graphs were made with Drawbot and represent actual factors calculated by MutatorMath. It is a two dimensional designspace with four masters. The axes are gray. The third dimension maps the value of the factors of the masters: 0 in the plane with the axes, 1 up in space. So while it looks like a three axis system, it is a two axis system with the factors as the third dimension. ![master at the origin](designSpace_neutral.jpg) The master at the origin, the place where all dimensions are 0, is called neutral. All other on-axis masters in the designspace are relative to this neutral. That means that the neutral is subtracted from each. At the end of the calculation the neutral is added again to inflate the instance. So the factor for the neutral is 1, it is part of everything. The factors of the neutral master form a box. ![on-axis master two](designSpace_on-axis-two.jpg) Now we insert the second master at `Location(A=1, B=0)`. It is on-axis because only one of its dimensions (A) has magnitude. Its factor is 1 along the line A=1. Note its factor is 0 at the origin and at the line A=0 (and all other axes if we had more). In this designspace the influence of this master forms a wedge. ![on-axis master one](designSpace_on-axis-one.jpg) Next we insert a another on-axis master, but now at `Location(A=0, B=1)`. This factor is 1 along the line B=1, it is 0 at the origin and the line B=1. It looks like another wedge. ![off-axis master](designSpace_off-axis.jpg) But we also want to include masters at other locations, what about `Location(A=1, B=1)` for instance? We can see that at that location both on-axis masters are at full force, and even the neutral is added with factor 1. Here something interesting needs to happen. First MutatorMath calculates the instance at that location using only the on-axis masters. If you were to look at this instance closely it would be distorted: it is an extrapolation. This on-axis-only instance is then subtracted from the object we want to insert. Now the sum of the masters will look familiar again: the off-axis master corrects it. The factors for an off-axis master look like a part of a pyramid. MutatorMath-3.0.1/Docs/designSpaceFileFormat.md000066400000000000000000000002741365052737200214050ustar00rootroot00000000000000DesignSpace Format ================== Please refer to the [designSpaceDocument](https://github.com/LettError/designSpaceDocument) repository for an up to data specification of the file. MutatorMath-3.0.1/Docs/designSpace_neutral.jpg000066400000000000000000000515351365052737200213540ustar00rootroot00000000000000JFIFddDucky<&Adobed v%=;S[       @!240 P1"BA356pCE#$D  !1@Aq"2r QaPBRs0#tb3CSpc$4D5u@P1 q`!Aa0pQ2r!@1AQaq 0Pp _\i@;᝶5}e/I.W`LQP&fEdc |Dn>P 1Hb>LXdc |@/,@!0 b_͐ ỲC` #56@1f?2 Gɀklb~d(Ř$1&P 1Hb>LXdc |@/57A[RsNw_k.56A"Ɠ>t-鮀koלzq{< 56AePNǯynck,Q-+:@/)Su}"Ɲ6UyL@/Ɇv6z=pP,}ޜnh56H׽:g@uvNQ56Jם V%V,5>әw 9şkAJʵ~T#IV~U}8 b_ *nuh b\zH~Y70Yc^`3ƾ|9ĨEXS.t;MC,`aΙ&"ז$(VT)6ex|@~W^`kZZos$iEu/Lky0|U[[SfZS &8y_;aa*I8gh޲;G/LkD(;ӄItyvۤ8&8ݠPph>K]tcS~gobϦ5ۦLmi/NQ374sN];G߶<@VYvj)*P UvJEJgolhyg|LVE 9YjS:0PuYjZHTjȱ} U_*A5@z];:GlhIGd=J&;fp\uɹ ;]#y0TJM[&ypi82 ՙfMȁRւқu&cXS1k3hfY}Ͷ: (j*K:ݭʻku>4əۅ[-MsOMfi7Y1ݸ+[L'{ҰJʻukr@OM\׶c,WMY75fzjo|ޔWwDr#3im/o?f ɻsZpOMVQFgi(ԬkZڤbܗ]5YGt kJI)n9gDn 骞[Wwzjsh B8+6Zۍ=5$3:qWMBly1{OMF]fL*, 4/OMFZ HQ66KBp 餇><%B<7` ͭy"o=qWMzzheQjVO*`2JO)KInۊkCJd}6b>[NdXˆwt.ݷtק-t9t_dgK̩=qWMzzj$7m]5験ݷtקOl#vU^=qWMzzj$7m]5験ݷtקOl#vU^=qWMzzj$7m]5験ݷ r ;.̒NuVκfeLKRo,,FfRgl7%Qbiynid7m(B#3mV8BR%y*Y=p̽S;s/hfI%5{a Y!,sΈG%LFaAhA!]`cYډS'a{r^r+so|YޑU'iL-ޕgTFOYo?[=혢;17m=XF~j^=ur{`8֔%=KHm8Ḧ:nfUE`3(ԯ( y!/4&LuR%Ɏ-[<\)4ƩJgbF0NmZȩdR6c?/ʽlM5W' YXVlJWcK6NI/ҚR2D?RRٗҔ 2 Jq>arq,J)Eb#34#|&˼t9&Bߑq(u'RF?U6ZGޟEœmfCa2ސӛnG}">OkT}j#1I;jZbdJK5mj㲤>j+V3^Zc{-6n9efۇc-$yi#IZHG<夃ʾdEƋIZHG<夏-$9uħ(KIZHG<夏-$yi"FRJ4XEmhg'd$x#&H2G3#?3GLdG"b :qgھ+2i. +cgθ6\\'u\|R\:$rK\*ԗ0ZD:(V"sd|P\{\*.r \WpY"Jl:ιsX߸ UjUn WI\B\HVo 0שu͐YlîArp*. >u.NsD 눸,֤:u͐3Is >u.Nsd|R\eo@"sd|!aa`eaa`>+( /AsuAOcP/-_rpV< H#~( ?C_"$ہ+P5 `5Pw0gX3Mk^þ M*ֽk> ;V`gzf ڧ &`e\5Z5a)Q#vXG]Us3 YjJt[6Lrjs*m6) X[M-W:֓dP*ZnꮢIXUtTUde70֡6)JU'ʱV-W0Ud@[VlOld33I\ê!zJJq* V ;lrDxLrez\HvT&Gp!{^/{^/ͣQ SDaiujjjjjjjjτM~WA]: l5?!['& zn('<~v=4u13\;(TijH̘2" YY;1[f׫Bk0%)0kHU ;gl :)q/`CL4Lj[ pqIӚ:"f{ݏ)Y*)'07seǔr=xsVu9-u>8[FAI`ז89L—aN|V!y^Br U`8WDHVa(1Ź3d~-ј`8匈byN{\HVa ʍ@N fVM&[>'ʽBZ$T(F4VjH,֣8:.dEY`c]1[@V($Ha8җ(oms -JI@Ap{*Q:(4F2SdBr+F37l>yFϗq4Ҙ(IM9{#pI,R07$OpUvr՞$GV r'W`bM2*^_MW^_7u%4\[B-}xRjPʛCoڴT*UuyI6gi:#ɾ$<$XV r'W`bWo0SC7t* ,ys mR)@{=)gGRJr\苗!t)-K)gDT]RV "{y=Ал+&Ej"uv%Gj&FdyʂBfʬͽ]އڋ)v#Ĉ]]궏bF˼ՁD@ eEj"uv2"`k:wZ5ȝ]ȭXN]V r'W`c.+V}1y\Մi15,@ 6,qEj3=B? }bԛyxU"nP|"& BRZ R_J w-$ B2 qI2o8)n!jßLCY^銛L]VC(4^y(T '<6ćL3.&kݣŖzh$5ΚbdeD]8/QZ}{q;@dCv\9?%,ԑ8+5ų[cdw\q9kͽPMzB2KKxW]uèZҜ\З{5Jہhtݰ&S o laW49;kYQ*)/NZKrf%~8jl|HE J֪ 8qJNo趩wKbBEHª2HZ)ះ!d7^UuJiDWÍ6@-{ii(Ӷ8QlpNF;cv 4 Xm$W F;cv 4i(Ӷ8QlY[hR{vŔ F;cv 4i(Ӷ8QlpNرBmVF9iLNJn_#af\EJQu+ZEgQ E1č;#vG4$iHӲ8dq#NFč;#vG4$iHӲ8dq#NFč;#vG4$iHӲ8dq#NFč;#vG4t*``J_PJTܒ3C^&mY,':Eʼ)vMT--GĖ(Ȭ$FUdrV{;54'1$BG3mե|2cZg0m(̅LHH.)TN]CںJ%Us/9fS;R]TE5YTmhJ>QJB8j}ZE<,驲kف⥨BMɋum QFIV<Ӆ^RyI;Mux[ch$ǟ6Emg(ii zap;qG xIxa7ֶmjq CfҸVm>ǚ:b̥2H{bs3LʯyG樂\rǒ-QE)_Z1ǯ2LUy<%ZB%fԍsBv zwQ+̽FmHE"-S̙ǐ'SՋ/O퐕jr$Qvk&i1=WUu!fe7h1]N:),7zkF9zBbrgNV2L Ouzք4{Z1TU>KM4ҙais7t -+4&btwbK>J8z"gh@<?!Ҧ<+zSѳ 2m7_b$L]@ q&t&Alr %e'cxԚr灩/HrW2̮es+\W2̮es+\W2̮es+\W2̩(ڄvll#eAވY^]<ҫ-6u^SBxtBow[1nwiJJj|@yί-?Wcg=ߵ(O @ca!=kٖ[(7crRwn=)0e^60 .4=Av?nk{R-|\{Pз@zxxؼYίQ7ر1rQ>"bxV9|jVt |qwx3O3SvDz|LV\`wR[?<ҫ.m^e M 3HeM?z`?@0Vʶ_|V_bVO@xؐoG߆t;|>z{_gabS!Nz&4>•cE-5ee+}7p gRHΰcڌFziSRՈ@&٫)7a}9WoCX2|ի7p 0˒S$/&AHM Rܝ(pԇZ͵dFtm..K~zσ` uq1.Y1P) =V_)M;뤐kxZލ"=_ʽCwPx@ }$ =Cg}1>l4gC꣆Kz?ƊU[֯?xWn8CnPA' Q*A+7p М II^mK 3?{R~P6Tgc@ 66`GNye_n*6e)=d><ݏ !f;p|%"#lD\. 2N a/Z Tv37pu# ؖrm3R|zٜzNFs_)(r7jcM.j9etOA,S=i7%+nS |dna_Y)vNӣVdL{Qh鴖Oެ v-o Nr9AKwkJE=ߵzjQ O%{S(#IG$: HZ*:s %9cB W )۟3(yPԼ?77VRз@zksp{7OHSvG^^yoW:t@q%d*<DrrΠg #Wd4؜^ώ.zVuռ[c24rw'n pD2-YoZm7 eѣ@(u:V .tTζY f!:MCtSZ"[7*`&soj$5cyb陘{'mzAbIb,`h kgFZuL\ϭez}h , I m VNWAy$b#6hJEȣ"Q?G(|hA1ee?G(Eȣ"e(&Àȣ"Q?G(E "VUσhgQ'"al;)Mcs!ŭ6j~L]kbQ?G(أbQ?G(أbQ?G(أbPerU 31OCb[% JZC gJY NQ5>tPc_2Pb#Zy XhIr)>a,ʥ%eG9')PvT^MPc2RA3>HZq'hTk}[55l] 0@)WQTd?#ZÒ?IC匚eEw$uRhtuyYMNڙc.qʂ(EisRDhTOorQ9 #?@; #jdВdhOà @dU"zar +֡pkz<~76pmAVIʓ èGVXA/7|]%'GcaKNl#?!,J i 1@ N?!Lpfx\ Rcb&54L٘co֨L )֨M#XSQcpM@E^vEMtDV:&hMTLuޢ.M,К*f6AKbq1LiTJ5j"qÀcGN:;55=l+*( 8įwⱩ4%cN9zvBh -F:fvY ȩ*"q1lr*hc"qpIEM,u1ɸ&$z1#ʕʄҲ)6Ўp1.E̤t9#8舭*`@K*~5BӁMgҟtqKzL~TW߽֔x3Б"jt(ce+SV(%:(S!&Q{:cb )̭6G:Zz <['T $J O-?)N E/ɕb1+F2XET85s?!OvPMgDcG1 INn%,TTTTTTTTTTTTTTTTTTTTTTTTTTRچx]k9wK#v:IFc`PEvcJȣ6벤Q6$2Ӟ68'ufi# pk66ܜ^ʞxfvX7f$%Jj0$o@3[O3O^TRI;ȶ5r]tR$P'dlL㮄Q}/cSPz,SAIh c'9Xۦ:'gNlxictAy$Y P;@c{'1=%b=Y2X^QNp6өzX5euFNVd<={lfs G@V){$M7зh`2׶ĬZ uY3F|%TG^׶ĠC1Q@,3imyH$Ő..ӝHcY1nCUAN"^kv^ ڏY]@qI\WF,[ #ڲכpNH-fndyz%\mLP"D̥׃"S$`fq6@Ncf͂ 7=(?䂄/HmRpu=*!2% &%AȩfژZE;n$A(i|dyz,{'#Kgf%:4IyTgr\E1Rz`7ʅy="nPGsDgar4#-ee"^k([S,B>kW;w\wsߪ{U~W;w\wsߪ{U~W;w\wsߪ{U~W;wCqwee@ ~`w[8$fA6H N:p]AX4_iƷVvl$2 !@P;2̹@̷Œ%{I/oWnBǶ:OoYr3k`{D^H¯vEZȫ< .w鎽FޝgC%nyaދꛉyT$llpJV;J.uj?=8H,oe]bsc 'jX cDR 9dxRV|@dq`ggg"sc$KNM% sZ> 7"ڣj_eq"]9[Nxd iۡ+h<K?pdw :^ P23|!&Q "07+ HsZY1Y|<62Nm1jlbcZqΡc0&Q#6k4q LD#y[ReQ'.:y! ̢ĔM!C;dBJUHU Wdn&Jtzr+(nt$(~g١ B0{e1|/u=C5 =i$KJ9+[ Fu"ND Ė<Ox)cD 3rT) E^0IW+A%%Ő <መVtp$HJR&A)D1 PqE :·(4zGIcX"2Zw 5 *'aѮ~ypfjr%{VEecEl,Ov lLR+9uhw{%Jb2ڜTw#2[E:.fm'Kd{_YWIO6%)RLUm%}Ih(ʲ+iNPxSz4RH6 %B e:NIg/23ʧ` L_H%N|wW=nRp.sPAw GpƦ"DI#S P)* Q*ZτYm;Qi @P)J$YFzp|`>_#$"Ғk4B9JdG1`$YӀ=3HA{F`uCׯ^z롂E>Ezׯ]%IԜ *ׯ^xsXb9"}#"քidḙWAz:sׯ^zׯ^zׯ^z;ĖdM:NaĘ j3k!t `#٠@#@䒎G#^\t 6U  l??iHe)Ƞ52ޢJA!:ہr*ewF;4"I2-@Vt8c|~ʛA^VGN6uR{*o46Ӎ5gu{pq f`MEl2Sμ ek-On¢T鳁VmIbJ8jNnۉF"2Z0 > \G;Gw0]x> VJK %-JN$Uہu\IjF=4bFi? ǟ@qy}ǟ@qy}ǟ@qy}ǟ@qrR+rhL%*-1Cs7ět1HrA6b7n> Mg O_; ~G3@}; ~G3@}; ~G3@}; ~G3bۻ=zcG$7_Aw,֑h KL%qNr߈$h jO%ʗPgtRqK7M:md|g!4YƱ Q EA?:sx!~q|~hU&^jq>5@aecqk~"tN맚YƠ( ~q}EXHCUew8mlۡ;x_QP[co!ڦZwc&ezQ0t=O0f4g[4?8- 4|C^:{V6: n뒈VN㲧pۭ.r[:2_ GK DG(=$gء(PS/03=FAѿOj 1HJDgˋ\W"]XV 󯚐W/zR4֜/SW֒jD&Ct܅ o*ȤA;SoU˕D$5z?s~j`gomV 0K,jxL[n CZWeyϳ:S\ߕ2&i#S"z8S(\޼ٯTNj(d"w)ޣXSM&a6ٔ:9dY_e8z%-TX+vl9S0^du\s[Gƫs@cͧ0ȹ#(Yi rV 9 e0{䢒N˔Gȗ2T.H}siߏo1As/-iM&>wSmZiiK(VU*GMutatorMath-3.0.1/Docs/designSpace_off-axis.jpg000066400000000000000000000376361365052737200214240ustar00rootroot00000000000000JFIFddDucky<&Adobed :/?       @P0234!1 `"56pAB#C$D  !1@AQ2rPaq"0Rs B#3StpbC4D5P!q1A 0`Q@a!@1AQPaq0 `p᐀ @+ K^W 4^)^9$:`)}ItO@ם"Xbz_&5AbA7 t<0"^ +jHncMHp[[=> "^ +|Lk|<_)u`^  uw?D+k|H63-#4ם1H "9i}}<{[DK SQۻ|G t #Osj2iJs%"_ם'}@F6FqO ʼ jN.neN$,4||t봿WnJsWţ; fcH+.SͭUxKq}6|_^?e@'[+ vTOer`lFLľa;F tX q,S9.#j~qbSνIQnWAө Zrk4֝mk_ѡCiYO"8ˠ^q^ybQEx;g^ɤםg5 if2mP6PYڕfgjy4FĀ @Xq糯dp  P&U%IJH_{:M')`KXײi81e`糯dp57 k{:M'oG>05={& bX4y41i糯dp>&5q5={& k cg^ɤHK(k{:M'l#y4 [Xײi81 糯dp5ak{:M'ؤrʨXײi8b -c$5={&>&5q糯dp `fr糯dp#_ `&5ތ6Y糯dpX4糯dr5 8PdXPKh@FJl= Cʏcpd`jr^yZG8"5MҤ; ύtNe|>1;0>@:MR6Rv'"*dhr5*Qh8̷r3jeqRR|c v)xTdGf-uEŋ|(ӿ!|c v(zzKsNur\ rUm-]S2Q竓c1W'0cbOa1W'0c~gB%\9*`Y'0b>|c v7F=dٝ#<|c v7Ft{I1دcڲOa{<|c v7N%dΡ<|c v>#1;\^ղ1׭TSO4koV95^Qxj/ Eᨼ5^Qxj/ Eᨼ5^Qxj/ Eᨼ5^M6!\woLLLLLLLLLLLLLL-RRЇrRRq%)IVmeSP̲aM^[qm1ޭn\GVP޻*\T:-r^Wf'܈Q&我u֛ys F6!9ϱryn %`Ãk@xC-Rg4,0{VfVӳr " 3>\p.FSƈdYDFh"%9my_H|?"5?g#>՟t{1`3ݼ?W) }"ygQҲ=+" NK#R YpS ajVFg 6SAJa&*2N>S*8S*8S*8֧N>듀8 8 rq8Ӏ*8Ӏ*8ӀzqwIAI2@}+NfGV_JË*RG??她1,6FXKج KChMK-XZ0|Tr_~E EaY>V5}?eqa?Mt5  ɀQ!l%`+@O< )R+6P3OKoIRk JQځ|T勗kzbaGnZ&ɛ~ςG/%Ԟ:Z\?9Sҹ7KYFo!4e[ʶK 8 R;HISE?ijPszG3o`e=SzF)Ih}L$doi3 s0Oޠv(}WfJ:+Fru@vEb:bJXI5L֠h U[)3RT+sWncRvE iyIE2'/t]dAZL[_p͇H>RMH :5PI\_-&Ҁw"hCr3 l>Bz^,e" )DLe"pYkÃf焜G (H$'"c@Ծ m}ZEJy tP⥨BMdum QFI9ٯK ?Mq$}i95m!8-6N MPg¬ 4㗢9%pZ+)8`k6%`mILsGTw)LR*1wS9Art٦qʯv~/˾Z a?)ĘT~톴c^ekג9UTmheR5Κ#ZS=E~F9_bO3oB/r+{r%*}r3?ITb d%hZ9;h?)ĘǡSVUA=}arehMM3@ca_?!WKta={Ϟ SkⰃcHF=KSI@Z|n z'@v(!CMSխZXUUNmZ͓Ll=*n5)ud14T桂PE|H΂te+:qX BU@p3g*ͬ(X;YfITu'u3̟e+,z|gA3>9+od kRXz_;~7e+,ZU.Zuyfd<5Yg,?R:c]qҲ:x{q-C2O+ovޞs ҳoƯc K+,8n~bУ!y+ov,LhNe6'aYe0 3+K@Oe_0矧chGm.4~)UCbdwM6J͑[V" >L^]xL#BMBHrw)P$$gzjFH eA)M#@JCڙJRiHP"zމk(ZpllqϱJfݩ%&f+-,{boUî!7tW(zJh'YbS &6BA扡tA1WJ3Z;zאlP˴!C&o`-pJ,JߔE뀌S-xgPkOC yQ @ ;PgbO*ڼb^("&[޳[}q* 6_QB3K%_Vluʼb9.03w:ߥs oy^oK3UPnz覚]j$MR 3Ҭ(t9>Y9BRpsan DyWJ.nL`@zRʣ({ ҥI:ܚ׳*S5PN9DF;t -Fy,`B$gX1Fm\D#s=4j ee>η,?5j զf ل! ~"[ # %2BhM4Е,AHMڀH}Ũ/^ڱ\YOTlgJf蔺7>aכ© C|,#&#E *!_3;el4޳I6ŵyiUMy#3« WMRC E,uĐ $(ф&bi, [c9bNy7̍Wn50pwMy݌gn50K/sy^o^y^o1y^oǃgMy`lgn5OB$[PUtPDwCCѥeM;ODHI.98` I@ |Zvհ@cN:A 7?!Wsß Ϗ>9x?9>ߠ>8NT5 >\k_^Q8ĚҦ8OSS1~s?9~s?9~s?9~sX̟?!We~'=750i$˜ 888fR_Uh*$p!E8Xi##\(xރK(nT: ^7' (9F**HADڢM,N\(aF2|u'\|p1jE%R@fʵ1jVE#BȼWJ5cx_! J|VVAF`ؓ߁n*[TXވ$Gnʇ̭F|- =kǣc'JLxy⌇zhbOrZ̭Jnhԉ?>EgEm2 A5 f ,“M\ԚI-$f՝ .u*w/ ƥC3˹˦7-,&/+L7(|b4bIϳs~C18JFXJV;zs@٢@_cdHXt=TP_( f"I$'rJfw$j(S͔!@Wdn, A}&9Gm}rS{TFH[%YE?j]Vnea "jŔ ;Fٍ2EVZ/V!fV[+JBfO"o^E` (RÓ!I- 2&_j̯FLdV7A=˙~NfP%`3jĺ=ĺU+~+:5D8['ۘg_j̯FHf8i#,fPz0=k<@XC'XC7*ͮ]e`%j[nZYJ7~ ڴ|3u駷\I-9.OZ@G5 =4 k}הث2{ՙ|zUeeue&]z񥏂M_gM99=P6{xۖ;B&JV_d[|V ڀrE\= dҫ++"A-or} }@ +͑ڇ)|RSllͮ]er&,z}@W^A1YUgJkⲲd3H@.R$*>1$ǁ0fKf WP(M2Njն;4_:0=Go5p`{}4lBZ_&U)G$m:C">ܬlF$椕fa/M)aRjlrm@0uVs'CK!R/Μ3#Yc-3H rhK xFt&52NUN'4M\N&&)n7_6 6GnVVW5j226B aGɑ>G )`Q֋Z@5] iD_epN|r@/e%dub-Xv~Zj KND^tЮ s&@+@tF7,Ė˶9.=T̰'0lhl@T@]oZ< $lP9j%"A75gAMaFsֺ{b,8C 42wnx5m,[6: ;jPv#`Iׁ/` 2nTsTUg+Lʔ mE>V*HD͎ɭal'ZQW E4~9Ud/֒ [ȉcw&dEJC""+/sKٷjTBæc; Sb5٨w5k0RaQ С;Q"˨3t +TJhŋd5R{VCn)~6&E8P@pҀG' &o*΃ldJ\ Ne-NđkQp96 , _ \62e =ND&RdĨ89,SSQN۲b*+l#(J'l} ~l. m~Ǐ#K`K'ˀu4/ˍ%"A/ %|#Rnx.Kx l9h`q/`O'Kx4>q/`lxY6@8*b!q/`P%Ξm40Ouq/`O_uqĿ7Ct1Ŀ7 ( Ŀ7׀}UW7IN<(3M OAǒp*"}?_\y21._ ɟW1@.޴:1A$n(j*o5J;`zE-M$˗.\r˗.\r˗.\r˗#$V`Lu0ڝuJ;plz>$! ^zׯ^zׯ^zׯ^zD$+%C\zf'EH^3w 1əEVZ|BJuq+Ni-Q\#L:[RkCOx( :2(<`VI2GӉY1@*ڇ?z#8^s\yjk#@"ƇML4VV js@Ql`@ )tО9Jx$&V?#֟FH,˕ /o( QNũ&Gܶ,V&)-ad%2\kR0Ly& 6m1|N{f0ZJ BQw :ǂ{ wAP1ja$'m)3]wvÚ. Pb.xۋ0V C%7_+|q0bD[TB$:KR^ް#2' MIɤհH7o5FFZ$!qL2>`# ` t rrIGR#ܯ]:YTKn* 6?WҎzGycKΣs)9Tx^^e<*qqC;S#$*>ԙG&MG)-kzV@`>L*A^h hd*krW3,h&d2ޏ f}?uC5S}TM"#.q„x7U8AcuS+N#ycuSX֪p\ N T'O#S pO' cSv| 35SùxT\CO'TA8 #X~ycSN1YOkS#cSB1\sJJs3JH!1%> :`3#o8EEEEEEEEEEEEEEEEEEEEEEEEEEEE(}2tqDG_`GoWJ؟xqT:Hc?q]#MutatorMath-3.0.1/Docs/designSpace_on-axis-one.jpg000066400000000000000000000427721365052737200220420ustar00rootroot00000000000000JFIFddDucky<&Adobed ` 4E       @P1240! A3p"56`BD$%   !1@Aq"2rQa0PBR#s3St pCb$4D5 P@`!aq1AQ!1@QAaPq0 `p᐀ f2ǀ}ɮ o6/FMq1 R,O >o͓Ph>~H 0O=tyjY^ߢmXek%пx8l83E=\|`[t>cPJ#.QŲPs~7U}MU?SWH;}{wE?w_Inqex@?WVE655>|yzo;5.)&˺o5:{;z7y+\@=KWf|cf9vT3Ci~/!]01˼} u$xt_NQ-=]]qŠpe鮾%ڇ[·yxNoomc;Dj}DzQ)Ii>yץ{FqE+$yw&J|(e|>y.`|>v\f[p]yQݮJTxfgKJIumq$NDTao-uY at#Z j5kP֡CZ j5kP֡CZ j5kP֡CZ jeDiPS0J!>N6AlEe{(d%/UVaK:=c5}> i;\2DVNny79'=Ҍ f"Zf|ՙ e7Y=Acՙaٳl?ucK>7qH͖ɶZF6Z̳b$5W 2L%[$!j5ΈJp=n8R©k$! 0h=ԕgSՒ&G2vf㍠7]!ޓ#N(O5NFgڰj RtϺb[&KJUrVZ C#Y%Hp՛74\թ~ɐ7q)(L_Lfo>\+Tu6DidCZ4 ZǖGhAoCLIݤ̬Zя$rgSfIݽA)@w?Q-KlR$D46LJ~ᕶ.8GuH;><qpdQv+W:"<ܕc̏$O% y)HJ@R<% y)HJ@O$O$O$O$O$O$O$}MrWe3#?3GLdG"b :qg$+WLʹj0*+NaVP31aaaaaaaaaaaaaaaaaaaaaaaaaa`c3 YjJtZlZBZêֶWFHZEZna\ZM%@XHqV#m70ꮪԕ꬚Ss ;P`Uc s* A{gda>v"VXX?jĠ4/@ei;zI+uW:ȶ&Ilam/u{ⱂ" ėcA\maFOQ쟞> >jI]?29j+)r\r.>js_ދkUH!H\aQQJ_/RsI*Yyo=Uտyn-U Wyn-[5 W:ʫɵItt{Xnaj*Yv0wYU#}Wez[VljޱgZ[ιb޵$:$(^r(^GZAf ?jZD:Y:3Y6Ag \=X :MQoBޙlQ%7-Y|D JAַ8<>wVW.> 6"|aqcIb&.CP5 @^a| \A:{-_po=I*bʵ͞gjg_d?+pp6GD{qc踭ЇM%dQCHn4Wu1M#&(hfM}]h77?}2L"$?+lB^ǔ֕p:vd!%KJc(qř,! R2㨳hIVB))NՔڪVe4/ J+⸮+⸮+⸮+⸮+⸮+⸮+6Ċ D"T%lS%MG.7J] \%d4\ؔg3yi~Ŀt蘶5ڏ sX%8ڢp̀W.7ڴLV[<pU Ω,h_u"kZjяds$On< Yi͖%َeZ:l2ֹ/u7G2A7G IҰ 7я8~9aJeq٭PN|K&dǁɼ^Sx&-Lj~PaHdB%KQ-RY'|Ntv!"Vxd 8dޱ]uC =ե!hvN /z`<.JȆJ KDX6uBZH.,d]R%k0EAW,}Nl)69'_B~عs2 m8Q(Ӈ: VV$\ˊ?L_dYLӁЋ(vzHlHx;,j~ pe耞Z}ph ^IJJg$lWn:2yF׍ۣ틟: ?QSOD\R"3$U \] |B ?0 {y=q~ q &8fFJxz.7Y8jRN1bF*+fm),^_Dl7UOxh*ޚL]eUzFTU:$ z%i6wJe"7ZeaRB$4m 55ކݴ=p*2pf XXe +DT>"Caip.m!U %elG̑vATR%1 XlyfSSvt:ш4,iY$0߽l]fr  C~y3UVQNi+*w!~樏iI\[hiT `6)G^nux!9wi4-XXȪ_Kvt. N3wB3enhM3JRT=)&Dk/emEZUEEtJqN+&jkD_/oEi.ă\֮w`kuy1OJ,x7?wݩ;Z5:'.뉂>(K㼅\Ϯ r_>;Z5:|㼅\Ϯ rL}1y VWw`kuyBՁA :!j  &>Z7Ț뼅|Ϯ >Z3Țǝ-X^}w`ouc;Z7ʚǝ-XM^}w`o5c;Z7ʚ|HOE:dAp+Ndiȱ:v#lp:v#lp:v#lp:v#lp:v#lp:v#lp:vP(U`ENǺ蛇Z1G*iAdqvGiAdqvGiAdqvGiAdqvGiAdqvGiAdqvGiAdqvGRZ x(ױ1$E-tyTY%Hi'4[mb|DM1Z"*Y06 %pҾ +TN]tJfxI2Xzҟ[#<H$Q1c<%>H}1JS@.'#Ȩ9w/zkge>/PljRV7͡eP`.c-iwJ<-lq=Rin~8_qW}oCX0ի7p ~`̕<yȖ֋4 %2BhM4Е,AH€d>E/^ڱ\Yl*KRo=ệxlo`4puqb4\b,R=V_)ÖHAHü6pV:}wRH\COω @ IQɄ&biX[c8Ĝ #$l]ệxlkć]gxn8?I;wN>Yw [e ;e6;^wᲜxlwᲜvcwᲟwᲖ%[wV5uxn)]^}&7p ׋oxn)Ju]oxnv݀, w;G ;@6־7pVי]ểzMwxn৾[:kwٯ:wٯ]wyu^`5j j% uF` oՏ)N \A܄( >( >( >(J>(>h>h>I/L=D_0Ch8). }qGQ}qGQ}qGQ}qGQ}qGQ}qGQ}qGQ2 l!F `HWH `@yk_(3㾧Z@j. a& ȦhɊv-p!\YjYF̘'B "fz#r2NѬQ+3F!6jjٔ(#d6fußU|o8$\2GKSo 1+\OT^~)T3PxC\Eʖh*˥D"hA\ՀpSjFiJ'yt5`v;^ {tu`::={٥^{:DD[c.E <122HyPf"I$'F9o2ރg]fD>+d, \0pey8HroMOvNr-ś`_g:Ʈքg;,y i&>a0t-d8U4SRZ!36^&x1 V,r/4SflyWigWCdD )b ",8`Rx@,wdBSʒSX=GՅ&@zD/X{w(^4pdy3Pd0`,oEu_WXK2ɏsmW[ck$G/3ȼ!t.T<0|^2CuJ,uieh%b 2_"tPϨR+^<Λ N )M|y`B3|jR10uiƔ.kK }]e,&1No:N[|?S䬦!^hJ`0{:>5oꗱKqh C<^ey;W:=s ؠ5GQmɡյMrjEȠ\A= HA% al$фFLW%Đ6iA)g*rA<OLBPx I2MH af <3ajR"|K0I]Cޠ,J#ZT*&XLLAJB,?"p6CA L]Qvn)E7"w!nD+^b9.7#A;-nQgl%I !b ɻ5@EQ,[2PIW?qA(J'XCQmea 'U2 8mβx8V0<(k>#čAYڙrV$b~JrxT F=,%=Z(3eȼZ8D,| ZS s6I]%kbcL$@ˤ&CkG,@qII/;b^lF2=!n)HǤzDI#K=D / ?%*΃ld@EF'%.C^ ExS$`e 9q6@Ncf͂ 7>aABCK;=iԜgD&$@fژ\袝eUCesWGQ=d3$x>ׄe g8gpilٵ N.R^BE3q\E1Rz`7 zDܠ e ʮ 5"^$JH޿Hz'`%DxGμ%#4ξ 5H;.DAD&zk?߰ m/Wk3O`% li-LKk,`%%}]!y.NMy)GԻ'^J= y(7 5W xA_ L;|-|_%!מ;?ow^IBzvI: C\ T]L!6vPRHKƺ2< C+veσmp 3Y˗.\r˗6,Xb C0^B3~: Džκ7nY<Sѐg[^zׯ^zׯ^zׯ^zvI8$p ]q Tu}H7Al s)BʅZ<k+^8voIj2>tè%-a :~@XQG₲ȣY$ʕN&d::\T9R8ZJG0Nk/,ڣ>C ec}9hb@ 1N` Bhr6K!/5!:Ʃ$0_רYp,ܲYki(0^Efyǂ{ xe L01ja$'*LA]拃1' QX{qf0# \|L"1uc!iiD%̗&̉BSRri+A9 4/xfb܋[@i>Mp;$ nAJ:xBAeP.[qT6(Po?G.u# qÁ-b}Nk^`F!$hy}\8TfЮt+] WBЮt+] WBЮt+] WBЮC@p>?tLۖTwC.oP'+kG+k*R0U>x޺V/O~^䗞)/ *҅~6ǝ-km&_p 4Qȩ{Ӷҷh/\]P)icXbB{KhY*`3g. eyk C+BCV>|;/RnhUr)e؆V(DEY>sβ7{j?C汿/Z6҅^2o@<:ګ;΁A#Vs;וt% EJ( H$V)e%xJyާv)Q(P{qFVlҺ:{A0U_k2WQ~qW>*T+ '(%s/8iRQJdZ:84T0s^NeJT~(bvO33L+/’&߶~jc\ >iR0࿙j9^YyBRTYTSƨBP19?Ze@njt%h)+-JQ(q<* sd)h`"&!Ħn3ͳN`ܟ.H&'/oZDGΉ lx+͟s{׏`xν;ٯ}5`-x yu ^mz/ ^v^;כջg^>5`s޼e)׫uP%hW[|L(Hu{42HfOB<;jz;wTSuN;wT7UjZVU**) /ˣܞx@|Bv[~]]sPb'|өRc?3N/?G߁!#]Mbɮ(\05ނ"Hjd1vZ BPT* BPT* BPT* B2I0[]u 둏6w^i,[*CeXix>^<۱SWo{'044ߒu}Kڲ̍x&4d CnSNE449WWgIbͼf@ i:>lѦ؀6רy |wÏn˔O}yGyLw>~ J,JcИs6D(yybQEx7Ͷ JBJ3u9oHy!DmͤZW< L.Jiz;mi w6-+@#mͤZW<F9oHys6i\smm ҹ1|@scͶ J;mi w6-+@#mͤt)|\Ȩsmm 6]~f˟8smm =Q&+{=.N#mͤD G^qܸ|;miDG:; ӥw6"+&#!Mo|smm $H׌zݯȲ`1|@IY19G}\Pw6ײrb$EEUB,+L^ek6)iњ6<oqnsmm DVby'f"zDYW8sx:Nݗtbi(&*cͶ$H57e秶-oHqZ[kˊVw3Y9O3չ|cͶD5-z|&I6uڙR_kc6pf&rz[0س<7vџ8)tkI(D_ i"+**<%ԗۺsNCMۮ.]@؉7/ܲsNCMۯdǓl؉7/ܲbS_kc.uϡsf'"$5r㨀4"$:BuCE/7lT@sa4M}bn`|>=!I__tfLd K9p%[A2RM*XOƤSf%>LG33:=*;qPXI:w j;ք"UDBc+q:ԅ >.( -hI5)5n;#΋O[wڜ/]iTcRwuH$.vH=2 pTuzjS#r(qn'SU +Z,1ѻq%2;lŪvju">D]?n,1ѻqSg;4u_J}TTʒ5lor'Xcv'Xcv'Xcv'Xcv'Xcv'Xcv'Xcv'Xcv'Xcv'Xcv'XcDͪ}qjcJw,1e6"ͩۼaE-YHu2ˋ7PPDD_X0$I<8qS϶=2[QC"/ vOQr2ze9r#0L%l葸̞aE-Y DE<̽I\Y0^eROٝޤ r. mF"/6'ZOL#"3 d[I\;Nnѣ}OL#%ڌ%D@̈Qjڳ4Q'lmAq߅-LMVxnǨfDS$]'XcaWH쳨JKD^,r Yʂ~k]M)??~ezOb=OL#Ėa,""?7(<V3rH]ʣO\@7j jڋ^`Ra_KM:wcp%UJc8G%KC7XmKHJUN˭Njgw$ŪHy ZHդZHդZHդZHդZHդZHդZHդZHդZHդZHդZHդZHդZHդZH!r$Y2䆴l d&Hd&Hd&Hd&Hd&Hd&Hd&Hd&Hd&Hd&Hd&Hd&Hd&Hd&HqY1Ԉ%+NK)Bo:K3oTpiR&̲aMVSqi1޾.#K̨ImoU.*/+nf'~)a#3ڏ54DӮӘj7t^Z)bS2Q-Dl8pig,:|h$)Qu=Ħ5ڗU&1DMVLJOF]W{G^mnQݨǕs_u5zeGgҎ%dGmER@u5 32ՏV:V:x Վ |C @ɱW ;w; >xՎHV:V+@Xc取>Xc取>Xc%F Vx,n^g{+~&c{+~BK;{oExh"?5:w¹ 0epyl=L IYYiAӾ$E|Hl -D2, RX!ll;y9}{uz{^R{6cE}/~",Ӗ[=?Ot(|)CvV8{ogpI3 F`EaZ++Ӿ߾g~JB??Kcp<$b$.Dc}?euĠ~IR =ND$ u5^I5T2uVHt(| )R+5P;̧i)J$BV$y4RG?05G:b,VT=LQÎF3}hBi~hϒO=r-.^ >/2fOFxN^Uu锽Ӌ*F%TOq |'i8Smy4v1DS|aO^I3B?՚ʳ$^"L-9 @4>,Gdv"-ysa;)Ĕ$L–3>1\t6; Bʳ( ND #G`E8e ˮأIF?ˎF3KF3x)N%Nr1LQEk2Hc!'<6biџʐlOJM'ZldY$~ q9Rg"m^|!S$Dsqr}U'ڦ%H̒9!;9vUm'!`olY^b '1(CsDD8CX}H[զhCiUm&nK2ri8VY4HMjbyC*m BZR)yl(βuGFEx H9w똦V 4bWnRnRTEF֛SobH"4?RJ)p.el,it꒵lQ侄Vrx|#gb N<+e* e6[I R>mI%:G]]涏aOC فJ~Lti7Z.߂o0Zt^)p̦TK/|_.-o<^iBd 4EQQ0^ER 2 alَȴa '*̡(M H]V 4%e ˮАүw[076cE4z9S/: 9|\mlى]sTh'icn+fFJbJ"*I_VIV Eȏ:My+fFFni[X)(NA.ſ:/*dѪKiWuh**]G3QU T')=* p2j'(#4LuR.画O>лSͩkeT)e?FmPtw[076z*hoSuHQ֬H\[eA/AEnJr@$*܀䨧1653cd**fbO BWrӜJȚdOQz)%&Rwe mj mYP QR]ٱߖmT;iك)B{5(׾,^{5(׾,^{5(׾,^{5(׾,^{5(׾,^{5(׾,^{5(׾,^[ʳOF ́ =l]MNE-#^贍{5H׺-#^贍{5H׺-#^贍{5H׺-#^贍{5H׺-#^贍{5H׺-#^贍{5H׺-#^贍{5H׺qiBN:RY'(1['9@JETcGe~JxPJ GNS Jh>ҌĖ퇘a6TWVBp}Ԥ$'.um\Wɖ~4SmVie\ABKm2*!E+I#L\_irY4NFʅofۋ}I4y%Wv*-E#f wNyJ~Ms$|:5]EbӨx!9-k9GN ͮPyb UI;E9om0?KZCqX嶤Q4](|S*u=C'AZg*o:;+. Lt_TQ~17baיV`zGJޞJͭ ZG,:ua:<+1/a6HE2w2[R3s5Of/p?BV Ȑw] xҍLc]UԆ/iyhu S5NYoj1&85X(t#^< EcJ'MZctRqZnv-զA1tsYrTfN^)|~z˪*t3,.L )u ~3?!yd':z̞g<SžCMt#Ҳmz*|)#h O$o&ˆ>8fg,RÝ,k=Oj d{4Φ .^kVMgO0o O$L^潛gYf5+&d ClȨpoW>mgJ=XȴeoS]Ҳk:`fQh!/g\}ghYz`Q5|'xj| Ϗ|rLJȬS8I9xʆh t,qoC>mg,<O)dz|cytΔ! Z$v=c+)+=zZOYjO)P2fVZa@pbnpoJg,4U 0ҊFt^ 垦hʛdx{7%-x3!֜ŭ:cV`NjzRphw8IFKfK=)[LNy\x{7!whWaZkrUE&0ϐVW!pou(D=(͠Kng5-Xly8LvGЃw;'ޭ]Ẵa$H^ ))<(pԇȵV+k6ꍌU\]FMwI)9\FLF@ TBx* ig}tmokj+C7Vپ0JSCBK:i$7J4a;B 7n3 Evlg,I4&aJ զg$oOxn59'|b{ui=MwIZkrOf]{7'7Vپ1=Ẵ զg$oOxn59'|b{uiȒ56 8Cs$e͡ʬ 7XyVBO>)VE_';ui4x7h[Ր s۬M:̀&a@X w] idxMOd-˴P{ Oxn51y9<>XZkb-/?2~4$x%Z;~Spx!H ޶ jO{)!ܲ5 +%3[|9(*IEY'JO%AGE&K=4>_"tv6$ခ&X28uiGVP[I82u?!sР*b{r#ȟ׫2'.NZN|֩~AoJb`ɘr_*r}3xh!*sj)7"ޔ/feiWߌ ZJMY<%Z1G? |'9iQ+MfNʍ_}-?%'`DS`ֈIgf(&Z9U uP%[QTM:lAOƅl8یN ʛ3_fxZdDq;l 17lh^+-) I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I+I$I$I$I$I$I$I$BfI$I$I$I$I$I$I$sI$I$I$I$I$I$I#1Y I$I$I$I$I$I$OcxI$I$I$I$I$I$f6ۙ$I$I$I$I$I$HmI$I$I$I$I$I$Fm3rI$I$I$I$I$ImꛒI$I$I$I$I$moRܒI$I$I$I$I#Uma6I$I$I$I$Xmmѷ$I$I$I$I$mmM$I$I$I$Jmmm3m$I$I$I$VmmmnI$I$I$5@mmm~rI$I$I$:mmm[I$I$I$mmm*ܒI$I$H}mmm}I$I #mmmm$I$I Immmmm$I$Immmmm$I$A GmmmmnI$I$I$>mmmmrI$I$I$mmmmےI$I$I$mmmmܒI$I$I$}mmmmI$I$I#mmmm$I$I$Immmmm$I$I$Hmmmmm$I$I$GmmmmnI$I$I$>mmmmrI$I$I$mmmmrI$I$I$mmmm$I$I$I$}mmmd I$I$I#mmm>e$I$I$Immml$I$I$I$HmmmI&I$I$I$Gmmm$II$I$I$>mmzI$II$I$I$mm$I$($I$I$I$m-I$I$I$I$I$I$}md*BA5Fd2I$I$I$I#ey%"I$I$I$ImPr/tI$I$I$Hl$=lܒI$I$I$<I$I$E$I$I$I$CI$I$I$M$I$I$I$i$I$I$I$2i$I$I$I$q$I$I$I'$I$I$ $I$I$I$I$I$I$I$ I$I$I$H I$I$I$A$I$I$I$ $I$I$I I$I$I$I$H$I$I$I I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$?44 W7J@o9"ub)9-@fdF^-CI@LF7I!86skWE BKY8& B#F'E//ѧD́ȷ(R j4EF 3Ze+BNх6vcf̼@j\},&.oZUeesxڍot)$XGZQ&BZ`dLFw݊f_ONqI "a}>uݛ9+ԸY=j\],޴潼,>N^Z ͜>}+CS2z,穙d/QZd|=%Ә٫3`υgP%rXٳ.zTBe&KP@LJR͵0*?ŠvݗW =I\aGQ=d3K;f oS>^8/GÃݮFoPfk䎉E#xFy[.cfi/@ Ql(lp( erdS,[WLk{b:D:@cQM:}۵eqbeH~Ksn!%I*}~źو^xjdS$ㄾ߮  t63ʃa:fٍX1n.C V]ѽ$L.&[ǓWD-zP/"ShȠjՔ'e_43ŠH )w^m/s~ QtF*2V (g ޢK; goz@[2*O !V 0z$KqV2%ilTB0 +TeTI ٔ4v.!d. &&ױ,vI(O Z#|%~.6&{5'l@] Hhr$B3IC7錯^zׯ^zׯ^zׯ^$`Ş|A7 @Ԍ𑶤 Y3ׯ^zׯ^zׯ^zׯ8e2/8qg_6PzEJsIWQ{tt47H`Y\lX,,GOhe4T3K`x`/e;7ErJ f̏09nKXB ?I,(G₲ȣY$ʕN&d:8\CSjh,zpEͪ3IPz\(7Øf$ 0S[X<%(  ,&Sq=M<(r >5x$&V>>Y*^#P4RLmPXDК aqJTD0_׈Yp,fY!9i(0] 3E, ,m!%2 :>abHO )3]wvÚ. Pb.xۋ0V C%7_+|q0bD[TB$:KR^p#2' MIɤհH7o5FFZ$!qL2>`# ` t rrIGR#ܯ],%ͷCbF?Ȭ1F sQgP˜թkR;Qd2BpiE#XD WR? Z)(`QKzE#%"^HaSKjYgbF*@FbS݃GN3pQHةRԃq0O~qI.$h[*>????B:RxZ@PA ^qhg46=pߚ/n+̫O]i8EdiÄ-ejWb;ΜIoMLJE%BUE ңe bX۔Z-D M&&$DՀ(0%L$(")v#ƳWĬ|qL^KZxyjdM19鞢(Z}D(,ҝU|q 7䖋ٴuj#5@< HFrX(]\IdI.*AdEIB,pbRlhMl#ӀNgMJo4B 0|i;q4bK8д4Y?5 kD U,2!$JeAh@A;%M75uvVnf:4>n` FQw ܸtP{*QnV Fѝ/J=nR6ٿܘd@ *ś gPґe㛐Ѻ0oP݌L nlLJM)-JbL WLiہE4p͉(ѨDD1ɣ$f*9Z͊sb\ح|6+_%͊sb\ح|6+_%͊sb\ح|6+_%͊sb0 JeZbu1xLNBLAQґ洲ˉ׎Zh2ґ+׌AJu@{׉Zh *obؽxt˶9k-4S k/όׂAM'Ěp5up-7V A5`l0VeS[)5D&00 2jRiʌC؄mC"N j_ܡQ"Ds3/usJ{d#ƾuKL\n/'~U_,Ф#w j3<u*6v1 ɜFu*Os:+` N!93FnZu4SٶoAI-7Hl#SIΠI $X&Pu{R}֡d bl. XFdΐJ$7~\`hE:44_yI2k}F y6sNN[i ӕRՍ3}}r ƜD=ƛp}ƙx{AEeƹcʣA.MutatorMath-3.0.1/Docs/designspaceDocumentIcon.png000066400000000000000000000444251365052737200221760ustar00rootroot00000000000000PNG  IHDRx pHYs   OiCCPPhotoshop ICC profilexڝSgTS=BKKoR RB&*! J!QEEȠQ, !{kּ> H3Q5 B.@ $pd!s#~<<+"x M0B\t8K@zB@F&S`cbP-`'{[! eDh;VEX0fK9-0IWfH  0Q){`##xFW<+*x<$9E[-qWW.(I+6aa@.y24x6_-"bbϫp@t~,/;m%h^ uf@Wp~<5j>{-]cK'Xto(hw?G%fIq^D$.Tʳ?D*A, `6B$BB dr`)B(Ͱ*`/@4Qhp.U=pa( Aa!ڈbX#!H$ ɈQ"K5H1RT UH=r9\F;2G1Q= C7F dt1r=6Ыhڏ>C03l0.B8, c˱" VcϱwE 6wB aAHXLXNH $4 7 Q'"K&b21XH,#/{C7$C2'ITFnR#,4H#dk9, +ȅ3![ b@qS(RjJ4e2AURݨT5ZBRQ4u9̓IKhhitݕNWGw Ljg(gwLӋT071oUX**| J&*/Tު UUT^S}FU3S ԖUPSSg;goT?~YYLOCQ_ cx,!k u5&|v*=9C3J3WRf?qtN (~))4L1e\kXHQG6EYAJ'\'GgSSݧ M=:.kDwn^Loy}/TmG X $ <5qo</QC]@Caaᄑ.ȽJtq]zۯ6iܟ4)Y3sCQ? 0k߬~OCOg#/c/Wװwa>>r><72Y_7ȷOo_C#dz%gA[z|!?:eAAA!h쐭!ΑiP~aa~ 'W?pX15wCsDDDޛg1O9-J5*>.j<74?.fYXXIlK9.*6nl {/]py.,:@LN8A*%w% yg"/6шC\*NH*Mz쑼5y$3,幄'L Lݛ:v m2=:1qB!Mggfvˬen/kY- BTZ(*geWf͉9+̳ې7ᒶKW-X潬j9(xoʿܔĹdff-[n ڴ VE/(ۻCɾUUMfeI?m]Nmq#׹=TR+Gw- 6 U#pDy  :v{vg/jBFS[b[O>zG499?rCd&ˮ/~јѡ򗓿m|x31^VwwO| (hSЧc3- cHRMz%u0`:o_F>@IDATxyT￵-ho t7.T@⒛Q̝ 8N5ĉI>s͌NIr3WE4ˌ$"06ݨlEz?馷sNU|kWu2 CY)@ @[Rzje "VrQ@swPH; ßkxqJ,~b#~$#~O{6ğgO@!~O@Sd@@^fiD uG@ď?GďxG(~G?΁p"d;42 ~G?G#~G?C?G(?T?(?G? mO@@'#~@ď? ~G?/]%GF@|?@?o ~ď ocğ~|O#~kψ?#~g;G@Aď-@C2?|#~??'G#x;:t۫p(K%=|a?Gyք?NL*@@ď 'eA@g O2 Ot2z2՟~@#~ ~ďOG ?G"~?G5OpG#~ ď?Ϭd?G#~b#?'@?C#~ ~ď?G@@':2?G* ?Gg6??G#~ ~ď: #~ď?G)(aφo,~?O}=?irď?O ~NJ$? ~ď? ~sϏ )@@ğ1 COR G#~ Iǽ7G@@#~ğ>'?G'Έ#~ďvO@!@#~ ~ďFP#$u!~ ?wcO: ) ;0G@;AI3W\/X@Dď?G%LnE cG#~ ~ďz?E@#~ğ#4e??G#~ ~ğ= ~ ?G'XbBď&?G#~ ~ď?G@@?ϏY#~ ~ğEI#~ď?'G#~O8#~ď??Gi&~SBCp=GN{-q\z?oA#~ ~ďڋ?CH#?G?G`AǾCf@|_G#~ď?@$L#~ďREX@ďF??G?9 bď?G@@#~ď "te?G?m~\Hď?G#~Oď?G ?$#~ ~ďv@),8#~ď?G?}{2@;R$G#~ğ"~ďSOc!#~ď??G#~ ~OM#~OğEI#~ ~ď?G@HٲE宫$jj2:*+$-E2´rui:4#T1G+$g])#H9 OPxjyZ{l]-j{,砰! +TTPqS*8e-7ʳexl -Rp2E: ר7pmzq`h\]~y:$D@3K1=MO_趤|ߧ/)G?M[ "~R [)~/ n( |\>O?9a/[вe .[6, [o۲ζy<{Ge+% ~fYp{jyVO_moضA8\3|0Syl};86nwFeK |7߬`Zx߷ւw:.Su2wM*<"nwjyVܱCS8\=3nW`bs3Cȟ)~/SO5Ik~w u7<}YWo3Ii# dQeQg]›wMWG?WV˳irw(~~Toٍ@@@oy\~Zfew|PRߝw o/gVyߪz}sۦ~yDZUp[-F%p1b{ܣ+[=[27(:7]If<~yQZ]*ں',[m*g*gi-!vs7͕vkGu̧`T~mz ~sA`ϟ_qVssmG@@$y6nP+т@n8}}+ה?l^4 { _&c?RGpǟ+ݯ|l"~ xܤ+gʴYgܬ/ݤKn\͋N~m*ǫE~sҴYg7-5i<4>fi97 >(Ao-'r>IYÈ$[w"Q 3b\ʕG+;%u\.v`'sqҧ#Ef(vIzY*\ҵnouvl|T7?]Ϸmg[-QqI ޹e"Ou߽~Fdv[f(Cd~I[>'O[=S~,^5kxM,tdf鮈)'>%]W7~7ǹ. Z+ݻ? W$4L {i%]J6ͥ.Z "wߏjQE?Éd|Hw?GiD*|V&Ө̓ #~ɐ駕`7l7G)ATcOͅS7>oǴXS o[!(A߰%஫CV@f?C@Bvg),-tJUG@@lV̇%:%)+W[?Ujnr?+Le]Tʐxꕿ ~*(ٷ΢?D|Xbc&㣮4 lKop)lKaEiA7w퀈&~)= oY"],Lt(!~)ؒAlYuiưOGL?wFZF-AXJK ;OTԿ}Qs1D+Bw0'@?o=Y)?-A '#⏎S]lc\L9@rO Z7|kְ ?S%Aއ?g6 F!aO`3F=d^@'??Sk?+ާboJo ꕑcrb8 ~Od17OB,H\6ew7l"Ws35=co^< 4G=4E:sIJ2o[m vd%g*lF`C˨?wFu8h@@ iE"4'/ ϵ9uAٲZYL @H0Ghqç@t2!~A`<1MXXs?jnþ(0]m?9!)x)cU,QL`a5N@ Uozow]e$4J`aF_qgeDK ]Od,5@[O8KRkzA:6Ԁ)X Fg?([R<+u` %8k$~#;ORD|_+I͖ WXҦV`kFvl?1 0 N͌uGc(YXnqhG Ή4c` +%&Q)R9Qs@@@i/~N@?I8g??p^F ٰCpEE2KgKWȨX%DMo)#2NhgbIƖ?6xcR1E8)sMx ?##tҨXO `t57%$$H} t,iHrQtQ/c[f>O@5VF ĥS 4Xf#F'0N0$ ߁.Epij g /F r-@HR8S8 .>H n;:pzK9< -XKn!0B;?.lDjup##~$Xt&L0u,(.V`RkEhsoO8[)Z Oo\48&@Rd@au\ϑI &X14qD3OOFq%iZXT LK7STFK|r,{OċJ64 S`/#~8Mwʨ.y6sh?'3K_Q\L/@oS[ #&tXtsGPD* s,`HVxiSL {?l7 6q=` 1{FOlj_t;NW"uU| Uz},},u83CV'R|:C2C,]mf#EAvrq7/~?MIP+)Y ڶ]_'@vXhRf 5I3Z?RYQRz2"Ep-S$Uyo1{LXaS Hi;8YȈ %~>E4r?,Jm'S9jHsNe:+?Qb(\Q5k(1m (eHɼ[X @H7K ua6NNRH'.Z S׃Xfr()i S'ĉ׎/'-wS ͙ÛcVjlS50 ?buYâ@R3 ͬp:gPY+?2E ͙ f9DD HI'6\?R #[ByvF_)wQ'Z HV? OwӗfN ? @&ߡ?=kRf:^g^?6B ͙XhNY+tǗ?'@߰vМZX ['Rl:N<8 xr?c9N'"yX|[qXm/~ ~Cs?4)rt|sm ?]>b]ո&C"!uSz^?o3ӷdΔY K0v?L LHmȋRHdwd** ރtLn#SƑBjafCYD-,7y^:+LOCW*\H7`e u(_??{ePCoglTGN{>c@(L>cs׫o;[%X{ݻ3&XH̴ `E#~Oͬ 7yN\Z܎!nx)o?b+{:r77.PgʁcL o)+gzkw 3}o7( ~\ڝZVS $aŃիW|۶){:e[gʛԳr 6b.kΎپ^}S7{6&r2m95?!OwKvGrYe\7tU ?+ZʅA0OaG ,\…Rbx,QC5!(=oT9֢zY9;Rz^|z/>!F[Yy| yea [}֊w =BqzoI+oo6e|۶9f \^…]X e bJ?u+kwveڷ1\ދW ;6FyF j AYw(+:9r|&Y߶sGQ\%ѵ$[۶M=o۶5 Uv *T^nFB?) w$Ҡ}m`- j@~EvaZjuVF:CYw o{<2(XX#~ LX#ޕ]9Y_//ﶭGpyBkke샵x1;X>t {A-j3=7r#] _ДcX'5|C OAy[BA c}E (SPnG ~0} $*~73RދKwވEyOc?IE@ y-Fe~O?'@?fG ~ď??Ggď ?@2?GDğP!~ď?(ď?G@@'??# ~Gtn?RE?G#~ďS^DG#~ďVt|&[ď%uvv1Jdxi噈70RM;}oKEYkgg>jlTcc::T__/IQ+??_̘!IQ~^qCB ?*]Mӧ}Kv5jlԞz˒/LF)--934c ͩUmmӫ]M jTP{OmߡOZnzq.jlLpƌ5􇁒~EHz]5\W_P~* =/C^n ;"Kןuaa ZVra:S\? 466jmc>jkkkѵ^7ic~{뽔`^ע Y/\xY7tZ{O?N -h蹏z@ҧ -0L}3 %dU M'7h퓿е^?]AetX OOy4 L/.MҲ9 "_> gyl}'i Sqb@fgM2=ktӍ7V{]j4kUyT0Zq>t}%u^?QtϹY3@?O66 iӧǶike(7dtF}7B`̑ɌXA඿u E=w2J#Ƿ Eq<(ƺz }/I/lؠg]6sv7zK7xl@{j[Qӂƺt/_,xjq7zf߮ ݽG ݍ `بtu\ʗh:;;_^׿JM}}w6[7I';_f3G|zCj5;t9ק**?huϮ3>d֬WhisT b=IPfkd7g'M׉K( ڦR۴ (EZ xG=?† z'yLrVR=>;2L1v^i i*u{@<<=2Z&-c\F&únG^2|J 5OHnJ`4Mo>oz rux$]gT ʹ vh N$ۯR:{lJW^B!LruQA!=Gjco?]͎Rt!_VuڋT݄=) S iM["?rB`v醷{4,#gT?j ̬ȟɡ=;6o #A! `8KFܻ#9!a&~C?Ai> [ K߹! ' _stM`EwN |H?'ARd?7OoT@Gɹw8wcPgg@(+OιBxNR栞$ ré>F?S=>=- :%)w?ho{ /InzMnz_V"6 a8_r n!~5F+}M=i:+d`1zOOyhe[ KQr.Ha$WZC$~IZz-pVYa)MfC`Oi{v=u7IG-J!Lry-h $HA@1?'~A-f<S'x5WZI?' ~[/EV﩯=0Ѭ?P[ I' 6KXoa4[8Uh>K< 6Ds0Ga#- ֿY:JΡi!쉇?cĭ,@ה Y KL{4? &Tȟm?^?C'vt?"~&,L 0GY?Ba 4K[8ѿ:Ji@H5]_ظ3_`Fְڂ&OJ6ޝѿyBYy@Ԡ4OBc?#~I[jmm&i/e4K,O? IYѲiBYy S `ak'@o(7ߢv&4v6h$JQ5zQK@ݑ'Se[oRG0G K%A^9Fp%?tN)0 i$XЛo2kp$ fx tsS@mp:{ mO ?~` lFcझ]69a66Pgg'czM0<2 BN5IDH-hHn0tc>,a,~E#~o0` Q+ ?l捍(I^`c| `>ƓPV.EQ}O5Xh0`Ƣ-h}bq:6 Sk@oV:I`Oxs~A\`0@7OpBFth` iB,h%`'Œ@!f`D8O AH%8Hs# ~@@@ ~l,--&u)ۏRB8$ &0AѻP\P"LuL$8$&)V?cJkfu3e&: Qd<Ȩ.IS":Fm.6w!~$coH*)*+x(8;Dž hGo^ZUF\ h^tL`@"  FKzm\1@?ʌU]7q-jX; fr\*uOHg,@kc.嶵NR4ƺEii ı^&c 4~e=M@EQX@ .p,j@Uћ_>o)IP "GqTSdrg%Q`t@\:KDjfun$E.S')O8?ʵW_4^` GyM45IZ>5I  tҥQxN_?TteFU;Z X3ET/2z?GHz`$- `C (`\!M2W װE> ?*!]KI<}]tg!C I+zz%tD '3[*R8ZQX (]  A[b?t^3/J0 3hv|x k GsEO'H`Pd ]Z)>XOҘ} [&u;rX;\>5ʶ j]60xֶhMҐ% ~kwɂ@ LFjuSwW5?'d!0IvQ&):|~ .pٺ Ie1`Ij0`3v2 ,{.CpD()ߢ&u ű 0G}~[$.u3M)\z5 PJr oYWIfMYbh ]?eK뚫-AjHBE^Z|~[okUUU֘o!5\J"Ҽ?'@v?J~~ ץ}&b mz?O7!!! /_M,lYh*8~ Nd4n5q?'@G_WUUfv&Wqei,7@0B@<ܴ"G?X@xߡLeUK 2Jf@R?U!̇:J`!ebld#~ 8t`\"h#$܈;Oh__ώ&;ެ3LhE^陋=CG@HQ:wyv睼fP@gYǘ6Mv3 s|   {_}g|6o%4 l26,Z*CLwYgѭI&Wɇ >~ok]wܡ{-.4KgY-GUf1'[Bj R|ʭ[v37Kz_ȑ#hώϞ_vw>PGof/Dί%#/0{G˧B?,+:;):W_u*ŗ_̸ 0S, 0(__"-:q>pʲn1_e`|KE :K,%3#`E#jpA`_F@I\l`fmMZK_ysd7ktgf?~kTiqM?M#}_}jiF+}qKM?#D#G_[o]͛;WK_ odcAo@[襆F)8t G wWtw[41T>?ե/hGիGƉ_;zw԰w@IIgkܹ1xU{cniu|8t..+*_:di >mAWDj.-ٜ/ۍZjI ;,MMzww^LTWWzΝJ{bX{t-I[vV}z2*R]R^E5p_2-F' I ,ǥ'E|~j2>_@@i/TeeӁ:p)UUVRճ۷]5X9[q_:IL"~;?RX>ͭ"?0d{$ ".RY2@@'~cwuuۻw:t@Q4Pg穪jcy6>m=8DC'i[;7Se@5(_Ӌ;k@ 71'A/e9RٴH=RHa=9)h Ju*gڈ,'"BT]9Ce? X"N/Г|WCCAdԤ m#LψGbˀ?GΖ/O9? ~ď? ~o ~ď;A =?G?}?v@#~@ď? 7#~G![ď#~ď#~ď ?G'&[ď? k#~@@#~@H?G_:y#~@;e?GďRj}@#~ ~ď??G#~@@f[475EG"ydnJ@ @ @H$🾊IENDB`MutatorMath-3.0.1/Docs/mutatorMath_colorField.jpg000066400000000000000000002720401365052737200220400ustar00rootroot00000000000000xExifMM* (12҇i `'`'Adobe Photoshop CS5 Macintosh2014:09:15 15:01:430221nv(~HH Adobe_CMAdobed            " ?   3!1AQa"q2B#$Rb34rC%Scs5&DTdE£t6UeuF'Vfv7GWgw5!1AQaq"2B#R3$brCScs4%&5DTdEU6teuFVfv'7GWgw ?FB5dni8w up8=ǂi %ѫ#ݨTln}ux(Fj9FQ-pwDdA()ĶFNF8*#hw[xwb<68Z͆ <&yx=w C.qGzGpV4cS74K\ck3/ώ9]HGwBk{GF+dRrl`\Ӯ{mz 2-CS1!1 <B:iY=eb&Ǐ-;D&^im950Uo !vf#+KMk&xԎn_[t`=Gkr*҈ít]EcHkc\o0OFEƜ1}uΉ~U^q)bE_Qx8&/F|ٵW3 _򶁸j9N'Lߤ>)7)#w#fAG~?] -N~6Adf6QW-mu Ve=C!DF$tH?ܧk}Ï?D*gJo|l}!ǟ+v\{+}ӊ; 8x5KoojU`% ˄#]?IgIK hm[~)Z'CǏ%f;݇լfjy?CU_g6y. mok_L|:nxTZ{k CٍU-+KA7XH+`hx ׹c,kD\aFmw>ZHU`\18ợ7//qin]U@ִD\^Z`2Ɨ5!?ur5ڝLQD9n?fǩ̕T;l r#0qOoK;׮,\g͏[Ghg{G黟-tL锼DΉCf=몿B[Ie4=^߻eշ;9?u?ol7kJg]#stH?d߳Sod@?jR*X:blo3?!IRlnBKh|RR SA!MavInp-ŴC^'p2济o&*Նhx+ @G[Pt 8ip&x8@J@%?y^AEme-sƳv 3~+muAy@: IP5A;ޯzU@@:V=JڲYcdUUt60惹k҅Uh^~?~r||}>E芚+;{Yxb-UU=U^z͟mVi|| t_5UXiU՞t4ϊ3jfFV=[[6?*,hMLT Nh@5aO7Ƹs=fw4P%̱Fej8)1p -j9M֥Lr;y-{^xm[}.}U<ٵu4Cb>[H$4ac2 7Y]O-Hx%aFVox> yt Ѵ{Gejr?^ߢ<;ox+Doa]~LjŹgP{[aF[πQ ;y'@;\9 "m >in>z q>sۯ?wݑ?yޢ!^?{;x" gb?=K~WKI0_r1?V2آwk>J߽E^Sk1w|Yn{]ǁ [7;x!A)YA|v[csýv*8Y+YBO0b[{]߱S x66i*Jzukow]nwQٷUG?"Ջr6;jٱS=ʭdVE+u1ur-3J٩O\4j}BKr!m, <KW\yyc/hv9?Kŀʷ_gRQ{ƍIokmp7Q"8VS,3 մs;~ph'|B{LISaĈ??YKG#UExȩ|~ T;o|ΔCZaUowfq||zv|> >;ߣ<"W!jq߷1-WQ&fx‹6<^?hp<|> 0n0N=AMƃh?<0r +r9],ry^ð#Tu+koc%uXoOV0I?7XF=F,~,ˬ/$\̊SbtwqTls\~KK:߫[\QV85)5U))I=Wpx&?~D9p>a*qqyn|UlsGdj6;aZX:}Wm;~9rv?lq,j5=H}q;ch6ofݟw}!ςnQ?(9\DyBꕁr9ȷ({\ϢDV"C,N1Nx┷NvEgs2[~$e5nYF~"WwϊM5> 9,ͫi^>c54 <{w?7oO6Pz+sۿ]Ǣw<ݧr\;NZX:}WMz,?zk?ү+S#%idwփifG/_U͍GsLnV~gՀȯT&e;,b7^Qd!$k^}8`?oנ}i 9Kzq@?fc藓#iӸ)mwH}7揊s#9"d))N"x?"i ~D)+^[0 zA߰Wqtxptd erh+B|pv+xoA۲?U!>t+{[>䶷+t)sSeFJV>2Sɹ?n)7)#w#fAG~?] -N~6Adf6QW-mu Ve=C!DF$tH?ܧk}Ï?D*gJo|l}!ǟ+v\{+}ӊ; 8x5KoojU`% ˄#]?IgIK hm[~)Z'CǏ%f;݇լfjy?CU_g6y. mok_L|:nxTZ{k CٍU-+KA7XH+`hx ׹c,kD\aFmw>ZHU`\18ợ7//qin]U@ִD\^Z`2Ɨ5!?ur5ڝLQD9n?fǩ̕T;l r#0qOoK;׮,\g͏[Ghg{G黟-tL锼DΉCf=몿B[Ie4=^߻eշ;9?u?ol7kJg]#stH?d߳Sod@?jR*X:blo3?!IRlnBKh|RR SA!MavInp-ŴC^'p2济o&*Նhx+ @G[Pt 8ip&x8@J@%?y^AEme-sƳv 3~+muAy@: IP5A;ޯzU@@:V=JڲYcdUUt60惹k҅Uh^~?~r||}>E芚+;{Yxb-UU=U^z͟mVi|| t_5UXiU՞t4ϊ3jfFV=[[6?*,hMLT Nh@5aO7Ƹs=fw4P%̱Fej8)1p -j9M֥Lr;y-{^xm[}.}U<ٵu4Cb>[H$4ac2 7Y]O-Hx%aFVox> yt Ѵ{Gejr?^ߢ<;ox+Doa]~LjŹgP{[aF[πQ ;y'@;\9 "m >in>z q>sۯ?wݑ?yޢ!^?{;x" gb?=K~WKI0_r1?V2آwk>J߽E^Sk1w|Yn{]ǁ [7;x!A)YA|v[csýv*8Y+YBO0b[{]߱S x66i*Jzukow]nwQٷUG?"Ջr6;jٱS=ʭdVE+u1ur-3J٩O\4j}BKr!m, <KW\yyc/hv9?Kŀʷ_gRQ{ƍIokmp7Q"8VS,3 մs;~ph'|B{LISaĈ??YKG#UExȩ|~ T;o|ΔCZaUowfq||zv|> >;ߣ<"W!jq߷1-WQ&fx‹6<^?hp<|> 0n0N=AMƃh?<0r +r9],ry^ð#Tu+koc%uXoOV0I?7XF=F,~,ˬ/$\̊SbtwqTls\~KK:߫[\QV85)5U))I=Wpx&?~D9p>a*qqyn|UlsGdj6;aZX:}Wm;~9rv?lq,j5=H}q;ch6ofݟw}!ςnQ?(9\DyBꕁr9ȷ({\ϢDV"C,N1Nx┷NvEgs2[~$e5nYF~"WwϊM5> 9,ͫi^>c54 <{w?7oO6Pz+sۿ]Ǣw<ݧr\;NZX:}WMz,?zk?ү+S#%idwփifG/_U͍GsLnV~gՀȯT&e;,b7^Qd!$k^}8`?oנ}i 9Kzq@?fc藓#iӸ)mwH}7揊s#9"d))N"x?"i ~D)+^[0 zA߰Wqtxptd erh+B|pv+xoA۲?U!>t+{[>䶷+t)sSeFJV>2Sɹ?n @ICC_PROFILE0ADBEmntrRGB XYZ  3;acspAPPLnone-ADBE cprt2desc0kwtptbkptrTRCgTRCbTRCrXYZgXYZbXYZtextCopyright 2000 Adobe Systems IncorporateddescAdobe RGB (1998)XYZ QXYZ curv3curv3curv3XYZ OXYZ 4,XYZ &1/Adobed         s  s!1AQa"q2B#R3b$r%C4Scs5D'6Tdt& EFVU(eufv7GWgw8HXhx)9IYiy*:JZjzm!1AQa"q2#BRbr3$4CS%cs5DT &6E'dtU7()󄔤euFVfvGWgw8HXhx9IYiy*:JZjz ?SmNڻWbL2Q`Jm۶mbG_ѱi϶BMckn>؅[~_[WZ۱ckn:wiCyZv60{+Ď bT0Z6mQv2$+Zv6x" AS鍻 ۱[v6ibu2tһbyn<%4#d)#zg)kfG6_{+_ =?}fGr"BXbv$BCK#KnSWcĮĴ72H Odx"Ljd_׆2P~&RLB7*e;)-;)-;)-/X~g+DQ;leicV %S#c+vWPUثWaT`I%G"b]2/F#,=JWs?~4~i]Ҳ %mM~m.dfZw'7ӽY?ĚQ̲[-oYNH Ai"3OͿ(ү_{<0W+'raW>O돃ÿ:\|=KQz=Y^OZfpMs?~ky N~Po])ԳL w|2!+IeqbDgǹ]h \|8+{-돇U_}?<1F*S_Vk\~Sr#_.xK?1w}]_Vk\w}[[gpWw޼E/5#] m1p?lqos\?~~$?w#C~~$?w#C~~$?w#C _xP7[կ]=$\y47?1jq5这'[Ogb?2k这'Zf}y-S.?ůgb?6k这'Zf}?~w:u2X\ެ?}яůg޳}OMb=}OMCz?y?w_1q5?ů'c?6k?ů'c?6k?ů'ތDz]/.;ħ+:F?y?؝D{Tۏ/C:^a .nv*U/1+2l]KE]ØU-O=̓6_1(UUes3G|s,JY(+CZ\Yq}e?a7F P d 1H5aU.TmaK PrՏGx` 掍®8V0KWdثW*UثV*]y`LW&C"ثWbg!Fh+2 ]vTퟗ%V+WbQzgeVvv*Uتc?G"PT0+WbQv XثWbQc23tܘK2Cb]Mw_̌?R<`UثV1![6CWbRo,IKv*v '_5x/vn]V5]ѷj]]x?_8~frv*oE>a .nv*U/1+2l]KE]ØU-O=̓6_1(UUes3G|s,JY(+CZ\Yq}e?a7F P d 1H5aU.TmaK PrՏGx` 掍®8V0KWdثW*UثVM.0A&S*, EWbQ27B,%.]Tퟗ%VثWb3?o2yZv*U1?#(*ثWb_,J UUت,Y1n,]]R1:Rv*C2x ٴ ]Ro,IKv* 67|>/ (fUثUko3SGn5|6; v[qqs#O͌CWbU-ވ_ג!%b]1BY/y%fMivȫrs ǯafK%s @v#] {h/zNeK3ev(v+^˚K.?o2rAfrq]<O̟!sP3i #b? ?is3OJZz գѾU׆ s 쒻v**UثVL?0&S*, bi]+G<Ddv*Qj}%l.]EiQS~]Wb]:s_$r%C +vDE%w]}WlUE//Fa .nv*UU,XC'L]+ U*fIM)%(%*9fi fl4; WbMq}e-NH<8vWW5Jd B6PH;URyf^B s,z;)=ZVo*bUouv*Uثov7T]bbݿCU1W}nX2%ꐟĆ8邑HI,P}LxVI?o ӿIG,P}LxVJ?bc´]E }RڲE]&,dJ'U2,iߥX;|S~Occ´o!sO E"/TX$?|SwH~<*),LxUjnM-!{Dǻ UߤY!w4H~w4H~imߤY!~OdcKiX%z˿|dZP /׍-;$?|^4 /׍+H'C5JEkąYaSH/5KN /׍-;,?|^4բYE+/e / ,yLiߤYaZwX~xVA_MѢϗ,,_|^f0}}?/_}}?/_}}?/__'T,_|^l,}x|5wYb__ekW}}/_Ʃ֣K4v%lɽK%?漍;%?miߤ/)5kN%.OTM ~fU?So"lwW|E5c+J/]UXKO[.Mؑ]^N|GՊx]wG?z 64Zẻu t5Gfnϓ\ٿY;?H}J<Re$4g/J&I/ӏssrOIi yE9@LNO[9gf;{KۋxT Z5:_{#qy{e$⛺KU3iĿn`@_Sw,tS ?/7oKU0g/)FbeWkhI1ԕl^aa,i+U3? qţ.4?ZߦYaS_,?Wc0~8*ldk V^ WWR ؿo2c_R!ҮrݤIG׷H-޽E55$_|^?n/_Gz;ַHNw&*wyK^ 05%C1,_|^_Kȷ}f/ek,}xrr0,_|^_V5o1,_|^>_/׏/+5,}xrr0,_|^>*UثWbG0ɔPVWb\?oYJ9.bk#$NVثWb7?o4 լ UثTP{+Ď@]v4_(+!ثWb,7MK2;vlCNdVfsb]:'U\نJi]]T27K4oQ%ثWb?1|+&WbQo`݅M.]zr.O:dv;],:?+ɦj_oӋ켛Yg&st?-Q k\UW'//F//F//FyyS1N3/E)w͸"-U'g|h~j9mm۱B3X2e>R]®]L *`u1u1u1DX?2IHC]Lm66#Uy(e8>`u1u1u1u1u1u1u1u1)UqWzzgoWw)UqWzzgoWw)UqTMi޺yAa U J\UޞDU]J\UޞDUQzЏZn>>,u0 ZgoW-:goWE:goWZut%4*ͧs?FAZooW*w-77_-;E/UƖMM"KH4ibQ&BE/UʩIUqEE'Umt)?+nKUq[M/PޫѯYVY!RL :_SWqȥһE/TQP`cE/d‚ұ"`WzV?RLxޕ_SwcE/TǁQ;c不-xݙʼn \l?ȥƝ/_.aE/Tǁ]/_";K!fdiRaE/י,[)xUհ~"kDž][)xU}E/TZ_%k5@zZx|2l?ߒȵʻհ~K"kPV/_[/WmP#SO~ Rߒȵ wcE׏ ֱ~K"kDžx]X%k5xSh%=6MK~_^cSg K/-ix]Y%k5KR/_^n-(vuV97a4oi/ט4"V_^z'xմy?kxP}.}I*<a!|"Ž /)oFz_RL|EweE/T⣁ޅ_Sx_+j6z7, d6_G.JWrzurD|hpmYE][ykS 3 éB#0R!OoTj>;_SG,=p~C}j:cO/&Ɵ0kj:cIAN)%cS:h~w=ϥpg˚^ş7iKkKU3a.E'U.E'Uex[E/UexQ5խ+,~wřHc(ӿ߲ȥÅ)?+zw_Sϕw=;/_c6oA,_OPe%XKU3(u,ȥZu,ȥZu,?ȥ|Iw+KU1%ܮeE/T8w-7/_NpK"cœi,ȥdZWK/R}T~ЧI2|'e ,ȥh3WpK"dw-;_S,NeE/Tœi?RLxw-"/R̿~.̧ $?RL}j/_ܴ_RLxw-;_S,NeE/TNj'rӸYKU1ܴ_RLxw-;_S,NeE/TNj'rӸYKU1ܴ*UثWbG0ɔPWbQR27B,U v*qUk_~Y(ثWb+?o4_0vv*Uتk?G*,WbQ܏ U2Hv*Uت!,3O,]v(_nFAP]vY 0v*Uث*,,KCWb8m'_4ڿيثWb7[ѻTn`#ءث*ۯNgh3cyiv*Uت./@] #?߯:\@pg WC+xBey fKɌiL]LU]AqaəZ_S͛WuqWvǪuc櫲;v*UaXC(}!v*U /_Lҙ(ekv*UثWb]*UثWbG0ɔPWbQR27B,U v*Uk|3c$FRUثTf)*+` UثTP{+ĎTY!Wb]-'*PUثTCY1f 2Xv* Q6!'c̍?Ԃv*ٵ` Uث*,,KWbJqiNi[WbQo~vG{CWbUGPm'S3t_[4v*UR_ז Πr .]]R߯:L@pLePUQ3cXK2v*Us?,?3+K1*9`Uث꫇cv*Uت"ĆQ Pz Wb]"pɤp})*9z]v*UثWb_*UثWbG0ɔPWbQR27B,U v*U^dd"rb]4yM&U[`Wb]s_$rv*Uo>8S v*U?_y!%Wbkbv b]v*UثWb*UثWbG0ɔPWbQR27B,U v*U^dd"rb]4yM&U[`Wb]s_$rv*Uo>8S v*U?_y!%Wbkbv b]v*UثWb*UثWbG0ɔPWbQR67B,U v**l9'#$I|rkw5ƕWs_iQzS:/T2`C-;ƖcKN1P#?G*!)]\V\V\imn ϾYTIPKNƖC-+ɵ?_y1C2iHWZuqE CN_9LiZw5Ɩ|qO()oQw+h i]GcJZ?Bw{$\cg?0U<^DO׍-;O׍-;Oׂ8XiV=e6wᘴΝcKNc1z7M?oɿ߆kpo &߆4^?0"]_OU5<N&f2 fߍޛ~#5wǍx]鿇1^KxEAkxr R?ΐj#Aw.\?8 sw.\1p}R?5^O">{t8up,?ן\`.yW/]6_u D]A,P٫;Tl!en | :| :| ;xc/$T?=߱d9Cf`q = =a-!q'dn-|.x]'?O7Dil!D4xON0iii܆-"oOPɤ>Z9 v*UثWb]*UثWbG0ɔPWbQR67B,U v*lU|?hI[+`UثTf)PU2ثWbSC9AJ;v* ~ח!W'iv6ڪCeǙZf2CfJv(VCNdi(\϶ڻVCb$@m]v2P^Gg;78)md;lUbgOIm_ɬlv*Uت3Ov}05#3_MbǢJRE:oرbRUVudlJMnl]]T__c_Hk+rCv4ƒǧ`fH=(@]-[v4imX7ffFzs m8]Lm.6SMZe~חn;ccc4ZhC"Fʧ@X;;v6Q'aM&fi(e]]v*UثWb_*UثWbG0ɔPWbQR67B,U v**~_Ub]4yM&_HثWbSC9AICb]>^dCBI.]V222Xv*Jx?CNd`PثWb1AO6]Ĥ'dYΊDv(v* 'gNiYrrk1[v*ݟG~o]t%'2tS%YbUثU[_AJMv®]l޿70+r.]P03gULCMI>ܐ9P/9-l-u1u1u1Wӿk O6CdUثTma)Zo xfB XWb]v*Uث*UثWbG0ɔPWbQR67B,U v**~_Ub]4yM&_H;v*Uتs?G()(|PUثGpÖrHU%ثWbY1^2BJ]^.>&gv*UثGrPASͣKCWbUؔ2?K9ٹQȖN]%8O1|jZVb]4gs_Ff{Wb]'u̝7IVlv*UVuy<PRf݃Wb*[7/5j]Pz'd͞TeثWb9hfF7$)=Z]r(Z2?PaY;v*U_Mf$1y;$b]o?OL.]v*UثWb_*UثWbG0ɔPWbQR67B,U v**~_Ub]4yM&_S)`v*U9?#CWaWbQ/HU%ثWbY1^2BJ]qwL 3=WbZ=Ӗb m-إثC) ?dss)v*U8O1|j.V.Mf+cWbQ9߁UثP?KN&ddfػWcjmUmޘ_זbٶwv+ [73`V]jl}(@fZv;+Us$̝?ӕ {cmb[Zo` g]Wӿk 0vv* Uyf^bT6̄5wWcqZWWWWKY&o1W~ԿowKY&o1W~ԿowKY&o1TME0c$*$-*њM"b$-*њM"b4G| Y 4ڻt튡Fj_7銻f,ȷFj_7銻f,ȷtKD ~Կo!L)ߣu/eEƖ7RY[Liiߣu/eEƖz^-ZQp;`*UFj?7o锵~oW~oW~oW~oTOk[iJ\#HUڇK"`ӿGj/o-;v,ȶӿGj/o>O^?,CYo钥wCYo+G/Li]?PicJ5ԡ_K.S{f^!G4 3&}KW~icJ/LHUhloUxn}D9zG4 39ߣ_G4 1W~ibm>^,/_< 3fN g6_< 1Zw/_ ]^̰HAB6Mϊo_ȔӾ},6+NKح;7/`BnwRĕG\V=eɯ^$lŦ}J'cJ^$li]+ğ *3O$-h߁;ğ-3þw4Oһw?[b"IgwKIRAŷ ӏS}BYo G },7ׅPyck_< 1T1o ֿ_OH1J?Gj/A6<.,-( wCYeo1~?_8 wCYeo1]/S_omʹ2Ç_ zM"e,;ׅߢ?dEx]/SI[L|X E7Əzj^Ui1$F~CfK<~տ 3wWXO0~wWr2K̉KIuhٗbI>Z?9[L󸿝I| ;V,sȷ;<;N,sȧw%gZ"c_$'T9[L#[dsEj?q>+S9[L;+Oߢ?cE¾+S9[L;+Oߢ?cE¾*UثWb@/aL*]v*9i1b\UثWbv*Uت7HeIDljw]}WlUQe bvv*ZX]_s[0Lӆ2AQbUثPu/ 5WbVI|Yzͨ n®®T2+q7rQsbN+*TZ&fW+&W`RT#tgs]ѿ 75Uث&b %-ƊCEڻJL2'K yy.]M-=2c"kZv%cĴFJ?[3'fF9@ gNNJKNNJKNNJKJ2G-) >[$ Gh!3đ28ˀ;)/vQ2hCv*;v;V{ h SKW`WbZzLm4֝v;Ҫum|4C&v+Niح*_FL .dPUخ8}J)nd2خvv+o$1]ٔNƕһWcJ{(D>E)v)b 7\ Xv+nmح*UثWbG0ɔPWbQR67B,U v*UruPWb]FoL rAWvUثWb?йĎP[ثTOF?Uv*UتAǙv2BfQb UT*]!'˰H(,bUثUv#mSWaWbȕ 27rQsbd]Wb7O174X5͎] PӿݟGv~nkðW`J\!'S-ƀUT;*?d?XclHvN]J2bWbU[C$b]_FL .KWbZ=2pB]v*cD G5(lUثWb׿/bMdG"ɔv*JrlY4v*Uث*UثWbG0ɔPWbQR67B,U v*UruPWb]FoL rAWvUثWb?йĎP[ثTOF?Uv*UتAǙv2BfQb UT*]!'˰H(,bUثUv#mSWaWbȕ 27rQsbd]Wb7O174X5͎] PӿݟGv~nkðW`J\!'S-ƀUT;*?d?XclHvN]J2bWbU[C$b]_FL .KWbZ=2pB]v*cD G5(lUثWb׿/bMdG"ɔv*JrlY4v*Uث*UثWbC_LU v*U's`"PWbW/\Uv;v*Uo&2WvUثWbޣ 'H.]L?t^ec5v*Uت՗X7NH<,]+v*by:;WbV|Yz 65;WbC%C"{'%G ثWbVW+b;v(v*ӿݟGv]cN&Y|;WchckJ_d?Y(ZBئӱWb̀I]v[Pv4V*?)$˱m.]zd̔v*U^D6eZG'b]V_ms'Ȋ$ثWbUcHk,KWb]ъh]FDC4b#?C"!1W~!&q:w#$=C~!HȈUߤgHD?F*3$?"D?QT#C~!HȈUߤgHD?F*3$?"w ъJaI\,?"w+DC4b%qȈUj~c19$UI\E"a2jw+L?F W~D4cJW>Și]J/э*}{0bW;1\-_b18ȘU^b18鿖/Ӏ~g, RFy,mg@س'q}4%9/%;"]կHuљ)=K1/_KZZN_Η_KZZN_Η_KEеWY)cB+\9L(6ƾ7"bsqûo0MȘii^b18Ӿ7"bq1Z^/gу_#/RKH\E"bs;1~D41 N!si´>ȘxV=ڭֵ#n6JPѕNb .&EƬ K6|SM?w-Շ˟X$???soysKJ͝_>\%R?ҟiŢr/IԮu[;yt.( U]="97?Ml5k0ĐW),0 0P]i-ʈJHxG"b1KHxG"b11ZwL_F<+N#qh´%ԭ #6'Q_bqijgNDŽ*?Zw<#Q8(&TN ONZwi:k]YއN|c.A!3Ja?ii4fŏCy5 x_5 x,+3^ۂȕQ츌Aӑ:wùӏ'ޟ.LN?zqw4}C"cqËӃƒx-~AӑٌAw gD4o,Ș3>A-ӏg޾w gD4j5OuYko"br3Y-i;Y-i;Y-i;𢋴kyyZ[60M_Fi~'<~,V"/eGm""q^|~,V"/d41Plm޿\{Ԙ/NV_Gi)f#?/~kH8)f#wӏn?/~kH8)f#TtK QGif#,tOZ<_ND~?,FӿBnx3w_. պqTG%|.VOGiS7"к'[?/LdW*M B%Im x{`jG%8bV?Gi+w!ߠt/YmѵثWbT[?e>%-.]z_N]yPS>lt ݲjW`T~1P%C).54$UثW`T?+?vLjb;v*U?!Ob>bػv*R(? DL}WCWbU2HaDE;۱[v+nQv!b]ClAثWb/oOsT[7b]U-_nVNiثUK_/|r UثTdF i܉b]O"XثWb_*UثWbG0ɔPWbQR67B,U v*UثWb]tyO&_AUCSWb]C9[ثWcK1=I09]LFtOyAqXm6b]R=n3v*U?9t@Ϙz8sxIAثc\7T28_BO>sQsBO]vNM'd6.^/lv*UثB @ } qx3JWb0JdX%yv*U_O{*ĆNT[C+Wb(_`]CWbQAFlVثWb/oO橕ثWaUKG#$;v)v*kUʘv**OϛLVO4v*UV/~y]v**UثWbG0ɔPWbQR67B,U v*UثWb]tyO&_UrUثWbP{+Ďb>v*UO7buO?SN.ͣUثT[gD%-.]z_N]yP3~gI4XSoQwtNUP|)~m < >I]v*'gNɚ]gɬlv*Uث,B @ } pxL읊v=9?-;OkQ)Jv*UتT 1B') ]tݯee+)v*Uت=?h??6͜ٻv*Uev4ƕz7*=agRqtX~mb]Z:'dqDzͫ7aKWbT-cu2Ssb]/q'&)]ueoΒHi[O|]O|U˜G*8Noe>r\|Ú|*UثT?+?vL?.^.Mf+cWb^aNf&cgdv=$dm%2 Vb.]Woʟ!(DPUثHGGdWbRUثTzm87v*Uت2xSy(橔ni]+BLٻv*URuy(sD*L]]vdFF_S\VBv*UV/~y]v(*UثWbG0ɔPWbQR67B,U v*U|k⪞{x;O|xޒ{ļN%Fi1/:/A*Xk',4k bW7ޔ"ҸK^9\4 omOFԿĚKweO&ReXS|744;*kIԹV?_PDXli&m \fyv*JX5==!'S/1(?A#'wSG#35`[v+nJ*>?LI<%# CkWbUʟ!Y]v)_KdW`KWb&AWbQ_K~ȞI5LkWbWlADel-حخD_Ú%W*`9;WwbO?5 +nmإV/~yWQ]݊Wwb*UثWbG0ɔPWbQR67B,U v**~_Ub]4yM&_K'aWb]Qe$rlWbQ/U\v*UV/F?Le}AW(mv*UتoS$0h(rov(v*Uؤ#c~C)-].]x??6M l݊v*_>DLye]]v*WlADelv*oE9\r#Wb]*-gDECWbUb', v*U*UثWbG0ɔPWbQR67B,U v**~_Ub]4yM&_K'aWb]Qe$rlWbQ/U\v*UV/F?Le}AW(mv*UتoS$0h(rov(v*Uؤ#c~C)-].]x??6M l݊v*_>DLye]]v*WlADelv*oE9\r#Wb]*-gDECWbUb', v*U*UثWbC0ɔPWbQR67F,U v**~_AVV]LUQO</ZV-4UثWbƣ 'HWbQ/*I]v*?e#fi2v%J]!'S/1(lΦ.ƕһV쟖Y mvE]PYM͊E.ƕһ16왦e]Fϣ]pnv*Uؠ?1/PT)mvث]*c[T "TUث!إkQl %ثW`Tzmdgwv;.ee'|Lye[Sk]vUx?϶FTlev;*PDrcWcaQm?'D`q5;+]vUXM1+;1v;+]vWմhDU]m;Z&)UqW}[NJ\UVӿoWwմhDUQv-2q(0!_Ř6/_6/_6/_6/_bt?2KCk?CJ\Uߢ?_WwjDU[a-RȕtG*^ jDTŽ~JLxWߢ_SwjDTDžxzffĔMoIv+KaE/T5}R/_XKU1K)>ӾaE/TZLfUJhy2\ E/Tǁ]iRLxޖE/Tǁ]iRLx i6u/dUfKU0M;7_ӿ߳ȥM;7_%?tyJ,?ȥRLm]/_XKU1*Uh_k꧄~Q_S3cE/TƕV"cJXKU1wl޼Kze*'_Amng-ȥ7Zw?hE/Tu~?RLwW~?RLM'z}&w0ioLȨ?P֟'SW~?PһuI"TSxo#jL\>i+&GCȱU3ߣXGCȱU1W~cb;O߷}~~gȵ\ {GE׆>-mmߣ"kcLǿ:sS$}%# 7m~k֝:Zw??miZO^@DQ"~a^Bה] Zw-?miP?}Bk֑k?`YI-w?m.׍;|?^6x-iO 6+#]>/U>/]>/]>/]V1eov @;}J/L(߭?R#;Q[\M'|VQoiJS#jlÍS#jew~x8߭?v^5ZF&{璅[5o'^xD~'n݃ew;/-+?2&5SO܎wSOv^7}M?߇qxբN?D5SOlǍSOx4~SOx4~*UثWb?b|UثWbd?oX*UثU8*UثTN7bM6*v*Uت?Qe$qT>*UثUD#[]v*|?_yJYUث%EYwwG/bT81v!]luFY Fdث+ ?dossb)v*UتqiNi5+'f+cWbQw9tr0sZ݊vEXn)PV[v*eA DeUثWb ȷ.ڻa$p];v*Uk̟'l]Uث[_ $U0bV݊?aÚ$0v)bPU߳FFSXk"mح+COdK/ثWb_*UثWb?b|UثWbd?oX*UثU8*UثTN7bM6*v*Uت?Qe$qT>*UثUD#[]v*|?_yJYUث%EYwwG/bT81v!]luFY Fdث+ ?dossb)v*UؤVWrq}.Vb]tgs_aF7v*UaY+e-]V{ Fc;;v*Rv!ȷ]]L"yTě#6p];v*Uk̟'l]UثU}J݊v*X~0Vʘ;v*Uت-gUE]v*??,J ]v*U*UثWb?b|UثWbd?oX*UثU8*UثTN7bM6*v*Uت?Qe$qT>*UثUD#[]v*|?_yJYUث%EYwwG/bT81v!]luFY Fdث+ ?dossbHv*UةN"x?&b]]Fϣ]hov*U؄"?7\,b]YDcT"2b]B>?2K+WbM8.v*O6 v*UتQ%l n]_?^J<ԫe'b]T[~ MAb]COdKb]*UثWb?b|UثWbd?oX*UثU8*UثTN7bM6*v*Uت?Qe$qT>*UثUD#[]v*|?_yJYUثEYwwG20}TĩpbBv*>ѭm]W`*,fG"UثREZ6M_1[v*ӿݟG~UثEXnY+e-]V{ De]|ݯe%-`Wb)L"yTě#6p];v*Uk̟'l]UثU}J݊v*X~yVO6]vW"2Y]V쟟Ȗ%~.]v*'ԥ~C#W}J_?>wԥ~C#W}J_?>wԥ~C#Tmv ت՟k]g~#W}Y߰UV>G-~z4] V Rf~C#7׍GE5kn!x)*?3z_%%[GC5^'}Qߐx׉T?:5wkǍx:}i;vlxJ_GC5|@wԥxt?^-R<_:-R<_:-R<_:-'7eD?h> JGC5wGC5wGC5wGC5f趟mMRSP;)ג5 y.JSTE?*P^fqqWʂ7_4ӊTE?U*_害:WuѹSYPɂ'E7V2c?o?ՙ!ym]&*XڻMU#%Q6z]澞?{</ӟSMU#>ػMU#wGE5byU^uד/PR&?כ1&~GE5W~GE5Į{kĮ{kRPiu1:j~!ݳsb*?ՑU6/W~F5cjWؿmJqy;QD6c~l?.V/ߢ<#YwؿUߢ<#XExG#bTf^Sd7;F^* F^*Uߣ/??dV*їʟ2?RQ6:u3lA|G]x'O)w5cKnu1?imߣF'Ս-YBBHx O_1:WF'ƕߣb\i]:WF'ƕߣb\iQ^+#[*1mwॷ~_84~ Ɩ:ЈQve .Y'}BW ?xWPUOw.jDžxw) Rk vbe_9 ?Uxw_4?Džx9 ?<+ƺ;;ceO뉂%5\x%nǀ_ W <+\x%l! }KB;SV UÍS/Džx9\xWS/¼n EIZ(+KX_S/WS/Džx9\xWS/Džxbue1`dI/Ž7}RKq^7}RKq^7}RKq^7}RKqG*UثWbCOLU v*U0_E,dcn]Ug,d;v*Ug1M&^ACWbSC8Wb]Wpuoʼ$]dڧ>]Juc&?A](?ثVרPR@Z݆ڻv (O.ީoלةdRUث)_'_4 o1v*ӿݟGvb]UYl%+eM]Vz ?1{W|U{_r]QsG 7NƕһTE2`.eiiӱ}}FFӱcKK?`7AVʩӱbVGa4ƕ)Ub'0ȱv*Uث+*UثWbG0ɔPWbQ<% ]*ֿl2ADl]v*?ޣ/7vv*Uتq?G mCWb]+տ8?/z_3a^{Wb]mWP.}C%e̴;v*J&?fFY.]l}<PRVbb]vysbKWbĩMxo8e]Fiϣ_!ثWbco2JGb]YpEaꂊ:7v*UkAl (v*UTdEż*v+Niح"-eMB\GGb]t_/ĄIkv*UتOG$v*UثFv_W E.]U쟞D+,]v*U*UثWbG0ɔPWbS\?oYJ9.]$NVثWb3?o2 Cy['`Wb]s_$p>v*UB["'36%v*Uث& g?Pf1C+W`*Q6!'S20}LJ1v*Uثc mXb]#% 7K"b]RZqK˕o1v*ӿݟGn75CWbJ*Ǭ7\e+e ]Vztovv*Uت:?2`PUثS9r yn]D[s'\GGb]> $ȵv*U|?'JRثWb_22Yv*UV/~yȱv*UثW*UثWbG0ɔPWbS\?oY1QȱEJ?,ػv*UG1M&_Ho*dUثWbj}e[ثWa rWdثWbU'˿PCnd(!ثW`R1: J1v*Uثc mXb]#% 7K"b]Jok6+b6;v*U>G#O!ثWbcob]YpEaQYѽثWbQv!av)v WcJ5#/OHf6 i]+D[s'ɮCp*GGb]xADdX;v*U|?'͌r.ثTgeUP/k »ت_d%anƖݍ-[v4*UثWbC0ɔPWbS\?oY0Q"vآݶ+n)k]2chWbQzgelcju1P{+ĎCl vث]k򟚵/$\:=nU#ymv*J+Te͇%a_0Xտh+[aԱ ъUK [aԱ ъѤjOkYOau߿sC'pLuc&)e;lUbMw_̌PbV.vث\G'*fՀvث]*2P]S߯9܎lTȥbvةMxo8xb[1*Wm4gs[is\;v*Bzu~RUثUk?ȿ,# +17v*PR av4cJi]nS9 (NƕһTDč7 kgaS`/`!0cKNƖ-/X~UrcxS] c¼.¼(5cK]/UO ec{\ixOs/ uq=ή4'.]uqT e2ݶ6m.7B,  CcknVퟖ _+NڻTV7|m!~TJ]ڻWcjG2Y!Wb]//$ثWbEdff)PCWbgbq3#bUثU mXb]W`*_%ةJ]v*Sx?;oN>i5+һ1v*ݟGn>٭r}WlUo?uSAG*lv*Uتbb]B+,ð%һWa|bM2QSScv4ƕP~ƶ@gNKNƖ! 5tiiӱ[_/7A [dkv4!cK4!cKx?OJCvPT5>^^GZ8f!cL"ix"ix"ix"i.]v!S[#ɔʋ]L"\?_Y\fAڻUbdUثTV)bREZWb©q_$s%*UثT} *5v*Uت'c'*`v%]NޙoלةJ]v*Sx?+oN>i5+һ1v*ݟku?Tvk\b]Uw7& (M]Tz"]^H!K1sCWb!a,ð%ثWcU&F\8)dv*UP~Q(:3v+Niث)ӱcKJL_lO5,[ ʗ?# 2 Y˱Wb¶Y enƖݍ-[v4.]v*y`LTX v*UتarbK2v*Uتo?|'b]yM&BWb]*s\W,0+WbQ// \v*U1Y?1f)PCWbgbq3#bUثV1\ڰ]T'W_LvnlT,)'4 ]]F?5Gfv*U؅_s?kTUثUK/ХM]VYbb]UTCn%ɜn]T3EG0fUثW1kcWbUmޘ_ג62Rnv*Uث sR?>lKVeثWbk]v*U.]v*y`LTX v*UتarbK2v*Uتo?|'b]yM&BWb]*s\W,0+WbQ// \v*U1Y?1f)PCWbgbq3#bUثV1\ڰ]T'W_LvnlT,)'4 ]]F?5Gfv*U؅_s?kTUثUK/ХM]VYbb]UTCn%ɜn]T3EG0fUثW1kcWbUmޘ_ג62Rnv*Uث sR?>lKVeثWbk]v*U.]nG H20*)ƕثT/ELlĴWbª}9$"2һWcJޯ/`H_Uث G2钆4]_^>A*5v*Uت'<c7V2S̠b]lCN_bVlb튻b>ثk-=AW60]:ze]^ssbdPUةMxo8+һ1v*ݟkuQ٭rv*?A\rmv6][悥M®]Ҭ=r PUHTCn8d7gnuc۱mxy]?SbO ثWb=0RAkfvv+]mޘ_ĩb[C%ث+H ,d-vv6Z8 `NڻWcj[in&w[iljmYo?"/%}ePȈx}C"a1MW:~a GJ{JH%K-D4dx۾L?F?J?9 JϬ?&/#Ƈ}`1+PȘxX?L_F%U?/pJI }"brzi޹}"bqwpȘmQ:tPrteĐpȘ术oӏ]pȘx޷Wilj]pȘD8 w>(ms:ңqȘ*W+D4Wz\_&/w"bqT}_J1EE4/ӊ}"qWs︿T_N*VSqy Ss3MՌ︿T2QLi]QLi]QLiUJ[F?Q._zo1EG3aLm/ƖQLim/Ɩ ?PAU?qȨlXs︿T }"𫹏_*? }" T'S=8\fة?Qȴ)憎ZLii1uSOJonӏtj ax\_JO阭?qȴ-?*O銣4}a}]< 6OD}Lm4'?ckN6Qe% z*/ʭ޳*/޳*/]7LUtS?'Na4z?Z9_Fw׮-2Wӏ98 Zȸ<lwWӏP.?ZȸD"o2k+"pc\"H(_>-#E48>-#E4lOHq8q[.?\N>VʭMݿ qDKa'ѓ˿a'чrܝ/8|(-0_hrܝ/8|!ܷ%C^eK+yA3ON.=P ;D?$1!˿f%я񷙿IC4cùlmomP0[>iY[mҋhcs d/ǑJ̊]UBA.]EWM6) [7`Wb]*s{\G Q]v*ZpO9JM]v*_?'Lc%b]TEw?}Ld6 v*C2x+f1vv*U(N?މoלG6*yN]QfWr}+ثWb;9Qov*Uءl4?Id>+lv*UتOAJ2] K9).]T5/LPN][s7dOT 7b]ڝ'AAv*UتdOINZ UثU Kqɥ̐?J7chv6Pޏ_L 'b]vb8]v* ?e2*v*Uت>/ǑJ̊]UBA.]EWM6) [7`Wb]*s{\G Q]v*ZpO9JM]v*_?'Lc%b]TEw?}Ld6 v*C2x+f1vv*U(N?މoלG6*yN]QfWr}+ثWb;9Qovv*Ul4?IHܭثWb?c FUѹPUثT_c,R Uz&(L'b]x2'E*_CWbwܟ)Yty3|'b]vb8]v* ?e2*v*Uت>/Ǒ+W`UX>^ $+).ƕثT^9bvv*UB7edUثWb xثWbx~/V2[Hv*UؕDZwsLK3`ثWb>c'BlaWb]J_9͊ECWb8xo8٤x\_Jlv*Uت3Nv}5Fnk[݊iح;-Q\}InTUثUz # Qmv*UثT_c, v*UUCQb dUثU?DY ~IWb*wܟ*;v*UZ2'K X,lv4im۰چ8 ƕتˣɘP2u1WSu1WbvbSi]+v*yaLW%P]L#l?_y*A]+v*ocJUت/Lz甿iHTٻv*B7md̀J*UثTY̼|UrjUثTJQI?Rfn-̤;v*J-cN&_c%ƕһV1![6Wb]J\29SȲv(v*Tv7lj.V/~b;v*U>G# 75iح;[77&/R>+lbVOsRe}v*U/Xf`KWbUCQbv "ثWbn&` 7bV݊r Ryc'b]YDxbĥ9cc)ӱZv+J~<KC 'chv6U we*&N]v(>FR6z_K5wTWz_K5w5?T6NFSǍ]m77_ooO5DwvazȧoMⱒPM77_TM77_۾T[w45!m7Ik5i"J_UӿoFwtF'э$1?m]];&4b[M?_^S2 VM#TIba3)K#h]KF/ъ/_W=_1ߪX$1Φ6X$1imTIb۾a1£挳+}R%4fҘ;/_ ۾a1[w,?_Fw,?_F E!:֒%y?g;9Y; 挍2w_51;v?/һv?/Ҕm%8|KFi5Yr}*R l}JO51ZwԬ$_}JO51ZFiVNߴ=ohM:~i5Y?miQ?Ӎ;6gpҍvs0vNcoVI5-hөmF+N?өmF4}\:Pk^CZ}72ٰ;VF nտэ}jchAmZ}71ODȾlx?!%gh 6cleߥ|7ُ[.-"!^25K؟ޠ?W?ɗcӍ׌C#ӖX3./?./?*T`q*WkQ 2pҋIAWY2ŏ]-_d?7lq_Zoُ1w|be׶-Z_ꌐERDUyF͙?E%D7lG拿#E4](YF͘R)ԝ(YF͘P#EGQ}g{V$@61)Lk#Gя(K,#GS'F`cp+4c`gԡq3m^~c# SYR;m,#GS~\#^}ߏ;m,#GSJӶ4<.^}ߏ;m,#GSJӶ4<.>WkZv*U1qrBv*Uت9?f!FE*xUث*呒b2v*Uت+MzcM6J) $ v*B7d^f2Q]v*ZpO9 UثWbxLc%b]U~c4̌?S-̓b]>/![6Wb% ?9N]Slk?.V/~b6;v*U?vG#Tnk\b]/oĆ+IWMƕһVCtfvWc2߯1jxwcHBAܕ2v4ƕҡ?E=PfC7`WbmJ?^J$d5۱[v+nmB2Iw|UثWb?(eŒUث\W{Rd$UثWbkZv*U1qrBv*Uت9?f! $PUثUk_!$dUثTV/ly_$aWb]T2?J8WbS _g31r UɫWbQK"K3)]W/2[]l}<|lثWb% ?7Ώ%])&٤x\_Jlv*Uت;Mv9F75CWbPڗ7C+A 6;v*UCsRr UثWb~vxIv*UB2B$Mv*Uث SzGbM&XR]J_dYjv*UثWkZv*U1qrBv*Uت9?f! $PUثUk_!$dUثTV/ly_$aWb]T2?J8WbS _g31r UɫWbQK"K3)]W/2[]l}<|lثWb% ?7Ώ%])&٤x\_Jlv*Uت;Mv9F75CWbPڗ7C+A 6;v*UCsRr UثWb~vxIv*UB2B$Mv*Uث SzGbM&XR]J_dYjv*UثWkmڻWcjU1qrAQ]G'sb#$Pm]Uk_!$dUثTV/ly_%ivv*U؅No?#dv*U0~s3 \v*U)^d2UثU{_qM2Pc%`UثV1+f1v*UثIBw7ys"bb%6y T4 _݊v*ݟk;C75KCWbmK!RAlv*Uثk y:3v*UثV?u~ ž*UثT[0̈r nXbPsf";${" nmآԮo2b]4[XC%n5;v)v(_#1C&S& ‡mlUq@6/Q}aK0;v*UثT~?5|mI?۾';I2V}J_񫾥/Ʈ+R7۸VC_FJ<Չa<Bd'Ɩ8۾[wKljQ]L8à1R%? ;pkP+}1/Wk6l ŧ %lO\~Y>qۿN,pXڕjsQ\RhP;帰QNr!v 돂'mp+v 돂P5+v (݆MPA}~.}~ <=|%w_u _}H(Cd6E\ >kA>v>p*W; ?i2\-GPXO|xㆽx_;׏~S< V;di@VsiixcKc^'s?qv*B7?m??ir%G"]G42H[KWbֿ &('b]t/l9ثWa S,dv*Uk?31r UɫWbQ Lc%b]}n5]l}<PBmXv*U$<X7̊܋'bb)&٤x\_Jb6v*U?vG# ;5]<rc ^O ثWbEK .b]+{.?#ĎYaWb]/Nɛ HXs'b]s?42n>~nZv?m}Q՗(\v*Uت'L_HbIثWbQZ`)qWb]v*UثWbqv*B7?m??ir%G"]G42H[KWbU?,LQ9['b]t/l9ثWa S,dv*Uk?31r UɫWbQ Lc%b]}n5]l}<PBmXv*U$<X7̊܋'bb)&٤x\_Jb6v*U?vG# ;5]<rc ^O ثWbEK .b]+{.?#ĎYaWb]/Nɛ HXs'b]s?42x|ܵv*UT-}ǛV_rثWb7:6?[Wb]0&SB]v*UثWb_qv**Pɥv*Us#!nE.]VI1Dlv*ޱmpV^`Wb]*swMij5*UثT} %W&]D/ʟE37KՌRv*mgF dl;v*Ue\1v*UثIBy/o#N]S[oOMIXLmv*Uت7Mv9Fvk[݊v*y 5&1XAWbZ=Ӓ].]V%{\G)P®]R_2m6>IN]j9h2e|ܱv*URm9\b]4hC rMn]v*OLU v*UثWb]qv**Pɥv*Us#!nE.]VI1Dlv*ޱmpV^`Wb]*swMij5*UثT} %W&]D/ʟE37KՌRv*mgF dl;v*Ue\1v*UثIBy/o#N]S[oOMIXLmv*Uت7Mv9Fvk[݊v*y 5&1XAWbZ=Ӓ].]V%{\G)P®]R_2m6>IN]j9h2e|ܱv*URm9\b]4hC rMn]v*OLU v*UثWb].՚gߣ.ՊeW#bW~l_V*ї_jZ>M,zE틴J;BEmOeW#blwؿvW~l_V6ߣ.ՍGE}B1DS}:~ r _.#Yї~)~F5bї~U.C}?߱,DOՑKF^x'j]2?lV*ї #cTN] HO'3vl9"G]##u2?-;u2?Zw?dV4 -'Z}ѹh]9OuׂW~OV*^ #j]:?bX>O^?W ?*_ʿipZ[]]o?̌ɯ]*՛ `]*ՍuV6Xڶ4z:\3B.j͗ _5|@w.}BW a]_5K+#ri?s FG\wnQQW}FG\mS[k;;{Y+ҩ+C#mW??mRQ?+n!q[FiwԪwɿFV][WKn+q}V vaPlj )#i|S <+n)-\imk[Vkr@),##qM#q[wG#¶K,F'DžmޗYO-nYt`ˉGXxjǁ GG5cM߰D7[&<-,;AB}hvxR՗pWo?>/(wjDŽv~#+↮m{$5c?E_\mFQ]Y?ߐ欳G5Wŋ'׏ _.|?V<+Ed gjQYe8|NjƗQX;3^$Fe0-Ixh%Dz8lTׅGUߣh|?^*4?>w׊o'30twPߣh|?^*4?>w׊|k]>G5?C#W~xGUߣh|?^*4?>Yp݊v* L.>/4%O#ثT|Ϗ2k Fvv*lBB'vv*ӿޡmp搊; vv7:ҥWbS _g2}!*bv*U2ߩs7L9YUث*ٛOs#O2vlX;v*Uè2xUͅ1v4WcA*Jxm}sA72+r%!ثWcJLYr*UثTf?k{C~fk]]3oȨ06;vv*_rQR|)v*UUj?й9!𫱴Wa*ֿ3}!ǟ4lfUثUt# ^qثWbX> 'Hly!Ev*UG4??Γ3}.49sb]9heMWD,p]v*UثWb3:V?PWb]0&SB]v*UثWb_Yqv*G5L.>(%*xثWb?e# r Y!ثWaUk_"R8UثTN$m4V_lv**3,J*UثTY̼_HJX]D'̿\uc&(!ثW`Uk7N dl;v*U}a(\_\Ks#qw֮߯6]늦P$jsmd7??wn߯*_\UY~ai<W_7>?(A*v)ֿ0=&dZY8]E'sg%PWb]okv*?i U v*Uت+Pz0&SCbb]v*UثyθN]v)cQɵ˚VdRUثT|Odg2 dJCWbU?/$"ثWb?Il9$ثWa ߯3Y)b]Lm?tY_HJX]WOoԹN̤;v* ٗA\Ldذv*Uث\1'v*JiE v(v*T,_#9x\_Jb6;v*UeFkEثWb8S![!< v*UNoՄ)yFb]bmV39 2eZb=y'̜\6K2ثWblv*UثWbZ]ZhCB]v*?ބ0ɔPkv*UUUثWb_yθN]v)cQɵ˚VdRUثT|Odg2 dJCWbU?/$"ثWb?Il9$ثWa ߯3Y)b]Lm?tY_HJX]WOoԹN̤;v* ٗA\Ldذv*Uث\1'v*JiE v(v*T,_#9x\_Jb6;v*UeFkEثWb8S![!< v*UNoՄ)yFb]bmV39 2eZb=y'̜\6K2ثWblv*UثWbZ]ZhCB]v*?ބ0ɔPkv*UUUثWb_y8N]vLbMB\ҳ"b]x#S2 d C+WaUk_ )v*Uت'Oz'HEJ]]v/5*UثTOf/%[,Wb]ο\c'fRv*I/Ldذv*Uث\~d;WcJUت|O5 dY;;v*|HY/%LfUثTUtd9 ͿŸd O%ثWbwK~!K0;v*T;}_:O>(A)+Wbmk'̜\>KrUثTR6B\U v*Uثt]CtZhCB]v*?ބ0ɔPثXt8XxXWb]ҁ}Vs Um]X?Um]X?%S|?%-ҧh?9K'W}ZN6mS {h~NS#9l9$}Vpq+8+8*{x B+/X}VO]X??w`_\UfN ^d['s)~'w-8oU`|}?N~'6,KN*['qW}R?*`_%ƮƮƮƮ3TO(;R['qCo]KN*ZZ[V?Ys1rV~'1KN*['qW}R?9Q|;pW?)]6IY;VJ[A3+oՏ__?TV,d-jO.ɸ]EG4? qT.*UثWbb]ZhCB]v*OLU v*UUUثWb_Ӄ1nv6:18 4഻WcjmS ?,d; y;v* ieHEv*Uت#OzW9ndv*!Sc9*UثTOeUثUto37KՌ̤;v*UV A̓b]v*UثWb3CWbĥ3ybUثWbߵf]"JفՈvv*! hcneNcWbQ7t?=uxvWK|kWbQ&O?yQ{nX;bUOY'`R_iv*Uت*?KqWb]v*UثTV+OHb\UثWbQZ`)qWb]v*UثWbԃ.;v*U؎iLe/%*yv*U0xS2LNCWbY^Zb]4ØHFIK+Wb8??3)]Lm?tY_HJX]WOfC3tXɬCWbU`'̍?ɼ0v*UثWb]v) 0\T;v*JS;???.V/[1]v*m_Fk܂ `ub UB?:???݇6ʜb]to'1ݗ?#c\@C]luF0?OmK|0tC*UثZس'OI}I~IثWb?f!.*]v*UثWbQZgt?4!qWb]Ej?ߧaL*]v*UثWb]Ճ.;v*UJc/Tq!.iSȥثWbŸOdg2i]x~YdymڻDiRcb]TXdv*U1yg31}!*bv*U]?_ c&)]U/A͋b]v*UثWbR`!ثWbS;??.f.JوUثU{o3_%lbUثG_GZ?%m]FӷXNb.ir^?G/>MثVQb0?OmK|,{,r\Uثbjb0=&[Q%&b]lv*UثWb]EiҴĆ*]v*U~2v*UثWb]v*փ.;v*UT^.B\<]v*[)dF| b]x~_'^[iv6ڢ4ib]T??3)]Lm?t^fbBUv*Uتp7MfRv*I?_7 `m]v6ڻWaW`Hdb]L?XG$sG𹘹+f#cWbv+Jߵf]1kv*UتG_GZ?Kn/<+sFӷX6v'ڏs_$r|@C]luF( ̇֗s,{X;v*mwgO-%&b]lv*UثWb]EiҴ*]v*U `)qWb]v*UثWb׋V%_9^'[ x[ x[ x[ DTKK3«'q}rKvAo,O50qTY?hǍ]K/ xTY?hǍQvSM)N K`$_#ź];? ?ii}NO5q}NO5p,S?朖9$"g'9uR m]+? ?iR mUl>^-Շrx!+_LɴVgk銻VgkTk+Yμ~!MK4knK4knK4knK4T_߸W31}!!W6/,KoxqW}F4iUH}{NfC;6}i̚CoW?4_xqwmӍ*VPQoc*SOf=24?4#'K;i i|E{k4#05f4cljSO?67}M?k 8;ƞ^H_9Uqw?Ӎ?m[q؆2lh :.WPN&/\e}"NR1cKMxF6j)Z$xre7~Z$xr^Ǘ'%=7~Z$xF3ރ ͥ5[mY..'uIdXJI5\"i7_pxޝ,XwgG'RDž|w}oOyr,+㯿O1Y0~zzg'e&TMn49?*SL|M#b49?*M?|[zԤPަG'R]ir,UަG'R]ir,UަG'R]ir,UަG'R]ir,UަG'RQ:kߤmiXLEyCzgoKw&TWzgoKw&TWzgoKDɧzX%ITm%?x?XM37O?XM37O?XM37O?XM37O?XM37OЍg$;v*U:d\ p%ثWb?#SAv UتScHEv*Uت"__ r|B;2UثWb7XJUثWbOAW,KWbUC3tdv*Uت]_2R[v;]wWc]S0KȥUثTO$sYdU۱[v+nmZ !ثWxaV=G'^?%ONثWb bd v*UBεoi?G:}!O&;v*UZ/CTxXv*Usg%PWb]v*Uت+Lf$1T.*UثWbG0ɔPWb]v*UثWэg";v*Uzd\ r)v*Uت:O\ #]]ޟNY!ثWb1 KWb]*o7yWbSO>s3rĻv*U]?37JN̾;v*UR.f/ٔثWb]v*Uؤ'æ`&-KWb©ΟOHQE[)KWbUk~ k]V=F^?[[rdUثU 1?D 2J Uثy֭K'HAR&ثWbY'ثWb?f!.*]v*UثWbQZgt?4!qWb]Ej?ߧaL*]v*UثWb]ҍg";v*Uzd\ r)v*Uت:O\ #]]ޟNY!ثWb1 KWb]*o7yWbSO>s3rĻv*U]?37JN̾;v*UR.f/ٔثWb]v*Uؤ'æ`&-KWb©ΟOHQE[)KWbUk~ k]V=F^?[[rdUثU 1?D 2J Uثy֭K'HAR&ثWbY'ثWb?f!.*]v*UثWbQZgt?4!qWb]Ej?ߧaL*]v*UثWb]Ӎg$;v*U6T?62&anGdثTuğ)\ #]]U{?O,ƐRUثTEҿ@ve%ثWb©?rVb]Lm?tY_HHUUثUtP+;2v*GWԂʶ.ڻWcjm]v*)% KڻW`L?tG5*Jmحbm_FkU\SSaCsHc%ONثWb bWb] Vf$sU? `۱[v*}͗a?zL!H<,]v*9h\UثWb]v*UJ3Gv*UثTV?b\UثWb]v*Uث&/O9'4D_tFa< q%?q%?i41~?ъO?~?#?F?/kqwG4Jѯiƕ^Nqr|]O0KG??w~4Uߣ?ӄ*m.W> W3SKG??V9ߩ 4ᥧ~8ӿG?? Zz ?f/2 ~'7Ӗ+GOow?߉ 4'?UY,%hxlұtCG8ڻ|ΟӍG8mWa(J3!LE;2:?iQNοZwdu>*Ӿ'1V?鏊L|U}FO_c;2:?J@<#L+*weeL|E}F__`1_s6f%JTeTn#^7}R_TcKƫ7Olǒ88k돆wu[w[c{\ss_ȘzK~=~/*enWsLimފo-_1>?fS!Ix=-?O,c[TtF< [TtF< [TtF<)u-?O }nu;Efb1}zC/R O%L8{omol?3 =hԚ+PT~*[l?tOw-o7S]KmMTW}R[axo-a(KWb]*q'yn*UثT_f/%[,Wb]'cfXɼCWaU_/+Ykb]v*UثWb/Pob]:?0s`Uv*UثYKWb|(^Oo<1WbPs 4YwV.]Te>r}H^c+Wb0=&*]v*9h\UثWb]v*UJ3Gv*UثTV/aL*]v*UثWb].)v*UثTɺ'c 2fRUثTuƿRdAv=Rv*2iܽ]v*e/ɿ,%Iv*UثN$g3,]v*l]v*ݏc&)®]W/|Wb]v*UثHO쏗(7]v*ZpOL\]v`:$;0RUث!Xxc+[Xyfc;Gb]/?%h/r[]v!X+#}!dƶ+Wb, b\UثWb?f!.*]v*UثWbQZgt?4!qWb]Ej?ߧaL*]v*UثWb].)v*UثTɺ'c 2fRUثTuƿRe CWb*"q#rv*Uتп&0ne%ثWb8dv*UتcifbBUv*Uتv>gfn̾;v*Ur!Zv*UثWb]> r)v*Uتai?12s`Ur;v*Uم v*UǨQ׏&2%M1WbPs 49qb*Uث 5Oi?G7C!2mn]E]qeOB]EG4? qT.*UثWb]v*?i U v*Uت+Q? e1T.*UثWb]v*U.)v*UثTɺ'c 2fRUثTuƿRe CWb*"q#rv*Uتп&0ne%ثWb8dv*UتcifbBUv*Uتv>gfn̾;v*Ur!Zv*UثWb]> r)v*Uتai?12s`Ur;v*Uم v*UǨQ׏&2%M1WbPs 49qb*Uث 5Oi?G7C!2mn]E]qeOB]EG4? qT.*UثWb]v*?i U v*Uت+Q? e1T.*UثWb]v*UKg:w/7ݏZw/7ݏZw/7ݏZw/7ݍdID\ϩ[Kdm4J_oZw/7ݍ;җ֑5J{eC0ћqw71Wz3?w71DYC/~>,ĐFޔq)1WzR#}bQKv$s F3?2Fo}bf}*Fo}bf}?Oldף77݊ћ]bfFTwoFoo,Wz3#}ثUތv*Rcn)MR#}f_K鍫)1w/7ڮX1]Kd)ޔvOBd݊v*0=&*]v*9h\UثWb]v*UJ3Gv*UثTV?b\UثWb]v*Uث~pn#W`]* 25KWbQ .\T; v*gWy]WzWbS KWb]Sgm?3-]v*WeUثU>c%ِb]VC8*UثWb]v*U\䩁]XQѐ[.]>77S.F.fUثT6'D|ՙ-n®]/Qe>SB`WbQWY'PWbQQ6B\U v*UثWb]ZhCB]v*OLU v*UثWb]~pN#Wb]S'@d'2 dRUثT}?#~ˣAW v*U^qsHEUثU{/~M9f/%0̤v*UB^g[v*U1yg31}!*bv*UQ>s/OՌ̀b]:aWb]v*UثWbs+NX%]lt?,Z®]v*UثWbBxa~Cf o b]}ɁTb]]@䐷1;v*UQ׏&2Ao}o.hUثT6'D"O-ثWbK q_$ss2ثTU_1T.*UثTT͟3CB]v*UثWb3:V?PWb]0&SB]v*UثWb_~p|.#Wc®Dž]1=D2 d).ƕһT}?#~Hdp𫱥v4LJuWcȥv4ƕҫ+o1}A)eR]+v4J?oyn*UثT_f/%[,Wb]'Neiؖ"6ڶ;k v6ڻWcjm]ڻ`T?31}!*,WPx UuW*t(-xeC<0ںWPx`ucj 6ںWPxcj m]A፫<1u6j#nٍinኻ]b<1V銻v*UثMH[RҺҺDf*H<2@9o<֗?5vK덫iqw/6MbiDywCԂ-SKQc\UߤjF7]KQc\UߤjF7X}roocxkd?Jj7`Қ#)\1*Қ#⨛OQYu0&"I8Pߥ5?kF?]SSc\Uߥ5?kF?]SSc\U~5D ZqT7MOZqW~owMOZqW~owMOZqW~owMOZqW~owMOZqTNj-چ,`#n9oҚ#)\1*Қ#)\1*Қ#⨛KQY @a;HC㊡Jj7늻-sJj7늻-sJj7늻-sJj7늻-sJj7늻-s7kv*UثWb]v*/_c%UثWb]v*Uʲ$-ʕUثXLf>.N/Sv*UتXM9<R AثWa ɿoskO0kv*0=&*]v*9h\UثWb]v*UJ3Gv*UثTV?b\UثWb]v*Uث7kv*UثWb]v*/_c%UثWb]v*Uʲ$-ʕUثXLf>.N/Sv*UتXM9<R AثWa ɿoskO0kv*0=&*]v*9h\UثWb]v*UJ3Gv*UثTV?b\UثWb]v*Uث7%wCo5wCo5wCo#jObϫ[Vdm{Xm{X-]kojZ H[c;U}\˴덫myڻ덫myAT[~'s'}R?o\~-qw-oS KKA>xo['rw-8o]KN*AAW}ROlj[/<\;v*0=&*]v*9h\UثWb]v*UJ3Gv*UثTV?b\UثWb]v*Uثזg8]vL'|7Wb]a~̈}!Tɫ+WbeWey#s%]v*i'Y Ls-.]v̝v*U0>יl]v*UثWb]v*UثWb]v*UثWb]v*UثWb]*Ǽ(Hf&.F0#v*Uت [5M&OԂ+6mAثWb#z$XΏ'U>j9&]'OIqWb]lv*UثWb]EiҴĆ*]v*U~2v*UثWb]v*Жg8]vL'|7Wb]a~̈}!Tɫ+WbeWey#s%]v*i'Y Ls-.]v̝v*U0>יl]v*UثWb]v*UثWb]v*UثWb]v*UثWb]*Ǽ(Hf&.F0#v*Uت [5M&OԂ+6mAثWb#z$XΏ'U>j9&]'OIqWb]lv*UثWb]EiҴĆ*]v*U~2v*UثWb]v*іg8]vL'|7Wb]a~̈}!Tɫ+WbeWey#s%]v*i'Y Ls-.]v̝v*U0>יl]v*UثWb]v*UثWb]v*UثWb]v*UثWb]*Ǽ(Hf&.F0#v*Uت [5M&OԂ+6mAثWb#z$XΏ'U>j9&]'OIqWb]lv*UثWb]EiҴĆ*]v*U~2v*UثWb]v*MutatorMath-3.0.1/Docs/mutatorMath_pyramids_and_wedges.py000066400000000000000000000117411365052737200236350ustar00rootroot00000000000000 """ This is a script for Drawbot (http://drawbot.com) It draws a graph of the factors of a 2-axis, 4 master designspace. """ from mutatorMath.objects.mutator import buildMutator from mutatorMath.objects.location import Location import random as rann import os, time, math import itertools class MathDot(object): def __init__(self, pt=(0,0), s=0.5, alpha=1, name=None): self.pt = pt self.size = s self.alpha = alpha self.name = name def draw(self): save() s = self.size * .5 fill(1,0,0, self.alpha) oval(self.pt[0]-.5*s, self.pt[1]-0.5*s, s, s) fill(1,0.5,0) stroke(None) fontSize(9 * 0.1) text("%s\n%3.2f"%(self.name, self.size), (self.pt[0], self.pt[1]-3)) restore() def __repr__(self): return ""%(self.pt[0],self.pt[1], self.name) def copy(self): return self.__class__(self.pt, self.size, self.alpha, self.name) def __add__(self, other): new = self.copy() new.pt = new.pt[0]+other.pt[0], new.pt[1]+other.pt[1] new.size += other.size new.alpha += other.alpha return new def __sub__(self, other): new = self.copy() new.pt = new.pt[0]-other.pt[0], new.pt[1]-other.pt[1] new.size -= other.size new.alpha -= other.alpha return new def __div__(self, factor): if not isinstance(factor, tuple): factor = (factor, factor) new = self.copy() new.pt = self.pt[0]/factor[0], self.pt[1]/factor[1] new.size = self.size/factor[0] new.alpha = self.alpha/factor[0] return new def __mul__(self, factor): if not isinstance(factor, tuple): factor = (factor, factor) new = self.copy() new.pt = self.pt[0]*factor[0], self.pt[1]*factor[1] new.size *= factor[0] new.alpha = self.alpha*factor[0] return new __rmul__ = __mul__ items = [ (Location(pop=0, snap=0), MathDot((0,0), name="neutral")), (Location(pop=0, snap=1), MathDot((0, 100), name="on-axis-one-A")), (Location(pop=1, snap=0), MathDot((100, 0), name="on-axis-two-A")), (Location(pop=1, snap=1), MathDot((100, 100), name="off-axis-A")), ] bias, mb = buildMutator(items) grid = {} for loc, (master, xx) in mb.items(): for axis, value in loc: if not axis in grid: grid[axis] = [] if not (axis,value) in grid[axis]: grid[axis].append((axis,value)) nodes = list(itertools.product(*grid.values())) nodes.sort() corners = {} for n in nodes: l = Location() l.fromTuple(n) for factor, obj, name in mb.getFactors(l, allFactors=True): if not obj.name in corners: corners[obj.name] = [] corners[obj.name].append((factor, l)) def dot((x,y), s=10): save() fill(1,0,0 ,.5) oval(x-.5*s, y-.5*s, s, s) restore() def nameToStroke(name, alpha=1): # simple conversion of a name to some sort of unique-ish color. rann.seed(name) fill(None) stroke(rann.random(), 0, rann.random(), alpha) def nameToFill(name, alpha=1): # simple conversion of a name to some sort of unique-ish color. rann.seed(name) stroke(None) fill(rann.random(), 0, rann.random(), alpha) # make the drawings for name in corners: newPage(500,500) designSpaceScale = 250 save() margin = 102 translate(margin, margin) projectionAngle = math.radians(20) strokeWidth(10) stroke(.5) fill(None) a = b = 1 # scale these to the number of masters on an axis line((0,0), (0,designSpaceScale*a)) line((0,0), (designSpaceScale*b, 0)) polyPts = [] for factor, loc in corners[name]: save() dy = math.cos(projectionAngle) * factor * designSpaceScale *.5 dx = math.sin(projectionAngle) * factor * designSpaceScale *.5 pos1 = (loc['pop']*designSpaceScale+dx, loc['snap']*designSpaceScale+dy) pos2 = (loc['pop']*designSpaceScale, loc['snap']*designSpaceScale) polyPts.append(pos1) nameToStroke(name, 1) strokeWidth(10) line(pos1, pos2) dot(pos1) dot(pos2) w, h = textSize(name) pos3 = pos1[0] - w -10, pos1[1] strokeWidth(20) nameToStroke(name, 0.4) line(pos1, pos3) fill(1) stroke(None) text(name, pos3) restore() # draw the plane # note: these polygons are hardwired to the number of masters. # While you can easily add more masters to see what the factors do, # these surfaces won't do the right thing. if len(polyPts)==4: stroke(None) nameToFill(name, 0.25) polygon(polyPts[0], polyPts[1], polyPts[3]) if "off" in name: nameToFill(name, 0.15) polygon(polyPts[0], polyPts[2], polyPts[3]) restore() path = os.path.join(os.getcwd(), "mutatorMath_pyramids_wedges_%s.png"%(name)) saveImage(path) MutatorMath-3.0.1/Docs/old_designSpaceFileFormat.md000066400000000000000000000364621365052737200222530ustar00rootroot00000000000000MutatorMath DesignSpace Format (old) ==================================== ![A MutatorMath Designspace Document Icon](designspaceDocumentIcon.png) Please refer to the [designSpaceDocument](https://github.com/LettError/designSpaceDocument) repository for an up to data specification of the file. The UFO support in MutatorMath introduces a useful storage format in XML for MutatorMath designspaces. This document describes the format. ## Document structure The document must contain a single **designspace** top level element. The current format version is `3`. The designspace element must contain one **sources** element and one **instances** element. * The **sources** element contains one or more **source** elements. * The **instances** element contains one or more **instance** elements. The designspace format makes a difference between "masters" and "sources". Source specifically indicates the UFO file. Master indicates a particular use of a source in a MutatorMath calculation. In general: the sources bring data to the calculation, instances take data out. The font.info contains different kinds of data. Some are strings (names, urls, copyrights), some are numerical but not geometrical (versions, PANOSE). The designspace offers some controls Both instance and source elements contain paths to files. These paths are expected to be relative to the path of the .designspace document. This allows the same .designspace to be deployed to multiple locations, but still reference the proper source files. It also allows sources to be stored in their own directories, and instances to be created into their own directories. ## An example of a DesignSpace description ```xml ``` ## The Elements ```xml tag="aaaa" name="nice name for axis" minimum="72" maximum="1000" default="96" /> filename="" [name=""] > [] [] [] [] [] filename="" familyname="" stylename="" [postscriptfontname=""] [stylemapfamilyname=""] [stylemapstylename=""] > [ [] ] [ [] ] [ name="" [unicode=""] > [] [ nice glyph! ] [ source="" [glyphname=""] > ] ] ``` ## The axis element ## The source element The **source** element stores all the data needed to locate a UFO file and indicates on how to use the different kinds of data in a MutatorMath calculation. The source element can contain a number of child elements. The **location** element is required, it positions the source in the designspace. The **lib**, **groups**, **info**, **kerning** elements are optional. Some types of data can be muted: this means that specific data will not be used to calculate an instance. #### Attributes of the source element * **filename** * Required, string. * Path to a UFO, **relative to the path of the designspace document.** * **name** * Required, string. * A unique identifier for this source, can be used to refer to this source, for instance in the **master** element. #### Child elements * `````` * Required. * ``````: * If the **lib** element is present and its copy attribute is set to "1", this source will be the provider of font.lib data. * Only one source can be the lib data provider. * Optional. If the lib element is not present this source will not be the provider of font.lib data. * `````` * If the **groups** element is present and its copy attribute is set to "1", this source will be the provider of font.groups data. * Only one source can be the groups data provider. * Optional. If the groups element is not present this source will not be the provider of font.groups data. * `````` * If the **info** element is present and the `copy` attribute is set to "1", this source will be the provider of the non numerical attributes of the font.info. * Only one source can be the info data provider. * The optional `mute` attribute when set to "1", indicates the numerical attributes of font.info should excluded from the calculation. * If the info element is not present this source will not be the provider of font.info data, but the numerical font.info data will be entered into the calculation. * `````` * Optional. If present, this kerning from this source is to be excluded from calculations. * `````` * Optional. If present, this glyph from this source is to be excluded from calculations. ##### Example ```xml ``` ## The instance element The `instance` element stores all the data needed to perform a MutatorMath calculation with the previously defined sources and create a new UFO. The instance element can contain a number of child elements. The `location` element is required, it defines a point in the designspace. The `lib`, `groups`, `info` elements are optional. Wrapped in the **glyphs** element, an instance can contain zero or more **glyph** elements. A glyph element can be used to define exceptions in the designspace geometry: for instance, set a different location for one specific glyph, a different set of sources. It is expected the instance generates all glyphs that are available in the sources. An instance may have special definitions for some glyphs, these complement the basic glyphset. The familyname and stylename are necessary to make UFOs. Some additional names can be added. #### Attributes of the instance element * **filename** * String, required. * Path to a UFO, **relative to the path of the designspace document.** * If this path does not exist, it should be created when the instance is processed. * **familyname** * String, required. * FamilyName field for the new instance. Corresponds with font.info.familyName. * **stylename** * String, required. * StyleName field for the new instance. Corresponds with font.info.familyName. * **postscriptfontname** * String, optional * PostScript FontName, corresponds with font.info.postScriptFontName * **stylemapfamilyname** * String, optional * Stylemap familyname, corresponds with font.info.styleMapFamilyName * **stylemapstylename** * String, optional * Stylemap stylename, corresponds with font.info.styleMapStyleName #### Child elements * `````` * Required. * `````` * Optional. * Add this element if the instance needs to calculate the font.info data. If the info element contains a location element this supercedes the instance location. * ```...``` * Optional. The glyphs element can contain one or more **glyph** elements. * `````` * Optional. * Add this element if the instance needs to calculate the font.kerning data. If the kerning element contains a location element this supercedes the instance location. * A kerning element may have one child **location** element. If present this location should be used in calculating the kerning. #### Example ```xml ``` ## The location element The location element describes a point in the designspace. Locations are used to position a source as a master, and to indicate where the instances are to be calculated. Location elements are used in several places in a designspace. A location element has no attributes, but needs to contain at least one **dimension** child elements. ```xml [...] ``` #### Attributes of the dimension element * **name** * Required, string. Name of the dimension. For instance "width" or "weight". * **xvalue** * Required, value. A string representation of distance in this dimension. * **yvalue** * Optional value if this dimension is to be anisotropic. #### Examples ```xml ``` ## The glyph element The optional **glyph** element can be used in a instance element to store information about masters and locations that are different from the ones defined for the instance. ```xml name="" [unicode=""] > [] [ nice glyph! ] [ ...a number of master elements ] ``` #### Attributes of the glyph element * **name** * Required, string. The glyph name. * **unicode** * Optional, hex. The unicode value of the glyph, expressed as a string in the format **"0xFF"**. If no unicode value is given, use the unicode for this glyph used in the sources. #### Child elements of the glyph element * `````` * Optional, location element. If the location element is present it will be the location for this glyph. If it is not present, the location defined for the instance will be used. * ```...``` * Optional, string. Corresponds with the **defcon** glyph.note attribute. * ```...``` * a list of master elements. ## The master element Used in the masters element to specify which glyphs from which sources have to be used for this glyph. ```xml [] ... ``` #### Attributes of the master element * **source** * Optional, string. * Name of the source, must match with the **name** attribute of exactly one **source** element. * **glyphname** * Optional, string * Alternative glyphname if data is to come from a different glyph. #### Child elements of the master element * **location** * Optional. * If a location element is present, use this alternative location for this master. If no location is present, use the location defined for the source. ## Notes on this document Initial version of this specification. The package is rather new and changes are to be expected. MutatorMath-3.0.1/LICENSE000066400000000000000000000027401365052737200150020ustar00rootroot00000000000000Copyright (c) 2004-2016, LettError and Erik van Blokland All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of LettError nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. MutatorMath-3.0.1/Lib/000077500000000000000000000000001365052737200145005ustar00rootroot00000000000000MutatorMath-3.0.1/Lib/mutatorMath/000077500000000000000000000000001365052737200170055ustar00rootroot00000000000000MutatorMath-3.0.1/Lib/mutatorMath/__init__.py000066400000000000000000000031751365052737200211240ustar00rootroot00000000000000# -*- coding: utf-8 -*- """ Copyright (c) 2004-2016, LettError and Erik van Blokland All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of LettError nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. """ __version__ = "3.0.1" from mutatorMath.objects.location import Location from mutatorMath.objects.mutator import Mutator MutatorMath-3.0.1/Lib/mutatorMath/objects/000077500000000000000000000000001365052737200204365ustar00rootroot00000000000000MutatorMath-3.0.1/Lib/mutatorMath/objects/__init__.py000066400000000000000000000000401365052737200225410ustar00rootroot00000000000000""" Objects for MutatorMath. """MutatorMath-3.0.1/Lib/mutatorMath/objects/bender.py000066400000000000000000000200031365052737200222420ustar00rootroot00000000000000 import sys from mutatorMath.objects.error import MutatorError from mutatorMath.objects.location import Location, biasFromLocations import mutatorMath.objects.mutator def noBend(loc): return loc class WarpMutator(mutatorMath.objects.mutator.Mutator): def __call__(self, value): if isinstance(value, tuple): # handle split location return self.makeInstance(Location(w=value[0])), self.makeInstance(Location(w=value[1])) return self.makeInstance(Location(w=value)) """ A warpmap is a list of tuples that describe non-linear behaviour for a single dimension in a designspace. Bender is an object that accepts warpmaps and transforms locations accordingly. For instance: w = {'a': [(0, 0), (500, 200), (1000, 1000)]} b = Bender(w) assert b(Location(a=0)) == Location(a=0) assert b(Location(a=250)) == Location(a=100) assert b(Location(a=500)) == Location(a=200) assert b(Location(a=750)) == Location(a=600) assert b(Location(a=1000)) == Location(a=1000) A Mutator can use a Bender to transform the locations for its masters as well as its instances. Great care has to be taken not to mix up transformed / untransformed. So the changes in Mutator are small. """ class Bender(object): # object with a dictionary of warpmaps # call instance with a location to bend it def __init__(self, axes): # axes dict: # { : {'map':[], 'minimum':0, 'maximum':1000, 'default':0, 'tag':'aaaa', 'name':"longname"}} warpDict = {} self.maps = {} # not needed? self.warps = {} for axisName, axisAttributes in axes.items(): mapData = axisAttributes.get('map', []) if type(mapData)==list: if mapData==0: # this axis has no bender self.warps[axisName] = None else: self._makeWarpFromList(axisName, mapData, axisAttributes['minimum'], axisAttributes['maximum']) elif hasattr(mapData, '__call__'): self.warps[axisName] = mapData def __repr__(self): return ""%(str(self.warps.items())) def getMap(self, axisName): return self.maps.get(axisName, []) def _makeWarpFromList(self, axisName, warpMap, minimum, maximum): if not warpMap: warpMap = [(minimum,minimum), (maximum,maximum)] self.warps[axisName] = warpMap # check for the extremes, add if necessary if not sum([a==minimum for a, b in warpMap]): warpMap = [(minimum,minimum)] + warpMap if not sum([a==maximum for a, b in warpMap]): warpMap.append((maximum,maximum)) items = [] for x, y in warpMap: items.append((Location(w=x), y)) m = WarpMutator() items.sort() bias = biasFromLocations([loc for loc, obj in items], True) m.setBias(bias) n = None ofx = [] onx = [] for loc, obj in items: if (loc-bias).isOrigin(): m.setNeutral(obj) break if m.getNeutral() is None: raise MutatorError("Did not find a neutral for this system", m) for loc, obj in items: lb = loc-bias if lb.isOrigin(): continue if lb.isOnAxis(): onx.append((lb, obj-m.getNeutral())) else: ofx.append((lb, obj-m.getNeutral())) for loc, obj in onx: m.addDelta(loc, obj, punch=False, axisOnly=True) for loc, obj in ofx: m.addDelta(loc, obj, punch=True, axisOnly=True) self.warps[axisName] = m def __call__(self, loc): # bend a location according to the defined warps new = loc.copy() for dim, warp in self.warps.items(): if warp is None: new[dim] = loc[dim] continue if not dim in loc: continue try: new[dim] = warp(loc.get(dim)) except: ex_type, ex, tb = sys.exc_info() raise MutatorError("A warpfunction \"%s\" (for axis \"%s\") raised \"%s\" at location %s"%(str(warp), dim, ex, loc.asString()), loc) return new if __name__ == "__main__": # no bender assert noBend(Location(a=1234)) == Location(a=1234) assert noBend(Location(a=(12,34))) == Location(a=(12,34)) # linear map, single axis w = {'aaaa':{'map': [(0, 0), (1000, 1000)], 'name':'aaaaAxis', 'tag':'aaaa', 'minimum':0, 'maximum':1000, 'default':0}} b = Bender(w) assert b(Location(aaaa=0)) == Location(aaaa=0) assert b(Location(aaaa=500)) == Location(aaaa=500) assert b(Location(aaaa=1000)) == Location(aaaa=1000) # linear map, single axis #w = {'a': [(0, 100), (1000, 900)]} w = {'aaaa':{'map': [(0, 100), (1000, 900)], 'name':'aaaaAxis', 'tag':'aaaa', 'minimum':0, 'maximum':1000, 'default':0}} b = Bender(w) assert b(Location(aaaa=0)) == Location(aaaa=100) assert b(Location(aaaa=500)) == Location(aaaa=500) assert b(Location(aaaa=1000)) == Location(aaaa=900) # linear map, single axis, not mapped to 1000 #w = {'a': [(0, 100), (1000, 900)]} w = {'aaaa':{'map': [(-1, 2), (0,0), (1, 2)], 'name':'aaaaAxis', 'tag':'aaaa', 'minimum':-1, 'maximum':1, 'default':0}} b = Bender(w) assert b(Location(aaaa=(-1, 1))) == Location(aaaa=(2,2)) assert b(Location(aaaa=-1)) == Location(aaaa=2) assert b(Location(aaaa=-0.5)) == Location(aaaa=1) assert b(Location(aaaa=0)) == Location(aaaa=0) assert b(Location(aaaa=0.5)) == Location(aaaa=1) assert b(Location(aaaa=1)) == Location(aaaa=2) # one split map, single axis #w = {'a': [(0, 0), (500, 200), (600, 600)]} w = {'aaaa':{'map': [(0, 100), (500, 200), (600, 600)], 'name':'aaaaAxis', 'tag':'aaaa', 'minimum':0, 'maximum':600, 'default':0}} b = Bender(w) assert b(Location(aaaa=(100, 200))) == Location(aaaa=(120,140)) assert b(Location(aaaa=0)) == Location(aaaa=100) assert b(Location(aaaa=250)) == Location(aaaa=150) assert b(Location(aaaa=500)) == Location(aaaa=200) assert b(Location(aaaa=600)) == Location(aaaa=600) assert b(Location(aaaa=750)) == Location(aaaa=1200) assert b(Location(aaaa=1000)) == Location(aaaa=2200) # implicit extremes w = {'aaaa':{'map': [(500, 200)], 'name':'aaaaAxis', 'tag':'aaaa', 'minimum':0, 'maximum':600, 'default':0}} b = Bender(w) assert b(Location(aaaa=(250, 100))) == Location(aaaa=(100, 40)) assert b(Location(aaaa=0)) == Location(aaaa=0) assert b(Location(aaaa=250)) == Location(aaaa=100) assert b(Location(aaaa=500)) == Location(aaaa=200) assert b(Location(aaaa=600)) == Location(aaaa=600) assert b(Location(aaaa=750)) == Location(aaaa=1200) assert b(Location(aaaa=1000)) == Location(aaaa=2200) # now with warp functions # warp functions must be able to handle split tuples def warpFunc_1(value): if isinstance(value, tuple): return value[0]*2, value[1]*2 return value * 2 def warpFunc_2(value): if isinstance(value, tuple): return value[0] ** 2, value[1] ** 2 return value ** 2 def warpFunc_Error(value): return 1/0 w = { 'aaaa':{'map': warpFunc_1, 'name':'aaaaAxis', 'tag':'aaaa', 'minimum':0, 'maximum':1000, 'default':0}, 'bbbb':{'map': warpFunc_2, 'name':'bbbbAxis', 'tag':'bbbb', 'minimum':0, 'maximum':1000, 'default':0}, } # w = {'a': warpFunc_1, 'b': warpFunc_2, 'c': warpFunc_Error} b = Bender(w) assert b(Location(aaaa=(100, -100))) == Location(aaaa=(200.000,-200.000)) assert b(Location(aaaa=100)) == Location(aaaa=200) assert b(Location(bbbb=100)) == Location(bbbb=10000) # # see if the errors are caught and reported: try: b(Location(c=-1)) except: ex_type, ex, tb = sys.exc_info() err = 'A warpfunction "warpFunc_Error" (for axis "c") raised "integer division or modulo by zero" at location c:-1' assert ex.msg == err MutatorMath-3.0.1/Lib/mutatorMath/objects/error.py000066400000000000000000000003571365052737200221460ustar00rootroot00000000000000# -*- coding: utf-8 -*- """ Mutator Error """ class MutatorError(Exception): def __init__(self, msg, obj=None): self.msg = msg self.obj = obj def __str__(self): return repr(self.msg) + repr(self.obj) MutatorMath-3.0.1/Lib/mutatorMath/objects/location.py000066400000000000000000000524141365052737200226260ustar00rootroot00000000000000# -*- coding: utf-8 -*- from __future__ import print_function, division import math, sys import itertools, operator from mutatorMath import __version__ _EPSILON = sys.float_info.epsilon __all__ = ["Location", "sortLocations"] def numberToString(value): # return a nicely formatted string of this value # return tuples as a tuple-looking string with formatted numbers # return ints as ints, no commas # return floats as compact rounded value if value is None: return "None" if type(value)==tuple: t = [] for v in value: t.append(numberToString(v)) return "(%s)"%(",".join(t)) if int(value) == value: # it is an int return "%d"%(value) return "%3.3f"%value class Location(dict): """ A object subclassed from dict to store n-dimensional locations. - key is dimension or axis name - value is the coordinate. - Location objects behave like numbers. - If a specific dimension is missing, assume it is zero. - Convert to and from dict, tuple. :: >>> l = Location(pop=1, snap=-100) >>> print(l) Location objects can be used as math objects: :: >>> l = Location(pop=1) >>> l * 2 >>> 2 * l >>> l / 2 >>> l = Location(pop=1) >>> m = Location(pop=10) >>> l + m >>> l = Location(pop=1) >>> m = Location(pop=10) >>> l - m """ def __repr__(self): t = ["<%s"%self.__class__.__name__] t.append(self.asString()) t.append(">") return " ".join(t) def __lt__(self, other): if len(self) < len(other): return True elif len(self) > len(other): return False self_keys = sorted(self.keys()) other_keys = sorted(other.keys()) for i, key in enumerate(self_keys): if key < other_keys[i]: return True elif key > other_keys[i]: return False if self[key] < other[key]: return True return False def expand(self, axisNames): """ Expand the location with zero values for all axes in axisNames that aren't filled in the current location. :: >>> l = Location(pop=1) >>> l.expand(['snap', 'crackle']) >>> print(l) """ for k in axisNames: if k not in self: self[k] = 0 def copy(self): """ Return a copy of this location. :: >>> l = Location(pop=1, snap=0) >>> l.copy() """ new = self.__class__() new.update(self) return new def fromTuple(self, locationTuple): """ Read the coordinates from a tuple. :: >>> t = (('pop', 1), ('snap', -100)) >>> l = Location() >>> l.fromTuple(t) >>> print(l) """ for key, value in locationTuple: try: self[key] = float(value) except TypeError: self[key] = tuple([float(v) for v in value]) def asTuple(self): """Return the location as a tuple. Sort the dimension names alphabetically. :: >>> l = Location(pop=1, snap=-100) >>> l.asTuple() (('pop', 1), ('snap', -100)) """ t = [] k = sorted(self.keys()) for key in k: t.append((key, self[key])) return tuple(t) def getType(self, short=False): """Return a string describing the type of the location, i.e. origin, on axis, off axis etc. :: >>> l = Location() >>> l.getType() 'origin' >>> l = Location(pop=1) >>> l.getType() 'on-axis, pop' >>> l = Location(pop=1, snap=1) >>> l.getType() 'off-axis, pop snap' >>> l = Location(pop=(1,2)) >>> l.getType() 'on-axis, pop, split' """ if self.isOrigin(): return "origin" t = [] onAxis = self.isOnAxis() if onAxis is False: if short: t.append("off-axis") else: t.append("off-axis, "+ " ".join(self.getActiveAxes())) else: if short: t.append("on-axis") else: t.append("on-axis, %s"%onAxis) if self.isAmbivalent(): t.append("split") return ', '.join(t) def getActiveAxes(self): """ Return a list of names of axes which are not zero :: >>> l = Location(pop=1, snap=0, crackle=1) >>> l.getActiveAxes() ['crackle', 'pop'] """ names = sorted(k for k in self.keys() if self[k]!=0) return names def asString(self, strict=False): """ Return the location as a string. :: >>> l = Location(pop=1, snap=(-100.0, -200)) >>> l.asString() 'pop:1, snap:(-100.000,-200.000)' """ if len(self.keys())==0: return "origin" v = [] n = [] try: for name, value in self.asTuple(): s = '' if value is None: s = "None" elif type(value) == tuple or type(value) == list: s = "(%.3f,%.3f)"%(value[0], value[1]) elif int(value) == value: s = "%d"%(int(value)) else: s = "%.3f"%(value) if s != '': n.append("%s:%s"%(name, s)) return ", ".join(n) except TypeError: import traceback print("Location value error:", name, value) for key, value in self.items(): print("\t\tkey:", key) print("\t\tvalue:", value) traceback.print_exc() return "error" def asDict(self): """ Return the location as a plain python dict. :: >>> l = Location(pop=1, snap=-100) >>> l.asDict()['snap'] -100 >>> l.asDict()['pop'] 1 """ new = {} new.update(self) return new def asSortedStringDict(self, roundValue=False): """ Return the data in a dict with sorted names and column titles. :: >>> l = Location(pop=1, snap=(1,10)) >>> l.asSortedStringDict()[0]['value'] '1' >>> l.asSortedStringDict()[0]['axis'] 'pop' >>> l.asSortedStringDict()[1]['axis'] 'snap' >>> l.asSortedStringDict()[1]['value'] '(1,10)' """ data = [] names = sorted(self.keys()) for n in names: data.append({'axis':n, 'value':numberToString(self[n])}) return data def strip(self): """ Remove coordinates that are zero, the opposite of expand(). :: >>> l = Location(pop=1, snap=0) >>> l.strip() """ result = [] for k, v in self.items(): if isinstance(v, tuple): if v > (_EPSILON, ) * len(v) or v < (-_EPSILON, ) * len(v): result.append((k, v)) elif v > _EPSILON or v < -_EPSILON: result.append((k, v)) return self.__class__(result) def common(self, other): """ Return two objects with the same dimensions if they lie in the same orthogonal plane. :: >>> l = Location(pop=1, snap=2) >>> m = Location(crackle=1, snap=3) >>> l.common(m) (, ) """ selfDim = set(self.keys()) otherDim = set(other.keys()) dims = selfDim | otherDim newSelf = None newOther = None for dim in dims: sd = self.get(dim, None) od = other.get(dim, None) if sd is None or od is None: # axis is missing in one or the other continue if -_EPSILON < sd < _EPSILON and -_EPSILON < od < _EPSILON: # values are both zero continue if newSelf is None: newSelf = self.__class__() if newOther is None: newOther = self.__class__() newSelf[dim] = self[dim] newOther[dim] = other[dim] return newSelf, newOther # # # tests # # def isOrigin(self): """ Return True if the location is at the origin. :: >>> l = Location(pop=1) >>> l.isOrigin() False >>> l = Location() >>> l.isOrigin() True """ for name, value in self.items(): if isinstance(value, tuple): if (value < (-_EPSILON,) * len(value) or value > (_EPSILON,) * len(value)): return False if value < -_EPSILON or value > _EPSILON: return False return True def isOnAxis(self): """ Returns statements about this location: * False if the location is not on-axis * The name of the axis if it is on-axis * None if the Location is at the origin Note: this is only valid for an unbiased location. :: >>> l = Location(pop=1) >>> l.isOnAxis() 'pop' >>> l = Location(pop=1, snap=1) >>> l.isOnAxis() False >>> l = Location() >>> l.isOnAxis() is None True """ new = self.__class__() new.update(self) s = new.strip() dims = list(s.keys()) if len(dims)> 1: return False elif len(dims)==1: return dims[0] return None def isAmbivalent(self, dim=None): """ Return True if any of the factors are in fact tuples. If a dimension name is given only that dimension is tested. :: >>> l = Location(pop=1) >>> l.isAmbivalent() False >>> l = Location(pop=1, snap=(100, -100)) >>> l.isAmbivalent() True """ if dim is not None: try: return isinstance(self[dim], tuple) except KeyError: # dimension is not present, it should be 0, so not ambivalent return False for dim, val in self.items(): if isinstance(val, tuple): return True return False def split(self): """ Split an ambivalent location into 2. One for the x, the other for the y. :: >>> l = Location(pop=(-5,5)) >>> l.split() (, ) """ x = self.__class__() y = self.__class__() for dim, val in self.items(): if isinstance(val, tuple): x[dim] = val[0] y[dim] = val[1] else: x[dim] = val y[dim] = val return x, y def spliceX(self): """ Return a copy with the x values preferred for ambivalent locations. :: >>> l = Location(pop=(-5,5)) >>> l.spliceX() """ new = self.__class__() for dim, val in self.items(): if isinstance(val, tuple): new[dim] = val[0] else: new[dim] = val return new def spliceY(self): """ Return a copy with the y values preferred for ambivalent locations. :: >>> l = Location(pop=(-5,5)) >>> l.spliceY() """ new = self.__class__() for dim, val in self.items(): if isinstance(val, tuple): new[dim] = val[1] else: new[dim] = val return new def distance(self, other=None): """Return the geometric distance to the other location. If no object is provided, this will calculate the distance to the origin. :: >>> l = Location(pop=100) >>> m = Location(pop=200) >>> l.distance(m) 100.0 >>> l = Location() >>> m = Location(pop=200) >>> l.distance(m) 200.0 >>> l = Location(pop=3, snap=5) >>> m = Location(pop=7, snap=8) >>> l.distance(m) 5.0 """ t = 0 if other is None: other = self.__class__() for axisName in set(self.keys()) | set(other.keys()): t += (other.get(axisName,0)-self.get(axisName,0))**2 return math.sqrt(t) def sameAs(self, other): """ Check if this is the same location. :: >>> l = Location(pop=5, snap=100) >>> m = Location(pop=5.0, snap=100.0) >>> l.sameAs(m) 0 >>> l = Location(pop=5, snap=100) >>> m = Location(pop=5.0, snap=100.0001) >>> l.sameAs(m) -1 """ if not hasattr(other, "get"): return -1 d = self.distance(other) if d < _EPSILON: return 0 return -1 # math operators def __add__(self, other): new = self.__class__() new.update(self) new.update(other) selfDim = set(self.keys()) otherDim = set(other.keys()) for key in selfDim & otherDim: ts = type(self[key])!=tuple to = type(other[key])!=tuple if ts: sx = sy = self[key] else: sx = self[key][0] sy = self[key][1] if to: ox = oy = other[key] else: ox = other[key][0] oy = other[key][1] x = sx+ox y = sy+oy if x==y: new[key] = x else: new[key] = x,y return new def __sub__(self, other): new = self.__class__() new.update(self) for key, value in other.items(): try: new[key] = -value except TypeError: new[key] = (-value[0], -value[1]) selfDim = set(self.keys()) otherDim = set(other.keys()) for key in selfDim & otherDim: ts = type(self[key])!=tuple to = type(other[key])!=tuple if ts: sx = sy = self[key] else: sx = self[key][0] sy = self[key][1] if to: ox = oy = other[key] else: ox = other[key][0] oy = other[key][1] x = sx-ox y = sy-oy if x==y: new[key] = x else: new[key] = x,y return new def __mul__(self, factor): new = self.__class__() if isinstance(factor, tuple): for key, value in self.items(): if type(value) == tuple: new[key] = factor[0] * value[0], factor[1] * value[1] else: new[key] = factor[0] * value, factor[1] * value else: for key, value in self.items(): if type(value) == tuple: new[key] = factor * value[0], factor * value[1] else: new[key] = factor * value return new __rmul__ = __mul__ def __truediv__(self, factor): if factor == 0: raise ZeroDivisionError if isinstance(factor, tuple): if factor[0] == 0 or factor[1] == 0: raise ZeroDivisionError return self * (1.0/factor[0]) + self * (1.0/factor[1]) return self * (1.0/factor) __div__ = __truediv__ def transform(self, transformDict): if transformDict is None: return self new = self.__class__() for dim, (offset, scale) in transformDict.items(): new[dim] = (self.get(dim,0)+offset)*scale return new def sortLocations(locations): """ Sort the locations by ranking: 1. all on-axis points 2. all off-axis points which project onto on-axis points these would be involved in master to master interpolations necessary for patching. Projecting off-axis masters have at least one coordinate in common with an on-axis master. 3. non-projecting off-axis points, 'wild' off axis points These would be involved in projecting limits and need to be patched. """ onAxis = [] onAxisValues = {} offAxis = [] offAxis_projecting = [] offAxis_wild = [] # first get the on-axis points for l in locations: if l.isOrigin(): continue if l.isOnAxis(): onAxis.append(l) for axis in l.keys(): if axis not in onAxisValues: onAxisValues[axis] = [] onAxisValues[axis].append(l[axis]) else: offAxis.append(l) for l in offAxis: ok = False for axis in l.keys(): if axis not in onAxisValues: continue if l[axis] in onAxisValues[axis]: ok = True if ok: offAxis_projecting.append(l) else: offAxis_wild.append(l) return onAxis, offAxis_projecting, offAxis_wild def biasFromLocations(locs, preferOrigin=True): """ Find the vector that translates the whole system to the origin. """ dims = {} locs.sort() for l in locs: for d in l.keys(): if not d in dims: dims[d] = [] v = l[d] if type(v)==tuple: dims[d].append(v[0]) dims[d].append(v[1]) else: dims[d].append(v) candidate = Location() for k in dims.keys(): dims[k].sort() v = mostCommon(dims[k]) if dims[k].count(v) > 1: # add the dimension with two or more hits candidate[k] = mostCommon(dims[k]) matches = [] # 1. do we have an exact match? for l in locs: if candidate == l: return l # 2. find a location that matches candidate (but has more dimensions) for l in locs: ok = True for k, v in candidate.items(): if l.get(k)!=v: ok = False break if ok: if not l in matches: matches.append(l) matches.sort() if len(matches)>0: if preferOrigin: for c in matches: if c.isOrigin(): return c return matches[0] # 3. no matches. Find the best from the available locations results = {} for bias in locs: rel = [] for l in locs: rel.append((l - bias).isOnAxis()) c = rel.count(False) if not c in results: results[c] = [] results[c].append(bias) if results: candidates = results[min(results.keys())] if preferOrigin: for c in candidates: if c.isOrigin(): return c candidates.sort() return candidates[0] return Location() def mostCommon(L): """ # http://stackoverflow.com/questions/1518522/python-most-common-element-in-a-list >>> mostCommon([1, 2, 2, 3]) 2 >>> mostCommon([1, 2, 3]) 1 >>> mostCommon([-1, 2, 3]) -1 >>> mostCommon([-1, -2, -3]) -1 >>> mostCommon([-1, -2, -3, -1]) -1 >>> mostCommon([-1, -1, -2, -2]) -1 >>> mostCommon([0, 0.125, 0.275, 1]) 0 >>> mostCommon([0, 0.1, 0.4, 0.4]) 0.4 """ # get an iterable of (item, iterable) pairs SL = sorted((x, i) for i, x in enumerate(L)) # print 'SL:', SL groups = itertools.groupby(SL, key=operator.itemgetter(0)) # auxiliary function to get "quality" for an item def _auxfun(g): item, iterable = g count = 0 min_index = len(L) for _, where in iterable: count += 1 min_index = min(min_index, where) # print 'item %r, count %r, minind %r' % (item, count, min_index) return count, -min_index # pick the highest-count/earliest item return max(groups, key=_auxfun)[0] if __name__ == "__main__": import doctest sys.exit(doctest.testmod().failed) MutatorMath-3.0.1/Lib/mutatorMath/objects/mutator.py000066400000000000000000000363161365052737200225140ustar00rootroot00000000000000# -*- coding: utf-8 -*- from mutatorMath.objects.error import MutatorError from mutatorMath.objects.location import Location, sortLocations, biasFromLocations import sys, warnings from operator import itemgetter __all__ = ['Mutator', 'buildMutator'] _EPSILON = sys.float_info.epsilon def noBend(loc): return loc def buildMutator(items, axes=None, bias=None): """ Build a mutator with the (location, obj) pairs in items. Determine the bias based on the given locations. """ from mutatorMath.objects.bender import Bender items = [(Location(loc),obj) for loc, obj in items] if bias is None: bias = Location() else: bias = Location(bias) m = Mutator() if axes is not None: # make a Bender object # but do not transform the locations from the items bender = Bender(axes) m.setBender(bender) else: bender = noBend # the order itself does not matter, but we should always build in the same order. items = sorted(items) if not bias: bias = biasFromLocations([loc for loc, obj in items], True) m.setBias(bias) n = None ofx = [] onx = [] for loc, obj in items: nn = (loc-bias) if nn.isOrigin(): m.setNeutral(obj) break if m.getNeutral() is None: raise MutatorError("Did not find a neutral for this system", items) for loc, obj in items: lb = loc-bias if lb.isOrigin(): continue if lb.isOnAxis(): onx.append((lb, obj-m.getNeutral())) else: ofx.append((lb, obj-m.getNeutral())) for loc, obj in onx: m.addDelta(loc, obj, punch=False, axisOnly=True) for loc, obj in ofx: m.addDelta(loc, obj, punch=True, axisOnly=True) return bias, m class Mutator(dict): """ Calculator for multi dimensional interpolations. :: # The mutator needs one neutral object. m = Mutator(myNeutralMathObject) # The mutator needs one or more deltas. m.addDelta(Location(pop=1), myMasterMathObject-myNeutralMathObject) # The mutator calculates instances at other locations. Remember to inflate. m.getInstance(Location(pop=0.5)) + myNeutralMathObject """ def __init__(self, neutral=None): self._axes = {} self._tags = {} self._bender = noBend self._neutral = neutral self._bias = Location() def setBender(self, bender): self._bender = bender def setBias(self, bias): self._bias = bias def getBias(self): return self._bias def setNeutral(self, aMathObject, deltaName="origin"): """Set the neutral object.""" self._neutral = aMathObject self.addDelta(Location(), aMathObject-aMathObject, deltaName, punch=False, axisOnly=True) def getNeutral(self): """Get the neutral object.""" return self._neutral def addDelta(self, location, aMathObject, deltaName = None, punch=False, axisOnly=True): """ Add a delta at this location. * location: a Location object * mathObject: a math-sensitive object * deltaName: optional string/token * punch: * True: add the difference with the instance value at that location and the delta * False: just add the delta. """ if punch: r = self.getInstance(location, axisOnly=axisOnly) if r is not None: self[location.asTuple()] = aMathObject-r, deltaName else: raise MutatorError("Could not get instance.") else: self[location.asTuple()] = aMathObject, deltaName # # info # def getAxisNames(self): """ Collect a set of axis names from all deltas. """ s = {} for l, x in self.items(): s.update(dict.fromkeys([k for k, v in l], None)) return set(s.keys()) def _collectAxisPoints(self): """ Return a dictionary with all on-axis locations. """ for l, (value, deltaName) in self.items(): location = Location(l) name = location.isOnAxis() if name is not None and name is not False: if name not in self._axes: self._axes[name] = [] if l not in self._axes[name]: self._axes[name].append(l) return self._axes def _collectOffAxisPoints(self): """ Return a dictionary with all off-axis locations. """ offAxis = {} for l, (value, deltaName) in self.items(): location = Location(l) name = location.isOnAxis() if name is None or name is False: offAxis[l] = 1 return list(offAxis.keys()) def collectLocations(self): """ Return a dictionary with all objects. """ pts = [] for l, (value, deltaName) in self.items(): pts.append(Location(l)) return pts def _allLocations(self): """ Return a list of all locations of all objects. """ l = [] for locationTuple in self.keys(): l.append(Location(locationTuple)) return l # # get instances # def getInstance(self, aLocation, axisOnly=False, getFactors=False): """ Calculate the delta at aLocation. * aLocation: a Location object, expected to be in bent space * axisOnly: * True: calculate an instance only with the on-axis masters. * False: calculate an instance with on-axis and off-axis masters. * getFactors: * True: return a list of the calculated factors. """ self._collectAxisPoints() factors = self.getFactors(aLocation, axisOnly) total = None for f, item, name in factors: if total is None: total = f * item continue total += f * item if total is None: total = 0 * self._neutral if getFactors: return total, factors return total def makeLocation(self, aLocation): if isinstance(aLocation, Location): return aLocation return Location(aLocation) def makeInstance(self, aLocation, bend=False): """ Calculate an instance with the right bias and add the neutral. aLocation: expected to be in input space """ aLocation = self.makeLocation(aLocation) if bend: aLocation = self._bender(aLocation) if not aLocation.isAmbivalent(): instanceObject = self.getInstance(aLocation-self._bias) else: locX, locY = aLocation.split() instanceObject = self.getInstance(locX-self._bias)*(1,0)+self.getInstance(locY-self._bias)*(0,1) return instanceObject+self._neutral def getFactors(self, aLocation, axisOnly=False, allFactors=False): """ Return a list of all factors and math items at aLocation. factor, mathItem, deltaName all = True: include factors that are zero or near-zero """ deltas = [] aLocation.expand(self.getAxisNames()) limits = getLimits(self._allLocations(), aLocation) for deltaLocationTuple, (mathItem, deltaName) in sorted(self.items()): deltaLocation = Location(deltaLocationTuple) deltaLocation.expand( self.getAxisNames()) factor = self._accumulateFactors(aLocation, deltaLocation, limits, axisOnly) if not (factor-_EPSILON < 0 < factor+_EPSILON) or allFactors: # only add non-zero deltas. deltas.append((factor, mathItem, deltaName)) deltas = sorted(deltas, key=itemgetter(0), reverse=True) return deltas # # calculate # def _accumulateFactors(self, aLocation, deltaLocation, limits, axisOnly): """ Calculate the factors of deltaLocation towards aLocation, """ relative = [] deltaAxis = deltaLocation.isOnAxis() if deltaAxis is None: relative.append(1) elif deltaAxis: deltasOnSameAxis = self._axes.get(deltaAxis, []) d = ((deltaAxis, 0),) if d not in deltasOnSameAxis: deltasOnSameAxis.append(d) if len(deltasOnSameAxis) == 1: relative.append(aLocation[deltaAxis] * deltaLocation[deltaAxis]) else: factor = self._calcOnAxisFactor(aLocation, deltaAxis, deltasOnSameAxis, deltaLocation) relative.append(factor) elif not axisOnly: factor = self._calcOffAxisFactor(aLocation, deltaLocation, limits) relative.append(factor) if not relative: return 0 f = None for v in relative: if f is None: f = v else: f *= v return f def _calcOnAxisFactor(self, aLocation, deltaAxis, deltasOnSameAxis, deltaLocation): """ Calculate the on-axis factors. """ if deltaAxis == "origin": f = 0 v = 0 else: f = aLocation[deltaAxis] v = deltaLocation[deltaAxis] i = [] iv = {} for value in deltasOnSameAxis: iv[Location(value)[deltaAxis]]=1 i = sorted(iv.keys()) r = 0 B, M, A = [], [], [] mA, mB, mM = None, None, None for value in i: if value < f: B.append(value) elif value > f: A.append(value) else: M.append(value) if len(B) > 0: mB = max(B) B.sort() if len(A) > 0: mA = min(A) A.sort() if len(M) > 0: mM = min(M) M.sort() if mM is not None: if ((f-_EPSILON < v) and (f+_EPSILON > v)) or f==v: r = 1 else: r = 0 elif mB is not None and mA is not None: if v < mB or v > mA: r = 0 else: if v == mA: r = float(f-mB)/(mA-mB) else: r = float(f-mA)/(mB-mA) elif mB is None and mA is not None: if v==A[1]: r = float(f-A[0])/(A[1]-A[0]) elif v == A[0]: r = float(f-A[1])/(A[0]-A[1]) else: r = 0 elif mB is not None and mA is None: if v == B[-2]: r = float(f-B[-1])/(B[-2]-B[-1]) elif v == mB: r = float(f-B[-2])/(B[-1]-B[-2]) else: r = 0 return r def _calcOffAxisFactor(self, aLocation, deltaLocation, limits): """ Calculate the off-axis factors. """ relative = [] for dim in limits.keys(): f = aLocation[dim] v = deltaLocation[dim] mB, M, mA = limits[dim] r = 0 if mA is not None and v > mA: relative.append(0) continue elif mB is not None and v < mB: relative.append(0) continue if f < v-_EPSILON: if mB is None: if M is not None and mA is not None: if v == M: r = (float(max(f,mA)-min(f, mA))/float(max(M,mA)-min(M, mA))) else: r = -(float(max(f,mA)-min(f, mA))/float(max(M,mA)-min(M, mA)) -1) else: r = 0 elif mA is None: r = 0 else: r = float(f-mB)/(mA-mB) elif f > v+_EPSILON: if mB is None: r = 0 elif mA is None: if M is not None and mB is not None: if v == M: r = (float(max(f,mB)-min(f, mB))/(max(mB, M)-min(mB, M))) else: r = -(float(max(f,mB)-min(f, mB))/(max(mB, M)-min(mB, M)) - 1) else: r = 0 else: r = float(mA-f)/(mA-mB) else: r = 1 relative.append(r) f = 1 for i in relative: f *= i return f def getLimits(locations, current, sortResults=True, verbose=False): """ Find the projections for each delta in the list of locations, relative to the current location. Return only the dimensions that are relevant for current. """ limit = {} for l in locations: a, b = current.common(l) if a is None: continue for name, value in b.items(): f = a[name] if name not in limit: limit[name] = {} limit[name]['<'] = {} limit[name]['='] = {} limit[name]['>'] = {} if f > 0: limit[name]['>'] = {0: [Location()]} elif f<0: limit[name]['<'] = {0: [Location()]} else: limit[name]['='] = {0: [Location()]} if current[name] < value - _EPSILON: if value not in limit[name]["<"]: limit[name]["<"][value] = [] limit[name]["<"][value].append(l) elif current[name] > value + _EPSILON: if value not in limit[name][">"]: limit[name][">"][value] = [] limit[name][">"][value].append(l) else: if value not in limit[name]["="]: limit[name]["="][value] = [] limit[name]["="][value].append(l) if not sortResults: return limit # now we have all the data, let's sort to the relevant values l = {} for name, lims in limit.items(): less = [] more = [] if lims[">"].keys(): less = sorted(lims[">"].keys()) lim_min = less[-1] else: lim_min = None if lims["<"].keys(): more = sorted(lims["<"].keys()) lim_max = more[0] else: lim_max = None if lim_min is None and lim_max is not None: # extrapolation < min if len(limit[name]['='])>0: l[name] = (None, list(limit[name]['='].keys())[0], None) elif len(more) > 1 and len(limit[name]['='])==0: # extrapolation l[name] = (None, more[0], more[1]) elif lim_min is not None and lim_max is None: # extrapolation < max if len(limit[name]['='])>0: # less > 0, M > 0, more = None # -> end of a limit l[name] = (None, limit[name]['='], None) elif len(less) > 1 and len(limit[name]['='])==0: # less > 0, M = None, more = None # extrapolation l[name] = (less[-2], less[-1], None) else: if len(limit[name]['=']) > 0: l[name] = (None, list(limit[name]['='].keys())[0], None) else: l[name] = (lim_min, None, lim_max) return l if __name__ == "__main__": import doctest sys.exit(doctest.testmod().failed) MutatorMath-3.0.1/Lib/mutatorMath/test/000077500000000000000000000000001365052737200177645ustar00rootroot00000000000000MutatorMath-3.0.1/Lib/mutatorMath/test/__init__.py000066400000000000000000000000631365052737200220740ustar00rootroot00000000000000""" Some tests for implementation of MutatorMath"""MutatorMath-3.0.1/Lib/mutatorMath/test/objects/000077500000000000000000000000001365052737200214155ustar00rootroot00000000000000MutatorMath-3.0.1/Lib/mutatorMath/test/objects/__init__.py000066400000000000000000000000001365052737200235140ustar00rootroot00000000000000MutatorMath-3.0.1/Lib/mutatorMath/test/objects/location.py000066400000000000000000000405451365052737200236070ustar00rootroot00000000000000from mutatorMath.objects.location import Location, biasFromLocations, sortLocations def _testBiasFromLocations(bias, locs): """ # Find the designspace vector for the best bias. # Test results: (, ) >>> locs = [Location(a=10), Location(a=10, b=10, c=10), Location(a=10, c=15), Location(a=5, c=15)] >>> bias = biasFromLocations(locs) >>> bias >>> _testBiasFromLocations(bias, locs) (2, 1) >>> locs = [Location(a=10, b=0), Location(a=5, b=10), Location(a=20, b=0)] >>> bias = biasFromLocations(locs) >>> bias >>> _testBiasFromLocations(bias, locs) (1, 1) >>> locs = [Location(a=10, b=300), Location(a=20, b=300), Location(a=20, b=600), Location(a=30, b=300)] >>> bias = biasFromLocations(locs) >>> bias >>> _testBiasFromLocations(bias, locs) (3, 0) >>> locs = [Location(a=-10, b=300), Location(a=0, b=400), Location(a=20, b=300)] >>> bias = biasFromLocations(locs) >>> bias >>> _testBiasFromLocations(bias, locs) (1, 1) >>> locs = [Location(wt=0, wd=500), ... Location(wt=1000, wd=900), ... Location(wt=1200, wd=900), ... Location(wt=-200, wd=600), ... Location(wt=0, wd=600), ... Location(wt=1000, wd=600), ... Location(wt=1200, wd=600), ... Location(wt=-200, wd=300), ... Location(wt=0, wd=300),] >>> bias = biasFromLocations(locs) >>> bias >>> _testBiasFromLocations(bias, locs) (5, 3) >>> locs = [ ... Location(wt=1, sz=0), ... Location(wt=0, sz=0), ... Location(wt=0.275, sz=0), ... Location(wt=0.275, sz=1), ... Location(wt=1, sz=1), ... Location(wt=0.125, sz=0.4), ... Location(wt=1, sz=0.4), ... Location(wt=0.6, sz=0.4), ... Location(wt=0, sz=0.4), ... Location(wt=0.275, sz=0.4), ... Location(wt=0, sz=1), ... Location(wt=0.125, sz=1), ... Location(wt=0.6, sz=0), ... Location(wt=0.125, sz=0),] >>> bias = biasFromLocations(locs) >>> bias >>> _testBiasFromLocations(bias, locs) (6, 7) # Nothing lines up >>> locs = [ ... Location(pop=1), ... Location(snap=1), ... Location(crackle=1)] >>> bias = biasFromLocations(locs) >>> bias >>> _testBiasFromLocations(bias, locs) (0, 2) # ... why crackle? because it sorts first >>> locs.sort() >>> locs [, , ] # Two things line up >>> locs = [ ... Location(pop=-1), ... Location(pop=1),] >>> bias = biasFromLocations(locs) >>> bias >>> _testBiasFromLocations(bias, locs) (1, 0) # Two things line up >>> locs = [ ... Location(pop=-1, snap=-1), ... Location(pop=1, snap=0), ... Location(pop=1, snap=1), ... Location(pop=1, snap=1), ... ] >>> bias = biasFromLocations(locs) >>> bias >>> _testBiasFromLocations(bias, locs) (2, 1) # Almost Nothing Lines Up 1 # An incomplete set of masters can # create a situation in which there is nothing to interpolate. # However, we still need to find a bias. >>> locs = [ ... Location(wt=1, sz=0.4), ... Location(wt=0.275, sz=0.4), ... Location(wt=0, sz=1), ... Location(wt=0.125, sz=0),] >>> bias = biasFromLocations(locs) >>> bias >>> _testBiasFromLocations(bias, locs) (1, 2) # Almost Nothing Lines Up 2 >>> locs = [ ... Location(wt=1, sz=0.4), ... Location(wt=0.275, sz=0.4), ... Location(wt=0, sz=1), ... Location(wt=0.6, sz=1), ... Location(wt=0.125, sz=0),] >>> bias = biasFromLocations(locs) >>> bias >>> _testBiasFromLocations(bias, locs) (1, 3) # A square on the origin >>> locs = [ ... Location(wt=0, wd=0), ... Location(wt=1, wd=0), ... Location(wt=0, wd=1), ... Location(wt=1, wd=1),] >>> bias = biasFromLocations(locs) >>> bias >>> _testBiasFromLocations(bias, locs) (2, 1) # A square, not on the origin >>> locs = [ ... Location(wt=100, wd=100), ... Location(wt=200, wd=100), ... Location(wt=100, wd=200), ... Location(wt=200, wd=200),] >>> bias = biasFromLocations(locs) >>> bias >>> _testBiasFromLocations(bias, locs) (2, 1) # A square, not on the origin >>> locs = [ ... Location(wt=200, wd=100), ... Location(wt=100, wd=200), ... Location(wt=200, wd=200),] >>> bias = biasFromLocations(locs) >>> bias >>> _testBiasFromLocations(bias, locs) (2, 0) # Two axes, three masters >>> locs = [ ... Location(ct=0, wd=0), ... Location(ct=0, wd=1000), ... Location(ct=100, wd=1000),] >>> bias = biasFromLocations(locs) >>> bias >>> _testBiasFromLocations(bias, locs) (2, 0) # Complex 4 D space >>> locs = [ ... Location(A=0, H=0, G=1000, W=0), ... Location(A=0, H=0, G=1000, W=700), ... Location(A=0, H=0, G=1000, W=1000), ... Location(A=0, H=1000, G=0, W=200), ... Location(A=0, H=1000, G=0, W=300), ... Location(A=0, H=1000, G=0, W=700), ... Location(A=0, H=1000, G=0, W=1000), ... Location(A=1000, H=0, G=0, W=0),] >>> bias = biasFromLocations(locs) >>> bias >>> locs = [ ... Location(S=0, U=0, Wt=54, Wd=385), ... Location(S=0, U=268, Wt=54, Wd=1000), ... Location(S=8, U=550, Wt=851, Wd=126), ... Location(S=8, U=868, Wt=1000, Wd=1000),] >>> bias = biasFromLocations(locs) >>> bias # empty locs >>> locs = [] >>> bias = biasFromLocations(locs) >>> bias """ rel = [] # translate the test locations over the bias for l in locs: rel.append((l - bias).isOnAxis()) # MUST have one origin assert None in rel # how many end up off-axis? offAxis = rel.count(False) # how many end up on-axis? onAxis = len(rel)-offAxis-1 # a good bias has more masters at on-axis locations. return onAxis, offAxis def test_common(): """ Make a new location with only the dimensions that the two have in common. >>> a = Location(pop=.25, snap=.5, snip=10) >>> b = Location(pop=-.35, snap=.6, pip=10) >>> [n.asTuple() for n in a.common(b)] [(('pop', 0.25), ('snap', 0.5)), (('pop', -0.35), ('snap', 0.6))] """ def test_misc(): """ >>> l = Location(apop=-1, bpop=10, cpop=-100) >>> l.isOnAxis() False # remove empty dimensions >>> a = Location(pop=.25, snap=1, plop=0) >>> a.strip().asTuple() (('pop', 0.25), ('snap', 1)) # add dimensions, set to 0 >>> a = Location(pop=.25, snap=1) >>> a.expand(['plop', 'flop']) >>> a.asTuple() (('flop', 0), ('plop', 0), ('pop', 0.25), ('snap', 1)) # create a location from a list of name / value tuples. >>> a = Location() >>> t = [('weight', 1), ('width', 2), ('zip', 3)] >>> a.fromTuple(t) >>> a """ def test_onAxis(): """ # origin will return None >>> l = Location(pop=0, aap=0, lalala=0, poop=0) >>> l.isOnAxis() # on axis will return axis name >>> l = Location(pop=0, aap=1, lalala=0, poop=0) >>> l.isOnAxis() 'aap' # off axis will return False >>> l = Location(pop=0, aap=1, lalala=1, poop=0) >>> l.isOnAxis() False """ def test_distance(): """ # Hypotenuse distance between two locations. >>> a = Location(pop=0, snap=0) >>> b = Location(pop=100, snap=0) >>> a.distance(b) 100.0 >>> a = Location(pop=0, snap=3) >>> b = Location(pop=4, snap=0) >>> a.distance(b) 5.0 >>> a = Location() >>> b = Location(pop=3, snap=4) >>> a.distance(b) 5.0 """ def test_limits_sorts(): """ Test some of the functions that handle Locations. # get the extent of a group of locations. >>> a = Location(pop=.25, snap=1, plop=0) >>> b = Location(pop=-1, aap=10) >>> c = Location(pop=.25, snap=.5) >>> d = Location(pop=.35, snap=1) >>> e = Location(pop=1) >>> f = Location(snap=1) >>> l = [a, b, c, d, e, f] >>> test = Location(pop=.5, snap=.5) >>> from mutatorMath.objects.mutator import getLimits >>> limits = getLimits(l, test) >>> 'snap' in limits and 'pop' in limits True >>> limits['snap'] (None, 0.5, None) >>> limits['pop'] (0.35, None, 1) # sort a group of locations >>> sortLocations(l) ([, ], [, ], [, ]) >>> a1, a2, a3 = sortLocations(l) # assert that each location in a1 is on axis, >>> sum([a.isOnAxis() is not None and a.isOnAxis() is not False for a in a1]) 2 # assert that each location in a1 is off axis, >>> sum([a.isOnAxis() is False for a in a2]) 2 # how to test for wild locations? Can only see if they're offAxis. Relevant? >>> sum([a.isOnAxis() is False for a in a3]) 2 """ def test_ambivalence(): """ Test ambivalence qualities of locations. >>> a = Location(pop=(.25, .33), snap=1, plop=0) >>> b = Location(pop=.25, snap=1, plop=0) >>> a.isAmbivalent() True >>> b.isAmbivalent() False >>> a.spliceX().asTuple() == (('plop', 0), ('pop', 0.25), ('snap', 1)) True >>> a.spliceY().asTuple() == (('plop', 0), ('pop', 0.33), ('snap', 1)) True >>> b.spliceX() == b.spliceY() True >>> a = Location(pop=(.25, .33), snap=1, plop=0) >>> a * 2 >>> a * (2,0) """ def test_asString(): """ Test the conversions to string. >>> a = Location(pop=(.25, .33), snap=1.0, plop=0) >>> assert a.asString(strict=False) == "plop:0, pop:(0.250,0.330), snap:1" >>> assert a.asString(strict=True) == "plop:0, pop:(0.250,0.330), snap:1" >>> a = Location(pop=0) >>> assert a.asString() == "pop:0" >>> assert a.asString(strict=True) == "pop:0" >>> a = Location(pop=-1, sip=1) >>> assert a.asString() == "pop:-1, sip:1" >>> assert a.asString(strict=True) == "pop:-1, sip:1" >>> a = Location() >>> assert a.asString() == "origin" # more string conversions >>> a = Location(pop=1) >>> assert a.asSortedStringDict() == [{'value': '1', 'axis': 'pop'}] >>> a = Location(pop=(0,1)) >>> assert a.asSortedStringDict() == [{'value': '(0,1)', 'axis': 'pop'}] # a description of the type of location >>> assert Location(a=1, b=0, c=0).getType() == "on-axis, a" >>> assert Location(a=1, b=2).getType() == "off-axis, a b" >>> assert Location(a=1).getType() == "on-axis, a" >>> assert Location(a=(1,1), b=2).getType() == "off-axis, a b, split" >>> assert Location().getType() == "origin" """ def test_comparisons(): """ Test the math comparison qualities. The equal operator is useful. The < and > operators make assumptions about the geometry that might not be appropriate. >>> a = Location() >>> a.isOrigin() True >>> b = Location(pop=2) >>> c = Location(pop=2) >>> b.distance(a) 2.0 >>> a.distance(b) 2.0 >>> assert (a>b) == False >>> assert (c>> assert (c==b) == True """ def test_sorting(): """ Test the sorting qualities. >>> a = Location(pop=0) >>> b = Location(pop=1) >>> c = Location(pop=2) >>> d = Location(pop=-1) >>> l = [b, d, a, c] >>> l.sort() >>> l [, , , ] >>> e = Location(pop=1, snap=1) >>> l = [a, e] >>> l.sort() >>> l [, ] >>> f = Location(pop=-1, snap=-1) >>> l = [a, e, f] >>> l.sort() >>> l [, , ] >>> l = [Location(pop=-1), Location(pop=1)] >>> l.sort() >>> l [, ] """ def test_basicMath(): """ Test the basic mathematical properties of Location. # addition >>> Location(a=1) + Location(a=2) == Location(a=3) True # addition of ambivalent location >>> Location(a=1) + Location(a=(2, 1)) == Location(a=(3,2)) True # subtraction >>> Location(a=2) - Location(a=1) == Location(a=1) True # subtraction of ambivalent location >>> Location(a=1) - Location(a=(2, 1)) == Location(a=(-1,0)) True >>> Location(a=(1,4)) - Location(a=(2, 1)) == Location(a=(-1,3)) True >>> Location(a=(2,1)) - Location(a=(2, 1)) == Location(a=0) True # multiplication >>> Location(a=3) * 3 == Location(a=9) True # multiplication of ambivalent location >>> Location(a=(2, 1)) * 3 == Location(a=(6,3)) True # division >>> Location(a=10) / 2 == Location(a=5) True >>> Location(a=10, b=6) / 2 == Location(a=5, b=3) True # should raise zero division error >>> hasRaisedError = False >>> try: ... Location(a=5) / 0 ... except ZeroDivisionError: ... hasRaisedError = True >>> assert hasRaisedError # interpolation >>> a = Location(a=(100, 200)) >>> b = Location(a=(0, 0)) >>> f = 0 >>> a+f*(b-a) == Location(a=(100,200)) True >>> f = 0.5 >>> a+f*(b-a) == Location(a=(50,100)) True >>> f = 1 >>> a+f*(b-a) == Location(a=0) True """ def test17(): """ See if getLimits can deal with ambiguous locations. >>> a = Location(pop=(0.25, 4), snap=1, plop=0) >>> print(a.split()) (, ) """ def regressionTests(): """ Test all the basic math operations >>> assert Location(a=1) + Location(a=2) == Location(a=3) # addition >>> assert Location(a=1.0) - Location(a=2.0) == Location(a=-1.0) # subtraction >>> assert Location(a=1.0) * 2 == Location(a=2.0) # multiplication >>> assert Location(a=1.0) * 0 == Location(a=0.0) # multiplication >>> assert Location(a=2.0) / 2 == Location(a=1.0) # division >>> assert Location(a=(1,2)) * 2 == Location(a=(2,4)) # multiplication with ambivalence >>> assert Location(a=(2,4)) / 2 == Location(a=(1,2)) # division with ambivalence >>> assert Location(a=(2,4)) - Location(a=1) == Location(a=(1,3)) """ if __name__ == '__main__': import sys import doctest sys.exit(doctest.testmod().failed) MutatorMath-3.0.1/Lib/mutatorMath/test/objects/mutator.py000066400000000000000000000167071365052737200234750ustar00rootroot00000000000000from mutatorMath.objects.error import MutatorError from mutatorMath.objects.location import Location, sortLocations, biasFromLocations from mutatorMath.objects.mutator import Mutator, buildMutator, getLimits def test_singleAxis(n): """ Tests for a single axis. A mutator is created with a single value, 100, on a single axis. values we enter should be reproduced >>> test_singleAxis(1) 100.0 >>> test_singleAxis(0) 0 a value in the middle should be in the middle >>> test_singleAxis(.5) 50.0 >>> test_singleAxis(.99) 99.0 extrapolation over zero >>> test_singleAxis(-1) -100.0 >>> test_singleAxis(-2) -200.0 >>> test_singleAxis(-1.5) -150.0 extrapolation over value >>> test_singleAxis(2) 200.0 """ m = Mutator() neutral = 0 value = 100.0 m.setNeutral(neutral-neutral) m.addDelta(Location(pop=1), value-neutral, deltaName="test") return m.getInstance(Location(pop=n)) + neutral def test_twoAxes(l, n): """Test for a system with two axes, 2 values both on-axis. >>> test_twoAxes(1, 1) 0.0 >>> test_twoAxes(1, 0) 100.0 >>> test_twoAxes(0, 1) -100.0 >>> test_twoAxes(2, 0) 200.0 >>> test_twoAxes(0, 2) -200.0 a value in the middle should be in the middle """ m = Mutator() neutral = 0 value = 100.0 m.setNeutral(neutral-neutral) m.addDelta(Location(pop=1), value-neutral, deltaName="test1") m.addDelta(Location(snap=1), -1*value-neutral, deltaName="test2") return m.getInstance(Location(pop=l, snap=n)) + neutral def test_twoAxesOffAxis(l, n): """Test for a system with two axes. Three values, two on-axis, one off-axis. >>> test_twoAxesOffAxis(0, 0) 0 >>> test_twoAxesOffAxis(1, 1) 50.0 >>> test_twoAxesOffAxis(2, 2) 200.0 >>> test_twoAxesOffAxis(1, 0) 100.0 >>> test_twoAxesOffAxis(0, 1) -100.0 >>> test_twoAxesOffAxis(2, 0) 200.0 >>> test_twoAxesOffAxis(0, 2) -200.0 a value in the middle should be in the middle """ m = Mutator() neutral = 0 value = 100.0 m.setNeutral(neutral-neutral) m.addDelta(Location(pop=1), value-neutral, deltaName="test1") m.addDelta(Location(snap=1), -1*value-neutral, deltaName="test2") m.addDelta(Location(pop=1, snap=1), 50, punch=True, deltaName="test2") return m.getInstance(Location(pop=l, snap=n)) + neutral def test_twoAxesOffAxisSmall(l, n): """Test for a system with two axes. Three values, two on-axis, one off-axis. >>> test_twoAxesOffAxisSmall(0, 0) 0 >>> test_twoAxesOffAxisSmall(1, 1) 5e-16 >>> test_twoAxesOffAxisSmall(2, 2) 2e-15 >>> test_twoAxesOffAxisSmall(1, 0) 1e-15 >>> test_twoAxesOffAxisSmall(0, 1) -1e-15 >>> test_twoAxesOffAxisSmall(2, 0) 2e-15 >>> test_twoAxesOffAxisSmall(0, 2) -2e-15 a value in the middle should be in the middle """ m = Mutator() neutral = 0 value = 1e-15 m.setNeutral(neutral-neutral) m.addDelta(Location(pop=1), value-neutral, deltaName="test1") m.addDelta(Location(snap=1), -1*value-neutral, deltaName="test2") m.addDelta(Location(pop=1, snap=1), 0.5*value-neutral, punch=True, deltaName="test2") return m.getInstance(Location(pop=l, snap=n)) + neutral def test_getLimits(a, b, t): """Test the getLimits function >>> test_getLimits(0, 1, 0) {'pop': (None, 0, None)} >>> test_getLimits(0, 1, 0.5) {'pop': (0, None, 1)} >>> test_getLimits(0, 1, 1) {'pop': (None, {1: []}, None)} """ la = Location(pop=a) lb = Location(pop=b) locations = [la, lb] test = Location(pop=t) print(getLimits(locations, test)) def test_methods(): """ Test some of the methods. >>> m = test_methods() >>> sorted(list(m.getAxisNames())) ['pop', 'snap'] """ m = Mutator() neutral = 0 value = 100.0 m.setNeutral(neutral-neutral) m.addDelta(Location(pop=1), value-neutral, deltaName="test1") m.addDelta(Location(snap=1), -1*value-neutral, deltaName="test2") m.addDelta(Location(pop=1, snap=1), 50, punch=True, deltaName="test2") return m def test_builder(): """ Test the mutator builder. >>> items = [ ... (Location(pop=1, snap=1), 1), ... (Location(pop=2, snap=1), 2), ... (Location(pop=3, snap=1), 3), ... (Location(pop=1, snap=2), 4), ... (Location(pop=2, snap=2), 5), ... (Location(pop=3, snap=2), 6), ... ] >>> bias, mb = buildMutator(items) >>> bias >>> mb.makeInstance(Location(pop=1, snap=1)) 1 >>> mb.makeInstance(Location(pop=1, snap=2)) 4 >>> mb.makeInstance(Location(pop=3, snap=2)) 6 >>> mb.makeInstance(Location(pop=3, snap=1.5)) 4.5 """ def test_builderBender_1(): """ Test the mutator builder with a warp dict >>> items = [ ... (Location(pop=0), 0), ... (Location(pop=10), 10), ... ] >>> axisdict = dict(pop = dict(name='pop', minimum=0, maximum=1000, default=0, map=[(0,0), (5, 2), (7, 7), (10,10)])) >>> bias, mb = buildMutator(items, axisdict) >>> bias >>> sorted(mb.items()) [((), (0, 'origin')), ((('pop', 10),), (10, None))] >>> mb.makeInstance(Location(pop=0), bend=True) 0 >>> mb.makeInstance(Location(pop=1), bend=True) 0.4 >>> mb.makeInstance(Location(pop=2), bend=True) 0.8 >>> mb.makeInstance(Location(pop=3), bend=True) 1.2 >>> mb.makeInstance(Location(pop=4), bend=True) 1.6 >>> mb.makeInstance(Location(pop=5), bend=True) 2.0 >>> mb.makeInstance(Location(pop=6), bend=True) 4.5 >>> mb.makeInstance(Location(pop=7), bend=True) 7.0 >>> mb.makeInstance(Location(pop=8), bend=True) 7.999999999999999 >>> mb.makeInstance(Location(pop=9), bend=True) 9.0 >>> mb.makeInstance(Location(pop=10), bend=True) 10 """ def test_builderBender_2(): """ Test the mutator builder with a warp dict >>> items = [ ... (Location(pop=0), 0), ... #(Location(pop=7), 5), ... (Location(pop=10), 10), ... ] >>> axisdict = dict(pop = dict(name='pop', minimum=0, maximum=1000, default=0, map=[(0,0), (5, 2), (10,10)])) >>> bias, mb = buildMutator(items, axisdict) >>> bias >>> sorted(mb.items()) [((), (0, 'origin')), ((('pop', 10),), (10, None))] >>> mb.makeInstance(Location(pop=0), bend=True) 0 >>> r = [] >>> for p in range(0, 11): ... r.append(mb.makeInstance(Location(pop=p), bend=True)) >>> r [0, 0.4, 0.8, 1.2, 1.6, 2.0, 3.5999999999999996, 5.2, 6.799999999999999, 8.4, 10] """ def test_builderBender_3(): """ Test case from https://github.com/googlefonts/fontmake/pull/552#issuecomment-493617252 >>> masters = [ ... (Location(Width=70), 0), ... (Location(Width=100), 100), ... ] >>> axes = { ... 'Width': { ... 'tag': 'wdth', ... 'name': 'Width', ... 'minimum': 62.5, ... 'default': 100.0, ... 'maximum': 100.0, ... 'map': [(62.5, 70.0), (75.0, 79.0), (87.5, 89.0), (100.0, 100.0)], ... } ... } >>> _, mut = buildMutator(masters, axes) >>> mut.makeInstance(Location(Width=79), bend=False) 30.0 >>> mut.makeInstance(Location(Width=75), bend=True) 30.0 """ if __name__ == "__main__": import sys import doctest sys.exit(doctest.testmod().failed) MutatorMath-3.0.1/Lib/mutatorMath/test/run.py000066400000000000000000000037771365052737200211600ustar00rootroot00000000000000import os import sys import unittest import doctest # The 'mutatorMath.test' sub-package is not installed along with the others. # But we need it to be importable so we can use it in setup.py 'test_suite'. # We would also like to run the test suite against an installed version of # mutatorMath package, and not just against the Lib/mutatorMath source # directory, in order to catch issues with packaging. # Therefore, below we first import mutatorMath, so whatever 'mutatorMath' is # first found on the PYTHONPATH will be loaded in sys.modules. # Then, we temporarily extend the PYTHONPATH to include the location of the # 'mutatorMath.test' sub-package, relative to the current 'run.py' script. # This way we can import the doctest modules without also attempting to # import the 'mutatorMath.test' sub-package, which may be missing when we # testing an installed mutatorMath, vs the 'editable' Lib/mutatorMath. import mutatorMath HERE = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) sys.path.insert(0, HERE) try: import test.objects.mutator import test.objects.location import test.ufo.test import test.ufo.geometryTest import test.ufo.kerningTest import test.ufo.mutingTest finally: sys.path.remove(HERE) def load_tests(loader, tests, ignore): # doctests inline in the actual Location and Mutator objects comments tests.addTests(doctest.DocTestSuite(mutatorMath.objects.location)) tests.addTests(doctest.DocTestSuite(mutatorMath.objects.mutator)) # standalone Location and Mutator doctests tests.addTests(doctest.DocTestSuite(test.objects.mutator)) tests.addTests(doctest.DocTestSuite(test.objects.location)) # doctests in the test.ufo package tests.addTests(doctest.DocTestSuite(test.ufo.test)) tests.addTests(doctest.DocTestSuite(test.ufo.geometryTest)) tests.addTests(doctest.DocTestSuite(test.ufo.kerningTest)) tests.addTests(doctest.DocTestSuite(test.ufo.mutingTest)) return tests if __name__ == '__main__': sys.exit(unittest.main()) MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/000077500000000000000000000000001365052737200205555ustar00rootroot00000000000000MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/__init__.py000066400000000000000000000000671365052737200226710ustar00rootroot00000000000000""" Some tests for the UFO implementation of Mutator"""MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/buildExample.py000066400000000000000000000006031365052737200235410ustar00rootroot00000000000000# -*- coding: utf-8 -*- """ Example of a build script for a specific mutatorMath project. A script like this could live near the designspace description. It could be called to build a bunch of instances without opening any application. """ from mutatorMath.ufo import build import os here = os.path.join(os.getcwd(), 'data') build(here, outputUFOFormatVersion=2) print('done') MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/000077500000000000000000000000001365052737200214665ustar00rootroot00000000000000MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/axes_test.designspace000066400000000000000000000014561365052737200257020ustar00rootroot00000000000000 MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/exporttest_basic.designspace000066400000000000000000000042541365052737200272640ustar00rootroot00000000000000 testnote123 MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/exporttest_build.designspace000066400000000000000000000016521365052737200273010ustar00rootroot00000000000000 MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/exporttest_info.designspace000066400000000000000000000020301365052737200271240ustar00rootroot00000000000000 MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/exporttest_kerning.designspace000066400000000000000000000020451365052737200276340ustar00rootroot00000000000000 MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/exporttest_kerning_muted.designspace000066400000000000000000000016761365052737200310430ustar00rootroot00000000000000 MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/000077500000000000000000000000001365052737200231515ustar00rootroot00000000000000MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/bold/000077500000000000000000000000001365052737200240715ustar00rootroot00000000000000MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/bold/BoldCondensed.ufo/000077500000000000000000000000001365052737200273645ustar00rootroot00000000000000MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/bold/BoldCondensed.ufo/features.fea000066400000000000000000000000521365052737200316540ustar00rootroot00000000000000# this is the feature from boldcondensed. MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/bold/BoldCondensed.ufo/fontinfo.plist000066400000000000000000000033601365052737200322650ustar00rootroot00000000000000 ascender 800 capHeight 800 copyright License same as MutatorMath. BSD 3-clause. [test-token: A] descender -200 familyName MutatorMathTest guidelines italicAngle 0 openTypeNameLicense License same as MutatorMath. BSD 3-clause. [test-token: A] openTypeOS2VendorID LTTR postscriptBlueValues postscriptDefaultWidthX 500 postscriptFamilyBlues postscriptFamilyOtherBlues postscriptFontName MutatorMathTest-BoldCondensed postscriptFullName MutatorMathTest BoldCondensed postscriptOtherBlues postscriptSlantAngle 0 postscriptStemSnapH postscriptStemSnapV postscriptWindowsCharacterSet 1 styleMapFamilyName styleMapStyleName regular styleName BoldCondensed unitsPerEm 1000 versionMajor 1 versionMinor 2 xHeight 500 year 2004 MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/bold/BoldCondensed.ufo/glyphs.background/000077500000000000000000000000001365052737200330105ustar00rootroot00000000000000contents.plist000066400000000000000000000002741365052737200356460ustar00rootroot00000000000000MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/bold/BoldCondensed.ufo/glyphs.background layerinfo.plist000066400000000000000000000003541365052737200360000ustar00rootroot00000000000000MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/bold/BoldCondensed.ufo/glyphs.background color 0.5,1,0,0.7 MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/bold/BoldCondensed.ufo/glyphs/000077500000000000000000000000001365052737200306725ustar00rootroot00000000000000MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/bold/BoldCondensed.ufo/glyphs/A_.glif000066400000000000000000000016611365052737200320600ustar00rootroot00000000000000 MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/bold/BoldCondensed.ufo/glyphs/B_.glif000066400000000000000000000033571365052737200320650ustar00rootroot00000000000000 MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/bold/BoldCondensed.ufo/glyphs/C_.glif000066400000000000000000000026041365052737200320600ustar00rootroot00000000000000 MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/bold/BoldCondensed.ufo/glyphs/D_.glif000066400000000000000000000021501365052737200320550ustar00rootroot00000000000000 MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/bold/BoldCondensed.ufo/glyphs/E_.glif000066400000000000000000000016601365052737200320630ustar00rootroot00000000000000 MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/bold/BoldCondensed.ufo/glyphs/F_.glif000066400000000000000000000013531365052737200320630ustar00rootroot00000000000000 MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/bold/BoldCondensed.ufo/glyphs/G_.glif000066400000000000000000000033121365052737200320610ustar00rootroot00000000000000 MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/bold/BoldCondensed.ufo/glyphs/H_.glif000066400000000000000000000013471365052737200320700ustar00rootroot00000000000000 MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/bold/BoldCondensed.ufo/glyphs/I_.glif000066400000000000000000000013451365052737200320670ustar00rootroot00000000000000 MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/bold/BoldCondensed.ufo/glyphs/J_.glif000066400000000000000000000014661365052737200320740ustar00rootroot00000000000000 MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/bold/BoldCondensed.ufo/glyphs/K_.glif000066400000000000000000000015501365052737200320670ustar00rootroot00000000000000 MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/bold/BoldCondensed.ufo/glyphs/L_.glif000066400000000000000000000010361365052737200320670ustar00rootroot00000000000000 MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/bold/BoldCondensed.ufo/glyphs/M_.glif000066400000000000000000000016231365052737200320720ustar00rootroot00000000000000 MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/bold/BoldCondensed.ufo/glyphs/N_.glif000066400000000000000000000013451365052737200320740ustar00rootroot00000000000000 MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/bold/BoldCondensed.ufo/glyphs/O_.glif000066400000000000000000000025451365052737200321000ustar00rootroot00000000000000 MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/bold/BoldCondensed.ufo/glyphs/P_.glif000066400000000000000000000021561365052737200320770ustar00rootroot00000000000000 MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/bold/BoldCondensed.ufo/glyphs/Q_.glif000066400000000000000000000005731365052737200321010ustar00rootroot00000000000000 MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/bold/BoldCondensed.ufo/glyphs/R_.glif000066400000000000000000000026521365052737200321020ustar00rootroot00000000000000 MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/bold/BoldCondensed.ufo/glyphs/S_.glif000066400000000000000000000045051365052737200321020ustar00rootroot00000000000000 com.typemytype.robofont.Image.Brightness 0 com.typemytype.robofont.Image.Contrast 1 com.typemytype.robofont.Image.Saturation 1 com.typemytype.robofont.Image.Sharpness 0.4 MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/bold/BoldCondensed.ufo/glyphs/T_.glif000066400000000000000000000010421365052737200320740ustar00rootroot00000000000000 MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/bold/BoldCondensed.ufo/glyphs/U_.glif000066400000000000000000000016501365052737200321020ustar00rootroot00000000000000 MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/bold/BoldCondensed.ufo/glyphs/V_.glif000066400000000000000000000013441365052737200321030ustar00rootroot00000000000000 MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/bold/BoldCondensed.ufo/glyphs/W_.glif000066400000000000000000000015321365052737200321030ustar00rootroot00000000000000 MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/bold/BoldCondensed.ufo/glyphs/X_.glif000066400000000000000000000014401365052737200321020ustar00rootroot00000000000000 MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/bold/BoldCondensed.ufo/glyphs/Y_.glif000066400000000000000000000013541365052737200321070ustar00rootroot00000000000000 MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/bold/BoldCondensed.ufo/glyphs/Z_.glif000066400000000000000000000013501365052737200321040ustar00rootroot00000000000000 MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/bold/BoldCondensed.ufo/glyphs/colon.glif000066400000000000000000000003621365052737200326500ustar00rootroot00000000000000 MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/bold/BoldCondensed.ufo/glyphs/comma.glif000066400000000000000000000010111365052737200326220ustar00rootroot00000000000000 MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/bold/BoldCondensed.ufo/glyphs/contents.plist000066400000000000000000000030751365052737200336110ustar00rootroot00000000000000 A A_.glif B B_.glif C C_.glif D D_.glif E E_.glif F F_.glif G G_.glif H H_.glif I I_.glif J J_.glif K K_.glif L L_.glif M M_.glif N N_.glif O O_.glif P P_.glif Q Q_.glif R R_.glif S S_.glif T T_.glif U U_.glif V V_.glif W W_.glif X X_.glif Y Y_.glif Z Z_.glif colon colon.glif comma comma.glif period period.glif quotedblleft quotedblleft.glif quotedblright quotedblright.glif semicolon semicolon.glif space space.glif layerinfo.plist000066400000000000000000000005301365052737200336560ustar00rootroot00000000000000MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/bold/BoldCondensed.ufo/glyphs color 1,0.75,0,0.7 lib com.typemytype.robofont.segmentType curve MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/bold/BoldCondensed.ufo/glyphs/period.glif000066400000000000000000000005361365052737200330230ustar00rootroot00000000000000 quotedblleft.glif000066400000000000000000000005051365052737200341500ustar00rootroot00000000000000MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/bold/BoldCondensed.ufo/glyphs quotedblright.glif000066400000000000000000000004101365052737200343260ustar00rootroot00000000000000MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/bold/BoldCondensed.ufo/glyphs MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/bold/BoldCondensed.ufo/glyphs/semicolon.glif000066400000000000000000000003651365052737200335310ustar00rootroot00000000000000 MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/bold/BoldCondensed.ufo/glyphs/space.glif000066400000000000000000000002321365052737200326250ustar00rootroot00000000000000 MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/bold/BoldCondensed.ufo/groups.plist000066400000000000000000000005201365052737200317550ustar00rootroot00000000000000 public.kern1.@MMK_L_A A public.kern2.@MMK_R_A A MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/bold/BoldCondensed.ufo/kerning.plist000066400000000000000000000073401365052737200321020ustar00rootroot00000000000000 A J -20 O -30 T -70 U -30 V -50 B A -20 J -50 O -20 S -10 T -10 U -20 V -30 C A -20 J -50 T -20 V -20 E J -20 T -10 V -10 F A -40 J -80 O -10 S -20 U -10 V -10 G J -20 S -10 T -40 U -10 V -30 H J -30 S -10 T -10 J J -70 L J -20 O -20 T -110 U -20 V -60 O A -30 J -60 S -10 T -30 V -30 P A -50 J -100 S -10 T -10 U -10 V -20 R H -10 J -20 O -30 S -20 T -30 U -30 V -40 S A -20 H -20 J -40 O -10 S -10 T -30 U -10 V -30 W -10 T A -65 H -10 J -130 O -20 U A -30 J -60 S -10 V -10 V J -100 O -30 S -20 U -10 MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/bold/BoldCondensed.ufo/layercontents.plist000066400000000000000000000005371365052737200333400ustar00rootroot00000000000000 foreground glyphs background glyphs.background MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/bold/BoldCondensed.ufo/lib.plist000066400000000000000000000147761365052737200312260ustar00rootroot00000000000000 com.typemytype.robofont.background.layerStrokeColor 0.0 0.8 0.2 0.7 com.typemytype.robofont.compileSettings.autohint com.typemytype.robofont.compileSettings.checkOutlines com.typemytype.robofont.compileSettings.createDummyDSIG com.typemytype.robofont.compileSettings.decompose com.typemytype.robofont.compileSettings.generateFormat 0 com.typemytype.robofont.compileSettings.releaseMode com.typemytype.robofont.foreground.layerStrokeColor 0.5 0.0 0.5 0.7 com.typemytype.robofont.italicSlantOffset 0 com.typemytype.robofont.segmentType curve com.typemytype.robofont.shouldAddPointsInSplineConversion 1 com.typesupply.defcon.sortDescriptor ascending space A B C D E F G H I J K L M N O P Q R S T U V W X Y Z a b c d e f g h i j k l m n ntilde o p q r s t u v w x y z zcaron zero one two three four five six seven eight nine underscore hyphen endash emdash parenleft parenright bracketleft bracketright braceleft braceright numbersign percent period comma colon semicolon exclam question slash backslash bar at ampersand paragraph bullet dollar trademark fi fl .notdef a_b_c Atilde Adieresis Acircumflex Aring Ccedilla Agrave Aacute quotedblright quotedblleft type glyphList public.glyphOrder space A B C D E F G H I J K L M N O P Q R S T U V W X Y Z a b c d e f g h i j k l m n ntilde o p q r s t u v w x y z zcaron zero one two three four five six seven eight nine underscore hyphen endash emdash parenleft parenright bracketleft bracketright braceleft braceright numbersign percent period comma colon semicolon exclam question slash backslash bar at ampersand paragraph bullet dollar trademark fi fl .notdef a_b_c Atilde Adieresis Acircumflex Aring Ccedilla Agrave Aacute quotedblright quotedblleft MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/bold/BoldCondensed.ufo/metainfo.plist000066400000000000000000000004451365052737200322460ustar00rootroot00000000000000 creator org.robofab.ufoLib formatVersion 3 MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/bold/BoldWide.ufo/000077500000000000000000000000001365052737200263525ustar00rootroot00000000000000MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/bold/BoldWide.ufo/features.fea000066400000000000000000000000441365052737200306430ustar00rootroot00000000000000# this is the feature from BoldWide MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/bold/BoldWide.ufo/fontinfo.plist000066400000000000000000000033411365052737200312520ustar00rootroot00000000000000 ascender 800 capHeight 800 copyright License same as MutatorMath. BSD 3-clause. [test-token: B] descender -200 familyName MutatorMathTest guidelines italicAngle 0 openTypeNameLicense License same as MutatorMath. BSD 3-clause. [test-token: B] openTypeOS2VendorID LTTR postscriptBlueValues postscriptDefaultWidthX 500 postscriptFamilyBlues postscriptFamilyOtherBlues postscriptFontName MutatorMathTest-BoldWide postscriptFullName MutatorMathTest BoldWide postscriptOtherBlues postscriptSlantAngle 0 postscriptStemSnapH postscriptStemSnapV postscriptWindowsCharacterSet 1 styleMapFamilyName styleMapStyleName regular styleName BoldWide unitsPerEm 1000 versionMajor 1 versionMinor 2 xHeight 500 year 2004 MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/bold/BoldWide.ufo/glyphs.background/000077500000000000000000000000001365052737200317765ustar00rootroot00000000000000contents.plist000066400000000000000000000002741365052737200346340ustar00rootroot00000000000000MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/bold/BoldWide.ufo/glyphs.background layerinfo.plist000066400000000000000000000003541365052737200347660ustar00rootroot00000000000000MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/bold/BoldWide.ufo/glyphs.background color 0.5,1,0,0.7 MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/bold/BoldWide.ufo/glyphs/000077500000000000000000000000001365052737200276605ustar00rootroot00000000000000MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/bold/BoldWide.ufo/glyphs/A_.glif000066400000000000000000000016631365052737200310500ustar00rootroot00000000000000 MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/bold/BoldWide.ufo/glyphs/B_.glif000066400000000000000000000033741365052737200310520ustar00rootroot00000000000000 MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/bold/BoldWide.ufo/glyphs/C_.glif000066400000000000000000000026121365052737200310450ustar00rootroot00000000000000 MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/bold/BoldWide.ufo/glyphs/D_.glif000066400000000000000000000021541365052737200310470ustar00rootroot00000000000000 MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/bold/BoldWide.ufo/glyphs/E_.glif000066400000000000000000000016651365052737200310560ustar00rootroot00000000000000 MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/bold/BoldWide.ufo/glyphs/F_.glif000066400000000000000000000013601365052737200310470ustar00rootroot00000000000000 MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/bold/BoldWide.ufo/glyphs/G_.glif000066400000000000000000000033221365052737200310500ustar00rootroot00000000000000 MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/bold/BoldWide.ufo/glyphs/H_.glif000066400000000000000000000013541365052737200310540ustar00rootroot00000000000000 MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/bold/BoldWide.ufo/glyphs/I_.glif000066400000000000000000000013461365052737200310560ustar00rootroot00000000000000 MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/bold/BoldWide.ufo/glyphs/J_.glif000066400000000000000000000014671365052737200310630ustar00rootroot00000000000000 MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/bold/BoldWide.ufo/glyphs/K_.glif000066400000000000000000000015531365052737200310600ustar00rootroot00000000000000 MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/bold/BoldWide.ufo/glyphs/L_.glif000066400000000000000000000010411365052737200310510ustar00rootroot00000000000000 MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/bold/BoldWide.ufo/glyphs/M_.glif000066400000000000000000000016271365052737200310640ustar00rootroot00000000000000 MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/bold/BoldWide.ufo/glyphs/N_.glif000066400000000000000000000013511365052737200310570ustar00rootroot00000000000000 MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/bold/BoldWide.ufo/glyphs/O_.glif000066400000000000000000000025531365052737200310650ustar00rootroot00000000000000 MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/bold/BoldWide.ufo/glyphs/P_.glif000066400000000000000000000021641365052737200310640ustar00rootroot00000000000000 MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/bold/BoldWide.ufo/glyphs/Q_.glif000066400000000000000000000005741365052737200310700ustar00rootroot00000000000000 MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/bold/BoldWide.ufo/glyphs/R_.glif000066400000000000000000000026641365052737200310730ustar00rootroot00000000000000 MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/bold/BoldWide.ufo/glyphs/S_.glif000066400000000000000000000037251365052737200310730ustar00rootroot00000000000000 MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/bold/BoldWide.ufo/glyphs/T_.glif000066400000000000000000000010451365052737200310650ustar00rootroot00000000000000 MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/bold/BoldWide.ufo/glyphs/U_.glif000066400000000000000000000020151365052737200310640ustar00rootroot00000000000000 MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/bold/BoldWide.ufo/glyphs/V_.glif000066400000000000000000000013461365052737200310730ustar00rootroot00000000000000 MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/bold/BoldWide.ufo/glyphs/W_.glif000066400000000000000000000015351365052737200310740ustar00rootroot00000000000000 MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/bold/BoldWide.ufo/glyphs/X_.glif000066400000000000000000000014431365052737200310730ustar00rootroot00000000000000 MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/bold/BoldWide.ufo/glyphs/Y_.glif000066400000000000000000000013561365052737200310770ustar00rootroot00000000000000 MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/bold/BoldWide.ufo/glyphs/Z_.glif000066400000000000000000000013561365052737200311000ustar00rootroot00000000000000 MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/bold/BoldWide.ufo/glyphs/colon.glif000066400000000000000000000003621365052737200316360ustar00rootroot00000000000000 MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/bold/BoldWide.ufo/glyphs/comma.glif000066400000000000000000000010121365052737200316110ustar00rootroot00000000000000 MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/bold/BoldWide.ufo/glyphs/contents.plist000066400000000000000000000032731365052737200325770ustar00rootroot00000000000000 A A_.glif B B_.glif C C_.glif D D_.glif E E_.glif F F_.glif G G_.glif H H_.glif I I_.glif J J_.glif K K_.glif L L_.glif M M_.glif N N_.glif O O_.glif P P_.glif Q Q_.glif R R_.glif S S_.glif T T_.glif U U_.glif V V_.glif W W_.glif X X_.glif Y Y_.glif Z Z_.glif colon colon.glif comma comma.glif period period.glif quotedblbase quotedblbase.glif quotedblleft quotedblleft.glif quotedblright quotedblright.glif quotesinglbase quotesinglbase.glif semicolon semicolon.glif space space.glif MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/bold/BoldWide.ufo/glyphs/layerinfo.plist000066400000000000000000000005301365052737200327230ustar00rootroot00000000000000 color 1,0.75,0,0.7 lib com.typemytype.robofont.segmentType curve MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/bold/BoldWide.ufo/glyphs/period.glif000066400000000000000000000005361365052737200320110ustar00rootroot00000000000000 MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/bold/BoldWide.ufo/glyphs/quotedblbase.glif000066400000000000000000000004031365052737200331720ustar00rootroot00000000000000 MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/bold/BoldWide.ufo/glyphs/quotedblleft.glif000066400000000000000000000005051365052737200332150ustar00rootroot00000000000000 MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/bold/BoldWide.ufo/glyphs/quotedblright.glif000066400000000000000000000004101365052737200333730ustar00rootroot00000000000000 MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/bold/BoldWide.ufo/glyphs/quotesinglbase.glif000066400000000000000000000003151365052737200335470ustar00rootroot00000000000000 MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/bold/BoldWide.ufo/glyphs/semicolon.glif000066400000000000000000000003651365052737200325170ustar00rootroot00000000000000 MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/bold/BoldWide.ufo/glyphs/space.glif000066400000000000000000000002321365052737200316130ustar00rootroot00000000000000 MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/bold/BoldWide.ufo/groups.plist000066400000000000000000000005201365052737200307430ustar00rootroot00000000000000 public.kern1.@MMK_L_A A public.kern2.@MMK_R_A A MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/bold/BoldWide.ufo/layercontents.plist000066400000000000000000000005371365052737200323260ustar00rootroot00000000000000 foreground glyphs background glyphs.background MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/bold/BoldWide.ufo/lib.plist000066400000000000000000000331041365052737200301760ustar00rootroot00000000000000 com.typemytype.robofont.background.layerStrokeColor 0.0 0.8 0.2 0.7 com.typemytype.robofont.compileSettings.autohint com.typemytype.robofont.compileSettings.checkOutlines com.typemytype.robofont.compileSettings.createDummyDSIG com.typemytype.robofont.compileSettings.decompose com.typemytype.robofont.compileSettings.generateFormat 0 com.typemytype.robofont.compileSettings.releaseMode com.typemytype.robofont.foreground.layerStrokeColor 0.5 0.0 0.5 0.7 com.typemytype.robofont.italicSlantOffset 0 com.typemytype.robofont.segmentType curve com.typemytype.robofont.shouldAddPointsInSplineConversion 1 com.typesupply.defcon.sortDescriptor ascending space A Agrave Aacute Acircumflex Atilde Adieresis Aring B C Ccedilla D E Egrave Eacute Ecircumflex Edieresis F G H I Igrave Iacute Icircumflex Idieresis J K L M N Ntilde O Ograve Oacute Ocircumflex Otilde Odieresis P Q R S Scaron T U Ugrave Uacute Ucircumflex Udieresis V W X Y Yacute Ydieresis Z Zcaron AE Eth Oslash Thorn Lslash OE a agrave aacute acircumflex atilde adieresis aring b c ccedilla d e egrave eacute ecircumflex edieresis f g h i igrave iacute icircumflex idieresis j k l m n ntilde o ograve oacute ocircumflex otilde odieresis p q r s scaron t u ugrave uacute ucircumflex udieresis v w x y yacute ydieresis z zcaron ordfeminine ordmasculine germandbls ae eth oslash thorn dotlessi lslash oe zero one two three four five six seven eight nine onesuperior twosuperior threesuperior onequarter onehalf threequarters underscore hyphen endash emdash parenleft parenright bracketleft bracketright braceleft braceright numbersign percent perthousand quotesingle quotedbl quoteleft quoteright quotedblleft quotedblright quotesinglbase quotedblbase guilsinglleft guilsinglright guillemotleft guillemotright asterisk dagger daggerdbl period comma colon semicolon ellipsis exclam exclamdown question questiondown slash backslash fraction bar brokenbar at ampersand section paragraph periodcentered bullet plus minus plusminus divide multiply equal less greater logicalnot mu dollar cent sterling currency yen Euro florin asciicircum asciitilde acute grave hungarumlaut circumflex caron breve tilde macron dieresis dotaccent ring cedilla ogonek copyright registered trademark degree fi fl .notdef a_b_c type glyphList public.glyphOrder space A Agrave Aacute Acircumflex Atilde Adieresis Aring B C Ccedilla D E Egrave Eacute Ecircumflex Edieresis F G H I Igrave Iacute Icircumflex Idieresis J K L M N Ntilde O Ograve Oacute Ocircumflex Otilde Odieresis P Q R S Scaron T U Ugrave Uacute Ucircumflex Udieresis V W X Y Yacute Ydieresis Z Zcaron AE Eth Oslash Thorn Lslash OE a agrave aacute acircumflex atilde adieresis aring b c ccedilla d e egrave eacute ecircumflex edieresis f g h i igrave iacute icircumflex idieresis j k l m n ntilde o ograve oacute ocircumflex otilde odieresis p q r s scaron t u ugrave uacute ucircumflex udieresis v w x y yacute ydieresis z zcaron ordfeminine ordmasculine germandbls ae eth oslash thorn dotlessi lslash oe zero one two three four five six seven eight nine onesuperior twosuperior threesuperior onequarter onehalf threequarters underscore hyphen endash emdash parenleft parenright bracketleft bracketright braceleft braceright numbersign percent perthousand quotesingle quotedbl quoteleft quoteright quotedblleft quotedblright quotesinglbase quotedblbase guilsinglleft guilsinglright guillemotleft guillemotright asterisk dagger daggerdbl period comma colon semicolon ellipsis exclam exclamdown question questiondown slash backslash fraction bar brokenbar at ampersand section paragraph periodcentered bullet plus minus plusminus divide multiply equal less greater logicalnot mu dollar cent sterling currency yen Euro florin asciicircum asciitilde acute grave hungarumlaut circumflex caron breve tilde macron dieresis dotaccent ring cedilla ogonek copyright registered trademark degree fi fl .notdef a_b_c MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/bold/BoldWide.ufo/metainfo.plist000066400000000000000000000004451365052737200312340ustar00rootroot00000000000000 creator org.robofab.ufoLib formatVersion 3 MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/cleanup.py000066400000000000000000000001501365052737200251460ustar00rootroot00000000000000for f in AllFonts(): for g in f: l = g.getLayer("background") l.clear() f.save()MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/fixSpacing.py000066400000000000000000000040271365052737200256210ustar00rootroot00000000000000 for f in AllFonts(): if 'Wide' in f.info.styleName: wk = 'wide' else: wk = 'cond' if 'Bold' in f.info.styleName: bk = 'bold' else: bk = 'light' print 'current', wk, bk groups = { "L_straight": ['B', 'D', 'E', 'F', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'P', 'R', 'U', 'Z', "quotesinglbase", "quotedblbase", "period", "comma", "colon", "semicolon", "quotedblright", "quotedblleft"], "R_straight": [ 'E', 'F', 'G', 'H', 'I', 'J', 'M', 'N', 'U', 'Z', "quotesinglbase", "quotedblbase", "period", "comma", "colon", "semicolon", "quotedblright", "quotedblleft"], "L_angled": ['A', 'V', 'W', 'X', 'Y'], "R_angled": ['A', 'K', 'V', 'W', 'X', 'Y'], "L_short": ['T', ], "R_short": ['L', 'T', 'P', 'R', 'B', ], "L_round": ['C','G', 'O', 'Q', 'S'], "R_round": ['C', 'D', 'O', 'Q', 'S'], } margins = { 'bold': { 'straight': dict(cond=30, wide=60), 'angled': dict(cond=10, wide=20), 'round': dict(cond=20, wide=40), 'short': dict(cond=20, wide=20), }, 'light': { 'straight': dict(cond=60, wide=120), 'angled': dict(cond=20, wide=40), 'round': dict(cond=50, wide=80), 'short': dict(cond=30, wide=40), }, } for n in f.keys(): #print n for k, v in groups.items(): if n in v: parts = k.split("_") left = None right = None if parts[0] == "L": left = margins[bk].get(parts[1])[wk] #print "links", n, k, left elif parts[0] == "R": right = margins[bk].get(parts[1])[wk] #print "rechts", n, k, right if n in f: if left: f[n].leftMargin = left if right: f[n].rightMargin = right MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/intermediate/000077500000000000000000000000001365052737200256235ustar00rootroot00000000000000MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/intermediate/IntermediateCondensed.ufo/000077500000000000000000000000001365052737200326505ustar00rootroot00000000000000features.fea000066400000000000000000000001041365052737200350570ustar00rootroot00000000000000MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/intermediate/IntermediateCondensed.ufo# this is the feature from lightCondensed # Hi_this_is_the_feature. fontinfo.plist000066400000000000000000000024001365052737200354640ustar00rootroot00000000000000MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/intermediate/IntermediateCondensed.ufo ascender 763.459275 capHeight 763.459275 copyright Copyright-token-string descender -200 familyName Intermediate italicAngle 0 openTypeOS2VendorID ADBE postscriptBlueValues postscriptDefaultWidthX 500 postscriptFamilyBlues postscriptFamilyOtherBlues postscriptOtherBlues postscriptSlantAngle 0 postscriptStemSnapH postscriptStemSnapV postscriptWindowsCharacterSet 1 styleName Narrow unitsPerEm 1000.0 versionMajor 1 versionMinor 2 xHeight 500 glyphs/000077500000000000000000000000001365052737200340775ustar00rootroot00000000000000MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/intermediate/IntermediateCondensed.ufoA_.glif000066400000000000000000000016771365052737200352740ustar00rootroot00000000000000MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/intermediate/IntermediateCondensed.ufo/glyphs B_.glif000066400000000000000000000043511365052737200352650ustar00rootroot00000000000000MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/intermediate/IntermediateCondensed.ufo/glyphs C_.glif000066400000000000000000000035771365052737200352770ustar00rootroot00000000000000MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/intermediate/IntermediateCondensed.ufo/glyphs D_.glif000066400000000000000000000027671365052737200353000ustar00rootroot00000000000000MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/intermediate/IntermediateCondensed.ufo/glyphs E_.glif000066400000000000000000000016661365052737200352760ustar00rootroot00000000000000MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/intermediate/IntermediateCondensed.ufo/glyphs F_.glif000066400000000000000000000016331365052737200352710ustar00rootroot00000000000000MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/intermediate/IntermediateCondensed.ufo/glyphs G_.glif000066400000000000000000000044031365052737200352700ustar00rootroot00000000000000MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/intermediate/IntermediateCondensed.ufo/glyphs H_.glif000066400000000000000000000016221365052737200352710ustar00rootroot00000000000000MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/intermediate/IntermediateCondensed.ufo/glyphs I_.glif000066400000000000000000000016601365052737200352740ustar00rootroot00000000000000MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/intermediate/IntermediateCondensed.ufo/glyphs J_.glif000066400000000000000000000017401365052737200352740ustar00rootroot00000000000000MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/intermediate/IntermediateCondensed.ufo/glyphs K_.glif000066400000000000000000000021401365052737200352700ustar00rootroot00000000000000MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/intermediate/IntermediateCondensed.ufo/glyphs L_.glif000066400000000000000000000012341365052737200352740ustar00rootroot00000000000000MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/intermediate/IntermediateCondensed.ufo/glyphs M_.glif000066400000000000000000000020511365052737200352730ustar00rootroot00000000000000MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/intermediate/IntermediateCondensed.ufo/glyphs N_.glif000066400000000000000000000016501365052737200353000ustar00rootroot00000000000000MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/intermediate/IntermediateCondensed.ufo/glyphs O_.glif000066400000000000000000000034701365052737200353030ustar00rootroot00000000000000MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/intermediate/IntermediateCondensed.ufo/glyphs P_.glif000066400000000000000000000027451365052737200353100ustar00rootroot00000000000000MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/intermediate/IntermediateCondensed.ufo/glyphs Q_.glif000066400000000000000000000006761365052737200353120ustar00rootroot00000000000000MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/intermediate/IntermediateCondensed.ufo/glyphs R_.glif000066400000000000000000000036351365052737200353110ustar00rootroot00000000000000MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/intermediate/IntermediateCondensed.ufo/glyphs S_.glif000066400000000000000000000052231365052737200353050ustar00rootroot00000000000000MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/intermediate/IntermediateCondensed.ufo/glyphs T_.glif000066400000000000000000000012231365052737200353020ustar00rootroot00000000000000MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/intermediate/IntermediateCondensed.ufo/glyphs U_.glif000066400000000000000000000023621365052737200353100ustar00rootroot00000000000000MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/intermediate/IntermediateCondensed.ufo/glyphs V_.glif000066400000000000000000000016541365052737200353140ustar00rootroot00000000000000MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/intermediate/IntermediateCondensed.ufo/glyphs colon.glif000066400000000000000000000004201365052737200360500ustar00rootroot00000000000000MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/intermediate/IntermediateCondensed.ufo/glyphs comma.glif000066400000000000000000000012161365052737200360360ustar00rootroot00000000000000MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/intermediate/IntermediateCondensed.ufo/glyphs contents.plist000066400000000000000000000030441365052737200370120ustar00rootroot00000000000000MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/intermediate/IntermediateCondensed.ufo/glyphs A A_.glif B B_.glif C C_.glif D D_.glif E E_.glif F F_.glif G G_.glif H H_.glif I I_.glif J J_.glif K K_.glif L L_.glif M M_.glif N N_.glif O O_.glif P P_.glif Q Q_.glif R R_.glif S S_.glif T T_.glif U U_.glif V V_.glif colon colon.glif comma comma.glif period period.glif quotedblbase quotedblbase.glif quotedblleft quotedblleft.glif quotedblright quotedblright.glif quotesinglbase quotesinglbase.glif semicolon semicolon.glif space space.glif period.glif000066400000000000000000000006571365052737200362340ustar00rootroot00000000000000MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/intermediate/IntermediateCondensed.ufo/glyphs quotedblbase.glif000066400000000000000000000003531365052737200374150ustar00rootroot00000000000000MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/intermediate/IntermediateCondensed.ufo/glyphs quotedblleft.glif000066400000000000000000000006041365052737200374340ustar00rootroot00000000000000MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/intermediate/IntermediateCondensed.ufo/glyphs quotedblright.glif000066400000000000000000000004521365052737200376200ustar00rootroot00000000000000MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/intermediate/IntermediateCondensed.ufo/glyphs quotesinglbase.glif000066400000000000000000000003011365052737200377610ustar00rootroot00000000000000MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/intermediate/IntermediateCondensed.ufo/glyphs semicolon.glif000066400000000000000000000004151365052737200367320ustar00rootroot00000000000000MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/intermediate/IntermediateCondensed.ufo/glyphs space.glif000066400000000000000000000002341365052737200360340ustar00rootroot00000000000000MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/intermediate/IntermediateCondensed.ufo/glyphs kerning.plist000066400000000000000000000102511365052737200353020ustar00rootroot00000000000000MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/intermediate/IntermediateCondensed.ufo @MMK_L_A V -36.540724999999995 A J -12.691855 O -19.037782500000002 T -44.4214925 U -19.037782500000002 V -31.729637500000003 B A -12.691855 J -31.729637500000003 O -12.691855 S -6.3459275 T -6.3459275 U -12.691855 V -19.037782500000002 C A -12.691855 J -31.729637500000003 T -12.691855 V -12.691855 E J -12.691855 T -6.3459275 V -6.3459275 F A -25.38371 J -50.76742 O -6.3459275 S -12.691855 U -6.3459275 V -6.3459275 G J -12.691855 S -6.3459275 T -25.38371 U -6.3459275 V -19.037782500000002 H J -19.037782500000002 S -6.3459275 T -6.3459275 J J -44.4214925 L J -12.691855 O -12.691855 T -69.80520250000001 U -12.691855 V -38.075565000000005 O A -19.037782500000002 J -38.075565000000005 S -6.3459275 T -19.037782500000002 V -19.037782500000002 P A -31.729637500000003 J -63.459275000000005 S -6.3459275 T -6.3459275 U -6.3459275 V -12.691855 R H -6.3459275 J -12.691855 O -19.037782500000002 S -12.691855 T -19.037782500000002 U -19.037782500000002 V -25.38371 S A -12.691855 H -12.691855 J -25.38371 O -6.3459275 S -6.3459275 T -19.037782500000002 U -6.3459275 V -19.037782500000002 W -6.3459275 T A -41.248528750000006 H -6.3459275 J -82.49705750000001 O -12.691855 U A -19.037782500000002 J -38.075565000000005 S -6.3459275 V -6.3459275 V @MMK_R_A -36.540724999999995 J -63.459275000000005 O -19.037782500000002 S -12.691855 U -6.3459275 lib.plist000066400000000000000000000455621365052737200344300ustar00rootroot00000000000000MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/intermediate/IntermediateCondensed.ufo com.typemytype.robofont.background.layerStrokeColor 0.0 0.8 0.2 0.7 com.typemytype.robofont.compileSettings.autohint com.typemytype.robofont.compileSettings.checkOutlines com.typemytype.robofont.compileSettings.createDummyDSIG com.typemytype.robofont.compileSettings.decompose com.typemytype.robofont.compileSettings.generateFormat 0 com.typemytype.robofont.compileSettings.releaseMode com.typemytype.robofont.foreground.layerStrokeColor 0.5 0.0 0.5 0.7 com.typemytype.robofont.italicSlantOffset 0 com.typemytype.robofont.layerOrder background com.typemytype.robofont.segmentType curve com.typemytype.robofont.shouldAddPointsInSplineConversion 1 com.typemytype.robofont.sort ascending space A Agrave Aacute Acircumflex Atilde Adieresis Aring B C Ccedilla D E Egrave Eacute Ecircumflex Edieresis F G H I Igrave Iacute Icircumflex Idieresis J K L M N Ntilde O Ograve Oacute Ocircumflex Otilde Odieresis P Q R S Scaron T U Ugrave Uacute Ucircumflex Udieresis V W X Y Yacute Ydieresis Z Zcaron AE Eth Oslash Thorn Lslash OE a agrave aacute acircumflex atilde adieresis aring b c ccedilla d e egrave eacute ecircumflex edieresis f g h i igrave iacute icircumflex idieresis j k l m n ntilde o ograve oacute ocircumflex otilde odieresis p q r s scaron t u ugrave uacute ucircumflex udieresis v w x y yacute ydieresis z zcaron ordfeminine ordmasculine germandbls ae eth oslash thorn dotlessi lslash oe mu zero one two three four five six seven eight nine onesuperior twosuperior threesuperior onequarter onehalf threequarters underscore hyphen endash emdash parenleft parenright bracketleft bracketright braceleft braceright numbersign percent perthousand quotesingle quotedbl quoteleft quoteright quotedblleft quotedblright quotesinglbase quotedblbase guilsinglleft guilsinglright asterisk dagger daggerdbl period comma colon semicolon ellipsis exclam exclamdown question questiondown slash backslash fraction bar brokenbar at ampersand section paragraph periodcentered bullet plus minus plusminus divide multiply equal less greater logicalnot dollar cent sterling currency yen Euro asciicircum asciitilde acute grave hungarumlaut circumflex caron breve tilde macron dieresis dotaccent ring cedilla ogonek copyright registered trademark degree florin guillemotleft guillemotright fi fl a_b_c .notdef type glyphList com.typesupply.MetricsMachine4.groupColors @MMK_L_A 1.0 0.0 0.0 0.25 @MMK_L_C 1.0 0.5 0.0 0.25 @MMK_L_E 1.0 1.0 0.0 0.25 @MMK_L_I 0.0 1.0 0.0 0.25 @MMK_L_N 0.0 1.0 1.0 0.25 @MMK_L_O 0.0 0.5 1.0 0.25 @MMK_L_S 0.0 0.0 1.0 0.25 @MMK_L_U 0.5 0.0 1.0 0.25 @MMK_L_Y 1.0 0.0 1.0 0.25 @MMK_L_Z 1.0 0.0 0.5 0.25 @MMK_L_a 1.0 0.0 0.0 0.25 @MMK_L_c 1.0 0.5 0.0 0.25 @MMK_L_e 1.0 1.0 0.0 0.25 @MMK_L_i 0.0 1.0 0.0 0.25 @MMK_L_n 0.0 1.0 1.0 0.25 @MMK_L_o 0.0 0.5 1.0 0.25 @MMK_L_s 0.0 0.0 1.0 0.25 @MMK_L_u 0.5 0.0 1.0 0.25 @MMK_L_y 1.0 0.0 1.0 0.25 @MMK_L_z 1.0 0.0 0.5 0.25 @MMK_R_A 1.0 0.0 0.0 0.25 @MMK_R_C 1.0 0.5 0.0 0.25 @MMK_R_E 1.0 1.0 0.0 0.25 @MMK_R_I 0.0 1.0 0.0 0.25 @MMK_R_N 0.0 1.0 1.0 0.25 @MMK_R_O 0.0 0.5 1.0 0.25 @MMK_R_S 0.0 0.0 1.0 0.25 @MMK_R_U 0.5 0.0 1.0 0.25 @MMK_R_Y 1.0 0.0 1.0 0.25 @MMK_R_Z 1.0 0.0 0.5 0.25 @MMK_R_a 1.0 0.0 0.0 0.25 @MMK_R_c 1.0 0.5 0.0 0.25 @MMK_R_e 1.0 1.0 0.0 0.25 @MMK_R_i 0.0 1.0 0.0 0.25 @MMK_R_n 0.0 1.0 1.0 0.25 @MMK_R_o 0.0 0.5 1.0 0.25 @MMK_R_s 0.0 0.0 1.0 0.25 @MMK_R_u 0.5 0.0 1.0 0.25 @MMK_R_y 1.0 0.0 1.0 0.25 @MMK_R_z 1.0 0.0 0.5 0.25 designspace width 0.0 weight 634.59275 public.glyphOrder space A Agrave Aacute Acircumflex Atilde Adieresis Aring B C Ccedilla D E Egrave Eacute Ecircumflex Edieresis F G H I Igrave Iacute Icircumflex Idieresis J K L M N Ntilde O Ograve Oacute Ocircumflex Otilde Odieresis P Q R S Scaron T U Ugrave Uacute Ucircumflex Udieresis V W X Y Yacute Ydieresis Z Zcaron AE Eth Oslash Thorn Lslash OE a agrave aacute acircumflex atilde adieresis aring b c ccedilla d e egrave eacute ecircumflex edieresis f g h i igrave iacute icircumflex idieresis j k l m n ntilde o ograve oacute ocircumflex otilde odieresis p q r s scaron t u ugrave uacute ucircumflex udieresis v w x y yacute ydieresis z zcaron ordfeminine ordmasculine germandbls ae eth oslash thorn dotlessi lslash oe mu zero one two three four five six seven eight nine onesuperior twosuperior threesuperior onequarter onehalf threequarters underscore hyphen endash emdash parenleft parenright bracketleft bracketright braceleft braceright numbersign percent perthousand quotesingle quotedbl quoteleft quoteright quotedblleft quotedblright quotesinglbase quotedblbase guilsinglleft guilsinglright asterisk dagger daggerdbl period comma colon semicolon ellipsis exclam exclamdown question questiondown slash backslash fraction bar brokenbar at ampersand section paragraph periodcentered bullet plus minus plusminus divide multiply equal less greater logicalnot dollar cent sterling currency yen Euro asciicircum asciitilde acute grave hungarumlaut circumflex caron breve tilde macron dieresis dotaccent ring cedilla ogonek copyright registered trademark degree florin guillemotleft guillemotright fi fl a_b_c .notdef testLibItemKey a b c metainfo.plist000066400000000000000000000004561365052737200354550ustar00rootroot00000000000000MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/intermediate/IntermediateCondensed.ufo creator org.robofab.ufoLib formatVersion 2 MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/intermediate/IntermediateWide.ufo/000077500000000000000000000000001365052737200316365ustar00rootroot00000000000000features.fea000066400000000000000000000001041365052737200340450ustar00rootroot00000000000000MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/intermediate/IntermediateWide.ufo# this is the feature from lightCondensed # Hi_this_is_the_feature. fontinfo.plist000066400000000000000000000023761365052737200344660ustar00rootroot00000000000000MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/intermediate/IntermediateWide.ufo ascender 763.459275 capHeight 763.459275 copyright Copyright-token-string descender -200 familyName Intermediate italicAngle 0 openTypeOS2VendorID ADBE postscriptBlueValues postscriptDefaultWidthX 500 postscriptFamilyBlues postscriptFamilyOtherBlues postscriptOtherBlues postscriptSlantAngle 0 postscriptStemSnapH postscriptStemSnapV postscriptWindowsCharacterSet 1 styleName Wide unitsPerEm 1000.0 versionMajor 1 versionMinor 2 xHeight 500 MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/intermediate/IntermediateWide.ufo/glyphs/000077500000000000000000000000001365052737200331445ustar00rootroot00000000000000A_.glif000066400000000000000000000017011365052737200342460ustar00rootroot00000000000000MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/intermediate/IntermediateWide.ufo/glyphs B_.glif000066400000000000000000000044041365052737200342520ustar00rootroot00000000000000MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/intermediate/IntermediateWide.ufo/glyphs C_.glif000066400000000000000000000035531365052737200342570ustar00rootroot00000000000000MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/intermediate/IntermediateWide.ufo/glyphs D_.glif000066400000000000000000000027321365052737200342560ustar00rootroot00000000000000MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/intermediate/IntermediateWide.ufo/glyphs E_.glif000066400000000000000000000021531365052737200342540ustar00rootroot00000000000000MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/intermediate/IntermediateWide.ufo/glyphs F_.glif000066400000000000000000000016171365052737200342610ustar00rootroot00000000000000MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/intermediate/IntermediateWide.ufo/glyphs G_.glif000066400000000000000000000043321365052737200342570ustar00rootroot00000000000000MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/intermediate/IntermediateWide.ufo/glyphs H_.glif000066400000000000000000000017011365052737200342550ustar00rootroot00000000000000MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/intermediate/IntermediateWide.ufo/glyphs I_.glif000066400000000000000000000016551365052737200342660ustar00rootroot00000000000000MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/intermediate/IntermediateWide.ufo/glyphs J_.glif000066400000000000000000000021741365052737200342640ustar00rootroot00000000000000MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/intermediate/IntermediateWide.ufo/glyphs K_.glif000066400000000000000000000021351365052737200342620ustar00rootroot00000000000000MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/intermediate/IntermediateWide.ufo/glyphs L_.glif000066400000000000000000000012311365052737200342570ustar00rootroot00000000000000MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/intermediate/IntermediateWide.ufo/glyphs M_.glif000066400000000000000000000021311365052737200342600ustar00rootroot00000000000000MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/intermediate/IntermediateWide.ufo/glyphs N_.glif000066400000000000000000000016731365052737200342730ustar00rootroot00000000000000MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/intermediate/IntermediateWide.ufo/glyphs O_.glif000066400000000000000000000034751365052737200342760ustar00rootroot00000000000000MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/intermediate/IntermediateWide.ufo/glyphs P_.glif000066400000000000000000000030731365052737200342710ustar00rootroot00000000000000MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/intermediate/IntermediateWide.ufo/glyphs Q_.glif000066400000000000000000000007041365052737200342700ustar00rootroot00000000000000MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/intermediate/IntermediateWide.ufo/glyphs R_.glif000066400000000000000000000036031365052737200342720ustar00rootroot00000000000000MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/intermediate/IntermediateWide.ufo/glyphs S_.glif000066400000000000000000000052121365052737200342710ustar00rootroot00000000000000MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/intermediate/IntermediateWide.ufo/glyphs T_.glif000066400000000000000000000012271365052737200342740ustar00rootroot00000000000000MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/intermediate/IntermediateWide.ufo/glyphs U_.glif000066400000000000000000000023471365052737200343010ustar00rootroot00000000000000MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/intermediate/IntermediateWide.ufo/glyphs V_.glif000066400000000000000000000016251365052737200343000ustar00rootroot00000000000000MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/intermediate/IntermediateWide.ufo/glyphs colon.glif000066400000000000000000000004201365052737200350360ustar00rootroot00000000000000MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/intermediate/IntermediateWide.ufo/glyphs comma.glif000066400000000000000000000012251365052737200350240ustar00rootroot00000000000000MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/intermediate/IntermediateWide.ufo/glyphs contents.plist000066400000000000000000000030441365052737200360000ustar00rootroot00000000000000MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/intermediate/IntermediateWide.ufo/glyphs A A_.glif B B_.glif C C_.glif D D_.glif E E_.glif F F_.glif G G_.glif H H_.glif I I_.glif J J_.glif K K_.glif L L_.glif M M_.glif N N_.glif O O_.glif P P_.glif Q Q_.glif R R_.glif S S_.glif T T_.glif U U_.glif V V_.glif colon colon.glif comma comma.glif period period.glif quotedblbase quotedblbase.glif quotedblleft quotedblleft.glif quotedblright quotedblright.glif quotesinglbase quotesinglbase.glif semicolon semicolon.glif space space.glif period.glif000066400000000000000000000006711365052737200352160ustar00rootroot00000000000000MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/intermediate/IntermediateWide.ufo/glyphs quotedblbase.glif000066400000000000000000000004531365052737200364040ustar00rootroot00000000000000MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/intermediate/IntermediateWide.ufo/glyphs quotedblleft.glif000066400000000000000000000005701365052737200364240ustar00rootroot00000000000000MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/intermediate/IntermediateWide.ufo/glyphs quotedblright.glif000066400000000000000000000004461365052737200366110ustar00rootroot00000000000000MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/intermediate/IntermediateWide.ufo/glyphs quotesinglbase.glif000066400000000000000000000003361365052737200367570ustar00rootroot00000000000000MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/intermediate/IntermediateWide.ufo/glyphs semicolon.glif000066400000000000000000000004151365052737200357200ustar00rootroot00000000000000MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/intermediate/IntermediateWide.ufo/glyphs space.glif000066400000000000000000000002341365052737200350220ustar00rootroot00000000000000MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/intermediate/IntermediateWide.ufo/glyphs kerning.plist000066400000000000000000000110711365052737200342710ustar00rootroot00000000000000MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/intermediate/IntermediateWide.ufo @MMK_L_A V -1.4210854715202004e-14 B H -14.61629 J -31.05961624999999 O -3.654072499999998 S -9.135181250000002 T -31.059616249999998 U -12.78925375 V -31.059616250000005 public.kern2.@MMK_R_A -9.135181249999999 C J -27.405543749999993 T -14.61629 V -23.75147125 public.kern2.@MMK_R_A -14.61629 E J -29.23258 T -3.6540725000000016 F H -12.78925375 J -89.52477625 O -18.270362499999997 S -25.5785075 U -3.6540725000000016 public.kern2.@MMK_R_A -45.67590625 G J -21.924435000000003 O -5.481108749999999 S -5.481108750000001 T -38.36776125 U -7.308145 V -25.5785075 public.kern2.@MMK_R_A -14.61629 H J -14.616290000000006 S -10.962217500000001 J J -49.32997874999998 O -3.6540725 public.kern2.@MMK_R_A -25.5785075 L J -9.135181250000002 O -34.71368875 T -111.44921125000002 U -31.059616250000005 V -67.60034125000001 O J -43.848870000000005 S -16.44332625 T -32.8866525 V -29.23258 public.kern2.@MMK_R_A -10.962217499999998 P J -73.08144999999999 T -7.308145 public.kern2.@MMK_R_A -34.713688749999996 R H -16.44332625 J -56.63812374999999 O -20.097398750000004 S -25.5785075 T -29.23258 U -21.924435000000003 V -34.71368875 public.kern2.@MMK_R_A -29.23258 S H -1.827036249999999 J -42.02183375 O -14.61629 S -16.44332625 T -18.270362500000005 U -3.6540725000000016 V -7.308145000000003 public.kern2.@MMK_R_A -16.44332625 T H -5.481108750000001 J -115.10328374999995 O -32.8866525 S -9.135181249999999 public.kern2.@MMK_R_A -78.56255875 U J -51.157015 public.kern2.@MMK_R_A -27.40554375 V @MMK_R_A -1.4210854715202004e-14 H -7.308145 J -96.83292124999997 O -20.097398750000004 S -18.270362500000005 public.kern2.@MMK_R_A -76.7355225 public.kern1.@MMK_L_A J -12.78925375 O -20.097398749999996 T -69.42737749999999 U -34.713688749999996 V -65.773305 MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/intermediate/IntermediateWide.ufo/lib.plist000066400000000000000000000455651365052737200335000ustar00rootroot00000000000000 com.typemytype.robofont.background.layerStrokeColor 0.0 0.8 0.2 0.7 com.typemytype.robofont.compileSettings.autohint com.typemytype.robofont.compileSettings.checkOutlines com.typemytype.robofont.compileSettings.createDummyDSIG com.typemytype.robofont.compileSettings.decompose com.typemytype.robofont.compileSettings.generateFormat 0 com.typemytype.robofont.compileSettings.releaseMode com.typemytype.robofont.foreground.layerStrokeColor 0.5 0.0 0.5 0.7 com.typemytype.robofont.italicSlantOffset 0 com.typemytype.robofont.layerOrder background com.typemytype.robofont.segmentType curve com.typemytype.robofont.shouldAddPointsInSplineConversion 1 com.typemytype.robofont.sort ascending space A Agrave Aacute Acircumflex Atilde Adieresis Aring B C Ccedilla D E Egrave Eacute Ecircumflex Edieresis F G H I Igrave Iacute Icircumflex Idieresis J K L M N Ntilde O Ograve Oacute Ocircumflex Otilde Odieresis P Q R S Scaron T U Ugrave Uacute Ucircumflex Udieresis V W X Y Yacute Ydieresis Z Zcaron AE Eth Oslash Thorn Lslash OE a agrave aacute acircumflex atilde adieresis aring b c ccedilla d e egrave eacute ecircumflex edieresis f g h i igrave iacute icircumflex idieresis j k l m n ntilde o ograve oacute ocircumflex otilde odieresis p q r s scaron t u ugrave uacute ucircumflex udieresis v w x y yacute ydieresis z zcaron ordfeminine ordmasculine germandbls ae eth oslash thorn dotlessi lslash oe mu zero one two three four five six seven eight nine onesuperior twosuperior threesuperior onequarter onehalf threequarters underscore hyphen endash emdash parenleft parenright bracketleft bracketright braceleft braceright numbersign percent perthousand quotesingle quotedbl quoteleft quoteright quotedblleft quotedblright quotesinglbase quotedblbase guilsinglleft guilsinglright asterisk dagger daggerdbl period comma colon semicolon ellipsis exclam exclamdown question questiondown slash backslash fraction bar brokenbar at ampersand section paragraph periodcentered bullet plus minus plusminus divide multiply equal less greater logicalnot dollar cent sterling currency yen Euro asciicircum asciitilde acute grave hungarumlaut circumflex caron breve tilde macron dieresis dotaccent ring cedilla ogonek copyright registered trademark degree florin guillemotleft guillemotright fi fl a_b_c .notdef type glyphList com.typesupply.MetricsMachine4.groupColors @MMK_L_A 1.0 0.0 0.0 0.25 @MMK_L_C 1.0 0.5 0.0 0.25 @MMK_L_E 1.0 1.0 0.0 0.25 @MMK_L_I 0.0 1.0 0.0 0.25 @MMK_L_N 0.0 1.0 1.0 0.25 @MMK_L_O 0.0 0.5 1.0 0.25 @MMK_L_S 0.0 0.0 1.0 0.25 @MMK_L_U 0.5 0.0 1.0 0.25 @MMK_L_Y 1.0 0.0 1.0 0.25 @MMK_L_Z 1.0 0.0 0.5 0.25 @MMK_L_a 1.0 0.0 0.0 0.25 @MMK_L_c 1.0 0.5 0.0 0.25 @MMK_L_e 1.0 1.0 0.0 0.25 @MMK_L_i 0.0 1.0 0.0 0.25 @MMK_L_n 0.0 1.0 1.0 0.25 @MMK_L_o 0.0 0.5 1.0 0.25 @MMK_L_s 0.0 0.0 1.0 0.25 @MMK_L_u 0.5 0.0 1.0 0.25 @MMK_L_y 1.0 0.0 1.0 0.25 @MMK_L_z 1.0 0.0 0.5 0.25 @MMK_R_A 1.0 0.0 0.0 0.25 @MMK_R_C 1.0 0.5 0.0 0.25 @MMK_R_E 1.0 1.0 0.0 0.25 @MMK_R_I 0.0 1.0 0.0 0.25 @MMK_R_N 0.0 1.0 1.0 0.25 @MMK_R_O 0.0 0.5 1.0 0.25 @MMK_R_S 0.0 0.0 1.0 0.25 @MMK_R_U 0.5 0.0 1.0 0.25 @MMK_R_Y 1.0 0.0 1.0 0.25 @MMK_R_Z 1.0 0.0 0.5 0.25 @MMK_R_a 1.0 0.0 0.0 0.25 @MMK_R_c 1.0 0.5 0.0 0.25 @MMK_R_e 1.0 1.0 0.0 0.25 @MMK_R_i 0.0 1.0 0.0 0.25 @MMK_R_n 0.0 1.0 1.0 0.25 @MMK_R_o 0.0 0.5 1.0 0.25 @MMK_R_s 0.0 0.0 1.0 0.25 @MMK_R_u 0.5 0.0 1.0 0.25 @MMK_R_y 1.0 0.0 1.0 0.25 @MMK_R_z 1.0 0.0 0.5 0.25 designspace width 1000.0 weight 634.59275 public.glyphOrder space A Agrave Aacute Acircumflex Atilde Adieresis Aring B C Ccedilla D E Egrave Eacute Ecircumflex Edieresis F G H I Igrave Iacute Icircumflex Idieresis J K L M N Ntilde O Ograve Oacute Ocircumflex Otilde Odieresis P Q R S Scaron T U Ugrave Uacute Ucircumflex Udieresis V W X Y Yacute Ydieresis Z Zcaron AE Eth Oslash Thorn Lslash OE a agrave aacute acircumflex atilde adieresis aring b c ccedilla d e egrave eacute ecircumflex edieresis f g h i igrave iacute icircumflex idieresis j k l m n ntilde o ograve oacute ocircumflex otilde odieresis p q r s scaron t u ugrave uacute ucircumflex udieresis v w x y yacute ydieresis z zcaron ordfeminine ordmasculine germandbls ae eth oslash thorn dotlessi lslash oe mu zero one two three four five six seven eight nine onesuperior twosuperior threesuperior onequarter onehalf threequarters underscore hyphen endash emdash parenleft parenright bracketleft bracketright braceleft braceright numbersign percent perthousand quotesingle quotedbl quoteleft quoteright quotedblleft quotedblright quotesinglbase quotedblbase guilsinglleft guilsinglright asterisk dagger daggerdbl period comma colon semicolon ellipsis exclam exclamdown question questiondown slash backslash fraction bar brokenbar at ampersand section paragraph periodcentered bullet plus minus plusminus divide multiply equal less greater logicalnot dollar cent sterling currency yen Euro asciicircum asciitilde acute grave hungarumlaut circumflex caron breve tilde macron dieresis dotaccent ring cedilla ogonek copyright registered trademark degree florin guillemotleft guillemotright fi fl a_b_c .notdef testLibItemKey a b c metainfo.plist000066400000000000000000000004561365052737200344430ustar00rootroot00000000000000MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/intermediate/IntermediateWide.ufo creator org.robofab.ufoLib formatVersion 2 MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/light/000077500000000000000000000000001365052737200242605ustar00rootroot00000000000000MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/light/LightCondensed.ufo/000077500000000000000000000000001365052737200277425ustar00rootroot00000000000000MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/light/LightCondensed.ufo/features.fea000066400000000000000000000001041365052737200322300ustar00rootroot00000000000000# this is the feature from lightCondensed # Hi_this_is_the_feature. MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/light/LightCondensed.ufo/fontinfo.plist000066400000000000000000000033631365052737200326460ustar00rootroot00000000000000 ascender 700 capHeight 700 copyright License same as MutatorMath. BSD 3-clause. [test-token: C] descender -200 familyName MutatorMathTest guidelines italicAngle 0 openTypeNameLicense License same as MutatorMath. BSD 3-clause. [test-token: C] openTypeOS2VendorID LTTR postscriptBlueValues postscriptDefaultWidthX 500 postscriptFamilyBlues postscriptFamilyOtherBlues postscriptFontName MutatorMathTest-LightCondensed postscriptFullName MutatorMathTest LightCondensed postscriptOtherBlues postscriptSlantAngle 0 postscriptStemSnapH postscriptStemSnapV postscriptWindowsCharacterSet 1 styleMapFamilyName styleMapStyleName regular styleName LightCondensed unitsPerEm 1000 versionMajor 1 versionMinor 2 xHeight 500 year 2004 MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/light/LightCondensed.ufo/glyphs.background/000077500000000000000000000000001365052737200333665ustar00rootroot00000000000000contents.plist000066400000000000000000000002741365052737200362240ustar00rootroot00000000000000MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/light/LightCondensed.ufo/glyphs.background layerinfo.plist000066400000000000000000000003541365052737200363560ustar00rootroot00000000000000MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/light/LightCondensed.ufo/glyphs.background color 0.5,1,0,0.7 MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/light/LightCondensed.ufo/glyphs/000077500000000000000000000000001365052737200312505ustar00rootroot00000000000000MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/light/LightCondensed.ufo/glyphs/A_.glif000066400000000000000000000016561365052737200324420ustar00rootroot00000000000000 MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/light/LightCondensed.ufo/glyphs/B_.glif000066400000000000000000000033451365052737200324400ustar00rootroot00000000000000 MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/light/LightCondensed.ufo/glyphs/C_.glif000066400000000000000000000025721365052737200324420ustar00rootroot00000000000000 MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/light/LightCondensed.ufo/glyphs/D_.glif000066400000000000000000000021401365052737200324320ustar00rootroot00000000000000 MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/light/LightCondensed.ufo/glyphs/E_.glif000066400000000000000000000016501365052737200324400ustar00rootroot00000000000000 MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/light/LightCondensed.ufo/glyphs/F_.glif000066400000000000000000000013471365052737200324440ustar00rootroot00000000000000 MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/light/LightCondensed.ufo/glyphs/G_.glif000066400000000000000000000033001365052737200324340ustar00rootroot00000000000000 MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/light/LightCondensed.ufo/glyphs/H_.glif000066400000000000000000000013451365052737200324440ustar00rootroot00000000000000 MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/light/LightCondensed.ufo/glyphs/I_.glif000066400000000000000000000013431365052737200324430ustar00rootroot00000000000000 MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/light/LightCondensed.ufo/glyphs/J_.glif000066400000000000000000000014561365052737200324510ustar00rootroot00000000000000 MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/light/LightCondensed.ufo/glyphs/K_.glif000066400000000000000000000015461365052737200324520ustar00rootroot00000000000000 MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/light/LightCondensed.ufo/glyphs/L_.glif000066400000000000000000000010321365052737200324410ustar00rootroot00000000000000 MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/light/LightCondensed.ufo/glyphs/M_.glif000066400000000000000000000016221365052737200324470ustar00rootroot00000000000000 MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/light/LightCondensed.ufo/glyphs/N_.glif000066400000000000000000000013401365052737200324450ustar00rootroot00000000000000 MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/light/LightCondensed.ufo/glyphs/O_.glif000066400000000000000000000025341365052737200324540ustar00rootroot00000000000000 MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/light/LightCondensed.ufo/glyphs/P_.glif000066400000000000000000000021521365052737200324510ustar00rootroot00000000000000 MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/light/LightCondensed.ufo/glyphs/Q_.glif000066400000000000000000000005711365052737200324550ustar00rootroot00000000000000 MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/light/LightCondensed.ufo/glyphs/R_.glif000066400000000000000000000026461365052737200324630ustar00rootroot00000000000000 MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/light/LightCondensed.ufo/glyphs/S_.glif000066400000000000000000000036761365052737200324700ustar00rootroot00000000000000 MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/light/LightCondensed.ufo/glyphs/T_.glif000066400000000000000000000010421365052737200324520ustar00rootroot00000000000000 MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/light/LightCondensed.ufo/glyphs/U_.glif000066400000000000000000000016411365052737200324600ustar00rootroot00000000000000 MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/light/LightCondensed.ufo/glyphs/V_.glif000066400000000000000000000013411365052737200324560ustar00rootroot00000000000000 MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/light/LightCondensed.ufo/glyphs/W_.glif000066400000000000000000000015251365052737200324630ustar00rootroot00000000000000 MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/light/LightCondensed.ufo/glyphs/X_.glif000066400000000000000000000014361365052737200324650ustar00rootroot00000000000000 MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/light/LightCondensed.ufo/glyphs/Y_.glif000066400000000000000000000013531365052737200324640ustar00rootroot00000000000000 MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/light/LightCondensed.ufo/glyphs/Z_.glif000066400000000000000000000013441365052737200324650ustar00rootroot00000000000000 MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/light/LightCondensed.ufo/glyphs/colon.glif000066400000000000000000000003461365052737200332300ustar00rootroot00000000000000 MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/light/LightCondensed.ufo/glyphs/comma.glif000066400000000000000000000010031365052737200332010ustar00rootroot00000000000000 contents.plist000066400000000000000000000032731365052737200341100ustar00rootroot00000000000000MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/light/LightCondensed.ufo/glyphs A A_.glif B B_.glif C C_.glif D D_.glif E E_.glif F F_.glif G G_.glif H H_.glif I I_.glif J J_.glif K K_.glif L L_.glif M M_.glif N N_.glif O O_.glif P P_.glif Q Q_.glif R R_.glif S S_.glif T T_.glif U U_.glif V V_.glif W W_.glif X X_.glif Y Y_.glif Z Z_.glif colon colon.glif comma comma.glif period period.glif quotedblbase quotedblbase.glif quotedblleft quotedblleft.glif quotedblright quotedblright.glif quotesinglbase quotesinglbase.glif semicolon semicolon.glif space space.glif layerinfo.plist000066400000000000000000000005301365052737200342340ustar00rootroot00000000000000MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/light/LightCondensed.ufo/glyphs color 1,0.75,0,0.7 lib com.typemytype.robofont.segmentType curve MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/light/LightCondensed.ufo/glyphs/period.glif000066400000000000000000000005361365052737200334010ustar00rootroot00000000000000 quotedblbase.glif000066400000000000000000000003531365052737200345070ustar00rootroot00000000000000MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/light/LightCondensed.ufo/glyphs quotedblleft.glif000066400000000000000000000005051365052737200345260ustar00rootroot00000000000000MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/light/LightCondensed.ufo/glyphs quotedblright.glif000066400000000000000000000004101365052737200347040ustar00rootroot00000000000000MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/light/LightCondensed.ufo/glyphs quotesinglbase.glif000066400000000000000000000003011365052737200350530ustar00rootroot00000000000000MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/light/LightCondensed.ufo/glyphs semicolon.glif000066400000000000000000000003511365052737200340230ustar00rootroot00000000000000MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/light/LightCondensed.ufo/glyphs MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/light/LightCondensed.ufo/glyphs/space.glif000066400000000000000000000002321365052737200332030ustar00rootroot00000000000000 MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/light/LightCondensed.ufo/groups.plist000066400000000000000000000006701365052737200323410ustar00rootroot00000000000000 public.kern1.@MMK_L_A A public.kern2.@MMK_R_A A testGroup E F H MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/light/LightCondensed.ufo/kerning.plist000066400000000000000000000005641365052737200324610ustar00rootroot00000000000000 V public.kern2.@MMK_R_A -100 public.kern1.@MMK_L_A V -100 MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/light/LightCondensed.ufo/layercontents.plist000066400000000000000000000005371365052737200337160ustar00rootroot00000000000000 foreground glyphs background glyphs.background MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/light/LightCondensed.ufo/lib.plist000066400000000000000000000451311365052737200315710ustar00rootroot00000000000000 com.typemytype.robofont.background.layerStrokeColor 0.0 0.8 0.2 0.7 com.typemytype.robofont.compileSettings.autohint com.typemytype.robofont.compileSettings.checkOutlines com.typemytype.robofont.compileSettings.createDummyDSIG com.typemytype.robofont.compileSettings.decompose com.typemytype.robofont.compileSettings.generateFormat 0 com.typemytype.robofont.compileSettings.releaseMode com.typemytype.robofont.foreground.layerStrokeColor 0.5 0.0 0.5 0.7 com.typemytype.robofont.italicSlantOffset 0 com.typemytype.robofont.segmentType curve com.typemytype.robofont.shouldAddPointsInSplineConversion 1 com.typesupply.MetricsMachine4.groupColors @MMK_L_A 1.0 0.0 0.0 0.25 @MMK_L_C 1.0 0.5 0.0 0.25 @MMK_L_E 1.0 1.0 0.0 0.25 @MMK_L_I 0.0 1.0 0.0 0.25 @MMK_L_N 0.0 1.0 1.0 0.25 @MMK_L_O 0.0 0.5 1.0 0.25 @MMK_L_S 0.0 0.0 1.0 0.25 @MMK_L_U 0.5 0.0 1.0 0.25 @MMK_L_Y 1.0 0.0 1.0 0.25 @MMK_L_Z 1.0 0.0 0.5 0.25 @MMK_L_a 1.0 0.0 0.0 0.25 @MMK_L_c 1.0 0.5 0.0 0.25 @MMK_L_e 1.0 1.0 0.0 0.25 @MMK_L_i 0.0 1.0 0.0 0.25 @MMK_L_n 0.0 1.0 1.0 0.25 @MMK_L_o 0.0 0.5 1.0 0.25 @MMK_L_s 0.0 0.0 1.0 0.25 @MMK_L_u 0.5 0.0 1.0 0.25 @MMK_L_y 1.0 0.0 1.0 0.25 @MMK_L_z 1.0 0.0 0.5 0.25 @MMK_R_A 1.0 0.0 0.0 0.25 @MMK_R_C 1.0 0.5 0.0 0.25 @MMK_R_E 1.0 1.0 0.0 0.25 @MMK_R_I 0.0 1.0 0.0 0.25 @MMK_R_N 0.0 1.0 1.0 0.25 @MMK_R_O 0.0 0.5 1.0 0.25 @MMK_R_S 0.0 0.0 1.0 0.25 @MMK_R_U 0.5 0.0 1.0 0.25 @MMK_R_Y 1.0 0.0 1.0 0.25 @MMK_R_Z 1.0 0.0 0.5 0.25 @MMK_R_a 1.0 0.0 0.0 0.25 @MMK_R_c 1.0 0.5 0.0 0.25 @MMK_R_e 1.0 1.0 0.0 0.25 @MMK_R_i 0.0 1.0 0.0 0.25 @MMK_R_n 0.0 1.0 1.0 0.25 @MMK_R_o 0.0 0.5 1.0 0.25 @MMK_R_s 0.0 0.0 1.0 0.25 @MMK_R_u 0.5 0.0 1.0 0.25 @MMK_R_y 1.0 0.0 1.0 0.25 @MMK_R_z 1.0 0.0 0.5 0.25 com.typesupply.defcon.sortDescriptor ascending space A Agrave Aacute Acircumflex Atilde Adieresis Aring B C Ccedilla D E Egrave Eacute Ecircumflex Edieresis F G H I Igrave Iacute Icircumflex Idieresis J K L M N Ntilde O Ograve Oacute Ocircumflex Otilde Odieresis P Q R S Scaron T U Ugrave Uacute Ucircumflex Udieresis V W X Y Yacute Ydieresis Z Zcaron AE Eth Oslash Thorn Lslash OE a agrave aacute acircumflex atilde adieresis aring b c ccedilla d e egrave eacute ecircumflex edieresis f g h i igrave iacute icircumflex idieresis j k l m n ntilde o ograve oacute ocircumflex otilde odieresis p q r s scaron t u ugrave uacute ucircumflex udieresis v w x y yacute ydieresis z zcaron ordfeminine ordmasculine germandbls ae eth oslash thorn dotlessi lslash oe mu zero one two three four five six seven eight nine onesuperior twosuperior threesuperior onequarter onehalf threequarters underscore hyphen endash emdash parenleft parenright bracketleft bracketright braceleft braceright numbersign percent perthousand quotesingle quotedbl quoteleft quoteright quotedblleft quotedblright quotesinglbase quotedblbase guilsinglleft guilsinglright asterisk dagger daggerdbl period comma colon semicolon ellipsis exclam exclamdown question questiondown slash backslash fraction bar brokenbar at ampersand section paragraph periodcentered bullet plus minus plusminus divide multiply equal less greater logicalnot dollar cent sterling currency yen Euro asciicircum asciitilde acute grave hungarumlaut circumflex caron breve tilde macron dieresis dotaccent ring cedilla ogonek copyright registered trademark degree florin guillemotleft guillemotright fi fl a_b_c .notdef type glyphList public.glyphOrder space A Agrave Aacute Acircumflex Atilde Adieresis Aring B C Ccedilla D E Egrave Eacute Ecircumflex Edieresis F G H I Igrave Iacute Icircumflex Idieresis J K L M N Ntilde O Ograve Oacute Ocircumflex Otilde Odieresis P Q R S Scaron T U Ugrave Uacute Ucircumflex Udieresis V W X Y Yacute Ydieresis Z Zcaron AE Eth Oslash Thorn Lslash OE a agrave aacute acircumflex atilde adieresis aring b c ccedilla d e egrave eacute ecircumflex edieresis f g h i igrave iacute icircumflex idieresis j k l m n ntilde o ograve oacute ocircumflex otilde odieresis p q r s scaron t u ugrave uacute ucircumflex udieresis v w x y yacute ydieresis z zcaron ordfeminine ordmasculine germandbls ae eth oslash thorn dotlessi lslash oe mu zero one two three four five six seven eight nine onesuperior twosuperior threesuperior onequarter onehalf threequarters underscore hyphen endash emdash parenleft parenright bracketleft bracketright braceleft braceright numbersign percent perthousand quotesingle quotedbl quoteleft quoteright quotedblleft quotedblright quotesinglbase quotedblbase guilsinglleft guilsinglright asterisk dagger daggerdbl period comma colon semicolon ellipsis exclam exclamdown question questiondown slash backslash fraction bar brokenbar at ampersand section paragraph periodcentered bullet plus minus plusminus divide multiply equal less greater logicalnot dollar cent sterling currency yen Euro asciicircum asciitilde acute grave hungarumlaut circumflex caron breve tilde macron dieresis dotaccent ring cedilla ogonek copyright registered trademark degree florin guillemotleft guillemotright fi fl a_b_c .notdef testLibItemKey a b c MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/light/LightCondensed.ufo/metainfo.plist000066400000000000000000000004451365052737200326240ustar00rootroot00000000000000 creator org.robofab.ufoLib formatVersion 3 MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/light/LightWide.ufo/000077500000000000000000000000001365052737200267305ustar00rootroot00000000000000MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/light/LightWide.ufo/features.fea000066400000000000000000000000451365052737200312220ustar00rootroot00000000000000# this is the feature from lightWide MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/light/LightWide.ufo/fontinfo.plist000066400000000000000000000033441365052737200316330ustar00rootroot00000000000000 ascender 700 capHeight 700 copyright License same as MutatorMath. BSD 3-clause. [test-token: D] descender -200 familyName MutatorMathTest guidelines italicAngle 0 openTypeNameLicense License same as MutatorMath. BSD 3-clause. [test-token: D] openTypeOS2VendorID LTTR postscriptBlueValues postscriptDefaultWidthX 500 postscriptFamilyBlues postscriptFamilyOtherBlues postscriptFontName MutatorMathTest-LightWide postscriptFullName MutatorMathTest LightWide postscriptOtherBlues postscriptSlantAngle 0 postscriptStemSnapH postscriptStemSnapV postscriptWindowsCharacterSet 1 styleMapFamilyName styleMapStyleName regular styleName LightWide unitsPerEm 1000 versionMajor 1 versionMinor 2 xHeight 500 year 2004 MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/light/LightWide.ufo/glyphs.background/000077500000000000000000000000001365052737200323545ustar00rootroot00000000000000contents.plist000066400000000000000000000002741365052737200352120ustar00rootroot00000000000000MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/light/LightWide.ufo/glyphs.background layerinfo.plist000066400000000000000000000003541365052737200353440ustar00rootroot00000000000000MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/light/LightWide.ufo/glyphs.background color 0.5,1,0,0.7 MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/light/LightWide.ufo/glyphs/000077500000000000000000000000001365052737200302365ustar00rootroot00000000000000MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/light/LightWide.ufo/glyphs/A_.glif000066400000000000000000000016631365052737200314260ustar00rootroot00000000000000 MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/light/LightWide.ufo/glyphs/B_.glif000066400000000000000000000033711365052737200314250ustar00rootroot00000000000000 MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/light/LightWide.ufo/glyphs/C_.glif000066400000000000000000000026141365052737200314250ustar00rootroot00000000000000 MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/light/LightWide.ufo/glyphs/D_.glif000066400000000000000000000021561365052737200314270ustar00rootroot00000000000000 MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/light/LightWide.ufo/glyphs/E_.glif000066400000000000000000000016611365052737200314300ustar00rootroot00000000000000 MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/light/LightWide.ufo/glyphs/F_.glif000066400000000000000000000017061365052737200314310ustar00rootroot00000000000000 com.typemytype.robofont.guideline.magnetic.tCW6w1QdWl 5 MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/light/LightWide.ufo/glyphs/G_.glif000066400000000000000000000033271365052737200314330ustar00rootroot00000000000000 MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/light/LightWide.ufo/glyphs/H_.glif000066400000000000000000000013601365052737200314270ustar00rootroot00000000000000 MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/light/LightWide.ufo/glyphs/I_.glif000066400000000000000000000013471365052737200314350ustar00rootroot00000000000000 MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/light/LightWide.ufo/glyphs/J_.glif000066400000000000000000000014631365052737200314350ustar00rootroot00000000000000 MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/light/LightWide.ufo/glyphs/K_.glif000066400000000000000000000015521365052737200314350ustar00rootroot00000000000000 MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/light/LightWide.ufo/glyphs/L_.glif000066400000000000000000000010361365052737200314330ustar00rootroot00000000000000 MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/light/LightWide.ufo/glyphs/M_.glif000066400000000000000000000016341365052737200314400ustar00rootroot00000000000000 MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/light/LightWide.ufo/glyphs/N_.glif000066400000000000000000000013551365052737200314410ustar00rootroot00000000000000 MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/light/LightWide.ufo/glyphs/O_.glif000066400000000000000000000025541365052737200314440ustar00rootroot00000000000000 MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/light/LightWide.ufo/glyphs/P_.glif000066400000000000000000000021671365052737200314450ustar00rootroot00000000000000 MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/light/LightWide.ufo/glyphs/Q_.glif000066400000000000000000000005721365052737200314440ustar00rootroot00000000000000 MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/light/LightWide.ufo/glyphs/R_.glif000066400000000000000000000026631365052737200314500ustar00rootroot00000000000000 MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/light/LightWide.ufo/glyphs/S_.glif000066400000000000000000000037251365052737200314510ustar00rootroot00000000000000 MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/light/LightWide.ufo/glyphs/T_.glif000066400000000000000000000010451365052737200314430ustar00rootroot00000000000000 MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/light/LightWide.ufo/glyphs/U_.glif000066400000000000000000000016571365052737200314550ustar00rootroot00000000000000 MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/light/LightWide.ufo/glyphs/V_.glif000066400000000000000000000013441365052737200314470ustar00rootroot00000000000000 MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/light/LightWide.ufo/glyphs/W_.glif000066400000000000000000000015311365052737200314460ustar00rootroot00000000000000 MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/light/LightWide.ufo/glyphs/X_.glif000066400000000000000000000014401365052737200314460ustar00rootroot00000000000000 MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/light/LightWide.ufo/glyphs/Y_.glif000066400000000000000000000013531365052737200314520ustar00rootroot00000000000000 MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/light/LightWide.ufo/glyphs/Z_.glif000066400000000000000000000013601365052737200314510ustar00rootroot00000000000000 MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/light/LightWide.ufo/glyphs/colon.glif000066400000000000000000000003461365052737200322160ustar00rootroot00000000000000 MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/light/LightWide.ufo/glyphs/comma.glif000066400000000000000000000010111365052737200321660ustar00rootroot00000000000000 MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/light/LightWide.ufo/glyphs/contents.plist000066400000000000000000000032731365052737200331550ustar00rootroot00000000000000 A A_.glif B B_.glif C C_.glif D D_.glif E E_.glif F F_.glif G G_.glif H H_.glif I I_.glif J J_.glif K K_.glif L L_.glif M M_.glif N N_.glif O O_.glif P P_.glif Q Q_.glif R R_.glif S S_.glif T T_.glif U U_.glif V V_.glif W W_.glif X X_.glif Y Y_.glif Z Z_.glif colon colon.glif comma comma.glif period period.glif quotedblbase quotedblbase.glif quotedblleft quotedblleft.glif quotedblright quotedblright.glif quotesinglbase quotesinglbase.glif semicolon semicolon.glif space space.glif MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/light/LightWide.ufo/glyphs/layerinfo.plist000066400000000000000000000005301365052737200333010ustar00rootroot00000000000000 color 1,0.75,0,0.7 lib com.typemytype.robofont.segmentType curve MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/light/LightWide.ufo/glyphs/period.glif000066400000000000000000000005401365052737200323620ustar00rootroot00000000000000 MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/light/LightWide.ufo/glyphs/quotedblbase.glif000066400000000000000000000003531365052737200335540ustar00rootroot00000000000000 MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/light/LightWide.ufo/glyphs/quotedblleft.glif000066400000000000000000000005051365052737200335730ustar00rootroot00000000000000 quotedblright.glif000066400000000000000000000004101365052737200336720ustar00rootroot00000000000000MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/light/LightWide.ufo/glyphs quotesinglbase.glif000066400000000000000000000003011365052737200340410ustar00rootroot00000000000000MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/light/LightWide.ufo/glyphs MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/light/LightWide.ufo/glyphs/semicolon.glif000066400000000000000000000003511365052737200330700ustar00rootroot00000000000000 MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/light/LightWide.ufo/glyphs/space.glif000066400000000000000000000002321365052737200321710ustar00rootroot00000000000000 MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/light/LightWide.ufo/groups.plist000066400000000000000000000005201365052737200313210ustar00rootroot00000000000000 public.kern1.@MMK_L_A A public.kern2.@MMK_R_A A MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/light/LightWide.ufo/kerning.plist000066400000000000000000000077601365052737200314540ustar00rootroot00000000000000 B H -40 J -85 O -10 S -25 T -85 U -35 V -85 public.kern2.@MMK_R_A -25 C J -75 T -40 V -65 public.kern2.@MMK_R_A -40 E J -80 T -10 F H -35 J -245 O -50 S -70 U -10 public.kern2.@MMK_R_A -125 G J -60 O -15 S -15 T -105 U -20 V -70 public.kern2.@MMK_R_A -40 H J -40 S -30 J J -135 O -10 public.kern2.@MMK_R_A -70 L J -25 O -95 T -305 U -85 V -185 O J -120 S -45 T -90 V -80 public.kern2.@MMK_R_A -30 P J -200 T -20 public.kern2.@MMK_R_A -95 R H -45 J -155 O -55 S -70 T -80 U -60 V -95 public.kern2.@MMK_R_A -80 S H -5 J -115 O -40 S -45 T -50 U -10 V -20 public.kern2.@MMK_R_A -45 T H -15 J -315 O -90 S -25 public.kern2.@MMK_R_A -215 U J -140 public.kern2.@MMK_R_A -75 V H -20 J -265 O -55 S -50 public.kern2.@MMK_R_A -210 public.kern1.@MMK_L_A J -35 O -55 T -190 U -95 V -180 MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/light/LightWide.ufo/layercontents.plist000066400000000000000000000005371365052737200327040ustar00rootroot00000000000000 foreground glyphs background glyphs.background MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/light/LightWide.ufo/lib.plist000066400000000000000000000447551365052737200305720ustar00rootroot00000000000000 com.typemytype.robofont.background.layerStrokeColor 1.0 0.75 0.0 0.7 com.typemytype.robofont.compileSettings.autohint com.typemytype.robofont.compileSettings.checkOutlines com.typemytype.robofont.compileSettings.createDummyDSIG com.typemytype.robofont.compileSettings.decompose com.typemytype.robofont.compileSettings.generateFormat 0 com.typemytype.robofont.compileSettings.releaseMode com.typemytype.robofont.foreground.layerStrokeColor 0.5 0.0 0.5 0.7 com.typemytype.robofont.italicSlantOffset 0 com.typemytype.robofont.segmentType curve com.typemytype.robofont.shouldAddPointsInSplineConversion 1 com.typesupply.MetricsMachine4.groupColors @MMK_L_A 1.0 0.0 0.0 0.25 @MMK_L_C 1.0 0.5 0.0 0.25 @MMK_L_E 1.0 1.0 0.0 0.25 @MMK_L_I 0.0 1.0 0.0 0.25 @MMK_L_N 0.0 1.0 1.0 0.25 @MMK_L_O 0.0 0.5 1.0 0.25 @MMK_L_S 0.0 0.0 1.0 0.25 @MMK_L_U 0.5 0.0 1.0 0.25 @MMK_L_Y 1.0 0.0 1.0 0.25 @MMK_L_Z 1.0 0.0 0.5 0.25 @MMK_L_a 1.0 0.0 0.0 0.25 @MMK_L_c 1.0 0.5 0.0 0.25 @MMK_L_e 1.0 1.0 0.0 0.25 @MMK_L_i 0.0 1.0 0.0 0.25 @MMK_L_n 0.0 1.0 1.0 0.25 @MMK_L_o 0.0 0.5 1.0 0.25 @MMK_L_s 0.0 0.0 1.0 0.25 @MMK_L_u 0.5 0.0 1.0 0.25 @MMK_L_y 1.0 0.0 1.0 0.25 @MMK_L_z 1.0 0.0 0.5 0.25 @MMK_R_A 1.0 0.0 0.0 0.25 @MMK_R_C 1.0 0.5 0.0 0.25 @MMK_R_E 1.0 1.0 0.0 0.25 @MMK_R_I 0.0 1.0 0.0 0.25 @MMK_R_N 0.0 1.0 1.0 0.25 @MMK_R_O 0.0 0.5 1.0 0.25 @MMK_R_S 0.0 0.0 1.0 0.25 @MMK_R_U 0.5 0.0 1.0 0.25 @MMK_R_Y 1.0 0.0 1.0 0.25 @MMK_R_Z 1.0 0.0 0.5 0.25 @MMK_R_a 1.0 0.0 0.0 0.25 @MMK_R_c 1.0 0.5 0.0 0.25 @MMK_R_e 1.0 1.0 0.0 0.25 @MMK_R_i 0.0 1.0 0.0 0.25 @MMK_R_n 0.0 1.0 1.0 0.25 @MMK_R_o 0.0 0.5 1.0 0.25 @MMK_R_s 0.0 0.0 1.0 0.25 @MMK_R_u 0.5 0.0 1.0 0.25 @MMK_R_y 1.0 0.0 1.0 0.25 @MMK_R_z 1.0 0.0 0.5 0.25 com.typesupply.defcon.sortDescriptor ascending space A Agrave Aacute Acircumflex Atilde Adieresis Aring B C Ccedilla D E Egrave Eacute Ecircumflex Edieresis F G H I Igrave Iacute Icircumflex Idieresis J K L M N Ntilde O Ograve Oacute Ocircumflex Otilde Odieresis P Q R S Scaron T U Ugrave Uacute Ucircumflex Udieresis V W X Y Yacute Ydieresis Z Zcaron AE Eth Oslash Thorn Lslash OE a agrave aacute acircumflex atilde adieresis aring b c ccedilla d e egrave eacute ecircumflex edieresis f g h i igrave iacute icircumflex idieresis j k l m n ntilde o ograve oacute ocircumflex otilde odieresis p q r s scaron t u ugrave uacute ucircumflex udieresis v w x y yacute ydieresis z zcaron ordfeminine ordmasculine germandbls ae eth oslash thorn dotlessi lslash oe zero one two three four five six seven eight nine onesuperior twosuperior threesuperior onequarter onehalf threequarters underscore hyphen endash emdash parenleft parenright bracketleft bracketright braceleft braceright numbersign percent perthousand quotesingle quotedbl quoteleft quoteright quotedblleft quotedblright quotesinglbase quotedblbase guilsinglleft guilsinglright guillemotleft guillemotright asterisk dagger daggerdbl period comma colon semicolon ellipsis exclam exclamdown question questiondown slash backslash fraction bar brokenbar at ampersand section paragraph periodcentered bullet plus minus plusminus divide multiply equal less greater logicalnot mu dollar cent sterling currency yen Euro florin asciicircum asciitilde acute grave hungarumlaut circumflex caron breve tilde macron dieresis dotaccent ring cedilla ogonek copyright registered trademark degree fi fl .notdef a_b_c type glyphList public.glyphOrder space A Agrave Aacute Acircumflex Atilde Adieresis Aring B C Ccedilla D E Egrave Eacute Ecircumflex Edieresis F G H I Igrave Iacute Icircumflex Idieresis J K L M N Ntilde O Ograve Oacute Ocircumflex Otilde Odieresis P Q R S Scaron T U Ugrave Uacute Ucircumflex Udieresis V W X Y Yacute Ydieresis Z Zcaron AE Eth Oslash Thorn Lslash OE a agrave aacute acircumflex atilde adieresis aring b c ccedilla d e egrave eacute ecircumflex edieresis f g h i igrave iacute icircumflex idieresis j k l m n ntilde o ograve oacute ocircumflex otilde odieresis p q r s scaron t u ugrave uacute ucircumflex udieresis v w x y yacute ydieresis z zcaron ordfeminine ordmasculine germandbls ae eth oslash thorn dotlessi lslash oe zero one two three four five six seven eight nine onesuperior twosuperior threesuperior onequarter onehalf threequarters underscore hyphen endash emdash parenleft parenright bracketleft bracketright braceleft braceright numbersign percent perthousand quotesingle quotedbl quoteleft quoteright quotedblleft quotedblright quotesinglbase quotedblbase guilsinglleft guilsinglright guillemotleft guillemotright asterisk dagger daggerdbl period comma colon semicolon ellipsis exclam exclamdown question questiondown slash backslash fraction bar brokenbar at ampersand section paragraph periodcentered bullet plus minus plusminus divide multiply equal less greater logicalnot mu dollar cent sterling currency yen Euro florin asciicircum asciitilde acute grave hungarumlaut circumflex caron breve tilde macron dieresis dotaccent ring cedilla ogonek copyright registered trademark degree fi fl .notdef a_b_c MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/light/LightWide.ufo/metainfo.plist000066400000000000000000000004451365052737200316120ustar00rootroot00000000000000 creator org.robofab.ufoLib formatVersion 3 MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/sourceOverview.designspace000066400000000000000000000041111365052737200304040ustar00rootroot00000000000000 MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/sourceOverview.sp3000066400000000000000000000056631365052737200266410ustar00rootroot00000000000000 MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/stems/000077500000000000000000000000001365052737200243045ustar00rootroot00000000000000MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/stems/StemBold.ufo/000077500000000000000000000000001365052737200266055ustar00rootroot00000000000000MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/stems/StemBold.ufo/fontinfo.plist000066400000000000000000000015031365052737200315030ustar00rootroot00000000000000 ascender 750 capHeight 700 descender -250 familyName Stem postscriptBlueValues postscriptFamilyBlues postscriptFamilyOtherBlues postscriptOtherBlues postscriptStemSnapH postscriptStemSnapV styleName Tall unitsPerEm 1000 xHeight 500 MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/stems/StemBold.ufo/glyphs/000077500000000000000000000000001365052737200301135ustar00rootroot00000000000000MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/stems/StemBold.ufo/glyphs/I_.glif000066400000000000000000000005311365052737200313040ustar00rootroot00000000000000 MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/stems/StemBold.ufo/glyphs/contents.plist000066400000000000000000000003551365052737200330300ustar00rootroot00000000000000 I I_.glif MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/stems/StemBold.ufo/lib.plist000066400000000000000000000031701365052737200304310ustar00rootroot00000000000000 com.letterror.lightMeter.prefs chunkSize 5 diameter 200 drawTail invert toolDiameter 30 toolStyle fluid com.typemytype.robofont.background.layerStrokeColor 0.0 0.8 0.2 0.7 com.typemytype.robofont.compileSettings.autohint com.typemytype.robofont.compileSettings.checkOutlines com.typemytype.robofont.compileSettings.decompose com.typemytype.robofont.compileSettings.generateFormat 0 com.typemytype.robofont.compileSettings.releaseMode com.typemytype.robofont.italicSlantOffset 0 com.typemytype.robofont.layerOrder background com.typemytype.robofont.segmentType curve com.typemytype.robofont.shouldAddPointsInSplineConversion 1 com.typemytype.robofont.sort ascending I type glyphList public.glyphOrder I MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/stems/StemBold.ufo/metainfo.plist000066400000000000000000000004561365052737200314710ustar00rootroot00000000000000 creator org.robofab.ufoLib formatVersion 2 MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/stems/StemThin.ufo/000077500000000000000000000000001365052737200266275ustar00rootroot00000000000000MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/stems/StemThin.ufo/fontinfo.plist000066400000000000000000000015041365052737200315260ustar00rootroot00000000000000 ascender 750 capHeight 500 descender -250 familyName Stem postscriptBlueValues postscriptFamilyBlues postscriptFamilyOtherBlues postscriptOtherBlues postscriptStemSnapH postscriptStemSnapV styleName Small unitsPerEm 1000 xHeight 300 MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/stems/StemThin.ufo/glyphs/000077500000000000000000000000001365052737200301355ustar00rootroot00000000000000MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/stems/StemThin.ufo/glyphs/I_.glif000066400000000000000000000004761365052737200313360ustar00rootroot00000000000000 MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/stems/StemThin.ufo/glyphs/contents.plist000066400000000000000000000003551365052737200330520ustar00rootroot00000000000000 I I_.glif MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/stems/StemThin.ufo/lib.plist000066400000000000000000000024751365052737200304620ustar00rootroot00000000000000 com.typemytype.robofont.background.layerStrokeColor 0.0 0.8 0.2 0.7 com.typemytype.robofont.compileSettings.autohint com.typemytype.robofont.compileSettings.checkOutlines com.typemytype.robofont.compileSettings.decompose com.typemytype.robofont.compileSettings.generateFormat 0 com.typemytype.robofont.compileSettings.releaseMode com.typemytype.robofont.italicSlantOffset 0 com.typemytype.robofont.layerOrder background com.typemytype.robofont.segmentType curve com.typemytype.robofont.shouldAddPointsInSplineConversion 1 com.typemytype.robofont.sort ascending I type glyphList public.glyphOrder I MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/data/sources/stems/StemThin.ufo/metainfo.plist000066400000000000000000000004561365052737200315130ustar00rootroot00000000000000 creator org.robofab.ufoLib formatVersion 2 MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/geometryTest.py000066400000000000000000000077371365052737200236400ustar00rootroot00000000000000# -*- coding: utf-8 -*- from __future__ import print_function from defcon.objects.font import Font from fontMath import MathKerning from fontMath.mathKerning import MathKerning from mutatorMath.ufo.document import DesignSpaceDocumentWriter, DesignSpaceDocumentReader from mutatorMath.objects.location import Location import os, sys, shutil """ These are some basic tests for glyph geometry. """ def testingProgressFunc(state, action, text, tick): """ Progress function that gets passed to the DesignSpaceDocumentReader should report on the faulty kerning pairs it found. """ pass def addGlyphs(font, s): # we need to add the glyphs for n in ['glyphOne', 'glyphTwo']: font.newGlyph(n) g = font[n] p = g.getPen() p.moveTo((0,0)) p.lineTo((s,0)) p.lineTo((s,s)) p.lineTo((0,s)) p.closePath() g.width = s def fillInfo(font): font.info.unitsPerEm = 1000 font.info.ascender = 800 font.info.descender = -200 def makeTestFonts(rootPath): """ Make some test fonts that have the kerning problem.""" path1 = os.path.join(rootPath, "geometryMaster1.ufo") path2 = os.path.join(rootPath, "geometryMaster2.ufo") path3 = os.path.join(rootPath, "geometryInstance.ufo") path4 = os.path.join(rootPath, "geometryInstanceAnisotropic1.ufo") path5 = os.path.join(rootPath, "geometryInstanceAnisotropic2.ufo") # Two masters f1 = Font() addGlyphs(f1, 100) f2 = Font() addGlyphs(f2, 500) fillInfo(f1) fillInfo(f2) # save f1.save(path1, 2) f2.save(path2, 2) return path1, path2, path3, path4, path5 def testGeometry(rootPath, cleanUp=True): # that works, let's do it via MutatorMath path1, path2, path3, path4, path5 = makeTestFonts(rootPath) documentPath = os.path.join(rootPath, 'geometryTest.designspace') doc = DesignSpaceDocumentWriter(documentPath, verbose=True) doc.addSource( path1, name="master_1", location=dict(width=0), copyLib=True, copyGroups=True, copyInfo=True, copyFeatures=True, ) doc.addSource( path2, name="master_2", location=dict(width=1000), copyLib=False, copyGroups=False, copyInfo=False, copyFeatures=False, ) doc.startInstance(fileName=path3, familyName="TestInstance", styleName="Regular", location=dict(width=500) ) doc.endInstance() doc.startInstance(fileName=path4, familyName="TestInstance", styleName="Anisotropic1", location=dict(width=(0, 1000)) ) doc.endInstance() doc.startInstance(fileName=path5, familyName="TestInstance", styleName="Anisotropic2", location=dict(width=(1000, 0)) ) doc.endInstance() doc.save() # execute the designspace. doc = DesignSpaceDocumentReader(documentPath, 2, roundGeometry=True, verbose=True, progressFunc=testingProgressFunc) doc.process(makeGlyphs=True, makeKerning=False, makeInfo=True) r1 = Font(path3) assert r1['glyphOne'].bounds == (0, 0, 300, 300) r2 = Font(path4) assert r2['glyphOne'].bounds == (0, 0, 100, 500) r3 = Font(path5) assert r3['glyphOne'].bounds == (0, 0, 500, 100) if cleanUp: # remove the mess os.remove(documentPath) shutil.rmtree(path1) shutil.rmtree(path2) shutil.rmtree(path3) shutil.rmtree(path4) shutil.rmtree(path5) return True def test1(): """ >>> import time >>> import os >>> testData = os.path.join(os.path.dirname(__file__), "testData") >>> try: ... os.mkdir(testData) ... except OSError: ... pass >>> testGeometry(testData, cleanUp=False) True """ if __name__ == "__main__": import doctest sys.exit(doctest.testmod().failed) MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/kerningTest.py000066400000000000000000000112161365052737200234250ustar00rootroot00000000000000# -*- coding: utf-8 -*- from __future__ import print_function from defcon.objects.font import Font from fontMath import MathKerning #from ufoLib.validators import kerningValidatorReportPairs from fontMath.mathKerning import MathKerning from mutatorMath.ufo.document import DesignSpaceDocumentWriter, DesignSpaceDocumentReader from mutatorMath.objects.location import Location import os, sys, shutil """ This is a test for a very specific problem that can occur when two or more masters have valid kerning and groups, but interpolations receive exceptions from both masters. These exceptions can be in conflict. It is hard to predict these conflicts exclusively on the input. But they are easy to detect using the kerning validator from ufoLib. 20160425 Changes in the validation of kerning in Defcon made this test irrelevant. """ def addGlyphs(font): # we need to add the glyphs for n in ['glyphOne', 'glyphTwo', 'glyphThree', 'glyphFour']: font.newGlyph(n) g = font[n] p = g.getPen() p.moveTo((100,100)) p.lineTo((200,200)) p.lineTo((0,100)) p.closePath() def makeTestFonts(rootPath): """ Make some test fonts that have the kerning problem.""" path1 = os.path.join(rootPath, "validMaster1.ufo") path2 = os.path.join(rootPath, "validMaster2.ufo") path3 = os.path.join(rootPath, "invalidInstance.ufo") # Two masters f1 = Font() f1.groups['public.kern1.@MMK_L_one'] = ['glyphOne', 'glyphTwo'] f1.groups['public.kern2.@MMK_R_two'] = ['glyphThree', 'glyphFour'] addGlyphs(f1) f2 = Font() f2.groups.update(f1.groups) # both masters have the same groups addGlyphs(f2) assert f1.groups == f2.groups # a normal group / group pair in each master f1.kerning[('public.kern1.@MMK_L_one', 'public.kern2.@MMK_R_two')] = 1000 f1.kerning[('a', 'b')] = 10 f2.kerning[('public.kern1.@MMK_L_one', 'public.kern2.@MMK_R_two')] = 2000 f2.kerning[('a', 'b')] = 10 # a valid exception to this pair in each master f1.kerning[('public.kern1.@MMK_L_one', 'glyphThree')] = -500 f2.kerning[('glyphOne', 'public.kern2.@MMK_R_two')] = -800 # make sure the kerning and groups in each master validate. #assert kerningValidatorReportPairs(f1.kerning, f1.groups) == (True, [], []) #assert kerningValidatorReportPairs(f2.kerning, f2.groups) == (True, [], []) # save f1.save(path1, 3) f2.save(path2, 3) return path1, path2, path3 def testingProgressFunc(state, action, text, tick): """ Progress function that gets passed to the DesignSpaceDocumentReader should report on the faulty kerning pairs it found. """ failPair1 = "invalidInstance.ufo:\nThese kerning pairs failed validation and have been removed:\nglyphOne, public.kern2.@MMK_R_two (-400) conflicts with public.kern1.@MMK_L_one, glyphThree (-250)\npublic.kern1.@MMK_L_one, glyphThree (-250) conflicts with glyphOne, public.kern2.@MMK_R_two (-400)" if state == "error" and action == 'kerning': assert failPair1 in text def testOuroborosKerning(rootPath, cleanUp=True): # that works, let's do it via MutatorMath path1, path2, path3 = makeTestFonts(rootPath) documentPath = os.path.join(rootPath, 'kerningTest.designspace') doc = DesignSpaceDocumentWriter(documentPath, verbose=True) doc.addSource( path1, name="master_1", location=dict(width=0), copyLib=True, copyGroups=True, copyInfo=True, copyFeatures=True, ) doc.addSource( path2, name="master_2", location=dict(width=1000), copyLib=False, copyGroups=False, copyInfo=False, copyFeatures=False, ) doc.startInstance(fileName=path3, familyName="TestInstance", styleName="Regular", location=dict(width=100) ) doc.writeKerning(location=dict(width=500)) doc.endInstance() doc.save() # execute the designspace. Kerning errors should be tripped by the doc = DesignSpaceDocumentReader(documentPath, 2, roundGeometry=True, verbose=True, progressFunc=testingProgressFunc) doc.process(makeGlyphs=True, makeKerning=True, makeInfo=True) if cleanUp: # remove the mess shutil.rmtree(path1) shutil.rmtree(path2) shutil.rmtree(path3) return True def test1(): """ >>> import time >>> import os >>> testOuroborosKerning(os.path.join(os.getcwd(), "testData"), cleanUp=True) True """ if __name__ == "__main__": import doctest sys.exit(doctest.testmod().failed) MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/mutingTest.py000066400000000000000000000105511365052737200232740ustar00rootroot00000000000000# -*- coding: utf-8 -*- from __future__ import print_function from defcon.objects.font import Font from fontMath import MathKerning from fontMath.mathKerning import MathKerning from mutatorMath.ufo.document import DesignSpaceDocumentWriter, DesignSpaceDocumentReader from mutatorMath.objects.location import Location import os, sys, shutil """ This is a test for the muting functionality. - mute masters - mute specific glyphs """ def testingProgressFunc(state, action, text, tick): """ Progress function that gets passed to the DesignSpaceDocumentReader should report on the faulty kerning pairs it found. """ pass #print "testingProgressFunc", state, action, text, tick def addGlyphs(font, offset=0): # we need to add the glyphs # add an offset so we can see if the masters are active. for n in ['glyphOne', 'glyphTwo', 'glyphThree', 'glyphFour']: font.newGlyph(n) g = font[n] p = g.getPen() p.moveTo((100+offset,100+offset)) p.lineTo((200+offset,200+offset)) p.lineTo((offset,100+offset)) p.closePath() def makeTestFonts(rootPath): """ Make some test fonts that have the kerning problem.""" path1 = os.path.join(rootPath, "mutingMaster1.ufo") path2 = os.path.join(rootPath, "mutingMaster2.ufo") path3 = os.path.join(rootPath, "mutedGlyphInstance.ufo") # Two masters f1 = Font() addGlyphs(f1, 0) f1.info.unitsPerEm = 1000 f1.kerning[('glyphOne', 'glyphOne')] = -100 f2 = Font() addGlyphs(f2, 33) f2.info.unitsPerEm = 2000 f2.kerning[('glyphOne', 'glyphOne')] = -200 # save f1.save(path1, 3) f2.save(path2, 3) return path1, path2, path3 def testMutingOptions(rootPath, cleanUp=True): # that works, let's do it via MutatorMath # path1 and path2 are masters. path3 is the instance path1, path2, path3 = makeTestFonts(rootPath) documentPath = os.path.join(rootPath, 'mutingTest.designspace') doc = DesignSpaceDocumentWriter(documentPath, verbose=True) doc.addSource( path1, name="master_1", location=dict(width=0), copyLib=True, copyGroups=True, copyInfo=True, copyFeatures=True, muteKerning=True ) doc.addSource( path2, name="master_2", location=dict(width=1000), copyLib=False, copyGroups=False, copyInfo=False, copyFeatures=False, muteInfo=True, mutedGlyphNames=['glyphThree'] # mute glyphThree in master 1 ) doc.startInstance(fileName=path3, familyName="TestInstance", styleName="Regular", location=dict(width=500) ) doc.writeGlyph('glyphFour', mute=True) # mute glyphFour in the instance doc.writeKerning() doc.writeInfo() doc.endInstance() doc.save() # execute the designspace. doc = DesignSpaceDocumentReader(documentPath, 2, roundGeometry=True, verbose=True, progressFunc=testingProgressFunc) doc.process(makeGlyphs=True, makeKerning=True, makeInfo=True) # look at the results m1 = Font(path1) m2 = Font(path2) r = Font(path3) # # the glyphThree master was muted in the second master # so the instance glyphThree should be the same as the first master: assert r['glyphThree'].bounds == m1['glyphThree'].bounds # we muted glyphFour in the instance. # so it should not be part of the instance UFO: assert "glyphFour" not in r # font.info is muted for master2, so the instance has to have the values from master 1 assert r.info.unitsPerEm == m1.info.unitsPerEm # kerning is muted for master1, so the instance has to have the kerning from master 2 assert r.kerning[('glyphOne', 'glyphOne')] == m2.kerning[('glyphOne', 'glyphOne')] if cleanUp: # remove the mess try: os.remove(documentPath) shutil.rmtree(path1) shutil.rmtree(path2) shutil.rmtree(path3) except: pass return True def test1(): """ >>> import time >>> import os >>> testData = os.path.join(os.path.dirname(__file__), "testData") >>> testMutingOptions(testData, cleanUp=False) True """ if __name__ == "__main__": import doctest sys.exit(doctest.testmod().failed) MutatorMath-3.0.1/Lib/mutatorMath/test/ufo/test.py000066400000000000000000000476421365052737200221230ustar00rootroot00000000000000# -*- coding: utf-8 -*- from __future__ import print_function """ These are tests for writing and processing designspace.designspace documents - write various designspaces - read them - process them using the test fonts - show masters can be read from different directories - show instances can be generated into different directories - do some basic output testing. """ import os import defcon.objects.font import mutatorMath.objects.error from mutatorMath.ufo.document import DesignSpaceDocumentWriter, DesignSpaceDocumentReader from mutatorMath.objects.location import Location def stripPrefix(d): # strip the "public.kern" prefixes from d new = [] for pair, value in d: a, b = pair if "public.kern" in a: a = a[13:] if "public.kern" in b: b = b[13:] new.append(((a,b), value)) return sorted(new) #t = [(('V', 'public.kern2.@MMK_R_A'), -100), (('public.kern1.@MMK_L_A', 'V'), -100)] #stripPrefix(t) def test1(): """ >>> import time >>> from mutatorMath.ufo.document import initializeLogger >>> testRoot = os.path.join(os.path.dirname(__file__), 'data') >>> documentPath = os.path.join(testRoot, 'exporttest_basic.designspace') >>> sourcePath = os.path.join(testRoot, 'sources') >>> instancePath = os.path.join(testRoot, 'instances') >>> master1Path = os.path.join(testRoot, ) >>> #logPath = None # >>> logPath = os.path.join(testRoot, "tests.log") >>> if logPath is not None: ... if os.path.exists(logPath): ... os.remove(logPath) ... initializeLogger(logPath) >>> ufoVersion=2 >>> roundGeometry=True # this will trigger some fails if run with a pre-ufo3 fontMath. >>> doc = DesignSpaceDocumentWriter(documentPath, verbose=True) >>> doc.addSource( ... os.path.join(sourcePath, "light", "LightCondensed.ufo"), ... name="master_1", ... location=dict(width=0), ... copyLib=True, # change to see the copy Lib test fail ... copyGroups=True, # change to see assertions fail on groupSource and instance groups. ... copyInfo=True, ... copyFeatures=True, ... muteKerning=False, ... muteInfo=True, ... mutedGlyphNames=['a',], ... familyName="ExplicitSourceFamilyName", ... styleName="ExplicitSourceStyleName",) >>> doc.addSource( ... os.path.join(sourcePath, "light", "LightWide.ufo"), ... name="master_2", ... location=dict(width=1), ... copyLib=False, ... copyGroups=False, ... copyInfo=False, ... muteKerning=True, ... muteInfo=True, ... mutedGlyphNames=['b'] ) >>> testOutputFileName = os.path.join(instancePath, "A", "testOutput_glyphs.ufo") >>> glyphMasters = [('M', "master_1", dict(width=0)), ('M', "master_2", dict(width=1)), ] >>> doc.startInstance(fileName=testOutputFileName, ... familyName="TestFamily", ... styleName="TestStyleName", ... location=dict(width=(0.2, 0.8))) >>> doc.writeGlyph("M", unicodes=[0xff], location=dict(width=0.9), masters=None, note="testnote123") >>> doc.writeGlyph("N", location=dict(width=0.7), masters=glyphMasters) >>> doc.endInstance() >>> doc.save() >>> doc = DesignSpaceDocumentReader(documentPath, ufoVersion, roundGeometry=roundGeometry, verbose=True, logPath=logPath) >>> doc.process(makeGlyphs=True, makeKerning=False, makeInfo=False) # check if we found the UFOs >>> assert "master_1" in doc.sources >>> assert "master_2" in doc.sources # check if we are reading the muting flags >>> assert doc.libSource == 'master_1' >>> assert doc.groupsSource == 'master_1' >>> assert doc.libSource == 'master_1' >>> assert doc.muted == {'info': ['master_1', 'master_2'], 'glyphs': {'master_2': ['b'], 'master_1': ['a']}, 'kerning': ['master_2']} # check the locations >>> fontObj, loc = doc.sources['master_1'] >>> loc.asTuple() (('width', 0.0),) >>> loc.asTuple() (('width', 0.0),) >>> fontObj, loc = doc.sources['master_2'] >>> loc.asTuple() (('width', 1.0),) # check the instances >>> assert os.path.basename(testOutputFileName) in doc.results >>> resultUFOPath = doc.results[os.path.basename(testOutputFileName)] >>> instance = defcon.objects.font.Font(resultUFOPath) # note: the next assertion will fail if the calculations were made with the # pre-ufo3 fontMath. >>> assert instance['M'].unicodes == [0xff] # check the groups >>> ('testGroup', ['E', 'F', 'H']) in list(instance.groups.items()) True # check the lib >>> assert "testLibItemKey" in instance.lib.keys() # check the feature text was copied from the source >>> assert "Hi_this_is_the_feature." in instance.features.text # basic kerning processing. >>> documentPath = os.path.join(testRoot, 'exporttest_kerning.designspace') >>> doc = DesignSpaceDocumentWriter(documentPath, verbose=True) >>> doc.addSource( ... os.path.join(sourcePath, "light", "LightCondensed.ufo"), ... name="master_1", ... location=dict(weight=0), ... copyLib=True, ... copyGroups=True, ... copyInfo=True, ... muteKerning=False, ... muteInfo=False) >>> doc.addSource( ... os.path.join(sourcePath, "bold", "BoldCondensed.ufo"), ... name="master_2", ... location=dict(weight=1), ... copyLib=False, ... copyGroups=False, ... copyInfo=False, ... muteKerning=False, ... muteInfo=False ) >>> testOutputFileName = os.path.join(instancePath, "B", "testOutput_kerning.ufo") >>> doc.startInstance(fileName=testOutputFileName, familyName="TestFamily", styleName="TestStyleName", location=dict(weight=0.6)) # give this kerning master a different location >>> #doc.writeKerning(location=dict(weight=1)) >>> #doc.endInstance() >>> #doc.save() >>> #doc = DesignSpaceDocumentReader(documentPath, ufoVersion, roundGeometry=roundGeometry, verbose=True, logPath=logPath) >>> #doc.process(makeGlyphs=False, makeKerning=True, makeInfo=False) >>> #assert os.path.basename(testOutputFileName) in doc.results >>> #resultUFOPath = doc.results[os.path.basename(testOutputFileName)] >>> #instance = defcon.objects.font.Font(resultUFOPath) >>> #assert sorted(instance.kerning.items()) == stripPrefix([(('@MMK_L_A', 'V'), 100), (('V', '@MMK_R_A'), 100)]) # test the effects of muting the kerning >>> documentPath = os.path.join(testRoot, 'exporttest_kerning_muted.designspace') >>> doc = DesignSpaceDocumentWriter(documentPath, verbose=True) >>> doc.addSource( ... os.path.join(sourcePath, "light", "LightCondensed.ufo"), ... name="master_1", ... location=dict(weight=0), ... copyLib=True, ... copyGroups=True, ... copyInfo=True, ... muteKerning=False, ... muteInfo=False) >>> doc.addSource( ... os.path.join(sourcePath, "bold", "BoldCondensed.ufo"), ... name="master_2", ... location=dict(weight=1), ... copyLib=False, ... copyGroups=False, ... copyInfo=False, ... muteKerning=True, # mute a master at a non-origin location! ... muteInfo=False ) >>> testOutputFileName = os.path.join(instancePath, "C", "testOutput_kerning_muted.ufo") >>> testLocation = dict(weight=0.6) # change this location to see calculation assertions fail. >>> doc.startInstance(fileName=testOutputFileName, familyName="TestFamily", styleName="TestStyleName", location=testLocation) >>> doc.writeKerning() >>> doc.endInstance() >>> doc.save() >>> doc = DesignSpaceDocumentReader(documentPath, ufoVersion, roundGeometry=roundGeometry, verbose=True, logPath=logPath) >>> paths = doc.getSourcePaths() >>> len(paths) 2 >>> doc.process(makeGlyphs=False, makeKerning=True, makeInfo=False) >>> assert doc.groupsSource == 'master_1' >>> assert os.path.basename(testOutputFileName) in doc.results >>> resultUFOPath = doc.results[os.path.basename(testOutputFileName)] >>> instance = defcon.objects.font.Font(resultUFOPath) # the bold condensed kerning master has been muted, we expect the light condensed data in the instance >>> assert [(('@MMK_L_A', 'V'), -100), (('V', '@MMK_R_A'), -100)] == stripPrefix(sorted(instance.kerning.items())) # info data # calculating fields # copying fields >>> documentPath = os.path.join(testRoot, 'exporttest_info.designspace') >>> doc = DesignSpaceDocumentWriter(documentPath, verbose=True) >>> doc.addSource( ... os.path.join(sourcePath, "light", "LightCondensed.ufo"), ... name="master_1", ... location=dict(weight=0), ... copyLib=False, ... copyGroups=False, ... copyInfo=True, # flip to False and see some assertions fail ... muteKerning=True, ... muteInfo=False) >>> doc.addSource( ... os.path.join(sourcePath, "bold", "BoldCondensed.ufo"), ... name="master_2", ... location=dict(weight=1), ... copyLib=False, ... copyGroups=False, ... copyInfo=False, ... muteKerning=True, ... muteInfo=False ) >>> testOutputFileName = os.path.join(instancePath, "D", "testOutput_info.ufo") >>> testLocation = dict(weight=0.5) # change this location to see calculation assertions fail. >>> doc.startInstance( ... fileName=testOutputFileName, ... familyName="TestFamily", ... styleName="TestStyleName", ... location=testLocation, ... postScriptFontName="TestPostScriptFontNameValue", ... styleMapFamilyName="TestStyleMapFamilyNameValue", ... styleMapStyleName="bold italic", ... ) >>> doc.writeInfo() >>> doc.endInstance() >>> doc.save() >>> doc = DesignSpaceDocumentReader(documentPath, ufoVersion, roundGeometry=roundGeometry, verbose=True, logPath=logPath) >>> doc.process(makeGlyphs=False, makeKerning=False, makeInfo=True) >>> assert os.path.basename(testOutputFileName) in doc.results >>> resultUFOPath = doc.results[os.path.basename(testOutputFileName)] >>> instance = defcon.objects.font.Font(resultUFOPath) # example calculated values >>> assert instance.info.ascender == 750 >>> assert instance.info.capHeight == 750 # example copied values >>> assert instance.info.versionMajor == 1 >>> assert instance.info.openTypeOS2VendorID == "LTTR" >>> assert instance.info.copyright == "License same as MutatorMath. BSD 3-clause. [test-token: C]" # test the build script >>> documentPath = os.path.join(testRoot, 'exporttest_build.designspace') >>> doc = DesignSpaceDocumentWriter(documentPath, verbose=True) >>> doc.addSource( ... os.path.join(sourcePath, "light", "LightCondensed.ufo"), ... name="master_1", ... location=dict(weight=0), ... copyLib=True, ... copyGroups=True, ... copyInfo=True, ... muteKerning=False, ... muteInfo=False) >>> doc.addSource( ... os.path.join(sourcePath, "bold", "BoldCondensed.ufo"), ... name="master_2", ... location=dict(weight=1), ... copyLib=False, ... copyGroups=False, ... copyInfo=False, ... muteKerning=False, ... muteInfo=False ) >>> testOutputFileName = os.path.join(instancePath, "E", "testOutput_build.ufo") >>> testLocation = dict(weight=0.25) # change this location to see calculation assertions fail. >>> doc.startInstance( ... fileName=testOutputFileName, ... familyName="TestFamily", ... styleName="TestStyleName", ... location=testLocation) >>> doc.writeInfo() >>> doc.writeKerning() >>> doc.endInstance() >>> doc.save() # test build function -- single designspace file >>> from mutatorMath.ufo import build >>> import os >>> import posixpath >>> here = os.path.join(os.path.dirname(__file__), 'data', 'exporttest_basic.designspace') >>> results = build(here, outputUFOFormatVersion=2) >>> ufoFullPath = results[0]['testOutput_glyphs.ufo'] >>> ufoRelPath = os.path.relpath(ufoFullPath, os.path.dirname(__file__)) >>> posixRelPath = posixpath.join(*ufoRelPath.split(os.path.sep)) >>> posixRelPath 'data/instances/A/testOutput_glyphs.ufo' # test the axes elements >>> documentPath = os.path.join(testRoot, 'warpmap_test.designspace') >>> doc = DesignSpaceDocumentWriter(documentPath, verbose=True) >>> def grow(base, factor, steps): ... return [(i*100, int(round(base*(1+factor)**i))) for i in range(steps)] >>> doc.addAxis("wght", "weight", 0, 1000, 0, grow(100,0.55,11)) >>> doc.addSource( ... os.path.join(sourcePath, "stems", "StemThin.ufo"), ... name="master_1", ... location=dict(weight=0), ... copyLib=True, ... copyGroups=True, ... copyInfo=True, ... muteKerning=False, ... muteInfo=False) >>> doc.addSource( ... os.path.join(sourcePath, "stems", "StemBold.ufo"), ... name="master_2", ... location=dict(weight=1000), ... copyLib=False, ... copyGroups=False, ... copyInfo=False, ... muteKerning=False, ... muteInfo=False ) >>> testOutputFileName = os.path.join(instancePath, "W", "StemOutput.ufo") >>> testLocation = dict(weight=0) # change this location to see calculation assertions fail. >>> doc.startInstance( ... fileName=testOutputFileName, ... familyName="TestFamily", ... styleName="Warped", ... location=testLocation) >>> doc.writeInfo() >>> doc.writeKerning() >>> glyphMasters = [('I', "master_1", dict(weight=0)), ('I', "master_2", dict(weight=1000)), ] >>> for i in range(0, 1000, 50): ... doc.writeGlyph("I.%04d"%i, location=dict(weight=i), masters=glyphMasters) ... >>> doc.endInstance() >>> doc.save() >>> doc = DesignSpaceDocumentReader(documentPath, ufoVersion, roundGeometry=roundGeometry, verbose=True, logPath=logPath) >>> doc.process(makeGlyphs=True, makeKerning=False, makeInfo=False) >>> documentPath = os.path.join(testRoot, 'no_warpmap_test.designspace') >>> doc = DesignSpaceDocumentWriter(documentPath, verbose=True) >>> doc.addAxis("wght", "weight", 0, 1000, 0) >>> doc.addSource( ... os.path.join(sourcePath, "stems", "StemThin.ufo"), ... name="master_1", ... location=dict(weight=0), ... copyLib=True, ... copyGroups=True, ... copyInfo=True, ... muteKerning=False, ... muteInfo=False) >>> doc.addSource( ... os.path.join(sourcePath, "stems", "StemBold.ufo"), ... name="master_2", ... location=dict(weight=1000), ... copyLib=False, ... copyGroups=False, ... copyInfo=False, ... muteKerning=False, ... muteInfo=False ) >>> testOutputFileName = os.path.join(instancePath, "W", "StemOutput_nowarp.ufo") >>> testLocation = dict(weight=0) # change this location to see calculation assertions fail. >>> doc.startInstance( ... fileName=testOutputFileName, ... familyName="TestFamily", ... styleName="NotWarped", ... location=testLocation) >>> doc.writeInfo() >>> doc.writeKerning() >>> glyphMasters = [('I', "master_1", dict(weight=0)), ('I', "master_2", dict(weight=1000)), ] >>> for i in range(0, 1000, 50): ... doc.writeGlyph("I.%04d"%i, location=dict(weight=i), masters=glyphMasters) ... >>> doc.endInstance() >>> doc.save() >>> doc = DesignSpaceDocumentReader(documentPath, ufoVersion, roundGeometry=roundGeometry, verbose=True, logPath=logPath) >>> doc.process(makeGlyphs=True, makeKerning=False, makeInfo=False) # test the axes element >>> from pprint import pprint >>> documentPath = os.path.join(testRoot, 'axes_test.designspace') >>> doc = DesignSpaceDocumentWriter(documentPath, verbose=True) >>> def grow(base, factor, steps): ... return [(i*100, int(round(base*(1+factor)**i))) for i in range(steps)] >>> # axis with a warp map >>> warpMap = grow(100,0.55,11) >>> doc.addAxis("wght", "weight", -1000, 1000, 0, warpMap) >>> # axis without a warp map >>> doc.addAxis("wdth", "width", 0, 1000, 0) >>> doc.save() >>> doc = DesignSpaceDocumentReader(documentPath, ufoVersion, roundGeometry=roundGeometry, verbose=True, logPath=logPath) >>> pprint(doc.axes) {'weight': {'default': 0.0, 'map': [(0.0, 100.0), (100.0, 155.0), (200.0, 240.0), (300.0, 372.0), (400.0, 577.0), (500.0, 895.0), (600.0, 1387.0), (700.0, 2149.0), (800.0, 3332.0), (900.0, 5164.0), (1000.0, 8004.0)], 'maximum': 1000.0, 'minimum': -1000.0, 'name': 'weight', 'tag': 'wght'}, 'width': {'default': 0.0, 'map': [], 'maximum': 1000.0, 'minimum': 0.0, 'name': 'width', 'tag': 'wdth'}} >>> doc.process(makeGlyphs=False, makeKerning=False, makeInfo=False) """ def bender_and_mutatorTest(): """ >>> from mutatorMath.objects.bender import Bender >>> from mutatorMath.objects.location import Location >>> from mutatorMath.objects.mutator import buildMutator >>> w = {'aaaa':{ ... 'map': [(300, 50), ... (400, 100), ... (700, 150)], ... 'name':'aaaaAxis', ... 'tag':'aaaa', ... 'minimum':0, ... 'maximum':1000, ... 'default':0}} >>> b = Bender(w) >>> assert b(dict(aaaa=300)) == {'aaaa': 50} >>> assert b(dict(aaaa=400)) == {'aaaa': 100} >>> assert b(dict(aaaa=700)) == {'aaaa': 150} master locations are always in internal design coordinates, thus they are considered to be already mapped or bent. >>> items = [ ... (Location(aaaa=50), 0), ... (Location(aaaa=100), 50), ... (Location(aaaa=150), 100), ... ] >>> bias, mut = buildMutator(items, w, bias=Location(aaaa=100)) >>> bias >>> bias, mut = buildMutator(items, w, bias=Location(aaaa=150)) >>> bias >>> bias, mut = buildMutator(items, w, bias=Location(aaaa=50)) >>> bias >>> expect = sorted([(('aaaa', 100),), (('aaaa', 50),), ()]) >>> expect [(), (('aaaa', 50),), (('aaaa', 100),)] >>> got = sorted(mut.keys()) >>> got [(), (('aaaa', 50),), (('aaaa', 100),)] >>> assert got == expect Instance location are not bent by default, i.e. are interpreted as internal design coordinates: >>> assert mut.makeInstance(Location(aaaa=50)) == 0 >>> assert mut.makeInstance(Location(aaaa=100)) == 50 >>> assert mut.makeInstance(Location(aaaa=150)) == 100 If bend=True, instance locations are interpreted as user-space coordinates >>> assert mut.makeInstance(Location(aaaa=300), bend=True) == 0 >>> assert mut.makeInstance(Location(aaaa=400), bend=True) == 50 >>> assert mut.makeInstance(Location(aaaa=700), bend=True) == 100 """ if __name__ == '__main__': import sys import doctest sys.exit(doctest.testmod().failed) MutatorMath-3.0.1/Lib/mutatorMath/ufo/000077500000000000000000000000001365052737200175765ustar00rootroot00000000000000MutatorMath-3.0.1/Lib/mutatorMath/ufo/__init__.py000066400000000000000000000032221365052737200217060ustar00rootroot00000000000000 """ These are some UFO specific tools for use with Mutator. build() is a convenience function for reading and executing a designspace file. documentPath: filepath to the .designspace document outputUFOFormatVersion: ufo format for output verbose: True / False for lots or no feedback logPath: filepath to a log file progressFunc: an optional callback to report progress. see mutatorMath.ufo.tokenProgressFunc """ def tokenProgressFunc(state="update", action=None, text=None, tick=0): """ state: string, "update", "reading sources", "wrapping up" action: string, "stop", "start" text: string, value, additional parameter. For instance ufoname. tick: a float between 0 and 1 indicating progress. """ print("tokenProgressFunc %s: %s\n%s (%s)"%(state, str(action), str(text), str(tick))) def build( documentPath, outputUFOFormatVersion=2, roundGeometry=True, verbose=True, logPath=None, progressFunc=None, bendLocations=False, ): """ Simple builder for UFO designspaces. """ from mutatorMath.ufo.document import DesignSpaceDocumentReader import os, glob if os.path.isdir(documentPath): # process all *.designspace documents in this folder todo = glob.glob(os.path.join(documentPath, "*.designspace")) else: # process the todo = [documentPath] results = [] for path in todo: reader = DesignSpaceDocumentReader( path, ufoVersion=outputUFOFormatVersion, roundGeometry=roundGeometry, verbose=verbose, logPath=logPath, progressFunc=progressFunc, ) reader.process(bendLocations=bendLocations) results.append(reader.results) reader = None return results MutatorMath-3.0.1/Lib/mutatorMath/ufo/document.py000066400000000000000000001071671365052737200220020ustar00rootroot00000000000000# -*- coding: utf-8 -*- from __future__ import print_function import logging import os import posixpath import xml.etree.ElementTree as ET import defcon from mutatorMath.objects.error import MutatorError from mutatorMath.objects.location import Location from mutatorMath.objects.mutator import Mutator from mutatorMath.ufo.instance import InstanceWriter """ Read and write mutator math designspace files. A DesignSpaceDocumentWriter object can be instructed to write a properly formed description of a designspace for UFO fonts. A DesignSpaceDocumentReader object can then execute such a designspace document and generate the UFO's described. """ import logging def initializeLogger(proposedLogPath): logging.basicConfig(filename=proposedLogPath, level=logging.INFO, format='%(asctime)s %(message)s') def _indent(elem, whitespace=" ", level=0): # taken from http://effbot.org/zone/element-lib.htm#prettyprint i = "\n" + level * whitespace if len(elem): if not elem.text or not elem.text.strip(): elem.text = i + whitespace if not elem.tail or not elem.tail.strip(): elem.tail = i for elem in elem: _indent(elem, whitespace, level+1) if not elem.tail or not elem.tail.strip(): elem.tail = i else: if level and (not elem.tail or not elem.tail.strip()): elem.tail = i class DesignSpaceDocumentWriter(object): """ Writer for a design space description file. * path: path for the document * toolVersion: version of this tool """ _whiteSpace = " " def __init__(self, path, toolVersion=3, verbose=False): self.path = path self.toolVersion = toolVersion self.verbose = verbose self.root = ET.Element("designspace") self.root.attrib['format'] = "%d"%toolVersion self.root.append(ET.Element("axes")) self.root.append(ET.Element("sources")) self.root.append(ET.Element("instances")) self.logger = None if verbose: self.logger = logging.getLogger("mutatorMath") self.currentInstance = None def save(self, pretty=True): """ Save the xml. Make pretty if necessary. """ self.endInstance() if pretty: _indent(self.root, whitespace=self._whiteSpace) tree = ET.ElementTree(self.root) tree.write(self.path, encoding="utf-8", method='xml', xml_declaration=True) if self.logger: self.logger.info("Writing %s", self.path) def _makeLocationElement(self, locationObject, name=None): """ Convert Location object to an locationElement.""" locElement = ET.Element("location") if name is not None: locElement.attrib['name'] = name for dimensionName, dimensionValue in locationObject.items(): dimElement = ET.Element('dimension') dimElement.attrib['name'] = dimensionName if type(dimensionValue)==tuple: dimElement.attrib['xvalue'] = "%f"%dimensionValue[0] dimElement.attrib['yvalue'] = "%f"%dimensionValue[1] else: dimElement.attrib['xvalue'] = "%f"%dimensionValue locElement.append(dimElement) return locElement def _posixPathRelativeToDocument(self, otherPath): relative = os.path.relpath(otherPath, os.path.dirname(self.path)) return posixpath.join(*relative.split(os.path.sep)) def addSource(self, path, name, location, copyLib=False, copyGroups=False, copyInfo=False, copyFeatures=False, muteKerning=False, muteInfo=False, mutedGlyphNames=None, familyName=None, styleName=None, ): """ Add a new UFO source to the document. * path: path to this UFO, will be written as a relative path to the document path. * name: reference name for this source * location: name of the location for this UFO * copyLib: copy the contents of this source to instances * copyGroups: copy the groups of this source to instances * copyInfo: copy the non-numerical fields from this source.info to instances. * copyFeatures: copy the feature text from this source to instances * muteKerning: mute the kerning data from this source * muteInfo: mute the font info data from this source * familyName: family name for this UFO (to be able to work on the names without reading the whole UFO) * styleName: style name for this UFO (to be able to work on the names without reading the whole UFO) Note: no separate flag for mute font: the source is just not added. """ sourceElement = ET.Element("source") sourceElement.attrib['filename'] = self._posixPathRelativeToDocument(path) sourceElement.attrib['name'] = name if copyLib: libElement = ET.Element('lib') libElement.attrib['copy'] = "1" sourceElement.append(libElement) if copyGroups: groupsElement = ET.Element('groups') groupsElement.attrib['copy'] = "1" sourceElement.append(groupsElement) if copyFeatures: featuresElement = ET.Element('features') featuresElement.attrib['copy'] = "1" sourceElement.append(featuresElement) if copyInfo or muteInfo: # copy info: infoElement = ET.Element('info') if copyInfo: infoElement.attrib['copy'] = "1" if muteInfo: infoElement.attrib['mute'] = "1" sourceElement.append(infoElement) if muteKerning: # add kerning element to the source kerningElement = ET.Element("kerning") kerningElement.attrib["mute"] = '1' sourceElement.append(kerningElement) if mutedGlyphNames: # add muted glyphnames to the source for name in mutedGlyphNames: glyphElement = ET.Element("glyph") glyphElement.attrib["name"] = name glyphElement.attrib["mute"] = '1' sourceElement.append(glyphElement) if familyName is not None: sourceElement.attrib['familyname'] = familyName if styleName is not None: sourceElement.attrib['stylename'] = styleName locationElement = self._makeLocationElement(location) sourceElement.append(locationElement) self.root.findall('.sources')[0].append(sourceElement) def startInstance(self, name=None, location=None, familyName=None, styleName=None, fileName=None, postScriptFontName=None, styleMapFamilyName=None, styleMapStyleName=None, ): """ Start a new instance. Instances can need a lot of configuration. So this method starts a new instance element. Use endInstance() to finish it. * name: the name of this instance * familyName: name for the font.info.familyName field. Required. * styleName: name fot the font.info.styleName field. Required. * fileName: filename for the instance UFO file. Required. * postScriptFontName: name for the font.info.postScriptFontName field. Optional. * styleMapFamilyName: name for the font.info.styleMapFamilyName field. Optional. * styleMapStyleName: name for the font.info.styleMapStyleName field. Optional. """ if self.currentInstance is not None: # We still have the previous one open self.endInstance() instanceElement = ET.Element('instance') if name is not None: instanceElement.attrib['name'] = name if location is not None: locationElement = self._makeLocationElement(location) instanceElement.append(locationElement) if familyName is not None: instanceElement.attrib['familyname'] = familyName if styleName is not None: instanceElement.attrib['stylename'] = styleName if fileName is not None: instanceElement.attrib['filename'] = self._posixPathRelativeToDocument(fileName) if postScriptFontName is not None: instanceElement.attrib['postscriptfontname'] = postScriptFontName if styleMapFamilyName is not None: instanceElement.attrib['stylemapfamilyname'] = styleMapFamilyName if styleMapStyleName is not None: instanceElement.attrib['stylemapstylename'] = styleMapStyleName self.currentInstance = instanceElement def endInstance(self): """ Finalise the instance definition started by startInstance(). """ if self.currentInstance is None: return allInstances = self.root.findall('.instances')[0].append(self.currentInstance) self.currentInstance = None def writeGlyph(self, name, unicodes=None, location=None, masters=None, note=None, mute=False, ): """ Add a new glyph to the current instance. * name: the glyph name. Required. * unicodes: unicode values for this glyph if it needs to be different from the unicode values associated with this glyph name in the masters. * location: a design space location for this glyph if it needs to be different from the instance location. * masters: a list of masters and locations for this glyph if they need to be different from the masters specified for this instance. * note: a note for this glyph * mute: if this glyph is muted. None of the other attributes matter if this one is true. """ if self.currentInstance is None: return glyphElement = ET.Element('glyph') if mute: glyphElement.attrib['mute'] = "1" if unicodes is not None: glyphElement.attrib['unicode'] = " ".join([hex(u) for u in unicodes]) if location is not None: locationElement = self._makeLocationElement(location) glyphElement.append(locationElement) if name is not None: glyphElement.attrib['name'] = name if note is not None: noteElement = ET.Element('note') noteElement.text = note glyphElement.append(noteElement) if masters is not None: mastersElement = ET.Element("masters") for glyphName, masterName, location in masters: masterElement = ET.Element("master") if glyphName is not None: masterElement.attrib['glyphname'] = glyphName masterElement.attrib['source'] = masterName if location is not None: locationElement = self._makeLocationElement(location) masterElement.append(locationElement) mastersElement.append(masterElement) glyphElement.append(mastersElement) if self.currentInstance.findall('.glyphs') == []: glyphsElement = ET.Element('glyphs') self.currentInstance.append(glyphsElement) else: glyphsElement = self.currentInstance.findall('.glyphs')[0] glyphsElement.append(glyphElement) def writeInfo(self, location=None, masters=None): """ Write font into the current instance. Note: the masters attribute is ignored at the moment. """ if self.currentInstance is None: return infoElement = ET.Element("info") if location is not None: locationElement = self._makeLocationElement(location) infoElement.append(locationElement) self.currentInstance.append(infoElement) def writeKerning(self, location=None, masters=None): """ Write kerning into the current instance. Note: the masters attribute is ignored at the moment. """ if self.currentInstance is None: return kerningElement = ET.Element("kerning") if location is not None: locationElement = self._makeLocationElement(location) kerningElement.append(locationElement) self.currentInstance.append(kerningElement) def writeWarp(self, warpDict): """ Write a list of (in, out) values for a warpmap """ warpElement = ET.Element("warp") axisNames = sorted(warpDict.keys()) for name in axisNames: axisElement = ET.Element("axis") axisElement.attrib['name'] = name for a, b in warpDict[name]: warpPt = ET.Element("map") warpPt.attrib['input'] = str(a) warpPt.attrib['output'] = str(b) axisElement.append(warpPt) warpElement.append(axisElement) self.root.append(warpElement) def addAxis(self, tag, name, minimum, maximum, default, warpMap=None): """ Write an axis element. This will be added to the element. """ axisElement = ET.Element("axis") axisElement.attrib['name'] = name axisElement.attrib['tag'] = tag axisElement.attrib['minimum'] = str(minimum) axisElement.attrib['maximum'] = str(maximum) axisElement.attrib['default'] = str(default) if warpMap is not None: for a, b in warpMap: warpPt = ET.Element("map") warpPt.attrib['input'] = str(a) warpPt.attrib['output'] = str(b) axisElement.append(warpPt) self.root.findall('.axes')[0].append(axisElement) class DesignSpaceDocumentReader(object): """ Read a designspace description. Build Instance objects, generate them. * documentPath: path of the document to read * ufoVersion: target UFO version * roundGeometry: apply rounding to all geometry """ _fontClass = defcon.Font _glyphClass = defcon.Glyph _libClass = defcon.Lib _glyphContourClass = defcon.Contour _glyphPointClass = defcon.Point _glyphComponentClass = defcon.Component _glyphAnchorClass = defcon.Anchor _kerningClass = defcon.Kerning _groupsClass = defcon.Groups _infoClass = defcon.Info _featuresClass = defcon.Features _instanceWriterClass = InstanceWriter _tempFontLibGlyphMuteKey = "_mutatorMath.temp.mutedGlyphNames" _tempFontLocationKey = "_mutatorMath.temp.fontLocation" def __init__(self, documentPath, ufoVersion, roundGeometry=False, verbose=False, logPath=None, progressFunc=None ): self.path = documentPath self.ufoVersion = ufoVersion self.roundGeometry = roundGeometry self.documentFormatVersion = 0 self.sources = {} self.instances = {} self.axes = {} # dict with axes info self.axesOrder = [] # order in which the axes were defined self.warpDict = None # let's stop using this one self.libSource = None self.groupsSource = None self.infoSource = None self.featuresSource = None self.progressFunc=progressFunc self.muted = dict(kerning=[], info=[], glyphs={}) self.verbose = verbose self.logger = None if self.verbose: self.logger = logging.getLogger("mutatorMath") self.results = {} # dict with instancename / filepaths for post processing. tree = ET.parse(self.path) self.root = tree.getroot() self.readVersion() assert self.documentFormatVersion >= 3 self.readAxes() self.readWarp() self.readSources() def reportProgress(self, state, action, text=None, tick=None): """ If we want to keep other code updated about our progress. state: 'prep' reading sources 'generate' making instances 'done' wrapping up 'error' reporting a problem action: 'start' begin generating 'stop' end generating 'source' which ufo we're reading text: ufoname (for instance) tick: a float between 0 and 1 indicating progress. """ if self.progressFunc is not None: self.progressFunc(state=state, action=action, text=text, tick=tick) def getSourcePaths(self, makeGlyphs=True, makeKerning=True, makeInfo=True): """ Return a list of paths referenced in the document.""" paths = [] for name in self.sources.keys(): paths.append(self.sources[name][0].path) return paths def process( self, makeGlyphs=True, makeKerning=True, makeInfo=True, bendLocations=False, ): """ Process the input file and generate the instances. """ if self.logger: self.logger.info("Reading %s", self.path) self.readInstances( makeGlyphs=makeGlyphs, makeKerning=makeKerning, makeInfo=makeInfo, bendLocations=bendLocations, ) self.reportProgress("done", 'stop') def readVersion(self): """ Read the document version. :: """ ds = self.root.findall("[@format]")[0] raw_format = ds.attrib['format'] try: self.documentFormatVersion = int(raw_format) except ValueError: # as of fontTools >= 3.27 'format' is formatted as a float "4.0" self.documentFormatVersion = float(raw_format) def readWarp(self): """ Read the warp element :: """ warpDict = {} for warpAxisElement in self.root.findall(".warp/axis"): axisName = warpAxisElement.attrib.get("name") warpDict[axisName] = [] for warpPoint in warpAxisElement.findall(".map"): inputValue = float(warpPoint.attrib.get("input")) outputValue = float(warpPoint.attrib.get("output")) warpDict[axisName].append((inputValue, outputValue)) self.warpDict = warpDict def readAxes(self): """ Read the axes element. """ for axisElement in self.root.findall(".axes/axis"): axis = {} axis['name'] = name = axisElement.attrib.get("name") axis['tag'] = axisElement.attrib.get("tag") axis['minimum'] = float(axisElement.attrib.get("minimum")) axis['maximum'] = float(axisElement.attrib.get("maximum")) axis['default'] = float(axisElement.attrib.get("default")) # we're not using the map for anything. axis['map'] = [] for warpPoint in axisElement.findall(".map"): inputValue = float(warpPoint.attrib.get("input")) outputValue = float(warpPoint.attrib.get("output")) axis['map'].append((inputValue, outputValue)) # there are labelnames in the element # but we don't need them for building the fonts. self.axes[name] = axis self.axesOrder.append(axis['name']) def readSources(self): """ Read the source elements. :: """ for sourceCount, sourceElement in enumerate(self.root.findall(".sources/source")): # shall we just read the UFO here? filename = sourceElement.attrib.get('filename') # filename is a path relaive to the documentpath. resolve first. sourcePath = os.path.abspath(os.path.join(os.path.dirname(self.path), filename)) sourceName = sourceElement.attrib.get('name') if sourceName is None: # if the source element has no name attribute # (some authoring tools do not need them) # then we should make a temporary one. We still need it for reference. sourceName = "temp_master.%d"%(sourceCount) self.reportProgress("prep", 'load', sourcePath) if not os.path.exists(sourcePath): raise MutatorError("Source not found at %s"%sourcePath) sourceObject = self._instantiateFont(sourcePath) # read the locations sourceLocationObject = None sourceLocationObject = self.locationFromElement(sourceElement) if sourceLocationObject is None: raise MutatorError("No location defined for source %s"%sourceName) # read lib flag for libElement in sourceElement.findall('.lib'): if libElement.attrib.get('copy') == '1': self.libSource = sourceName # read the groups flag for groupsElement in sourceElement.findall('.groups'): if groupsElement.attrib.get('copy') == '1': self.groupsSource = sourceName # read the info flag for infoElement in sourceElement.findall(".info"): if infoElement.attrib.get('copy') == '1': self.infoSource = sourceName if infoElement.attrib.get('mute') == '1': self.muted['info'].append(sourceName) # read the features flag for featuresElement in sourceElement.findall(".features"): if featuresElement.attrib.get('copy') == '1': if self.featuresSource is not None: self.featuresSource = None else: self.featuresSource = sourceName mutedGlyphs = [] for glyphElement in sourceElement.findall(".glyph"): glyphName = glyphElement.attrib.get('name') if glyphName is None: continue if glyphElement.attrib.get('mute') == '1': if not sourceName in self.muted['glyphs']: self.muted['glyphs'][sourceName] = [] self.muted['glyphs'][sourceName].append(glyphName) for kerningElement in sourceElement.findall(".kerning"): if kerningElement.attrib.get('mute') == '1': self.muted['kerning'].append(sourceName) # store self.sources[sourceName] = sourceObject, sourceLocationObject self.reportProgress("prep", 'done') def locationFromElement(self, element): """ Find the MutatorMath location of this element, either by name or from a child element. """ elementLocation = None for locationElement in element.findall('.location'): elementLocation = self.readLocationElement(locationElement) break return elementLocation def readLocationElement(self, locationElement): """ Format 0 location reader """ loc = Location() for dimensionElement in locationElement.findall(".dimension"): dimName = dimensionElement.attrib.get("name") xValue = yValue = None try: xValue = dimensionElement.attrib.get('xvalue') xValue = float(xValue) except ValueError: if self.logger: self.logger.info("KeyError in readLocation xValue %3.3f", xValue) try: yValue = dimensionElement.attrib.get('yvalue') if yValue is not None: yValue = float(yValue) except ValueError: pass if yValue is not None: loc[dimName] = (xValue, yValue) else: loc[dimName] = xValue return loc def readInstance( self, key, makeGlyphs=True, makeKerning=True, makeInfo=True, bendLocations=False, ): """ Read a single instance element. key: an (attribute, value) tuple used to find the requested instance. :: """ attrib, value = key for instanceElement in self.root.findall('.instances/instance'): if instanceElement.attrib.get(attrib) == value: self._readSingleInstanceElement( instanceElement, makeGlyphs=makeGlyphs, makeKerning=makeKerning, makeInfo=makeInfo, bendLocations=bendLocations, ) return raise MutatorError("No instance found with key: (%s, %s)." % key) def readInstances( self, makeGlyphs=True, makeKerning=True, makeInfo=True, bendLocations=False, ): """ Read all instance elements. :: """ for instanceElement in self.root.findall('.instances/instance'): self._readSingleInstanceElement( instanceElement, makeGlyphs=makeGlyphs, makeKerning=makeKerning, makeInfo=makeInfo, bendLocations=bendLocations, ) def _readSingleInstanceElement( self, instanceElement, makeGlyphs=True, makeKerning=True, makeInfo=True, bendLocations=False, ): """ Read a single instance element. If we have glyph specifications, only make those. Otherwise make all available glyphs. """ # get the data from the instanceElement itself filename = instanceElement.attrib.get('filename') instancePath = os.path.join(os.path.dirname(self.path), filename) self.reportProgress("generate", 'start', instancePath) if self.verbose and self.logger: self.logger.info("\tGenerating instance %s", os.path.basename(instancePath)) filenameTokenForResults = os.path.basename(filename) instanceObject = self._instanceWriterClass( instancePath, ufoVersion=self.ufoVersion, roundGeometry=self.roundGeometry, axes = self.axes, verbose=self.verbose, logger=self.logger, bendLocations=bendLocations, ) self.results[filenameTokenForResults] = instancePath # set the masters instanceObject.setSources(self.sources) self.unicodeMap = instanceObject.makeUnicodeMapFromSources() instanceObject.setMuted(self.muted) familyname = instanceElement.attrib.get('familyname') if familyname is not None: instanceObject.setFamilyName(familyname) stylename = instanceElement.attrib.get('stylename') if stylename is not None: instanceObject.setStyleName(stylename) postScriptFontName = instanceElement.attrib.get('postscriptfontname') if postScriptFontName is not None: instanceObject.setPostScriptFontName(postScriptFontName) styleMapFamilyName = instanceElement.attrib.get('stylemapfamilyname') if styleMapFamilyName is not None: instanceObject.setStyleMapFamilyName(styleMapFamilyName) styleMapStyleName = instanceElement.attrib.get('stylemapstylename') if styleMapStyleName is not None: instanceObject.setStyleMapStyleName(styleMapStyleName) # location instanceLocation = self.locationFromElement(instanceElement) if instanceLocation is not None: instanceObject.setLocation(instanceLocation) if makeGlyphs: # step 1: generate all glyphs we have mutators for. names = instanceObject.getAvailableGlyphnames() for n in names: unicodes = self.unicodeMap.get(n, None) try: instanceObject.addGlyph(n, unicodes) except AssertionError: if self.verbose and self.logger: self.logger.info("Problem making glyph %s, skipping.", n) # step 2: generate all the glyphs that have special definitions. for glyphElement in instanceElement.findall('.glyphs/glyph'): self.readGlyphElement(glyphElement, instanceObject) # read the kerning if makeKerning: for kerningElement in instanceElement.findall('.kerning'): self.readKerningElement(kerningElement, instanceObject) break # read the fontinfo if makeInfo: for infoElement in instanceElement.findall('.info'): self.readInfoElement(infoElement, instanceObject) # copy the features if self.featuresSource is not None: instanceObject.copyFeatures(self.featuresSource) # copy the groups if self.groupsSource is not None: if self.groupsSource in self.sources: groupSourceObject, loc = self.sources[self.groupsSource] # copy the groups from the designated source to the new instance # note: setGroups will filter the group members # only glyphs present in the font will be added to the group. # Depending on the ufoversion we might or might not expect the kerningGroupConversionRenameMaps attribute. if hasattr(groupSourceObject, "kerningGroupConversionRenameMaps"): renameMap = groupSourceObject.kerningGroupConversionRenameMaps else: renameMap = {} instanceObject.setGroups(groupSourceObject.groups, kerningGroupConversionRenameMaps=renameMap) # lib items if self.libSource is not None: if self.libSource in self.sources: libSourceObject, loc = self.sources[self.libSource] instanceObject.setLib(libSourceObject.lib) # save the instance. Done. success, report = instanceObject.save() if not success and self.logger: # report problems other than validation errors and failed glyphs self.logger.info("%s:\nErrors generating: %s", filename, report) # report failed glyphs failed = instanceObject.getFailed() if failed: failed.sort() msg = "%s:\nErrors calculating %s glyphs: \n%s"%(filename, len(failed),"\t"+"\n\t".join(failed)) self.reportProgress('error', 'glyphs', msg) if self.verbose and self.logger: self.logger.info(msg) # report missing unicodes missing = instanceObject.getMissingUnicodes() if missing: missing.sort() msg = "%s:\nPossibly missing unicodes for %s glyphs: \n%s"%(filename, len(missing),"\t"+"\n\t".join(missing)) self.reportProgress('error', 'unicodes', msg) # store self.instances[postScriptFontName] = instanceObject self.reportProgress("generate", 'stop', filenameTokenForResults) def readInfoElement(self, infoElement, instanceObject): """ Read the info element. :: """ infoLocation = self.locationFromElement(infoElement) instanceObject.addInfo(infoLocation, copySourceName=self.infoSource) def readKerningElement(self, kerningElement, instanceObject): """ Read the kerning element. :: Make kerning at the location and with the masters specified at the instance level. """ kerningLocation = self.locationFromElement(kerningElement) instanceObject.addKerning(kerningLocation) def readGlyphElement(self, glyphElement, instanceObject): """ Read the glyph element. :: This is an instance from an anisotropic interpolation. """ # name glyphName = glyphElement.attrib.get('name') if glyphName is None: raise MutatorError("Glyph object without name attribute.") # mute mute = glyphElement.attrib.get("mute") if mute == "1": instanceObject.muteGlyph(glyphName) # we do not need to stick around after this return # unicode unicodes = glyphElement.attrib.get('unicode') if unicodes == None: unicodes = self.unicodeMap.get(glyphName, None) else: try: unicodes = [int(u, 16) for u in unicodes.split(" ")] except ValueError: raise MutatorError("unicode values %s are not integers" % unicodes) # note note = None for noteElement in glyphElement.findall('.note'): note = noteElement.text break # location instanceLocation = self.locationFromElement(glyphElement) # masters glyphSources = None for masterElement in glyphElement.findall('.masters/master'): fontSourceName = masterElement.attrib.get('source') fontSource, fontLocation = self.sources.get(fontSourceName) if fontSource is None: raise MutatorError("Unknown glyph master: %s"%masterElement) sourceLocation = self.locationFromElement(masterElement) if sourceLocation is None: # if we don't read a location, use the instance location sourceLocation = fontLocation masterGlyphName = masterElement.attrib.get('glyphname') if masterGlyphName is None: # if we don't read a glyphname, use the one we have masterGlyphName = glyphName d = dict( font=fontSource, location=sourceLocation, glyphName=masterGlyphName) if glyphSources is None: glyphSources = [] glyphSources.append(d) # calculate the glyph instanceObject.addGlyph(glyphName, unicodes, instanceLocation, glyphSources, note=note) def _instantiateFont(self, path): """ Return a instance of a font object with all the given subclasses """ return self._fontClass(path, libClass=self._libClass, kerningClass=self._kerningClass, groupsClass=self._groupsClass, infoClass=self._infoClass, featuresClass=self._featuresClass, glyphClass=self._glyphClass, glyphContourClass=self._glyphContourClass, glyphPointClass=self._glyphPointClass, glyphComponentClass=self._glyphComponentClass, glyphAnchorClass=self._glyphAnchorClass) MutatorMath-3.0.1/Lib/mutatorMath/ufo/instance.py000066400000000000000000000403001365052737200217510ustar00rootroot00000000000000# -*- coding: utf-8 -*- from mutatorMath.objects.error import MutatorError from mutatorMath.objects.mutator import Mutator, buildMutator from mutatorMath.objects.bender import Bender, noBend from fontTools.ufoLib import ( fontInfoAttributesVersion1, fontInfoAttributesVersion2, fontInfoAttributesVersion3, ) import warnings, logging import fontMath from fontMath.mathKerning import MathKerning from fontMath.mathInfo import MathInfo from fontMath.mathGlyph import MathGlyph import defcon import os class InstanceWriter(object): """ Simple object to build a UFO instance. Collect the data needed for an instance and generate it as fast as possible. Make a font object. Add data straight to the font. Calculate the data immediately, while reading the document. Don't edit the data. Don't represent the data. """ _fontClass = defcon.objects.font.Font _tempFontLibGlyphMuteKey = "_mutatorMath.temp.mutedGlyphNames" def __init__(self, path, ufoVersion=1, roundGeometry=False, axes=None, verbose=False, logger=None, bendLocations=False, ): self.path = path self.font = self._fontClass() self.ufoVersion = ufoVersion self.roundGeometry = roundGeometry self.bendLocations = bendLocations if axes is not None: self.axes = axes else: self.axes = {} self.sources = {} self.muted = dict(kerning=[], info=[], glyphs={}) # muted data in the masters self.mutedGlyphsNames = [] # muted glyphs in the instance self.familyName = None self.styleName = None self.postScriptFontName = None self.locationObject = None self.unicodeValues = {} self.verbose=verbose self.logger = None if self.verbose: self.logger = logging.getLogger("mutatorMath") self._failed = [] # list of glyphnames we could not generate self._missingUnicodes = [] # list of glyphnames with missing unicode values def setSources(self, sources): """ Set a list of sources.""" self.sources = sources def setMuted(self, muted): """ Set the mute states. """ self.muted.update(muted) def muteGlyph(self, glyphName): """ Mute the generating of this specific glyph. """ self.mutedGlyphsNames.append(glyphName) def setGroups(self, groups, kerningGroupConversionRenameMaps=None): """ Copy the groups into our font. """ skipping = [] for name, members in groups.items(): checked = [] for m in members: if m in self.font: checked.append(m) else: skipping.append(m) if checked: self.font.groups[name] = checked if skipping: if self.verbose and self.logger: self.logger.info("\tNote: some glyphnames were removed from groups: %s (unavailable in the font)", ", ".join(skipping)) if kerningGroupConversionRenameMaps: # in case the sources were UFO2, # and defcon upconverted them to UFO3 # and now we have to down convert them again, # we don't want the UFO3 public prefixes in the group names self.font.kerningGroupConversionRenameMaps = kerningGroupConversionRenameMaps def getFailed(self): """ Return the list of glyphnames that failed to generate.""" return self._failed def getMissingUnicodes(self): """ Return the list of glyphnames with missing unicode values. """ return self._missingUnicodes def setLib(self, lib): """ Copy the lib items into our font. """ for name, item in lib.items(): self.font.lib[name] = item def setPostScriptFontName(self, name): """ Set the postScriptFontName. """ self.font.info.postscriptFontName = name def setStyleMapFamilyName(self, name): """ Set the stylemap FamilyName. """ self.font.info.styleMapFamilyName = name def setStyleMapStyleName(self, name): """ Set the stylemap StyleName. """ self.font.info.styleMapStyleName = name def setStyleName(self, name): """ Set the styleName. """ self.font.info.styleName = name def setFamilyName(self, name): """ Set the familyName""" self.font.info.familyName = name def copyFeatures(self, featureSource): """ Copy the features from this source """ if featureSource in self.sources: src, loc = self.sources[featureSource] if isinstance(src.features.text, str): self.font.features.text = u""+src.features.text elif isinstance(src.features.text, unicode): self.font.features.text = src.features.text def makeUnicodeMapFromSources(self): """ Create a dict with glyphName -> unicode value pairs using the data in the sources. If all master glyphs have the same unicode value this value will be used in the map. If master glyphs have conflicting value, a warning will be printed, no value will be used. If only a single master has a value, that value will be used. """ values = {} for locationName, (source, loc) in self.sources.items(): # this will be expensive in large fonts for glyph in source: if glyph.unicodes is not None: if glyph.name not in values: values[glyph.name] = {} for u in glyph.unicodes: values[glyph.name][u] = 1 for name, u in values.items(): if len(u) == 0: # only report missing unicodes if the name has no extension if "." not in name: self._missingUnicodes.append(name) continue k = list(u.keys()) self.unicodeValues[name] = k return self.unicodeValues def getAvailableGlyphnames(self): """ Return a list of all glyphnames we have masters for.""" glyphNames = {} for locationName, (source, loc) in self.sources.items(): for glyph in source: glyphNames[glyph.name] = 1 names = sorted(glyphNames.keys()) return names def setLocation(self, locationObject): """ Set the location directly. """ self.locationObject = locationObject def addInfo(self, instanceLocation=None, sources=None, copySourceName=None): """ Add font info data. """ if instanceLocation is None: instanceLocation = self.locationObject infoObject = self.font.info infoMasters = [] if sources is None: sources = self.sources items = [] for sourceName, (source, sourceLocation) in sources.items(): if sourceName in self.muted['info']: # info in this master was muted, so do not add. continue items.append((sourceLocation, MathInfo(source.info))) try: bias, m = buildMutator(items, axes=self.axes) except: if self.logger: self.logger.exception("Error processing font info. %s", items) return instanceObject = m.makeInstance(instanceLocation, bend=self.bendLocations) if self.roundGeometry: try: instanceObject = instanceObject.round() except AttributeError: warnings.warn("MathInfo object missing round() method.") instanceObject.extractInfo(self.font.info) # handle the copyable info fields if copySourceName is not None: if not copySourceName in sources: if self.verbose and self.logger: self.logger.info("Copy info source %s not found, skipping.", copySourceName) return copySourceObject, loc = sources[copySourceName] self._copyFontInfo(self.font.info, copySourceObject.info) def _copyFontInfo(self, targetInfo, sourceInfo): """ Copy the non-calculating fields from the source info. """ infoAttributes = [ "versionMajor", "versionMinor", "copyright", "trademark", "note", "openTypeGaspRangeRecords", "openTypeHeadCreated", "openTypeHeadFlags", "openTypeNameDesigner", "openTypeNameDesignerURL", "openTypeNameManufacturer", "openTypeNameManufacturerURL", "openTypeNameLicense", "openTypeNameLicenseURL", "openTypeNameVersion", "openTypeNameUniqueID", "openTypeNameDescription", "#openTypeNamePreferredFamilyName", "#openTypeNamePreferredSubfamilyName", "#openTypeNameCompatibleFullName", "openTypeNameSampleText", "openTypeNameWWSFamilyName", "openTypeNameWWSSubfamilyName", "openTypeNameRecords", "openTypeOS2Selection", "openTypeOS2VendorID", "openTypeOS2Panose", "openTypeOS2FamilyClass", "openTypeOS2UnicodeRanges", "openTypeOS2CodePageRanges", "openTypeOS2Type", "postscriptIsFixedPitch", "postscriptForceBold", "postscriptDefaultCharacter", "postscriptWindowsCharacterSet" ] for infoAttribute in infoAttributes: copy = False if self.ufoVersion == 1 and infoAttribute in fontInfoAttributesVersion1: copy = True elif self.ufoVersion == 2 and infoAttribute in fontInfoAttributesVersion2: copy = True elif self.ufoVersion == 3 and infoAttribute in fontInfoAttributesVersion3: copy = True if copy: value = getattr(sourceInfo, infoAttribute) setattr(targetInfo, infoAttribute, value) def addKerning(self, instanceLocation=None, sources=None): """ Calculate the kerning data for this location and add it to this instance. * instanceLocation: Location object * source: dict of {sourcename: (source, sourceLocation)} """ items = [] kerningObject = self.font.kerning kerningMasters = [] if instanceLocation is None: instanceLocation = self.locationObject if sources is None: # kerning has no special requests, add the default sources sources = self.sources for sourceName, (source, sourceLocation) in sources.items(): if sourceName in self.muted['kerning']: # kerning in this master was muted, so do not add. if self.verbose and self.logger: self.logger.info("\tMuting kerning data for %s", instanceLocation) continue if len(source.kerning.keys())>0: items.append((sourceLocation, MathKerning(source.kerning, source.groups))) if items: m = None try: bias, m = buildMutator(items, axes=self.axes) except: if self.logger: self.logger.exception("\tError processing kerning data. %s", items) return instanceObject = m.makeInstance(instanceLocation, bend=self.bendLocations) if self.roundGeometry: instanceObject.round() instanceObject.extractKerning(self.font) def addGlyph(self, glyphName, unicodes=None, instanceLocation=None, sources=None, note=None): """ Calculate a new glyph and add it to this instance. * glyphName: The name of the glyph * unicodes: The unicode values for this glyph (optional) * instanceLocation: Location for this glyph * sources: List of sources for this glyph. * note: Note for this glyph. """ self.font.newGlyph(glyphName) glyphObject = self.font[glyphName] if note is not None: glyphObject.note = note # why does this not save? if unicodes is not None: glyphObject.unicodes = unicodes if instanceLocation is None: instanceLocation = self.locationObject glyphMasters = [] if sources is None: # glyph has no special requests, add the default sources for sourceName, (source, sourceLocation) in self.sources.items(): if glyphName in self.muted['glyphs'].get(sourceName, []): # this glyph in this master was muted, so do not add. continue d = dict( font=source, location=sourceLocation, glyphName=glyphName) glyphMasters.append(d) else: # use the glyph sources provided # if self.verbose and self.logger: # self.logger.info("\tGlyph %s has special masters %s", glyphName, sources) glyphMasters = sources # make the glyphs try: self._calculateGlyph(glyphObject, instanceLocation, glyphMasters) except: self._failed.append(glyphName) def _calculateGlyph(self, targetGlyphObject, instanceLocationObject, glyphMasters): """ Build a Mutator object for this glyph. * name: glyphName * location: Location object * glyphMasters: dict with font objects. """ sources = None items = [] for item in glyphMasters: locationObject = item['location'] fontObject = item['font'] glyphName = item['glyphName'] if not glyphName in fontObject: continue glyphObject = MathGlyph(fontObject[glyphName]) items.append((locationObject, glyphObject)) bias, m = buildMutator(items, axes=self.axes) instanceObject = m.makeInstance(instanceLocationObject, bend=self.bendLocations) if self.roundGeometry: try: instanceObject = instanceObject.round() except AttributeError: if self.verbose and self.logger: self.logger.info("MathGlyph object missing round() method.") try: instanceObject.extractGlyph(targetGlyphObject, onlyGeometry=True) except TypeError: # this causes ruled glyphs to end up in the wrong glyphname # but defcon2 objects don't support it pPen = targetGlyphObject.getPointPen() targetGlyphObject.clear() instanceObject.drawPoints(pPen) targetGlyphObject.width = instanceObject.width def save(self): """ Save the UFO.""" # handle glyphs that were muted for name in self.mutedGlyphsNames: if name not in self.font: continue if self.logger: self.logger.info("removing muted glyph %s", name) del self.font[name] # XXX housekeeping: # remove glyph from groups / kerning as well? # remove components referencing this glyph? # fontTools.ufoLib no longer calls os.makedirs for us if the # parent directories of the Font we are saving do not exist. # We want to keep backward compatibility with the previous # MutatorMath behavior, so we create the instance' parent # directories if they do not exist. We assume that the users # knows what they are doing... directory = os.path.dirname(os.path.normpath(self.path)) if directory and not os.path.exists(directory): os.makedirs(directory) try: self.font.save(os.path.abspath(self.path), self.ufoVersion) except defcon.DefconError as error: if self.logger: self.logger.exception("Error generating.") return False, error.report return True, None MutatorMath-3.0.1/MANIFEST.in000066400000000000000000000003721365052737200155320ustar00rootroot00000000000000include README.rst include LICENSE include requirements.txt include tox.ini recursive-include Lib/mutatorMath/test *.py recursive-include Lib/mutatorMath/test/ufo/data *.designspace *.glif *.plist *.fea recursive-include Docs *.jpg *.png *.md *.py MutatorMath-3.0.1/README.rst000066400000000000000000000226251365052737200154700ustar00rootroot00000000000000|Build Status| |Coverage Status| |PyPI Version| MutatorMath =========== .. figure:: https://raw.githubusercontent.com/LettError/MutatorMath/master/Docs/mutatorMath_colorField.jpg :alt: A MutatorMath Colorfield A MutatorMath Colorfield MutatorMath is a Python library for the calculation of piecewise linear interpolations in n-dimensions with any number of masters. It was developed for interpolating data related to fonts, but if can handle any arithmetic object. - The **objects/** subpackage contains the general calculation tools. - The **ufo/** subpackage contains tools to specifically process UFO data. - MutatorMath has no user interface, just the math. License ------- The MutatorMath package is published under the `BSD-3 license `__. Dependencies ------------ The basic Mutator and Location objects will run on any standard Python 2.7 or higher distribution, and is been tested on Python 3.5 or higher. The UFO processing tools in MutatorMath need some additional libraries. +-----------+-----------------------+-----------------------------------------------+ | Library | Author | URL | +===========+=======================+===============================================+ | FontTools | FontTools | https://github.com/fonttools/fonttools | +-----------+-----------------------+-----------------------------------------------+ | Defcon | TypeSupply.com | https://github.com/typesupply/defcon | +-----------+-----------------------+-----------------------------------------------+ | FontMath | TypeSupply.com | https://github.com/typesupply/fontMath | +-----------+-----------------------+-----------------------------------------------+ MutatorMath terminology ----------------------- - **designspace**: abstract Euclidian space with any number of dimensions. - **axis**: A dimension in the designspace. Dimension names can be descriptive, for instance ``x``, ``y``, ``width``, ``weight``, ``pop``, ``snap``. - **location**: Coordinates of a point in the designspace stored as a dictionary of named dimensions. For instance ``Location(x=0)`` and ``Location(x=10)`` are in the same dimension, whereas ``Location(snap=10)`` is not. There will be implementation limits on the number of dimensions, but theoretically there is no limit. - **split location** or **ambivalent location**: A location in which one or more dimensions have a 2-tuple rather than a single value. This is used to describe anisotropic locations. For instance ``Location(weight=(50, 60))`` means the horizontal value is 50, the vertical value is 60. Support for anisotropic coordinates is only 2 dimensional. - **origin**: A special location at which all dimension values are 0. Unnamed dimensions are assumed to be zero. ``Location()`` is at the *origin*. - **on-axis**: A location with a single non-zero axis value. ``Location(width=1000)`` is considered to be on-axis. - **off-axis**: A location with more than one non-zero axis value. ``Location(width=1000, weight=1000)`` is considered to be off-axis. - **bias**: A design space vector that translates all masters and instances. - arithmetic support - objects that offer arithmetic behavior - objects that respond to ``+``, ``-``, ``*`` and ``/`` - objects with ``__add__``, ``__sub__``, ``__mul__``, ``__rmul__``, ``__div__`` and ``__rdiv__`` methods - **master**: an arithmetic object that provides the input data. - **neutral**: a master inserted at the origin - **instance**: an object calculated at a specific location, same class as the master. `An explanation with colorful graphs of how the MutatorMath calculates the factors. `__ Building a Mutator ------------------ A convenient Mutator builder function ``buildMutator()`` accepts a list of ``(location, object)`` pairs. Internally it sorts the neutral / on / off axis masters and calculates the bias. - Master locations must not overlap. - For a more in-depth examples of building Mutator objects, read the doctests. UFO === A UFO stores data related to the design and production of fonts (specification at `UnifiedFontObject.org `__. The ufo/ subpackage contains some tools to make the building and processing of UFO mutators easier. Designspace document -------------------- The requirements for a UFO designspace will differ from project to project. The location of the masters and instances, special wishes for kerning and specific glyphs etc. This package provides tools to read and write a description of a designspace to XML. Such a file stores all information necessary: which source UFOs, where to insert them, which glyphs to generate, which instances etc. - A designspace document can be processed by a simple python build script. (See ``buildExample.py``). That makes it possible to build instances on remote computers, as part of cron jobs etc. - Examples and tests of the reader and writer can be found in the ``test/ufo/`` directory. - MutatorMath proposes the ``.designspace`` extension. Designspace XML structure ------------------------- A ``.designspace`` file contains all data needed for setting up interpolations between a number of master UFOs. A detailed description of the designspace format here: `designspace file format `__ Writing a designspace ~~~~~~~~~~~~~~~~~~~~~ **DesignSpaceDocumentWriter** object writes an XML representation of a designspace. - **addSource**\ (path, name, location, copyLib, copyGroups, copyInfo, muteKerning, muteInfo) - **path**: absolute path to the source UFO. Note: in the output the source path will relative to the documentPath. - **name**: reference name for this source - **location**: name of the location for this UFO - **copyLib**: copy the contents of this source to instances - **copyGroups**: copy the groups of this source to instances - **copyInfo**: copy the non-numerical fields from this source.info to instances. - **muteKerning**: mute the kerning data from this source - **muteInfo**: mute the font info data from this source - **familyName**: the family name for this source. Optional. Can be used for processing or building names of instances. - **styleName**: the style name for this source. Optional. Can be used for processing or building names of instances. - **startInstance**\ (name, familyName, styleName, fileName, postScriptFontName, styleMapFamilyName, styleMapStyleName) This starts a new current instance object. - **name**: the name of this instance - **familyName**: name for the font.info.familyName field. Required. - **styleName**: name fot the font.info.styleName field. Required. - **fileName**: absolute path for the instance UFO. Note: in the output the instance path will relative to the documentPath. - **postScriptFontName**: name for the font.info.postScriptFontName field. Optional. - **styleMapFamilyName**: name for the font.info.styleMapFamilyName field. Optional. - **styleMapStyleName**: name for the font.info.styleMapStyleName field. Optional. - **endInstance**\ () Finishes the current instance. - **writeGlyph**\ (name, unicodes, location, masters) Add a new glyph to the current instance. - **name**: the glyph name. Required. - **unicodes**: unicode values for this glyph if it needs to be different from the unicode values associated with this glyph name in the masters. - **location**: a design space location for this glyph if it needs to be different from the instance location. - **masters**: a list of masters and locations for this glyph if they need to be different from the masters specified for this instance. - **writeInfo()**: Indicate the info data should be generated for the current instance. - **writeKerning()**: Indicate the kerning data should be generated for the current instance. Reading a designspace ~~~~~~~~~~~~~~~~~~~~~ **DesignSpaceDocumentReader** reads a DesignSpaceDocument. First it will look for all UFO masters and then it will build all instances. - **DesignSpaceDocumentReader**\ (documentPath, ufoVersion, roundGeometry) - **documentPath**: path of the designspace document to read. - **ufoVersion**: target UFO version. Should be 2 or 3. - **roundGeometry**: apply rounding to all geometry DesignSpaceDocumentReader assumes all paths for sources and instances are relative to the documentPath. Legal ----- - **Superpolator** is a registered trademark of `LettError Type & Typography `__, More on `Superpolator.com `__ Thanks ------ - MutatorMath was made possible with kind support from the Adobe Type Team. - Thanks to `TypeSupply `__ for writing FontMath. - Thanks to `TypeMyType `__ for writing RoboFont. .. |Build Status| image:: https://travis-ci.org/LettError/MutatorMath.svg?branch=master :target: https://travis-ci.org/LettError/MutatorMath .. |Coverage Status| image:: https://coveralls.io/repos/github/LettError/MutatorMath/badge.svg?branch=master :target: https://coveralls.io/github/LettError/MutatorMath?branch=master .. |PyPI Version| image:: https://img.shields.io/pypi/v/MutatorMath.svg :target: https://pypi.org/project/MutatorMath/ MutatorMath-3.0.1/requirements.txt000066400000000000000000000000361365052737200172550ustar00rootroot00000000000000defcon==0.6.0 fontMath==0.4.8 MutatorMath-3.0.1/setup.cfg000066400000000000000000000012341365052737200156130ustar00rootroot00000000000000[bumpversion] current_version = 3.0.1 commit = True tag = False tag_name = {new_version} parse = (?P\d+)\.(?P\d+)\.(?P\d+)(\.(?P[a-z]+)(?P\d+))? serialize = {major}.{minor}.{patch}.{release}{dev} {major}.{minor}.{patch} [bumpversion:part:release] optional_value = final values = dev final [bumpversion:part:dev] [bumpversion:file:Lib/mutatorMath/__init__.py] search = __version__ = "{current_version}" replace = __version__ = "{new_version}" [bumpversion:file:setup.py] search = version="{current_version}" replace = version="{new_version}" [wheel] universal = 1 [sdist] formats = zip [metadata] license_file = LICENSE MutatorMath-3.0.1/setup.py000077500000000000000000000170301365052737200155100ustar00rootroot00000000000000#!/usr/bin/env python import sys from setuptools import setup, Command, find_packages from distutils import log import contextlib @contextlib.contextmanager def capture_logger(name): """ Context manager to capture a logger output with a StringIO stream. """ import logging logger = logging.getLogger(name) try: import StringIO stream = StringIO.StringIO() except ImportError: from io import StringIO stream = StringIO() handler = logging.StreamHandler(stream) logger.addHandler(handler) try: yield stream finally: logger.removeHandler(handler) class bump_version(Command): description = "increment the package version and commit the changes" user_options = [ ("major", None, "bump the first digit, for incompatible API changes"), ("minor", None, "bump the second digit, for new backward-compatible features"), ("patch", None, "bump the third digit, for bug fixes (default)"), ] def initialize_options(self): self.minor = False self.major = False self.patch = False def finalize_options(self): part = None for attr in ("major", "minor", "patch"): if getattr(self, attr, False): if part is None: part = attr else: from distutils.errors import DistutilsOptionError raise DistutilsOptionError( "version part options are mutually exclusive") self.part = part or "patch" def bumpversion(self, part, commit=True, tag=False, message=None, allow_dirty=False): """ Run bumpversion.main() with the specified arguments, and return the new computed version string. """ import bumpversion args = ( (['--verbose'] if self.verbose > 1 else []) + (['--allow-dirty'] if allow_dirty else []) + (['--commit'] if commit else ['--no-commit']) + (['--tag'] if tag else ['--no-tag']) + (['--message', message] if message is not None else []) + ['--list', part] ) log.debug( "$ bumpversion %s" % " ".join(a.replace(" ", "\\ ") for a in args)) with capture_logger("bumpversion.list") as out: bumpversion.main(args) last_line = out.getvalue().splitlines()[-1] new_version = last_line.replace("new_version=", "") return new_version def run(self): log.info("bumping '%s' version" % self.part) self.bumpversion(self.part) class release(bump_version): """Drop the developmental release '.devN' suffix from the package version, open the default text $EDITOR to write release notes, commit the changes and generate a git tag. Release notes can also be set with the -m/--message option, or by reading from standard input. If --major, --minor or --patch options are passed, the respective 'SemVer' digit is also incremented before tagging the release. """ description = "tag a new release" user_options = bump_version.user_options + [ ("message=", 'm', "message containing the release notes"), ] def initialize_options(self): bump_version.initialize_options(self) self.message = None def finalize_options(self): bump_version.finalize_options(self) self.bump_first = any( getattr(self, a, False) for a in ("major", "minor", "patch")) if not self.bump_first: import re current_version = self.distribution.metadata.get_version() if not re.search(r"\.dev[0-9]+", current_version): from distutils.errors import DistutilsSetupError raise DistutilsSetupError( "current version (%s) has no '.devN' suffix.\n " "Run 'setup.py bump_version', or use any of " "--major, --minor, --patch options" % current_version) message = self.message if message is None: if sys.stdin.isatty(): # stdin is interactive, use editor to write release notes message = self.edit_release_notes() else: # read release notes from stdin pipe message = sys.stdin.read() if not message.strip(): from distutils.errors import DistutilsSetupError raise DistutilsSetupError("release notes message is empty") self.message = "Release {new_version}\n\n%s" % (message) @staticmethod def edit_release_notes(): """Use the default text $EDITOR to write release notes. If $EDITOR is not set, use 'nano'.""" from tempfile import mkstemp import os import shlex import subprocess text_editor = shlex.split(os.environ.get('EDITOR', 'nano')) fd, tmp = mkstemp(prefix='bumpversion-') try: os.close(fd) with open(tmp, 'w') as f: f.write("\n\n# Write release notes.\n" "# Lines starting with '#' will be ignored.") subprocess.check_call(text_editor + [tmp]) with open(tmp, 'r') as f: changes = "".join( l for l in f.readlines() if not l.startswith('#')) finally: os.remove(tmp) return changes def run(self): if self.bump_first: # bump the specified version part but don't commit immediately log.info("bumping '%s' version" % self.part) self.bumpversion(self.part, commit=False) dirty=True else: dirty=False log.info("stripping developmental release suffix") # drop '.dev0' suffix, commit with given message and create git tag self.bumpversion( "release", tag=True, message=self.message, allow_dirty=dirty) with open('README.rst', 'r') as f: long_description = f.read() setup( name="MutatorMath", version="3.0.1", description=("Python for piecewise linear interpolation in multiple " "dimensions with multiple, arbitrarily placed, masters."), long_description=long_description, author="Erik van Blokland", author_email="erik@letterror.com", url="https://github.com/LettError/MutatorMath", license="BSD 3 Clause", packages=find_packages("Lib", exclude=['*.test', '*.test.*']), package_dir={"": "Lib"}, setup_requires=[ 'bumpversion', ] if {'release', 'bump_version'}.intersection(sys.argv) else [], install_requires=[ "fonttools>=3.32.0", "defcon>=0.3.5", "fontMath>=0.4.8", ], cmdclass={ "release": release, "bump_version": bump_version, }, classifiers=[ "Development Status :: 5 - Production/Stable", "Environment :: Console", "Environment :: Other Environment", "Intended Audience :: Developers", "Intended Audience :: End Users/Desktop", "License :: OSI Approved :: BSD License", "Natural Language :: English", "Operating System :: OS Independent", "Programming Language :: Python", "Programming Language :: Python :: 2", "Programming Language :: Python :: 3", "Topic :: Multimedia :: Graphics", "Topic :: Multimedia :: Graphics :: Graphics Conversion", "Topic :: Multimedia :: Graphics :: Editors :: Vector-Based", "Topic :: Software Development :: Libraries :: Python Modules", ], test_suite="mutatorMath.test.run", ) MutatorMath-3.0.1/tox.ini000066400000000000000000000017111365052737200153050ustar00rootroot00000000000000[tox] envlist = py{27,36,37}-cov, htmlcov skip_missing_interpreters = true [testenv] # If the tox env name contains '-cov', we install from the sdist and run the # test suite against the installed package: # $ tox -e py27-cov deps = -rrequirements.txt cov: coverage # If the tox env name contains "-cov", we run the test suite through the # "coverage" tool to collect test coverage data. # The {posargs} are positional arguments passed to the 'tox' script after the # '--' separator. E.g. you can use them to run a specific test module in tox: # $ tox -- Lib/mutatorMath/test/ufo/kerningTest.py # With no {posargs}, tox will run the whole test suite in verbose mode. commands = cov: coverage run --parallel-mode {posargs:Lib/mutatorMath/test/run.py -v} !cov: python {posargs:Lib/mutatorMath/test/run.py -v} [testenv:htmlcov] basepython = {env:TOXPYTHON:python} deps = coverage skip_install = true commands = coverage combine coverage html