fix palette to gradient renaming
[PyX/mjg.git] / pyx / graph / style.py
blob450f599d34eb12f24d25df5a6599d5a53e55aaf3
1 # -*- coding: ISO-8859-1 -*-
4 # Copyright (C) 2002-2004 Jörg Lehmann <joergl@users.sourceforge.net>
5 # Copyright (C) 2003-2004 Michael Schindler <m-schindler@users.sourceforge.net>
6 # Copyright (C) 2002-2006 André Wobst <wobsta@users.sourceforge.net>
8 # This file is part of PyX (http://pyx.sourceforge.net/).
10 # PyX is free software; you can redistribute it and/or modify
11 # it under the terms of the GNU General Public License as published by
12 # the Free Software Foundation; either version 2 of the License, or
13 # (at your option) any later version.
15 # PyX is distributed in the hope that it will be useful,
16 # but WITHOUT ANY WARRANTY; without even the implied warranty of
17 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 # GNU General Public License for more details.
20 # You should have received a copy of the GNU General Public License
21 # along with PyX; if not, write to the Free Software
22 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
25 import math, warnings
26 from pyx import attr, deco, style, color, unit, canvas, path
27 from pyx import text as textmodule
29 builtinrange = range
31 try:
32 enumerate([])
33 except NameError:
34 # fallback implementation for Python 2.2. and below
35 def enumerate(list):
36 return zip(xrange(len(list)), list)
38 class _style:
39 """Interface class for graph styles
41 Each graph style must support the methods described in this
42 class. However, since a graph style might not need to perform
43 actions on all the various events, it does not need to overwrite
44 all methods of this base class (e.g. this class is not an abstract
45 class in any respect).
47 A style should never store private data by istance variables
48 (i.e. accessing self), but it should use the sharedata and privatedata
49 instances instead. A style instance can be used multiple times with
50 different sharedata and privatedata instances at the very same time.
51 The sharedata and privatedata instances act as data containers and
52 sharedata allows for sharing information across several styles.
54 Every style contains two class variables, which are not to be
55 modified:
56 - providesdata is a list of variable names a style offers via
57 the sharedata instance. This list is used to determine whether
58 all needs of subsequent styles are fullfilled. Otherwise
59 getdefaultprovider should return a proper style to be used.
60 - needsdata is a list of variable names the style needs to access in the
61 sharedata instance.
62 """
64 providesdata = [] # by default, we provide nothing
65 needsdata = [] # and do not depend on anything
67 def columnnames(self, privatedata, sharedata, graph, columnnames):
68 """Set column information
70 This method is used setup the column name information to be
71 accessible to the style later on. The style should analyse
72 the list of column names. The method should return a list of
73 column names which the style will make use of."""
74 return []
76 def adjustaxis(self, privatedata, sharedata, graph, columnname, data):
77 """Adjust axis range
79 This method is called in order to adjust the axis range to
80 the provided data. columnname is the column name (each style
81 is subsequently called for all column names)."""
82 pass
84 def selectstyle(self, privatedata, sharedata, graph, selectindex, selecttotal):
85 """Select stroke/fill attributes
87 This method is called to allow for the selection of
88 changable attributes of a style."""
89 pass
91 def initdrawpoints(self, privatedata, sharedata, graph):
92 """Initialize drawing of data
94 This method might be used to initialize the drawing of data."""
95 pass
97 def drawpoint(self, privatedata, sharedata, graph, point):
98 """Draw data
100 This method is called for each data point. The data is
101 available in the dictionary point. The dictionary
102 keys are the column names."""
103 pass
105 def donedrawpoints(self, privatedata, sharedata, graph):
106 """Finalize drawing of data
108 This method is called after the last data point was
109 drawn using the drawpoint method above."""
110 pass
112 def key_pt(self, privatedata, sharedata, graph, x_pt, y_pt, width_pt, height_pt):
113 """Draw graph key"""
116 # The following two methods are used to register and get a default provider
117 # for keys. A key is a variable name in sharedata. A provider is a style
118 # which creates variables in sharedata.
120 _defaultprovider = {}
122 def registerdefaultprovider(style, keys):
123 """sets a style as a default creator for sharedata variables 'keys'"""
124 assert not len(style.needsdata), "currently we state, that a style should not depend on other sharedata variables"
125 for key in keys:
126 assert key in style.providesdata, "key not provided by style"
127 # we might allow for overwriting the defaults, i.e. the following is not checked:
128 # assert key in _defaultprovider.keys(), "default provider already registered for key"
129 _defaultprovider[key] = style
131 def getdefaultprovider(key):
132 """returns a style, which acts as a default creator for the
133 sharedata variable 'key'"""
134 return _defaultprovider[key]
137 class pos(_style):
139 providesdata = ["vpos", "vposmissing", "vposavailable", "vposvalid", "poscolumnnames"]
141 def __init__(self, epsilon=1e-10):
142 self.epsilon = epsilon
144 def columnnames(self, privatedata, sharedata, graph, columnnames):
145 sharedata.poscolumnnames = []
146 sharedata.vposmissing = []
147 for count, axisnames in enumerate(graph.axesnames):
148 for axisname in axisnames:
149 for columnname in columnnames:
150 if axisname == columnname:
151 sharedata.poscolumnnames.append(columnname)
152 if len(sharedata.poscolumnnames) > count+1:
153 raise ValueError("multiple axes per graph dimension")
154 elif len(sharedata.poscolumnnames) < count+1:
155 sharedata.vposmissing.append(count)
156 sharedata.poscolumnnames.append(None)
157 return [columnname for columnname in sharedata.poscolumnnames if columnname is not None]
159 def adjustaxis(self, privatedata, sharedata, graph, columnname, data):
160 if columnname in sharedata.poscolumnnames:
161 graph.axes[columnname].adjustaxis(data)
163 def initdrawpoints(self, privatedata, sharedata, graph):
164 sharedata.vpos = [None]*(len(graph.axesnames))
165 privatedata.pointpostmplist = [[columnname, index, graph.axes[columnname]] # temporarily used by drawpoint only
166 for index, columnname in enumerate([columnname for columnname in sharedata.poscolumnnames if columnname is not None])]
167 for missing in sharedata.vposmissing:
168 for pointpostmp in privatedata.pointpostmplist:
169 if pointpostmp[1] >= missing:
170 pointpostmp[1] += 1
172 def drawpoint(self, privatedata, sharedata, graph, point):
173 sharedata.vposavailable = 1 # valid position (but might be outside of the graph)
174 sharedata.vposvalid = 1 # valid position inside the graph
175 for columnname, index, axis in privatedata.pointpostmplist:
176 try:
177 v = axis.convert(point[columnname])
178 except (ArithmeticError, ValueError, TypeError):
179 sharedata.vposavailable = sharedata.vposvalid = 0
180 sharedata.vpos[index] = None
181 else:
182 if v < -self.epsilon or v > 1+self.epsilon:
183 sharedata.vposvalid = 0
184 sharedata.vpos[index] = v
187 registerdefaultprovider(pos(), pos.providesdata)
190 class range(_style):
192 providesdata = ["vrange", "vrangemissing", "vrangeminmissing", "vrangemaxmissing"]
194 # internal bit masks
195 mask_value = 1
196 mask_min = 2
197 mask_max = 4
198 mask_dmin = 8
199 mask_dmax = 16
200 mask_d = 32
202 def __init__(self, usenames={}, epsilon=1e-10):
203 self.usenames = usenames
204 self.epsilon = epsilon
206 def _numberofbits(self, mask):
207 if not mask:
208 return 0
209 if mask & 1:
210 return self._numberofbits(mask >> 1) + 1
211 else:
212 return self._numberofbits(mask >> 1)
214 def columnnames(self, privatedata, sharedata, graph, columnnames):
215 usecolumns = []
216 privatedata.rangeposcolumns = []
217 sharedata.vrangemissing = []
218 sharedata.vrangeminmissing = []
219 sharedata.vrangemaxmissing = []
220 privatedata.rangeposdeltacolumns = {} # temporarily used by adjustaxis only
221 for count, axisnames in enumerate(graph.axesnames):
222 for axisname in axisnames:
223 try:
224 usename = self.usenames[axisname]
225 except KeyError:
226 usename = axisname
227 mask = 0
228 for columnname in columnnames:
229 addusecolumns = 1
230 if usename == columnname:
231 mask += self.mask_value
232 elif usename + "min" == columnname:
233 mask += self.mask_min
234 elif usename + "max" == columnname:
235 mask += self.mask_max
236 elif "d" + usename + "min" == columnname:
237 mask += self.mask_dmin
238 elif "d" + usename + "max" == columnname:
239 mask += self.mask_dmax
240 elif "d" + usename == columnname:
241 mask += self.mask_d
242 else:
243 addusecolumns = 0
244 if addusecolumns:
245 usecolumns.append(columnname)
246 if mask & (self.mask_min | self.mask_max | self.mask_dmin | self.mask_dmax | self.mask_d):
247 if (self._numberofbits(mask & (self.mask_min | self.mask_dmin | self.mask_d)) > 1 or
248 self._numberofbits(mask & (self.mask_max | self.mask_dmax | self.mask_d)) > 1):
249 raise ValueError("multiple range definition")
250 if mask & (self.mask_dmin | self.mask_dmax | self.mask_d):
251 if not (mask & self.mask_value):
252 raise ValueError("missing value for delta")
253 privatedata.rangeposdeltacolumns[axisname] = {}
254 privatedata.rangeposcolumns.append((axisname, usename, mask))
255 elif mask == self.mask_value:
256 usecolumns = usecolumns[:-1]
257 if len(privatedata.rangeposcolumns) + len(sharedata.vrangemissing) > count+1:
258 raise ValueError("multiple axes per graph dimension")
259 elif len(privatedata.rangeposcolumns) + len(sharedata.vrangemissing) < count+1:
260 sharedata.vrangemissing.append(count)
261 sharedata.vrangeminmissing.append(count)
262 sharedata.vrangemaxmissing.append(count)
263 else:
264 if not (privatedata.rangeposcolumns[-1][2] & (self.mask_min | self.mask_dmin | self.mask_d)):
265 sharedata.vrangeminmissing.append(count)
266 if not (privatedata.rangeposcolumns[-1][2] & (self.mask_max | self.mask_dmax | self.mask_d)):
267 sharedata.vrangemaxmissing.append(count)
268 return usecolumns
270 def adjustaxis(self, privatedata, sharedata, graph, columnname, data):
271 if columnname in [c + "min" for a, c, m in privatedata.rangeposcolumns if m & self.mask_min]:
272 graph.axes[columnname[:-3]].adjustaxis(data)
273 if columnname in [c + "max" for a, c, m in privatedata.rangeposcolumns if m & self.mask_max]:
274 graph.axes[columnname[:-3]].adjustaxis(data)
276 # delta handling: fill rangeposdeltacolumns
277 for axisname, usename, mask in privatedata.rangeposcolumns:
278 if columnname == usename and mask & (self.mask_dmin | self.mask_dmax | self.mask_d):
279 privatedata.rangeposdeltacolumns[axisname][self.mask_value] = data
280 if columnname == "d" + usename + "min" and mask & self.mask_dmin:
281 privatedata.rangeposdeltacolumns[axisname][self.mask_dmin] = data
282 if columnname == "d" + usename + "max" and mask & self.mask_dmax:
283 privatedata.rangeposdeltacolumns[axisname][self.mask_dmax] = data
284 if columnname == "d" + usename and mask & self.mask_d:
285 privatedata.rangeposdeltacolumns[axisname][self.mask_d] = data
287 # delta handling: process rangeposdeltacolumns
288 for a, d in privatedata.rangeposdeltacolumns.items():
289 if d.has_key(self.mask_value):
290 for k in d.keys():
291 if k != self.mask_value:
292 if k & (self.mask_dmin | self.mask_d):
293 mindata = []
294 try:
295 mindata.append(d[self.mask_value] - d[k])
296 except:
297 pass
298 graph.axes[a].adjustaxis(mindata)
299 if k & (self.mask_dmax | self.mask_d):
300 maxdata = []
301 try:
302 maxdata.append(d[self.mask_value] + d[k])
303 except:
304 pass
305 graph.axes[a].adjustaxis(maxdata)
306 del d[k]
308 def initdrawpoints(self, privatedata, sharedata, graph):
309 sharedata.vrange = [[None for x in xrange(2)] for y in privatedata.rangeposcolumns + sharedata.vrangemissing]
310 privatedata.rangepostmplist = [[usename, mask, index, graph.axes[axisname]] # temporarily used by drawpoint only
311 for index, (axisname, usename, mask) in enumerate(privatedata.rangeposcolumns)]
312 for missing in sharedata.vrangemissing:
313 for rangepostmp in privatedata.rangepostmplist:
314 if rangepostmp[2] >= missing:
315 rangepostmp[2] += 1
317 def drawpoint(self, privatedata, sharedata, graph, point):
318 for usename, mask, index, axis in privatedata.rangepostmplist:
319 try:
320 if mask & self.mask_min:
321 sharedata.vrange[index][0] = axis.convert(point[usename + "min"])
322 if mask & self.mask_dmin:
323 sharedata.vrange[index][0] = axis.convert(point[usename] - point["d" + usename + "min"])
324 if mask & self.mask_d:
325 sharedata.vrange[index][0] = axis.convert(point[usename] - point["d" + usename])
326 except (ArithmeticError, ValueError, TypeError):
327 sharedata.vrange[index][0] = None
328 try:
329 if mask & self.mask_max:
330 sharedata.vrange[index][1] = axis.convert(point[usename + "max"])
331 if mask & self.mask_dmax:
332 sharedata.vrange[index][1] = axis.convert(point[usename] + point["d" + usename + "max"])
333 if mask & self.mask_d:
334 sharedata.vrange[index][1] = axis.convert(point[usename] + point["d" + usename])
335 except (ArithmeticError, ValueError, TypeError):
336 sharedata.vrange[index][1] = None
338 # some range checks for data consistency
339 if (sharedata.vrange[index][0] is not None and sharedata.vrange[index][1] is not None and
340 sharedata.vrange[index][0] > sharedata.vrange[index][1] + self.epsilon):
341 raise ValueError("inverse range")
342 # disabled due to missing vpos access:
343 # if (sharedata.vrange[index][0] is not None and sharedata.vpos[index] is not None and
344 # sharedata.vrange[index][0] > sharedata.vpos[index] + self.epsilon):
345 # raise ValueError("negative minimum errorbar")
346 # if (sharedata.vrange[index][1] is not None and sharedata.vpos[index] is not None and
347 # sharedata.vrange[index][1] < sharedata.vpos[index] - self.epsilon):
348 # raise ValueError("negative maximum errorbar")
351 registerdefaultprovider(range(), range.providesdata)
354 def _crosssymbol(c, x_pt, y_pt, size_pt, attrs):
355 c.draw(path.path(path.moveto_pt(x_pt-0.5*size_pt, y_pt-0.5*size_pt),
356 path.lineto_pt(x_pt+0.5*size_pt, y_pt+0.5*size_pt),
357 path.moveto_pt(x_pt-0.5*size_pt, y_pt+0.5*size_pt),
358 path.lineto_pt(x_pt+0.5*size_pt, y_pt-0.5*size_pt)), attrs)
360 def _plussymbol(c, x_pt, y_pt, size_pt, attrs):
361 c.draw(path.path(path.moveto_pt(x_pt-0.707106781*size_pt, y_pt),
362 path.lineto_pt(x_pt+0.707106781*size_pt, y_pt),
363 path.moveto_pt(x_pt, y_pt-0.707106781*size_pt),
364 path.lineto_pt(x_pt, y_pt+0.707106781*size_pt)), attrs)
366 def _squaresymbol(c, x_pt, y_pt, size_pt, attrs):
367 c.draw(path.path(path.moveto_pt(x_pt-0.5*size_pt, y_pt-0.5*size_pt),
368 path.lineto_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.closepath()), attrs)
373 def _trianglesymbol(c, x_pt, y_pt, size_pt, attrs):
374 c.draw(path.path(path.moveto_pt(x_pt-0.759835685*size_pt, y_pt-0.438691337*size_pt),
375 path.lineto_pt(x_pt+0.759835685*size_pt, y_pt-0.438691337*size_pt),
376 path.lineto_pt(x_pt, y_pt+0.877382675*size_pt),
377 path.closepath()), attrs)
379 def _circlesymbol(c, x_pt, y_pt, size_pt, attrs):
380 c.draw(path.path(path.arc_pt(x_pt, y_pt, 0.564189583*size_pt, 0, 360),
381 path.closepath()), attrs)
383 def _diamondsymbol(c, x_pt, y_pt, size_pt, attrs):
384 c.draw(path.path(path.moveto_pt(x_pt-0.537284965*size_pt, y_pt),
385 path.lineto_pt(x_pt, y_pt-0.930604859*size_pt),
386 path.lineto_pt(x_pt+0.537284965*size_pt, y_pt),
387 path.lineto_pt(x_pt, y_pt+0.930604859*size_pt),
388 path.closepath()), attrs)
391 class _styleneedingpointpos(_style):
393 needsdata = ["vposmissing"]
395 def columnnames(self, privatedata, sharedata, graph, columnnames):
396 if len(sharedata.vposmissing):
397 raise ValueError("incomplete position information")
398 return []
401 class symbol(_styleneedingpointpos):
403 needsdata = ["vpos", "vposmissing", "vposvalid"]
405 # "inject" the predefinied symbols into the class:
407 # Note, that statements like cross = _crosssymbol are
408 # invalid, since the would lead to unbound methods, but
409 # a single entry changeable list does the trick.
411 # Once we require Python 2.2+ we should use staticmethods
412 # to implement the default symbols inplace.
414 cross = attr.changelist([_crosssymbol])
415 plus = attr.changelist([_plussymbol])
416 square = attr.changelist([_squaresymbol])
417 triangle = attr.changelist([_trianglesymbol])
418 circle = attr.changelist([_circlesymbol])
419 diamond = attr.changelist([_diamondsymbol])
421 changecross = attr.changelist([_crosssymbol, _plussymbol, _squaresymbol, _trianglesymbol, _circlesymbol, _diamondsymbol])
422 changeplus = attr.changelist([_plussymbol, _squaresymbol, _trianglesymbol, _circlesymbol, _diamondsymbol, _crosssymbol])
423 changesquare = attr.changelist([_squaresymbol, _trianglesymbol, _circlesymbol, _diamondsymbol, _crosssymbol, _plussymbol])
424 changetriangle = attr.changelist([_trianglesymbol, _circlesymbol, _diamondsymbol, _crosssymbol, _plussymbol, _squaresymbol])
425 changecircle = attr.changelist([_circlesymbol, _diamondsymbol, _crosssymbol, _plussymbol, _squaresymbol, _trianglesymbol])
426 changediamond = attr.changelist([_diamondsymbol, _crosssymbol, _plussymbol, _squaresymbol, _trianglesymbol, _circlesymbol])
427 changesquaretwice = attr.changelist([_squaresymbol, _squaresymbol, _trianglesymbol, _trianglesymbol, _circlesymbol, _circlesymbol, _diamondsymbol, _diamondsymbol])
428 changetriangletwice = attr.changelist([_trianglesymbol, _trianglesymbol, _circlesymbol, _circlesymbol, _diamondsymbol, _diamondsymbol, _squaresymbol, _squaresymbol])
429 changecircletwice = attr.changelist([_circlesymbol, _circlesymbol, _diamondsymbol, _diamondsymbol, _squaresymbol, _squaresymbol, _trianglesymbol, _trianglesymbol])
430 changediamondtwice = attr.changelist([_diamondsymbol, _diamondsymbol, _squaresymbol, _squaresymbol, _trianglesymbol, _trianglesymbol, _circlesymbol, _circlesymbol])
432 changestrokedfilled = attr.changelist([deco.stroked, deco.filled])
433 changefilledstroked = attr.changelist([deco.filled, deco.stroked])
435 defaultsymbolattrs = [deco.stroked]
437 def __init__(self, symbol=changecross, size=0.2*unit.v_cm, symbolattrs=[]):
438 self.symbol = symbol
439 self.size = size
440 self.symbolattrs = symbolattrs
442 def selectstyle(self, privatedata, sharedata, graph, selectindex, selecttotal):
443 privatedata.symbol = attr.selectattr(self.symbol, selectindex, selecttotal)
444 privatedata.size_pt = unit.topt(attr.selectattr(self.size, selectindex, selecttotal))
445 if self.symbolattrs is not None:
446 privatedata.symbolattrs = attr.selectattrs(self.defaultsymbolattrs + self.symbolattrs, selectindex, selecttotal)
447 else:
448 privatedata.symbolattrs = None
450 def initdrawpoints(self, privatedata, sharedata, graph):
451 privatedata.symbolcanvas = canvas.canvas()
453 def drawpoint(self, privatedata, sharedata, graph, point):
454 if sharedata.vposvalid and privatedata.symbolattrs is not None:
455 x_pt, y_pt = graph.vpos_pt(*sharedata.vpos)
456 privatedata.symbol(privatedata.symbolcanvas, x_pt, y_pt, privatedata.size_pt, privatedata.symbolattrs)
458 def donedrawpoints(self, privatedata, sharedata, graph):
459 graph.insert(privatedata.symbolcanvas)
461 def key_pt(self, privatedata, sharedata, graph, x_pt, y_pt, width_pt, height_pt):
462 if privatedata.symbolattrs is not None:
463 privatedata.symbol(graph, x_pt+0.5*width_pt, y_pt+0.5*height_pt, privatedata.size_pt, privatedata.symbolattrs)
466 class _line(_styleneedingpointpos):
468 # this style is not a complete style, but it provides the basic functionality to
469 # create a line, which is cut at the graph boundaries (or at otherwise invalid points)
471 needsdata = ["vposmissing"]
473 def initpointstopath(self, privatedata):
474 privatedata.path = path.path()
475 privatedata.linebasepoints = []
476 privatedata.lastvpos = None
478 def addpointstopath(self, privatedata):
479 # add baselinepoints to privatedata.path
480 if len(privatedata.linebasepoints) > 1:
481 privatedata.path.append(path.moveto_pt(*privatedata.linebasepoints[0]))
482 if len(privatedata.linebasepoints) > 2:
483 privatedata.path.append(path.multilineto_pt(privatedata.linebasepoints[1:]))
484 else:
485 privatedata.path.append(path.lineto_pt(*privatedata.linebasepoints[1]))
486 privatedata.linebasepoints = []
488 def addpoint(self, privatedata, graphvpos_pt, vposavailable, vposvalid, vpos):
489 # append linebasepoints
490 if vposavailable:
491 if len(privatedata.linebasepoints):
492 # the last point was inside the graph
493 if vposvalid: # shortcut for the common case
494 privatedata.linebasepoints.append(graphvpos_pt(*vpos))
495 else:
496 # cut end
497 cut = 1
498 for vstart, vend in zip(privatedata.lastvpos, vpos):
499 newcut = None
500 if vend > 1:
501 # 1 = vstart + (vend - vstart) * cut
502 try:
503 newcut = (1 - vstart)/(vend - vstart)
504 except (ArithmeticError, TypeError):
505 break
506 if vend < 0:
507 # 0 = vstart + (vend - vstart) * cut
508 try:
509 newcut = - vstart/(vend - vstart)
510 except (ArithmeticError, TypeError):
511 break
512 if newcut is not None and newcut < cut:
513 cut = newcut
514 else:
515 cutvpos = []
516 for vstart, vend in zip(privatedata.lastvpos, vpos):
517 cutvpos.append(vstart + (vend - vstart) * cut)
518 privatedata.linebasepoints.append(graphvpos_pt(*cutvpos))
519 self.addpointstopath(privatedata)
520 else:
521 # the last point was outside the graph
522 if privatedata.lastvpos is not None:
523 if vposvalid:
524 # cut beginning
525 cut = 0
526 for vstart, vend in zip(privatedata.lastvpos, vpos):
527 newcut = None
528 if vstart > 1:
529 # 1 = vstart + (vend - vstart) * cut
530 try:
531 newcut = (1 - vstart)/(vend - vstart)
532 except (ArithmeticError, TypeError):
533 break
534 if vstart < 0:
535 # 0 = vstart + (vend - vstart) * cut
536 try:
537 newcut = - vstart/(vend - vstart)
538 except (ArithmeticError, TypeError):
539 break
540 if newcut is not None and newcut > cut:
541 cut = newcut
542 else:
543 cutvpos = []
544 for vstart, vend in zip(privatedata.lastvpos, vpos):
545 cutvpos.append(vstart + (vend - vstart) * cut)
546 privatedata.linebasepoints.append(graphvpos_pt(*cutvpos))
547 privatedata.linebasepoints.append(graphvpos_pt(*vpos))
548 else:
549 # sometimes cut beginning and end
550 cutfrom = 0
551 cutto = 1
552 for vstart, vend in zip(privatedata.lastvpos, vpos):
553 newcutfrom = None
554 if vstart > 1:
555 if vend > 1:
556 break
557 # 1 = vstart + (vend - vstart) * cutfrom
558 try:
559 newcutfrom = (1 - vstart)/(vend - vstart)
560 except (ArithmeticError, TypeError):
561 break
562 if vstart < 0:
563 if vend < 0:
564 break
565 # 0 = vstart + (vend - vstart) * cutfrom
566 try:
567 newcutfrom = - vstart/(vend - vstart)
568 except (ArithmeticError, TypeError):
569 break
570 if newcutfrom is not None and newcutfrom > cutfrom:
571 cutfrom = newcutfrom
572 newcutto = None
573 if vend > 1:
574 # 1 = vstart + (vend - vstart) * cutto
575 try:
576 newcutto = (1 - vstart)/(vend - vstart)
577 except (ArithmeticError, TypeError):
578 break
579 if vend < 0:
580 # 0 = vstart + (vend - vstart) * cutto
581 try:
582 newcutto = - vstart/(vend - vstart)
583 except (ArithmeticError, TypeError):
584 break
585 if newcutto is not None and newcutto < cutto:
586 cutto = newcutto
587 else:
588 if cutfrom < cutto:
589 cutfromvpos = []
590 cuttovpos = []
591 for vstart, vend in zip(privatedata.lastvpos, vpos):
592 cutfromvpos.append(vstart + (vend - vstart) * cutfrom)
593 cuttovpos.append(vstart + (vend - vstart) * cutto)
594 privatedata.linebasepoints.append(graphvpos_pt(*cutfromvpos))
595 privatedata.linebasepoints.append(graphvpos_pt(*cuttovpos))
596 self.addpointstopath(privatedata)
597 privatedata.lastvpos = vpos[:]
598 else:
599 if len(privatedata.linebasepoints) > 1:
600 self.addpointstopath(privatedata)
601 privatedata.lastvpos = None
603 def addinvalid(self, privatedata):
604 if len(privatedata.linebasepoints) > 1:
605 self.addpointstopath(privatedata)
606 privatedata.lastvpos = None
608 def donepointstopath(self, privatedata):
609 if len(privatedata.linebasepoints) > 1:
610 self.addpointstopath(privatedata)
611 return privatedata.path
614 class line(_line):
616 needsdata = ["vpos", "vposmissing", "vposavailable", "vposvalid"]
618 changelinestyle = attr.changelist([style.linestyle.solid,
619 style.linestyle.dashed,
620 style.linestyle.dotted,
621 style.linestyle.dashdotted])
623 defaultlineattrs = [changelinestyle]
625 def __init__(self, lineattrs=[]):
626 self.lineattrs = lineattrs
628 def selectstyle(self, privatedata, sharedata, graph, selectindex, selecttotal):
629 if self.lineattrs is not None:
630 privatedata.lineattrs = attr.selectattrs(self.defaultlineattrs + self.lineattrs, selectindex, selecttotal)
631 else:
632 privatedata.lineattrs = None
634 def initdrawpoints(self, privatedata, sharedata, graph):
635 self.initpointstopath(privatedata)
637 def drawpoint(self, privatedata, sharedata, graph, point):
638 self.addpoint(privatedata, graph.vpos_pt, sharedata.vposavailable, sharedata.vposvalid, sharedata.vpos)
640 def donedrawpoints(self, privatedata, sharedata, graph):
641 path = self.donepointstopath(privatedata)
642 if privatedata.lineattrs is not None and len(path):
643 graph.stroke(path, privatedata.lineattrs)
645 def key_pt(self, privatedata, sharedata, graph, x_pt, y_pt, width_pt, height_pt):
646 if privatedata.lineattrs is not None:
647 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)
650 class errorbar(_style):
652 needsdata = ["vpos", "vposmissing", "vposavailable", "vposvalid", "vrange", "vrangeminmissing", "vrangemaxmissing"]
654 defaulterrorbarattrs = []
656 def __init__(self, size=0.1*unit.v_cm,
657 errorbarattrs=[],
658 epsilon=1e-10):
659 self.size = size
660 self.errorbarattrs = errorbarattrs
661 self.epsilon = epsilon
663 def columnnames(self, privatedata, sharedata, graph, columnnames):
664 for i in sharedata.vposmissing:
665 if i in sharedata.vrangeminmissing and i in sharedata.vrangemaxmissing:
666 raise ValueError("position and range for a graph dimension missing")
667 return []
669 def selectstyle(self, privatedata, sharedata, graph, selectindex, selecttotal):
670 privatedata.errorsize_pt = unit.topt(attr.selectattr(self.size, selectindex, selecttotal))
671 privatedata.errorbarattrs = attr.selectattrs(self.defaulterrorbarattrs + self.errorbarattrs, selectindex, selecttotal)
673 def initdrawpoints(self, privatedata, sharedata, graph):
674 if privatedata.errorbarattrs is not None:
675 privatedata.errorbarcanvas = canvas.canvas(privatedata.errorbarattrs)
676 privatedata.dimensionlist = list(xrange(len(sharedata.vpos)))
678 def drawpoint(self, privatedata, sharedata, graph, point):
679 if privatedata.errorbarattrs is not None:
680 for i in privatedata.dimensionlist:
681 for j in privatedata.dimensionlist:
682 if (i != j and
683 (sharedata.vpos[j] is None or
684 sharedata.vpos[j] < -self.epsilon or
685 sharedata.vpos[j] > 1+self.epsilon)):
686 break
687 else:
688 if ((sharedata.vrange[i][0] is None and sharedata.vpos[i] is None) or
689 (sharedata.vrange[i][1] is None and sharedata.vpos[i] is None) or
690 (sharedata.vrange[i][0] is None and sharedata.vrange[i][1] is None)):
691 continue
692 vminpos = sharedata.vpos[:]
693 if sharedata.vrange[i][0] is not None:
694 vminpos[i] = sharedata.vrange[i][0]
695 mincap = 1
696 else:
697 mincap = 0
698 if vminpos[i] > 1+self.epsilon:
699 continue
700 if vminpos[i] < -self.epsilon:
701 vminpos[i] = 0
702 mincap = 0
703 vmaxpos = sharedata.vpos[:]
704 if sharedata.vrange[i][1] is not None:
705 vmaxpos[i] = sharedata.vrange[i][1]
706 maxcap = 1
707 else:
708 maxcap = 0
709 if vmaxpos[i] < -self.epsilon:
710 continue
711 if vmaxpos[i] > 1+self.epsilon:
712 vmaxpos[i] = 1
713 maxcap = 0
714 privatedata.errorbarcanvas.stroke(graph.vgeodesic(*(vminpos + vmaxpos)))
715 for j in privatedata.dimensionlist:
716 if i != j:
717 if mincap:
718 privatedata.errorbarcanvas.stroke(graph.vcap_pt(j, privatedata.errorsize_pt, *vminpos))
719 if maxcap:
720 privatedata.errorbarcanvas.stroke(graph.vcap_pt(j, privatedata.errorsize_pt, *vmaxpos))
722 def donedrawpoints(self, privatedata, sharedata, graph):
723 if privatedata.errorbarattrs is not None:
724 graph.insert(privatedata.errorbarcanvas)
727 class text(_styleneedingpointpos):
729 needsdata = ["vpos", "vposmissing", "vposvalid"]
731 defaulttextattrs = [textmodule.halign.center, textmodule.vshift.mathaxis]
733 def __init__(self, textname="text", textdx=0*unit.v_cm, textdy=0.3*unit.v_cm, textattrs=[]):
734 self.textname = textname
735 self.textdx = textdx
736 self.textdy = textdy
737 self.textattrs = textattrs
739 def columnnames(self, privatedata, sharedata, graph, columnnames):
740 if self.textname not in columnnames:
741 raise ValueError("column '%s' missing" % self.textname)
742 return [self.textname] + _styleneedingpointpos.columnnames(self, privatedata, sharedata, graph, columnnames)
744 def selectstyle(self, privatedata, sharedata, graph, selectindex, selecttotal):
745 if self.textattrs is not None:
746 privatedata.textattrs = attr.selectattrs(self.defaulttextattrs + self.textattrs, selectindex, selecttotal)
747 else:
748 privatedata.textattrs = None
750 def initdrawpoints(self, privatedata, sharedata, grap):
751 privatedata.textdx_pt = unit.topt(self.textdx)
752 privatedata.textdy_pt = unit.topt(self.textdy)
754 def drawpoint(self, privatedata, sharedata, graph, point):
755 if privatedata.textattrs is not None and sharedata.vposvalid:
756 x_pt, y_pt = graph.vpos_pt(*sharedata.vpos)
757 try:
758 text = str(point[self.textname])
759 except:
760 pass
761 else:
762 graph.text_pt(x_pt + privatedata.textdx_pt, y_pt + privatedata.textdy_pt, text, privatedata.textattrs)
764 def key_pt(self, privatedata, sharedata, graph, x_pt, y_pt, width_pt, height_pt):
765 raise RuntimeError("Style currently doesn't provide a graph key")
768 class arrow(_styleneedingpointpos):
770 needsdata = ["vpos", "vposmissing", "vposvalid"]
772 defaultlineattrs = []
773 defaultarrowattrs = []
775 def __init__(self, linelength=0.25*unit.v_cm, arrowsize=0.15*unit.v_cm, lineattrs=[], arrowattrs=[], epsilon=1e-5):
776 self.linelength = linelength
777 self.arrowsize = arrowsize
778 self.lineattrs = lineattrs
779 self.arrowattrs = arrowattrs
780 self.epsilon = epsilon
782 def columnnames(self, privatedata, sharedata, graph, columnnames):
783 if len(graph.axesnames) != 2:
784 raise ValueError("arrow style restricted on two-dimensional graphs")
785 if "size" not in columnnames:
786 raise ValueError("size missing")
787 if "angle" not in columnnames:
788 raise ValueError("angle missing")
789 return ["size", "angle"] + _styleneedingpointpos.columnnames(self, privatedata, sharedata, graph, columnnames)
791 def selectstyle(self, privatedata, sharedata, graph, selectindex, selecttotal):
792 if self.lineattrs is not None:
793 privatedata.lineattrs = attr.selectattrs(self.defaultlineattrs + self.lineattrs, selectindex, selecttotal)
794 else:
795 privatedata.lineattrs = None
796 if self.arrowattrs is not None:
797 privatedata.arrowattrs = attr.selectattrs(self.defaultarrowattrs + self.arrowattrs, selectindex, selecttotal)
798 else:
799 privatedata.arrowattrs = None
801 def initdrawpoints(self, privatedata, sharedata, graph):
802 privatedata.arrowcanvas = canvas.canvas()
804 def drawpoint(self, privatedata, sharedata, graph, point):
805 if privatedata.lineattrs is not None and privatedata.arrowattrs is not None and sharedata.vposvalid:
806 linelength_pt = unit.topt(self.linelength)
807 x_pt, y_pt = graph.vpos_pt(*sharedata.vpos)
808 try:
809 angle = point["angle"] + 0.0
810 size = point["size"] + 0.0
811 except:
812 pass
813 else:
814 if point["size"] > self.epsilon:
815 dx = math.cos(angle*math.pi/180)
816 dy = math.sin(angle*math.pi/180)
817 x1 = x_pt-0.5*dx*linelength_pt*size
818 y1 = y_pt-0.5*dy*linelength_pt*size
819 x2 = x_pt+0.5*dx*linelength_pt*size
820 y2 = y_pt+0.5*dy*linelength_pt*size
821 privatedata.arrowcanvas.stroke(path.line_pt(x1, y1, x2, y2), privatedata.lineattrs +
822 [deco.earrow(privatedata.arrowattrs, size=self.arrowsize*size)])
824 def donedrawpoints(self, privatedata, sharedata, graph):
825 graph.insert(privatedata.arrowcanvas)
827 def key_pt(self, privatedata, sharedata, graph, x_pt, y_pt, width_pt, height_pt):
828 raise RuntimeError("Style currently doesn't provide a graph key")
831 class rect(_style):
833 needsdata = ["vrange", "vrangeminmissing", "vrangemaxmissing"]
835 def __init__(self, gradient=color.gradient.Grey):
836 self.gradient = gradient
838 def columnnames(self, privatedata, sharedata, graph, columnnames):
839 if len(graph.axesnames) != 2:
840 raise TypeError("arrow style restricted on two-dimensional graphs")
841 if "color" not in columnnames:
842 raise ValueError("color missing")
843 if len(sharedata.vrangeminmissing) + len(sharedata.vrangemaxmissing):
844 raise ValueError("incomplete range")
845 return ["color"]
847 def initdrawpoints(self, privatedata, sharedata, graph):
848 privatedata.rectcanvas = graph.insert(canvas.canvas())
850 def drawpoint(self, privatedata, sharedata, graph, point):
851 xvmin = sharedata.vrange[0][0]
852 xvmax = sharedata.vrange[0][1]
853 yvmin = sharedata.vrange[1][0]
854 yvmax = sharedata.vrange[1][1]
855 if (xvmin is not None and xvmin < 1 and
856 xvmax is not None and xvmax > 0 and
857 yvmin is not None and yvmin < 1 and
858 yvmax is not None and yvmax > 0):
859 if xvmin < 0:
860 xvmin = 0
861 elif xvmax > 1:
862 xvmax = 1
863 if yvmin < 0:
864 yvmin = 0
865 elif yvmax > 1:
866 yvmax = 1
867 p = graph.vgeodesic(xvmin, yvmin, xvmax, yvmin)
868 p.append(graph.vgeodesic_el(xvmax, yvmin, xvmax, yvmax))
869 p.append(graph.vgeodesic_el(xvmax, yvmax, xvmin, yvmax))
870 p.append(graph.vgeodesic_el(xvmin, yvmax, xvmin, yvmin))
871 p.append(path.closepath())
872 privatedata.rectcanvas.fill(p, [self.gradient.getcolor(point["color"])])
874 def key_pt(self, privatedata, sharedata, graph, x_pt, y_pt, width_pt, height_pt):
875 raise RuntimeError("Style currently doesn't provide a graph key")
878 class histogram(_style):
880 needsdata = ["vpos", "vposmissing", "vrange", "vrangeminmissing", "vrangemaxmissing"]
882 defaultlineattrs = [deco.stroked]
883 defaultfrompathattrs = []
885 def __init__(self, lineattrs=[], steps=0, fromvalue=0, frompathattrs=[], fillable=0,
886 autohistogramaxisindex=0, autohistogrampointpos=0.5, epsilon=1e-10):
887 self.lineattrs = lineattrs
888 self.steps = steps
889 self.fromvalue = fromvalue
890 self.frompathattrs = frompathattrs
891 self.fillable = fillable # TODO: fillable paths might not properly be closed by straight lines on curved graph geometries
892 self.autohistogramaxisindex = autohistogramaxisindex
893 self.autohistogrampointpos = autohistogrampointpos
894 self.epsilon = epsilon
896 def columnnames(self, privatedata, sharedata, graph, columnnames):
897 if len(graph.axesnames) != 2:
898 raise TypeError("histogram style restricted on two-dimensional graphs")
899 privatedata.rangeaxisindex = None
900 for i in builtinrange(len(graph.axesnames)):
901 if i in sharedata.vrangeminmissing or i in sharedata.vrangemaxmissing:
902 if i in sharedata.vposmissing:
903 raise ValueError("pos and range missing")
904 else:
905 if privatedata.rangeaxisindex is not None:
906 raise ValueError("multiple ranges")
907 privatedata.rangeaxisindex = i
908 if privatedata.rangeaxisindex is None:
909 privatedata.rangeaxisindex = self.autohistogramaxisindex
910 privatedata.autohistogram = 1
911 else:
912 privatedata.autohistogram = 0
913 return []
915 def adjustaxis(self, privatedata, sharedata, graph, columnname, data):
916 if privatedata.autohistogram and columnname == sharedata.poscolumnnames[privatedata.rangeaxisindex]:
917 if len(data) == 1:
918 raise ValueError("several data points needed for automatic histogram width calculation")
919 if data:
920 delta = data[1] - data[0]
921 min = data[0] - self.autohistogrampointpos * delta
922 max = data[-1] + (1-self.autohistogrampointpos) * delta
923 graph.axes[columnname].adjustaxis([min, max])
925 def selectstyle(self, privatedata, sharedata, graph, selectindex, selecttotal):
926 privatedata.insertfrompath = selectindex == 0
927 if self.lineattrs is not None:
928 privatedata.lineattrs = attr.selectattrs(self.defaultlineattrs + self.lineattrs, selectindex, selecttotal)
929 else:
930 privatedata.lineattrs = None
932 def vmoveto(self, privatedata, sharedata, graph, vpos, vvalue):
933 if -self.epsilon < vpos < 1+self.epsilon and -self.epsilon < vvalue < 1+self.epsilon:
934 if privatedata.rangeaxisindex:
935 privatedata.path.append(path.moveto_pt(*graph.vpos_pt(vvalue, vpos)))
936 else:
937 privatedata.path.append(path.moveto_pt(*graph.vpos_pt(vpos, vvalue)))
939 def vposline(self, privatedata, sharedata, graph, vpos, vvalue1, vvalue2):
940 if -self.epsilon < vpos < 1+self.epsilon:
941 vvalue1cut = 0
942 if vvalue1 < 0:
943 vvalue1 = 0
944 vvalue1cut = -1
945 elif vvalue1 > 1:
946 vvalue1 = 1
947 vvalue1cut = 1
948 vvalue2cut = 0
949 if vvalue2 < 0:
950 vvalue2 = 0
951 vvalue2cut = -1
952 elif vvalue2 > 1:
953 vvalue2 = 1
954 vvalue2cut = 1
955 if abs(vvalue1cut + vvalue2cut) <= 1:
956 if vvalue1cut and not self.fillable:
957 if privatedata.rangeaxisindex:
958 privatedata.path.append(path.moveto_pt(*graph.vpos_pt(vvalue1, vpos)))
959 else:
960 privatedata.path.append(path.moveto_pt(*graph.vpos_pt(vpos, vvalue1)))
961 if privatedata.rangeaxisindex:
962 privatedata.path.append(graph.vgeodesic_el(vvalue1, vpos, vvalue2, vpos))
963 else:
964 privatedata.path.append(graph.vgeodesic_el(vpos, vvalue1, vpos, vvalue2))
966 def vvalueline(self, privatedata, sharedata, graph, vvalue, vpos1, vpos2):
967 if self.fillable:
968 if vvalue < -self.epsilon:
969 vvalue = 0
970 warnings.warn("cut at graph boundary adds artificial lines to fillable step histogram path")
971 if vvalue > 1+self.epsilon:
972 vvalue = 1
973 warnings.warn("cut at graph boundary adds artificial lines to fillable step histogram path")
974 if self.fillable or (-self.epsilon < vvalue < 1+self.epsilon):
975 vpos1cut = 0
976 if vpos1 < 0:
977 vpos1 = 0
978 vpos1cut = -1
979 elif vpos1 > 1:
980 vpos1 = 1
981 vpos1cut = 1
982 vpos2cut = 0
983 if vpos2 < 0:
984 vpos2 = 0
985 vpos2cut = -1
986 elif vpos2 > 1:
987 vpos2 = 1
988 vpos2cut = 1
989 if abs(vpos1cut + vpos2cut) <= 1:
990 if vpos1cut:
991 if self.fillable:
992 if privatedata.rangeaxisindex:
993 privatedata.path.append(path.moveto_pt(*graph.vpos_pt(privatedata.vfromvalue, vpos1)))
994 privatedata.path.append(graph.vgeodesic_el(privatedata.vfromvalue, vpos1, vvalue, vpos1))
995 else:
996 privatedata.path.append(path.moveto_pt(*graph.vpos_pt(vpos1, privatedata.vfromvalue)))
997 privatedata.path.append(graph.vgeodesic_el(vpos1, privatedata.vfromvalue, vpos1, vvalue))
998 else:
999 if privatedata.rangeaxisindex:
1000 privatedata.path.append(path.moveto_pt(*graph.vpos_pt(vvalue, vpos1)))
1001 else:
1002 privatedata.path.append(path.moveto_pt(*graph.vpos_pt(vpos1, vvalue)))
1003 if privatedata.rangeaxisindex:
1004 privatedata.path.append(graph.vgeodesic_el(vvalue, vpos1, vvalue, vpos2))
1005 else:
1006 privatedata.path.append(graph.vgeodesic_el(vpos1, vvalue, vpos2, vvalue))
1007 if self.fillable and vpos2cut:
1008 warnings.warn("cut at graph boundary adds artificial lines to fillable step histogram path")
1009 if privatedata.rangeaxisindex:
1010 privatedata.path.append(graph.vgeodesic_el(vvalue, vpos2, privatedata.vfromvalue, vpos2))
1011 else:
1012 privatedata.path.append(graph.vgeodesic_el(vpos2, vvalue, vpos2, privatedata.vfromvalue))
1014 def drawvalue(self, privatedata, sharedata, graph, vmin, vmax, vvalue):
1015 currentvalid = vmin is not None and vmax is not None and vvalue is not None
1016 if self.fillable and not self.steps:
1017 if not currentvalid:
1018 return
1019 vmincut = 0
1020 if vmin < -self.epsilon:
1021 vmin = 0
1022 vmincut = -1
1023 elif vmin > 1+self.epsilon:
1024 vmin = 1
1025 vmincut = 1
1026 vmaxcut = 0
1027 if vmax < -self.epsilon:
1028 vmax = 0
1029 vmaxcut = -1
1030 if vmax > 1+self.epsilon:
1031 vmax = 1
1032 vmaxcut = 1
1033 vvaluecut = 0
1034 if vvalue < -self.epsilon:
1035 vvalue = 0
1036 vvaluecut = -1
1037 if vvalue > 1+self.epsilon:
1038 vvalue = 1
1039 vvaluecut = 1
1040 done = 0
1041 if abs(vmincut) + abs(vmaxcut) + abs(vvaluecut) + abs(privatedata.vfromvaluecut) > 1:
1042 if abs(vmincut + vmaxcut) > 1 or abs(vvaluecut+privatedata.vfromvaluecut) > 1:
1043 done = 1
1044 else:
1045 warnings.warn("multiple cuts at graph boundary add artificial lines to fillable rectangle histogram path")
1046 elif vmincut:
1047 done = 1
1048 if privatedata.rangeaxisindex:
1049 privatedata.path.append(path.moveto_pt(*graph.vpos_pt(privatedata.vfromvalue, vmin)))
1050 privatedata.path.append(graph.vgeodesic_el(privatedata.vfromvalue, vmin, privatedata.vfromvalue, vmax))
1051 privatedata.path.append(graph.vgeodesic_el(privatedata.vfromvalue, vmax, vvalue, vmax))
1052 privatedata.path.append(graph.vgeodesic_el(vvalue, vmax, vvalue, vmin))
1053 else:
1054 privatedata.path.append(path.moveto_pt(*graph.vpos_pt(vmin, privatedata.vfromvalue)))
1055 privatedata.path.append(graph.vgeodesic_el(vmin, privatedata.vfromvalue, vmax, privatedata.vfromvalue))
1056 privatedata.path.append(graph.vgeodesic_el(vmax, privatedata.vfromvalue, vmax, vvalue))
1057 privatedata.path.append(graph.vgeodesic_el(vmax, vvalue, vmin, vvalue))
1058 elif vmaxcut:
1059 done = 1
1060 if privatedata.rangeaxisindex:
1061 privatedata.path.append(path.moveto_pt(*graph.vpos_pt(vvalue, vmax)))
1062 privatedata.path.append(graph.vgeodesic_el(vvalue, vmax, vvalue, vmin))
1063 privatedata.path.append(graph.vgeodesic_el(vvalue, vmin, privatedata.vfromvalue, vmin))
1064 privatedata.path.append(graph.vgeodesic_el(privatedata.vfromvalue, vmin, privatedata.vfromvalue, vmax))
1065 else:
1066 privatedata.path.append(path.moveto_pt(*graph.vpos_pt(vmax, vvalue)))
1067 privatedata.path.append(graph.vgeodesic_el(vmax, vvalue, vmin, vvalue))
1068 privatedata.path.append(graph.vgeodesic_el(vmin, vvalue, vmin, privatedata.vfromvalue))
1069 privatedata.path.append(graph.vgeodesic_el(vmin, privatedata.vfromvalue, vmax, privatedata.vfromvalue))
1070 elif privatedata.vfromvaluecut:
1071 done = 1
1072 if privatedata.rangeaxisindex:
1073 privatedata.path.append(path.moveto_pt(*graph.vpos_pt(privatedata.vfromvalue, vmax)))
1074 privatedata.path.append(graph.vgeodesic_el(privatedata.vfromvalue, vmax, vvalue, vmax))
1075 privatedata.path.append(graph.vgeodesic_el(vvalue, vmax, vvalue, vmin))
1076 privatedata.path.append(graph.vgeodesic_el(vvalue, vmin, privatedata.vfromvalue, vmin))
1077 else:
1078 privatedata.path.append(path.moveto_pt(*graph.vpos_pt(vmax, privatedata.vfromvalue)))
1079 privatedata.path.append(graph.vgeodesic_el(vmax, privatedata.vfromvalue, vmax, vvalue))
1080 privatedata.path.append(graph.vgeodesic_el(vmax, vvalue, vmin, vvalue))
1081 privatedata.path.append(graph.vgeodesic_el(vmin, vvalue, vmin, privatedata.vfromvalue))
1082 elif vvaluecut:
1083 done = 1
1084 if privatedata.rangeaxisindex:
1085 privatedata.path.append(path.moveto_pt(*graph.vpos_pt(vvalue, vmin)))
1086 privatedata.path.append(graph.vgeodesic_el(vvalue, vmin, privatedata.vfromvalue, vmin))
1087 privatedata.path.append(graph.vgeodesic_el(privatedata.vfromvalue, vmin, privatedata.vfromvalue, vmax))
1088 privatedata.path.append(graph.vgeodesic_el(privatedata.vfromvalue, vmax, vvalue, vmax))
1089 else:
1090 privatedata.path.append(path.moveto_pt(*graph.vpos_pt(vmin, vvalue)))
1091 privatedata.path.append(graph.vgeodesic_el(vmin, vvalue, vmin, privatedata.vfromvalue))
1092 privatedata.path.append(graph.vgeodesic_el(vmin, privatedata.vfromvalue, vmax, privatedata.vfromvalue))
1093 privatedata.path.append(graph.vgeodesic_el(vmax, privatedata.vfromvalue, vmax, vvalue))
1094 if not done:
1095 if privatedata.rangeaxisindex:
1096 privatedata.path.append(path.moveto_pt(*graph.vpos_pt(privatedata.vfromvalue, vmin)))
1097 privatedata.path.append(graph.vgeodesic_el(privatedata.vfromvalue, vmin, privatedata.vfromvalue, vmax))
1098 privatedata.path.append(graph.vgeodesic_el(privatedata.vfromvalue, vmax, vvalue, vmax))
1099 privatedata.path.append(graph.vgeodesic_el(vvalue, vmax, vvalue, vmin))
1100 privatedata.path.append(graph.vgeodesic_el(vvalue, vmin, privatedata.vfromvalue, vmin))
1101 privatedata.path.append(path.closepath())
1102 else:
1103 privatedata.path.append(path.moveto_pt(*graph.vpos_pt(vmin, privatedata.vfromvalue)))
1104 privatedata.path.append(graph.vgeodesic_el(vmin, privatedata.vfromvalue, vmax, privatedata.vfromvalue))
1105 privatedata.path.append(graph.vgeodesic_el(vmax, privatedata.vfromvalue, vmax, vvalue))
1106 privatedata.path.append(graph.vgeodesic_el(vmax, vvalue, vmin, vvalue))
1107 privatedata.path.append(graph.vgeodesic_el(vmin, vvalue, vmin, privatedata.vfromvalue))
1108 privatedata.path.append(path.closepath())
1109 else:
1110 try:
1111 gap = abs(vmin - privatedata.lastvmax) > self.epsilon
1112 except (ArithmeticError, ValueError, TypeError):
1113 gap = 1
1114 if (privatedata.lastvvalue is not None and currentvalid and not gap and
1115 (self.steps or (privatedata.lastvvalue-privatedata.vfromvalue)*(vvalue-privatedata.vfromvalue) < 0)):
1116 self.vposline(privatedata, sharedata, graph,
1117 vmin, privatedata.lastvvalue, vvalue)
1118 else:
1119 if privatedata.lastvvalue is not None and currentvalid:
1120 currentbigger = abs(privatedata.lastvvalue-privatedata.vfromvalue) < abs(vvalue-privatedata.vfromvalue)
1121 if privatedata.lastvvalue is not None and (not currentvalid or not currentbigger or gap):
1122 self.vposline(privatedata, sharedata, graph,
1123 privatedata.lastvmax, privatedata.lastvvalue, privatedata.vfromvalue)
1124 if currentvalid:
1125 self.vmoveto(privatedata, sharedata, graph,
1126 vmin, vvalue)
1127 if currentvalid and (privatedata.lastvvalue is None or currentbigger or gap):
1128 self.vmoveto(privatedata, sharedata, graph,
1129 vmin, privatedata.vfromvalue)
1130 self.vposline(privatedata, sharedata, graph,
1131 vmin, privatedata.vfromvalue, vvalue)
1132 if currentvalid:
1133 self.vvalueline(privatedata, sharedata, graph,
1134 vvalue, vmin, vmax)
1135 privatedata.lastvvalue = vvalue
1136 privatedata.lastvmax = vmax
1137 else:
1138 privatedata.lastvvalue = privatedata.lastvmax = None
1140 def initdrawpoints(self, privatedata, sharedata, graph):
1141 privatedata.path = path.path()
1142 privatedata.lastvvalue = privatedata.lastvmax = None
1143 privatedata.vcurrentpoint = None
1144 privatedata.count = 0
1145 if self.fromvalue is not None:
1146 valueaxisname = sharedata.poscolumnnames[1-privatedata.rangeaxisindex]
1147 privatedata.vfromvalue = graph.axes[valueaxisname].convert(self.fromvalue)
1148 privatedata.vfromvaluecut = 0
1149 if privatedata.vfromvalue < 0:
1150 privatedata.vfromvalue = 0
1151 privatedata.vfromvaluecut = -1
1152 if privatedata.vfromvalue > 1:
1153 privatedata.vfromvalue = 1
1154 privatedata.vfromvaluecut = 1
1155 if self.frompathattrs is not None and privatedata.insertfrompath:
1156 graph.stroke(graph.axes[valueaxisname].vgridpath(privatedata.vfromvalue),
1157 self.defaultfrompathattrs + self.frompathattrs)
1158 else:
1159 privatedata.vfromvalue = 0
1161 def drawpoint(self, privatedata, sharedata, graph, point):
1162 if privatedata.autohistogram:
1163 # automatic range handling
1164 privatedata.count += 1
1165 if privatedata.count == 2:
1166 if privatedata.rangeaxisindex:
1167 privatedata.vrange = sharedata.vpos[1] - privatedata.lastvpos[1]
1168 self.drawvalue(privatedata, sharedata, graph,
1169 privatedata.lastvpos[1] - self.autohistogrampointpos*privatedata.vrange,
1170 privatedata.lastvpos[1] + (1-self.autohistogrampointpos)*privatedata.vrange,
1171 privatedata.lastvpos[0])
1172 else:
1173 privatedata.vrange = sharedata.vpos[0] - privatedata.lastvpos[0]
1174 self.drawvalue(privatedata, sharedata, graph,
1175 privatedata.lastvpos[0] - self.autohistogrampointpos*privatedata.vrange,
1176 privatedata.lastvpos[0] + (1-self.autohistogrampointpos)*privatedata.vrange,
1177 privatedata.lastvpos[1])
1178 elif privatedata.count > 2:
1179 if privatedata.rangeaxisindex:
1180 vrange = sharedata.vpos[1] - privatedata.lastvpos[1]
1181 else:
1182 vrange = sharedata.vpos[0] - privatedata.lastvpos[0]
1183 if abs(privatedata.vrange - vrange) > self.epsilon:
1184 raise ValueError("equal steps (in graph coordinates) needed for automatic width calculation")
1185 if privatedata.count > 1:
1186 if privatedata.rangeaxisindex:
1187 self.drawvalue(privatedata, sharedata, graph,
1188 sharedata.vpos[1] - self.autohistogrampointpos*privatedata.vrange,
1189 sharedata.vpos[1] + (1-self.autohistogrampointpos)*privatedata.vrange,
1190 sharedata.vpos[0])
1191 else:
1192 self.drawvalue(privatedata, sharedata, graph,
1193 sharedata.vpos[0] - self.autohistogrampointpos*privatedata.vrange,
1194 sharedata.vpos[0] + (1-self.autohistogrampointpos)*privatedata.vrange,
1195 sharedata.vpos[1])
1196 privatedata.lastvpos = sharedata.vpos[:]
1197 else:
1198 if privatedata.rangeaxisindex:
1199 self.drawvalue(privatedata, sharedata, graph,
1200 sharedata.vrange[1][0], sharedata.vrange[1][1], sharedata.vpos[0])
1201 else:
1202 self.drawvalue(privatedata, sharedata, graph,
1203 sharedata.vrange[0][0], sharedata.vrange[0][1], sharedata.vpos[1])
1205 def donedrawpoints(self, privatedata, sharedata, graph):
1206 self.drawvalue(privatedata, sharedata, graph, None, None, None)
1207 if privatedata.lineattrs is not None and len(privatedata.path):
1208 graph.draw(privatedata.path, privatedata.lineattrs)
1210 def key_pt(self, privatedata, sharedata, graph, x_pt, y_pt, width_pt, height_pt):
1211 if privatedata.lineattrs is not None:
1212 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)
1215 class barpos(_style):
1217 providesdata = ["vpos", "vposmissing", "vposavailable", "vposvalid", "vbarrange", "barposcolumnnames", "barvalueindex", "lastbarvalue", "stackedbar", "stackedbardraw"]
1219 defaultfrompathattrs = []
1221 def __init__(self, fromvalue=None, frompathattrs=[], epsilon=1e-10):
1222 self.fromvalue = fromvalue
1223 self.frompathattrs = frompathattrs
1224 self.epsilon = epsilon
1226 def columnnames(self, privatedata, sharedata, graph, columnnames):
1227 sharedata.barposcolumnnames = []
1228 sharedata.barvalueindex = None
1229 for dimension, axisnames in enumerate(graph.axesnames):
1230 found = 0
1231 for axisname in axisnames:
1232 if axisname in columnnames:
1233 if sharedata.barvalueindex is not None:
1234 raise ValueError("multiple values")
1235 sharedata.barvalueindex = dimension
1236 sharedata.barposcolumnnames.append(axisname)
1237 found += 1
1238 if (axisname + "name") in columnnames:
1239 sharedata.barposcolumnnames.append(axisname + "name")
1240 found += 1
1241 if found > 1:
1242 raise ValueError("multiple names and value")
1243 if not found:
1244 raise ValueError("value/name missing")
1245 if sharedata.barvalueindex is None:
1246 raise ValueError("missing value")
1247 sharedata.vposmissing = []
1248 return sharedata.barposcolumnnames
1250 def addsubvalue(self, value, subvalue):
1251 try:
1252 value + ""
1253 except:
1254 try:
1255 return value[0], self.addsubvalue(value[1], subvalue)
1256 except:
1257 return value, subvalue
1258 else:
1259 return value, subvalue
1261 def adjustaxis(self, privatedata, sharedata, graph, columnname, data):
1262 try:
1263 i = sharedata.barposcolumnnames.index(columnname)
1264 except ValueError:
1265 pass
1266 else:
1267 if i == sharedata.barvalueindex:
1268 if self.fromvalue is not None:
1269 graph.axes[sharedata.barposcolumnnames[i]].adjustaxis([self.fromvalue])
1270 graph.axes[sharedata.barposcolumnnames[i]].adjustaxis(data)
1271 else:
1272 graph.axes[sharedata.barposcolumnnames[i][:-4]].adjustaxis([self.addsubvalue(x, 0) for x in data])
1273 graph.axes[sharedata.barposcolumnnames[i][:-4]].adjustaxis([self.addsubvalue(x, 1) for x in data])
1275 def selectstyle(self, privatedata, sharedata, graph, selectindex, selecttotal):
1276 privatedata.insertfrompath = selectindex == 0
1278 def initdrawpoints(self, privatedata, sharedata, graph):
1279 sharedata.vpos = [None]*(len(sharedata.barposcolumnnames))
1280 sharedata.vbarrange = [[None for i in xrange(2)] for x in sharedata.barposcolumnnames]
1281 sharedata.stackedbar = sharedata.stackedbardraw = 0
1283 if self.fromvalue is not None:
1284 privatedata.vfromvalue = graph.axes[sharedata.barposcolumnnames[sharedata.barvalueindex][0]].convert(self.fromvalue)
1285 if privatedata.vfromvalue < 0:
1286 privatedata.vfromvalue = 0
1287 if privatedata.vfromvalue > 1:
1288 privatedata.vfromvalue = 1
1289 if self.frompathattrs is not None and privatedata.insertfrompath:
1290 graph.stroke(graph.axes[sharedata.barposcolumnnames[sharedata.barvalueindex][0]].vgridpath(privatedata.vfromvalue),
1291 self.defaultfrompathattrs + self.frompathattrs)
1292 else:
1293 privatedata.vfromvalue = 0
1295 def drawpoint(self, privatedata, sharedata, graph, point):
1296 sharedata.vposavailable = sharedata.vposvalid = 1
1297 for i, barname in enumerate(sharedata.barposcolumnnames):
1298 if i == sharedata.barvalueindex:
1299 sharedata.vbarrange[i][0] = privatedata.vfromvalue
1300 sharedata.lastbarvalue = point[barname]
1301 try:
1302 sharedata.vpos[i] = sharedata.vbarrange[i][1] = graph.axes[barname].convert(sharedata.lastbarvalue)
1303 except (ArithmeticError, ValueError, TypeError):
1304 sharedata.vpos[i] = sharedata.vbarrange[i][1] = None
1305 else:
1306 for j in xrange(2):
1307 try:
1308 sharedata.vbarrange[i][j] = graph.axes[barname[:-4]].convert(self.addsubvalue(point[barname], j))
1309 except (ArithmeticError, ValueError, TypeError):
1310 sharedata.vbarrange[i][j] = None
1311 try:
1312 sharedata.vpos[i] = 0.5*(sharedata.vbarrange[i][0]+sharedata.vbarrange[i][1])
1313 except (ArithmeticError, ValueError, TypeError):
1314 sharedata.vpos[i] = None
1315 if sharedata.vpos[i] is None:
1316 sharedata.vposavailable = sharedata.vposvalid = 0
1317 elif sharedata.vpos[i] < -self.epsilon or sharedata.vpos[i] > 1+self.epsilon:
1318 sharedata.vposvalid = 0
1320 registerdefaultprovider(barpos(), ["vbarrange", "barposcolumnnames", "barvalueindex", "lastbarvalue", "stackedbar", "stackedbardraw"])
1323 class stackedbarpos(_style):
1325 # provides no additional data, but needs some data (and modifies some of them)
1326 needsdata = ["vbarrange", "barposcolumnnames", "barvalueindex", "lastbarvalue", "stackedbar", "stackedbardraw"]
1328 def __init__(self, stackname, addontop=0, epsilon=1e-10):
1329 self.stackname = stackname
1330 self.epsilon = epsilon
1331 self.addontop = addontop
1333 def columnnames(self, privatedata, sharedata, graph, columnnames):
1334 if self.stackname not in columnnames:
1335 raise ValueError("column '%s' missing" % self.stackname)
1336 return [self.stackname]
1338 def adjustaxis(self, privatedata, sharedata, graph, columnname, data):
1339 if columnname == self.stackname:
1340 graph.axes[sharedata.barposcolumnnames[sharedata.barvalueindex]].adjustaxis(data)
1342 def initdrawpoints(self, privatedata, sharedata, graph):
1343 if sharedata.stackedbardraw: # do not count the start bar when not gets painted
1344 sharedata.stackedbar += 1
1346 def drawpoint(self, privatedata, sharedata, graph, point):
1347 sharedata.vbarrange[sharedata.barvalueindex][0] = sharedata.vbarrange[sharedata.barvalueindex][1]
1348 if self.addontop:
1349 try:
1350 sharedata.lastbarvalue += point[self.stackname]
1351 except (ArithmeticError, ValueError, TypeError):
1352 sharedata.lastbarvalue = None
1353 else:
1354 sharedata.lastbarvalue = point[self.stackname]
1355 try:
1356 sharedata.vpos[sharedata.barvalueindex] = sharedata.vbarrange[sharedata.barvalueindex][1] = graph.axes[sharedata.barposcolumnnames[sharedata.barvalueindex]].convert(sharedata.lastbarvalue)
1357 except (ArithmeticError, ValueError, TypeError):
1358 sharedata.vpos[sharedata.barvalueindex] = sharedata.vbarrange[sharedata.barvalueindex][1] = None
1359 sharedata.vposavailable = sharedata.vposvalid = 0
1360 else:
1361 if not sharedata.vposavailable or not sharedata.vposvalid:
1362 sharedata.vposavailable = sharedata.vposvalid = 1
1363 for v in sharedata.vpos:
1364 if v is None:
1365 sharedata.vposavailable = sharedata.vposvalid = 0
1366 break
1367 if v < -self.epsilon or v > 1+self.epsilon:
1368 sharedata.vposvalid = 0
1371 class bar(_style):
1373 needsdata = ["vbarrange"]
1375 defaultbarattrs = [color.gradient.Rainbow, deco.stroked([color.grey.black])]
1377 def __init__(self, barattrs=[]):
1378 self.barattrs = barattrs
1380 def columnnames(self, privatedata, sharedata, graph, columnnames):
1381 if len(graph.axesnames) != 2:
1382 raise TypeError("bar style restricted on two-dimensional graphs")
1383 return []
1385 def selectstyle(self, privatedata, sharedata, graph, selectindex, selecttotal):
1386 privatedata.barattrs = attr.selectattrs(self.defaultbarattrs + self.barattrs, selectindex, selecttotal)
1388 def initdrawpoints(self, privatedata, sharedata, graph):
1389 privatedata.rectcanvas = graph.insert(canvas.canvas())
1390 sharedata.stackedbardraw = 1
1391 privatedata.stackedbar = sharedata.stackedbar
1393 def drawpointfill(self, privatedata, p):
1394 if p:
1395 privatedata.rectcanvas.fill(p, privatedata.barattrs)
1397 def drawpoint(self, privatedata, sharedata, graph, point):
1398 xvmin = sharedata.vbarrange[0][0]
1399 xvmax = sharedata.vbarrange[0][1]
1400 yvmin = sharedata.vbarrange[1][0]
1401 yvmax = sharedata.vbarrange[1][1]
1402 try:
1403 if xvmin > xvmax:
1404 xvmin, xvmax = xvmax, xvmin
1405 except:
1406 pass
1407 try:
1408 if yvmin > yvmax:
1409 yvmin, yvmax = yvmax, yvmin
1410 except:
1411 pass
1412 if (xvmin is not None and xvmin < 1 and
1413 xvmax is not None and xvmax > 0 and
1414 yvmin is not None and yvmin < 1 and
1415 yvmax is not None and yvmax > 0):
1416 if xvmin < 0:
1417 xvmin = 0
1418 elif xvmax > 1:
1419 xvmax = 1
1420 if yvmin < 0:
1421 yvmin = 0
1422 elif yvmax > 1:
1423 yvmax = 1
1424 p = graph.vgeodesic(xvmin, yvmin, xvmax, yvmin)
1425 p.append(graph.vgeodesic_el(xvmax, yvmin, xvmax, yvmax))
1426 p.append(graph.vgeodesic_el(xvmax, yvmax, xvmin, yvmax))
1427 p.append(graph.vgeodesic_el(xvmin, yvmax, xvmin, yvmin))
1428 p.append(path.closepath())
1429 self.drawpointfill(privatedata, p)
1430 else:
1431 self.drawpointfill(privatedata, None)
1433 def key_pt(self, privatedata, sharedata, graph, x_pt, y_pt, width_pt, height_pt):
1434 selectindex = privatedata.stackedbar
1435 selecttotal = sharedata.stackedbar + 1
1436 graph.fill(path.rect_pt(x_pt + width_pt*selectindex/float(selecttotal), y_pt, width_pt/float(selecttotal), height_pt), privatedata.barattrs)
1439 class changebar(bar):
1441 def selectstyle(self, privatedata, sharedata, graph, selectindex, selecttotal):
1442 if selecttotal != 1:
1443 raise RuntimeError("Changebar can't change its appearance. Thus you can't use it to plot several bars side by side on a subaxis.")
1445 def initdrawpoints(self, privatedata, sharedata, graph):
1446 bar.initdrawpoints(self, privatedata, sharedata, graph)
1447 privatedata.bars = []
1449 def drawpointfill(self, privatedata, p):
1450 privatedata.bars.append(p)
1452 def donedrawpoints(self, privatedata, sharedata, graph):
1453 selecttotal = len(privatedata.bars)
1454 for selectindex, p in enumerate(privatedata.bars):
1455 if p:
1456 barattrs = attr.selectattrs(self.defaultbarattrs + self.barattrs, selectindex, selecttotal)
1457 privatedata.rectcanvas.fill(p, barattrs)
1459 def key_pt(self, privatedata, sharedata, graph, x_pt, y_pt, width_pt, height_pt):
1460 raise RuntimeError("Style currently doesn't provide a graph key")
1463 class surface(_line):
1465 needsdata = ["vpos", "vposmissing", "vposavailable", "vposvalid"]
1467 def __init__(self, colorname="color", gradient=color.gradient.Rainbow,
1468 mincolor=None, maxcolor=None, index1=0, index2=1, epsilon=1e-10):
1469 self.colorname = colorname
1470 self.gradient = gradient
1471 self.mincolor = mincolor
1472 self.maxcolor = maxcolor
1473 self.index1 = index1
1474 self.index2 = index2
1475 self.epsilon = epsilon
1477 def columnnames(self, privatedata, sharedata, graph, columnnames):
1478 privatedata.colorize = self.colorname in columnnames
1479 return privatedata.colorize and [self.colorname] or [] + _line.columnnames(self, privatedata, sharedata, graph, columnnames)
1481 def selectstyle(self, privatedata, sharedata, graph, selectindex, selecttotal):
1482 if selecttotal != 1:
1483 raise RuntimeError("Surface can't change its appearance.")
1485 def initdrawpoints(self, privatedata, sharedata, graph):
1486 privatedata.values1 = {}
1487 privatedata.values2 = {}
1488 privatedata.data12 = {}
1489 privatedata.data21 = {}
1490 privatedata.colors = {}
1491 privatedata.mincolor = privatedata.maxcolor = None
1493 def drawpoint(self, privatedata, sharedata, graph, point):
1494 if sharedata.vposavailable:
1495 value1 = sharedata.vpos[self.index1]
1496 value2 = sharedata.vpos[self.index2]
1497 privatedata.values1.setdefault(value1, 1)
1498 privatedata.values2.setdefault(value2, 1)
1499 data = sharedata.vposavailable, sharedata.vposvalid, sharedata.vpos[:]
1500 privatedata.data12.setdefault(value1, {})[value2] = data
1501 privatedata.data21.setdefault(value2, {})[value1] = data
1502 if privatedata.colorize:
1503 try:
1504 color = point[self.colorname] + 0
1505 except:
1506 pass
1507 else:
1508 privatedata.colors.setdefault(value1, {})[value2] = color
1509 if privatedata.mincolor is None or color < privatedata.mincolor:
1510 privatedata.mincolor = color
1511 if privatedata.mincolor is None or privatedata.maxcolor < color:
1512 privatedata.maxcolor = color
1514 def donedrawpoints(self, privatedata, sharedata, graph):
1515 values1 = privatedata.values1.keys()
1516 values1.sort()
1517 values2 = privatedata.values2.keys()
1518 values2.sort()
1519 if self.mincolor is not None:
1520 mincolor = self.mincolor
1521 if self.maxcolor is not None:
1522 maxcolor = self.maxcolor
1523 for value1 in values1:
1524 data2 = privatedata.data12[value1]
1525 self.initpointstopath(privatedata)
1526 for value2 in values2:
1527 try:
1528 data = data2[value2]
1529 except KeyError:
1530 self.addinvalid(privatedata)
1531 else:
1532 self.addpoint(privatedata, graph.vpos_pt, *data)
1533 p = self.donepointstopath(privatedata)
1534 if len(p):
1535 graph.stroke(p)
1536 for value2 in values2:
1537 data1 = privatedata.data21[value2]
1538 self.initpointstopath(privatedata)
1539 for value1 in values1:
1540 try:
1541 data = data1[value1]
1542 except KeyError:
1543 self.addinvalid(privatedata)
1544 else:
1545 self.addpoint(privatedata, graph.vpos_pt, *data)
1546 p = self.donepointstopath(privatedata)
1547 if len(p):
1548 graph.stroke(p)
1549 if privatedata.colorize:
1550 for value1 in values1:
1551 data2 = privatedata.data12[value1]
1552 self.initpointstopath(privatedata)
1553 for value2 in values2:
1554 try:
1555 x_pt, y_pt = graph.vpos_pt(*data2[value2][2])
1556 except KeyError:
1557 pass
1558 else:
1559 try:
1560 color = privatedata.colors[value1][value2]
1561 except KeyError:
1562 pass
1563 else:
1564 color = (color - privatedata.mincolor) / float(privatedata.maxcolor - privatedata.mincolor)
1565 graph.fill(path.circle_pt(x_pt, y_pt, 1), [self.gradient.getcolor(color)])
1567 def key_pt(self, privatedata, sharedata, graph, x_pt, y_pt, width_pt, height_pt):
1568 raise NotImplementedError