2 # -*- coding: ISO-8859-1 -*-
5 # Copyright (C) 2002-2004 Jörg Lehmann <joergl@users.sourceforge.net>
6 # Copyright (C) 2003-2004 Michael Schindler <m-schindler@users.sourceforge.net>
7 # Copyright (C) 2002-2004 André Wobst <wobsta@users.sourceforge.net>
9 # This file is part of PyX (http://pyx.sourceforge.net/).
11 # PyX is free software; you can redistribute it and/or modify
12 # it under the terms of the GNU General Public License as published by
13 # the Free Software Foundation; either version 2 of the License, or
14 # (at your option) any later version.
16 # PyX is distributed in the hope that it will be useful,
17 # but WITHOUT ANY WARRANTY; without even the implied warranty of
18 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 # GNU General Public License for more details.
21 # You should have received a copy of the GNU General Public License
22 # along with PyX; if not, write to the Free Software
23 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
27 from pyx
import attr
, deco
, style
, color
, unit
, canvas
, path
28 from pyx
import text
as textmodule
33 # fallback implementation for Python 2.2. and below
35 return zip(xrange(len(list)), list)
38 """Interface class for graph styles
40 Each graph style must support the methods described in this
41 class. However, since a graph style might not need to perform
42 actions on all the various events, it does not need to overwrite
43 all methods of this base class (e.g. this class is not an abstract
44 class in any respect).
46 A style should never store private data by istance variables
47 (i.e. accessing self), but it should use the sharedata and privatedata
48 instances instead. A style instance can be used multiple times with
49 different sharedata and privatedata instances at the very same time.
50 The sharedata and privatedata instances act as data containers and
51 sharedata allows for sharing information across several styles.
53 Every style contains two class variables, which are not to be
55 - providesdata is a list of variable names a style offers via
56 the sharedata instance. This list is used to determine whether
57 all needs of subsequent styles are fullfilled. Otherwise
58 getdefaultprovider should return a proper style to be used.
59 - needsdata is a list of variable names the style needs to access in the
63 providesdata
= [] # by default, we provide nothing
64 needsdata
= [] # and do not depend on anything
66 def columns(self
, privatedata
, sharedata
, graph
, columns
):
67 """Set column information
69 This method is used setup the column information to be
70 accessible to the style later on. The style should analyse
71 the list of strings columns, which contain the column names
72 of the data. The method should return a list of column names
73 which the style will make use of."""
76 def selectstyle(self
, privatedata
, sharedata
, graph
, selectindex
, selecttotal
):
77 """Select stroke/fill attributes
79 This method is called to allow for the selection of
80 changable attributes of a style."""
83 def adjustaxis(self
, privatedata
, sharedata
, graph
, column
, data
, index
):
86 This method is called in order to adjust the axis range to
87 the provided data. Column is the name of the column (each
88 style is subsequently called for all column names). If index
89 is not None, data is a list of points and index is the index
90 of the column within a point. Otherwise data is already the
91 axis data. Note, that data might be different for different
92 columns, e.i. data might come from various places and is
93 combined without copying but keeping references."""
96 def initdrawpoints(self
, privatedata
, sharedata
, graph
):
97 """Initialize drawing of data
99 This method might be used to initialize the drawing of data."""
102 def drawpoint(self
, privatedata
, sharedata
, graph
):
105 This method is called for each data point. The data is
106 available in the dictionary sharedata.point. The dictionary
107 keys are the column names."""
110 def donedrawpoints(self
, privatedata
, sharedata
, graph
):
111 """Finalize drawing of data
113 This method is called after the last data point was
114 drawn using the drawpoint method above."""
117 def key_pt(self
, privatedata
, sharedata
, graph
, x_pt
, y_pt
, width_pt
, height_pt
):
121 # The following two methods are used to register and get a default provider
122 # for keys. A key is a variable name in sharedata. A provider is a style
123 # which creates variables in sharedata.
125 _defaultprovider
= {}
127 def registerdefaultprovider(style
, keys
):
128 """sets a style as a default creator for sharedata variables 'keys'"""
129 assert not len(style
.needsdata
), "currently we state, that a style should not depend on other sharedata variables"
131 assert key
in style
.providesdata
, "key not provided by style"
132 # we might allow for overwriting the defaults, i.e. the following is not checked:
133 # assert key in _defaultprovider.keys(), "default provider already registered for key"
134 _defaultprovider
[key
] = style
136 def getdefaultprovider(key
):
137 """returns a style, which acts as a default creator for the
138 sharedata variable 'key'"""
139 return _defaultprovider
[key
]
144 providesdata
= ["vpos", "vposmissing", "vposavailable", "vposvalid"]
146 def __init__(self
, epsilon
=1e-10):
147 self
.epsilon
= epsilon
149 def columns(self
, privatedata
, sharedata
, graph
, columns
):
150 privatedata
.pointposcolumns
= []
151 sharedata
.vposmissing
= []
152 for count
, axisnames
in enumerate(graph
.axesnames
):
153 for axisname
in axisnames
:
154 for column
in columns
:
155 if axisname
== column
:
156 privatedata
.pointposcolumns
.append(column
)
157 if len(privatedata
.pointposcolumns
) + len(sharedata
.vposmissing
) > count
+1:
158 raise ValueError("multiple axes per graph dimension")
159 elif len(privatedata
.pointposcolumns
) + len(sharedata
.vposmissing
) < count
+1:
160 sharedata
.vposmissing
.append(count
)
161 return privatedata
.pointposcolumns
163 def adjustaxis(self
, privatedata
, sharedata
, graph
, column
, data
, index
):
164 if column
in privatedata
.pointposcolumns
:
165 graph
.axes
[column
].adjustrange(data
, index
)
167 def initdrawpoints(self
, privatedata
, sharedata
, graph
):
168 sharedata
.vpos
= [None]*(len(privatedata
.pointposcolumns
) + len(sharedata
.vposmissing
))
169 privatedata
.pointpostmplist
= [[column
, index
, graph
.axes
[column
]] # temporarily used by drawpoint only
170 for index
, column
in enumerate(privatedata
.pointposcolumns
)]
171 for missing
in sharedata
.vposmissing
:
172 for pointpostmp
in privatedata
.pointpostmplist
:
173 if pointpostmp
[1] >= missing
:
176 def drawpoint(self
, privatedata
, sharedata
, graph
):
177 sharedata
.vposavailable
= 1 # valid position (but might be outside of the graph)
178 sharedata
.vposvalid
= 1 # valid position inside the graph
179 for column
, index
, axis
in privatedata
.pointpostmplist
:
181 v
= axis
.convert(sharedata
.point
[column
])
182 except (ArithmeticError, ValueError, TypeError):
183 sharedata
.vposavailable
= sharedata
.vposvalid
= 0
184 sharedata
.vpos
[index
] = None
186 if v
< -self
.epsilon
or v
> 1+self
.epsilon
:
187 sharedata
.vposvalid
= 0
188 sharedata
.vpos
[index
] = v
191 registerdefaultprovider(pos(), pos
.providesdata
)
196 providesdata
= ["vrange", "vrangemissing", "vrangeminmissing", "vrangemaxmissing"]
206 def __init__(self
, epsilon
=1e-10):
207 self
.epsilon
= epsilon
209 def columns(self
, privatedata
, sharedata
, graph
, columns
):
210 def numberofbits(mask
):
214 return numberofbits(mask
>> 1) + 1
216 return numberofbits(mask
>> 1)
218 privatedata
.rangeposcolumns
= []
219 sharedata
.vrangemissing
= []
220 sharedata
.vrangeminmissing
= []
221 sharedata
.vrangemaxmissing
= []
222 privatedata
.rangeposdeltacolumns
= {} # temporarily used by adjustaxis only
223 for count
, axisnames
in enumerate(graph
.axesnames
):
224 for axisname
in axisnames
:
226 for column
in columns
:
228 if axisname
== column
:
229 mask
+= self
.mask_value
230 elif axisname
+ "min" == column
:
231 mask
+= self
.mask_min
232 elif axisname
+ "max" == column
:
233 mask
+= self
.mask_max
234 elif "d" + axisname
+ "min" == column
:
235 mask
+= self
.mask_dmin
236 elif "d" + axisname
+ "max" == column
:
237 mask
+= self
.mask_dmax
238 elif "d" + axisname
== column
:
243 usecolumns
.append(column
)
244 if mask
& (self
.mask_min | self
.mask_max | self
.mask_dmin | self
.mask_dmax | self
.mask_d
):
245 if (numberofbits(mask
& (self
.mask_min | self
.mask_dmin | self
.mask_d
)) > 1 or
246 numberofbits(mask
& (self
.mask_max | self
.mask_dmax | self
.mask_d
)) > 1):
247 raise ValueError("multiple range definition")
248 if mask
& (self
.mask_dmin | self
.mask_dmax | self
.mask_d
):
249 if not (mask
& self
.mask_value
):
250 raise ValueError("missing value for delta")
251 privatedata
.rangeposdeltacolumns
[axisname
] = {}
252 privatedata
.rangeposcolumns
.append((axisname
, mask
))
253 elif mask
== self
.mask_value
:
254 usecolumns
= usecolumns
[:-1]
255 if len(privatedata
.rangeposcolumns
) + len(sharedata
.vrangemissing
) > count
+1:
256 raise ValueError("multiple axes per graph dimension")
257 elif len(privatedata
.rangeposcolumns
) + len(sharedata
.vrangemissing
) < count
+1:
258 sharedata
.vrangemissing
.append(count
)
260 if not (privatedata
.rangeposcolumns
[-1][1] & (self
.mask_min | self
.mask_dmin | self
.mask_d
)):
261 sharedata
.vrangeminmissing
.append(count
)
262 if not (privatedata
.rangeposcolumns
[-1][1] & (self
.mask_max | self
.mask_dmax | self
.mask_d
)):
263 sharedata
.vrangemaxmissing
.append(count
)
266 def adjustaxis(self
, privatedata
, sharedata
, graph
, column
, data
, index
):
267 if column
in [c
+ "min" for c
, m
in privatedata
.rangeposcolumns
if m
& self
.mask_min
]:
268 graph
.axes
[column
[:-3]].adjustrange(data
, index
)
269 if column
in [c
+ "max" for c
, m
in privatedata
.rangeposcolumns
if m
& self
.mask_max
]:
270 graph
.axes
[column
[:-3]].adjustrange(data
, index
)
272 # delta handling: fill rangeposdeltacolumns
273 if column
in [c
for c
, m
in privatedata
.rangeposcolumns
if m
& (self
.mask_dmin | self
.mask_dmax | self
.mask_d
)]:
274 privatedata
.rangeposdeltacolumns
[column
][self
.mask_value
] = data
, index
275 if column
in ["d" + c
+ "min" for c
, m
in privatedata
.rangeposcolumns
if m
& self
.mask_dmin
]:
276 privatedata
.rangeposdeltacolumns
[column
[1:-3]][self
.mask_dmin
] = data
, index
277 if column
in ["d" + c
+ "max" for c
, m
in privatedata
.rangeposcolumns
if m
& self
.mask_dmax
]:
278 privatedata
.rangeposdeltacolumns
[column
[1:-3]][self
.mask_dmax
] = data
, index
279 if column
in ["d" + c
for c
, m
in privatedata
.rangeposcolumns
if m
& self
.mask_d
]:
280 privatedata
.rangeposdeltacolumns
[column
[1:]][self
.mask_d
] = data
, index
282 # delta handling: process rangeposdeltacolumns
283 for c
, d
in privatedata
.rangeposdeltacolumns
.items():
284 if d
.has_key(self
.mask_value
):
286 if k
!= self
.mask_value
:
287 if k
& (self
.mask_dmin | self
.mask_d
):
288 graph
.axes
[c
].adjustrange(d
[self
.mask_value
][0], d
[self
.mask_value
][1],
289 deltamindata
=d
[k
][0], deltaminindex
=d
[k
][1])
290 if k
& (self
.mask_dmax | self
.mask_d
):
291 graph
.axes
[c
].adjustrange(d
[self
.mask_value
][0], d
[self
.mask_value
][1],
292 deltamaxdata
=d
[k
][0], deltamaxindex
=d
[k
][1])
295 def initdrawpoints(self
, privatedata
, sharedata
, graph
):
296 sharedata
.vrange
= [[None for x
in xrange(2)] for y
in privatedata
.rangeposcolumns
+ sharedata
.vrangemissing
]
297 privatedata
.rangepostmplist
= [[column
, mask
, index
, graph
.axes
[column
]] # temporarily used by drawpoint only
298 for index
, (column
, mask
) in enumerate(privatedata
.rangeposcolumns
)]
299 for missing
in sharedata
.vrangemissing
:
300 for rangepostmp
in privatedata
.rangepostmplist
:
301 if rangepostmp
[2] >= missing
:
304 def drawpoint(self
, privatedata
, sharedata
, graph
):
305 for column
, mask
, index
, axis
in privatedata
.rangepostmplist
:
307 if mask
& self
.mask_min
:
308 sharedata
.vrange
[index
][0] = axis
.convert(sharedata
.point
[column
+ "min"])
309 if mask
& self
.mask_dmin
:
310 sharedata
.vrange
[index
][0] = axis
.convert(sharedata
.point
[column
] - sharedata
.point
["d" + column
+ "min"])
311 if mask
& self
.mask_d
:
312 sharedata
.vrange
[index
][0] = axis
.convert(sharedata
.point
[column
] - sharedata
.point
["d" + column
])
313 except (ArithmeticError, ValueError, TypeError):
314 sharedata
.vrange
[index
][0] = None
316 if mask
& self
.mask_max
:
317 sharedata
.vrange
[index
][1] = axis
.convert(sharedata
.point
[column
+ "max"])
318 if mask
& self
.mask_dmax
:
319 sharedata
.vrange
[index
][1] = axis
.convert(sharedata
.point
[column
] + sharedata
.point
["d" + column
+ "max"])
320 if mask
& self
.mask_d
:
321 sharedata
.vrange
[index
][1] = axis
.convert(sharedata
.point
[column
] + sharedata
.point
["d" + column
])
322 except (ArithmeticError, ValueError, TypeError):
323 sharedata
.vrange
[index
][1] = None
325 # some range checks for data consistency
326 if (sharedata
.vrange
[index
][0] is not None and sharedata
.vrange
[index
][1] is not None and
327 sharedata
.vrange
[index
][0] > sharedata
.vrange
[index
][1] + self
.epsilon
):
328 raise ValueError("inverse range")
329 #if (sharedata.vrange[index][0] is not None and sharedata.vpos[index] is not None and
330 # sharedata.vrange[index][0] > sharedata.vpos[index] + self.epsilon):
331 # raise ValueError("negative minimum errorbar")
332 #if (sharedata.vrange[index][1] is not None and sharedata.vpos[index] is not None and
333 # sharedata.vrange[index][1] < sharedata.vpos[index] - self.epsilon):
334 # raise ValueError("negative maximum errorbar")
337 registerdefaultprovider(range(), range.providesdata
)
340 def _crosssymbol(c
, x_pt
, y_pt
, size_pt
, attrs
):
341 c
.draw(path
.path(path
.moveto_pt(x_pt
-0.5*size_pt
, y_pt
-0.5*size_pt
),
342 path
.lineto_pt(x_pt
+0.5*size_pt
, y_pt
+0.5*size_pt
),
343 path
.moveto_pt(x_pt
-0.5*size_pt
, y_pt
+0.5*size_pt
),
344 path
.lineto_pt(x_pt
+0.5*size_pt
, y_pt
-0.5*size_pt
)), attrs
)
346 def _plussymbol(c
, x_pt
, y_pt
, size_pt
, attrs
):
347 c
.draw(path
.path(path
.moveto_pt(x_pt
-0.707106781*size_pt
, y_pt
),
348 path
.lineto_pt(x_pt
+0.707106781*size_pt
, y_pt
),
349 path
.moveto_pt(x_pt
, y_pt
-0.707106781*size_pt
),
350 path
.lineto_pt(x_pt
, y_pt
+0.707106781*size_pt
)), attrs
)
352 def _squaresymbol(c
, x_pt
, y_pt
, size_pt
, attrs
):
353 c
.draw(path
.path(path
.moveto_pt(x_pt
-0.5*size_pt
, y_pt
-0.5*size_pt
),
354 path
.lineto_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
.closepath()), attrs
)
359 def _trianglesymbol(c
, x_pt
, y_pt
, size_pt
, attrs
):
360 c
.draw(path
.path(path
.moveto_pt(x_pt
-0.759835685*size_pt
, y_pt
-0.438691337*size_pt
),
361 path
.lineto_pt(x_pt
+0.759835685*size_pt
, y_pt
-0.438691337*size_pt
),
362 path
.lineto_pt(x_pt
, y_pt
+0.877382675*size_pt
),
363 path
.closepath()), attrs
)
365 def _circlesymbol(c
, x_pt
, y_pt
, size_pt
, attrs
):
366 c
.draw(path
.path(path
.arc_pt(x_pt
, y_pt
, 0.564189583*size_pt
, 0, 360),
367 path
.closepath()), attrs
)
369 def _diamondsymbol(c
, x_pt
, y_pt
, size_pt
, attrs
):
370 c
.draw(path
.path(path
.moveto_pt(x_pt
-0.537284965*size_pt
, y_pt
),
371 path
.lineto_pt(x_pt
, y_pt
-0.930604859*size_pt
),
372 path
.lineto_pt(x_pt
+0.537284965*size_pt
, y_pt
),
373 path
.lineto_pt(x_pt
, y_pt
+0.930604859*size_pt
),
374 path
.closepath()), attrs
)
377 class _styleneedingpointpos(_style
):
379 needsdata
= ["vposmissing"]
381 def columns(self
, privatedata
, sharedata
, graph
, columns
):
382 if len(sharedata
.vposmissing
):
383 raise ValueError("position columns incomplete")
387 class symbol(_styleneedingpointpos
):
389 needsdata
= ["vpos", "vposmissing", "vposvalid"]
392 # note, that statements like cross = _crosssymbol are
393 # invalid, since the would lead to unbound methods, but
394 # a single entry changeable list does the trick
395 cross
= attr
.changelist([_crosssymbol
])
396 plus
= attr
.changelist([_plussymbol
])
397 square
= attr
.changelist([_squaresymbol
])
398 triangle
= attr
.changelist([_trianglesymbol
])
399 circle
= attr
.changelist([_circlesymbol
])
400 diamond
= attr
.changelist([_diamondsymbol
])
402 changecross
= attr
.changelist([_crosssymbol
, _plussymbol
, _squaresymbol
, _trianglesymbol
, _circlesymbol
, _diamondsymbol
])
403 changeplus
= attr
.changelist([_plussymbol
, _squaresymbol
, _trianglesymbol
, _circlesymbol
, _diamondsymbol
, cross
])
404 changesquare
= attr
.changelist([_squaresymbol
, _trianglesymbol
, _circlesymbol
, _diamondsymbol
, cross
, _plussymbol
])
405 changetriangle
= attr
.changelist([_trianglesymbol
, _circlesymbol
, _diamondsymbol
, cross
, _plussymbol
, _squaresymbol
])
406 changecircle
= attr
.changelist([_circlesymbol
, _diamondsymbol
, cross
, _plussymbol
, _squaresymbol
, _trianglesymbol
])
407 changediamond
= attr
.changelist([_diamondsymbol
, cross
, _plussymbol
, _squaresymbol
, _trianglesymbol
, _circlesymbol
])
408 changesquaretwice
= attr
.changelist([_squaresymbol
, _squaresymbol
, _trianglesymbol
, _trianglesymbol
, _circlesymbol
, _circlesymbol
, _diamondsymbol
, _diamondsymbol
])
409 changetriangletwice
= attr
.changelist([_trianglesymbol
, _trianglesymbol
, _circlesymbol
, _circlesymbol
, _diamondsymbol
, _diamondsymbol
, _squaresymbol
, _squaresymbol
])
410 changecircletwice
= attr
.changelist([_circlesymbol
, _circlesymbol
, _diamondsymbol
, _diamondsymbol
, _squaresymbol
, _squaresymbol
, _trianglesymbol
, _trianglesymbol
])
411 changediamondtwice
= attr
.changelist([_diamondsymbol
, _diamondsymbol
, _squaresymbol
, _squaresymbol
, _trianglesymbol
, _trianglesymbol
, _circlesymbol
, _circlesymbol
])
413 changestrokedfilled
= attr
.changelist([deco
.stroked
, deco
.filled
])
414 changefilledstroked
= attr
.changelist([deco
.filled
, deco
.stroked
])
416 defaultsymbolattrs
= [deco
.stroked
]
418 def __init__(self
, symbol
=changecross
, size
=0.2*unit
.v_cm
, symbolattrs
=[]):
421 self
.symbolattrs
= symbolattrs
423 def selectstyle(self
, privatedata
, sharedata
, graph
, selectindex
, selecttotal
):
424 privatedata
.symbol
= attr
.selectattr(self
.symbol
, selectindex
, selecttotal
)
425 privatedata
.size_pt
= unit
.topt(attr
.selectattr(self
.size
, selectindex
, selecttotal
))
426 if self
.symbolattrs
is not None:
427 privatedata
.symbolattrs
= attr
.selectattrs(self
.defaultsymbolattrs
+ self
.symbolattrs
, selectindex
, selecttotal
)
429 privatedata
.symbolattrs
= None
431 def initdrawpoints(self
, privatedata
, sharedata
, graph
):
432 privatedata
.symbolcanvas
= graph
.insert(canvas
.canvas())
434 def drawpoint(self
, privatedata
, sharedata
, graph
):
435 if sharedata
.vposvalid
and privatedata
.symbolattrs
is not None:
436 xpos
, ypos
= graph
.vpos_pt(*sharedata
.vpos
)
437 privatedata
.symbol(privatedata
.symbolcanvas
, xpos
, ypos
, privatedata
.size_pt
, privatedata
.symbolattrs
)
439 def key_pt(self
, privatedata
, sharedata
, graph
, x_pt
, y_pt
, width_pt
, height_pt
):
440 if privatedata
.symbolattrs
is not None:
441 privatedata
.symbol(graph
, x_pt
+0.5*width_pt
, y_pt
+0.5*height_pt
, privatedata
.size_pt
, privatedata
.symbolattrs
)
444 class line(_styleneedingpointpos
):
446 needsdata
= ["vpos", "vposmissing", "vposavailable", "vposvalid"]
448 changelinestyle
= attr
.changelist([style
.linestyle
.solid
,
449 style
.linestyle
.dashed
,
450 style
.linestyle
.dotted
,
451 style
.linestyle
.dashdotted
])
453 defaultlineattrs
= [changelinestyle
]
455 def __init__(self
, lineattrs
=[]):
456 self
.lineattrs
= lineattrs
458 def selectstyle(self
, privatedata
, sharedata
, graph
, selectindex
, selecttotal
):
459 privatedata
.lineattrs
= attr
.selectattrs(self
.defaultlineattrs
+ self
.lineattrs
, selectindex
, selecttotal
)
461 def initdrawpoints(self
, privatedata
, sharedata
, graph
):
462 if privatedata
.lineattrs
is not None:
463 privatedata
.linecanvas
= graph
.insert(canvas
.canvas())
464 privatedata
.linecanvas
.set(privatedata
.lineattrs
)
465 privatedata
.path
= path
.path()
466 privatedata
.linebasepoints
= []
467 privatedata
.lastvpos
= None
469 def addpointstopath(self
, privatedata
, sharedata
):
470 # add baselinepoints to privatedata.path
471 if len(privatedata
.linebasepoints
) > 1:
472 privatedata
.path
.append(path
.moveto_pt(*privatedata
.linebasepoints
[0]))
473 if len(privatedata
.linebasepoints
) > 2:
474 privatedata
.path
.append(path
.multilineto_pt(privatedata
.linebasepoints
[1:]))
476 privatedata
.path
.append(path
.lineto_pt(*privatedata
.linebasepoints
[1]))
477 privatedata
.linebasepoints
= []
479 def drawpoint(self
, privatedata
, sharedata
, graph
):
480 # append linebasepoints
481 if sharedata
.vposavailable
:
482 if len(privatedata
.linebasepoints
):
483 # the last point was inside the graph
484 if sharedata
.vposvalid
: # shortcut for the common case
485 privatedata
.linebasepoints
.append(graph
.vpos_pt(*sharedata
.vpos
))
489 for vstart
, vend
in zip(privatedata
.lastvpos
, sharedata
.vpos
):
492 # 1 = vstart + (vend - vstart) * cut
494 newcut
= (1 - vstart
)/(vend
- vstart
)
495 except ArithmeticError:
498 # 0 = vstart + (vend - vstart) * cut
500 newcut
= - vstart
/(vend
- vstart
)
501 except ArithmeticError:
503 if newcut
is not None and newcut
< cut
:
507 for vstart
, vend
in zip(privatedata
.lastvpos
, sharedata
.vpos
):
508 cutvpos
.append(vstart
+ (vend
- vstart
) * cut
)
509 privatedata
.linebasepoints
.append(graph
.vpos_pt(*cutvpos
))
510 self
.addpointstopath(privatedata
, sharedata
)
512 # the last point was outside the graph
513 if privatedata
.lastvpos
is not None:
514 if sharedata
.vposvalid
:
517 for vstart
, vend
in zip(privatedata
.lastvpos
, sharedata
.vpos
):
520 # 1 = vstart + (vend - vstart) * cut
522 newcut
= (1 - vstart
)/(vend
- vstart
)
523 except ArithmeticError:
526 # 0 = vstart + (vend - vstart) * cut
528 newcut
= - vstart
/(vend
- vstart
)
529 except ArithmeticError:
531 if newcut
is not None and newcut
> cut
:
535 for vstart
, vend
in zip(privatedata
.lastvpos
, sharedata
.vpos
):
536 cutvpos
.append(vstart
+ (vend
- vstart
) * cut
)
537 privatedata
.linebasepoints
.append(graph
.vpos_pt(*cutvpos
))
538 privatedata
.linebasepoints
.append(graph
.vpos_pt(*sharedata
.vpos
))
540 # sometimes cut beginning and end
543 for vstart
, vend
in zip(privatedata
.lastvpos
, sharedata
.vpos
):
548 # 1 = vstart + (vend - vstart) * cutfrom
550 newcutfrom
= (1 - vstart
)/(vend
- vstart
)
551 except ArithmeticError:
556 # 0 = vstart + (vend - vstart) * cutfrom
558 newcutfrom
= - vstart
/(vend
- vstart
)
559 except ArithmeticError:
561 if newcutfrom
is not None and newcutfrom
> cutfrom
:
565 # 1 = vstart + (vend - vstart) * cutto
567 newcutto
= (1 - vstart
)/(vend
- vstart
)
568 except ArithmeticError:
571 # 0 = vstart + (vend - vstart) * cutto
573 newcutto
= - vstart
/(vend
- vstart
)
574 except ArithmeticError:
576 if newcutto
is not None and newcutto
< cutto
:
582 for vstart
, vend
in zip(privatedata
.lastvpos
, sharedata
.vpos
):
583 cutfromvpos
.append(vstart
+ (vend
- vstart
) * cutfrom
)
584 cuttovpos
.append(vstart
+ (vend
- vstart
) * cutto
)
585 privatedata
.linebasepoints
.append(graph
.vpos_pt(*cutfromvpos
))
586 privatedata
.linebasepoints
.append(graph
.vpos_pt(*cuttovpos
))
587 self
.addpointstopath(privatedata
, sharedata
)
588 privatedata
.lastvpos
= sharedata
.vpos
[:]
590 if len(privatedata
.linebasepoints
) > 1:
591 self
.addpointstopath(privatedata
, sharedata
)
592 privatedata
.lastvpos
= None
594 def donedrawpoints(self
, privatedata
, sharedata
, graph
):
595 if len(privatedata
.linebasepoints
) > 1:
596 self
.addpointstopath(privatedata
, sharedata
)
597 if privatedata
.lineattrs
is not None and len(privatedata
.path
.path
):
598 privatedata
.linecanvas
.stroke(privatedata
.path
)
600 def key_pt(self
, privatedata
, sharedata
, graph
, x_pt
, y_pt
, width_pt
, height_pt
):
601 if privatedata
.lineattrs
is not None:
602 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 class errorbar(_style
):
607 needsdata
= ["vpos", "vposmissing", "vposavailable", "vposvalid", "vrange", "vrangemissing"]
609 defaulterrorbarattrs
= []
611 def __init__(self
, size
=0.1*unit
.v_cm
,
615 self
.errorbarattrs
= errorbarattrs
616 self
.epsilon
= epsilon
618 def columns(self
, privatedata
, sharedata
, graph
, columns
):
619 for i
in sharedata
.vposmissing
:
620 if i
in sharedata
.vrangemissing
:
621 raise ValueError("position and range for a graph dimension missing")
624 def selectstyle(self
, privatedata
, sharedata
, graph
, selectindex
, selecttotal
):
625 privatedata
.errorsize_pt
= unit
.topt(attr
.selectattr(self
.size
, selectindex
, selecttotal
))
626 privatedata
.errorbarattrs
= attr
.selectattrs(self
.defaulterrorbarattrs
+ self
.errorbarattrs
, selectindex
, selecttotal
)
628 def initdrawpoints(self
, privatedata
, sharedata
, graph
):
629 if privatedata
.errorbarattrs
is not None:
630 privatedata
.errorbarcanvas
= graph
.insert(canvas
.canvas())
631 privatedata
.errorbarcanvas
.set(privatedata
.errorbarattrs
)
632 privatedata
.dimensionlist
= list(xrange(len(sharedata
.vpos
)))
634 def drawpoint(self
, privatedata
, sharedata
, graph
):
635 if privatedata
.errorbarattrs
is not None:
636 for i
in privatedata
.dimensionlist
:
637 for j
in privatedata
.dimensionlist
:
639 (sharedata
.vpos
[j
] is None or
640 sharedata
.vpos
[j
] < -self
.epsilon
or
641 sharedata
.vpos
[j
] > 1+self
.epsilon
)):
644 if ((sharedata
.vrange
[i
][0] is None and sharedata
.vpos
[i
] is None) or
645 (sharedata
.vrange
[i
][1] is None and sharedata
.vpos
[i
] is None) or
646 (sharedata
.vrange
[i
][0] is None and sharedata
.vrange
[i
][1] is None)):
648 vminpos
= sharedata
.vpos
[:]
649 if sharedata
.vrange
[i
][0] is not None:
650 vminpos
[i
] = sharedata
.vrange
[i
][0]
654 if vminpos
[i
] > 1+self
.epsilon
:
656 if vminpos
[i
] < -self
.epsilon
:
659 vmaxpos
= sharedata
.vpos
[:]
660 if sharedata
.vrange
[i
][1] is not None:
661 vmaxpos
[i
] = sharedata
.vrange
[i
][1]
665 if vmaxpos
[i
] < -self
.epsilon
:
667 if vmaxpos
[i
] > 1+self
.epsilon
:
670 privatedata
.errorbarcanvas
.stroke(graph
.vgeodesic(*(vminpos
+ vmaxpos
)))
671 for j
in privatedata
.dimensionlist
:
674 privatedata
.errorbarcanvas
.stroke(graph
.vcap_pt(j
, privatedata
.errorsize_pt
, *vminpos
))
676 privatedata
.errorbarcanvas
.stroke(graph
.vcap_pt(j
, privatedata
.errorsize_pt
, *vmaxpos
))
679 class text(_styleneedingpointpos
):
681 needsdata
= ["vpos", "vposmissing", "vposvalid"]
683 defaulttextattrs
= [textmodule
.halign
.center
, textmodule
.vshift
.mathaxis
]
685 def __init__(self
, textdx
=0*unit
.v_cm
, textdy
=0.3*unit
.v_cm
, textattrs
=[], **kwargs
):
688 self
.textattrs
= textattrs
690 def columns(self
, privatedata
, sharedata
, graph
, columns
):
691 if "text" not in columns
:
692 raise ValueError("text missing")
693 return ["text"] + _styleneedingpointpos
.columns(self
, privatedata
, sharedata
, graph
, columns
)
695 def selectstyle(self
, privatedata
, sharedata
, graph
, selectindex
, selecttotal
):
696 if self
.textattrs
is not None:
697 privatedata
.textattrs
= attr
.selectattrs(self
.defaulttextattrs
+ self
.textattrs
, selectindex
, selecttotal
)
699 privatedata
.textattrs
= None
701 def initdrawpoints(self
, privatedata
, sharedata
, grap
):
702 privatedata
.textdx_pt
= unit
.topt(self
.textdx
)
703 privatedata
.textdy_pt
= unit
.topt(self
.textdy
)
705 def drawpoint(self
, privatedata
, sharedata
, graph
):
706 if privatedata
.textattrs
is not None and sharedata
.vposvalid
:
707 x_pt
, y_pt
= graph
.vpos_pt(*sharedata
.vpos
)
709 text
= str(sharedata
.point
["text"])
713 graph
.text_pt(x_pt
+ privatedata
.textdx_pt
, y_pt
+ privatedata
.textdy_pt
, text
, privatedata
.textattrs
)
715 def key_pt(self
, privatedata
, sharedata
, graph
, x_pt
, y_pt
, width_pt
, height_pt
):
716 raise RuntimeError("Style currently doesn't provide a graph key")
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
)
746 privatedata
.lineattrs
= None
747 if self
.arrowattrs
is not None:
748 privatedata
.arrowattrs
= attr
.selectattrs(self
.defaultarrowattrs
+ self
.arrowattrs
, selectindex
, selecttotal
)
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
)
760 angle
= sharedata
.point
["angle"] + 0.0
761 size
= sharedata
.point
["size"] + 0.0
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
):
776 raise RuntimeError("Style currently doesn't provide a graph key")
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")
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):
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"]
823 if colorvalue
!= privatedata
.lastcolorvalue
:
824 privatedata
.rectcanvas
.set([self
.palette
.getcolor(colorvalue
)])
828 privatedata
.rectcanvas
.fill(p
)
830 def key_pt(self
, privatedata
, sharedata
, graph
, x_pt
, y_pt
, width_pt
, height_pt
):
831 raise RuntimeError("Style currently doesn't provide a graph key")
834 class barpos(_style
):
836 providesdata
= ["vpos", "vposmissing", "vposavailable", "vposvalid", "vbarrange", "barcolumns", "barvalueindex", "stackedbar"]
838 defaultfrompathattrs
= []
840 def __init__(self
, fromvalue
=None, frompathattrs
=[], subindex
=0, subnames
=None, epsilon
=1e-10):
841 # NOTE subindex is a perspective for higher dimensional plots
842 # (just ignore it for the moment -- we don't even need to document about it)
843 self
.fromvalue
= fromvalue
844 self
.frompathattrs
= frompathattrs
845 self
.subindex
= subindex
846 self
.subnames
= subnames
847 self
.epsilon
= epsilon
849 def columns(self
, privatedata
, sharedata
, graph
, columns
):
850 sharedata
.barcolumns
= []
851 sharedata
.barvalueindex
= None
852 for dimension
, axisnames
in enumerate(graph
.axesnames
):
854 for axisname
in axisnames
:
855 if axisname
in columns
:
856 if sharedata
.barvalueindex
is not None:
857 raise ValueError("multiple values")
858 sharedata
.barvalueindex
= dimension
859 sharedata
.barcolumns
.append(axisname
)
861 if (axisname
+ "name") in columns
:
862 sharedata
.barcolumns
.append(axisname
+ "name")
865 raise ValueError("multiple names")
867 raise ValueError("value/name missing")
868 if sharedata
.barvalueindex
is None:
869 raise ValueError("missing value")
870 if self
.subindex
>= sharedata
.barvalueindex
:
871 privatedata
.barpossubindex
= self
.subindex
+ 1
873 privatedata
.barpossubindex
= self
.subindex
874 sharedata
.vposmissing
= []
875 return sharedata
.barcolumns
877 def selectstyle(self
, privatedata
, sharedata
, graph
, selectindex
, selecttotal
):
879 if self
.subnames
is not None:
880 raise ValueError("subnames set for single-bar data")
881 privatedata
.barpossubname
= []
883 if self
.subnames
is not None:
884 privatedata
.barpossubname
= [self
.subnames
[selectindex
]]
886 privatedata
.barpossubname
= [selectindex
]
888 def adjustaxis(self
, privatedata
, sharedata
, graph
, column
, data
, index
):
890 i
= sharedata
.barcolumns
.index(column
)
894 if i
== sharedata
.barvalueindex
:
895 if self
.fromvalue
is not None:
896 graph
.axes
[sharedata
.barcolumns
[i
]].adjustrange([self
.fromvalue
], None)
897 graph
.axes
[sharedata
.barcolumns
[i
]].adjustrange(data
, index
)
899 if i
== privatedata
.barpossubindex
:
900 graph
.axes
[sharedata
.barcolumns
[i
][:-4]].adjustrange(data
, index
, privatedata
.barpossubname
)
902 graph
.axes
[sharedata
.barcolumns
[i
][:-4]].adjustrange(data
, index
)
904 def initdrawpoints(self
, privatedata
, sharedata
, graph
):
905 sharedata
.vpos
= [None]*(len(sharedata
.barcolumns
))
906 sharedata
.vbarrange
= [[None for i
in xrange(2)] for x
in sharedata
.barcolumns
]
907 sharedata
.stackedbar
= sharedata
.stackedbardraw
= 0
909 if self
.fromvalue
is not None:
910 privatedata
.vfromvalue
= graph
.axes
[sharedata
.barcolumns
[sharedata
.barvalueindex
][0]].convert(self
.fromvalue
)
911 if privatedata
.vfromvalue
< 0:
912 privatedata
.vfromvalue
= 0
913 if privatedata
.vfromvalue
> 1:
914 privatedata
.vfromvalue
= 1
915 if self
.frompathattrs
is not None:
917 graph
.stroke(graph
.axespos
[sharedata
.barcolumns
[sharedata
.barvalueindex
][0]].vgridpath(privatedata
.vfromvalue
),
918 self
.defaultfrompathattrs
+ self
.frompathattrs
)
920 privatedata
.vfromvalue
= 0
922 def drawpoint(self
, privatedata
, sharedata
, graph
):
923 sharedata
.vposavailable
= sharedata
.vposvalid
= 1
924 for i
, barname
in enumerate(sharedata
.barcolumns
):
925 if i
== sharedata
.barvalueindex
:
926 sharedata
.vbarrange
[i
][0] = privatedata
.vfromvalue
928 sharedata
.vpos
[i
] = sharedata
.vbarrange
[i
][1] = graph
.axes
[barname
].convert(sharedata
.point
[barname
])
929 except (ArithmeticError, ValueError, TypeError):
930 sharedata
.vpos
[i
] = sharedata
.vbarrange
[i
][1] = None
934 if i
== privatedata
.barpossubindex
:
935 sharedata
.vbarrange
[i
][j
] = graph
.axes
[barname
[:-4]].convert(([sharedata
.point
[barname
]] + privatedata
.barpossubname
+ [j
]))
937 sharedata
.vbarrange
[i
][j
] = graph
.axes
[barname
[:-4]].convert((sharedata
.point
[barname
], j
))
938 except (ArithmeticError, ValueError, TypeError):
939 sharedata
.vbarrange
[i
][j
] = None
941 sharedata
.vpos
[i
] = 0.5*(sharedata
.vbarrange
[i
][0]+sharedata
.vbarrange
[i
][1])
942 except (ArithmeticError, ValueError, TypeError):
943 sharedata
.vpos
[i
] = None
944 if sharedata
.vpos
[i
] is None:
945 sharedata
.vposavailable
= sharedata
.vposvalid
= 0
946 elif sharedata
.vpos
[i
] < -self
.epsilon
or sharedata
.vpos
[i
] > 1+self
.epsilon
:
947 sharedata
.vposvalid
= 0
949 registerdefaultprovider(barpos(), ["vbarrange", "barcolumns", "barvalueindex"])
952 class stackedbarpos(_style
):
954 # provides no additional data, but needs some data (and modifies some of them)
955 needsdata
= ["vbarrange", "barcolumns", "barvalueindex"]
957 def __init__(self
, stackname
, epsilon
=1e-10):
958 self
.stackname
= stackname
959 self
.epsilon
= epsilon
961 def columns(self
, privatedata
, sharedata
, graph
, columns
):
962 if self
.stackname
not in columns
:
963 raise ValueError("stackname column missing")
964 return [self
.stackname
]
966 def adjustaxis(self
, privatedata
, sharedata
, graph
, column
, data
, index
):
967 if column
== self
.stackname
:
968 graph
.axes
[sharedata
.barcolumns
[sharedata
.barvalueindex
]].adjustrange(data
, index
)
970 def initdrawpoints(self
, privatedata
, sharedata
, graph
):
971 if sharedata
.stackedbardraw
: # do not count the start bar when not gets painted
972 sharedata
.stackedbar
+= 1
974 def drawpoint(self
, privatedata
, sharedata
, graph
):
975 sharedata
.vbarrange
[sharedata
.barvalueindex
][0] = sharedata
.vbarrange
[sharedata
.barvalueindex
][1]
977 sharedata
.vpos
[sharedata
.barvalueindex
] = sharedata
.vbarrange
[sharedata
.barvalueindex
][1] = graph
.axes
[sharedata
.barcolumns
[sharedata
.barvalueindex
]].convert(sharedata
.point
[self
.stackname
])
978 except (ArithmeticError, ValueError, TypeError):
979 sharedata
.vpos
[sharedata
.barvalueindex
] = sharedata
.vbarrange
[sharedata
.barvalueindex
][1] = None
980 sharedata
.vposavailable
= sharedata
.vposvalid
= 0
982 if not sharedata
.vposavailable
or not sharedata
.vposvalid
:
983 sharedata
.vposavailable
= sharedata
.vposvalid
= 1
984 for v
in sharedata
.vpos
:
986 sharedata
.vposavailable
= sharedata
.vposvalid
= 0
988 if v
< -self
.epsilon
or v
> 1+self
.epsilon
:
989 sharedata
.vposvalid
= 0
994 needsdata
= ["vbarrange"]
996 defaultbarattrs
= [color
.palette
.Rainbow
, deco
.stroked([color
.gray
.black
])]
998 def __init__(self
, barattrs
=[]):
999 self
.barattrs
= barattrs
1001 def columns(self
, privatedata
, sharedata
, graph
, columns
):
1002 if len(graph
.axesnames
) != 2:
1003 raise TypeError("bar style restricted on two-dimensional graphs")
1006 def selectstyle(self
, privatedata
, sharedata
, graph
, selectindex
, selecttotal
):
1008 privatedata
.barattrs
= attr
.selectattrs(self
.defaultbarattrs
+ self
.barattrs
, selectindex
, selecttotal
)
1010 privatedata
.barattrs
= self
.defaultbarattrs
+ self
.barattrs
1012 def initdrawpoints(self
, privatedata
, sharedata
, graph
):
1013 privatedata
.rectcanvas
= graph
.insert(canvas
.canvas())
1014 sharedata
.stackedbardraw
= 1
1015 privatedata
.stackedbar
= sharedata
.stackedbar
1017 def drawpoint(self
, privatedata
, sharedata
, graph
):
1018 xvmin
= sharedata
.vbarrange
[0][0]
1019 xvmax
= sharedata
.vbarrange
[0][1]
1020 yvmin
= sharedata
.vbarrange
[1][0]
1021 yvmax
= sharedata
.vbarrange
[1][1]
1022 if (xvmin
is not None and xvmin
< 1 and
1023 xvmax
is not None and xvmax
> 0 and
1024 yvmin
is not None and yvmin
< 1 and
1025 yvmax
is not None and yvmax
> 0):
1034 p
= graph
.vgeodesic(xvmin
, yvmin
, xvmax
, yvmin
)
1035 p
.append(graph
.vgeodesic_el(xvmax
, yvmin
, xvmax
, yvmax
))
1036 p
.append(graph
.vgeodesic_el(xvmax
, yvmax
, xvmin
, yvmax
))
1037 p
.append(graph
.vgeodesic_el(xvmin
, yvmax
, xvmin
, yvmin
))
1038 p
.append(path
.closepath())
1039 privatedata
.rectcanvas
.fill(p
, privatedata
.barattrs
)
1041 def key_pt(self
, privatedata
, sharedata
, graph
, x_pt
, y_pt
, width_pt
, height_pt
):
1042 selectindex
= privatedata
.stackedbar
1043 selecttotal
= sharedata
.stackedbar
+ 1
1044 graph
.fill(path
.rect_pt(x_pt
+ width_pt
*selectindex
/float(selecttotal
), y_pt
, width_pt
/float(selecttotal
), height_pt
), privatedata
.barattrs
)