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
27
from pyx import canvas, color, attr, text, style, unit, box, path
28
from pyx import trafo as trafomodule
29
from pyx.graph.axis import tick
32
goldenmean = 0.5 * (math.sqrt(5) + 1)
35
class axiscanvas(canvas.canvas):
37
- an axis canvas is a regular canvas returned by an
38
axispainters painter method
39
- it contains a PyX length extent to be used for the
40
alignment of additional axes; the axis extent should
41
be handled by the axispainters painter method; you may
42
apprehend this as a size information comparable to a
43
bounding box, which must be handled manually
44
- it contains a list of textboxes called labels which are
45
used to rate the distances between the labels if needed
46
by the axis later on; the painter method has not only to
47
insert the labels into this canvas, but should also fill
48
this list, when a rating of the distances should be
49
performed by the axis"""
51
# __implements__ = sole implementation
53
def __init__(self, *args, **kwargs):
54
"""initializes the instance
56
- sets labels to an empty list"""
57
canvas.canvas.__init__(self, *args, **kwargs)
63
"""create rotations accordingly to tick directions
64
- upsidedown rotations are suppressed by rotating them by another 180 degree"""
66
# __implements__ = sole implementation
68
def __init__(self, direction, epsilon=1e-10):
69
"""initializes the instance
70
- direction is an angle to be used relative to the tick direction
71
- epsilon is the value by which 90 degrees can be exceeded before
72
an 180 degree rotation is added"""
73
self.direction = direction
74
self.epsilon = epsilon
76
def trafo(self, dx, dy):
77
"""returns a rotation transformation accordingly to the tick direction
78
- dx and dy are the direction of the tick"""
79
direction = self.direction + math.atan2(dy, dx) * 180 / math.pi
80
while (direction > 180 + self.epsilon):
82
while (direction < -180 - self.epsilon):
84
while (direction > 90 + self.epsilon):
86
while (direction < -90 - self.epsilon):
88
return trafomodule.rotate(direction)
91
rotatetext.parallel = rotatetext(90)
92
rotatetext.orthogonal = rotatetext(180)
96
"class for painting axes"
98
def paint(self, axispos, axis, ac=None):
99
"""paint the axis into an axiscanvas
100
- returns the axiscanvas
101
- when no axiscanvas is provided (the typical case), a new
102
axiscanvas is created. however, when extending an painter
103
by inheritance, painting on the same axiscanvas is supported
104
by setting the axiscanvas attribute
105
- axispos is an instance, which implements _Iaxispos to
106
define the tick positions
107
- the axis and should not be modified (we may
108
add some temporary variables like axis.ticks[i].temp_xxx,
109
which might be used just temporary) -- the idea is that
110
all things can be used several times
111
- also do not modify the instance (self) -- even this
112
instance might be used several times; thus do not modify
113
attributes like self.titleattrs etc. (use local copies)
114
- the method might access some additional attributes from
115
the axis, e.g. the axis title -- the axis painter should
116
document this behavior and rely on the availability of
117
those attributes -> it becomes a question of the proper
118
usage of the combination of axis & axispainter
119
- the axiscanvas is a axiscanvas instance and should be
120
filled with ticks, labels, title, etc.; note that the
121
extent and labels instance variables should be handled
122
as documented in the axiscanvas"""
126
"""interface definition of axis tick position methods
127
- these methods are used for the postitioning of the ticks
128
when painting an axis"""
129
# TODO: should we add a local transformation (for label text etc?)
130
# (this might replace tickdirection (and even tickposition?))
132
def basepath(self, x1=None, x2=None):
133
"""return the basepath as a path
134
- x1 is the start position; if not set, the basepath starts
135
from the beginning of the axis, which might imply a
136
value outside of the graph coordinate range [0; 1]
137
- x2 is analogous to x1, but for the end position"""
139
def vbasepath(self, v1=None, v2=None):
140
"""return the basepath as a path
141
- like basepath, but for graph coordinates"""
143
def gridpath(self, x):
144
"""return the gridpath as a path for a given position x
145
- might return None when no gridpath is available"""
147
def vgridpath(self, v):
148
"""return the gridpath as a path for a given position v
150
- might return None when no gridpath is available"""
152
def tickpoint_pt(self, x):
153
"""return the position at the basepath as a tuple (x, y) in
154
postscript points for the position x"""
156
def tickpoint(self, x):
157
"""return the position at the basepath as a tuple (x, y) in
158
in PyX length for the position x"""
160
def vtickpoint_pt(self, v):
161
"like tickpoint_pt, but for graph coordinates"
163
def vtickpoint(self, v):
164
"like tickpoint, but for graph coordinates"
166
def tickdirection(self, x):
167
"""return the direction of a tick as a tuple (dx, dy) for the
168
position x (the direction points towards the graph)"""
170
def vtickdirection(self, v):
171
"""like tickposition, but for graph coordinates"""
175
"""implements those parts of _Iaxispos which can be build
176
out of the axis convert method and other _Iaxispos methods
177
- base _Iaxispos methods, which need to be implemented:
182
- other methods needed for _Iaxispos are build out of those
183
listed above when this class is inherited"""
185
def __init__(self, convert):
186
"""initializes the instance
187
- convert is a convert method from an axis"""
188
self.convert = convert
190
def basepath(self, x1=None, x2=None):
193
return self.vbasepath()
195
return self.vbasepath(v2=self.convert(x2))
198
return self.vbasepath(v1=self.convert(x1))
200
return self.vbasepath(v1=self.convert(x1), v2=self.convert(x2))
202
def gridpath(self, x):
203
return self.vgridpath(self.convert(x))
205
def tickpoint_pt(self, x):
206
return self.vtickpoint_pt(self.convert(x))
208
def tickpoint(self, x):
209
return self.vtickpoint(self.convert(x))
211
def vtickpoint(self, v):
212
return [x * unit.t_pt for x in self.vtickpoint(v)]
214
def tickdirection(self, x):
215
return self.vtickdirection(self.convert(x))
218
class pathaxispos(_axispos):
219
"""axis tick position methods along an arbitrary path"""
221
__implements__ = _Iaxispos
223
def __init__(self, p, convert, direction=1):
225
self.normpath = p.normpath()
226
self.arclen_pt = self.normpath.arclen_pt()
227
self.arclen = self.arclen_pt * unit.t_pt
228
_axispos.__init__(self, convert)
229
self.direction = direction
231
def vbasepath(self, v1=None, v2=None):
236
return self.normpath.split(self.normpath.arclentoparam(v2 * self.arclen))[0]
239
return self.normpath.split(self.normpath.arclentoparam(v1 * self.arclen))[1]
241
return self.normpath.split(*self.normpath.arclentoparam([v1 * self.arclen, v2 * self.arclen]))[1]
243
def vgridpath(self, v):
246
def vtickpoint_pt(self, v):
247
return self.normpath.at_pt(self.normpath.arclentoparam(v * self.arclen))
249
def vtickdirection(self, v):
250
t= self.normpath.tangent(self.normpath.arclentoparam(v * self.arclen))
251
tbegin = t.begin_pt()
253
dx = tend[0]-tbegin[0]
254
dy = tend[1]-tbegin[1]
255
norm = math.hypot(dx, dy)
256
if self.direction == 1:
257
return -dy/norm, dx/norm
258
elif self.direction == -1:
259
return dy/norm, -dx/norm
260
raise RuntimeError("unknown direction")
264
"""class for painting an axis title
265
- the axis must have a title attribute when using this painter;
266
this title might be None"""
268
__implements__ = _Iaxispainter
270
defaulttitleattrs = [text.halign.center, text.vshift.mathaxis]
272
def __init__(self, titledist=0.3*unit.v_cm,
274
titledirection=rotatetext.parallel,
276
texrunner=text.defaulttexrunner):
277
"""initialized the instance
278
- titledist is a visual PyX length giving the distance
279
of the title from the axis extent already there (a title might
280
be added after labels or other things are plotted already)
281
- titleattrs is a list of attributes for a texrunners text
282
method; a single is allowed without being a list; None
284
- titledirection is an instance of rotatetext or None
285
- titlepos is the position of the title in graph coordinates
286
- texrunner is the texrunner to be used to create text
287
(the texrunner is available for further use in derived
288
classes as instance variable texrunner)"""
289
self.titledist = titledist
290
self.titleattrs = titleattrs
291
self.titledirection = titledirection
292
self.titlepos = titlepos
293
self.texrunner = texrunner
295
def paint(self, axispos, axis, ac=None):
298
if axis.title is not None and self.titleattrs is not None:
299
x, y = axispos.vtickpoint_pt(self.titlepos)
300
dx, dy = axispos.vtickdirection(self.titlepos)
301
titleattrs = self.defaulttitleattrs + self.titleattrs
302
if self.titledirection is not None:
303
titleattrs.append(self.titledirection.trafo(dx, dy))
304
title = self.texrunner.text_pt(x, y, axis.title, titleattrs)
305
ac.extent += self.titledist
306
title.linealign(ac.extent, -dx, -dy)
307
ac.extent += title.extent(dx, dy)
312
class geometricseries(attr.changeattr):
314
def __init__(self, initial, factor):
315
self.initial = initial
318
def select(self, index, total):
319
return self.initial * (self.factor ** index)
322
class ticklength(geometricseries): pass
324
_base = 0.12 * unit.v_cm
326
ticklength.SHORT = ticklength(_base/math.sqrt(64), 1/goldenmean)
327
ticklength.SHORt = ticklength(_base/math.sqrt(32), 1/goldenmean)
328
ticklength.SHOrt = ticklength(_base/math.sqrt(16), 1/goldenmean)
329
ticklength.SHort = ticklength(_base/math.sqrt(8), 1/goldenmean)
330
ticklength.Short = ticklength(_base/math.sqrt(4), 1/goldenmean)
331
ticklength.short = ticklength(_base/math.sqrt(2), 1/goldenmean)
332
ticklength.normal = ticklength(_base, 1/goldenmean)
333
ticklength.long = ticklength(_base*math.sqrt(2), 1/goldenmean)
334
ticklength.Long = ticklength(_base*math.sqrt(4), 1/goldenmean)
335
ticklength.LOng = ticklength(_base*math.sqrt(8), 1/goldenmean)
336
ticklength.LONg = ticklength(_base*math.sqrt(16), 1/goldenmean)
337
ticklength.LONG = ticklength(_base*math.sqrt(32), 1/goldenmean)
340
class regular(_title):
341
"""class for painting the ticks and labels of an axis
342
- the inherited _title is used to paint the title of
344
- note that the type of the elements of ticks given as an argument
345
of the paint method must be suitable for the tick position methods
348
__implements__ = _Iaxispainter
350
defaulttickattrs = []
351
defaultgridattrs = []
352
defaultbasepathattrs = [style.linecap.square]
353
defaultlabelattrs = [text.halign.center, text.vshift.mathaxis]
355
def __init__(self, innerticklength=ticklength.normal,
356
outerticklength=None,
360
labeldist=0.3*unit.v_cm,
366
"""initializes the instance
367
- innerticklength and outerticklength are changable
368
visual PyX lengths for ticks, subticks, etc. plotted inside
369
and outside of the graph; None turns off ticks inside or
371
- tickattrs are a list of stroke attributes for the ticks;
373
- gridattrs are a list of lists used as stroke
374
attributes for ticks, subticks etc.; None turns off
376
- basepathattrs are a list of stroke attributes for the base line
377
of the axis; None turns off the basepath
378
- labeldist is a visual PyX length for the distance of the labels
379
from the axis basepath
380
- labelattrs is a list of attributes for a texrunners text
381
method; None turns off the labels
382
- labeldirection is an instance of rotatetext or None
383
- labelhequalize and labelvequalize (booleans) perform an equal
384
alignment for straight vertical and horizontal axes, respectively
385
- futher keyword arguments are passed to _axistitle"""
386
self.innerticklength = innerticklength
387
self.outerticklength = outerticklength
388
self.tickattrs = tickattrs
389
self.gridattrs = gridattrs
390
self.basepathattrs = basepathattrs
391
self.labeldist = labeldist
392
self.labelattrs = labelattrs
393
self.labeldirection = labeldirection
394
self.labelhequalize = labelhequalize
395
self.labelvequalize = labelvequalize
396
_title.__init__(self, **kwargs)
398
def paint(self, axispos, axis, ac=None):
402
t.temp_v = axis.convert(t)
403
t.temp_x, t.temp_y = axispos.vtickpoint_pt(t.temp_v)
404
t.temp_dx, t.temp_dy = axispos.vtickdirection(t.temp_v)
405
maxticklevel, maxlabellevel = tick.maxlevels(axis.ticks)
407
# create & align t.temp_labelbox
409
if t.labellevel is not None:
410
labelattrs = attr.selectattrs(self.labelattrs, t.labellevel, maxlabellevel)
411
if labelattrs is not None:
412
labelattrs = self.defaultlabelattrs + labelattrs
413
if self.labeldirection is not None:
414
labelattrs.append(self.labeldirection.trafo(t.temp_dx, t.temp_dy))
415
if t.labelattrs is not None:
416
labelattrs.extend(t.labelattrs)
417
t.temp_labelbox = self.texrunner.text_pt(t.temp_x, t.temp_y, t.label, labelattrs)
418
if len(axis.ticks) > 1:
420
for t in axis.ticks[1:]:
421
if t.temp_dx != axis.ticks[0].temp_dx or t.temp_dy != axis.ticks[0].temp_dy:
425
if equaldirection and ((not axis.ticks[0].temp_dx and self.labelvequalize) or
426
(not axis.ticks[0].temp_dy and self.labelhequalize)):
427
if self.labelattrs is not None:
428
box.linealignequal([t.temp_labelbox for t in axis.ticks if t.labellevel is not None],
429
self.labeldist, -axis.ticks[0].temp_dx, -axis.ticks[0].temp_dy)
432
if t.labellevel is not None and self.labelattrs is not None:
433
t.temp_labelbox.linealign(self.labeldist, -t.temp_dx, -t.temp_dy)
436
if t.ticklevel is not None:
437
tickattrs = attr.selectattrs(self.defaulttickattrs + self.tickattrs, t.ticklevel, maxticklevel)
438
if tickattrs is not None:
439
innerticklength = attr.selectattr(self.innerticklength, t.ticklevel, maxticklevel)
440
outerticklength = attr.selectattr(self.outerticklength, t.ticklevel, maxticklevel)
441
if innerticklength is not None or outerticklength is not None:
442
if innerticklength is None:
444
if outerticklength is None:
446
innerticklength_pt = unit.topt(innerticklength)
447
outerticklength_pt = unit.topt(outerticklength)
448
x1 = t.temp_x + t.temp_dx * innerticklength_pt
449
y1 = t.temp_y + t.temp_dy * innerticklength_pt
450
x2 = t.temp_x - t.temp_dx * outerticklength_pt
451
y2 = t.temp_y - t.temp_dy * outerticklength_pt
452
ac.stroke(path.line_pt(x1, y1, x2, y2), tickattrs)
453
if outerticklength is not None and outerticklength > ac.extent:
454
ac.extent = outerticklength
455
if outerticklength is not None and -innerticklength > ac.extent:
456
ac.extent = -innerticklength
457
if self.gridattrs is not None:
458
gridattrs = attr.selectattrs(self.defaultgridattrs + self.gridattrs, t.ticklevel, maxticklevel)
459
if gridattrs is not None:
460
ac.stroke(axispos.vgridpath(t.temp_v), gridattrs)
461
if t.labellevel is not None and self.labelattrs is not None:
462
ac.insert(t.temp_labelbox)
463
ac.labels.append(t.temp_labelbox)
464
extent = t.temp_labelbox.extent(t.temp_dx, t.temp_dy) + self.labeldist
465
if extent > ac.extent:
467
if self.basepathattrs is not None:
468
ac.stroke(axispos.vbasepath(), self.defaultbasepathattrs + self.basepathattrs)
470
# for t in axis.ticks:
471
# del t.temp_v # we've inserted those temporary variables ... and do not care any longer about them
476
# if t.labellevel is not None and self.labelattrs is not None:
477
# del t.temp_labelbox
479
_title.paint(self, axispos, axis, ac=ac)
484
class linked(regular):
485
"""class for painting a linked axis
486
- the inherited regular is used to paint the axis
487
- modifies some constructor defaults"""
489
__implements__ = _Iaxispainter
491
def __init__(self, labelattrs=None,
494
"""initializes the instance
495
- the labelattrs default is set to None thus skipping the labels
496
- the titleattrs default is set to None thus skipping the title
497
- all keyword arguments are passed to regular"""
498
regular.__init__(self, labelattrs=labelattrs,
499
titleattrs=titleattrs,
504
"""implementation of the _Iaxispos interface for a subaxis"""
506
__implements__ = _Iaxispos
508
def __init__(self, convert, baseaxispos, vmin, vmax, vminover, vmaxover):
509
"""initializes the instance
510
- convert is the subaxis convert method
511
- baseaxispos is the axispos instance of the base axis
512
- vmin, vmax is the range covered by the subaxis in graph coordinates
513
- vminover, vmaxover is the extended range of the subaxis including
514
regions between several subaxes (for basepath drawing etc.)"""
515
self.convert = convert
516
self.baseaxispos = baseaxispos
519
self.vminover = vminover
520
self.vmaxover = vmaxover
522
def basepath(self, x1=None, x2=None):
524
v1 = self.vmin+self.convert(x1)*(self.vmax-self.vmin)
528
v2 = self.vmin+self.convert(x2)*(self.vmax-self.vmin)
531
return self.baseaxispos.vbasepath(v1, v2)
533
def vbasepath(self, v1=None, v2=None):
535
v1 = self.vmin+v1*(self.vmax-self.vmin)
539
v2 = self.vmin+v2*(self.vmax-self.vmin)
542
return self.baseaxispos.vbasepath(v1, v2)
544
def gridpath(self, x):
545
return self.baseaxispos.vgridpath(self.vmin+self.convert(x)*(self.vmax-self.vmin))
547
def vgridpath(self, v):
548
return self.baseaxispos.vgridpath(self.vmin+v*(self.vmax-self.vmin))
550
def tickpoint_pt(self, x, axis=None):
551
return self.baseaxispos.vtickpoint_pt(self.vmin+self.convert(x)*(self.vmax-self.vmin))
553
def tickpoint(self, x, axis=None):
554
return self.baseaxispos.vtickpoint(self.vmin+self.convert(x)*(self.vmax-self.vmin))
556
def vtickpoint_pt(self, v, axis=None):
557
return self.baseaxispos.vtickpoint_pt(self.vmin+v*(self.vmax-self.vmin))
559
def vtickpoint(self, v, axis=None):
560
return self.baseaxispos.vtickpoint(self.vmin+v*(self.vmax-self.vmin))
562
def tickdirection(self, x, axis=None):
563
return self.baseaxispos.vtickdirection(self.vmin+self.convert(x)*(self.vmax-self.vmin))
565
def vtickdirection(self, v, axis=None):
566
return self.baseaxispos.vtickdirection(self.vmin+v*(self.vmax-self.vmin))
570
"""class for painting a splitaxis
571
- the inherited _title is used to paint the title of
573
- the splitaxis access the subaxes attribute of the axis"""
575
__implements__ = _Iaxispainter
577
defaultbreaklinesattrs = []
579
def __init__(self, breaklinesdist=0.05*unit.v_cm,
580
breaklineslength=0.5*unit.v_cm,
584
"""initializes the instance
585
- breaklinesdist is a visual length of the distance between
586
the two lines of the axis break
587
- breaklineslength is a visual length of the length of the
588
two lines of the axis break
589
- breaklinesangle is the angle of the lines of the axis break
590
- breaklinesattrs are a list of stroke attributes for the
591
axis break lines; a single entry is allowed without being a
592
list; None turns off the break lines
593
- futher keyword arguments are passed to _title"""
594
self.breaklinesdist = breaklinesdist
595
self.breaklineslength = breaklineslength
596
self.breaklinesangle = breaklinesangle
597
self.breaklinesattrs = breaklinesattrs
598
_title.__init__(self, **args)
600
def paint(self, axispos, axis, ac=None):
603
for subaxis in axis.subaxes:
604
subaxis.finish(subaxispos(subaxis.convert, axispos, subaxis.vmin, subaxis.vmax, subaxis.vminover, subaxis.vmaxover))
605
ac.insert(subaxis.axiscanvas)
606
if ac.extent < subaxis.axiscanvas.extent:
607
ac.extent = subaxis.axiscanvas.extent
608
if self.breaklinesattrs is not None:
609
self.sin = math.sin(self.breaklinesangle*math.pi/180.0)
610
self.cos = math.cos(self.breaklinesangle*math.pi/180.0)
611
breaklinesextent = (0.5*self.breaklinesdist*math.fabs(self.cos) +
612
0.5*self.breaklineslength*math.fabs(self.sin))
613
if ac.extent < breaklinesextent:
614
ac.extent = breaklinesextent
615
for subaxis1, subaxis2 in zip(axis.subaxes[:-1], axis.subaxes[1:]):
616
# use a tangent of the basepath (this is independent of the tickdirection)
617
v = 0.5 * (subaxis1.vmax + subaxis2.vmin)
618
p = axispos.vbasepath(v, None).normpath()
619
breakline = p.tangent(0, length=self.breaklineslength)
620
widthline = p.tangent(0, length=self.breaklinesdist).transformed(trafomodule.rotate(self.breaklinesangle+90, *breakline.begin()))
622
tocenter = map(lambda x: 0.5*(x[0]-x[1]), zip(breakline.begin(), breakline.end()))
623
towidth = map(lambda x: 0.5*(x[0]-x[1]), zip(widthline.begin(), widthline.end()))
624
breakline = breakline.transformed(trafomodule.translate(*tocenter).rotated(self.breaklinesangle, *breakline.begin()))
625
breakline1 = breakline.transformed(trafomodule.translate(*towidth))
626
breakline2 = breakline.transformed(trafomodule.translate(-towidth[0], -towidth[1]))
627
ac.fill(path.path(path.moveto_pt(*breakline1.begin_pt()),
628
path.lineto_pt(*breakline1.end_pt()),
629
path.lineto_pt(*breakline2.end_pt()),
630
path.lineto_pt(*breakline2.begin_pt()),
631
path.closepath()), [color.gray.white])
632
ac.stroke(breakline1, self.defaultbreaklinesattrs + self.breaklinesattrs)
633
ac.stroke(breakline2, self.defaultbreaklinesattrs + self.breaklinesattrs)
634
_title.paint(self, axispos, axis, ac=ac)
638
class linkedsplit(split):
639
"""class for painting a linked splitaxis
640
- the inherited split is used to paint the axis
641
- modifies some constructor defaults"""
643
__implements__ = _Iaxispainter
645
def __init__(self, titleattrs=None, **kwargs):
646
"""initializes the instance
647
- the titleattrs default is set to None thus skipping the title
648
- all keyword arguments are passed to split"""
649
split.__init__(self, titleattrs=titleattrs, **kwargs)
653
"""class for painting a baraxis
654
- the inherited _title is used to paint the title of
656
- the bar access the multisubaxis, names, and subaxis
657
relsizes attributes"""
659
__implements__ = _Iaxispainter
661
defaulttickattrs = []
662
defaultbasepathattrs = [style.linecap.square]
663
defaultnameattrs = [text.halign.center, text.vshift.mathaxis]
665
def __init__(self, innerticklength=None,
666
outerticklength=None,
669
namedist=0.3*unit.v_cm,
676
"""initializes the instance
677
- innerticklength and outerticklength are a visual length of
678
the ticks to be plotted at the axis basepath to visually
679
separate the bars; if neither innerticklength nor
680
outerticklength are set, not ticks are plotted
681
- breaklinesattrs are a list of stroke attributes for the
682
axis tick; a single entry is allowed without being a
683
list; None turns off the ticks
684
- namedist is a visual PyX length for the distance of the bar
685
names from the axis basepath
686
- nameattrs is a list of attributes for a texrunners text
687
method; a single entry is allowed without being a list;
688
None turns off the names
689
- namedirection is an instance of rotatetext or None
690
- namehequalize and namevequalize (booleans) perform an equal
691
alignment for straight vertical and horizontal axes, respectively
692
- futher keyword arguments are passed to _title"""
693
self.innerticklength = innerticklength
694
self.outerticklength = outerticklength
695
self.tickattrs = tickattrs
696
self.basepathattrs = basepathattrs
697
self.namedist = namedist
698
self.nameattrs = nameattrs
699
self.namedirection = namedirection
700
self.namepos = namepos
701
self.namehequalize = namehequalize
702
self.namevequalize = namevequalize
703
_title.__init__(self, **args)
705
def paint(self, axispos, axis, ac=None):
708
if axis.multisubaxis is not None:
709
for subaxis in axis.subaxis:
710
subaxis.finish(subaxispos(subaxis.convert, axispos, subaxis.vmin, subaxis.vmax, None, None))
711
ac.insert(subaxis.axiscanvas)
712
if ac.extent < subaxis.axiscanvas.extent:
713
ac.extent = subaxis.axiscanvas.extent
715
for name in axis.names:
716
v = axis.convert((name, self.namepos))
717
x, y = axispos.vtickpoint_pt(v)
718
dx, dy = axispos.vtickdirection(v)
719
namepos.append((v, x, y, dx, dy))
721
if self.nameattrs is not None:
722
for (v, x, y, dx, dy), name in zip(namepos, axis.names):
723
nameattrs = self.defaultnameattrs + self.nameattrs
724
if self.namedirection is not None:
725
nameattrs.append(self.namedirection.trafo(tick.temp_dx, tick.temp_dy))
726
nameboxes.append(self.texrunner.text_pt(x, y, str(name), nameattrs))
727
labeldist = ac.extent + self.namedist
730
for np in namepos[1:]:
731
if np[3] != namepos[0][3] or np[4] != namepos[0][4]:
735
if equaldirection and ((not namepos[0][3] and self.namevequalize) or
736
(not namepos[0][4] and self.namehequalize)):
737
box.linealignequal(nameboxes, labeldist, -namepos[0][3], -namepos[0][4])
739
for namebox, np in zip(nameboxes, namepos):
740
namebox.linealign(labeldist, -np[3], -np[4])
741
if self.basepathattrs is not None:
742
p = axispos.vbasepath()
744
ac.stroke(p, self.defaultbasepathattrs + self.basepathattrs)
745
if self.tickattrs is not None and (self.innerticklength is not None or
746
self.outerticklength is not None):
747
if self.innerticklength is not None:
748
innerticklength_pt = unit.topt(self.innerticklength)
749
if ac.extent < -self.innerticklength:
750
ac.extent = -self.innerticklength
751
elif self.outerticklength is not None:
752
innerticklength_pt = 0
753
if self.outerticklength is not None:
754
outerticklength_pt = unit.topt(self.outerticklength)
755
if ac.extent < self.outerticklength:
756
ac.extent = self.outerticklength
757
elif self.innerticklength is not None:
758
outerticklength_pt = 0
759
for pos in axis.relsizes:
760
if pos == axis.relsizes[0]:
761
pos -= axis.firstdist
762
elif pos != axis.relsizes[-1]:
763
pos -= 0.5 * axis.dist
764
v = pos / axis.relsizes[-1]
765
x, y = axispos.vtickpoint_pt(v)
766
dx, dy = axispos.vtickdirection(v)
767
x1 = x + dx * innerticklength_pt
768
y1 = y + dy * innerticklength_pt
769
x2 = x - dx * outerticklength_pt
770
y2 = y - dy * outerticklength_pt
771
ac.stroke(path.line_pt(x1, y1, x2, y2), self.defaulttickattrs + self.tickattrs)
772
for (v, x, y, dx, dy), namebox in zip(namepos, nameboxes):
773
newextent = namebox.extent(dx, dy) + labeldist
774
if ac.extent < newextent:
775
ac.extent = newextent
776
for namebox in nameboxes:
778
_title.paint(self, axispos, axis, ac=ac)
782
class linkedbar(bar):
783
"""class for painting a linked baraxis
784
- the inherited bar is used to paint the axis
785
- modifies some constructor defaults"""
787
__implements__ = _Iaxispainter
789
def __init__(self, nameattrs=None, titleattrs=None, **kwargs):
790
"""initializes the instance
791
- the titleattrs default is set to None thus skipping the title
792
- the nameattrs default is set to None thus skipping the names
793
- all keyword arguments are passed to bar"""
794
bar.__init__(self, nameattrs=nameattrs, titleattrs=titleattrs, **kwargs)