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
, dy_pt
, selectindex
, selecttotal
):
120 This method draws a key for the style to graph at the given
121 position x_pt, y_pt indicating the lower left corner of the
122 given area width_pt, height_pt. The style might draw several
123 key entries shifted vertically by dy_pt. The method returns
124 the number of key entries or None, when nothing was drawn."""
128 # The following two methods are used to register and get a default provider
129 # for keys. A key is a variable name in sharedata. A provider is a style
130 # which creates variables in sharedata.
132 _defaultprovider
= {}
134 def registerdefaultprovider(style
, keys
):
135 """sets a style as a default creator for sharedata variables 'keys'"""
136 assert not len(style
.needsdata
), "currently we state, that a style should not depend on other sharedata variables"
138 assert key
in style
.providesdata
, "key not provided by style"
139 # we might allow for overwriting the defaults, i.e. the following is not checked:
140 # assert key in _defaultprovider.keys(), "default provider already registered for key"
141 _defaultprovider
[key
] = style
143 def getdefaultprovider(key
):
144 """returns a style, which acts as a default creator for the
145 sharedata variable 'key'"""
146 return _defaultprovider
[key
]
151 providesdata
= ["vpos", "vposmissing", "vposavailable", "vposvalid"]
153 def __init__(self
, epsilon
=1e-10):
154 self
.epsilon
= epsilon
156 def columns(self
, privatedata
, sharedata
, graph
, columns
):
157 privatedata
.pointposcolumns
= []
158 sharedata
.vposmissing
= []
159 for count
, axisnames
in enumerate(graph
.axesnames
):
160 for axisname
in axisnames
:
161 for column
in columns
:
162 if axisname
== column
:
163 privatedata
.pointposcolumns
.append(column
)
164 if len(privatedata
.pointposcolumns
) + len(sharedata
.vposmissing
) > count
+1:
165 raise ValueError("multiple axes per graph dimension")
166 elif len(privatedata
.pointposcolumns
) + len(sharedata
.vposmissing
) < count
+1:
167 sharedata
.vposmissing
.append(count
)
168 return privatedata
.pointposcolumns
170 def adjustaxis(self
, privatedata
, sharedata
, graph
, column
, data
, index
):
171 if column
in privatedata
.pointposcolumns
:
172 graph
.axes
[column
].adjustrange(data
, index
)
174 def initdrawpoints(self
, privatedata
, sharedata
, graph
):
175 sharedata
.vpos
= [None]*(len(privatedata
.pointposcolumns
) + len(sharedata
.vposmissing
))
176 privatedata
.pointpostmplist
= [[column
, index
, graph
.axes
[column
]] # temporarily used by drawpoint only
177 for index
, column
in enumerate(privatedata
.pointposcolumns
)]
178 for missing
in sharedata
.vposmissing
:
179 for pointpostmp
in privatedata
.pointpostmplist
:
180 if pointpostmp
[1] >= missing
:
183 def drawpoint(self
, privatedata
, sharedata
, graph
):
184 sharedata
.vposavailable
= 1 # valid position (but might be outside of the graph)
185 sharedata
.vposvalid
= 1 # valid position inside the graph
186 for column
, index
, axis
in privatedata
.pointpostmplist
:
188 v
= axis
.convert(sharedata
.point
[column
])
189 except (ArithmeticError, ValueError, TypeError):
190 sharedata
.vposavailable
= sharedata
.vposvalid
= 0
191 sharedata
.vpos
[index
] = None
193 if v
< -self
.epsilon
or v
> 1+self
.epsilon
:
194 sharedata
.vposvalid
= 0
195 sharedata
.vpos
[index
] = v
198 registerdefaultprovider(_pos(), _pos
.providesdata
)
201 class _range(_style
):
203 providesdata
= ["vrange", "vrangemissing", "vrangeminmissing", "vrangemaxmissing"]
213 def __init__(self
, epsilon
=1e-10):
214 self
.epsilon
= epsilon
216 def columns(self
, privatedata
, sharedata
, graph
, columns
):
217 def numberofbits(mask
):
221 return numberofbits(mask
>> 1) + 1
223 return numberofbits(mask
>> 1)
225 privatedata
.rangeposcolumns
= []
226 sharedata
.vrangemissing
= []
227 sharedata
.vrangeminmissing
= []
228 sharedata
.vrangemaxmissing
= []
229 privatedata
.rangeposdeltacolumns
= {} # temporarily used by adjustaxis only
230 for count
, axisnames
in enumerate(graph
.axesnames
):
231 for axisname
in axisnames
:
233 for column
in columns
:
235 if axisname
== column
:
236 mask
+= self
.mask_value
237 elif axisname
+ "min" == column
:
238 mask
+= self
.mask_min
239 elif axisname
+ "max" == column
:
240 mask
+= self
.mask_max
241 elif "d" + axisname
+ "min" == column
:
242 mask
+= self
.mask_dmin
243 elif "d" + axisname
+ "max" == column
:
244 mask
+= self
.mask_dmax
245 elif "d" + axisname
== column
:
250 usecolumns
.append(column
)
251 if mask
& (self
.mask_min | self
.mask_max | self
.mask_dmin | self
.mask_dmax | self
.mask_d
):
252 if (numberofbits(mask
& (self
.mask_min | self
.mask_dmin | self
.mask_d
)) > 1 or
253 numberofbits(mask
& (self
.mask_max | self
.mask_dmax | self
.mask_d
)) > 1):
254 raise ValueError("multiple range definition")
255 if mask
& (self
.mask_dmin | self
.mask_dmax | self
.mask_d
):
256 if not (mask
& self
.mask_value
):
257 raise ValueError("missing value for delta")
258 privatedata
.rangeposdeltacolumns
[axisname
] = {}
259 privatedata
.rangeposcolumns
.append((axisname
, mask
))
260 elif mask
== self
.mask_value
:
261 usecolumns
= usecolumns
[:-1]
262 if len(privatedata
.rangeposcolumns
) + len(sharedata
.vrangemissing
) > count
+1:
263 raise ValueError("multiple axes per graph dimension")
264 elif len(privatedata
.rangeposcolumns
) + len(sharedata
.vrangemissing
) < count
+1:
265 sharedata
.vrangemissing
.append(count
)
267 if not (privatedata
.rangeposcolumns
[-1][1] & (self
.mask_min | self
.mask_dmin | self
.mask_d
)):
268 sharedata
.vrangeminmissing
.append(count
)
269 if not (privatedata
.rangeposcolumns
[-1][1] & (self
.mask_max | self
.mask_dmax | self
.mask_d
)):
270 sharedata
.vrangemaxmissing
.append(count
)
273 def adjustaxis(self
, privatedata
, sharedata
, graph
, column
, data
, index
):
274 if column
in [c
+ "min" for c
, m
in privatedata
.rangeposcolumns
if m
& self
.mask_min
]:
275 graph
.axes
[column
[:-3]].adjustrange(data
, index
)
276 if column
in [c
+ "max" for c
, m
in privatedata
.rangeposcolumns
if m
& self
.mask_max
]:
277 graph
.axes
[column
[:-3]].adjustrange(data
, index
)
279 # delta handling: fill rangeposdeltacolumns
280 if column
in [c
for c
, m
in privatedata
.rangeposcolumns
if m
& (self
.mask_dmin | self
.mask_dmax | self
.mask_d
)]:
281 privatedata
.rangeposdeltacolumns
[column
][self
.mask_value
] = data
, index
282 if column
in ["d" + c
+ "min" for c
, m
in privatedata
.rangeposcolumns
if m
& self
.mask_dmin
]:
283 privatedata
.rangeposdeltacolumns
[column
[1:-3]][self
.mask_dmin
] = data
, index
284 if column
in ["d" + c
+ "max" for c
, m
in privatedata
.rangeposcolumns
if m
& self
.mask_dmax
]:
285 privatedata
.rangeposdeltacolumns
[column
[1:-3]][self
.mask_dmax
] = data
, index
286 if column
in ["d" + c
for c
, m
in privatedata
.rangeposcolumns
if m
& self
.mask_d
]:
287 privatedata
.rangeposdeltacolumns
[column
[1:]][self
.mask_d
] = data
, index
289 # delta handling: process rangeposdeltacolumns
290 for c
, d
in privatedata
.rangeposdeltacolumns
.items():
291 if d
.has_key(self
.mask_value
):
293 if k
!= self
.mask_value
:
294 if k
& (self
.mask_dmin | self
.mask_d
):
295 graph
.axes
[c
].adjustrange(d
[self
.mask_value
][0], d
[self
.mask_value
][1],
296 deltamindata
=d
[k
][0], deltaminindex
=d
[k
][1])
297 if k
& (self
.mask_dmax | self
.mask_d
):
298 graph
.axes
[c
].adjustrange(d
[self
.mask_value
][0], d
[self
.mask_value
][1],
299 deltamaxdata
=d
[k
][0], deltamaxindex
=d
[k
][1])
302 def initdrawpoints(self
, privatedata
, sharedata
, graph
):
303 sharedata
.vrange
= [[None for x
in range(2)] for y
in privatedata
.rangeposcolumns
+ sharedata
.vrangemissing
]
304 privatedata
.rangepostmplist
= [[column
, mask
, index
, graph
.axes
[column
]] # temporarily used by drawpoint only
305 for index
, (column
, mask
) in enumerate(privatedata
.rangeposcolumns
)]
306 for missing
in sharedata
.vrangemissing
:
307 for rangepostmp
in privatedata
.rangepostmplist
:
308 if rangepostmp
[2] >= missing
:
311 def drawpoint(self
, privatedata
, sharedata
, graph
):
312 for column
, mask
, index
, axis
in privatedata
.rangepostmplist
:
314 if mask
& self
.mask_min
:
315 sharedata
.vrange
[index
][0] = axis
.convert(sharedata
.point
[column
+ "min"])
316 if mask
& self
.mask_dmin
:
317 sharedata
.vrange
[index
][0] = axis
.convert(sharedata
.point
[column
] - sharedata
.point
["d" + column
+ "min"])
318 if mask
& self
.mask_d
:
319 sharedata
.vrange
[index
][0] = axis
.convert(sharedata
.point
[column
] - sharedata
.point
["d" + column
])
320 except (ArithmeticError, ValueError, TypeError):
321 sharedata
.vrange
[index
][0] = None
323 if mask
& self
.mask_max
:
324 sharedata
.vrange
[index
][1] = axis
.convert(sharedata
.point
[column
+ "max"])
325 if mask
& self
.mask_dmax
:
326 sharedata
.vrange
[index
][1] = axis
.convert(sharedata
.point
[column
] + sharedata
.point
["d" + column
+ "max"])
327 if mask
& self
.mask_d
:
328 sharedata
.vrange
[index
][1] = axis
.convert(sharedata
.point
[column
] + sharedata
.point
["d" + column
])
329 except (ArithmeticError, ValueError, TypeError):
330 sharedata
.vrange
[index
][1] = None
332 # some range checks for data consistency
333 if (sharedata
.vrange
[index
][0] is not None and sharedata
.vrange
[index
][1] is not None and
334 sharedata
.vrange
[index
][0] > sharedata
.vrange
[index
][1] + self
.epsilon
):
335 raise ValueError("inverse range")
336 #if (sharedata.vrange[index][0] is not None and sharedata.vpos[index] is not None and
337 # sharedata.vrange[index][0] > sharedata.vpos[index] + self.epsilon):
338 # raise ValueError("negative minimum errorbar")
339 #if (sharedata.vrange[index][1] is not None and sharedata.vpos[index] is not None and
340 # sharedata.vrange[index][1] < sharedata.vpos[index] - self.epsilon):
341 # raise ValueError("negative maximum errorbar")
344 registerdefaultprovider(_range(), _range
.providesdata
)
347 def _crosssymbol(c
, x_pt
, y_pt
, size_pt
, attrs
):
348 c
.draw(path
.path(path
.moveto_pt(x_pt
-0.5*size_pt
, y_pt
-0.5*size_pt
),
349 path
.lineto_pt(x_pt
+0.5*size_pt
, y_pt
+0.5*size_pt
),
350 path
.moveto_pt(x_pt
-0.5*size_pt
, y_pt
+0.5*size_pt
),
351 path
.lineto_pt(x_pt
+0.5*size_pt
, y_pt
-0.5*size_pt
)), attrs
)
353 def _plussymbol(c
, x_pt
, y_pt
, size_pt
, attrs
):
354 c
.draw(path
.path(path
.moveto_pt(x_pt
-0.707106781*size_pt
, y_pt
),
355 path
.lineto_pt(x_pt
+0.707106781*size_pt
, y_pt
),
356 path
.moveto_pt(x_pt
, y_pt
-0.707106781*size_pt
),
357 path
.lineto_pt(x_pt
, y_pt
+0.707106781*size_pt
)), attrs
)
359 def _squaresymbol(c
, x_pt
, y_pt
, size_pt
, attrs
):
360 c
.draw(path
.path(path
.moveto_pt(x_pt
-0.5*size_pt
, y_pt
-0.5*size_pt
),
361 path
.lineto_pt(x_pt
+0.5*size_pt
, y_pt
-0.5*size_pt
),
362 path
.lineto_pt(x_pt
+0.5*size_pt
, y_pt
+0.5*size_pt
),
363 path
.lineto_pt(x_pt
-0.5*size_pt
, y_pt
+0.5*size_pt
),
364 path
.closepath()), attrs
)
366 def _trianglesymbol(c
, x_pt
, y_pt
, size_pt
, attrs
):
367 c
.draw(path
.path(path
.moveto_pt(x_pt
-0.759835685*size_pt
, y_pt
-0.438691337*size_pt
),
368 path
.lineto_pt(x_pt
+0.759835685*size_pt
, y_pt
-0.438691337*size_pt
),
369 path
.lineto_pt(x_pt
, y_pt
+0.877382675*size_pt
),
370 path
.closepath()), attrs
)
372 def _circlesymbol(c
, x_pt
, y_pt
, size_pt
, attrs
):
373 c
.draw(path
.path(path
.arc_pt(x_pt
, y_pt
, 0.564189583*size_pt
, 0, 360),
374 path
.closepath()), attrs
)
376 def _diamondsymbol(c
, x_pt
, y_pt
, size_pt
, attrs
):
377 c
.draw(path
.path(path
.moveto_pt(x_pt
-0.537284965*size_pt
, y_pt
),
378 path
.lineto_pt(x_pt
, y_pt
-0.930604859*size_pt
),
379 path
.lineto_pt(x_pt
+0.537284965*size_pt
, y_pt
),
380 path
.lineto_pt(x_pt
, y_pt
+0.930604859*size_pt
),
381 path
.closepath()), attrs
)
384 class _styleneedingpointpos(_style
):
386 needsdata
= ["vposmissing"]
388 def columns(self
, privatedata
, sharedata
, graph
, columns
):
389 if len(sharedata
.vposmissing
):
390 raise ValueError("position columns incomplete")
394 class symbol(_styleneedingpointpos
):
396 needsdata
= ["vpos", "vposmissing", "vposvalid"]
399 # note, that statements like cross = _crosssymbol are
400 # invalid, since the would lead to unbound methods, but
401 # a single entry changeable list does the trick
402 cross
= attr
.changelist([_crosssymbol
])
403 plus
= attr
.changelist([_plussymbol
])
404 square
= attr
.changelist([_squaresymbol
])
405 triangle
= attr
.changelist([_trianglesymbol
])
406 circle
= attr
.changelist([_circlesymbol
])
407 diamond
= attr
.changelist([_diamondsymbol
])
409 changecross
= attr
.changelist([_crosssymbol
, _plussymbol
, _squaresymbol
, _trianglesymbol
, _circlesymbol
, _diamondsymbol
])
410 changeplus
= attr
.changelist([_plussymbol
, _squaresymbol
, _trianglesymbol
, _circlesymbol
, _diamondsymbol
, cross
])
411 changesquare
= attr
.changelist([_squaresymbol
, _trianglesymbol
, _circlesymbol
, _diamondsymbol
, cross
, _plussymbol
])
412 changetriangle
= attr
.changelist([_trianglesymbol
, _circlesymbol
, _diamondsymbol
, cross
, _plussymbol
, _squaresymbol
])
413 changecircle
= attr
.changelist([_circlesymbol
, _diamondsymbol
, cross
, _plussymbol
, _squaresymbol
, _trianglesymbol
])
414 changediamond
= attr
.changelist([_diamondsymbol
, cross
, _plussymbol
, _squaresymbol
, _trianglesymbol
, _circlesymbol
])
415 changesquaretwice
= attr
.changelist([_squaresymbol
, _squaresymbol
, _trianglesymbol
, _trianglesymbol
, _circlesymbol
, _circlesymbol
, _diamondsymbol
, _diamondsymbol
])
416 changetriangletwice
= attr
.changelist([_trianglesymbol
, _trianglesymbol
, _circlesymbol
, _circlesymbol
, _diamondsymbol
, _diamondsymbol
, _squaresymbol
, _squaresymbol
])
417 changecircletwice
= attr
.changelist([_circlesymbol
, _circlesymbol
, _diamondsymbol
, _diamondsymbol
, _squaresymbol
, _squaresymbol
, _trianglesymbol
, _trianglesymbol
])
418 changediamondtwice
= attr
.changelist([_diamondsymbol
, _diamondsymbol
, _squaresymbol
, _squaresymbol
, _trianglesymbol
, _trianglesymbol
, _circlesymbol
, _circlesymbol
])
420 changestrokedfilled
= attr
.changelist([deco
.stroked
, deco
.filled
])
421 changefilledstroked
= attr
.changelist([deco
.filled
, deco
.stroked
])
423 defaultsymbolattrs
= [deco
.stroked
]
425 def __init__(self
, symbol
=changecross
, size
=0.2*unit
.v_cm
, symbolattrs
=[]):
428 self
.symbolattrs
= symbolattrs
430 def selectstyle(self
, privatedata
, sharedata
, graph
, selectindex
, selecttotal
):
431 privatedata
.symbol
= attr
.selectattr(self
.symbol
, selectindex
, selecttotal
)
432 privatedata
.size_pt
= unit
.topt(attr
.selectattr(self
.size
, selectindex
, selecttotal
))
433 if self
.symbolattrs
is not None:
434 privatedata
.symbolattrs
= attr
.selectattrs(self
.defaultsymbolattrs
+ self
.symbolattrs
, selectindex
, selecttotal
)
436 privatedata
.symbolattrs
= None
438 def initdrawpoints(self
, privatedata
, sharedata
, graph
):
439 privatedata
.symbolcanvas
= graph
.insert(canvas
.canvas())
441 def drawpoint(self
, privatedata
, sharedata
, graph
):
442 if sharedata
.vposvalid
and privatedata
.symbolattrs
is not None:
443 xpos
, ypos
= graph
.vpos_pt(*sharedata
.vpos
)
444 privatedata
.symbol(privatedata
.symbolcanvas
, xpos
, ypos
, privatedata
.size_pt
, privatedata
.symbolattrs
)
446 def key_pt(self
, privatedata
, sharedata
, graph
, x_pt
, y_pt
, width_pt
, height_pt
, dy_pt
, selectindex
, selecttotal
):
447 if privatedata
.symbolattrs
is not None:
448 privatedata
.symbol(graph
, x_pt
+0.5*width_pt
, y_pt
+0.5*height_pt
, privatedata
.size_pt
, privatedata
.symbolattrs
)
452 class line(_styleneedingpointpos
):
454 needsdata
= ["vpos", "vposmissing", "vposavailable", "vposvalid"]
456 changelinestyle
= attr
.changelist([style
.linestyle
.solid
,
457 style
.linestyle
.dashed
,
458 style
.linestyle
.dotted
,
459 style
.linestyle
.dashdotted
])
461 defaultlineattrs
= [changelinestyle
]
463 def __init__(self
, lineattrs
=[]):
464 self
.lineattrs
= lineattrs
466 def selectstyle(self
, privatedata
, sharedata
, graph
, selectindex
, selecttotal
):
467 privatedata
.lineattrs
= attr
.selectattrs(self
.defaultlineattrs
+ self
.lineattrs
, selectindex
, selecttotal
)
469 def initdrawpoints(self
, privatedata
, sharedata
, graph
):
470 if privatedata
.lineattrs
is not None:
471 privatedata
.linecanvas
= graph
.insert(canvas
.canvas())
472 privatedata
.linecanvas
.set(privatedata
.lineattrs
)
473 privatedata
.path
= path
.path()
474 privatedata
.linebasepoints
= []
475 privatedata
.lastvpos
= None
477 def addpointstopath(self
, privatedata
, sharedata
):
478 # add baselinepoints to privatedata.path
479 if len(privatedata
.linebasepoints
) > 1:
480 privatedata
.path
.append(path
.moveto_pt(*privatedata
.linebasepoints
[0]))
481 if len(privatedata
.linebasepoints
) > 2:
482 privatedata
.path
.append(path
.multilineto_pt(privatedata
.linebasepoints
[1:]))
484 privatedata
.path
.append(path
.lineto_pt(*privatedata
.linebasepoints
[1]))
485 privatedata
.linebasepoints
= []
487 def drawpoint(self
, privatedata
, sharedata
, graph
):
488 # append linebasepoints
489 if sharedata
.vposavailable
:
490 if len(privatedata
.linebasepoints
):
491 # the last point was inside the graph
492 if sharedata
.vposvalid
: # shortcut for the common case
493 privatedata
.linebasepoints
.append(graph
.vpos_pt(*sharedata
.vpos
))
497 for vstart
, vend
in zip(privatedata
.lastvpos
, sharedata
.vpos
):
500 # 1 = vstart + (vend - vstart) * cut
502 newcut
= (1 - vstart
)/(vend
- vstart
)
503 except ArithmeticError:
506 # 0 = vstart + (vend - vstart) * cut
508 newcut
= - vstart
/(vend
- vstart
)
509 except ArithmeticError:
511 if newcut
is not None and newcut
< cut
:
515 for vstart
, vend
in zip(privatedata
.lastvpos
, sharedata
.vpos
):
516 cutvpos
.append(vstart
+ (vend
- vstart
) * cut
)
517 privatedata
.linebasepoints
.append(graph
.vpos_pt(*cutvpos
))
518 self
.addpointstopath(privatedata
, sharedata
)
520 # the last point was outside the graph
521 if privatedata
.lastvpos
is not None:
522 if sharedata
.vposvalid
:
525 for vstart
, vend
in zip(privatedata
.lastvpos
, sharedata
.vpos
):
528 # 1 = vstart + (vend - vstart) * cut
530 newcut
= (1 - vstart
)/(vend
- vstart
)
531 except ArithmeticError:
534 # 0 = vstart + (vend - vstart) * cut
536 newcut
= - vstart
/(vend
- vstart
)
537 except ArithmeticError:
539 if newcut
is not None and newcut
> cut
:
543 for vstart
, vend
in zip(privatedata
.lastvpos
, sharedata
.vpos
):
544 cutvpos
.append(vstart
+ (vend
- vstart
) * cut
)
545 privatedata
.linebasepoints
.append(graph
.vpos_pt(*cutvpos
))
546 privatedata
.linebasepoints
.append(graph
.vpos_pt(*sharedata
.vpos
))
548 # sometimes cut beginning and end
551 for vstart
, vend
in zip(privatedata
.lastvpos
, sharedata
.vpos
):
556 # 1 = vstart + (vend - vstart) * cutfrom
558 newcutfrom
= (1 - vstart
)/(vend
- vstart
)
559 except ArithmeticError:
564 # 0 = vstart + (vend - vstart) * cutfrom
566 newcutfrom
= - vstart
/(vend
- vstart
)
567 except ArithmeticError:
569 if newcutfrom
is not None and newcutfrom
> cutfrom
:
573 # 1 = vstart + (vend - vstart) * cutto
575 newcutto
= (1 - vstart
)/(vend
- vstart
)
576 except ArithmeticError:
579 # 0 = vstart + (vend - vstart) * cutto
581 newcutto
= - vstart
/(vend
- vstart
)
582 except ArithmeticError:
584 if newcutto
is not None and newcutto
< cutto
:
590 for vstart
, vend
in zip(privatedata
.lastvpos
, sharedata
.vpos
):
591 cutfromvpos
.append(vstart
+ (vend
- vstart
) * cutfrom
)
592 cuttovpos
.append(vstart
+ (vend
- vstart
) * cutto
)
593 privatedata
.linebasepoints
.append(graph
.vpos_pt(*cutfromvpos
))
594 privatedata
.linebasepoints
.append(graph
.vpos_pt(*cuttovpos
))
595 self
.addpointstopath(privatedata
, sharedata
)
596 privatedata
.lastvpos
= sharedata
.vpos
[:]
598 if len(privatedata
.linebasepoints
) > 1:
599 self
.addpointstopath(privatedata
, sharedata
)
600 privatedata
.lastvpos
= None
602 def donedrawpoints(self
, privatedata
, sharedata
, graph
):
603 if len(privatedata
.linebasepoints
) > 1:
604 self
.addpointstopath(privatedata
, sharedata
)
605 if privatedata
.lineattrs
is not None and len(privatedata
.path
.path
):
606 privatedata
.linecanvas
.stroke(privatedata
.path
)
608 def key_pt(self
, privatedata
, sharedata
, graph
, x_pt
, y_pt
, width_pt
, height_pt
, dy_pt
, selectindex
, selecttotal
):
609 if privatedata
.lineattrs
is not None:
610 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
)
614 class errorbar(_style
):
616 needsdata
= ["vpos", "vposmissing", "vposavailable", "vposvalid", "vrange", "vrangemissing"]
618 defaulterrorbarattrs
= []
620 def __init__(self
, size
=0.1*unit
.v_cm
,
624 self
.errorbarattrs
= errorbarattrs
625 self
.epsilon
= epsilon
627 def columns(self
, privatedata
, sharedata
, graph
, columns
):
628 for i
in sharedata
.vposmissing
:
629 if i
in sharedata
.vrangemissing
:
630 raise ValueError("position and range for a graph dimension missing")
633 def selectstyle(self
, privatedata
, sharedata
, graph
, selectindex
, selecttotal
):
634 privatedata
.errorsize_pt
= unit
.topt(attr
.selectattr(self
.size
, selectindex
, selecttotal
))
635 privatedata
.errorbarattrs
= attr
.selectattrs(self
.defaulterrorbarattrs
+ self
.errorbarattrs
, selectindex
, selecttotal
)
637 def initdrawpoints(self
, privatedata
, sharedata
, graph
):
638 if privatedata
.errorbarattrs
is not None:
639 privatedata
.errorbarcanvas
= graph
.insert(canvas
.canvas())
640 privatedata
.errorbarcanvas
.set(privatedata
.errorbarattrs
)
641 privatedata
.dimensionlist
= range(len(sharedata
.vpos
))
643 def drawpoint(self
, privatedata
, sharedata
, graph
):
644 if privatedata
.errorbarattrs
is not None:
645 for i
in privatedata
.dimensionlist
:
646 for j
in privatedata
.dimensionlist
:
648 (sharedata
.vpos
[j
] is None or
649 sharedata
.vpos
[j
] < -self
.epsilon
or
650 sharedata
.vpos
[j
] > 1+self
.epsilon
)):
653 if ((sharedata
.vrange
[i
][0] is None and sharedata
.vpos
[i
] is None) or
654 (sharedata
.vrange
[i
][1] is None and sharedata
.vpos
[i
] is None) or
655 (sharedata
.vrange
[i
][0] is None and sharedata
.vrange
[i
][1] is None)):
657 vminpos
= sharedata
.vpos
[:]
658 if sharedata
.vrange
[i
][0] is not None:
659 vminpos
[i
] = sharedata
.vrange
[i
][0]
663 if vminpos
[i
] > 1+self
.epsilon
:
665 if vminpos
[i
] < -self
.epsilon
:
668 vmaxpos
= sharedata
.vpos
[:]
669 if sharedata
.vrange
[i
][1] is not None:
670 vmaxpos
[i
] = sharedata
.vrange
[i
][1]
674 if vmaxpos
[i
] < -self
.epsilon
:
676 if vmaxpos
[i
] > 1+self
.epsilon
:
679 privatedata
.errorbarcanvas
.stroke(graph
.vgeodesic(*(vminpos
+ vmaxpos
)))
680 for j
in privatedata
.dimensionlist
:
683 privatedata
.errorbarcanvas
.stroke(graph
.vcap_pt(j
, privatedata
.errorsize_pt
, *vminpos
))
685 privatedata
.errorbarcanvas
.stroke(graph
.vcap_pt(j
, privatedata
.errorsize_pt
, *vmaxpos
))
688 class text(_styleneedingpointpos
):
690 needsdata
= ["vpos", "vposmissing", "vposvalid"]
692 defaulttextattrs
= [textmodule
.halign
.center
, textmodule
.vshift
.mathaxis
]
694 def __init__(self
, textdx
=0*unit
.v_cm
, textdy
=0.3*unit
.v_cm
, textattrs
=[], **kwargs
):
697 self
.textattrs
= textattrs
699 def columns(self
, privatedata
, sharedata
, graph
, columns
):
700 if "text" not in columns
:
701 raise ValueError("text missing")
702 return ["text"] + _styleneedingpointpos
.columns(self
, privatedata
, sharedata
, graph
, columns
)
704 def selectstyle(self
, privatedata
, sharedata
, graph
, selectindex
, selecttotal
):
705 if self
.textattrs
is not None:
706 privatedata
.textattrs
= attr
.selectattrs(self
.defaulttextattrs
+ self
.textattrs
, selectindex
, selecttotal
)
708 privatedata
.textattrs
= None
710 def initdrawpoints(self
, privatedata
, sharedata
, grap
):
711 privatedata
.textdx_pt
= unit
.topt(self
.textdx
)
712 privatedata
.textdy_pt
= unit
.topt(self
.textdy
)
714 def drawpoint(self
, privatedata
, sharedata
, graph
):
715 if privatedata
.textattrs
is not None and sharedata
.vposvalid
:
716 x_pt
, y_pt
= graph
.vpos_pt(*sharedata
.vpos
)
718 text
= str(sharedata
.point
["text"])
722 graph
.text_pt(x_pt
+ privatedata
.textdx_pt
, y_pt
+ privatedata
.textdy_pt
, text
, privatedata
.textattrs
)
725 class arrow(_styleneedingpointpos
):
727 needsdata
= ["vpos", "vposmissing", "vposvalid"]
729 defaultlineattrs
= []
730 defaultarrowattrs
= []
732 def __init__(self
, linelength
=0.25*unit
.v_cm
, arrowsize
=0.15*unit
.v_cm
, lineattrs
=[], arrowattrs
=[], epsilon
=1e-10):
733 self
.linelength
= linelength
734 self
.arrowsize
= arrowsize
735 self
.lineattrs
= lineattrs
736 self
.arrowattrs
= arrowattrs
737 self
.epsilon
= epsilon
739 def columns(self
, privatedata
, sharedata
, graph
, columns
):
740 if len(graph
.axesnames
) != 2:
741 raise ValueError("arrow style restricted on two-dimensional graphs")
742 if "size" not in columns
:
743 raise ValueError("size missing")
744 if "angle" not in columns
:
745 raise ValueError("angle missing")
746 return ["size", "angle"] + _styleneedingpointpos
.columns(self
, privatedata
, sharedata
, graph
, columns
)
748 def selectstyle(self
, privatedata
, sharedata
, graph
, selectindex
, selecttotal
):
749 if self
.lineattrs
is not None:
750 privatedata
.lineattrs
= attr
.selectattrs(self
.defaultlineattrs
+ self
.lineattrs
, selectindex
, selecttotal
)
752 privatedata
.lineattrs
= None
753 if self
.arrowattrs
is not None:
754 privatedata
.arrowattrs
= attr
.selectattrs(self
.defaultarrowattrs
+ self
.arrowattrs
, selectindex
, selecttotal
)
756 privatedata
.arrowattrs
= None
758 def initdrawpoints(self
, privatedata
, sharedata
, graph
):
759 privatedata
.arrowcanvas
= graph
.insert(canvas
.canvas())
761 def drawpoint(self
, privatedata
, sharedata
, graph
):
762 if privatedata
.lineattrs
is not None and privatedata
.arrowattrs
is not None and sharedata
.vposvalid
:
763 linelength_pt
= unit
.topt(self
.linelength
)
764 x_pt
, y_pt
= graph
.vpos_pt(*sharedata
.vpos
)
766 angle
= sharedata
.point
["angle"] + 0.0
767 size
= sharedata
.point
["size"] + 0.0
771 if sharedata
.point
["size"] > self
.epsilon
:
772 dx
= math
.cos(angle
*math
.pi
/180)
773 dy
= math
.sin(angle
*math
.pi
/180)
774 x1
= x_pt
-0.5*dx
*linelength_pt
*size
775 y1
= y_pt
-0.5*dy
*linelength_pt
*size
776 x2
= x_pt
+0.5*dx
*linelength_pt
*size
777 y2
= y_pt
+0.5*dy
*linelength_pt
*size
778 privatedata
.arrowcanvas
.stroke(path
.line_pt(x1
, y1
, x2
, y2
), privatedata
.lineattrs
+
779 [deco
.earrow(privatedata
.arrowattrs
, size
=self
.arrowsize
*size
)])
781 def key_pt(self
, privatedata
, sharedata
, graph
, x_pt
, y_pt
, width_pt
, height_pt
, dy_pt
, selectindex
, selecttotal
):
787 needsdata
= ["vrange", "vrangeminmissing", "vrangemaxmissing"]
789 def __init__(self
, palette
=color
.palette
.Gray
):
790 self
.palette
= palette
792 def columns(self
, privatedata
, sharedata
, graph
, columns
):
793 if len(graph
.axesnames
) != 2:
794 raise TypeError("arrow style restricted on two-dimensional graphs")
795 if "color" not in columns
:
796 raise ValueError("color missing")
797 if len(sharedata
.vrangeminmissing
) + len(sharedata
.vrangemaxmissing
):
798 raise ValueError("range columns incomplete")
801 def initdrawpoints(self
, privatedata
, sharedata
, graph
):
802 privatedata
.rectcanvas
= graph
.insert(canvas
.canvas())
803 privatedata
.lastcolorvalue
= None
805 def drawpoint(self
, privatedata
, sharedata
, graph
):
806 xvmin
= sharedata
.vrange
[0][0]
807 xvmax
= sharedata
.vrange
[0][1]
808 yvmin
= sharedata
.vrange
[1][0]
809 yvmax
= sharedata
.vrange
[1][1]
810 if (xvmin
is not None and xvmin
< 1 and
811 xvmax
is not None and xvmax
> 0 and
812 yvmin
is not None and yvmin
< 1 and
813 yvmax
is not None and yvmax
> 0):
822 p
= graph
.vgeodesic(xvmin
, yvmin
, xvmax
, yvmin
)
823 p
.append(graph
.vgeodesic_el(xvmax
, yvmin
, xvmax
, yvmax
))
824 p
.append(graph
.vgeodesic_el(xvmax
, yvmax
, xvmin
, yvmax
))
825 p
.append(graph
.vgeodesic_el(xvmin
, yvmax
, xvmin
, yvmin
))
826 p
.append(path
.closepath())
827 colorvalue
= sharedata
.point
["color"]
829 if colorvalue
!= privatedata
.lastcolorvalue
:
830 privatedata
.rectcanvas
.set([self
.palette
.getcolor(colorvalue
)])
834 privatedata
.rectcanvas
.fill(p
)
836 def key_pt(self
, privatedata
, sharedata
, graph
, x_pt
, y_pt
, width_pt
, height_pt
, dy_pt
, selectindex
, selecttotal
):
840 class barpos(_style
):
842 providesdata
= ["vpos", "vposmissing", "vposavailable", "vposvalid", "barcolumns", "barvalueindex", "vbarpos"]
844 def __init__(self
, fromvalue
=None, subindex
=0, subnames
=None, epsilon
=1e-10):
845 # TODO: vpos configuration ...
846 self
.fromvalue
= fromvalue
847 self
.subnames
= subnames
848 self
.subindex
= subindex
849 self
.epsilon
= epsilon
851 def columns(self
, privatedata
, sharedata
, graph
, columns
):
852 # TODO: we might check whether barcolumns/barvalueindex is already available
853 sharedata
.barcolumns
= []
854 sharedata
.barvalueindex
= None
855 for dimension
, axisnames
in enumerate(graph
.axesnames
):
856 for axisname
in axisnames
:
857 if axisname
in columns
:
858 if sharedata
.barvalueindex
is not None:
859 raise ValueError("multiple values")
860 valuecolumns
= [axisname
]
862 stackedvalue
= "%sstack%i" % (axisname
, len(valuecolumns
))
863 if stackedvalue
in columns
:
864 valuecolumns
.append(stackedvalue
)
867 sharedata
.barcolumns
.append(valuecolumns
)
868 sharedata
.barvalueindex
= dimension
872 for axisname
in axisnames
:
873 if (axisname
+ "name") in columns
:
875 raise ValueError("multiple names")
877 sharedata
.barcolumns
.append(axisname
+ "name")
879 raise ValueError("value/name missing")
880 if sharedata
.barvalueindex
is None:
881 raise ValueError("missing value")
882 if self
.subindex
>= sharedata
.barvalueindex
:
883 privatedata
.barpossubindex
= self
.subindex
+ 1
885 privatedata
.barpossubindex
= self
.subindex
886 sharedata
.vposmissing
= []
887 return sharedata
.barcolumns
[sharedata
.barvalueindex
] + [sharedata
.barcolumns
[i
] for i
in range(len(sharedata
.barcolumns
)) if i
!= sharedata
.barvalueindex
]
889 def selectstyle(self
, privatedata
, sharedata
, graph
, selectindex
, selecttotal
):
891 if self
.subnames
is not None:
892 raise ValueError("subnames set for single-bar data")
893 privatedata
.barpossubname
= []
895 if self
.subnames
is not None:
896 privatedata
.barpossubname
= [self
.subnames
[selectindex
]]
898 privatedata
.barpossubname
= [selectindex
]
900 def adjustaxis(self
, privatedata
, sharedata
, graph
, column
, data
, index
):
901 if column
in sharedata
.barcolumns
[sharedata
.barvalueindex
]:
902 graph
.axes
[sharedata
.barcolumns
[sharedata
.barvalueindex
][0]].adjustrange(data
, index
)
903 if self
.fromvalue
is not None and column
== sharedata
.barcolumns
[sharedata
.barvalueindex
][0]:
904 graph
.axes
[sharedata
.barcolumns
[sharedata
.barvalueindex
][0]].adjustrange([self
.fromvalue
], None)
907 i
= sharedata
.barcolumns
.index(column
)
911 if i
== privatedata
.barpossubindex
:
912 graph
.axes
[column
[:-4]].adjustrange(data
, index
, privatedata
.barpossubname
)
914 graph
.axes
[column
[:-4]].adjustrange(data
, index
)
916 def initdrawpoints(self
, privatedata
, sharedata
, graph
):
917 sharedata
.vpos
= [None]*(len(sharedata
.barcolumns
))
918 sharedata
.vbarpos
= [[None for i
in range(2)] for x
in sharedata
.barcolumns
]
920 if self
.fromvalue
is not None:
921 vfromvalue
= graph
.axes
[sharedata
.barcolumns
[sharedata
.barvalueindex
][0]].convert(self
.fromvalue
)
929 sharedata
.vbarpos
[sharedata
.barvalueindex
] = [vfromvalue
] + [None]*len(sharedata
.barcolumns
[sharedata
.barvalueindex
])
931 def drawpoint(self
, privatedata
, sharedata
, graph
):
932 sharedata
.vposavailable
= sharedata
.vposvalid
= 1
933 for i
, barname
in enumerate(sharedata
.barcolumns
):
934 if i
== sharedata
.barvalueindex
:
935 for j
, valuename
in enumerate(sharedata
.barcolumns
[sharedata
.barvalueindex
]):
937 sharedata
.vbarpos
[i
][j
+1] = graph
.axes
[sharedata
.barcolumns
[i
][0]].convert(sharedata
.point
[valuename
])
938 except (ArithmeticError, ValueError, TypeError):
939 sharedata
.vbarpos
[i
][j
+1] = None
940 sharedata
.vpos
[i
] = sharedata
.vbarpos
[i
][-1]
944 if i
== privatedata
.barpossubindex
:
945 sharedata
.vbarpos
[i
][j
] = graph
.axes
[barname
[:-4]].convert(([sharedata
.point
[barname
]] + privatedata
.barpossubname
+ [j
]))
947 sharedata
.vbarpos
[i
][j
] = graph
.axes
[barname
[:-4]].convert((sharedata
.point
[barname
], j
))
948 except (ArithmeticError, ValueError, TypeError):
949 sharedata
.vbarpos
[i
][j
] = None
951 sharedata
.vpos
[i
] = 0.5*(sharedata
.vbarpos
[i
][0]+sharedata
.vbarpos
[i
][1])
952 except (ArithmeticError, ValueError, TypeError):
953 sharedata
.vpos
[i
] = None
954 if sharedata
.vpos
[i
] is None:
955 sharedata
.vposavailable
= sharedata
.vposvalid
= 0
956 elif sharedata
.vpos
[i
] < -self
.epsilon
or sharedata
.vpos
[i
] > 1+self
.epsilon
:
957 sharedata
.vposvalid
= 0
959 registerdefaultprovider(barpos(), ["barcolumns", "barvalueindex", "vbarpos"])
964 needsdata
= ["barvalueindex", "vbarpos"]
966 defaultfrompathattrs
= []
967 defaultbarattrs
= [color
.palette
.Rainbow
, deco
.stroked([color
.gray
.black
])]
969 def __init__(self
, frompathattrs
=[], barattrs
=[], subnames
=None, multikey
=0, epsilon
=1e-10):
970 self
.frompathattrs
= frompathattrs
971 self
.barattrs
= barattrs
972 self
.subnames
= subnames
973 self
.multikey
= multikey
974 self
.epsilon
= epsilon
976 def selectstyle(self
, privatedata
, sharedata
, graph
, selectindex
, selecttotal
):
978 privatedata
.frompathattrs
= None
980 privatedata
.frompathattrs
= self
.defaultfrompathattrs
+ self
.frompathattrs
982 if self
.barattrs
is not None:
983 privatedata
.barattrs
= attr
.selectattrs(self
.defaultbarattrs
+ self
.barattrs
, selectindex
, selecttotal
)
985 privatedata
.barattrs
= None
987 privatedata
.barattrs
= self
.defaultbarattrs
+ self
.barattrs
988 privatedata
.barselectindex
= selectindex
989 privatedata
.barselecttotal
= selecttotal
990 if privatedata
.barselecttotal
!= 1 and self
.subnames
is not None:
991 raise ValueError("subnames not allowed when iterating over bars")
993 def initdrawpoints(self
, privatedata
, sharedata
, graph
):
994 privatedata
.bartmpvpos
= [None]*4
995 l
= len(sharedata
.vbarpos
[sharedata
.barvalueindex
])
997 privatedata
.bartmplist
= []
998 for i
in xrange(1, l
):
999 barattrs
= attr
.selectattrs(privatedata
.barattrs
, i
-1, l
)
1000 if barattrs
is not None:
1001 privatedata
.bartmplist
.append((i
, barattrs
))
1003 privatedata
.bartmplist
= [(1, privatedata
.barattrs
)]
1004 if privatedata
.frompathattrs
is not None:
1005 vfromvalue
= sharedata
.vbarpos
[sharedata
.barvalueindex
][0]
1006 if vfromvalue
> self
.epsilon
and vfromvalue
< 1 - self
.epsilon
:
1007 if sharedata
.barvalueindex
:
1008 p
= graph
.vgeodesic(0, vfromvalue
, 1, vfromvalue
)
1010 p
= graph
.vgeodesic(vfromvalue
, 0, vfromvalue
, 1)
1011 graph
.stroke(p
, privatedata
.frompathattrs
)
1012 privatedata
.barcanvas
= graph
.insert(canvas
.canvas())
1014 def drawpoint(self
, privatedata
, sharedata
, graph
):
1015 if privatedata
.barattrs
is not None:
1016 for i
, barattrs
in privatedata
.bartmplist
:
1017 if None not in sharedata
.vbarpos
[1-sharedata
.barvalueindex
]+sharedata
.vbarpos
[sharedata
.barvalueindex
][i
-1:i
+1]:
1018 privatedata
.bartmpvpos
[1-sharedata
.barvalueindex
] = sharedata
.vbarpos
[1-sharedata
.barvalueindex
][0]
1019 privatedata
.bartmpvpos
[ sharedata
.barvalueindex
] = sharedata
.vbarpos
[sharedata
.barvalueindex
][i
-1]
1020 privatedata
.bartmpvpos
[3-sharedata
.barvalueindex
] = sharedata
.vbarpos
[1-sharedata
.barvalueindex
][0]
1021 privatedata
.bartmpvpos
[2+sharedata
.barvalueindex
] = sharedata
.vbarpos
[sharedata
.barvalueindex
][i
]
1022 p
= graph
.vgeodesic(*privatedata
.bartmpvpos
)
1023 privatedata
.bartmpvpos
[1-sharedata
.barvalueindex
] = sharedata
.vbarpos
[1-sharedata
.barvalueindex
][0]
1024 privatedata
.bartmpvpos
[ sharedata
.barvalueindex
] = sharedata
.vbarpos
[sharedata
.barvalueindex
][i
]
1025 privatedata
.bartmpvpos
[3-sharedata
.barvalueindex
] = sharedata
.vbarpos
[1-sharedata
.barvalueindex
][1]
1026 privatedata
.bartmpvpos
[2+sharedata
.barvalueindex
] = sharedata
.vbarpos
[sharedata
.barvalueindex
][i
]
1027 p
.append(graph
.vgeodesic_el(*privatedata
.bartmpvpos
))
1028 privatedata
.bartmpvpos
[1-sharedata
.barvalueindex
] = sharedata
.vbarpos
[1-sharedata
.barvalueindex
][1]
1029 privatedata
.bartmpvpos
[ sharedata
.barvalueindex
] = sharedata
.vbarpos
[sharedata
.barvalueindex
][i
]
1030 privatedata
.bartmpvpos
[3-sharedata
.barvalueindex
] = sharedata
.vbarpos
[1-sharedata
.barvalueindex
][1]
1031 privatedata
.bartmpvpos
[2+sharedata
.barvalueindex
] = sharedata
.vbarpos
[sharedata
.barvalueindex
][i
-1]
1032 p
.append(graph
.vgeodesic_el(*privatedata
.bartmpvpos
))
1033 privatedata
.bartmpvpos
[1-sharedata
.barvalueindex
] = sharedata
.vbarpos
[1-sharedata
.barvalueindex
][1]
1034 privatedata
.bartmpvpos
[ sharedata
.barvalueindex
] = sharedata
.vbarpos
[sharedata
.barvalueindex
][i
-1]
1035 privatedata
.bartmpvpos
[3-sharedata
.barvalueindex
] = sharedata
.vbarpos
[1-sharedata
.barvalueindex
][0]
1036 privatedata
.bartmpvpos
[2+sharedata
.barvalueindex
] = sharedata
.vbarpos
[sharedata
.barvalueindex
][i
-1]
1037 p
.append(graph
.vgeodesic_el(*privatedata
.bartmpvpos
))
1038 p
.append(path
.closepath())
1039 privatedata
.barcanvas
.fill(p
, barattrs
)
1041 def key_pt(self
, privatedata
, sharedata
, c
, x_pt
, y_pt
, width_pt
, height_pt
, dy_pt
, selectindex
, selecttotal
):
1044 for i
, barattrs
in privatedata
.bartmplist
:
1045 c
.fill(path
.rect_pt(x_pt
, y_pt
-l
*dy_pt
, width_pt
, height_pt
), barattrs
)
1049 for i
, barattrs
in privatedata
.bartmplist
:
1050 c
.fill(path
.rect_pt(x_pt
+(i
-1)*width_pt
/privatedata
.bartmplist
[-1][0], y_pt
,
1051 width_pt
/privatedata
.bartmplist
[-1][0], height_pt
), barattrs
)
1055 class barpos_new(_style
):
1057 providesdata
= ["vpos", "vposmissing", "vposavailable", "vposvalid", "vbarrange", "stacked"]
1059 # defaultfrompathattrs = []
1061 def __init__(self
, fromvalue
=None, stackname
=None, subindex
=0, subnames
=None, epsilon
=1e-10):
1062 # TODO vpos configuration ...
1063 # NOTE subindex is a perspective for higher dimensional plots
1064 # (just ignore it for the moment -- we don't even need to document about it)
1065 if fromvalue
is not None and stackname
is not None:
1066 raise ValueError("you can either start at a fromvalue or stack bars")
1067 self
.fromvalue
= fromvalue
1068 self
.stackname
= stackname
1069 self
.subindex
= subindex
1070 self
.subnames
= subnames
1071 self
.epsilon
= epsilon
1073 def columns(self
, privatedata
, sharedata
, graph
, columns
):
1074 privatedata
.barcolumns
= []
1075 privatedata
.barvalueindex
= None
1076 for dimension
, axisnames
in enumerate(graph
.axesnames
):
1078 for axisname
in axisnames
:
1079 if axisname
in columns
:
1080 if privatedata
.barvalueindex
is not None:
1081 raise ValueError("multiple values")
1082 privatedata
.barvalueindex
= dimension
1083 privatedata
.barcolumns
.append(axisname
)
1085 if (axisname
+ "name") in columns
:
1086 privatedata
.barcolumns
.append(axisname
+ "name")
1089 raise ValueError("multiple names")
1091 raise ValueError("value/name missing")
1092 if privatedata
.barvalueindex
is None:
1093 raise ValueError("missing value")
1094 if self
.stackname
is not None and self
.stackname
not in columns
:
1095 raise ValueError("stackname column missing")
1096 if self
.subindex
>= privatedata
.barvalueindex
:
1097 privatedata
.barpossubindex
= self
.subindex
+ 1
1099 privatedata
.barpossubindex
= self
.subindex
1100 sharedata
.vposmissing
= []
1101 if self
.stackname
is not None:
1102 return privatedata
.barcolumns
+ [self
.stackname
]
1104 return privatedata
.barcolumns
1106 def selectstyle(self
, privatedata
, sharedata
, graph
, selectindex
, selecttotal
):
1107 if selecttotal
== 1:
1108 if self
.subnames
is not None:
1109 raise ValueError("subnames set for single-bar data")
1110 privatedata
.barpossubname
= []
1112 if self
.subnames
is not None:
1113 privatedata
.barpossubname
= [self
.subnames
[selectindex
]]
1115 privatedata
.barpossubname
= [selectindex
]
1117 def adjustaxis(self
, privatedata
, sharedata
, graph
, column
, data
, index
):
1119 if column
== self
.stackname
:
1120 i
= privatedata
.barvalueindex
1122 i
= privatedata
.barcolumns
.index(column
)
1126 if i
== privatedata
.barvalueindex
:
1127 if self
.fromvalue
is not None:
1128 graph
.axes
[privatedata
.barcolumns
[i
]].adjustrange([self
.fromvalue
], None)
1129 if self
.stackname
is None or column
== self
.stackname
:
1130 graph
.axes
[privatedata
.barcolumns
[i
]].adjustrange(data
, index
)
1132 if i
== privatedata
.barpossubindex
:
1133 graph
.axes
[privatedata
.barcolumns
[i
][:-4]].adjustrange(data
, index
, privatedata
.barpossubname
)
1135 graph
.axes
[privatedata
.barcolumns
[i
][:-4]].adjustrange(data
, index
)
1137 def initdrawpoints(self
, privatedata
, sharedata
, graph
):
1138 sharedata
.vpos
= [None]*(len(privatedata
.barcolumns
))
1139 sharedata
.vbarrange
= [[None for i
in range(2)] for x
in privatedata
.barcolumns
]
1141 if self
.fromvalue
is not None:
1142 privatedata
.vfromvalue
= graph
.axes
[privatedata
.barcolumns
[privatedata
.barvalueindex
][0]].convert(self
.fromvalue
)
1143 if privatedata
.vfromvalue
< 0:
1144 privatedata
.vfromvalue
= 0
1145 if privatedata
.vfromvalue
> 1:
1146 privatedata
.vfromvalue
= 1
1148 privatedata
.vfromvalue
= 0
1150 def drawpoint(self
, privatedata
, sharedata
, graph
):
1151 sharedata
.vposavailable
= sharedata
.vposvalid
= 1
1152 for i
, barname
in enumerate(privatedata
.barcolumns
):
1153 if i
== privatedata
.barvalueindex
:
1155 if self
.stackname
is None:
1156 sharedata
.vbarrange
[i
][0] = privatedata
.vfromvalue
1157 sharedata
.vbarrange
[i
][1] = graph
.axes
[barname
].convert(sharedata
.point
[barname
])
1159 sharedata
.vbarrange
[i
][0] = sharedata
.vbarrange
[i
][1]
1160 sharedata
.vbarrange
[i
][1] = graph
.axes
[barname
].convert(sharedata
.point
[self
.stackname
])
1161 except (ArithmeticError, ValueError, TypeError):
1162 sharedata
.vbarrange
[i
][1] = None
1164 sharedata
.vpos
[i
] = sharedata
.vbarrange
[i
][1]
1168 if i
== privatedata
.barpossubindex
:
1169 sharedata
.vbarrange
[i
][j
] = graph
.axes
[barname
[:-4]].convert(([sharedata
.point
[barname
]] + privatedata
.barpossubname
+ [j
]))
1171 sharedata
.vbarrange
[i
][j
] = graph
.axes
[barname
[:-4]].convert((sharedata
.point
[barname
], j
))
1172 except (ArithmeticError, ValueError, TypeError):
1173 sharedata
.vbarrange
[i
][j
] = None
1175 sharedata
.vpos
[i
] = 0.5*(sharedata
.vbarrange
[i
][0]+sharedata
.vbarrange
[i
][1])
1176 except (ArithmeticError, ValueError, TypeError):
1177 sharedata
.vpos
[i
] = None
1178 if sharedata
.vpos
[i
] is None:
1179 sharedata
.vposavailable
= sharedata
.vposvalid
= 0
1180 elif sharedata
.vpos
[i
] < -self
.epsilon
or sharedata
.vpos
[i
] > 1+self
.epsilon
:
1181 sharedata
.vposvalid
= 0
1183 registerdefaultprovider(barpos_new(), ["vbarrange", "stacked"])
1186 class bar_new(_style
):
1188 needsdata
= ["vbarrange"]
1190 defaultbarattrs
= [color
.palette
.Rainbow
, deco
.stroked([color
.gray
.black
])]
1192 def __init__(self
, barattrs
=[]):
1193 self
.barattrs
= barattrs
1195 def columns(self
, privatedata
, sharedata
, graph
, columns
):
1196 if len(graph
.axesnames
) != 2:
1197 raise TypeError("bar style restricted on two-dimensional graphs")
1200 def selectstyle(self
, privatedata
, sharedata
, graph
, selectindex
, selecttotal
):
1202 privatedata
.barattrs
= attr
.selectattrs(self
.defaultbarattrs
+ self
.barattrs
, selectindex
, selecttotal
)
1204 privatedata
.barattrs
= self
.defaultbarattrs
+ self
.barattrs
1206 def initdrawpoints(self
, privatedata
, sharedata
, graph
):
1207 privatedata
.rectcanvas
= graph
.insert(canvas
.canvas())
1209 def drawpoint(self
, privatedata
, sharedata
, graph
):
1210 xvmin
= sharedata
.vbarrange
[0][0]
1211 xvmax
= sharedata
.vbarrange
[0][1]
1212 yvmin
= sharedata
.vbarrange
[1][0]
1213 yvmax
= sharedata
.vbarrange
[1][1]
1214 if None not in [xvmin
, xvmax
, yvmin
, yvmax
]:
1216 p
= graph
.vgeodesic(xvmin
, yvmin
, xvmax
, yvmin
)
1217 p
.append(graph
.vgeodesic_el(xvmax
, yvmin
, xvmax
, yvmax
))
1218 p
.append(graph
.vgeodesic_el(xvmax
, yvmax
, xvmin
, yvmax
))
1219 p
.append(graph
.vgeodesic_el(xvmin
, yvmax
, xvmin
, yvmin
))
1220 p
.append(path
.closepath())
1221 privatedata
.rectcanvas
.fill(p
, privatedata
.barattrs
)
1223 def key_pt(self
, privatedata
, sharedata
, graph
, x_pt
, y_pt
, width_pt
, height_pt
, dy_pt
, selectindex
, selecttotal
):