some code reorganisation/renaming: separate graph directory, write(-Postscript) metho...
[PyX/mjg.git] / pyx / graph / parter.py
blob9720a671a71927221af97095271bbd61759d91df
1 #!/usr/bin/env python
2 # -*- coding: ISO-8859-1 -*-
5 # Copyright (C) 2002-2004 Jörg Lehmann <joergl@users.sourceforge.net>
6 # Copyright (C) 2003-2004 Michael Schindler <m-schindler@users.sourceforge.net>
7 # Copyright (C) 2002-2004 André Wobst <wobsta@users.sourceforge.net>
9 # This file is part of PyX (http://pyx.sourceforge.net/).
11 # PyX is free software; you can redistribute it and/or modify
12 # it under the terms of the GNU General Public License as published by
13 # the Free Software Foundation; either version 2 of the License, or
14 # (at your option) any later version.
16 # PyX is distributed in the hope that it will be useful,
17 # but WITHOUT ANY WARRANTY; without even the implied warranty of
18 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 # GNU General Public License for more details.
21 # You should have received a copy of the GNU General Public License
22 # along with PyX; if not, write to the Free Software
23 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
26 import math
27 from pyx import helper
30 # partitioner (parter)
31 # please note the nomenclature:
32 # - a part (partition) is a list of tick instances; thus ticks `==' part
33 # - a parter (partitioner) is a class creating ticks
36 class frac:
37 """fraction class for rational arithmetics
38 the axis partitioning uses rational arithmetics (with infinite accuracy)
39 basically it contains self.enum and self.denom"""
41 def stringfrac(self, s):
42 "converts a string 0.123 into a frac"
43 expparts = s.split("e")
44 if len(expparts) > 2:
45 raise ValueError("multiple 'e' found in '%s'" % s)
46 commaparts = expparts[0].split(".")
47 if len(commaparts) > 2:
48 raise ValueError("multiple '.' found in '%s'" % expparts[0])
49 if len(commaparts) == 1:
50 commaparts = [commaparts[0], ""]
51 result = frac((1, 10l), power=len(commaparts[1]))
52 neg = len(commaparts[0]) and commaparts[0][0] == "-"
53 if neg:
54 commaparts[0] = commaparts[0][1:]
55 elif len(commaparts[0]) and commaparts[0][0] == "+":
56 commaparts[0] = commaparts[0][1:]
57 if len(commaparts[0]):
58 if not commaparts[0].isdigit():
59 raise ValueError("unrecognized characters in '%s'" % s)
60 x = long(commaparts[0])
61 else:
62 x = 0
63 if len(commaparts[1]):
64 if not commaparts[1].isdigit():
65 raise ValueError("unrecognized characters in '%s'" % s)
66 y = long(commaparts[1])
67 else:
68 y = 0
69 result.enum = x*result.denom+y
70 if neg:
71 result.enum = -result.enum
72 if len(expparts) == 2:
73 neg = expparts[1][0] == "-"
74 if neg:
75 expparts[1] = expparts[1][1:]
76 elif expparts[1][0] == "+":
77 expparts[1] = expparts[1][1:]
78 if not expparts[1].isdigit():
79 raise ValueError("unrecognized characters in '%s'" % s)
80 if neg:
81 result *= frac((1, 10l), power=long(expparts[1]))
82 else:
83 result *= frac((10, 1l), power=long(expparts[1]))
84 return result
86 def floatfrac(self, x, floatprecision):
87 "converts a float into a frac with finite resolution"
88 if helper.isinteger(floatprecision) and floatprecision < 0:
89 raise RuntimeError("float resolution must be non-negative integer")
90 return self.stringfrac(("%%.%ig" % floatprecision) % x)
92 def __init__(self, x, power=None, floatprecision=10):
93 "for power!=None: frac=(enum/denom)**power"
94 if helper.isnumber(x):
95 value = self.floatfrac(x, floatprecision)
96 enum, denom = value.enum, value.denom
97 elif helper.isstring(x):
98 fraction = x.split("/")
99 if len(fraction) > 2:
100 raise ValueError("multiple '/' found in '%s'" % x)
101 value = self.stringfrac(fraction[0])
102 if len(fraction) == 2:
103 value2 = self.stringfrac(fraction[1])
104 value = value / value2
105 enum, denom = value.enum, value.denom
106 else:
107 try:
108 enum, denom = x
109 except (TypeError, AttributeError):
110 enum, denom = x.enum, x.denom
111 if not helper.isinteger(enum) or not helper.isinteger(denom): raise TypeError("integer type expected")
112 if not denom: raise ZeroDivisionError("zero denominator")
113 if power != None:
114 if not helper.isinteger(power): raise TypeError("integer type expected")
115 if power >= 0:
116 self.enum = long(enum) ** power
117 self.denom = long(denom) ** power
118 else:
119 self.enum = long(denom) ** (-power)
120 self.denom = long(enum) ** (-power)
121 else:
122 self.enum = enum
123 self.denom = denom
125 def __cmp__(self, other):
126 if other is None:
127 return 1
128 return cmp(self.enum * other.denom, other.enum * self.denom)
130 def __abs__(self):
131 return frac((abs(self.enum), abs(self.denom)))
133 def __mul__(self, other):
134 return frac((self.enum * other.enum, self.denom * other.denom))
136 def __div__(self, other):
137 return frac((self.enum * other.denom, self.denom * other.enum))
139 def __float__(self):
140 "caution: avoid final precision of floats"
141 return float(self.enum) / self.denom
143 def __str__(self):
144 return "%i/%i" % (self.enum, self.denom)
147 class tick(frac):
148 """tick class
149 a tick is a frac enhanced by
150 - self.ticklevel (0 = tick, 1 = subtick, etc.)
151 - self.labellevel (0 = label, 1 = sublabel, etc.)
152 - self.label (a string) and self.labelattrs (a list, defaults to [])
153 When ticklevel or labellevel is None, no tick or label is present at that value.
154 When label is None, it should be automatically created (and stored), once the
155 an axis painter needs it. Classes, which implement _Itexter do precisely that."""
157 def __init__(self, pos, ticklevel=0, labellevel=0, label=None, labelattrs=[], **kwargs):
158 """initializes the instance
159 - see class description for the parameter description
160 - **kwargs are passed to the frac constructor"""
161 frac.__init__(self, pos, **kwargs)
162 self.ticklevel = ticklevel
163 self.labellevel = labellevel
164 self.label = label
165 self.labelattrs = labelattrs
167 def merge(self, other):
168 """merges two ticks together:
169 - the lower ticklevel/labellevel wins
170 - the label is *never* taken over from other
171 - the ticks should be at the same position (otherwise it doesn't make sense)
172 -> this is NOT checked"""
173 if self.ticklevel is None or (other.ticklevel is not None and other.ticklevel < self.ticklevel):
174 self.ticklevel = other.ticklevel
175 if self.labellevel is None or (other.labellevel is not None and other.labellevel < self.labellevel):
176 self.labellevel = other.labellevel
179 def _mergeticklists(list1, list2):
180 """helper function to merge tick lists
181 - return a merged list of ticks out of list1 and list2
182 - CAUTION: original lists have to be ordered
183 (the returned list is also ordered)"""
184 # TODO: improve this using bisect?!
186 # do not destroy original lists
187 list1 = list1[:]
188 i = 0
189 j = 0
190 try:
191 while 1: # we keep on going until we reach an index error
192 while list2[j] < list1[i]: # insert tick
193 list1.insert(i, list2[j])
194 i += 1
195 j += 1
196 if list2[j] == list1[i]: # merge tick
197 list1[i].merge(list2[j])
198 j += 1
199 i += 1
200 except IndexError:
201 if j < len(list2):
202 list1 += list2[j:]
203 return list1
206 def _mergelabels(ticks, labels):
207 """helper function to merge labels into ticks
208 - when labels is not None, the label of all ticks with
209 labellevel different from None are set
210 - labels need to be a list of lists of strings,
211 where the first list contain the strings to be
212 used as labels for the ticks with labellevel 0,
213 the second list for labellevel 1, etc.
214 - when the maximum labellevel is 0, just a list of
215 strings might be provided as the labels argument
216 - IndexError is raised, when a list length doesn't match"""
217 if helper.issequenceofsequences(labels):
218 level = 0
219 for label in labels:
220 usetext = helper.ensuresequence(label)
221 i = 0
222 for tick in ticks:
223 if tick.labellevel == level:
224 tick.label = usetext[i]
225 i += 1
226 if i != len(usetext):
227 raise IndexError("wrong list length of labels at level %i" % level)
228 level += 1
229 elif labels is not None:
230 usetext = helper.ensuresequence(labels)
231 i = 0
232 for tick in ticks:
233 if tick.labellevel == 0:
234 tick.label = usetext[i]
235 i += 1
236 if i != len(usetext):
237 raise IndexError("wrong list length of labels")
239 def _maxlevels(ticks):
240 "returns a tuple maxticklist, maxlabellevel from a list of tick instances"
241 maxticklevel = maxlabellevel = 0
242 for tick in ticks:
243 if tick.ticklevel is not None and tick.ticklevel >= maxticklevel:
244 maxticklevel = tick.ticklevel + 1
245 if tick.labellevel is not None and tick.labellevel >= maxlabellevel:
246 maxlabellevel = tick.labellevel + 1
247 return maxticklevel, maxlabellevel
250 class _Iparter:
251 """interface definition of a partition scheme
252 partition schemes are used to create a list of ticks"""
254 def defaultpart(self, min, max, extendmin, extendmax):
255 """create a partition
256 - returns an ordered list of ticks for the interval min to max
257 - the interval is given in float numbers, thus an appropriate
258 conversion to rational numbers has to be performed
259 - extendmin and extendmax are booleans (integers)
260 - when extendmin or extendmax is set, the ticks might
261 extend the min-max range towards lower and higher
262 ranges, respectively"""
264 def lesspart(self):
265 """create another partition which contains less ticks
266 - this method is called several times after a call of defaultpart
267 - returns an ordered list of ticks with less ticks compared to
268 the partition returned by defaultpart and by previous calls
269 of lesspart
270 - the creation of a partition with strictly *less* ticks
271 is not to be taken serious
272 - the method might return None, when no other appropriate
273 partition can be created"""
276 def morepart(self):
277 """create another partition which contains more ticks
278 see lesspart, but increase the number of ticks"""
281 class linparter:
282 """linear partition scheme
283 ticks and label distances are explicitly provided to the constructor"""
285 __implements__ = _Iparter
287 def __init__(self, tickdist=None, labeldist=None, labels=None, extendtick=0, extendlabel=None, epsilon=1e-10):
288 """configuration of the partition scheme
289 - tickdist and labeldist should be a list, where the first value
290 is the distance between ticks with ticklevel/labellevel 0,
291 the second list for ticklevel/labellevel 1, etc.;
292 a single entry is allowed without being a list
293 - tickdist and labeldist values are passed to the frac constructor
294 - when labeldist is None and tickdist is not None, the tick entries
295 for ticklevel 0 are used for labels and vice versa (ticks<->labels)
296 - labels are applied to the resulting partition via the
297 mergelabels function (additional information available there)
298 - extendtick allows for the extension of the range given to the
299 defaultpart method to include the next tick with the specified
300 level (None turns off this feature); note, that this feature is
301 also disabled, when an axis prohibits its range extension by
302 the extendmin/extendmax variables given to the defaultpart method
303 - extendlabel is analogous to extendtick, but for labels
304 - epsilon allows for exceeding the axis range by this relative
305 value (relative to the axis range given to the defaultpart method)
306 without creating another tick specified by extendtick/extendlabel"""
307 if tickdist is None and labeldist is not None:
308 self.ticklist = (frac(helper.ensuresequence(labeldist)[0]),)
309 else:
310 self.ticklist = map(frac, helper.ensuresequence(tickdist))
311 if labeldist is None and tickdist is not None:
312 self.labellist = (frac(helper.ensuresequence(tickdist)[0]),)
313 else:
314 self.labellist = map(frac, helper.ensuresequence(labeldist))
315 self.labels = labels
316 self.extendtick = extendtick
317 self.extendlabel = extendlabel
318 self.epsilon = epsilon
320 def extendminmax(self, min, max, frac, extendmin, extendmax):
321 """return new min, max tuple extending the range min, max
322 - frac is the tick distance to be used
323 - extendmin and extendmax are booleans to allow for the extension"""
324 if extendmin:
325 min = float(frac) * math.floor(min / float(frac) + self.epsilon)
326 if extendmax:
327 max = float(frac) * math.ceil(max / float(frac) - self.epsilon)
328 return min, max
330 def getticks(self, min, max, frac, ticklevel=None, labellevel=None):
331 """return a list of equal spaced ticks
332 - the tick distance is frac, the ticklevel is set to ticklevel and
333 the labellevel is set to labellevel
334 - min, max is the range where ticks should be placed"""
335 imin = int(math.ceil(min / float(frac) - 0.5 * self.epsilon))
336 imax = int(math.floor(max / float(frac) + 0.5 * self.epsilon))
337 ticks = []
338 for i in range(imin, imax + 1):
339 ticks.append(tick((long(i) * frac.enum, frac.denom), ticklevel=ticklevel, labellevel=labellevel))
340 return ticks
342 def defaultpart(self, min, max, extendmin, extendmax):
343 if self.extendtick is not None and len(self.ticklist) > self.extendtick:
344 min, max = self.extendminmax(min, max, self.ticklist[self.extendtick], extendmin, extendmax)
345 if self.extendlabel is not None and len(self.labellist) > self.extendlabel:
346 min, max = self.extendminmax(min, max, self.labellist[self.extendlabel], extendmin, extendmax)
348 ticks = []
349 for i in range(len(self.ticklist)):
350 ticks = _mergeticklists(ticks, self.getticks(min, max, self.ticklist[i], ticklevel = i))
351 for i in range(len(self.labellist)):
352 ticks = _mergeticklists(ticks, self.getticks(min, max, self.labellist[i], labellevel = i))
354 _mergelabels(ticks, self.labels)
356 return ticks
358 def lesspart(self):
359 return None
361 def morepart(self):
362 return None
365 class autolinparter:
366 """automatic linear partition scheme
367 - possible tick distances are explicitly provided to the constructor
368 - tick distances are adjusted to the axis range by multiplication or division by 10"""
370 __implements__ = _Iparter
372 defaultvariants = ((frac((1, 1)), frac((1, 2))),
373 (frac((2, 1)), frac((1, 1))),
374 (frac((5, 2)), frac((5, 4))),
375 (frac((5, 1)), frac((5, 2))))
377 def __init__(self, variants=defaultvariants, extendtick=0, epsilon=1e-10):
378 """configuration of the partition scheme
379 - variants is a list of tickdist
380 - tickdist should be a list, where the first value
381 is the distance between ticks with ticklevel 0,
382 the second for ticklevel 1, etc.
383 - tickdist values are passed to the frac constructor
384 - labellevel is set to None except for those ticks in the partitions,
385 where ticklevel is zero. There labellevel is also set to zero.
386 - extendtick allows for the extension of the range given to the
387 defaultpart method to include the next tick with the specified
388 level (None turns off this feature); note, that this feature is
389 also disabled, when an axis prohibits its range extension by
390 the extendmin/extendmax variables given to the defaultpart method
391 - epsilon allows for exceeding the axis range by this relative
392 value (relative to the axis range given to the defaultpart method)
393 without creating another tick specified by extendtick"""
394 self.variants = variants
395 self.extendtick = extendtick
396 self.epsilon = epsilon
398 def defaultpart(self, min, max, extendmin, extendmax):
399 logmm = math.log(max - min) / math.log(10)
400 if logmm < 0: # correction for rounding towards zero of the int routine
401 base = frac((10L, 1), int(logmm - 1))
402 else:
403 base = frac((10L, 1), int(logmm))
404 ticks = map(frac, self.variants[0])
405 useticks = [tick * base for tick in ticks]
406 self.lesstickindex = self.moretickindex = 0
407 self.lessbase = frac((base.enum, base.denom))
408 self.morebase = frac((base.enum, base.denom))
409 self.min, self.max, self.extendmin, self.extendmax = min, max, extendmin, extendmax
410 part = linparter(tickdist=useticks, extendtick=self.extendtick, epsilon=self.epsilon)
411 return part.defaultpart(self.min, self.max, self.extendmin, self.extendmax)
413 def lesspart(self):
414 if self.lesstickindex < len(self.variants) - 1:
415 self.lesstickindex += 1
416 else:
417 self.lesstickindex = 0
418 self.lessbase.enum *= 10
419 ticks = map(frac, self.variants[self.lesstickindex])
420 useticks = [tick * self.lessbase for tick in ticks]
421 part = linparter(tickdist=useticks, extendtick=self.extendtick, epsilon=self.epsilon)
422 return part.defaultpart(self.min, self.max, self.extendmin, self.extendmax)
424 def morepart(self):
425 if self.moretickindex:
426 self.moretickindex -= 1
427 else:
428 self.moretickindex = len(self.variants) - 1
429 self.morebase.denom *= 10
430 ticks = map(frac, self.variants[self.moretickindex])
431 useticks = [tick * self.morebase for tick in ticks]
432 part = linparter(tickdist=useticks, extendtick=self.extendtick, epsilon=self.epsilon)
433 return part.defaultpart(self.min, self.max, self.extendmin, self.extendmax)
436 class preexp:
437 """storage class for the definition of logarithmic axes partitions
438 instances of this class define tick positions suitable for
439 logarithmic axes by the following instance variables:
440 - exp: integer, which defines multiplicator (usually 10)
441 - pres: list of tick positions (rational numbers, e.g. instances of frac)
442 possible positions are these tick positions and arbitrary divisions
443 and multiplications by the exp value"""
445 def __init__(self, pres, exp):
446 "create a preexp instance and store its pres and exp information"
447 self.pres = helper.ensuresequence(pres)
448 self.exp = exp
451 class logparter(linparter):
452 """logarithmic partition scheme
453 ticks and label positions are explicitly provided to the constructor"""
455 __implements__ = _Iparter
457 pre1exp5 = preexp(frac((1, 1)), 100000)
458 pre1exp4 = preexp(frac((1, 1)), 10000)
459 pre1exp3 = preexp(frac((1, 1)), 1000)
460 pre1exp2 = preexp(frac((1, 1)), 100)
461 pre1exp = preexp(frac((1, 1)), 10)
462 pre125exp = preexp((frac((1, 1)), frac((2, 1)), frac((5, 1))), 10)
463 pre1to9exp = preexp(map(lambda x: frac((x, 1)), range(1, 10)), 10)
464 # ^- we always include 1 in order to get extendto(tick|label)level to work as expected
466 def __init__(self, tickpos=None, labelpos=None, labels=None, extendtick=0, extendlabel=None, epsilon=1e-10):
467 """configuration of the partition scheme
468 - tickpos and labelpos should be a list, where the first entry
469 is a preexp instance describing ticks with ticklevel/labellevel 0,
470 the second is a preexp instance for ticklevel/labellevel 1, etc.;
471 a single entry is allowed without being a list
472 - when labelpos is None and tickpos is not None, the tick entries
473 for ticklevel 0 are used for labels and vice versa (ticks<->labels)
474 - labels are applied to the resulting partition via the
475 mergetexts function (additional information available there)
476 - extendtick allows for the extension of the range given to the
477 defaultpart method to include the next tick with the specified
478 level (None turns off this feature); note, that this feature is
479 also disabled, when an axis prohibits its range extension by
480 the extendmin/extendmax variables given to the defaultpart method
481 - extendlabel is analogous to extendtick, but for labels
482 - epsilon allows for exceeding the axis range by this relative
483 logarithm value (relative to the logarithm axis range given
484 to the defaultpart method) without creating another tick
485 specified by extendtick/extendlabel"""
486 if tickpos is None and labels is not None:
487 self.ticklist = (helper.ensuresequence(labelpos)[0],)
488 else:
489 self.ticklist = helper.ensuresequence(tickpos)
491 if labelpos is None and tickpos is not None:
492 self.labellist = (helper.ensuresequence(tickpos)[0],)
493 else:
494 self.labellist = helper.ensuresequence(labelpos)
495 self.labels = labels
496 self.extendtick = extendtick
497 self.extendlabel = extendlabel
498 self.epsilon = epsilon
500 def extendminmax(self, min, max, preexp, extendmin, extendmax):
501 """return new min, max tuple extending the range min, max
502 preexp describes the allowed tick positions
503 extendmin and extendmax are booleans to allow for the extension"""
504 minpower = None
505 maxpower = None
506 for i in xrange(len(preexp.pres)):
507 imin = int(math.floor(math.log(min / float(preexp.pres[i])) /
508 math.log(preexp.exp) + self.epsilon)) + 1
509 imax = int(math.ceil(math.log(max / float(preexp.pres[i])) /
510 math.log(preexp.exp) - self.epsilon)) - 1
511 if minpower is None or imin < minpower:
512 minpower, minindex = imin, i
513 if maxpower is None or imax >= maxpower:
514 maxpower, maxindex = imax, i
515 if minindex:
516 minfrac = preexp.pres[minindex - 1]
517 else:
518 minfrac = preexp.pres[-1]
519 minpower -= 1
520 if maxindex != len(preexp.pres) - 1:
521 maxfrac = preexp.pres[maxindex + 1]
522 else:
523 maxfrac = preexp.pres[0]
524 maxpower += 1
525 if extendmin:
526 min = float(minfrac) * float(preexp.exp) ** minpower
527 if extendmax:
528 max = float(maxfrac) * float(preexp.exp) ** maxpower
529 return min, max
531 def getticks(self, min, max, preexp, ticklevel=None, labellevel=None):
532 """return a list of ticks
533 - preexp describes the allowed tick positions
534 - the ticklevel of the ticks is set to ticklevel and
535 the labellevel is set to labellevel
536 - min, max is the range where ticks should be placed"""
537 ticks = []
538 minimin = 0
539 maximax = 0
540 for f in preexp.pres:
541 fracticks = []
542 imin = int(math.ceil(math.log(min / float(f)) /
543 math.log(preexp.exp) - 0.5 * self.epsilon))
544 imax = int(math.floor(math.log(max / float(f)) /
545 math.log(preexp.exp) + 0.5 * self.epsilon))
546 for i in range(imin, imax + 1):
547 pos = f * frac((preexp.exp, 1), i)
548 fracticks.append(tick((pos.enum, pos.denom), ticklevel = ticklevel, labellevel = labellevel))
549 ticks = _mergeticklists(ticks, fracticks)
550 return ticks
553 class autologparter(logparter):
554 """automatic logarithmic partition scheme
555 possible tick positions are explicitly provided to the constructor"""
557 __implements__ = _Iparter
559 defaultvariants = (((logparter.pre1exp, # ticks
560 logparter.pre1to9exp), # subticks
561 (logparter.pre1exp, # labels
562 logparter.pre125exp)), # sublevels
564 ((logparter.pre1exp, # ticks
565 logparter.pre1to9exp), # subticks
566 None), # labels like ticks
568 ((logparter.pre1exp2, # ticks
569 logparter.pre1exp), # subticks
570 None), # labels like ticks
572 ((logparter.pre1exp3, # ticks
573 logparter.pre1exp), # subticks
574 None), # labels like ticks
576 ((logparter.pre1exp4, # ticks
577 logparter.pre1exp), # subticks
578 None), # labels like ticks
580 ((logparter.pre1exp5, # ticks
581 logparter.pre1exp), # subticks
582 None)) # labels like ticks
584 def __init__(self, variants=defaultvariants, extendtick=0, extendlabel=None, epsilon=1e-10):
585 """configuration of the partition scheme
586 - variants should be a list of pairs of lists of preexp
587 instances
588 - within each pair the first list contains preexp, where
589 the first preexp instance describes ticks positions with
590 ticklevel 0, the second preexp for ticklevel 1, etc.
591 - the second list within each pair describes the same as
592 before, but for labels
593 - within each pair: when the second entry (for the labels) is None
594 and the first entry (for the ticks) ticks is not None, the tick
595 entries for ticklevel 0 are used for labels and vice versa
596 (ticks<->labels)
597 - extendtick allows for the extension of the range given to the
598 defaultpart method to include the next tick with the specified
599 level (None turns off this feature); note, that this feature is
600 also disabled, when an axis prohibits its range extension by
601 the extendmin/extendmax variables given to the defaultpart method
602 - extendlabel is analogous to extendtick, but for labels
603 - epsilon allows for exceeding the axis range by this relative
604 logarithm value (relative to the logarithm axis range given
605 to the defaultpart method) without creating another tick
606 specified by extendtick/extendlabel"""
607 self.variants = variants
608 if len(variants) > 2:
609 self.variantsindex = divmod(len(variants), 2)[0]
610 else:
611 self.variantsindex = 0
612 self.extendtick = extendtick
613 self.extendlabel = extendlabel
614 self.epsilon = epsilon
616 def defaultpart(self, min, max, extendmin, extendmax):
617 self.min, self.max, self.extendmin, self.extendmax = min, max, extendmin, extendmax
618 self.morevariantsindex = self.variantsindex
619 self.lessvariantsindex = self.variantsindex
620 part = logparter(tickpos=self.variants[self.variantsindex][0], labelpos=self.variants[self.variantsindex][1],
621 extendtick=self.extendtick, extendlabel=self.extendlabel, epsilon=self.epsilon)
622 return part.defaultpart(self.min, self.max, self.extendmin, self.extendmax)
624 def lesspart(self):
625 self.lessvariantsindex += 1
626 if self.lessvariantsindex < len(self.variants):
627 part = logparter(tickpos=self.variants[self.lessvariantsindex][0], labelpos=self.variants[self.lessvariantsindex][1],
628 extendtick=self.extendtick, extendlabel=self.extendlabel, epsilon=self.epsilon)
629 return part.defaultpart(self.min, self.max, self.extendmin, self.extendmax)
631 def morepart(self):
632 self.morevariantsindex -= 1
633 if self.morevariantsindex >= 0:
634 part = logparter(tickpos=self.variants[self.morevariantsindex][0], labelpos=self.variants[self.morevariantsindex][1],
635 extendtick=self.extendtick, extendlabel=self.extendlabel, epsilon=self.epsilon)
636 return part.defaultpart(self.min, self.max, self.extendmin, self.extendmax)