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'"""
125 assert key
in style
.providesdata
, "key not provided by style"
126 # we might allow for overwriting the defaults, i.e. the following is not checked:
127 # assert key in _defaultprovider.keys(), "default provider already registered for key"
128 _defaultprovider
[key
] = style
130 def getdefaultprovider(key
):
131 """returns a style, which acts as a default creator for the
132 sharedata variable 'key'"""
133 return _defaultprovider
[key
]
138 providesdata
= ["vpos", "vposmissing", "vposavailable", "vposvalid", "poscolumnnames"]
140 def __init__(self
, epsilon
=1e-10):
141 self
.epsilon
= epsilon
143 def columnnames(self
, privatedata
, sharedata
, graph
, columnnames
):
144 sharedata
.poscolumnnames
= []
145 sharedata
.vposmissing
= []
146 for count
, axisnames
in enumerate(graph
.axesnames
):
147 for axisname
in axisnames
:
148 for columnname
in columnnames
:
149 if axisname
== columnname
:
150 sharedata
.poscolumnnames
.append(columnname
)
151 if len(sharedata
.poscolumnnames
) > count
+1:
152 raise ValueError("multiple axes per graph dimension")
153 elif len(sharedata
.poscolumnnames
) < count
+1:
154 sharedata
.vposmissing
.append(count
)
155 sharedata
.poscolumnnames
.append(None)
156 return [columnname
for columnname
in sharedata
.poscolumnnames
if columnname
is not None]
158 def adjustaxis(self
, privatedata
, sharedata
, graph
, columnname
, data
):
159 if columnname
in sharedata
.poscolumnnames
:
160 graph
.axes
[columnname
].adjustaxis(data
)
162 def initdrawpoints(self
, privatedata
, sharedata
, graph
):
163 sharedata
.vpos
= [None]*(len(graph
.axesnames
))
164 privatedata
.pointpostmplist
= [[columnname
, index
, graph
.axes
[columnname
]] # temporarily used by drawpoint only
165 for index
, columnname
in enumerate([columnname
for columnname
in sharedata
.poscolumnnames
if columnname
is not None])]
166 for missing
in sharedata
.vposmissing
:
167 for pointpostmp
in privatedata
.pointpostmplist
:
168 if pointpostmp
[1] >= missing
:
171 def drawpoint(self
, privatedata
, sharedata
, graph
, point
):
172 sharedata
.vposavailable
= 1 # valid position (but might be outside of the graph)
173 sharedata
.vposvalid
= 1 # valid position inside the graph
174 for columnname
, index
, axis
in privatedata
.pointpostmplist
:
176 v
= axis
.convert(point
[columnname
])
177 except (ArithmeticError, ValueError, TypeError):
178 sharedata
.vposavailable
= sharedata
.vposvalid
= 0
179 sharedata
.vpos
[index
] = None
181 if v
< -self
.epsilon
or v
> 1+self
.epsilon
:
182 sharedata
.vposvalid
= 0
183 sharedata
.vpos
[index
] = v
186 registerdefaultprovider(pos(), pos
.providesdata
)
191 providesdata
= ["vrange", "vrangemissing", "vrangeminmissing", "vrangemaxmissing"]
201 def __init__(self
, usenames
={}, epsilon
=1e-10):
202 self
.usenames
= usenames
203 self
.epsilon
= epsilon
205 def _numberofbits(self
, mask
):
209 return self
._numberofbits
(mask
>> 1) + 1
211 return self
._numberofbits
(mask
>> 1)
213 def columnnames(self
, privatedata
, sharedata
, graph
, columnnames
):
215 privatedata
.rangeposcolumns
= []
216 sharedata
.vrangemissing
= []
217 sharedata
.vrangeminmissing
= []
218 sharedata
.vrangemaxmissing
= []
219 privatedata
.rangeposdeltacolumns
= {} # temporarily used by adjustaxis only
220 for count
, axisnames
in enumerate(graph
.axesnames
):
221 for axisname
in axisnames
:
223 usename
= self
.usenames
[axisname
]
227 for columnname
in columnnames
:
229 if usename
== columnname
:
230 mask
+= self
.mask_value
231 elif usename
+ "min" == columnname
:
232 mask
+= self
.mask_min
233 elif usename
+ "max" == columnname
:
234 mask
+= self
.mask_max
235 elif "d" + usename
+ "min" == columnname
:
236 mask
+= self
.mask_dmin
237 elif "d" + usename
+ "max" == columnname
:
238 mask
+= self
.mask_dmax
239 elif "d" + usename
== columnname
:
244 usecolumns
.append(columnname
)
245 if mask
& (self
.mask_min | self
.mask_max | self
.mask_dmin | self
.mask_dmax | self
.mask_d
):
246 if (self
._numberofbits
(mask
& (self
.mask_min | self
.mask_dmin | self
.mask_d
)) > 1 or
247 self
._numberofbits
(mask
& (self
.mask_max | self
.mask_dmax | self
.mask_d
)) > 1):
248 raise ValueError("multiple range definition")
249 if mask
& (self
.mask_dmin | self
.mask_dmax | self
.mask_d
):
250 if not (mask
& self
.mask_value
):
251 raise ValueError("missing value for delta")
252 privatedata
.rangeposdeltacolumns
[axisname
] = {}
253 privatedata
.rangeposcolumns
.append((axisname
, usename
, mask
))
254 elif mask
== self
.mask_value
:
255 usecolumns
= usecolumns
[:-1]
256 if len(privatedata
.rangeposcolumns
) + len(sharedata
.vrangemissing
) > count
+1:
257 raise ValueError("multiple axes per graph dimension")
258 elif len(privatedata
.rangeposcolumns
) + len(sharedata
.vrangemissing
) < count
+1:
259 sharedata
.vrangemissing
.append(count
)
260 sharedata
.vrangeminmissing
.append(count
)
261 sharedata
.vrangemaxmissing
.append(count
)
263 if not (privatedata
.rangeposcolumns
[-1][2] & (self
.mask_min | self
.mask_dmin | self
.mask_d
)):
264 sharedata
.vrangeminmissing
.append(count
)
265 if not (privatedata
.rangeposcolumns
[-1][2] & (self
.mask_max | self
.mask_dmax | self
.mask_d
)):
266 sharedata
.vrangemaxmissing
.append(count
)
269 def adjustaxis(self
, privatedata
, sharedata
, graph
, columnname
, data
):
270 if columnname
in [c
+ "min" for a
, c
, m
in privatedata
.rangeposcolumns
if m
& self
.mask_min
]:
271 graph
.axes
[columnname
[:-3]].adjustaxis(data
)
272 if columnname
in [c
+ "max" for a
, c
, m
in privatedata
.rangeposcolumns
if m
& self
.mask_max
]:
273 graph
.axes
[columnname
[:-3]].adjustaxis(data
)
275 # delta handling: fill rangeposdeltacolumns
276 for axisname
, usename
, mask
in privatedata
.rangeposcolumns
:
277 if columnname
== usename
and mask
& (self
.mask_dmin | self
.mask_dmax | self
.mask_d
):
278 privatedata
.rangeposdeltacolumns
[axisname
][self
.mask_value
] = data
279 if columnname
== "d" + usename
+ "min" and mask
& self
.mask_dmin
:
280 privatedata
.rangeposdeltacolumns
[axisname
][self
.mask_dmin
] = data
281 if columnname
== "d" + usename
+ "max" and mask
& self
.mask_dmax
:
282 privatedata
.rangeposdeltacolumns
[axisname
][self
.mask_dmax
] = data
283 if columnname
== "d" + usename
and mask
& self
.mask_d
:
284 privatedata
.rangeposdeltacolumns
[axisname
][self
.mask_d
] = data
286 # delta handling: process rangeposdeltacolumns
287 for a
, d
in privatedata
.rangeposdeltacolumns
.items():
288 if d
.has_key(self
.mask_value
):
290 if k
!= self
.mask_value
:
291 if k
& (self
.mask_dmin | self
.mask_d
):
293 for value
, delta
in zip(d
[self
.mask_value
], d
[k
]):
295 mindata
.append(value
-delta
)
298 graph
.axes
[a
].adjustaxis(mindata
)
299 if k
& (self
.mask_dmax | self
.mask_d
):
301 for value
, delta
in zip(d
[self
.mask_value
], d
[k
]):
303 maxdata
.append(value
+delta
)
306 graph
.axes
[a
].adjustaxis(maxdata
)
309 def initdrawpoints(self
, privatedata
, sharedata
, graph
):
310 sharedata
.vrange
= [[None for x
in xrange(2)] for y
in privatedata
.rangeposcolumns
+ sharedata
.vrangemissing
]
311 privatedata
.rangepostmplist
= [[usename
, mask
, index
, graph
.axes
[axisname
]] # temporarily used by drawpoint only
312 for index
, (axisname
, usename
, mask
) in enumerate(privatedata
.rangeposcolumns
)]
313 for missing
in sharedata
.vrangemissing
:
314 for rangepostmp
in privatedata
.rangepostmplist
:
315 if rangepostmp
[2] >= missing
:
318 def drawpoint(self
, privatedata
, sharedata
, graph
, point
):
319 for usename
, mask
, index
, axis
in privatedata
.rangepostmplist
:
321 if mask
& self
.mask_min
:
322 sharedata
.vrange
[index
][0] = axis
.convert(point
[usename
+ "min"])
323 if mask
& self
.mask_dmin
:
324 sharedata
.vrange
[index
][0] = axis
.convert(point
[usename
] - point
["d" + usename
+ "min"])
325 if mask
& self
.mask_d
:
326 sharedata
.vrange
[index
][0] = axis
.convert(point
[usename
] - point
["d" + usename
])
327 except (ArithmeticError, ValueError, TypeError):
328 sharedata
.vrange
[index
][0] = None
330 if mask
& self
.mask_max
:
331 sharedata
.vrange
[index
][1] = axis
.convert(point
[usename
+ "max"])
332 if mask
& self
.mask_dmax
:
333 sharedata
.vrange
[index
][1] = axis
.convert(point
[usename
] + point
["d" + usename
+ "max"])
334 if mask
& self
.mask_d
:
335 sharedata
.vrange
[index
][1] = axis
.convert(point
[usename
] + point
["d" + usename
])
336 except (ArithmeticError, ValueError, TypeError):
337 sharedata
.vrange
[index
][1] = None
339 # some range checks for data consistency
340 if (sharedata
.vrange
[index
][0] is not None and sharedata
.vrange
[index
][1] is not None and
341 sharedata
.vrange
[index
][0] > sharedata
.vrange
[index
][1] + self
.epsilon
):
342 raise ValueError("inverse range")
343 # disabled due to missing vpos access:
344 # if (sharedata.vrange[index][0] is not None and sharedata.vpos[index] is not None and
345 # sharedata.vrange[index][0] > sharedata.vpos[index] + self.epsilon):
346 # raise ValueError("negative minimum errorbar")
347 # if (sharedata.vrange[index][1] is not None and sharedata.vpos[index] is not None and
348 # sharedata.vrange[index][1] < sharedata.vpos[index] - self.epsilon):
349 # raise ValueError("negative maximum errorbar")
352 registerdefaultprovider(range(), range.providesdata
)
355 def _crosssymbol(c
, x_pt
, y_pt
, size_pt
, attrs
):
356 c
.draw(path
.path(path
.moveto_pt(x_pt
-0.5*size_pt
, y_pt
-0.5*size_pt
),
357 path
.lineto_pt(x_pt
+0.5*size_pt
, y_pt
+0.5*size_pt
),
358 path
.moveto_pt(x_pt
-0.5*size_pt
, y_pt
+0.5*size_pt
),
359 path
.lineto_pt(x_pt
+0.5*size_pt
, y_pt
-0.5*size_pt
)), attrs
)
361 def _plussymbol(c
, x_pt
, y_pt
, size_pt
, attrs
):
362 c
.draw(path
.path(path
.moveto_pt(x_pt
-0.707106781*size_pt
, y_pt
),
363 path
.lineto_pt(x_pt
+0.707106781*size_pt
, y_pt
),
364 path
.moveto_pt(x_pt
, y_pt
-0.707106781*size_pt
),
365 path
.lineto_pt(x_pt
, y_pt
+0.707106781*size_pt
)), attrs
)
367 def _squaresymbol(c
, x_pt
, y_pt
, size_pt
, attrs
):
368 c
.draw(path
.path(path
.moveto_pt(x_pt
-0.5*size_pt
, y_pt
-0.5*size_pt
),
369 path
.lineto_pt(x_pt
+0.5*size_pt
, y_pt
-0.5*size_pt
),
370 path
.lineto_pt(x_pt
+0.5*size_pt
, y_pt
+0.5*size_pt
),
371 path
.lineto_pt(x_pt
-0.5*size_pt
, y_pt
+0.5*size_pt
),
372 path
.closepath()), attrs
)
374 def _trianglesymbol(c
, x_pt
, y_pt
, size_pt
, attrs
):
375 c
.draw(path
.path(path
.moveto_pt(x_pt
-0.759835685*size_pt
, y_pt
-0.438691337*size_pt
),
376 path
.lineto_pt(x_pt
+0.759835685*size_pt
, y_pt
-0.438691337*size_pt
),
377 path
.lineto_pt(x_pt
, y_pt
+0.877382675*size_pt
),
378 path
.closepath()), attrs
)
380 def _circlesymbol(c
, x_pt
, y_pt
, size_pt
, attrs
):
381 c
.draw(path
.path(path
.arc_pt(x_pt
, y_pt
, 0.564189583*size_pt
, 0, 360),
382 path
.closepath()), attrs
)
384 def _diamondsymbol(c
, x_pt
, y_pt
, size_pt
, attrs
):
385 c
.draw(path
.path(path
.moveto_pt(x_pt
-0.537284965*size_pt
, y_pt
),
386 path
.lineto_pt(x_pt
, y_pt
-0.930604859*size_pt
),
387 path
.lineto_pt(x_pt
+0.537284965*size_pt
, y_pt
),
388 path
.lineto_pt(x_pt
, y_pt
+0.930604859*size_pt
),
389 path
.closepath()), attrs
)
392 class _styleneedingpointpos(_style
):
394 needsdata
= ["vposmissing"]
396 def columnnames(self
, privatedata
, sharedata
, graph
, columnnames
):
397 if len(sharedata
.vposmissing
):
398 raise ValueError("incomplete position information")
402 class symbol(_styleneedingpointpos
):
404 needsdata
= ["vpos", "vposmissing", "vposvalid"]
406 # "inject" the predefinied symbols into the class:
408 # Note, that statements like cross = _crosssymbol are
409 # invalid, since the would lead to unbound methods, but
410 # a single entry changeable list does the trick.
412 # Once we require Python 2.2+ we should use staticmethods
413 # to implement the default symbols inplace.
415 cross
= attr
.changelist([_crosssymbol
])
416 plus
= attr
.changelist([_plussymbol
])
417 square
= attr
.changelist([_squaresymbol
])
418 triangle
= attr
.changelist([_trianglesymbol
])
419 circle
= attr
.changelist([_circlesymbol
])
420 diamond
= attr
.changelist([_diamondsymbol
])
422 changecross
= attr
.changelist([_crosssymbol
, _plussymbol
, _squaresymbol
, _trianglesymbol
, _circlesymbol
, _diamondsymbol
])
423 changeplus
= attr
.changelist([_plussymbol
, _squaresymbol
, _trianglesymbol
, _circlesymbol
, _diamondsymbol
, _crosssymbol
])
424 changesquare
= attr
.changelist([_squaresymbol
, _trianglesymbol
, _circlesymbol
, _diamondsymbol
, _crosssymbol
, _plussymbol
])
425 changetriangle
= attr
.changelist([_trianglesymbol
, _circlesymbol
, _diamondsymbol
, _crosssymbol
, _plussymbol
, _squaresymbol
])
426 changecircle
= attr
.changelist([_circlesymbol
, _diamondsymbol
, _crosssymbol
, _plussymbol
, _squaresymbol
, _trianglesymbol
])
427 changediamond
= attr
.changelist([_diamondsymbol
, _crosssymbol
, _plussymbol
, _squaresymbol
, _trianglesymbol
, _circlesymbol
])
428 changesquaretwice
= attr
.changelist([_squaresymbol
, _squaresymbol
, _trianglesymbol
, _trianglesymbol
, _circlesymbol
, _circlesymbol
, _diamondsymbol
, _diamondsymbol
])
429 changetriangletwice
= attr
.changelist([_trianglesymbol
, _trianglesymbol
, _circlesymbol
, _circlesymbol
, _diamondsymbol
, _diamondsymbol
, _squaresymbol
, _squaresymbol
])
430 changecircletwice
= attr
.changelist([_circlesymbol
, _circlesymbol
, _diamondsymbol
, _diamondsymbol
, _squaresymbol
, _squaresymbol
, _trianglesymbol
, _trianglesymbol
])
431 changediamondtwice
= attr
.changelist([_diamondsymbol
, _diamondsymbol
, _squaresymbol
, _squaresymbol
, _trianglesymbol
, _trianglesymbol
, _circlesymbol
, _circlesymbol
])
433 changestrokedfilled
= attr
.changelist([deco
.stroked
, deco
.filled
])
434 changefilledstroked
= attr
.changelist([deco
.filled
, deco
.stroked
])
436 defaultsymbolattrs
= [deco
.stroked
]
438 def __init__(self
, symbol
=changecross
, size
=0.2*unit
.v_cm
, symbolattrs
=[]):
441 self
.symbolattrs
= symbolattrs
443 def selectstyle(self
, privatedata
, sharedata
, graph
, selectindex
, selecttotal
):
444 privatedata
.symbol
= attr
.selectattr(self
.symbol
, selectindex
, selecttotal
)
445 privatedata
.size_pt
= unit
.topt(attr
.selectattr(self
.size
, selectindex
, selecttotal
))
446 if self
.symbolattrs
is not None:
447 privatedata
.symbolattrs
= attr
.selectattrs(self
.defaultsymbolattrs
+ self
.symbolattrs
, selectindex
, selecttotal
)
449 privatedata
.symbolattrs
= None
451 def initdrawpoints(self
, privatedata
, sharedata
, graph
):
452 privatedata
.symbolcanvas
= canvas
.canvas()
454 def drawpoint(self
, privatedata
, sharedata
, graph
, point
):
455 if sharedata
.vposvalid
and privatedata
.symbolattrs
is not None:
456 x_pt
, y_pt
= graph
.vpos_pt(*sharedata
.vpos
)
457 privatedata
.symbol(privatedata
.symbolcanvas
, x_pt
, y_pt
, privatedata
.size_pt
, privatedata
.symbolattrs
)
459 def donedrawpoints(self
, privatedata
, sharedata
, graph
):
460 graph
.insert(privatedata
.symbolcanvas
)
462 def key_pt(self
, privatedata
, sharedata
, graph
, x_pt
, y_pt
, width_pt
, height_pt
):
463 if privatedata
.symbolattrs
is not None:
464 privatedata
.symbol(graph
, x_pt
+0.5*width_pt
, y_pt
+0.5*height_pt
, privatedata
.size_pt
, privatedata
.symbolattrs
)
467 class _line(_styleneedingpointpos
):
469 # this style is not a complete style, but it provides the basic functionality to
470 # create a line, which is cut at the graph boundaries (or at otherwise invalid points)
472 def initpointstopath(self
, privatedata
):
473 privatedata
.path
= path
.path()
474 privatedata
.linebasepoints
= []
475 privatedata
.lastvpos
= None
477 def addpointstopath(self
, privatedata
):
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 addpoint(self
, privatedata
, graphvpos_pt
, vposavailable
, vposvalid
, vpos
):
488 # append linebasepoints
490 if len(privatedata
.linebasepoints
):
491 # the last point was inside the graph
492 if vposvalid
: # shortcut for the common case
493 privatedata
.linebasepoints
.append(graphvpos_pt(*vpos
))
497 for vstart
, vend
in zip(privatedata
.lastvpos
, vpos
):
500 # 1 = vstart + (vend - vstart) * cut
502 newcut
= (1 - vstart
)/(vend
- vstart
)
503 except (ArithmeticError, TypeError):
506 # 0 = vstart + (vend - vstart) * cut
508 newcut
= - vstart
/(vend
- vstart
)
509 except (ArithmeticError, TypeError):
511 if newcut
is not None and newcut
< cut
:
515 for vstart
, vend
in zip(privatedata
.lastvpos
, vpos
):
516 cutvpos
.append(vstart
+ (vend
- vstart
) * cut
)
517 privatedata
.linebasepoints
.append(graphvpos_pt(*cutvpos
))
518 self
.addpointstopath(privatedata
)
520 # the last point was outside the graph
521 if privatedata
.lastvpos
is not None:
525 for vstart
, vend
in zip(privatedata
.lastvpos
, vpos
):
528 # 1 = vstart + (vend - vstart) * cut
530 newcut
= (1 - vstart
)/(vend
- vstart
)
531 except (ArithmeticError, TypeError):
534 # 0 = vstart + (vend - vstart) * cut
536 newcut
= - vstart
/(vend
- vstart
)
537 except (ArithmeticError, TypeError):
539 if newcut
is not None and newcut
> cut
:
543 for vstart
, vend
in zip(privatedata
.lastvpos
, vpos
):
544 cutvpos
.append(vstart
+ (vend
- vstart
) * cut
)
545 privatedata
.linebasepoints
.append(graphvpos_pt(*cutvpos
))
546 privatedata
.linebasepoints
.append(graphvpos_pt(*vpos
))
548 # sometimes cut beginning and end
551 for vstart
, vend
in zip(privatedata
.lastvpos
, vpos
):
556 # 1 = vstart + (vend - vstart) * cutfrom
558 newcutfrom
= (1 - vstart
)/(vend
- vstart
)
559 except (ArithmeticError, TypeError):
564 # 0 = vstart + (vend - vstart) * cutfrom
566 newcutfrom
= - vstart
/(vend
- vstart
)
567 except (ArithmeticError, TypeError):
569 if newcutfrom
is not None and newcutfrom
> cutfrom
:
573 # 1 = vstart + (vend - vstart) * cutto
575 newcutto
= (1 - vstart
)/(vend
- vstart
)
576 except (ArithmeticError, TypeError):
579 # 0 = vstart + (vend - vstart) * cutto
581 newcutto
= - vstart
/(vend
- vstart
)
582 except (ArithmeticError, TypeError):
584 if newcutto
is not None and newcutto
< cutto
:
590 for vstart
, vend
in zip(privatedata
.lastvpos
, vpos
):
591 cutfromvpos
.append(vstart
+ (vend
- vstart
) * cutfrom
)
592 cuttovpos
.append(vstart
+ (vend
- vstart
) * cutto
)
593 privatedata
.linebasepoints
.append(graphvpos_pt(*cutfromvpos
))
594 privatedata
.linebasepoints
.append(graphvpos_pt(*cuttovpos
))
595 self
.addpointstopath(privatedata
)
596 privatedata
.lastvpos
= vpos
[:]
598 if len(privatedata
.linebasepoints
) > 1:
599 self
.addpointstopath(privatedata
)
600 privatedata
.lastvpos
= None
602 def addinvalid(self
, privatedata
):
603 if len(privatedata
.linebasepoints
) > 1:
604 self
.addpointstopath(privatedata
)
605 privatedata
.lastvpos
= None
607 def donepointstopath(self
, privatedata
):
608 if len(privatedata
.linebasepoints
) > 1:
609 self
.addpointstopath(privatedata
)
610 return privatedata
.path
615 needsdata
= ["vpos", "vposmissing", "vposavailable", "vposvalid"]
617 changelinestyle
= attr
.changelist([style
.linestyle
.solid
,
618 style
.linestyle
.dashed
,
619 style
.linestyle
.dotted
,
620 style
.linestyle
.dashdotted
])
622 defaultlineattrs
= [changelinestyle
]
624 def __init__(self
, lineattrs
=[]):
625 self
.lineattrs
= lineattrs
627 def selectstyle(self
, privatedata
, sharedata
, graph
, selectindex
, selecttotal
):
628 if self
.lineattrs
is not None:
629 privatedata
.lineattrs
= attr
.selectattrs(self
.defaultlineattrs
+ self
.lineattrs
, selectindex
, selecttotal
)
631 privatedata
.lineattrs
= None
633 def initdrawpoints(self
, privatedata
, sharedata
, graph
):
634 self
.initpointstopath(privatedata
)
636 def drawpoint(self
, privatedata
, sharedata
, graph
, point
):
637 self
.addpoint(privatedata
, graph
.vpos_pt
, sharedata
.vposavailable
, sharedata
.vposvalid
, sharedata
.vpos
)
639 def donedrawpoints(self
, privatedata
, sharedata
, graph
):
640 path
= self
.donepointstopath(privatedata
)
641 if privatedata
.lineattrs
is not None and len(path
):
642 graph
.stroke(path
, privatedata
.lineattrs
)
644 def key_pt(self
, privatedata
, sharedata
, graph
, x_pt
, y_pt
, width_pt
, height_pt
):
645 if privatedata
.lineattrs
is not None:
646 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
)
649 class impulses(_styleneedingpointpos
):
651 needsdata
= ["vpos", "vposmissing", "vposavailable", "vposvalid", "poscolumnnames"]
653 defaultlineattrs
= [line
.changelinestyle
]
654 defaultfrompathattrs
= []
656 def __init__(self
, lineattrs
=[], fromvalue
=0, frompathattrs
=[], valueaxisindex
=1):
657 self
.lineattrs
= lineattrs
658 self
.fromvalue
= fromvalue
659 self
.frompathattrs
= frompathattrs
660 self
.valueaxisindex
= valueaxisindex
662 def selectstyle(self
, privatedata
, sharedata
, graph
, selectindex
, selecttotal
):
663 privatedata
.insertfrompath
= selectindex
== 0
664 if self
.lineattrs
is not None:
665 privatedata
.lineattrs
= attr
.selectattrs(self
.defaultlineattrs
+ self
.lineattrs
, selectindex
, selecttotal
)
667 privatedata
.lineattrs
= None
669 def adjustaxis(self
, privatedata
, sharedata
, graph
, columnname
, data
):
670 if self
.fromvalue
is not None:
672 i
= sharedata
.poscolumnnames
.index(columnname
)
676 if i
== self
.valueaxisindex
:
677 graph
.axes
[sharedata
.poscolumnnames
[i
]].adjustaxis([self
.fromvalue
])
679 def initdrawpoints(self
, privatedata
, sharedata
, graph
):
680 privatedata
.impulsescanvas
= canvas
.canvas()
681 if self
.fromvalue
is not None:
682 valueaxisname
= sharedata
.poscolumnnames
[self
.valueaxisindex
]
683 privatedata
.vfromvalue
= graph
.axes
[valueaxisname
].convert(self
.fromvalue
)
684 privatedata
.vfromvaluecut
= 0
685 if privatedata
.vfromvalue
< 0:
686 privatedata
.vfromvalue
= 0
687 if privatedata
.vfromvalue
> 1:
688 privatedata
.vfromvalue
= 1
689 if self
.frompathattrs
is not None and privatedata
.insertfrompath
:
690 graph
.stroke(graph
.axes
[valueaxisname
].vgridpath(privatedata
.vfromvalue
),
691 self
.defaultfrompathattrs
+ self
.frompathattrs
)
693 privatedata
.vfromvalue
= 0
695 def drawpoint(self
, privatedata
, sharedata
, graph
, point
):
696 if sharedata
.vposvalid
and privatedata
.lineattrs
is not None:
697 vpos
= sharedata
.vpos
[:]
698 vpos
[self
.valueaxisindex
] = privatedata
.vfromvalue
699 privatedata
.impulsescanvas
.stroke(graph
.vgeodesic(*(vpos
+ sharedata
.vpos
)), privatedata
.lineattrs
)
701 def donedrawpoints(self
, privatedata
, sharedata
, graph
):
702 graph
.insert(privatedata
.impulsescanvas
)
704 def key_pt(self
, privatedata
, sharedata
, graph
, x_pt
, y_pt
, width_pt
, height_pt
):
705 if privatedata
.lineattrs
is not None:
706 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
)
709 class errorbar(_style
):
711 needsdata
= ["vpos", "vposmissing", "vposavailable", "vposvalid", "vrange", "vrangeminmissing", "vrangemaxmissing"]
713 defaulterrorbarattrs
= []
715 def __init__(self
, size
=0.1*unit
.v_cm
,
719 self
.errorbarattrs
= errorbarattrs
720 self
.epsilon
= epsilon
722 def columnnames(self
, privatedata
, sharedata
, graph
, columnnames
):
723 for i
in sharedata
.vposmissing
:
724 if i
in sharedata
.vrangeminmissing
and i
in sharedata
.vrangemaxmissing
:
725 raise ValueError("position and range for a graph dimension missing")
728 def selectstyle(self
, privatedata
, sharedata
, graph
, selectindex
, selecttotal
):
729 privatedata
.errorsize_pt
= unit
.topt(attr
.selectattr(self
.size
, selectindex
, selecttotal
))
730 privatedata
.errorbarattrs
= attr
.selectattrs(self
.defaulterrorbarattrs
+ self
.errorbarattrs
, selectindex
, selecttotal
)
732 def initdrawpoints(self
, privatedata
, sharedata
, graph
):
733 if privatedata
.errorbarattrs
is not None:
734 privatedata
.errorbarcanvas
= canvas
.canvas(privatedata
.errorbarattrs
)
735 privatedata
.dimensionlist
= list(xrange(len(sharedata
.vpos
)))
737 def drawpoint(self
, privatedata
, sharedata
, graph
, point
):
738 if privatedata
.errorbarattrs
is not None:
739 for i
in privatedata
.dimensionlist
:
740 for j
in privatedata
.dimensionlist
:
742 (sharedata
.vpos
[j
] is None or
743 sharedata
.vpos
[j
] < -self
.epsilon
or
744 sharedata
.vpos
[j
] > 1+self
.epsilon
)):
747 if ((sharedata
.vrange
[i
][0] is None and sharedata
.vpos
[i
] is None) or
748 (sharedata
.vrange
[i
][1] is None and sharedata
.vpos
[i
] is None) or
749 (sharedata
.vrange
[i
][0] is None and sharedata
.vrange
[i
][1] is None)):
751 vminpos
= sharedata
.vpos
[:]
752 if sharedata
.vrange
[i
][0] is not None:
753 vminpos
[i
] = sharedata
.vrange
[i
][0]
757 if vminpos
[i
] > 1+self
.epsilon
:
759 if vminpos
[i
] < -self
.epsilon
:
762 vmaxpos
= sharedata
.vpos
[:]
763 if sharedata
.vrange
[i
][1] is not None:
764 vmaxpos
[i
] = sharedata
.vrange
[i
][1]
768 if vmaxpos
[i
] < -self
.epsilon
:
770 if vmaxpos
[i
] > 1+self
.epsilon
:
773 privatedata
.errorbarcanvas
.stroke(graph
.vgeodesic(*(vminpos
+ vmaxpos
)))
774 for j
in privatedata
.dimensionlist
:
777 privatedata
.errorbarcanvas
.stroke(graph
.vcap_pt(j
, privatedata
.errorsize_pt
, *vminpos
))
779 privatedata
.errorbarcanvas
.stroke(graph
.vcap_pt(j
, privatedata
.errorsize_pt
, *vmaxpos
))
781 def donedrawpoints(self
, privatedata
, sharedata
, graph
):
782 if privatedata
.errorbarattrs
is not None:
783 graph
.insert(privatedata
.errorbarcanvas
)
786 class text(_styleneedingpointpos
):
788 needsdata
= ["vpos", "vposmissing", "vposvalid"]
790 defaulttextattrs
= [textmodule
.halign
.center
, textmodule
.vshift
.mathaxis
]
792 def __init__(self
, textname
="text", dxname
=None, dyname
=None,
793 dxunit
=0.3*unit
.v_cm
, dyunit
=0.3*unit
.v_cm
,
794 textdx
=0*unit
.v_cm
, textdy
=0.3*unit
.v_cm
, textattrs
=[]):
795 self
.textname
= textname
802 self
.textattrs
= textattrs
804 def columnnames(self
, privatedata
, sharedata
, graph
, columnnames
):
805 if self
.textname
not in columnnames
:
806 raise ValueError("column '%s' missing" % self
.textname
)
807 names
= [self
.textname
]
808 if self
.dxname
is not None:
809 if self
.dxname
not in columnnames
:
810 raise ValueError("column '%s' missing" % self
.dxname
)
811 names
.append(self
.dxname
)
812 if self
.dyname
is not None:
813 if self
.dyname
not in columnnames
:
814 raise ValueError("column '%s' missing" % self
.dyname
)
815 names
.append(self
.dyname
)
816 return names
+ _styleneedingpointpos
.columnnames(self
, privatedata
, sharedata
, graph
, columnnames
)
818 def selectstyle(self
, privatedata
, sharedata
, graph
, selectindex
, selecttotal
):
819 if self
.textattrs
is not None:
820 privatedata
.textattrs
= attr
.selectattrs(self
.defaulttextattrs
+ self
.textattrs
, selectindex
, selecttotal
)
822 privatedata
.textattrs
= None
824 def initdrawpoints(self
, privatedata
, sharedata
, grap
):
825 if self
.dxname
is None:
826 privatedata
.textdx_pt
= unit
.topt(self
.textdx
)
828 privatedata
.dxunit_pt
= unit
.topt(self
.dxunit
)
829 if self
.dyname
is None:
830 privatedata
.textdy_pt
= unit
.topt(self
.textdy
)
832 privatedata
.dyunit_pt
= unit
.topt(self
.dyunit
)
834 def drawpoint(self
, privatedata
, sharedata
, graph
, point
):
835 if privatedata
.textattrs
is not None and sharedata
.vposvalid
:
836 x_pt
, y_pt
= graph
.vpos_pt(*sharedata
.vpos
)
838 text
= str(point
[self
.textname
])
842 if self
.dxname
is None:
843 dx_pt
= privatedata
.textdx_pt
845 dx_pt
= float(point
[self
.dxname
]) * privatedata
.dxunit_pt
846 if self
.dyname
is None:
847 dy_pt
= privatedata
.textdy_pt
849 dy_pt
= float(point
[self
.dyname
]) * privatedata
.dyunit_pt
850 graph
.text_pt(x_pt
+ dx_pt
, y_pt
+ dy_pt
, text
, privatedata
.textattrs
)
852 def key_pt(self
, privatedata
, sharedata
, graph
, x_pt
, y_pt
, width_pt
, height_pt
):
853 raise RuntimeError("Style currently doesn't provide a graph key")
856 class arrow(_styleneedingpointpos
):
858 needsdata
= ["vpos", "vposmissing", "vposvalid"]
860 defaultlineattrs
= []
861 defaultarrowattrs
= []
863 def __init__(self
, linelength
=0.25*unit
.v_cm
, arrowsize
=0.15*unit
.v_cm
, lineattrs
=[], arrowattrs
=[], arrowpos
=0.5, epsilon
=1e-5):
864 self
.linelength
= linelength
865 self
.arrowsize
= arrowsize
866 self
.lineattrs
= lineattrs
867 self
.arrowattrs
= arrowattrs
868 self
.arrowpos
= arrowpos
869 self
.epsilon
= epsilon
871 def columnnames(self
, privatedata
, sharedata
, graph
, columnnames
):
872 if len(graph
.axesnames
) != 2:
873 raise ValueError("arrow style restricted on two-dimensional graphs")
874 if "size" not in columnnames
:
875 raise ValueError("size missing")
876 if "angle" not in columnnames
:
877 raise ValueError("angle missing")
878 return ["size", "angle"] + _styleneedingpointpos
.columnnames(self
, privatedata
, sharedata
, graph
, columnnames
)
880 def selectstyle(self
, privatedata
, sharedata
, graph
, selectindex
, selecttotal
):
881 if self
.lineattrs
is not None:
882 privatedata
.lineattrs
= attr
.selectattrs(self
.defaultlineattrs
+ self
.lineattrs
, selectindex
, selecttotal
)
884 privatedata
.lineattrs
= None
885 if self
.arrowattrs
is not None:
886 privatedata
.arrowattrs
= attr
.selectattrs(self
.defaultarrowattrs
+ self
.arrowattrs
, selectindex
, selecttotal
)
888 privatedata
.arrowattrs
= None
890 def initdrawpoints(self
, privatedata
, sharedata
, graph
):
891 privatedata
.arrowcanvas
= canvas
.canvas()
893 def drawpoint(self
, privatedata
, sharedata
, graph
, point
):
894 if privatedata
.lineattrs
is not None and privatedata
.arrowattrs
is not None and sharedata
.vposvalid
:
895 linelength_pt
= unit
.topt(self
.linelength
)
896 x_pt
, y_pt
= graph
.vpos_pt(*sharedata
.vpos
)
898 angle
= point
["angle"] + 0.0
899 size
= point
["size"] + 0.0
903 if point
["size"] > self
.epsilon
:
904 dx
= math
.cos(angle
*math
.pi
/180)
905 dy
= math
.sin(angle
*math
.pi
/180)
906 x1
= x_pt
-self
.arrowpos
*dx
*linelength_pt
*size
907 y1
= y_pt
-self
.arrowpos
*dy
*linelength_pt
*size
908 x2
= x_pt
+(1-self
.arrowpos
)*dx
*linelength_pt
*size
909 y2
= y_pt
+(1-self
.arrowpos
)*dy
*linelength_pt
*size
910 privatedata
.arrowcanvas
.stroke(path
.line_pt(x1
, y1
, x2
, y2
), privatedata
.lineattrs
+
911 [deco
.earrow(privatedata
.arrowattrs
, size
=self
.arrowsize
*size
)])
913 def donedrawpoints(self
, privatedata
, sharedata
, graph
):
914 graph
.insert(privatedata
.arrowcanvas
)
916 def key_pt(self
, privatedata
, sharedata
, graph
, x_pt
, y_pt
, width_pt
, height_pt
):
917 raise RuntimeError("Style currently doesn't provide a graph key")
922 needsdata
= ["vrange", "vrangeminmissing", "vrangemaxmissing"]
924 def __init__(self
, gradient
=color
.gradient
.Grey
):
925 self
.gradient
= gradient
927 def columnnames(self
, privatedata
, sharedata
, graph
, columnnames
):
928 if len(graph
.axesnames
) != 2:
929 raise TypeError("arrow style restricted on two-dimensional graphs")
930 if "color" not in columnnames
:
931 raise ValueError("color missing")
932 if len(sharedata
.vrangeminmissing
) + len(sharedata
.vrangemaxmissing
):
933 raise ValueError("incomplete range")
936 def initdrawpoints(self
, privatedata
, sharedata
, graph
):
937 privatedata
.rectcanvas
= graph
.insert(canvas
.canvas())
939 def drawpoint(self
, privatedata
, sharedata
, graph
, point
):
940 xvmin
= sharedata
.vrange
[0][0]
941 xvmax
= sharedata
.vrange
[0][1]
942 yvmin
= sharedata
.vrange
[1][0]
943 yvmax
= sharedata
.vrange
[1][1]
944 if (xvmin
is not None and xvmin
< 1 and
945 xvmax
is not None and xvmax
> 0 and
946 yvmin
is not None and yvmin
< 1 and
947 yvmax
is not None and yvmax
> 0):
956 p
= graph
.vgeodesic(xvmin
, yvmin
, xvmax
, yvmin
)
957 p
.append(graph
.vgeodesic_el(xvmax
, yvmin
, xvmax
, yvmax
))
958 p
.append(graph
.vgeodesic_el(xvmax
, yvmax
, xvmin
, yvmax
))
959 p
.append(graph
.vgeodesic_el(xvmin
, yvmax
, xvmin
, yvmin
))
960 p
.append(path
.closepath())
961 privatedata
.rectcanvas
.fill(p
, [self
.gradient
.getcolor(point
["color"])])
963 def key_pt(self
, privatedata
, sharedata
, graph
, x_pt
, y_pt
, width_pt
, height_pt
):
964 raise RuntimeError("Style currently doesn't provide a graph key")
967 class histogram(_style
):
969 needsdata
= ["vpos", "vposmissing", "vrange", "vrangeminmissing", "vrangemaxmissing"]
971 defaultlineattrs
= [deco
.stroked
]
972 defaultfrompathattrs
= []
974 def __init__(self
, lineattrs
=[], steps
=0, fromvalue
=0, frompathattrs
=[], fillable
=0, rectkey
=0,
975 autohistogramaxisindex
=0, autohistogrampointpos
=0.5, epsilon
=1e-10):
976 self
.lineattrs
= lineattrs
978 self
.fromvalue
= fromvalue
979 self
.frompathattrs
= frompathattrs
980 self
.fillable
= fillable
# TODO: fillable paths might not properly be closed by straight lines on curved graph geometries
981 self
.rectkey
= rectkey
982 self
.autohistogramaxisindex
= autohistogramaxisindex
983 self
.autohistogrampointpos
= autohistogrampointpos
984 self
.epsilon
= epsilon
986 def columnnames(self
, privatedata
, sharedata
, graph
, columnnames
):
987 if len(graph
.axesnames
) != 2:
988 raise TypeError("histogram style restricted on two-dimensional graphs")
989 privatedata
.rangeaxisindex
= None
990 for i
in builtinrange(len(graph
.axesnames
)):
991 if i
in sharedata
.vrangeminmissing
or i
in sharedata
.vrangemaxmissing
:
992 if i
in sharedata
.vposmissing
:
993 raise ValueError("pos and range missing")
995 if privatedata
.rangeaxisindex
is not None:
996 raise ValueError("multiple ranges")
997 privatedata
.rangeaxisindex
= i
998 if privatedata
.rangeaxisindex
is None:
999 privatedata
.rangeaxisindex
= self
.autohistogramaxisindex
1000 privatedata
.autohistogram
= 1
1002 privatedata
.autohistogram
= 0
1005 def adjustaxis(self
, privatedata
, sharedata
, graph
, columnname
, data
):
1006 if privatedata
.autohistogram
and columnname
== sharedata
.poscolumnnames
[privatedata
.rangeaxisindex
]:
1008 raise ValueError("several data points needed for automatic histogram width calculation")
1010 delta
= data
[1] - data
[0]
1011 min = data
[0] - self
.autohistogrampointpos
* delta
1012 max = data
[-1] + (1-self
.autohistogrampointpos
) * delta
1013 graph
.axes
[columnname
].adjustaxis([min, max])
1014 elif self
.fromvalue
is not None and columnname
== sharedata
.poscolumnnames
[1-privatedata
.rangeaxisindex
]:
1015 graph
.axes
[columnname
].adjustaxis([self
.fromvalue
])
1017 def selectstyle(self
, privatedata
, sharedata
, graph
, selectindex
, selecttotal
):
1018 privatedata
.insertfrompath
= selectindex
== 0
1019 if self
.lineattrs
is not None:
1020 privatedata
.lineattrs
= attr
.selectattrs(self
.defaultlineattrs
+ self
.lineattrs
, selectindex
, selecttotal
)
1022 privatedata
.lineattrs
= None
1024 def vmoveto(self
, privatedata
, sharedata
, graph
, vpos
, vvalue
):
1025 if -self
.epsilon
< vpos
< 1+self
.epsilon
and -self
.epsilon
< vvalue
< 1+self
.epsilon
:
1026 if privatedata
.rangeaxisindex
:
1027 privatedata
.path
.append(path
.moveto_pt(*graph
.vpos_pt(vvalue
, vpos
)))
1029 privatedata
.path
.append(path
.moveto_pt(*graph
.vpos_pt(vpos
, vvalue
)))
1031 def vposline(self
, privatedata
, sharedata
, graph
, vpos
, vvalue1
, vvalue2
):
1032 if -self
.epsilon
< vpos
< 1+self
.epsilon
:
1047 if abs(vvalue1cut
+ vvalue2cut
) <= 1:
1048 if vvalue1cut
and not self
.fillable
:
1049 if privatedata
.rangeaxisindex
:
1050 privatedata
.path
.append(path
.moveto_pt(*graph
.vpos_pt(vvalue1
, vpos
)))
1052 privatedata
.path
.append(path
.moveto_pt(*graph
.vpos_pt(vpos
, vvalue1
)))
1053 if privatedata
.rangeaxisindex
:
1054 privatedata
.path
.append(graph
.vgeodesic_el(vvalue1
, vpos
, vvalue2
, vpos
))
1056 privatedata
.path
.append(graph
.vgeodesic_el(vpos
, vvalue1
, vpos
, vvalue2
))
1058 def vvalueline(self
, privatedata
, sharedata
, graph
, vvalue
, vpos1
, vpos2
):
1060 if vvalue
< -self
.epsilon
:
1062 warnings
.warn("cut at graph boundary adds artificial lines to fillable step histogram path")
1063 if vvalue
> 1+self
.epsilon
:
1065 warnings
.warn("cut at graph boundary adds artificial lines to fillable step histogram path")
1066 if self
.fillable
or (-self
.epsilon
< vvalue
< 1+self
.epsilon
):
1081 if abs(vpos1cut
+ vpos2cut
) <= 1:
1084 if privatedata
.rangeaxisindex
:
1085 privatedata
.path
.append(path
.moveto_pt(*graph
.vpos_pt(privatedata
.vfromvalue
, vpos1
)))
1086 privatedata
.path
.append(graph
.vgeodesic_el(privatedata
.vfromvalue
, vpos1
, vvalue
, vpos1
))
1088 privatedata
.path
.append(path
.moveto_pt(*graph
.vpos_pt(vpos1
, privatedata
.vfromvalue
)))
1089 privatedata
.path
.append(graph
.vgeodesic_el(vpos1
, privatedata
.vfromvalue
, vpos1
, vvalue
))
1091 if privatedata
.rangeaxisindex
:
1092 privatedata
.path
.append(path
.moveto_pt(*graph
.vpos_pt(vvalue
, vpos1
)))
1094 privatedata
.path
.append(path
.moveto_pt(*graph
.vpos_pt(vpos1
, vvalue
)))
1095 if privatedata
.rangeaxisindex
:
1096 privatedata
.path
.append(graph
.vgeodesic_el(vvalue
, vpos1
, vvalue
, vpos2
))
1098 privatedata
.path
.append(graph
.vgeodesic_el(vpos1
, vvalue
, vpos2
, vvalue
))
1099 if self
.fillable
and vpos2cut
:
1100 warnings
.warn("cut at graph boundary adds artificial lines to fillable step histogram path")
1101 if privatedata
.rangeaxisindex
:
1102 privatedata
.path
.append(graph
.vgeodesic_el(vvalue
, vpos2
, privatedata
.vfromvalue
, vpos2
))
1104 privatedata
.path
.append(graph
.vgeodesic_el(vpos2
, vvalue
, vpos2
, privatedata
.vfromvalue
))
1106 def drawvalue(self
, privatedata
, sharedata
, graph
, vmin
, vmax
, vvalue
):
1107 currentvalid
= vmin
is not None and vmax
is not None and vvalue
is not None
1108 if self
.fillable
and not self
.steps
:
1109 if not currentvalid
:
1112 if vmin
< -self
.epsilon
:
1115 elif vmin
> 1+self
.epsilon
:
1119 if vmax
< -self
.epsilon
:
1122 if vmax
> 1+self
.epsilon
:
1126 if vvalue
< -self
.epsilon
:
1129 if vvalue
> 1+self
.epsilon
:
1133 if abs(vmincut
) + abs(vmaxcut
) + abs(vvaluecut
) + abs(privatedata
.vfromvaluecut
) > 1:
1134 if abs(vmincut
+ vmaxcut
) > 1 or abs(vvaluecut
+privatedata
.vfromvaluecut
) > 1:
1137 warnings
.warn("multiple cuts at graph boundary add artificial lines to fillable rectangle histogram path")
1140 if privatedata
.rangeaxisindex
:
1141 privatedata
.path
.append(path
.moveto_pt(*graph
.vpos_pt(privatedata
.vfromvalue
, vmin
)))
1142 privatedata
.path
.append(graph
.vgeodesic_el(privatedata
.vfromvalue
, vmin
, privatedata
.vfromvalue
, vmax
))
1143 privatedata
.path
.append(graph
.vgeodesic_el(privatedata
.vfromvalue
, vmax
, vvalue
, vmax
))
1144 privatedata
.path
.append(graph
.vgeodesic_el(vvalue
, vmax
, vvalue
, vmin
))
1146 privatedata
.path
.append(path
.moveto_pt(*graph
.vpos_pt(vmin
, privatedata
.vfromvalue
)))
1147 privatedata
.path
.append(graph
.vgeodesic_el(vmin
, privatedata
.vfromvalue
, vmax
, privatedata
.vfromvalue
))
1148 privatedata
.path
.append(graph
.vgeodesic_el(vmax
, privatedata
.vfromvalue
, vmax
, vvalue
))
1149 privatedata
.path
.append(graph
.vgeodesic_el(vmax
, vvalue
, vmin
, vvalue
))
1152 if privatedata
.rangeaxisindex
:
1153 privatedata
.path
.append(path
.moveto_pt(*graph
.vpos_pt(vvalue
, vmax
)))
1154 privatedata
.path
.append(graph
.vgeodesic_el(vvalue
, vmax
, vvalue
, vmin
))
1155 privatedata
.path
.append(graph
.vgeodesic_el(vvalue
, vmin
, privatedata
.vfromvalue
, vmin
))
1156 privatedata
.path
.append(graph
.vgeodesic_el(privatedata
.vfromvalue
, vmin
, privatedata
.vfromvalue
, vmax
))
1158 privatedata
.path
.append(path
.moveto_pt(*graph
.vpos_pt(vmax
, vvalue
)))
1159 privatedata
.path
.append(graph
.vgeodesic_el(vmax
, vvalue
, vmin
, vvalue
))
1160 privatedata
.path
.append(graph
.vgeodesic_el(vmin
, vvalue
, vmin
, privatedata
.vfromvalue
))
1161 privatedata
.path
.append(graph
.vgeodesic_el(vmin
, privatedata
.vfromvalue
, vmax
, privatedata
.vfromvalue
))
1162 elif privatedata
.vfromvaluecut
:
1164 if privatedata
.rangeaxisindex
:
1165 privatedata
.path
.append(path
.moveto_pt(*graph
.vpos_pt(privatedata
.vfromvalue
, vmax
)))
1166 privatedata
.path
.append(graph
.vgeodesic_el(privatedata
.vfromvalue
, vmax
, vvalue
, vmax
))
1167 privatedata
.path
.append(graph
.vgeodesic_el(vvalue
, vmax
, vvalue
, vmin
))
1168 privatedata
.path
.append(graph
.vgeodesic_el(vvalue
, vmin
, privatedata
.vfromvalue
, vmin
))
1170 privatedata
.path
.append(path
.moveto_pt(*graph
.vpos_pt(vmax
, privatedata
.vfromvalue
)))
1171 privatedata
.path
.append(graph
.vgeodesic_el(vmax
, privatedata
.vfromvalue
, vmax
, vvalue
))
1172 privatedata
.path
.append(graph
.vgeodesic_el(vmax
, vvalue
, vmin
, vvalue
))
1173 privatedata
.path
.append(graph
.vgeodesic_el(vmin
, vvalue
, vmin
, privatedata
.vfromvalue
))
1176 if privatedata
.rangeaxisindex
:
1177 privatedata
.path
.append(path
.moveto_pt(*graph
.vpos_pt(vvalue
, vmin
)))
1178 privatedata
.path
.append(graph
.vgeodesic_el(vvalue
, vmin
, privatedata
.vfromvalue
, vmin
))
1179 privatedata
.path
.append(graph
.vgeodesic_el(privatedata
.vfromvalue
, vmin
, privatedata
.vfromvalue
, vmax
))
1180 privatedata
.path
.append(graph
.vgeodesic_el(privatedata
.vfromvalue
, vmax
, vvalue
, vmax
))
1182 privatedata
.path
.append(path
.moveto_pt(*graph
.vpos_pt(vmin
, vvalue
)))
1183 privatedata
.path
.append(graph
.vgeodesic_el(vmin
, vvalue
, vmin
, privatedata
.vfromvalue
))
1184 privatedata
.path
.append(graph
.vgeodesic_el(vmin
, privatedata
.vfromvalue
, vmax
, privatedata
.vfromvalue
))
1185 privatedata
.path
.append(graph
.vgeodesic_el(vmax
, privatedata
.vfromvalue
, vmax
, vvalue
))
1187 if privatedata
.rangeaxisindex
:
1188 privatedata
.path
.append(path
.moveto_pt(*graph
.vpos_pt(privatedata
.vfromvalue
, vmin
)))
1189 privatedata
.path
.append(graph
.vgeodesic_el(privatedata
.vfromvalue
, vmin
, privatedata
.vfromvalue
, vmax
))
1190 privatedata
.path
.append(graph
.vgeodesic_el(privatedata
.vfromvalue
, vmax
, vvalue
, vmax
))
1191 privatedata
.path
.append(graph
.vgeodesic_el(vvalue
, vmax
, vvalue
, vmin
))
1192 privatedata
.path
.append(graph
.vgeodesic_el(vvalue
, vmin
, privatedata
.vfromvalue
, vmin
))
1193 privatedata
.path
.append(path
.closepath())
1195 privatedata
.path
.append(path
.moveto_pt(*graph
.vpos_pt(vmin
, privatedata
.vfromvalue
)))
1196 privatedata
.path
.append(graph
.vgeodesic_el(vmin
, privatedata
.vfromvalue
, vmax
, privatedata
.vfromvalue
))
1197 privatedata
.path
.append(graph
.vgeodesic_el(vmax
, privatedata
.vfromvalue
, vmax
, vvalue
))
1198 privatedata
.path
.append(graph
.vgeodesic_el(vmax
, vvalue
, vmin
, vvalue
))
1199 privatedata
.path
.append(graph
.vgeodesic_el(vmin
, vvalue
, vmin
, privatedata
.vfromvalue
))
1200 privatedata
.path
.append(path
.closepath())
1203 gap
= abs(vmin
- privatedata
.lastvmax
) > self
.epsilon
1204 except (ArithmeticError, ValueError, TypeError):
1206 if (privatedata
.lastvvalue
is not None and currentvalid
and not gap
and
1207 (self
.steps
or (privatedata
.lastvvalue
-privatedata
.vfromvalue
)*(vvalue
-privatedata
.vfromvalue
) < 0)):
1208 self
.vposline(privatedata
, sharedata
, graph
,
1209 vmin
, privatedata
.lastvvalue
, vvalue
)
1211 if privatedata
.lastvvalue
is not None and currentvalid
:
1212 currentbigger
= abs(privatedata
.lastvvalue
-privatedata
.vfromvalue
) < abs(vvalue
-privatedata
.vfromvalue
)
1213 if privatedata
.lastvvalue
is not None and (not currentvalid
or not currentbigger
or gap
):
1214 self
.vposline(privatedata
, sharedata
, graph
,
1215 privatedata
.lastvmax
, privatedata
.lastvvalue
, privatedata
.vfromvalue
)
1217 self
.vmoveto(privatedata
, sharedata
, graph
,
1219 if currentvalid
and (privatedata
.lastvvalue
is None or currentbigger
or gap
):
1220 self
.vmoveto(privatedata
, sharedata
, graph
,
1221 vmin
, privatedata
.vfromvalue
)
1222 self
.vposline(privatedata
, sharedata
, graph
,
1223 vmin
, privatedata
.vfromvalue
, vvalue
)
1225 self
.vvalueline(privatedata
, sharedata
, graph
,
1227 privatedata
.lastvvalue
= vvalue
1228 privatedata
.lastvmax
= vmax
1230 privatedata
.lastvvalue
= privatedata
.lastvmax
= None
1232 def initdrawpoints(self
, privatedata
, sharedata
, graph
):
1233 privatedata
.path
= path
.path()
1234 privatedata
.lastvvalue
= privatedata
.lastvmax
= None
1235 privatedata
.vcurrentpoint
= None
1236 privatedata
.count
= 0
1237 if self
.fromvalue
is not None:
1238 valueaxisname
= sharedata
.poscolumnnames
[1-privatedata
.rangeaxisindex
]
1239 privatedata
.vfromvalue
= graph
.axes
[valueaxisname
].convert(self
.fromvalue
)
1240 privatedata
.vfromvaluecut
= 0
1241 if privatedata
.vfromvalue
< 0:
1242 privatedata
.vfromvalue
= 0
1243 privatedata
.vfromvaluecut
= -1
1244 if privatedata
.vfromvalue
> 1:
1245 privatedata
.vfromvalue
= 1
1246 privatedata
.vfromvaluecut
= 1
1247 if self
.frompathattrs
is not None and privatedata
.insertfrompath
:
1248 graph
.stroke(graph
.axes
[valueaxisname
].vgridpath(privatedata
.vfromvalue
),
1249 self
.defaultfrompathattrs
+ self
.frompathattrs
)
1251 privatedata
.vfromvalue
= 0
1253 def drawpoint(self
, privatedata
, sharedata
, graph
, point
):
1254 if privatedata
.autohistogram
:
1255 # automatic range handling
1256 privatedata
.count
+= 1
1257 if privatedata
.count
== 2:
1258 if privatedata
.rangeaxisindex
:
1259 privatedata
.vrange
= sharedata
.vpos
[1] - privatedata
.lastvpos
[1]
1260 self
.drawvalue(privatedata
, sharedata
, graph
,
1261 privatedata
.lastvpos
[1] - self
.autohistogrampointpos
*privatedata
.vrange
,
1262 privatedata
.lastvpos
[1] + (1-self
.autohistogrampointpos
)*privatedata
.vrange
,
1263 privatedata
.lastvpos
[0])
1265 privatedata
.vrange
= sharedata
.vpos
[0] - privatedata
.lastvpos
[0]
1266 self
.drawvalue(privatedata
, sharedata
, graph
,
1267 privatedata
.lastvpos
[0] - self
.autohistogrampointpos
*privatedata
.vrange
,
1268 privatedata
.lastvpos
[0] + (1-self
.autohistogrampointpos
)*privatedata
.vrange
,
1269 privatedata
.lastvpos
[1])
1270 elif privatedata
.count
> 2:
1271 if privatedata
.rangeaxisindex
:
1272 vrange
= sharedata
.vpos
[1] - privatedata
.lastvpos
[1]
1274 vrange
= sharedata
.vpos
[0] - privatedata
.lastvpos
[0]
1275 if abs(privatedata
.vrange
- vrange
) > self
.epsilon
:
1276 raise ValueError("equal steps (in graph coordinates) needed for automatic width calculation")
1277 if privatedata
.count
> 1:
1278 if privatedata
.rangeaxisindex
:
1279 self
.drawvalue(privatedata
, sharedata
, graph
,
1280 sharedata
.vpos
[1] - self
.autohistogrampointpos
*privatedata
.vrange
,
1281 sharedata
.vpos
[1] + (1-self
.autohistogrampointpos
)*privatedata
.vrange
,
1284 self
.drawvalue(privatedata
, sharedata
, graph
,
1285 sharedata
.vpos
[0] - self
.autohistogrampointpos
*privatedata
.vrange
,
1286 sharedata
.vpos
[0] + (1-self
.autohistogrampointpos
)*privatedata
.vrange
,
1288 privatedata
.lastvpos
= sharedata
.vpos
[:]
1290 if privatedata
.rangeaxisindex
:
1291 self
.drawvalue(privatedata
, sharedata
, graph
,
1292 sharedata
.vrange
[1][0], sharedata
.vrange
[1][1], sharedata
.vpos
[0])
1294 self
.drawvalue(privatedata
, sharedata
, graph
,
1295 sharedata
.vrange
[0][0], sharedata
.vrange
[0][1], sharedata
.vpos
[1])
1297 def donedrawpoints(self
, privatedata
, sharedata
, graph
):
1298 self
.drawvalue(privatedata
, sharedata
, graph
, None, None, None)
1299 if privatedata
.lineattrs
is not None and len(privatedata
.path
):
1300 graph
.draw(privatedata
.path
, privatedata
.lineattrs
)
1302 def key_pt(self
, privatedata
, sharedata
, graph
, x_pt
, y_pt
, width_pt
, height_pt
):
1303 if privatedata
.lineattrs
is not None:
1305 p
= path
.rect_pt(x_pt
, y_pt
, width_pt
, height_pt
)
1307 p
= path
.line_pt(x_pt
, y_pt
+0.5*height_pt
, x_pt
+width_pt
, y_pt
+0.5*height_pt
)
1308 graph
.draw(p
, privatedata
.lineattrs
)
1311 class barpos(_style
):
1313 providesdata
= ["vpos", "vposmissing", "vposavailable", "vposvalid", "vbarrange", "barposcolumnnames", "barvalueindex", "lastbarvalue", "stackedbar", "stackedbardraw"]
1315 defaultfrompathattrs
= []
1317 def __init__(self
, fromvalue
=None, frompathattrs
=[], epsilon
=1e-10):
1318 self
.fromvalue
= fromvalue
1319 self
.frompathattrs
= frompathattrs
1320 self
.epsilon
= epsilon
1322 def columnnames(self
, privatedata
, sharedata
, graph
, columnnames
):
1323 sharedata
.barposcolumnnames
= []
1324 sharedata
.barvalueindex
= None
1325 for dimension
, axisnames
in enumerate(graph
.axesnames
):
1327 for axisname
in axisnames
:
1328 if axisname
in columnnames
:
1329 if sharedata
.barvalueindex
is not None:
1330 raise ValueError("multiple values")
1331 sharedata
.barvalueindex
= dimension
1332 sharedata
.barposcolumnnames
.append(axisname
)
1334 if (axisname
+ "name") in columnnames
:
1335 sharedata
.barposcolumnnames
.append(axisname
+ "name")
1338 raise ValueError("multiple names and value")
1340 raise ValueError("value/name missing")
1341 if sharedata
.barvalueindex
is None:
1342 raise ValueError("missing value")
1343 sharedata
.vposmissing
= []
1344 return sharedata
.barposcolumnnames
1346 def addsubvalue(self
, value
, subvalue
):
1351 return value
[0], self
.addsubvalue(value
[1], subvalue
)
1353 return value
, subvalue
1355 return value
, subvalue
1357 def adjustaxis(self
, privatedata
, sharedata
, graph
, columnname
, data
):
1359 i
= sharedata
.barposcolumnnames
.index(columnname
)
1363 if i
== sharedata
.barvalueindex
:
1364 if self
.fromvalue
is not None:
1365 graph
.axes
[sharedata
.barposcolumnnames
[i
]].adjustaxis([self
.fromvalue
])
1366 graph
.axes
[sharedata
.barposcolumnnames
[i
]].adjustaxis(data
)
1368 graph
.axes
[sharedata
.barposcolumnnames
[i
][:-4]].adjustaxis([self
.addsubvalue(x
, 0) for x
in data
])
1369 graph
.axes
[sharedata
.barposcolumnnames
[i
][:-4]].adjustaxis([self
.addsubvalue(x
, 1) for x
in data
])
1371 def selectstyle(self
, privatedata
, sharedata
, graph
, selectindex
, selecttotal
):
1372 privatedata
.insertfrompath
= selectindex
== 0
1374 def initdrawpoints(self
, privatedata
, sharedata
, graph
):
1375 sharedata
.vpos
= [None]*(len(sharedata
.barposcolumnnames
))
1376 sharedata
.vbarrange
= [[None for i
in xrange(2)] for x
in sharedata
.barposcolumnnames
]
1377 sharedata
.stackedbar
= sharedata
.stackedbardraw
= 0
1379 if self
.fromvalue
is not None:
1380 privatedata
.vfromvalue
= graph
.axes
[sharedata
.barposcolumnnames
[sharedata
.barvalueindex
][0]].convert(self
.fromvalue
)
1381 if privatedata
.vfromvalue
< 0:
1382 privatedata
.vfromvalue
= 0
1383 if privatedata
.vfromvalue
> 1:
1384 privatedata
.vfromvalue
= 1
1385 if self
.frompathattrs
is not None and privatedata
.insertfrompath
:
1386 graph
.stroke(graph
.axes
[sharedata
.barposcolumnnames
[sharedata
.barvalueindex
][0]].vgridpath(privatedata
.vfromvalue
),
1387 self
.defaultfrompathattrs
+ self
.frompathattrs
)
1389 privatedata
.vfromvalue
= 0
1391 def drawpoint(self
, privatedata
, sharedata
, graph
, point
):
1392 sharedata
.vposavailable
= sharedata
.vposvalid
= 1
1393 for i
, barname
in enumerate(sharedata
.barposcolumnnames
):
1394 if i
== sharedata
.barvalueindex
:
1395 sharedata
.vbarrange
[i
][0] = privatedata
.vfromvalue
1396 sharedata
.lastbarvalue
= point
[barname
]
1398 sharedata
.vpos
[i
] = sharedata
.vbarrange
[i
][1] = graph
.axes
[barname
].convert(sharedata
.lastbarvalue
)
1399 except (ArithmeticError, ValueError, TypeError):
1400 sharedata
.vpos
[i
] = sharedata
.vbarrange
[i
][1] = None
1404 sharedata
.vbarrange
[i
][j
] = graph
.axes
[barname
[:-4]].convert(self
.addsubvalue(point
[barname
], j
))
1405 except (ArithmeticError, ValueError, TypeError):
1406 sharedata
.vbarrange
[i
][j
] = None
1408 sharedata
.vpos
[i
] = 0.5*(sharedata
.vbarrange
[i
][0]+sharedata
.vbarrange
[i
][1])
1409 except (ArithmeticError, ValueError, TypeError):
1410 sharedata
.vpos
[i
] = None
1411 if sharedata
.vpos
[i
] is None:
1412 sharedata
.vposavailable
= sharedata
.vposvalid
= 0
1413 elif sharedata
.vpos
[i
] < -self
.epsilon
or sharedata
.vpos
[i
] > 1+self
.epsilon
:
1414 sharedata
.vposvalid
= 0
1416 registerdefaultprovider(barpos(), ["vbarrange", "barposcolumnnames", "barvalueindex", "lastbarvalue", "stackedbar", "stackedbardraw"])
1419 class stackedbarpos(_style
):
1421 # provides no additional data, but needs some data (and modifies some of them)
1422 needsdata
= ["vbarrange", "barposcolumnnames", "barvalueindex", "lastbarvalue", "stackedbar", "stackedbardraw"]
1424 def __init__(self
, stackname
, addontop
=0, epsilon
=1e-10):
1425 self
.stackname
= stackname
1426 self
.epsilon
= epsilon
1427 self
.addontop
= addontop
1429 def columnnames(self
, privatedata
, sharedata
, graph
, columnnames
):
1430 if self
.stackname
not in columnnames
:
1431 raise ValueError("column '%s' missing" % self
.stackname
)
1432 return [self
.stackname
]
1434 def adjustaxis(self
, privatedata
, sharedata
, graph
, columnname
, data
):
1435 if columnname
== self
.stackname
:
1436 graph
.axes
[sharedata
.barposcolumnnames
[sharedata
.barvalueindex
]].adjustaxis(data
)
1438 def initdrawpoints(self
, privatedata
, sharedata
, graph
):
1439 if sharedata
.stackedbardraw
: # do not count the start bar when not gets painted
1440 sharedata
.stackedbar
+= 1
1442 def drawpoint(self
, privatedata
, sharedata
, graph
, point
):
1443 sharedata
.vbarrange
[sharedata
.barvalueindex
][0] = sharedata
.vbarrange
[sharedata
.barvalueindex
][1]
1446 sharedata
.lastbarvalue
+= point
[self
.stackname
]
1447 except (ArithmeticError, ValueError, TypeError):
1448 sharedata
.lastbarvalue
= None
1450 sharedata
.lastbarvalue
= point
[self
.stackname
]
1452 sharedata
.vpos
[sharedata
.barvalueindex
] = sharedata
.vbarrange
[sharedata
.barvalueindex
][1] = graph
.axes
[sharedata
.barposcolumnnames
[sharedata
.barvalueindex
]].convert(sharedata
.lastbarvalue
)
1453 except (ArithmeticError, ValueError, TypeError):
1454 sharedata
.vpos
[sharedata
.barvalueindex
] = sharedata
.vbarrange
[sharedata
.barvalueindex
][1] = None
1455 sharedata
.vposavailable
= sharedata
.vposvalid
= 0
1457 if not sharedata
.vposavailable
or not sharedata
.vposvalid
:
1458 sharedata
.vposavailable
= sharedata
.vposvalid
= 1
1459 for v
in sharedata
.vpos
:
1461 sharedata
.vposavailable
= sharedata
.vposvalid
= 0
1463 if v
< -self
.epsilon
or v
> 1+self
.epsilon
:
1464 sharedata
.vposvalid
= 0
1469 needsdata
= ["vbarrange"]
1471 defaultbarattrs
= [color
.gradient
.Rainbow
, deco
.stroked([color
.grey
.black
])]
1473 def __init__(self
, barattrs
=[]):
1474 self
.barattrs
= barattrs
1476 def columnnames(self
, privatedata
, sharedata
, graph
, columnnames
):
1477 if len(graph
.axesnames
) != 2:
1478 raise TypeError("bar style restricted on two-dimensional graphs")
1481 def selectstyle(self
, privatedata
, sharedata
, graph
, selectindex
, selecttotal
):
1482 privatedata
.barattrs
= attr
.selectattrs(self
.defaultbarattrs
+ self
.barattrs
, selectindex
, selecttotal
)
1484 def initdrawpoints(self
, privatedata
, sharedata
, graph
):
1485 privatedata
.rectcanvas
= graph
.insert(canvas
.canvas())
1486 sharedata
.stackedbardraw
= 1
1487 privatedata
.stackedbar
= sharedata
.stackedbar
1489 def drawpointfill(self
, privatedata
, p
):
1491 privatedata
.rectcanvas
.fill(p
, privatedata
.barattrs
)
1493 def drawpoint(self
, privatedata
, sharedata
, graph
, point
):
1494 xvmin
= sharedata
.vbarrange
[0][0]
1495 xvmax
= sharedata
.vbarrange
[0][1]
1496 yvmin
= sharedata
.vbarrange
[1][0]
1497 yvmax
= sharedata
.vbarrange
[1][1]
1500 xvmin
, xvmax
= xvmax
, xvmin
1505 yvmin
, yvmax
= yvmax
, yvmin
1508 if (xvmin
is not None and xvmin
< 1 and
1509 xvmax
is not None and xvmax
> 0 and
1510 yvmin
is not None and yvmin
< 1 and
1511 yvmax
is not None and yvmax
> 0):
1520 p
= graph
.vgeodesic(xvmin
, yvmin
, xvmax
, yvmin
)
1521 p
.append(graph
.vgeodesic_el(xvmax
, yvmin
, xvmax
, yvmax
))
1522 p
.append(graph
.vgeodesic_el(xvmax
, yvmax
, xvmin
, yvmax
))
1523 p
.append(graph
.vgeodesic_el(xvmin
, yvmax
, xvmin
, yvmin
))
1524 p
.append(path
.closepath())
1525 self
.drawpointfill(privatedata
, p
)
1527 self
.drawpointfill(privatedata
, None)
1529 def key_pt(self
, privatedata
, sharedata
, graph
, x_pt
, y_pt
, width_pt
, height_pt
):
1530 selectindex
= privatedata
.stackedbar
1531 selecttotal
= sharedata
.stackedbar
+ 1
1532 graph
.fill(path
.rect_pt(x_pt
+ width_pt
*selectindex
/float(selecttotal
), y_pt
, width_pt
/float(selecttotal
), height_pt
), privatedata
.barattrs
)
1535 class changebar(bar
):
1537 def selectstyle(self
, privatedata
, sharedata
, graph
, selectindex
, selecttotal
):
1538 if selecttotal
!= 1:
1539 raise RuntimeError("Changebar can't change its appearance. Thus you can't use it to plot several bars side by side on a subaxis.")
1541 def initdrawpoints(self
, privatedata
, sharedata
, graph
):
1542 bar
.initdrawpoints(self
, privatedata
, sharedata
, graph
)
1543 privatedata
.bars
= []
1545 def drawpointfill(self
, privatedata
, p
):
1546 privatedata
.bars
.append(p
)
1548 def donedrawpoints(self
, privatedata
, sharedata
, graph
):
1549 selecttotal
= len(privatedata
.bars
)
1550 for selectindex
, p
in enumerate(privatedata
.bars
):
1552 barattrs
= attr
.selectattrs(self
.defaultbarattrs
+ self
.barattrs
, selectindex
, selecttotal
)
1553 privatedata
.rectcanvas
.fill(p
, barattrs
)
1555 def key_pt(self
, privatedata
, sharedata
, graph
, x_pt
, y_pt
, width_pt
, height_pt
):
1556 raise RuntimeError("Style currently doesn't provide a graph key")
1559 class gridpos(_style
):
1561 needsdata
= ["vpos", "vposmissing", "vposavailable", "vposvalid"]
1562 providesdata
= ["values1", "values2", "data12", "data21", "index1", "index2"]
1564 def __init__(self
, index1
=0, index2
=1, epsilon
=1e-10):
1565 self
.index1
= index1
1566 self
.index2
= index2
1567 self
.epsilon
= epsilon
1569 def initdrawpoints(self
, privatedata
, sharedata
, graph
):
1570 sharedata
.index1
= self
.index1
1571 sharedata
.index2
= self
.index2
1572 sharedata
.values1
= {}
1573 sharedata
.values2
= {}
1574 sharedata
.data12
= {}
1575 sharedata
.data21
= {}
1577 def drawpoint(self
, privatedata
, sharedata
, graph
, point
):
1578 if sharedata
.vposavailable
:
1579 sharedata
.value1
= sharedata
.vpos
[self
.index1
]
1580 sharedata
.value2
= sharedata
.vpos
[self
.index2
]
1581 if not sharedata
.values1
.has_key(sharedata
.value1
):
1582 for hasvalue
in sharedata
.values1
.keys():
1583 if hasvalue
- self
.epsilon
<= sharedata
.value1
<= hasvalue
+ self
.epsilon
:
1584 sharedata
.value1
= hasvalue
1587 sharedata
.values1
[sharedata
.value1
] = 1
1588 if not sharedata
.values2
.has_key(sharedata
.value2
):
1589 for hasvalue
in sharedata
.values2
.keys():
1590 if hasvalue
- self
.epsilon
<= sharedata
.value2
<= hasvalue
+ self
.epsilon
:
1591 sharedata
.value2
= hasvalue
1594 sharedata
.values2
[sharedata
.value2
] = 1
1595 data
= sharedata
.vposavailable
, sharedata
.vposvalid
, sharedata
.vpos
[:]
1596 sharedata
.data12
.setdefault(sharedata
.value1
, {})[sharedata
.value2
] = data
1597 sharedata
.data21
.setdefault(sharedata
.value2
, {})[sharedata
.value1
] = data
1599 registerdefaultprovider(gridpos(), gridpos
.providesdata
)
1602 class grid(_line
, _style
):
1604 needsdata
= ["values1", "values2", "data12", "data21"]
1606 defaultgridattrs
= [line
.changelinestyle
]
1608 def __init__(self
, gridlines1
=1, gridlines2
=1, gridattrs
=[]):
1609 self
.gridlines1
= gridlines1
1610 self
.gridlines2
= gridlines2
1611 self
.gridattrs
= gridattrs
1613 def selectstyle(self
, privatedata
, sharedata
, graph
, selectindex
, selecttotal
):
1614 if self
.gridattrs
is not None:
1615 privatedata
.gridattrs
= attr
.selectattrs(self
.defaultgridattrs
+ self
.gridattrs
, selectindex
, selecttotal
)
1617 privatedata
.gridattrs
= None
1619 def donedrawpoints(self
, privatedata
, sharedata
, graph
):
1620 values1
= sharedata
.values1
.keys()
1622 values2
= sharedata
.values2
.keys()
1625 for value2
in values2
:
1626 data1
= sharedata
.data21
[value2
]
1627 self
.initpointstopath(privatedata
)
1628 for value1
in values1
:
1630 data
= data1
[value1
]
1632 self
.addinvalid(privatedata
)
1634 self
.addpoint(privatedata
, graph
.vpos_pt
, *data
)
1635 p
= self
.donepointstopath(privatedata
)
1637 graph
.stroke(p
, privatedata
.gridattrs
)
1639 for value1
in values1
:
1640 data2
= sharedata
.data12
[value1
]
1641 self
.initpointstopath(privatedata
)
1642 for value2
in values2
:
1644 data
= data2
[value2
]
1646 self
.addinvalid(privatedata
)
1648 self
.addpoint(privatedata
, graph
.vpos_pt
, *data
)
1649 p
= self
.donepointstopath(privatedata
)
1651 graph
.stroke(p
, privatedata
.gridattrs
)
1654 class surface(_style
):
1656 needsdata
= ["values1", "values2", "data12", "data21"]
1658 def __init__(self
, colorname
="color", gradient
=color
.gradient
.Grey
, mincolor
=None, maxcolor
=None,
1659 gridlines1
=0.05, gridlines2
=0.05, gridcolor
=None,
1660 backcolor
=color
.gray
.black
, **kwargs
):
1661 self
.colorname
= colorname
1662 self
.gradient
= gradient
1663 self
.mincolor
= mincolor
1664 self
.maxcolor
= maxcolor
1665 self
.gridlines1
= gridlines1
1666 self
.gridlines2
= gridlines2
1667 self
.gridcolor
= gridcolor
1668 self
.backcolor
= backcolor
1670 colorspacestring
= gradient
.getcolor(0).colorspacestring()
1671 if self
.gridcolor
is not None and self
.gridcolor
.colorspacestring() != colorspacestring
:
1672 raise RuntimeError("colorspace mismatch (gradient/grid)")
1673 if self
.backcolor
is not None and self
.backcolor
.colorspacestring() != colorspacestring
:
1674 raise RuntimeError("colorspace mismatch (gradient/back)")
1676 def midvalue(self
, v1
, v2
, v3
, v4
):
1677 return [0.25*sum(values
) for values
in zip(v1
, v2
, v3
, v4
)]
1679 def midcolor(self
, c1
, c2
, c3
, c4
):
1680 return 0.25*(c1
+c2
+c3
+c4
)
1682 def lightning(self
, angle
, zindex
):
1683 if angle
< 0 and self
.backcolor
is not None:
1684 return self
.backcolor
1685 return self
.gradient
.getcolor(0.7-0.4*abs(angle
)+0.1*zindex
)
1687 def columnnames(self
, privatedata
, sharedata
, graph
, columnnames
):
1688 privatedata
.colorize
= self
.colorname
in columnnames
1689 if privatedata
.colorize
:
1690 return [self
.colorname
]
1693 def initdrawpoints(self
, privatedata
, sharedata
, graph
):
1694 privatedata
.colors
= {}
1695 privatedata
.mincolor
= privatedata
.maxcolor
= None
1697 def drawpoint(self
, privatedata
, sharedata
, graph
, point
):
1698 if privatedata
.colorize
:
1700 color
= point
[self
.colorname
] + 0
1704 privatedata
.colors
.setdefault(sharedata
.value1
, {})[sharedata
.value2
] = color
1705 if privatedata
.mincolor
is None or color
< privatedata
.mincolor
:
1706 privatedata
.mincolor
= color
1707 if privatedata
.mincolor
is None or privatedata
.maxcolor
< color
:
1708 privatedata
.maxcolor
= color
1710 def donedrawpoints(self
, privatedata
, sharedata
, graph
):
1711 v1
= [0]*len(graph
.axesnames
)
1712 v2
= [0]*len(graph
.axesnames
)
1713 v3
= [0]*len(graph
.axesnames
)
1714 v4
= [0]*len(graph
.axesnames
)
1715 v1
[sharedata
.index2
] = 0.5
1716 v2
[sharedata
.index1
] = 0.5
1717 v3
[sharedata
.index1
] = 0.5
1718 v3
[sharedata
.index2
] = 1
1719 v4
[sharedata
.index1
] = 1
1720 v4
[sharedata
.index2
] = 0.5
1721 sortElements
= [-graph
.vzindex(*v1
),
1722 -graph
.vzindex(*v2
),
1723 -graph
.vzindex(*v3
),
1724 -graph
.vzindex(*v4
)]
1726 values1
= sharedata
.values1
.keys()
1728 v1
= [0]*len(graph
.axesnames
)
1729 v2
= [0]*len(graph
.axesnames
)
1730 v1
[sharedata
.index1
] = -1
1731 v2
[sharedata
.index1
] = 1
1733 if graph
.vzindex(*v1
) < graph
.vzindex(*v2
):
1736 sortElements
= [sortElements
[3], sortElements
[1], sortElements
[2], sortElements
[0]]
1738 values2
= sharedata
.values2
.keys()
1740 v1
= [0]*len(graph
.axesnames
)
1741 v2
= [0]*len(graph
.axesnames
)
1742 v1
[sharedata
.index2
] = -1
1743 v2
[sharedata
.index2
] = 1
1744 if graph
.vzindex(*v1
) < graph
.vzindex(*v2
):
1747 sortElements
= [sortElements
[0], sortElements
[2], sortElements
[1], sortElements
[3]]
1749 sortElements
= [(zindex
, i
) for i
, zindex
in enumerate(sortElements
)]
1752 if self
.mincolor
is not None:
1753 mincolor
= self
.mincolor
1754 if self
.maxcolor
is not None:
1755 maxcolor
= self
.maxcolor
1758 for value1a
, value1b
in zip(values1
[:-1], values1
[1:]):
1759 for value2a
, value2b
in zip(values2
[:-1], values2
[1:]):
1761 available1
, valid1
, v1
= sharedata
.data12
[value1a
][value2a
]
1762 available2
, valid2
, v2
= sharedata
.data12
[value1a
][value2b
]
1763 available3
, valid3
, v3
= sharedata
.data12
[value1b
][value2a
]
1764 available4
, valid4
, v4
= sharedata
.data12
[value1b
][value2b
]
1767 if not available1
or not available2
or not available3
or not available4
:
1769 if not valid1
or not valid2
or not valid3
or not valid4
:
1770 warnings
.warn("surface elements partially outside of the graph are (currently) skipped completely")
1772 def shrink(index
, v1
, v2
, by
):
1775 for i
in builtinrange(3):
1777 v1
[i
], v2
[i
] = v1
[i
] + by
*(v2
[i
]-v1
[i
]), v2
[i
] + by
*(v1
[i
]-v2
[i
])
1779 v1f
, v2f
, v3f
, v4f
= v1
, v2
, v3
, v4
1780 if self
.gridcolor
is not None and self
.gridlines1
:
1781 v1
, v2
= shrink(sharedata
.index1
, v1
, v2
, self
.gridlines1
)
1782 v3
, v4
= shrink(sharedata
.index1
, v3
, v4
, self
.gridlines1
)
1783 if self
.gridcolor
is not None and self
.gridlines2
:
1784 v1
, v3
= shrink(sharedata
.index2
, v1
, v3
, self
.gridlines2
)
1785 v2
, v4
= shrink(sharedata
.index2
, v2
, v4
, self
.gridlines2
)
1786 v5
= self
.midvalue(v1
, v2
, v3
, v4
)
1787 x1_pt
, y1_pt
= graph
.vpos_pt(*v1
)
1788 x2_pt
, y2_pt
= graph
.vpos_pt(*v2
)
1789 x3_pt
, y3_pt
= graph
.vpos_pt(*v3
)
1790 x4_pt
, y4_pt
= graph
.vpos_pt(*v4
)
1791 x5_pt
, y5_pt
= graph
.vpos_pt(*v5
)
1792 if privatedata
.colorize
:
1793 def colorfromgradient(c
):
1794 return self
.gradient
.getcolor((c
- privatedata
.mincolor
) /
1795 float(privatedata
.maxcolor
- privatedata
.mincolor
))
1796 c1
= privatedata
.colors
[value1a
][value2a
]
1797 c2
= privatedata
.colors
[value1a
][value2b
]
1798 c3
= privatedata
.colors
[value1b
][value2a
]
1799 c4
= privatedata
.colors
[value1b
][value2b
]
1800 c5
= self
.midcolor(c1
, c2
, c3
, c4
)
1801 c1a
= c1b
= colorfromgradient(c1
)
1802 c2a
= c2c
= colorfromgradient(c2
)
1803 c3b
= c3d
= colorfromgradient(c3
)
1804 c4c
= c4d
= colorfromgradient(c4
)
1805 c5a
= c5b
= c5c
= c5d
= colorfromgradient(c5
)
1806 if self
.backcolor
is not None and sign
*graph
.vangle(*(v1
+v2
+v5
)) < 0:
1807 c1a
= c2a
= c5a
= self
.backcolor
1808 if self
.backcolor
is not None and sign
*graph
.vangle(*(v3
+v1
+v5
)) < 0:
1809 c3b
= c1b
= c5b
= self
.backcolor
1810 if self
.backcolor
is not None and sign
*graph
.vangle(*(v2
+v4
+v5
)) < 0:
1811 c2c
= c4c
= c5c
= self
.backcolor
1812 if self
.backcolor
is not None and sign
*graph
.vangle(*(v4
+v3
+v5
)) < 0:
1813 c4d
= c3d
= c5d
= self
.backcolor
1815 zindex
= graph
.vzindex(*v5
)
1816 c1a
= c2a
= c5a
= self
.lightning(sign
*graph
.vangle(*(v1
+v2
+v5
)), zindex
)
1817 c3b
= c1b
= c5b
= self
.lightning(sign
*graph
.vangle(*(v3
+v1
+v5
)), zindex
)
1818 c2c
= c4c
= c5c
= self
.lightning(sign
*graph
.vangle(*(v2
+v4
+v5
)), zindex
)
1819 c4d
= c3d
= c5d
= self
.lightning(sign
*graph
.vangle(*(v4
+v3
+v5
)), zindex
)
1820 for zindex
, i
in sortElements
:
1822 elements
.append(mesh
.element((mesh
.node_pt((x1_pt
, y1_pt
), c1a
),
1823 mesh
.node_pt((x2_pt
, y2_pt
), c2a
),
1824 mesh
.node_pt((x5_pt
, y5_pt
), c5a
))))
1825 if self
.gridcolor
is not None and self
.gridlines2
:
1826 elements
.append(mesh
.element((mesh
.node_pt(graph
.vpos_pt(*v1f
), self
.gridcolor
),
1827 mesh
.node_pt(graph
.vpos_pt(*v2
), self
.gridcolor
),
1828 mesh
.node_pt(graph
.vpos_pt(*v1
), self
.gridcolor
))))
1829 elements
.append(mesh
.element((mesh
.node_pt(graph
.vpos_pt(*v1f
), self
.gridcolor
),
1830 mesh
.node_pt(graph
.vpos_pt(*v2
), self
.gridcolor
),
1831 mesh
.node_pt(graph
.vpos_pt(*v2f
), self
.gridcolor
))))
1833 elements
.append(mesh
.element((mesh
.node_pt((x3_pt
, y3_pt
), c3b
),
1834 mesh
.node_pt((x1_pt
, y1_pt
), c1b
),
1835 mesh
.node_pt((x5_pt
, y5_pt
), c5b
))))
1836 if self
.gridcolor
is not None and self
.gridlines1
:
1837 elements
.append(mesh
.element((mesh
.node_pt(graph
.vpos_pt(*v1f
), self
.gridcolor
),
1838 mesh
.node_pt(graph
.vpos_pt(*v3
), self
.gridcolor
),
1839 mesh
.node_pt(graph
.vpos_pt(*v1
), self
.gridcolor
))))
1840 elements
.append(mesh
.element((mesh
.node_pt(graph
.vpos_pt(*v1f
), self
.gridcolor
),
1841 mesh
.node_pt(graph
.vpos_pt(*v3
), self
.gridcolor
),
1842 mesh
.node_pt(graph
.vpos_pt(*v3f
), self
.gridcolor
))))
1844 elements
.append(mesh
.element((mesh
.node_pt((x2_pt
, y2_pt
), c2c
),
1845 mesh
.node_pt((x4_pt
, y4_pt
), c4c
),
1846 mesh
.node_pt((x5_pt
, y5_pt
), c5c
))))
1847 if self
.gridcolor
is not None and self
.gridlines1
:
1848 elements
.append(mesh
.element((mesh
.node_pt(graph
.vpos_pt(*v2f
), self
.gridcolor
),
1849 mesh
.node_pt(graph
.vpos_pt(*v4
), self
.gridcolor
),
1850 mesh
.node_pt(graph
.vpos_pt(*v2
), self
.gridcolor
))))
1851 elements
.append(mesh
.element((mesh
.node_pt(graph
.vpos_pt(*v2f
), self
.gridcolor
),
1852 mesh
.node_pt(graph
.vpos_pt(*v4
), self
.gridcolor
),
1853 mesh
.node_pt(graph
.vpos_pt(*v4f
), self
.gridcolor
))))
1855 elements
.append(mesh
.element((mesh
.node_pt((x4_pt
, y4_pt
), c4d
),
1856 mesh
.node_pt((x3_pt
, y3_pt
), c3d
),
1857 mesh
.node_pt((x5_pt
, y5_pt
), c5d
))))
1858 if self
.gridcolor
is not None and self
.gridlines2
:
1859 elements
.append(mesh
.element((mesh
.node_pt(graph
.vpos_pt(*v3f
), self
.gridcolor
),
1860 mesh
.node_pt(graph
.vpos_pt(*v4
), self
.gridcolor
),
1861 mesh
.node_pt(graph
.vpos_pt(*v3
), self
.gridcolor
))))
1862 elements
.append(mesh
.element((mesh
.node_pt(graph
.vpos_pt(*v3f
), self
.gridcolor
),
1863 mesh
.node_pt(graph
.vpos_pt(*v4
), self
.gridcolor
),
1864 mesh
.node_pt(graph
.vpos_pt(*v4f
), self
.gridcolor
))))
1865 m
= mesh
.mesh(elements
, check
=0)