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-2005 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
27 from pyx
import attr
, deco
, style
, color
, unit
, canvas
, path
28 from pyx
import text
as textmodule
35 # fallback implementation for Python 2.2. and below
37 return zip(xrange(len(list)), list)
40 """Interface class for graph styles
42 Each graph style must support the methods described in this
43 class. However, since a graph style might not need to perform
44 actions on all the various events, it does not need to overwrite
45 all methods of this base class (e.g. this class is not an abstract
46 class in any respect).
48 A style should never store private data by istance variables
49 (i.e. accessing self), but it should use the sharedata and privatedata
50 instances instead. A style instance can be used multiple times with
51 different sharedata and privatedata instances at the very same time.
52 The sharedata and privatedata instances act as data containers and
53 sharedata allows for sharing information across several styles.
55 Every style contains two class variables, which are not to be
57 - providesdata is a list of variable names a style offers via
58 the sharedata instance. This list is used to determine whether
59 all needs of subsequent styles are fullfilled. Otherwise
60 getdefaultprovider should return a proper style to be used.
61 - needsdata is a list of variable names the style needs to access in the
65 providesdata
= [] # by default, we provide nothing
66 needsdata
= [] # and do not depend on anything
68 def columnnames(self
, privatedata
, sharedata
, graph
, columnnames
):
69 """Set column information
71 This method is used setup the column name information to be
72 accessible to the style later on. The style should analyse
73 the list of column names. The method should return a list of
74 column names which the style will make use of."""
77 def selectstyle(self
, privatedata
, sharedata
, graph
, selectindex
, selecttotal
):
78 """Select stroke/fill attributes
80 This method is called to allow for the selection of
81 changable attributes of a style."""
84 def adjustaxis(self
, privatedata
, sharedata
, graph
, columnname
, data
):
87 This method is called in order to adjust the axis range to
88 the provided data. columnname is the column name (each style
89 is subsequently called for all column names)."""
92 def initdrawpoints(self
, privatedata
, sharedata
, graph
):
93 """Initialize drawing of data
95 This method might be used to initialize the drawing of data."""
98 def drawpoint(self
, privatedata
, sharedata
, graph
, point
):
101 This method is called for each data point. The data is
102 available in the dictionary point. The dictionary
103 keys are the column names."""
106 def donedrawpoints(self
, privatedata
, sharedata
, graph
):
107 """Finalize drawing of data
109 This method is called after the last data point was
110 drawn using the drawpoint method above."""
113 def key_pt(self
, privatedata
, sharedata
, graph
, x_pt
, y_pt
, width_pt
, height_pt
):
117 # The following two methods are used to register and get a default provider
118 # for keys. A key is a variable name in sharedata. A provider is a style
119 # which creates variables in sharedata.
121 _defaultprovider
= {}
123 def registerdefaultprovider(style
, keys
):
124 """sets a style as a default creator for sharedata variables 'keys'"""
125 assert not len(style
.needsdata
), "currently we state, that a style should not depend on other sharedata variables"
127 assert key
in style
.providesdata
, "key not provided by style"
128 # we might allow for overwriting the defaults, i.e. the following is not checked:
129 # assert key in _defaultprovider.keys(), "default provider already registered for key"
130 _defaultprovider
[key
] = style
132 def getdefaultprovider(key
):
133 """returns a style, which acts as a default creator for the
134 sharedata variable 'key'"""
135 return _defaultprovider
[key
]
140 providesdata
= ["vpos", "vposmissing", "vposavailable", "vposvalid", "poscolumnnames"]
142 def __init__(self
, epsilon
=1e-10):
143 self
.epsilon
= epsilon
145 def columnnames(self
, privatedata
, sharedata
, graph
, columnnames
):
146 sharedata
.poscolumnnames
= []
147 sharedata
.vposmissing
= []
148 for count
, axisnames
in enumerate(graph
.axesnames
):
149 for axisname
in axisnames
:
150 for columnname
in columnnames
:
151 if axisname
== columnname
:
152 sharedata
.poscolumnnames
.append(columnname
)
153 if len(sharedata
.poscolumnnames
) > count
+1:
154 raise ValueError("multiple axes per graph dimension")
155 elif len(sharedata
.poscolumnnames
) < count
+1:
156 sharedata
.vposmissing
.append(count
)
157 sharedata
.poscolumnnames
.append(None)
158 return [columnname
for columnname
in sharedata
.poscolumnnames
if columnname
is not None]
160 def adjustaxis(self
, privatedata
, sharedata
, graph
, columnname
, data
):
161 if columnname
in sharedata
.poscolumnnames
:
162 graph
.axes
[columnname
].adjustaxis(data
)
164 def initdrawpoints(self
, privatedata
, sharedata
, graph
):
165 sharedata
.vpos
= [None]*(len(graph
.axesnames
))
166 privatedata
.pointpostmplist
= [[columnname
, index
, graph
.axes
[columnname
]] # temporarily used by drawpoint only
167 for index
, columnname
in enumerate([columnname
for columnname
in sharedata
.poscolumnnames
if columnname
is not None])]
168 for missing
in sharedata
.vposmissing
:
169 for pointpostmp
in privatedata
.pointpostmplist
:
170 if pointpostmp
[1] >= missing
:
173 def drawpoint(self
, privatedata
, sharedata
, graph
, point
):
174 sharedata
.vposavailable
= 1 # valid position (but might be outside of the graph)
175 sharedata
.vposvalid
= 1 # valid position inside the graph
176 for columnname
, index
, axis
in privatedata
.pointpostmplist
:
178 v
= axis
.convert(point
[columnname
])
179 except (ArithmeticError, ValueError, TypeError):
180 sharedata
.vposavailable
= sharedata
.vposvalid
= 0
181 sharedata
.vpos
[index
] = None
183 if v
< -self
.epsilon
or v
> 1+self
.epsilon
:
184 sharedata
.vposvalid
= 0
185 sharedata
.vpos
[index
] = v
188 registerdefaultprovider(pos(), pos
.providesdata
)
193 providesdata
= ["vrange", "vrangemissing", "vrangeminmissing", "vrangemaxmissing"]
203 def __init__(self
, usenames
={}, epsilon
=1e-10):
204 self
.usenames
= usenames
205 self
.epsilon
= epsilon
207 def _numberofbits(self
, mask
):
211 return self
._numberofbits
(mask
>> 1) + 1
213 return self
._numberofbits
(mask
>> 1)
215 def columnnames(self
, privatedata
, sharedata
, graph
, columnnames
):
217 privatedata
.rangeposcolumns
= []
218 sharedata
.vrangemissing
= []
219 sharedata
.vrangeminmissing
= []
220 sharedata
.vrangemaxmissing
= []
221 privatedata
.rangeposdeltacolumns
= {} # temporarily used by adjustaxis only
222 for count
, axisnames
in enumerate(graph
.axesnames
):
223 for axisname
in axisnames
:
225 usename
= self
.usenames
[axisname
]
229 for columnname
in columnnames
:
231 if usename
== columnname
:
232 mask
+= self
.mask_value
233 elif usename
+ "min" == columnname
:
234 mask
+= self
.mask_min
235 elif usename
+ "max" == columnname
:
236 mask
+= self
.mask_max
237 elif "d" + usename
+ "min" == columnname
:
238 mask
+= self
.mask_dmin
239 elif "d" + usename
+ "max" == columnname
:
240 mask
+= self
.mask_dmax
241 elif "d" + usename
== columnname
:
246 usecolumns
.append(columnname
)
247 if mask
& (self
.mask_min | self
.mask_max | self
.mask_dmin | self
.mask_dmax | self
.mask_d
):
248 if (self
._numberofbits
(mask
& (self
.mask_min | self
.mask_dmin | self
.mask_d
)) > 1 or
249 self
._numberofbits
(mask
& (self
.mask_max | self
.mask_dmax | self
.mask_d
)) > 1):
250 raise ValueError("multiple range definition")
251 if mask
& (self
.mask_dmin | self
.mask_dmax | self
.mask_d
):
252 if not (mask
& self
.mask_value
):
253 raise ValueError("missing value for delta")
254 privatedata
.rangeposdeltacolumns
[axisname
] = {}
255 privatedata
.rangeposcolumns
.append((axisname
, usename
, mask
))
256 elif mask
== self
.mask_value
:
257 usecolumns
= usecolumns
[:-1]
258 if len(privatedata
.rangeposcolumns
) + len(sharedata
.vrangemissing
) > count
+1:
259 raise ValueError("multiple axes per graph dimension")
260 elif len(privatedata
.rangeposcolumns
) + len(sharedata
.vrangemissing
) < count
+1:
261 sharedata
.vrangemissing
.append(count
)
262 sharedata
.vrangeminmissing
.append(count
)
263 sharedata
.vrangemaxmissing
.append(count
)
265 if not (privatedata
.rangeposcolumns
[-1][2] & (self
.mask_min | self
.mask_dmin | self
.mask_d
)):
266 sharedata
.vrangeminmissing
.append(count
)
267 if not (privatedata
.rangeposcolumns
[-1][2] & (self
.mask_max | self
.mask_dmax | self
.mask_d
)):
268 sharedata
.vrangemaxmissing
.append(count
)
271 def adjustaxis(self
, privatedata
, sharedata
, graph
, columnname
, data
):
272 if columnname
in [c
+ "min" for a
, c
, m
in privatedata
.rangeposcolumns
if m
& self
.mask_min
]:
273 graph
.axes
[columnname
[:-3]].adjustaxis(data
)
274 if columnname
in [c
+ "max" for a
, c
, m
in privatedata
.rangeposcolumns
if m
& self
.mask_max
]:
275 graph
.axes
[columnname
[:-3]].adjustaxis(data
)
277 # delta handling: fill rangeposdeltacolumns
278 for axisname
, usename
, mask
in privatedata
.rangeposcolumns
:
279 if columnname
== usename
and mask
& (self
.mask_dmin | self
.mask_dmax | self
.mask_d
):
280 privatedata
.rangeposdeltacolumns
[axisname
][self
.mask_value
] = data
281 if columnname
== "d" + usename
+ "min" and mask
& self
.mask_dmin
:
282 privatedata
.rangeposdeltacolumns
[axisname
][self
.mask_dmin
] = data
283 if columnname
== "d" + usename
+ "max" and mask
& self
.mask_dmax
:
284 privatedata
.rangeposdeltacolumns
[axisname
][self
.mask_dmax
] = data
285 if columnname
== "d" + usename
and mask
& self
.mask_d
:
286 privatedata
.rangeposdeltacolumns
[axisname
][self
.mask_d
] = data
288 # delta handling: process rangeposdeltacolumns
289 for a
, d
in privatedata
.rangeposdeltacolumns
.items():
290 if d
.has_key(self
.mask_value
):
292 if k
!= self
.mask_value
:
293 if k
& (self
.mask_dmin | self
.mask_d
):
296 mindata
.append(d
[self
.mask_value
] - d
[k
])
299 graph
.axes
[a
].adjustaxis(mindata
)
300 if k
& (self
.mask_dmax | self
.mask_d
):
303 maxdata
.append(d
[self
.mask_value
] + d
[k
])
306 graph
.axes
[a
].adjustaxis(maxdata
)
309 def initdrawpoints(self
, privatedata
, sharedata
, graph
):
310 sharedata
.vrange
= [[None for x
in xrange(2)] for y
in privatedata
.rangeposcolumns
+ sharedata
.vrangemissing
]
311 privatedata
.rangepostmplist
= [[usename
, mask
, index
, graph
.axes
[axisname
]] # temporarily used by drawpoint only
312 for index
, (axisname
, usename
, mask
) in enumerate(privatedata
.rangeposcolumns
)]
313 for missing
in sharedata
.vrangemissing
:
314 for rangepostmp
in privatedata
.rangepostmplist
:
315 if rangepostmp
[2] >= missing
:
318 def drawpoint(self
, privatedata
, sharedata
, graph
, point
):
319 for usename
, mask
, index
, axis
in privatedata
.rangepostmplist
:
321 if mask
& self
.mask_min
:
322 sharedata
.vrange
[index
][0] = axis
.convert(point
[usename
+ "min"])
323 if mask
& self
.mask_dmin
:
324 sharedata
.vrange
[index
][0] = axis
.convert(point
[usename
] - point
["d" + usename
+ "min"])
325 if mask
& self
.mask_d
:
326 sharedata
.vrange
[index
][0] = axis
.convert(point
[usename
] - point
["d" + usename
])
327 except (ArithmeticError, ValueError, TypeError):
328 sharedata
.vrange
[index
][0] = None
330 if mask
& self
.mask_max
:
331 sharedata
.vrange
[index
][1] = axis
.convert(point
[usename
+ "max"])
332 if mask
& self
.mask_dmax
:
333 sharedata
.vrange
[index
][1] = axis
.convert(point
[usename
] + point
["d" + usename
+ "max"])
334 if mask
& self
.mask_d
:
335 sharedata
.vrange
[index
][1] = axis
.convert(point
[usename
] + point
["d" + usename
])
336 except (ArithmeticError, ValueError, TypeError):
337 sharedata
.vrange
[index
][1] = None
339 # some range checks for data consistency
340 if (sharedata
.vrange
[index
][0] is not None and sharedata
.vrange
[index
][1] is not None and
341 sharedata
.vrange
[index
][0] > sharedata
.vrange
[index
][1] + self
.epsilon
):
342 raise ValueError("inverse range")
343 # disabled due to missing vpos access:
344 # if (sharedata.vrange[index][0] is not None and sharedata.vpos[index] is not None and
345 # sharedata.vrange[index][0] > sharedata.vpos[index] + self.epsilon):
346 # raise ValueError("negative minimum errorbar")
347 # if (sharedata.vrange[index][1] is not None and sharedata.vpos[index] is not None and
348 # sharedata.vrange[index][1] < sharedata.vpos[index] - self.epsilon):
349 # raise ValueError("negative maximum errorbar")
352 registerdefaultprovider(range(), range.providesdata
)
355 def _crosssymbol(c
, x_pt
, y_pt
, size_pt
, attrs
):
356 c
.draw(path
.path(path
.moveto_pt(x_pt
-0.5*size_pt
, y_pt
-0.5*size_pt
),
357 path
.lineto_pt(x_pt
+0.5*size_pt
, y_pt
+0.5*size_pt
),
358 path
.moveto_pt(x_pt
-0.5*size_pt
, y_pt
+0.5*size_pt
),
359 path
.lineto_pt(x_pt
+0.5*size_pt
, y_pt
-0.5*size_pt
)), attrs
)
361 def _plussymbol(c
, x_pt
, y_pt
, size_pt
, attrs
):
362 c
.draw(path
.path(path
.moveto_pt(x_pt
-0.707106781*size_pt
, y_pt
),
363 path
.lineto_pt(x_pt
+0.707106781*size_pt
, y_pt
),
364 path
.moveto_pt(x_pt
, y_pt
-0.707106781*size_pt
),
365 path
.lineto_pt(x_pt
, y_pt
+0.707106781*size_pt
)), attrs
)
367 def _squaresymbol(c
, x_pt
, y_pt
, size_pt
, attrs
):
368 c
.draw(path
.path(path
.moveto_pt(x_pt
-0.5*size_pt
, y_pt
-0.5*size_pt
),
369 path
.lineto_pt(x_pt
+0.5*size_pt
, y_pt
-0.5*size_pt
),
370 path
.lineto_pt(x_pt
+0.5*size_pt
, y_pt
+0.5*size_pt
),
371 path
.lineto_pt(x_pt
-0.5*size_pt
, y_pt
+0.5*size_pt
),
372 path
.closepath()), attrs
)
374 def _trianglesymbol(c
, x_pt
, y_pt
, size_pt
, attrs
):
375 c
.draw(path
.path(path
.moveto_pt(x_pt
-0.759835685*size_pt
, y_pt
-0.438691337*size_pt
),
376 path
.lineto_pt(x_pt
+0.759835685*size_pt
, y_pt
-0.438691337*size_pt
),
377 path
.lineto_pt(x_pt
, y_pt
+0.877382675*size_pt
),
378 path
.closepath()), attrs
)
380 def _circlesymbol(c
, x_pt
, y_pt
, size_pt
, attrs
):
381 c
.draw(path
.path(path
.arc_pt(x_pt
, y_pt
, 0.564189583*size_pt
, 0, 360),
382 path
.closepath()), attrs
)
384 def _diamondsymbol(c
, x_pt
, y_pt
, size_pt
, attrs
):
385 c
.draw(path
.path(path
.moveto_pt(x_pt
-0.537284965*size_pt
, y_pt
),
386 path
.lineto_pt(x_pt
, y_pt
-0.930604859*size_pt
),
387 path
.lineto_pt(x_pt
+0.537284965*size_pt
, y_pt
),
388 path
.lineto_pt(x_pt
, y_pt
+0.930604859*size_pt
),
389 path
.closepath()), attrs
)
392 class _styleneedingpointpos(_style
):
394 needsdata
= ["vposmissing"]
396 def columnnames(self
, privatedata
, sharedata
, graph
, columnnames
):
397 if len(sharedata
.vposmissing
):
398 raise ValueError("incomplete position information")
402 class symbol(_styleneedingpointpos
):
404 needsdata
= ["vpos", "vposmissing", "vposvalid"]
406 # "inject" the predefinied symbols into the class:
408 # Note, that statements like cross = _crosssymbol are
409 # invalid, since the would lead to unbound methods, but
410 # a single entry changeable list does the trick.
412 # Once we require Python 2.2+ we should use staticmethods
413 # to implement the default symbols inplace.
415 cross
= attr
.changelist([_crosssymbol
])
416 plus
= attr
.changelist([_plussymbol
])
417 square
= attr
.changelist([_squaresymbol
])
418 triangle
= attr
.changelist([_trianglesymbol
])
419 circle
= attr
.changelist([_circlesymbol
])
420 diamond
= attr
.changelist([_diamondsymbol
])
422 changecross
= attr
.changelist([_crosssymbol
, _plussymbol
, _squaresymbol
, _trianglesymbol
, _circlesymbol
, _diamondsymbol
])
423 changeplus
= attr
.changelist([_plussymbol
, _squaresymbol
, _trianglesymbol
, _circlesymbol
, _diamondsymbol
, _crosssymbol
])
424 changesquare
= attr
.changelist([_squaresymbol
, _trianglesymbol
, _circlesymbol
, _diamondsymbol
, _crosssymbol
, _plussymbol
])
425 changetriangle
= attr
.changelist([_trianglesymbol
, _circlesymbol
, _diamondsymbol
, _crosssymbol
, _plussymbol
, _squaresymbol
])
426 changecircle
= attr
.changelist([_circlesymbol
, _diamondsymbol
, _crosssymbol
, _plussymbol
, _squaresymbol
, _trianglesymbol
])
427 changediamond
= attr
.changelist([_diamondsymbol
, _crosssymbol
, _plussymbol
, _squaresymbol
, _trianglesymbol
, _circlesymbol
])
428 changesquaretwice
= attr
.changelist([_squaresymbol
, _squaresymbol
, _trianglesymbol
, _trianglesymbol
, _circlesymbol
, _circlesymbol
, _diamondsymbol
, _diamondsymbol
])
429 changetriangletwice
= attr
.changelist([_trianglesymbol
, _trianglesymbol
, _circlesymbol
, _circlesymbol
, _diamondsymbol
, _diamondsymbol
, _squaresymbol
, _squaresymbol
])
430 changecircletwice
= attr
.changelist([_circlesymbol
, _circlesymbol
, _diamondsymbol
, _diamondsymbol
, _squaresymbol
, _squaresymbol
, _trianglesymbol
, _trianglesymbol
])
431 changediamondtwice
= attr
.changelist([_diamondsymbol
, _diamondsymbol
, _squaresymbol
, _squaresymbol
, _trianglesymbol
, _trianglesymbol
, _circlesymbol
, _circlesymbol
])
433 changestrokedfilled
= attr
.changelist([deco
.stroked
, deco
.filled
])
434 changefilledstroked
= attr
.changelist([deco
.filled
, deco
.stroked
])
436 defaultsymbolattrs
= [deco
.stroked
]
438 def __init__(self
, symbol
=changecross
, size
=0.2*unit
.v_cm
, symbolattrs
=[]):
441 self
.symbolattrs
= symbolattrs
443 def selectstyle(self
, privatedata
, sharedata
, graph
, selectindex
, selecttotal
):
444 privatedata
.symbol
= attr
.selectattr(self
.symbol
, selectindex
, selecttotal
)
445 privatedata
.size_pt
= unit
.topt(attr
.selectattr(self
.size
, selectindex
, selecttotal
))
446 if self
.symbolattrs
is not None:
447 privatedata
.symbolattrs
= attr
.selectattrs(self
.defaultsymbolattrs
+ self
.symbolattrs
, selectindex
, selecttotal
)
449 privatedata
.symbolattrs
= None
451 def initdrawpoints(self
, privatedata
, sharedata
, graph
):
452 privatedata
.symbolcanvas
= canvas
.canvas()
454 def drawpoint(self
, privatedata
, sharedata
, graph
, point
):
455 if sharedata
.vposvalid
and privatedata
.symbolattrs
is not None:
456 x_pt
, y_pt
= graph
.vpos_pt(*sharedata
.vpos
)
457 privatedata
.symbol(privatedata
.symbolcanvas
, x_pt
, y_pt
, privatedata
.size_pt
, privatedata
.symbolattrs
)
459 def donedrawpoints(self
, privatedata
, sharedata
, graph
):
460 graph
.insert(privatedata
.symbolcanvas
)
462 def key_pt(self
, privatedata
, sharedata
, graph
, x_pt
, y_pt
, width_pt
, height_pt
):
463 if privatedata
.symbolattrs
is not None:
464 privatedata
.symbol(graph
, x_pt
+0.5*width_pt
, y_pt
+0.5*height_pt
, privatedata
.size_pt
, privatedata
.symbolattrs
)
467 class line(_styleneedingpointpos
):
469 needsdata
= ["vpos", "vposmissing", "vposavailable", "vposvalid"]
471 changelinestyle
= attr
.changelist([style
.linestyle
.solid
,
472 style
.linestyle
.dashed
,
473 style
.linestyle
.dotted
,
474 style
.linestyle
.dashdotted
])
476 defaultlineattrs
= [changelinestyle
]
478 def __init__(self
, lineattrs
=[]):
479 self
.lineattrs
= lineattrs
481 def selectstyle(self
, privatedata
, sharedata
, graph
, selectindex
, selecttotal
):
482 if self
.lineattrs
is not None:
483 privatedata
.lineattrs
= attr
.selectattrs(self
.defaultlineattrs
+ self
.lineattrs
, selectindex
, selecttotal
)
485 privatedata
.lineattrs
= None
487 def initdrawpoints(self
, privatedata
, sharedata
, graph
):
488 privatedata
.path
= path
.path()
489 privatedata
.linebasepoints
= []
490 privatedata
.lastvpos
= None
492 def addpointstopath(self
, privatedata
, sharedata
):
493 # add baselinepoints to privatedata.path
494 if len(privatedata
.linebasepoints
) > 1:
495 privatedata
.path
.append(path
.moveto_pt(*privatedata
.linebasepoints
[0]))
496 if len(privatedata
.linebasepoints
) > 2:
497 privatedata
.path
.append(path
.multilineto_pt(privatedata
.linebasepoints
[1:]))
499 privatedata
.path
.append(path
.lineto_pt(*privatedata
.linebasepoints
[1]))
500 privatedata
.linebasepoints
= []
502 def drawpoint(self
, privatedata
, sharedata
, graph
, point
):
503 # append linebasepoints
504 if sharedata
.vposavailable
:
505 if len(privatedata
.linebasepoints
):
506 # the last point was inside the graph
507 if sharedata
.vposvalid
: # shortcut for the common case
508 privatedata
.linebasepoints
.append(graph
.vpos_pt(*sharedata
.vpos
))
512 for vstart
, vend
in zip(privatedata
.lastvpos
, sharedata
.vpos
):
515 # 1 = vstart + (vend - vstart) * cut
517 newcut
= (1 - vstart
)/(vend
- vstart
)
518 except (ArithmeticError, TypeError):
521 # 0 = vstart + (vend - vstart) * cut
523 newcut
= - vstart
/(vend
- vstart
)
524 except (ArithmeticError, TypeError):
526 if newcut
is not None and newcut
< cut
:
530 for vstart
, vend
in zip(privatedata
.lastvpos
, sharedata
.vpos
):
531 cutvpos
.append(vstart
+ (vend
- vstart
) * cut
)
532 privatedata
.linebasepoints
.append(graph
.vpos_pt(*cutvpos
))
533 self
.addpointstopath(privatedata
, sharedata
)
535 # the last point was outside the graph
536 if privatedata
.lastvpos
is not None:
537 if sharedata
.vposvalid
:
540 for vstart
, vend
in zip(privatedata
.lastvpos
, sharedata
.vpos
):
543 # 1 = vstart + (vend - vstart) * cut
545 newcut
= (1 - vstart
)/(vend
- vstart
)
546 except (ArithmeticError, TypeError):
549 # 0 = vstart + (vend - vstart) * cut
551 newcut
= - vstart
/(vend
- vstart
)
552 except (ArithmeticError, TypeError):
554 if newcut
is not None and newcut
> cut
:
558 for vstart
, vend
in zip(privatedata
.lastvpos
, sharedata
.vpos
):
559 cutvpos
.append(vstart
+ (vend
- vstart
) * cut
)
560 privatedata
.linebasepoints
.append(graph
.vpos_pt(*cutvpos
))
561 privatedata
.linebasepoints
.append(graph
.vpos_pt(*sharedata
.vpos
))
563 # sometimes cut beginning and end
566 for vstart
, vend
in zip(privatedata
.lastvpos
, sharedata
.vpos
):
571 # 1 = vstart + (vend - vstart) * cutfrom
573 newcutfrom
= (1 - vstart
)/(vend
- vstart
)
574 except (ArithmeticError, TypeError):
579 # 0 = vstart + (vend - vstart) * cutfrom
581 newcutfrom
= - vstart
/(vend
- vstart
)
582 except (ArithmeticError, TypeError):
584 if newcutfrom
is not None and newcutfrom
> cutfrom
:
588 # 1 = vstart + (vend - vstart) * cutto
590 newcutto
= (1 - vstart
)/(vend
- vstart
)
591 except (ArithmeticError, TypeError):
594 # 0 = vstart + (vend - vstart) * cutto
596 newcutto
= - vstart
/(vend
- vstart
)
597 except (ArithmeticError, TypeError):
599 if newcutto
is not None and newcutto
< cutto
:
605 for vstart
, vend
in zip(privatedata
.lastvpos
, sharedata
.vpos
):
606 cutfromvpos
.append(vstart
+ (vend
- vstart
) * cutfrom
)
607 cuttovpos
.append(vstart
+ (vend
- vstart
) * cutto
)
608 privatedata
.linebasepoints
.append(graph
.vpos_pt(*cutfromvpos
))
609 privatedata
.linebasepoints
.append(graph
.vpos_pt(*cuttovpos
))
610 self
.addpointstopath(privatedata
, sharedata
)
611 privatedata
.lastvpos
= sharedata
.vpos
[:]
613 if len(privatedata
.linebasepoints
) > 1:
614 self
.addpointstopath(privatedata
, sharedata
)
615 privatedata
.lastvpos
= None
617 def donedrawpoints(self
, privatedata
, sharedata
, graph
):
618 if len(privatedata
.linebasepoints
) > 1:
619 self
.addpointstopath(privatedata
, sharedata
)
620 if privatedata
.lineattrs
is not None and len(privatedata
.path
):
621 graph
.stroke(privatedata
.path
, privatedata
.lineattrs
)
623 def key_pt(self
, privatedata
, sharedata
, graph
, x_pt
, y_pt
, width_pt
, height_pt
):
624 if privatedata
.lineattrs
is not None:
625 graph
.stroke(path
.line_pt(x_pt
, y_pt
+0.5*height_pt
, x_pt
+width_pt
, y_pt
+0.5*height_pt
), privatedata
.lineattrs
)
628 class errorbar(_style
):
630 needsdata
= ["vpos", "vposmissing", "vposavailable", "vposvalid", "vrange", "vrangeminmissing", "vrangemaxmissing"]
632 defaulterrorbarattrs
= []
634 def __init__(self
, size
=0.1*unit
.v_cm
,
638 self
.errorbarattrs
= errorbarattrs
639 self
.epsilon
= epsilon
641 def columnnames(self
, privatedata
, sharedata
, graph
, columnnames
):
642 for i
in sharedata
.vposmissing
:
643 if i
in sharedata
.vrangeminmissing
and i
in sharedata
.vrangemaxmissing
:
644 raise ValueError("position and range for a graph dimension missing")
647 def selectstyle(self
, privatedata
, sharedata
, graph
, selectindex
, selecttotal
):
648 privatedata
.errorsize_pt
= unit
.topt(attr
.selectattr(self
.size
, selectindex
, selecttotal
))
649 privatedata
.errorbarattrs
= attr
.selectattrs(self
.defaulterrorbarattrs
+ self
.errorbarattrs
, selectindex
, selecttotal
)
651 def initdrawpoints(self
, privatedata
, sharedata
, graph
):
652 if privatedata
.errorbarattrs
is not None:
653 privatedata
.errorbarcanvas
= canvas
.canvas()
654 privatedata
.errorbarcanvas
.set(privatedata
.errorbarattrs
)
655 privatedata
.dimensionlist
= list(xrange(len(sharedata
.vpos
)))
657 def drawpoint(self
, privatedata
, sharedata
, graph
, point
):
658 if privatedata
.errorbarattrs
is not None:
659 for i
in privatedata
.dimensionlist
:
660 for j
in privatedata
.dimensionlist
:
662 (sharedata
.vpos
[j
] is None or
663 sharedata
.vpos
[j
] < -self
.epsilon
or
664 sharedata
.vpos
[j
] > 1+self
.epsilon
)):
667 if ((sharedata
.vrange
[i
][0] is None and sharedata
.vpos
[i
] is None) or
668 (sharedata
.vrange
[i
][1] is None and sharedata
.vpos
[i
] is None) or
669 (sharedata
.vrange
[i
][0] is None and sharedata
.vrange
[i
][1] is None)):
671 vminpos
= sharedata
.vpos
[:]
672 if sharedata
.vrange
[i
][0] is not None:
673 vminpos
[i
] = sharedata
.vrange
[i
][0]
677 if vminpos
[i
] > 1+self
.epsilon
:
679 if vminpos
[i
] < -self
.epsilon
:
682 vmaxpos
= sharedata
.vpos
[:]
683 if sharedata
.vrange
[i
][1] is not None:
684 vmaxpos
[i
] = sharedata
.vrange
[i
][1]
688 if vmaxpos
[i
] < -self
.epsilon
:
690 if vmaxpos
[i
] > 1+self
.epsilon
:
693 privatedata
.errorbarcanvas
.stroke(graph
.vgeodesic(*(vminpos
+ vmaxpos
)))
694 for j
in privatedata
.dimensionlist
:
697 privatedata
.errorbarcanvas
.stroke(graph
.vcap_pt(j
, privatedata
.errorsize_pt
, *vminpos
))
699 privatedata
.errorbarcanvas
.stroke(graph
.vcap_pt(j
, privatedata
.errorsize_pt
, *vmaxpos
))
701 def donedrawpoints(self
, privatedata
, sharedata
, graph
):
702 if privatedata
.errorbarattrs
is not None:
703 graph
.insert(privatedata
.errorbarcanvas
)
706 class text(_styleneedingpointpos
):
708 needsdata
= ["vpos", "vposmissing", "vposvalid"]
710 defaulttextattrs
= [textmodule
.halign
.center
, textmodule
.vshift
.mathaxis
]
712 def __init__(self
, textname
="text", textdx
=0*unit
.v_cm
, textdy
=0.3*unit
.v_cm
, textattrs
=[]):
713 self
.textname
= textname
716 self
.textattrs
= textattrs
718 def columnnames(self
, privatedata
, sharedata
, graph
, columnnames
):
719 if self
.textname
not in columnnames
:
720 raise ValueError("column '%s' missing" % self
.textname
)
721 return [self
.textname
] + _styleneedingpointpos
.columnnames(self
, privatedata
, sharedata
, graph
, columnnames
)
723 def selectstyle(self
, privatedata
, sharedata
, graph
, selectindex
, selecttotal
):
724 if self
.textattrs
is not None:
725 privatedata
.textattrs
= attr
.selectattrs(self
.defaulttextattrs
+ self
.textattrs
, selectindex
, selecttotal
)
727 privatedata
.textattrs
= None
729 def initdrawpoints(self
, privatedata
, sharedata
, grap
):
730 privatedata
.textdx_pt
= unit
.topt(self
.textdx
)
731 privatedata
.textdy_pt
= unit
.topt(self
.textdy
)
733 def drawpoint(self
, privatedata
, sharedata
, graph
, point
):
734 if privatedata
.textattrs
is not None and sharedata
.vposvalid
:
735 x_pt
, y_pt
= graph
.vpos_pt(*sharedata
.vpos
)
737 text
= str(point
[self
.textname
])
741 graph
.text_pt(x_pt
+ privatedata
.textdx_pt
, y_pt
+ privatedata
.textdy_pt
, text
, privatedata
.textattrs
)
743 def key_pt(self
, privatedata
, sharedata
, graph
, x_pt
, y_pt
, width_pt
, height_pt
):
744 raise RuntimeError("Style currently doesn't provide a graph key")
747 class arrow(_styleneedingpointpos
):
749 needsdata
= ["vpos", "vposmissing", "vposvalid"]
751 defaultlineattrs
= []
752 defaultarrowattrs
= []
754 def __init__(self
, linelength
=0.25*unit
.v_cm
, arrowsize
=0.15*unit
.v_cm
, lineattrs
=[], arrowattrs
=[], epsilon
=1e-5):
755 self
.linelength
= linelength
756 self
.arrowsize
= arrowsize
757 self
.lineattrs
= lineattrs
758 self
.arrowattrs
= arrowattrs
759 self
.epsilon
= epsilon
761 def columnnames(self
, privatedata
, sharedata
, graph
, columnnames
):
762 if len(graph
.axesnames
) != 2:
763 raise ValueError("arrow style restricted on two-dimensional graphs")
764 if "size" not in columnnames
:
765 raise ValueError("size missing")
766 if "angle" not in columnnames
:
767 raise ValueError("angle missing")
768 return ["size", "angle"] + _styleneedingpointpos
.columnnames(self
, privatedata
, sharedata
, graph
, columnnames
)
770 def selectstyle(self
, privatedata
, sharedata
, graph
, selectindex
, selecttotal
):
771 if self
.lineattrs
is not None:
772 privatedata
.lineattrs
= attr
.selectattrs(self
.defaultlineattrs
+ self
.lineattrs
, selectindex
, selecttotal
)
774 privatedata
.lineattrs
= None
775 if self
.arrowattrs
is not None:
776 privatedata
.arrowattrs
= attr
.selectattrs(self
.defaultarrowattrs
+ self
.arrowattrs
, selectindex
, selecttotal
)
778 privatedata
.arrowattrs
= None
780 def initdrawpoints(self
, privatedata
, sharedata
, graph
):
781 privatedata
.arrowcanvas
= canvas
.canvas()
783 def drawpoint(self
, privatedata
, sharedata
, graph
, point
):
784 if privatedata
.lineattrs
is not None and privatedata
.arrowattrs
is not None and sharedata
.vposvalid
:
785 linelength_pt
= unit
.topt(self
.linelength
)
786 x_pt
, y_pt
= graph
.vpos_pt(*sharedata
.vpos
)
788 angle
= point
["angle"] + 0.0
789 size
= point
["size"] + 0.0
793 if point
["size"] > self
.epsilon
:
794 dx
= math
.cos(angle
*math
.pi
/180)
795 dy
= math
.sin(angle
*math
.pi
/180)
796 x1
= x_pt
-0.5*dx
*linelength_pt
*size
797 y1
= y_pt
-0.5*dy
*linelength_pt
*size
798 x2
= x_pt
+0.5*dx
*linelength_pt
*size
799 y2
= y_pt
+0.5*dy
*linelength_pt
*size
800 privatedata
.arrowcanvas
.stroke(path
.line_pt(x1
, y1
, x2
, y2
), privatedata
.lineattrs
+
801 [deco
.earrow(privatedata
.arrowattrs
, size
=self
.arrowsize
*size
)])
803 def donedrawpoints(self
, privatedata
, sharedata
, graph
):
804 graph
.insert(privatedata
.arrowcanvas
)
806 def key_pt(self
, privatedata
, sharedata
, graph
, x_pt
, y_pt
, width_pt
, height_pt
):
807 raise RuntimeError("Style currently doesn't provide a graph key")
812 needsdata
= ["vrange", "vrangeminmissing", "vrangemaxmissing"]
814 def __init__(self
, palette
=color
.palette
.Grey
):
815 self
.palette
= palette
817 def columnnames(self
, privatedata
, sharedata
, graph
, columnnames
):
818 if len(graph
.axesnames
) != 2:
819 raise TypeError("arrow style restricted on two-dimensional graphs")
820 if "color" not in columnnames
:
821 raise ValueError("color missing")
822 if len(sharedata
.vrangeminmissing
) + len(sharedata
.vrangemaxmissing
):
823 raise ValueError("incomplete range")
826 def initdrawpoints(self
, privatedata
, sharedata
, graph
):
827 privatedata
.rectcanvas
= graph
.insert(canvas
.canvas())
828 privatedata
.lastcolorvalue
= None
830 def drawpoint(self
, privatedata
, sharedata
, graph
, point
):
831 xvmin
= sharedata
.vrange
[0][0]
832 xvmax
= sharedata
.vrange
[0][1]
833 yvmin
= sharedata
.vrange
[1][0]
834 yvmax
= sharedata
.vrange
[1][1]
835 if (xvmin
is not None and xvmin
< 1 and
836 xvmax
is not None and xvmax
> 0 and
837 yvmin
is not None and yvmin
< 1 and
838 yvmax
is not None and yvmax
> 0):
847 p
= graph
.vgeodesic(xvmin
, yvmin
, xvmax
, yvmin
)
848 p
.append(graph
.vgeodesic_el(xvmax
, yvmin
, xvmax
, yvmax
))
849 p
.append(graph
.vgeodesic_el(xvmax
, yvmax
, xvmin
, yvmax
))
850 p
.append(graph
.vgeodesic_el(xvmin
, yvmax
, xvmin
, yvmin
))
851 p
.append(path
.closepath())
852 colorvalue
= point
["color"]
854 if colorvalue
!= privatedata
.lastcolorvalue
:
855 privatedata
.rectcanvas
.set([self
.palette
.getcolor(colorvalue
)])
859 privatedata
.rectcanvas
.fill(p
)
861 def key_pt(self
, privatedata
, sharedata
, graph
, x_pt
, y_pt
, width_pt
, height_pt
):
862 raise RuntimeError("Style currently doesn't provide a graph key")
865 class histogram(_style
):
867 needsdata
= ["vpos", "vposmissing", "vrange", "vrangeminmissing", "vrangemaxmissing"]
869 defaultlineattrs
= [deco
.stroked
]
870 defaultfrompathattrs
= []
872 def __init__(self
, lineattrs
=[], steps
=0, fromvalue
=0, frompathattrs
=[], fillable
=0,
873 autohistogramaxisindex
=0, autohistogrampointpos
=0.5, epsilon
=1e-10):
874 self
.lineattrs
= lineattrs
876 self
.fromvalue
= fromvalue
877 self
.frompathattrs
= frompathattrs
878 self
.fillable
= fillable
# TODO: fillable paths might not properly be closed by straight lines on curved graph geometries
879 self
.autohistogramaxisindex
= autohistogramaxisindex
880 self
.autohistogrampointpos
= autohistogrampointpos
881 self
.epsilon
= epsilon
883 def columnnames(self
, privatedata
, sharedata
, graph
, columnnames
):
884 if len(graph
.axesnames
) != 2:
885 raise TypeError("histogram style restricted on two-dimensional graphs")
886 privatedata
.rangeaxisindex
= None
887 for i
in builtinrange(len(graph
.axesnames
)):
888 if i
in sharedata
.vrangeminmissing
or i
in sharedata
.vrangemaxmissing
:
889 if i
in sharedata
.vposmissing
:
890 raise ValueError("pos and range missing")
892 if privatedata
.rangeaxisindex
is not None:
893 raise ValueError("multiple ranges")
894 privatedata
.rangeaxisindex
= i
895 if privatedata
.rangeaxisindex
is None:
896 privatedata
.rangeaxisindex
= self
.autohistogramaxisindex
897 privatedata
.autohistogram
= 1
899 privatedata
.autohistogram
= 0
902 def adjustaxis(self
, privatedata
, sharedata
, graph
, columnname
, data
):
903 if privatedata
.autohistogram
and columnname
== sharedata
.poscolumnnames
[privatedata
.rangeaxisindex
]:
905 raise ValueError("several data points needed for automatic histogram width calculation")
907 delta
= data
[1] - data
[0]
908 min = data
[0] - self
.autohistogrampointpos
* delta
909 max = data
[-1] + (1-self
.autohistogrampointpos
) * delta
910 graph
.axes
[columnname
].adjustaxis([min, max])
912 def selectstyle(self
, privatedata
, sharedata
, graph
, selectindex
, selecttotal
):
913 if self
.lineattrs
is not None:
914 privatedata
.lineattrs
= attr
.selectattrs(self
.defaultlineattrs
+ self
.lineattrs
, selectindex
, selecttotal
)
916 privatedata
.lineattrs
= None
918 def vmoveto(self
, privatedata
, sharedata
, graph
, vpos
, vvalue
):
919 if -self
.epsilon
< vpos
< 1+self
.epsilon
and -self
.epsilon
< vvalue
< 1+self
.epsilon
:
920 if privatedata
.rangeaxisindex
:
921 privatedata
.path
.append(path
.moveto_pt(*graph
.vpos_pt(vvalue
, vpos
)))
923 privatedata
.path
.append(path
.moveto_pt(*graph
.vpos_pt(vpos
, vvalue
)))
925 def vposline(self
, privatedata
, sharedata
, graph
, vpos
, vvalue1
, vvalue2
):
926 if -self
.epsilon
< vpos
< 1+self
.epsilon
:
941 if abs(vvalue1cut
+ vvalue2cut
) <= 1:
942 if vvalue1cut
and not self
.fillable
:
943 if privatedata
.rangeaxisindex
:
944 privatedata
.path
.append(path
.moveto_pt(*graph
.vpos_pt(vvalue1
, vpos
)))
946 privatedata
.path
.append(path
.moveto_pt(*graph
.vpos_pt(vpos
, vvalue1
)))
947 if privatedata
.rangeaxisindex
:
948 privatedata
.path
.append(graph
.vgeodesic_el(vvalue1
, vpos
, vvalue2
, vpos
))
950 privatedata
.path
.append(graph
.vgeodesic_el(vpos
, vvalue1
, vpos
, vvalue2
))
952 def vvalueline(self
, privatedata
, sharedata
, graph
, vvalue
, vpos1
, vpos2
):
954 if vvalue
< -self
.epsilon
:
956 warnings
.warn("cut at graph boundary adds artificial lines to fillable step histogram path")
957 if vvalue
> 1+self
.epsilon
:
959 warnings
.warn("cut at graph boundary adds artificial lines to fillable step histogram path")
960 if self
.fillable
or (-self
.epsilon
< vvalue
< 1+self
.epsilon
):
975 if abs(vpos1cut
+ vpos2cut
) <= 1:
978 if privatedata
.rangeaxisindex
:
979 privatedata
.path
.append(path
.moveto_pt(*graph
.vpos_pt(privatedata
.vfromvalue
, vpos1
)))
980 privatedata
.path
.append(graph
.vgeodesic_el(privatedata
.vfromvalue
, vpos1
, vvalue
, vpos1
))
982 privatedata
.path
.append(path
.moveto_pt(*graph
.vpos_pt(vpos1
, privatedata
.vfromvalue
)))
983 privatedata
.path
.append(graph
.vgeodesic_el(vpos1
, privatedata
.vfromvalue
, vpos1
, vvalue
))
985 if privatedata
.rangeaxisindex
:
986 privatedata
.path
.append(path
.moveto_pt(*graph
.vpos_pt(vvalue
, vpos1
)))
988 privatedata
.path
.append(path
.moveto_pt(*graph
.vpos_pt(vpos1
, vvalue
)))
989 if privatedata
.rangeaxisindex
:
990 privatedata
.path
.append(graph
.vgeodesic_el(vvalue
, vpos1
, vvalue
, vpos2
))
992 privatedata
.path
.append(graph
.vgeodesic_el(vpos1
, vvalue
, vpos2
, vvalue
))
993 if self
.fillable
and vpos2cut
:
994 warnings
.warn("cut at graph boundary adds artificial lines to fillable step histogram path")
995 if privatedata
.rangeaxisindex
:
996 privatedata
.path
.append(graph
.vgeodesic_el(vvalue
, vpos2
, privatedata
.vfromvalue
, vpos2
))
998 privatedata
.path
.append(graph
.vgeodesic_el(vpos2
, vvalue
, vpos2
, privatedata
.vfromvalue
))
1000 def drawvalue(self
, privatedata
, sharedata
, graph
, vmin
, vmax
, vvalue
):
1001 currentvalid
= vmin
is not None and vmax
is not None and vvalue
is not None
1002 if self
.fillable
and not self
.steps
:
1003 if not currentvalid
:
1006 if vmin
< -self
.epsilon
:
1009 elif vmin
> 1+self
.epsilon
:
1013 if vmax
< -self
.epsilon
:
1016 if vmax
> 1+self
.epsilon
:
1020 if vvalue
< -self
.epsilon
:
1023 if vvalue
> 1+self
.epsilon
:
1027 if abs(vmincut
) + abs(vmaxcut
) + abs(vvaluecut
) + abs(privatedata
.vfromvaluecut
) > 1:
1028 if abs(vmincut
+ vmaxcut
) > 1 or abs(vvaluecut
+privatedata
.vfromvaluecut
) > 1:
1031 warnings
.warn("multiple cuts at graph boundary add artificial lines to fillable rectangle histogram path")
1034 if privatedata
.rangeaxisindex
:
1035 privatedata
.path
.append(path
.moveto_pt(*graph
.vpos_pt(privatedata
.vfromvalue
, vmin
)))
1036 privatedata
.path
.append(graph
.vgeodesic_el(privatedata
.vfromvalue
, vmin
, privatedata
.vfromvalue
, vmax
))
1037 privatedata
.path
.append(graph
.vgeodesic_el(privatedata
.vfromvalue
, vmax
, vvalue
, vmax
))
1038 privatedata
.path
.append(graph
.vgeodesic_el(vvalue
, vmax
, vvalue
, vmin
))
1040 privatedata
.path
.append(path
.moveto_pt(*graph
.vpos_pt(vmin
, privatedata
.vfromvalue
)))
1041 privatedata
.path
.append(graph
.vgeodesic_el(vmin
, privatedata
.vfromvalue
, vmax
, privatedata
.vfromvalue
))
1042 privatedata
.path
.append(graph
.vgeodesic_el(vmax
, privatedata
.vfromvalue
, vmax
, vvalue
))
1043 privatedata
.path
.append(graph
.vgeodesic_el(vmax
, vvalue
, vmin
, vvalue
))
1046 if privatedata
.rangeaxisindex
:
1047 privatedata
.path
.append(path
.moveto_pt(*graph
.vpos_pt(vvalue
, vmax
)))
1048 privatedata
.path
.append(graph
.vgeodesic_el(vvalue
, vmax
, vvalue
, vmin
))
1049 privatedata
.path
.append(graph
.vgeodesic_el(vvalue
, vmin
, privatedata
.vfromvalue
, vmin
))
1050 privatedata
.path
.append(graph
.vgeodesic_el(privatedata
.vfromvalue
, vmin
, privatedata
.vfromvalue
, vmax
))
1052 privatedata
.path
.append(path
.moveto_pt(*graph
.vpos_pt(vmax
, vvalue
)))
1053 privatedata
.path
.append(graph
.vgeodesic_el(vmax
, vvalue
, vmin
, vvalue
))
1054 privatedata
.path
.append(graph
.vgeodesic_el(vmin
, vvalue
, vmin
, privatedata
.vfromvalue
))
1055 privatedata
.path
.append(graph
.vgeodesic_el(vmin
, privatedata
.vfromvalue
, vmax
, privatedata
.vfromvalue
))
1056 elif privatedata
.vfromvaluecut
:
1058 if privatedata
.rangeaxisindex
:
1059 privatedata
.path
.append(path
.moveto_pt(*graph
.vpos_pt(privatedata
.vfromvalue
, vmax
)))
1060 privatedata
.path
.append(graph
.vgeodesic_el(privatedata
.vfromvalue
, vmax
, vvalue
, vmax
))
1061 privatedata
.path
.append(graph
.vgeodesic_el(vvalue
, vmax
, vvalue
, vmin
))
1062 privatedata
.path
.append(graph
.vgeodesic_el(vvalue
, vmin
, privatedata
.vfromvalue
, vmin
))
1064 privatedata
.path
.append(path
.moveto_pt(*graph
.vpos_pt(vmax
, privatedata
.vfromvalue
)))
1065 privatedata
.path
.append(graph
.vgeodesic_el(vmax
, privatedata
.vfromvalue
, vmax
, vvalue
))
1066 privatedata
.path
.append(graph
.vgeodesic_el(vmax
, vvalue
, vmin
, vvalue
))
1067 privatedata
.path
.append(graph
.vgeodesic_el(vmin
, vvalue
, vmin
, privatedata
.vfromvalue
))
1070 if privatedata
.rangeaxisindex
:
1071 privatedata
.path
.append(path
.moveto_pt(*graph
.vpos_pt(vvalue
, vmin
)))
1072 privatedata
.path
.append(graph
.vgeodesic_el(vvalue
, vmin
, privatedata
.vfromvalue
, vmin
))
1073 privatedata
.path
.append(graph
.vgeodesic_el(privatedata
.vfromvalue
, vmin
, privatedata
.vfromvalue
, vmax
))
1074 privatedata
.path
.append(graph
.vgeodesic_el(privatedata
.vfromvalue
, vmax
, vvalue
, vmax
))
1076 privatedata
.path
.append(path
.moveto_pt(*graph
.vpos_pt(vmin
, vvalue
)))
1077 privatedata
.path
.append(graph
.vgeodesic_el(vmin
, vvalue
, vmin
, privatedata
.vfromvalue
))
1078 privatedata
.path
.append(graph
.vgeodesic_el(vmin
, privatedata
.vfromvalue
, vmax
, privatedata
.vfromvalue
))
1079 privatedata
.path
.append(graph
.vgeodesic_el(vmax
, privatedata
.vfromvalue
, vmax
, vvalue
))
1081 if privatedata
.rangeaxisindex
:
1082 privatedata
.path
.append(path
.moveto_pt(*graph
.vpos_pt(privatedata
.vfromvalue
, vmin
)))
1083 privatedata
.path
.append(graph
.vgeodesic_el(privatedata
.vfromvalue
, vmin
, privatedata
.vfromvalue
, vmax
))
1084 privatedata
.path
.append(graph
.vgeodesic_el(privatedata
.vfromvalue
, vmax
, vvalue
, vmax
))
1085 privatedata
.path
.append(graph
.vgeodesic_el(vvalue
, vmax
, vvalue
, vmin
))
1086 privatedata
.path
.append(graph
.vgeodesic_el(vvalue
, vmin
, privatedata
.vfromvalue
, vmin
))
1087 privatedata
.path
.append(path
.closepath())
1089 privatedata
.path
.append(path
.moveto_pt(*graph
.vpos_pt(vmin
, privatedata
.vfromvalue
)))
1090 privatedata
.path
.append(graph
.vgeodesic_el(vmin
, privatedata
.vfromvalue
, vmax
, privatedata
.vfromvalue
))
1091 privatedata
.path
.append(graph
.vgeodesic_el(vmax
, privatedata
.vfromvalue
, vmax
, vvalue
))
1092 privatedata
.path
.append(graph
.vgeodesic_el(vmax
, vvalue
, vmin
, vvalue
))
1093 privatedata
.path
.append(graph
.vgeodesic_el(vmin
, vvalue
, vmin
, privatedata
.vfromvalue
))
1094 privatedata
.path
.append(path
.closepath())
1097 gap
= abs(vmin
- privatedata
.lastvmax
) > self
.epsilon
1098 except (ArithmeticError, ValueError, TypeError):
1100 if (privatedata
.lastvvalue
is not None and
1104 (privatedata
.lastvvalue
-privatedata
.vfromvalue
)*(vvalue
-privatedata
.vfromvalue
) < 0)):
1105 self
.vposline(privatedata
, sharedata
, graph
,
1106 vmin
, privatedata
.lastvvalue
, vvalue
)
1108 if (privatedata
.lastvvalue
is not None and
1109 (not currentvalid
or
1110 privatedata
.lastvvalue
> vvalue
or
1112 self
.vposline(privatedata
, sharedata
, graph
,
1113 privatedata
.lastvmax
, privatedata
.lastvvalue
, privatedata
.vfromvalue
)
1115 self
.vmoveto(privatedata
, sharedata
, graph
,
1117 if (currentvalid
and
1118 (privatedata
.lastvvalue
is None or
1119 privatedata
.lastvvalue
< vvalue
or
1121 self
.vmoveto(privatedata
, sharedata
, graph
,
1122 vmin
, privatedata
.vfromvalue
)
1123 self
.vposline(privatedata
, sharedata
, graph
,
1124 vmin
, privatedata
.vfromvalue
, vvalue
)
1126 self
.vvalueline(privatedata
, sharedata
, graph
,
1128 privatedata
.lastvvalue
= vvalue
1129 privatedata
.lastvmax
= vmax
1131 privatedata
.lastvvalue
= privatedata
.lastvmax
= None
1133 def initdrawpoints(self
, privatedata
, sharedata
, graph
):
1134 privatedata
.path
= path
.path()
1135 privatedata
.lastvvalue
= privatedata
.lastvmax
= None
1136 privatedata
.vcurrentpoint
= None
1137 privatedata
.count
= 0
1138 if self
.fromvalue
is not None:
1139 valueaxisname
= sharedata
.poscolumnnames
[1-privatedata
.rangeaxisindex
]
1140 privatedata
.vfromvalue
= graph
.axes
[valueaxisname
].convert(self
.fromvalue
)
1141 privatedata
.vfromvaluecut
= 0
1142 if privatedata
.vfromvalue
< 0:
1143 privatedata
.vfromvalue
= 0
1144 privatedata
.vfromvaluecut
= -1
1145 if privatedata
.vfromvalue
> 1:
1146 privatedata
.vfromvalue
= 1
1147 privatedata
.vfromvaluecut
= 1
1148 if self
.frompathattrs
is not None:
1149 graph
.stroke(graph
.axes
[valueaxisname
].vgridpath(privatedata
.vfromvalue
),
1150 self
.defaultfrompathattrs
+ self
.frompathattrs
)
1152 privatedata
.vfromvalue
= 0
1154 def drawpoint(self
, privatedata
, sharedata
, graph
, point
):
1155 if privatedata
.autohistogram
:
1156 # automatic range handling
1157 privatedata
.count
+= 1
1158 if privatedata
.count
== 2:
1159 if privatedata
.rangeaxisindex
:
1160 privatedata
.vrange
= sharedata
.vpos
[1] - privatedata
.lastvpos
[1]
1161 self
.drawvalue(privatedata
, sharedata
, graph
,
1162 privatedata
.lastvpos
[1] - self
.autohistogrampointpos
*privatedata
.vrange
,
1163 privatedata
.lastvpos
[1] + (1-self
.autohistogrampointpos
)*privatedata
.vrange
,
1164 privatedata
.lastvpos
[0])
1166 privatedata
.vrange
= sharedata
.vpos
[0] - privatedata
.lastvpos
[0]
1167 self
.drawvalue(privatedata
, sharedata
, graph
,
1168 privatedata
.lastvpos
[0] - self
.autohistogrampointpos
*privatedata
.vrange
,
1169 privatedata
.lastvpos
[0] + (1-self
.autohistogrampointpos
)*privatedata
.vrange
,
1170 privatedata
.lastvpos
[1])
1171 elif privatedata
.count
> 2:
1172 if privatedata
.rangeaxisindex
:
1173 vrange
= sharedata
.vpos
[1] - privatedata
.lastvpos
[1]
1175 vrange
= sharedata
.vpos
[0] - privatedata
.lastvpos
[0]
1176 if abs(privatedata
.vrange
- vrange
) > self
.epsilon
:
1177 raise ValueError("equal steps (in graph coordinates) needed for automatic width calculation")
1178 if privatedata
.count
> 1:
1179 if privatedata
.rangeaxisindex
:
1180 self
.drawvalue(privatedata
, sharedata
, graph
,
1181 sharedata
.vpos
[1] - self
.autohistogrampointpos
*privatedata
.vrange
,
1182 sharedata
.vpos
[1] + (1-self
.autohistogrampointpos
)*privatedata
.vrange
,
1185 self
.drawvalue(privatedata
, sharedata
, graph
,
1186 sharedata
.vpos
[0] - self
.autohistogrampointpos
*privatedata
.vrange
,
1187 sharedata
.vpos
[0] + (1-self
.autohistogrampointpos
)*privatedata
.vrange
,
1189 privatedata
.lastvpos
= sharedata
.vpos
[:]
1191 if privatedata
.rangeaxisindex
:
1192 self
.drawvalue(privatedata
, sharedata
, graph
,
1193 sharedata
.vrange
[1][0], sharedata
.vrange
[1][1], sharedata
.vpos
[0])
1195 self
.drawvalue(privatedata
, sharedata
, graph
,
1196 sharedata
.vrange
[0][0], sharedata
.vrange
[0][1], sharedata
.vpos
[1])
1198 def donedrawpoints(self
, privatedata
, sharedata
, graph
):
1199 self
.drawvalue(privatedata
, sharedata
, graph
, None, None, None)
1200 if privatedata
.lineattrs
is not None and len(privatedata
.path
):
1201 graph
.draw(privatedata
.path
, privatedata
.lineattrs
)
1204 class barpos(_style
):
1206 providesdata
= ["vpos", "vposmissing", "vposavailable", "vposvalid", "vbarrange", "barposcolumnnames", "barvalueindex", "lastbarvalue", "stackedbar", "stackedbardraw"]
1208 defaultfrompathattrs
= []
1210 def __init__(self
, fromvalue
=None, frompathattrs
=[], subindex
=0, subnames
=None, epsilon
=1e-10):
1211 # NOTE subindex is a perspective for higher dimensional plots
1212 # (just ignore it for the moment -- we don't even need to document about it)
1213 self
.fromvalue
= fromvalue
1214 self
.frompathattrs
= frompathattrs
1215 self
.subindex
= subindex
1216 self
.subnames
= subnames
1217 self
.epsilon
= epsilon
1219 def columnnames(self
, privatedata
, sharedata
, graph
, columnnames
):
1220 sharedata
.barposcolumnnames
= []
1221 sharedata
.barvalueindex
= None
1222 for dimension
, axisnames
in enumerate(graph
.axesnames
):
1224 for axisname
in axisnames
:
1225 if axisname
in columnnames
:
1226 if sharedata
.barvalueindex
is not None:
1227 raise ValueError("multiple values")
1228 sharedata
.barvalueindex
= dimension
1229 sharedata
.barposcolumnnames
.append(axisname
)
1231 if (axisname
+ "name") in columnnames
:
1232 sharedata
.barposcolumnnames
.append(axisname
+ "name")
1235 raise ValueError("multiple names and value")
1237 raise ValueError("value/name missing")
1238 if sharedata
.barvalueindex
is None:
1239 raise ValueError("missing value")
1240 if self
.subindex
>= sharedata
.barvalueindex
:
1241 privatedata
.barpossubindex
= self
.subindex
+ 1
1243 privatedata
.barpossubindex
= self
.subindex
1244 sharedata
.vposmissing
= []
1245 return sharedata
.barposcolumnnames
1247 def selectstyle(self
, privatedata
, sharedata
, graph
, selectindex
, selecttotal
):
1248 if selecttotal
== 1:
1249 if self
.subnames
is not None:
1250 raise ValueError("subnames set for single-bar data")
1251 privatedata
.barpossubname
= None
1253 if self
.subnames
is not None:
1254 privatedata
.barpossubname
= self
.subnames
[selectindex
]
1256 privatedata
.barpossubname
= selectindex
1258 def adjustaxis(self
, privatedata
, sharedata
, graph
, columnname
, data
):
1260 i
= sharedata
.barposcolumnnames
.index(columnname
)
1264 if i
== sharedata
.barvalueindex
:
1265 if self
.fromvalue
is not None:
1266 graph
.axes
[sharedata
.barposcolumnnames
[i
]].adjustaxis([self
.fromvalue
])
1267 graph
.axes
[sharedata
.barposcolumnnames
[i
]].adjustaxis(data
)
1269 if i
== privatedata
.barpossubindex
and privatedata
.barpossubname
is not None:
1270 graph
.axes
[sharedata
.barposcolumnnames
[i
][:-4]].adjustaxis([(x
, (privatedata
.barpossubname
, 0)) for x
in data
])
1271 graph
.axes
[sharedata
.barposcolumnnames
[i
][:-4]].adjustaxis([(x
, (privatedata
.barpossubname
, 1)) for x
in data
])
1273 graph
.axes
[sharedata
.barposcolumnnames
[i
][:-4]].adjustaxis([(x
, 0) for x
in data
])
1274 graph
.axes
[sharedata
.barposcolumnnames
[i
][:-4]].adjustaxis([(x
, 1) for x
in data
])
1276 def initdrawpoints(self
, privatedata
, sharedata
, graph
):
1277 sharedata
.vpos
= [None]*(len(sharedata
.barposcolumnnames
))
1278 sharedata
.vbarrange
= [[None for i
in xrange(2)] for x
in sharedata
.barposcolumnnames
]
1279 sharedata
.stackedbar
= sharedata
.stackedbardraw
= 0
1281 if self
.fromvalue
is not None:
1282 privatedata
.vfromvalue
= graph
.axes
[sharedata
.barposcolumnnames
[sharedata
.barvalueindex
][0]].convert(self
.fromvalue
)
1283 if privatedata
.vfromvalue
< 0:
1284 privatedata
.vfromvalue
= 0
1285 if privatedata
.vfromvalue
> 1:
1286 privatedata
.vfromvalue
= 1
1287 if self
.frompathattrs
is not None:
1289 graph
.stroke(graph
.axes
[sharedata
.barposcolumnnames
[sharedata
.barvalueindex
][0]].vgridpath(privatedata
.vfromvalue
),
1290 self
.defaultfrompathattrs
+ self
.frompathattrs
)
1292 privatedata
.vfromvalue
= 0
1294 def drawpoint(self
, privatedata
, sharedata
, graph
, point
):
1295 sharedata
.vposavailable
= sharedata
.vposvalid
= 1
1296 for i
, barname
in enumerate(sharedata
.barposcolumnnames
):
1297 if i
== sharedata
.barvalueindex
:
1298 sharedata
.vbarrange
[i
][0] = privatedata
.vfromvalue
1299 sharedata
.lastbarvalue
= point
[barname
]
1301 sharedata
.vpos
[i
] = sharedata
.vbarrange
[i
][1] = graph
.axes
[barname
].convert(sharedata
.lastbarvalue
)
1302 except (ArithmeticError, ValueError, TypeError):
1303 sharedata
.vpos
[i
] = sharedata
.vbarrange
[i
][1] = None
1307 if i
== privatedata
.barpossubindex
and privatedata
.barpossubname
is not None:
1308 sharedata
.vbarrange
[i
][j
] = graph
.axes
[barname
[:-4]].convert((point
[barname
], (privatedata
.barpossubname
, j
)))
1310 sharedata
.vbarrange
[i
][j
] = graph
.axes
[barname
[:-4]].convert((point
[barname
], j
))
1311 except (ArithmeticError, ValueError, TypeError):
1312 sharedata
.vbarrange
[i
][j
] = None
1314 sharedata
.vpos
[i
] = 0.5*(sharedata
.vbarrange
[i
][0]+sharedata
.vbarrange
[i
][1])
1315 except (ArithmeticError, ValueError, TypeError):
1316 sharedata
.vpos
[i
] = None
1317 if sharedata
.vpos
[i
] is None:
1318 sharedata
.vposavailable
= sharedata
.vposvalid
= 0
1319 elif sharedata
.vpos
[i
] < -self
.epsilon
or sharedata
.vpos
[i
] > 1+self
.epsilon
:
1320 sharedata
.vposvalid
= 0
1322 registerdefaultprovider(barpos(), ["vbarrange", "barposcolumnnames", "barvalueindex", "lastbarvalue", "stackedbar", "stackedbardraw"])
1325 class stackedbarpos(_style
):
1327 # provides no additional data, but needs some data (and modifies some of them)
1328 needsdata
= ["vbarrange", "barposcolumnnames", "barvalueindex", "lastbarvalue", "stackedbar", "stackedbardraw"]
1330 def __init__(self
, stackname
, addontop
=0, epsilon
=1e-10):
1331 self
.stackname
= stackname
1332 self
.epsilon
= epsilon
1333 self
.addontop
= addontop
1335 def columnnames(self
, privatedata
, sharedata
, graph
, columnnames
):
1336 if self
.stackname
not in columnnames
:
1337 raise ValueError("column '%s' missing" % self
.stackname
)
1338 return [self
.stackname
]
1340 def adjustaxis(self
, privatedata
, sharedata
, graph
, columnname
, data
):
1341 if columnname
== self
.stackname
:
1342 graph
.axes
[sharedata
.barposcolumnnames
[sharedata
.barvalueindex
]].adjustaxis(data
)
1344 def initdrawpoints(self
, privatedata
, sharedata
, graph
):
1345 if sharedata
.stackedbardraw
: # do not count the start bar when not gets painted
1346 sharedata
.stackedbar
+= 1
1348 def drawpoint(self
, privatedata
, sharedata
, graph
, point
):
1349 sharedata
.vbarrange
[sharedata
.barvalueindex
][0] = sharedata
.vbarrange
[sharedata
.barvalueindex
][1]
1352 sharedata
.lastbarvalue
+= point
[self
.stackname
]
1353 except (ArithmeticError, ValueError, TypeError):
1354 sharedata
.lastbarvalue
= None
1356 sharedata
.lastbarvalue
= point
[self
.stackname
]
1358 sharedata
.vpos
[sharedata
.barvalueindex
] = sharedata
.vbarrange
[sharedata
.barvalueindex
][1] = graph
.axes
[sharedata
.barposcolumnnames
[sharedata
.barvalueindex
]].convert(sharedata
.lastbarvalue
)
1359 except (ArithmeticError, ValueError, TypeError):
1360 sharedata
.vpos
[sharedata
.barvalueindex
] = sharedata
.vbarrange
[sharedata
.barvalueindex
][1] = None
1361 sharedata
.vposavailable
= sharedata
.vposvalid
= 0
1363 if not sharedata
.vposavailable
or not sharedata
.vposvalid
:
1364 sharedata
.vposavailable
= sharedata
.vposvalid
= 1
1365 for v
in sharedata
.vpos
:
1367 sharedata
.vposavailable
= sharedata
.vposvalid
= 0
1369 if v
< -self
.epsilon
or v
> 1+self
.epsilon
:
1370 sharedata
.vposvalid
= 0
1375 needsdata
= ["vbarrange"]
1377 defaultbarattrs
= [color
.palette
.Rainbow
, deco
.stroked([color
.grey
.black
])]
1379 def __init__(self
, barattrs
=[]):
1380 self
.barattrs
= barattrs
1382 def columnnames(self
, privatedata
, sharedata
, graph
, columnnames
):
1383 if len(graph
.axesnames
) != 2:
1384 raise TypeError("bar style restricted on two-dimensional graphs")
1387 def selectstyle(self
, privatedata
, sharedata
, graph
, selectindex
, selecttotal
):
1389 privatedata
.barattrs
= attr
.selectattrs(self
.defaultbarattrs
+ self
.barattrs
, selectindex
, selecttotal
)
1391 privatedata
.barattrs
= self
.defaultbarattrs
+ self
.barattrs
1393 def initdrawpoints(self
, privatedata
, sharedata
, graph
):
1394 privatedata
.rectcanvas
= graph
.insert(canvas
.canvas())
1395 sharedata
.stackedbardraw
= 1
1396 privatedata
.stackedbar
= sharedata
.stackedbar
1398 def drawpoint(self
, privatedata
, sharedata
, graph
, point
):
1399 xvmin
= sharedata
.vbarrange
[0][0]
1400 xvmax
= sharedata
.vbarrange
[0][1]
1401 yvmin
= sharedata
.vbarrange
[1][0]
1402 yvmax
= sharedata
.vbarrange
[1][1]
1405 xvmin
, xvmax
= xvmax
, xvmin
1410 yvmin
, yvmax
= yvmax
, yvmin
1413 if (xvmin
is not None and xvmin
< 1 and
1414 xvmax
is not None and xvmax
> 0 and
1415 yvmin
is not None and yvmin
< 1 and
1416 yvmax
is not None and yvmax
> 0):
1425 p
= graph
.vgeodesic(xvmin
, yvmin
, xvmax
, yvmin
)
1426 p
.append(graph
.vgeodesic_el(xvmax
, yvmin
, xvmax
, yvmax
))
1427 p
.append(graph
.vgeodesic_el(xvmax
, yvmax
, xvmin
, yvmax
))
1428 p
.append(graph
.vgeodesic_el(xvmin
, yvmax
, xvmin
, yvmin
))
1429 p
.append(path
.closepath())
1430 privatedata
.rectcanvas
.fill(p
, privatedata
.barattrs
)
1432 def key_pt(self
, privatedata
, sharedata
, graph
, x_pt
, y_pt
, width_pt
, height_pt
):
1433 selectindex
= privatedata
.stackedbar
1434 selecttotal
= sharedata
.stackedbar
+ 1
1435 graph
.fill(path
.rect_pt(x_pt
+ width_pt
*selectindex
/float(selecttotal
), y_pt
, width_pt
/float(selecttotal
), height_pt
), privatedata
.barattrs
)