- plotitem acts as a container for data now (i.e. data instances
[PyX/mjg.git] / pyx / graph / style.py
blob1cc8a140d5b519045d1ef4fd0ecc1d87d42d6fb6
1 #!/usr/bin/env python
2 # -*- coding: ISO-8859-1 -*-
5 # Copyright (C) 2002-2004 Jörg Lehmann <joergl@users.sourceforge.net>
6 # Copyright (C) 2003-2004 Michael Schindler <m-schindler@users.sourceforge.net>
7 # Copyright (C) 2002-2004 André Wobst <wobsta@users.sourceforge.net>
9 # This file is part of PyX (http://pyx.sourceforge.net/).
11 # PyX is free software; you can redistribute it and/or modify
12 # it under the terms of the GNU General Public License as published by
13 # the Free Software Foundation; either version 2 of the License, or
14 # (at your option) any later version.
16 # PyX is distributed in the hope that it will be useful,
17 # but WITHOUT ANY WARRANTY; without even the implied warranty of
18 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 # GNU General Public License for more details.
21 # You should have received a copy of the GNU General Public License
22 # along with PyX; if not, write to the Free Software
23 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
26 import math
27 from pyx import attr, deco, style, color, unit, canvas, path
28 from pyx import text as textmodule
31 class _style:
32 """Interface class for graph styles
34 Each graph style must support the methods described in this
35 class. However, since a graph style might not need to perform
36 actions on all the various events, it does not need to overwrite
37 all methods of this base class (e.g. this class is not an abstract
38 class in any respect).
40 A style should never store private data by istance variables
41 (i.e. accessing self), but it should use the sharedata and privatedata
42 instances instead. A style instance can be used multiple times with
43 different sharedata and privatedata instances at the very same time.
44 The sharedata and privatedata instances act as data containers and
45 sharedata allows for sharing information across several styles.
47 Every style contains two class variables, which are not to be
48 modified:
49 - providesdata is a list of variable names a style offers via
50 the sharedata instance. This list is used to determine whether
51 all needs of subsequent styles are fullfilled. Otherwise
52 getdefaultprovider should return a proper style to be used.
53 - needsdata is a list of variable names the style needs to access in the
54 sharedata instance.
55 """
57 providesdata = [] # by default, we provide nothing
58 needsdata = [] # and do not depend on anything
60 def columns(self, privatedata, sharedata, graph, columns):
61 """Set column information
63 This method is used setup the column information to be
64 accessible to the style later on. The style should analyse
65 the list of strings columns, which contain the column names
66 of the data. The method should return a list of column names
67 which the style will make use of."""
68 return []
70 def selectstyle(self, privatedata, sharedata, graph, selectindex, selecttotal):
71 """Select stroke/fill attributes
73 This method is called to allow for the selection of
74 changable attributes of a style."""
75 pass
77 def adjustaxis(self, privatedata, sharedata, graph, column, data, index):
78 """Adjust axis range
80 This method is called in order to adjust the axis range to
81 the provided data. Column is the name of the column (each
82 style is subsequently called for all column names). If index
83 is not None, data is a list of points and index is the index
84 of the column within a point. Otherwise data is already the
85 axis data. Note, that data might be different for different
86 columns, e.i. data might come from various places and is
87 combined without copying but keeping references."""
88 pass
90 def initdrawpoints(self, privatedata, sharedata, graph):
91 """Initialize drawing of data
93 This method might be used to initialize the drawing of data."""
94 pass
96 def drawpoint(self, privatedata, sharedata, graph):
97 """Draw data
99 This method is called for each data point. The data is
100 available in the dictionary sharedata.point. The dictionary
101 keys are the column names."""
102 pass
104 def donedrawpoints(self, privatedata, sharedata, graph):
105 """Finalize drawing of data
107 This method is called after the last data point was
108 drawn using the drawpoint method above."""
109 pass
111 def key_pt(self, privatedata, sharedata, graph, x_pt, y_pt, width_pt, height_pt, dy_pt):
112 """Draw graph key
114 This method draws a key for the style to graph at the given
115 position x_pt, y_pt indicating the lower left corner of the
116 given area width_pt, height_pt. The style might draw several
117 key entries shifted vertically by dy_pt. The method returns
118 the number of key entries or None, when nothing was drawn."""
119 return None
122 # The following two methods are used to register and get a default provider
123 # for keys. A key is a variable name in sharedata. A provider is a style
124 # which creates variables in sharedata.
126 _defaultprovider = {}
128 def registerdefaultprovider(style, keys):
129 """sets a style as a default creator for sharedata variables 'keys'"""
130 assert not len(style.needsdata), "currently we state, that a style should not depend on other sharedata variables"
131 for key in keys:
132 assert key in style.providesdata, "key not provided by style"
133 # we might allow for overwriting the defaults, i.e. the following is not checked:
134 # assert key in _defaultprovider.keys(), "default provider already registered for key"
135 _defaultprovider[key] = style
137 def getdefaultprovider(key):
138 """returns a style, which acts as a default creator for the
139 sharedata variable 'key'"""
140 return _defaultprovider[key]
143 class _pos(_style):
145 providesdata = ["vpos", "vposmissing", "vposavailable", "vposvalid"]
147 def __init__(self, epsilon=1e-10):
148 self.epsilon = epsilon
150 def columns(self, privatedata, sharedata, graph, columns):
151 privatedata.pointposcolumns = []
152 sharedata.vposmissing = []
153 for count, axisnames in enumerate(graph.axesnames):
154 for axisname in axisnames:
155 for column in columns:
156 if axisname == column:
157 privatedata.pointposcolumns.append(column)
158 if len(privatedata.pointposcolumns) + len(sharedata.vposmissing) > count+1:
159 raise ValueError("multiple axes per graph dimension")
160 elif len(privatedata.pointposcolumns) + len(sharedata.vposmissing) < count+1:
161 sharedata.vposmissing.append(count)
162 return privatedata.pointposcolumns
164 def adjustaxis(self, privatedata, sharedata, graph, column, data, index):
165 if column in privatedata.pointposcolumns:
166 graph.axes[column].adjustrange(data, index)
168 def initdrawpoints(self, privatedata, sharedata, graph):
169 sharedata.vpos = [None]*(len(privatedata.pointposcolumns) + len(sharedata.vposmissing))
170 privatedata.pointpostmplist = [[column, index, graph.axes[column]] # temporarily used by drawpoint only
171 for index, column in enumerate(privatedata.pointposcolumns)]
172 for missing in sharedata.vposmissing:
173 for pointpostmp in privatedata.pointpostmplist:
174 if pointpostmp[1] >= missing:
175 pointpostmp[1] += 1
177 def drawpoint(self, privatedata, sharedata, graph):
178 sharedata.vposavailable = 1 # valid position (but might be outside of the graph)
179 sharedata.vposvalid = 1 # valid position inside the graph
180 for column, index, axis in privatedata.pointpostmplist:
181 try:
182 v = axis.convert(sharedata.point[column])
183 except (ArithmeticError, ValueError, TypeError):
184 sharedata.vposavailable = sharedata.vposvalid = 0
185 sharedata.vpos[index] = None
186 else:
187 if v < -self.epsilon or v > 1+self.epsilon:
188 sharedata.vposvalid = 0
189 sharedata.vpos[index] = v
192 registerdefaultprovider(_pos(), _pos.providesdata)
195 class _range(_style):
197 providesdata = ["vrange", "vrangemissing", "vrangeminmissing", "vrangemaxmissing"]
199 # internal bit masks
200 mask_value = 1
201 mask_min = 2
202 mask_max = 4
203 mask_dmin = 8
204 mask_dmax = 16
205 mask_d = 32
207 def __init__(self, epsilon=1e-10):
208 self.epsilon = epsilon
210 def columns(self, privatedata, sharedata, graph, columns):
211 def numberofbits(mask):
212 if not mask:
213 return 0
214 if mask & 1:
215 return numberofbits(mask >> 1) + 1
216 else:
217 return numberofbits(mask >> 1)
218 usecolumns = []
219 privatedata.rangeposcolumns = []
220 sharedata.vrangemissing = []
221 sharedata.vrangeminmissing = []
222 sharedata.vrangemaxmissing = []
223 privatedata.rangeposdeltacolumns = {} # temporarily used by adjustaxis only
224 for count, axisnames in enumerate(graph.axesnames):
225 for axisname in axisnames:
226 mask = 0
227 for column in columns:
228 addusecolumns = 1
229 if axisname == column:
230 mask += self.mask_value
231 elif axisname + "min" == column:
232 mask += self.mask_min
233 elif axisname + "max" == column:
234 mask += self.mask_max
235 elif "d" + axisname + "min" == column:
236 mask += self.mask_dmin
237 elif "d" + axisname + "max" == column:
238 mask += self.mask_dmax
239 elif "d" + axisname == column:
240 mask += self.mask_d
241 else:
242 addusecolumns = 0
243 if addusecolumns:
244 usecolumns.append(column)
245 if mask & (self.mask_min | self.mask_max | self.mask_dmin | self.mask_dmax | self.mask_d):
246 if (numberofbits(mask & (self.mask_min | self.mask_dmin | self.mask_d)) > 1 or
247 numberofbits(mask & (self.mask_max | self.mask_dmax | self.mask_d)) > 1):
248 raise ValueError("multiple errorbar definition")
249 if mask & (self.mask_dmin | self.mask_dmax | self.mask_d):
250 if not (mask & self.mask_value):
251 raise ValueError("missing value for delta")
252 privatedata.rangeposdeltacolumns[axisname] = {}
253 privatedata.rangeposcolumns.append((axisname, mask))
254 elif mask == self.mask_value:
255 usecolumns = usecolumns[:-1]
256 if len(privatedata.rangeposcolumns) + len(sharedata.vrangemissing) > count+1:
257 raise ValueError("multiple axes per graph dimension")
258 elif len(privatedata.rangeposcolumns) + len(sharedata.vrangemissing) < count+1:
259 sharedata.vrangemissing.append(count)
260 else:
261 if not (privatedata.rangeposcolumns[-1][1] & (self.mask_min | self.mask_dmin | self.mask_d)):
262 sharedata.vrangeminmissing.append(count)
263 if not (privatedata.rangeposcolumns[-1][1] & (self.mask_max | self.mask_dmax | self.mask_d)):
264 sharedata.vrangemaxmissing.append(count)
265 return usecolumns
267 def adjustaxis(self, privatedata, sharedata, graph, column, data, index):
268 if column in [c + "min" for c, m in privatedata.rangeposcolumns if m & self.mask_min]:
269 graph.axes[column[:-3]].adjustrange(data, index)
270 if column in [c + "max" for c, m in privatedata.rangeposcolumns if m & self.mask_max]:
271 graph.axes[column[:-3]].adjustrange(data, index)
273 # delta handling: fill rangeposdeltacolumns
274 if column in [c for c, m in privatedata.rangeposcolumns if m & (self.mask_dmin | self.mask_dmax | self.mask_d)]:
275 privatedata.rangeposdeltacolumns[column][self.mask_value] = data, index
276 if column in ["d" + c + "min" for c, m in privatedata.rangeposcolumns if m & self.mask_dmin]:
277 privatedata.rangeposdeltacolumns[column[1:-3]][self.mask_dmin] = data, index
278 if column in ["d" + c + "max" for c, m in privatedata.rangeposcolumns if m & self.mask_dmax]:
279 privatedata.rangeposdeltacolumns[column[1:-3]][self.mask_dmax] = data, index
280 if column in ["d" + c for c, m in privatedata.rangeposcolumns if m & self.mask_d]:
281 privatedata.rangeposdeltacolumns[column[1:]][self.mask_d] = data, index
283 # delta handling: process rangeposdeltacolumns
284 for c, d in privatedata.rangeposdeltacolumns.items():
285 if d.has_key(self.mask_value):
286 for k in d.keys():
287 if k != self.mask_value:
288 if k & (self.mask_dmin | self.mask_d):
289 graph.axes[c].adjustrange(d[self.mask_value][0], d[self.mask_value][1],
290 deltamindata=d[k][0], deltaminindex=d[k][1])
291 if k & (self.mask_dmax | self.mask_d):
292 graph.axes[c].adjustrange(d[self.mask_value][0], d[self.mask_value][1],
293 deltamaxdata=d[k][0], deltamaxindex=d[k][1])
294 del d[k]
296 def initdrawpoints(self, privatedata, sharedata, graph):
297 sharedata.vrange = [[None for x in range(2)] for y in privatedata.rangeposcolumns + sharedata.vrangemissing]
298 privatedata.rangepostmplist = [[column, mask, index, graph.axes[column]] # temporarily used by drawpoint only
299 for index, (column, mask) in enumerate(privatedata.rangeposcolumns)]
300 for missing in sharedata.vrangemissing:
301 for rangepostmp in privatedata.rangepostmplist:
302 if rangepostmp[2] >= missing:
303 rangepostmp[2] += 1
305 def drawpoint(self, privatedata, sharedata, graph):
306 for column, mask, index, axis in privatedata.rangepostmplist:
307 try:
308 if mask & self.mask_min:
309 sharedata.vrange[index][0] = axis.convert(sharedata.point[column + "min"])
310 if mask & self.mask_dmin:
311 sharedata.vrange[index][0] = axis.convert(sharedata.point[column] - sharedata.point["d" + column + "min"])
312 if mask & self.mask_d:
313 sharedata.vrange[index][0] = axis.convert(sharedata.point[column] - sharedata.point["d" + column])
314 except (ArithmeticError, ValueError, TypeError):
315 sharedata.vrange[index][0] = None
316 try:
317 if mask & self.mask_max:
318 sharedata.vrange[index][1] = axis.convert(sharedata.point[column + "max"])
319 if mask & self.mask_dmax:
320 sharedata.vrange[index][1] = axis.convert(sharedata.point[column] + sharedata.point["d" + column + "max"])
321 if mask & self.mask_d:
322 sharedata.vrange[index][1] = axis.convert(sharedata.point[column] + sharedata.point["d" + column])
323 except (ArithmeticError, ValueError, TypeError):
324 sharedata.vrange[index][1] = None
326 # some range checks for data consistency
327 if (sharedata.vrange[index][0] is not None and sharedata.vrange[index][1] is not None and
328 sharedata.vrange[index][0] > sharedata.vrange[index][1] + self.epsilon):
329 raise ValueError("negative errorbar range")
330 #if (sharedata.vrange[index][0] is not None and sharedata.vpos[index] is not None and
331 # sharedata.vrange[index][0] > sharedata.vpos[index] + self.epsilon):
332 # raise ValueError("negative minimum errorbar")
333 #if (sharedata.vrange[index][1] is not None and sharedata.vpos[index] is not None and
334 # sharedata.vrange[index][1] < sharedata.vpos[index] - self.epsilon):
335 # raise ValueError("negative maximum errorbar")
338 registerdefaultprovider(_range(), _range.providesdata)
341 def _crosssymbol(c, x_pt, y_pt, size_pt, attrs):
342 c.draw(path.path(path.moveto_pt(x_pt-0.5*size_pt, y_pt-0.5*size_pt),
343 path.lineto_pt(x_pt+0.5*size_pt, y_pt+0.5*size_pt),
344 path.moveto_pt(x_pt-0.5*size_pt, y_pt+0.5*size_pt),
345 path.lineto_pt(x_pt+0.5*size_pt, y_pt-0.5*size_pt)), attrs)
347 def _plussymbol(c, x_pt, y_pt, size_pt, attrs):
348 c.draw(path.path(path.moveto_pt(x_pt-0.707106781*size_pt, y_pt),
349 path.lineto_pt(x_pt+0.707106781*size_pt, y_pt),
350 path.moveto_pt(x_pt, y_pt-0.707106781*size_pt),
351 path.lineto_pt(x_pt, y_pt+0.707106781*size_pt)), attrs)
353 def _squaresymbol(c, x_pt, y_pt, size_pt, attrs):
354 c.draw(path.path(path.moveto_pt(x_pt-0.5*size_pt, y_pt-0.5*size_pt),
355 path.lineto_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.lineto_pt(x_pt-0.5*size_pt, y_pt+0.5*size_pt),
358 path.closepath()), attrs)
360 def _trianglesymbol(c, x_pt, y_pt, size_pt, attrs):
361 c.draw(path.path(path.moveto_pt(x_pt-0.759835685*size_pt, y_pt-0.438691337*size_pt),
362 path.lineto_pt(x_pt+0.759835685*size_pt, y_pt-0.438691337*size_pt),
363 path.lineto_pt(x_pt, y_pt+0.877382675*size_pt),
364 path.closepath()), attrs)
366 def _circlesymbol(c, x_pt, y_pt, size_pt, attrs):
367 c.draw(path.path(path.arc_pt(x_pt, y_pt, 0.564189583*size_pt, 0, 360),
368 path.closepath()), attrs)
370 def _diamondsymbol(c, x_pt, y_pt, size_pt, attrs):
371 c.draw(path.path(path.moveto_pt(x_pt-0.537284965*size_pt, y_pt),
372 path.lineto_pt(x_pt, y_pt-0.930604859*size_pt),
373 path.lineto_pt(x_pt+0.537284965*size_pt, y_pt),
374 path.lineto_pt(x_pt, y_pt+0.930604859*size_pt),
375 path.closepath()), attrs)
378 class _styleneedingpointpos(_style):
380 needsdata = ["vposmissing"]
382 def columns(self, privatedata, sharedata, graph, columns):
383 if len(sharedata.vposmissing):
384 raise ValueError("position columns incomplete")
385 return []
388 class symbol(_styleneedingpointpos):
390 needsdata = ["vpos", "vposmissing", "vposvalid"]
392 # insert symbols
393 # note, that statements like cross = _crosssymbol are
394 # invalid, since the would lead to unbound methods, but
395 # a single entry changeable list does the trick
396 cross = attr.changelist([_crosssymbol])
397 plus = attr.changelist([_plussymbol])
398 square = attr.changelist([_squaresymbol])
399 triangle = attr.changelist([_trianglesymbol])
400 circle = attr.changelist([_circlesymbol])
401 diamond = attr.changelist([_diamondsymbol])
403 changecross = attr.changelist([_crosssymbol, _plussymbol, _squaresymbol, _trianglesymbol, _circlesymbol, _diamondsymbol])
404 changeplus = attr.changelist([_plussymbol, _squaresymbol, _trianglesymbol, _circlesymbol, _diamondsymbol, cross])
405 changesquare = attr.changelist([_squaresymbol, _trianglesymbol, _circlesymbol, _diamondsymbol, cross, _plussymbol])
406 changetriangle = attr.changelist([_trianglesymbol, _circlesymbol, _diamondsymbol, cross, _plussymbol, _squaresymbol])
407 changecircle = attr.changelist([_circlesymbol, _diamondsymbol, cross, _plussymbol, _squaresymbol, _trianglesymbol])
408 changediamond = attr.changelist([_diamondsymbol, cross, _plussymbol, _squaresymbol, _trianglesymbol, _circlesymbol])
409 changesquaretwice = attr.changelist([_squaresymbol, _squaresymbol, _trianglesymbol, _trianglesymbol, _circlesymbol, _circlesymbol, _diamondsymbol, _diamondsymbol])
410 changetriangletwice = attr.changelist([_trianglesymbol, _trianglesymbol, _circlesymbol, _circlesymbol, _diamondsymbol, _diamondsymbol, _squaresymbol, _squaresymbol])
411 changecircletwice = attr.changelist([_circlesymbol, _circlesymbol, _diamondsymbol, _diamondsymbol, _squaresymbol, _squaresymbol, _trianglesymbol, _trianglesymbol])
412 changediamondtwice = attr.changelist([_diamondsymbol, _diamondsymbol, _squaresymbol, _squaresymbol, _trianglesymbol, _trianglesymbol, _circlesymbol, _circlesymbol])
414 changestrokedfilled = attr.changelist([deco.stroked, deco.filled])
415 changefilledstroked = attr.changelist([deco.filled, deco.stroked])
417 defaultsymbolattrs = [deco.stroked]
419 def __init__(self, symbol=changecross, size=0.2*unit.v_cm, symbolattrs=[]):
420 self.symbol = symbol
421 self.size = size
422 self.symbolattrs = symbolattrs
424 def selectstyle(self, privatedata, sharedata, graph, selectindex, selecttotal):
425 privatedata.symbol = attr.selectattr(self.symbol, selectindex, selecttotal)
426 privatedata.size_pt = unit.topt(attr.selectattr(self.size, selectindex, selecttotal))
427 if self.symbolattrs is not None:
428 privatedata.symbolattrs = attr.selectattrs(self.defaultsymbolattrs + self.symbolattrs, selectindex, selecttotal)
429 else:
430 privatedata.symbolattrs = None
432 def initdrawpoints(self, privatedata, sharedata, graph):
433 privatedata.symbolcanvas = graph.insert(canvas.canvas())
435 def drawpoint(self, privatedata, sharedata, graph):
436 if sharedata.vposvalid and privatedata.symbolattrs is not None:
437 xpos, ypos = graph.vpos_pt(*sharedata.vpos)
438 privatedata.symbol(privatedata.symbolcanvas, xpos, ypos, privatedata.size_pt, privatedata.symbolattrs)
440 def key_pt(self, privatedata, sharedata, graph, x_pt, y_pt, width_pt, height_pt, dy_pt):
441 if privatedata.symbolattrs is not None:
442 privatedata.symbol(graph, x_pt+0.5*width_pt, y_pt+0.5*height_pt, privatedata.size_pt, privatedata.symbolattrs)
443 return 1
446 class line(_styleneedingpointpos):
448 needsdata = ["vpos", "vposmissing", "vposavailable", "vposvalid"]
450 changelinestyle = attr.changelist([style.linestyle.solid,
451 style.linestyle.dashed,
452 style.linestyle.dotted,
453 style.linestyle.dashdotted])
455 defaultlineattrs = [changelinestyle]
457 def __init__(self, lineattrs=[]):
458 self.lineattrs = lineattrs
460 def selectstyle(self, privatedata, sharedata, graph, selectindex, selecttotal):
461 privatedata.lineattrs = attr.selectattrs(self.defaultlineattrs + self.lineattrs, selectindex, selecttotal)
463 def initdrawpoints(self, privatedata, sharedata, graph):
464 if privatedata.lineattrs is not None:
465 privatedata.linecanvas = graph.insert(canvas.canvas())
466 privatedata.linecanvas.set(privatedata.lineattrs)
467 privatedata.path = path.path()
468 privatedata.linebasepoints = []
469 privatedata.lastvpos = None
471 def addpointstopath(self, privatedata, sharedata):
472 # add baselinepoints to privatedata.path
473 if len(privatedata.linebasepoints) > 1:
474 privatedata.path.append(path.moveto_pt(*privatedata.linebasepoints[0]))
475 if len(privatedata.linebasepoints) > 2:
476 privatedata.path.append(path.multilineto_pt(privatedata.linebasepoints[1:]))
477 else:
478 privatedata.path.append(path.lineto_pt(*privatedata.linebasepoints[1]))
479 privatedata.linebasepoints = []
481 def drawpoint(self, privatedata, sharedata, graph):
482 # append linebasepoints
483 if sharedata.vposavailable:
484 if len(privatedata.linebasepoints):
485 # the last point was inside the graph
486 if sharedata.vposvalid: # shortcut for the common case
487 privatedata.linebasepoints.append(graph.vpos_pt(*sharedata.vpos))
488 else:
489 # cut end
490 cut = 1
491 for vstart, vend in zip(privatedata.lastvpos, sharedata.vpos):
492 newcut = None
493 if vend > 1:
494 # 1 = vstart + (vend - vstart) * cut
495 try:
496 newcut = (1 - vstart)/(vend - vstart)
497 except ArithmeticError:
498 break
499 if vend < 0:
500 # 0 = vstart + (vend - vstart) * cut
501 try:
502 newcut = - vstart/(vend - vstart)
503 except ArithmeticError:
504 break
505 if newcut is not None and newcut < cut:
506 cut = newcut
507 else:
508 cutvpos = []
509 for vstart, vend in zip(privatedata.lastvpos, sharedata.vpos):
510 cutvpos.append(vstart + (vend - vstart) * cut)
511 privatedata.linebasepoints.append(graph.vpos_pt(*cutvpos))
512 self.addpointstopath(privatedata, sharedata)
513 else:
514 # the last point was outside the graph
515 if privatedata.lastvpos is not None:
516 if sharedata.vposvalid:
517 # cut beginning
518 cut = 0
519 for vstart, vend in zip(privatedata.lastvpos, sharedata.vpos):
520 newcut = None
521 if vstart > 1:
522 # 1 = vstart + (vend - vstart) * cut
523 try:
524 newcut = (1 - vstart)/(vend - vstart)
525 except ArithmeticError:
526 break
527 if vstart < 0:
528 # 0 = vstart + (vend - vstart) * cut
529 try:
530 newcut = - vstart/(vend - vstart)
531 except ArithmeticError:
532 break
533 if newcut is not None and newcut > cut:
534 cut = newcut
535 else:
536 cutvpos = []
537 for vstart, vend in zip(privatedata.lastvpos, sharedata.vpos):
538 cutvpos.append(vstart + (vend - vstart) * cut)
539 privatedata.linebasepoints.append(graph.vpos_pt(*cutvpos))
540 privatedata.linebasepoints.append(graph.vpos_pt(*sharedata.vpos))
541 else:
542 # sometimes cut beginning and end
543 cutfrom = 0
544 cutto = 1
545 for vstart, vend in zip(privatedata.lastvpos, sharedata.vpos):
546 newcutfrom = None
547 if vstart > 1:
548 if vend > 1:
549 break
550 # 1 = vstart + (vend - vstart) * cutfrom
551 try:
552 newcutfrom = (1 - vstart)/(vend - vstart)
553 except ArithmeticError:
554 break
555 if vstart < 0:
556 if vend < 0:
557 break
558 # 0 = vstart + (vend - vstart) * cutfrom
559 try:
560 newcutfrom = - vstart/(vend - vstart)
561 except ArithmeticError:
562 break
563 if newcutfrom is not None and newcutfrom > cutfrom:
564 cutfrom = newcutfrom
565 newcutto = None
566 if vend > 1:
567 # 1 = vstart + (vend - vstart) * cutto
568 try:
569 newcutto = (1 - vstart)/(vend - vstart)
570 except ArithmeticError:
571 break
572 if vend < 0:
573 # 0 = vstart + (vend - vstart) * cutto
574 try:
575 newcutto = - vstart/(vend - vstart)
576 except ArithmeticError:
577 break
578 if newcutto is not None and newcutto < cutto:
579 cutto = newcutto
580 else:
581 if cutfrom < cutto:
582 cutfromvpos = []
583 cuttovpos = []
584 for vstart, vend in zip(privatedata.lastvpos, sharedata.vpos):
585 cutfromvpos.append(vstart + (vend - vstart) * cutfrom)
586 cuttovpos.append(vstart + (vend - vstart) * cutto)
587 privatedata.linebasepoints.append(graph.vpos_pt(*cutfromvpos))
588 privatedata.linebasepoints.append(graph.vpos_pt(*cuttovpos))
589 self.addpointstopath(privatedata, sharedata)
590 privatedata.lastvpos = sharedata.vpos[:]
591 else:
592 if len(privatedata.linebasepoints) > 1:
593 self.addpointstopath(privatedata, sharedata)
594 privatedata.lastvpos = None
596 def donedrawpoints(self, privatedata, sharedata, graph):
597 if len(privatedata.linebasepoints) > 1:
598 self.addpointstopath(privatedata, sharedata)
599 if privatedata.lineattrs is not None and len(privatedata.path.path):
600 privatedata.linecanvas.stroke(privatedata.path)
602 def key_pt(self, privatedata, sharedata, graph, x_pt, y_pt, width_pt, height_pt, dy_pt):
603 if privatedata.lineattrs is not None:
604 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)
605 return 1
608 class errorbar(_style):
610 needsdata = ["vpos", "vposmissing", "vposavailable", "vposvalid", "vrange", "vrangemissing"]
612 defaulterrorbarattrs = []
614 def __init__(self, size=0.1*unit.v_cm,
615 errorbarattrs=[],
616 epsilon=1e-10):
617 self.size = size
618 self.errorbarattrs = errorbarattrs
619 self.epsilon = epsilon
621 def columns(self, privatedata, sharedata, graph, columns):
622 for i in sharedata.vposmissing:
623 if i in sharedata.vrangemissing:
624 raise ValueError("position and range for a graph dimension missing")
625 return []
627 def selectstyle(self, privatedata, sharedata, graph, selectindex, selecttotal):
628 privatedata.errorsize_pt = unit.topt(attr.selectattr(self.size, selectindex, selecttotal))
629 privatedata.errorbarattrs = attr.selectattrs(self.defaulterrorbarattrs + self.errorbarattrs, selectindex, selecttotal)
631 def initdrawpoints(self, privatedata, sharedata, graph):
632 if privatedata.errorbarattrs is not None:
633 privatedata.errorbarcanvas = graph.insert(canvas.canvas())
634 privatedata.errorbarcanvas.set(privatedata.errorbarattrs)
635 privatedata.dimensionlist = range(len(sharedata.vpos))
637 def drawpoint(self, privatedata, sharedata, graph):
638 if privatedata.errorbarattrs is not None:
639 for i in privatedata.dimensionlist:
640 for j in privatedata.dimensionlist:
641 if (i != j and
642 (sharedata.vpos[j] is None or
643 sharedata.vpos[j] < -self.epsilon or
644 sharedata.vpos[j] > 1+self.epsilon)):
645 break
646 else:
647 if ((sharedata.vrange[i][0] is None and sharedata.vpos[i] is None) or
648 (sharedata.vrange[i][1] is None and sharedata.vpos[i] is None) or
649 (sharedata.vrange[i][0] is None and sharedata.vrange[i][1] is None)):
650 continue
651 vminpos = sharedata.vpos[:]
652 if sharedata.vrange[i][0] is not None:
653 vminpos[i] = sharedata.vrange[i][0]
654 mincap = 1
655 else:
656 mincap = 0
657 if vminpos[i] > 1+self.epsilon:
658 continue
659 if vminpos[i] < -self.epsilon:
660 vminpos[i] = 0
661 mincap = 0
662 vmaxpos = sharedata.vpos[:]
663 if sharedata.vrange[i][1] is not None:
664 vmaxpos[i] = sharedata.vrange[i][1]
665 maxcap = 1
666 else:
667 maxcap = 0
668 if vmaxpos[i] < -self.epsilon:
669 continue
670 if vmaxpos[i] > 1+self.epsilon:
671 vmaxpos[i] = 1
672 maxcap = 0
673 privatedata.errorbarcanvas.stroke(graph.vgeodesic(*(vminpos + vmaxpos)))
674 for j in privatedata.dimensionlist:
675 if i != j:
676 if mincap:
677 privatedata.errorbarcanvas.stroke(graph.vcap_pt(j, privatedata.errorsize_pt, *vminpos))
678 if maxcap:
679 privatedata.errorbarcanvas.stroke(graph.vcap_pt(j, privatedata.errorsize_pt, *vmaxpos))
682 class text(_styleneedingpointpos):
684 needsdata = ["vpos", "vposmissing", "vposvalid"]
686 defaulttextattrs = [textmodule.halign.center, textmodule.vshift.mathaxis]
688 def __init__(self, textdx=0*unit.v_cm, textdy=0.3*unit.v_cm, textattrs=[], **kwargs):
689 self.textdx = textdx
690 self.textdy = textdy
691 self.textattrs = textattrs
693 def columns(self, privatedata, sharedata, graph, columns):
694 if "text" not in columns:
695 raise ValueError("text missing")
696 return ["text"] + _styleneedingpointpos.columns(self, privatedata, sharedata, graph, columns)
698 def selectstyle(self, privatedata, sharedata, graph, selectindex, selecttotal):
699 if self.textattrs is not None:
700 privatedata.textattrs = attr.selectattrs(self.defaulttextattrs + self.textattrs, selectindex, selecttotal)
701 else:
702 privatedata.textattrs = None
704 def initdrawpoints(self, privatedata, sharedata, grap):
705 privatedata.textdx_pt = unit.topt(self.textdx)
706 privatedata.textdy_pt = unit.topt(self.textdy)
708 def drawpoint(self, privatedata, sharedata, graph):
709 if privatedata.textattrs is not None and sharedata.vposvalid:
710 x_pt, y_pt = graph.vpos_pt(*sharedata.vpos)
711 try:
712 text = str(sharedata.point["text"])
713 except:
714 pass
715 else:
716 graph.text_pt(x_pt + privatedata.textdx_pt, y_pt + privatedata.textdy_pt, text, privatedata.textattrs)
719 class arrow(_styleneedingpointpos):
721 needsdata = ["vpos", "vposmissing", "vposvalid"]
723 defaultlineattrs = []
724 defaultarrowattrs = []
726 def __init__(self, linelength=0.25*unit.v_cm, arrowsize=0.15*unit.v_cm, lineattrs=[], arrowattrs=[], epsilon=1e-10):
727 self.linelength = linelength
728 self.arrowsize = arrowsize
729 self.lineattrs = lineattrs
730 self.arrowattrs = arrowattrs
731 self.epsilon = epsilon
733 def columns(self, privatedata, sharedata, graph, columns):
734 if len(graph.axesnames) != 2:
735 raise ValueError("arrow style restricted on two-dimensional graphs")
736 if "size" not in columns:
737 raise ValueError("size missing")
738 if "angle" not in columns:
739 raise ValueError("angle missing")
740 return ["size", "angle"] + _styleneedingpointpos.columns(self, privatedata, sharedata, graph, columns)
742 def selectstyle(self, privatedata, sharedata, graph, selectindex, selecttotal):
743 if self.lineattrs is not None:
744 privatedata.lineattrs = attr.selectattrs(self.defaultlineattrs + self.lineattrs, selectindex, selecttotal)
745 else:
746 privatedata.lineattrs = None
747 if self.arrowattrs is not None:
748 privatedata.arrowattrs = attr.selectattrs(self.defaultarrowattrs + self.arrowattrs, selectindex, selecttotal)
749 else:
750 privatedata.arrowattrs = None
752 def initdrawpoints(self, privatedata, sharedata, graph):
753 privatedata.arrowcanvas = graph.insert(canvas.canvas())
755 def drawpoint(self, privatedata, sharedata, graph):
756 if privatedata.lineattrs is not None and privatedata.arrowattrs is not None and sharedata.vposvalid:
757 linelength_pt = unit.topt(self.linelength)
758 x_pt, y_pt = graph.vpos_pt(*sharedata.vpos)
759 try:
760 angle = sharedata.point["angle"] + 0.0
761 size = sharedata.point["size"] + 0.0
762 except:
763 pass
764 else:
765 if sharedata.point["size"] > self.epsilon:
766 dx = math.cos(angle*math.pi/180)
767 dy = math.sin(angle*math.pi/180)
768 x1 = x_pt-0.5*dx*linelength_pt*size
769 y1 = y_pt-0.5*dy*linelength_pt*size
770 x2 = x_pt+0.5*dx*linelength_pt*size
771 y2 = y_pt+0.5*dy*linelength_pt*size
772 privatedata.arrowcanvas.stroke(path.line_pt(x1, y1, x2, y2), privatedata.lineattrs +
773 [deco.earrow(privatedata.arrowattrs, size=self.arrowsize*size)])
775 def key_pt(self, privatedata, sharedata, graph, x_pt, y_pt, width_pt, height_pt, dy_pt):
776 raise "TODO"
779 class rect(_style):
781 needsdata = ["vrange", "vrangeminmissing", "vrangemaxmissing"]
783 def __init__(self, palette=color.palette.Gray):
784 self.palette = palette
786 def columns(self, privatedata, sharedata, graph, columns):
787 if len(graph.axesnames) != 2:
788 raise TypeError("arrow style restricted on two-dimensional graphs")
789 if "color" not in columns:
790 raise ValueError("color missing")
791 if len(sharedata.vrangeminmissing) + len(sharedata.vrangemaxmissing):
792 raise ValueError("range columns incomplete")
793 return ["color"]
795 def initdrawpoints(self, privatedata, sharedata, graph):
796 privatedata.rectcanvas = graph.insert(canvas.canvas())
797 privatedata.lastcolorvalue = None
799 def drawpoint(self, privatedata, sharedata, graph):
800 xvmin = sharedata.vrange[0][0]
801 xvmax = sharedata.vrange[0][1]
802 yvmin = sharedata.vrange[1][0]
803 yvmax = sharedata.vrange[1][1]
804 if (xvmin is not None and xvmin < 1 and
805 xvmax is not None and xvmax > 0 and
806 yvmin is not None and yvmin < 1 and
807 yvmax is not None and yvmax > 0):
808 if xvmin < 0:
809 xvmin = 0
810 elif xvmax > 1:
811 xvmax = 1
812 if yvmin < 0:
813 yvmin = 0
814 elif yvmax > 1:
815 yvmax = 1
816 p = graph.vgeodesic(xvmin, yvmin, xvmax, yvmin)
817 p.append(graph.vgeodesic_el(xvmax, yvmin, xvmax, yvmax))
818 p.append(graph.vgeodesic_el(xvmax, yvmax, xvmin, yvmax))
819 p.append(graph.vgeodesic_el(xvmin, yvmax, xvmin, yvmin))
820 p.append(path.closepath())
821 colorvalue = sharedata.point["color"]
822 try:
823 if colorvalue != privatedata.lastcolorvalue:
824 privatedata.rectcanvas.set([self.palette.getcolor(colorvalue)])
825 except:
826 pass
827 else:
828 privatedata.rectcanvas.fill(p)
830 def key_pt(self, privatedata, sharedata, graph, x_pt, y_pt, width_pt, height_pt, dy_pt):
831 raise "TODO"
834 class barpos(_style):
836 providesdata = ["vpos", "vposmissing", "vposavailable", "vposvalid", "barcolumns", "barvalueindex", "vbarpos"]
838 def __init__(self, fromvalue=None, subindex=0, subnames=None, epsilon=1e-10):
839 # TODO: vpos configuration ...
840 self.fromvalue = fromvalue
841 self.subnames = subnames
842 self.subindex = subindex
843 self.epsilon = epsilon
845 def columns(self, privatedata, sharedata, graph, columns):
846 # TODO: we might check whether barcolumns/barvalueindex is already available
847 sharedata.barcolumns = []
848 sharedata.barvalueindex = None
849 for dimension, axisnames in enumerate(graph.axesnames):
850 for axisname in axisnames:
851 if axisname in columns:
852 if sharedata.barvalueindex is not None:
853 raise ValueError("multiple values")
854 valuecolumns = [axisname]
855 while 1:
856 stackedvalue = "%sstack%i" % (axisname, len(valuecolumns))
857 if stackedvalue in columns:
858 valuecolumns.append(stackedvalue)
859 else:
860 break
861 sharedata.barcolumns.append(valuecolumns)
862 sharedata.barvalueindex = dimension
863 break
864 else:
865 found = 0
866 for axisname in axisnames:
867 if (axisname + "name") in columns:
868 if found > 1:
869 raise ValueError("multiple names")
870 found = 1
871 sharedata.barcolumns.append(axisname + "name")
872 if not found:
873 raise ValueError("value/name missing")
874 if sharedata.barvalueindex is None:
875 raise ValueError("missing value")
876 if self.subindex >= sharedata.barvalueindex:
877 privatedata.barpossubindex = self.subindex + 1
878 else:
879 privatedata.barpossubindex = self.subindex
880 sharedata.vposmissing = []
881 return sharedata.barcolumns[sharedata.barvalueindex] + [sharedata.barcolumns[i] for i in range(len(sharedata.barcolumns)) if i != sharedata.barvalueindex]
883 def selectstyle(self, privatedata, sharedata, graph, selectindex, selecttotal):
884 if selecttotal == 1:
885 if self.subnames is not None:
886 raise ValueError("subnames set for single-bar data")
887 privatedata.barpossubname = []
888 else:
889 if self.subnames is not None:
890 privatedata.barpossubname = [self.subnames[selectindex]]
891 else:
892 privatedata.barpossubname = [selectindex]
894 def adjustaxis(self, privatedata, sharedata, graph, column, data, index):
895 if column in sharedata.barcolumns[sharedata.barvalueindex]:
896 graph.axes[sharedata.barcolumns[sharedata.barvalueindex][0]].adjustrange(data, index)
897 if self.fromvalue is not None and column == sharedata.barcolumns[sharedata.barvalueindex][0]:
898 graph.axes[sharedata.barcolumns[sharedata.barvalueindex][0]].adjustrange([self.fromvalue], None)
899 else:
900 try:
901 i = sharedata.barcolumns.index(column)
902 except ValueError:
903 pass
904 else:
905 if i == privatedata.barpossubindex:
906 graph.axes[column[:-4]].adjustrange(data, index, privatedata.barpossubname)
907 else:
908 graph.axes[column[:-4]].adjustrange(data, index)
910 def initdrawpoints(self, privatedata, sharedata, graph):
911 sharedata.vpos = [None]*(len(sharedata.barcolumns))
912 sharedata.vbarpos = [[None for i in range(2)] for x in sharedata.barcolumns]
914 if self.fromvalue is not None:
915 vfromvalue = graph.axes[sharedata.barcolumns[sharedata.barvalueindex][0]].convert(self.fromvalue)
916 if vfromvalue < 0:
917 vfromvalue = 0
918 if vfromvalue > 1:
919 vfromvalue = 1
920 else:
921 vfromvalue = 0
923 sharedata.vbarpos[sharedata.barvalueindex] = [vfromvalue] + [None]*len(sharedata.barcolumns[sharedata.barvalueindex])
925 def drawpoint(self, privatedata, sharedata, graph):
926 sharedata.vposavailable = sharedata.vposvalid = 1
927 for i, barname in enumerate(sharedata.barcolumns):
928 if i == sharedata.barvalueindex:
929 for j, valuename in enumerate(sharedata.barcolumns[sharedata.barvalueindex]):
930 try:
931 sharedata.vbarpos[i][j+1] = graph.axes[sharedata.barcolumns[i][0]].convert(sharedata.point[valuename])
932 except (ArithmeticError, ValueError, TypeError):
933 sharedata.vbarpos[i][j+1] = None
934 sharedata.vpos[i] = sharedata.vbarpos[i][-1]
935 else:
936 for j in range(2):
937 try:
938 if i == privatedata.barpossubindex:
939 sharedata.vbarpos[i][j] = graph.axes[barname[:-4]].convert(([sharedata.point[barname]] + privatedata.barpossubname + [j]))
940 else:
941 sharedata.vbarpos[i][j] = graph.axes[barname[:-4]].convert((sharedata.point[barname], j))
942 except (ArithmeticError, ValueError, TypeError):
943 sharedata.vbarpos[i][j] = None
944 try:
945 sharedata.vpos[i] = 0.5*(sharedata.vbarpos[i][0]+sharedata.vbarpos[i][1])
946 except (ArithmeticError, ValueError, TypeError):
947 sharedata.vpos[i] = None
948 if sharedata.vpos[i] is None:
949 sharedata.vposavailable = sharedata.vposvalid = 0
950 elif sharedata.vpos[i] < -self.epsilon or sharedata.vpos[i] > 1+self.epsilon:
951 sharedata.vposvalid = 0
953 registerdefaultprovider(barpos(), ["barcolumns", "barvalueindex", "vbarpos"])
956 class bar(_style):
958 needsdata = ["barvalueindex", "vbarpos"]
960 defaultfrompathattrs = []
961 defaultbarattrs = [color.palette.Rainbow, deco.stroked([color.gray.black])]
963 def __init__(self, frompathattrs=[], barattrs=[], subnames=None, multikey=0, epsilon=1e-10):
964 self.frompathattrs = frompathattrs
965 self.barattrs = barattrs
966 self.subnames = subnames
967 self.multikey = multikey
968 self.epsilon = epsilon
970 def selectstyle(self, privatedata, sharedata, graph, selectindex, selecttotal):
971 if selectindex:
972 privatedata.frompathattrs = None
973 else:
974 privatedata.frompathattrs = self.defaultfrompathattrs + self.frompathattrs
975 if selecttotal > 1:
976 if self.barattrs is not None:
977 privatedata.barattrs = attr.selectattrs(self.defaultbarattrs + self.barattrs, selectindex, selecttotal)
978 else:
979 privatedata.barattrs = None
980 else:
981 privatedata.barattrs = self.defaultbarattrs + self.barattrs
982 privatedata.barselectindex = selectindex
983 privatedata.barselecttotal = selecttotal
984 if privatedata.barselecttotal != 1 and self.subnames is not None:
985 raise ValueError("subnames not allowed when iterating over bars")
987 def initdrawpoints(self, privatedata, sharedata, graph):
988 privatedata.bartmpvpos = [None]*4
989 l = len(sharedata.vbarpos[sharedata.barvalueindex])
990 if l > 1:
991 privatedata.bartmplist = []
992 for i in xrange(1, l):
993 barattrs = attr.selectattrs(privatedata.barattrs, i-1, l)
994 if barattrs is not None:
995 privatedata.bartmplist.append((i, barattrs))
996 else:
997 privatedata.bartmplist = [(1, privatedata.barattrs)]
998 if privatedata.frompathattrs is not None:
999 vfromvalue = sharedata.vbarpos[sharedata.barvalueindex][0]
1000 if vfromvalue > self.epsilon and vfromvalue < 1 - self.epsilon:
1001 if sharedata.barvalueindex:
1002 p = graph.vgeodesic(0, vfromvalue, 1, vfromvalue)
1003 else:
1004 p = graph.vgeodesic(vfromvalue, 0, vfromvalue, 1)
1005 graph.stroke(p, privatedata.frompathattrs)
1006 privatedata.barcanvas = graph.insert(canvas.canvas())
1008 def drawpoint(self, privatedata, sharedata, graph):
1009 if privatedata.barattrs is not None:
1010 for i, barattrs in privatedata.bartmplist:
1011 if None not in sharedata.vbarpos[1-sharedata.barvalueindex]+sharedata.vbarpos[sharedata.barvalueindex][i-1:i+1]:
1012 privatedata.bartmpvpos[1-sharedata.barvalueindex] = sharedata.vbarpos[1-sharedata.barvalueindex][0]
1013 privatedata.bartmpvpos[ sharedata.barvalueindex] = sharedata.vbarpos[sharedata.barvalueindex][i-1]
1014 privatedata.bartmpvpos[3-sharedata.barvalueindex] = sharedata.vbarpos[1-sharedata.barvalueindex][0]
1015 privatedata.bartmpvpos[2+sharedata.barvalueindex] = sharedata.vbarpos[sharedata.barvalueindex][i]
1016 p = graph.vgeodesic(*privatedata.bartmpvpos)
1017 privatedata.bartmpvpos[1-sharedata.barvalueindex] = sharedata.vbarpos[1-sharedata.barvalueindex][0]
1018 privatedata.bartmpvpos[ sharedata.barvalueindex] = sharedata.vbarpos[sharedata.barvalueindex][i]
1019 privatedata.bartmpvpos[3-sharedata.barvalueindex] = sharedata.vbarpos[1-sharedata.barvalueindex][1]
1020 privatedata.bartmpvpos[2+sharedata.barvalueindex] = sharedata.vbarpos[sharedata.barvalueindex][i]
1021 p.append(graph.vgeodesic_el(*privatedata.bartmpvpos))
1022 privatedata.bartmpvpos[1-sharedata.barvalueindex] = sharedata.vbarpos[1-sharedata.barvalueindex][1]
1023 privatedata.bartmpvpos[ sharedata.barvalueindex] = sharedata.vbarpos[sharedata.barvalueindex][i]
1024 privatedata.bartmpvpos[3-sharedata.barvalueindex] = sharedata.vbarpos[1-sharedata.barvalueindex][1]
1025 privatedata.bartmpvpos[2+sharedata.barvalueindex] = sharedata.vbarpos[sharedata.barvalueindex][i-1]
1026 p.append(graph.vgeodesic_el(*privatedata.bartmpvpos))
1027 privatedata.bartmpvpos[1-sharedata.barvalueindex] = sharedata.vbarpos[1-sharedata.barvalueindex][1]
1028 privatedata.bartmpvpos[ sharedata.barvalueindex] = sharedata.vbarpos[sharedata.barvalueindex][i-1]
1029 privatedata.bartmpvpos[3-sharedata.barvalueindex] = sharedata.vbarpos[1-sharedata.barvalueindex][0]
1030 privatedata.bartmpvpos[2+sharedata.barvalueindex] = sharedata.vbarpos[sharedata.barvalueindex][i-1]
1031 p.append(graph.vgeodesic_el(*privatedata.bartmpvpos))
1032 p.append(path.closepath())
1033 privatedata.barcanvas.fill(p, barattrs)
1035 def key_pt(self, privatedata, sharedata, c, x_pt, y_pt, width_pt, height_pt, dy_pt):
1036 if self.multikey:
1037 l = 0
1038 for i, barattrs in privatedata.bartmplist:
1039 c.fill(path.rect_pt(x_pt, y_pt-l*dy_pt, width_pt, height_pt), barattrs)
1040 l += 1
1041 return l
1042 else:
1043 for i, barattrs in privatedata.bartmplist:
1044 c.fill(path.rect_pt(x_pt+(i-1)*width_pt/privatedata.bartmplist[-1][0], y_pt,
1045 width_pt/privatedata.bartmplist[-1][0], height_pt), barattrs)
1046 return 1
1049 class barpos_new(_style):
1051 providesdata = ["vpos", "vposmissing", "vposavailable", "vposvalid", "vrange", "vrangemissing", "vrangemissing", "vrangeminmissing", "vrangemaxmissing", "stacked"]
1053 def __init__(self, fromvalue=None, stackname=None, epsilon=1e-10):
1054 # TODO: vpos configuration ...
1055 self.fromvalue = fromvalue
1056 self.stackname = stackname
1057 self.epsilon = epsilon
1059 def columns(self, privatedata, sharedata, graph, columns):
1060 privatedata.barcolumns = []
1061 privatedata.barvalueindex = None
1062 for dimension, axisnames in enumerate(graph.axesnames):
1063 found = 0
1064 for axisname in axisnames:
1065 if axisname in columns or axisname == self.stackname:
1066 if privatedata.barvalueindex is not None:
1067 raise ValueError("multiple values")
1068 privatedata.barcolumns.append(axisname)
1069 privatedata.barvalueindex = dimension
1070 privatedata.stacked = (axisname == self.stackname)
1071 found += 1
1072 if (axisname + "name") in columns:
1073 privatedata.barcolumns.append(axisname + "name")
1074 found += 1
1075 if found > 1:
1076 raise ValueError("multiple names")
1077 if not found:
1078 raise ValueError("value/name missing")
1079 if privatedata.barvalueindex is None:
1080 raise ValueError("missing value")
1081 sharedata.vposmissing = sharedata.vrangemissing = sharedata.vrangeminmissing = sharedata.vrangemaxmissing = []
1082 return privatedata.barcolumns
1084 # def selectstyle(self, privatedata, sharedata, graph, selectindex, selecttotal):
1085 # if selecttotal == 1:
1086 # if self.subnames is not None:
1087 # raise ValueError("subnames set for single-bar data")
1088 # privatedata.barpossubname = []
1089 # else:
1090 # if self.subnames is not None:
1091 # privatedata.barpossubname = [self.subnames[selectindex]]
1092 # else:
1093 # privatedata.barpossubname = [selectindex]
1095 def adjustaxis(self, privatedata, sharedata, graph, column, data, index):
1096 try:
1097 i = privatedata.barcolumns.index(column)
1098 except:
1099 pass
1100 else:
1101 if i == privatedata.barvalueindex:
1102 if self.fromvalue is not None:
1103 graph.axes[privatedata.barcolumns[i]].adjustrange([self.fromvalue], None)
1104 graph.axes[privatedata.barcolumns[i]].adjustrange(data, index)
1105 else:
1106 graph.axes[privatedata.barcolumns[i][:-4]].adjustrange(data, index)
1108 def initdrawpoints(self, privatedata, sharedata, graph):
1109 sharedata.vpos = [None]*(len(privatedata.barcolumns))
1110 sharedata.vrange = [[None for i in range(2)] for x in privatedata.barcolumns]
1112 if self.fromvalue is not None:
1113 vfromvalue = graph.axes[privatedata.barcolumns[privatedata.barvalueindex][0]].convert(self.fromvalue)
1114 if vfromvalue < 0:
1115 vfromvalue = 0
1116 if vfromvalue > 1:
1117 vfromvalue = 1
1118 else:
1119 vfromvalue = 0
1121 sharedata.vrange[privatedata.barvalueindex][0] = vfromvalue
1123 def drawpoint(self, privatedata, sharedata, graph):
1124 sharedata.vposavailable = sharedata.vposvalid = 1
1125 for i, barname in enumerate(privatedata.barcolumns):
1126 if i == privatedata.barvalueindex:
1127 try:
1128 sharedata.vrange[i][1] = graph.axes[barname].convert(sharedata.point[barname])
1129 except (ArithmeticError, ValueError, TypeError):
1130 sharedata.vrange[i][1] = None
1131 else:
1132 sharedata.vpos[i] = sharedata.vrange[i][1]
1133 else:
1134 for j in range(2):
1135 try:
1136 sharedata.vrange[i][j] = graph.axes[barname[:-4]].convert((sharedata.point[barname], j))
1137 except (ArithmeticError, ValueError, TypeError):
1138 sharedata.vrange[i][j] = None
1139 try:
1140 sharedata.vpos[i] = 0.5*(sharedata.vrange[i][0]+sharedata.vrange[i][1])
1141 except (ArithmeticError, ValueError, TypeError):
1142 sharedata.vpos[i] = None
1143 if sharedata.vpos[i] is None:
1144 sharedata.vposavailable = sharedata.vposvalid = 0
1145 elif sharedata.vpos[i] < -self.epsilon or sharedata.vpos[i] > 1+self.epsilon:
1146 sharedata.vposvalid = 0
1148 #registerdefaultprovider(barpos(), ["barcolumns", "barvalueindex", "vbarpos"])
1151 class bar_new(_style):
1153 needsdata = ["vrange", "vrangeminmissing", "vrangemaxmissing"]
1155 def __init__(self, barattrs=[deco.filled([color.rgb.red]), deco.stroked()]):
1156 self.barattrs = barattrs
1158 def columns(self, privatedata, sharedata, graph, columns):
1159 if len(graph.axesnames) != 2:
1160 raise TypeError("bar style restricted on two-dimensional graphs")
1161 if len(sharedata.vrangeminmissing) + len(sharedata.vrangemaxmissing):
1162 raise ValueError("range columns incomplete")
1163 return []
1165 def initdrawpoints(self, privatedata, sharedata, graph):
1166 privatedata.rectcanvas = graph.insert(canvas.canvas())
1168 def drawpoint(self, privatedata, sharedata, graph):
1169 xvmin = sharedata.vrange[0][0]
1170 xvmax = sharedata.vrange[0][1]
1171 yvmin = sharedata.vrange[1][0]
1172 yvmax = sharedata.vrange[1][1]
1173 if (xvmin is not None and xvmin < 1 and
1174 xvmax is not None and xvmax > 0 and
1175 yvmin is not None and yvmin < 1 and
1176 yvmax is not None and yvmax > 0):
1177 if xvmin < 0:
1178 xvmin = 0
1179 elif xvmax > 1:
1180 xvmax = 1
1181 if yvmin < 0:
1182 yvmin = 0
1183 elif yvmax > 1:
1184 yvmax = 1
1185 p = graph.vgeodesic(xvmin, yvmin, xvmax, yvmin)
1186 p.append(graph.vgeodesic_el(xvmax, yvmin, xvmax, yvmax))
1187 p.append(graph.vgeodesic_el(xvmax, yvmax, xvmin, yvmax))
1188 p.append(graph.vgeodesic_el(xvmin, yvmax, xvmin, yvmin))
1189 p.append(path.closepath())
1190 privatedata.rectcanvas.draw(p, self.barattrs)
1192 def key_pt(self, privatedata, sharedata, graph, x_pt, y_pt, width_pt, height_pt, dy_pt):
1193 raise "TODO"