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 io
, logging
, math
26 from pyx
import attr
, deco
, bitmap
, style
, color
, unit
, canvas
, path
, mesh
, trafo
27 from pyx
import text
as textmodule
28 from .graph
import registerdefaultprovider
, graphx
32 logger
= logging
.getLogger("pyx")
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 from . import data
as datamodule
155 privatedata
.keygraph
.plot(datamodule
.values(x
=data
), [gradient(gradient
=self
.gradient
)])
157 def color(self
, privatedata
, c
):
158 vc
= privatedata
.keygraph
.axes
["x"].convert(c
)
160 logger
.warning("gradient color range is exceeded (lower bound)")
163 logger
.warning("gradient color range is exceeded (upper bound)")
165 return self
.gradient
.getcolor(vc
)
167 def donedrawpoints(self
, privatedata
, sharedata
, graph
):
168 if self
.keygraph
is _autokeygraph
:
169 graph
.layer("key").insert(privatedata
.keygraph
, [graph
.autokeygraphtrafo(privatedata
.keygraph
)])
174 providesdata
= ["vpos", "vposmissing", "vposavailable", "vposvalid", "poscolumnnames"]
176 def __init__(self
, usenames
={}, epsilon
=1e-10):
177 self
.usenames
= usenames
178 self
.epsilon
= epsilon
180 def columnnames(self
, privatedata
, sharedata
, graph
, columnnames
, dataaxisnames
):
181 privatedata
.poscolumnnames
= []
182 privatedata
.vposmissing
= []
183 privatedata
.axisnames
= {}
184 for count
, axisnames
in enumerate(graph
.axesnames
):
185 for axisname
in axisnames
:
187 usename
= self
.usenames
[axisname
]
190 for columnname
in columnnames
:
191 if usename
== columnname
:
192 privatedata
.poscolumnnames
.append(columnname
)
193 privatedata
.axisnames
[columnname
] = axisname
194 if len(privatedata
.poscolumnnames
) > count
+1:
195 raise ValueError("multiple axes per graph dimension")
196 elif len(privatedata
.poscolumnnames
) < count
+1:
197 privatedata
.vposmissing
.append(count
)
198 privatedata
.poscolumnnames
.append(None)
199 # Make poscolumnnames and vposmissing available to the outside,
200 # but keep a private reference. A copy is not needed, because
201 # the data is not altered in place, but might be exchanged my a
202 # later, different pos style in the styles list (due to different
204 sharedata
.poscolumnnames
= privatedata
.poscolumnnames
205 sharedata
.vposmissing
= privatedata
.vposmissing
206 dataaxisnames
.update(privatedata
.axisnames
)
207 return [columnname
for columnname
in privatedata
.poscolumnnames
if columnname
is not None]
209 def adjustaxis(self
, privatedata
, sharedata
, graph
, plotitem
, columnname
, data
):
210 if columnname
in privatedata
.axisnames
:
211 graph
.axes
[privatedata
.axisnames
[columnname
]].adjustaxis(data
)
213 def initdrawpoints(self
, privatedata
, sharedata
, graph
):
214 sharedata
.vpos
= [None]*(len(graph
.axesnames
))
215 privatedata
.pointpostmplist
= [[columnname
, index
, graph
.axes
[privatedata
.axisnames
[columnname
]]] # temporarily used by drawpoint only
216 for index
, columnname
in enumerate([columnname
for columnname
in privatedata
.poscolumnnames
if columnname
is not None])]
217 for missing
in privatedata
.vposmissing
:
218 for pointpostmp
in privatedata
.pointpostmplist
:
219 if pointpostmp
[1] >= missing
:
222 def drawpoint(self
, privatedata
, sharedata
, graph
, point
):
223 sharedata
.vposavailable
= 1 # valid position (but might be outside of the graph)
224 sharedata
.vposvalid
= 1 # valid position inside the graph
225 for columnname
, index
, axis
in privatedata
.pointpostmplist
:
227 v
= axis
.convert(point
[columnname
])
228 except (ArithmeticError, ValueError, TypeError):
229 sharedata
.vposavailable
= sharedata
.vposvalid
= 0
230 sharedata
.vpos
[index
] = None
232 if v
< -self
.epsilon
or v
> 1+self
.epsilon
:
233 sharedata
.vposvalid
= 0
234 sharedata
.vpos
[index
] = v
237 registerdefaultprovider(pos(), pos
.providesdata
)
242 providesdata
= ["vrange", "vrangemissing", "vrangeminmissing", "vrangemaxmissing"]
252 def __init__(self
, usenames
={}, epsilon
=1e-10):
253 self
.usenames
= usenames
254 self
.epsilon
= epsilon
256 def _numberofbits(self
, mask
):
260 return self
._numberofbits
(mask
>> 1) + 1
262 return self
._numberofbits
(mask
>> 1)
264 def columnnames(self
, privatedata
, sharedata
, graph
, columnnames
, dataaxisnames
):
266 privatedata
.rangeposcolumns
= []
267 sharedata
.vrangemissing
= []
268 sharedata
.vrangeminmissing
= []
269 sharedata
.vrangemaxmissing
= []
270 privatedata
.rangeposdeltacolumns
= {} # temporarily used by adjustaxis only
271 for count
, axisnames
in enumerate(graph
.axesnames
):
272 for axisname
in axisnames
:
274 usename
= self
.usenames
[axisname
]
278 for columnname
in columnnames
:
280 if usename
== columnname
:
281 mask
+= self
.mask_value
282 elif usename
+ "min" == columnname
:
283 mask
+= self
.mask_min
284 elif usename
+ "max" == columnname
:
285 mask
+= self
.mask_max
286 elif "d" + usename
+ "min" == columnname
:
287 mask
+= self
.mask_dmin
288 elif "d" + usename
+ "max" == columnname
:
289 mask
+= self
.mask_dmax
290 elif "d" + usename
== columnname
:
295 usecolumns
.append(columnname
)
296 dataaxisnames
[columnname
] = axisname
297 if mask
& (self
.mask_min | self
.mask_max | self
.mask_dmin | self
.mask_dmax | self
.mask_d
):
298 if (self
._numberofbits
(mask
& (self
.mask_min | self
.mask_dmin | self
.mask_d
)) > 1 or
299 self
._numberofbits
(mask
& (self
.mask_max | self
.mask_dmax | self
.mask_d
)) > 1):
300 raise ValueError("multiple range definition")
301 if mask
& (self
.mask_dmin | self
.mask_dmax | self
.mask_d
):
302 if not (mask
& self
.mask_value
):
303 raise ValueError("missing value for delta")
304 privatedata
.rangeposdeltacolumns
[axisname
] = {}
305 privatedata
.rangeposcolumns
.append((axisname
, usename
, mask
))
306 elif mask
== self
.mask_value
:
307 usecolumns
= usecolumns
[:-1]
308 if len(privatedata
.rangeposcolumns
) + len(sharedata
.vrangemissing
) > count
+1:
309 raise ValueError("multiple axes per graph dimension")
310 elif len(privatedata
.rangeposcolumns
) + len(sharedata
.vrangemissing
) < count
+1:
311 sharedata
.vrangemissing
.append(count
)
312 sharedata
.vrangeminmissing
.append(count
)
313 sharedata
.vrangemaxmissing
.append(count
)
315 if not (privatedata
.rangeposcolumns
[-1][2] & (self
.mask_min | self
.mask_dmin | self
.mask_d
)):
316 sharedata
.vrangeminmissing
.append(count
)
317 if not (privatedata
.rangeposcolumns
[-1][2] & (self
.mask_max | self
.mask_dmax | self
.mask_d
)):
318 sharedata
.vrangemaxmissing
.append(count
)
321 def adjustaxis(self
, privatedata
, sharedata
, graph
, plotitem
, columnname
, data
):
322 for axisname
, usename
, mask
in privatedata
.rangeposcolumns
:
323 if columnname
== usename
+ "min" and mask
& self
.mask_min
:
324 graph
.axes
[axisname
].adjustaxis(data
)
325 if columnname
== usename
+ "max" and mask
& self
.mask_max
:
326 graph
.axes
[axisname
].adjustaxis(data
)
328 # delta handling: fill rangeposdeltacolumns
329 for axisname
, usename
, mask
in privatedata
.rangeposcolumns
:
330 if columnname
== usename
and mask
& (self
.mask_dmin | self
.mask_dmax | self
.mask_d
):
331 privatedata
.rangeposdeltacolumns
[axisname
][self
.mask_value
] = data
332 if columnname
== "d" + usename
+ "min" and mask
& self
.mask_dmin
:
333 privatedata
.rangeposdeltacolumns
[axisname
][self
.mask_dmin
] = data
334 if columnname
== "d" + usename
+ "max" and mask
& self
.mask_dmax
:
335 privatedata
.rangeposdeltacolumns
[axisname
][self
.mask_dmax
] = data
336 if columnname
== "d" + usename
and mask
& self
.mask_d
:
337 privatedata
.rangeposdeltacolumns
[axisname
][self
.mask_d
] = data
339 # delta handling: process rangeposdeltacolumns
340 for a
, d
in list(privatedata
.rangeposdeltacolumns
.items()):
341 if self
.mask_value
in d
:
342 for k
in list(d
.keys()):
343 if k
!= self
.mask_value
:
344 if k
& (self
.mask_dmin | self
.mask_d
):
346 for value
, delta
in zip(d
[self
.mask_value
], d
[k
]):
348 mindata
.append(value
-delta
)
351 graph
.axes
[a
].adjustaxis(mindata
)
352 if k
& (self
.mask_dmax | self
.mask_d
):
354 for value
, delta
in zip(d
[self
.mask_value
], d
[k
]):
356 maxdata
.append(value
+delta
)
359 graph
.axes
[a
].adjustaxis(maxdata
)
362 def initdrawpoints(self
, privatedata
, sharedata
, graph
):
363 sharedata
.vrange
= [[None for x
in builtinrange(2)] for y
in privatedata
.rangeposcolumns
+ sharedata
.vrangemissing
]
364 privatedata
.rangepostmplist
= [[usename
, mask
, index
, graph
.axes
[axisname
]] # temporarily used by drawpoint only
365 for index
, (axisname
, usename
, mask
) in enumerate(privatedata
.rangeposcolumns
)]
366 for missing
in sharedata
.vrangemissing
:
367 for rangepostmp
in privatedata
.rangepostmplist
:
368 if rangepostmp
[2] >= missing
:
371 def drawpoint(self
, privatedata
, sharedata
, graph
, point
):
372 for usename
, mask
, index
, axis
in privatedata
.rangepostmplist
:
374 if mask
& self
.mask_min
:
375 sharedata
.vrange
[index
][0] = axis
.convert(point
[usename
+ "min"])
376 if mask
& self
.mask_dmin
:
377 sharedata
.vrange
[index
][0] = axis
.convert(point
[usename
] - point
["d" + usename
+ "min"])
378 if mask
& self
.mask_d
:
379 sharedata
.vrange
[index
][0] = axis
.convert(point
[usename
] - point
["d" + usename
])
380 except (ArithmeticError, ValueError, TypeError):
381 sharedata
.vrange
[index
][0] = None
383 if mask
& self
.mask_max
:
384 sharedata
.vrange
[index
][1] = axis
.convert(point
[usename
+ "max"])
385 if mask
& self
.mask_dmax
:
386 sharedata
.vrange
[index
][1] = axis
.convert(point
[usename
] + point
["d" + usename
+ "max"])
387 if mask
& self
.mask_d
:
388 sharedata
.vrange
[index
][1] = axis
.convert(point
[usename
] + point
["d" + usename
])
389 except (ArithmeticError, ValueError, TypeError):
390 sharedata
.vrange
[index
][1] = None
392 # some range checks for data consistency
393 if (sharedata
.vrange
[index
][0] is not None and sharedata
.vrange
[index
][1] is not None and
394 sharedata
.vrange
[index
][0] > sharedata
.vrange
[index
][1] + self
.epsilon
):
395 raise ValueError("inverse range")
396 # disabled due to missing vpos access:
397 # if (sharedata.vrange[index][0] is not None and sharedata.vpos[index] is not None and
398 # sharedata.vrange[index][0] > sharedata.vpos[index] + self.epsilon):
399 # raise ValueError("negative minimum errorbar")
400 # if (sharedata.vrange[index][1] is not None and sharedata.vpos[index] is not None and
401 # sharedata.vrange[index][1] < sharedata.vpos[index] - self.epsilon):
402 # raise ValueError("negative maximum errorbar")
405 registerdefaultprovider(range(), range.providesdata
)
408 def _crosssymbol(c
, x_pt
, y_pt
, size_pt
, attrs
):
409 c
.draw(path
.path(path
.moveto_pt(x_pt
-0.5*size_pt
, y_pt
-0.5*size_pt
),
410 path
.lineto_pt(x_pt
+0.5*size_pt
, y_pt
+0.5*size_pt
),
411 path
.moveto_pt(x_pt
-0.5*size_pt
, y_pt
+0.5*size_pt
),
412 path
.lineto_pt(x_pt
+0.5*size_pt
, y_pt
-0.5*size_pt
)), attrs
)
414 def _plussymbol(c
, x_pt
, y_pt
, size_pt
, attrs
):
415 c
.draw(path
.path(path
.moveto_pt(x_pt
-0.707106781*size_pt
, y_pt
),
416 path
.lineto_pt(x_pt
+0.707106781*size_pt
, y_pt
),
417 path
.moveto_pt(x_pt
, y_pt
-0.707106781*size_pt
),
418 path
.lineto_pt(x_pt
, y_pt
+0.707106781*size_pt
)), attrs
)
420 def _squaresymbol(c
, x_pt
, y_pt
, size_pt
, attrs
):
421 c
.draw(path
.path(path
.moveto_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
.lineto_pt(x_pt
-0.5*size_pt
, y_pt
+0.5*size_pt
),
425 path
.closepath()), attrs
)
427 def _trianglesymbol(c
, x_pt
, y_pt
, size_pt
, attrs
):
428 c
.draw(path
.path(path
.moveto_pt(x_pt
-0.759835685*size_pt
, y_pt
-0.438691337*size_pt
),
429 path
.lineto_pt(x_pt
+0.759835685*size_pt
, y_pt
-0.438691337*size_pt
),
430 path
.lineto_pt(x_pt
, y_pt
+0.877382675*size_pt
),
431 path
.closepath()), attrs
)
433 def _circlesymbol(c
, x_pt
, y_pt
, size_pt
, attrs
):
434 c
.draw(path
.path(path
.arc_pt(x_pt
, y_pt
, 0.564189583*size_pt
, 0, 360),
435 path
.closepath()), attrs
)
437 def _diamondsymbol(c
, x_pt
, y_pt
, size_pt
, attrs
):
438 c
.draw(path
.path(path
.moveto_pt(x_pt
-0.537284965*size_pt
, y_pt
),
439 path
.lineto_pt(x_pt
, y_pt
-0.930604859*size_pt
),
440 path
.lineto_pt(x_pt
+0.537284965*size_pt
, y_pt
),
441 path
.lineto_pt(x_pt
, y_pt
+0.930604859*size_pt
),
442 path
.closepath()), attrs
)
445 class _styleneedingpointpos(_style
):
447 needsdata
= ["vposmissing"]
449 def columnnames(self
, privatedata
, sharedata
, graph
, columnnames
, dataaxisnames
):
450 if len(sharedata
.vposmissing
):
451 raise ValueError("incomplete position information")
455 class symbol(_styleneedingpointpos
):
457 needsdata
= ["vpos", "vposmissing", "vposvalid"]
459 # "inject" the predefinied symbols into the class:
461 # Note, that statements like cross = _crosssymbol are
462 # invalid, since the would lead to unbound methods, but
463 # a single entry changeable list does the trick.
465 # Once we require Python 2.2+ we should use staticmethods
466 # to implement the default symbols inplace.
468 cross
= attr
.changelist([_crosssymbol
])
469 plus
= attr
.changelist([_plussymbol
])
470 square
= attr
.changelist([_squaresymbol
])
471 triangle
= attr
.changelist([_trianglesymbol
])
472 circle
= attr
.changelist([_circlesymbol
])
473 diamond
= attr
.changelist([_diamondsymbol
])
475 changecross
= attr
.changelist([_crosssymbol
, _plussymbol
, _squaresymbol
, _trianglesymbol
, _circlesymbol
, _diamondsymbol
])
476 changeplus
= attr
.changelist([_plussymbol
, _squaresymbol
, _trianglesymbol
, _circlesymbol
, _diamondsymbol
, _crosssymbol
])
477 changesquare
= attr
.changelist([_squaresymbol
, _trianglesymbol
, _circlesymbol
, _diamondsymbol
, _crosssymbol
, _plussymbol
])
478 changetriangle
= attr
.changelist([_trianglesymbol
, _circlesymbol
, _diamondsymbol
, _crosssymbol
, _plussymbol
, _squaresymbol
])
479 changecircle
= attr
.changelist([_circlesymbol
, _diamondsymbol
, _crosssymbol
, _plussymbol
, _squaresymbol
, _trianglesymbol
])
480 changediamond
= attr
.changelist([_diamondsymbol
, _crosssymbol
, _plussymbol
, _squaresymbol
, _trianglesymbol
, _circlesymbol
])
481 changesquaretwice
= attr
.changelist([_squaresymbol
, _squaresymbol
, _trianglesymbol
, _trianglesymbol
, _circlesymbol
, _circlesymbol
, _diamondsymbol
, _diamondsymbol
])
482 changetriangletwice
= attr
.changelist([_trianglesymbol
, _trianglesymbol
, _circlesymbol
, _circlesymbol
, _diamondsymbol
, _diamondsymbol
, _squaresymbol
, _squaresymbol
])
483 changecircletwice
= attr
.changelist([_circlesymbol
, _circlesymbol
, _diamondsymbol
, _diamondsymbol
, _squaresymbol
, _squaresymbol
, _trianglesymbol
, _trianglesymbol
])
484 changediamondtwice
= attr
.changelist([_diamondsymbol
, _diamondsymbol
, _squaresymbol
, _squaresymbol
, _trianglesymbol
, _trianglesymbol
, _circlesymbol
, _circlesymbol
])
486 changestrokedfilled
= attr
.changelist([deco
.stroked
, deco
.filled
])
487 changefilledstroked
= attr
.changelist([deco
.filled
, deco
.stroked
])
489 defaultsymbolattrs
= [deco
.stroked
]
491 def __init__(self
, symbol
=changecross
, size
=0.2*unit
.v_cm
, symbolattrs
=[]):
494 self
.symbolattrs
= symbolattrs
496 def selectstyle(self
, privatedata
, sharedata
, graph
, selectindex
, selecttotal
):
497 privatedata
.symbol
= attr
.selectattr(self
.symbol
, selectindex
, selecttotal
)
498 privatedata
.size_pt
= unit
.topt(attr
.selectattr(self
.size
, selectindex
, selecttotal
))
499 if self
.symbolattrs
is not None:
500 privatedata
.symbolattrs
= attr
.selectattrs(self
.defaultsymbolattrs
+ self
.symbolattrs
, selectindex
, selecttotal
)
502 privatedata
.symbolattrs
= None
504 def initdrawpoints(self
, privatedata
, sharedata
, graph
):
505 privatedata
.symbolcanvas
= canvas
.canvas()
507 def drawpoint(self
, privatedata
, sharedata
, graph
, point
):
508 if sharedata
.vposvalid
and privatedata
.symbolattrs
is not None:
509 x_pt
, y_pt
= graph
.vpos_pt(*sharedata
.vpos
)
510 privatedata
.symbol(privatedata
.symbolcanvas
, x_pt
, y_pt
, privatedata
.size_pt
, privatedata
.symbolattrs
)
512 def donedrawpoints(self
, privatedata
, sharedata
, graph
):
513 graph
.layer("data").insert(privatedata
.symbolcanvas
)
515 def key_pt(self
, privatedata
, sharedata
, graph
, x_pt
, y_pt
, width_pt
, height_pt
):
516 if privatedata
.symbolattrs
is not None:
517 privatedata
.symbol(graph
, x_pt
+0.5*width_pt
, y_pt
+0.5*height_pt
, privatedata
.size_pt
, privatedata
.symbolattrs
)
520 class _line(_styleneedingpointpos
):
522 # this style is not a complete style, but it provides the basic functionality to
523 # create a line, which is cut at the graph boundaries (or at otherwise invalid points)
525 def __init__(self
, epsilon
=1e-10):
526 self
.epsilon
= epsilon
528 def initpointstopath(self
, privatedata
):
529 privatedata
.path
= path
.path()
530 privatedata
.linebasepoints
= []
531 privatedata
.lastvpos
= None
533 def addpointstopath(self
, privatedata
):
534 # add baselinepoints to privatedata.path
535 if len(privatedata
.linebasepoints
) > 1:
536 privatedata
.path
.append(path
.moveto_pt(*privatedata
.linebasepoints
[0]))
537 if len(privatedata
.linebasepoints
) > 2:
538 privatedata
.path
.append(path
.multilineto_pt(privatedata
.linebasepoints
[1:]))
540 privatedata
.path
.append(path
.lineto_pt(*privatedata
.linebasepoints
[1]))
541 privatedata
.linebasepoints
= []
543 def addpoint(self
, privatedata
, graphvpos_pt
, vposavailable
, vposvalid
, vpos
):
544 # append linebasepoints
546 if len(privatedata
.linebasepoints
):
547 # the last point was inside the graph
548 if vposvalid
: # shortcut for the common case
549 privatedata
.linebasepoints
.append(graphvpos_pt(*vpos
))
553 for vstart
, vend
in zip(privatedata
.lastvpos
, vpos
):
554 if abs(vend
- vstart
) > self
.epsilon
:
557 # 1 = vstart + (vend - vstart) * cut
558 newcut
= (1 - vstart
)/(vend
- vstart
)
560 # 0 = vstart + (vend - vstart) * cut
561 newcut
= - vstart
/(vend
- vstart
)
562 if newcut
is not None and newcut
< cut
:
565 for vstart
, vend
in zip(privatedata
.lastvpos
, vpos
):
566 cutvpos
.append(vstart
+ (vend
- vstart
) * cut
)
567 privatedata
.linebasepoints
.append(graphvpos_pt(*cutvpos
))
568 self
.addpointstopath(privatedata
)
570 # the last point was outside the graph
571 if privatedata
.lastvpos
is not None:
575 for vstart
, vend
in zip(privatedata
.lastvpos
, vpos
):
576 if abs(vend
- vstart
) > self
.epsilon
:
579 # 1 = vstart + (vend - vstart) * cut
580 newcut
= (1 - vstart
)/(vend
- vstart
)
582 # 0 = vstart + (vend - vstart) * cut
583 newcut
= - vstart
/(vend
- vstart
)
584 if newcut
is not None and newcut
> cut
:
587 for vstart
, vend
in zip(privatedata
.lastvpos
, vpos
):
588 cutvpos
.append(vstart
+ (vend
- vstart
) * cut
)
589 privatedata
.linebasepoints
.append(graphvpos_pt(*cutvpos
))
590 privatedata
.linebasepoints
.append(graphvpos_pt(*vpos
))
592 # sometimes cut beginning and end
595 for vstart
, vend
in zip(privatedata
.lastvpos
, vpos
):
596 if vstart
> 1 and vend
> 1: break
597 if vstart
< 0 and vend
< 0: break
598 if abs(vend
- vstart
) > self
.epsilon
:
601 newcutfrom
= (1 - vstart
)/(vend
- vstart
)
603 # 0 = vstart + (vend - vstart) * cutfrom
604 newcutfrom
= - vstart
/(vend
- vstart
)
605 if newcutfrom
is not None and newcutfrom
> cutfrom
:
609 # 1 = vstart + (vend - vstart) * cutto
610 newcutto
= (1 - vstart
)/(vend
- vstart
)
612 # 0 = vstart + (vend - vstart) * cutto
613 newcutto
= - vstart
/(vend
- vstart
)
614 if newcutto
is not None and newcutto
< cutto
:
620 for vstart
, vend
in zip(privatedata
.lastvpos
, vpos
):
621 cutfromvpos
.append(vstart
+ (vend
- vstart
) * cutfrom
)
622 cuttovpos
.append(vstart
+ (vend
- vstart
) * cutto
)
623 privatedata
.linebasepoints
.append(graphvpos_pt(*cutfromvpos
))
624 privatedata
.linebasepoints
.append(graphvpos_pt(*cuttovpos
))
625 self
.addpointstopath(privatedata
)
626 privatedata
.lastvpos
= vpos
[:]
628 if len(privatedata
.linebasepoints
) > 1:
629 self
.addpointstopath(privatedata
)
630 privatedata
.lastvpos
= None
632 def addinvalid(self
, privatedata
):
633 if len(privatedata
.linebasepoints
) > 1:
634 self
.addpointstopath(privatedata
)
635 privatedata
.lastvpos
= None
637 def donepointstopath(self
, privatedata
):
638 if len(privatedata
.linebasepoints
) > 1:
639 self
.addpointstopath(privatedata
)
640 return privatedata
.path
645 needsdata
= ["vpos", "vposmissing", "vposavailable", "vposvalid"]
647 changelinestyle
= attr
.changelist([style
.linestyle
.solid
,
648 style
.linestyle
.dashed
,
649 style
.linestyle
.dotted
,
650 style
.linestyle
.dashdotted
])
652 defaultlineattrs
= [changelinestyle
]
654 def __init__(self
, lineattrs
=[], **kwargs
):
655 _line
.__init
__(self
, **kwargs
)
656 self
.lineattrs
= lineattrs
658 def selectstyle(self
, privatedata
, sharedata
, graph
, selectindex
, selecttotal
):
659 if self
.lineattrs
is not None:
660 privatedata
.lineattrs
= attr
.selectattrs(self
.defaultlineattrs
+ self
.lineattrs
, selectindex
, selecttotal
)
662 privatedata
.lineattrs
= None
664 def initdrawpoints(self
, privatedata
, sharedata
, graph
):
665 self
.initpointstopath(privatedata
)
667 def drawpoint(self
, privatedata
, sharedata
, graph
, point
):
668 self
.addpoint(privatedata
, graph
.vpos_pt
, sharedata
.vposavailable
, sharedata
.vposvalid
, sharedata
.vpos
)
670 def donedrawpoints(self
, privatedata
, sharedata
, graph
):
671 path
= self
.donepointstopath(privatedata
)
672 if privatedata
.lineattrs
is not None and len(path
):
673 graph
.layer("data").stroke(path
, privatedata
.lineattrs
)
675 def key_pt(self
, privatedata
, sharedata
, graph
, x_pt
, y_pt
, width_pt
, height_pt
):
676 if privatedata
.lineattrs
is not None:
677 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
)
680 class impulses(_styleneedingpointpos
):
682 needsdata
= ["vpos", "vposmissing", "vposavailable", "vposvalid", "poscolumnnames"]
684 defaultlineattrs
= [line
.changelinestyle
]
685 defaultfrompathattrs
= []
687 def __init__(self
, lineattrs
=[], fromvalue
=0, frompathattrs
=[], valueaxisindex
=1):
688 self
.lineattrs
= lineattrs
689 self
.fromvalue
= fromvalue
690 self
.frompathattrs
= frompathattrs
691 self
.valueaxisindex
= valueaxisindex
693 def selectstyle(self
, privatedata
, sharedata
, graph
, selectindex
, selecttotal
):
694 privatedata
.insertfrompath
= selectindex
== 0
695 if self
.lineattrs
is not None:
696 privatedata
.lineattrs
= attr
.selectattrs(self
.defaultlineattrs
+ self
.lineattrs
, selectindex
, selecttotal
)
698 privatedata
.lineattrs
= None
700 def adjustaxis(self
, privatedata
, sharedata
, graph
, plotitem
, columnname
, data
):
701 if self
.fromvalue
is not None:
703 i
= sharedata
.poscolumnnames
.index(columnname
)
707 if i
== self
.valueaxisindex
:
708 graph
.axes
[sharedata
.poscolumnnames
[i
]].adjustaxis([self
.fromvalue
])
710 def initdrawpoints(self
, privatedata
, sharedata
, graph
):
711 privatedata
.impulsescanvas
= canvas
.canvas()
712 if self
.fromvalue
is not None:
713 valueaxisname
= sharedata
.poscolumnnames
[self
.valueaxisindex
]
714 privatedata
.vfromvalue
= graph
.axes
[valueaxisname
].convert(self
.fromvalue
)
715 privatedata
.vfromvaluecut
= 0
716 if privatedata
.vfromvalue
< 0:
717 privatedata
.vfromvalue
= 0
718 if privatedata
.vfromvalue
> 1:
719 privatedata
.vfromvalue
= 1
720 if self
.frompathattrs
is not None and privatedata
.insertfrompath
:
721 graph
.layer("data").stroke(graph
.axes
[valueaxisname
].vgridpath(privatedata
.vfromvalue
),
722 self
.defaultfrompathattrs
+ self
.frompathattrs
)
724 privatedata
.vfromvalue
= 0
726 def drawpoint(self
, privatedata
, sharedata
, graph
, point
):
727 if sharedata
.vposvalid
and privatedata
.lineattrs
is not None:
728 vpos
= sharedata
.vpos
[:]
729 vpos
[self
.valueaxisindex
] = privatedata
.vfromvalue
730 privatedata
.impulsescanvas
.stroke(graph
.vgeodesic(*(vpos
+ sharedata
.vpos
)), privatedata
.lineattrs
)
732 def donedrawpoints(self
, privatedata
, sharedata
, graph
):
733 graph
.layer("data").insert(privatedata
.impulsescanvas
)
735 def key_pt(self
, privatedata
, sharedata
, graph
, x_pt
, y_pt
, width_pt
, height_pt
):
736 if privatedata
.lineattrs
is not None:
737 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
)
740 class errorbar(_style
):
742 needsdata
= ["vpos", "vposmissing", "vposavailable", "vposvalid", "vrange", "vrangeminmissing", "vrangemaxmissing"]
744 defaulterrorbarattrs
= []
746 def __init__(self
, size
=0.1*unit
.v_cm
,
750 self
.errorbarattrs
= errorbarattrs
751 self
.epsilon
= epsilon
753 def columnnames(self
, privatedata
, sharedata
, graph
, columnnames
, dataaxisnames
):
754 for i
in sharedata
.vposmissing
:
755 if i
in sharedata
.vrangeminmissing
and i
in sharedata
.vrangemaxmissing
:
756 raise ValueError("position and range for a graph dimension missing")
759 def selectstyle(self
, privatedata
, sharedata
, graph
, selectindex
, selecttotal
):
760 privatedata
.errorsize_pt
= unit
.topt(attr
.selectattr(self
.size
, selectindex
, selecttotal
))
761 privatedata
.errorbarattrs
= attr
.selectattrs(self
.defaulterrorbarattrs
+ self
.errorbarattrs
, selectindex
, selecttotal
)
763 def initdrawpoints(self
, privatedata
, sharedata
, graph
):
764 if privatedata
.errorbarattrs
is not None:
765 privatedata
.errorbarcanvas
= canvas
.canvas(privatedata
.errorbarattrs
)
766 privatedata
.dimensionlist
= list(builtinrange(len(sharedata
.vpos
)))
768 def drawpoint(self
, privatedata
, sharedata
, graph
, point
):
769 if privatedata
.errorbarattrs
is not None:
770 for i
in privatedata
.dimensionlist
:
771 for j
in privatedata
.dimensionlist
:
773 (sharedata
.vpos
[j
] is None or
774 sharedata
.vpos
[j
] < -self
.epsilon
or
775 sharedata
.vpos
[j
] > 1+self
.epsilon
)):
778 if ((sharedata
.vrange
[i
][0] is None and sharedata
.vpos
[i
] is None) or
779 (sharedata
.vrange
[i
][1] is None and sharedata
.vpos
[i
] is None) or
780 (sharedata
.vrange
[i
][0] is None and sharedata
.vrange
[i
][1] is None)):
782 vminpos
= sharedata
.vpos
[:]
783 if sharedata
.vrange
[i
][0] is not None:
784 vminpos
[i
] = sharedata
.vrange
[i
][0]
788 if vminpos
[i
] > 1+self
.epsilon
:
790 if vminpos
[i
] < -self
.epsilon
:
793 vmaxpos
= sharedata
.vpos
[:]
794 if sharedata
.vrange
[i
][1] is not None:
795 vmaxpos
[i
] = sharedata
.vrange
[i
][1]
799 if vmaxpos
[i
] < -self
.epsilon
:
801 if vmaxpos
[i
] > 1+self
.epsilon
:
804 privatedata
.errorbarcanvas
.stroke(graph
.vgeodesic(*(vminpos
+ vmaxpos
)))
805 for j
in privatedata
.dimensionlist
:
808 privatedata
.errorbarcanvas
.stroke(graph
.vcap_pt(j
, privatedata
.errorsize_pt
, *vminpos
))
810 privatedata
.errorbarcanvas
.stroke(graph
.vcap_pt(j
, privatedata
.errorsize_pt
, *vmaxpos
))
812 def donedrawpoints(self
, privatedata
, sharedata
, graph
):
813 if privatedata
.errorbarattrs
is not None:
814 graph
.layer("data").insert(privatedata
.errorbarcanvas
)
817 class text(_styleneedingpointpos
):
819 needsdata
= ["vpos", "vposmissing", "vposvalid"]
821 defaulttextattrs
= [textmodule
.halign
.center
, textmodule
.vshift
.mathaxis
]
823 def __init__(self
, textname
="text", dxname
=None, dyname
=None,
824 dxunit
=0.3*unit
.v_cm
, dyunit
=0.3*unit
.v_cm
,
825 textdx
=0*unit
.v_cm
, textdy
=0.3*unit
.v_cm
, textattrs
=[]):
826 self
.textname
= textname
833 self
.textattrs
= textattrs
835 def columnnames(self
, privatedata
, sharedata
, graph
, columnnames
, dataaxisnames
):
836 if self
.textname
not in columnnames
:
837 raise ValueError("column '%s' missing" % self
.textname
)
838 names
= [self
.textname
]
839 if self
.dxname
is not None:
840 if self
.dxname
not in columnnames
:
841 raise ValueError("column '%s' missing" % self
.dxname
)
842 names
.append(self
.dxname
)
843 if self
.dyname
is not None:
844 if self
.dyname
not in columnnames
:
845 raise ValueError("column '%s' missing" % self
.dyname
)
846 names
.append(self
.dyname
)
847 return names
+ _styleneedingpointpos
.columnnames(self
, privatedata
, sharedata
, graph
, columnnames
, dataaxisnames
)
849 def selectstyle(self
, privatedata
, sharedata
, graph
, selectindex
, selecttotal
):
850 if self
.textattrs
is not None:
851 privatedata
.textattrs
= attr
.selectattrs(self
.defaulttextattrs
+ self
.textattrs
, selectindex
, selecttotal
)
853 privatedata
.textattrs
= None
855 def initdrawpoints(self
, privatedata
, sharedata
, grap
):
856 if self
.dxname
is None:
857 privatedata
.textdx_pt
= unit
.topt(self
.textdx
)
859 privatedata
.dxunit_pt
= unit
.topt(self
.dxunit
)
860 if self
.dyname
is None:
861 privatedata
.textdy_pt
= unit
.topt(self
.textdy
)
863 privatedata
.dyunit_pt
= unit
.topt(self
.dyunit
)
865 def drawpoint(self
, privatedata
, sharedata
, graph
, point
):
866 if privatedata
.textattrs
is not None and sharedata
.vposvalid
:
867 x_pt
, y_pt
= graph
.vpos_pt(*sharedata
.vpos
)
869 text
= str(point
[self
.textname
])
873 if self
.dxname
is None:
874 dx_pt
= privatedata
.textdx_pt
876 dx_pt
= float(point
[self
.dxname
]) * privatedata
.dxunit_pt
877 if self
.dyname
is None:
878 dy_pt
= privatedata
.textdy_pt
880 dy_pt
= float(point
[self
.dyname
]) * privatedata
.dyunit_pt
881 graph
.layer("data").text_pt(x_pt
+ dx_pt
, y_pt
+ dy_pt
, text
, privatedata
.textattrs
)
883 def key_pt(self
, privatedata
, sharedata
, graph
, x_pt
, y_pt
, width_pt
, height_pt
):
884 raise RuntimeError("Style currently doesn't provide a graph key")
887 class arrow(_styleneedingpointpos
):
889 needsdata
= ["vpos", "vposmissing", "vposvalid"]
891 defaultlineattrs
= []
892 defaultarrowattrs
= []
894 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
):
895 self
.linelength
= linelength
896 self
.arrowsize
= arrowsize
897 self
.lineattrs
= lineattrs
898 self
.arrowattrs
= arrowattrs
899 self
.arrowpos
= arrowpos
900 self
.epsilon
= epsilon
901 self
.decorator
= decorator
903 def columnnames(self
, privatedata
, sharedata
, graph
, columnnames
, dataaxisnames
):
904 if len(graph
.axesnames
) != 2:
905 raise ValueError("arrow style restricted on two-dimensional graphs")
906 if "size" not in columnnames
:
907 raise ValueError("size missing")
908 if "angle" not in columnnames
:
909 raise ValueError("angle missing")
910 return ["size", "angle"] + _styleneedingpointpos
.columnnames(self
, privatedata
, sharedata
, graph
, columnnames
, dataaxisnames
)
912 def selectstyle(self
, privatedata
, sharedata
, graph
, selectindex
, selecttotal
):
913 if self
.lineattrs
is not None:
914 privatedata
.lineattrs
= attr
.selectattrs(self
.defaultlineattrs
+ self
.lineattrs
, selectindex
, selecttotal
)
916 privatedata
.lineattrs
= None
917 if self
.arrowattrs
is not None:
918 privatedata
.arrowattrs
= attr
.selectattrs(self
.defaultarrowattrs
+ self
.arrowattrs
, selectindex
, selecttotal
)
920 privatedata
.arrowattrs
= None
922 def initdrawpoints(self
, privatedata
, sharedata
, graph
):
923 privatedata
.arrowcanvas
= canvas
.canvas()
925 def drawpoint(self
, privatedata
, sharedata
, graph
, point
):
926 if privatedata
.lineattrs
is not None and privatedata
.arrowattrs
is not None and sharedata
.vposvalid
:
927 linelength_pt
= unit
.topt(self
.linelength
)
928 x_pt
, y_pt
= graph
.vpos_pt(*sharedata
.vpos
)
930 angle
= point
["angle"] + 0.0
931 size
= point
["size"] + 0.0
935 if point
["size"] > self
.epsilon
:
936 dx
= math
.cos(angle
*math
.pi
/180)
937 dy
= math
.sin(angle
*math
.pi
/180)
938 x1
= x_pt
-self
.arrowpos
*dx
*linelength_pt
*size
939 y1
= y_pt
-self
.arrowpos
*dy
*linelength_pt
*size
940 x2
= x_pt
+(1-self
.arrowpos
)*dx
*linelength_pt
*size
941 y2
= y_pt
+(1-self
.arrowpos
)*dy
*linelength_pt
*size
943 privatedata
.arrowcanvas
.stroke(path
.line_pt(x1
, y1
, x2
, y2
),
944 privatedata
.lineattrs
+[self
.decorator(privatedata
.arrowattrs
, size
=self
.arrowsize
*size
)])
946 privatedata
.arrowcanvas
.stroke(path
.line_pt(x1
, y1
, x2
, y2
), privatedata
.lineattrs
)
948 def donedrawpoints(self
, privatedata
, sharedata
, graph
):
949 graph
.layer("data").insert(privatedata
.arrowcanvas
)
951 def key_pt(self
, privatedata
, sharedata
, graph
, x_pt
, y_pt
, width_pt
, height_pt
):
952 raise RuntimeError("Style currently doesn't provide a graph key")
955 class rect(_keygraphstyle
):
957 needsdata
= ["vrange", "vrangeminmissing", "vrangemaxmissing"]
959 def columnnames(self
, privatedata
, sharedata
, graph
, columnnames
, dataaxisnames
):
960 if len(graph
.axesnames
) != 2:
961 raise TypeError("rect style restricted on two-dimensional graphs")
962 if len(sharedata
.vrangeminmissing
) + len(sharedata
.vrangemaxmissing
):
963 raise ValueError("incomplete range")
964 return _keygraphstyle
.columnnames(self
, privatedata
, sharedata
, graph
, columnnames
, dataaxisnames
)
966 def initdrawpoints(self
, privatedata
, sharedata
, graph
):
967 privatedata
.rectcanvas
= graph
.layer("filldata").insert(canvas
.canvas())
969 def drawpoint(self
, privatedata
, sharedata
, graph
, point
):
970 xvmin
= sharedata
.vrange
[0][0]
971 xvmax
= sharedata
.vrange
[0][1]
972 yvmin
= sharedata
.vrange
[1][0]
973 yvmax
= sharedata
.vrange
[1][1]
974 if (xvmin
is not None and xvmin
< 1 and
975 xvmax
is not None and xvmax
> 0 and
976 yvmin
is not None and yvmin
< 1 and
977 yvmax
is not None and yvmax
> 0):
986 p
= graph
.vgeodesic(xvmin
, yvmin
, xvmax
, yvmin
)
987 p
.append(graph
.vgeodesic_el(xvmax
, yvmin
, xvmax
, yvmax
))
988 p
.append(graph
.vgeodesic_el(xvmax
, yvmax
, xvmin
, yvmax
))
989 p
.append(graph
.vgeodesic_el(xvmin
, yvmax
, xvmin
, yvmin
))
990 p
.append(path
.closepath())
991 privatedata
.rectcanvas
.fill(p
, [self
.color(privatedata
, point
["color"])])
994 class histogram(_style
):
996 needsdata
= ["vpos", "vposmissing", "vrange", "vrangeminmissing", "vrangemaxmissing"]
998 defaultlineattrs
= [deco
.stroked
]
999 defaultfrompathattrs
= []
1001 def __init__(self
, lineattrs
=[], steps
=0, fromvalue
=0, frompathattrs
=[], fillable
=0, rectkey
=0,
1002 autohistogramaxisindex
=0, autohistogrampointpos
=0.5, epsilon
=1e-10):
1003 self
.lineattrs
= lineattrs
1005 self
.fromvalue
= fromvalue
1006 self
.frompathattrs
= frompathattrs
1007 self
.fillable
= fillable
# TODO: fillable paths might not properly be closed by straight lines on curved graph geometries
1008 self
.rectkey
= rectkey
1009 self
.autohistogramaxisindex
= autohistogramaxisindex
1010 self
.autohistogrampointpos
= autohistogrampointpos
1011 self
.epsilon
= epsilon
1013 def columnnames(self
, privatedata
, sharedata
, graph
, columnnames
, dataaxisnames
):
1014 if len(graph
.axesnames
) != 2:
1015 raise TypeError("histogram style restricted on two-dimensional graphs")
1016 privatedata
.rangeaxisindex
= None
1017 for i
in builtinrange(len(graph
.axesnames
)):
1018 if i
in sharedata
.vrangeminmissing
or i
in sharedata
.vrangemaxmissing
:
1019 if i
in sharedata
.vposmissing
:
1020 raise ValueError("pos and range missing")
1022 if privatedata
.rangeaxisindex
is not None:
1023 raise ValueError("multiple ranges")
1024 privatedata
.rangeaxisindex
= i
1025 if privatedata
.rangeaxisindex
is None:
1026 privatedata
.rangeaxisindex
= self
.autohistogramaxisindex
1027 privatedata
.autohistogram
= 1
1029 privatedata
.autohistogram
= 0
1032 def adjustaxis(self
, privatedata
, sharedata
, graph
, plotitem
, columnname
, data
):
1033 if privatedata
.autohistogram
and columnname
== sharedata
.poscolumnnames
[privatedata
.rangeaxisindex
]:
1035 raise ValueError("several data points needed for automatic histogram width calculation")
1037 delta
= data
[1] - data
[0]
1038 min = data
[0] - self
.autohistogrampointpos
* delta
1039 max = data
[-1] + (1-self
.autohistogrampointpos
) * delta
1040 graph
.axes
[columnname
].adjustaxis([min, max])
1041 elif self
.fromvalue
is not None and columnname
== sharedata
.poscolumnnames
[1-privatedata
.rangeaxisindex
]:
1042 graph
.axes
[columnname
].adjustaxis([self
.fromvalue
])
1044 def selectstyle(self
, privatedata
, sharedata
, graph
, selectindex
, selecttotal
):
1045 privatedata
.insertfrompath
= selectindex
== 0
1046 if self
.lineattrs
is not None:
1047 privatedata
.lineattrs
= attr
.selectattrs(self
.defaultlineattrs
+ self
.lineattrs
, selectindex
, selecttotal
)
1049 privatedata
.lineattrs
= None
1051 def vmoveto(self
, privatedata
, sharedata
, graph
, vpos
, vvalue
):
1052 if -self
.epsilon
< vpos
< 1+self
.epsilon
and -self
.epsilon
< vvalue
< 1+self
.epsilon
:
1053 if privatedata
.rangeaxisindex
:
1054 privatedata
.path
.append(path
.moveto_pt(*graph
.vpos_pt(vvalue
, vpos
)))
1056 privatedata
.path
.append(path
.moveto_pt(*graph
.vpos_pt(vpos
, vvalue
)))
1058 def vposline(self
, privatedata
, sharedata
, graph
, vpos
, vvalue1
, vvalue2
):
1059 if -self
.epsilon
< vpos
< 1+self
.epsilon
:
1074 if abs(vvalue1cut
+ vvalue2cut
) <= 1:
1075 if vvalue1cut
and not self
.fillable
:
1076 if privatedata
.rangeaxisindex
:
1077 privatedata
.path
.append(path
.moveto_pt(*graph
.vpos_pt(vvalue1
, vpos
)))
1079 privatedata
.path
.append(path
.moveto_pt(*graph
.vpos_pt(vpos
, vvalue1
)))
1080 if privatedata
.rangeaxisindex
:
1081 privatedata
.path
.append(graph
.vgeodesic_el(vvalue1
, vpos
, vvalue2
, vpos
))
1083 privatedata
.path
.append(graph
.vgeodesic_el(vpos
, vvalue1
, vpos
, vvalue2
))
1085 def vvalueline(self
, privatedata
, sharedata
, graph
, vvalue
, vpos1
, vpos2
):
1087 if vvalue
< -self
.epsilon
:
1089 logger
.warning("cut at graph boundary adds artificial lines to fillable step histogram path")
1090 if vvalue
> 1+self
.epsilon
:
1092 logger
.warning("cut at graph boundary adds artificial lines to fillable step histogram path")
1093 if self
.fillable
or (-self
.epsilon
< vvalue
< 1+self
.epsilon
):
1108 if abs(vpos1cut
+ vpos2cut
) <= 1:
1111 if privatedata
.rangeaxisindex
:
1112 privatedata
.path
.append(path
.moveto_pt(*graph
.vpos_pt(privatedata
.vfromvalue
, vpos1
)))
1113 privatedata
.path
.append(graph
.vgeodesic_el(privatedata
.vfromvalue
, vpos1
, vvalue
, vpos1
))
1115 privatedata
.path
.append(path
.moveto_pt(*graph
.vpos_pt(vpos1
, privatedata
.vfromvalue
)))
1116 privatedata
.path
.append(graph
.vgeodesic_el(vpos1
, privatedata
.vfromvalue
, vpos1
, vvalue
))
1118 if privatedata
.rangeaxisindex
:
1119 privatedata
.path
.append(path
.moveto_pt(*graph
.vpos_pt(vvalue
, vpos1
)))
1121 privatedata
.path
.append(path
.moveto_pt(*graph
.vpos_pt(vpos1
, vvalue
)))
1122 if privatedata
.rangeaxisindex
:
1123 privatedata
.path
.append(graph
.vgeodesic_el(vvalue
, vpos1
, vvalue
, vpos2
))
1125 privatedata
.path
.append(graph
.vgeodesic_el(vpos1
, vvalue
, vpos2
, vvalue
))
1126 if self
.fillable
and vpos2cut
:
1127 logger
.warning("cut at graph boundary adds artificial lines to fillable step histogram path")
1128 if privatedata
.rangeaxisindex
:
1129 privatedata
.path
.append(graph
.vgeodesic_el(vvalue
, vpos2
, privatedata
.vfromvalue
, vpos2
))
1131 privatedata
.path
.append(graph
.vgeodesic_el(vpos2
, vvalue
, vpos2
, privatedata
.vfromvalue
))
1133 def drawvalue(self
, privatedata
, sharedata
, graph
, vmin
, vmax
, vvalue
):
1134 currentvalid
= vmin
is not None and vmax
is not None and vvalue
is not None
1135 if self
.fillable
and not self
.steps
:
1136 if not currentvalid
:
1139 if vmin
< -self
.epsilon
:
1142 elif vmin
> 1+self
.epsilon
:
1146 if vmax
< -self
.epsilon
:
1149 if vmax
> 1+self
.epsilon
:
1153 if vvalue
< -self
.epsilon
:
1156 if vvalue
> 1+self
.epsilon
:
1160 if abs(vmincut
) + abs(vmaxcut
) + abs(vvaluecut
) + abs(privatedata
.vfromvaluecut
) > 1:
1161 if abs(vmincut
+ vmaxcut
) > 1 or abs(vvaluecut
+privatedata
.vfromvaluecut
) > 1:
1164 logger
.warning("multiple cuts at graph boundary add artificial lines to fillable rectangle histogram path")
1167 if privatedata
.rangeaxisindex
:
1168 privatedata
.path
.append(path
.moveto_pt(*graph
.vpos_pt(privatedata
.vfromvalue
, vmin
)))
1169 privatedata
.path
.append(graph
.vgeodesic_el(privatedata
.vfromvalue
, vmin
, privatedata
.vfromvalue
, vmax
))
1170 privatedata
.path
.append(graph
.vgeodesic_el(privatedata
.vfromvalue
, vmax
, vvalue
, vmax
))
1171 privatedata
.path
.append(graph
.vgeodesic_el(vvalue
, vmax
, vvalue
, vmin
))
1173 privatedata
.path
.append(path
.moveto_pt(*graph
.vpos_pt(vmin
, privatedata
.vfromvalue
)))
1174 privatedata
.path
.append(graph
.vgeodesic_el(vmin
, privatedata
.vfromvalue
, vmax
, privatedata
.vfromvalue
))
1175 privatedata
.path
.append(graph
.vgeodesic_el(vmax
, privatedata
.vfromvalue
, vmax
, vvalue
))
1176 privatedata
.path
.append(graph
.vgeodesic_el(vmax
, vvalue
, vmin
, vvalue
))
1179 if privatedata
.rangeaxisindex
:
1180 privatedata
.path
.append(path
.moveto_pt(*graph
.vpos_pt(vvalue
, vmax
)))
1181 privatedata
.path
.append(graph
.vgeodesic_el(vvalue
, vmax
, vvalue
, vmin
))
1182 privatedata
.path
.append(graph
.vgeodesic_el(vvalue
, vmin
, privatedata
.vfromvalue
, vmin
))
1183 privatedata
.path
.append(graph
.vgeodesic_el(privatedata
.vfromvalue
, vmin
, privatedata
.vfromvalue
, vmax
))
1185 privatedata
.path
.append(path
.moveto_pt(*graph
.vpos_pt(vmax
, vvalue
)))
1186 privatedata
.path
.append(graph
.vgeodesic_el(vmax
, vvalue
, vmin
, vvalue
))
1187 privatedata
.path
.append(graph
.vgeodesic_el(vmin
, vvalue
, vmin
, privatedata
.vfromvalue
))
1188 privatedata
.path
.append(graph
.vgeodesic_el(vmin
, privatedata
.vfromvalue
, vmax
, privatedata
.vfromvalue
))
1189 elif privatedata
.vfromvaluecut
:
1191 if privatedata
.rangeaxisindex
:
1192 privatedata
.path
.append(path
.moveto_pt(*graph
.vpos_pt(privatedata
.vfromvalue
, vmax
)))
1193 privatedata
.path
.append(graph
.vgeodesic_el(privatedata
.vfromvalue
, vmax
, vvalue
, vmax
))
1194 privatedata
.path
.append(graph
.vgeodesic_el(vvalue
, vmax
, vvalue
, vmin
))
1195 privatedata
.path
.append(graph
.vgeodesic_el(vvalue
, vmin
, privatedata
.vfromvalue
, vmin
))
1197 privatedata
.path
.append(path
.moveto_pt(*graph
.vpos_pt(vmax
, privatedata
.vfromvalue
)))
1198 privatedata
.path
.append(graph
.vgeodesic_el(vmax
, privatedata
.vfromvalue
, vmax
, vvalue
))
1199 privatedata
.path
.append(graph
.vgeodesic_el(vmax
, vvalue
, vmin
, vvalue
))
1200 privatedata
.path
.append(graph
.vgeodesic_el(vmin
, vvalue
, vmin
, privatedata
.vfromvalue
))
1203 if privatedata
.rangeaxisindex
:
1204 privatedata
.path
.append(path
.moveto_pt(*graph
.vpos_pt(vvalue
, vmin
)))
1205 privatedata
.path
.append(graph
.vgeodesic_el(vvalue
, vmin
, privatedata
.vfromvalue
, vmin
))
1206 privatedata
.path
.append(graph
.vgeodesic_el(privatedata
.vfromvalue
, vmin
, privatedata
.vfromvalue
, vmax
))
1207 privatedata
.path
.append(graph
.vgeodesic_el(privatedata
.vfromvalue
, vmax
, vvalue
, vmax
))
1209 privatedata
.path
.append(path
.moveto_pt(*graph
.vpos_pt(vmin
, vvalue
)))
1210 privatedata
.path
.append(graph
.vgeodesic_el(vmin
, vvalue
, vmin
, privatedata
.vfromvalue
))
1211 privatedata
.path
.append(graph
.vgeodesic_el(vmin
, privatedata
.vfromvalue
, vmax
, privatedata
.vfromvalue
))
1212 privatedata
.path
.append(graph
.vgeodesic_el(vmax
, privatedata
.vfromvalue
, vmax
, vvalue
))
1214 if privatedata
.rangeaxisindex
:
1215 privatedata
.path
.append(path
.moveto_pt(*graph
.vpos_pt(privatedata
.vfromvalue
, vmin
)))
1216 privatedata
.path
.append(graph
.vgeodesic_el(privatedata
.vfromvalue
, vmin
, privatedata
.vfromvalue
, vmax
))
1217 privatedata
.path
.append(graph
.vgeodesic_el(privatedata
.vfromvalue
, vmax
, vvalue
, vmax
))
1218 privatedata
.path
.append(graph
.vgeodesic_el(vvalue
, vmax
, vvalue
, vmin
))
1219 privatedata
.path
.append(graph
.vgeodesic_el(vvalue
, vmin
, privatedata
.vfromvalue
, vmin
))
1220 privatedata
.path
.append(path
.closepath())
1222 privatedata
.path
.append(path
.moveto_pt(*graph
.vpos_pt(vmin
, privatedata
.vfromvalue
)))
1223 privatedata
.path
.append(graph
.vgeodesic_el(vmin
, privatedata
.vfromvalue
, vmax
, privatedata
.vfromvalue
))
1224 privatedata
.path
.append(graph
.vgeodesic_el(vmax
, privatedata
.vfromvalue
, vmax
, vvalue
))
1225 privatedata
.path
.append(graph
.vgeodesic_el(vmax
, vvalue
, vmin
, vvalue
))
1226 privatedata
.path
.append(graph
.vgeodesic_el(vmin
, vvalue
, vmin
, privatedata
.vfromvalue
))
1227 privatedata
.path
.append(path
.closepath())
1230 gap
= abs(vmin
- privatedata
.lastvmax
) > self
.epsilon
1231 except (ArithmeticError, ValueError, TypeError):
1233 if (privatedata
.lastvvalue
is not None and currentvalid
and not gap
and
1234 (self
.steps
or (privatedata
.lastvvalue
-privatedata
.vfromvalue
)*(vvalue
-privatedata
.vfromvalue
) < 0)):
1235 self
.vposline(privatedata
, sharedata
, graph
,
1236 vmin
, privatedata
.lastvvalue
, vvalue
)
1238 if privatedata
.lastvvalue
is not None and currentvalid
:
1239 currentbigger
= abs(privatedata
.lastvvalue
-privatedata
.vfromvalue
) < abs(vvalue
-privatedata
.vfromvalue
)
1240 if privatedata
.lastvvalue
is not None and (not currentvalid
or not currentbigger
or gap
):
1241 self
.vposline(privatedata
, sharedata
, graph
,
1242 privatedata
.lastvmax
, privatedata
.lastvvalue
, privatedata
.vfromvalue
)
1244 self
.vmoveto(privatedata
, sharedata
, graph
,
1246 if currentvalid
and (privatedata
.lastvvalue
is None or currentbigger
or gap
):
1247 self
.vmoveto(privatedata
, sharedata
, graph
,
1248 vmin
, privatedata
.vfromvalue
)
1249 self
.vposline(privatedata
, sharedata
, graph
,
1250 vmin
, privatedata
.vfromvalue
, vvalue
)
1252 self
.vvalueline(privatedata
, sharedata
, graph
,
1254 privatedata
.lastvvalue
= vvalue
1255 privatedata
.lastvmax
= vmax
1257 privatedata
.lastvvalue
= privatedata
.lastvmax
= None
1259 def initdrawpoints(self
, privatedata
, sharedata
, graph
):
1260 privatedata
.path
= path
.path()
1261 privatedata
.lastvvalue
= privatedata
.lastvmax
= None
1262 privatedata
.vcurrentpoint
= None
1263 privatedata
.count
= 0
1264 if self
.fromvalue
is not None:
1265 valueaxisname
= sharedata
.poscolumnnames
[1-privatedata
.rangeaxisindex
]
1266 privatedata
.vfromvalue
= graph
.axes
[valueaxisname
].convert(self
.fromvalue
)
1267 privatedata
.vfromvaluecut
= 0
1268 if privatedata
.vfromvalue
< 0:
1269 privatedata
.vfromvalue
= 0
1270 privatedata
.vfromvaluecut
= -1
1271 if privatedata
.vfromvalue
> 1:
1272 privatedata
.vfromvalue
= 1
1273 privatedata
.vfromvaluecut
= 1
1274 if self
.frompathattrs
is not None and privatedata
.insertfrompath
:
1275 graph
.layer("data").stroke(graph
.axes
[valueaxisname
].vgridpath(privatedata
.vfromvalue
),
1276 self
.defaultfrompathattrs
+ self
.frompathattrs
)
1278 privatedata
.vfromvalue
= 0
1280 def drawpoint(self
, privatedata
, sharedata
, graph
, point
):
1281 if privatedata
.autohistogram
:
1282 # automatic range handling
1283 privatedata
.count
+= 1
1284 if privatedata
.count
== 2:
1285 if privatedata
.rangeaxisindex
:
1286 privatedata
.vrange
= sharedata
.vpos
[1] - privatedata
.lastvpos
[1]
1287 self
.drawvalue(privatedata
, sharedata
, graph
,
1288 privatedata
.lastvpos
[1] - self
.autohistogrampointpos
*privatedata
.vrange
,
1289 privatedata
.lastvpos
[1] + (1-self
.autohistogrampointpos
)*privatedata
.vrange
,
1290 privatedata
.lastvpos
[0])
1292 privatedata
.vrange
= sharedata
.vpos
[0] - privatedata
.lastvpos
[0]
1293 self
.drawvalue(privatedata
, sharedata
, graph
,
1294 privatedata
.lastvpos
[0] - self
.autohistogrampointpos
*privatedata
.vrange
,
1295 privatedata
.lastvpos
[0] + (1-self
.autohistogrampointpos
)*privatedata
.vrange
,
1296 privatedata
.lastvpos
[1])
1297 elif privatedata
.count
> 2:
1298 if privatedata
.rangeaxisindex
:
1299 vrange
= sharedata
.vpos
[1] - privatedata
.lastvpos
[1]
1301 vrange
= sharedata
.vpos
[0] - privatedata
.lastvpos
[0]
1302 if abs(privatedata
.vrange
- vrange
) > self
.epsilon
:
1303 raise ValueError("equal steps (in graph coordinates) needed for automatic width calculation")
1304 if privatedata
.count
> 1:
1305 if privatedata
.rangeaxisindex
:
1306 self
.drawvalue(privatedata
, sharedata
, graph
,
1307 sharedata
.vpos
[1] - self
.autohistogrampointpos
*privatedata
.vrange
,
1308 sharedata
.vpos
[1] + (1-self
.autohistogrampointpos
)*privatedata
.vrange
,
1311 self
.drawvalue(privatedata
, sharedata
, graph
,
1312 sharedata
.vpos
[0] - self
.autohistogrampointpos
*privatedata
.vrange
,
1313 sharedata
.vpos
[0] + (1-self
.autohistogrampointpos
)*privatedata
.vrange
,
1315 privatedata
.lastvpos
= sharedata
.vpos
[:]
1317 if privatedata
.rangeaxisindex
:
1318 self
.drawvalue(privatedata
, sharedata
, graph
,
1319 sharedata
.vrange
[1][0], sharedata
.vrange
[1][1], sharedata
.vpos
[0])
1321 self
.drawvalue(privatedata
, sharedata
, graph
,
1322 sharedata
.vrange
[0][0], sharedata
.vrange
[0][1], sharedata
.vpos
[1])
1324 def donedrawpoints(self
, privatedata
, sharedata
, graph
):
1325 self
.drawvalue(privatedata
, sharedata
, graph
, None, None, None)
1326 if privatedata
.lineattrs
is not None and len(privatedata
.path
):
1327 graph
.layer("data").draw(privatedata
.path
, privatedata
.lineattrs
)
1329 def key_pt(self
, privatedata
, sharedata
, graph
, x_pt
, y_pt
, width_pt
, height_pt
):
1330 if privatedata
.lineattrs
is not None:
1332 p
= path
.rect_pt(x_pt
, y_pt
, width_pt
, height_pt
)
1334 p
= path
.line_pt(x_pt
, y_pt
+0.5*height_pt
, x_pt
+width_pt
, y_pt
+0.5*height_pt
)
1335 graph
.draw(p
, privatedata
.lineattrs
)
1338 class barpos(_style
):
1340 providesdata
= ["vpos", "vposmissing", "vposavailable", "vposvalid", "vbarrange", "barposcolumnnames", "barvalueindex", "lastbarvalue", "stackedbar", "stackedbardraw"]
1342 defaultfrompathattrs
= []
1344 def __init__(self
, fromvalue
=None, frompathattrs
=[], epsilon
=1e-10):
1345 self
.fromvalue
= fromvalue
1346 self
.frompathattrs
= frompathattrs
1347 self
.epsilon
= epsilon
1349 def columnnames(self
, privatedata
, sharedata
, graph
, columnnames
, dataaxisnames
):
1350 sharedata
.barposcolumnnames
= []
1351 sharedata
.barvalueindex
= None
1352 for dimension
, axisnames
in enumerate(graph
.axesnames
):
1354 for axisname
in axisnames
:
1355 if axisname
in columnnames
:
1356 if sharedata
.barvalueindex
is not None:
1357 raise ValueError("multiple values")
1358 sharedata
.barvalueindex
= dimension
1359 sharedata
.barposcolumnnames
.append(axisname
)
1361 if (axisname
+ "name") in columnnames
:
1362 sharedata
.barposcolumnnames
.append(axisname
+ "name")
1365 raise ValueError("multiple names and value")
1367 raise ValueError("value/name missing")
1368 if sharedata
.barvalueindex
is None:
1369 raise ValueError("missing value")
1370 sharedata
.vposmissing
= []
1371 return sharedata
.barposcolumnnames
1373 def addsubvalue(self
, value
, subvalue
):
1378 return value
[0], self
.addsubvalue(value
[1], subvalue
)
1380 return value
, subvalue
1382 return value
, subvalue
1384 def adjustaxis(self
, privatedata
, sharedata
, graph
, plotitem
, columnname
, data
):
1386 i
= sharedata
.barposcolumnnames
.index(columnname
)
1390 if i
== sharedata
.barvalueindex
:
1391 if self
.fromvalue
is not None:
1392 graph
.axes
[sharedata
.barposcolumnnames
[i
]].adjustaxis([self
.fromvalue
])
1393 graph
.axes
[sharedata
.barposcolumnnames
[i
]].adjustaxis(data
)
1395 graph
.axes
[sharedata
.barposcolumnnames
[i
][:-4]].adjustaxis([self
.addsubvalue(x
, 0) for x
in data
])
1396 graph
.axes
[sharedata
.barposcolumnnames
[i
][:-4]].adjustaxis([self
.addsubvalue(x
, 1) for x
in data
])
1398 def selectstyle(self
, privatedata
, sharedata
, graph
, selectindex
, selecttotal
):
1399 privatedata
.insertfrompath
= selectindex
== 0
1401 def initdrawpoints(self
, privatedata
, sharedata
, graph
):
1402 sharedata
.vpos
= [None]*(len(sharedata
.barposcolumnnames
))
1403 sharedata
.vbarrange
= [[None for i
in builtinrange(2)] for x
in sharedata
.barposcolumnnames
]
1404 sharedata
.stackedbar
= sharedata
.stackedbardraw
= 0
1406 if self
.fromvalue
is not None:
1407 privatedata
.vfromvalue
= graph
.axes
[sharedata
.barposcolumnnames
[sharedata
.barvalueindex
]].convert(self
.fromvalue
)
1408 if privatedata
.vfromvalue
< 0:
1409 privatedata
.vfromvalue
= 0
1410 if privatedata
.vfromvalue
> 1:
1411 privatedata
.vfromvalue
= 1
1412 if self
.frompathattrs
is not None and privatedata
.insertfrompath
:
1413 graph
.layer("data").stroke(graph
.axes
[sharedata
.barposcolumnnames
[sharedata
.barvalueindex
]].vgridpath(privatedata
.vfromvalue
),
1414 self
.defaultfrompathattrs
+ self
.frompathattrs
)
1416 privatedata
.vfromvalue
= 0
1418 def drawpoint(self
, privatedata
, sharedata
, graph
, point
):
1419 sharedata
.vposavailable
= sharedata
.vposvalid
= 1
1420 for i
, barname
in enumerate(sharedata
.barposcolumnnames
):
1421 if i
== sharedata
.barvalueindex
:
1422 sharedata
.vbarrange
[i
][0] = privatedata
.vfromvalue
1423 sharedata
.lastbarvalue
= point
[barname
]
1425 sharedata
.vpos
[i
] = sharedata
.vbarrange
[i
][1] = graph
.axes
[barname
].convert(sharedata
.lastbarvalue
)
1426 except (ArithmeticError, ValueError, TypeError):
1427 sharedata
.vpos
[i
] = sharedata
.vbarrange
[i
][1] = None
1429 for j
in builtinrange(2):
1431 sharedata
.vbarrange
[i
][j
] = graph
.axes
[barname
[:-4]].convert(self
.addsubvalue(point
[barname
], j
))
1432 except (ArithmeticError, ValueError, TypeError):
1433 sharedata
.vbarrange
[i
][j
] = None
1435 sharedata
.vpos
[i
] = 0.5*(sharedata
.vbarrange
[i
][0]+sharedata
.vbarrange
[i
][1])
1436 except (ArithmeticError, ValueError, TypeError):
1437 sharedata
.vpos
[i
] = None
1438 if sharedata
.vpos
[i
] is None:
1439 sharedata
.vposavailable
= sharedata
.vposvalid
= 0
1440 elif sharedata
.vpos
[i
] < -self
.epsilon
or sharedata
.vpos
[i
] > 1+self
.epsilon
:
1441 sharedata
.vposvalid
= 0
1443 registerdefaultprovider(barpos(), ["vbarrange", "barposcolumnnames", "barvalueindex", "lastbarvalue", "stackedbar", "stackedbardraw"])
1446 class stackedbarpos(_style
):
1448 # provides no additional data, but needs some data (and modifies some of them)
1449 needsdata
= ["vbarrange", "barposcolumnnames", "barvalueindex", "lastbarvalue", "stackedbar", "stackedbardraw"]
1451 def __init__(self
, stackname
, addontop
=0, epsilon
=1e-10):
1452 self
.stackname
= stackname
1453 self
.epsilon
= epsilon
1454 self
.addontop
= addontop
1456 def columnnames(self
, privatedata
, sharedata
, graph
, columnnames
, dataaxisnames
):
1457 if self
.stackname
not in columnnames
:
1458 raise ValueError("column '%s' missing" % self
.stackname
)
1459 return [self
.stackname
]
1461 def adjustaxis(self
, privatedata
, sharedata
, graph
, plotitem
, columnname
, data
):
1462 if columnname
== self
.stackname
:
1463 graph
.axes
[sharedata
.barposcolumnnames
[sharedata
.barvalueindex
]].adjustaxis(data
)
1465 def initdrawpoints(self
, privatedata
, sharedata
, graph
):
1466 if sharedata
.stackedbardraw
: # do not count the start bar when not gets painted
1467 sharedata
.stackedbar
+= 1
1469 def drawpoint(self
, privatedata
, sharedata
, graph
, point
):
1470 sharedata
.vbarrange
[sharedata
.barvalueindex
][0] = sharedata
.vbarrange
[sharedata
.barvalueindex
][1]
1473 sharedata
.lastbarvalue
+= point
[self
.stackname
]
1474 except (ArithmeticError, ValueError, TypeError):
1475 sharedata
.lastbarvalue
= None
1477 sharedata
.lastbarvalue
= point
[self
.stackname
]
1479 sharedata
.vpos
[sharedata
.barvalueindex
] = sharedata
.vbarrange
[sharedata
.barvalueindex
][1] = graph
.axes
[sharedata
.barposcolumnnames
[sharedata
.barvalueindex
]].convert(sharedata
.lastbarvalue
)
1480 except (ArithmeticError, ValueError, TypeError):
1481 sharedata
.vpos
[sharedata
.barvalueindex
] = sharedata
.vbarrange
[sharedata
.barvalueindex
][1] = None
1482 sharedata
.vposavailable
= sharedata
.vposvalid
= 0
1484 if not sharedata
.vposavailable
or not sharedata
.vposvalid
:
1485 sharedata
.vposavailable
= sharedata
.vposvalid
= 1
1486 for v
in sharedata
.vpos
:
1488 sharedata
.vposavailable
= sharedata
.vposvalid
= 0
1490 if v
< -self
.epsilon
or v
> 1+self
.epsilon
:
1491 sharedata
.vposvalid
= 0
1496 needsdata
= ["vbarrange"]
1498 defaultbarattrs
= [color
.gradient
.Rainbow
, deco
.stroked([color
.grey
.black
])]
1500 def __init__(self
, barattrs
=[], epsilon
=1e-10, gradient
=color
.gradient
.RedBlack
):
1501 self
.barattrs
= barattrs
1502 self
.epsilon
= epsilon
1503 self
.gradient
= gradient
1505 def lighting(self
, angle
, zindex
):
1506 return self
.gradient
.getcolor(0.7-0.4*abs(angle
)+0.1*zindex
)
1508 def columnnames(self
, privatedata
, sharedata
, graph
, columnnames
, dataaxisnames
):
1511 def selectstyle(self
, privatedata
, sharedata
, graph
, selectindex
, selecttotal
):
1512 privatedata
.barattrs
= attr
.selectattrs(self
.defaultbarattrs
+ self
.barattrs
, selectindex
, selecttotal
)
1514 def initdrawpoints(self
, privatedata
, sharedata
, graph
):
1515 privatedata
.barcanvas
= graph
.layer("filldata").insert(canvas
.canvas())
1516 sharedata
.stackedbardraw
= 1
1517 privatedata
.stackedbar
= sharedata
.stackedbar
1518 privatedata
.todraw
= []
1520 def drawpointfill(self
, privatedata
, p
):
1522 privatedata
.barcanvas
.fill(p
, privatedata
.barattrs
)
1524 def drawpoint(self
, privatedata
, sharedata
, graph
, point
):
1526 for vmin
, vmax
in sharedata
.vbarrange
:
1527 if vmin
is None or vmax
is None:
1528 self
.drawpointfill(privatedata
, None)
1531 vmin
, vmax
= vmax
, vmin
1532 if vmin
> 1 or vmax
< 0:
1533 self
.drawpointfill(privatedata
, None)
1539 vbarrange
.append((vmin
, vmax
))
1540 if len(vbarrange
) == 2:
1541 p
= graph
.vgeodesic(vbarrange
[0][0], vbarrange
[1][0], vbarrange
[0][1], vbarrange
[1][0])
1542 p
.append(graph
.vgeodesic_el(vbarrange
[0][1], vbarrange
[1][0], vbarrange
[0][1], vbarrange
[1][1]))
1543 p
.append(graph
.vgeodesic_el(vbarrange
[0][1], vbarrange
[1][1], vbarrange
[0][0], vbarrange
[1][1]))
1544 p
.append(graph
.vgeodesic_el(vbarrange
[0][0], vbarrange
[1][1], vbarrange
[0][0], vbarrange
[1][0]))
1545 p
.append(path
.closepath())
1546 self
.drawpointfill(privatedata
, p
)
1547 elif len(vbarrange
) == 3:
1549 if abs(vbarrange
[0][0] - vbarrange
[0][1]) > self
.epsilon
and abs(vbarrange
[1][0] - vbarrange
[1][1]):
1550 planes
.append((vbarrange
[0][0], vbarrange
[1][0], vbarrange
[2][0],
1551 vbarrange
[0][1], vbarrange
[1][0], vbarrange
[2][0],
1552 vbarrange
[0][1], vbarrange
[1][1], vbarrange
[2][0],
1553 vbarrange
[0][0], vbarrange
[1][1], vbarrange
[2][0]))
1554 planes
.append((vbarrange
[0][0], vbarrange
[1][0], vbarrange
[2][1],
1555 vbarrange
[0][0], vbarrange
[1][1], vbarrange
[2][1],
1556 vbarrange
[0][1], vbarrange
[1][1], vbarrange
[2][1],
1557 vbarrange
[0][1], vbarrange
[1][0], vbarrange
[2][1]))
1558 if abs(vbarrange
[0][0] - vbarrange
[0][1]) > self
.epsilon
and abs(vbarrange
[2][0] - vbarrange
[2][1]):
1559 planes
.append((vbarrange
[0][0], vbarrange
[1][0], vbarrange
[2][0],
1560 vbarrange
[0][0], vbarrange
[1][0], vbarrange
[2][1],
1561 vbarrange
[0][1], vbarrange
[1][0], vbarrange
[2][1],
1562 vbarrange
[0][1], vbarrange
[1][0], vbarrange
[2][0]))
1563 planes
.append((vbarrange
[0][0], vbarrange
[1][1], vbarrange
[2][0],
1564 vbarrange
[0][1], vbarrange
[1][1], vbarrange
[2][0],
1565 vbarrange
[0][1], vbarrange
[1][1], vbarrange
[2][1],
1566 vbarrange
[0][0], vbarrange
[1][1], vbarrange
[2][1]))
1567 if abs(vbarrange
[1][0] - vbarrange
[1][1]) > self
.epsilon
and abs(vbarrange
[2][0] - vbarrange
[2][1]):
1568 planes
.append((vbarrange
[0][0], vbarrange
[1][0], vbarrange
[2][0],
1569 vbarrange
[0][0], vbarrange
[1][1], vbarrange
[2][0],
1570 vbarrange
[0][0], vbarrange
[1][1], vbarrange
[2][1],
1571 vbarrange
[0][0], vbarrange
[1][0], vbarrange
[2][1]))
1572 planes
.append((vbarrange
[0][1], vbarrange
[1][0], vbarrange
[2][0],
1573 vbarrange
[0][1], vbarrange
[1][0], vbarrange
[2][1],
1574 vbarrange
[0][1], vbarrange
[1][1], vbarrange
[2][1],
1575 vbarrange
[0][1], vbarrange
[1][1], vbarrange
[2][0]))
1576 v
= [0.5 * (vbarrange
[0][0] + vbarrange
[0][1]),
1577 0.5 * (vbarrange
[1][0] + vbarrange
[1][1]),
1578 0.5 * (vbarrange
[2][0] + vbarrange
[2][1])]
1579 v
[sharedata
.barvalueindex
] = 0.5
1580 zindex
= graph
.vzindex(*v
)
1581 for v11
, v12
, v13
, v21
, v22
, v23
, v31
, v32
, v33
, v41
, v42
, v43
in planes
:
1582 angle
= graph
.vangle(v11
, v12
, v13
, v21
, v22
, v23
, v41
, v42
, v43
)
1584 p
= graph
.vgeodesic(v11
, v12
, v13
, v21
, v22
, v23
)
1585 p
.append(graph
.vgeodesic_el(v21
, v22
, v23
, v31
, v32
, v33
))
1586 p
.append(graph
.vgeodesic_el(v31
, v32
, v33
, v41
, v42
, v43
))
1587 p
.append(graph
.vgeodesic_el(v41
, v42
, v43
, v11
, v12
, v13
))
1588 p
.append(path
.closepath())
1590 privatedata
.todraw
.append((-zindex
, p
, privatedata
.barattrs
+ [self
.lighting(angle
, zindex
)]))
1592 privatedata
.todraw
.append((-zindex
, p
, privatedata
.barattrs
))
1594 raise TypeError("bar style restricted to two- and three dimensional graphs")
1596 def donedrawpoints(self
, privatedata
, sharedata
, graph
):
1597 privatedata
.todraw
.sort(key
=lambda x
: x
[0])
1598 for vzindex
, p
, a
in privatedata
.todraw
:
1599 privatedata
.barcanvas
.fill(p
, a
)
1601 def key_pt(self
, privatedata
, sharedata
, graph
, x_pt
, y_pt
, width_pt
, height_pt
):
1602 selectindex
= privatedata
.stackedbar
1603 selecttotal
= sharedata
.stackedbar
+ 1
1604 graph
.fill(path
.rect_pt(x_pt
+ width_pt
*selectindex
/float(selecttotal
), y_pt
, width_pt
/float(selecttotal
), height_pt
), privatedata
.barattrs
)
1607 class changebar(bar
):
1609 def selectstyle(self
, privatedata
, sharedata
, graph
, selectindex
, selecttotal
):
1610 if selecttotal
!= 1:
1611 raise RuntimeError("Changebar can't change its appearance. Thus you can't use it to plot several bars side by side on a subaxis.")
1613 def initdrawpoints(self
, privatedata
, sharedata
, graph
):
1614 if len(graph
.axesnames
) != 2:
1615 raise TypeError("changebar style restricted on two-dimensional graphs (at least for the moment)")
1616 bar
.initdrawpoints(self
, privatedata
, sharedata
, graph
)
1617 privatedata
.bars
= []
1619 def drawpointfill(self
, privatedata
, p
):
1620 privatedata
.bars
.append(p
)
1622 def donedrawpoints(self
, privatedata
, sharedata
, graph
):
1623 selecttotal
= len(privatedata
.bars
)
1624 for selectindex
, p
in enumerate(privatedata
.bars
):
1626 barattrs
= attr
.selectattrs(self
.defaultbarattrs
+ self
.barattrs
, selectindex
, selecttotal
)
1627 privatedata
.barcanvas
.fill(p
, barattrs
)
1629 def key_pt(self
, privatedata
, sharedata
, graph
, x_pt
, y_pt
, width_pt
, height_pt
):
1630 raise RuntimeError("Style currently doesn't provide a graph key")
1633 class gridpos(_style
):
1635 needsdata
= ["vpos", "vposmissing", "vposavailable", "vposvalid"]
1636 providesdata
= ["values1", "values2", "data12", "data21", "index1", "index2"]
1638 def __init__(self
, index1
=0, index2
=1, epsilon
=1e-10):
1639 self
.index1
= index1
1640 self
.index2
= index2
1641 self
.epsilon
= epsilon
1643 def initdrawpoints(self
, privatedata
, sharedata
, graph
):
1644 sharedata
.index1
= self
.index1
1645 sharedata
.index2
= self
.index2
1646 sharedata
.values1
= {}
1647 sharedata
.values2
= {}
1648 sharedata
.data12
= {}
1649 sharedata
.data21
= {}
1651 def drawpoint(self
, privatedata
, sharedata
, graph
, point
):
1652 if sharedata
.vposavailable
:
1653 sharedata
.value1
= sharedata
.vpos
[self
.index1
]
1654 sharedata
.value2
= sharedata
.vpos
[self
.index2
]
1655 if sharedata
.value1
not in sharedata
.values1
:
1656 for hasvalue
in list(sharedata
.values1
.keys()):
1657 if hasvalue
- self
.epsilon
<= sharedata
.value1
<= hasvalue
+ self
.epsilon
:
1658 sharedata
.value1
= hasvalue
1661 sharedata
.values1
[sharedata
.value1
] = 1
1662 if sharedata
.value2
not in sharedata
.values2
:
1663 for hasvalue
in list(sharedata
.values2
.keys()):
1664 if hasvalue
- self
.epsilon
<= sharedata
.value2
<= hasvalue
+ self
.epsilon
:
1665 sharedata
.value2
= hasvalue
1668 sharedata
.values2
[sharedata
.value2
] = 1
1669 data
= sharedata
.vposavailable
, sharedata
.vposvalid
, sharedata
.vpos
[:]
1670 sharedata
.data12
.setdefault(sharedata
.value1
, {})[sharedata
.value2
] = data
1671 sharedata
.data21
.setdefault(sharedata
.value2
, {})[sharedata
.value1
] = data
1673 registerdefaultprovider(gridpos(), gridpos
.providesdata
)
1678 needsdata
= ["values1", "values2", "data12", "data21"]
1680 defaultgridattrs
= [line
.changelinestyle
]
1682 def __init__(self
, gridlines1
=1, gridlines2
=1, gridattrs
=[], **kwargs
):
1683 _line
.__init
__(self
, **kwargs
)
1684 self
.gridlines1
= gridlines1
1685 self
.gridlines2
= gridlines2
1686 self
.gridattrs
= gridattrs
1688 def selectstyle(self
, privatedata
, sharedata
, graph
, selectindex
, selecttotal
):
1689 if self
.gridattrs
is not None:
1690 privatedata
.gridattrs
= attr
.selectattrs(self
.defaultgridattrs
+ self
.gridattrs
, selectindex
, selecttotal
)
1692 privatedata
.gridattrs
= None
1694 def donedrawpoints(self
, privatedata
, sharedata
, graph
):
1695 values1
= list(sharedata
.values1
.keys())
1697 values2
= list(sharedata
.values2
.keys())
1700 for value2
in values2
:
1701 data1
= sharedata
.data21
[value2
]
1702 self
.initpointstopath(privatedata
)
1703 for value1
in values1
:
1705 data
= data1
[value1
]
1707 self
.addinvalid(privatedata
)
1709 self
.addpoint(privatedata
, graph
.vpos_pt
, *data
)
1710 p
= self
.donepointstopath(privatedata
)
1712 graph
.layer("data").stroke(p
, privatedata
.gridattrs
)
1714 for value1
in values1
:
1715 data2
= sharedata
.data12
[value1
]
1716 self
.initpointstopath(privatedata
)
1717 for value2
in values2
:
1719 data
= data2
[value2
]
1721 self
.addinvalid(privatedata
)
1723 self
.addpoint(privatedata
, graph
.vpos_pt
, *data
)
1724 p
= self
.donepointstopath(privatedata
)
1726 graph
.layer("data").stroke(p
, privatedata
.gridattrs
)
1729 class surface(_keygraphstyle
):
1731 needsdata
= ["values1", "values2", "data12", "data21"]
1733 def __init__(self
, gridlines1
=0.05, gridlines2
=0.05, gridcolor
=None,
1734 backcolor
=color
.gray
.black
, **kwargs
):
1735 _keygraphstyle
.__init
__(self
, **kwargs
)
1736 self
.gridlines1
= gridlines1
1737 self
.gridlines2
= gridlines2
1738 self
.gridcolor
= gridcolor
1739 self
.backcolor
= backcolor
1741 colorspacestring
= self
.gradient
.getcolor(0).colorspacestring()
1742 if self
.gridcolor
is not None and self
.gridcolor
.colorspacestring() != colorspacestring
:
1743 raise RuntimeError("colorspace mismatch (gradient/grid)")
1744 if self
.backcolor
is not None and self
.backcolor
.colorspacestring() != colorspacestring
:
1745 raise RuntimeError("colorspace mismatch (gradient/back)")
1747 def midvalue(self
, v1
, v2
, v3
, v4
):
1748 return [0.25*sum(values
) for values
in zip(v1
, v2
, v3
, v4
)]
1750 def midcolor(self
, c1
, c2
, c3
, c4
):
1751 return 0.25*(c1
+c2
+c3
+c4
)
1753 def lighting(self
, angle
, zindex
):
1754 if angle
< 0 and self
.backcolor
is not None:
1755 return self
.backcolor
1756 return self
.gradient
.getcolor(0.7-0.4*abs(angle
)+0.1*zindex
)
1758 def columnnames(self
, privatedata
, sharedata
, graph
, columnnames
, dataaxisnames
):
1759 privatedata
.colorize
= self
.colorname
in columnnames
1760 if privatedata
.colorize
:
1761 return _keygraphstyle
.columnnames(self
, privatedata
, sharedata
, graph
, columnnames
, dataaxisnames
)
1764 def initdrawpoints(self
, privatedata
, sharedata
, graph
):
1765 privatedata
.colors
= {}
1767 def drawpoint(self
, privatedata
, sharedata
, graph
, point
):
1768 if privatedata
.colorize
:
1769 privatedata
.colors
.setdefault(sharedata
.value1
, {})[sharedata
.value2
] = point
[self
.colorname
]
1771 def donedrawpoints(self
, privatedata
, sharedata
, graph
):
1772 v1
= [0]*len(graph
.axesnames
)
1773 v2
= [0]*len(graph
.axesnames
)
1774 v3
= [0]*len(graph
.axesnames
)
1775 v4
= [0]*len(graph
.axesnames
)
1776 v1
[sharedata
.index2
] = 0.5
1777 v2
[sharedata
.index1
] = 0.5
1778 v3
[sharedata
.index1
] = 0.5
1779 v3
[sharedata
.index2
] = 1
1780 v4
[sharedata
.index1
] = 1
1781 v4
[sharedata
.index2
] = 0.5
1782 sortElements
= [-graph
.vzindex(*v1
),
1783 -graph
.vzindex(*v2
),
1784 -graph
.vzindex(*v3
),
1785 -graph
.vzindex(*v4
)]
1787 values1
= list(sharedata
.values1
.keys())
1789 v1
= [0]*len(graph
.axesnames
)
1790 v2
= [0]*len(graph
.axesnames
)
1791 v1
[sharedata
.index1
] = -1
1792 v2
[sharedata
.index1
] = 1
1794 if graph
.vzindex(*v1
) < graph
.vzindex(*v2
):
1797 sortElements
= [sortElements
[3], sortElements
[1], sortElements
[2], sortElements
[0]]
1799 values2
= list(sharedata
.values2
.keys())
1801 v1
= [0]*len(graph
.axesnames
)
1802 v2
= [0]*len(graph
.axesnames
)
1803 v1
[sharedata
.index2
] = -1
1804 v2
[sharedata
.index2
] = 1
1805 if graph
.vzindex(*v1
) < graph
.vzindex(*v2
):
1808 sortElements
= [sortElements
[0], sortElements
[2], sortElements
[1], sortElements
[3]]
1810 sortElements
= [(zindex
, i
) for i
, zindex
in enumerate(sortElements
)]
1815 for value1a
, value1b
in zip(values1
[:-1], values1
[1:]):
1816 for value2a
, value2b
in zip(values2
[:-1], values2
[1:]):
1818 available1
, valid1
, v1
= sharedata
.data12
[value1a
][value2a
]
1819 available2
, valid2
, v2
= sharedata
.data12
[value1a
][value2b
]
1820 available3
, valid3
, v3
= sharedata
.data12
[value1b
][value2a
]
1821 available4
, valid4
, v4
= sharedata
.data12
[value1b
][value2b
]
1824 if not available1
or not available2
or not available3
or not available4
:
1826 if not valid1
or not valid2
or not valid3
or not valid4
:
1827 logger
.warning("surface elements partially outside of the graph are (currently) skipped completely")
1829 def shrink(index
, v1
, v2
, by
):
1832 for i
in builtinrange(3):
1834 v1
[i
], v2
[i
] = v1
[i
] + by
*(v2
[i
]-v1
[i
]), v2
[i
] + by
*(v1
[i
]-v2
[i
])
1836 v1f
, v2f
, v3f
, v4f
= v1
, v2
, v3
, v4
1837 if self
.gridcolor
is not None and self
.gridlines1
:
1838 v1
, v2
= shrink(sharedata
.index1
, v1
, v2
, self
.gridlines1
)
1839 v3
, v4
= shrink(sharedata
.index1
, v3
, v4
, self
.gridlines1
)
1840 if self
.gridcolor
is not None and self
.gridlines2
:
1841 v1
, v3
= shrink(sharedata
.index2
, v1
, v3
, self
.gridlines2
)
1842 v2
, v4
= shrink(sharedata
.index2
, v2
, v4
, self
.gridlines2
)
1843 v5
= self
.midvalue(v1
, v2
, v3
, v4
)
1844 x1_pt
, y1_pt
= graph
.vpos_pt(*v1
)
1845 x2_pt
, y2_pt
= graph
.vpos_pt(*v2
)
1846 x3_pt
, y3_pt
= graph
.vpos_pt(*v3
)
1847 x4_pt
, y4_pt
= graph
.vpos_pt(*v4
)
1848 x5_pt
, y5_pt
= graph
.vpos_pt(*v5
)
1849 if privatedata
.colorize
:
1850 c1
= privatedata
.colors
[value1a
][value2a
]
1851 c2
= privatedata
.colors
[value1a
][value2b
]
1852 c3
= privatedata
.colors
[value1b
][value2a
]
1853 c4
= privatedata
.colors
[value1b
][value2b
]
1854 c5
= self
.midcolor(c1
, c2
, c3
, c4
)
1855 c1a
= c1b
= self
.color(privatedata
, c1
)
1856 c2a
= c2c
= self
.color(privatedata
, c2
)
1857 c3b
= c3d
= self
.color(privatedata
, c3
)
1858 c4c
= c4d
= self
.color(privatedata
, c4
)
1859 c5a
= c5b
= c5c
= c5d
= self
.color(privatedata
, c5
)
1860 if self
.backcolor
is not None and sign
*graph
.vangle(*(v1
+v2
+v5
)) < 0:
1861 c1a
= c2a
= c5a
= self
.backcolor
1862 if self
.backcolor
is not None and sign
*graph
.vangle(*(v3
+v1
+v5
)) < 0:
1863 c3b
= c1b
= c5b
= self
.backcolor
1864 if self
.backcolor
is not None and sign
*graph
.vangle(*(v2
+v4
+v5
)) < 0:
1865 c2c
= c4c
= c5c
= self
.backcolor
1866 if self
.backcolor
is not None and sign
*graph
.vangle(*(v4
+v3
+v5
)) < 0:
1867 c4d
= c3d
= c5d
= self
.backcolor
1869 zindex
= graph
.vzindex(*v5
)
1870 c1a
= c2a
= c5a
= self
.lighting(sign
*graph
.vangle(*(v1
+v2
+v5
)), zindex
)
1871 c3b
= c1b
= c5b
= self
.lighting(sign
*graph
.vangle(*(v3
+v1
+v5
)), zindex
)
1872 c2c
= c4c
= c5c
= self
.lighting(sign
*graph
.vangle(*(v2
+v4
+v5
)), zindex
)
1873 c4d
= c3d
= c5d
= self
.lighting(sign
*graph
.vangle(*(v4
+v3
+v5
)), zindex
)
1874 for zindex
, i
in sortElements
:
1876 elements
.append(mesh
.element((mesh
.node_pt((x1_pt
, y1_pt
), c1a
),
1877 mesh
.node_pt((x2_pt
, y2_pt
), c2a
),
1878 mesh
.node_pt((x5_pt
, y5_pt
), c5a
))))
1879 if self
.gridcolor
is not None and self
.gridlines2
:
1880 elements
.append(mesh
.element((mesh
.node_pt(graph
.vpos_pt(*v1f
), self
.gridcolor
),
1881 mesh
.node_pt(graph
.vpos_pt(*v2
), self
.gridcolor
),
1882 mesh
.node_pt(graph
.vpos_pt(*v1
), self
.gridcolor
))))
1883 elements
.append(mesh
.element((mesh
.node_pt(graph
.vpos_pt(*v1f
), self
.gridcolor
),
1884 mesh
.node_pt(graph
.vpos_pt(*v2
), self
.gridcolor
),
1885 mesh
.node_pt(graph
.vpos_pt(*v2f
), self
.gridcolor
))))
1887 elements
.append(mesh
.element((mesh
.node_pt((x3_pt
, y3_pt
), c3b
),
1888 mesh
.node_pt((x1_pt
, y1_pt
), c1b
),
1889 mesh
.node_pt((x5_pt
, y5_pt
), c5b
))))
1890 if self
.gridcolor
is not None and self
.gridlines1
:
1891 elements
.append(mesh
.element((mesh
.node_pt(graph
.vpos_pt(*v1f
), self
.gridcolor
),
1892 mesh
.node_pt(graph
.vpos_pt(*v3
), self
.gridcolor
),
1893 mesh
.node_pt(graph
.vpos_pt(*v1
), self
.gridcolor
))))
1894 elements
.append(mesh
.element((mesh
.node_pt(graph
.vpos_pt(*v1f
), self
.gridcolor
),
1895 mesh
.node_pt(graph
.vpos_pt(*v3
), self
.gridcolor
),
1896 mesh
.node_pt(graph
.vpos_pt(*v3f
), self
.gridcolor
))))
1898 elements
.append(mesh
.element((mesh
.node_pt((x2_pt
, y2_pt
), c2c
),
1899 mesh
.node_pt((x4_pt
, y4_pt
), c4c
),
1900 mesh
.node_pt((x5_pt
, y5_pt
), c5c
))))
1901 if self
.gridcolor
is not None and self
.gridlines1
:
1902 elements
.append(mesh
.element((mesh
.node_pt(graph
.vpos_pt(*v2f
), self
.gridcolor
),
1903 mesh
.node_pt(graph
.vpos_pt(*v4
), self
.gridcolor
),
1904 mesh
.node_pt(graph
.vpos_pt(*v2
), self
.gridcolor
))))
1905 elements
.append(mesh
.element((mesh
.node_pt(graph
.vpos_pt(*v2f
), self
.gridcolor
),
1906 mesh
.node_pt(graph
.vpos_pt(*v4
), self
.gridcolor
),
1907 mesh
.node_pt(graph
.vpos_pt(*v4f
), self
.gridcolor
))))
1909 elements
.append(mesh
.element((mesh
.node_pt((x4_pt
, y4_pt
), c4d
),
1910 mesh
.node_pt((x3_pt
, y3_pt
), c3d
),
1911 mesh
.node_pt((x5_pt
, y5_pt
), c5d
))))
1912 if self
.gridcolor
is not None and self
.gridlines2
:
1913 elements
.append(mesh
.element((mesh
.node_pt(graph
.vpos_pt(*v3f
), self
.gridcolor
),
1914 mesh
.node_pt(graph
.vpos_pt(*v4
), self
.gridcolor
),
1915 mesh
.node_pt(graph
.vpos_pt(*v3
), self
.gridcolor
))))
1916 elements
.append(mesh
.element((mesh
.node_pt(graph
.vpos_pt(*v3f
), self
.gridcolor
),
1917 mesh
.node_pt(graph
.vpos_pt(*v4
), self
.gridcolor
),
1918 mesh
.node_pt(graph
.vpos_pt(*v4f
), self
.gridcolor
))))
1919 m
= mesh
.mesh(elements
, check
=0)
1920 graph
.layer("filldata").insert(m
)
1922 if privatedata
.colorize
:
1923 _keygraphstyle
.donedrawpoints(self
, privatedata
, sharedata
, graph
)
1926 class density(_keygraphstyle
):
1928 needsdata
= ["values1", "values2", "data12", "data21"]
1930 def __init__(self
, epsilon
=1e-10, **kwargs
):
1931 _keygraphstyle
.__init
__(self
, **kwargs
)
1932 self
.epsilon
= epsilon
1934 def initdrawpoints(self
, privatedata
, sharedata
, graph
):
1935 privatedata
.colors
= {}
1936 privatedata
.vfixed
= [None]*len(graph
.axesnames
)
1938 def drawpoint(self
, privatedata
, sharedata
, graph
, point
):
1939 privatedata
.colors
.setdefault(sharedata
.value1
, {})[sharedata
.value2
] = point
[self
.colorname
]
1940 if len(privatedata
.vfixed
) > 2 and sharedata
.vposavailable
:
1941 for i
, (v1
, v2
) in enumerate(list(zip(privatedata
.vfixed
, sharedata
.vpos
))):
1942 if i
!= sharedata
.index1
and i
!= sharedata
.index2
:
1944 privatedata
.vfixed
[i
] = v2
1945 elif abs(v1
-v2
) > self
.epsilon
:
1946 raise ValueError("data must be in a plane for the density style")
1948 def donedrawpoints(self
, privatedata
, sharedata
, graph
):
1949 privatedata
.keygraph
.doaxes()
1951 values1
= sorted(list(sharedata
.values1
.keys()))
1952 values2
= sorted(list(sharedata
.values2
.keys()))
1953 def equidistant(values
):
1956 raise ValueError("several data points required by the density style in each dimension")
1957 range = values
[-1] - values
[0]
1958 for i
, value
in enumerate(values
):
1959 if abs(value
- values
[0] - i
* range / l
) > self
.epsilon
:
1960 raise ValueError("data must be equidistant for the density style")
1961 equidistant(values1
)
1962 equidistant(values2
)
1964 for value2
in values2
:
1965 for value1
in values1
:
1967 available
, valid
, v
= sharedata
.data12
[value1
][value2
]
1977 mode
= {"/DeviceGray": "L",
1978 "/DeviceRGB": "RGB",
1979 "/DeviceCMYK": "CMYK"}[self
.gradient
.getcolor(0).colorspacestring()]
1982 empty
= b
"\0"*len(mode
)
1984 for value2
in values2
:
1985 for value1
in values1
:
1987 available
, valid
, v
= sharedata
.data12
[value1
][value2
]
1994 c
= privatedata
.colors
[value1
][value2
]
1995 c
= self
.color(privatedata
, c
)
1997 data
.write(bytes((255,)))
1998 data
.write(c
.to8bitbytes())
1999 i
= bitmap
.image(len(values1
), len(values2
), mode
, data
.getvalue())
2001 v1enlargement
= (values1
[-1]-values1
[0])*0.5/len(values1
)
2002 v2enlargement
= (values2
[-1]-values2
[0])*0.5/len(values2
)
2004 privatedata
.vfixed
[sharedata
.index1
] = values1
[0]-v1enlargement
2005 privatedata
.vfixed
[sharedata
.index2
] = values2
[-1]+v2enlargement
2006 x1_pt
, y1_pt
= graph
.vpos_pt(*privatedata
.vfixed
)
2007 privatedata
.vfixed
[sharedata
.index1
] = values1
[-1]+v1enlargement
2008 privatedata
.vfixed
[sharedata
.index2
] = values2
[-1]+v2enlargement
2009 x2_pt
, y2_pt
= graph
.vpos_pt(*privatedata
.vfixed
)
2010 privatedata
.vfixed
[sharedata
.index1
] = values1
[0]-v1enlargement
2011 privatedata
.vfixed
[sharedata
.index2
] = values2
[0]-v2enlargement
2012 x3_pt
, y3_pt
= graph
.vpos_pt(*privatedata
.vfixed
)
2013 t
= trafo
.trafo_pt(((x2_pt
-x1_pt
, x3_pt
-x1_pt
), (y2_pt
-y1_pt
, y3_pt
-y1_pt
)), (x1_pt
, y1_pt
))
2015 privatedata
.vfixed
[sharedata
.index1
] = values1
[-1]+v1enlargement
2016 privatedata
.vfixed
[sharedata
.index2
] = values2
[0]-v2enlargement
2017 vx4
, vy4
= t
.inverse().apply_pt(*graph
.vpos_pt(*privatedata
.vfixed
))
2018 if abs(vx4
- 1) > self
.epsilon
or abs(vy4
- 1) > self
.epsilon
:
2019 raise ValueError("invalid graph layout for density style (bitmap positioning by affine transformation failed)")
2022 privatedata
.vfixed
[sharedata
.index1
] = 0
2023 privatedata
.vfixed
[sharedata
.index2
] = 0
2024 p
.append(path
.moveto_pt(*graph
.vpos_pt(*privatedata
.vfixed
)))
2025 vfixed2
= privatedata
.vfixed
+ privatedata
.vfixed
2026 vfixed2
[sharedata
.index1
] = 0
2027 vfixed2
[sharedata
.index2
] = 0
2028 vfixed2
[sharedata
.index1
+ len(graph
.axesnames
)] = 1
2029 vfixed2
[sharedata
.index2
+ len(graph
.axesnames
)] = 0
2030 p
.append(graph
.vgeodesic_el(*vfixed2
))
2031 vfixed2
[sharedata
.index1
] = 1
2032 vfixed2
[sharedata
.index2
] = 0
2033 vfixed2
[sharedata
.index1
+ len(graph
.axesnames
)] = 1
2034 vfixed2
[sharedata
.index2
+ len(graph
.axesnames
)] = 1
2035 p
.append(graph
.vgeodesic_el(*vfixed2
))
2036 vfixed2
[sharedata
.index1
] = 1
2037 vfixed2
[sharedata
.index2
] = 1
2038 vfixed2
[sharedata
.index1
+ len(graph
.axesnames
)] = 0
2039 vfixed2
[sharedata
.index2
+ len(graph
.axesnames
)] = 1
2040 p
.append(graph
.vgeodesic_el(*vfixed2
))
2041 vfixed2
[sharedata
.index1
] = 0
2042 vfixed2
[sharedata
.index2
] = 1
2043 vfixed2
[sharedata
.index1
+ len(graph
.axesnames
)] = 0
2044 vfixed2
[sharedata
.index2
+ len(graph
.axesnames
)] = 0
2045 p
.append(graph
.vgeodesic_el(*vfixed2
))
2046 p
.append(path
.closepath())
2048 c
= canvas
.canvas([canvas
.clip(p
)])
2049 b
= bitmap
.bitmap_trafo(t
, i
)
2051 graph
.layer("filldata").insert(c
)
2053 _keygraphstyle
.donedrawpoints(self
, privatedata
, sharedata
, graph
)
2057 class gradient(_style
):
2059 defaultbarattrs
= [color
.gradient
.Rainbow
, deco
.stroked([color
.grey
.black
])]
2061 def __init__(self
, gradient
=color
.gradient
.Gray
, resolution
=100, columnname
="x"):
2062 self
.gradient
= gradient
2063 self
.resolution
= resolution
2064 self
.columnname
= columnname
2066 def columnnames(self
, privatedata
, sharedata
, graph
, columnnames
, dataaxisnames
):
2067 return [self
.columnname
]
2069 def adjustaxis(self
, privatedata
, sharedata
, graph
, plotitem
, columnname
, data
):
2070 graph
.axes
[self
.columnname
].adjustaxis(data
)
2072 def selectstyle(self
, privatedata
, sharedata
, graph
, selectindex
, selecttotal
):
2075 def initdrawpoints(self
, privatedata
, sharedata
, graph
):
2078 def drawpoint(self
, privatedata
, sharedata
, graph
, point
):
2081 def donedrawpoints(self
, privatedata
, sharedata
, graph
):
2082 mode
= {"/DeviceGray": "L",
2083 "/DeviceRGB": "RGB",
2084 "/DeviceCMYK": "CMYK"}[self
.gradient
.getcolor(0).colorspacestring()]
2086 for i
in builtinrange(self
.resolution
):
2087 c
= self
.gradient
.getcolor(i
*1.0/self
.resolution
)
2088 data
.write(c
.to8bitbytes())
2089 i
= bitmap
.image(self
.resolution
, 1, mode
, data
.getvalue())
2091 llx_pt
, lly_pt
= graph
.vpos_pt(0)
2092 urx_pt
, ury_pt
= graph
.vpos_pt(1)
2094 if graph
.direction
== "horizontal":
2095 lly_pt
-= 0.5*graph
.height_pt
2096 ury_pt
+= 0.5*graph
.height_pt
2098 llx_pt
-= 0.5*graph
.width_pt
2099 urx_pt
+= 0.5*graph
.width_pt
2101 c
= canvas
.canvas([canvas
.clip(path
.rect_pt(llx_pt
, lly_pt
, urx_pt
-llx_pt
, ury_pt
-lly_pt
))])
2103 if graph
.direction
== "horizontal":
2104 add_pt
= (urx_pt
-llx_pt
)*0.5/(self
.resolution
-1)
2108 add_pt
= (ury_pt
-lly_pt
)*0.5/(self
.resolution
-1)
2112 if graph
.direction
== "horizontal":
2113 t
= trafo
.trafo_pt(((urx_pt
-llx_pt
,0 ), (0, ury_pt
-lly_pt
)), (llx_pt
, lly_pt
))
2115 t
= trafo
.trafo_pt(((0, urx_pt
-llx_pt
), (ury_pt
-lly_pt
, 0)), (llx_pt
, lly_pt
))
2117 b
= bitmap
.bitmap_trafo(t
, i
)
2119 graph
.layer("filldata").insert(c
)