1 # -*- encoding: utf-8 -*-
4 # Copyright (C) 2002-2004 Jörg Lehmann <joergl@users.sourceforge.net>
5 # Copyright (C) 2003-2004 Michael Schindler <m-schindler@users.sourceforge.net>
6 # Copyright (C) 2002-2004 André Wobst <wobsta@users.sourceforge.net>
8 # This file is part of PyX (http://pyx.sourceforge.net/).
10 # PyX is free software; you can redistribute it and/or modify
11 # it under the terms of the GNU General Public License as published by
12 # the Free Software Foundation; either version 2 of the License, or
13 # (at your option) any later version.
15 # PyX is distributed in the hope that it will be useful,
16 # but WITHOUT ANY WARRANTY; without even the implied warranty of
17 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 # GNU General Public License for more details.
20 # You should have received a copy of the GNU General Public License
21 # along with PyX; if not, write to the Free Software
22 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
26 from pyx
.graph
.axis
import tick
31 def labels(self
, ticks
):
32 """fill the label attribute of ticks
33 - ticks is a list of instances of tick
34 - for each element of ticks the value of the attribute label is set to
35 a string appropriate to the attributes num and denom of that tick
37 - label attributes of the tick instances are just kept, whenever they
39 - the method might modify the labelattrs attribute of the ticks; be sure
40 to not modify it in-place!"""
44 "a texter creating decimal labels (e.g. '1.234' or even '0.\overline{3}')"
46 __implements__
= _Itexter
48 def __init__(self
, prefix
="", infix
="", suffix
="", equalprecision
=0,
49 decimalsep
=".", thousandsep
="", thousandthpartsep
="",
50 plus
="", minus
="-", period
=r
"\overline{%s}",
51 labelattrs
=[text
.mathmode
]):
52 r
"""initializes the instance
53 - prefix, infix, and suffix (strings) are added at the begin,
54 immediately after the minus, and at the end of the label,
56 - decimalsep, thousandsep, and thousandthpartsep (strings)
57 are used as separators
58 - plus or minus (string) is inserted for non-negative or negative numbers
59 - period (string) is taken as a format string generating a period;
60 it has to contain exactly one string insert operators "%s" for the
61 period; usually it should be r"\overline{%s}"
62 - labelattrs is a list of attributes to be added to the label attributes
63 given in the painter"""
67 self
.equalprecision
= equalprecision
68 self
.decimalsep
= decimalsep
69 self
.thousandsep
= thousandsep
70 self
.thousandthpartsep
= thousandthpartsep
74 self
.labelattrs
= labelattrs
76 def labels(self
, ticks
):
80 if tick
.label
is None and tick
.labellevel
is not None:
81 labeledticks
.append(tick
)
82 m
, n
= tick
.num
, tick
.denom
85 quotient
, remainder
= divmod(m
, n
)
86 quotient
= str(quotient
)
87 if len(self
.thousandsep
):
91 tick
.label
+= quotient
[i
]
92 if not ((l
-i
-1) % 3) and l
> i
+1:
93 tick
.label
+= self
.thousandsep
97 tick
.label
+= self
.decimalsep
99 tick
.temp_decprecision
= 0
101 tick
.temp_decprecision
+= 1
102 if remainder
in oldremainders
:
103 tick
.temp_decprecision
= None
104 periodstart
= len(tick
.label
) - (len(oldremainders
) - oldremainders
.index(remainder
))
105 tick
.label
= tick
.label
[:periodstart
] + self
.period
% tick
.label
[periodstart
:]
107 oldremainders
+= [remainder
]
109 quotient
, remainder
= divmod(remainder
, n
)
110 if not ((tick
.temp_decprecision
- 1) % 3) and tick
.temp_decprecision
> 1:
111 tick
.label
+= self
.thousandthpartsep
112 tick
.label
+= str(quotient
)
114 if maxdecprecision
< tick
.temp_decprecision
:
115 maxdecprecision
= tick
.temp_decprecision
116 if self
.equalprecision
:
117 for tick
in labeledticks
:
118 if tick
.temp_decprecision
is not None:
119 if tick
.temp_decprecision
== 0 and maxdecprecision
> 0:
120 tick
.label
+= self
.decimalsep
121 for i
in range(tick
.temp_decprecision
, maxdecprecision
):
122 if not ((i
- 1) % 3) and i
> 1:
123 tick
.label
+= self
.thousandthpartsep
125 for tick
in labeledticks
:
126 if tick
.num
* tick
.denom
< 0:
127 plusminus
= self
.minus
129 plusminus
= self
.plus
130 tick
.label
= "%s%s%s%s%s" % (self
.prefix
, plusminus
, self
.infix
, tick
.label
, self
.suffix
)
131 tick
.labelattrs
= tick
.labelattrs
+ self
.labelattrs
133 # del tick.temp_decprecision # we've inserted this temporary variable ... and do not care any longer about it
137 "a texter creating labels with exponentials (e.g. '2\cdot10^5')"
139 __implements__
= _Itexter
141 def __init__(self
, plus
="", minus
="-",
142 mantissaexp
=r
"{{%s}\cdot10^{%s}}",
145 nomantissaexp
=r
"{10^{%s}}",
146 minusnomantissaexp
=r
"{-10^{%s}}",
147 mantissamin
=tick
.rational((1, 1)), mantissamax
=tick
.rational((10, 1)),
148 skipmantissa1
=0, skipallmantissa1
=1,
149 mantissatexter
=decimal()):
150 r
"""initializes the instance
151 - plus or minus (string) is inserted for non-negative or negative exponents
152 - mantissaexp (string) is taken as a format string generating the exponent;
153 it has to contain exactly two string insert operators "%s" --
154 the first for the mantissa and the second for the exponent;
155 examples are r"{{%s}\cdot10^{%s}}" and r"{{%s}{\rm e}{%s}}"
156 - skipexp0 (string) is taken as a format string used for exponent 0;
157 exactly one string insert operators "%s" for the mantissa;
158 None turns off the special handling of exponent 0;
159 an example is r"{%s}"
160 - skipexp1 (string) is taken as a format string used for exponent 1;
161 exactly one string insert operators "%s" for the mantissa;
162 None turns off the special handling of exponent 1;
163 an example is r"{{%s}\cdot10}"
164 - nomantissaexp (string) is taken as a format string generating the exponent
165 when the mantissa is one and should be skipped; it has to contain
166 exactly one string insert operators "%s" for the exponent;
167 an examples is r"{10^{%s}}"
168 - minusnomantissaexp (string) is taken as a format string generating the exponent
169 when the mantissa is minus one and should be skipped; it has to contain
170 exactly one string insert operators "%s" for the exponent;
171 None turns off the special handling of mantissa -1;
172 an examples is r"{-10^{%s}}"
173 - mantissamin and mantissamax are the minimum and maximum of the mantissa;
174 they are rational instances greater than zero and mantissamin < mantissamax;
175 the sign of the tick is ignored here
176 - skipmantissa1 (boolean) turns on skipping of any mantissa equals one
177 (and minus when minusnomantissaexp is set)
178 - skipallmantissa1 (boolean) as above, but all mantissas must be 1 (or -1)
179 - mantissatexter is the texter for the mantissa
180 - the skipping of a mantissa is stronger than the skipping of an exponent"""
183 self
.mantissaexp
= mantissaexp
184 self
.skipexp0
= skipexp0
185 self
.skipexp1
= skipexp1
186 self
.nomantissaexp
= nomantissaexp
187 self
.minusnomantissaexp
= minusnomantissaexp
188 self
.mantissamin
= mantissamin
189 self
.mantissamax
= mantissamax
190 self
.mantissamindivmax
= self
.mantissamin
/ self
.mantissamax
191 self
.mantissamaxdivmin
= self
.mantissamax
/ self
.mantissamin
192 self
.skipmantissa1
= skipmantissa1
193 self
.skipallmantissa1
= skipallmantissa1
194 self
.mantissatexter
= mantissatexter
196 def labels(self
, ticks
):
199 if tick
.label
is None and tick
.labellevel
is not None:
200 tick
.temp_orgnum
, tick
.temp_orgdenom
= tick
.num
, tick
.denom
201 labeledticks
.append(tick
)
204 while abs(tick
) >= self
.mantissamax
:
206 x
= tick
* self
.mantissamindivmax
207 tick
.num
, tick
.denom
= x
.num
, x
.denom
208 while abs(tick
) < self
.mantissamin
:
210 x
= tick
* self
.mantissamaxdivmin
211 tick
.num
, tick
.denom
= x
.num
, x
.denom
212 if tick
.temp_exp
< 0:
213 tick
.temp_exp
= "%s%i" % (self
.minus
, -tick
.temp_exp
)
215 tick
.temp_exp
= "%s%i" % (self
.plus
, tick
.temp_exp
)
216 self
.mantissatexter
.labels(labeledticks
)
217 if self
.minusnomantissaexp
is not None:
218 allmantissa1
= len(labeledticks
) == len([tick
for tick
in labeledticks
if abs(tick
.num
) == abs(tick
.denom
)])
220 allmantissa1
= len(labeledticks
) == len([tick
for tick
in labeledticks
if tick
.num
== tick
.denom
])
221 for tick
in labeledticks
:
222 if (self
.skipallmantissa1
and allmantissa1
or
223 (self
.skipmantissa1
and (tick
.num
== tick
.denom
or
224 (tick
.num
== -tick
.denom
and self
.minusnomantissaexp
is not None)))):
225 if tick
.num
== tick
.denom
:
226 tick
.label
= self
.nomantissaexp
% tick
.temp_exp
228 tick
.label
= self
.minusnomantissaexp
% tick
.temp_exp
230 if tick
.temp_exp
== "0" and self
.skipexp0
is not None:
231 tick
.label
= self
.skipexp0
% tick
.label
232 elif tick
.temp_exp
== "1" and self
.skipexp1
is not None:
233 tick
.label
= self
.skipexp1
% tick
.label
235 tick
.label
= self
.mantissaexp
% (tick
.label
, tick
.temp_exp
)
236 tick
.num
, tick
.denom
= tick
.temp_orgnum
, tick
.temp_orgdenom
238 # del tick.temp_orgnum # we've inserted those temporary variables ... and do not care any longer about them
239 # del tick.temp_orgdenom
244 "a texter creating decimal or exponential labels"
246 __implements__
= _Itexter
248 def __init__(self
, smallestdecimal
=tick
.rational((1, 1000)),
249 biggestdecimal
=tick
.rational((9999, 1)),
252 exponential
=exponential()):
253 """initializes the instance
254 - smallestdecimal and biggestdecimal are the smallest and
255 biggest decimal values, where the decimal texter should be used;
256 they are rational instances; the sign of the tick is ignored here;
257 a tick at zero is considered for the decimal texter as well
258 - equaldecision (boolean) uses decimal texter or exponential texter
259 globaly (set) or for each tick separately (unset)
260 - decimal and exponential are texters to be used"""
261 self
.smallestdecimal
= smallestdecimal
262 self
.biggestdecimal
= biggestdecimal
263 self
.equaldecision
= equaldecision
264 self
.decimal
= decimal
265 self
.exponential
= exponential
267 def labels(self
, ticks
):
271 if tick
.label
is None and tick
.labellevel
is not None:
272 if not tick
.num
or (abs(tick
) >= self
.smallestdecimal
and abs(tick
) <= self
.biggestdecimal
):
273 decticks
.append(tick
)
275 expticks
.append(tick
)
276 if self
.equaldecision
:
278 self
.exponential
.labels(ticks
)
280 self
.decimal
.labels(ticks
)
282 for tick
in decticks
:
283 self
.decimal
.labels([tick
])
284 for tick
in expticks
:
285 self
.exponential
.labels([tick
])
289 "a texter creating rational labels (e.g. 'a/b' or even 'a \over b')"
290 # XXX: we use divmod here to be more expicit
292 __implements__
= _Itexter
294 def __init__(self
, prefix
="", infix
="", suffix
="",
295 numprefix
="", numinfix
="", numsuffix
="",
296 denomprefix
="", denominfix
="", denomsuffix
="",
297 plus
="", minus
="-", minuspos
=0, over
=r
"{{%s}\over{%s}}",
298 equaldenom
=0, skip1
=1, skipnum0
=1, skipnum1
=1, skipdenom1
=1,
299 labelattrs
=[text
.mathmode
]):
300 r
"""initializes the instance
301 - prefix, infix, and suffix (strings) are added at the begin,
302 immediately after the minus, and at the end of the label,
304 - prefixnum, infixnum, and suffixnum (strings) are added
305 to the labels numerator correspondingly
306 - prefixdenom, infixdenom, and suffixdenom (strings) are added
307 to the labels denominator correspondingly
308 - plus or minus (string) is inserted for non-negative or negative numbers
309 - minuspos is an integer, which determines the position, where the
310 plus or minus sign has to be placed; the following values are allowed:
311 1 - writes the plus or minus in front of the numerator
312 0 - writes the plus or minus in front of the hole fraction
313 -1 - writes the plus or minus in front of the denominator
314 - over (string) is taken as a format string generating the
315 fraction bar; it has to contain exactly two string insert
316 operators "%s" -- the first for the numerator and the second
317 for the denominator; by far the most common examples are
318 r"{{%s}\over{%s}}" and "{{%s}/{%s}}"
319 - usually the numerator and denominator are canceled; however,
320 when equaldenom is set, the least common multiple of all
322 - skip1 (boolean) just prints the prefix, the plus or minus,
323 the infix and the suffix, when the value is plus or minus one
324 and at least one of prefix, infix and the suffix is present
325 - skipnum0 (boolean) just prints a zero instead of
326 the hole fraction, when the numerator is zero;
327 no prefixes, infixes, and suffixes are taken into account
328 - skipnum1 (boolean) just prints the numprefix, the plus or minus,
329 the numinfix and the numsuffix, when the num value is plus or minus one
330 and at least one of numprefix, numinfix and the numsuffix is present
331 - skipdenom1 (boolean) just prints the numerator instead of
332 the hole fraction, when the denominator is one and none of the parameters
333 denomprefix, denominfix and denomsuffix are set and minuspos is not -1 or the
335 - labelattrs is a list of attributes for a texrunners text method;
336 None is considered as an empty list; labelattrs might be changed
337 in the painter as well"""
341 self
.numprefix
= numprefix
342 self
.numinfix
= numinfix
343 self
.numsuffix
= numsuffix
344 self
.denomprefix
= denomprefix
345 self
.denominfix
= denominfix
346 self
.denomsuffix
= denomsuffix
349 self
.minuspos
= minuspos
351 self
.equaldenom
= equaldenom
353 self
.skipnum0
= skipnum0
354 self
.skipnum1
= skipnum1
355 self
.skipdenom1
= skipdenom1
356 self
.labelattrs
= labelattrs
359 """returns the greates common divisor of all elements in n
360 - the elements of n must be non-negative integers
361 - return None if the number of elements is zero
362 - the greates common divisor is not affected when some
363 of the elements are zero, but it becomes zero when
364 all elements are zero"""
370 i
, (dummy
, j
) = j
, divmod(i
, j
)
375 res
= self
.gcd(res
, i
)
379 """returns the least common multiple of all elements in n
380 - the elements of n must be non-negative integers
381 - return None if the number of elements is zero
382 - the least common multiple is zero when some of the
387 res
= divmod(res
* i
, self
.gcd(res
, i
))[0]
390 def labels(self
, ticks
):
393 if tick
.label
is None and tick
.labellevel
is not None:
394 labeledticks
.append(tick
)
395 tick
.temp_rationalnum
= tick
.num
396 tick
.temp_rationaldenom
= tick
.denom
397 tick
.temp_rationalminus
= 1
398 if tick
.temp_rationalnum
< 0:
399 tick
.temp_rationalminus
= -tick
.temp_rationalminus
400 tick
.temp_rationalnum
= -tick
.temp_rationalnum
401 if tick
.temp_rationaldenom
< 0:
402 tick
.temp_rationalminus
= -tick
.temp_rationalminus
403 tick
.temp_rationaldenom
= -tick
.temp_rationaldenom
404 gcd
= self
.gcd(tick
.temp_rationalnum
, tick
.temp_rationaldenom
)
405 (tick
.temp_rationalnum
, dummy1
), (tick
.temp_rationaldenom
, dummy2
) = divmod(tick
.temp_rationalnum
, gcd
), divmod(tick
.temp_rationaldenom
, gcd
)
407 equaldenom
= self
.lcm(*[tick
.temp_rationaldenom
for tick
in ticks
if tick
.label
is None])
408 if equaldenom
is not None:
409 for tick
in labeledticks
:
410 factor
, dummy
= divmod(equaldenom
, tick
.temp_rationaldenom
)
411 tick
.temp_rationalnum
, tick
.temp_rationaldenom
= factor
* tick
.temp_rationalnum
, factor
* tick
.temp_rationaldenom
412 for tick
in labeledticks
:
413 rationalminus
= rationalnumminus
= rationaldenomminus
= ""
414 if tick
.temp_rationalminus
== -1:
415 plusminus
= self
.minus
417 plusminus
= self
.plus
418 if self
.minuspos
== 0:
419 rationalminus
= plusminus
420 elif self
.minuspos
== 1:
421 rationalnumminus
= plusminus
422 elif self
.minuspos
== -1:
423 rationaldenomminus
= plusminus
425 raise RuntimeError("invalid minuspos")
426 if self
.skipnum0
and tick
.temp_rationalnum
== 0:
428 elif (self
.skip1
and self
.skipdenom1
and tick
.temp_rationalnum
== 1 and tick
.temp_rationaldenom
== 1 and
429 (len(self
.prefix
) or len(self
.infix
) or len(self
.suffix
)) and
430 not len(rationalnumminus
) and not len(self
.numprefix
) and not len(self
.numinfix
) and not len(self
.numsuffix
) and
431 not len(rationaldenomminus
) and not len(self
.denomprefix
) and not len(self
.denominfix
) and not len(self
.denomsuffix
)):
432 tick
.label
= "%s%s%s%s" % (self
.prefix
, rationalminus
, self
.infix
, self
.suffix
)
434 if self
.skipnum1
and tick
.temp_rationalnum
== 1 and (len(self
.numprefix
) or len(self
.numinfix
) or len(self
.numsuffix
)):
435 tick
.temp_rationalnum
= "%s%s%s%s" % (self
.numprefix
, rationalnumminus
, self
.numinfix
, self
.numsuffix
)
437 tick
.temp_rationalnum
= "%s%s%s%i%s" % (self
.numprefix
, rationalnumminus
, self
.numinfix
, tick
.temp_rationalnum
, self
.numsuffix
)
438 if self
.skipdenom1
and tick
.temp_rationaldenom
== 1 and not len(rationaldenomminus
) and not len(self
.denomprefix
) and not len(self
.denominfix
) and not len(self
.denomsuffix
):
439 rational
= tick
.temp_rationalnum
441 tick
.temp_rationaldenom
= "%s%s%s%i%s" % (self
.denomprefix
, rationaldenomminus
, self
.denominfix
, tick
.temp_rationaldenom
, self
.denomsuffix
)
442 rational
= self
.over
% (tick
.temp_rationalnum
, tick
.temp_rationaldenom
)
443 tick
.label
= "%s%s%s%s%s" % (self
.prefix
, rationalminus
, self
.infix
, rational
, self
.suffix
)
444 tick
.labelattrs
= tick
.labelattrs
+ self
.labelattrs
446 # del tick.temp_rationalnum # we've inserted those temporary variables ... and do not care any longer about them
447 # del tick.temp_rationaldenom
448 # del tick.temp_rationalminus