1 # -*- coding: ISO-8859-1 -*-
4 # Copyright (C) 2002-2004 Jörg Lehmann <joergl@users.sourceforge.net>
5 # Copyright (C) 2003-2004 Michael Schindler <m-schindler@users.sourceforge.net>
6 # Copyright (C) 2002-2006 André Wobst <wobsta@users.sourceforge.net>
8 # This file is part of PyX (http://pyx.sourceforge.net/).
10 # PyX is free software; you can redistribute it and/or modify
11 # it under the terms of the GNU General Public License as published by
12 # the Free Software Foundation; either version 2 of the License, or
13 # (at your option) any later version.
15 # PyX is distributed in the hope that it will be useful,
16 # but WITHOUT ANY WARRANTY; without even the implied warranty of
17 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 # GNU General Public License for more details.
20 # You should have received a copy of the GNU General Public License
21 # along with PyX; if not, write to the Free Software
22 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
26 from pyx
import attr
, deco
, style
, color
, unit
, canvas
, path
, mesh
27 from pyx
import text
as textmodule
34 # fallback implementation for Python 2.2. and below
36 return zip(xrange(len(list)), list)
39 """Interface class for graph styles
41 Each graph style must support the methods described in this
42 class. However, since a graph style might not need to perform
43 actions on all the various events, it does not need to overwrite
44 all methods of this base class (e.g. this class is not an abstract
45 class in any respect).
47 A style should never store private data by istance variables
48 (i.e. accessing self), but it should use the sharedata and privatedata
49 instances instead. A style instance can be used multiple times with
50 different sharedata and privatedata instances at the very same time.
51 The sharedata and privatedata instances act as data containers and
52 sharedata allows for sharing information across several styles.
54 Every style contains two class variables, which are not to be
56 - providesdata is a list of variable names a style offers via
57 the sharedata instance. This list is used to determine whether
58 all needs of subsequent styles are fullfilled. Otherwise
59 getdefaultprovider should return a proper style to be used.
60 - needsdata is a list of variable names the style needs to access in the
64 providesdata
= [] # by default, we provide nothing
65 needsdata
= [] # and do not depend on anything
67 def columnnames(self
, privatedata
, sharedata
, graph
, columnnames
):
68 """Set column information
70 This method is used setup the column name information to be
71 accessible to the style later on. The style should analyse
72 the list of column names. The method should return a list of
73 column names which the style will make use of."""
76 def adjustaxis(self
, privatedata
, sharedata
, graph
, columnname
, data
):
79 This method is called in order to adjust the axis range to
80 the provided data. columnname is the column name (each style
81 is subsequently called for all column names)."""
84 def selectstyle(self
, privatedata
, sharedata
, graph
, selectindex
, selecttotal
):
85 """Select stroke/fill attributes
87 This method is called to allow for the selection of
88 changable attributes of a style."""
91 def initdrawpoints(self
, privatedata
, sharedata
, graph
):
92 """Initialize drawing of data
94 This method might be used to initialize the drawing of data."""
97 def drawpoint(self
, privatedata
, sharedata
, graph
, point
):
100 This method is called for each data point. The data is
101 available in the dictionary point. The dictionary
102 keys are the column names."""
105 def donedrawpoints(self
, privatedata
, sharedata
, graph
):
106 """Finalize drawing of data
108 This method is called after the last data point was
109 drawn using the drawpoint method above."""
112 def key_pt(self
, privatedata
, sharedata
, graph
, x_pt
, y_pt
, width_pt
, height_pt
):
116 # The following two methods are used to register and get a default provider
117 # for keys. A key is a variable name in sharedata. A provider is a style
118 # which creates variables in sharedata.
120 _defaultprovider
= {}
122 def registerdefaultprovider(style
, keys
):
123 """sets a style as a default creator for sharedata variables 'keys'"""
124 assert not len(style
.needsdata
), "currently we state, that a style should not depend on other sharedata variables"
126 assert key
in style
.providesdata
, "key not provided by style"
127 # we might allow for overwriting the defaults, i.e. the following is not checked:
128 # assert key in _defaultprovider.keys(), "default provider already registered for key"
129 _defaultprovider
[key
] = style
131 def getdefaultprovider(key
):
132 """returns a style, which acts as a default creator for the
133 sharedata variable 'key'"""
134 return _defaultprovider
[key
]
139 providesdata
= ["vpos", "vposmissing", "vposavailable", "vposvalid", "poscolumnnames"]
141 def __init__(self
, epsilon
=1e-10):
142 self
.epsilon
= epsilon
144 def columnnames(self
, privatedata
, sharedata
, graph
, columnnames
):
145 sharedata
.poscolumnnames
= []
146 sharedata
.vposmissing
= []
147 for count
, axisnames
in enumerate(graph
.axesnames
):
148 for axisname
in axisnames
:
149 for columnname
in columnnames
:
150 if axisname
== columnname
:
151 sharedata
.poscolumnnames
.append(columnname
)
152 if len(sharedata
.poscolumnnames
) > count
+1:
153 raise ValueError("multiple axes per graph dimension")
154 elif len(sharedata
.poscolumnnames
) < count
+1:
155 sharedata
.vposmissing
.append(count
)
156 sharedata
.poscolumnnames
.append(None)
157 return [columnname
for columnname
in sharedata
.poscolumnnames
if columnname
is not None]
159 def adjustaxis(self
, privatedata
, sharedata
, graph
, columnname
, data
):
160 if columnname
in sharedata
.poscolumnnames
:
161 graph
.axes
[columnname
].adjustaxis(data
)
163 def initdrawpoints(self
, privatedata
, sharedata
, graph
):
164 sharedata
.vpos
= [None]*(len(graph
.axesnames
))
165 privatedata
.pointpostmplist
= [[columnname
, index
, graph
.axes
[columnname
]] # temporarily used by drawpoint only
166 for index
, columnname
in enumerate([columnname
for columnname
in sharedata
.poscolumnnames
if columnname
is not None])]
167 for missing
in sharedata
.vposmissing
:
168 for pointpostmp
in privatedata
.pointpostmplist
:
169 if pointpostmp
[1] >= missing
:
172 def drawpoint(self
, privatedata
, sharedata
, graph
, point
):
173 sharedata
.vposavailable
= 1 # valid position (but might be outside of the graph)
174 sharedata
.vposvalid
= 1 # valid position inside the graph
175 for columnname
, index
, axis
in privatedata
.pointpostmplist
:
177 v
= axis
.convert(point
[columnname
])
178 except (ArithmeticError, ValueError, TypeError):
179 sharedata
.vposavailable
= sharedata
.vposvalid
= 0
180 sharedata
.vpos
[index
] = None
182 if v
< -self
.epsilon
or v
> 1+self
.epsilon
:
183 sharedata
.vposvalid
= 0
184 sharedata
.vpos
[index
] = v
187 registerdefaultprovider(pos(), pos
.providesdata
)
192 providesdata
= ["vrange", "vrangemissing", "vrangeminmissing", "vrangemaxmissing"]
202 def __init__(self
, usenames
={}, epsilon
=1e-10):
203 self
.usenames
= usenames
204 self
.epsilon
= epsilon
206 def _numberofbits(self
, mask
):
210 return self
._numberofbits
(mask
>> 1) + 1
212 return self
._numberofbits
(mask
>> 1)
214 def columnnames(self
, privatedata
, sharedata
, graph
, columnnames
):
216 privatedata
.rangeposcolumns
= []
217 sharedata
.vrangemissing
= []
218 sharedata
.vrangeminmissing
= []
219 sharedata
.vrangemaxmissing
= []
220 privatedata
.rangeposdeltacolumns
= {} # temporarily used by adjustaxis only
221 for count
, axisnames
in enumerate(graph
.axesnames
):
222 for axisname
in axisnames
:
224 usename
= self
.usenames
[axisname
]
228 for columnname
in columnnames
:
230 if usename
== columnname
:
231 mask
+= self
.mask_value
232 elif usename
+ "min" == columnname
:
233 mask
+= self
.mask_min
234 elif usename
+ "max" == columnname
:
235 mask
+= self
.mask_max
236 elif "d" + usename
+ "min" == columnname
:
237 mask
+= self
.mask_dmin
238 elif "d" + usename
+ "max" == columnname
:
239 mask
+= self
.mask_dmax
240 elif "d" + usename
== columnname
:
245 usecolumns
.append(columnname
)
246 if mask
& (self
.mask_min | self
.mask_max | self
.mask_dmin | self
.mask_dmax | self
.mask_d
):
247 if (self
._numberofbits
(mask
& (self
.mask_min | self
.mask_dmin | self
.mask_d
)) > 1 or
248 self
._numberofbits
(mask
& (self
.mask_max | self
.mask_dmax | self
.mask_d
)) > 1):
249 raise ValueError("multiple range definition")
250 if mask
& (self
.mask_dmin | self
.mask_dmax | self
.mask_d
):
251 if not (mask
& self
.mask_value
):
252 raise ValueError("missing value for delta")
253 privatedata
.rangeposdeltacolumns
[axisname
] = {}
254 privatedata
.rangeposcolumns
.append((axisname
, usename
, mask
))
255 elif mask
== self
.mask_value
:
256 usecolumns
= usecolumns
[:-1]
257 if len(privatedata
.rangeposcolumns
) + len(sharedata
.vrangemissing
) > count
+1:
258 raise ValueError("multiple axes per graph dimension")
259 elif len(privatedata
.rangeposcolumns
) + len(sharedata
.vrangemissing
) < count
+1:
260 sharedata
.vrangemissing
.append(count
)
261 sharedata
.vrangeminmissing
.append(count
)
262 sharedata
.vrangemaxmissing
.append(count
)
264 if not (privatedata
.rangeposcolumns
[-1][2] & (self
.mask_min | self
.mask_dmin | self
.mask_d
)):
265 sharedata
.vrangeminmissing
.append(count
)
266 if not (privatedata
.rangeposcolumns
[-1][2] & (self
.mask_max | self
.mask_dmax | self
.mask_d
)):
267 sharedata
.vrangemaxmissing
.append(count
)
270 def adjustaxis(self
, privatedata
, sharedata
, graph
, columnname
, data
):
271 if columnname
in [c
+ "min" for a
, c
, m
in privatedata
.rangeposcolumns
if m
& self
.mask_min
]:
272 graph
.axes
[columnname
[:-3]].adjustaxis(data
)
273 if columnname
in [c
+ "max" for a
, c
, m
in privatedata
.rangeposcolumns
if m
& self
.mask_max
]:
274 graph
.axes
[columnname
[:-3]].adjustaxis(data
)
276 # delta handling: fill rangeposdeltacolumns
277 for axisname
, usename
, mask
in privatedata
.rangeposcolumns
:
278 if columnname
== usename
and mask
& (self
.mask_dmin | self
.mask_dmax | self
.mask_d
):
279 privatedata
.rangeposdeltacolumns
[axisname
][self
.mask_value
] = data
280 if columnname
== "d" + usename
+ "min" and mask
& self
.mask_dmin
:
281 privatedata
.rangeposdeltacolumns
[axisname
][self
.mask_dmin
] = data
282 if columnname
== "d" + usename
+ "max" and mask
& self
.mask_dmax
:
283 privatedata
.rangeposdeltacolumns
[axisname
][self
.mask_dmax
] = data
284 if columnname
== "d" + usename
and mask
& self
.mask_d
:
285 privatedata
.rangeposdeltacolumns
[axisname
][self
.mask_d
] = data
287 # delta handling: process rangeposdeltacolumns
288 for a
, d
in privatedata
.rangeposdeltacolumns
.items():
289 if d
.has_key(self
.mask_value
):
291 if k
!= self
.mask_value
:
292 if k
& (self
.mask_dmin | self
.mask_d
):
294 for value
, delta
in zip(d
[self
.mask_value
], d
[k
]):
296 mindata
.append(value
-delta
)
299 graph
.axes
[a
].adjustaxis(mindata
)
300 if k
& (self
.mask_dmax | self
.mask_d
):
302 for value
, delta
in zip(d
[self
.mask_value
], d
[k
]):
304 maxdata
.append(value
+delta
)
307 graph
.axes
[a
].adjustaxis(maxdata
)
310 def initdrawpoints(self
, privatedata
, sharedata
, graph
):
311 sharedata
.vrange
= [[None for x
in xrange(2)] for y
in privatedata
.rangeposcolumns
+ sharedata
.vrangemissing
]
312 privatedata
.rangepostmplist
= [[usename
, mask
, index
, graph
.axes
[axisname
]] # temporarily used by drawpoint only
313 for index
, (axisname
, usename
, mask
) in enumerate(privatedata
.rangeposcolumns
)]
314 for missing
in sharedata
.vrangemissing
:
315 for rangepostmp
in privatedata
.rangepostmplist
:
316 if rangepostmp
[2] >= missing
:
319 def drawpoint(self
, privatedata
, sharedata
, graph
, point
):
320 for usename
, mask
, index
, axis
in privatedata
.rangepostmplist
:
322 if mask
& self
.mask_min
:
323 sharedata
.vrange
[index
][0] = axis
.convert(point
[usename
+ "min"])
324 if mask
& self
.mask_dmin
:
325 sharedata
.vrange
[index
][0] = axis
.convert(point
[usename
] - point
["d" + usename
+ "min"])
326 if mask
& self
.mask_d
:
327 sharedata
.vrange
[index
][0] = axis
.convert(point
[usename
] - point
["d" + usename
])
328 except (ArithmeticError, ValueError, TypeError):
329 sharedata
.vrange
[index
][0] = None
331 if mask
& self
.mask_max
:
332 sharedata
.vrange
[index
][1] = axis
.convert(point
[usename
+ "max"])
333 if mask
& self
.mask_dmax
:
334 sharedata
.vrange
[index
][1] = axis
.convert(point
[usename
] + point
["d" + usename
+ "max"])
335 if mask
& self
.mask_d
:
336 sharedata
.vrange
[index
][1] = axis
.convert(point
[usename
] + point
["d" + usename
])
337 except (ArithmeticError, ValueError, TypeError):
338 sharedata
.vrange
[index
][1] = None
340 # some range checks for data consistency
341 if (sharedata
.vrange
[index
][0] is not None and sharedata
.vrange
[index
][1] is not None and
342 sharedata
.vrange
[index
][0] > sharedata
.vrange
[index
][1] + self
.epsilon
):
343 raise ValueError("inverse range")
344 # disabled due to missing vpos access:
345 # if (sharedata.vrange[index][0] is not None and sharedata.vpos[index] is not None and
346 # sharedata.vrange[index][0] > sharedata.vpos[index] + self.epsilon):
347 # raise ValueError("negative minimum errorbar")
348 # if (sharedata.vrange[index][1] is not None and sharedata.vpos[index] is not None and
349 # sharedata.vrange[index][1] < sharedata.vpos[index] - self.epsilon):
350 # raise ValueError("negative maximum errorbar")
353 registerdefaultprovider(range(), range.providesdata
)
356 def _crosssymbol(c
, x_pt
, y_pt
, size_pt
, attrs
):
357 c
.draw(path
.path(path
.moveto_pt(x_pt
-0.5*size_pt
, y_pt
-0.5*size_pt
),
358 path
.lineto_pt(x_pt
+0.5*size_pt
, y_pt
+0.5*size_pt
),
359 path
.moveto_pt(x_pt
-0.5*size_pt
, y_pt
+0.5*size_pt
),
360 path
.lineto_pt(x_pt
+0.5*size_pt
, y_pt
-0.5*size_pt
)), attrs
)
362 def _plussymbol(c
, x_pt
, y_pt
, size_pt
, attrs
):
363 c
.draw(path
.path(path
.moveto_pt(x_pt
-0.707106781*size_pt
, y_pt
),
364 path
.lineto_pt(x_pt
+0.707106781*size_pt
, y_pt
),
365 path
.moveto_pt(x_pt
, y_pt
-0.707106781*size_pt
),
366 path
.lineto_pt(x_pt
, y_pt
+0.707106781*size_pt
)), attrs
)
368 def _squaresymbol(c
, x_pt
, y_pt
, size_pt
, attrs
):
369 c
.draw(path
.path(path
.moveto_pt(x_pt
-0.5*size_pt
, y_pt
-0.5*size_pt
),
370 path
.lineto_pt(x_pt
+0.5*size_pt
, y_pt
-0.5*size_pt
),
371 path
.lineto_pt(x_pt
+0.5*size_pt
, y_pt
+0.5*size_pt
),
372 path
.lineto_pt(x_pt
-0.5*size_pt
, y_pt
+0.5*size_pt
),
373 path
.closepath()), attrs
)
375 def _trianglesymbol(c
, x_pt
, y_pt
, size_pt
, attrs
):
376 c
.draw(path
.path(path
.moveto_pt(x_pt
-0.759835685*size_pt
, y_pt
-0.438691337*size_pt
),
377 path
.lineto_pt(x_pt
+0.759835685*size_pt
, y_pt
-0.438691337*size_pt
),
378 path
.lineto_pt(x_pt
, y_pt
+0.877382675*size_pt
),
379 path
.closepath()), attrs
)
381 def _circlesymbol(c
, x_pt
, y_pt
, size_pt
, attrs
):
382 c
.draw(path
.path(path
.arc_pt(x_pt
, y_pt
, 0.564189583*size_pt
, 0, 360),
383 path
.closepath()), attrs
)
385 def _diamondsymbol(c
, x_pt
, y_pt
, size_pt
, attrs
):
386 c
.draw(path
.path(path
.moveto_pt(x_pt
-0.537284965*size_pt
, y_pt
),
387 path
.lineto_pt(x_pt
, y_pt
-0.930604859*size_pt
),
388 path
.lineto_pt(x_pt
+0.537284965*size_pt
, y_pt
),
389 path
.lineto_pt(x_pt
, y_pt
+0.930604859*size_pt
),
390 path
.closepath()), attrs
)
393 class _styleneedingpointpos(_style
):
395 needsdata
= ["vposmissing"]
397 def columnnames(self
, privatedata
, sharedata
, graph
, columnnames
):
398 if len(sharedata
.vposmissing
):
399 raise ValueError("incomplete position information")
403 class symbol(_styleneedingpointpos
):
405 needsdata
= ["vpos", "vposmissing", "vposvalid"]
407 # "inject" the predefinied symbols into the class:
409 # Note, that statements like cross = _crosssymbol are
410 # invalid, since the would lead to unbound methods, but
411 # a single entry changeable list does the trick.
413 # Once we require Python 2.2+ we should use staticmethods
414 # to implement the default symbols inplace.
416 cross
= attr
.changelist([_crosssymbol
])
417 plus
= attr
.changelist([_plussymbol
])
418 square
= attr
.changelist([_squaresymbol
])
419 triangle
= attr
.changelist([_trianglesymbol
])
420 circle
= attr
.changelist([_circlesymbol
])
421 diamond
= attr
.changelist([_diamondsymbol
])
423 changecross
= attr
.changelist([_crosssymbol
, _plussymbol
, _squaresymbol
, _trianglesymbol
, _circlesymbol
, _diamondsymbol
])
424 changeplus
= attr
.changelist([_plussymbol
, _squaresymbol
, _trianglesymbol
, _circlesymbol
, _diamondsymbol
, _crosssymbol
])
425 changesquare
= attr
.changelist([_squaresymbol
, _trianglesymbol
, _circlesymbol
, _diamondsymbol
, _crosssymbol
, _plussymbol
])
426 changetriangle
= attr
.changelist([_trianglesymbol
, _circlesymbol
, _diamondsymbol
, _crosssymbol
, _plussymbol
, _squaresymbol
])
427 changecircle
= attr
.changelist([_circlesymbol
, _diamondsymbol
, _crosssymbol
, _plussymbol
, _squaresymbol
, _trianglesymbol
])
428 changediamond
= attr
.changelist([_diamondsymbol
, _crosssymbol
, _plussymbol
, _squaresymbol
, _trianglesymbol
, _circlesymbol
])
429 changesquaretwice
= attr
.changelist([_squaresymbol
, _squaresymbol
, _trianglesymbol
, _trianglesymbol
, _circlesymbol
, _circlesymbol
, _diamondsymbol
, _diamondsymbol
])
430 changetriangletwice
= attr
.changelist([_trianglesymbol
, _trianglesymbol
, _circlesymbol
, _circlesymbol
, _diamondsymbol
, _diamondsymbol
, _squaresymbol
, _squaresymbol
])
431 changecircletwice
= attr
.changelist([_circlesymbol
, _circlesymbol
, _diamondsymbol
, _diamondsymbol
, _squaresymbol
, _squaresymbol
, _trianglesymbol
, _trianglesymbol
])
432 changediamondtwice
= attr
.changelist([_diamondsymbol
, _diamondsymbol
, _squaresymbol
, _squaresymbol
, _trianglesymbol
, _trianglesymbol
, _circlesymbol
, _circlesymbol
])
434 changestrokedfilled
= attr
.changelist([deco
.stroked
, deco
.filled
])
435 changefilledstroked
= attr
.changelist([deco
.filled
, deco
.stroked
])
437 defaultsymbolattrs
= [deco
.stroked
]
439 def __init__(self
, symbol
=changecross
, size
=0.2*unit
.v_cm
, symbolattrs
=[]):
442 self
.symbolattrs
= symbolattrs
444 def selectstyle(self
, privatedata
, sharedata
, graph
, selectindex
, selecttotal
):
445 privatedata
.symbol
= attr
.selectattr(self
.symbol
, selectindex
, selecttotal
)
446 privatedata
.size_pt
= unit
.topt(attr
.selectattr(self
.size
, selectindex
, selecttotal
))
447 if self
.symbolattrs
is not None:
448 privatedata
.symbolattrs
= attr
.selectattrs(self
.defaultsymbolattrs
+ self
.symbolattrs
, selectindex
, selecttotal
)
450 privatedata
.symbolattrs
= None
452 def initdrawpoints(self
, privatedata
, sharedata
, graph
):
453 privatedata
.symbolcanvas
= canvas
.canvas()
455 def drawpoint(self
, privatedata
, sharedata
, graph
, point
):
456 if sharedata
.vposvalid
and privatedata
.symbolattrs
is not None:
457 x_pt
, y_pt
= graph
.vpos_pt(*sharedata
.vpos
)
458 privatedata
.symbol(privatedata
.symbolcanvas
, x_pt
, y_pt
, privatedata
.size_pt
, privatedata
.symbolattrs
)
460 def donedrawpoints(self
, privatedata
, sharedata
, graph
):
461 graph
.insert(privatedata
.symbolcanvas
)
463 def key_pt(self
, privatedata
, sharedata
, graph
, x_pt
, y_pt
, width_pt
, height_pt
):
464 if privatedata
.symbolattrs
is not None:
465 privatedata
.symbol(graph
, x_pt
+0.5*width_pt
, y_pt
+0.5*height_pt
, privatedata
.size_pt
, privatedata
.symbolattrs
)
468 class _line(_styleneedingpointpos
):
470 # this style is not a complete style, but it provides the basic functionality to
471 # create a line, which is cut at the graph boundaries (or at otherwise invalid points)
473 needsdata
= ["vposmissing"]
475 def initpointstopath(self
, privatedata
):
476 privatedata
.path
= path
.path()
477 privatedata
.linebasepoints
= []
478 privatedata
.lastvpos
= None
480 def addpointstopath(self
, privatedata
):
481 # add baselinepoints to privatedata.path
482 if len(privatedata
.linebasepoints
) > 1:
483 privatedata
.path
.append(path
.moveto_pt(*privatedata
.linebasepoints
[0]))
484 if len(privatedata
.linebasepoints
) > 2:
485 privatedata
.path
.append(path
.multilineto_pt(privatedata
.linebasepoints
[1:]))
487 privatedata
.path
.append(path
.lineto_pt(*privatedata
.linebasepoints
[1]))
488 privatedata
.linebasepoints
= []
490 def addpoint(self
, privatedata
, graphvpos_pt
, vposavailable
, vposvalid
, vpos
):
491 # append linebasepoints
493 if len(privatedata
.linebasepoints
):
494 # the last point was inside the graph
495 if vposvalid
: # shortcut for the common case
496 privatedata
.linebasepoints
.append(graphvpos_pt(*vpos
))
500 for vstart
, vend
in zip(privatedata
.lastvpos
, vpos
):
503 # 1 = vstart + (vend - vstart) * cut
505 newcut
= (1 - vstart
)/(vend
- vstart
)
506 except (ArithmeticError, TypeError):
509 # 0 = vstart + (vend - vstart) * cut
511 newcut
= - vstart
/(vend
- vstart
)
512 except (ArithmeticError, TypeError):
514 if newcut
is not None and newcut
< cut
:
518 for vstart
, vend
in zip(privatedata
.lastvpos
, vpos
):
519 cutvpos
.append(vstart
+ (vend
- vstart
) * cut
)
520 privatedata
.linebasepoints
.append(graphvpos_pt(*cutvpos
))
521 self
.addpointstopath(privatedata
)
523 # the last point was outside the graph
524 if privatedata
.lastvpos
is not None:
528 for vstart
, vend
in zip(privatedata
.lastvpos
, vpos
):
531 # 1 = vstart + (vend - vstart) * cut
533 newcut
= (1 - vstart
)/(vend
- vstart
)
534 except (ArithmeticError, TypeError):
537 # 0 = vstart + (vend - vstart) * cut
539 newcut
= - vstart
/(vend
- vstart
)
540 except (ArithmeticError, TypeError):
542 if newcut
is not None and newcut
> cut
:
546 for vstart
, vend
in zip(privatedata
.lastvpos
, vpos
):
547 cutvpos
.append(vstart
+ (vend
- vstart
) * cut
)
548 privatedata
.linebasepoints
.append(graphvpos_pt(*cutvpos
))
549 privatedata
.linebasepoints
.append(graphvpos_pt(*vpos
))
551 # sometimes cut beginning and end
554 for vstart
, vend
in zip(privatedata
.lastvpos
, vpos
):
559 # 1 = vstart + (vend - vstart) * cutfrom
561 newcutfrom
= (1 - vstart
)/(vend
- vstart
)
562 except (ArithmeticError, TypeError):
567 # 0 = vstart + (vend - vstart) * cutfrom
569 newcutfrom
= - vstart
/(vend
- vstart
)
570 except (ArithmeticError, TypeError):
572 if newcutfrom
is not None and newcutfrom
> cutfrom
:
576 # 1 = vstart + (vend - vstart) * cutto
578 newcutto
= (1 - vstart
)/(vend
- vstart
)
579 except (ArithmeticError, TypeError):
582 # 0 = vstart + (vend - vstart) * cutto
584 newcutto
= - vstart
/(vend
- vstart
)
585 except (ArithmeticError, TypeError):
587 if newcutto
is not None and newcutto
< cutto
:
593 for vstart
, vend
in zip(privatedata
.lastvpos
, vpos
):
594 cutfromvpos
.append(vstart
+ (vend
- vstart
) * cutfrom
)
595 cuttovpos
.append(vstart
+ (vend
- vstart
) * cutto
)
596 privatedata
.linebasepoints
.append(graphvpos_pt(*cutfromvpos
))
597 privatedata
.linebasepoints
.append(graphvpos_pt(*cuttovpos
))
598 self
.addpointstopath(privatedata
)
599 privatedata
.lastvpos
= vpos
[:]
601 if len(privatedata
.linebasepoints
) > 1:
602 self
.addpointstopath(privatedata
)
603 privatedata
.lastvpos
= None
605 def addinvalid(self
, privatedata
):
606 if len(privatedata
.linebasepoints
) > 1:
607 self
.addpointstopath(privatedata
)
608 privatedata
.lastvpos
= None
610 def donepointstopath(self
, privatedata
):
611 if len(privatedata
.linebasepoints
) > 1:
612 self
.addpointstopath(privatedata
)
613 return privatedata
.path
618 needsdata
= ["vpos", "vposmissing", "vposavailable", "vposvalid"]
620 changelinestyle
= attr
.changelist([style
.linestyle
.solid
,
621 style
.linestyle
.dashed
,
622 style
.linestyle
.dotted
,
623 style
.linestyle
.dashdotted
])
625 defaultlineattrs
= [changelinestyle
]
627 def __init__(self
, lineattrs
=[]):
628 self
.lineattrs
= lineattrs
630 def selectstyle(self
, privatedata
, sharedata
, graph
, selectindex
, selecttotal
):
631 if self
.lineattrs
is not None:
632 privatedata
.lineattrs
= attr
.selectattrs(self
.defaultlineattrs
+ self
.lineattrs
, selectindex
, selecttotal
)
634 privatedata
.lineattrs
= None
636 def initdrawpoints(self
, privatedata
, sharedata
, graph
):
637 self
.initpointstopath(privatedata
)
639 def drawpoint(self
, privatedata
, sharedata
, graph
, point
):
640 self
.addpoint(privatedata
, graph
.vpos_pt
, sharedata
.vposavailable
, sharedata
.vposvalid
, sharedata
.vpos
)
642 def donedrawpoints(self
, privatedata
, sharedata
, graph
):
643 path
= self
.donepointstopath(privatedata
)
644 if privatedata
.lineattrs
is not None and len(path
):
645 graph
.stroke(path
, privatedata
.lineattrs
)
647 def key_pt(self
, privatedata
, sharedata
, graph
, x_pt
, y_pt
, width_pt
, height_pt
):
648 if privatedata
.lineattrs
is not None:
649 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
)
652 class impulses(_styleneedingpointpos
):
654 needsdata
= ["vpos", "vposmissing", "vposavailable", "vposvalid", "poscolumnnames"]
656 changelinestyle
= attr
.changelist([style
.linestyle
.solid
,
657 style
.linestyle
.dashed
,
658 style
.linestyle
.dotted
,
659 style
.linestyle
.dashdotted
])
661 defaultlineattrs
= [changelinestyle
]
662 defaultfrompathattrs
= []
664 def __init__(self
, lineattrs
=[], fromvalue
=0, frompathattrs
=[], valueaxisindex
=1):
665 self
.lineattrs
= lineattrs
666 self
.fromvalue
= fromvalue
667 self
.frompathattrs
= frompathattrs
668 self
.valueaxisindex
= valueaxisindex
670 def selectstyle(self
, privatedata
, sharedata
, graph
, selectindex
, selecttotal
):
671 privatedata
.insertfrompath
= selectindex
== 0
672 if self
.lineattrs
is not None:
673 privatedata
.lineattrs
= attr
.selectattrs(self
.defaultlineattrs
+ self
.lineattrs
, selectindex
, selecttotal
)
675 privatedata
.lineattrs
= None
677 def adjustaxis(self
, privatedata
, sharedata
, graph
, columnname
, data
):
678 if self
.fromvalue
is not None:
680 i
= sharedata
.poscolumnnames
.index(columnname
)
684 if i
== self
.valueaxisindex
:
685 graph
.axes
[sharedata
.poscolumnnames
[i
]].adjustaxis([self
.fromvalue
])
687 def initdrawpoints(self
, privatedata
, sharedata
, graph
):
688 privatedata
.impulsescanvas
= canvas
.canvas()
689 if self
.fromvalue
is not None:
690 valueaxisname
= sharedata
.poscolumnnames
[self
.valueaxisindex
]
691 privatedata
.vfromvalue
= graph
.axes
[valueaxisname
].convert(self
.fromvalue
)
692 privatedata
.vfromvaluecut
= 0
693 if privatedata
.vfromvalue
< 0:
694 privatedata
.vfromvalue
= 0
695 if privatedata
.vfromvalue
> 1:
696 privatedata
.vfromvalue
= 1
697 if self
.frompathattrs
is not None and privatedata
.insertfrompath
:
698 graph
.stroke(graph
.axes
[valueaxisname
].vgridpath(privatedata
.vfromvalue
),
699 self
.defaultfrompathattrs
+ self
.frompathattrs
)
701 privatedata
.vfromvalue
= 0
703 def drawpoint(self
, privatedata
, sharedata
, graph
, point
):
704 if sharedata
.vposvalid
and privatedata
.lineattrs
is not None:
705 vpos
= sharedata
.vpos
[:]
706 vpos
[self
.valueaxisindex
] = privatedata
.vfromvalue
707 privatedata
.impulsescanvas
.stroke(graph
.vgeodesic(*(vpos
+ sharedata
.vpos
)), privatedata
.lineattrs
)
709 def donedrawpoints(self
, privatedata
, sharedata
, graph
):
710 graph
.insert(privatedata
.impulsescanvas
)
712 def key_pt(self
, privatedata
, sharedata
, graph
, x_pt
, y_pt
, width_pt
, height_pt
):
713 if privatedata
.lineattrs
is not None:
714 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
)
717 class errorbar(_style
):
719 needsdata
= ["vpos", "vposmissing", "vposavailable", "vposvalid", "vrange", "vrangeminmissing", "vrangemaxmissing"]
721 defaulterrorbarattrs
= []
723 def __init__(self
, size
=0.1*unit
.v_cm
,
727 self
.errorbarattrs
= errorbarattrs
728 self
.epsilon
= epsilon
730 def columnnames(self
, privatedata
, sharedata
, graph
, columnnames
):
731 for i
in sharedata
.vposmissing
:
732 if i
in sharedata
.vrangeminmissing
and i
in sharedata
.vrangemaxmissing
:
733 raise ValueError("position and range for a graph dimension missing")
736 def selectstyle(self
, privatedata
, sharedata
, graph
, selectindex
, selecttotal
):
737 privatedata
.errorsize_pt
= unit
.topt(attr
.selectattr(self
.size
, selectindex
, selecttotal
))
738 privatedata
.errorbarattrs
= attr
.selectattrs(self
.defaulterrorbarattrs
+ self
.errorbarattrs
, selectindex
, selecttotal
)
740 def initdrawpoints(self
, privatedata
, sharedata
, graph
):
741 if privatedata
.errorbarattrs
is not None:
742 privatedata
.errorbarcanvas
= canvas
.canvas(privatedata
.errorbarattrs
)
743 privatedata
.dimensionlist
= list(xrange(len(sharedata
.vpos
)))
745 def drawpoint(self
, privatedata
, sharedata
, graph
, point
):
746 if privatedata
.errorbarattrs
is not None:
747 for i
in privatedata
.dimensionlist
:
748 for j
in privatedata
.dimensionlist
:
750 (sharedata
.vpos
[j
] is None or
751 sharedata
.vpos
[j
] < -self
.epsilon
or
752 sharedata
.vpos
[j
] > 1+self
.epsilon
)):
755 if ((sharedata
.vrange
[i
][0] is None and sharedata
.vpos
[i
] is None) or
756 (sharedata
.vrange
[i
][1] is None and sharedata
.vpos
[i
] is None) or
757 (sharedata
.vrange
[i
][0] is None and sharedata
.vrange
[i
][1] is None)):
759 vminpos
= sharedata
.vpos
[:]
760 if sharedata
.vrange
[i
][0] is not None:
761 vminpos
[i
] = sharedata
.vrange
[i
][0]
765 if vminpos
[i
] > 1+self
.epsilon
:
767 if vminpos
[i
] < -self
.epsilon
:
770 vmaxpos
= sharedata
.vpos
[:]
771 if sharedata
.vrange
[i
][1] is not None:
772 vmaxpos
[i
] = sharedata
.vrange
[i
][1]
776 if vmaxpos
[i
] < -self
.epsilon
:
778 if vmaxpos
[i
] > 1+self
.epsilon
:
781 privatedata
.errorbarcanvas
.stroke(graph
.vgeodesic(*(vminpos
+ vmaxpos
)))
782 for j
in privatedata
.dimensionlist
:
785 privatedata
.errorbarcanvas
.stroke(graph
.vcap_pt(j
, privatedata
.errorsize_pt
, *vminpos
))
787 privatedata
.errorbarcanvas
.stroke(graph
.vcap_pt(j
, privatedata
.errorsize_pt
, *vmaxpos
))
789 def donedrawpoints(self
, privatedata
, sharedata
, graph
):
790 if privatedata
.errorbarattrs
is not None:
791 graph
.insert(privatedata
.errorbarcanvas
)
794 class text(_styleneedingpointpos
):
796 needsdata
= ["vpos", "vposmissing", "vposvalid"]
798 defaulttextattrs
= [textmodule
.halign
.center
, textmodule
.vshift
.mathaxis
]
800 def __init__(self
, textname
="text", dxname
=None, dyname
=None,
801 dxunit
=0.3*unit
.v_cm
, dyunit
=0.3*unit
.v_cm
,
802 textdx
=0*unit
.v_cm
, textdy
=0.3*unit
.v_cm
, textattrs
=[]):
803 self
.textname
= textname
810 self
.textattrs
= textattrs
812 def columnnames(self
, privatedata
, sharedata
, graph
, columnnames
):
813 if self
.textname
not in columnnames
:
814 raise ValueError("column '%s' missing" % self
.textname
)
815 names
= [self
.textname
]
816 if self
.dxname
is not None:
817 if self
.dxname
not in columnnames
:
818 raise ValueError("column '%s' missing" % self
.dxname
)
819 names
.append(self
.dxname
)
820 if self
.dyname
is not None:
821 if self
.dyname
not in columnnames
:
822 raise ValueError("column '%s' missing" % self
.dyname
)
823 names
.append(self
.dyname
)
824 return names
+ _styleneedingpointpos
.columnnames(self
, privatedata
, sharedata
, graph
, columnnames
)
826 def selectstyle(self
, privatedata
, sharedata
, graph
, selectindex
, selecttotal
):
827 if self
.textattrs
is not None:
828 privatedata
.textattrs
= attr
.selectattrs(self
.defaulttextattrs
+ self
.textattrs
, selectindex
, selecttotal
)
830 privatedata
.textattrs
= None
832 def initdrawpoints(self
, privatedata
, sharedata
, grap
):
833 if self
.dxname
is None:
834 privatedata
.textdx_pt
= unit
.topt(self
.textdx
)
836 privatedata
.dxunit_pt
= unit
.topt(self
.dxunit
)
837 if self
.dyname
is None:
838 privatedata
.textdy_pt
= unit
.topt(self
.textdy
)
840 privatedata
.dyunit_pt
= unit
.topt(self
.dyunit
)
842 def drawpoint(self
, privatedata
, sharedata
, graph
, point
):
843 if privatedata
.textattrs
is not None and sharedata
.vposvalid
:
844 x_pt
, y_pt
= graph
.vpos_pt(*sharedata
.vpos
)
846 text
= str(point
[self
.textname
])
850 if self
.dxname
is None:
851 dx_pt
= privatedata
.textdx_pt
853 dx_pt
= float(point
[self
.dxname
]) * privatedata
.dxunit_pt
854 if self
.dyname
is None:
855 dy_pt
= privatedata
.textdy_pt
857 dy_pt
= float(point
[self
.dyname
]) * privatedata
.dyunit_pt
858 graph
.text_pt(x_pt
+ dx_pt
, y_pt
+ dy_pt
, text
, privatedata
.textattrs
)
860 def key_pt(self
, privatedata
, sharedata
, graph
, x_pt
, y_pt
, width_pt
, height_pt
):
861 raise RuntimeError("Style currently doesn't provide a graph key")
864 class arrow(_styleneedingpointpos
):
866 needsdata
= ["vpos", "vposmissing", "vposvalid"]
868 defaultlineattrs
= []
869 defaultarrowattrs
= []
871 def __init__(self
, linelength
=0.25*unit
.v_cm
, arrowsize
=0.15*unit
.v_cm
, lineattrs
=[], arrowattrs
=[], arrowpos
=0.5, epsilon
=1e-5):
872 self
.linelength
= linelength
873 self
.arrowsize
= arrowsize
874 self
.lineattrs
= lineattrs
875 self
.arrowattrs
= arrowattrs
876 self
.arrowpos
= arrowpos
877 self
.epsilon
= epsilon
879 def columnnames(self
, privatedata
, sharedata
, graph
, columnnames
):
880 if len(graph
.axesnames
) != 2:
881 raise ValueError("arrow style restricted on two-dimensional graphs")
882 if "size" not in columnnames
:
883 raise ValueError("size missing")
884 if "angle" not in columnnames
:
885 raise ValueError("angle missing")
886 return ["size", "angle"] + _styleneedingpointpos
.columnnames(self
, privatedata
, sharedata
, graph
, columnnames
)
888 def selectstyle(self
, privatedata
, sharedata
, graph
, selectindex
, selecttotal
):
889 if self
.lineattrs
is not None:
890 privatedata
.lineattrs
= attr
.selectattrs(self
.defaultlineattrs
+ self
.lineattrs
, selectindex
, selecttotal
)
892 privatedata
.lineattrs
= None
893 if self
.arrowattrs
is not None:
894 privatedata
.arrowattrs
= attr
.selectattrs(self
.defaultarrowattrs
+ self
.arrowattrs
, selectindex
, selecttotal
)
896 privatedata
.arrowattrs
= None
898 def initdrawpoints(self
, privatedata
, sharedata
, graph
):
899 privatedata
.arrowcanvas
= canvas
.canvas()
901 def drawpoint(self
, privatedata
, sharedata
, graph
, point
):
902 if privatedata
.lineattrs
is not None and privatedata
.arrowattrs
is not None and sharedata
.vposvalid
:
903 linelength_pt
= unit
.topt(self
.linelength
)
904 x_pt
, y_pt
= graph
.vpos_pt(*sharedata
.vpos
)
906 angle
= point
["angle"] + 0.0
907 size
= point
["size"] + 0.0
911 if point
["size"] > self
.epsilon
:
912 dx
= math
.cos(angle
*math
.pi
/180)
913 dy
= math
.sin(angle
*math
.pi
/180)
914 x1
= x_pt
-self
.arrowpos
*dx
*linelength_pt
*size
915 y1
= y_pt
-self
.arrowpos
*dy
*linelength_pt
*size
916 x2
= x_pt
+(1-self
.arrowpos
)*dx
*linelength_pt
*size
917 y2
= y_pt
+(1-self
.arrowpos
)*dy
*linelength_pt
*size
918 privatedata
.arrowcanvas
.stroke(path
.line_pt(x1
, y1
, x2
, y2
), privatedata
.lineattrs
+
919 [deco
.earrow(privatedata
.arrowattrs
, size
=self
.arrowsize
*size
)])
921 def donedrawpoints(self
, privatedata
, sharedata
, graph
):
922 graph
.insert(privatedata
.arrowcanvas
)
924 def key_pt(self
, privatedata
, sharedata
, graph
, x_pt
, y_pt
, width_pt
, height_pt
):
925 raise RuntimeError("Style currently doesn't provide a graph key")
930 needsdata
= ["vrange", "vrangeminmissing", "vrangemaxmissing"]
932 def __init__(self
, gradient
=color
.gradient
.Grey
):
933 self
.gradient
= gradient
935 def columnnames(self
, privatedata
, sharedata
, graph
, columnnames
):
936 if len(graph
.axesnames
) != 2:
937 raise TypeError("arrow style restricted on two-dimensional graphs")
938 if "color" not in columnnames
:
939 raise ValueError("color missing")
940 if len(sharedata
.vrangeminmissing
) + len(sharedata
.vrangemaxmissing
):
941 raise ValueError("incomplete range")
944 def initdrawpoints(self
, privatedata
, sharedata
, graph
):
945 privatedata
.rectcanvas
= graph
.insert(canvas
.canvas())
947 def drawpoint(self
, privatedata
, sharedata
, graph
, point
):
948 xvmin
= sharedata
.vrange
[0][0]
949 xvmax
= sharedata
.vrange
[0][1]
950 yvmin
= sharedata
.vrange
[1][0]
951 yvmax
= sharedata
.vrange
[1][1]
952 if (xvmin
is not None and xvmin
< 1 and
953 xvmax
is not None and xvmax
> 0 and
954 yvmin
is not None and yvmin
< 1 and
955 yvmax
is not None and yvmax
> 0):
964 p
= graph
.vgeodesic(xvmin
, yvmin
, xvmax
, yvmin
)
965 p
.append(graph
.vgeodesic_el(xvmax
, yvmin
, xvmax
, yvmax
))
966 p
.append(graph
.vgeodesic_el(xvmax
, yvmax
, xvmin
, yvmax
))
967 p
.append(graph
.vgeodesic_el(xvmin
, yvmax
, xvmin
, yvmin
))
968 p
.append(path
.closepath())
969 privatedata
.rectcanvas
.fill(p
, [self
.gradient
.getcolor(point
["color"])])
971 def key_pt(self
, privatedata
, sharedata
, graph
, x_pt
, y_pt
, width_pt
, height_pt
):
972 raise RuntimeError("Style currently doesn't provide a graph key")
975 class histogram(_style
):
977 needsdata
= ["vpos", "vposmissing", "vrange", "vrangeminmissing", "vrangemaxmissing"]
979 defaultlineattrs
= [deco
.stroked
]
980 defaultfrompathattrs
= []
982 def __init__(self
, lineattrs
=[], steps
=0, fromvalue
=0, frompathattrs
=[], fillable
=0, rectkey
=0,
983 autohistogramaxisindex
=0, autohistogrampointpos
=0.5, epsilon
=1e-10):
984 self
.lineattrs
= lineattrs
986 self
.fromvalue
= fromvalue
987 self
.frompathattrs
= frompathattrs
988 self
.fillable
= fillable
# TODO: fillable paths might not properly be closed by straight lines on curved graph geometries
989 self
.rectkey
= rectkey
990 self
.autohistogramaxisindex
= autohistogramaxisindex
991 self
.autohistogrampointpos
= autohistogrampointpos
992 self
.epsilon
= epsilon
994 def columnnames(self
, privatedata
, sharedata
, graph
, columnnames
):
995 if len(graph
.axesnames
) != 2:
996 raise TypeError("histogram style restricted on two-dimensional graphs")
997 privatedata
.rangeaxisindex
= None
998 for i
in builtinrange(len(graph
.axesnames
)):
999 if i
in sharedata
.vrangeminmissing
or i
in sharedata
.vrangemaxmissing
:
1000 if i
in sharedata
.vposmissing
:
1001 raise ValueError("pos and range missing")
1003 if privatedata
.rangeaxisindex
is not None:
1004 raise ValueError("multiple ranges")
1005 privatedata
.rangeaxisindex
= i
1006 if privatedata
.rangeaxisindex
is None:
1007 privatedata
.rangeaxisindex
= self
.autohistogramaxisindex
1008 privatedata
.autohistogram
= 1
1010 privatedata
.autohistogram
= 0
1013 def adjustaxis(self
, privatedata
, sharedata
, graph
, columnname
, data
):
1014 if privatedata
.autohistogram
and columnname
== sharedata
.poscolumnnames
[privatedata
.rangeaxisindex
]:
1016 raise ValueError("several data points needed for automatic histogram width calculation")
1018 delta
= data
[1] - data
[0]
1019 min = data
[0] - self
.autohistogrampointpos
* delta
1020 max = data
[-1] + (1-self
.autohistogrampointpos
) * delta
1021 graph
.axes
[columnname
].adjustaxis([min, max])
1022 elif self
.fromvalue
is not None and columnname
== sharedata
.poscolumnnames
[1-privatedata
.rangeaxisindex
]:
1023 graph
.axes
[columnname
].adjustaxis([self
.fromvalue
])
1025 def selectstyle(self
, privatedata
, sharedata
, graph
, selectindex
, selecttotal
):
1026 privatedata
.insertfrompath
= selectindex
== 0
1027 if self
.lineattrs
is not None:
1028 privatedata
.lineattrs
= attr
.selectattrs(self
.defaultlineattrs
+ self
.lineattrs
, selectindex
, selecttotal
)
1030 privatedata
.lineattrs
= None
1032 def vmoveto(self
, privatedata
, sharedata
, graph
, vpos
, vvalue
):
1033 if -self
.epsilon
< vpos
< 1+self
.epsilon
and -self
.epsilon
< vvalue
< 1+self
.epsilon
:
1034 if privatedata
.rangeaxisindex
:
1035 privatedata
.path
.append(path
.moveto_pt(*graph
.vpos_pt(vvalue
, vpos
)))
1037 privatedata
.path
.append(path
.moveto_pt(*graph
.vpos_pt(vpos
, vvalue
)))
1039 def vposline(self
, privatedata
, sharedata
, graph
, vpos
, vvalue1
, vvalue2
):
1040 if -self
.epsilon
< vpos
< 1+self
.epsilon
:
1055 if abs(vvalue1cut
+ vvalue2cut
) <= 1:
1056 if vvalue1cut
and not self
.fillable
:
1057 if privatedata
.rangeaxisindex
:
1058 privatedata
.path
.append(path
.moveto_pt(*graph
.vpos_pt(vvalue1
, vpos
)))
1060 privatedata
.path
.append(path
.moveto_pt(*graph
.vpos_pt(vpos
, vvalue1
)))
1061 if privatedata
.rangeaxisindex
:
1062 privatedata
.path
.append(graph
.vgeodesic_el(vvalue1
, vpos
, vvalue2
, vpos
))
1064 privatedata
.path
.append(graph
.vgeodesic_el(vpos
, vvalue1
, vpos
, vvalue2
))
1066 def vvalueline(self
, privatedata
, sharedata
, graph
, vvalue
, vpos1
, vpos2
):
1068 if vvalue
< -self
.epsilon
:
1070 warnings
.warn("cut at graph boundary adds artificial lines to fillable step histogram path")
1071 if vvalue
> 1+self
.epsilon
:
1073 warnings
.warn("cut at graph boundary adds artificial lines to fillable step histogram path")
1074 if self
.fillable
or (-self
.epsilon
< vvalue
< 1+self
.epsilon
):
1089 if abs(vpos1cut
+ vpos2cut
) <= 1:
1092 if privatedata
.rangeaxisindex
:
1093 privatedata
.path
.append(path
.moveto_pt(*graph
.vpos_pt(privatedata
.vfromvalue
, vpos1
)))
1094 privatedata
.path
.append(graph
.vgeodesic_el(privatedata
.vfromvalue
, vpos1
, vvalue
, vpos1
))
1096 privatedata
.path
.append(path
.moveto_pt(*graph
.vpos_pt(vpos1
, privatedata
.vfromvalue
)))
1097 privatedata
.path
.append(graph
.vgeodesic_el(vpos1
, privatedata
.vfromvalue
, vpos1
, vvalue
))
1099 if privatedata
.rangeaxisindex
:
1100 privatedata
.path
.append(path
.moveto_pt(*graph
.vpos_pt(vvalue
, vpos1
)))
1102 privatedata
.path
.append(path
.moveto_pt(*graph
.vpos_pt(vpos1
, vvalue
)))
1103 if privatedata
.rangeaxisindex
:
1104 privatedata
.path
.append(graph
.vgeodesic_el(vvalue
, vpos1
, vvalue
, vpos2
))
1106 privatedata
.path
.append(graph
.vgeodesic_el(vpos1
, vvalue
, vpos2
, vvalue
))
1107 if self
.fillable
and vpos2cut
:
1108 warnings
.warn("cut at graph boundary adds artificial lines to fillable step histogram path")
1109 if privatedata
.rangeaxisindex
:
1110 privatedata
.path
.append(graph
.vgeodesic_el(vvalue
, vpos2
, privatedata
.vfromvalue
, vpos2
))
1112 privatedata
.path
.append(graph
.vgeodesic_el(vpos2
, vvalue
, vpos2
, privatedata
.vfromvalue
))
1114 def drawvalue(self
, privatedata
, sharedata
, graph
, vmin
, vmax
, vvalue
):
1115 currentvalid
= vmin
is not None and vmax
is not None and vvalue
is not None
1116 if self
.fillable
and not self
.steps
:
1117 if not currentvalid
:
1120 if vmin
< -self
.epsilon
:
1123 elif vmin
> 1+self
.epsilon
:
1127 if vmax
< -self
.epsilon
:
1130 if vmax
> 1+self
.epsilon
:
1134 if vvalue
< -self
.epsilon
:
1137 if vvalue
> 1+self
.epsilon
:
1141 if abs(vmincut
) + abs(vmaxcut
) + abs(vvaluecut
) + abs(privatedata
.vfromvaluecut
) > 1:
1142 if abs(vmincut
+ vmaxcut
) > 1 or abs(vvaluecut
+privatedata
.vfromvaluecut
) > 1:
1145 warnings
.warn("multiple cuts at graph boundary add artificial lines to fillable rectangle histogram path")
1148 if privatedata
.rangeaxisindex
:
1149 privatedata
.path
.append(path
.moveto_pt(*graph
.vpos_pt(privatedata
.vfromvalue
, vmin
)))
1150 privatedata
.path
.append(graph
.vgeodesic_el(privatedata
.vfromvalue
, vmin
, privatedata
.vfromvalue
, vmax
))
1151 privatedata
.path
.append(graph
.vgeodesic_el(privatedata
.vfromvalue
, vmax
, vvalue
, vmax
))
1152 privatedata
.path
.append(graph
.vgeodesic_el(vvalue
, vmax
, vvalue
, vmin
))
1154 privatedata
.path
.append(path
.moveto_pt(*graph
.vpos_pt(vmin
, privatedata
.vfromvalue
)))
1155 privatedata
.path
.append(graph
.vgeodesic_el(vmin
, privatedata
.vfromvalue
, vmax
, privatedata
.vfromvalue
))
1156 privatedata
.path
.append(graph
.vgeodesic_el(vmax
, privatedata
.vfromvalue
, vmax
, vvalue
))
1157 privatedata
.path
.append(graph
.vgeodesic_el(vmax
, vvalue
, vmin
, vvalue
))
1160 if privatedata
.rangeaxisindex
:
1161 privatedata
.path
.append(path
.moveto_pt(*graph
.vpos_pt(vvalue
, vmax
)))
1162 privatedata
.path
.append(graph
.vgeodesic_el(vvalue
, vmax
, vvalue
, vmin
))
1163 privatedata
.path
.append(graph
.vgeodesic_el(vvalue
, vmin
, privatedata
.vfromvalue
, vmin
))
1164 privatedata
.path
.append(graph
.vgeodesic_el(privatedata
.vfromvalue
, vmin
, privatedata
.vfromvalue
, vmax
))
1166 privatedata
.path
.append(path
.moveto_pt(*graph
.vpos_pt(vmax
, vvalue
)))
1167 privatedata
.path
.append(graph
.vgeodesic_el(vmax
, vvalue
, vmin
, vvalue
))
1168 privatedata
.path
.append(graph
.vgeodesic_el(vmin
, vvalue
, vmin
, privatedata
.vfromvalue
))
1169 privatedata
.path
.append(graph
.vgeodesic_el(vmin
, privatedata
.vfromvalue
, vmax
, privatedata
.vfromvalue
))
1170 elif privatedata
.vfromvaluecut
:
1172 if privatedata
.rangeaxisindex
:
1173 privatedata
.path
.append(path
.moveto_pt(*graph
.vpos_pt(privatedata
.vfromvalue
, vmax
)))
1174 privatedata
.path
.append(graph
.vgeodesic_el(privatedata
.vfromvalue
, vmax
, vvalue
, vmax
))
1175 privatedata
.path
.append(graph
.vgeodesic_el(vvalue
, vmax
, vvalue
, vmin
))
1176 privatedata
.path
.append(graph
.vgeodesic_el(vvalue
, vmin
, privatedata
.vfromvalue
, vmin
))
1178 privatedata
.path
.append(path
.moveto_pt(*graph
.vpos_pt(vmax
, privatedata
.vfromvalue
)))
1179 privatedata
.path
.append(graph
.vgeodesic_el(vmax
, privatedata
.vfromvalue
, vmax
, vvalue
))
1180 privatedata
.path
.append(graph
.vgeodesic_el(vmax
, vvalue
, vmin
, vvalue
))
1181 privatedata
.path
.append(graph
.vgeodesic_el(vmin
, vvalue
, vmin
, privatedata
.vfromvalue
))
1184 if privatedata
.rangeaxisindex
:
1185 privatedata
.path
.append(path
.moveto_pt(*graph
.vpos_pt(vvalue
, vmin
)))
1186 privatedata
.path
.append(graph
.vgeodesic_el(vvalue
, vmin
, privatedata
.vfromvalue
, vmin
))
1187 privatedata
.path
.append(graph
.vgeodesic_el(privatedata
.vfromvalue
, vmin
, privatedata
.vfromvalue
, vmax
))
1188 privatedata
.path
.append(graph
.vgeodesic_el(privatedata
.vfromvalue
, vmax
, vvalue
, vmax
))
1190 privatedata
.path
.append(path
.moveto_pt(*graph
.vpos_pt(vmin
, vvalue
)))
1191 privatedata
.path
.append(graph
.vgeodesic_el(vmin
, vvalue
, vmin
, privatedata
.vfromvalue
))
1192 privatedata
.path
.append(graph
.vgeodesic_el(vmin
, privatedata
.vfromvalue
, vmax
, privatedata
.vfromvalue
))
1193 privatedata
.path
.append(graph
.vgeodesic_el(vmax
, privatedata
.vfromvalue
, vmax
, vvalue
))
1195 if privatedata
.rangeaxisindex
:
1196 privatedata
.path
.append(path
.moveto_pt(*graph
.vpos_pt(privatedata
.vfromvalue
, vmin
)))
1197 privatedata
.path
.append(graph
.vgeodesic_el(privatedata
.vfromvalue
, vmin
, privatedata
.vfromvalue
, vmax
))
1198 privatedata
.path
.append(graph
.vgeodesic_el(privatedata
.vfromvalue
, vmax
, vvalue
, vmax
))
1199 privatedata
.path
.append(graph
.vgeodesic_el(vvalue
, vmax
, vvalue
, vmin
))
1200 privatedata
.path
.append(graph
.vgeodesic_el(vvalue
, vmin
, privatedata
.vfromvalue
, vmin
))
1201 privatedata
.path
.append(path
.closepath())
1203 privatedata
.path
.append(path
.moveto_pt(*graph
.vpos_pt(vmin
, privatedata
.vfromvalue
)))
1204 privatedata
.path
.append(graph
.vgeodesic_el(vmin
, privatedata
.vfromvalue
, vmax
, privatedata
.vfromvalue
))
1205 privatedata
.path
.append(graph
.vgeodesic_el(vmax
, privatedata
.vfromvalue
, vmax
, vvalue
))
1206 privatedata
.path
.append(graph
.vgeodesic_el(vmax
, vvalue
, vmin
, vvalue
))
1207 privatedata
.path
.append(graph
.vgeodesic_el(vmin
, vvalue
, vmin
, privatedata
.vfromvalue
))
1208 privatedata
.path
.append(path
.closepath())
1211 gap
= abs(vmin
- privatedata
.lastvmax
) > self
.epsilon
1212 except (ArithmeticError, ValueError, TypeError):
1214 if (privatedata
.lastvvalue
is not None and currentvalid
and not gap
and
1215 (self
.steps
or (privatedata
.lastvvalue
-privatedata
.vfromvalue
)*(vvalue
-privatedata
.vfromvalue
) < 0)):
1216 self
.vposline(privatedata
, sharedata
, graph
,
1217 vmin
, privatedata
.lastvvalue
, vvalue
)
1219 if privatedata
.lastvvalue
is not None and currentvalid
:
1220 currentbigger
= abs(privatedata
.lastvvalue
-privatedata
.vfromvalue
) < abs(vvalue
-privatedata
.vfromvalue
)
1221 if privatedata
.lastvvalue
is not None and (not currentvalid
or not currentbigger
or gap
):
1222 self
.vposline(privatedata
, sharedata
, graph
,
1223 privatedata
.lastvmax
, privatedata
.lastvvalue
, privatedata
.vfromvalue
)
1225 self
.vmoveto(privatedata
, sharedata
, graph
,
1227 if currentvalid
and (privatedata
.lastvvalue
is None or currentbigger
or gap
):
1228 self
.vmoveto(privatedata
, sharedata
, graph
,
1229 vmin
, privatedata
.vfromvalue
)
1230 self
.vposline(privatedata
, sharedata
, graph
,
1231 vmin
, privatedata
.vfromvalue
, vvalue
)
1233 self
.vvalueline(privatedata
, sharedata
, graph
,
1235 privatedata
.lastvvalue
= vvalue
1236 privatedata
.lastvmax
= vmax
1238 privatedata
.lastvvalue
= privatedata
.lastvmax
= None
1240 def initdrawpoints(self
, privatedata
, sharedata
, graph
):
1241 privatedata
.path
= path
.path()
1242 privatedata
.lastvvalue
= privatedata
.lastvmax
= None
1243 privatedata
.vcurrentpoint
= None
1244 privatedata
.count
= 0
1245 if self
.fromvalue
is not None:
1246 valueaxisname
= sharedata
.poscolumnnames
[1-privatedata
.rangeaxisindex
]
1247 privatedata
.vfromvalue
= graph
.axes
[valueaxisname
].convert(self
.fromvalue
)
1248 privatedata
.vfromvaluecut
= 0
1249 if privatedata
.vfromvalue
< 0:
1250 privatedata
.vfromvalue
= 0
1251 privatedata
.vfromvaluecut
= -1
1252 if privatedata
.vfromvalue
> 1:
1253 privatedata
.vfromvalue
= 1
1254 privatedata
.vfromvaluecut
= 1
1255 if self
.frompathattrs
is not None and privatedata
.insertfrompath
:
1256 graph
.stroke(graph
.axes
[valueaxisname
].vgridpath(privatedata
.vfromvalue
),
1257 self
.defaultfrompathattrs
+ self
.frompathattrs
)
1259 privatedata
.vfromvalue
= 0
1261 def drawpoint(self
, privatedata
, sharedata
, graph
, point
):
1262 if privatedata
.autohistogram
:
1263 # automatic range handling
1264 privatedata
.count
+= 1
1265 if privatedata
.count
== 2:
1266 if privatedata
.rangeaxisindex
:
1267 privatedata
.vrange
= sharedata
.vpos
[1] - privatedata
.lastvpos
[1]
1268 self
.drawvalue(privatedata
, sharedata
, graph
,
1269 privatedata
.lastvpos
[1] - self
.autohistogrampointpos
*privatedata
.vrange
,
1270 privatedata
.lastvpos
[1] + (1-self
.autohistogrampointpos
)*privatedata
.vrange
,
1271 privatedata
.lastvpos
[0])
1273 privatedata
.vrange
= sharedata
.vpos
[0] - privatedata
.lastvpos
[0]
1274 self
.drawvalue(privatedata
, sharedata
, graph
,
1275 privatedata
.lastvpos
[0] - self
.autohistogrampointpos
*privatedata
.vrange
,
1276 privatedata
.lastvpos
[0] + (1-self
.autohistogrampointpos
)*privatedata
.vrange
,
1277 privatedata
.lastvpos
[1])
1278 elif privatedata
.count
> 2:
1279 if privatedata
.rangeaxisindex
:
1280 vrange
= sharedata
.vpos
[1] - privatedata
.lastvpos
[1]
1282 vrange
= sharedata
.vpos
[0] - privatedata
.lastvpos
[0]
1283 if abs(privatedata
.vrange
- vrange
) > self
.epsilon
:
1284 raise ValueError("equal steps (in graph coordinates) needed for automatic width calculation")
1285 if privatedata
.count
> 1:
1286 if privatedata
.rangeaxisindex
:
1287 self
.drawvalue(privatedata
, sharedata
, graph
,
1288 sharedata
.vpos
[1] - self
.autohistogrampointpos
*privatedata
.vrange
,
1289 sharedata
.vpos
[1] + (1-self
.autohistogrampointpos
)*privatedata
.vrange
,
1292 self
.drawvalue(privatedata
, sharedata
, graph
,
1293 sharedata
.vpos
[0] - self
.autohistogrampointpos
*privatedata
.vrange
,
1294 sharedata
.vpos
[0] + (1-self
.autohistogrampointpos
)*privatedata
.vrange
,
1296 privatedata
.lastvpos
= sharedata
.vpos
[:]
1298 if privatedata
.rangeaxisindex
:
1299 self
.drawvalue(privatedata
, sharedata
, graph
,
1300 sharedata
.vrange
[1][0], sharedata
.vrange
[1][1], sharedata
.vpos
[0])
1302 self
.drawvalue(privatedata
, sharedata
, graph
,
1303 sharedata
.vrange
[0][0], sharedata
.vrange
[0][1], sharedata
.vpos
[1])
1305 def donedrawpoints(self
, privatedata
, sharedata
, graph
):
1306 self
.drawvalue(privatedata
, sharedata
, graph
, None, None, None)
1307 if privatedata
.lineattrs
is not None and len(privatedata
.path
):
1308 graph
.draw(privatedata
.path
, privatedata
.lineattrs
)
1310 def key_pt(self
, privatedata
, sharedata
, graph
, x_pt
, y_pt
, width_pt
, height_pt
):
1311 if privatedata
.lineattrs
is not None:
1313 p
= path
.rect_pt(x_pt
, y_pt
, width_pt
, height_pt
)
1315 p
= path
.line_pt(x_pt
, y_pt
+0.5*height_pt
, x_pt
+width_pt
, y_pt
+0.5*height_pt
)
1316 graph
.draw(p
, privatedata
.lineattrs
)
1319 class barpos(_style
):
1321 providesdata
= ["vpos", "vposmissing", "vposavailable", "vposvalid", "vbarrange", "barposcolumnnames", "barvalueindex", "lastbarvalue", "stackedbar", "stackedbardraw"]
1323 defaultfrompathattrs
= []
1325 def __init__(self
, fromvalue
=None, frompathattrs
=[], epsilon
=1e-10):
1326 self
.fromvalue
= fromvalue
1327 self
.frompathattrs
= frompathattrs
1328 self
.epsilon
= epsilon
1330 def columnnames(self
, privatedata
, sharedata
, graph
, columnnames
):
1331 sharedata
.barposcolumnnames
= []
1332 sharedata
.barvalueindex
= None
1333 for dimension
, axisnames
in enumerate(graph
.axesnames
):
1335 for axisname
in axisnames
:
1336 if axisname
in columnnames
:
1337 if sharedata
.barvalueindex
is not None:
1338 raise ValueError("multiple values")
1339 sharedata
.barvalueindex
= dimension
1340 sharedata
.barposcolumnnames
.append(axisname
)
1342 if (axisname
+ "name") in columnnames
:
1343 sharedata
.barposcolumnnames
.append(axisname
+ "name")
1346 raise ValueError("multiple names and value")
1348 raise ValueError("value/name missing")
1349 if sharedata
.barvalueindex
is None:
1350 raise ValueError("missing value")
1351 sharedata
.vposmissing
= []
1352 return sharedata
.barposcolumnnames
1354 def addsubvalue(self
, value
, subvalue
):
1359 return value
[0], self
.addsubvalue(value
[1], subvalue
)
1361 return value
, subvalue
1363 return value
, subvalue
1365 def adjustaxis(self
, privatedata
, sharedata
, graph
, columnname
, data
):
1367 i
= sharedata
.barposcolumnnames
.index(columnname
)
1371 if i
== sharedata
.barvalueindex
:
1372 if self
.fromvalue
is not None:
1373 graph
.axes
[sharedata
.barposcolumnnames
[i
]].adjustaxis([self
.fromvalue
])
1374 graph
.axes
[sharedata
.barposcolumnnames
[i
]].adjustaxis(data
)
1376 graph
.axes
[sharedata
.barposcolumnnames
[i
][:-4]].adjustaxis([self
.addsubvalue(x
, 0) for x
in data
])
1377 graph
.axes
[sharedata
.barposcolumnnames
[i
][:-4]].adjustaxis([self
.addsubvalue(x
, 1) for x
in data
])
1379 def selectstyle(self
, privatedata
, sharedata
, graph
, selectindex
, selecttotal
):
1380 privatedata
.insertfrompath
= selectindex
== 0
1382 def initdrawpoints(self
, privatedata
, sharedata
, graph
):
1383 sharedata
.vpos
= [None]*(len(sharedata
.barposcolumnnames
))
1384 sharedata
.vbarrange
= [[None for i
in xrange(2)] for x
in sharedata
.barposcolumnnames
]
1385 sharedata
.stackedbar
= sharedata
.stackedbardraw
= 0
1387 if self
.fromvalue
is not None:
1388 privatedata
.vfromvalue
= graph
.axes
[sharedata
.barposcolumnnames
[sharedata
.barvalueindex
][0]].convert(self
.fromvalue
)
1389 if privatedata
.vfromvalue
< 0:
1390 privatedata
.vfromvalue
= 0
1391 if privatedata
.vfromvalue
> 1:
1392 privatedata
.vfromvalue
= 1
1393 if self
.frompathattrs
is not None and privatedata
.insertfrompath
:
1394 graph
.stroke(graph
.axes
[sharedata
.barposcolumnnames
[sharedata
.barvalueindex
][0]].vgridpath(privatedata
.vfromvalue
),
1395 self
.defaultfrompathattrs
+ self
.frompathattrs
)
1397 privatedata
.vfromvalue
= 0
1399 def drawpoint(self
, privatedata
, sharedata
, graph
, point
):
1400 sharedata
.vposavailable
= sharedata
.vposvalid
= 1
1401 for i
, barname
in enumerate(sharedata
.barposcolumnnames
):
1402 if i
== sharedata
.barvalueindex
:
1403 sharedata
.vbarrange
[i
][0] = privatedata
.vfromvalue
1404 sharedata
.lastbarvalue
= point
[barname
]
1406 sharedata
.vpos
[i
] = sharedata
.vbarrange
[i
][1] = graph
.axes
[barname
].convert(sharedata
.lastbarvalue
)
1407 except (ArithmeticError, ValueError, TypeError):
1408 sharedata
.vpos
[i
] = sharedata
.vbarrange
[i
][1] = None
1412 sharedata
.vbarrange
[i
][j
] = graph
.axes
[barname
[:-4]].convert(self
.addsubvalue(point
[barname
], j
))
1413 except (ArithmeticError, ValueError, TypeError):
1414 sharedata
.vbarrange
[i
][j
] = None
1416 sharedata
.vpos
[i
] = 0.5*(sharedata
.vbarrange
[i
][0]+sharedata
.vbarrange
[i
][1])
1417 except (ArithmeticError, ValueError, TypeError):
1418 sharedata
.vpos
[i
] = None
1419 if sharedata
.vpos
[i
] is None:
1420 sharedata
.vposavailable
= sharedata
.vposvalid
= 0
1421 elif sharedata
.vpos
[i
] < -self
.epsilon
or sharedata
.vpos
[i
] > 1+self
.epsilon
:
1422 sharedata
.vposvalid
= 0
1424 registerdefaultprovider(barpos(), ["vbarrange", "barposcolumnnames", "barvalueindex", "lastbarvalue", "stackedbar", "stackedbardraw"])
1427 class stackedbarpos(_style
):
1429 # provides no additional data, but needs some data (and modifies some of them)
1430 needsdata
= ["vbarrange", "barposcolumnnames", "barvalueindex", "lastbarvalue", "stackedbar", "stackedbardraw"]
1432 def __init__(self
, stackname
, addontop
=0, epsilon
=1e-10):
1433 self
.stackname
= stackname
1434 self
.epsilon
= epsilon
1435 self
.addontop
= addontop
1437 def columnnames(self
, privatedata
, sharedata
, graph
, columnnames
):
1438 if self
.stackname
not in columnnames
:
1439 raise ValueError("column '%s' missing" % self
.stackname
)
1440 return [self
.stackname
]
1442 def adjustaxis(self
, privatedata
, sharedata
, graph
, columnname
, data
):
1443 if columnname
== self
.stackname
:
1444 graph
.axes
[sharedata
.barposcolumnnames
[sharedata
.barvalueindex
]].adjustaxis(data
)
1446 def initdrawpoints(self
, privatedata
, sharedata
, graph
):
1447 if sharedata
.stackedbardraw
: # do not count the start bar when not gets painted
1448 sharedata
.stackedbar
+= 1
1450 def drawpoint(self
, privatedata
, sharedata
, graph
, point
):
1451 sharedata
.vbarrange
[sharedata
.barvalueindex
][0] = sharedata
.vbarrange
[sharedata
.barvalueindex
][1]
1454 sharedata
.lastbarvalue
+= point
[self
.stackname
]
1455 except (ArithmeticError, ValueError, TypeError):
1456 sharedata
.lastbarvalue
= None
1458 sharedata
.lastbarvalue
= point
[self
.stackname
]
1460 sharedata
.vpos
[sharedata
.barvalueindex
] = sharedata
.vbarrange
[sharedata
.barvalueindex
][1] = graph
.axes
[sharedata
.barposcolumnnames
[sharedata
.barvalueindex
]].convert(sharedata
.lastbarvalue
)
1461 except (ArithmeticError, ValueError, TypeError):
1462 sharedata
.vpos
[sharedata
.barvalueindex
] = sharedata
.vbarrange
[sharedata
.barvalueindex
][1] = None
1463 sharedata
.vposavailable
= sharedata
.vposvalid
= 0
1465 if not sharedata
.vposavailable
or not sharedata
.vposvalid
:
1466 sharedata
.vposavailable
= sharedata
.vposvalid
= 1
1467 for v
in sharedata
.vpos
:
1469 sharedata
.vposavailable
= sharedata
.vposvalid
= 0
1471 if v
< -self
.epsilon
or v
> 1+self
.epsilon
:
1472 sharedata
.vposvalid
= 0
1477 needsdata
= ["vbarrange"]
1479 defaultbarattrs
= [color
.gradient
.Rainbow
, deco
.stroked([color
.grey
.black
])]
1481 def __init__(self
, barattrs
=[]):
1482 self
.barattrs
= barattrs
1484 def columnnames(self
, privatedata
, sharedata
, graph
, columnnames
):
1485 if len(graph
.axesnames
) != 2:
1486 raise TypeError("bar style restricted on two-dimensional graphs")
1489 def selectstyle(self
, privatedata
, sharedata
, graph
, selectindex
, selecttotal
):
1490 privatedata
.barattrs
= attr
.selectattrs(self
.defaultbarattrs
+ self
.barattrs
, selectindex
, selecttotal
)
1492 def initdrawpoints(self
, privatedata
, sharedata
, graph
):
1493 privatedata
.rectcanvas
= graph
.insert(canvas
.canvas())
1494 sharedata
.stackedbardraw
= 1
1495 privatedata
.stackedbar
= sharedata
.stackedbar
1497 def drawpointfill(self
, privatedata
, p
):
1499 privatedata
.rectcanvas
.fill(p
, privatedata
.barattrs
)
1501 def drawpoint(self
, privatedata
, sharedata
, graph
, point
):
1502 xvmin
= sharedata
.vbarrange
[0][0]
1503 xvmax
= sharedata
.vbarrange
[0][1]
1504 yvmin
= sharedata
.vbarrange
[1][0]
1505 yvmax
= sharedata
.vbarrange
[1][1]
1508 xvmin
, xvmax
= xvmax
, xvmin
1513 yvmin
, yvmax
= yvmax
, yvmin
1516 if (xvmin
is not None and xvmin
< 1 and
1517 xvmax
is not None and xvmax
> 0 and
1518 yvmin
is not None and yvmin
< 1 and
1519 yvmax
is not None and yvmax
> 0):
1528 p
= graph
.vgeodesic(xvmin
, yvmin
, xvmax
, yvmin
)
1529 p
.append(graph
.vgeodesic_el(xvmax
, yvmin
, xvmax
, yvmax
))
1530 p
.append(graph
.vgeodesic_el(xvmax
, yvmax
, xvmin
, yvmax
))
1531 p
.append(graph
.vgeodesic_el(xvmin
, yvmax
, xvmin
, yvmin
))
1532 p
.append(path
.closepath())
1533 self
.drawpointfill(privatedata
, p
)
1535 self
.drawpointfill(privatedata
, None)
1537 def key_pt(self
, privatedata
, sharedata
, graph
, x_pt
, y_pt
, width_pt
, height_pt
):
1538 selectindex
= privatedata
.stackedbar
1539 selecttotal
= sharedata
.stackedbar
+ 1
1540 graph
.fill(path
.rect_pt(x_pt
+ width_pt
*selectindex
/float(selecttotal
), y_pt
, width_pt
/float(selecttotal
), height_pt
), privatedata
.barattrs
)
1543 class changebar(bar
):
1545 def selectstyle(self
, privatedata
, sharedata
, graph
, selectindex
, selecttotal
):
1546 if selecttotal
!= 1:
1547 raise RuntimeError("Changebar can't change its appearance. Thus you can't use it to plot several bars side by side on a subaxis.")
1549 def initdrawpoints(self
, privatedata
, sharedata
, graph
):
1550 bar
.initdrawpoints(self
, privatedata
, sharedata
, graph
)
1551 privatedata
.bars
= []
1553 def drawpointfill(self
, privatedata
, p
):
1554 privatedata
.bars
.append(p
)
1556 def donedrawpoints(self
, privatedata
, sharedata
, graph
):
1557 selecttotal
= len(privatedata
.bars
)
1558 for selectindex
, p
in enumerate(privatedata
.bars
):
1560 barattrs
= attr
.selectattrs(self
.defaultbarattrs
+ self
.barattrs
, selectindex
, selecttotal
)
1561 privatedata
.rectcanvas
.fill(p
, barattrs
)
1563 def key_pt(self
, privatedata
, sharedata
, graph
, x_pt
, y_pt
, width_pt
, height_pt
):
1564 raise RuntimeError("Style currently doesn't provide a graph key")
1569 needsdata
= ["vpos", "vposmissing", "vposavailable", "vposvalid"]
1571 def __init__(self
, index1
=0, index2
=1, strokelines1
=1, strokelines2
=1, gridattrs
=[],
1573 self
.index1
= index1
1574 self
.index2
= index2
1575 self
.epsilon
= epsilon
1577 def initdrawpoints(self
, privatedata
, sharedata
, graph
):
1578 privatedata
.values1
= {}
1579 privatedata
.values2
= {}
1580 privatedata
.data12
= {}
1581 privatedata
.data21
= {}
1583 def drawpoint(self
, privatedata
, sharedata
, graph
, point
):
1584 if sharedata
.vposavailable
:
1585 privatedata
.value1
= sharedata
.vpos
[self
.index1
]
1586 privatedata
.value2
= sharedata
.vpos
[self
.index2
]
1587 if not privatedata
.values1
.has_key(privatedata
.value1
):
1588 for hasvalue
in privatedata
.values1
.keys():
1589 if hasvalue
- self
.epsilon
<= privatedata
.value1
<= hasvalue
+ self
.epsilon
:
1590 privatedata
.value1
= hasvalue
1593 privatedata
.values1
[privatedata
.value1
] = 1
1594 if not privatedata
.values2
.has_key(privatedata
.value2
):
1595 for hasvalue
in privatedata
.values2
.keys():
1596 if hasvalue
- self
.epsilon
<= privatedata
.value2
<= hasvalue
+ self
.epsilon
:
1597 privatedata
.value2
= hasvalue
1600 privatedata
.values2
[privatedata
.value2
] = 1
1601 data
= sharedata
.vposavailable
, sharedata
.vposvalid
, sharedata
.vpos
[:]
1602 privatedata
.data12
.setdefault(privatedata
.value1
, {})[privatedata
.value2
] = data
1603 privatedata
.data21
.setdefault(privatedata
.value2
, {})[privatedata
.value1
] = data
1605 def key_pt(self
, privatedata
, sharedata
, graph
, x_pt
, y_pt
, width_pt
, height_pt
):
1606 raise NotImplementedError
1609 class grid(_surface
, _line
):
1611 defaultgridattrs
= []
1613 def __init__(self
, strokelines1
=1, strokelines2
=1, gridattrs
=[], **kwargs
):
1614 _surface
.__init
__(self
, **kwargs
)
1615 self
.strokelines1
= strokelines1
1616 self
.strokelines2
= strokelines2
1617 self
.gridattrs
= gridattrs
1619 def selectstyle(self
, privatedata
, sharedata
, graph
, selectindex
, selecttotal
):
1620 if self
.gridattrs
is not None:
1621 privatedata
.gridattrs
= attr
.selectattrs(self
.defaultgridattrs
+ self
.gridattrs
, selectindex
, selecttotal
)
1623 privatedata
.gridattrs
= None
1625 def donedrawpoints(self
, privatedata
, sharedata
, graph
):
1626 values1
= privatedata
.values1
.keys()
1628 values2
= privatedata
.values2
.keys()
1630 if self
.strokelines1
:
1631 for value2
in values2
:
1632 data1
= privatedata
.data21
[value2
]
1633 self
.initpointstopath(privatedata
)
1634 for value1
in values1
:
1636 data
= data1
[value1
]
1638 self
.addinvalid(privatedata
)
1640 self
.addpoint(privatedata
, graph
.vpos_pt
, *data
)
1641 p
= self
.donepointstopath(privatedata
)
1643 graph
.stroke(p
, privatedata
.gridattrs
)
1644 if self
.strokelines2
:
1645 for value1
in values1
:
1646 data2
= privatedata
.data12
[value1
]
1647 self
.initpointstopath(privatedata
)
1648 for value2
in values2
:
1650 data
= data2
[value2
]
1652 self
.addinvalid(privatedata
)
1654 self
.addpoint(privatedata
, graph
.vpos_pt
, *data
)
1655 p
= self
.donepointstopath(privatedata
)
1657 graph
.stroke(p
, privatedata
.gridattrs
)
1660 class surface(_surface
, _style
):
1662 needsdata
= ["vpos", "vposmissing", "vposavailable", "vposvalid"]
1664 def __init__(self
, colorname
="color", gradient
=color
.gradient
.Rainbow
,
1665 mincolor
=None, maxcolor
=None, **kwargs
):
1666 _surface
.__init
__(self
, **kwargs
)
1667 self
.colorname
= colorname
1668 self
.gradient
= gradient
1669 self
.mincolor
= mincolor
1670 self
.maxcolor
= maxcolor
1672 def columnnames(self
, privatedata
, sharedata
, graph
, columnnames
):
1673 privatedata
.colorize
= self
.colorname
in columnnames
1674 return privatedata
.colorize
and [self
.colorname
] or [] + _surface
.columnnames(self
, privatedata
, sharedata
, graph
, columnnames
)
1676 def initdrawpoints(self
, privatedata
, sharedata
, graph
):
1677 _surface
.initdrawpoints(self
, privatedata
, sharedata
, graph
)
1678 privatedata
.colors
= {}
1679 privatedata
.mincolor
= privatedata
.maxcolor
= None
1681 def drawpoint(self
, privatedata
, sharedata
, graph
, point
):
1682 _surface
.drawpoint(self
, privatedata
, sharedata
, graph
, point
)
1683 if sharedata
.vposavailable
:
1685 color
= point
[self
.colorname
] + 0
1689 privatedata
.colors
.setdefault(privatedata
.value1
, {})[privatedata
.value2
] = color
1690 if privatedata
.mincolor
is None or color
< privatedata
.mincolor
:
1691 privatedata
.mincolor
= color
1692 if privatedata
.mincolor
is None or privatedata
.maxcolor
< color
:
1693 privatedata
.maxcolor
= color
1695 def donedrawpoints(self
, privatedata
, sharedata
, graph
):
1696 v1
= [0]*len(graph
.axesnames
)
1697 v2
= [0]*len(graph
.axesnames
)
1698 v3
= [0]*len(graph
.axesnames
)
1699 v4
= [0]*len(graph
.axesnames
)
1700 v1
[self
.index2
] = 0.5
1701 v2
[self
.index1
] = 0.5
1702 v3
[self
.index1
] = 0.5
1705 v4
[self
.index2
] = 0.5
1706 sortElements
= [-graph
.vzindex(*v1
),
1707 -graph
.vzindex(*v2
),
1708 -graph
.vzindex(*v3
),
1709 -graph
.vzindex(*v4
)]
1711 values1
= privatedata
.values1
.keys()
1713 v1
= [0]*len(graph
.axesnames
)
1714 v2
= [0]*len(graph
.axesnames
)
1715 v1
[self
.index1
] = -1
1718 if graph
.vzindex(*v1
) < graph
.vzindex(*v2
):
1721 sortElements
= [sortElements
[3], sortElements
[1], sortElements
[2], sortElements
[0]]
1723 values2
= privatedata
.values2
.keys()
1725 v1
= [0]*len(graph
.axesnames
)
1726 v2
= [0]*len(graph
.axesnames
)
1727 v1
[self
.index2
] = -1
1729 if graph
.vzindex(*v1
) < graph
.vzindex(*v2
):
1732 sortElements
= [sortElements
[0], sortElements
[2], sortElements
[1], sortElements
[3]]
1734 sortElements
= [(zindex
, i
) for i
, zindex
in enumerate(sortElements
)]
1736 sortElements
= dict([(i
, pos
) for i
, (zindex
, pos
) in enumerate(sortElements
)])
1738 if self
.mincolor
is not None:
1739 mincolor
= self
.mincolor
1740 if self
.maxcolor
is not None:
1741 maxcolor
= self
.maxcolor
1744 for value1a
, value1b
in zip(values1
[:-1], values1
[1:]):
1745 for value2a
, value2b
in zip(values2
[:-1], values2
[1:]):
1747 available1
, valid1
, v1
= privatedata
.data12
[value1a
][value2a
]
1748 available2
, valid2
, v2
= privatedata
.data12
[value1a
][value2b
]
1749 available3
, valid3
, v3
= privatedata
.data12
[value1b
][value2a
]
1750 available4
, valid4
, v4
= privatedata
.data12
[value1b
][value2b
]
1753 if not available1
or not available2
or not available3
or not available4
:
1755 if not valid1
or not valid2
or not valid3
or not valid4
:
1756 warnings
.warn("surface elements partially outside of the graph are (currently) skipped completely")
1757 v5
= [0.25*sum(values
) for values
in zip(v1
, v2
, v3
, v4
)]
1758 x1_pt
, y1_pt
= graph
.vpos_pt(*v1
)
1759 x2_pt
, y2_pt
= graph
.vpos_pt(*v2
)
1760 x3_pt
, y3_pt
= graph
.vpos_pt(*v3
)
1761 x4_pt
, y4_pt
= graph
.vpos_pt(*v4
)
1762 x5_pt
, y5_pt
= graph
.vpos_pt(*v5
)
1763 c1
= privatedata
.colors
[value1a
][value2a
]
1764 c2
= privatedata
.colors
[value1a
][value2b
]
1765 c3
= privatedata
.colors
[value1b
][value2a
]
1766 c4
= privatedata
.colors
[value1b
][value2b
]
1767 c5
= 0.25*(c1
+c2
+c3
+c4
)
1769 return self
.gradient
.getcolor((c
- privatedata
.mincolor
) / float(privatedata
.maxcolor
- privatedata
.mincolor
)).rgb()
1770 color1
= getcolor(c1
)
1771 color2
= getcolor(c2
)
1772 color3
= getcolor(c3
)
1773 color4
= getcolor(c4
)
1774 color5
= getcolor(c5
)
1775 if sign
*graph
.vangle(*(v1
+v2
+v5
)) >= 0:
1776 e1
= mesh
.element((mesh
.node_pt((x1_pt
, y1_pt
), color1
),
1777 mesh
.node_pt((x2_pt
, y2_pt
), color2
),
1778 mesh
.node_pt((x5_pt
, y5_pt
), color5
)))
1780 e1
= mesh
.element((mesh
.node_pt((x1_pt
, y1_pt
), color
.rgb
.black
),
1781 mesh
.node_pt((x2_pt
, y2_pt
), color
.rgb
.black
),
1782 mesh
.node_pt((x5_pt
, y5_pt
), color
.rgb
.black
)))
1783 if sign
*graph
.vangle(*(v3
+v1
+v5
)) >= 0:
1784 e2
= mesh
.element((mesh
.node_pt((x1_pt
, y1_pt
), color1
),
1785 mesh
.node_pt((x3_pt
, y3_pt
), color3
),
1786 mesh
.node_pt((x5_pt
, y5_pt
), color5
)))
1788 e2
= mesh
.element((mesh
.node_pt((x1_pt
, y1_pt
), color
.rgb
.black
),
1789 mesh
.node_pt((x3_pt
, y3_pt
), color
.rgb
.black
),
1790 mesh
.node_pt((x5_pt
, y5_pt
), color
.rgb
.black
)))
1791 if sign
*graph
.vangle(*(v2
+v4
+v5
)) >= 0:
1792 e3
= mesh
.element((mesh
.node_pt((x2_pt
, y2_pt
), color2
),
1793 mesh
.node_pt((x4_pt
, y4_pt
), color4
),
1794 mesh
.node_pt((x5_pt
, y5_pt
), color5
)))
1796 e3
= mesh
.element((mesh
.node_pt((x2_pt
, y2_pt
), color
.rgb
.black
),
1797 mesh
.node_pt((x4_pt
, y4_pt
), color
.rgb
.black
),
1798 mesh
.node_pt((x5_pt
, y5_pt
), color
.rgb
.black
)))
1799 if sign
*graph
.vangle(*(v4
+v3
+v5
)) >= 0:
1800 e4
= mesh
.element((mesh
.node_pt((x3_pt
, y3_pt
), color3
),
1801 mesh
.node_pt((x4_pt
, y4_pt
), color4
),
1802 mesh
.node_pt((x5_pt
, y5_pt
), color5
)))
1804 e4
= mesh
.element((mesh
.node_pt((x3_pt
, y3_pt
), color
.rgb
.black
),
1805 mesh
.node_pt((x4_pt
, y4_pt
), color
.rgb
.black
),
1806 mesh
.node_pt((x5_pt
, y5_pt
), color
.rgb
.black
)))
1807 elements
.extend([[e1
, e2
, e3
, e4
][sortElements
[i
]] for i
in builtinrange(4)])
1808 m
= mesh
.mesh(elements
)