1 # -*- encoding: utf-8 -*-
4 # Copyright (C) 2002-2012 Jörg Lehmann <joergl@users.sourceforge.net>
5 # Copyright (C) 2003-2004 Michael Schindler <m-schindler@users.sourceforge.net>
6 # Copyright (C) 2002-2012 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
25 import math
, warnings
, cStringIO
26 from pyx
import attr
, deco
, bitmap
, style
, color
, unit
, canvas
, path
, mesh
, pycompat
, trafo
27 from pyx
import text
as textmodule
28 from graph
import registerdefaultprovider
, graphx
29 import data
as datamodule
36 """Interface class for graph styles
38 Each graph style must support the methods described in this
39 class. However, since a graph style might not need to perform
40 actions on all the various events, it does not need to overwrite
41 all methods of this base class (e.g. this class is not an abstract
42 class in any respect).
44 A style should never store private data by instance variables
45 (i.e. accessing self), but it should use the sharedata and privatedata
46 instances instead. A style instance can be used multiple times with
47 different sharedata and privatedata instances at the very same time.
48 The sharedata and privatedata instances act as data containers and
49 sharedata allows for sharing information across several styles.
51 Every style contains two class variables, which are not to be
53 - providesdata is a list of variable names a style offers via
54 the sharedata instance. This list is used to determine whether
55 all needs of subsequent styles are fulfilled. Otherwise
56 getdefaultprovider should return a proper style to be used.
57 - needsdata is a list of variable names the style needs to access in the
61 providesdata
= [] # by default, we provide nothing
62 needsdata
= [] # and do not depend on anything
64 def columnnames(self
, privatedata
, sharedata
, graph
, columnnames
, dataaxisnames
):
65 """Set column information
67 This method is used setup the column name information to be
68 accessible to the style later on. The style should analyse
69 the list of column names. The method should return a list of
70 column names which the style will make use of. If a style
71 uses some column data to feed into an axis with a different
72 name, it should add an entry into the dataaxisnames dictionary
73 with key begin the column name and the value being the axis
77 def adjustaxis(self
, privatedata
, sharedata
, graph
, plotitem
, columnname
, data
):
80 This method is called in order to adjust the axis range to
81 the provided data. columnname is the column name (each style
82 is subsequently called for all column names)."""
85 def selectstyle(self
, privatedata
, sharedata
, graph
, selectindex
, selecttotal
):
86 """Select stroke/fill attributes
88 This method is called to allow for the selection of
89 changable attributes of a style."""
92 def initdrawpoints(self
, privatedata
, sharedata
, graph
):
93 """Initialize drawing of data
95 This method might be used to initialize the drawing of data."""
98 def drawpoint(self
, privatedata
, sharedata
, graph
, point
):
101 This method is called for each data point. The data is
102 available in the dictionary point. The dictionary
103 keys are the column names."""
106 def donedrawpoints(self
, privatedata
, sharedata
, graph
):
107 """Finalize drawing of data
109 This method is called after the last data point was
110 drawn using the drawpoint method above."""
113 def key_pt(self
, privatedata
, sharedata
, graph
, x_pt
, y_pt
, width_pt
, height_pt
):
119 class _autokeygraph
: pass
122 class _keygraphstyle(_style
):
124 autographkey
= _autokeygraph
126 def __init__(self
, colorname
="color", gradient
=color
.gradient
.Grey
, coloraxis
=None, keygraph
=_autokeygraph
):
127 self
.colorname
= colorname
128 self
.gradient
= gradient
129 self
.coloraxis
= coloraxis
130 self
.keygraph
= keygraph
132 def columnnames(self
, privatedata
, sharedata
, graph
, columnnames
, dataaxisnames
):
133 return [self
.colorname
]
135 def adjustaxis(self
, privatedata
, sharedata
, graph
, plotitem
, columnname
, data
):
136 if columnname
== self
.colorname
:
137 if self
.keygraph
is None:
138 # we always need a keygraph, but we might not show it
139 if self
.coloraxis
is None:
140 coloraxis
= axis
.lin()
142 coloraxis
= self
.coloraxis
143 privatedata
.keygraph
= graphx(length
=10, direction
="vertical", x
=coloraxis
)
144 elif self
.keygraph
is _autokeygraph
:
145 if self
.coloraxis
is None:
146 coloraxis
= axis
.lin(title
=plotitem
.title
)
147 plotitem
.title
= None # Huui!?
149 coloraxis
= self
.coloraxis
150 privatedata
.keygraph
= graphx(x
=coloraxis
, **graph
.autokeygraphattrs())
152 privatedata
.keygraph
= self
.keygraph
153 # TODO: we shouldn't have multiple plotitems
154 privatedata
.keygraph
.plot(datamodule
.values(x
=data
), [gradient(gradient
=self
.gradient
)])
156 def color(self
, privatedata
, c
):
157 vc
= privatedata
.keygraph
.axes
["x"].convert(c
)
159 warnings
.warn("gradiend color range is exceeded (lower bound)")
162 warnings
.warn("gradiend color range is exceeded (upper bound)")
164 return self
.gradient
.getcolor(vc
)
166 def donedrawpoints(self
, privatedata
, sharedata
, graph
):
167 if self
.keygraph
is _autokeygraph
:
168 graph
.layer("key").insert(privatedata
.keygraph
, [graph
.autokeygraphtrafo(privatedata
.keygraph
)])
173 providesdata
= ["vpos", "vposmissing", "vposavailable", "vposvalid", "poscolumnnames"]
175 def __init__(self
, usenames
={}, epsilon
=1e-10):
176 self
.usenames
= usenames
177 self
.epsilon
= epsilon
179 def columnnames(self
, privatedata
, sharedata
, graph
, columnnames
, dataaxisnames
):
180 privatedata
.poscolumnnames
= []
181 privatedata
.vposmissing
= []
182 privatedata
.axisnames
= {}
183 for count
, axisnames
in enumerate(graph
.axesnames
):
184 for axisname
in axisnames
:
186 usename
= self
.usenames
[axisname
]
189 for columnname
in columnnames
:
190 if usename
== columnname
:
191 privatedata
.poscolumnnames
.append(columnname
)
192 privatedata
.axisnames
[columnname
] = axisname
193 if len(privatedata
.poscolumnnames
) > count
+1:
194 raise ValueError("multiple axes per graph dimension")
195 elif len(privatedata
.poscolumnnames
) < count
+1:
196 privatedata
.vposmissing
.append(count
)
197 privatedata
.poscolumnnames
.append(None)
198 # Make poscolumnnames and vposmissing available to the outside,
199 # but keep a private reference. A copy is not needed, because
200 # the data is not altered in place, but might be exchanged my a
201 # later, different pos style in the styles list (due to different
203 sharedata
.poscolumnnames
= privatedata
.poscolumnnames
204 sharedata
.vposmissing
= privatedata
.vposmissing
205 dataaxisnames
.update(privatedata
.axisnames
)
206 return [columnname
for columnname
in privatedata
.poscolumnnames
if columnname
is not None]
208 def adjustaxis(self
, privatedata
, sharedata
, graph
, plotitem
, columnname
, data
):
209 if columnname
in privatedata
.axisnames
:
210 graph
.axes
[privatedata
.axisnames
[columnname
]].adjustaxis(data
)
212 def initdrawpoints(self
, privatedata
, sharedata
, graph
):
213 sharedata
.vpos
= [None]*(len(graph
.axesnames
))
214 privatedata
.pointpostmplist
= [[columnname
, index
, graph
.axes
[privatedata
.axisnames
[columnname
]]] # temporarily used by drawpoint only
215 for index
, columnname
in enumerate([columnname
for columnname
in privatedata
.poscolumnnames
if columnname
is not None])]
216 for missing
in privatedata
.vposmissing
:
217 for pointpostmp
in privatedata
.pointpostmplist
:
218 if pointpostmp
[1] >= missing
:
221 def drawpoint(self
, privatedata
, sharedata
, graph
, point
):
222 sharedata
.vposavailable
= 1 # valid position (but might be outside of the graph)
223 sharedata
.vposvalid
= 1 # valid position inside the graph
224 for columnname
, index
, axis
in privatedata
.pointpostmplist
:
226 v
= axis
.convert(point
[columnname
])
227 except (ArithmeticError, ValueError, TypeError):
228 sharedata
.vposavailable
= sharedata
.vposvalid
= 0
229 sharedata
.vpos
[index
] = None
231 if v
< -self
.epsilon
or v
> 1+self
.epsilon
:
232 sharedata
.vposvalid
= 0
233 sharedata
.vpos
[index
] = v
236 registerdefaultprovider(pos(), pos
.providesdata
)
241 providesdata
= ["vrange", "vrangemissing", "vrangeminmissing", "vrangemaxmissing"]
251 def __init__(self
, usenames
={}, epsilon
=1e-10):
252 self
.usenames
= usenames
253 self
.epsilon
= epsilon
255 def _numberofbits(self
, mask
):
259 return self
._numberofbits
(mask
>> 1) + 1
261 return self
._numberofbits
(mask
>> 1)
263 def columnnames(self
, privatedata
, sharedata
, graph
, columnnames
, dataaxisnames
):
265 privatedata
.rangeposcolumns
= []
266 sharedata
.vrangemissing
= []
267 sharedata
.vrangeminmissing
= []
268 sharedata
.vrangemaxmissing
= []
269 privatedata
.rangeposdeltacolumns
= {} # temporarily used by adjustaxis only
270 for count
, axisnames
in enumerate(graph
.axesnames
):
271 for axisname
in axisnames
:
273 usename
= self
.usenames
[axisname
]
277 for columnname
in columnnames
:
279 if usename
== columnname
:
280 mask
+= self
.mask_value
281 elif usename
+ "min" == columnname
:
282 mask
+= self
.mask_min
283 elif usename
+ "max" == columnname
:
284 mask
+= self
.mask_max
285 elif "d" + usename
+ "min" == columnname
:
286 mask
+= self
.mask_dmin
287 elif "d" + usename
+ "max" == columnname
:
288 mask
+= self
.mask_dmax
289 elif "d" + usename
== columnname
:
294 usecolumns
.append(columnname
)
295 dataaxisnames
[columnname
] = axisname
296 if mask
& (self
.mask_min | self
.mask_max | self
.mask_dmin | self
.mask_dmax | self
.mask_d
):
297 if (self
._numberofbits
(mask
& (self
.mask_min | self
.mask_dmin | self
.mask_d
)) > 1 or
298 self
._numberofbits
(mask
& (self
.mask_max | self
.mask_dmax | self
.mask_d
)) > 1):
299 raise ValueError("multiple range definition")
300 if mask
& (self
.mask_dmin | self
.mask_dmax | self
.mask_d
):
301 if not (mask
& self
.mask_value
):
302 raise ValueError("missing value for delta")
303 privatedata
.rangeposdeltacolumns
[axisname
] = {}
304 privatedata
.rangeposcolumns
.append((axisname
, usename
, mask
))
305 elif mask
== self
.mask_value
:
306 usecolumns
= usecolumns
[:-1]
307 if len(privatedata
.rangeposcolumns
) + len(sharedata
.vrangemissing
) > count
+1:
308 raise ValueError("multiple axes per graph dimension")
309 elif len(privatedata
.rangeposcolumns
) + len(sharedata
.vrangemissing
) < count
+1:
310 sharedata
.vrangemissing
.append(count
)
311 sharedata
.vrangeminmissing
.append(count
)
312 sharedata
.vrangemaxmissing
.append(count
)
314 if not (privatedata
.rangeposcolumns
[-1][2] & (self
.mask_min | self
.mask_dmin | self
.mask_d
)):
315 sharedata
.vrangeminmissing
.append(count
)
316 if not (privatedata
.rangeposcolumns
[-1][2] & (self
.mask_max | self
.mask_dmax | self
.mask_d
)):
317 sharedata
.vrangemaxmissing
.append(count
)
320 def adjustaxis(self
, privatedata
, sharedata
, graph
, plotitem
, columnname
, data
):
321 for axisname
, usename
, mask
in privatedata
.rangeposcolumns
:
322 if columnname
== usename
+ "min" and mask
& self
.mask_min
:
323 graph
.axes
[axisname
].adjustaxis(data
)
324 if columnname
== usename
+ "max" and mask
& self
.mask_max
:
325 graph
.axes
[axisname
].adjustaxis(data
)
327 # delta handling: fill rangeposdeltacolumns
328 for axisname
, usename
, mask
in privatedata
.rangeposcolumns
:
329 if columnname
== usename
and mask
& (self
.mask_dmin | self
.mask_dmax | self
.mask_d
):
330 privatedata
.rangeposdeltacolumns
[axisname
][self
.mask_value
] = data
331 if columnname
== "d" + usename
+ "min" and mask
& self
.mask_dmin
:
332 privatedata
.rangeposdeltacolumns
[axisname
][self
.mask_dmin
] = data
333 if columnname
== "d" + usename
+ "max" and mask
& self
.mask_dmax
:
334 privatedata
.rangeposdeltacolumns
[axisname
][self
.mask_dmax
] = data
335 if columnname
== "d" + usename
and mask
& self
.mask_d
:
336 privatedata
.rangeposdeltacolumns
[axisname
][self
.mask_d
] = data
338 # delta handling: process rangeposdeltacolumns
339 for a
, d
in privatedata
.rangeposdeltacolumns
.items():
340 if d
.has_key(self
.mask_value
):
342 if k
!= self
.mask_value
:
343 if k
& (self
.mask_dmin | self
.mask_d
):
345 for value
, delta
in zip(d
[self
.mask_value
], d
[k
]):
347 mindata
.append(value
-delta
)
350 graph
.axes
[a
].adjustaxis(mindata
)
351 if k
& (self
.mask_dmax | self
.mask_d
):
353 for value
, delta
in zip(d
[self
.mask_value
], d
[k
]):
355 maxdata
.append(value
+delta
)
358 graph
.axes
[a
].adjustaxis(maxdata
)
361 def initdrawpoints(self
, privatedata
, sharedata
, graph
):
362 sharedata
.vrange
= [[None for x
in xrange(2)] for y
in privatedata
.rangeposcolumns
+ sharedata
.vrangemissing
]
363 privatedata
.rangepostmplist
= [[usename
, mask
, index
, graph
.axes
[axisname
]] # temporarily used by drawpoint only
364 for index
, (axisname
, usename
, mask
) in enumerate(privatedata
.rangeposcolumns
)]
365 for missing
in sharedata
.vrangemissing
:
366 for rangepostmp
in privatedata
.rangepostmplist
:
367 if rangepostmp
[2] >= missing
:
370 def drawpoint(self
, privatedata
, sharedata
, graph
, point
):
371 for usename
, mask
, index
, axis
in privatedata
.rangepostmplist
:
373 if mask
& self
.mask_min
:
374 sharedata
.vrange
[index
][0] = axis
.convert(point
[usename
+ "min"])
375 if mask
& self
.mask_dmin
:
376 sharedata
.vrange
[index
][0] = axis
.convert(point
[usename
] - point
["d" + usename
+ "min"])
377 if mask
& self
.mask_d
:
378 sharedata
.vrange
[index
][0] = axis
.convert(point
[usename
] - point
["d" + usename
])
379 except (ArithmeticError, ValueError, TypeError):
380 sharedata
.vrange
[index
][0] = None
382 if mask
& self
.mask_max
:
383 sharedata
.vrange
[index
][1] = axis
.convert(point
[usename
+ "max"])
384 if mask
& self
.mask_dmax
:
385 sharedata
.vrange
[index
][1] = axis
.convert(point
[usename
] + point
["d" + usename
+ "max"])
386 if mask
& self
.mask_d
:
387 sharedata
.vrange
[index
][1] = axis
.convert(point
[usename
] + point
["d" + usename
])
388 except (ArithmeticError, ValueError, TypeError):
389 sharedata
.vrange
[index
][1] = None
391 # some range checks for data consistency
392 if (sharedata
.vrange
[index
][0] is not None and sharedata
.vrange
[index
][1] is not None and
393 sharedata
.vrange
[index
][0] > sharedata
.vrange
[index
][1] + self
.epsilon
):
394 raise ValueError("inverse range")
395 # disabled due to missing vpos access:
396 # if (sharedata.vrange[index][0] is not None and sharedata.vpos[index] is not None and
397 # sharedata.vrange[index][0] > sharedata.vpos[index] + self.epsilon):
398 # raise ValueError("negative minimum errorbar")
399 # if (sharedata.vrange[index][1] is not None and sharedata.vpos[index] is not None and
400 # sharedata.vrange[index][1] < sharedata.vpos[index] - self.epsilon):
401 # raise ValueError("negative maximum errorbar")
404 registerdefaultprovider(range(), range.providesdata
)
407 def _crosssymbol(c
, x_pt
, y_pt
, size_pt
, attrs
):
408 c
.draw(path
.path(path
.moveto_pt(x_pt
-0.5*size_pt
, y_pt
-0.5*size_pt
),
409 path
.lineto_pt(x_pt
+0.5*size_pt
, y_pt
+0.5*size_pt
),
410 path
.moveto_pt(x_pt
-0.5*size_pt
, y_pt
+0.5*size_pt
),
411 path
.lineto_pt(x_pt
+0.5*size_pt
, y_pt
-0.5*size_pt
)), attrs
)
413 def _plussymbol(c
, x_pt
, y_pt
, size_pt
, attrs
):
414 c
.draw(path
.path(path
.moveto_pt(x_pt
-0.707106781*size_pt
, y_pt
),
415 path
.lineto_pt(x_pt
+0.707106781*size_pt
, y_pt
),
416 path
.moveto_pt(x_pt
, y_pt
-0.707106781*size_pt
),
417 path
.lineto_pt(x_pt
, y_pt
+0.707106781*size_pt
)), attrs
)
419 def _squaresymbol(c
, x_pt
, y_pt
, size_pt
, attrs
):
420 c
.draw(path
.path(path
.moveto_pt(x_pt
-0.5*size_pt
, y_pt
-0.5*size_pt
),
421 path
.lineto_pt(x_pt
+0.5*size_pt
, y_pt
-0.5*size_pt
),
422 path
.lineto_pt(x_pt
+0.5*size_pt
, y_pt
+0.5*size_pt
),
423 path
.lineto_pt(x_pt
-0.5*size_pt
, y_pt
+0.5*size_pt
),
424 path
.closepath()), attrs
)
426 def _trianglesymbol(c
, x_pt
, y_pt
, size_pt
, attrs
):
427 c
.draw(path
.path(path
.moveto_pt(x_pt
-0.759835685*size_pt
, y_pt
-0.438691337*size_pt
),
428 path
.lineto_pt(x_pt
+0.759835685*size_pt
, y_pt
-0.438691337*size_pt
),
429 path
.lineto_pt(x_pt
, y_pt
+0.877382675*size_pt
),
430 path
.closepath()), attrs
)
432 def _circlesymbol(c
, x_pt
, y_pt
, size_pt
, attrs
):
433 c
.draw(path
.path(path
.arc_pt(x_pt
, y_pt
, 0.564189583*size_pt
, 0, 360),
434 path
.closepath()), attrs
)
436 def _diamondsymbol(c
, x_pt
, y_pt
, size_pt
, attrs
):
437 c
.draw(path
.path(path
.moveto_pt(x_pt
-0.537284965*size_pt
, y_pt
),
438 path
.lineto_pt(x_pt
, y_pt
-0.930604859*size_pt
),
439 path
.lineto_pt(x_pt
+0.537284965*size_pt
, y_pt
),
440 path
.lineto_pt(x_pt
, y_pt
+0.930604859*size_pt
),
441 path
.closepath()), attrs
)
444 class _styleneedingpointpos(_style
):
446 needsdata
= ["vposmissing"]
448 def columnnames(self
, privatedata
, sharedata
, graph
, columnnames
, dataaxisnames
):
449 if len(sharedata
.vposmissing
):
450 raise ValueError("incomplete position information")
454 class symbol(_styleneedingpointpos
):
456 needsdata
= ["vpos", "vposmissing", "vposvalid"]
458 # "inject" the predefinied symbols into the class:
460 # Note, that statements like cross = _crosssymbol are
461 # invalid, since the would lead to unbound methods, but
462 # a single entry changeable list does the trick.
464 # Once we require Python 2.2+ we should use staticmethods
465 # to implement the default symbols inplace.
467 cross
= attr
.changelist([_crosssymbol
])
468 plus
= attr
.changelist([_plussymbol
])
469 square
= attr
.changelist([_squaresymbol
])
470 triangle
= attr
.changelist([_trianglesymbol
])
471 circle
= attr
.changelist([_circlesymbol
])
472 diamond
= attr
.changelist([_diamondsymbol
])
474 changecross
= attr
.changelist([_crosssymbol
, _plussymbol
, _squaresymbol
, _trianglesymbol
, _circlesymbol
, _diamondsymbol
])
475 changeplus
= attr
.changelist([_plussymbol
, _squaresymbol
, _trianglesymbol
, _circlesymbol
, _diamondsymbol
, _crosssymbol
])
476 changesquare
= attr
.changelist([_squaresymbol
, _trianglesymbol
, _circlesymbol
, _diamondsymbol
, _crosssymbol
, _plussymbol
])
477 changetriangle
= attr
.changelist([_trianglesymbol
, _circlesymbol
, _diamondsymbol
, _crosssymbol
, _plussymbol
, _squaresymbol
])
478 changecircle
= attr
.changelist([_circlesymbol
, _diamondsymbol
, _crosssymbol
, _plussymbol
, _squaresymbol
, _trianglesymbol
])
479 changediamond
= attr
.changelist([_diamondsymbol
, _crosssymbol
, _plussymbol
, _squaresymbol
, _trianglesymbol
, _circlesymbol
])
480 changesquaretwice
= attr
.changelist([_squaresymbol
, _squaresymbol
, _trianglesymbol
, _trianglesymbol
, _circlesymbol
, _circlesymbol
, _diamondsymbol
, _diamondsymbol
])
481 changetriangletwice
= attr
.changelist([_trianglesymbol
, _trianglesymbol
, _circlesymbol
, _circlesymbol
, _diamondsymbol
, _diamondsymbol
, _squaresymbol
, _squaresymbol
])
482 changecircletwice
= attr
.changelist([_circlesymbol
, _circlesymbol
, _diamondsymbol
, _diamondsymbol
, _squaresymbol
, _squaresymbol
, _trianglesymbol
, _trianglesymbol
])
483 changediamondtwice
= attr
.changelist([_diamondsymbol
, _diamondsymbol
, _squaresymbol
, _squaresymbol
, _trianglesymbol
, _trianglesymbol
, _circlesymbol
, _circlesymbol
])
485 changestrokedfilled
= attr
.changelist([deco
.stroked
, deco
.filled
])
486 changefilledstroked
= attr
.changelist([deco
.filled
, deco
.stroked
])
488 defaultsymbolattrs
= [deco
.stroked
]
490 def __init__(self
, symbol
=changecross
, size
=0.2*unit
.v_cm
, symbolattrs
=[]):
493 self
.symbolattrs
= symbolattrs
495 def selectstyle(self
, privatedata
, sharedata
, graph
, selectindex
, selecttotal
):
496 privatedata
.symbol
= attr
.selectattr(self
.symbol
, selectindex
, selecttotal
)
497 privatedata
.size_pt
= unit
.topt(attr
.selectattr(self
.size
, selectindex
, selecttotal
))
498 if self
.symbolattrs
is not None:
499 privatedata
.symbolattrs
= attr
.selectattrs(self
.defaultsymbolattrs
+ self
.symbolattrs
, selectindex
, selecttotal
)
501 privatedata
.symbolattrs
= None
503 def initdrawpoints(self
, privatedata
, sharedata
, graph
):
504 privatedata
.symbolcanvas
= canvas
.canvas()
506 def drawpoint(self
, privatedata
, sharedata
, graph
, point
):
507 if sharedata
.vposvalid
and privatedata
.symbolattrs
is not None:
508 x_pt
, y_pt
= graph
.vpos_pt(*sharedata
.vpos
)
509 privatedata
.symbol(privatedata
.symbolcanvas
, x_pt
, y_pt
, privatedata
.size_pt
, privatedata
.symbolattrs
)
511 def donedrawpoints(self
, privatedata
, sharedata
, graph
):
512 graph
.layer("data").insert(privatedata
.symbolcanvas
)
514 def key_pt(self
, privatedata
, sharedata
, graph
, x_pt
, y_pt
, width_pt
, height_pt
):
515 if privatedata
.symbolattrs
is not None:
516 privatedata
.symbol(graph
, x_pt
+0.5*width_pt
, y_pt
+0.5*height_pt
, privatedata
.size_pt
, privatedata
.symbolattrs
)
519 class _line(_styleneedingpointpos
):
521 # this style is not a complete style, but it provides the basic functionality to
522 # create a line, which is cut at the graph boundaries (or at otherwise invalid points)
524 def initpointstopath(self
, privatedata
):
525 privatedata
.path
= path
.path()
526 privatedata
.linebasepoints
= []
527 privatedata
.lastvpos
= None
529 def addpointstopath(self
, privatedata
):
530 # add baselinepoints to privatedata.path
531 if len(privatedata
.linebasepoints
) > 1:
532 privatedata
.path
.append(path
.moveto_pt(*privatedata
.linebasepoints
[0]))
533 if len(privatedata
.linebasepoints
) > 2:
534 privatedata
.path
.append(path
.multilineto_pt(privatedata
.linebasepoints
[1:]))
536 privatedata
.path
.append(path
.lineto_pt(*privatedata
.linebasepoints
[1]))
537 privatedata
.linebasepoints
= []
539 def addpoint(self
, privatedata
, graphvpos_pt
, vposavailable
, vposvalid
, vpos
):
540 # append linebasepoints
542 if len(privatedata
.linebasepoints
):
543 # the last point was inside the graph
544 if vposvalid
: # shortcut for the common case
545 privatedata
.linebasepoints
.append(graphvpos_pt(*vpos
))
549 for vstart
, vend
in zip(privatedata
.lastvpos
, vpos
):
552 # 1 = vstart + (vend - vstart) * cut
554 newcut
= (1 - vstart
)/(vend
- vstart
)
555 except (ArithmeticError, TypeError):
558 # 0 = vstart + (vend - vstart) * cut
560 newcut
= - vstart
/(vend
- vstart
)
561 except (ArithmeticError, TypeError):
563 if newcut
is not None and newcut
< cut
:
567 for vstart
, vend
in zip(privatedata
.lastvpos
, vpos
):
568 cutvpos
.append(vstart
+ (vend
- vstart
) * cut
)
569 privatedata
.linebasepoints
.append(graphvpos_pt(*cutvpos
))
570 self
.addpointstopath(privatedata
)
572 # the last point was outside the graph
573 if privatedata
.lastvpos
is not None:
577 for vstart
, vend
in zip(privatedata
.lastvpos
, vpos
):
580 # 1 = vstart + (vend - vstart) * cut
582 newcut
= (1 - vstart
)/(vend
- vstart
)
583 except (ArithmeticError, TypeError):
586 # 0 = vstart + (vend - vstart) * cut
588 newcut
= - vstart
/(vend
- vstart
)
589 except (ArithmeticError, TypeError):
591 if newcut
is not None and newcut
> cut
:
595 for vstart
, vend
in zip(privatedata
.lastvpos
, vpos
):
596 cutvpos
.append(vstart
+ (vend
- vstart
) * cut
)
597 privatedata
.linebasepoints
.append(graphvpos_pt(*cutvpos
))
598 privatedata
.linebasepoints
.append(graphvpos_pt(*vpos
))
600 # sometimes cut beginning and end
603 for vstart
, vend
in zip(privatedata
.lastvpos
, vpos
):
608 # 1 = vstart + (vend - vstart) * cutfrom
610 newcutfrom
= (1 - vstart
)/(vend
- vstart
)
611 except (ArithmeticError, TypeError):
616 # 0 = vstart + (vend - vstart) * cutfrom
618 newcutfrom
= - vstart
/(vend
- vstart
)
619 except (ArithmeticError, TypeError):
621 if newcutfrom
is not None and newcutfrom
> cutfrom
:
625 # 1 = vstart + (vend - vstart) * cutto
627 newcutto
= (1 - vstart
)/(vend
- vstart
)
628 except (ArithmeticError, TypeError):
631 # 0 = vstart + (vend - vstart) * cutto
633 newcutto
= - vstart
/(vend
- vstart
)
634 except (ArithmeticError, TypeError):
636 if newcutto
is not None and newcutto
< cutto
:
642 for vstart
, vend
in zip(privatedata
.lastvpos
, vpos
):
643 cutfromvpos
.append(vstart
+ (vend
- vstart
) * cutfrom
)
644 cuttovpos
.append(vstart
+ (vend
- vstart
) * cutto
)
645 privatedata
.linebasepoints
.append(graphvpos_pt(*cutfromvpos
))
646 privatedata
.linebasepoints
.append(graphvpos_pt(*cuttovpos
))
647 self
.addpointstopath(privatedata
)
648 privatedata
.lastvpos
= vpos
[:]
650 if len(privatedata
.linebasepoints
) > 1:
651 self
.addpointstopath(privatedata
)
652 privatedata
.lastvpos
= None
654 def addinvalid(self
, privatedata
):
655 if len(privatedata
.linebasepoints
) > 1:
656 self
.addpointstopath(privatedata
)
657 privatedata
.lastvpos
= None
659 def donepointstopath(self
, privatedata
):
660 if len(privatedata
.linebasepoints
) > 1:
661 self
.addpointstopath(privatedata
)
662 return privatedata
.path
667 needsdata
= ["vpos", "vposmissing", "vposavailable", "vposvalid"]
669 changelinestyle
= attr
.changelist([style
.linestyle
.solid
,
670 style
.linestyle
.dashed
,
671 style
.linestyle
.dotted
,
672 style
.linestyle
.dashdotted
])
674 defaultlineattrs
= [changelinestyle
]
676 def __init__(self
, lineattrs
=[]):
677 self
.lineattrs
= lineattrs
679 def selectstyle(self
, privatedata
, sharedata
, graph
, selectindex
, selecttotal
):
680 if self
.lineattrs
is not None:
681 privatedata
.lineattrs
= attr
.selectattrs(self
.defaultlineattrs
+ self
.lineattrs
, selectindex
, selecttotal
)
683 privatedata
.lineattrs
= None
685 def initdrawpoints(self
, privatedata
, sharedata
, graph
):
686 self
.initpointstopath(privatedata
)
688 def drawpoint(self
, privatedata
, sharedata
, graph
, point
):
689 self
.addpoint(privatedata
, graph
.vpos_pt
, sharedata
.vposavailable
, sharedata
.vposvalid
, sharedata
.vpos
)
691 def donedrawpoints(self
, privatedata
, sharedata
, graph
):
692 path
= self
.donepointstopath(privatedata
)
693 if privatedata
.lineattrs
is not None and len(path
):
694 graph
.layer("data").stroke(path
, privatedata
.lineattrs
)
696 def key_pt(self
, privatedata
, sharedata
, graph
, x_pt
, y_pt
, width_pt
, height_pt
):
697 if privatedata
.lineattrs
is not None:
698 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
)
701 class impulses(_styleneedingpointpos
):
703 needsdata
= ["vpos", "vposmissing", "vposavailable", "vposvalid", "poscolumnnames"]
705 defaultlineattrs
= [line
.changelinestyle
]
706 defaultfrompathattrs
= []
708 def __init__(self
, lineattrs
=[], fromvalue
=0, frompathattrs
=[], valueaxisindex
=1):
709 self
.lineattrs
= lineattrs
710 self
.fromvalue
= fromvalue
711 self
.frompathattrs
= frompathattrs
712 self
.valueaxisindex
= valueaxisindex
714 def selectstyle(self
, privatedata
, sharedata
, graph
, selectindex
, selecttotal
):
715 privatedata
.insertfrompath
= selectindex
== 0
716 if self
.lineattrs
is not None:
717 privatedata
.lineattrs
= attr
.selectattrs(self
.defaultlineattrs
+ self
.lineattrs
, selectindex
, selecttotal
)
719 privatedata
.lineattrs
= None
721 def adjustaxis(self
, privatedata
, sharedata
, graph
, plotitem
, columnname
, data
):
722 if self
.fromvalue
is not None:
724 i
= sharedata
.poscolumnnames
.index(columnname
)
728 if i
== self
.valueaxisindex
:
729 graph
.axes
[sharedata
.poscolumnnames
[i
]].adjustaxis([self
.fromvalue
])
731 def initdrawpoints(self
, privatedata
, sharedata
, graph
):
732 privatedata
.impulsescanvas
= canvas
.canvas()
733 if self
.fromvalue
is not None:
734 valueaxisname
= sharedata
.poscolumnnames
[self
.valueaxisindex
]
735 privatedata
.vfromvalue
= graph
.axes
[valueaxisname
].convert(self
.fromvalue
)
736 privatedata
.vfromvaluecut
= 0
737 if privatedata
.vfromvalue
< 0:
738 privatedata
.vfromvalue
= 0
739 if privatedata
.vfromvalue
> 1:
740 privatedata
.vfromvalue
= 1
741 if self
.frompathattrs
is not None and privatedata
.insertfrompath
:
742 graph
.layer("data").stroke(graph
.axes
[valueaxisname
].vgridpath(privatedata
.vfromvalue
),
743 self
.defaultfrompathattrs
+ self
.frompathattrs
)
745 privatedata
.vfromvalue
= 0
747 def drawpoint(self
, privatedata
, sharedata
, graph
, point
):
748 if sharedata
.vposvalid
and privatedata
.lineattrs
is not None:
749 vpos
= sharedata
.vpos
[:]
750 vpos
[self
.valueaxisindex
] = privatedata
.vfromvalue
751 privatedata
.impulsescanvas
.stroke(graph
.vgeodesic(*(vpos
+ sharedata
.vpos
)), privatedata
.lineattrs
)
753 def donedrawpoints(self
, privatedata
, sharedata
, graph
):
754 graph
.layer("data").insert(privatedata
.impulsescanvas
)
756 def key_pt(self
, privatedata
, sharedata
, graph
, x_pt
, y_pt
, width_pt
, height_pt
):
757 if privatedata
.lineattrs
is not None:
758 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
)
761 class errorbar(_style
):
763 needsdata
= ["vpos", "vposmissing", "vposavailable", "vposvalid", "vrange", "vrangeminmissing", "vrangemaxmissing"]
765 defaulterrorbarattrs
= []
767 def __init__(self
, size
=0.1*unit
.v_cm
,
771 self
.errorbarattrs
= errorbarattrs
772 self
.epsilon
= epsilon
774 def columnnames(self
, privatedata
, sharedata
, graph
, columnnames
, dataaxisnames
):
775 for i
in sharedata
.vposmissing
:
776 if i
in sharedata
.vrangeminmissing
and i
in sharedata
.vrangemaxmissing
:
777 raise ValueError("position and range for a graph dimension missing")
780 def selectstyle(self
, privatedata
, sharedata
, graph
, selectindex
, selecttotal
):
781 privatedata
.errorsize_pt
= unit
.topt(attr
.selectattr(self
.size
, selectindex
, selecttotal
))
782 privatedata
.errorbarattrs
= attr
.selectattrs(self
.defaulterrorbarattrs
+ self
.errorbarattrs
, selectindex
, selecttotal
)
784 def initdrawpoints(self
, privatedata
, sharedata
, graph
):
785 if privatedata
.errorbarattrs
is not None:
786 privatedata
.errorbarcanvas
= canvas
.canvas(privatedata
.errorbarattrs
)
787 privatedata
.dimensionlist
= list(xrange(len(sharedata
.vpos
)))
789 def drawpoint(self
, privatedata
, sharedata
, graph
, point
):
790 if privatedata
.errorbarattrs
is not None:
791 for i
in privatedata
.dimensionlist
:
792 for j
in privatedata
.dimensionlist
:
794 (sharedata
.vpos
[j
] is None or
795 sharedata
.vpos
[j
] < -self
.epsilon
or
796 sharedata
.vpos
[j
] > 1+self
.epsilon
)):
799 if ((sharedata
.vrange
[i
][0] is None and sharedata
.vpos
[i
] is None) or
800 (sharedata
.vrange
[i
][1] is None and sharedata
.vpos
[i
] is None) or
801 (sharedata
.vrange
[i
][0] is None and sharedata
.vrange
[i
][1] is None)):
803 vminpos
= sharedata
.vpos
[:]
804 if sharedata
.vrange
[i
][0] is not None:
805 vminpos
[i
] = sharedata
.vrange
[i
][0]
809 if vminpos
[i
] > 1+self
.epsilon
:
811 if vminpos
[i
] < -self
.epsilon
:
814 vmaxpos
= sharedata
.vpos
[:]
815 if sharedata
.vrange
[i
][1] is not None:
816 vmaxpos
[i
] = sharedata
.vrange
[i
][1]
820 if vmaxpos
[i
] < -self
.epsilon
:
822 if vmaxpos
[i
] > 1+self
.epsilon
:
825 privatedata
.errorbarcanvas
.stroke(graph
.vgeodesic(*(vminpos
+ vmaxpos
)))
826 for j
in privatedata
.dimensionlist
:
829 privatedata
.errorbarcanvas
.stroke(graph
.vcap_pt(j
, privatedata
.errorsize_pt
, *vminpos
))
831 privatedata
.errorbarcanvas
.stroke(graph
.vcap_pt(j
, privatedata
.errorsize_pt
, *vmaxpos
))
833 def donedrawpoints(self
, privatedata
, sharedata
, graph
):
834 if privatedata
.errorbarattrs
is not None:
835 graph
.layer("data").insert(privatedata
.errorbarcanvas
)
838 class text(_styleneedingpointpos
):
840 needsdata
= ["vpos", "vposmissing", "vposvalid"]
842 defaulttextattrs
= [textmodule
.halign
.center
, textmodule
.vshift
.mathaxis
]
844 def __init__(self
, textname
="text", dxname
=None, dyname
=None,
845 dxunit
=0.3*unit
.v_cm
, dyunit
=0.3*unit
.v_cm
,
846 textdx
=0*unit
.v_cm
, textdy
=0.3*unit
.v_cm
, textattrs
=[]):
847 self
.textname
= textname
854 self
.textattrs
= textattrs
856 def columnnames(self
, privatedata
, sharedata
, graph
, columnnames
, dataaxisnames
):
857 if self
.textname
not in columnnames
:
858 raise ValueError("column '%s' missing" % self
.textname
)
859 names
= [self
.textname
]
860 if self
.dxname
is not None:
861 if self
.dxname
not in columnnames
:
862 raise ValueError("column '%s' missing" % self
.dxname
)
863 names
.append(self
.dxname
)
864 if self
.dyname
is not None:
865 if self
.dyname
not in columnnames
:
866 raise ValueError("column '%s' missing" % self
.dyname
)
867 names
.append(self
.dyname
)
868 return names
+ _styleneedingpointpos
.columnnames(self
, privatedata
, sharedata
, graph
, columnnames
, dataaxisnames
)
870 def selectstyle(self
, privatedata
, sharedata
, graph
, selectindex
, selecttotal
):
871 if self
.textattrs
is not None:
872 privatedata
.textattrs
= attr
.selectattrs(self
.defaulttextattrs
+ self
.textattrs
, selectindex
, selecttotal
)
874 privatedata
.textattrs
= None
876 def initdrawpoints(self
, privatedata
, sharedata
, grap
):
877 if self
.dxname
is None:
878 privatedata
.textdx_pt
= unit
.topt(self
.textdx
)
880 privatedata
.dxunit_pt
= unit
.topt(self
.dxunit
)
881 if self
.dyname
is None:
882 privatedata
.textdy_pt
= unit
.topt(self
.textdy
)
884 privatedata
.dyunit_pt
= unit
.topt(self
.dyunit
)
886 def drawpoint(self
, privatedata
, sharedata
, graph
, point
):
887 if privatedata
.textattrs
is not None and sharedata
.vposvalid
:
888 x_pt
, y_pt
= graph
.vpos_pt(*sharedata
.vpos
)
890 text
= str(point
[self
.textname
])
894 if self
.dxname
is None:
895 dx_pt
= privatedata
.textdx_pt
897 dx_pt
= float(point
[self
.dxname
]) * privatedata
.dxunit_pt
898 if self
.dyname
is None:
899 dy_pt
= privatedata
.textdy_pt
901 dy_pt
= float(point
[self
.dyname
]) * privatedata
.dyunit_pt
902 graph
.layer("data").text_pt(x_pt
+ dx_pt
, y_pt
+ dy_pt
, text
, privatedata
.textattrs
)
904 def key_pt(self
, privatedata
, sharedata
, graph
, x_pt
, y_pt
, width_pt
, height_pt
):
905 raise RuntimeError("Style currently doesn't provide a graph key")
908 class arrow(_styleneedingpointpos
):
910 needsdata
= ["vpos", "vposmissing", "vposvalid"]
912 defaultlineattrs
= []
913 defaultarrowattrs
= []
915 def __init__(self
, linelength
=0.25*unit
.v_cm
, arrowsize
=0.15*unit
.v_cm
, lineattrs
=[], arrowattrs
=[], arrowpos
=0.5, epsilon
=1e-5, decorator
=deco
.earrow
):
916 self
.linelength
= linelength
917 self
.arrowsize
= arrowsize
918 self
.lineattrs
= lineattrs
919 self
.arrowattrs
= arrowattrs
920 self
.arrowpos
= arrowpos
921 self
.epsilon
= epsilon
922 self
.decorator
= decorator
924 def columnnames(self
, privatedata
, sharedata
, graph
, columnnames
, dataaxisnames
):
925 if len(graph
.axesnames
) != 2:
926 raise ValueError("arrow style restricted on two-dimensional graphs")
927 if "size" not in columnnames
:
928 raise ValueError("size missing")
929 if "angle" not in columnnames
:
930 raise ValueError("angle missing")
931 return ["size", "angle"] + _styleneedingpointpos
.columnnames(self
, privatedata
, sharedata
, graph
, columnnames
, dataaxisnames
)
933 def selectstyle(self
, privatedata
, sharedata
, graph
, selectindex
, selecttotal
):
934 if self
.lineattrs
is not None:
935 privatedata
.lineattrs
= attr
.selectattrs(self
.defaultlineattrs
+ self
.lineattrs
, selectindex
, selecttotal
)
937 privatedata
.lineattrs
= None
938 if self
.arrowattrs
is not None:
939 privatedata
.arrowattrs
= attr
.selectattrs(self
.defaultarrowattrs
+ self
.arrowattrs
, selectindex
, selecttotal
)
941 privatedata
.arrowattrs
= None
943 def initdrawpoints(self
, privatedata
, sharedata
, graph
):
944 privatedata
.arrowcanvas
= canvas
.canvas()
946 def drawpoint(self
, privatedata
, sharedata
, graph
, point
):
947 if privatedata
.lineattrs
is not None and privatedata
.arrowattrs
is not None and sharedata
.vposvalid
:
948 linelength_pt
= unit
.topt(self
.linelength
)
949 x_pt
, y_pt
= graph
.vpos_pt(*sharedata
.vpos
)
951 angle
= point
["angle"] + 0.0
952 size
= point
["size"] + 0.0
956 if point
["size"] > self
.epsilon
:
957 dx
= math
.cos(angle
*math
.pi
/180)
958 dy
= math
.sin(angle
*math
.pi
/180)
959 x1
= x_pt
-self
.arrowpos
*dx
*linelength_pt
*size
960 y1
= y_pt
-self
.arrowpos
*dy
*linelength_pt
*size
961 x2
= x_pt
+(1-self
.arrowpos
)*dx
*linelength_pt
*size
962 y2
= y_pt
+(1-self
.arrowpos
)*dy
*linelength_pt
*size
964 privatedata
.arrowcanvas
.stroke(path
.line_pt(x1
, y1
, x2
, y2
),
965 privatedata
.lineattrs
+[self
.decorator(privatedata
.arrowattrs
, size
=self
.arrowsize
*size
)])
967 privatedata
.arrowcanvas
.stroke(path
.line_pt(x1
, y1
, x2
, y2
), privatedata
.lineattrs
)
969 def donedrawpoints(self
, privatedata
, sharedata
, graph
):
970 graph
.layer("data").insert(privatedata
.arrowcanvas
)
972 def key_pt(self
, privatedata
, sharedata
, graph
, x_pt
, y_pt
, width_pt
, height_pt
):
973 raise RuntimeError("Style currently doesn't provide a graph key")
976 class rect(_keygraphstyle
):
978 needsdata
= ["vrange", "vrangeminmissing", "vrangemaxmissing"]
980 def columnnames(self
, privatedata
, sharedata
, graph
, columnnames
, dataaxisnames
):
981 if len(graph
.axesnames
) != 2:
982 raise TypeError("rect style restricted on two-dimensional graphs")
983 if len(sharedata
.vrangeminmissing
) + len(sharedata
.vrangemaxmissing
):
984 raise ValueError("incomplete range")
985 return _keygraphstyle
.columnnames(self
, privatedata
, sharedata
, graph
, columnnames
, dataaxisnames
)
987 def initdrawpoints(self
, privatedata
, sharedata
, graph
):
988 privatedata
.rectcanvas
= graph
.layer("filldata").insert(canvas
.canvas())
990 def drawpoint(self
, privatedata
, sharedata
, graph
, point
):
991 xvmin
= sharedata
.vrange
[0][0]
992 xvmax
= sharedata
.vrange
[0][1]
993 yvmin
= sharedata
.vrange
[1][0]
994 yvmax
= sharedata
.vrange
[1][1]
995 if (xvmin
is not None and xvmin
< 1 and
996 xvmax
is not None and xvmax
> 0 and
997 yvmin
is not None and yvmin
< 1 and
998 yvmax
is not None and yvmax
> 0):
1007 p
= graph
.vgeodesic(xvmin
, yvmin
, xvmax
, yvmin
)
1008 p
.append(graph
.vgeodesic_el(xvmax
, yvmin
, xvmax
, yvmax
))
1009 p
.append(graph
.vgeodesic_el(xvmax
, yvmax
, xvmin
, yvmax
))
1010 p
.append(graph
.vgeodesic_el(xvmin
, yvmax
, xvmin
, yvmin
))
1011 p
.append(path
.closepath())
1012 privatedata
.rectcanvas
.fill(p
, [self
.color(privatedata
, point
["color"])])
1015 class histogram(_style
):
1017 needsdata
= ["vpos", "vposmissing", "vrange", "vrangeminmissing", "vrangemaxmissing"]
1019 defaultlineattrs
= [deco
.stroked
]
1020 defaultfrompathattrs
= []
1022 def __init__(self
, lineattrs
=[], steps
=0, fromvalue
=0, frompathattrs
=[], fillable
=0, rectkey
=0,
1023 autohistogramaxisindex
=0, autohistogrampointpos
=0.5, epsilon
=1e-10):
1024 self
.lineattrs
= lineattrs
1026 self
.fromvalue
= fromvalue
1027 self
.frompathattrs
= frompathattrs
1028 self
.fillable
= fillable
# TODO: fillable paths might not properly be closed by straight lines on curved graph geometries
1029 self
.rectkey
= rectkey
1030 self
.autohistogramaxisindex
= autohistogramaxisindex
1031 self
.autohistogrampointpos
= autohistogrampointpos
1032 self
.epsilon
= epsilon
1034 def columnnames(self
, privatedata
, sharedata
, graph
, columnnames
, dataaxisnames
):
1035 if len(graph
.axesnames
) != 2:
1036 raise TypeError("histogram style restricted on two-dimensional graphs")
1037 privatedata
.rangeaxisindex
= None
1038 for i
in builtinrange(len(graph
.axesnames
)):
1039 if i
in sharedata
.vrangeminmissing
or i
in sharedata
.vrangemaxmissing
:
1040 if i
in sharedata
.vposmissing
:
1041 raise ValueError("pos and range missing")
1043 if privatedata
.rangeaxisindex
is not None:
1044 raise ValueError("multiple ranges")
1045 privatedata
.rangeaxisindex
= i
1046 if privatedata
.rangeaxisindex
is None:
1047 privatedata
.rangeaxisindex
= self
.autohistogramaxisindex
1048 privatedata
.autohistogram
= 1
1050 privatedata
.autohistogram
= 0
1053 def adjustaxis(self
, privatedata
, sharedata
, graph
, plotitem
, columnname
, data
):
1054 if privatedata
.autohistogram
and columnname
== sharedata
.poscolumnnames
[privatedata
.rangeaxisindex
]:
1056 raise ValueError("several data points needed for automatic histogram width calculation")
1058 delta
= data
[1] - data
[0]
1059 min = data
[0] - self
.autohistogrampointpos
* delta
1060 max = data
[-1] + (1-self
.autohistogrampointpos
) * delta
1061 graph
.axes
[columnname
].adjustaxis([min, max])
1062 elif self
.fromvalue
is not None and columnname
== sharedata
.poscolumnnames
[1-privatedata
.rangeaxisindex
]:
1063 graph
.axes
[columnname
].adjustaxis([self
.fromvalue
])
1065 def selectstyle(self
, privatedata
, sharedata
, graph
, selectindex
, selecttotal
):
1066 privatedata
.insertfrompath
= selectindex
== 0
1067 if self
.lineattrs
is not None:
1068 privatedata
.lineattrs
= attr
.selectattrs(self
.defaultlineattrs
+ self
.lineattrs
, selectindex
, selecttotal
)
1070 privatedata
.lineattrs
= None
1072 def vmoveto(self
, privatedata
, sharedata
, graph
, vpos
, vvalue
):
1073 if -self
.epsilon
< vpos
< 1+self
.epsilon
and -self
.epsilon
< vvalue
< 1+self
.epsilon
:
1074 if privatedata
.rangeaxisindex
:
1075 privatedata
.path
.append(path
.moveto_pt(*graph
.vpos_pt(vvalue
, vpos
)))
1077 privatedata
.path
.append(path
.moveto_pt(*graph
.vpos_pt(vpos
, vvalue
)))
1079 def vposline(self
, privatedata
, sharedata
, graph
, vpos
, vvalue1
, vvalue2
):
1080 if -self
.epsilon
< vpos
< 1+self
.epsilon
:
1095 if abs(vvalue1cut
+ vvalue2cut
) <= 1:
1096 if vvalue1cut
and not self
.fillable
:
1097 if privatedata
.rangeaxisindex
:
1098 privatedata
.path
.append(path
.moveto_pt(*graph
.vpos_pt(vvalue1
, vpos
)))
1100 privatedata
.path
.append(path
.moveto_pt(*graph
.vpos_pt(vpos
, vvalue1
)))
1101 if privatedata
.rangeaxisindex
:
1102 privatedata
.path
.append(graph
.vgeodesic_el(vvalue1
, vpos
, vvalue2
, vpos
))
1104 privatedata
.path
.append(graph
.vgeodesic_el(vpos
, vvalue1
, vpos
, vvalue2
))
1106 def vvalueline(self
, privatedata
, sharedata
, graph
, vvalue
, vpos1
, vpos2
):
1108 if vvalue
< -self
.epsilon
:
1110 warnings
.warn("cut at graph boundary adds artificial lines to fillable step histogram path")
1111 if vvalue
> 1+self
.epsilon
:
1113 warnings
.warn("cut at graph boundary adds artificial lines to fillable step histogram path")
1114 if self
.fillable
or (-self
.epsilon
< vvalue
< 1+self
.epsilon
):
1129 if abs(vpos1cut
+ vpos2cut
) <= 1:
1132 if privatedata
.rangeaxisindex
:
1133 privatedata
.path
.append(path
.moveto_pt(*graph
.vpos_pt(privatedata
.vfromvalue
, vpos1
)))
1134 privatedata
.path
.append(graph
.vgeodesic_el(privatedata
.vfromvalue
, vpos1
, vvalue
, vpos1
))
1136 privatedata
.path
.append(path
.moveto_pt(*graph
.vpos_pt(vpos1
, privatedata
.vfromvalue
)))
1137 privatedata
.path
.append(graph
.vgeodesic_el(vpos1
, privatedata
.vfromvalue
, vpos1
, vvalue
))
1139 if privatedata
.rangeaxisindex
:
1140 privatedata
.path
.append(path
.moveto_pt(*graph
.vpos_pt(vvalue
, vpos1
)))
1142 privatedata
.path
.append(path
.moveto_pt(*graph
.vpos_pt(vpos1
, vvalue
)))
1143 if privatedata
.rangeaxisindex
:
1144 privatedata
.path
.append(graph
.vgeodesic_el(vvalue
, vpos1
, vvalue
, vpos2
))
1146 privatedata
.path
.append(graph
.vgeodesic_el(vpos1
, vvalue
, vpos2
, vvalue
))
1147 if self
.fillable
and vpos2cut
:
1148 warnings
.warn("cut at graph boundary adds artificial lines to fillable step histogram path")
1149 if privatedata
.rangeaxisindex
:
1150 privatedata
.path
.append(graph
.vgeodesic_el(vvalue
, vpos2
, privatedata
.vfromvalue
, vpos2
))
1152 privatedata
.path
.append(graph
.vgeodesic_el(vpos2
, vvalue
, vpos2
, privatedata
.vfromvalue
))
1154 def drawvalue(self
, privatedata
, sharedata
, graph
, vmin
, vmax
, vvalue
):
1155 currentvalid
= vmin
is not None and vmax
is not None and vvalue
is not None
1156 if self
.fillable
and not self
.steps
:
1157 if not currentvalid
:
1160 if vmin
< -self
.epsilon
:
1163 elif vmin
> 1+self
.epsilon
:
1167 if vmax
< -self
.epsilon
:
1170 if vmax
> 1+self
.epsilon
:
1174 if vvalue
< -self
.epsilon
:
1177 if vvalue
> 1+self
.epsilon
:
1181 if abs(vmincut
) + abs(vmaxcut
) + abs(vvaluecut
) + abs(privatedata
.vfromvaluecut
) > 1:
1182 if abs(vmincut
+ vmaxcut
) > 1 or abs(vvaluecut
+privatedata
.vfromvaluecut
) > 1:
1185 warnings
.warn("multiple cuts at graph boundary add artificial lines to fillable rectangle histogram path")
1188 if privatedata
.rangeaxisindex
:
1189 privatedata
.path
.append(path
.moveto_pt(*graph
.vpos_pt(privatedata
.vfromvalue
, vmin
)))
1190 privatedata
.path
.append(graph
.vgeodesic_el(privatedata
.vfromvalue
, vmin
, privatedata
.vfromvalue
, vmax
))
1191 privatedata
.path
.append(graph
.vgeodesic_el(privatedata
.vfromvalue
, vmax
, vvalue
, vmax
))
1192 privatedata
.path
.append(graph
.vgeodesic_el(vvalue
, vmax
, vvalue
, vmin
))
1194 privatedata
.path
.append(path
.moveto_pt(*graph
.vpos_pt(vmin
, privatedata
.vfromvalue
)))
1195 privatedata
.path
.append(graph
.vgeodesic_el(vmin
, privatedata
.vfromvalue
, vmax
, privatedata
.vfromvalue
))
1196 privatedata
.path
.append(graph
.vgeodesic_el(vmax
, privatedata
.vfromvalue
, vmax
, vvalue
))
1197 privatedata
.path
.append(graph
.vgeodesic_el(vmax
, vvalue
, vmin
, vvalue
))
1200 if privatedata
.rangeaxisindex
:
1201 privatedata
.path
.append(path
.moveto_pt(*graph
.vpos_pt(vvalue
, vmax
)))
1202 privatedata
.path
.append(graph
.vgeodesic_el(vvalue
, vmax
, vvalue
, vmin
))
1203 privatedata
.path
.append(graph
.vgeodesic_el(vvalue
, vmin
, privatedata
.vfromvalue
, vmin
))
1204 privatedata
.path
.append(graph
.vgeodesic_el(privatedata
.vfromvalue
, vmin
, privatedata
.vfromvalue
, vmax
))
1206 privatedata
.path
.append(path
.moveto_pt(*graph
.vpos_pt(vmax
, vvalue
)))
1207 privatedata
.path
.append(graph
.vgeodesic_el(vmax
, vvalue
, vmin
, vvalue
))
1208 privatedata
.path
.append(graph
.vgeodesic_el(vmin
, vvalue
, vmin
, privatedata
.vfromvalue
))
1209 privatedata
.path
.append(graph
.vgeodesic_el(vmin
, privatedata
.vfromvalue
, vmax
, privatedata
.vfromvalue
))
1210 elif privatedata
.vfromvaluecut
:
1212 if privatedata
.rangeaxisindex
:
1213 privatedata
.path
.append(path
.moveto_pt(*graph
.vpos_pt(privatedata
.vfromvalue
, vmax
)))
1214 privatedata
.path
.append(graph
.vgeodesic_el(privatedata
.vfromvalue
, vmax
, vvalue
, vmax
))
1215 privatedata
.path
.append(graph
.vgeodesic_el(vvalue
, vmax
, vvalue
, vmin
))
1216 privatedata
.path
.append(graph
.vgeodesic_el(vvalue
, vmin
, privatedata
.vfromvalue
, vmin
))
1218 privatedata
.path
.append(path
.moveto_pt(*graph
.vpos_pt(vmax
, privatedata
.vfromvalue
)))
1219 privatedata
.path
.append(graph
.vgeodesic_el(vmax
, privatedata
.vfromvalue
, vmax
, vvalue
))
1220 privatedata
.path
.append(graph
.vgeodesic_el(vmax
, vvalue
, vmin
, vvalue
))
1221 privatedata
.path
.append(graph
.vgeodesic_el(vmin
, vvalue
, vmin
, privatedata
.vfromvalue
))
1224 if privatedata
.rangeaxisindex
:
1225 privatedata
.path
.append(path
.moveto_pt(*graph
.vpos_pt(vvalue
, vmin
)))
1226 privatedata
.path
.append(graph
.vgeodesic_el(vvalue
, vmin
, privatedata
.vfromvalue
, vmin
))
1227 privatedata
.path
.append(graph
.vgeodesic_el(privatedata
.vfromvalue
, vmin
, privatedata
.vfromvalue
, vmax
))
1228 privatedata
.path
.append(graph
.vgeodesic_el(privatedata
.vfromvalue
, vmax
, vvalue
, vmax
))
1230 privatedata
.path
.append(path
.moveto_pt(*graph
.vpos_pt(vmin
, vvalue
)))
1231 privatedata
.path
.append(graph
.vgeodesic_el(vmin
, vvalue
, vmin
, privatedata
.vfromvalue
))
1232 privatedata
.path
.append(graph
.vgeodesic_el(vmin
, privatedata
.vfromvalue
, vmax
, privatedata
.vfromvalue
))
1233 privatedata
.path
.append(graph
.vgeodesic_el(vmax
, privatedata
.vfromvalue
, vmax
, vvalue
))
1235 if privatedata
.rangeaxisindex
:
1236 privatedata
.path
.append(path
.moveto_pt(*graph
.vpos_pt(privatedata
.vfromvalue
, vmin
)))
1237 privatedata
.path
.append(graph
.vgeodesic_el(privatedata
.vfromvalue
, vmin
, privatedata
.vfromvalue
, vmax
))
1238 privatedata
.path
.append(graph
.vgeodesic_el(privatedata
.vfromvalue
, vmax
, vvalue
, vmax
))
1239 privatedata
.path
.append(graph
.vgeodesic_el(vvalue
, vmax
, vvalue
, vmin
))
1240 privatedata
.path
.append(graph
.vgeodesic_el(vvalue
, vmin
, privatedata
.vfromvalue
, vmin
))
1241 privatedata
.path
.append(path
.closepath())
1243 privatedata
.path
.append(path
.moveto_pt(*graph
.vpos_pt(vmin
, privatedata
.vfromvalue
)))
1244 privatedata
.path
.append(graph
.vgeodesic_el(vmin
, privatedata
.vfromvalue
, vmax
, privatedata
.vfromvalue
))
1245 privatedata
.path
.append(graph
.vgeodesic_el(vmax
, privatedata
.vfromvalue
, vmax
, vvalue
))
1246 privatedata
.path
.append(graph
.vgeodesic_el(vmax
, vvalue
, vmin
, vvalue
))
1247 privatedata
.path
.append(graph
.vgeodesic_el(vmin
, vvalue
, vmin
, privatedata
.vfromvalue
))
1248 privatedata
.path
.append(path
.closepath())
1251 gap
= abs(vmin
- privatedata
.lastvmax
) > self
.epsilon
1252 except (ArithmeticError, ValueError, TypeError):
1254 if (privatedata
.lastvvalue
is not None and currentvalid
and not gap
and
1255 (self
.steps
or (privatedata
.lastvvalue
-privatedata
.vfromvalue
)*(vvalue
-privatedata
.vfromvalue
) < 0)):
1256 self
.vposline(privatedata
, sharedata
, graph
,
1257 vmin
, privatedata
.lastvvalue
, vvalue
)
1259 if privatedata
.lastvvalue
is not None and currentvalid
:
1260 currentbigger
= abs(privatedata
.lastvvalue
-privatedata
.vfromvalue
) < abs(vvalue
-privatedata
.vfromvalue
)
1261 if privatedata
.lastvvalue
is not None and (not currentvalid
or not currentbigger
or gap
):
1262 self
.vposline(privatedata
, sharedata
, graph
,
1263 privatedata
.lastvmax
, privatedata
.lastvvalue
, privatedata
.vfromvalue
)
1265 self
.vmoveto(privatedata
, sharedata
, graph
,
1267 if currentvalid
and (privatedata
.lastvvalue
is None or currentbigger
or gap
):
1268 self
.vmoveto(privatedata
, sharedata
, graph
,
1269 vmin
, privatedata
.vfromvalue
)
1270 self
.vposline(privatedata
, sharedata
, graph
,
1271 vmin
, privatedata
.vfromvalue
, vvalue
)
1273 self
.vvalueline(privatedata
, sharedata
, graph
,
1275 privatedata
.lastvvalue
= vvalue
1276 privatedata
.lastvmax
= vmax
1278 privatedata
.lastvvalue
= privatedata
.lastvmax
= None
1280 def initdrawpoints(self
, privatedata
, sharedata
, graph
):
1281 privatedata
.path
= path
.path()
1282 privatedata
.lastvvalue
= privatedata
.lastvmax
= None
1283 privatedata
.vcurrentpoint
= None
1284 privatedata
.count
= 0
1285 if self
.fromvalue
is not None:
1286 valueaxisname
= sharedata
.poscolumnnames
[1-privatedata
.rangeaxisindex
]
1287 privatedata
.vfromvalue
= graph
.axes
[valueaxisname
].convert(self
.fromvalue
)
1288 privatedata
.vfromvaluecut
= 0
1289 if privatedata
.vfromvalue
< 0:
1290 privatedata
.vfromvalue
= 0
1291 privatedata
.vfromvaluecut
= -1
1292 if privatedata
.vfromvalue
> 1:
1293 privatedata
.vfromvalue
= 1
1294 privatedata
.vfromvaluecut
= 1
1295 if self
.frompathattrs
is not None and privatedata
.insertfrompath
:
1296 graph
.layer("data").stroke(graph
.axes
[valueaxisname
].vgridpath(privatedata
.vfromvalue
),
1297 self
.defaultfrompathattrs
+ self
.frompathattrs
)
1299 privatedata
.vfromvalue
= 0
1301 def drawpoint(self
, privatedata
, sharedata
, graph
, point
):
1302 if privatedata
.autohistogram
:
1303 # automatic range handling
1304 privatedata
.count
+= 1
1305 if privatedata
.count
== 2:
1306 if privatedata
.rangeaxisindex
:
1307 privatedata
.vrange
= sharedata
.vpos
[1] - privatedata
.lastvpos
[1]
1308 self
.drawvalue(privatedata
, sharedata
, graph
,
1309 privatedata
.lastvpos
[1] - self
.autohistogrampointpos
*privatedata
.vrange
,
1310 privatedata
.lastvpos
[1] + (1-self
.autohistogrampointpos
)*privatedata
.vrange
,
1311 privatedata
.lastvpos
[0])
1313 privatedata
.vrange
= sharedata
.vpos
[0] - privatedata
.lastvpos
[0]
1314 self
.drawvalue(privatedata
, sharedata
, graph
,
1315 privatedata
.lastvpos
[0] - self
.autohistogrampointpos
*privatedata
.vrange
,
1316 privatedata
.lastvpos
[0] + (1-self
.autohistogrampointpos
)*privatedata
.vrange
,
1317 privatedata
.lastvpos
[1])
1318 elif privatedata
.count
> 2:
1319 if privatedata
.rangeaxisindex
:
1320 vrange
= sharedata
.vpos
[1] - privatedata
.lastvpos
[1]
1322 vrange
= sharedata
.vpos
[0] - privatedata
.lastvpos
[0]
1323 if abs(privatedata
.vrange
- vrange
) > self
.epsilon
:
1324 raise ValueError("equal steps (in graph coordinates) needed for automatic width calculation")
1325 if privatedata
.count
> 1:
1326 if privatedata
.rangeaxisindex
:
1327 self
.drawvalue(privatedata
, sharedata
, graph
,
1328 sharedata
.vpos
[1] - self
.autohistogrampointpos
*privatedata
.vrange
,
1329 sharedata
.vpos
[1] + (1-self
.autohistogrampointpos
)*privatedata
.vrange
,
1332 self
.drawvalue(privatedata
, sharedata
, graph
,
1333 sharedata
.vpos
[0] - self
.autohistogrampointpos
*privatedata
.vrange
,
1334 sharedata
.vpos
[0] + (1-self
.autohistogrampointpos
)*privatedata
.vrange
,
1336 privatedata
.lastvpos
= sharedata
.vpos
[:]
1338 if privatedata
.rangeaxisindex
:
1339 self
.drawvalue(privatedata
, sharedata
, graph
,
1340 sharedata
.vrange
[1][0], sharedata
.vrange
[1][1], sharedata
.vpos
[0])
1342 self
.drawvalue(privatedata
, sharedata
, graph
,
1343 sharedata
.vrange
[0][0], sharedata
.vrange
[0][1], sharedata
.vpos
[1])
1345 def donedrawpoints(self
, privatedata
, sharedata
, graph
):
1346 self
.drawvalue(privatedata
, sharedata
, graph
, None, None, None)
1347 if privatedata
.lineattrs
is not None and len(privatedata
.path
):
1348 graph
.layer("data").draw(privatedata
.path
, privatedata
.lineattrs
)
1350 def key_pt(self
, privatedata
, sharedata
, graph
, x_pt
, y_pt
, width_pt
, height_pt
):
1351 if privatedata
.lineattrs
is not None:
1353 p
= path
.rect_pt(x_pt
, y_pt
, width_pt
, height_pt
)
1355 p
= path
.line_pt(x_pt
, y_pt
+0.5*height_pt
, x_pt
+width_pt
, y_pt
+0.5*height_pt
)
1356 graph
.draw(p
, privatedata
.lineattrs
)
1359 class barpos(_style
):
1361 providesdata
= ["vpos", "vposmissing", "vposavailable", "vposvalid", "vbarrange", "barposcolumnnames", "barvalueindex", "lastbarvalue", "stackedbar", "stackedbardraw"]
1363 defaultfrompathattrs
= []
1365 def __init__(self
, fromvalue
=None, frompathattrs
=[], epsilon
=1e-10):
1366 self
.fromvalue
= fromvalue
1367 self
.frompathattrs
= frompathattrs
1368 self
.epsilon
= epsilon
1370 def columnnames(self
, privatedata
, sharedata
, graph
, columnnames
, dataaxisnames
):
1371 sharedata
.barposcolumnnames
= []
1372 sharedata
.barvalueindex
= None
1373 for dimension
, axisnames
in enumerate(graph
.axesnames
):
1375 for axisname
in axisnames
:
1376 if axisname
in columnnames
:
1377 if sharedata
.barvalueindex
is not None:
1378 raise ValueError("multiple values")
1379 sharedata
.barvalueindex
= dimension
1380 sharedata
.barposcolumnnames
.append(axisname
)
1382 if (axisname
+ "name") in columnnames
:
1383 sharedata
.barposcolumnnames
.append(axisname
+ "name")
1386 raise ValueError("multiple names and value")
1388 raise ValueError("value/name missing")
1389 if sharedata
.barvalueindex
is None:
1390 raise ValueError("missing value")
1391 sharedata
.vposmissing
= []
1392 return sharedata
.barposcolumnnames
1394 def addsubvalue(self
, value
, subvalue
):
1399 return value
[0], self
.addsubvalue(value
[1], subvalue
)
1401 return value
, subvalue
1403 return value
, subvalue
1405 def adjustaxis(self
, privatedata
, sharedata
, graph
, plotitem
, columnname
, data
):
1407 i
= sharedata
.barposcolumnnames
.index(columnname
)
1411 if i
== sharedata
.barvalueindex
:
1412 if self
.fromvalue
is not None:
1413 graph
.axes
[sharedata
.barposcolumnnames
[i
]].adjustaxis([self
.fromvalue
])
1414 graph
.axes
[sharedata
.barposcolumnnames
[i
]].adjustaxis(data
)
1416 graph
.axes
[sharedata
.barposcolumnnames
[i
][:-4]].adjustaxis([self
.addsubvalue(x
, 0) for x
in data
])
1417 graph
.axes
[sharedata
.barposcolumnnames
[i
][:-4]].adjustaxis([self
.addsubvalue(x
, 1) for x
in data
])
1419 def selectstyle(self
, privatedata
, sharedata
, graph
, selectindex
, selecttotal
):
1420 privatedata
.insertfrompath
= selectindex
== 0
1422 def initdrawpoints(self
, privatedata
, sharedata
, graph
):
1423 sharedata
.vpos
= [None]*(len(sharedata
.barposcolumnnames
))
1424 sharedata
.vbarrange
= [[None for i
in xrange(2)] for x
in sharedata
.barposcolumnnames
]
1425 sharedata
.stackedbar
= sharedata
.stackedbardraw
= 0
1427 if self
.fromvalue
is not None:
1428 privatedata
.vfromvalue
= graph
.axes
[sharedata
.barposcolumnnames
[sharedata
.barvalueindex
]].convert(self
.fromvalue
)
1429 if privatedata
.vfromvalue
< 0:
1430 privatedata
.vfromvalue
= 0
1431 if privatedata
.vfromvalue
> 1:
1432 privatedata
.vfromvalue
= 1
1433 if self
.frompathattrs
is not None and privatedata
.insertfrompath
:
1434 graph
.layer("data").stroke(graph
.axes
[sharedata
.barposcolumnnames
[sharedata
.barvalueindex
]].vgridpath(privatedata
.vfromvalue
),
1435 self
.defaultfrompathattrs
+ self
.frompathattrs
)
1437 privatedata
.vfromvalue
= 0
1439 def drawpoint(self
, privatedata
, sharedata
, graph
, point
):
1440 sharedata
.vposavailable
= sharedata
.vposvalid
= 1
1441 for i
, barname
in enumerate(sharedata
.barposcolumnnames
):
1442 if i
== sharedata
.barvalueindex
:
1443 sharedata
.vbarrange
[i
][0] = privatedata
.vfromvalue
1444 sharedata
.lastbarvalue
= point
[barname
]
1446 sharedata
.vpos
[i
] = sharedata
.vbarrange
[i
][1] = graph
.axes
[barname
].convert(sharedata
.lastbarvalue
)
1447 except (ArithmeticError, ValueError, TypeError):
1448 sharedata
.vpos
[i
] = sharedata
.vbarrange
[i
][1] = None
1452 sharedata
.vbarrange
[i
][j
] = graph
.axes
[barname
[:-4]].convert(self
.addsubvalue(point
[barname
], j
))
1453 except (ArithmeticError, ValueError, TypeError):
1454 sharedata
.vbarrange
[i
][j
] = None
1456 sharedata
.vpos
[i
] = 0.5*(sharedata
.vbarrange
[i
][0]+sharedata
.vbarrange
[i
][1])
1457 except (ArithmeticError, ValueError, TypeError):
1458 sharedata
.vpos
[i
] = None
1459 if sharedata
.vpos
[i
] is None:
1460 sharedata
.vposavailable
= sharedata
.vposvalid
= 0
1461 elif sharedata
.vpos
[i
] < -self
.epsilon
or sharedata
.vpos
[i
] > 1+self
.epsilon
:
1462 sharedata
.vposvalid
= 0
1464 registerdefaultprovider(barpos(), ["vbarrange", "barposcolumnnames", "barvalueindex", "lastbarvalue", "stackedbar", "stackedbardraw"])
1467 class stackedbarpos(_style
):
1469 # provides no additional data, but needs some data (and modifies some of them)
1470 needsdata
= ["vbarrange", "barposcolumnnames", "barvalueindex", "lastbarvalue", "stackedbar", "stackedbardraw"]
1472 def __init__(self
, stackname
, addontop
=0, epsilon
=1e-10):
1473 self
.stackname
= stackname
1474 self
.epsilon
= epsilon
1475 self
.addontop
= addontop
1477 def columnnames(self
, privatedata
, sharedata
, graph
, columnnames
, dataaxisnames
):
1478 if self
.stackname
not in columnnames
:
1479 raise ValueError("column '%s' missing" % self
.stackname
)
1480 return [self
.stackname
]
1482 def adjustaxis(self
, privatedata
, sharedata
, graph
, plotitem
, columnname
, data
):
1483 if columnname
== self
.stackname
:
1484 graph
.axes
[sharedata
.barposcolumnnames
[sharedata
.barvalueindex
]].adjustaxis(data
)
1486 def initdrawpoints(self
, privatedata
, sharedata
, graph
):
1487 if sharedata
.stackedbardraw
: # do not count the start bar when not gets painted
1488 sharedata
.stackedbar
+= 1
1490 def drawpoint(self
, privatedata
, sharedata
, graph
, point
):
1491 sharedata
.vbarrange
[sharedata
.barvalueindex
][0] = sharedata
.vbarrange
[sharedata
.barvalueindex
][1]
1494 sharedata
.lastbarvalue
+= point
[self
.stackname
]
1495 except (ArithmeticError, ValueError, TypeError):
1496 sharedata
.lastbarvalue
= None
1498 sharedata
.lastbarvalue
= point
[self
.stackname
]
1500 sharedata
.vpos
[sharedata
.barvalueindex
] = sharedata
.vbarrange
[sharedata
.barvalueindex
][1] = graph
.axes
[sharedata
.barposcolumnnames
[sharedata
.barvalueindex
]].convert(sharedata
.lastbarvalue
)
1501 except (ArithmeticError, ValueError, TypeError):
1502 sharedata
.vpos
[sharedata
.barvalueindex
] = sharedata
.vbarrange
[sharedata
.barvalueindex
][1] = None
1503 sharedata
.vposavailable
= sharedata
.vposvalid
= 0
1505 if not sharedata
.vposavailable
or not sharedata
.vposvalid
:
1506 sharedata
.vposavailable
= sharedata
.vposvalid
= 1
1507 for v
in sharedata
.vpos
:
1509 sharedata
.vposavailable
= sharedata
.vposvalid
= 0
1511 if v
< -self
.epsilon
or v
> 1+self
.epsilon
:
1512 sharedata
.vposvalid
= 0
1517 needsdata
= ["vbarrange"]
1519 defaultbarattrs
= [color
.gradient
.Rainbow
, deco
.stroked([color
.grey
.black
])]
1521 def __init__(self
, barattrs
=[], epsilon
=1e-10, gradient
=color
.gradient
.RedBlack
):
1522 self
.barattrs
= barattrs
1523 self
.epsilon
= epsilon
1524 self
.gradient
= gradient
1526 def lighting(self
, angle
, zindex
):
1527 return self
.gradient
.getcolor(0.7-0.4*abs(angle
)+0.1*zindex
)
1529 def columnnames(self
, privatedata
, sharedata
, graph
, columnnames
, dataaxisnames
):
1532 def selectstyle(self
, privatedata
, sharedata
, graph
, selectindex
, selecttotal
):
1533 privatedata
.barattrs
= attr
.selectattrs(self
.defaultbarattrs
+ self
.barattrs
, selectindex
, selecttotal
)
1535 def initdrawpoints(self
, privatedata
, sharedata
, graph
):
1536 privatedata
.barcanvas
= graph
.layer("filldata").insert(canvas
.canvas())
1537 sharedata
.stackedbardraw
= 1
1538 privatedata
.stackedbar
= sharedata
.stackedbar
1539 privatedata
.todraw
= []
1541 def drawpointfill(self
, privatedata
, p
):
1543 privatedata
.barcanvas
.fill(p
, privatedata
.barattrs
)
1545 def drawpoint(self
, privatedata
, sharedata
, graph
, point
):
1547 for vmin
, vmax
in sharedata
.vbarrange
:
1548 if vmin
is None or vmax
is None:
1549 self
.drawpointfill(privatedata
, None)
1552 vmin
, vmax
= vmax
, vmin
1553 if vmin
> 1 or vmax
< 0:
1554 self
.drawpointfill(privatedata
, None)
1560 vbarrange
.append((vmin
, vmax
))
1561 if len(vbarrange
) == 2:
1562 p
= graph
.vgeodesic(vbarrange
[0][0], vbarrange
[1][0], vbarrange
[0][1], vbarrange
[1][0])
1563 p
.append(graph
.vgeodesic_el(vbarrange
[0][1], vbarrange
[1][0], vbarrange
[0][1], vbarrange
[1][1]))
1564 p
.append(graph
.vgeodesic_el(vbarrange
[0][1], vbarrange
[1][1], vbarrange
[0][0], vbarrange
[1][1]))
1565 p
.append(graph
.vgeodesic_el(vbarrange
[0][0], vbarrange
[1][1], vbarrange
[0][0], vbarrange
[1][0]))
1566 p
.append(path
.closepath())
1567 self
.drawpointfill(privatedata
, p
)
1568 elif len(vbarrange
) == 3:
1570 if abs(vbarrange
[0][0] - vbarrange
[0][1]) > self
.epsilon
and abs(vbarrange
[1][0] - vbarrange
[1][1]):
1571 planes
.append((vbarrange
[0][0], vbarrange
[1][0], vbarrange
[2][0],
1572 vbarrange
[0][1], vbarrange
[1][0], vbarrange
[2][0],
1573 vbarrange
[0][1], vbarrange
[1][1], vbarrange
[2][0],
1574 vbarrange
[0][0], vbarrange
[1][1], vbarrange
[2][0]))
1575 planes
.append((vbarrange
[0][0], vbarrange
[1][0], vbarrange
[2][1],
1576 vbarrange
[0][0], vbarrange
[1][1], vbarrange
[2][1],
1577 vbarrange
[0][1], vbarrange
[1][1], vbarrange
[2][1],
1578 vbarrange
[0][1], vbarrange
[1][0], vbarrange
[2][1]))
1579 if abs(vbarrange
[0][0] - vbarrange
[0][1]) > self
.epsilon
and abs(vbarrange
[2][0] - vbarrange
[2][1]):
1580 planes
.append((vbarrange
[0][0], vbarrange
[1][0], vbarrange
[2][0],
1581 vbarrange
[0][0], vbarrange
[1][0], vbarrange
[2][1],
1582 vbarrange
[0][1], vbarrange
[1][0], vbarrange
[2][1],
1583 vbarrange
[0][1], vbarrange
[1][0], vbarrange
[2][0]))
1584 planes
.append((vbarrange
[0][0], vbarrange
[1][1], vbarrange
[2][0],
1585 vbarrange
[0][1], vbarrange
[1][1], vbarrange
[2][0],
1586 vbarrange
[0][1], vbarrange
[1][1], vbarrange
[2][1],
1587 vbarrange
[0][0], vbarrange
[1][1], vbarrange
[2][1]))
1588 if abs(vbarrange
[1][0] - vbarrange
[1][1]) > self
.epsilon
and abs(vbarrange
[2][0] - vbarrange
[2][1]):
1589 planes
.append((vbarrange
[0][0], vbarrange
[1][0], vbarrange
[2][0],
1590 vbarrange
[0][0], vbarrange
[1][1], vbarrange
[2][0],
1591 vbarrange
[0][0], vbarrange
[1][1], vbarrange
[2][1],
1592 vbarrange
[0][0], vbarrange
[1][0], vbarrange
[2][1]))
1593 planes
.append((vbarrange
[0][1], vbarrange
[1][0], vbarrange
[2][0],
1594 vbarrange
[0][1], vbarrange
[1][0], vbarrange
[2][1],
1595 vbarrange
[0][1], vbarrange
[1][1], vbarrange
[2][1],
1596 vbarrange
[0][1], vbarrange
[1][1], vbarrange
[2][0]))
1597 v
= [0.5 * (vbarrange
[0][0] + vbarrange
[0][1]),
1598 0.5 * (vbarrange
[1][0] + vbarrange
[1][1]),
1599 0.5 * (vbarrange
[2][0] + vbarrange
[2][1])]
1600 v
[sharedata
.barvalueindex
] = 0.5
1601 zindex
= graph
.vzindex(*v
)
1602 for v11
, v12
, v13
, v21
, v22
, v23
, v31
, v32
, v33
, v41
, v42
, v43
in planes
:
1603 angle
= graph
.vangle(v11
, v12
, v13
, v21
, v22
, v23
, v41
, v42
, v43
)
1605 p
= graph
.vgeodesic(v11
, v12
, v13
, v21
, v22
, v23
)
1606 p
.append(graph
.vgeodesic_el(v21
, v22
, v23
, v31
, v32
, v33
))
1607 p
.append(graph
.vgeodesic_el(v31
, v32
, v33
, v41
, v42
, v43
))
1608 p
.append(graph
.vgeodesic_el(v41
, v42
, v43
, v11
, v12
, v13
))
1609 p
.append(path
.closepath())
1611 privatedata
.todraw
.append((-zindex
, p
, privatedata
.barattrs
+ [self
.lighting(angle
, zindex
)]))
1613 privatedata
.todraw
.append((-zindex
, p
, privatedata
.barattrs
))
1615 raise TypeError("bar style restricted to two- and three dimensional graphs")
1617 def donedrawpoints(self
, privatedata
, sharedata
, graph
):
1618 privatedata
.todraw
.sort()
1619 for vzindex
, p
, a
in privatedata
.todraw
:
1620 privatedata
.barcanvas
.fill(p
, a
)
1622 def key_pt(self
, privatedata
, sharedata
, graph
, x_pt
, y_pt
, width_pt
, height_pt
):
1623 selectindex
= privatedata
.stackedbar
1624 selecttotal
= sharedata
.stackedbar
+ 1
1625 graph
.fill(path
.rect_pt(x_pt
+ width_pt
*selectindex
/float(selecttotal
), y_pt
, width_pt
/float(selecttotal
), height_pt
), privatedata
.barattrs
)
1628 class changebar(bar
):
1630 def selectstyle(self
, privatedata
, sharedata
, graph
, selectindex
, selecttotal
):
1631 if selecttotal
!= 1:
1632 raise RuntimeError("Changebar can't change its appearance. Thus you can't use it to plot several bars side by side on a subaxis.")
1634 def initdrawpoints(self
, privatedata
, sharedata
, graph
):
1635 if len(graph
.axesnames
) != 2:
1636 raise TypeError("changebar style restricted on two-dimensional graphs (at least for the moment)")
1637 bar
.initdrawpoints(self
, privatedata
, sharedata
, graph
)
1638 privatedata
.bars
= []
1640 def drawpointfill(self
, privatedata
, p
):
1641 privatedata
.bars
.append(p
)
1643 def donedrawpoints(self
, privatedata
, sharedata
, graph
):
1644 selecttotal
= len(privatedata
.bars
)
1645 for selectindex
, p
in enumerate(privatedata
.bars
):
1647 barattrs
= attr
.selectattrs(self
.defaultbarattrs
+ self
.barattrs
, selectindex
, selecttotal
)
1648 privatedata
.barcanvas
.fill(p
, barattrs
)
1650 def key_pt(self
, privatedata
, sharedata
, graph
, x_pt
, y_pt
, width_pt
, height_pt
):
1651 raise RuntimeError("Style currently doesn't provide a graph key")
1654 class gridpos(_style
):
1656 needsdata
= ["vpos", "vposmissing", "vposavailable", "vposvalid"]
1657 providesdata
= ["values1", "values2", "data12", "data21", "index1", "index2"]
1659 def __init__(self
, index1
=0, index2
=1, epsilon
=1e-10):
1660 self
.index1
= index1
1661 self
.index2
= index2
1662 self
.epsilon
= epsilon
1664 def initdrawpoints(self
, privatedata
, sharedata
, graph
):
1665 sharedata
.index1
= self
.index1
1666 sharedata
.index2
= self
.index2
1667 sharedata
.values1
= {}
1668 sharedata
.values2
= {}
1669 sharedata
.data12
= {}
1670 sharedata
.data21
= {}
1672 def drawpoint(self
, privatedata
, sharedata
, graph
, point
):
1673 if sharedata
.vposavailable
:
1674 sharedata
.value1
= sharedata
.vpos
[self
.index1
]
1675 sharedata
.value2
= sharedata
.vpos
[self
.index2
]
1676 if not sharedata
.values1
.has_key(sharedata
.value1
):
1677 for hasvalue
in sharedata
.values1
.keys():
1678 if hasvalue
- self
.epsilon
<= sharedata
.value1
<= hasvalue
+ self
.epsilon
:
1679 sharedata
.value1
= hasvalue
1682 sharedata
.values1
[sharedata
.value1
] = 1
1683 if not sharedata
.values2
.has_key(sharedata
.value2
):
1684 for hasvalue
in sharedata
.values2
.keys():
1685 if hasvalue
- self
.epsilon
<= sharedata
.value2
<= hasvalue
+ self
.epsilon
:
1686 sharedata
.value2
= hasvalue
1689 sharedata
.values2
[sharedata
.value2
] = 1
1690 data
= sharedata
.vposavailable
, sharedata
.vposvalid
, sharedata
.vpos
[:]
1691 sharedata
.data12
.setdefault(sharedata
.value1
, {})[sharedata
.value2
] = data
1692 sharedata
.data21
.setdefault(sharedata
.value2
, {})[sharedata
.value1
] = data
1694 registerdefaultprovider(gridpos(), gridpos
.providesdata
)
1697 class grid(_line
, _style
):
1699 needsdata
= ["values1", "values2", "data12", "data21"]
1701 defaultgridattrs
= [line
.changelinestyle
]
1703 def __init__(self
, gridlines1
=1, gridlines2
=1, gridattrs
=[]):
1704 self
.gridlines1
= gridlines1
1705 self
.gridlines2
= gridlines2
1706 self
.gridattrs
= gridattrs
1708 def selectstyle(self
, privatedata
, sharedata
, graph
, selectindex
, selecttotal
):
1709 if self
.gridattrs
is not None:
1710 privatedata
.gridattrs
= attr
.selectattrs(self
.defaultgridattrs
+ self
.gridattrs
, selectindex
, selecttotal
)
1712 privatedata
.gridattrs
= None
1714 def donedrawpoints(self
, privatedata
, sharedata
, graph
):
1715 values1
= sharedata
.values1
.keys()
1717 values2
= sharedata
.values2
.keys()
1720 for value2
in values2
:
1721 data1
= sharedata
.data21
[value2
]
1722 self
.initpointstopath(privatedata
)
1723 for value1
in values1
:
1725 data
= data1
[value1
]
1727 self
.addinvalid(privatedata
)
1729 self
.addpoint(privatedata
, graph
.vpos_pt
, *data
)
1730 p
= self
.donepointstopath(privatedata
)
1732 graph
.layer("data").stroke(p
, privatedata
.gridattrs
)
1734 for value1
in values1
:
1735 data2
= sharedata
.data12
[value1
]
1736 self
.initpointstopath(privatedata
)
1737 for value2
in values2
:
1739 data
= data2
[value2
]
1741 self
.addinvalid(privatedata
)
1743 self
.addpoint(privatedata
, graph
.vpos_pt
, *data
)
1744 p
= self
.donepointstopath(privatedata
)
1746 graph
.layer("data").stroke(p
, privatedata
.gridattrs
)
1749 class surface(_keygraphstyle
):
1751 needsdata
= ["values1", "values2", "data12", "data21"]
1753 def __init__(self
, gridlines1
=0.05, gridlines2
=0.05, gridcolor
=None,
1754 backcolor
=color
.gray
.black
, **kwargs
):
1755 _keygraphstyle
.__init
__(self
, **kwargs
)
1756 self
.gridlines1
= gridlines1
1757 self
.gridlines2
= gridlines2
1758 self
.gridcolor
= gridcolor
1759 self
.backcolor
= backcolor
1761 colorspacestring
= self
.gradient
.getcolor(0).colorspacestring()
1762 if self
.gridcolor
is not None and self
.gridcolor
.colorspacestring() != colorspacestring
:
1763 raise RuntimeError("colorspace mismatch (gradient/grid)")
1764 if self
.backcolor
is not None and self
.backcolor
.colorspacestring() != colorspacestring
:
1765 raise RuntimeError("colorspace mismatch (gradient/back)")
1767 def midvalue(self
, v1
, v2
, v3
, v4
):
1768 return [0.25*sum(values
) for values
in zip(v1
, v2
, v3
, v4
)]
1770 def midcolor(self
, c1
, c2
, c3
, c4
):
1771 return 0.25*(c1
+c2
+c3
+c4
)
1773 def lighting(self
, angle
, zindex
):
1774 if angle
< 0 and self
.backcolor
is not None:
1775 return self
.backcolor
1776 return self
.gradient
.getcolor(0.7-0.4*abs(angle
)+0.1*zindex
)
1778 def columnnames(self
, privatedata
, sharedata
, graph
, columnnames
, dataaxisnames
):
1779 privatedata
.colorize
= self
.colorname
in columnnames
1780 if privatedata
.colorize
:
1781 return _keygraphstyle
.columnnames(self
, privatedata
, sharedata
, graph
, columnnames
, dataaxisnames
)
1784 def initdrawpoints(self
, privatedata
, sharedata
, graph
):
1785 privatedata
.colors
= {}
1786 privatedata
.mincolor
= privatedata
.maxcolor
= None
1788 def drawpoint(self
, privatedata
, sharedata
, graph
, point
):
1789 if privatedata
.colorize
:
1791 color
= point
[self
.colorname
] + 0
1795 privatedata
.colors
.setdefault(sharedata
.value1
, {})[sharedata
.value2
] = color
1796 if privatedata
.mincolor
is None or color
< privatedata
.mincolor
:
1797 privatedata
.mincolor
= color
1798 if privatedata
.mincolor
is None or privatedata
.maxcolor
< color
:
1799 privatedata
.maxcolor
= color
1801 def donedrawpoints(self
, privatedata
, sharedata
, graph
):
1802 v1
= [0]*len(graph
.axesnames
)
1803 v2
= [0]*len(graph
.axesnames
)
1804 v3
= [0]*len(graph
.axesnames
)
1805 v4
= [0]*len(graph
.axesnames
)
1806 v1
[sharedata
.index2
] = 0.5
1807 v2
[sharedata
.index1
] = 0.5
1808 v3
[sharedata
.index1
] = 0.5
1809 v3
[sharedata
.index2
] = 1
1810 v4
[sharedata
.index1
] = 1
1811 v4
[sharedata
.index2
] = 0.5
1812 sortElements
= [-graph
.vzindex(*v1
),
1813 -graph
.vzindex(*v2
),
1814 -graph
.vzindex(*v3
),
1815 -graph
.vzindex(*v4
)]
1817 values1
= sharedata
.values1
.keys()
1819 v1
= [0]*len(graph
.axesnames
)
1820 v2
= [0]*len(graph
.axesnames
)
1821 v1
[sharedata
.index1
] = -1
1822 v2
[sharedata
.index1
] = 1
1824 if graph
.vzindex(*v1
) < graph
.vzindex(*v2
):
1827 sortElements
= [sortElements
[3], sortElements
[1], sortElements
[2], sortElements
[0]]
1829 values2
= sharedata
.values2
.keys()
1831 v1
= [0]*len(graph
.axesnames
)
1832 v2
= [0]*len(graph
.axesnames
)
1833 v1
[sharedata
.index2
] = -1
1834 v2
[sharedata
.index2
] = 1
1835 if graph
.vzindex(*v1
) < graph
.vzindex(*v2
):
1838 sortElements
= [sortElements
[0], sortElements
[2], sortElements
[1], sortElements
[3]]
1840 sortElements
= [(zindex
, i
) for i
, zindex
in enumerate(sortElements
)]
1845 for value1a
, value1b
in zip(values1
[:-1], values1
[1:]):
1846 for value2a
, value2b
in zip(values2
[:-1], values2
[1:]):
1848 available1
, valid1
, v1
= sharedata
.data12
[value1a
][value2a
]
1849 available2
, valid2
, v2
= sharedata
.data12
[value1a
][value2b
]
1850 available3
, valid3
, v3
= sharedata
.data12
[value1b
][value2a
]
1851 available4
, valid4
, v4
= sharedata
.data12
[value1b
][value2b
]
1854 if not available1
or not available2
or not available3
or not available4
:
1856 if not valid1
or not valid2
or not valid3
or not valid4
:
1857 warnings
.warn("surface elements partially outside of the graph are (currently) skipped completely")
1859 def shrink(index
, v1
, v2
, by
):
1862 for i
in builtinrange(3):
1864 v1
[i
], v2
[i
] = v1
[i
] + by
*(v2
[i
]-v1
[i
]), v2
[i
] + by
*(v1
[i
]-v2
[i
])
1866 v1f
, v2f
, v3f
, v4f
= v1
, v2
, v3
, v4
1867 if self
.gridcolor
is not None and self
.gridlines1
:
1868 v1
, v2
= shrink(sharedata
.index1
, v1
, v2
, self
.gridlines1
)
1869 v3
, v4
= shrink(sharedata
.index1
, v3
, v4
, self
.gridlines1
)
1870 if self
.gridcolor
is not None and self
.gridlines2
:
1871 v1
, v3
= shrink(sharedata
.index2
, v1
, v3
, self
.gridlines2
)
1872 v2
, v4
= shrink(sharedata
.index2
, v2
, v4
, self
.gridlines2
)
1873 v5
= self
.midvalue(v1
, v2
, v3
, v4
)
1874 x1_pt
, y1_pt
= graph
.vpos_pt(*v1
)
1875 x2_pt
, y2_pt
= graph
.vpos_pt(*v2
)
1876 x3_pt
, y3_pt
= graph
.vpos_pt(*v3
)
1877 x4_pt
, y4_pt
= graph
.vpos_pt(*v4
)
1878 x5_pt
, y5_pt
= graph
.vpos_pt(*v5
)
1879 if privatedata
.colorize
:
1880 c1
= privatedata
.colors
[value1a
][value2a
]
1881 c2
= privatedata
.colors
[value1a
][value2b
]
1882 c3
= privatedata
.colors
[value1b
][value2a
]
1883 c4
= privatedata
.colors
[value1b
][value2b
]
1884 c5
= self
.midcolor(c1
, c2
, c3
, c4
)
1885 c1a
= c1b
= self
.color(privatedata
, c1
)
1886 c2a
= c2c
= self
.color(privatedata
, c2
)
1887 c3b
= c3d
= self
.color(privatedata
, c3
)
1888 c4c
= c4d
= self
.color(privatedata
, c4
)
1889 c5a
= c5b
= c5c
= c5d
= self
.color(privatedata
, c5
)
1890 if self
.backcolor
is not None and sign
*graph
.vangle(*(v1
+v2
+v5
)) < 0:
1891 c1a
= c2a
= c5a
= self
.backcolor
1892 if self
.backcolor
is not None and sign
*graph
.vangle(*(v3
+v1
+v5
)) < 0:
1893 c3b
= c1b
= c5b
= self
.backcolor
1894 if self
.backcolor
is not None and sign
*graph
.vangle(*(v2
+v4
+v5
)) < 0:
1895 c2c
= c4c
= c5c
= self
.backcolor
1896 if self
.backcolor
is not None and sign
*graph
.vangle(*(v4
+v3
+v5
)) < 0:
1897 c4d
= c3d
= c5d
= self
.backcolor
1899 zindex
= graph
.vzindex(*v5
)
1900 c1a
= c2a
= c5a
= self
.lighting(sign
*graph
.vangle(*(v1
+v2
+v5
)), zindex
)
1901 c3b
= c1b
= c5b
= self
.lighting(sign
*graph
.vangle(*(v3
+v1
+v5
)), zindex
)
1902 c2c
= c4c
= c5c
= self
.lighting(sign
*graph
.vangle(*(v2
+v4
+v5
)), zindex
)
1903 c4d
= c3d
= c5d
= self
.lighting(sign
*graph
.vangle(*(v4
+v3
+v5
)), zindex
)
1904 for zindex
, i
in sortElements
:
1906 elements
.append(mesh
.element((mesh
.node_pt((x1_pt
, y1_pt
), c1a
),
1907 mesh
.node_pt((x2_pt
, y2_pt
), c2a
),
1908 mesh
.node_pt((x5_pt
, y5_pt
), c5a
))))
1909 if self
.gridcolor
is not None and self
.gridlines2
:
1910 elements
.append(mesh
.element((mesh
.node_pt(graph
.vpos_pt(*v1f
), self
.gridcolor
),
1911 mesh
.node_pt(graph
.vpos_pt(*v2
), self
.gridcolor
),
1912 mesh
.node_pt(graph
.vpos_pt(*v1
), self
.gridcolor
))))
1913 elements
.append(mesh
.element((mesh
.node_pt(graph
.vpos_pt(*v1f
), self
.gridcolor
),
1914 mesh
.node_pt(graph
.vpos_pt(*v2
), self
.gridcolor
),
1915 mesh
.node_pt(graph
.vpos_pt(*v2f
), self
.gridcolor
))))
1917 elements
.append(mesh
.element((mesh
.node_pt((x3_pt
, y3_pt
), c3b
),
1918 mesh
.node_pt((x1_pt
, y1_pt
), c1b
),
1919 mesh
.node_pt((x5_pt
, y5_pt
), c5b
))))
1920 if self
.gridcolor
is not None and self
.gridlines1
:
1921 elements
.append(mesh
.element((mesh
.node_pt(graph
.vpos_pt(*v1f
), self
.gridcolor
),
1922 mesh
.node_pt(graph
.vpos_pt(*v3
), self
.gridcolor
),
1923 mesh
.node_pt(graph
.vpos_pt(*v1
), self
.gridcolor
))))
1924 elements
.append(mesh
.element((mesh
.node_pt(graph
.vpos_pt(*v1f
), self
.gridcolor
),
1925 mesh
.node_pt(graph
.vpos_pt(*v3
), self
.gridcolor
),
1926 mesh
.node_pt(graph
.vpos_pt(*v3f
), self
.gridcolor
))))
1928 elements
.append(mesh
.element((mesh
.node_pt((x2_pt
, y2_pt
), c2c
),
1929 mesh
.node_pt((x4_pt
, y4_pt
), c4c
),
1930 mesh
.node_pt((x5_pt
, y5_pt
), c5c
))))
1931 if self
.gridcolor
is not None and self
.gridlines1
:
1932 elements
.append(mesh
.element((mesh
.node_pt(graph
.vpos_pt(*v2f
), self
.gridcolor
),
1933 mesh
.node_pt(graph
.vpos_pt(*v4
), self
.gridcolor
),
1934 mesh
.node_pt(graph
.vpos_pt(*v2
), self
.gridcolor
))))
1935 elements
.append(mesh
.element((mesh
.node_pt(graph
.vpos_pt(*v2f
), self
.gridcolor
),
1936 mesh
.node_pt(graph
.vpos_pt(*v4
), self
.gridcolor
),
1937 mesh
.node_pt(graph
.vpos_pt(*v4f
), self
.gridcolor
))))
1939 elements
.append(mesh
.element((mesh
.node_pt((x4_pt
, y4_pt
), c4d
),
1940 mesh
.node_pt((x3_pt
, y3_pt
), c3d
),
1941 mesh
.node_pt((x5_pt
, y5_pt
), c5d
))))
1942 if self
.gridcolor
is not None and self
.gridlines2
:
1943 elements
.append(mesh
.element((mesh
.node_pt(graph
.vpos_pt(*v3f
), self
.gridcolor
),
1944 mesh
.node_pt(graph
.vpos_pt(*v4
), self
.gridcolor
),
1945 mesh
.node_pt(graph
.vpos_pt(*v3
), self
.gridcolor
))))
1946 elements
.append(mesh
.element((mesh
.node_pt(graph
.vpos_pt(*v3f
), self
.gridcolor
),
1947 mesh
.node_pt(graph
.vpos_pt(*v4
), self
.gridcolor
),
1948 mesh
.node_pt(graph
.vpos_pt(*v4f
), self
.gridcolor
))))
1949 m
= mesh
.mesh(elements
, check
=0)
1950 graph
.layer("filldata").insert(m
)
1952 if privatedata
.colorize
:
1953 _keygraphstyle
.donedrawpoints(self
, privatedata
, sharedata
, graph
)
1956 class density(_keygraphstyle
):
1958 needsdata
= ["values1", "values2", "data12", "data21"]
1960 def __init__(self
, epsilon
=1e-10, **kwargs
):
1961 _keygraphstyle
.__init
__(self
, **kwargs
)
1962 self
.epsilon
= epsilon
1964 def initdrawpoints(self
, privatedata
, sharedata
, graph
):
1965 privatedata
.colors
= {}
1966 privatedata
.vfixed
= [None]*len(graph
.axesnames
)
1968 def drawpoint(self
, privatedata
, sharedata
, graph
, point
):
1970 color
= point
[self
.colorname
] + 0
1974 privatedata
.colors
.setdefault(sharedata
.value1
, {})[sharedata
.value2
] = color
1975 if len(privatedata
.vfixed
) > 2 and sharedata
.vposavailable
:
1976 for i
, (v1
, v2
) in enumerate(zip(privatedata
.vfixed
, sharedata
.vpos
)):
1977 if i
!= sharedata
.index1
and i
!= sharedata
.index2
:
1979 privatedata
.vfixed
[i
] = v2
1980 elif abs(v1
-v2
) > self
.epsilon
:
1981 raise ValueError("data must be in a plane for the bitmap style")
1983 def donedrawpoints(self
, privatedata
, sharedata
, graph
):
1984 privatedata
.keygraph
.doaxes()
1986 values1
= pycompat
.sorted(sharedata
.values1
.keys())
1987 values2
= pycompat
.sorted(sharedata
.values2
.keys())
1988 def equidistant(values
):
1991 raise ValueError("several data points required by the bitmap style in each dimension")
1992 range = values
[-1] - values
[0]
1993 for i
, value
in enumerate(values
):
1994 if abs(value
- values
[0] - i
* range / l
) > self
.epsilon
:
1995 raise ValueError("data must be equidistant for the bitmap style")
1996 equidistant(values1
)
1997 equidistant(values2
)
1999 for value2
in values2
:
2000 for value1
in values1
:
2002 available
, valid
, v
= sharedata
.data12
[value1
][value2
]
2012 mode
= {"/DeviceGray": "L",
2013 "/DeviceRGB": "RGB",
2014 "/DeviceCMYK": "CMYK"}[self
.gradient
.getcolor(0).colorspacestring()]
2017 empty
= "\0"*len(mode
)
2018 data
= cStringIO
.StringIO()
2019 for value2
in values2
:
2020 for value1
in values1
:
2022 available
, valid
, v
= sharedata
.data12
[value1
][value2
]
2029 c
= privatedata
.colors
[value1
][value2
]
2030 c
= self
.color(privatedata
, c
)
2032 data
.write(chr(255))
2033 data
.write(c
.to8bitstring())
2034 i
= bitmap
.image(len(values1
), len(values2
), mode
, data
.getvalue())
2036 v1enlargement
= (values1
[-1]-values1
[0])*0.5/len(values1
)
2037 v2enlargement
= (values2
[-1]-values2
[0])*0.5/len(values2
)
2039 privatedata
.vfixed
[sharedata
.index1
] = values1
[0]-v1enlargement
2040 privatedata
.vfixed
[sharedata
.index2
] = values2
[-1]+v2enlargement
2041 x1_pt
, y1_pt
= graph
.vpos_pt(*privatedata
.vfixed
)
2042 privatedata
.vfixed
[sharedata
.index1
] = values1
[-1]+v1enlargement
2043 privatedata
.vfixed
[sharedata
.index2
] = values2
[-1]+v2enlargement
2044 x2_pt
, y2_pt
= graph
.vpos_pt(*privatedata
.vfixed
)
2045 privatedata
.vfixed
[sharedata
.index1
] = values1
[0]-v1enlargement
2046 privatedata
.vfixed
[sharedata
.index2
] = values2
[0]-v2enlargement
2047 x3_pt
, y3_pt
= graph
.vpos_pt(*privatedata
.vfixed
)
2048 t
= trafo
.trafo_pt(((x2_pt
-x1_pt
, x3_pt
-x1_pt
), (y2_pt
-y1_pt
, y3_pt
-y1_pt
)), (x1_pt
, y1_pt
))
2050 privatedata
.vfixed
[sharedata
.index1
] = values1
[-1]+v1enlargement
2051 privatedata
.vfixed
[sharedata
.index2
] = values2
[0]-v2enlargement
2052 vx4
, vy4
= t
.inverse().apply_pt(*graph
.vpos_pt(*privatedata
.vfixed
))
2053 if abs(vx4
- 1) > self
.epsilon
or abs(vy4
- 1) > self
.epsilon
:
2054 raise ValueError("invalid graph layout for bitmap style (bitmap positioning by affine transformation failed)")
2057 privatedata
.vfixed
[sharedata
.index1
] = 0
2058 privatedata
.vfixed
[sharedata
.index2
] = 0
2059 p
.append(path
.moveto_pt(*graph
.vpos_pt(*privatedata
.vfixed
)))
2060 vfixed2
= privatedata
.vfixed
+ privatedata
.vfixed
2061 vfixed2
[sharedata
.index1
] = 0
2062 vfixed2
[sharedata
.index2
] = 0
2063 vfixed2
[sharedata
.index1
+ len(graph
.axesnames
)] = 1
2064 vfixed2
[sharedata
.index2
+ len(graph
.axesnames
)] = 0
2065 p
.append(graph
.vgeodesic_el(*vfixed2
))
2066 vfixed2
[sharedata
.index1
] = 1
2067 vfixed2
[sharedata
.index2
] = 0
2068 vfixed2
[sharedata
.index1
+ len(graph
.axesnames
)] = 1
2069 vfixed2
[sharedata
.index2
+ len(graph
.axesnames
)] = 1
2070 p
.append(graph
.vgeodesic_el(*vfixed2
))
2071 vfixed2
[sharedata
.index1
] = 1
2072 vfixed2
[sharedata
.index2
] = 1
2073 vfixed2
[sharedata
.index1
+ len(graph
.axesnames
)] = 0
2074 vfixed2
[sharedata
.index2
+ len(graph
.axesnames
)] = 1
2075 p
.append(graph
.vgeodesic_el(*vfixed2
))
2076 vfixed2
[sharedata
.index1
] = 0
2077 vfixed2
[sharedata
.index2
] = 1
2078 vfixed2
[sharedata
.index1
+ len(graph
.axesnames
)] = 0
2079 vfixed2
[sharedata
.index2
+ len(graph
.axesnames
)] = 0
2080 p
.append(graph
.vgeodesic_el(*vfixed2
))
2081 p
.append(path
.closepath())
2083 c
= canvas
.canvas([canvas
.clip(p
)])
2084 b
= bitmap
.bitmap_trafo(t
, i
)
2086 graph
.layer("filldata").insert(c
)
2088 _keygraphstyle
.donedrawpoints(self
, privatedata
, sharedata
, graph
)
2092 class gradient(_style
):
2094 defaultbarattrs
= [color
.gradient
.Rainbow
, deco
.stroked([color
.grey
.black
])]
2096 def __init__(self
, gradient
=color
.gradient
.Gray
, resolution
=100, columnname
="x"):
2097 self
.gradient
= gradient
2098 self
.resolution
= resolution
2099 self
.columnname
= columnname
2101 def columnnames(self
, privatedata
, sharedata
, graph
, columnnames
, dataaxisnames
):
2102 return [self
.columnname
]
2104 def adjustaxis(self
, privatedata
, sharedata
, graph
, plotitem
, columnname
, data
):
2105 graph
.axes
[self
.columnname
].adjustaxis(data
)
2107 def selectstyle(self
, privatedata
, sharedata
, graph
, selectindex
, selecttotal
):
2110 def initdrawpoints(self
, privatedata
, sharedata
, graph
):
2113 def drawpoint(self
, privatedata
, sharedata
, graph
, point
):
2116 def donedrawpoints(self
, privatedata
, sharedata
, graph
):
2117 mode
= {"/DeviceGray": "L",
2118 "/DeviceRGB": "RGB",
2119 "/DeviceCMYK": "CMYK"}[self
.gradient
.getcolor(0).colorspacestring()]
2120 data
= cStringIO
.StringIO()
2121 for i
in builtinrange(self
.resolution
):
2122 c
= self
.gradient
.getcolor(i
*1.0/self
.resolution
)
2123 data
.write(c
.to8bitstring())
2124 i
= bitmap
.image(self
.resolution
, 1, mode
, data
.getvalue())
2126 llx_pt
, lly_pt
= graph
.vpos_pt(0)
2127 urx_pt
, ury_pt
= graph
.vpos_pt(1)
2129 if graph
.direction
== "horizontal":
2130 lly_pt
-= 0.5*graph
.height_pt
2131 ury_pt
+= 0.5*graph
.height_pt
2133 llx_pt
-= 0.5*graph
.width_pt
2134 urx_pt
+= 0.5*graph
.width_pt
2136 c
= canvas
.canvas([canvas
.clip(path
.rect_pt(llx_pt
, lly_pt
, urx_pt
-llx_pt
, ury_pt
-lly_pt
))])
2138 if graph
.direction
== "horizontal":
2139 add_pt
= (urx_pt
-llx_pt
)*0.5/(self
.resolution
-1)
2143 add_pt
= (ury_pt
-lly_pt
)*0.5/(self
.resolution
-1)
2147 if graph
.direction
== "horizontal":
2148 t
= trafo
.trafo_pt(((urx_pt
-llx_pt
,0 ), (0, ury_pt
-lly_pt
)), (llx_pt
, lly_pt
))
2150 t
= trafo
.trafo_pt(((0, urx_pt
-llx_pt
), (ury_pt
-lly_pt
, 0)), (llx_pt
, lly_pt
))
2152 b
= bitmap
.bitmap_trafo(t
, i
)
2154 graph
.layer("filldata").insert(c
)